diff --git a/docs/public-api-0.8.json b/docs/public-api-0.8.json
index 20ab60f9..e0691583 100644
--- a/docs/public-api-0.8.json
+++ b/docs/public-api-0.8.json
@@ -1316,6 +1316,7 @@
"createOutputProxy",
"createServerProxy",
"getActionRuntime",
+ "getComponentControllerRuntime",
"getNavRuntime",
"getReactiveRuntime",
"getRealtimeRuntime",
diff --git a/examples/inter-app-api-showcase/apps/admin/package.json b/examples/inter-app-api-showcase/apps/admin/package.json
index 070fe721..26370e04 100644
--- a/examples/inter-app-api-showcase/apps/admin/package.json
+++ b/examples/inter-app-api-showcase/apps/admin/package.json
@@ -53,11 +53,11 @@
"@iconify-json/lucide": "^1.2.118",
"@iconify/tailwind4": "^1.2.3",
"@tailwindcss/cli": "^4.0.0",
- "@types/bun": "latest",
+ "@types/bun": "^1.3.14",
"eslint": "^9.0.0",
- "prettier": "latest",
+ "prettier": "^3.9.6",
"tailwindcss": "^4.0.0",
"typescript": "^5.5.0",
- "typescript-eslint": "latest"
+ "typescript-eslint": "^8.65.0"
}
}
diff --git a/examples/inter-app-api-showcase/apps/web/package.json b/examples/inter-app-api-showcase/apps/web/package.json
index c1608733..d7c5fa72 100644
--- a/examples/inter-app-api-showcase/apps/web/package.json
+++ b/examples/inter-app-api-showcase/apps/web/package.json
@@ -53,11 +53,11 @@
"@iconify-json/lucide": "^1.2.118",
"@iconify/tailwind4": "^1.2.3",
"@tailwindcss/cli": "^4.0.0",
- "@types/bun": "latest",
+ "@types/bun": "^1.3.14",
"eslint": "^9.0.0",
- "prettier": "latest",
+ "prettier": "^3.9.6",
"tailwindcss": "^4.0.0",
"typescript": "^5.5.0",
- "typescript-eslint": "latest"
+ "typescript-eslint": "^8.65.0"
}
}
diff --git a/examples/inter-app-api-showcase/package.json b/examples/inter-app-api-showcase/package.json
index 1733ed14..3730770d 100644
--- a/examples/inter-app-api-showcase/package.json
+++ b/examples/inter-app-api-showcase/package.json
@@ -23,10 +23,10 @@
"devDependencies": {
"@wrnexus/cli": "0.8.6",
"@eslint/js": "^9.0.0",
- "@types/bun": "latest",
+ "@types/bun": "^1.3.14",
"eslint": "^9.0.0",
- "prettier": "latest",
+ "prettier": "^3.9.6",
"typescript": "^5.5.0",
- "typescript-eslint": "latest"
+ "typescript-eslint": "^8.65.0"
}
}
diff --git a/package.json b/package.json
index 513eea99..2bb01659 100644
--- a/package.json
+++ b/package.json
@@ -34,12 +34,8 @@
"format": "prettier . --write",
"format:check": "prettier . --check",
"check": "bun run typecheck && bun run lint && bun run check:component-imports && bun run test && bun run format:check",
- "verify:0.3": "node scripts/verify-0.3.mjs",
- "verify:0.4": "node scripts/verify-0.4.mjs",
- "validate:0.4": "node scripts/validate-0.4.mjs",
"auth:dev": "bun run --cwd examples/auth-showcase dev",
"auth:check": "bun run --cwd packages/auth check",
- "validate:auth": "node scripts/validate-auth.mjs",
"validate:0.6": "node scripts/validate-0.6.mjs",
"validate:0.7": "node scripts/validate-0.7.mjs",
"security:framework": "node scripts/security-performance-audit.mjs",
@@ -50,7 +46,7 @@
"check:ui-visual": "node scripts/check-ui-visual-contract.mjs",
"generate:ui-visual": "node scripts/check-ui-visual-contract.mjs --write",
"sbom": "node scripts/generate-sbom.mjs",
- "benchmark:framework": "node --experimental-transform-types scripts/benchmark-framework.mjs",
+ "benchmark:framework": "node --experimental-transform-types scripts/benchmark-framework.mjs --write",
"check:production": "bun run check:workspace && bun run check:public-api && bun run check:ui-visual && bun run validate:0.8 && bun run security:framework && bun run security:asvs && bun run check:editor-compiler && bun run check:editor-language-server && bun run check:editor-extension && bun run check",
"validate:staging": "node --experimental-transform-types scripts/test-package-integrity.mjs",
"stage:packages": "bun run scripts/publish-packages.ts",
diff --git a/scripts/benchmark-framework.mjs b/scripts/benchmark-framework.mjs
index 543fdb19..e732f307 100644
--- a/scripts/benchmark-framework.mjs
+++ b/scripts/benchmark-framework.mjs
@@ -9,6 +9,7 @@ import { secureJsonStringify } from "../packages/security/src/serialization.ts";
import { parse } from "../packages/syntax/src/index.ts";
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const writeReport = process.argv.includes("--write");
const frameworkVersion = JSON.parse(readFileSync(join(root, "package.json"), "utf8")).version;
const componentSource = readFileSync(
join(root, "packages", "ui", "components", "button.wrn"),
@@ -60,12 +61,14 @@ const report = {
environment: { runtime: process.version, platform: process.platform, arch: process.arch },
benchmarks: benchmarks.map(({ result, budget }) => ({ result, budget })),
};
-const reportDir = join(root, ".wrnexus", "reports");
-mkdirSync(reportDir, { recursive: true });
-writeFileSync(
- join(reportDir, `benchmark-${frameworkVersion}.json`),
- `${JSON.stringify(report, null, 2)}\n`,
-);
+if (writeReport) {
+ const reportDir = join(root, ".wrnexus", "reports");
+ mkdirSync(reportDir, { recursive: true });
+ writeFileSync(
+ join(reportDir, `benchmark-${frameworkVersion}.json`),
+ `${JSON.stringify(report, null, 2)}\n`,
+ );
+}
console.log(
JSON.stringify(
report.benchmarks.map(({ result }) => ({
diff --git a/scripts/install-captcha.mjs b/scripts/install-captcha.mjs
index 24929fe6..1712a26e 100644
--- a/scripts/install-captcha.mjs
+++ b/scripts/install-captcha.mjs
@@ -12,11 +12,19 @@ const args = process.argv.slice(2);
const positional = args.filter((arg) => !arg.startsWith("--"));
const target = resolve(positional[0] ?? process.cwd());
const flags = new Set(args.filter((arg) => arg.startsWith("--")));
+const install = flags.has("--install");
const force = flags.has("--force");
const withShowcase = flags.has("--with-showcase");
const withManaged = flags.has("--with-managed-service");
const runChecks = flags.has("--check");
+if (!install) {
+ console.log(
+ "Dry run: captcha installation would update the target repository. Re-run with --install to apply it.",
+ );
+ process.exit(0);
+}
+
async function exists(path) {
try {
await stat(path);
diff --git a/scripts/reset-ui-components.mjs b/scripts/reset-ui-components.mjs
deleted file mode 100644
index f71df4a6..00000000
--- a/scripts/reset-ui-components.mjs
+++ /dev/null
@@ -1,447 +0,0 @@
-import { readFileSync, readdirSync, unlinkSync, writeFileSync } from "node:fs";
-import { join } from "node:path";
-import process from "node:process";
-
-const root = join(import.meta.dirname, "..");
-const uiRoot = join(root, "packages", "ui");
-const componentsDir = join(uiRoot, "components");
-const preservedButton = readFileSync(join(componentsDir, "Button.wrn"), "utf8");
-const preservedAdvancedSelect = readFileSync(join(componentsDir, "AdvancedSelect.wrn"), "utf8");
-
-const groups = {
- layout: [
- "Container",
- "Columns",
- "Grid",
- "LayoutSplitter",
- "Typography",
- "Image",
- "Link",
- "Divider",
- "Kbd",
- "CustomScrollbar",
- ],
- base: [
- "Accordion",
- "Alert",
- "Avatar",
- "AvatarGroup",
- "Badge",
- "Blockquote",
- "Button",
- "ButtonGroup",
- "Card",
- "ChatBubble",
- "Carousel",
- "Collapse",
- "DatePicker",
- "DeviceFrame",
- "List",
- "ListGroup",
- "LegendIndicator",
- "Progress",
- "FileUploadProgress",
- "Rating",
- "Skeleton",
- "Spinner",
- "StyledIcon",
- "Toast",
- "Timeline",
- "TreeView",
- "Marquee",
- ],
- navigation: [
- "Navbar",
- "MegaMenu",
- "Nav",
- "Tabs",
- "Sidebar",
- "Scrollspy",
- "Breadcrumb",
- "Pagination",
- "Stepper",
- ],
- forms: [
- "Input",
- "InputGroup",
- "Textarea",
- "FileInput",
- "Checkbox",
- "Radio",
- "Switch",
- "Select",
- "RangeSlider",
- "ColorPicker",
- "TimePicker",
- ],
- "advanced-forms": [
- "AdvancedSelect",
- "Combobox",
- "SearchBox",
- "InputNumber",
- "StrongPassword",
- "TogglePassword",
- "ToggleCount",
- "CopyMarkup",
- "PinInput",
- ],
- overlays: ["Dropdown", "ContextMenu", "Modal", "Drawer", "Popover", "Tooltip"],
- tables: ["DataTable"],
- integrations: [
- "AdvancedRangeSlider",
- "AdvancedDatePicker",
- "Chart",
- "Clipboard",
- "Confetti",
- "DataMap",
- "DragAndDrop",
- "FileUpload",
- "Map",
- "ToastNotifications",
- "WysiwygEditor",
- ],
-};
-
-const displayName = (name) => name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
-const kebab = (name) => displayName(name).replace(/\s+/g, "-").toLowerCase();
-
-function layoutComponent(name) {
- if (name === "Image")
- return `component Image {
- props {
- src = ""
- alt = ""
- width = ""
- height = ""
- loading = "lazy"
- rounded = false
- class = ""
- }
- view {
-
-
-
-
- }
-}
-`;
- if (name === "Link")
- return `component Link {
- props {
- label = "Link"
- href = "#"
- target = ""
- rel = ""
- external = false
- class = ""
- }
- view {
- {label}{#if external}↗{/if}
- }
-}
-`;
- if (name === "Divider")
- return `component Divider {
- props {
- label = ""
- orientation = "horizontal"
- class = ""
- }
- view {
-
{#if label}{label}{/if}
- }
-}
-`;
- if (name === "Kbd")
- return `component Kbd {
- props {
- label = "⌘ K"
- class = ""
- }
- view { {label} }
-}
-`;
- const tag = name === "Typography" ? "article" : "div";
- return `component ${name} {
- props {
- columns = 2
- gap = "md"
- maxWidth = "xl"
- class = ""
- }
- view {
- <${tag} class="wire-next wire-next--${kebab(name)} wire-next--gap-{gap} wire-next--columns-{columns} wire-next--max-{maxWidth} {class}">${tag}>
- }
-}
-`;
-}
-
-function collectionComponent(name) {
- if (name === "Accordion" || name === "Collapse")
- return `component ${name} {
- props {
- items = []
- multiple = false
- class = ""
- }
- view {
-
- {#each items as item}
{item.label}
{item.content}
{/each}
-
-
- }
-}
-`;
- if (name === "Progress" || name === "FileUploadProgress")
- return `component ${name} {
- props {
- label = "Progress"
- value = 50
- max = 100
- showValue = true
- class = ""
- }
- view {
-
-
{label}{#if showValue}{value}%{/if}
-
-
- }
-}
-`;
- if (name === "Spinner" || name === "Skeleton")
- return `component ${name} {
- props {
- label = "Loading"
- size = "md"
- lines = 3
- class = ""
- }
- view {
-
- {#if "${name}" === "Skeleton"}{#each Array(lines).fill(0) as line}{/each}{:else}{/if}
-
- }
-}
-`;
- return `component ${name} {
- props {
- title = "${displayName(name)}"
- description = ""
- items = []
- variant = "default"
- class = ""
- }
- view {
-
- {#if title}{title}{/if}
- {#if description}{description}
{/if}
- {#if items}{#each items as item}{item.label}{/each}
{/if}
-
-
- }
-}
-`;
-}
-
-function navigationComponent(name) {
- return `component ${name} {
- props {
- label = "${displayName(name)}"
- items = []
- active = ""
- orientation = "horizontal"
- class = ""
- }
- view {
-
- }
-}
-`;
-}
-
-function formComponent(name) {
- const typeByName = {
- Input: "text",
- SearchBox: "search",
- InputNumber: "number",
- StrongPassword: "password",
- TogglePassword: "password",
- PinInput: "text",
- DatePicker: "date",
- AdvancedDatePicker: "date",
- TimePicker: "time",
- ColorPicker: "color",
- RangeSlider: "range",
- AdvancedRangeSlider: "range",
- FileInput: "file",
- };
- if (name === "Textarea" || name === "WysiwygEditor")
- return `component ${name} {
- props {
- label = "${displayName(name)}"
- name = ""
- value = ""
- placeholder = ""
- rows = 5
- disabled = false
- required = false
- class = ""
- }
- view {
-
- }
-}
-`;
- if (name === "Checkbox" || name === "Radio" || name === "Switch")
- return `component ${name} {
- props {
- label = "${displayName(name)}"
- name = ""
- value = "on"
- checked = false
- disabled = false
- class = ""
- }
- view {
-
- }
-}
-`;
- if (name.includes("Select") || name === "Combobox")
- return `component ${name} {
- props {
- label = "${displayName(name)}"
- name = ""
- value = ""
- options = []
- placeholder = "Select an option"
- multiple = false
- disabled = false
- class = ""
- }
- view {
-
- }
-}
-`;
- const inputType = typeByName[name] ?? "text";
- return `component ${name} {
- props {
- label = "${displayName(name)}"
- name = ""
- value = ""
- placeholder = ""
- type = "${inputType}"
- min = ""
- max = ""
- step = ""
- disabled = false
- required = false
- class = ""
- }
- view {
-
- }
-}
-`;
-}
-
-function overlayComponent(name) {
- const modal = name === "Modal" || name === "Drawer";
- return `component ${name} {
- props {
- title = "${displayName(name)}"
- description = ""
- open = false
- placement = "bottom"
- closeLabel = "Close"
- class = ""
- }
- view {
-
-
- {#if description}
{description}
{/if}
-
-
- }
-}
-`;
-}
-
-function dataComponent(name) {
- if (name === "DataTable")
- return `component ${name} {
- props {
- caption = "${displayName(name)}"
- columns = []
- rows = []
- striped = true
- class = ""
- }
- view {
- {caption}{#each columns as column}| {column.label} | {/each}
{#each rows as row}{#each columns as column}| {row[column.key]} | {/each}
{/each}
- }
-}
-`;
- return collectionComponent(name);
-}
-
-function sourceFor(name, category) {
- if (name === "Button") return preservedButton;
- if (name === "AdvancedSelect") return preservedAdvancedSelect;
- if (category === "layout") return layoutComponent(name);
- if (category === "navigation") return navigationComponent(name);
- if (category === "forms" || category === "advanced-forms") return formComponent(name);
- if (category === "overlays") return overlayComponent(name);
- if (category === "tables" || category === "integrations") return dataComponent(name);
- if (name === "DatePicker") return formComponent(name);
- return collectionComponent(name);
-}
-
-function withUniversalDesignProps(source) {
- let next = source;
- if (!/^\s+color\s*=/m.test(next)) {
- next = next.replace(/(\s+props\s*\{\r?\n)/, '$1 color = "primary"\n');
- }
- if (!/^\s+size\s*=/m.test(next)) {
- next = next.replace(/(\s+props\s*\{\r?\n)/, '$1 size = "default"\n');
- }
- return next.replace(
- /class="wire-next(?![^"]*wire-next--color-)/,
- 'class="wire-next wire-next--color-{color} wire-next--size-{size}',
- );
-}
-
-const entries = Object.entries(groups).flatMap(([category, names]) =>
- names.map((name) => ({
- name,
- category,
- purpose: `Theme-aware, responsive ${displayName(name).toLowerCase()} component.`,
- })),
-);
-
-for (const file of readdirSync(componentsDir)) {
- if (file.endsWith(".wrn")) unlinkSync(join(componentsDir, file));
-}
-for (const entry of entries) {
- writeFileSync(
- join(componentsDir, `${entry.name}.wrn`),
- withUniversalDesignProps(sourceFor(entry.name, entry.category)),
- "utf8",
- );
-}
-
-writeFileSync(
- join(uiRoot, "component-catalog.json"),
- `${JSON.stringify({ sourceDocuments: ["Screenshot-directed WRNexus UI catalog reset"], components: entries }, null, 2)}\n`,
-);
-writeFileSync(
- join(uiRoot, "component-migrations.json"),
- `${JSON.stringify({ generatedFrom: "full component catalog reset", removedCount: 0, replacements: {} }, null, 2)}\n`,
-);
-
-process.stdout.write(`Reset @wrnexus/ui to ${entries.length} canonical components.\n`);
diff --git a/scripts/validate-0.4.mjs b/scripts/validate-0.4.mjs
deleted file mode 100644
index d657aaf5..00000000
--- a/scripts/validate-0.4.mjs
+++ /dev/null
@@ -1,28 +0,0 @@
-/* global console */
-import { spawnSync } from "node:child_process";
-import process from "node:process";
-
-const root = process.cwd();
-const bun = process.platform === "win32" ? "bun.exe" : "bun";
-
-function run(command, args) {
- console.log(`\n> ${command} ${args.join(" ")}`);
- const result = spawnSync(command, args, {
- cwd: root,
- stdio: "inherit",
- env: process.env,
- });
- if (result.error) throw result.error;
- if (result.status !== 0) process.exit(result.status ?? 1);
-}
-
-run(process.execPath, ["scripts/verify-0.4.mjs"]);
-run(bun, ["run", "check"]);
-run(bun, ["test", "integration"]);
-run(bun, ["run", "--cwd", "examples/basic-app", "test"]);
-run(bun, ["run", "--cwd", "examples/basic-app", "build"]);
-run(bun, ["run", "--cwd", "examples/component-showcase", "check"]);
-run(bun, ["run", "--cwd", "examples/captcha-showcase", "check"]);
-run(bun, ["test", "services/managed-captcha"]);
-
-console.log("\n✓ WRNexusJS 0.4.0 complete validation passed.\n");
diff --git a/scripts/validate-0.6.mjs b/scripts/validate-0.6.mjs
index a89369ca..26170a9f 100644
--- a/scripts/validate-0.6.mjs
+++ b/scripts/validate-0.6.mjs
@@ -335,8 +335,8 @@ for (const [label, condition] of [
[
"peer client-function bindings",
clientCodegenSource.includes("context.functions") &&
- clientCodegenSource.includes("const scopedContext = { ...context, functions }") &&
- clientCodegenSource.includes("handler(scopedContext, ...args)"),
+ clientCodegenSource.includes("context.functions[name](...args)") &&
+ clientCodegenSource.includes("(...__wrnexusPeerArgs) => context.functions"),
],
[
"drift-free ToggleCount animation scheduling",
diff --git a/scripts/validate-auth.mjs b/scripts/validate-auth.mjs
deleted file mode 100644
index f09a830c..00000000
--- a/scripts/validate-auth.mjs
+++ /dev/null
@@ -1,24 +0,0 @@
-/* global console */
-import { spawnSync } from "node:child_process";
-import process from "node:process";
-
-const root = process.cwd();
-const bun = process.platform === "win32" ? "bun.exe" : "bun";
-
-function run(command, args) {
- console.log(`\n> ${command} ${args.join(" ")}`);
- const result = spawnSync(command, args, {
- cwd: root,
- stdio: "inherit",
- env: process.env,
- });
- if (result.error) throw result.error;
- if (result.status !== 0) process.exit(result.status ?? 1);
-}
-
-run(process.execPath, ["scripts/verify-auth.mjs"]);
-run(bun, ["run", "typecheck"]);
-run(bun, ["test", "packages/auth"]);
-run(bun, ["run", "--cwd", "examples/auth-showcase", "check"]);
-
-console.log("\n✓ @wrnexus/auth validation passed.\n");
diff --git a/scripts/verify-0.3.mjs b/scripts/verify-0.3.mjs
deleted file mode 100644
index 88abcca9..00000000
--- a/scripts/verify-0.3.mjs
+++ /dev/null
@@ -1,134 +0,0 @@
-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.3.0";
-const errors = [];
-const notes = [];
-const readJson = (file) => JSON.parse(readFileSync(file, "utf8"));
-
-function walk(dir, name = "package.json") {
- const out = [];
- if (!existsSync(dir)) return out;
- for (const entry of readdirSync(dir)) {
- if (entry === "node_modules" || entry === ".git" || entry === "dist") continue;
- const path = join(dir, entry);
- const stat = statSync(path);
- if (stat.isDirectory()) out.push(...walk(path, name));
- else if (entry === name) out.push(path);
- }
- return out;
-}
-
-const packageFiles = [join(root, "package.json"), ...walk(join(root, "packages"))];
-const packages = new Map();
-for (const file of packageFiles) {
- const pkg = readJson(file);
- if (pkg.name) packages.set(pkg.name, { file, pkg });
- if (file.includes(`${join(root, "packages")}/`) && pkg.version !== VERSION) {
- errors.push(`${relative(root, file)} has version ${pkg.version ?? ""}`);
- }
-}
-if (readJson(join(root, "package.json")).version !== VERSION) {
- errors.push(`root package version is not ${VERSION}`);
-}
-
-for (const { file, pkg } of packages.values()) {
- for (const field of ["dependencies", "devDependencies", "peerDependencies"]) {
- for (const [name, range] of Object.entries(pkg[field] ?? {})) {
- if (String(range).startsWith("workspace:") && !packages.has(name)) {
- errors.push(`${relative(root, file)} references missing workspace ${name}`);
- }
- }
- }
-}
-
-for (const required of [
- "packages/syntax/src/index.ts",
- "packages/plugin/src/index.ts",
- "scripts/build-editor-compiler.mjs",
- "docs/WRN-LANGUAGE-SPEC-1.0.md",
- "docs/ARCHITECTURE-0.3.md",
- "docs/UPGRADE-0.3.md",
- "docs/40-POINT-IMPLEMENTATION-0.3.md",
- "docs/TEST-CHECKLIST-0.3.md",
- "docs/AUDIT-0.3.md",
-]) {
- if (!existsSync(join(root, required))) errors.push(`missing ${required}`);
-}
-
-const updateSource = readFileSync(join(root, "packages/cli/src/update.ts"), "utf8");
-const migrationPairs = [
- ...updateSource.matchAll(/version:\s*"([^"]+)"[\s\S]*?id:\s*"([^"]+)"/g),
-].map((match) => ({ version: match[1], id: match[2] }));
-const ids = new Set();
-for (const migration of migrationPairs) {
- if (ids.has(migration.id)) errors.push(`duplicate migration id ${migration.id}`);
- ids.add(migration.id);
-}
-if (!migrationPairs.some((migration) => migration.version === VERSION)) {
- errors.push(`missing ${VERSION} updater migration`);
-}
-
-for (const jsonFile of [
- "editors/vscode/package.json",
- "editors/vscode/package-lock.json",
- "editors/vscode/snippets/wrn.json",
- "editors/vscode/syntaxes/wrn.tmLanguage.json",
-]) {
- try {
- readJson(join(root, jsonFile));
- } catch (error) {
- errors.push(`${jsonFile} is invalid JSON: ${error instanceof Error ? error.message : error}`);
- }
-}
-
-try {
- const lockText = readFileSync(join(root, "bun.lock"), "utf8").replace(/,\s*([}\]])/g, "$1");
- const lock = JSON.parse(lockText);
- for (const workspace of ["packages/syntax", "packages/plugin"]) {
- if (!lock.workspaces?.[workspace]) errors.push(`bun.lock missing ${workspace}`);
- }
- for (const name of ["@wrnexus/syntax", "@wrnexus/plugin"]) {
- if (!lock.packages?.[name]) errors.push(`bun.lock missing workspace link ${name}`);
- }
-} catch (error) {
- errors.push(`bun.lock structure is invalid: ${error instanceof Error ? error.message : error}`);
-}
-
-const compilerParser = readFileSync(join(root, "packages/compiler/src/parser.ts"), "utf8");
-if (!compilerParser.includes("@wrnexus/syntax")) {
- errors.push("compiler parser is not a compatibility re-export of @wrnexus/syntax");
-}
-
-const tsconfig = readJson(join(root, "tsconfig.json"));
-for (const name of packages.keys()) {
- if (name.startsWith("@wrnexus/") && !tsconfig.compilerOptions?.paths?.[name]) {
- errors.push(`tsconfig paths missing ${name}`);
- }
-}
-
-notes.push(`${packages.size} named packages checked`);
-notes.push(`${migrationPairs.length} migration entries checked`);
-notes.push("editor JSON and Bun workspace lock structure 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"),
- );
-}
diff --git a/scripts/verify-0.4.mjs b/scripts/verify-0.4.mjs
deleted file mode 100644
index 5db0cab7..00000000
--- a/scripts/verify-0.4.mjs
+++ /dev/null
@@ -1,218 +0,0 @@
-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 (/