remove hazardous legacy scripts and gate repository writes
This commit is contained in:
@@ -1316,6 +1316,7 @@
|
||||
"createOutputProxy",
|
||||
"createServerProxy",
|
||||
"getActionRuntime",
|
||||
"getComponentControllerRuntime",
|
||||
"getNavRuntime",
|
||||
"getReactiveRuntime",
|
||||
"getRealtimeRuntime",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-5
@@ -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",
|
||||
|
||||
@@ -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 }) => ({
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
<figure class="wire-next wire-next--image {rounded ? 'wire-next--rounded' : ''} {class}">
|
||||
<img src="{src}" alt="{alt}" width="{width}" height="{height}" loading="{loading}" />
|
||||
<slot />
|
||||
</figure>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Link")
|
||||
return `component Link {
|
||||
props {
|
||||
label = "Link"
|
||||
href = "#"
|
||||
target = ""
|
||||
rel = ""
|
||||
external = false
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<a {...attrs} href="{href}" target="{target}" rel="{rel}" class="wire-next wire-next--link {class}"><slot>{label}</slot>{#if external}<span aria-hidden="true">↗</span>{/if}</a>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Divider")
|
||||
return `component Divider {
|
||||
props {
|
||||
label = ""
|
||||
orientation = "horizontal"
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<div class="wire-next wire-next--divider wire-next--divider-{orientation} {class}" role="separator" aria-orientation="{orientation}">{#if label}<span>{label}</span>{/if}</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Kbd")
|
||||
return `component Kbd {
|
||||
props {
|
||||
label = "⌘ K"
|
||||
class = ""
|
||||
}
|
||||
view { <kbd class="wire-next wire-next--kbd {class}"><slot>{label}</slot></kbd> }
|
||||
}
|
||||
`;
|
||||
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}"><slot /></${tag}>
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function collectionComponent(name) {
|
||||
if (name === "Accordion" || name === "Collapse")
|
||||
return `component ${name} {
|
||||
props {
|
||||
items = []
|
||||
multiple = false
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<div class="wire-next wire-next--${kebab(name)} {class}">
|
||||
{#each items as item}<details open="{item.open}"><summary>{item.label}</summary><div>{item.content}</div></details>{/each}
|
||||
<slot />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Progress" || name === "FileUploadProgress")
|
||||
return `component ${name} {
|
||||
props {
|
||||
label = "Progress"
|
||||
value = 50
|
||||
max = 100
|
||||
showValue = true
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<div class="wire-next wire-next--${kebab(name)} {class}">
|
||||
<div class="wire-next__row"><span>{label}</span>{#if showValue}<strong>{value}%</strong>{/if}</div>
|
||||
<progress value="{value}" max="{max}"></progress>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Spinner" || name === "Skeleton")
|
||||
return `component ${name} {
|
||||
props {
|
||||
label = "Loading"
|
||||
size = "md"
|
||||
lines = 3
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<div class="wire-next wire-next--${kebab(name)} wire-next--size-{size} {class}" role="status" aria-label="{label}">
|
||||
{#if "${name}" === "Skeleton"}{#each Array(lines).fill(0) as line}<span></span>{/each}{:else}<i aria-hidden="true"></i>{/if}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
return `component ${name} {
|
||||
props {
|
||||
title = "${displayName(name)}"
|
||||
description = ""
|
||||
items = []
|
||||
variant = "default"
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<section class="wire-next wire-next--${kebab(name)} wire-next--variant-{variant} {class}">
|
||||
{#if title}<strong>{title}</strong>{/if}
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
{#if items}<div class="wire-next__items">{#each items as item}<span>{item.label}</span>{/each}</div>{/if}
|
||||
<slot />
|
||||
</section>
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function navigationComponent(name) {
|
||||
return `component ${name} {
|
||||
props {
|
||||
label = "${displayName(name)}"
|
||||
items = []
|
||||
active = ""
|
||||
orientation = "horizontal"
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<nav class="wire-next wire-next--${kebab(name)} wire-next--{orientation} {class}" aria-label="{label}">
|
||||
{#each items as item}<a href="{item.href}" aria-current="{item.value === active ? 'page' : ''}">{item.label}</a>{/each}
|
||||
<slot />
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
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 {
|
||||
<label class="wire-next wire-next--field wire-next--${kebab(name)} {class}"><span>{label}</span><textarea {...attrs} name="{name}" placeholder="{placeholder}" rows="{rows}" disabled="{disabled}" required="{required}">{value}</textarea></label>
|
||||
}
|
||||
}
|
||||
`;
|
||||
if (name === "Checkbox" || name === "Radio" || name === "Switch")
|
||||
return `component ${name} {
|
||||
props {
|
||||
label = "${displayName(name)}"
|
||||
name = ""
|
||||
value = "on"
|
||||
checked = false
|
||||
disabled = false
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<label class="wire-next wire-next--choice wire-next--${kebab(name)} {class}"><input {...attrs} type="${name === "Radio" ? "radio" : "checkbox"}" name="{name}" value="{value}" checked="{checked}" disabled="{disabled}" /><span>{label}</span></label>
|
||||
}
|
||||
}
|
||||
`;
|
||||
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 {
|
||||
<label class="wire-next wire-next--field wire-next--${kebab(name)} {class}"><span>{label}</span><select {...attrs} name="{name}" multiple="{multiple}" disabled="{disabled}"><option value="">{placeholder}</option>{#each options as option}<option value="{option.value}" selected="{option.value === value}">{option.label}</option>{/each}</select></label>
|
||||
}
|
||||
}
|
||||
`;
|
||||
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 {
|
||||
<label class="wire-next wire-next--field wire-next--${kebab(name)} {class}"><span>{label}</span><input {...attrs} type="{type}" name="{name}" value="{value}" placeholder="{placeholder}" min="{min}" max="{max}" step="{step}" disabled="{disabled}" required="{required}" /></label>
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
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 {
|
||||
<div data-show="{open}" class="wire-next wire-next--${kebab(name)} wire-next--placement-{placement} {class}" role="${modal ? "dialog" : "region"}" aria-modal="${modal}" aria-label="{title}">
|
||||
<header><strong>{title}</strong><button type="button" aria-label="{closeLabel}">×</button></header>
|
||||
{#if description}<p>{description}</p>{/if}
|
||||
<slot />
|
||||
</div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
function dataComponent(name) {
|
||||
if (name === "DataTable")
|
||||
return `component ${name} {
|
||||
props {
|
||||
caption = "${displayName(name)}"
|
||||
columns = []
|
||||
rows = []
|
||||
striped = true
|
||||
class = ""
|
||||
}
|
||||
view {
|
||||
<div class="wire-next wire-next--table {class}"><table><caption>{caption}</caption><thead><tr>{#each columns as column}<th>{column.label}</th>{/each}</tr></thead><tbody>{#each rows as row}<tr>{#each columns as column}<td>{row[column.key]}</td>{/each}</tr>{/each}</tbody></table><slot /></div>
|
||||
}
|
||||
}
|
||||
`;
|
||||
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`);
|
||||
@@ -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");
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
@@ -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 ?? "<missing>"}`);
|
||||
}
|
||||
}
|
||||
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"),
|
||||
);
|
||||
}
|
||||
@@ -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 ?? "<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"),
|
||||
);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* global console */
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const root = process.cwd();
|
||||
const required = [
|
||||
"packages/auth/package.json",
|
||||
"packages/auth/src/engine.ts",
|
||||
"packages/auth/src/plugin.ts",
|
||||
"packages/auth/src/routes/api.ts",
|
||||
"packages/auth/src/stores/memory.ts",
|
||||
"packages/auth/src/stores/sql.ts",
|
||||
"packages/auth/assets/client/auth.js",
|
||||
"packages/auth/migrations/001_auth.sql",
|
||||
"examples/auth-showcase/package.json",
|
||||
];
|
||||
for (const file of required) {
|
||||
if (!existsSync(join(root, file))) throw new Error(`Missing authentication file: ${file}`);
|
||||
}
|
||||
const manifest = JSON.parse(readFileSync(join(root, "packages/auth/package.json"), "utf8"));
|
||||
if (manifest.name !== "@wrnexus/auth") throw new Error("Invalid authentication package name");
|
||||
if (manifest.version !== "0.5.0") throw new Error("Invalid authentication package version");
|
||||
const components = readdirSync(join(root, "packages/auth/components")).filter((file) =>
|
||||
file.endsWith(".wrn"),
|
||||
);
|
||||
if (components.length < 16)
|
||||
throw new Error(`Expected at least 16 auth components, received ${components.length}`);
|
||||
const plugin = readFileSync(join(root, "packages/auth/src/plugin.ts"), "utf8");
|
||||
for (const expected of [
|
||||
"clientRuntimes",
|
||||
"routeEntries",
|
||||
"middleware",
|
||||
"migrations",
|
||||
"devToolbarPanels",
|
||||
]) {
|
||||
if (!plugin.includes(expected)) throw new Error(`Auth plugin is missing ${expected}`);
|
||||
}
|
||||
const runtime = readFileSync(join(root, "packages/auth/assets/client/auth.js"), "utf8");
|
||||
if (!runtime.includes("navigator.credentials")) throw new Error("Passkey runtime is incomplete");
|
||||
if (runtime.includes("document.write"))
|
||||
throw new Error("Passkey runtime contains unsafe document.write");
|
||||
console.log(`@wrnexus/auth structural verification passed (${components.length} components).`);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/managed-captcha-service",
|
||||
"version": "0.8.0",
|
||||
"version": "0.8.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user