import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; import { join, relative } from "node:path"; import process from "node:process"; const root = process.cwd(); const VERSION = "0.4.0"; const errors = []; const notes = []; const read = (file) => readFileSync(join(root, file), "utf8"); const readJson = (file) => JSON.parse(read(file)); function files(dir, filename = "package.json") { const output = []; if (!existsSync(dir)) return output; for (const entry of readdirSync(dir)) { if (["node_modules", ".git", ".publish", "dist", ".wrnexus"].includes(entry)) { continue; } const path = join(dir, entry); const stat = statSync(path); if (stat.isDirectory()) output.push(...files(path, filename)); else if (entry === filename) output.push(path); } return output; } const rootPackage = readJson("package.json"); if (rootPackage.version !== VERSION) { errors.push(`root package version is ${rootPackage.version ?? ""}, expected ${VERSION}`); } const packageFiles = files(join(root, "packages")); const packages = new Map(); for (const file of packageFiles) { const manifest = JSON.parse(readFileSync(file, "utf8")); if (!manifest.name?.startsWith("@wrnexus/")) continue; packages.set(manifest.name, { file, manifest }); if (manifest.version !== VERSION) { errors.push(`${relative(root, file)} has version ${manifest.version ?? ""}`); } } if (packages.size !== 30) errors.push(`expected 30 framework packages, found ${packages.size}`); const serviceManifest = readJson("services/managed-captcha/package.json"); if (serviceManifest.version !== VERSION) { errors.push(`managed CAPTCHA service has version ${serviceManifest.version ?? ""}`); } for (const { file, manifest } of packages.values()) { for (const field of [ "dependencies", "devDependencies", "peerDependencies", "optionalDependencies", ]) { for (const [name, range] of Object.entries(manifest[field] ?? {})) { if (String(range).startsWith("workspace:") && !packages.has(name)) { errors.push(`${relative(root, file)} references missing workspace ${name}`); } } } } for (const required of [ "packages/plugin/src/types.ts", "packages/plugin/src/manifest.ts", "packages/plugin/src/discovery.ts", "packages/dev-server/src/plugin-assets.ts", "packages/dev-server/src/plugin-migrations.ts", "packages/cli/src/inspect.ts", "packages/cli/src/system.ts", "packages/captcha/src/plugin.ts", "packages/captcha/assets/client/captcha.js", "packages/captcha/components/Captcha.wrn", "integration/platform-upgrade.test.ts", "docs/ARCHITECTURE-0.4.md", "docs/UPGRADE-0.4.md", "docs/PACKAGE-RUNTIMES-0.4.md", "docs/TEST-CHECKLIST-0.4.md", "CHANGELOG-0.4.0.md", ]) { if (!existsSync(join(root, required))) errors.push(`missing ${required}`); } const updateSource = read("packages/cli/src/update.ts"); const migrations = [...updateSource.matchAll(/version:\s*"([^"]+)"[\s\S]*?id:\s*"([^"]+)"/g)].map( (match) => ({ version: match[1], id: match[2] }), ); const migrationIds = new Set(); for (const migration of migrations) { if (migrationIds.has(migration.id)) errors.push(`duplicate migration id ${migration.id}`); migrationIds.add(migration.id); } if (!migrations.some((migration) => migration.version === VERSION)) { errors.push(`missing ${VERSION} update migration`); } const captchaComponent = read("packages/captcha/components/Captcha.wrn"); if (!captchaComponent.includes('data-wrnexus-runtime="captcha"')) { errors.push("Captcha.wrn does not declare the captcha client runtime marker"); } if (/ component.name === "Captcha")) { errors.push("Captcha is still duplicated in the core UI component reference"); } const tsconfig = readJson("tsconfig.json"); for (const name of packages.keys()) { if (!tsconfig.compilerOptions?.paths?.[name]) errors.push(`tsconfig paths missing ${name}`); } try { const lock = JSON.parse(read("bun.lock").replace(/,\s*([}\]])/g, "$1")); for (const { file, manifest } of packages.values()) { const workspace = relative(root, file) .replace(/\\/g, "/") .replace(/\/package\.json$/, ""); if (lock.workspaces?.[workspace]?.version !== VERSION) { errors.push(`bun.lock has a stale version for ${workspace}`); } if (!lock.packages?.[manifest.name]) errors.push(`bun.lock missing ${manifest.name}`); } } catch (error) { errors.push(`bun.lock structure is invalid: ${error instanceof Error ? error.message : error}`); } const editorManifest = readJson("editors/vscode/package.json"); if (editorManifest.version !== VERSION) { errors.push(`VS Code extension has version ${editorManifest.version ?? ""}`); } const editorLock = readJson("editors/vscode/package-lock.json"); if (editorLock.version !== VERSION || editorLock.packages?.[""]?.version !== VERSION) { errors.push("VS Code extension package-lock version is stale"); } for (const obsolete of [ ".publish", "AUDIO-PLAYBACK-FIX.md", "CAPTCHA-FIX8-README.md", "CAPTCHA-FIXED7-INSTRUCTIONS.md", "WRNexusJS-0.3.0-test-fixes.patch", ]) { if (existsSync(join(root, obsolete))) errors.push(`obsolete release artifact remains: ${obsolete}`); } for (const file of [ "editors/vscode/package.json", "editors/vscode/package-lock.json", "editors/vscode/snippets/wrn.json", "editors/vscode/syntaxes/wrn.tmLanguage.json", ]) { try { readJson(file); } catch (error) { errors.push(`${file} is invalid JSON: ${error instanceof Error ? error.message : error}`); } } notes.push(`${packages.size} framework packages aligned to ${VERSION}`); notes.push(`${migrations.length} unique updater migrations checked`); notes.push("automatic component/runtime/asset/migration discovery checked"); notes.push("CAPTCHA has no copied component or public JavaScript requirement"); notes.push("Bun workspace lock and editor JSON checked"); if (errors.length) { process.stderr.write( [ `WRNexusJS ${VERSION} verification failed:`, ...errors.map((error) => ` - ${error}`), "", ].join("\n"), ); process.exitCode = 1; } else { process.stdout.write( [ `WRNexusJS ${VERSION} structural verification passed.`, ...notes.map((note) => ` ✓ ${note}`), "", ].join("\n"), ); }