55 lines
3.4 KiB
PowerShell
55 lines
3.4 KiB
PowerShell
$ErrorActionPreference = 'Stop'
|
|
|
|
$root = Split-Path -Parent $PSScriptRoot
|
|
function Read-Page([string]$path) { Get-Content -LiteralPath (Join-Path $root $path) -Raw -Encoding UTF8 }
|
|
|
|
$contracts = [ordered]@{
|
|
'pages/records/r03-gift-list.vue' = @('appApi.getRelativeRecords', 'relativeId', 'openPage("R04"', 'record-list')
|
|
'pages/records/r04-gift-editor.vue' = @('appApi.createRelativeRecord', 'relativeName', 'mediaOssIds', 'pickAndUploadImage')
|
|
'pages/records/r05-ritual-list.vue' = @('appApi.getCeremonies', 'ceremonyId', 'openPage("R06"', 'openPage("R07"')
|
|
'pages/records/r06-ritual-detail.vue' = @('appApi.getCeremonyDetail', 'ceremonyId')
|
|
'pages/records/r07-ritual-editor.vue' = @('appApi.createCeremony', 'ceremonyType', 'ceremonyTitle')
|
|
'pages/records/r08-growth-journal.vue' = @('appApi.getGrowthRecords', 'appApi.createGrowthRecord', 'recordId', 'recordTitle', 'view.value = "list"')
|
|
'pages/records/r09-life-events.vue' = @('hasValidContext', 'returnToRecords')
|
|
'pages/records/r10-memo-list.vue' = @('appApi.getMemos', 'appApi.createMemo', 'memoId', 'memoTitle', 'view.value = "list"')
|
|
'pages/records/r11-merit-records.vue' = @('appApi.getMeritRecords', 'appApi.createMeritRecord', 'meritId', 'meritTitle', 'totalAmount')
|
|
}
|
|
|
|
foreach ($entry in $contracts.GetEnumerator()) {
|
|
$source = Read-Page $entry.Key
|
|
foreach ($token in $entry.Value) {
|
|
if (-not $source.Contains($token)) { throw "$($entry.Key) missing current record contract: $token" }
|
|
}
|
|
foreach ($shared in @('ModulePageBackground', 'PageHeader', 'AppButton')) {
|
|
if (-not $source.Contains($shared)) { throw "$($entry.Key) missing shared primitive: $shared" }
|
|
}
|
|
foreach ($forbidden in @('Fixture', 'localGrowthPreview', 'localMemoPreview', 'localMeritPreview', 'Date.now()', 'uni.navigateTo', 'uni.navigateBack', 'uni.redirectTo', 'uni.reLaunch')) {
|
|
if ($source.Contains($forbidden)) { throw "$($entry.Key) retains retired local or direct-navigation contract: $forbidden" }
|
|
}
|
|
if ($source -match '<ModulePage(?:\s|/|>)|import\s+ModulePage\s+from') { throw "$($entry.Key) must own its business page" }
|
|
if ($source -match 'uni\.(showToast|showModal|showLoading|showActionSheet)') { throw "$($entry.Key) must use project feedback primitives" }
|
|
}
|
|
|
|
$r04 = Read-Page 'pages/records/r04-gift-editor.vue'
|
|
foreach ($optionalRelativeField in @('relationName', 'eventName', 'eventTime', 'giftAmount', 'recordContent', 'mediaOssIds', 'sortOrder')) {
|
|
if ($r04 -match "if\s*\(!form\.$optionalRelativeField") { throw "R04 must not make optional backend field required: $optionalRelativeField" }
|
|
}
|
|
|
|
foreach ($page in @('pages/records/r08-growth-journal.vue', 'pages/records/r10-memo-list.vue', 'pages/records/r11-merit-records.vue')) {
|
|
$source = Read-Page $page
|
|
if ($source -notmatch 'onShow\(') { throw "$page must reread the service when returning to its list" }
|
|
if ($source -notmatch 'await\s+load') { throw "$page must read back after a successful write" }
|
|
}
|
|
|
|
$r10 = Read-Page 'pages/records/r10-memo-list.vue'
|
|
foreach ($forbidden in @('toggleMemo', 'memo.done =')) {
|
|
if ($r10.Contains($forbidden)) { throw "R10 must not invent a completed-state mutation: $forbidden" }
|
|
}
|
|
|
|
$r09 = Read-Page 'pages/records/r09-life-events.vue'
|
|
foreach ($forbidden in @('lifeEvents', 'createLifeEvent', 'saveLifeEvent', 'growth-records', 'LIFE_EVENT')) {
|
|
if ($r09.Contains($forbidden)) { throw "R09 must remain closed without a backend contract: $forbidden" }
|
|
}
|
|
|
|
Write-Output 'R-BUSINESS-FLOW-CONTRACT PASS'
|