feat(contracts): UniversalRow 契约 v1 冻结 + 守卫 + 测试
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "@sf/contracts",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "SiteForge 跨源契约:UniversalRow 统一中间格式(契约冻结,变更须升版)",
|
||||||
|
"type": "module",
|
||||||
|
"main": "./dist/src/index.js",
|
||||||
|
"types": "./dist/src/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/src/index.d.ts",
|
||||||
|
"default": "./dist/src/index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json",
|
||||||
|
"test": "vitest run"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './universal-row.js'
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
export const UNIVERSAL_ROW_VERSION = 1
|
||||||
|
|
||||||
|
export interface UniversalRow {
|
||||||
|
sourceType: string
|
||||||
|
sourceId: string
|
||||||
|
status: string
|
||||||
|
locale: string
|
||||||
|
sourceUrl?: string
|
||||||
|
slug?: string
|
||||||
|
title?: string
|
||||||
|
excerpt?: string
|
||||||
|
bodyHtml?: string
|
||||||
|
publishedAt?: string
|
||||||
|
modifiedAt?: string
|
||||||
|
taxonomyRefs: Array<{ taxonomy: string; sourceTermId: string }>
|
||||||
|
attachmentRefs: Array<{ role: string; sourceId?: string; sourceUrl?: string }>
|
||||||
|
seo: Record<string, unknown>
|
||||||
|
knownFields: Record<string, unknown>
|
||||||
|
extras: Record<string, unknown>
|
||||||
|
rawLocation: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const REQUIRED: (keyof UniversalRow)[] = [
|
||||||
|
'sourceType', 'sourceId', 'status', 'locale',
|
||||||
|
'taxonomyRefs', 'attachmentRefs', 'seo', 'knownFields', 'extras', 'rawLocation',
|
||||||
|
]
|
||||||
|
|
||||||
|
export function isUniversalRow(v: unknown): v is UniversalRow {
|
||||||
|
if (typeof v !== 'object' || v === null) return false
|
||||||
|
const row = v as Record<string, unknown>
|
||||||
|
return REQUIRED.every((k) => k in row)
|
||||||
|
&& Array.isArray(row.taxonomyRefs) && Array.isArray(row.attachmentRefs)
|
||||||
|
&& row.seo !== null && typeof row.seo === 'object'
|
||||||
|
&& row.knownFields !== null && typeof row.knownFields === 'object'
|
||||||
|
&& row.extras !== null && typeof row.extras === 'object'
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { UNIVERSAL_ROW_VERSION, isUniversalRow, type UniversalRow } from '../src/index.js'
|
||||||
|
|
||||||
|
const valid: UniversalRow = {
|
||||||
|
sourceType: 'wp:post', sourceId: '101', status: 'publish', locale: 'en',
|
||||||
|
sourceUrl: 'https://fixture.example/news/fixture-news-a.html',
|
||||||
|
slug: 'fixture-news-a', title: 'Fixture News A', excerpt: 'Fixture excerpt A',
|
||||||
|
bodyHtml: '<p>Body</p>', publishedAt: '2026-10-05T09:00:00Z', modifiedAt: '2026-10-06T10:00:00Z',
|
||||||
|
taxonomyRefs: [{ taxonomy: 'category', sourceTermId: '31' }],
|
||||||
|
attachmentRefs: [{ role: 'featured', sourceId: '201' }],
|
||||||
|
seo: { title: 'Fixture SEO Title A' },
|
||||||
|
knownFields: {}, extras: { custom_meta: 'x' },
|
||||||
|
rawLocation: 'wxr://item/3',
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('UniversalRow 契约', () => {
|
||||||
|
it('版本号为 1(冻结,变更须升版并保留转换测试)', () => {
|
||||||
|
expect(UNIVERSAL_ROW_VERSION).toBe(1)
|
||||||
|
})
|
||||||
|
it('合法对象通过守卫', () => {
|
||||||
|
expect(isUniversalRow(valid)).toBe(true)
|
||||||
|
})
|
||||||
|
it('缺必填字段拒绝', () => {
|
||||||
|
const { sourceId: _omit, ...bad } = valid
|
||||||
|
expect(isUniversalRow(bad)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": ["src", "test"]
|
||||||
|
}
|
||||||
Generated
+2
@@ -15,6 +15,8 @@ importers:
|
|||||||
specifier: ^2.1.0
|
specifier: ^2.1.0
|
||||||
version: 2.1.9
|
version: 2.1.9
|
||||||
|
|
||||||
|
packages/contracts: {}
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.21.5':
|
'@esbuild/aix-ppc64@0.21.5':
|
||||||
|
|||||||
Reference in New Issue
Block a user