31 changed files with 3709 additions and 1957 deletions
-4
View File
@@ -8,7 +8,3 @@ dist/
deploy/certbot/ deploy/certbot/
deploy/data-dump.json deploy/data-dump.json
uploads/ uploads/
# Data cleaning runtime artifacts (production snapshots, exports, reports)
data-cleaning/runs/
deploy/backups/
+34 -32
View File
@@ -1,32 +1,34 @@
{ {
"name": "@inkreach/admin", "name": "@inkreach/admin",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vue-tsc -b && vite build", "build": "vue-tsc -b && vite build",
"preview": "vite preview" "preview": "vite preview",
}, "test": "vitest run"
"dependencies": { },
"@element-plus/icons-vue": "^2.3.2", "dependencies": {
"@vueuse/core": "^14.3.0", "@element-plus/icons-vue": "^2.3.2",
"axios": "^1.18.0", "@vueuse/core": "^14.3.0",
"dayjs": "^1.11.21", "axios": "^1.18.0",
"element-plus": "^2.14.2", "dayjs": "^1.11.21",
"pinia": "^3.0.4", "element-plus": "^2.14.2",
"vue": "^3.5.34", "pinia": "^3.0.4",
"vue-router": "^4.6.4" "vue": "^3.5.34",
}, "vue-router": "^4.6.4"
"devDependencies": { },
"@types/node": "^24.12.3", "devDependencies": {
"@vitejs/plugin-vue": "^6.0.6", "@types/node": "^24.12.3",
"@vue/tsconfig": "^0.9.1", "@vitejs/plugin-vue": "^6.0.6",
"sass": "^1.101.0", "@vue/tsconfig": "^0.9.1",
"typescript": "~6.0.2", "sass": "^1.101.0",
"unplugin-auto-import": "^21.0.0", "typescript": "~6.0.2",
"unplugin-vue-components": "^32.1.0", "unplugin-auto-import": "^21.0.0",
"vite": "^8.0.12", "unplugin-vue-components": "^32.1.0",
"vue-tsc": "^3.2.8" "vite": "^8.0.12",
} "vitest": "^4.1.10",
} "vue-tsc": "^3.2.8"
}
}
+2
View File
@@ -18,6 +18,7 @@ declare module 'vue' {
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCascader: typeof import('element-plus/es')['ElCascader'] ElCascader: typeof import('element-plus/es')['ElCascader']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
ElContainer: typeof import('element-plus/es')['ElContainer'] ElContainer: typeof import('element-plus/es')['ElContainer']
ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
@@ -41,6 +42,7 @@ declare module 'vue' {
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup'] ElOptionGroup: typeof import('element-plus/es')['ElOptionGroup']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElSelect: typeof import('element-plus/es')['ElSelect'] ElSelect: typeof import('element-plus/es')['ElSelect']
+137 -124
View File
@@ -24,7 +24,7 @@ export interface User {
} }
// Good types // Good types
export interface Good { export interface Good {
id: string id: string
goodName: string goodName: string
goodImage: string | null goodImage: string | null
@@ -42,81 +42,92 @@ export interface Good {
position?: Position position?: Position
createdAt: string createdAt: string
updatedAt: string updatedAt: string
} mergedOriginGoods?: MergedOriginGoodSummary[]
}
export interface OriginGoodDetail {
productCode?: string | null
englishName?: string | null
productionCycleHours?: number | null
minWeightG?: string | null
productionProcess?: string | null
materialDescription?: string | null
sizeChart?: { columns?: Array<{ key: string; name: string }>; rows?: any[] } | null
packageSpecs?: { rows?: any[] } | null
syncedAt?: string | null
[key: string]: unknown
}
export interface OriginGoodVariant {
sdsVariantId: string
sku: string
sizeName?: string | null
colorName?: string | null
colorHex?: string | null
price?: string | null
enabled: boolean
[key: string]: unknown
}
export interface CustomGoodVariantRequest {
sku: string
sizeId?: string | null
sizeName?: string | null
colorId?: string | null
colorName?: string | null
colorHex?: string | null
imageUrl?: string | null
price?: string | null
originalPrice?: string | null
weightG?: string | null
boxLengthCm?: string | null
boxWidthCm?: string | null
boxHeightCm?: string | null
designData?: Record<string, unknown> | null
enabled?: boolean
sortOrder?: number
}
export interface CustomGoodDetailRequest {
productCode?: string | null
englishName?: string | null
productionCycleHours?: number | null
minWeightG?: string | null
productionProcess?: string | null
materialDescription?: string | null
blankDesignUrl?: string | null
detailsPageVideoUrl?: string | null
textureName?: string | null
reminder?: string | null
productPerformance?: string | null
applicableScenarios?: string | null
washingInstructions?: string | null
specialDescription?: string | null
designExplanation?: string | null
designArea?: string | null
pictureRequest?: string | null
sizeChart?: Record<string, unknown> | null
packageSpecs?: Record<string, unknown> | null
options?: Record<string, unknown> | null
media?: Record<string, unknown> | null
}
export interface GoodDetail extends Good {
originDetail: OriginGoodDetail | null
variants: OriginGoodVariant[]
}
export interface CreateGoodRequest { export interface MergedOriginGoodSummary {
id: string
sdsGoodId: string
goodName: string | null
goodImage: string | null
goodPrice: string | null
hasDetail: boolean
variantCount: number
}
export interface OriginGoodDetail {
productCode?: string | null
englishName?: string | null
productionCycleHours?: number | null
minWeightG?: string | null
productionProcess?: string | null
materialDescription?: string | null
sizeChart?: { columns?: Array<{ key: string; name: string }>; rows?: any[] } | null
packageSpecs?: { rows?: any[] } | null
syncedAt?: string | null
[key: string]: unknown
}
export interface OriginGoodVariant {
sdsVariantId: string
sku: string
sizeName?: string | null
colorName?: string | null
colorHex?: string | null
price?: string | null
enabled: boolean
[key: string]: unknown
}
export interface CustomGoodVariantRequest {
sku: string
sizeId?: string | null
sizeName?: string | null
colorId?: string | null
colorName?: string | null
colorHex?: string | null
imageUrl?: string | null
price?: string | null
originalPrice?: string | null
weightG?: string | null
boxLengthCm?: string | null
boxWidthCm?: string | null
boxHeightCm?: string | null
designData?: Record<string, unknown> | null
enabled?: boolean
sortOrder?: number
}
export interface CustomGoodDetailRequest {
productCode?: string | null
englishName?: string | null
productionCycleHours?: number | null
minWeightG?: string | null
productionProcess?: string | null
materialDescription?: string | null
blankDesignUrl?: string | null
detailsPageVideoUrl?: string | null
textureName?: string | null
reminder?: string | null
productPerformance?: string | null
applicableScenarios?: string | null
washingInstructions?: string | null
specialDescription?: string | null
designExplanation?: string | null
designArea?: string | null
pictureRequest?: string | null
sizeChart?: Record<string, unknown> | null
packageSpecs?: Record<string, unknown> | null
options?: Record<string, unknown> | null
media?: Record<string, unknown> | null
}
export interface GoodDetail extends Good {
originDetail: OriginGoodDetail | null
variants: OriginGoodVariant[]
}
export interface CreateGoodRequest {
goodName: string goodName: string
goodImage?: string goodImage?: string
originGoodId: number originGoodId: number
@@ -124,29 +135,30 @@ export interface CreateGoodRequest {
categoryId: number categoryId: number
tagIds?: number[] tagIds?: number[]
positionId?: number positionId?: number
goodPriority?: number goodPriority?: number
} mergedOriginGoodIds?: number[]
}
export interface CreateCustomGoodRequest {
goodName: string export interface CreateCustomGoodRequest {
goodImage?: string goodName: string
goodPrice?: string | null goodImage?: string
countryId: number goodPrice?: string | null
categoryId: number countryId: number
tagIds?: number[] categoryId: number
positionId?: number tagIds?: number[]
goodPriority?: number positionId?: number
detail?: CustomGoodDetailRequest goodPriority?: number
variants?: CustomGoodVariantRequest[] detail?: CustomGoodDetailRequest
} variants?: CustomGoodVariantRequest[]
}
export interface UpdateCustomGoodContentRequest {
goodName?: string export interface UpdateCustomGoodContentRequest {
goodImage?: string | null goodName?: string
goodPrice?: string | null goodImage?: string | null
detail?: CustomGoodDetailRequest goodPrice?: string | null
variants?: CustomGoodVariantRequest[] detail?: CustomGoodDetailRequest
} variants?: CustomGoodVariantRequest[]
}
export interface UpdateGoodRequest { export interface UpdateGoodRequest {
goodName?: string goodName?: string
@@ -157,6 +169,7 @@ export interface UpdateGoodRequest {
tagIds?: number[] tagIds?: number[]
positionId?: number | null positionId?: number | null
goodPriority?: number goodPriority?: number
mergedOriginGoodIds?: number[]
} }
export interface BatchCreateItem { export interface BatchCreateItem {
@@ -329,28 +342,28 @@ export interface UpdatePositionRequest {
} }
// Origin Good types // Origin Good types
export interface OriginGood { export interface OriginGood {
id: string id: string
goodName: string | null goodName: string | null
goodImage: string | null goodImage: string | null
goodPrice: string | null goodPrice: string | null
sdsGoodId: string sdsGoodId: string
source: 'SDS' | 'CUSTOM' source: 'SDS' | 'CUSTOM'
isCustom: boolean isCustom: boolean
sdsCategoryId: string | null sdsCategoryId: string | null
delisted?: boolean delisted?: boolean
createdAt: string createdAt: string
updatedAt: string updatedAt: string
hasDetail?: boolean hasDetail?: boolean
detailSyncedAt?: string | null detailSyncedAt?: string | null
variantCount?: number variantCount?: number
sizeRowCount?: number sizeRowCount?: number
packageRowCount?: number packageRowCount?: number
productCode?: string | null productCode?: string | null
} }
// Origin Goods Tree types // Origin Goods Tree types
export interface OriginGoodsTreeNode { export interface OriginGoodsTreeNode {
id: string id: string
goodName: string goodName: string
goodImage: string | null goodImage: string | null
@@ -359,12 +372,12 @@ export interface OriginGoodsTreeNode {
delisted: boolean delisted: boolean
configuredCount: number configuredCount: number
configuredCountries: string[] configuredCountries: string[]
configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[] configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]
hasDetail: boolean hasDetail: boolean
detailSyncedAt: string | null detailSyncedAt: string | null
variantCount: number variantCount: number
sizeRowCount: number sizeRowCount: number
packageRowCount: number packageRowCount: number
} }
export interface OriginGoodsTreeCategoryNode { export interface OriginGoodsTreeCategoryNode {
@@ -384,14 +397,14 @@ export interface OriginGoodsTreeResponse {
} }
// Sync types // Sync types
export interface SyncLog { export interface SyncLog {
id: string id: string
type: 'CATEGORIES' | 'PRODUCTS' | 'PRODUCT_DETAILS' type: 'CATEGORIES' | 'PRODUCTS' | 'PRODUCT_DETAILS'
status: 'RUNNING' | 'SUCCESS' | 'FAILED' status: 'RUNNING' | 'SUCCESS' | 'FAILED'
message: string | null message: string | null
startedAt: string startedAt: string
finishedAt: string | null finishedAt: string | null
} }
// Filter types // Filter types
export interface GoodsFilter { export interface GoodsFilter {
+46
View File
@@ -0,0 +1,46 @@
import { describe, expect, it } from 'vitest';
import { sameOriginGroup, truncateToProcess } from './origin-name';
describe('truncateToProcess', () => {
it('keeps first 3 dash segments (drops warehouse)', () => {
expect(truncateToProcess('美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美西洛杉矶一仓'))
.toBe('美国(包邮)180g纯棉T恤成人款-DG001-单面印花');
});
it('is identity for 3-segment names', () => {
expect(truncateToProcess('美国(不包邮)230g水洗炒雪花T恤-KRHM003-直喷双面'))
.toBe('美国(不包邮)230g水洗炒雪花T恤-KRHM003-直喷双面');
});
it('handles malformed names without country prefix', () => {
expect(truncateToProcess('(不包邮)230g水洗炒雪花T恤-FRTM001-双面印花'))
.toBe('(不包邮)230g水洗炒雪花T恤-FRTM001-双面印花');
});
it('returns empty for null/empty', () => {
expect(truncateToProcess(null)).toBe('');
expect(truncateToProcess('')).toBe('');
});
});
describe('sameOriginGroup', () => {
it('matches same product across warehouses', () => {
expect(sameOriginGroup(
'美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美西洛杉矶一仓',
'美国(包邮)180g纯棉T恤成人款-DG001-单面印花-美中亚特兰大仓',
)).toBe(true);
});
it('does not match different SKU', () => {
expect(sameOriginGroup(
'美国(包邮)180g纯棉T恤成人款-DG001-单面印花',
'美国(包邮)180g纯棉T恤成人款-DG002-单面印花',
)).toBe(false);
});
it('does not match different country', () => {
expect(sameOriginGroup(
'美国(包邮)T恤-DG001-单面印花',
'德国(不包邮)T恤-DG001-单面印花',
)).toBe(false);
});
it('does not match empty names', () => {
expect(sameOriginGroup(null, 'x-y-z')).toBe(false);
expect(sameOriginGroup('', '')).toBe(false);
});
});
+20
View File
@@ -0,0 +1,20 @@
/**
* 原产品名匹配规则(见 data-cleaning/README.md 命名调查):
* `国家(物流备注)品名-SKU-工艺位置[-仓库名]`
* 截断到「工艺位置」= 保留按 `-` 分段的前 3 段,丢弃可选的仓库名段。
* 国家前缀在第 1 段内,天然参与比较(不同国家不合并)。
*/
const KEEP_SEGMENTS = 3;
export function truncateToProcess(name: string | null | undefined): string {
if (!name) return '';
return name.split('-').slice(0, KEEP_SEGMENTS).join('-');
}
export function sameOriginGroup(
a: string | null | undefined,
b: string | null | undefined,
): boolean {
const ka = truncateToProcess(a);
return ka !== '' && ka === truncateToProcess(b);
}
@@ -52,7 +52,6 @@ const dialogLoading = ref(false)
const dialogForm = reactive<CreateCategoryRequest & { id?: string }>({ const dialogForm = reactive<CreateCategoryRequest & { id?: string }>({
categoryName: '', categoryName: '',
categoryIcon: '', categoryIcon: '',
parentCategoryId: '',
}) })
const dialogRules = { const dialogRules = {
@@ -146,7 +145,7 @@ onMounted(fetchTree)
> >
<el-table-column prop="categoryName" label="名称" min-width="220" /> <el-table-column prop="categoryName" label="名称" min-width="220" />
<el-table-column label="图标" width="100"> <el-table-column label="图标" width="100">
<template #default="{ row }: { row: CategoryTree }"> <template #default="{ row }: any">
<el-image <el-image
v-if="row.categoryIcon" v-if="row.categoryIcon"
:src="row.categoryIcon" :src="row.categoryIcon"
@@ -158,17 +157,17 @@ onMounted(fetchTree)
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="父级"> <el-table-column label="父级">
<template #default="{ row }: { row: CategoryTree }"> <template #default="{ row }: any">
{{ row.parent?.categoryName || '-' }} {{ row.parent?.categoryName || '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="子级数" width="100"> <el-table-column label="子级数" width="100">
<template #default="{ row }: { row: CategoryTree }"> <template #default="{ row }: any">
{{ row._count?.children ?? row.children?.length ?? 0 }} {{ row._count?.children ?? row.children?.length ?? 0 }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }: { row: CategoryTree }"> <template #default="{ row }: any">
<div class="table-actions"> <div class="table-actions">
<el-button size="small" type="primary" plain @click="openEditDialog(row)"> <el-button size="small" type="primary" plain @click="openEditDialog(row)">
<el-icon><Edit /></el-icon> <el-icon><Edit /></el-icon>
@@ -23,7 +23,7 @@ const filter = reactive<Required<CountryFilter>>({
async function fetchList() { async function fetchList() {
loading.value = true loading.value = true
try { try {
const res = await countriesApi.getCountriesList(filter) await countriesApi.getCountriesList(filter)
const data = await countriesApi.getCountriesList(filter) as any const data = await countriesApi.getCountriesList(filter) as any
const arr = Array.isArray(data) ? data : (data.items ?? []) const arr = Array.isArray(data) ? data : (data.items ?? [])
list.value = arr list.value = arr
@@ -147,7 +147,7 @@ onMounted(fetchList)
<el-table v-loading="loading" :data="list" border stripe> <el-table v-loading="loading" :data="list" border stripe>
<el-table-column label="图标" width="80"> <el-table-column label="图标" width="80">
<template #default="{ row }: { row: Country }"> <template #default="{ row }: any">
<el-image <el-image
v-if="row.countryIcon" v-if="row.countryIcon"
:src="row.countryIcon" :src="row.countryIcon"
@@ -160,12 +160,12 @@ onMounted(fetchList)
</el-table-column> </el-table-column>
<el-table-column prop="countryName" label="名称" min-width="200" /> <el-table-column prop="countryName" label="名称" min-width="200" />
<el-table-column label="创建时间" width="180"> <el-table-column label="创建时间" width="180">
<template #default="{ row }: { row: Country }"> <template #default="{ row }: any">
{{ new Date(row.createdAt).toLocaleString() }} {{ new Date(row.createdAt).toLocaleString() }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }: { row: Country }"> <template #default="{ row }: any">
<div class="table-actions"> <div class="table-actions">
<el-button size="small" type="primary" plain @click="openEditDialog(row)"> <el-button size="small" type="primary" plain @click="openEditDialog(row)">
<el-icon><Edit /></el-icon> <el-icon><Edit /></el-icon>
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,6 @@ import type {
UpdatePositionRequest, UpdatePositionRequest,
PositionFilter, PositionFilter,
Country, Country,
Category,
CategoryTree, CategoryTree,
} from '@/types' } from '@/types'
import { positionsApi } from '@/api/positions' import { positionsApi } from '@/api/positions'
@@ -41,7 +40,7 @@ async function loadLookups() {
async function fetchList() { async function fetchList() {
loading.value = true loading.value = true
try { try {
const res = await positionsApi.getPositionsList(filter) await positionsApi.getPositionsList(filter)
const data = await positionsApi.getPositionsList(filter) as any const data = await positionsApi.getPositionsList(filter) as any
const arr = Array.isArray(data) ? data : (data.items ?? []) const arr = Array.isArray(data) ? data : (data.items ?? [])
list.value = arr list.value = arr
@@ -88,8 +87,6 @@ const dialogLoading = ref(false)
const dialogForm = reactive<CreatePositionRequest & { id?: string }>({ const dialogForm = reactive<CreatePositionRequest & { id?: string }>({
indexVal: 0, indexVal: 0,
countryId: '',
categoryId: '',
}) })
const dialogRules = { const dialogRules = {
@@ -218,17 +215,17 @@ onMounted(async () => {
<el-table v-loading="loading" :data="list" border stripe> <el-table v-loading="loading" :data="list" border stripe>
<el-table-column label="排序值" prop="indexVal" width="100" sortable /> <el-table-column label="排序值" prop="indexVal" width="100" sortable />
<el-table-column label="国家" min-width="160"> <el-table-column label="国家" min-width="160">
<template #default="{ row }: { row: Position }"> <template #default="{ row }: any">
{{ row.country?.countryName || '-' }} {{ row.country?.countryName || '-' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="分类" min-width="200"> <el-table-column label="分类" min-width="200">
<template #default="{ row }: { row: Position }"> <template #default="{ row }: any">
{{ getCategoryName(row) }} {{ getCategoryName(row) }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }: { row: Position }"> <template #default="{ row }: any">
<div class="table-actions"> <div class="table-actions">
<el-button size="small" type="primary" plain @click="openEditDialog(row)"> <el-button size="small" type="primary" plain @click="openEditDialog(row)">
<el-icon><Edit /></el-icon> <el-icon><Edit /></el-icon>
+6 -7
View File
@@ -26,7 +26,7 @@ const filter = reactive<Required<TagFilter>>({
async function fetchList() { async function fetchList() {
loading.value = true loading.value = true
try { try {
const res = await tagsApi.getTagsList(filter) await tagsApi.getTagsList(filter)
const data = await tagsApi.getTagsList(filter) as any const data = await tagsApi.getTagsList(filter) as any
const arr = Array.isArray(data) ? data : (data.items ?? []) const arr = Array.isArray(data) ? data : (data.items ?? [])
list.value = arr list.value = arr
@@ -62,7 +62,6 @@ const dialogForm = reactive<CreateTagRequest & { id?: string; tagGroupId?: numbe
tagColor: '#ff6800', tagColor: '#ff6800',
tagFontColor: '#ffffff', tagFontColor: '#ffffff',
timing: '', timing: '',
tagGroupId: null,
}) })
const dialogRules = { const dialogRules = {
@@ -171,7 +170,7 @@ onMounted(() => {
<el-table v-loading="loading" :data="list" border stripe> <el-table v-loading="loading" :data="list" border stripe>
<el-table-column label="预览" width="120"> <el-table-column label="预览" width="120">
<template #default="{ row }: { row: Tag }"> <template #default="{ row }: any">
<span <span
v-if="row.tagColor" v-if="row.tagColor"
class="tag-preview" class="tag-preview"
@@ -182,7 +181,7 @@ onMounted(() => {
</el-table-column> </el-table-column>
<el-table-column prop="tagName" label="名称" min-width="200" /> <el-table-column prop="tagName" label="名称" min-width="200" />
<el-table-column label="背景色" width="140"> <el-table-column label="背景色" width="140">
<template #default="{ row }: { row: Tag }"> <template #default="{ row }: any">
<div class="color-cell"> <div class="color-cell">
<span class="color-swatch" :style="{ background: row.tagColor || '#d1d5db' }" /> <span class="color-swatch" :style="{ background: row.tagColor || '#d1d5db' }" />
<span class="color-hex">{{ row.tagColor || '-' }}</span> <span class="color-hex">{{ row.tagColor || '-' }}</span>
@@ -190,7 +189,7 @@ onMounted(() => {
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="字体色" width="140"> <el-table-column label="字体色" width="140">
<template #default="{ row }: { row: Tag }"> <template #default="{ row }: any">
<div class="color-cell"> <div class="color-cell">
<span class="color-swatch" :style="{ background: row.tagFontColor || '#d1d5db' }" /> <span class="color-swatch" :style="{ background: row.tagFontColor || '#d1d5db' }" />
<span class="color-hex">{{ row.tagFontColor || '-' }}</span> <span class="color-hex">{{ row.tagFontColor || '-' }}</span>
@@ -198,14 +197,14 @@ onMounted(() => {
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="所属分组" min-width="140"> <el-table-column label="所属分组" min-width="140">
<template #default="{ row }: { row: Tag }"> <template #default="{ row }: any">
<span v-if="row.tagGroup">{{ row.tagGroup.groupName }}</span> <span v-if="row.tagGroup">{{ row.tagGroup.groupName }}</span>
<span v-else style="color: #999;">未分组</span> <span v-else style="color: #999;">未分组</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="timing" label="定时" min-width="120" /> <el-table-column prop="timing" label="定时" min-width="120" />
<el-table-column label="操作" width="180" fixed="right"> <el-table-column label="操作" width="180" fixed="right">
<template #default="{ row }: { row: Tag }"> <template #default="{ row }: any">
<div class="table-actions"> <div class="table-actions">
<el-button size="small" type="primary" plain @click="openEditDialog(row)"> <el-button size="small" type="primary" plain @click="openEditDialog(row)">
<el-icon><Edit /></el-icon> <el-icon><Edit /></el-icon>
@@ -0,0 +1,17 @@
-- CreateTable
CREATE TABLE "good_origin_goods" (
"good_id" BIGINT NOT NULL,
"origin_good_id" BIGINT NOT NULL,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "good_origin_goods_pkey" PRIMARY KEY ("good_id","origin_good_id")
);
-- CreateIndex
CREATE INDEX "good_origin_goods_origin_good_id_idx" ON "good_origin_goods"("origin_good_id");
-- AddForeignKey
ALTER TABLE "good_origin_goods" ADD CONSTRAINT "good_origin_goods_good_id_fkey" FOREIGN KEY ("good_id") REFERENCES "goods"("good_id") ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE "good_origin_goods" ADD CONSTRAINT "good_origin_goods_origin_good_id_fkey" FOREIGN KEY ("origin_good_id") REFERENCES "origin_goods"("origin_good_id") ON DELETE CASCADE ON UPDATE NO ACTION;
@@ -1,3 +1,3 @@
# Please do not edit this file manually # Please do not edit this file manually
# It should be added in your version-control system (i.e. Git) # It should be added in your version-control system (i.e. Git)
provider = "postgresql" provider = "postgresql"
+17
View File
@@ -36,6 +36,7 @@ model OriginGood {
goods Good[] goods Good[]
detail OriginGoodDetail? detail OriginGoodDetail?
variants OriginGoodVariant[] variants OriginGoodVariant[]
mergedIntoGoods GoodOriginGood[]
@@index([sdsCategoryId]) @@index([sdsCategoryId])
@@index([source]) @@index([source])
@@ -215,6 +216,7 @@ model Good {
tag Tag? @relation(fields: [tagId], references: [id], onDelete: SetNull, onUpdate: NoAction) tag Tag? @relation(fields: [tagId], references: [id], onDelete: SetNull, onUpdate: NoAction)
position Position? @relation(fields: [positionId], references: [id], onDelete: SetNull, onUpdate: NoAction) position Position? @relation(fields: [positionId], references: [id], onDelete: SetNull, onUpdate: NoAction)
goodTags GoodTag[] goodTags GoodTag[]
mergedOriginGoods GoodOriginGood[]
@@index([originGoodId]) @@index([originGoodId])
@@index([countryId]) @@index([countryId])
@@ -241,6 +243,21 @@ model GoodTag {
@@map("good_tags") @@map("good_tags")
} }
// ---------- Good-OriginGood Junction (merged secondary sources, M:N) ----------
// Primary source stays on goods.origin_good_id and is NOT stored here.
model GoodOriginGood {
goodId BigInt @map("good_id")
originGoodId BigInt @map("origin_good_id")
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
good Good @relation(fields: [goodId], references: [id], onDelete: Cascade, onUpdate: NoAction)
originGood OriginGood @relation(fields: [originGoodId], references: [id], onDelete: Cascade, onUpdate: NoAction)
@@id([goodId, originGoodId])
@@index([originGoodId])
@@map("good_origin_goods")
}
// ---------- Users (admin authentication) ---------- // ---------- Users (admin authentication) ----------
enum Role { enum Role {
ADMIN ADMIN
@@ -15,6 +15,13 @@ export class BatchCreateItemDto {
@Min(1) @Min(1)
originGoodId!: number; originGoodId!: number;
@ApiProperty({ required: false, nullable: true, type: [Number], description: '副源原产品 ID,不含主源' })
@IsOptional()
@IsArray()
@IsInt({ each: true })
@Min(1, { each: true })
mergedOriginGoodIds?: number[];
@ApiProperty({ required: false }) @ApiProperty({ required: false })
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@@ -20,6 +20,13 @@ export class CreateGoodDto {
@Min(1) @Min(1)
originGoodId!: number; originGoodId!: number;
@ApiProperty({ required: false, nullable: true, type: [Number], description: '副源原产品 ID,不含主源' })
@IsOptional()
@IsArray()
@IsInt({ each: true })
@Min(1, { each: true })
mergedOriginGoodIds?: number[];
@ApiProperty() @ApiProperty()
@IsInt() @IsInt()
@Min(1) @Min(1)
+146 -80
View File
@@ -6,33 +6,58 @@ export interface GoodRelations {
category?: { id: bigint; categoryName: string; categoryIcon: string | null } | null; category?: { id: bigint; categoryName: string; categoryIcon: string | null } | null;
tag?: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } | null; tag?: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } | null;
position?: { id: bigint; indexVal: number } | null; position?: { id: bigint; indexVal: number } | null;
originGood?: { originGood?: {
id: bigint; id: bigint;
sdsGoodId: string; sdsGoodId: string;
source: 'SDS' | 'CUSTOM'; source: 'SDS' | 'CUSTOM';
goodName: string | null; goodName: string | null;
goodImage: string | null; goodImage: string | null;
goodPrice: unknown; goodPrice: unknown;
detail?: { detail?: {
productCode: string | null; productCode: string | null;
syncedAt: Date; syncedAt: Date;
sizeChart: unknown; sizeChart: unknown;
packageSpecs: unknown; packageSpecs: unknown;
[key: string]: unknown; [key: string]: unknown;
} | null; } | null;
variants?: Array<{ variants?: Array<{
sdsVariantId: string; sdsVariantId: string;
sku: string; sku: string;
sizeName: string | null; sizeName: string | null;
colorName: string | null; colorName: string | null;
colorHex: string | null; colorHex: string | null;
price: unknown; price: unknown;
enabled: boolean; enabled: boolean;
[key: string]: unknown; [key: string]: unknown;
}>; }>;
_count?: { variants: number }; _count?: { variants: number };
} | null; } | null;
goodTags?: { tag: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } }[]; goodTags?: { tag: { id: bigint; tagName: string; tagColor: string | null; tagFontColor: string | null } }[];
mergedOriginGoods?: Array<{
originGood: {
id: bigint;
sdsGoodId: string;
source: 'SDS' | 'CUSTOM';
goodName: string | null;
goodImage: string | null;
goodPrice: unknown;
detail?: { syncedAt: Date } | Record<string, unknown> | null;
variants?: Array<{ [key: string]: unknown }>;
_count?: { variants: number };
};
}>;
}
export interface MergedOriginGoodSummary {
id: string;
sdsGoodId: string;
source: 'SDS' | 'CUSTOM';
isCustom: boolean;
goodName: string | null;
goodImage: string | null;
goodPrice: string | null;
hasDetail: boolean;
variantCount: number;
} }
export class GoodDto { export class GoodDto {
@@ -81,24 +106,27 @@ export class GoodDto {
@ApiProperty({ required: false, type: Array }) @ApiProperty({ required: false, type: Array })
tags!: Array<{ id: string; tagName: string; tagColor: string | null; tagFontColor: string | null }>; tags!: Array<{ id: string; tagName: string; tagColor: string | null; tagFontColor: string | null }>;
@ApiProperty({ required: false, type: Array })
mergedOriginGoods!: MergedOriginGoodSummary[];
@ApiProperty({ required: false, nullable: true }) @ApiProperty({ required: false, nullable: true })
position?: { id: string; indexVal: number } | null; position?: { id: string; indexVal: number } | null;
@ApiProperty({ required: false, nullable: true }) @ApiProperty({ required: false, nullable: true })
originGood?: { originGood?: {
id: string; id: string;
sdsGoodId: string; sdsGoodId: string;
source: 'SDS' | 'CUSTOM'; source: 'SDS' | 'CUSTOM';
isCustom: boolean; isCustom: boolean;
goodName: string | null; goodName: string | null;
goodImage: string | null; goodImage: string | null;
goodPrice: string | null; goodPrice: string | null;
hasDetail: boolean; hasDetail: boolean;
detailSyncedAt: string | null; detailSyncedAt: string | null;
variantCount: number; variantCount: number;
sizeRowCount: number; sizeRowCount: number;
packageRowCount: number; packageRowCount: number;
productCode: string | null; productCode: string | null;
} | null; } | null;
static from( static from(
@@ -147,70 +175,108 @@ export class GoodDto {
tagFontColor: gt.tag.tagFontColor, tagFontColor: gt.tag.tagFontColor,
})) }))
: [], : [],
mergedOriginGoods: (rel.mergedOriginGoods ?? []).map((m) => ({
id: m.originGood.id.toString(),
sdsGoodId: m.originGood.sdsGoodId,
source: m.originGood.source,
isCustom: m.originGood.source === 'CUSTOM',
goodName: m.originGood.goodName,
goodImage: m.originGood.goodImage,
goodPrice:
m.originGood.goodPrice === null || m.originGood.goodPrice === undefined
? null
: (m.originGood.goodPrice as { toString(): string }).toString(),
hasDetail: Boolean(m.originGood.detail),
variantCount:
m.originGood._count?.variants ?? m.originGood.variants?.length ?? 0,
})),
position: rel.position position: rel.position
? { ? {
id: rel.position.id.toString(), id: rel.position.id.toString(),
indexVal: rel.position.indexVal, indexVal: rel.position.indexVal,
} }
: null, : null,
originGood: rel.originGood originGood: rel.originGood
? { ? {
id: rel.originGood.id.toString(), id: rel.originGood.id.toString(),
sdsGoodId: rel.originGood.sdsGoodId, sdsGoodId: rel.originGood.sdsGoodId,
source: rel.originGood.source, source: rel.originGood.source,
isCustom: rel.originGood.source === 'CUSTOM', isCustom: rel.originGood.source === 'CUSTOM',
goodName: rel.originGood.goodName, goodName: rel.originGood.goodName,
goodImage: rel.originGood.goodImage, goodImage: rel.originGood.goodImage,
goodPrice: goodPrice:
rel.originGood.goodPrice === null || rel.originGood.goodPrice === null ||
rel.originGood.goodPrice === undefined rel.originGood.goodPrice === undefined
? null ? null
: (rel.originGood.goodPrice as { toString(): string }).toString(), : (rel.originGood.goodPrice as { toString(): string }).toString(),
hasDetail: Boolean(rel.originGood.detail), hasDetail: Boolean(rel.originGood.detail),
detailSyncedAt: rel.originGood.detail?.syncedAt.toISOString() ?? null, detailSyncedAt: rel.originGood.detail?.syncedAt.toISOString() ?? null,
variantCount: rel.originGood._count?.variants ?? rel.originGood.variants?.length ?? 0, variantCount: rel.originGood._count?.variants ?? rel.originGood.variants?.length ?? 0,
sizeRowCount: GoodDto.jsonRows(rel.originGood.detail?.sizeChart), sizeRowCount: GoodDto.jsonRows(rel.originGood.detail?.sizeChart),
packageRowCount: GoodDto.jsonRows(rel.originGood.detail?.packageSpecs), packageRowCount: GoodDto.jsonRows(rel.originGood.detail?.packageSpecs),
productCode: rel.originGood.detail?.productCode ?? null, productCode: rel.originGood.detail?.productCode ?? null,
} }
: null, : null,
}; };
} }
private static jsonRows(value: unknown): number { private static jsonRows(value: unknown): number {
if (!value || typeof value !== 'object' || !('rows' in value)) return 0; if (!value || typeof value !== 'object' || !('rows' in value)) return 0;
const rows = (value as { rows?: unknown }).rows; const rows = (value as { rows?: unknown }).rows;
return Array.isArray(rows) ? rows.length : 0; return Array.isArray(rows) ? rows.length : 0;
} }
} }
export class GoodDetailDto extends GoodDto { export class GoodDetailDto extends GoodDto {
@ApiProperty({ nullable: true, type: Object }) @ApiProperty({ nullable: true, type: Object })
originDetail!: Record<string, unknown> | null; originDetail!: Record<string, unknown> | null;
@ApiProperty({ type: Array }) @ApiProperty({ type: Array })
variants!: Array<Record<string, unknown>>; variants!: Array<Record<string, unknown>>;
static fromGood(good: PrismaGood, rel: GoodRelations): GoodDetailDto { static fromGood(good: PrismaGood, rel: GoodRelations): GoodDetailDto {
const base = GoodDto.from(good, rel); const base = GoodDto.from(good, rel);
const detail = rel.originGood?.detail; const detail = rel.originGood?.detail;
return { const toAnnotated = (
...base, variant: Record<string, unknown>,
originDetail: detail ? { ...detail, syncedAt: detail.syncedAt.toISOString() } : null, originGoodId: string,
variants: (rel.originGood?.variants ?? []).map((variant) => ({ originGoodName: string | null,
...variant, ) => ({
price: ...variant,
variant.price === null || variant.price === undefined price:
? null variant.price === null || variant.price === undefined
: (variant.price as { toString(): string }).toString(), ? null
})), : (variant.price as unknown as { toString(): string }).toString(),
}; originGoodId,
} originGoodName,
} });
const primaryId = good.originGoodId.toString();
const primaryName = rel.originGood?.goodName ?? null;
const mergedVariants = [
...(rel.originGood?.variants ?? []).map((variant) =>
toAnnotated(variant as Record<string, unknown>, primaryId, primaryName),
),
...(rel.mergedOriginGoods ?? []).flatMap((m) =>
(m.originGood.variants ?? []).map((variant) =>
toAnnotated(
variant,
m.originGood.id.toString(),
m.originGood.goodName,
),
),
),
];
return {
...base,
originDetail: detail ? { ...detail, syncedAt: detail.syncedAt.toISOString() } : null,
variants: mergedVariants,
};
}
}
export interface PaginatedGoods { export interface PaginatedGoods {
items: GoodDto[]; items: GoodDto[];
total: number; total: number;
page: number; page: number;
pageSize: number; pageSize: number;
} }
@@ -19,6 +19,13 @@ export class UpdateGoodDto {
@Min(1) @Min(1)
originGoodId?: number; originGoodId?: number;
@ApiProperty({ required: false, nullable: true, type: [Number], description: '副源原产品 ID 全量覆盖,不含主源' })
@IsOptional()
@IsArray()
@IsInt({ each: true })
@Min(1, { each: true })
mergedOriginGoodIds?: number[];
@ApiProperty({ required: false }) @ApiProperty({ required: false })
@IsOptional() @IsOptional()
@IsInt() @IsInt()
+186 -57
View File
@@ -4,8 +4,8 @@ import {
NotFoundException, NotFoundException,
} from '@nestjs/common'; } from '@nestjs/common';
import { GoodsService } from './goods.service'; import { GoodsService } from './goods.service';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
import { SyncService } from '../sync/sync.service'; import { SyncService } from '../sync/sync.service';
describe('GoodsService', () => { describe('GoodsService', () => {
let service: GoodsService; let service: GoodsService;
@@ -23,14 +23,14 @@ describe('GoodsService', () => {
beforeAll(async () => { beforeAll(async () => {
const moduleRef = await Test.createTestingModule({ const moduleRef = await Test.createTestingModule({
providers: [ providers: [
GoodsService, GoodsService,
PrismaService, PrismaService,
{ {
provide: SyncService, provide: SyncService,
useValue: { queueProductDetailSync: jest.fn() }, useValue: { queueProductDetailSync: jest.fn() },
}, },
], ],
}).compile(); }).compile();
service = moduleRef.get(GoodsService); service = moduleRef.get(GoodsService);
prisma = moduleRef.get(PrismaService); prisma = moduleRef.get(PrismaService);
@@ -101,7 +101,7 @@ describe('GoodsService', () => {
expect(service).toBeDefined(); expect(service).toBeDefined();
}); });
it('creates and reads back a good', async () => { it('creates and reads back a good', async () => {
const created = await service.create({ const created = await service.create({
goodName: `Goods Test ${stamp} basic`, goodName: `Goods Test ${stamp} basic`,
originGoodId: Number(originGoodIds[0]), originGoodId: Number(originGoodIds[0]),
@@ -117,52 +117,52 @@ describe('GoodsService', () => {
const fetched = await service.findOne(BigInt(created.id)); const fetched = await service.findOne(BigInt(created.id));
expect(fetched.goodName).toBe(`Goods Test ${stamp} basic`); expect(fetched.goodName).toBe(`Goods Test ${stamp} basic`);
}); });
it('creates, edits, and removes a fully editable custom good', async () => { it('creates, edits, and removes a fully editable custom good', async () => {
const created = await service.createCustom({ const created = await service.createCustom({
goodName: `Goods Test ${stamp} custom`, goodName: `Goods Test ${stamp} custom`,
goodImage: 'https://example.com/custom.png', goodImage: 'https://example.com/custom.png',
goodPrice: '29.90', goodPrice: '29.90',
countryId: Number(countryId), countryId: Number(countryId),
categoryId: Number(categoryId), categoryId: Number(categoryId),
tagIds: [Number(tagId)], tagIds: [Number(tagId)],
detail: { detail: {
productCode: `CUSTOM-${stamp}`, productCode: `CUSTOM-${stamp}`,
materialDescription: 'Cotton', materialDescription: 'Cotton',
sizeChart: { columns: [], rows: [] }, sizeChart: { columns: [], rows: [] },
packageSpecs: { rows: [] }, packageSpecs: { rows: [] },
}, },
variants: [ variants: [
{ sku: `CUSTOM-SKU-${stamp}`, sizeName: 'S', price: '29.90' }, { sku: `CUSTOM-SKU-${stamp}`, sizeName: 'S', price: '29.90' },
], ],
}); });
expect(created.originGood?.source).toBe('CUSTOM'); expect(created.originGood?.source).toBe('CUSTOM');
expect(created.originGood?.isCustom).toBe(true); expect(created.originGood?.isCustom).toBe(true);
expect(created.originGood?.goodPrice).toBe('29.9'); expect(created.originGood?.goodPrice).toBe('29.9');
expect(created.variants).toHaveLength(1); expect(created.variants).toHaveLength(1);
const updated = await service.updateCustomContent(BigInt(created.id), { const updated = await service.updateCustomContent(BigInt(created.id), {
goodName: `Goods Test ${stamp} custom edited`, goodName: `Goods Test ${stamp} custom edited`,
goodPrice: '39.90', goodPrice: '39.90',
detail: { materialDescription: 'Organic cotton' }, detail: { materialDescription: 'Organic cotton' },
variants: [ variants: [
{ sku: `CUSTOM-SKU-${stamp}-M`, sizeName: 'M', price: '39.90' }, { sku: `CUSTOM-SKU-${stamp}-M`, sizeName: 'M', price: '39.90' },
], ],
}); });
expect(updated.goodName).toContain('custom edited'); expect(updated.goodName).toContain('custom edited');
expect(updated.originGood?.goodPrice).toBe('39.9'); expect(updated.originGood?.goodPrice).toBe('39.9');
expect(updated.originDetail?.materialDescription).toBe('Organic cotton'); expect(updated.originDetail?.materialDescription).toBe('Organic cotton');
expect(updated.originDetail?.productCode).toBe(`CUSTOM-${stamp}`); expect(updated.originDetail?.productCode).toBe(`CUSTOM-${stamp}`);
expect(updated.variants[0]?.sizeName).toBe('M'); expect(updated.variants[0]?.sizeName).toBe('M');
const customOriginId = BigInt(updated.originGoodId); const customOriginId = BigInt(updated.originGoodId);
await service.remove(BigInt(updated.id)); await service.remove(BigInt(updated.id));
await expect( await expect(
prisma.originGood.findUnique({ where: { id: customOriginId } }), prisma.originGood.findUnique({ where: { id: customOriginId } }),
).resolves.toBeNull(); ).resolves.toBeNull();
}); });
it('filters by countryId, tagId, positionId and keyword', async () => { it('filters by countryId, tagId, positionId and keyword', async () => {
const result = await service.findAll({ const result = await service.findAll({
@@ -261,6 +261,135 @@ describe('GoodsService', () => {
expect(after.total).toBe(before.total); expect(after.total).toBe(before.total);
}); });
describe('merged origin goods', () => {
it('creates a good with merged origin goods and reads them back', async () => {
const created = await service.create({
goodName: `Goods Test ${stamp} merged`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[2]), Number(originGoodIds[3])],
countryId: Number(countryId),
categoryId: Number(categoryId),
});
expect(created.mergedOriginGoods.map((m) => m.id).sort()).toEqual(
[originGoodIds[2].toString(), originGoodIds[3].toString()].sort(),
);
const fetched = await service.findOne(BigInt(created.id));
expect(fetched.mergedOriginGoods.length).toBe(2);
});
it('rejects mergedOriginGoodIds containing the primary', async () => {
await expect(
service.create({
goodName: `Goods Test ${stamp} bad-primary`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[1])],
countryId: Number(countryId),
categoryId: Number(categoryId),
}),
).rejects.toThrow(BadRequestException);
});
it('rejects mergedOriginGoodIds that do not exist', async () => {
await expect(
service.create({
goodName: `Goods Test ${stamp} bad-missing`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [999999999],
countryId: Number(countryId),
categoryId: Number(categoryId),
}),
).rejects.toThrow(BadRequestException);
});
it('replaces merged origin goods on update', async () => {
const created = await service.create({
goodName: `Goods Test ${stamp} replace`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[2])],
countryId: Number(countryId),
categoryId: Number(categoryId),
});
const updated = await service.update(BigInt(created.id), {
mergedOriginGoodIds: [Number(originGoodIds[3]), Number(originGoodIds[4])],
});
expect(updated.mergedOriginGoods.map((m) => m.id).sort()).toEqual(
[originGoodIds[3].toString(), originGoodIds[4].toString()].sort(),
);
});
it('moves old primary into merged list when switching primary', async () => {
const created = await service.create({
goodName: `Goods Test ${stamp} switch`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[2])],
countryId: Number(countryId),
categoryId: Number(categoryId),
});
const updated = await service.update(BigInt(created.id), {
originGoodId: Number(originGoodIds[2]),
mergedOriginGoodIds: [Number(originGoodIds[1]), Number(originGoodIds[3])],
});
expect(updated.originGoodId).toBe(originGoodIds[2].toString());
expect(updated.mergedOriginGoods.map((m) => m.id).sort()).toEqual(
[originGoodIds[1].toString(), originGoodIds[3].toString()].sort(),
);
});
it('cascades merged rows on good removal', async () => {
const created = await service.create({
goodName: `Goods Test ${stamp} cascade`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[2])],
countryId: Number(countryId),
categoryId: Number(categoryId),
});
await service.remove(BigInt(created.id));
const rows = await prisma.goodOriginGood.count({
where: { goodId: BigInt(created.id) },
});
expect(rows).toBe(0);
});
it('returns merged variants with source annotation in detail', async () => {
const v1 = await prisma.originGoodVariant.create({
data: {
originGoodId: originGoodIds[1],
sdsVariantId: `mv-pri-${stamp}`,
sku: `MV-PRI-${stamp}`,
colorName: '黑色',
},
});
const v2 = await prisma.originGoodVariant.create({
data: {
originGoodId: originGoodIds[2],
sdsVariantId: `mv-sec-${stamp}`,
sku: `MV-SEC-${stamp}`,
colorName: '白色',
},
});
try {
const created = await service.create({
goodName: `Goods Test ${stamp} variants`,
originGoodId: Number(originGoodIds[1]),
mergedOriginGoodIds: [Number(originGoodIds[2])],
countryId: Number(countryId),
categoryId: Number(categoryId),
});
const detail = await service.findOne(BigInt(created.id));
const sources = new Set(
detail.variants.map((v) => v['originGoodId'] as string),
);
expect(sources.has(originGoodIds[1].toString())).toBe(true);
expect(sources.has(originGoodIds[2].toString())).toBe(true);
expect(detail.variants).toHaveLength(2);
expect(detail.mergedOriginGoods.find((m) => m.id === originGoodIds[2].toString())?.variantCount).toBe(1);
} finally {
await prisma.originGoodVariant.delete({ where: { id: v1.id } });
await prisma.originGoodVariant.delete({ where: { id: v2.id } });
}
});
});
it('throws NotFoundException for unknown id', async () => { it('throws NotFoundException for unknown id', async () => {
await expect(service.findOne(BigInt(99999999))).rejects.toBeInstanceOf( await expect(service.findOne(BigInt(99999999))).rejects.toBeInstanceOf(
NotFoundException, NotFoundException,
+376 -281
View File
@@ -9,38 +9,50 @@ import { CreateGoodDto } from './dto/create-good.dto';
import { UpdateGoodDto } from './dto/update-good.dto'; import { UpdateGoodDto } from './dto/update-good.dto';
import { QueryGoodDto } from './dto/query-good.dto'; import { QueryGoodDto } from './dto/query-good.dto';
import { BatchCreateGoodDto } from './dto/batch-create-good.dto'; import { BatchCreateGoodDto } from './dto/batch-create-good.dto';
import { BatchPriorityDto } from './dto/batch-priority.dto'; import { BatchPriorityDto } from './dto/batch-priority.dto';
import { GoodDetailDto, GoodDto, PaginatedGoods } from './dto/good.dto'; import { GoodDetailDto, GoodDto, PaginatedGoods } from './dto/good.dto';
import { SyncService } from '../sync/sync.service'; import { SyncService } from '../sync/sync.service';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { import {
CreateCustomGoodDto, CreateCustomGoodDto,
CustomGoodDetailDto, CustomGoodDetailDto,
CustomGoodVariantDto, CustomGoodVariantDto,
UpdateCustomGoodContentDto, UpdateCustomGoodContentDto,
} from './dto/custom-good.dto'; } from './dto/custom-good.dto';
const GOOD_INCLUDE = { const GOOD_INCLUDE = {
country: true, country: true,
category: true, category: true,
tag: true, tag: true,
position: true, position: true,
originGood: { originGood: {
include: { include: {
detail: true, detail: true,
variants: { orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] }, variants: { orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] },
_count: { select: { variants: true } }, _count: { select: { variants: true } },
}, },
}, },
goodTags: { include: { tag: true } }, goodTags: { include: { tag: true } },
mergedOriginGoods: {
orderBy: { createdAt: 'asc' },
include: {
originGood: {
include: {
detail: true,
variants: { orderBy: [{ sortOrder: 'asc' }, { id: 'asc' }] },
_count: { select: { variants: true } },
},
},
},
},
} satisfies Prisma.GoodInclude; } satisfies Prisma.GoodInclude;
@Injectable() @Injectable()
export class GoodsService { export class GoodsService {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly syncService: SyncService, private readonly syncService: SyncService,
) {} ) {}
async findAll(query: QueryGoodDto): Promise<PaginatedGoods> { async findAll(query: QueryGoodDto): Promise<PaginatedGoods> {
const { page, pageSize, countryId, categoryId, tagId, positionId, keyword } = query; const { page, pageSize, countryId, categoryId, tagId, positionId, keyword } = query;
@@ -75,6 +87,7 @@ export class GoodsService {
position: g.position, position: g.position,
originGood: g.originGood, originGood: g.originGood,
goodTags: g.goodTags, goodTags: g.goodTags,
mergedOriginGoods: g.mergedOriginGoods,
})), })),
total, total,
page, page,
@@ -82,25 +95,31 @@ export class GoodsService {
}; };
} }
async findOne(id: bigint): Promise<GoodDetailDto> { async findOne(id: bigint): Promise<GoodDetailDto> {
const good = await this.prisma.good.findUnique({ const good = await this.prisma.good.findUnique({
where: { id }, where: { id },
include: GOOD_INCLUDE, include: GOOD_INCLUDE,
}); });
if (!good) throw new NotFoundException(`Good ${id} not found`); if (!good) throw new NotFoundException(`Good ${id} not found`);
return GoodDetailDto.fromGood(good, { return GoodDetailDto.fromGood(good, {
country: good.country, country: good.country,
category: good.category, category: good.category,
tag: good.tag, tag: good.tag,
position: good.position, position: good.position,
originGood: good.originGood, originGood: good.originGood,
goodTags: good.goodTags, goodTags: good.goodTags,
mergedOriginGoods: good.mergedOriginGoods,
}); });
} }
async create(dto: CreateGoodDto): Promise<GoodDto> { async create(dto: CreateGoodDto): Promise<GoodDto> {
await this.ensureReferences(dto); await this.ensureReferences(dto);
const result = await this.prisma.$transaction(async (tx) => { const mergedIds = this.dedupeMergedIds(
BigInt(dto.originGoodId),
dto.mergedOriginGoodIds,
);
await this.ensureMergedOriginGoods(mergedIds);
const result = await this.prisma.$transaction(async (tx) => {
const created = await tx.good.create({ const created = await tx.good.create({
data: { data: {
goodName: dto.goodName, goodName: dto.goodName,
@@ -120,132 +139,152 @@ export class GoodsService {
})), })),
}); });
} }
if (mergedIds.length > 0) {
await tx.goodOriginGood.createMany({
data: mergedIds.map((originGoodId) => ({
goodId: created.id,
originGoodId,
})),
});
}
const result = await tx.good.findUniqueOrThrow({ const result = await tx.good.findUniqueOrThrow({
where: { id: created.id }, where: { id: created.id },
include: GOOD_INCLUDE, include: GOOD_INCLUDE,
}); });
return GoodDto.from(result, { return GoodDto.from(result, {
country: result.country, country: result.country,
category: result.category, category: result.category,
tag: result.tag, tag: result.tag,
position: result.position, position: result.position,
originGood: result.originGood, originGood: result.originGood,
goodTags: result.goodTags, goodTags: result.goodTags,
}); mergedOriginGoods: result.mergedOriginGoods,
}); });
if ( });
result.originGood?.source === 'SDS' && if (
result.originGood.sdsGoodId && result.originGood?.source === 'SDS' &&
!result.originGood.hasDetail result.originGood.sdsGoodId &&
) { !result.originGood.hasDetail
this.syncService.queueProductDetailSync(result.originGood.sdsGoodId); ) {
} this.syncService.queueProductDetailSync(result.originGood.sdsGoodId);
return result; }
} return result;
}
async createCustom(dto: CreateCustomGoodDto): Promise<GoodDetailDto> {
await this.ensureCountry(dto.countryId); async createCustom(dto: CreateCustomGoodDto): Promise<GoodDetailDto> {
await this.ensureCategory(dto.categoryId); await this.ensureCountry(dto.countryId);
if (dto.positionId !== undefined) await this.ensurePosition(dto.positionId); await this.ensureCategory(dto.categoryId);
for (const tagId of dto.tagIds ?? []) await this.ensureTag(tagId); if (dto.positionId !== undefined) await this.ensurePosition(dto.positionId);
for (const tagId of dto.tagIds ?? []) await this.ensureTag(tagId);
const goodId = await this.prisma.$transaction(async (tx) => {
const originGood = await tx.originGood.create({ const goodId = await this.prisma.$transaction(async (tx) => {
data: { const originGood = await tx.originGood.create({
source: 'CUSTOM', data: {
sdsGoodId: `custom-${randomUUID()}`, source: 'CUSTOM',
goodName: dto.goodName, sdsGoodId: `custom-${randomUUID()}`,
goodImage: dto.goodImage ?? null, goodName: dto.goodName,
goodPrice: this.decimal(dto.goodPrice), goodImage: dto.goodImage ?? null,
detail: { goodPrice: this.decimal(dto.goodPrice),
create: this.customDetailData( detail: {
dto.detail ?? {}, create: this.customDetailData(
) as Prisma.OriginGoodDetailUncheckedCreateWithoutOriginGoodInput, dto.detail ?? {},
}, ) as Prisma.OriginGoodDetailUncheckedCreateWithoutOriginGoodInput,
}, },
}); },
if (dto.variants?.length) { });
await this.replaceCustomVariants(tx, originGood.id, dto.variants); if (dto.variants?.length) {
} await this.replaceCustomVariants(tx, originGood.id, dto.variants);
const good = await tx.good.create({ }
data: { const good = await tx.good.create({
originGoodId: originGood.id, data: {
countryId: BigInt(dto.countryId), originGoodId: originGood.id,
categoryId: BigInt(dto.categoryId), countryId: BigInt(dto.countryId),
positionId: categoryId: BigInt(dto.categoryId),
dto.positionId === undefined ? null : BigInt(dto.positionId), positionId:
goodName: dto.goodName, dto.positionId === undefined ? null : BigInt(dto.positionId),
goodImage: dto.goodImage ?? null, goodName: dto.goodName,
goodPriority: dto.goodPriority ?? 0, goodImage: dto.goodImage ?? null,
}, goodPriority: dto.goodPriority ?? 0,
}); },
if (dto.tagIds?.length) { });
await tx.goodTag.createMany({ if (dto.tagIds?.length) {
data: dto.tagIds.map((tagId) => ({ await tx.goodTag.createMany({
goodId: good.id, data: dto.tagIds.map((tagId) => ({
tagId: BigInt(tagId), goodId: good.id,
})), tagId: BigInt(tagId),
}); })),
} });
return good.id; }
}); return good.id;
return this.findOne(goodId); });
} return this.findOne(goodId);
}
async updateCustomContent(
id: bigint, async updateCustomContent(
dto: UpdateCustomGoodContentDto, id: bigint,
): Promise<GoodDetailDto> { dto: UpdateCustomGoodContentDto,
const existing = await this.prisma.good.findUnique({ ): Promise<GoodDetailDto> {
where: { id }, const existing = await this.prisma.good.findUnique({
include: { originGood: true }, where: { id },
}); include: { originGood: true },
if (!existing) throw new NotFoundException(`Good ${id} not found`); });
if (existing.originGood.source !== 'CUSTOM') { if (!existing) throw new NotFoundException(`Good ${id} not found`);
throw new BadRequestException('SDS 映射商品的上游信息不可修改'); if (existing.originGood.source !== 'CUSTOM') {
} throw new BadRequestException('SDS 映射商品的上游信息不可修改');
}
await this.prisma.$transaction(async (tx) => {
await tx.originGood.update({ await this.prisma.$transaction(async (tx) => {
where: { id: existing.originGoodId }, await tx.originGood.update({
data: { where: { id: existing.originGoodId },
goodName: dto.goodName, data: {
goodImage: dto.goodImage, goodName: dto.goodName,
goodPrice: goodImage: dto.goodImage,
dto.goodPrice === undefined ? undefined : this.decimal(dto.goodPrice), goodPrice:
}, dto.goodPrice === undefined ? undefined : this.decimal(dto.goodPrice),
}); },
if (dto.detail !== undefined) { });
await tx.originGoodDetail.upsert({ if (dto.detail !== undefined) {
where: { originGoodId: existing.originGoodId }, await tx.originGoodDetail.upsert({
create: { where: { originGoodId: existing.originGoodId },
originGoodId: existing.originGoodId, create: {
...(this.customDetailData( originGoodId: existing.originGoodId,
dto.detail, ...(this.customDetailData(
) as Prisma.OriginGoodDetailUncheckedCreateWithoutOriginGoodInput), dto.detail,
}, ) as Prisma.OriginGoodDetailUncheckedCreateWithoutOriginGoodInput),
update: this.customDetailData(dto.detail, true), },
}); update: this.customDetailData(dto.detail, true),
} });
if (dto.variants !== undefined) { }
await this.replaceCustomVariants( if (dto.variants !== undefined) {
tx, await this.replaceCustomVariants(
existing.originGoodId, tx,
dto.variants, existing.originGoodId,
); dto.variants,
} );
const goodData: Prisma.GoodUpdateInput = {}; }
if (dto.goodName !== undefined) goodData.goodName = dto.goodName; const goodData: Prisma.GoodUpdateInput = {};
if (dto.goodImage !== undefined) goodData.goodImage = dto.goodImage; if (dto.goodName !== undefined) goodData.goodName = dto.goodName;
if (Object.keys(goodData).length) { if (dto.goodImage !== undefined) goodData.goodImage = dto.goodImage;
await tx.good.update({ where: { id }, data: goodData }); if (Object.keys(goodData).length) {
} await tx.good.update({ where: { id }, data: goodData });
}); }
return this.findOne(id); });
} return this.findOne(id);
}
async update(id: bigint, dto: UpdateGoodDto): Promise<GoodDto> { async update(id: bigint, dto: UpdateGoodDto): Promise<GoodDto> {
await this.findOne(id); await this.findOne(id);
let mergedIds: bigint[] | undefined;
if (dto.mergedOriginGoodIds !== undefined || dto.originGoodId !== undefined) {
const current = await this.prisma.good.findUniqueOrThrow({
where: { id },
select: { originGoodId: true },
});
const primaryId =
dto.originGoodId !== undefined ? BigInt(dto.originGoodId) : current.originGoodId;
mergedIds = this.dedupeMergedIds(primaryId, dto.mergedOriginGoodIds);
await this.ensureMergedOriginGoods(mergedIds);
}
const data: Prisma.GoodUpdateInput = {}; const data: Prisma.GoodUpdateInput = {};
if (dto.goodName !== undefined) data.goodName = dto.goodName; if (dto.goodName !== undefined) data.goodName = dto.goodName;
if (dto.originGoodId !== undefined) { if (dto.originGoodId !== undefined) {
@@ -274,7 +313,7 @@ export class GoodsService {
} }
} }
const result = await this.prisma.$transaction(async (tx) => { const result = await this.prisma.$transaction(async (tx) => {
if (dto.tagIds !== undefined) { if (dto.tagIds !== undefined) {
await tx.goodTag.deleteMany({ where: { goodId: id } }); await tx.goodTag.deleteMany({ where: { goodId: id } });
if (dto.tagIds.length > 0) { if (dto.tagIds.length > 0) {
@@ -286,47 +325,59 @@ export class GoodsService {
}); });
} }
} }
if (mergedIds !== undefined) {
await tx.goodOriginGood.deleteMany({ where: { goodId: id } });
if (mergedIds.length > 0) {
await tx.goodOriginGood.createMany({
data: mergedIds.map((originGoodId) => ({
goodId: id,
originGoodId,
})),
});
}
}
const updated = await tx.good.update({ const updated = await tx.good.update({
where: { id }, where: { id },
data, data,
include: GOOD_INCLUDE, include: GOOD_INCLUDE,
}); });
return GoodDto.from(updated, { return GoodDto.from(updated, {
country: updated.country, country: updated.country,
category: updated.category, category: updated.category,
tag: updated.tag, tag: updated.tag,
position: updated.position, position: updated.position,
originGood: updated.originGood, originGood: updated.originGood,
goodTags: updated.goodTags, goodTags: updated.goodTags,
}); mergedOriginGoods: updated.mergedOriginGoods,
}); });
if ( });
result.originGood?.source === 'SDS' && if (
result.originGood.sdsGoodId && result.originGood?.source === 'SDS' &&
!result.originGood.hasDetail result.originGood.sdsGoodId &&
) { !result.originGood.hasDetail
this.syncService.queueProductDetailSync(result.originGood.sdsGoodId); ) {
} this.syncService.queueProductDetailSync(result.originGood.sdsGoodId);
return result; }
return result;
} }
async remove(id: bigint): Promise<{ id: string }> { async remove(id: bigint): Promise<{ id: string }> {
const good = await this.prisma.good.findUnique({ const good = await this.prisma.good.findUnique({
where: { id }, where: { id },
include: { originGood: true }, include: { originGood: true },
}); });
if (!good) throw new NotFoundException(`Good ${id} not found`); if (!good) throw new NotFoundException(`Good ${id} not found`);
await this.prisma.$transaction(async (tx) => { await this.prisma.$transaction(async (tx) => {
await tx.good.delete({ where: { id } }); await tx.good.delete({ where: { id } });
if (good.originGood.source === 'CUSTOM') { if (good.originGood.source === 'CUSTOM') {
const remaining = await tx.good.count({ const remaining = await tx.good.count({
where: { originGoodId: good.originGoodId }, where: { originGoodId: good.originGoodId },
}); });
if (remaining === 0) { if (remaining === 0) {
await tx.originGood.delete({ where: { id: good.originGoodId } }); await tx.originGood.delete({ where: { id: good.originGoodId } });
} }
} }
}); });
return { id: id.toString() }; return { id: id.toString() };
} }
@@ -335,16 +386,16 @@ export class GoodsService {
* or none do. * or none do.
*/ */
async batchUpdatePriority(dto: BatchPriorityDto): Promise<{ count: number }> { async batchUpdatePriority(dto: BatchPriorityDto): Promise<{ count: number }> {
const result = await this.prisma.$transaction(async (tx) => { const result = await this.prisma.$transaction(async (tx) => {
for (const item of dto.items) { for (const item of dto.items) {
await tx.good.update({ await tx.good.update({
where: { id: BigInt(item.id) }, where: { id: BigInt(item.id) },
data: { goodPriority: item.priority }, data: { goodPriority: item.priority },
}); });
} }
return { count: dto.items.length }; return { count: dto.items.length };
}); });
return result; return result;
} }
/** /**
@@ -358,7 +409,7 @@ export class GoodsService {
await this.ensureTag(tagId); await this.ensureTag(tagId);
} }
} }
const result = await this.prisma.$transaction(async (tx) => { const result = await this.prisma.$transaction(async (tx) => {
const created: GoodDto[] = []; const created: GoodDto[] = [];
for (const item of dto.items) { for (const item of dto.items) {
const og = await tx.originGood.findUnique({ const og = await tx.originGood.findUnique({
@@ -388,6 +439,24 @@ export class GoodsService {
})), })),
}); });
} }
const itemMerged = this.dedupeMergedIds(og.id, item.mergedOriginGoodIds);
if (itemMerged.length > 0) {
const existRows = await tx.originGood.findMany({
where: { id: { in: itemMerged } },
select: { id: true },
});
if (existRows.length !== itemMerged.length) {
const found = new Set(existRows.map((r) => r.id.toString()));
const missing = itemMerged.find((mid) => !found.has(mid.toString()));
throw new BadRequestException(`Origin good ${missing} not found`);
}
await tx.goodOriginGood.createMany({
data: itemMerged.map((originGoodId) => ({
goodId: row.id,
originGoodId,
})),
});
}
const result = await tx.good.findUniqueOrThrow({ const result = await tx.good.findUniqueOrThrow({
where: { id: row.id }, where: { id: row.id },
include: GOOD_INCLUDE, include: GOOD_INCLUDE,
@@ -399,23 +468,24 @@ export class GoodsService {
position: result.position, position: result.position,
originGood: result.originGood, originGood: result.originGood,
goodTags: result.goodTags, goodTags: result.goodTags,
mergedOriginGoods: result.mergedOriginGoods,
})); }));
} }
return created; return created;
}); });
for (const goodId of new Set( for (const goodId of new Set(
result result
.filter( .filter(
(item) => (item) =>
item.originGood?.source === 'SDS' && item.originGood?.source === 'SDS' &&
!item.originGood.hasDetail, !item.originGood.hasDetail,
) )
.map((item) => item.originGood?.sdsGoodId) .map((item) => item.originGood?.sdsGoodId)
.filter((id): id is string => Boolean(id)), .filter((id): id is string => Boolean(id)),
)) { )) {
this.syncService.queueProductDetailSync(goodId); this.syncService.queueProductDetailSync(goodId);
} }
return result; return result;
} }
/** /**
@@ -446,6 +516,31 @@ export class GoodsService {
if (!og) throw new BadRequestException(`Origin good ${id} not found`); if (!og) throw new BadRequestException(`Origin good ${id} not found`);
} }
/** Dedupe merged ids and reject any that equals the primary source. */
private dedupeMergedIds(primaryId: bigint, ids?: number[]): bigint[] {
if (!ids || ids.length === 0) return [];
const unique = [...new Set(ids.map((id) => BigInt(id)))];
if (unique.includes(primaryId)) {
throw new BadRequestException(
'mergedOriginGoodIds 不能包含主源 originGoodId',
);
}
return unique;
}
private async ensureMergedOriginGoods(ids: bigint[]) {
if (ids.length === 0) return;
const rows = await this.prisma.originGood.findMany({
where: { id: { in: ids } },
select: { id: true },
});
if (rows.length !== ids.length) {
const found = new Set(rows.map((r) => r.id.toString()));
const missing = ids.find((id) => !found.has(id.toString()));
throw new BadRequestException(`Origin good ${missing} not found`);
}
}
private async ensureCountry(id: number) { private async ensureCountry(id: number) {
const c = await this.prisma.country.findUnique({ where: { id: BigInt(id) } }); const c = await this.prisma.country.findUnique({ where: { id: BigInt(id) } });
if (!c) throw new BadRequestException(`Country ${id} not found`); if (!c) throw new BadRequestException(`Country ${id} not found`);
@@ -456,12 +551,12 @@ export class GoodsService {
if (!c) throw new BadRequestException(`Category ${id} not found`); if (!c) throw new BadRequestException(`Category ${id} not found`);
} }
private async ensureTag(id: number) { private async ensureTag(id: number) {
const t = await this.prisma.tag.findUnique({ where: { id: BigInt(id) } }); const t = await this.prisma.tag.findUnique({ where: { id: BigInt(id) } });
if (!t) throw new BadRequestException(`Tag ${id} not found`); if (!t) throw new BadRequestException(`Tag ${id} not found`);
} }
private async ensureReferences(dto: CreateGoodDto) { private async ensureReferences(dto: CreateGoodDto) {
await this.ensureOriginGood(dto.originGoodId); await this.ensureOriginGood(dto.originGoodId);
await this.ensureCountry(dto.countryId); await this.ensureCountry(dto.countryId);
await this.ensureCategory(dto.categoryId); await this.ensureCategory(dto.categoryId);
@@ -474,94 +569,94 @@ export class GoodsService {
const p = await this.prisma.position.findUnique({ where: { id: BigInt(dto.positionId) } }); const p = await this.prisma.position.findUnique({ where: { id: BigInt(dto.positionId) } });
if (!p) throw new BadRequestException(`Position ${dto.positionId} not found`); if (!p) throw new BadRequestException(`Position ${dto.positionId} not found`);
} }
} }
private async ensurePosition(id: number) { private async ensurePosition(id: number) {
const position = await this.prisma.position.findUnique({ const position = await this.prisma.position.findUnique({
where: { id: BigInt(id) }, where: { id: BigInt(id) },
}); });
if (!position) throw new BadRequestException(`Position ${id} not found`); if (!position) throw new BadRequestException(`Position ${id} not found`);
} }
private decimal(value: string | null | undefined): Prisma.Decimal | null { private decimal(value: string | null | undefined): Prisma.Decimal | null {
return value === undefined || value === null || value === '' return value === undefined || value === null || value === ''
? null ? null
: new Prisma.Decimal(value); : new Prisma.Decimal(value);
} }
private customDetailData( private customDetailData(
detail: CustomGoodDetailDto, detail: CustomGoodDetailDto,
preserveMissing = false, preserveMissing = false,
): Prisma.OriginGoodDetailUncheckedUpdateInput { ): Prisma.OriginGoodDetailUncheckedUpdateInput {
const nullable = <T>(value: T | null | undefined): T | null | undefined => const nullable = <T>(value: T | null | undefined): T | null | undefined =>
preserveMissing && value === undefined ? undefined : value ?? null; preserveMissing && value === undefined ? undefined : value ?? null;
const decimal = (value: string | null | undefined) => const decimal = (value: string | null | undefined) =>
preserveMissing && value === undefined ? undefined : this.decimal(value); preserveMissing && value === undefined ? undefined : this.decimal(value);
const json = ( const json = (
value: Record<string, unknown> | null | undefined, value: Record<string, unknown> | null | undefined,
): Prisma.InputJsonValue | Prisma.NullTypes.DbNull | undefined => ): Prisma.InputJsonValue | Prisma.NullTypes.DbNull | undefined =>
preserveMissing && value === undefined preserveMissing && value === undefined
? undefined ? undefined
: value === null || value === undefined : value === null || value === undefined
? Prisma.DbNull ? Prisma.DbNull
: (value as Prisma.InputJsonValue); : (value as Prisma.InputJsonValue);
return { return {
productCode: nullable(detail.productCode), productCode: nullable(detail.productCode),
englishName: nullable(detail.englishName), englishName: nullable(detail.englishName),
blankDesignUrl: nullable(detail.blankDesignUrl), blankDesignUrl: nullable(detail.blankDesignUrl),
detailsPageVideoUrl: nullable(detail.detailsPageVideoUrl), detailsPageVideoUrl: nullable(detail.detailsPageVideoUrl),
textureName: nullable(detail.textureName), textureName: nullable(detail.textureName),
productionCycleHours: nullable(detail.productionCycleHours), productionCycleHours: nullable(detail.productionCycleHours),
minWeightG: decimal(detail.minWeightG), minWeightG: decimal(detail.minWeightG),
reminder: nullable(detail.reminder), reminder: nullable(detail.reminder),
productionProcess: nullable(detail.productionProcess), productionProcess: nullable(detail.productionProcess),
materialDescription: nullable(detail.materialDescription), materialDescription: nullable(detail.materialDescription),
productPerformance: nullable(detail.productPerformance), productPerformance: nullable(detail.productPerformance),
applicableScenarios: nullable(detail.applicableScenarios), applicableScenarios: nullable(detail.applicableScenarios),
washingInstructions: nullable(detail.washingInstructions), washingInstructions: nullable(detail.washingInstructions),
specialDescription: nullable(detail.specialDescription), specialDescription: nullable(detail.specialDescription),
designExplanation: nullable(detail.designExplanation), designExplanation: nullable(detail.designExplanation),
designArea: nullable(detail.designArea), designArea: nullable(detail.designArea),
pictureRequest: nullable(detail.pictureRequest), pictureRequest: nullable(detail.pictureRequest),
sizeChart: json(detail.sizeChart), sizeChart: json(detail.sizeChart),
packageSpecs: json(detail.packageSpecs), packageSpecs: json(detail.packageSpecs),
options: json(detail.options), options: json(detail.options),
media: json(detail.media), media: json(detail.media),
}; };
} }
private async replaceCustomVariants( private async replaceCustomVariants(
tx: Prisma.TransactionClient, tx: Prisma.TransactionClient,
originGoodId: bigint, originGoodId: bigint,
variants: CustomGoodVariantDto[], variants: CustomGoodVariantDto[],
): Promise<void> { ): Promise<void> {
await tx.originGoodVariant.deleteMany({ where: { originGoodId } }); await tx.originGoodVariant.deleteMany({ where: { originGoodId } });
for (const variant of variants) { for (const variant of variants) {
await tx.originGoodVariant.create({ await tx.originGoodVariant.create({
data: { data: {
originGoodId, originGoodId,
sdsVariantId: `custom-${randomUUID()}`, sdsVariantId: `custom-${randomUUID()}`,
sku: variant.sku, sku: variant.sku,
sizeId: variant.sizeId ?? null, sizeId: variant.sizeId ?? null,
sizeName: variant.sizeName ?? null, sizeName: variant.sizeName ?? null,
colorId: variant.colorId ?? null, colorId: variant.colorId ?? null,
colorName: variant.colorName ?? null, colorName: variant.colorName ?? null,
colorHex: variant.colorHex ?? null, colorHex: variant.colorHex ?? null,
imageUrl: variant.imageUrl ?? null, imageUrl: variant.imageUrl ?? null,
price: this.decimal(variant.price), price: this.decimal(variant.price),
originalPrice: this.decimal(variant.originalPrice), originalPrice: this.decimal(variant.originalPrice),
weightG: this.decimal(variant.weightG), weightG: this.decimal(variant.weightG),
boxLengthCm: this.decimal(variant.boxLengthCm), boxLengthCm: this.decimal(variant.boxLengthCm),
boxWidthCm: this.decimal(variant.boxWidthCm), boxWidthCm: this.decimal(variant.boxWidthCm),
boxHeightCm: this.decimal(variant.boxHeightCm), boxHeightCm: this.decimal(variant.boxHeightCm),
enabled: variant.enabled ?? true, enabled: variant.enabled ?? true,
sortOrder: variant.sortOrder ?? 0, sortOrder: variant.sortOrder ?? 0,
designData: designData:
variant.designData === null || variant.designData === undefined variant.designData === null || variant.designData === undefined
? Prisma.DbNull ? Prisma.DbNull
: (variant.designData as Prisma.InputJsonValue), : (variant.designData as Prisma.InputJsonValue),
}, },
}); });
} }
} }
} }
@@ -77,4 +77,69 @@ describe('OriginGoodsService', () => {
expect(result.total).toBe(0); expect(result.total).toBe(0);
expect(result.items.length).toBe(0); expect(result.items.length).toBe(0);
}); });
describe('getTree merged references', () => {
/* eslint-disable @typescript-eslint/no-explicit-any */
function findOgNode(
treeResponse: { tree: any[] },
ogId: string,
): { configuredCount: number; configuredCountries: string[] } {
let found: { configuredCount: number; configuredCountries: string[] } | null = null;
const walk = (nodes: any[]) => {
for (const n of nodes) {
const hit = (n.originGoods ?? []).find((o: any) => o.id === ogId);
if (hit) {
found = hit;
return;
}
if (n.children?.length) walk(n.children);
}
};
walk(treeResponse.tree);
if (!found) throw new Error(`og node ${ogId} not found in tree`);
return found;
}
it('counts secondary references as configured', async () => {
const sdsCat = `tree-cat-${stamp}`;
await prisma.originGood.createMany({
data: [
{ sdsGoodId: `tree-a-${stamp}`, goodName: `Tree A ${stamp}`, sdsCategoryId: sdsCat },
{ sdsGoodId: `tree-b-${stamp}`, goodName: `Tree B ${stamp}`, sdsCategoryId: sdsCat },
],
});
createdSds.push(`tree-a-${stamp}`, `tree-b-${stamp}`);
const originA = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-a-${stamp}` } });
const originB = await prisma.originGood.findUniqueOrThrow({ where: { sdsGoodId: `tree-b-${stamp}` } });
const cat = await prisma.category.create({
data: { categoryName: `Tree Cat ${stamp}`, sdsCategoryId: sdsCat },
});
const country = await prisma.country.create({
data: { countryName: `Tree Country ${stamp}` },
});
const good = await prisma.good.create({
data: {
goodName: `Tree Good ${stamp}`,
originGoodId: originA.id,
countryId: country.id,
categoryId: cat.id,
},
});
await prisma.goodOriginGood.create({
data: { goodId: good.id, originGoodId: originB.id },
});
try {
const tree = await service.getTree();
const nodeB = findOgNode(tree, originB.id.toString());
expect(nodeB.configuredCount).toBeGreaterThanOrEqual(1);
expect(nodeB.configuredCountries).toContain(`Tree Country ${stamp}`);
} finally {
await prisma.good.delete({ where: { id: good.id } }).catch(() => undefined);
await prisma.country.delete({ where: { id: country.id } }).catch(() => undefined);
await prisma.category.delete({ where: { id: cat.id } }).catch(() => undefined);
}
});
});
}); });
+103 -68
View File
@@ -12,10 +12,10 @@ export interface PaginatedOriginGoods {
goodPrice: string | null; goodPrice: string | null;
sdsCategoryId: string | null; sdsCategoryId: string | null;
createdAt: string; createdAt: string;
updatedAt: string; updatedAt: string;
hasDetail: boolean; hasDetail: boolean;
detailSyncedAt: string | null; detailSyncedAt: string | null;
variantCount: number; variantCount: number;
}>; }>;
total: number; total: number;
page: number; page: number;
@@ -35,12 +35,12 @@ export interface OriginGoodsTreeNode {
delisted: boolean; delisted: boolean;
configuredCount: number; configuredCount: number;
configuredCountries: string[]; configuredCountries: string[];
configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]; configuredTags: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[];
hasDetail: boolean; hasDetail: boolean;
detailSyncedAt: string | null; detailSyncedAt: string | null;
variantCount: number; variantCount: number;
sizeRowCount: number; sizeRowCount: number;
packageRowCount: number; packageRowCount: number;
} }
/** A category node in the hierarchical tree, with origin goods as leaves. */ /** A category node in the hierarchical tree, with origin goods as leaves. */
@@ -67,19 +67,19 @@ export class OriginGoodsService {
async findAll(query: QueryOriginGoodDto): Promise<PaginatedOriginGoods> { async findAll(query: QueryOriginGoodDto): Promise<PaginatedOriginGoods> {
const { page, pageSize, keyword } = query; const { page, pageSize, keyword } = query;
const where: Prisma.OriginGoodWhereInput = { const where: Prisma.OriginGoodWhereInput = {
source: 'SDS', source: 'SDS',
...(keyword ...(keyword
? { goodName: { contains: keyword, mode: 'insensitive' as const } } ? { goodName: { contains: keyword, mode: 'insensitive' as const } }
: {}), : {}),
}; };
const [total, rows] = await this.prisma.$transaction([ const [total, rows] = await this.prisma.$transaction([
this.prisma.originGood.count({ where }), this.prisma.originGood.count({ where }),
this.prisma.originGood.findMany({ this.prisma.originGood.findMany({
where, where,
orderBy: { id: 'desc' }, orderBy: { id: 'desc' },
include: { detail: true, _count: { select: { variants: true } } }, include: { detail: true, _count: { select: { variants: true } } },
skip: (page - 1) * pageSize, skip: (page - 1) * pageSize,
take: pageSize, take: pageSize,
}), }),
@@ -94,10 +94,10 @@ export class OriginGoodsService {
goodPrice: r.goodPrice === null || r.goodPrice === undefined ? null : r.goodPrice.toString(), goodPrice: r.goodPrice === null || r.goodPrice === undefined ? null : r.goodPrice.toString(),
sdsCategoryId: r.sdsCategoryId, sdsCategoryId: r.sdsCategoryId,
createdAt: r.createdAt.toISOString(), createdAt: r.createdAt.toISOString(),
updatedAt: r.updatedAt.toISOString(), updatedAt: r.updatedAt.toISOString(),
hasDetail: Boolean(r.detail), hasDetail: Boolean(r.detail),
detailSyncedAt: r.detail?.syncedAt.toISOString() ?? null, detailSyncedAt: r.detail?.syncedAt.toISOString() ?? null,
variantCount: r._count.variants, variantCount: r._count.variants,
})), })),
total, total,
page, page,
@@ -114,7 +114,7 @@ export class OriginGoodsService {
* under a synthetic "未分类" root node. * under a synthetic "未分类" root node.
*/ */
async getTree(): Promise<OriginGoodsTreeResponse> { async getTree(): Promise<OriginGoodsTreeResponse> {
const [allCategories, allOriginGoods, configCounts, goodsWithCountries, goodsWithTags] = const [allCategories, allOriginGoods, configCounts, goodsWithCountries, goodsWithTags, mergedCounts, mergedWithCountries] =
await Promise.all([ await Promise.all([
this.prisma.category.findMany({ this.prisma.category.findMany({
where: { sdsCategoryId: { not: null } }, where: { sdsCategoryId: { not: null } },
@@ -126,11 +126,11 @@ export class OriginGoodsService {
parentCategoryId: true, parentCategoryId: true,
}, },
}), }),
this.prisma.originGood.findMany({ this.prisma.originGood.findMany({
where: { delisted: false, source: 'SDS' }, where: { delisted: false, source: 'SDS' },
orderBy: { goodName: 'asc' }, orderBy: { goodName: 'asc' },
include: { detail: true, _count: { select: { variants: true } } }, include: { detail: true, _count: { select: { variants: true } } },
}), }),
this.prisma.good.groupBy({ this.prisma.good.groupBy({
by: ['originGoodId'], by: ['originGoodId'],
_count: { _all: true }, _count: { _all: true },
@@ -144,7 +144,12 @@ export class OriginGoodsService {
}), }),
this.prisma.goodTag.findMany({ this.prisma.goodTag.findMany({
select: { select: {
good: { select: { originGoodId: true } }, good: {
select: {
originGoodId: true,
mergedOriginGoods: { select: { originGoodId: true } },
},
},
tag: { tag: {
select: { select: {
tagName: true, tagName: true,
@@ -157,26 +162,57 @@ export class OriginGoodsService {
}, },
}, },
}), }),
// Secondary-source references (good_origin_goods)
this.prisma.goodOriginGood.groupBy({
by: ['originGoodId'],
_count: { _all: true },
}),
this.prisma.goodOriginGood.findMany({
select: {
originGoodId: true,
good: { select: { country: { select: { countryName: true } } } },
},
}),
]); ]);
const countMap = new Map<string, number>(); const countMap = new Map<string, number>();
configCounts.forEach((c) => configCounts.forEach((c) =>
countMap.set(c.originGoodId.toString(), c._count._all), countMap.set(c.originGoodId.toString(), c._count._all),
); );
// Secondary (merged) references count towards configured status too.
const countryMap = new Map<string, string[]>(); mergedCounts.forEach((c) => {
goodsWithCountries.forEach((g) => { const key = c.originGoodId.toString();
const key = g.originGoodId.toString(); countMap.set(key, (countMap.get(key) ?? 0) + c._count._all);
const name = g.country?.countryName;
if (!name) return;
const arr = countryMap.get(key);
if (arr) arr.push(name);
else countryMap.set(key, [name]);
}); });
const countryMap = new Map<string, string[]>();
const addCountry = (key: string, name?: string | null) => {
if (!name) return;
const arr = countryMap.get(key);
if (!arr?.includes(name)) {
countryMap.set(key, [...(arr ?? []), name]);
}
};
goodsWithCountries.forEach((g) =>
addCountry(g.originGoodId.toString(), g.country?.countryName),
);
mergedWithCountries.forEach((m) =>
addCountry(m.originGoodId.toString(), m.good.country?.countryName),
);
const tagMap = new Map<string, { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]>(); const tagMap = new Map<string, { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number }[]>();
const addTag = (
key: string,
tagInfo: { tagName: string; tagColor: string | null; tagFontColor: string | null; tagGroupId: string | null; tagGroupName: string | null; sortOrder: number },
) => {
const arr = tagMap.get(key);
if (arr) {
if (!arr.some((t) => t.tagName === tagInfo.tagName)) arr.push(tagInfo);
} else {
tagMap.set(key, [tagInfo]);
}
};
goodsWithTags.forEach((gt) => { goodsWithTags.forEach((gt) => {
const key = gt.good.originGoodId.toString();
const tagInfo = { const tagInfo = {
tagName: gt.tag.tagName, tagName: gt.tag.tagName,
tagColor: gt.tag.tagColor, tagColor: gt.tag.tagColor,
@@ -185,11 +221,10 @@ export class OriginGoodsService {
tagGroupName: gt.tag.tagGroup?.groupName ?? null, tagGroupName: gt.tag.tagGroup?.groupName ?? null,
sortOrder: gt.tag.sortOrder, sortOrder: gt.tag.sortOrder,
}; };
const arr = tagMap.get(key); addTag(gt.good.originGoodId.toString(), tagInfo);
if (arr) { // A good's tags also mark its secondary origin goods as configured.
if (!arr.some((t) => t.tagName === tagInfo.tagName)) arr.push(tagInfo); for (const m of gt.good.mergedOriginGoods) {
} else { addTag(m.originGoodId.toString(), tagInfo);
tagMap.set(key, [tagInfo]);
} }
}); });
@@ -230,12 +265,12 @@ export class OriginGoodsService {
delisted: og.delisted, delisted: og.delisted,
configuredCount: countMap.get(og.id.toString()) ?? 0, configuredCount: countMap.get(og.id.toString()) ?? 0,
configuredCountries: countryMap.get(og.id.toString()) ?? [], configuredCountries: countryMap.get(og.id.toString()) ?? [],
configuredTags: tagMap.get(og.id.toString()) ?? [], configuredTags: tagMap.get(og.id.toString()) ?? [],
hasDetail: Boolean(og.detail), hasDetail: Boolean(og.detail),
detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null, detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null,
variantCount: og._count.variants, variantCount: og._count.variants,
sizeRowCount: this.jsonRows(og.detail?.sizeChart), sizeRowCount: this.jsonRows(og.detail?.sizeChart),
packageRowCount: this.jsonRows(og.detail?.packageSpecs), packageRowCount: this.jsonRows(og.detail?.packageSpecs),
})); }));
const childTotal = childNodes.reduce((s, n) => s + n.totalCount, 0); const childTotal = childNodes.reduce((s, n) => s + n.totalCount, 0);
@@ -281,12 +316,12 @@ export class OriginGoodsService {
delisted: og.delisted, delisted: og.delisted,
configuredCount: countMap.get(og.id.toString()) ?? 0, configuredCount: countMap.get(og.id.toString()) ?? 0,
configuredCountries: countryMap.get(og.id.toString()) ?? [], configuredCountries: countryMap.get(og.id.toString()) ?? [],
configuredTags: tagMap.get(og.id.toString()) ?? [], configuredTags: tagMap.get(og.id.toString()) ?? [],
hasDetail: Boolean(og.detail), hasDetail: Boolean(og.detail),
detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null, detailSyncedAt: og.detail?.syncedAt.toISOString() ?? null,
variantCount: og._count.variants, variantCount: og._count.variants,
sizeRowCount: this.jsonRows(og.detail?.sizeChart), sizeRowCount: this.jsonRows(og.detail?.sizeChart),
packageRowCount: this.jsonRows(og.detail?.packageSpecs), packageRowCount: this.jsonRows(og.detail?.packageSpecs),
})), })),
}); });
} }
@@ -297,16 +332,16 @@ export class OriginGoodsService {
(og) => (countMap.get(og.id.toString()) ?? 0) > 0, (og) => (countMap.get(og.id.toString()) ?? 0) > 0,
).length; ).length;
return { return {
tree, tree,
totalOriginGoods: allOriginGoods.length, totalOriginGoods: allOriginGoods.length,
configuredCount: totalConfigured, configuredCount: totalConfigured,
}; };
} }
private jsonRows(value: unknown): number { private jsonRows(value: unknown): number {
if (!value || typeof value !== 'object' || !('rows' in value)) return 0; if (!value || typeof value !== 'object' || !('rows' in value)) return 0;
const rows = (value as { rows?: unknown }).rows; const rows = (value as { rows?: unknown }).rows;
return Array.isArray(rows) ? rows.length : 0; return Array.isArray(rows) ? rows.length : 0;
} }
} }
+189 -154
View File
@@ -1,5 +1,5 @@
import { Test } from '@nestjs/testing'; import { Test } from '@nestjs/testing';
import { BadRequestException, NotFoundException } from '@nestjs/common'; import { BadRequestException, NotFoundException } from '@nestjs/common';
import { PublicService } from './public.service'; import { PublicService } from './public.service';
import { PrismaService } from '../prisma/prisma.service'; import { PrismaService } from '../prisma/prisma.service';
@@ -11,9 +11,9 @@ describe('PublicService', () => {
let categoryId: bigint; let categoryId: bigint;
let childCategoryId: bigint; let childCategoryId: bigint;
let otherCategoryId: bigint; let otherCategoryId: bigint;
let tagId: bigint; let tagId: bigint;
let filterGroupIds: bigint[] = []; let filterGroupIds: bigint[] = [];
let filterTagIds: bigint[] = []; let filterTagIds: bigint[] = [];
let originGoodId: bigint; let originGoodId: bigint;
let goodIds: bigint[] = []; let goodIds: bigint[] = [];
@@ -98,51 +98,51 @@ describe('PublicService', () => {
goodPriority: 1, goodPriority: 1,
}, },
}); });
goodIds = [g1.id, g2.id, g3.id]; goodIds = [g1.id, g2.id, g3.id];
const craftGroup = await prisma.tagGroup.create({ const craftGroup = await prisma.tagGroup.create({
data: { groupName: `Pub Craft ${stamp}`, sortOrder: 100 }, data: { groupName: `Pub Craft ${stamp}`, sortOrder: 100 },
}); });
const materialGroup = await prisma.tagGroup.create({ const materialGroup = await prisma.tagGroup.create({
data: { groupName: `Pub Material ${stamp}`, sortOrder: 101 }, data: { groupName: `Pub Material ${stamp}`, sortOrder: 101 },
}); });
filterGroupIds = [craftGroup.id, materialGroup.id]; filterGroupIds = [craftGroup.id, materialGroup.id];
const craftA = await prisma.tag.create({ const craftA = await prisma.tag.create({
data: { tagName: `Pub Craft A ${stamp}`, tagGroupId: craftGroup.id }, data: { tagName: `Pub Craft A ${stamp}`, tagGroupId: craftGroup.id },
}); });
const craftB = await prisma.tag.create({ const craftB = await prisma.tag.create({
data: { tagName: `Pub Craft B ${stamp}`, tagGroupId: craftGroup.id }, data: { tagName: `Pub Craft B ${stamp}`, tagGroupId: craftGroup.id },
}); });
const cotton = await prisma.tag.create({ const cotton = await prisma.tag.create({
data: { tagName: `Pub Cotton ${stamp}`, tagGroupId: materialGroup.id }, data: { tagName: `Pub Cotton ${stamp}`, tagGroupId: materialGroup.id },
}); });
filterTagIds = [craftA.id, craftB.id, cotton.id]; filterTagIds = [craftA.id, craftB.id, cotton.id];
await prisma.goodTag.createMany({ await prisma.goodTag.createMany({
data: [ data: [
{ goodId: g1.id, tagId: craftA.id }, { goodId: g1.id, tagId: craftA.id },
{ goodId: g1.id, tagId: cotton.id }, { goodId: g1.id, tagId: cotton.id },
{ goodId: g2.id, tagId: craftB.id }, { goodId: g2.id, tagId: craftB.id },
], ],
}); });
await prisma.originGoodDetail.create({ await prisma.originGoodDetail.create({
data: { data: {
originGoodId, originGoodId,
productCode: 'OZ10827003', productCode: 'OZ10827003',
productionProcess: '白墨烫画', productionProcess: '白墨烫画',
sizeChart: { columns: [], rows: [{ sizeId: 'size_0', sizeName: 'S', measurements: [] }] }, sizeChart: { columns: [], rows: [{ sizeId: 'size_0', sizeName: 'S', measurements: [] }] },
packageSpecs: { rows: [{ sizeId: 'size_0', sizeName: 'S' }] }, packageSpecs: { rows: [{ sizeId: 'size_0', sizeName: 'S' }] },
}, },
}); });
await prisma.originGoodVariant.create({ await prisma.originGoodVariant.create({
data: { data: {
originGoodId, originGoodId,
sdsVariantId: `pub-variant-${stamp}`, sdsVariantId: `pub-variant-${stamp}`,
sku: `OZ${stamp}`, sku: `OZ${stamp}`,
sizeName: 'S', sizeName: 'S',
price: 38, price: 38,
}, },
}); });
// Seed a good in `otherCategory` so the "onlyHaveGoods" filter // Seed a good in `otherCategory` so the "onlyHaveGoods" filter
// returns more than one category. // returns more than one category.
@@ -179,9 +179,9 @@ describe('PublicService', () => {
await prisma.position.deleteMany({ await prisma.position.deleteMany({
where: { countryId }, where: { countryId },
}); });
await prisma.tag.delete({ where: { id: tagId } }); await prisma.tag.delete({ where: { id: tagId } });
await prisma.tag.deleteMany({ where: { id: { in: filterTagIds } } }); await prisma.tag.deleteMany({ where: { id: { in: filterTagIds } } });
await prisma.tagGroup.deleteMany({ where: { id: { in: filterGroupIds } } }); await prisma.tagGroup.deleteMany({ where: { id: { in: filterGroupIds } } });
await prisma.originGood.delete({ where: { id: originGoodId } }); await prisma.originGood.delete({ where: { id: originGoodId } });
// Delete children before parent (FK self-relation is RESTRICT). // Delete children before parent (FK self-relation is RESTRICT).
await prisma.category.delete({ where: { id: childCategoryId } }); await prisma.category.delete({ where: { id: childCategoryId } });
@@ -220,139 +220,139 @@ describe('PublicService', () => {
const filtered = await service.getGoods({ const filtered = await service.getGoods({
page: 1, page: 1,
pageSize: 50, pageSize: 50,
countryId: countryId.toString(), countryId: countryId.toString(),
categoryId: categoryId.toString(), // includes child categoryId: categoryId.toString(), // includes child
keyword: `Pub `, keyword: `Pub `,
}); });
expect(filtered.total).toBeGreaterThanOrEqual(4); // High, Mid, NoPos, ChildGood expect(filtered.total).toBeGreaterThanOrEqual(4); // High, Mid, NoPos, ChildGood
expect(filtered.items.every((g) => g.country.id === countryId.toString())).toBe(true); expect(filtered.items.every((g) => g.country.id === countryId.toString())).toBe(true);
}); });
it('sorts by priority DESC, position.indexVal ASC, createdAt DESC', async () => { it('sorts by priority DESC, position.indexVal ASC, createdAt DESC', async () => {
const result = await service.getGoods({ const result = await service.getGoods({
page: 1, page: 1,
pageSize: 50, pageSize: 50,
countryId: countryId.toString(), countryId: countryId.toString(),
keyword: `Pub `, keyword: `Pub `,
}); });
const priorities = result.items.map((g) => g.goodPriority); const priorities = result.items.map((g) => g.goodPriority);
// First verify primary descending priority. // First verify primary descending priority.
const sorted = [...priorities].sort((a, b) => b - a); const sorted = [...priorities].sort((a, b) => b - a);
expect(priorities).toEqual(sorted); expect(priorities).toEqual(sorted);
}); });
it('uses OR within one tag group and AND across tag groups', async () => {
const sameGroup = await service.getGoods({
page: 1,
pageSize: 50,
countryId: countryId.toString(),
keyword: `Pub `,
tags: [
{
tagGroupId: filterGroupIds[0].toString(),
tagIds: filterTagIds.slice(0, 2).map(String),
},
],
});
expect(sameGroup.items.map((item) => item.goodName)).toEqual(
expect.arrayContaining([`Pub High ${stamp}`, `Pub Mid ${stamp}`]),
);
const acrossGroups = await service.getGoods({
page: 1,
pageSize: 50,
countryId: countryId.toString(),
keyword: `Pub `,
tags: [
{
tagGroupId: filterGroupIds[0].toString(),
tagIds: filterTagIds.slice(0, 2).map(String),
},
{
tagGroupId: filterGroupIds[1].toString(),
tagIds: [filterTagIds[2].toString()],
},
],
});
expect(acrossGroups.items.map((item) => item.goodName)).toContain(`Pub High ${stamp}`);
expect(acrossGroups.items.map((item) => item.goodName)).not.toContain(`Pub Mid ${stamp}`);
});
it('rejects a tag paired with the wrong tag group', async () => {
await expect(
service.getGoods({
page: 1,
pageSize: 20,
tags: [
{
tagGroupId: filterGroupIds[1].toString(),
tagIds: [filterTagIds[0].toString()],
},
],
}),
).rejects.toBeInstanceOf(BadRequestException);
});
it('returns the SDS product id as the public product id', async () => { it('uses OR within one tag group and AND across tag groups', async () => {
const sameGroup = await service.getGoods({
page: 1,
pageSize: 50,
countryId: countryId.toString(),
keyword: `Pub `,
tags: [
{
tagGroupId: filterGroupIds[0].toString(),
tagIds: filterTagIds.slice(0, 2).map(String),
},
],
});
expect(sameGroup.items.map((item) => item.goodName)).toEqual(
expect.arrayContaining([`Pub High ${stamp}`, `Pub Mid ${stamp}`]),
);
const acrossGroups = await service.getGoods({
page: 1,
pageSize: 50,
countryId: countryId.toString(),
keyword: `Pub `,
tags: [
{
tagGroupId: filterGroupIds[0].toString(),
tagIds: filterTagIds.slice(0, 2).map(String),
},
{
tagGroupId: filterGroupIds[1].toString(),
tagIds: [filterTagIds[2].toString()],
},
],
});
expect(acrossGroups.items.map((item) => item.goodName)).toContain(`Pub High ${stamp}`);
expect(acrossGroups.items.map((item) => item.goodName)).not.toContain(`Pub Mid ${stamp}`);
});
it('rejects a tag paired with the wrong tag group', async () => {
await expect(
service.getGoods({
page: 1,
pageSize: 20,
tags: [
{
tagGroupId: filterGroupIds[1].toString(),
tagIds: [filterTagIds[0].toString()],
},
],
}),
).rejects.toBeInstanceOf(BadRequestException);
});
it('returns the SDS product id as the public product id', async () => {
const result = await service.getGoods({ const result = await service.getGoods({
page: 1, page: 1,
pageSize: 1, pageSize: 1,
countryId: countryId.toString(), countryId: countryId.toString(),
keyword: `Pub High ${stamp}`, keyword: `Pub High ${stamp}`,
}); });
expect(result.items).toHaveLength(1); expect(result.items).toHaveLength(1);
expect(result.items[0].goodId).toBe(`pub-sds-${stamp}`); expect(result.items[0].goodId).toBe(`pub-sds-${stamp}`);
expect(result.items[0].goodId).not.toBe(goodIds[0].toString()); expect(result.items[0].goodId).not.toBe(goodIds[0].toString());
}); });
it('returns custom goods through the same public product contract', async () => { it('returns custom goods through the same public product contract', async () => {
const customPublicId = `custom-public-${stamp}`; const customPublicId = `custom-public-${stamp}`;
const origin = await prisma.originGood.create({ const origin = await prisma.originGood.create({
data: { data: {
source: 'CUSTOM', source: 'CUSTOM',
sdsGoodId: customPublicId, sdsGoodId: customPublicId,
goodName: `Pub Custom ${stamp}`, goodName: `Pub Custom ${stamp}`,
goodPrice: 42, goodPrice: 42,
detail: { create: { productCode: `CUSTOM-${stamp}` } }, detail: { create: { productCode: `CUSTOM-${stamp}` } },
}, },
}); });
const good = await prisma.good.create({ const good = await prisma.good.create({
data: { data: {
originGoodId: origin.id, originGoodId: origin.id,
countryId, countryId,
categoryId, categoryId,
goodName: `Pub Custom ${stamp}`, goodName: `Pub Custom ${stamp}`,
}, },
}); });
try { try {
const detail = await service.getGood(customPublicId); const detail = await service.getGood(customPublicId);
expect(detail.goodId).toBe(customPublicId); expect(detail.goodId).toBe(customPublicId);
expect(detail.goodName).toBe(`Pub Custom ${stamp}`); expect(detail.goodName).toBe(`Pub Custom ${stamp}`);
expect(detail.productCode).toBe(`CUSTOM-${stamp}`); expect(detail.productCode).toBe(`CUSTOM-${stamp}`);
} finally { } finally {
await prisma.good.delete({ where: { id: good.id } }); await prisma.good.delete({ where: { id: good.id } });
await prisma.originGood.delete({ where: { id: origin.id } }); await prisma.originGood.delete({ where: { id: origin.id } });
} }
}); });
it('getGood returns detail and 404 for unknown id', async () => { it('getGood returns detail and 404 for unknown id', async () => {
const first = await service.getGoods({ const first = await service.getGoods({
page: 1, page: 1,
pageSize: 1, pageSize: 1,
countryId: countryId.toString(), countryId: countryId.toString(),
keyword: `Pub `, keyword: `Pub `,
}); });
expect(first.items.length).toBe(1); expect(first.items.length).toBe(1);
const detail = await service.getGood(`pub-sds-${stamp}`); const detail = await service.getGood(`pub-sds-${stamp}`);
expect(detail.goodId).toBe(first.items[0].goodId); expect(detail.goodId).toBe(first.items[0].goodId);
expect(detail.productCode).toBe('OZ10827003'); expect(detail.productCode).toBe('OZ10827003');
expect(detail.details.productionProcess).toBe('白墨烫画'); expect(detail.details.productionProcess).toBe('白墨烫画');
expect((detail.sizeChart?.rows as unknown[])).toHaveLength(1); expect((detail.sizeChart?.rows as unknown[])).toHaveLength(1);
expect((detail.packageSpecs?.rows as unknown[])).toHaveLength(1); expect((detail.packageSpecs?.rows as unknown[])).toHaveLength(1);
expect(detail.variants).toHaveLength(1); expect(detail.variants).toHaveLength(1);
await expect(service.getGood('99999999')).rejects.toBeInstanceOf( await expect(service.getGood('99999999')).rejects.toBeInstanceOf(
NotFoundException, NotFoundException,
); );
}); });
@@ -379,4 +379,39 @@ describe('PublicService', () => {
const sortOrders = groups.map((g) => g.sortOrder); const sortOrders = groups.map((g) => g.sortOrder);
expect([...sortOrders].sort((a, b) => a - b)).toEqual(sortOrders); expect([...sortOrders].sort((a, b) => a - b)).toEqual(sortOrders);
}); });
describe('merged secondary origin goods', () => {
it('resolves a good by secondary sdsGoodId with merged variants', async () => {
const secondary = await prisma.originGood.create({
data: { sdsGoodId: `pub-secondary-${stamp}`, goodName: `Pub Secondary ${stamp}` },
});
const secVariant = await prisma.originGoodVariant.create({
data: {
originGoodId: secondary.id,
sdsVariantId: `pub-var-sec-${stamp}`,
sku: `PUB-SEC-${stamp}`,
colorId: 'black',
colorName: '黑色',
imageUrl: 'http://img/black-sec',
},
});
// Attach as secondary source of the highest-priority fixture good.
await prisma.goodOriginGood.create({
data: { goodId: goodIds[0], originGoodId: secondary.id },
});
try {
const detail = await service.getGood(`pub-secondary-${stamp}`);
expect(detail.goodId).toBe(`pub-sds-${stamp}`); // 对外 goodId 仍是主源
expect(detail.variants.length).toBeGreaterThanOrEqual(2);
const black = detail.mediaByColor.find((g) => g.colorName === '黑色');
expect(black).toBeTruthy();
expect(black!.images).toContain('http://img/black-sec');
} finally {
await prisma.goodOriginGood.deleteMany({ where: { originGoodId: secondary.id } });
await prisma.originGoodVariant.delete({ where: { id: secVariant.id } }).catch(() => undefined);
await prisma.originGood.delete({ where: { id: secondary.id } }).catch(() => undefined);
}
});
});
}); });
+29 -4
View File
@@ -34,6 +34,16 @@ const PUBLIC_GOOD_INCLUDE = {
variants: { orderBy: [{ sortOrder: 'asc' as const }, { id: 'asc' as const }] }, variants: { orderBy: [{ sortOrder: 'asc' as const }, { id: 'asc' as const }] },
}, },
}, },
mergedOriginGoods: {
orderBy: { createdAt: 'asc' as const },
include: {
originGood: {
include: {
variants: { orderBy: [{ sortOrder: 'asc' as const }, { id: 'asc' as const }] },
},
},
},
},
goodTags: { include: { tag: { include: { tagGroup: true } } } }, goodTags: { include: { tag: { include: { tagGroup: true } } } },
} satisfies Prisma.GoodInclude; } satisfies Prisma.GoodInclude;
@@ -198,7 +208,17 @@ export class PublicService {
async getGood(goodId: string): Promise<PublicGoodDetailDto> { async getGood(goodId: string): Promise<PublicGoodDetailDto> {
const good = await this.prisma.good.findFirst({ const good = await this.prisma.good.findFirst({
where: { originGood: { sdsGoodId: goodId, delisted: false } }, where: {
OR: [
{ originGood: { sdsGoodId: goodId, delisted: false } },
// Merged secondary sources also resolve to the same good.
{
mergedOriginGoods: {
some: { originGood: { sdsGoodId: goodId, delisted: false } },
},
},
],
},
include: PUBLIC_GOOD_INCLUDE, include: PUBLIC_GOOD_INCLUDE,
orderBy: [{ goodPriority: 'desc' }, { id: 'asc' }], orderBy: [{ goodPriority: 'desc' }, { id: 'asc' }],
}); });
@@ -277,7 +297,7 @@ export class PublicService {
* design-layer素材图 and the product-level blank garment photo are excluded * design-layer素材图 and the product-level blank garment photo are excluded
* because they are not per-color gallery photos. */ * because they are not per-color gallery photos. */
private groupImagesByColor( private groupImagesByColor(
variants: PublicGoodRow['originGood']['variants'], variants: Array<PublicGoodRow['originGood']['variants'][number]>,
): Array<{ colorId: string | null; colorName: string | null; colorHex: string | null; images: string[] }> { ): Array<{ colorId: string | null; colorName: string | null; colorHex: string | null; images: string[] }> {
const groups = new Map<string, { const groups = new Map<string, {
colorId: string | null; colorId: string | null;
@@ -335,6 +355,11 @@ export class PublicService {
private toPublicGoodDetail(good: PublicGoodRow): PublicGoodDetailDto { private toPublicGoodDetail(good: PublicGoodRow): PublicGoodDetailDto {
const base = this.toPublicGood(good); const base = this.toPublicGood(good);
const detail = good.originGood.detail; const detail = good.originGood.detail;
// Merge primary and secondary origin good variants (dedup identical URLs).
const allVariants = [
...good.originGood.variants,
...good.mergedOriginGoods.flatMap((m) => m.originGood.variants),
];
return { return {
...base, ...base,
productCode: detail?.productCode ?? null, productCode: detail?.productCode ?? null,
@@ -354,11 +379,11 @@ export class PublicService {
pictureRequest: detail?.pictureRequest ?? null, pictureRequest: detail?.pictureRequest ?? null,
}, },
media: (detail?.media as Record<string, unknown> | null) ?? null, media: (detail?.media as Record<string, unknown> | null) ?? null,
mediaByColor: this.groupImagesByColor(good.originGood.variants), mediaByColor: this.groupImagesByColor(allVariants),
options: (detail?.options as Record<string, unknown> | null) ?? null, options: (detail?.options as Record<string, unknown> | null) ?? null,
sizeChart: (detail?.sizeChart as Record<string, unknown> | null) ?? null, sizeChart: (detail?.sizeChart as Record<string, unknown> | null) ?? null,
packageSpecs: (detail?.packageSpecs as Record<string, unknown> | null) ?? null, packageSpecs: (detail?.packageSpecs as Record<string, unknown> | null) ?? null,
variants: good.originGood.variants.map((variant) => ({ variants: allVariants.map((variant) => ({
id: variant.sdsVariantId, id: variant.sdsVariantId,
sku: variant.sku, sku: variant.sku,
sizeId: variant.sizeId, sizeId: variant.sizeId,
-202
View File
@@ -1,202 +0,0 @@
# 商品数据清洗(Goods Data Cleaning
本目录存放商品数据清洗相关的全部脚本与规则文件。清洗目标:把右侧 SDS 原产品库
`origin_goods`)按规则批量配置为左侧官网商品(`goods`),替代后台手动逐个点击。
## 转换规则调查(2026-08-27,基于生产环境只读数据)
> 数据来源:`https://official.inkreach.cc/api` 生产接口(JWT 只读拉取):
> `/origin-goods?page=1..3&pageSize=200`(共 552 条)、`/goods?page=1..2&pageSize=200`(共 240 条)、
> `/tags`、`/categories`、`/countries`。未做任何写操作。
> 原始 JSON 快照存于 `runs/survey-2026-08-27/`(已加入 .gitignore,不入库)。
### 1. 右侧原产品(origin_goods)命名结构
右侧只有**一个字符串** `goodName`,整体模式:
```
国家(物流备注)品名-SKU-工艺位置[-仓库名]
↑ ↑ ↑ ↑ ↑
| | | | └─ 可选第4段:美西洛杉矶一仓 / 美中亚特兰大仓 等
| | | └─ 单面印花 / 双面印花 / 直喷双面 / 直喷单面 / 不打印 ...
| | └─ SKU 编码(字母+数字,如 DG001 / C1717 / KRHM003
| └─ 包邮 / 不包邮 / DHL包邮*运费结算订单时支付 / 不包邮光板 等变体
└─ 美国/德国/韩国...(全角括号为主,少量半角混用)
```
统计(552 条):
- **541 条**匹配 `国家(备注)其余` 模式;**11 条异常**
- 4 条自定义商品(`Goods Origin ...`source=CUSTOM
- 2 条缺国家前缀:`(不包邮)230g水洗炒雪花T恤-双面印花` / `(不包邮)180g纯棉T恤成人款-...`
- 1 条用空格代替括号:`法国 180g纯棉T恤-FRTM001-双面印花`
- 2 条半角左括号混用:`美国(不包邮)女士高弹中长款瑜伽运动裤-JSD004-...`
- 2 条同上变体
- 「备注)之后」按 `-` 分段:**387 条 = 3 段**(品名-SKU-工艺)、**154 条 = 4 段**(品名-SKU-工艺-仓库)
- 物流备注出现的变体形态(示例):`包邮``不包邮``DHL包邮``DHL包邮*运费结算订单时支付``包邮*运费订单结算时支付``不包邮光板`
- 国家前缀出现过的值:美国、德国、英国、意大利、西班牙、西班牙直发、韩国、日本、加拿大、墨西哥、巴西、波兰、澳大利亚
### 2. 左侧已配置商品(goods)现状 —— 人工配置的实际结果
240 条 goods 记录,覆盖 **209 个**不同 origin_good_id**343 个 origin 尚未配置**)。
**没有系统级拆分逻辑**:前端 [GoodsView.vue](../apps/admin/src/views/goods/GoodsView.vue) 提交的是完整原名,
后端 [goods.service.ts](../apps/api/src/goods/goods.service.ts) create/batchCreate 也原样入库。
因此左侧名称与右端的差异(113/240 条被改过)全部是**人工在编辑弹窗里改的**,且存在多种风格并存。
改名风格分布(113 条改名的归类):
| 风格 | 数量 | 示例 |
|------|------|------|
| A. 全拆:去前缀去括号,品名+空格+SKU,丢弃工艺段 | 27 | `韩国(包邮)200g纯棉T恤-KRTM001-单面印花``200g纯棉T恤 KRTM001` |
| B. 半改:仅把长备注缩成「X包邮」,其余原样保留 | 86 | `德国(DHL包邮*运费结算订单时支付)230g水洗T恤-DETM002-单面印花``德国(DHL包邮)230g水洗T恤-DETM002-单面印花` |
| R0. 保持原名不动 | 127 | 多为后期配置(2026-06 后期 ~ 07),未加工 |
标签体系(生产库现有 3 组 7 个标签)与左侧使用情况:
| 分组 | 标签(id) | 左侧使用次数 |
|------|----------|--------------|
| 物流渠道 | 包邮(30) / 不包邮(31) | 87 / 153 ← 基本每个商品都挂了物流标签 |
| 印刷位置 | 双面印(35) / 单面印(34) | 14 / 14 ← 只有少数挂了 |
| 印刷工艺 | 烫画(32) / 直喷(33) / 不打印(42) | 11 / 3 / 1 |
其他字段:goodPriority 几乎全部为 5positionId 全部为空;
国家字段与名称里的国家前缀一致率 238/240(仅 `西班牙直发(...)` 两条归入了「西班牙」)。
### 3. 人工操作流程(脚本要模拟的完整动作)
后台右侧树有「仅未配置」筛选按钮,人工实际是**两步操作**:
**第一步:配置(右→左创建)**
1. 右侧找到未配置的原产品,点击「配置」
2. 弹窗显示:原产品名(**纯文本,不可编辑**,[L1716](../apps/admin/src/views/goods/GoodsView.vue#L1716))、预览图片(可改但默认回填)
3. 人工选择三项:**国家、分类、标签**(名称在此步不能改)
4. 确认提交 → `POST /goods`,goodName 直接传原产品完整原名([L403](../apps/admin/src/views/goods/GoodsView.vue#L403)
**第二步:编辑改名(左侧已有记录上改)**
1. 左侧找到刚配置的商品,点击「编辑」
2. 编辑弹窗有 `el-input` 绑定 goodName[L1821](../apps/admin/src/views/goods/GoodsView.vue#L1821)),这里才能改名
3. 保存 → `PATCH /goods/:id`[L690](../apps/admin/src/views/goods/GoodsView.vue#L690)
因此脚本的**实际操作序列**是:先 `POST /goods`(用原名创建)→ 再 `PATCH /goods/:id`(改名)
弹窗字段与 API 参数对应:
| 步骤 | 弹窗字段 | API 参数 | 说明 |
|------|----------|----------|------|
| 配置 | 国家 | `countryId` | 必填 |
| 配置 | 分类 | `categoryId` | 必填 |
| 配置 | 标签 | `tagIds[]` | 写入 good_tags 中间表 |
| 配置 | 预览图片 | `goodImage` | 默认回填 origin_good.goodImage |
| 配置 | 名称 | `goodName` | **不可编辑**,自动传原名 |
| 编辑 | 名称 | `goodName` | **这一步才能改**,调 PATCH |
| 编辑 | 其余字段 | 同上 | 也可在编辑时调整 |
### 4. 定稿转换规则
> 以下规则已确认,脚本按此执行。
#### 4.1 整体策略
- **方案:API 驱动**(不导出/导入 DB,避免 BigInt 自增序列和外键约束问题)
- 脚本调用 `POST /goods` 逐条配置,走现有业务逻辑(校验、事务、标签关联全部由后端处理)
- 幂等:已存在的 (originGoodId, countryId) 组合跳过
- scope:仅处理**未配置**的原产品(configuredCount === 0),已配置的不动
#### 4.2 名称解析与改名规则
原产品名格式:`国家(物流备注)品名-SKU-工艺位置[-仓库名]`
**改名规则**:保留到 `-` 分隔的第 2 段,用空格连接,丢弃后续段(工艺、仓库)。
```
输入: 美国(包邮)180g纯棉T恤成人款-DG001-单面印花
解析: 品名="180g纯棉T恤成人款" SKU="DG001" 工艺="单面印花"(丢弃)
输出: goodName = "180g纯棉T恤成人款 DG001"
```
- 仓库名后缀(第 4 段,如「美西洛杉矶一仓」)直接丢弃
- 异常名称(缺国家前缀、半角括号等)单独输出到报告,不自动处理
#### 4.3 国家
- 从名称第一个 `` 之前提取国家文本
- 通过 `GET /countries` 拿到 countries 表,按 `countryName` 精确匹配 → `countryId`
- 特殊映射:「西班牙直发」→ 匹配「西班牙」
- 匹配不到的 → 输出到报告,不自动处理
#### 4.4 分类(品类)
- 右侧树已经按 categories 树分组(通过 `origin_goods.sds_category_id``categories.sds_category_id` 桥接)
- 同一原产品的分类就是它在右侧树中所挂的分类节点,直接取该节点的 `categoryId`
- 未分类的(sdsCategoryId 无匹配)→ 输出到报告
#### 4.5 标签(3 组,从名称解析)
标签体系(3 组 7 个):
| 分组 | 标签 | id | 解析规则 |
|------|------|----|----------|
| 物流渠道 | 包邮 / 不包邮 | 30 / 31 | 括号内含「包邮」→ 包邮(30),否则 → 不包邮(31) |
| 印刷位置 | 单面印 / 双面印 | 34 / 35 | 工艺段含「单面」→ 单面印(34),含「双面」→ 双面印(35) |
| 印刷工艺 | 烫画 / 直喷 / 不打印 | 32 / 33 / 42 | 工艺段含「直喷」→ 直喷(33);含「不打印」或「光板」→ 不打印(42);其余默认 → 烫画(32) |
工艺段 = `-` 分段的最后一段(去仓库段后),如 `单面印花``直喷双面``不打印``烫画`
解析示例:
```
美国(不包邮)180g纯棉T恤成人款-DG001-单面印花
→ 物流: 不包邮(31), 印刷位置: 单面印(34), 印刷工艺: 烫画(32)
美国(不包邮)207G重磅纯棉T恤-C1717-直喷双面
→ 物流: 不包邮(31), 印刷位置: 双面印(35), 印刷工艺: 直喷(33)
美国(不包邮光板)180g纯棉T恤成人款-DG001-不打印
→ 物流: 不包邮(31), 印刷位置: 跳过(工艺段=不打印无法判断单双面), 印刷工艺: 不打印(42)
```
#### 4.6 其他字段
- `goodImage`:直接用 origin_good.goodImage,不改
- `goodPriority`:默认 5(与现有 236/240 条一致)
- `positionId`:不填(与现有 240/240 条一致)
---
## 执行流水线(API 驱动方案)
```
① 拉取只读数据
调 GET /origin-goods/tree(含 configuredCount)、/tags、/categories、/countries
存到 runs/<date>/snapshot/
② 本地解析脚本 sync-plan.mjs
读取快照 → 筛选未配置 → 逐条解析名称 → 生成待创建指令列表
输出: runs/<date>/plan.json(每条包含 originGoodId/countryId/categoryId/goodName/tagIds/goodImage
+ runs/<date>/plan-report.md(人工检查点:共 N 条待创建、X 条异常需人工确认)
③ 人工审查 plan.json 和 plan-report.md
④ 执行脚本 sync-apply.mjs
读取 plan.json → 逐条 POST /goods(带 JWT,自动刷新 token
每条打印 +/skip/error;失败自动重试 3 次;生成 apply-report.md
⑤ 验证:调 /origin-goods/tree 确认未配置数归零,调 /goods 抽查新记录
```
原则:
- 不导出/导入数据库,全部通过 API 操作
- ② 和 ④ 分离:先生成计划供人工审查,确认后再执行
- 每步产物存 runs/(已 gitignore
- token 30 分钟过期,脚本内自动用 /auth/login 刷新
## 目录内容规划
| 文件 | 状态 | 说明 |
|------|------|------|
| `sync-rules.config.mjs` | 待创建 | 国家映射/异常处理等配置项(解析规则已定稿在上文) |
| `sync-plan.mjs` | 待创建 | 拉取 + 解析 + 生成 plan.json |
| `sync-apply.mjs` | 待创建 | 读取 plan.json + 逐条 POST /goods |
| `runs/` | 已创建 | 调查快照 survey-2026-08-27/ 已存在;后续每次运行产物按日期归档 |
详细实施计划见 [plans/feature/goods-data-cleaning-feature.md](../plans/feature/goods-data-cleaning-feature.md)。
+11
View File
@@ -39,6 +39,17 @@
宽度低于 1000px 后分类栏变为抽屉;移动端商品网格降为两列或单列,国家筛选仅在自身区域横向滚动,不会撑宽页面。 宽度低于 1000px 后分类栏变为抽屉;移动端商品网格降为两列或单列,国家筛选仅在自身区域横向滚动,不会撑宽页面。
## 多源合并商品
后台支持把名称相同但工厂/仓库不同的多个 SDS 原产品合并为一个官网商品:
- 数据层:主源存 `goods.origin_good_id`,副源存中间表 `good_origin_goods`
- 详情可达性:官网上通过**任一**关联原产品的 `sdsGoodId` 都能访问到该商品详情,即副源的旧链接不会 404。
- 变体合并:详情中的 SKU 是「主源变体 ∪ 全部副源变体」,按颜色归组展示媒体图;价格与详情页内容以主源为准。
- 后台维护入口:
- 配置弹窗(右栏拖拽/配置按钮):自动勾选同分类下同名兄弟原产品作为副源提交(`mergedOriginGoodIds`);
- 编辑弹窗「关联原产品」区:可搜索添加副源、移除副源、切换主源(切换后旧主源自动转为副源)。
## 验证 ## 验证
```bash ```bash
+1
View File
@@ -84,6 +84,7 @@ apps/api/
| `TagGroup` | 标签分组(含 `groupName``groupColor``groupIcon``sortOrder`),删除分组时组内 tag 的 `tagGroupId` 通过 `onDelete: SetNull` 自动置空 | | `TagGroup` | 标签分组(含 `groupName``groupColor``groupIcon``sortOrder`),删除分组时组内 tag 的 `tagGroupId` 通过 `onDelete: SetNull` 自动置空 |
| `Position` | 坑位:`(country, category)` 维度,关联多个 goods | | `Position` | 坑位:`(country, category)` 维度,关联多个 goods |
| `Good` | 商品:`originGood × country × category × tag? × position?`,含 `goodPriority` | | `Good` | 商品:`originGood × country × category × tag? × position?`,含 `goodPriority` |
| `GoodOriginGood` | 副源关联中间表(多对一):一个 Good 可关联多个副源 OriginGood;主源走 `goods.origin_good_id` 不入表。用于把名称相同但工厂/仓库不同的多个原产品合并为一个商品展示 |
| `User` | 后台用户(bcrypt 哈希) | | `User` | 后台用户(bcrypt 哈希) |
| `SyncLog` | 同步任务日志,含 `SyncType`CATEGORIES / PRODUCTS)和 `SyncStatus` | | `SyncLog` | 同步任务日志,含 `SyncType`CATEGORIES / PRODUCTS)和 `SyncStatus` |
@@ -1,280 +0,0 @@
# Goods Data Cleaning Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 用「备份 → 导出 → 脚本转换 → 导入 → 验证」的流水线,把右侧 SDS 原产品库(`origin_goods`)按规则批量配置为左侧官网商品(`goods`),替代后台手动逐个点击。直接作用于生产库。
**Background:**
- 后台 GoodsView 已实现手动配置能力:右侧树选原产品 → 选国家/品类/标签 → 生成 `goods` 记录。
- 手动逐个点击效率低,需要脚本化批量处理。
- 同步规则(品类映射 / 国家分配 / 标签分配 / 优先级策略)**尚未确定**,将在后续数据清洗计划中明确后填入 `data-cleaning/sync-rules.config.mjs`
**Architecture:** 三步式流水线,复用现有 export/import 脚本,只新写转换脚本与独立规则文件。规则文件占位先行——改规则不改代码。转换脚本为纯函数式(JSON in → JSON out),不直接连接数据库;导入前输出变更摘要报告作为人工检查点;导入前必须完成全量备份保证可回滚。
**Tech Stack:** Node.js ESM 脚本、@prisma/client(仅 export/import 使用)、PostgreSQL。
**目录约定:**
- 清洗相关脚本/规则/产物统一放根目录 `data-cleaning/`(见 [data-cleaning/README.md](../../data-cleaning/README.md)
- 复用现有 [apps/api/scripts/export-data.mjs](../../apps/api/scripts/export-data.mjs) 与 [apps/api/scripts/import-data.mjs](../../apps/api/scripts/import-data.mjs)
---
## 流水线总览
```
[生产库]
│ ① pg_dump 全量备份
② node scripts/export-data.mjs data-cleaning/runs/<date>/export.json
③ node data-cleaning/sync-transform.mjs data-cleaning/runs/<date>/export.json
→ 输出 transformed.json + change-report.md(人工检查点)
④ node scripts/import-data.mjs data-cleaning/runs/<date>/transformed.json
⑤ 验证:行数对比 / 抽查商品配置 / 官网公开 API 抽查
```
### Task 1: 创建规则文件占位 `sync-rules.config.mjs`
**Files:**
- Create: `data-cleaning/sync-rules.config.mjs`
- Test: 无(纯数据文件,由 Task 2 的测试覆盖加载逻辑)
- [x] **Step 1: 创建规则文件骨架**
```js
/**
* 商品数据清洗 — 同步规则配置
*
* 规则尚未确定。确定后只修改本文件,不改 sync-transform.mjs。
* 字段语义在规则确定时补充说明。
*/
export const rules = {
/** 品类映射:SDS 品类 → 本地品类(待定) */
categoryMapping: {
// '<sds_category_id 或名称>': '<本地 category_id 或名称>',
},
/** 国家分配:每条原产品生成哪些国家的 good(待定) */
countryAssignment: {
mode: 'none', // none | all | fixed | perCategory
fixedCountryIds: [],
perCategory: {},
},
/** 标签分配:新 good 挂哪些 tag(待定) */
tagAssignment: {
mode: 'none', // none | fixed | perCategory
fixedTagIds: [],
perCategory: {},
},
/** 优先级策略(待定) */
priority: {
defaultPriority: 0,
},
};
```
- [x] **Step 2: Commit**
```bash
git add data-cleaning/sync-rules.config.mjs
git commit -m "feat(data-cleaning): add sync rules config placeholder"
```
### Task 2: 创建转换脚本 `sync-transform.mjs`TDD
**Files:**
- Create: `data-cleaning/sync-transform.mjs`
- Test: `data-cleaning/sync-transform.test.mjs`
- [x] **Step 1: 写失败测试**
```js
// data-cleaning/sync-transform.test.mjs
import { describe, it, expect } from 'vitest';
import { transform } from './sync-transform.mjs';
import { rules } from './sync-rules.config.mjs';
const baseDump = () => ({
exportedAt: new Date().toISOString(),
tables: {
users: [], countries: [], categories: [], tag_groups: [], tags: [],
positions: [],
origin_goods: [
{ id: '1', sds_good_id: 'SDS-A', good_name: 'A', delisted: 'false', is_custom: 'false' },
{ id: '2', sds_good_id: 'SDS-B', good_name: 'B', delisted: 'true', is_custom: 'false' },
],
origin_good_variants: [], origin_good_details: [],
goods: [], good_tags: [], sync_logs: [],
},
});
describe('transform', () => {
it('rules 为空时原样返回且报告零变更', () => {
const dump = baseDump();
const { result, report } = transform(dump, rules);
expect(result.tables.goods).toHaveLength(0);
expect(report.created).toBe(0);
expect(result.tables.origin_goods).toHaveLength(2);
});
it('跳过已下架原产品', () => {
const dump = baseDump();
const testRules = { ...rules, countryAssignment: { mode: 'all' } };
const { report } = transform(dump, testRules);
// 只有未下架的 id=1 会生成 good
expect(report.created).toBe(1);
expect(report.skippedDelisted).toBe(1);
});
it('幂等:已存在的 (originGoodId,countryId) 不重复创建', () => {
const dump = baseDump();
dump.tables.goods = [
{ id: '100', origin_good_id: '1', country_id: '9', good_name: 'A', good_priority: '0' },
];
dump.tables.countries = [{ id: '9', country_name: 'US' }];
const testRules = {
...rules,
countryAssignment: { mode: 'fixed', fixedCountryIds: ['9'] },
};
const { report } = transform(dump, testRules);
expect(report.created).toBe(0);
expect(report.duplicates).toBe(1);
});
});
```
> 注:若仓库未配置 vitest 运行 `.mjs`,可用 `node --test`node:test)替代,断言等价改写。
- [x] **Step 2: 运行测试确认 RED**
Run: `cd apps/api && npx vitest run ../../data-cleaning/sync-transform.test.mjs`(或 `node --test data-cleaning/`
Expected: FAIL(模块不存在)
- [x] **Step 3: 最小实现**
```js
// data-cleaning/sync-transform.mjs
/**
* 数据转换脚本(纯函数式):读取导出 JSON + 规则文件,
* 输出待导入 JSONtransformed.json)与变更摘要(change-report.md)。
*
* Usage:
* node data-cleaning/sync-transform.mjs <export.json>
*/
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { dirname } from 'node:path';
import { rules } from './sync-rules.config.mjs';
/** 纯函数:dump + rules → { result, report } */
export function transform(dump, rulesConfig) {
const tables = JSON.parse(JSON.stringify(dump.tables)); // deep clone
const countries = tables.countries;
const existingKeys = new Set(
tables.goods.map((g) => `${g.origin_good_id}:${g.country_id}`),
);
const report = { created: 0, duplicates: 0, skippedDelisted: 0, details: [] };
const activeCountries = countries.filter((c) => c.id);
for (const og of tables.origin_goods) {
if (og.delisted === 'true' || og.delisted === true) {
if (og.is_custom !== 'true' && og.is_custom !== true) report.skippedDelisted++;
continue;
}
let targets = [];
if (rulesConfig.countryAssignment.mode === 'all') {
targets = activeCountries.map((c) => c.id);
} else if (rulesConfig.countryAssignment.mode === 'fixed') {
targets = rulesConfig.countryAssignment.fixedCountryIds;
}
for (const countryId of targets) {
const key = `${og.id}:${countryId}`;
if (existingKeys.has(key)) { report.duplicates++; continue; }
const newId = String(
tables.goods.reduce((m, g) => Math.max(m, Number(g.id || 0)), 0) + 1,
);
tables.goods.push({
id: newId,
origin_good_id: og.id,
country_id: countryId,
category_id: rulesConfig.categoryMapping.default ?? '',
good_name: og.good_name,
good_priority: String(rulesConfig.priority.defaultPriority ?? 0),
});
existingKeys.add(key);
report.created++;
report.details.push(`+ good[${newId}] origin=${og.sds_good_id} country=${countryId}`);
}
}
return { result: { exportedAt: dump.exportedAt, tables }, report };
}
function main() {
const input = process.argv[2];
if (!input) {
console.error('Usage: node data-cleaning/sync-transform.mjs <export.json>');
process.exit(1);
}
const dump = JSON.parse(readFileSync(input, 'utf8'));
const { result, report } = transform(dump, rules);
mkdirSync(`${dirname(input)}/out`, { recursive: true });
writeFileSync(`${dirname(input)}/out/transformed.json`, JSON.stringify(result));
writeFileSync(
`${dirname(input)}/out/change-report.md`,
['# 变更摘要', `- 新增 goods: ${report.created}`, `- 重复跳过: ${report.duplicates}`,
`- 下架跳过: ${report.skippedDelisted}`, '', ...report.details.map((d) => `- ${d}`)].join('\n'),
);
console.log(JSON.stringify(report, null, 2));
}
// 测试环境下不自动执行 main
if (process.env.NODE_ENV !== 'test' && import.meta.url === `file://${process.argv[1]}`) {
main();
}
```
- [x] **Step 4: 运行测试确认 GREEN**
Run: `cd apps/api && npx vitest run ../../data-cleaning/sync-transform.test.mjs`
Expected: PASS3 个用例全过)
- [x] **Step 5: Commit**
```bash
git add data-cleaning/sync-transform.mjs data-cleaning/sync-transform.test.mjs
git commit -m "feat(data-cleaning): add pure transform script with tests"
```
### Task 3: 生产执行手册写入 README 并联调演练
**Files:**
- Modify: `data-cleaning/README.md`
- [x] **Step 1: 补充执行命令段**(备份 / 导出 / 转换 / 人工检查 / 导入 / 验证 六个步骤的确切命令与预期输出)
- [x] **Step 2: 本地或预发演练一次全流程**(规则为空应零变更),确认 change-report 为空、行数一致
- [x] **Step 3: Commit**
```bash
git add data-cleaning/README.md
git commit -m "docs(data-cleaning): add production runbook"
```
---
## 待定事项(规则确定后回填)
| 项 | 状态 | 回填位置 |
|----|------|----------|
| 品类映射规则 | 未定 | `data-cleaning/sync-rules.config.mjs#categoryMapping` |
| 国家分配策略 | 未定 | `...#countryAssignment` |
| 标签分配策略 | 未定 | `...#tagAssignment` |
| 优先级策略 | 未定 | `...#priority` |
| 规则细节文档 | 未定 | 后续数据清洗计划文件夹(本目录)内新建规则说明 md |
File diff suppressed because it is too large Load Diff
+182 -143
View File
@@ -66,6 +66,9 @@ importers:
vite: vite:
specifier: ^8.0.12 specifier: ^8.0.12
version: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) version: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vitest:
specifier: ^4.1.10
version: 4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
vue-tsc: vue-tsc:
specifier: ^3.2.8 specifier: ^3.2.8
version: 3.3.7(typescript@6.0.3) version: 3.3.7(typescript@6.0.3)
@@ -228,7 +231,7 @@ importers:
version: 7.3.0 version: 7.3.0
'@nuxtjs/seo': '@nuxtjs/seo':
specifier: ^5.1.3 specifier: ^5.1.3
version: 5.3.2(3e5babf51d8d70e3fe05aad885f274e3) version: 5.3.2(n4en3flscg275nrtqhntxiu3a4)
'@tailwindcss/postcss': '@tailwindcss/postcss':
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.2 version: 4.3.2
@@ -237,7 +240,7 @@ importers:
version: 1.8.7 version: 1.8.7
nuxt: nuxt:
specifier: ^4.4.6 specifier: ^4.4.6
version: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) version: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
tailwindcss: tailwindcss:
specifier: ^4.3.0 specifier: ^4.3.0
version: 4.3.2 version: 4.3.2
@@ -246,11 +249,11 @@ importers:
version: 3.5.39(typescript@6.0.3) version: 3.5.39(typescript@6.0.3)
vue-router: vue-router:
specifier: ^5.0.7 specifier: ^5.0.7
version: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) version: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
devDependencies: devDependencies:
'@vitejs/plugin-vue': '@vitejs/plugin-vue':
specifier: ^6.0.7 specifier: ^6.0.7
version: 6.0.7(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) version: 6.0.7(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
'@vue/test-utils': '@vue/test-utils':
specifier: ^2.4.11 specifier: ^2.4.11
version: 2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3)) version: 2.4.11(@vue/compiler-dom@3.5.39)(@vue/server-renderer@3.5.39(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3))
@@ -259,7 +262,7 @@ importers:
version: 20.10.6 version: 20.10.6
vitest: vitest:
specifier: ^4.1.10 specifier: ^4.1.10
version: 4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) version: 4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
packages/shared-types: {} packages/shared-types: {}
@@ -5433,7 +5436,7 @@ packages:
glob@7.2.3: glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me deprecated: Glob versions prior to v9 are no longer supported
global-directory@4.0.1: global-directory@4.0.1:
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
@@ -9086,11 +9089,11 @@ snapshots:
'@ctrl/tinycolor@4.2.0': {} '@ctrl/tinycolor@4.2.0': {}
'@devframes/hub@0.5.4(devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))': '@devframes/hub@0.5.4(devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies: dependencies:
birpc: 4.0.0 birpc: 4.0.0
devframe: 0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) devframe: 0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
nostics: 0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) nostics: 0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
pathe: 2.0.3 pathe: 2.0.3
perfect-debounce: 2.1.0 perfect-debounce: 2.1.0
tinyexec: 1.2.4 tinyexec: 1.2.4
@@ -10004,19 +10007,19 @@ snapshots:
'@nuxt/devalue@2.0.2': {} '@nuxt/devalue@2.0.2': {}
'@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))': '@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
execa: 8.0.1 execa: 8.0.1
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
'@nuxt/devtools-kit@4.0.0-alpha.3(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))': '@nuxt/devtools-kit@4.0.0-alpha.3(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
tinyexec: 1.2.4 tinyexec: 1.2.4
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
@@ -10031,9 +10034,9 @@ snapshots:
pkg-types: 2.3.1 pkg-types: 2.3.1
semver: 7.8.5 semver: 7.8.5
'@nuxt/devtools@3.2.4(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': '@nuxt/devtools@3.2.4(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
dependencies: dependencies:
'@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/devtools-wizard': 3.2.4 '@nuxt/devtools-wizard': 3.2.4
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@vue/devtools-core': 8.1.5(vue@3.5.39(typescript@6.0.3)) '@vue/devtools-core': 8.1.5(vue@3.5.39(typescript@6.0.3))
@@ -10061,9 +10064,9 @@ snapshots:
sirv: 3.0.2 sirv: 3.0.2
structured-clone-es: 2.0.0 structured-clone-es: 2.0.0
tinyglobby: 0.2.17 tinyglobby: 0.2.17
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
vite-plugin-vue-tracer: 1.4.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) vite-plugin-vue-tracer: 1.4.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
which: 6.0.1 which: 6.0.1
ws: 8.21.1 ws: 8.21.1
transitivePeerDependencies: transitivePeerDependencies:
@@ -10072,13 +10075,13 @@ snapshots:
- utf-8-validate - utf-8-validate
- vue - vue
'@nuxt/fonts@0.14.0(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))': '@nuxt/fonts@0.14.0(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies: dependencies:
'@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
consola: 3.4.2 consola: 3.4.2
defu: 6.1.7 defu: 6.1.7
fontless: 0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) fontless: 0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
h3: 1.15.11 h3: 1.15.11
magic-regexp: 0.10.0 magic-regexp: 0.10.0
ofetch: 1.5.1 ofetch: 1.5.1
@@ -10087,7 +10090,7 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
ufo: 1.6.4 ufo: 1.6.4
unifont: 0.7.4 unifont: 0.7.4
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
@@ -10120,13 +10123,13 @@ snapshots:
- vite - vite
- webpack - webpack
'@nuxt/icon@2.3.1(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': '@nuxt/icon@2.3.1(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
dependencies: dependencies:
'@iconify/collections': 1.0.710 '@iconify/collections': 1.0.710
'@iconify/types': 2.0.0 '@iconify/types': 2.0.0
'@iconify/utils': 3.1.4 '@iconify/utils': 3.1.4
'@iconify/vue': 5.0.1(vue@3.5.39(typescript@6.0.3)) '@iconify/vue': 5.0.1(vue@3.5.39(typescript@6.0.3))
'@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
consola: 3.4.2 consola: 3.4.2
local-pkg: 1.2.1 local-pkg: 1.2.1
@@ -10166,7 +10169,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
'@nuxt/nitro-server@4.4.8(dbf1f1eaf9727d07d0cd606f44130212)': '@nuxt/nitro-server@4.4.8(eypsyafbmvyycgbrmhhmgxa62y)':
dependencies: dependencies:
'@nuxt/devalue': 2.0.2 '@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
@@ -10180,11 +10183,11 @@ snapshots:
escape-string-regexp: 5.0.0 escape-string-regexp: 5.0.0
exsolve: 1.1.0 exsolve: 1.1.0
h3: 1.15.11 h3: 1.15.11
impound: 1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) impound: 1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
klona: 2.0.6 klona: 2.0.6
mocked-exports: 0.1.1 mocked-exports: 0.1.1
nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nypm: 0.6.8 nypm: 0.6.8
ohash: 2.0.11 ohash: 2.0.11
pathe: 2.0.3 pathe: 2.0.3
@@ -10261,18 +10264,18 @@ snapshots:
rc9: 3.0.1 rc9: 3.0.1
std-env: 4.2.0 std-env: 4.2.0
'@nuxt/ui@4.9.0(ae082aaf330df44d8ae6c3e7f36302ae)': '@nuxt/ui@4.9.0(fsj2dsj4xkvrfb3kiovzzvkfri)':
dependencies: dependencies:
'@floating-ui/dom': 1.8.0 '@floating-ui/dom': 1.8.0
'@iconify/vue': 5.0.1(vue@3.5.39(typescript@6.0.3)) '@iconify/vue': 5.0.1(vue@3.5.39(typescript@6.0.3))
'@nuxt/fonts': 0.14.0(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) '@nuxt/fonts': 0.14.0(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(magicast@0.5.3)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
'@nuxt/icon': 2.3.1(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@nuxt/icon': 2.3.1(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@nuxt/schema': 4.4.8 '@nuxt/schema': 4.4.8
'@nuxtjs/color-mode': 4.0.1(magicast@0.5.3) '@nuxtjs/color-mode': 4.0.1(magicast@0.5.3)
'@standard-schema/spec': 1.1.0 '@standard-schema/spec': 1.1.0
'@tailwindcss/postcss': 4.3.2 '@tailwindcss/postcss': 4.3.2
'@tailwindcss/vite': 4.3.2(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@tailwindcss/vite': 4.3.2(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@tanstack/vue-table': 8.21.3(vue@3.5.39(typescript@6.0.3)) '@tanstack/vue-table': 8.21.3(vue@3.5.39(typescript@6.0.3))
'@tanstack/vue-virtual': 3.13.32(vue@3.5.39(typescript@6.0.3)) '@tanstack/vue-virtual': 3.13.32(vue@3.5.39(typescript@6.0.3))
'@tiptap/core': 3.27.4(@tiptap/pm@3.27.4) '@tiptap/core': 3.27.4(@tiptap/pm@3.27.4)
@@ -10322,16 +10325,16 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
typescript: 6.0.3 typescript: 6.0.3
ufo: 1.6.4 ufo: 1.6.4
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-auto-import: 21.0.0(@nuxt/kit@4.4.8(magicast@0.5.3))(@vueuse/core@14.3.0(vue@3.5.39(typescript@6.0.3))) unplugin-auto-import: 21.0.0(@nuxt/kit@4.4.8(magicast@0.5.3))(@vueuse/core@14.3.0(vue@3.5.39(typescript@6.0.3)))
unplugin-vue-components: 32.1.0(@nuxt/kit@4.4.8(magicast@0.5.3))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin-vue-components: 32.1.0(@nuxt/kit@4.4.8(magicast@0.5.3))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
vaul-vue: 0.4.1(reka-ui@2.9.10(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3)) vaul-vue: 0.4.1(reka-ui@2.9.10(vue@3.5.39(typescript@6.0.3)))(vue@3.5.39(typescript@6.0.3))
vue-component-type-helpers: 3.3.7 vue-component-type-helpers: 3.3.7
optionalDependencies: optionalDependencies:
'@internationalized/date': 3.12.2 '@internationalized/date': 3.12.2
'@internationalized/number': 3.6.7 '@internationalized/number': 3.6.7
valibot: 1.4.2(typescript@6.0.3) valibot: 1.4.2(typescript@6.0.3)
vue-router: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) vue-router: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
- '@azure/cosmos' - '@azure/cosmos'
@@ -10379,7 +10382,7 @@ snapshots:
- vue - vue
- webpack - webpack
'@nuxt/vite-builder@4.4.8(5d607853deecf4b9f07fa23831360c1a)': '@nuxt/vite-builder@4.4.8(cfsckhlsqtrazrdgotc5pi4pxe)':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@rollup/plugin-replace': 6.0.3(rollup@4.62.2) '@rollup/plugin-replace': 6.0.3(rollup@4.62.2)
@@ -10397,7 +10400,7 @@ snapshots:
magic-string: 0.30.21 magic-string: 0.30.21
mlly: 1.8.2 mlly: 1.8.2
mocked-exports: 0.1.1 mocked-exports: 0.1.1
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nypm: 0.6.8 nypm: 0.6.8
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
@@ -10456,15 +10459,15 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
'@nuxtjs/robots@6.1.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': '@nuxtjs/robots@6.1.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
dependencies: dependencies:
'@fingerprintjs/botd': 2.0.0 '@fingerprintjs/botd': 2.0.0
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
consola: 3.4.2 consola: 3.4.2
defu: 6.1.7 defu: 6.1.7
h3: 1.15.11 h3: 1.15.11
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
ufo: 1.6.4 ufo: 1.6.4
@@ -10475,18 +10478,18 @@ snapshots:
- vite - vite
- vue - vue
'@nuxtjs/seo@5.3.2(3e5babf51d8d70e3fe05aad885f274e3)': '@nuxtjs/seo@5.3.2(n4en3flscg275nrtqhntxiu3a4)':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@nuxtjs/robots': 6.1.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@nuxtjs/robots': 6.1.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
'@nuxtjs/sitemap': 8.2.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@nuxtjs/sitemap': 8.2.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nuxt-link-checker: 5.1.2(@nuxt/schema@4.4.8)(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-link-checker: 5.1.2(@nuxt/schema@4.4.8)(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxt-og-image: 6.7.2(89e151f4b8087f0beaf7453f17280511) nuxt-og-image: 6.7.2(kwiomen3divzn3co4sko4a7qdi)
nuxt-schema-org: 6.2.3(@nuxt/schema@4.4.8)(@unhead/vue@2.1.15(vue@3.5.39(typescript@6.0.3)))(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(unhead@2.1.15)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-schema-org: 6.2.3(@nuxt/schema@4.4.8)(@unhead/vue@2.1.15(vue@3.5.39(typescript@6.0.3)))(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(unhead@2.1.15)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxt-seo-utils: 8.3.1(bf10eca19ab1966732a57c1535280744) nuxt-seo-utils: 8.3.1(ogxe6tnaq2q26zewwucfquxshe)
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
- '@azure/cosmos' - '@azure/cosmos'
@@ -10585,14 +10588,14 @@ snapshots:
- yup - yup
- zod - zod
'@nuxtjs/sitemap@8.2.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': '@nuxtjs/sitemap@8.2.2(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
consola: 3.4.2 consola: 3.4.2
defu: 6.1.7 defu: 6.1.7
fast-xml-parser: 5.10.0 fast-xml-parser: 5.10.0
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
ofetch: 1.5.1 ofetch: 1.5.1
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
@@ -11445,12 +11448,12 @@ snapshots:
postcss: 8.5.16 postcss: 8.5.16
tailwindcss: 4.3.2 tailwindcss: 4.3.2
'@tailwindcss/vite@4.3.2(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))': '@tailwindcss/vite@4.3.2(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies: dependencies:
'@tailwindcss/node': 4.3.2 '@tailwindcss/node': 4.3.2
'@tailwindcss/oxide': 4.3.2 '@tailwindcss/oxide': 4.3.2
tailwindcss: 4.3.2 tailwindcss: 4.3.2
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
'@takumi-rs/core-darwin-arm64@1.8.7': '@takumi-rs/core-darwin-arm64@1.8.7':
optional: true optional: true
@@ -12076,20 +12079,20 @@ snapshots:
'@ungap/structured-clone@1.3.3': {} '@ungap/structured-clone@1.3.3': {}
'@unhead/bundler@3.1.8(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(lightningcss@1.32.0)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(unhead@2.1.15)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))': '@unhead/bundler@3.1.8(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(lightningcss@1.32.0)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(unhead@2.1.15)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies: dependencies:
'@vitejs/devtools-kit': 0.3.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) '@vitejs/devtools-kit': 0.3.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
magic-string: 0.30.21 magic-string: 0.30.21
oxc-parser: 0.139.0 oxc-parser: 0.139.0
oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.139.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.139.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
ufo: 1.6.4 ufo: 1.6.4
unhead: 2.1.15 unhead: 2.1.15
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
optionalDependencies: optionalDependencies:
esbuild: 0.28.1 esbuild: 0.28.1
lightningcss: 1.32.0 lightningcss: 1.32.0
rolldown: 1.1.5 rolldown: 1.1.5
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
webpack: 5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16) webpack: 5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
@@ -12141,17 +12144,17 @@ snapshots:
- rollup - rollup
- supports-color - supports-color
'@vitejs/devtools-kit@0.3.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))': '@vitejs/devtools-kit@0.3.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))':
dependencies: dependencies:
'@devframes/hub': 0.5.4(devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) '@devframes/hub': 0.5.4(devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
birpc: 4.0.0 birpc: 4.0.0
devframe: 0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) devframe: 0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
mlly: 1.8.2 mlly: 1.8.2
nostics: 1.1.4 nostics: 1.1.4
pathe: 2.0.3 pathe: 2.0.3
perfect-debounce: 2.1.0 perfect-debounce: 2.1.0
tinyexec: 1.2.4 tinyexec: 1.2.4
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
- '@modelcontextprotocol/sdk' - '@modelcontextprotocol/sdk'
@@ -12200,6 +12203,14 @@ snapshots:
chai: 6.2.2 chai: 6.2.2
tinyrainbow: 3.1.0 tinyrainbow: 3.1.0
'@vitest/mocker@4.1.10(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies:
'@vitest/spy': 4.1.10
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
'@vitest/mocker@4.1.10(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))': '@vitest/mocker@4.1.10(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))':
dependencies: dependencies:
'@vitest/spy': 4.1.10 '@vitest/spy': 4.1.10
@@ -12431,13 +12442,13 @@ snapshots:
'@vueuse/metadata@14.3.0': {} '@vueuse/metadata@14.3.0': {}
'@vueuse/nuxt@14.3.0(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))': '@vueuse/nuxt@14.3.0(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))':
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3)) '@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3))
'@vueuse/metadata': 14.3.0 '@vueuse/metadata': 14.3.0
local-pkg: 1.2.1 local-pkg: 1.2.1
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
@@ -13391,14 +13402,14 @@ snapshots:
devalue@5.8.1: {} devalue@5.8.1: {}
devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): devframe@0.5.4(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
'@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3)) '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3))
birpc: 4.0.0 birpc: 4.0.0
cac: 7.0.0 cac: 7.0.0
h3: 2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22)) h3: 2.0.1-rc.22(crossws@0.4.10(srvx@0.11.22))
mrmime: 2.0.1 mrmime: 2.0.1
nostics: 0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) nostics: 0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
pathe: 2.0.3 pathe: 2.0.3
valibot: 1.4.2(typescript@6.0.3) valibot: 1.4.2(typescript@6.0.3)
ws: 8.21.1 ws: 8.21.1
@@ -13992,7 +14003,7 @@ snapshots:
dependencies: dependencies:
tiny-inflate: 1.0.3 tiny-inflate: 1.0.3
fontless@0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)): fontless@0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies: dependencies:
consola: 3.4.2 consola: 3.4.2
css-tree: 3.2.1 css-tree: 3.2.1
@@ -14008,7 +14019,7 @@ snapshots:
unifont: 0.7.4 unifont: 0.7.4
unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
optionalDependencies: optionalDependencies:
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@azure/app-configuration' - '@azure/app-configuration'
- '@azure/cosmos' - '@azure/cosmos'
@@ -14369,12 +14380,12 @@ snapshots:
import-meta-resolve@4.2.0: {} import-meta-resolve@4.2.0: {}
impound@1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): impound@1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
'@jridgewell/trace-mapping': 0.3.31 '@jridgewell/trace-mapping': 0.3.31
es-module-lexer: 2.3.1 es-module-lexer: 2.3.1
pathe: 2.0.3 pathe: 2.0.3
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
@@ -15182,12 +15193,12 @@ snapshots:
ufo: 1.6.4 ufo: 1.6.4
unplugin: 2.3.11 unplugin: 2.3.11
magic-regexp@0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): magic-regexp@0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
magic-string: 0.30.21 magic-string: 0.30.21
regexp-tree: 0.1.27 regexp-tree: 0.1.27
type-level-regexp: 0.1.17 type-level-regexp: 0.1.17
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
- '@rspack/core' - '@rspack/core'
@@ -15427,7 +15438,7 @@ snapshots:
neo-async@2.6.2: {} neo-async@2.6.2: {}
nitropack@2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): nitropack@2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
'@cloudflare/kv-asset-handler': 0.4.2 '@cloudflare/kv-asset-handler': 0.4.2
'@rollup/plugin-alias': 6.0.0(rollup@4.62.2) '@rollup/plugin-alias': 6.0.0(rollup@4.62.2)
@@ -15492,7 +15503,7 @@ snapshots:
uncrypto: 0.1.3 uncrypto: 0.1.3
unctx: 2.5.0 unctx: 2.5.0
unenv: 2.0.0-rc.24 unenv: 2.0.0-rc.24
unimport: 6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unimport: 6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
untyped: 2.0.0 untyped: 2.0.0
@@ -15580,11 +15591,11 @@ snapshots:
normalize-wheel-es@1.2.0: {} normalize-wheel-es@1.2.0: {}
nostics@0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): nostics@0.2.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
magic-string: 0.30.21 magic-string: 0.30.21
oxc-parser: 0.132.0 oxc-parser: 0.132.0
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
- '@rspack/core' - '@rspack/core'
@@ -15622,7 +15633,7 @@ snapshots:
dependencies: dependencies:
boolbase: 1.0.0 boolbase: 1.0.0
nuxt-link-checker@5.1.2(@nuxt/schema@4.4.8)(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)): nuxt-link-checker@5.1.2(@nuxt/schema@4.4.8)(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)):
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3)) '@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3))
@@ -15631,9 +15642,9 @@ snapshots:
fuse.js: 7.5.0 fuse.js: 7.5.0
h3: 1.15.11 h3: 1.15.11
magic-string: 0.30.21 magic-string: 0.30.21
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
ofetch: 1.5.1 ofetch: 1.5.1
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
@@ -15668,7 +15679,7 @@ snapshots:
- vue - vue
- zod - zod
nuxt-og-image@6.7.2(89e151f4b8087f0beaf7453f17280511): nuxt-og-image@6.7.2(kwiomen3divzn3co4sko4a7qdi):
dependencies: dependencies:
'@clack/prompts': 1.7.0 '@clack/prompts': 1.7.0
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
@@ -15686,13 +15697,13 @@ snapshots:
magic-string: 0.30.21 magic-string: 0.30.21
magicast: 0.5.3 magicast: 0.5.3
mocked-exports: 0.1.1 mocked-exports: 0.1.1
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
nypm: 0.6.8 nypm: 0.6.8
ofetch: 1.5.1 ofetch: 1.5.1
ohash: 2.0.11 ohash: 2.0.11
oxc-parser: 0.138.0 oxc-parser: 0.138.0
oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.138.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.138.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
radix3: 1.1.2 radix3: 1.1.2
@@ -15702,12 +15713,12 @@ snapshots:
tinyglobby: 0.2.17 tinyglobby: 0.2.17
ufo: 1.6.4 ufo: 1.6.4
ultrahtml: 1.7.0 ultrahtml: 1.7.0
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1)
optionalDependencies: optionalDependencies:
'@takumi-rs/core': 1.8.7 '@takumi-rs/core': 1.8.7
fontless: 0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) fontless: 0.2.1(db0@0.3.4)(ioredis@5.11.1)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.5)(srvx@0.11.22)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
tailwindcss: 4.3.2 tailwindcss: 4.3.2
unifont: 0.7.4 unifont: 0.7.4
transitivePeerDependencies: transitivePeerDependencies:
@@ -15725,12 +15736,12 @@ snapshots:
- vue - vue
- webpack - webpack
nuxt-schema-org@6.2.3(@nuxt/schema@4.4.8)(@unhead/vue@2.1.15(vue@3.5.39(typescript@6.0.3)))(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(unhead@2.1.15)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)): nuxt-schema-org@6.2.3(@nuxt/schema@4.4.8)(@unhead/vue@2.1.15(vue@3.5.39(typescript@6.0.3)))(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(unhead@2.1.15)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)):
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
defu: 6.1.7 defu: 6.1.7
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
pkg-types: 2.3.1 pkg-types: 2.3.1
ufo: 1.6.4 ufo: 1.6.4
optionalDependencies: optionalDependencies:
@@ -15743,20 +15754,20 @@ snapshots:
- vite - vite
- vue - vue
nuxt-seo-utils@8.3.1(bf10eca19ab1966732a57c1535280744): nuxt-seo-utils@8.3.1(ogxe6tnaq2q26zewwucfquxshe):
dependencies: dependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@unhead/bundler': 3.1.8(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(lightningcss@1.32.0)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(unhead@2.1.15)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) '@unhead/bundler': 3.1.8(crossws@0.4.10(srvx@0.11.22))(esbuild@0.28.1)(lightningcss@1.32.0)(rolldown@1.1.5)(rollup@4.62.2)(typescript@6.0.3)(unhead@2.1.15)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
citty: 0.2.2 citty: 0.2.2
consola: 3.4.2 consola: 3.4.2
defu: 6.1.7 defu: 6.1.7
escape-string-regexp: 5.0.0 escape-string-regexp: 5.0.0
exsolve: 1.1.0 exsolve: 1.1.0
image-size: 2.0.2 image-size: 2.0.2
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-layer-devtools: 5.3.2(9f773a09097096979c920ff2c16f3a25) nuxtseo-layer-devtools: 5.3.2(il5rvok4mzqrxbdzkj3it4zp6m)
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
scule: 1.3.0 scule: 1.3.0
@@ -15859,13 +15870,13 @@ snapshots:
- magicast - magicast
- vue - vue
nuxt-site-config@4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)): nuxt-site-config@4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)):
dependencies: dependencies:
'@nuxt/devalue': 2.0.2 '@nuxt/devalue': 2.0.2
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
h3: 1.15.11 h3: 1.15.11
nuxt-site-config-kit: 4.1.1(magicast@0.5.3)(vue@3.5.39(typescript@6.0.3)) nuxt-site-config-kit: 4.1.1(magicast@0.5.3)(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
pathe: 2.0.3 pathe: 2.0.3
pkg-types: 2.3.1 pkg-types: 2.3.1
site-config-stack: 4.1.1(vue@3.5.39(typescript@6.0.3)) site-config-stack: 4.1.1(vue@3.5.39(typescript@6.0.3))
@@ -15878,16 +15889,16 @@ snapshots:
- vue - vue
- zod - zod
nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0): nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0):
dependencies: dependencies:
'@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@6.0.3) '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@6.0.3)
'@nuxt/cli': 3.37.0(@nuxt/schema@4.4.8)(cac@6.7.14)(magicast@0.5.3) '@nuxt/cli': 3.37.0(@nuxt/schema@4.4.8)(cac@6.7.14)(magicast@0.5.3)
'@nuxt/devtools': 3.2.4(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@nuxt/devtools': 3.2.4(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@nuxt/nitro-server': 4.4.8(dbf1f1eaf9727d07d0cd606f44130212) '@nuxt/nitro-server': 4.4.8(eypsyafbmvyycgbrmhhmgxa62y)
'@nuxt/schema': 4.4.8 '@nuxt/schema': 4.4.8
'@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.8(magicast@0.5.3)) '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.8(magicast@0.5.3))
'@nuxt/vite-builder': 4.4.8(5d607853deecf4b9f07fa23831360c1a) '@nuxt/vite-builder': 4.4.8(cfsckhlsqtrazrdgotc5pi4pxe)
'@unhead/vue': 2.1.15(vue@3.5.39(typescript@6.0.3)) '@unhead/vue': 2.1.15(vue@3.5.39(typescript@6.0.3))
'@vue/shared': 3.5.39 '@vue/shared': 3.5.39
chokidar: 5.0.0 chokidar: 5.0.0
@@ -15901,7 +15912,7 @@ snapshots:
exsolve: 1.1.0 exsolve: 1.1.0
hookable: 6.1.1 hookable: 6.1.1
ignore: 7.0.6 ignore: 7.0.6
impound: 1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) impound: 1.1.5(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
jiti: 2.7.0 jiti: 2.7.0
klona: 2.0.6 klona: 2.0.6
knitwork: 1.3.0 knitwork: 1.3.0
@@ -15915,7 +15926,7 @@ snapshots:
oxc-minify: 0.133.0 oxc-minify: 0.133.0
oxc-parser: 0.133.0 oxc-parser: 0.133.0
oxc-transform: 0.133.0 oxc-transform: 0.133.0
oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) oxc-walker: 1.0.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
pathe: 2.0.3 pathe: 2.0.3
perfect-debounce: 2.1.0 perfect-debounce: 2.1.0
picomatch: 4.0.5 picomatch: 4.0.5
@@ -15930,12 +15941,12 @@ snapshots:
uncrypto: 0.1.3 uncrypto: 0.1.3
unctx: 2.5.0 unctx: 2.5.0
unhead: 2.1.15 unhead: 2.1.15
unimport: 6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unimport: 6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unrouting: 0.1.7 unrouting: 0.1.7
untyped: 2.0.0 untyped: 2.0.0
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
vue-router: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) vue-router: 5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
optionalDependencies: optionalDependencies:
'@parcel/watcher': 2.5.6 '@parcel/watcher': 2.5.6
'@types/node': 24.13.3 '@types/node': 24.13.3
@@ -16013,17 +16024,17 @@ snapshots:
- xml2js - xml2js
- yaml - yaml
nuxtseo-layer-devtools@5.3.2(9f773a09097096979c920ff2c16f3a25): nuxtseo-layer-devtools@5.3.2(il5rvok4mzqrxbdzkj3it4zp6m):
dependencies: dependencies:
'@iconify-json/carbon': 1.2.24 '@iconify-json/carbon': 1.2.24
'@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@nuxt/ui': 4.9.0(ae082aaf330df44d8ae6c3e7f36302ae) '@nuxt/ui': 4.9.0(fsj2dsj4xkvrfb3kiovzzvkfri)
'@shikijs/langs': 4.3.1 '@shikijs/langs': 4.3.1
'@shikijs/themes': 4.3.1 '@shikijs/themes': 4.3.1
'@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3)) '@vueuse/core': 14.3.0(vue@3.5.39(typescript@6.0.3))
'@vueuse/nuxt': 14.3.0(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) '@vueuse/nuxt': 14.3.0(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
nuxtseo-shared: 5.3.2(0ff275a33ce92f3eb81e460b16ac0f97) nuxtseo-shared: 5.3.2(o3eoynamgeokw3gqc6vpj7eouu)
ofetch: 1.5.1 ofetch: 1.5.1
shiki: 4.3.1 shiki: 4.3.1
tailwindcss: 4.3.2 tailwindcss: 4.3.2
@@ -16106,16 +16117,16 @@ snapshots:
- yup - yup
- zod - zod
nuxtseo-shared@5.3.2(0ff275a33ce92f3eb81e460b16ac0f97): nuxtseo-shared@5.3.2(o3eoynamgeokw3gqc6vpj7eouu):
dependencies: dependencies:
'@clack/prompts': 1.7.0 '@clack/prompts': 1.7.0
'@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) '@nuxt/devtools-kit': 4.0.0-alpha.3(magicast@0.5.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
'@nuxt/schema': 4.4.8 '@nuxt/schema': 4.4.8
birpc: 4.0.0 birpc: 4.0.0
consola: 3.4.2 consola: 3.4.2
defu: 6.1.7 defu: 6.1.7
nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0) nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0)
nypm: 0.6.8 nypm: 0.6.8
ofetch: 1.5.1 ofetch: 1.5.1
pathe: 2.0.3 pathe: 2.0.3
@@ -16126,7 +16137,7 @@ snapshots:
ufo: 1.6.4 ufo: 1.6.4
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
optionalDependencies: optionalDependencies:
nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)) nuxt-site-config: 4.1.1(@nuxt/schema@4.4.8)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.13.3)(@vue/compiler-sfc@3.5.39)(cac@6.7.14)(db0@0.3.4)(esbuild@0.28.1)(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.5)(rollup@4.62.2))(rollup@4.62.2)(sass@1.101.0)(srvx@0.11.22)(terser@5.49.0)(typescript@6.0.3)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue-tsc@3.3.7(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))(yaml@2.9.0))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))
transitivePeerDependencies: transitivePeerDependencies:
- magicast - magicast
- vite - vite
@@ -16359,9 +16370,9 @@ snapshots:
'@oxc-transform/binding-win32-ia32-msvc': 0.133.0 '@oxc-transform/binding-win32-ia32-msvc': 0.133.0
'@oxc-transform/binding-win32-x64-msvc': 0.133.0 '@oxc-transform/binding-win32-x64-msvc': 0.133.0
oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
optionalDependencies: optionalDependencies:
oxc-parser: 0.133.0 oxc-parser: 0.133.0
rolldown: 1.1.5 rolldown: 1.1.5
@@ -16375,9 +16386,9 @@ snapshots:
- vite - vite
- webpack - webpack
oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.138.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.138.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
optionalDependencies: optionalDependencies:
oxc-parser: 0.138.0 oxc-parser: 0.138.0
rolldown: 1.1.5 rolldown: 1.1.5
@@ -16391,9 +16402,9 @@ snapshots:
- vite - vite
- webpack - webpack
oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.139.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): oxc-walker@1.0.0(esbuild@0.28.1)(oxc-parser@0.139.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) magic-regexp: 0.11.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
optionalDependencies: optionalDependencies:
oxc-parser: 0.139.0 oxc-parser: 0.139.0
rolldown: 1.1.5 rolldown: 1.1.5
@@ -17776,7 +17787,7 @@ snapshots:
unplugin: 2.3.11 unplugin: 2.3.11
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
unimport@6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): unimport@6.3.0(esbuild@0.28.1)(oxc-parser@0.133.0)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
acorn: 8.17.0 acorn: 8.17.0
escape-string-regexp: 5.0.0 escape-string-regexp: 5.0.0
@@ -17790,7 +17801,7 @@ snapshots:
scule: 1.3.0 scule: 1.3.0
strip-literal: 3.1.0 strip-literal: 3.1.0
tinyglobby: 0.2.17 tinyglobby: 0.2.17
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
optionalDependencies: optionalDependencies:
oxc-parser: 0.133.0 oxc-parser: 0.133.0
@@ -17849,7 +17860,7 @@ snapshots:
pathe: 2.0.3 pathe: 2.0.3
picomatch: 4.0.5 picomatch: 4.0.5
unplugin-vue-components@32.1.0(@nuxt/kit@4.4.8(magicast@0.5.3))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): unplugin-vue-components@32.1.0(@nuxt/kit@4.4.8(magicast@0.5.3))(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
chokidar: 5.0.0 chokidar: 5.0.0
local-pkg: 1.2.1 local-pkg: 1.2.1
@@ -17858,7 +17869,7 @@ snapshots:
obug: 2.1.3 obug: 2.1.3
picomatch: 4.0.5 picomatch: 4.0.5
tinyglobby: 0.2.17 tinyglobby: 0.2.17
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
optionalDependencies: optionalDependencies:
@@ -17906,7 +17917,7 @@ snapshots:
picomatch: 4.0.5 picomatch: 4.0.5
webpack-virtual-modules: 0.6.2 webpack-virtual-modules: 0.6.2
unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
'@jridgewell/remapping': 2.3.5 '@jridgewell/remapping': 2.3.5
picomatch: 4.0.5 picomatch: 4.0.5
@@ -17915,7 +17926,7 @@ snapshots:
esbuild: 0.28.1 esbuild: 0.28.1
rolldown: 1.1.5 rolldown: 1.1.5
rollup: 4.62.2 rollup: 4.62.2
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
webpack: 5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16) webpack: 5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)
unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)): unplugin@3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)):
@@ -18030,15 +18041,15 @@ snapshots:
'@types/unist': 3.0.3 '@types/unist': 3.0.3
vfile-message: 4.0.3 vfile-message: 4.0.3
vite-dev-rpc@2.0.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)): vite-dev-rpc@2.0.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies: dependencies:
birpc: 4.0.0 birpc: 4.0.0
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vite-hot-client: 2.2.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) vite-hot-client: 2.2.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
vite-hot-client@2.2.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)): vite-hot-client@2.2.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies: dependencies:
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vite-node@5.3.0(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0): vite-node@5.3.0(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0):
dependencies: dependencies:
@@ -18075,7 +18086,7 @@ snapshots:
typescript: 6.0.3 typescript: 6.0.3
vue-tsc: 3.3.7(typescript@6.0.3) vue-tsc: 3.3.7(typescript@6.0.3)
vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)): vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies: dependencies:
ansis: 4.3.1 ansis: 4.3.1
error-stack-parser-es: 1.0.5 error-stack-parser-es: 1.0.5
@@ -18085,19 +18096,19 @@ snapshots:
perfect-debounce: 2.1.0 perfect-debounce: 2.1.0
sirv: 3.0.2 sirv: 3.0.2
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vite-dev-rpc: 2.0.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)) vite-dev-rpc: 2.0.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
optionalDependencies: optionalDependencies:
'@nuxt/kit': 4.4.8(magicast@0.5.3) '@nuxt/kit': 4.4.8(magicast@0.5.3)
vite-plugin-vue-tracer@1.4.0(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)): vite-plugin-vue-tracer@1.4.0(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3)):
dependencies: dependencies:
estree-walker: 3.0.3 estree-walker: 3.0.3
exsolve: 1.1.0 exsolve: 1.1.0
magic-string: 0.30.21 magic-string: 0.30.21
pathe: 2.0.3 pathe: 2.0.3
source-map-js: 1.2.1 source-map-js: 1.2.1
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0): vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0):
@@ -18133,6 +18144,34 @@ snapshots:
terser: 5.49.0 terser: 5.49.0
yaml: 2.9.0 yaml: 2.9.0
vitest@4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies:
'@vitest/expect': 4.1.10
'@vitest/mocker': 4.1.10(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))
'@vitest/pretty-format': 4.1.10
'@vitest/runner': 4.1.10
'@vitest/snapshot': 4.1.10
'@vitest/spy': 4.1.10
'@vitest/utils': 4.1.10
es-module-lexer: 2.3.1
expect-type: 1.4.0
magic-string: 0.30.21
obug: 2.1.3
pathe: 2.0.3
picomatch: 4.0.5
std-env: 4.2.0
tinybench: 2.9.0
tinyexec: 1.2.4
tinyglobby: 0.2.17
tinyrainbow: 3.1.0
vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 24.13.3
happy-dom: 20.10.6
transitivePeerDependencies:
- msw
vitest@4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)): vitest@4.1.10(@types/node@24.13.3)(happy-dom@20.10.6)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)):
dependencies: dependencies:
'@vitest/expect': 4.1.10 '@vitest/expect': 4.1.10
@@ -18180,7 +18219,7 @@ snapshots:
'@vue/devtools-api': 6.6.4 '@vue/devtools-api': 6.6.4
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
vue-router@5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)): vue-router@5.2.0(@vue/compiler-sfc@3.5.39)(esbuild@0.28.1)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)))(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(vue@3.5.39(typescript@6.0.3))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)):
dependencies: dependencies:
'@babel/generator': 8.0.0 '@babel/generator': 8.0.0
'@vue-macros/common': 3.1.3(vue@3.5.39(typescript@6.0.3)) '@vue-macros/common': 3.1.3(vue@3.5.39(typescript@6.0.3))
@@ -18197,14 +18236,14 @@ snapshots:
picomatch: 4.0.5 picomatch: 4.0.5
scule: 1.3.0 scule: 1.3.0
tinyglobby: 0.2.17 tinyglobby: 0.2.17
unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16)) unplugin: 3.3.0(esbuild@0.28.1)(rolldown@1.1.5)(rollup@4.62.2)(vite@7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0))(webpack@5.97.1(esbuild@0.28.1)(lightningcss@1.32.0)(postcss@8.5.16))
unplugin-utils: 0.3.2 unplugin-utils: 0.3.2
vue: 3.5.39(typescript@6.0.3) vue: 3.5.39(typescript@6.0.3)
yaml: 2.9.0 yaml: 2.9.0
optionalDependencies: optionalDependencies:
'@vue/compiler-sfc': 3.5.39 '@vue/compiler-sfc': 3.5.39
pinia: 3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3)) pinia: 3.0.4(typescript@6.0.3)(vue@3.5.39(typescript@6.0.3))
vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0) vite: 7.3.6(@types/node@24.13.3)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.101.0)(terser@5.49.0)(yaml@2.9.0)
transitivePeerDependencies: transitivePeerDependencies:
- '@farmfe/core' - '@farmfe/core'
- '@rspack/core' - '@rspack/core'
+3 -1
View File
@@ -28,4 +28,6 @@ Default to all products and countries with no logistics or craft selection. Neve
Countries, categories, tags, tag groups, and icons are backend-owned. Preserve relative backend asset paths in the database and resolve them through `runtimeConfig.public.backendUrl` in `useProductCenter.ts`. Do not add country-name flag fallbacks or infer tag groups from numeric sort positions. Versioned Figma icon assets live under `apps/api/public/product-center/`; run `pnpm --filter @inkreach/api configure:product-center-icons` to apply them to matching existing records. Countries, categories, tags, tag groups, and icons are backend-owned. Preserve relative backend asset paths in the database and resolve them through `runtimeConfig.public.backendUrl` in `useProductCenter.ts`. Do not add country-name flag fallbacks or infer tag groups from numeric sort positions. Versioned Figma icon assets live under `apps/api/public/product-center/`; run `pnpm --filter @inkreach/api configure:product-center-icons` to apply them to matching existing records.
The public goods API exposes `originGood.sdsGoodId` as the product `id`. Use that value for InkPOD detail links; never build an InkPOD URL from the local `goods.good_id` primary key. The public goods API exposes `originGood.sdsGoodId` as the product `id`. Use that value for InkPOD detail links; never build an InkPOD URL from the local `goods.good_id` primary key.
A good may merge multiple origin goods (same-name variants across factories/warehouses): any associated origin's `sdsGoodId` resolves to the same detail via `/public/goods/:id`, and the detail's variants/media are the union of the primary and secondary origins. Price and detail content come from the primary origin.