Files
WRNexusJS/scripts/verify-0.4.mjs
2026-07-27 12:42:18 +05:30

219 lines
7.7 KiB
JavaScript

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 ?? "<missing>"}, 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 ?? "<missing>"}`);
}
}
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 ?? "<missing>"}`);
}
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 (/<script\b/i.test(captchaComponent)) {
errors.push("Captcha.wrn still contains a manual script tag");
}
for (const obsolete of [
"packages/ui/components/Captcha.wrn",
"examples/captcha-showcase/app/components/Captcha.wrn",
"examples/captcha-showcase/public/assets/wrnexus/captcha.js",
]) {
if (existsSync(join(root, obsolete)))
errors.push(`obsolete copied CAPTCHA file remains: ${obsolete}`);
}
const captchaManifest = readJson("packages/captcha/package.json");
if (!captchaManifest.wrnexus?.plugin?.plugin) {
errors.push("@wrnexus/captcha does not publish a wrnexus.plugin manifest");
}
const pluginSource = read("packages/plugin/src/index.ts");
for (const marker of ["clientRuntimes", "componentDirs", "migrations", "PUBLIC-PATH"]) {
if (!pluginSource.includes(marker)) errors.push(`plugin runner is missing ${marker}`);
}
const runtimeSource = read("packages/dev-server/src/runtime.ts");
if (!runtimeSource.includes("runtimeScriptsForMarkup")) {
errors.push("server runtime is not injecting package runtimes from rendered markers");
}
if (!runtimeSource.includes("JSON.stringify(scripts)")) {
errors.push("page ETags do not include structured script metadata");
}
const navSource = read("packages/csr/src/nav-runtime.ts");
for (const marker of ["__wrnexusRuntimes", "mountPackageRuntimes", "unmountPackageRuntimes"]) {
if (!navSource.includes(marker)) errors.push(`CSR navigation is missing ${marker}`);
}
const uiReference = readJson("packages/ui/component-reference.json");
if (uiReference.components?.some((component) => 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 ?? "<missing>"}`);
}
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"),
);
}