release: prepare WRNexusJS 0.8.8
Quality / quality (ubuntu-latest) (push) Failing after 9m49s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-11 13:40:03 +05:30
parent 816594a58b
commit feff30a2b5
97 changed files with 769 additions and 10487 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@wrnexus/cli",
"version": "0.8.7",
"version": "0.8.8",
"type": "module",
"main": "src/index.ts",
"exports": {
+12 -18
View File
@@ -24,6 +24,12 @@ function literalUnion(values: Iterable<string>): string {
return unique.length ? unique.map((value) => JSON.stringify(value)).join(" | ") : "never";
}
function generatedContractMap(name: string, members: string): string {
return members.trim()
? `interface ${name} {\n${members}\n }`
: `type ${name} = Record<string, never>;`;
}
function typeImport(fromDirectory: string, file: string): string {
const path = relative(fromDirectory, file).replace(/\\/g, "/");
return JSON.stringify(path.startsWith(".") ? path : `./${path}`);
@@ -203,24 +209,12 @@ declare namespace WRNexusGenerated {
type TranslationKey = ${literalUnion(localeKeys)};
type QueueName = ${literalUnion(queueNames)};
type CacheKey = ${literalUnion(cacheKeys)};
interface Components {
${componentMap}
}
interface ApiContracts {
${apiContracts}
}
interface MiddlewareContexts {
${middlewareContracts}
}
interface DatabaseQueries {
${databaseContracts}
}
interface RealtimeMessages {
${realtimeContracts}
}
interface QueuePayloads {
${queueContracts}
}
${generatedContractMap("Components", componentMap)}
${generatedContractMap("ApiContracts", apiContracts)}
${generatedContractMap("MiddlewareContexts", middlewareContracts)}
${generatedContractMap("DatabaseQueries", databaseContracts)}
${generatedContractMap("RealtimeMessages", realtimeContracts)}
${generatedContractMap("QueuePayloads", queueContracts)}
type ApplicationConfig = ${configFile ? `(typeof import(${typeImport(typeDir, configFile)}))["default"]` : "Record<string, never>"};
}
`;
+20
View File
@@ -33,6 +33,7 @@ import { pathToFileURL } from "node:url";
import { formatWrn, parse } from "@wrnexus/syntax";
import { AI_GUIDE, CLAUDE_MD } from "./ai-guide.ts";
import { inspectProject, type DoctorCheck } from "./doctor.ts";
import { generateApplicationTypes } from "./types.ts";
/** The version of the CLI currently running (its own package.json). */
function cliVersion(): string {
@@ -1783,6 +1784,10 @@ const MIGRATIONS: Migration[] = [
description:
"Adds compatible imports, type checking, stores, and legacy behavior configuration.",
apply(ctx) {
// Workspace manifests are updated alongside each configured application,
// but they are not themselves runnable apps. Keep app-owned declarations
// under the individual app roots instead of creating `<workspace>/app`.
if (!existsSync(join(ctx.appRoot, "app", "pages"))) return;
updateV060Config(ctx);
const types = join(ctx.appRoot, "app", "types");
if (!existsSync(types)) {
@@ -2115,6 +2120,17 @@ const MIGRATIONS: Migration[] = [
// select for non-link items as well as navigation items.
},
},
{
version: "0.8.8",
id: "0.8.8-editor-types-and-update-verification",
description:
"Adds typed page context support and regenerates lint-safe application declarations before update verification.",
apply() {
// No source rewrite is required. The 0.8.8 CLI regenerates application
// declarations immediately before verification, repairing stale empty
// interface contracts produced by earlier releases.
},
},
];
/** Release tooling uses this to require an explicit migration entry per version. */
@@ -2297,6 +2313,10 @@ export function blockingVerificationChecks(checks: DoctorCheck[]): DoctorCheck[]
}
function verifyApp(app: UpdatedApp): boolean {
// Generated declarations must reflect the newly installed CLI before the
// application's lint/typecheck scripts inspect them. This also repairs stale
// generated output left by an older framework version.
if (existsSync(join(app.root, "app", "pages"))) generateApplicationTypes(app.root);
const health = inspectProject(app.root);
const failed = blockingVerificationChecks(health);
+3
View File
@@ -48,6 +48,9 @@ test("generate types emits application-wide deterministic contracts", () => {
expect(output).toContain('"/realtime/chat": RealtimeMessage<');
expect(output).toContain("interface QueuePayloads");
expect(output).toContain('"email": QueuePayload<');
expect(output).toContain("type MiddlewareContexts = Record<string, never>");
expect(output).toContain("type DatabaseQueries = Record<string, never>");
expect(output).not.toMatch(/interface\s+\w+\s*\{\s*\}/);
});
test("application checker validates every wrn source", () => {
+24
View File
@@ -169,6 +169,30 @@ test("update verification skips unavailable scripts", () => {
expect(verificationCommands({ check: "bun run lint" })).toEqual([["run", "check"]]);
});
test("0.6 type declarations are created only inside runnable apps", () => {
const workspaceRoot = mkdtempSync(join(tmpdir(), "wrnexus-update-workspace-"));
const appRoot = join(workspaceRoot, "apps", "web");
mkdirSync(join(appRoot, "app", "pages"), { recursive: true });
writeFileSync(
join(workspaceRoot, "package.json"),
JSON.stringify({ name: "workspace", wrnexus: { version: "0.5.9" } }),
);
writeFileSync(
join(appRoot, "package.json"),
JSON.stringify({ name: "web", wrnexus: { version: "0.5.9" } }),
);
try {
updateApp(workspaceRoot, "0.6.0", false);
updateApp(appRoot, "0.6.0", false);
expect(existsSync(join(workspaceRoot, "app", "types", "global.d.ts"))).toBe(false);
expect(existsSync(join(appRoot, "app", "types", "global.d.ts"))).toBe(true);
} finally {
rmSync(workspaceRoot, { recursive: true, force: true });
}
});
test("0.3 WRN source normalization is conservative and idempotent", async () => {
const { migrateWrnSource } = await import("../src/update.ts");
const source = `component Card {