$ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $api = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $root 'utils/api.js') $page = Get-Content -Raw -Encoding UTF8 -LiteralPath (Join-Path $root 'pages/profile/m10-about-settings.vue') $issues = New-Object System.Collections.Generic.List[string] function Require-Text { param([string]$Source, [string]$Text, [string]$Description) if (-not $Source.Contains($Text)) { $script:issues.Add($Description) } } Require-Text $api 'async logout(requestOptions = {}) {' 'api owner must expose logout' Require-Text $api "url: '/genealogy/app/auth/logout'" 'logout must use the declared APP logout owner' Require-Text $api "method: 'DELETE'" 'logout must use DELETE' Require-Text $api 'requireData: false' 'logout must accept the declared VoidResult envelope' Require-Text $api 'requestController: requestOptions.requestController ?? null' 'logout must accept page cancellation ownership' Require-Text $page 'appApi.logout({ requestController: logoutRequestController });' 'M10 must call the shared logout owner' Require-Text $page 'session.clear();' 'M10 must clear the local session after every request outcome' Require-Text $page 'return goRoot("A01");' 'M10 must return to A01 after local session clear' Require-Text $page 'logoutRequestController.abort()' 'M10 must cancel an in-flight request when unloading' $logoutOwner = [regex]::Match($api, "async logout\(requestOptions = \{\}\) \{[\s\S]*?\n \},\n async ").Value if (-not $logoutOwner) { $issues.Add('logout owner boundary is not identifiable') } elseif ($logoutOwner -match '(?m)^\s*data:') { $issues.Add('logout must not add a request body') } if ($page -match 'session\.clear\(\);[\s\S]{0,80}appApi\.logout') { $issues.Add('M10 must not clear local session before starting the remote request') } if ($issues.Count -gt 0) { throw ("LOGOUT-OPENAPI-CONTRACT FAIL`n- " + ($issues -join "`n- ")) } Write-Output 'LOGOUT-OPENAPI-CONTRACT PASS'