Author SHA1 Message Date
yeuimu bbd925070a 部署h5 2026-08-28 15:23:54 +08:00
yeuimu 270e516f94 fix(public): media images are objects ({id,url,sortOrder}) - handle both shapes in mergeMedia 2026-08-28 11:22:53 +08:00
yeuimu d826c5bdd4 feat(public): merge secondary media gallery images with URL dedup 2026-08-28 11:19:27 +08:00
yeuimu 457792b729 feat(public): dedupe merged variants and merge specs/options from secondary origins 2026-08-28 11:05:34 +08:00
yeuimu 8233dda07a fix(admin): initial goods load also uses pageSize 1000 2026-08-28 09:49:40 +08:00
yeuimu cbdb507be9 fix(api): allow pageSize up to 1000 so admin goods tree can load full list 2026-08-28 09:36:15 +08:00
yeuimu b3bd10de06 fix(admin): load all goods in left tree (pageSize 1000) so low-priority new goods are searchable 2026-08-28 09:31:02 +08:00
yeuimu e7582bbd6e fix(deploy): add .dockerignore so host node_modules does not break image build 2026-08-27 19:13:57 +08:00
yeuimu 4c50189538 merge: multi-origin-good-merge-zll into develop 2026-08-27 18:51:28 +08:00
yeuimu 4bebb9e4b2 docs: document merged origin goods feature 2026-08-27 18:49:17 +08:00
yeuimu ac29a0c7bf feat(admin): show merged count badge on good nodes 2026-08-27 18:45:51 +08:00
yeuimu ac3a39f40d feat(admin): manage merged origin goods in good edit modal 2026-08-27 18:43:30 +08:00
yeuimu 491e74b905 feat(admin): add origin name grouping utils with vitest coverage 2026-08-27 18:40:22 +08:00
yeuimu 0aa6cbf1d9 feat(admin): suggest and merge sibling origin goods in config modal 2026-08-27 18:40:13 +08:00
yeuimu 697b82a64b fix(admin): resolve pre-existing template type errors blocking build 2026-08-27 18:40:08 +08:00
yeuimu f06dfffbda feat(public): resolve goods by secondary sds id and merge variants 2026-08-27 18:20:25 +08:00
yeuimu d9ecd04747 feat(origin-goods): include merged secondary references in tree stats 2026-08-27 18:18:29 +08:00
yeuimu 9c279ae393 feat(goods): support merged secondary origin goods in create/update/detail 2026-08-27 18:15:56 +08:00
yeuimu 848eed0b6f feat(db): add good_origin_goods junction table for merged origin goods 2026-08-27 18:09:00 +08:00
yeuimu 043d2463a6 docs(plan): add multi origin good merge design and implementation plan 2026-08-27 17:08:06 +08:00
75 changed files with 4045 additions and 1478 deletions
+26
View File
@@ -0,0 +1,26 @@
# 依赖与包管理缓存(容器内会重新安装,禁止从宿主机带入)
node_modules
**/node_modules
.pnpm-store
.pnpm-debug.log
# 构建产物
dist
**/dist
.turbo
# Git 与本地文件
.git
.gitignore
.dockerignore
# 测试与文档
coverage
**/*.spec.ts
**/*.test.ts
# 运行数据与本地产物
data-cleaning
deploy/backups
apps/api/uploads
*.log
+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;
} }
+1 -1
View File
@@ -21,7 +21,7 @@ export class QueryGoodDto {
@Type(() => Number) @Type(() => Number)
@IsInt() @IsInt()
@Min(1) @Min(1)
@Max(200) @Max(1000)
pageSize: number = 20; pageSize: number = 20;
@ApiProperty({ required: false }) @ApiProperty({ required: false })
@@ -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;
} }
} }
+279 -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,129 @@ 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);
}
});
it('dedupes variants by color+size (primary wins) and merges specs/options', async () => {
const stamp2 = `${stamp}-merge2`;
const primaryOg = await prisma.originGood.create({
data: { sdsGoodId: `pub-pri-${stamp2}`, goodName: `Pub Pri ${stamp2}` },
});
const secondaryOg = await prisma.originGood.create({
data: { sdsGoodId: `pub-sec-${stamp2}`, goodName: `Pub Sec ${stamp2}` },
});
// Primary: Black|S and Black|M, size chart S/M, package S, options S/M.
await prisma.originGoodVariant.createMany({
data: [
{ originGoodId: primaryOg.id, sdsVariantId: `v1-${stamp2}`, sku: `SK1-${stamp2}`, colorName: 'Black', sizeName: 'S', price: 16 },
{ originGoodId: primaryOg.id, sdsVariantId: `v2-${stamp2}`, sku: `SK2-${stamp2}`, colorName: 'black', sizeName: 'M', price: 16 },
],
});
await prisma.originGoodDetail.create({
data: {
originGoodId: primaryOg.id,
sizeChart: { rows: [{ sizeName: 'S', measurements: [{ key: 'chest', cm: '94' }] }, { sizeName: 'M', measurements: [{ key: 'chest', cm: '100' }] }] },
packageSpecs: { rows: [{ sizeName: 'S' }] },
options: { sizes: [{ name: 'S' }, { name: 'M' }] },
media: { images: [{ id: 'i1', url: 'http://img/pri-a', sortOrder: 0 }, { id: 'i2', url: 'http://img/pri-b', sortOrder: 1 }], primaryImageUrl: 'http://img/pri-a' },
},
});
// Secondary: duplicate Black|S with a DIFFERENT price (must be dropped,
// primary wins), plus a unique color Kelly|S; specs add XXXL rows.
await prisma.originGoodVariant.createMany({
data: [
{ originGoodId: secondaryOg.id, sdsVariantId: `v3-${stamp2}`, sku: `SK3-${stamp2}`, colorName: 'Black', sizeName: 'S', price: 20 },
{ originGoodId: secondaryOg.id, sdsVariantId: `v4-${stamp2}`, sku: `SK4-${stamp2}`, colorName: 'Kelly', sizeName: 'S', price: 22 },
],
});
await prisma.originGoodDetail.create({
data: {
originGoodId: secondaryOg.id,
sizeChart: { rows: [{ sizeName: 'XXXL', measurements: [{ key: 'chest', cm: '120' }] }] },
packageSpecs: { rows: [{ sizeName: 'M' }, { sizeName: 'XXXL' }] },
options: { sizes: [{ name: 'XXXL' }] },
media: { images: [{ id: 'i1', url: 'http://img/pri-a', sortOrder: 0 }, { id: 'i9', url: 'http://img/sec-x', sortOrder: 0 }], primaryImageUrl: 'http://img/pri-a' },
},
});
const mergedGood = await prisma.good.create({
data: {
goodName: `Pub Merged ${stamp2}`,
originGoodId: primaryOg.id,
countryId,
categoryId,
},
});
await prisma.goodOriginGood.create({
data: { goodId: mergedGood.id, originGoodId: secondaryOg.id },
});
try {
const detail = await service.getGood(`pub-pri-${stamp2}`);
// Variants: 3 unique color+size combos (case-insensitive); duplicate
// Black|S from the secondary deduped.
expect(
detail.variants.map((v) => `${v.colorName}/${v.sizeName}`.toLowerCase()).sort(),
).toEqual(['black/m', 'black/s', 'kelly/s']);
const blackS = detail.variants.find((v) => v.colorName === 'Black' && v.sizeName === 'S');
expect(blackS!.price).toBe('16'); // primary price wins over secondary 20
// Size chart: S/M from primary, XXXL appended from secondary.
const chartSizes = (detail.sizeChart as any).rows.map((r: any) => r.sizeName).sort();
expect(chartSizes).toEqual(['M', 'S', 'XXXL']);
// Package specs: S from primary, M/XXXL appended from secondary.
const pkgSizes = (detail.packageSpecs as any).rows.map((r: any) => r.sizeName).sort();
expect(pkgSizes).toEqual(['M', 'S', 'XXXL']);
// Options: sizes unioned S/M + XXXL.
const optSizes = (detail.options as any).sizes.map((s: any) => s.name).sort();
expect(optSizes).toEqual(['M', 'S', 'XXXL']);
// Media gallery: primary images first, secondary-only URL appended,
// duplicate URL (pri-a) kept once. Entries keep their object shape.
const media = detail.media as any;
expect(media.images.map((i: any) => i.url)).toEqual([
'http://img/pri-a',
'http://img/pri-b',
'http://img/sec-x',
]);
expect(media.primaryImageUrl).toBe('http://img/pri-a');
} finally {
await prisma.goodOriginGood.deleteMany({ where: { goodId: mergedGood.id } });
await prisma.good.delete({ where: { id: mergedGood.id } });
await prisma.originGoodVariant.deleteMany({ where: { originGoodId: { in: [primaryOg.id, secondaryOg.id] } } });
await prisma.originGoodDetail.deleteMany({ where: { originGoodId: { in: [primaryOg.id, secondaryOg.id] } } });
await prisma.originGood.delete({ where: { id: primaryOg.id } });
await prisma.originGood.delete({ where: { id: secondaryOg.id } }).catch(() => undefined);
}
});
});
}); });
+162 -8
View File
@@ -34,6 +34,17 @@ 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: {
detail: true,
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 +209,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 +298,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 +356,17 @@ 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 goods: variants deduped by
// color+size (primary wins), detail specs filled from secondaries
// for sizes/options the primary does not have.
const secondaryDetails = good.mergedOriginGoods
.map((m) => m.originGood.detail)
.filter((d): d is NonNullable<typeof d> => Boolean(d));
const secondaryVariantLists = good.mergedOriginGoods.map((m) => m.originGood.variants);
const allVariants = this.dedupeVariants([
...good.originGood.variants,
...secondaryVariantLists.flat(),
]);
return { return {
...base, ...base,
productCode: detail?.productCode ?? null, productCode: detail?.productCode ?? null,
@@ -353,12 +385,12 @@ export class PublicService {
designArea: detail?.designArea ?? null, designArea: detail?.designArea ?? null,
pictureRequest: detail?.pictureRequest ?? null, pictureRequest: detail?.pictureRequest ?? null,
}, },
media: (detail?.media as Record<string, unknown> | null) ?? null, media: this.mergeMedia(detail, secondaryDetails),
mediaByColor: this.groupImagesByColor(good.originGood.variants), mediaByColor: this.groupImagesByColor(allVariants),
options: (detail?.options as Record<string, unknown> | null) ?? null, options: this.mergeOptions(detail, secondaryDetails),
sizeChart: (detail?.sizeChart as Record<string, unknown> | null) ?? null, sizeChart: this.mergeRowsByKey(detail, secondaryDetails, 'sizeChart'),
packageSpecs: (detail?.packageSpecs as Record<string, unknown> | null) ?? null, packageSpecs: this.mergeRowsByKey(detail, secondaryDetails, 'packageSpecs'),
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,
@@ -380,6 +412,128 @@ export class PublicService {
}; };
} }
/** Norm key for variant/dedupe matching: case-insensitive, trimmed. */
private static normName(value: string | null | undefined): string {
return (value ?? '').trim().toLowerCase();
}
/** Dedupe variants by color+size (case-insensitive); first (primary) wins. */
private dedupeVariants(variants: PublicGoodRow['originGood']['variants']) {
const seen = new Set<string>();
const result: PublicGoodRow['originGood']['variants'] = [];
for (const variant of variants) {
const key = `${PublicService.normName(variant.colorName)}|${PublicService.normName(variant.sizeName)}`;
if (seen.has(key)) continue;
seen.add(key);
result.push(variant);
}
return result;
}
/**
* Merge a `{ rows: [...] }` spec (sizeChart / packageSpecs) across primary
* and secondary details: primary rows win, rows for sizes the primary
* lacks are appended from secondaries in order.
*/
private mergeRowsByKey(
primary: PublicGoodRow['originGood']['detail'],
secondaries: NonNullable<PublicGoodRow['originGood']['detail']>[],
field: 'sizeChart' | 'packageSpecs',
): Record<string, unknown> | null {
const primaryRows = (primary?.[field] as { rows?: Array<Record<string, unknown>> } | null)?.rows;
if (!Array.isArray(primaryRows) && secondaries.length === 0) return null;
const rows: Array<Record<string, unknown>> = Array.isArray(primaryRows) ? [...primaryRows] : [];
const seen = new Set(rows.map((r) => PublicService.normName(String(r?.sizeName ?? ''))));
for (const sec of secondaries) {
const secRows = (sec[field] as { rows?: Array<Record<string, unknown>> } | null)?.rows;
if (!Array.isArray(secRows)) continue;
for (const row of secRows) {
const key = PublicService.normName(String(row?.sizeName ?? ''));
if (seen.has(key)) continue;
seen.add(key);
rows.push(row);
}
}
if (rows.length === 0) return null;
return { ...(primary?.[field] as Record<string, unknown> | null ?? {}), rows };
}
/**
* Merge options across details: sizes/colors are unioned by name,
* primary entries win and secondary-only ones are appended.
*/
private mergeOptions(
primary: PublicGoodRow['originGood']['detail'],
secondaries: NonNullable<PublicGoodRow['originGood']['detail']>[],
): Record<string, unknown> | null {
const primaryOptions = primary?.options as
| { sizes?: Array<Record<string, unknown>>; colors?: Array<Record<string, unknown>> }
| null;
if (!primaryOptions && secondaries.length === 0) return null;
const merged: Record<string, unknown> = { ...(primaryOptions ?? {}) };
for (const listKey of ['sizes', 'colors'] as const) {
const base = Array.isArray(primaryOptions?.[listKey])
? [...(primaryOptions![listKey] as Array<Record<string, unknown>>)]
: null;
if (!base && secondaries.length === 0) continue;
const rows = base ?? [];
const seen = new Set(rows.map((r) => PublicService.normName(String(r?.name ?? ''))));
for (const sec of secondaries) {
const secOptions = sec.options as
| { sizes?: Array<Record<string, unknown>>; colors?: Array<Record<string, unknown>> }
| null;
const secRows = secOptions?.[listKey];
if (!Array.isArray(secRows)) continue;
for (const row of secRows) {
const key = PublicService.normName(String(row?.name ?? ''));
if (seen.has(key)) continue;
seen.add(key);
rows.push(row);
}
}
merged[listKey] = rows;
}
return merged;
}
/**
* Merge the gallery `media` across details: primary images first,
* secondary-only image URLs appended (URL-deduped). `primaryImageUrl`
* stays the primary's. Image entries keep their original shape
* (`{id,url,sortOrder}` objects or plain strings).
*/
private mergeMedia(
primary: PublicGoodRow['originGood']['detail'],
secondaries: NonNullable<PublicGoodRow['originGood']['detail']>[],
): Record<string, unknown> | null {
const imageUrl = (img: unknown): string | null => {
if (typeof img === 'string') return img;
if (img && typeof img === 'object' && typeof (img as { url?: unknown }).url === 'string') {
return (img as { url: string }).url;
}
return null;
};
const primaryMedia = (primary?.media as Record<string, unknown> | null) ?? null;
const primaryImages = Array.isArray(primaryMedia?.images)
? (primaryMedia!.images as unknown[])
: null;
if (!primaryImages && secondaries.length === 0) return null;
const images: unknown[] = primaryImages ? [...primaryImages] : [];
const seen = new Set(images.map(imageUrl).filter((u): u is string => Boolean(u)));
for (const sec of secondaries) {
const secMedia = sec.media as { images?: unknown } | null;
if (!Array.isArray(secMedia?.images)) continue;
for (const img of secMedia!.images as unknown[]) {
const url = imageUrl(img);
if (!url || seen.has(url)) continue;
seen.add(url);
images.push(img);
}
}
if (images.length === 0) return null;
return { ...(primaryMedia ?? {}), images };
}
private async buildTagGroupFilters( private async buildTagGroupFilters(
selectedGroups: PublicTagFilterDto[], selectedGroups: PublicTagFilterDto[],
): Promise<Prisma.GoodWhereInput[]> { ): Promise<Prisma.GoodWhereInput[]> {
+1
View File
@@ -18,4 +18,5 @@ RUN pnpm --filter @inkreach/admin exec vite build
FROM nginx:1.27-alpine FROM nginx:1.27-alpine
COPY --from=build /app/apps/admin/dist /usr/share/nginx/html/admin COPY --from=build /app/apps/admin/dist /usr/share/nginx/html/admin
COPY deploy/nginx/admin.conf /etc/nginx/conf.d/default.conf COPY deploy/nginx/admin.conf /etc/nginx/conf.d/default.conf
COPY deploy/h5 /usr/share/nginx/html/h5
EXPOSE 80 EXPOSE 80
+2
View File
@@ -0,0 +1,2 @@
<!DOCTYPE html><html lang=zh-CN><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><title>印美达POD供应链</title><script>var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write('<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' + (coverSupport ? ', viewport-fit=cover' : '') + '" />')</script><link rel=stylesheet href=/h5/static/index.149e085d.css></head><body><noscript><strong>Please enable JavaScript to continue.</strong></noscript><div id=app></div><script src=/h5/static/js/chunk-vendors.9aacc1b6.js></script><script src=/h5/static/js/index.1e01d76d.js></script></body></html>
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.1 3.19995H7.8L2 20.8H4.8L6.3 15.8H12.4L13.9 20.8H16.9L11.1 3.19995ZM7 13.6L7.7 11.2C8.3 9.29995 8.8 7.29995 9.3 5.29995H9.4C10 7.29995 10.5 9.29995 11.1 11.2L11.8 13.6H7ZM22 3.19995H19.2V20.8H22V3.19995Z" fill="#FF6800"/>
</svg>

After

Width:  |  Height:  |  Size: 338 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.75 20.7501L11.223 21.5684C11.5066 21.726 11.6484 21.8047 11.7986 21.8356C11.9315 21.863 12.0685 21.863 12.2015 21.8356C12.3516 21.8047 12.4934 21.726 12.777 21.5684L14.25 20.7501M5.25 18.2501L3.82297 17.4573C3.52346 17.2909 3.37368 17.2077 3.26463 17.0893C3.16816 16.9847 3.09515 16.8606 3.05048 16.7254C3 16.5726 3 16.4013 3 16.0586V14.5001M3 9.50009V7.94153C3 7.59889 3 7.42757 3.05048 7.27477C3.09515 7.13959 3.16816 7.01551 3.26463 6.91082C3.37368 6.79248 3.52345 6.70928 3.82297 6.54288L5.25 5.75009M9.75 3.25008L11.223 2.43177C11.5066 2.27421 11.6484 2.19543 11.7986 2.16454C11.9315 2.13721 12.0685 2.13721 12.2015 2.16454C12.3516 2.19543 12.4934 2.27421 12.777 2.43177L14.25 3.25008M18.75 5.75008L20.177 6.54288C20.4766 6.70928 20.6263 6.79248 20.7354 6.91082C20.8318 7.01551 20.9049 7.13959 20.9495 7.27477C21 7.42757 21 7.59889 21 7.94153V9.50008M21 14.5001V16.0586C21 16.4013 21 16.5726 20.9495 16.7254C20.9049 16.8606 20.8318 16.9847 20.7354 17.0893C20.6263 17.2077 20.4766 17.2909 20.177 17.4573L18.75 18.2501M9.75 10.7501L12 12.0001M12 12.0001L14.25 10.7501M12 12.0001V14.5001M3 7.00008L5.25 8.25008M18.75 8.25008L21 7.00008M12 19.5001V22.0001" stroke="#FF6800" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15 2.4578C14.053 2.16035 13.0452 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 10.2847 21.5681 8.67022 20.8071 7.25945M17 5.75H17.005M10.5001 21.8883L10.5002 19.6849C10.5002 19.5656 10.5429 19.4502 10.6205 19.3596L13.1063 16.4594C13.3106 16.2211 13.2473 15.8556 12.9748 15.6999L10.1185 14.0677C10.0409 14.0234 9.97663 13.9591 9.93234 13.8814L8.07046 10.6186C7.97356 10.4488 7.78657 10.3511 7.59183 10.3684L2.06418 10.8607M21 6C21 8.20914 19 10 17 12C15 10 13 8.20914 13 6C13 3.79086 14.7909 2 17 2C19.2091 2 21 3.79086 21 6ZM17.25 5.75C17.25 5.88807 17.1381 6 17 6C16.8619 6 16.75 5.88807 16.75 5.75C16.75 5.61193 16.8619 5.5 17 5.5C17.1381 5.5 17.25 5.61193 17.25 5.75Z" stroke="#FF6800" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 912 B

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 2V22H23V2H1ZM12.0524 3.5C12.2469 3.63134 12.3919 3.81891 12.4662 4.035C12.5764 4.31994 12.6469 4.6175 12.6757 4.92C12.7149 5.20699 12.6883 5.49867 12.5979 5.77491C12.5075 6.05115 12.3553 6.30537 12.1519 6.52C11.753 6.91856 11.2095 7.15647 10.6329 7.185H7.67333L9.96762 8.57C10.3781 8.82008 10.6952 9.18758 10.8738 9.62C11.066 10.0242 11.094 10.4822 10.9524 10.905C10.7732 11.4672 10.405 11.9575 9.90476 12.3C9.655 12.4886 9.38025 12.645 9.08762 12.765C8.81252 12.8861 8.51318 12.9488 8.21024 12.9488C7.9073 12.9488 7.60796 12.8861 7.33286 12.765C6.8121 12.5325 6.40772 12.1141 6.20667 11.6C6.17524 11.53 6.14905 11.46 6.12286 11.39L5.26905 9L4.58286 11.45C4.45612 11.8863 4.19298 12.2752 3.82857 12.565C3.49145 12.8531 3.05535 13.0132 2.60286 13.015H2.57143V3.5H12.0524ZM2.57143 14.5H2.61857C3.44255 14.4939 4.23663 14.2046 4.85524 13.685C5.0435 13.5319 5.21866 13.3647 5.37905 13.185C5.8881 13.7725 6.58643 14.1827 7.36523 14.3519C8.14403 14.521 8.95956 14.4395 9.68476 14.12C10.0927 13.9597 10.478 13.7514 10.8319 13.5C11.5989 12.9643 12.1647 12.2068 12.4452 11.34C12.693 10.5887 12.6483 9.77736 12.3195 9.055C12.2336 8.83283 12.1267 8.6186 12 8.415C12.5087 8.21816 12.9663 7.91786 13.341 7.535C13.6909 7.16659 13.9546 6.73122 14.1145 6.2579C14.2744 5.78457 14.3267 5.28416 14.2681 4.79C14.2259 4.35831 14.1274 3.93334 13.9748 3.525V3.5H21.4286V12.535C20.5882 12.5887 19.783 12.8774 19.1133 13.365C18.4318 13.8365 17.9518 14.5273 17.7619 15.31C17.7004 15.5459 17.6636 15.7871 17.6519 16.03C17.1243 15.9104 16.5759 15.9001 16.0438 16C15.5261 16.1029 15.0375 16.3105 14.611 16.6089C14.1845 16.9074 13.83 17.2897 13.5714 17.73C13.345 18.1093 13.1691 18.5141 13.0476 18.935C12.8815 19.4411 12.8036 19.9698 12.8171 20.5H2.57143V14.5ZM14.3886 20.5C14.3623 20.1136 14.4084 19.7259 14.5248 19.355C14.6127 19.0553 14.7413 18.7679 14.9071 18.5C15.056 18.2384 15.2622 18.0105 15.5117 17.8319C15.7612 17.6533 16.0481 17.5281 16.3529 17.465C16.9403 17.3631 17.5463 17.4813 18.0448 17.795C18.0996 17.8247 18.1521 17.8581 18.2019 17.895L20.2971 19.31L19.391 17C19.2291 16.5677 19.209 16.0985 19.3333 15.655C19.4369 15.2079 19.7091 14.8128 20.0981 14.545C20.4925 14.264 20.9598 14.0911 21.4495 14.045V20.5H14.3886Z" fill="#FF6800"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,3 @@
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 6H10M7 9L10 6L7 3" stroke="#FF6800" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.898 16.875H12.8215C11.0215 16.7805 8.59034 14.9985 6.31559 12.1117L5.47634 11.052C3.20159 8.16525 2.01021 5.34487 2.29034 3.51113C2.48609 2.21738 4.31871 1.125 5.36609 1.125C5.88134 1.125 6.06921 1.39388 6.13671 1.55588C6.72509 2.664 7.45859 4.58663 7.46421 5.27625V5.32463L7.44734 5.37075C7.31571 5.733 7.02434 5.90625 6.76559 6.06038C6.42471 6.26288 6.23234 6.3945 6.20759 6.77587C6.20196 6.89062 6.29646 7.41825 7.79608 9.35775L8.43734 10.1711C9.94258 12.0364 10.4151 12.2366 10.5242 12.2524C10.8876 12.3075 11.0608 12.1444 11.3207 11.8418C11.5187 11.6089 11.7437 11.3512 12.1138 11.295L12.1633 11.286L12.2083 11.3017C12.8653 11.4424 14.5371 12.5764 15.4933 13.4201C15.6283 13.5146 15.868 13.8004 15.679 14.427C15.382 15.4114 14.0848 16.875 12.898 16.875Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 890 B

@@ -0,0 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.24985 4.5V2.25C5.24985 1.83579 5.58564 1.5 5.99985 1.5H14.9998C15.4141 1.5 15.7498 1.83579 15.7498 2.25V12.75C15.7498 13.1642 15.4141 13.5 14.9998 13.5H12.7498V15.7493C12.7498 16.1639 12.4124 16.5 11.9948 16.5H3.00499C2.58794 16.5 2.25 16.1665 2.25 15.7493L2.25195 5.25065C2.25202 4.83608 2.58948 4.5 3.00706 4.5H5.24985ZM3.75181 6L3.75014 15H11.2498V6H3.75181ZM6.74985 4.5H12.7498V12H14.2498V3H6.74985V4.5Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 538 B

+8
View File
@@ -0,0 +1,8 @@
<svg width="72" height="12" viewBox="0 0 72 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.05848 5.78271V9.09609C8.25115 8.8399 9.34012 8.57005 10.3219 8.28654L10.6365 9.36936C9.14998 9.77585 7.5632 10.1414 5.88309 10.4659L5.40256 9.48209C5.70678 9.31471 5.85543 9.0756 5.85543 8.75793V1.20887C7.45949 0.959511 8.89762 0.631587 10.1663 0.225099L10.9096 1.1713C9.81719 1.51971 8.53117 1.83739 7.05156 2.11749V4.64523H10.5293V5.7793H7.05848V5.78271ZM13.6891 10.0901L13.3606 8.9458L14.4842 8.96971C14.8126 8.96971 14.9751 8.79209 14.9751 8.43342V1.98085H12.4929V11.7707H11.283V0.860453H16.185V8.68278C16.185 9.62214 15.7494 10.0901 14.8748 10.0901H13.6891Z" fill="#FF6800"/>
<path d="M24.9731 6.93727C24.9316 7.18663 24.8798 7.4189 24.821 7.6341H29.5502V8.7545H25.3914C26.2488 9.72461 27.8494 10.3873 30.1932 10.7357L29.564 11.8561C26.8676 11.3335 25.0976 10.3805 24.2575 9.00386C23.9982 9.45134 23.6767 9.84417 23.2999 10.1755C22.4668 10.8997 21.0079 11.4701 18.9268 11.8937L18.4359 10.7357C20.2439 10.4214 21.5368 9.96714 22.3181 9.37961C22.5601 9.18149 22.771 8.97312 22.9473 8.75792H19.027V7.63752H23.5661C23.6422 7.39841 23.7078 7.12172 23.7666 6.81429L24.9731 6.93727ZM19.148 1.48213H21.8583C21.6889 1.09956 21.5092 0.75797 21.3156 0.46079L22.5774 0.262673C22.7295 0.576932 22.8954 0.986834 23.0821 1.48213H25.5159C25.7095 1.0654 25.8547 0.648666 25.9584 0.225099L27.2444 0.385643C27.1338 0.757972 26.9851 1.12347 26.7915 1.48213H29.4154V2.55472H24.8901V3.50091H28.7482V4.52225H24.8901V5.48211H29.8302V6.5786H18.7366V5.48211H23.6767V4.52225H19.8567V3.50091H23.6767V2.55472H19.1515V1.48213H19.148Z" fill="#FF6800"/>
<path d="M41.0382 11.4974C40.6026 11.4974 39.8179 11.4906 38.6943 11.4735C37.8439 11.4667 37.1491 11.395 36.6028 11.2618C36.0636 11.1115 35.6038 10.8211 35.2166 10.3907C35.0403 10.1823 34.8709 10.0799 34.7119 10.0799C34.4422 10.0799 33.979 10.6571 33.3256 11.8117L32.444 11.0021C33.0836 9.97397 33.6575 9.32496 34.1726 9.05852V5.55726H32.4579V4.41295H35.3203V9.14733C35.386 9.19857 35.4517 9.26005 35.5104 9.3352C35.8388 9.66654 36.1569 9.91248 36.468 10.0696C36.8552 10.2438 37.4429 10.3429 38.2346 10.3702C39.2267 10.3873 40.1083 10.3941 40.8826 10.3941L42.6354 10.3805C43.2161 10.3565 43.6725 10.3292 44.0078 10.3053L43.7174 11.5009H41.0382V11.4974ZM33.4604 0.337824C34.3178 0.986838 35.0437 1.62902 35.6418 2.26779L34.7603 3.13883C34.2555 2.51714 33.5503 1.84422 32.6411 1.12006L33.4604 0.337824ZM36.2848 2.88947H38.9709C39.0366 2.2917 39.0746 1.68026 39.085 1.05857V0.225099H40.2811V0.798966C40.2811 1.52996 40.2431 2.2268 40.167 2.89289H43.1919V4.0372H40.3814C40.8031 5.57434 41.8644 7.12173 43.5722 8.68278L42.7391 9.55382C41.2353 8.06792 40.2258 6.60593 39.7142 5.16785C39.1334 7.11831 38.1136 8.5803 36.6512 9.55382L35.9184 8.58371C37.3876 7.6546 38.3486 6.13796 38.7911 4.0372H36.2848V2.88947Z" fill="#FF6800"/>
<path d="M52.599 0.228514C54.1789 1.8408 55.9938 3.10467 58.0438 4.02695L57.4527 5.08587C56.8062 4.77844 56.2185 4.45735 55.6861 4.12601V5.24642H52.7753V7.11489H56.3188V8.25921H52.7753V10.2267H57.4665V11.3847H46.888V10.2267H51.5654V8.25921H48.0115V7.11489H51.5654V5.24642H48.6546V4.12601C48.1153 4.45052 47.5103 4.77844 46.8396 5.10978L46.2588 4.07477C48.454 3.03635 50.2793 1.75541 51.7417 0.225099H52.599V0.228514ZM55.6619 4.11235C54.3517 3.31645 53.1867 2.37709 52.1703 1.29768C51.23 2.31902 50.065 3.25838 48.6787 4.11235H55.6619Z" fill="#FF6800"/>
<path d="M60.6473 4.69988H61.9852V2.05942H60.509V0.94926H64.5814V2.05942H63.1952V4.69988H64.3187V5.82028H63.1952V8.01326C63.6895 7.79806 64.1527 7.58628 64.5814 7.37791V8.52222C63.3784 9.11317 62.0509 9.62555 60.5989 10.0662L60.2947 8.89456C60.9654 8.74426 61.5289 8.60079 61.9852 8.45733V5.81686H60.6473V4.69988ZM64.692 2.25754H67.4646V0.225099H68.6123V2.25412H70.327C69.9813 1.86471 69.5353 1.44456 68.9891 0.997083L69.6702 0.348069C70.327 0.836537 70.8594 1.31134 71.2708 1.76907L70.7902 2.25412H71.4955V3.38819H68.6089V4.49834C68.7782 5.02097 68.9718 5.51285 69.1896 5.96716C69.684 5.3933 70.1438 4.76478 70.5621 4.07477L71.4575 4.73404C70.8767 5.63241 70.2924 6.37365 69.7047 6.9646C70.327 8.02693 71.091 8.95604 72.0002 9.75535L71.2051 10.6879C70.1127 9.62555 69.2484 8.37876 68.6089 6.95093V10.4009C68.6089 11.19 68.1975 11.5828 67.3747 11.5828H66.0541L65.8017 10.4624C66.2719 10.5205 66.6626 10.5513 66.9737 10.5513C67.3021 10.5513 67.4646 10.4044 67.4646 10.114V7.54871C66.6902 8.62129 65.7948 9.54698 64.7785 10.3258L64.1493 9.30446C65.3834 8.49832 66.4897 7.38474 67.4646 5.96716V3.38819H64.692V2.25754ZM65.577 4.17383C66.1475 5.00389 66.5865 5.71439 66.8873 6.30533L66.0541 6.8792C65.7015 6.23019 65.2348 5.5231 64.654 4.7477L65.577 4.17383Z" fill="#FF6800"/>
<line x1="0.253237" y1="1.10693e-08" x2="0.253237" y2="11.1424" stroke="#FF6800" stroke-width="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

+14
View File
@@ -0,0 +1,14 @@
<svg width="73" height="30" viewBox="0 0 73 30" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M47.9351 20.0809C41.0877 26.1054 33.2907 28.4959 30.5222 25.4211C29.4344 24.2135 29.2848 22.3443 29.916 20.1529C27.9686 23.6941 27.5504 26.8884 29.1447 28.659C31.9151 31.7338 39.7121 29.3414 46.5575 23.3187C50.717 19.66 53.5679 15.5027 54.5445 12.1132C53.1075 14.7255 50.8417 17.5254 47.9351 20.0809Z" fill="#FF6800"/>
<path d="M2.29076 9.25257H0.604346C0.270517 9.25257 0 9.51987 0 9.84972V11.5161C0 11.8459 0.270517 12.1132 0.604346 12.1132H2.29076C2.62459 12.1132 2.8951 11.8459 2.8951 11.5161V9.84972C2.8951 9.51987 2.62459 9.25257 2.29076 9.25257Z" fill="#FF6800"/>
<path d="M2.29076 13.0497H0.604346C0.270517 13.0497 0 13.3189 0 13.6487V22.2685C0 22.5984 0.270517 22.8657 0.604346 22.8657H2.29076C2.62459 22.8657 2.8951 22.5984 2.8951 22.2685V13.6487C2.8951 13.3189 2.62459 13.0497 2.29076 13.0497Z" fill="#FF6800"/>
<path d="M8.71549 13.0573C8.18022 13.0648 7.66796 13.1672 7.19408 13.3454C7.08856 13.1691 6.89478 13.0497 6.67223 13.0497H4.85728C4.52345 13.0497 4.25293 13.317 4.25293 13.6468V17.6013V17.6999V22.2989C4.25293 22.6287 4.52345 22.896 4.85728 22.896H6.67223C7.00606 22.896 7.27658 22.6287 7.27658 22.2989V17.7017V17.5349C7.27658 16.6951 7.98452 16.0146 8.84212 16.0468C9.65751 16.0771 10.2925 16.7653 10.2925 17.5728V22.3235C10.2925 22.6533 10.5631 22.9206 10.8969 22.9206H12.7118C13.0457 22.9206 13.3162 22.6533 13.3162 22.3235V17.5349C13.3181 15.0421 11.248 13.0193 8.71549 13.0573Z" fill="#FF6800"/>
<path d="M67.7277 13.0573C67.2289 13.0648 66.7512 13.1539 66.3041 13.3113V7.84594C66.3041 7.51609 66.0336 7.24879 65.6998 7.24879H63.8848C63.551 7.24879 63.2805 7.51609 63.2805 7.84594V16.9074C63.2805 16.9738 63.2862 17.0382 63.2939 17.1008C63.2747 17.2657 63.2651 17.4326 63.2651 17.6013V22.2988C63.2651 22.6287 63.5357 22.896 63.8695 22.896H65.6844C66.0183 22.896 66.2888 22.6287 66.2888 22.2988V17.5349C66.2888 16.6951 66.9967 16.0146 67.8543 16.0468C68.6697 16.0771 69.3048 16.7653 69.3048 17.5728V22.3235C69.3048 22.6533 69.5753 22.9206 69.9091 22.9206H71.7241C72.0579 22.9206 72.3284 22.6533 72.3284 22.3235V17.5349C72.3284 15.0421 70.2583 13.0193 67.7277 13.0573Z" fill="#FF6800"/>
<path d="M23.1448 13.1312H21.3299C20.996 13.1312 20.7255 13.3985 20.7255 13.7283V14.3255C20.7255 15.1122 20.1058 15.7586 19.325 15.8155C19.2655 15.8136 19.2041 15.8136 19.1446 15.8136C19.1293 15.8136 19.1158 15.8155 19.1005 15.8155C18.7072 15.7871 18.2851 15.6089 17.8861 15.2278C17.769 15.116 17.7019 14.9605 17.7019 14.7994V7.84594C17.7019 7.51609 17.4314 7.24879 17.0975 7.24879H15.2826C14.9487 7.24879 14.6782 7.51609 14.6782 7.84594V13.7283V16.2269V20.3614V22.3842C14.6782 22.714 14.9487 22.9813 15.2826 22.9813H17.0975C17.4314 22.9813 17.7019 22.714 17.7019 22.3842V22.3387V20.2932C17.7019 19.5084 18.3196 18.8619 19.0986 18.8051C19.1715 18.8069 19.2444 18.8069 19.3173 18.8051C20.1135 18.86 20.7255 19.5368 20.7255 20.3311V22.3861C20.7255 22.7159 20.996 22.9832 21.3299 22.9832H23.1448C23.4786 22.9832 23.7492 22.7159 23.7492 22.3861V20.2951C23.7492 19.1501 23.3117 18.1055 22.5942 17.3112C23.3117 16.5188 23.7492 15.4724 23.7492 14.3255V13.7283C23.7492 13.3985 23.4786 13.1312 23.1448 13.1312Z" fill="#FF6800"/>
<path d="M29.701 16.0714C29.7605 16.0733 29.818 16.0468 29.8526 15.9994C30.3495 15.2904 30.8963 14.5833 31.4872 13.8819C31.6157 13.7283 31.5562 13.4971 31.3701 13.4212C30.8176 13.1956 30.2094 13.0743 29.5725 13.0838C29.066 13.0914 28.5825 13.1843 28.1316 13.3435C28.0837 13.1843 27.9378 13.0686 27.7613 13.0686H25.7296C25.3958 13.0686 25.1252 13.3359 25.1252 13.6658V17.2847C25.1176 17.3984 25.1118 17.5122 25.1118 17.6278V22.3254C25.1118 22.4941 25.1828 22.6477 25.2979 22.7557C25.4073 22.8676 25.5608 22.9377 25.7315 22.9377H27.5465C27.8803 22.9377 28.1508 22.6704 28.1508 22.3405V17.3662C28.2487 16.6174 28.9106 16.043 29.701 16.0714Z" fill="#FF6800"/>
<path d="M30.9828 18.8069C31.1152 19.6145 31.4624 20.3861 31.9766 21.0249H31.9747C33.8146 23.3415 37.4541 23.5386 39.8177 21.5481C39.9444 21.442 39.9578 21.2524 39.8504 21.1273L38.4536 19.5046C38.35 19.3832 38.1678 19.3662 38.0392 19.461C37.0032 20.2249 35.9346 20.4031 34.8487 19.8553L38.2752 18.4581L40.7578 17.4439C40.9132 17.3814 40.9861 17.2051 40.9209 17.0534C40.66 16.4354 40.3281 15.5918 39.9731 15.1349C39.0484 13.8478 37.5404 13.0572 35.902 13.0591H35.9039C32.8553 13.0364 30.4571 15.8439 30.9828 18.8069ZM33.7973 17.6562C33.9201 16.0449 35.9864 15.1823 37.22 16.261C36.3413 16.6193 34.6741 17.2998 33.7973 17.6581V17.6562Z" fill="#FF6800"/>
<path d="M57.9139 15.952C58.6065 15.6695 59.3835 15.7473 59.9955 16.1397C60.2315 16.2913 60.5461 16.2534 60.7457 16.0563L61.8469 14.9681C62.1079 14.7103 62.0752 14.2857 61.7798 14.0658C60.9203 13.4231 59.8651 13.0705 58.7734 13.0724C56.5153 13.0724 54.5372 14.5719 53.954 16.7273C53.3688 18.8828 54.3223 21.1576 56.2793 22.2742C58.0309 23.2732 60.197 23.1197 61.7779 21.9425C62.0733 21.7225 62.1079 21.2979 61.8469 21.0382L60.7457 19.9501C60.5442 19.751 60.2296 19.7188 59.9898 19.8704C59.3777 20.2591 58.6046 20.3368 57.9139 20.0543C57.0736 19.7112 56.5268 18.9017 56.5268 18.0032C56.5268 17.1065 57.0736 16.2951 57.9139 15.952Z" fill="#FF6800"/>
<path d="M51.7179 14.479V13.3814C51.7179 13.2165 51.5817 13.0819 51.4147 13.0819H49.147C48.9801 13.0819 48.8439 13.2165 48.8439 13.3814V13.5236C48.1609 13.207 47.3954 13.0383 46.5877 13.0592C43.9535 13.1312 41.8047 15.2563 41.7395 17.861C41.6915 19.715 42.6815 21.3434 44.176 22.223C44.9435 21.6467 45.7013 21.0268 46.4457 20.3671C46.5531 20.2704 46.6606 20.1738 46.768 20.0771C46.7546 20.0771 46.7431 20.079 46.7296 20.079C45.4826 20.079 44.483 19.0079 44.6269 17.7472C44.7343 16.788 45.5171 16.0146 46.4879 15.9084C47.7637 15.7681 48.8477 16.7539 48.8477 17.9861C48.8477 18.0013 48.8477 18.0164 48.8458 18.0335C49.9336 16.8695 50.9006 15.6733 51.7179 14.479Z" fill="#FF6800"/>
<path d="M48.8467 22.3766V22.8467H51.4175C51.5845 22.8467 51.7207 22.7121 51.7207 22.5472V19.2657C50.7307 20.3842 50.0208 21.3358 48.8467 22.3766Z" fill="#FF6800"/>
<path d="M36.5144 9.03833C43.1162 2.69528 51.0705 0.200528 54.2822 3.46494C55.6827 4.88862 55.9494 7.17106 55.2606 9.81937C56.878 6.24785 57.018 3.06874 55.2971 1.319C52.2236 -1.80324 44.3805 0.807156 37.7807 7.14831C35.6165 9.2279 33.8534 11.4516 32.5718 13.6089C33.6615 12.0582 34.9853 10.5094 36.5144 9.03833Z" fill="#FF6800"/>
</svg>

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -0,0 +1,3 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7.58317 23.8334V11.9167M2.1665 14.0834V21.6667C2.1665 22.8634 3.13655 23.8334 4.33317 23.8334H18.8782C20.4824 23.8334 21.8466 22.6631 22.0905 21.0776L23.2572 13.4943C23.5601 11.5256 22.0368 9.75008 20.045 9.75008H16.2498C15.6515 9.75008 15.1665 9.26505 15.1665 8.66675V4.83807C15.1665 3.36274 13.9705 2.16675 12.4952 2.16675C12.1432 2.16675 11.8244 2.37399 11.6815 2.69554L7.86911 11.2734C7.69523 11.6647 7.30727 11.9167 6.87914 11.9167H4.33317C3.13655 11.9167 2.1665 12.8868 2.1665 14.0834Z" stroke="#FF6800" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 689 B

@@ -0,0 +1,3 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.33215 8.10647L7.97753 11.4246C8.21925 11.5972 8.34012 11.6835 8.46661 11.7034C8.5779 11.7209 8.69189 11.7032 8.79269 11.653C8.90725 11.5958 8.99637 11.4769 9.1746 11.2393L10.1562 9.93048C10.2066 9.86327 10.2318 9.82967 10.2614 9.80068C10.2877 9.77494 10.3166 9.75196 10.3475 9.73208C10.3824 9.70967 10.4208 9.69262 10.4976 9.6585L14.6885 7.79585C14.8623 7.71864 14.9491 7.68003 15.0146 7.61973C15.0726 7.56641 15.1182 7.50108 15.1483 7.42832C15.1823 7.34607 15.1886 7.25122 15.2013 7.06153L15.5082 2.4586M14.6248 14.6251L17.4588 15.8396C17.7876 15.9805 17.9521 16.0511 18.0392 16.1681C18.1156 16.2708 18.1531 16.3972 18.1452 16.5249C18.1362 16.6705 18.037 16.8194 17.8385 17.117L16.5071 19.1142C16.4131 19.2552 16.366 19.3258 16.3039 19.3769C16.2489 19.4221 16.1855 19.456 16.1173 19.4767C16.0404 19.5001 15.9556 19.5001 15.7861 19.5001H13.6245C13.3999 19.5001 13.2876 19.5001 13.192 19.4625C13.1076 19.4293 13.0329 19.3755 12.9747 19.3059C12.9088 19.2272 12.8733 19.1206 12.8023 18.9075L12.03 16.5907C11.9881 16.4649 11.9672 16.4021 11.9617 16.3383C11.9568 16.2816 11.9608 16.2245 11.9737 16.1692C11.9882 16.1068 12.0178 16.0475 12.0771 15.929L12.6637 14.7557C12.7828 14.5173 12.8424 14.3981 12.9343 14.3241C13.0151 14.2589 13.1132 14.2184 13.2164 14.2072C13.3336 14.1947 13.46 14.2368 13.7129 14.3211L14.6248 14.6251ZM23.8332 13.0001C23.8332 18.9831 18.9829 23.8334 12.9998 23.8334C7.01675 23.8334 2.1665 18.9831 2.1665 13.0001C2.1665 7.01699 7.01675 2.16675 12.9998 2.16675C18.9829 2.16675 23.8332 7.01699 23.8332 13.0001Z" stroke="#FF6800" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,3 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.437 2.16675H9.20178C9.00734 2.16675 8.91013 2.16675 8.8243 2.19636C8.7484 2.22253 8.67927 2.26526 8.62191 2.32144C8.55705 2.38496 8.51357 2.47192 8.42661 2.64583L3.87661 11.7458C3.66896 12.1611 3.56513 12.3688 3.59007 12.5376C3.61185 12.6849 3.69338 12.8169 3.81547 12.9023C3.9553 13.0001 4.18745 13.0001 4.65178 13.0001H11.3745L8.12449 23.8334L21.3337 10.135C21.7793 9.67284 22.0022 9.44176 22.0152 9.24403C22.0264 9.07241 21.9556 8.90561 21.8242 8.79461C21.6728 8.66675 21.3519 8.66675 20.7098 8.66675H12.9995L15.437 2.16675Z" stroke="#FF6800" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 728 B

@@ -0,0 +1,3 @@
<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M22.2083 7.88436L13 13.0001M13 13.0001L3.79163 7.88436M13 13.0001V23.2918M22.75 17.3969V8.60337C22.75 8.23217 22.75 8.04658 22.6953 7.88104C22.647 7.7346 22.5678 7.60018 22.4633 7.48676C22.3452 7.35856 22.183 7.26843 21.8584 7.08816L13.8418 2.63446C13.5345 2.46377 13.3809 2.37842 13.2183 2.34496C13.0742 2.31535 12.9258 2.31535 12.7818 2.34496C12.6191 2.37842 12.4655 2.46377 12.1582 2.63446L4.14155 7.08816C3.81707 7.26843 3.65483 7.35856 3.53668 7.48676C3.43217 7.60018 3.35308 7.7346 3.30469 7.88104C3.25 8.04658 3.25 8.23217 3.25 8.60337V17.3969C3.25 17.7681 3.25 17.9537 3.30469 18.1192C3.35308 18.2657 3.43217 18.4001 3.53668 18.5134C3.65483 18.6417 3.81707 18.7318 4.14155 18.9121L12.1582 23.3658C12.4655 23.5365 12.6191 23.6218 12.7818 23.6553C12.9258 23.685 13.0742 23.685 13.2183 23.6553C13.3809 23.6218 13.5345 23.5365 13.8418 23.3658L21.8584 18.9121C22.183 18.7318 22.3452 18.6417 22.4633 18.5134C22.5678 18.4001 22.647 18.2657 22.6953 18.1192C22.75 17.9537 22.75 17.7681 22.75 17.3969Z" stroke="#FF6800" stroke-width="2" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+8
View File
@@ -49,6 +49,14 @@ server {
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
} }
location = /h5 {
return 301 /h5/;
}
location /h5/{
try_files $uri $uri/ /h5/index.html;
}
# Uploaded files served by the API # Uploaded files served by the API
location /uploads/ { location /uploads/ {
proxy_pass http://api:3001/uploads/; proxy_pass http://api:3001/uploads/;
+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` |
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.