diff --git a/docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md b/docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md index 0a2d2318..038b9070 100644 --- a/docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md +++ b/docs/WRNEXUS-COMPLETE-FEATURE-REPORT.md @@ -119,11 +119,11 @@ Legacy API binding remains valid: `ssr { api users GET /api/users { return users | Legacy/earlier approach | Current approach | Compatibility/status | | ------------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Compiler-owned/internal parsing imports | Canonical `@wrnexus/syntax` lexer/parser/AST/diagnostics | Compiler re-exports remain for compatibility; direct internals are deprecated. | -| Implicit component discovery everywhere | Explicit imports and generated contracts | Upgraded apps can retain `legacyComponentDiscovery`; unresolved symbols are reported rather than guessed. | -| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts remain behind compatibility configuration. | +| Implicit component discovery everywhere | Explicit imports and generated contracts | Unresolved symbols are reported rather than guessed; there is no compatibility opt-out. | +| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts are no longer supported; the config key that toggled them was removed. | | `@event changed = function` in props | `outputs { changed(payload: Type) }` | v0.6 migration converts declarations; ambiguous payloads become `unknown`. | | `$emit("changed", value)` and `event.detail` | `output.changed(value)` and direct `payload` | Static cases auto-migrated; dynamic emit names require review. | -| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; compatibility default can preserve ambiguous behavior. | +| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; the legacy default runtime that preserved ambiguous behavior was removed. | | Manually copied CAPTCHA JS/script tags | Package-discovered client runtime/assets | v0.4 removes tags and archives old assets under `.wrnexus/legacy-assets/0.4.0`. | | Package components/routes/assets wired manually | Automatic package/plugin discovery and contribution registry | Current CLI/build/dev server inspect and consume contributions. | | Only scalar/quoted dynamic props | Native arrays/objects and JSX-style unquoted expressions | Quoted expression attributes remain supported. | @@ -137,7 +137,7 @@ Legacy API binding remains valid: `ssr { api users GET /api/users { return users | Per-component/global style placement inconsistencies | `style {}` promoted to document head | Current pipeline supports CSP, HMR and CSR navigation. | | Manually maintained API/component knowledge | Generated public API and component references plus validation gates | `check:public-api`, generated-type checks, UI visual contract and package audits detect drift. | -Compatibility flags visible in generated/upgraded config include `legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime`. New projects default legacy flags off; migration-created configs may enable them to preserve behavior until source modernization is complete. +`legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime` (and the other pre-1.0 compatibility keys) were removed before the first public release; a config that still sets any of them is rejected by validation rather than honored. ## 7. Diagnostics, security, and correctness guarantees diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts index 4a837d55..0ce15e75 100644 --- a/packages/cli/src/update.ts +++ b/packages/cli/src/update.ts @@ -950,6 +950,12 @@ export function updateApp( const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as Record; const from = appVersion(appRoot, pkg); const log = (m: string) => console.log(` ${m}`); + if (cmp(from, "0.8.0") < 0) { + log( + `⚠ detected version ${from}: automated migration from below 0.8.0 is no longer supported. ` + + `Only 0.8.x+ migrations will run; review the project manually before relying on this update.`, + ); + } const report = options.report ?? ({ diff --git a/packages/cli/test/update.test.ts b/packages/cli/test/update.test.ts index 2f8413d0..59db15cd 100644 --- a/packages/cli/test/update.test.ts +++ b/packages/cli/test/update.test.ts @@ -111,6 +111,33 @@ test("update verification still blocks real doctor errors", () => { ).toEqual([fatal]); }); +test("updateApp refreshes framework-owned files without marking an unverified update complete", () => { + const root = mkdtempSync(join(tmpdir(), "wrnexus-update-unverified-")); + mkdirSync(join(root, "app", "pages"), { recursive: true }); + writeFileSync( + join(root, "package.json"), + JSON.stringify({ + name: "current-app", + dependencies: { "@wrnexus/core": "0.8.5" }, + wrnexus: { version: "0.8.5" }, + }), + ); + + try { + const result = updateApp(root, "0.8.6", false); + expect(result).not.toBeNull(); + const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); + expect(pkg.dependencies["@wrnexus/core"]).toBe("^0.8.6"); + // updateApp only bumps dependency versions; the `wrnexus.version` marker is + // set later, once install/build/doctor verification actually succeeds. + expect(pkg.wrnexus.version).toBe("0.8.5"); + expect(readFileSync(join(root, "public", "llms.txt"), "utf8")).toContain("# WrNexus"); + expect(readFileSync(join(root, "CLAUDE.md"), "utf8").length).toBeGreaterThan(0); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + test("0.8 migration modernizes every WRN source with imports and a review report", () => { const root = mkdtempSync(join(tmpdir(), "wrnexus-update-current-source-")); mkdirSync(join(root, "app", "components"), { recursive: true }); @@ -163,3 +190,53 @@ test("0.8 migration modernizes every WRN source with imports and a review report rmSync(root, { recursive: true, force: true }); } }); + +test("updateApp warns when a marker-less project is below 0.8.0, and stays quiet at 0.8.x", () => { + const belowRoot = mkdtempSync(join(tmpdir(), "wrnexus-update-below-")); + const currentRoot = mkdtempSync(join(tmpdir(), "wrnexus-update-current-")); + try { + // No `wrnexus.version` marker and no installed @wrnexus/cli resolves to + // "0.0.0" — the common, benign case (e.g. examples/basic-app). + writeFileSync( + join(belowRoot, "package.json"), + JSON.stringify({ name: "marker-less-app", dependencies: {} }), + ); + writeFileSync( + join(currentRoot, "package.json"), + JSON.stringify({ + name: "current-app", + dependencies: { "@wrnexus/core": "^0.8.5" }, + wrnexus: { version: "0.8.5" }, + }), + ); + + const originalLog = console.log; + const capture = (): string[] => { + const logs: string[] = []; + console.log = (...args: unknown[]) => { + logs.push(args.join(" ")); + }; + return logs; + }; + + let belowLogs: string[]; + let currentLogs: string[]; + try { + belowLogs = capture(); + updateApp(belowRoot, "0.8.9", true); + + currentLogs = capture(); + updateApp(currentRoot, "0.8.9", true); + } finally { + console.log = originalLog; + } + + expect(belowLogs.some((l) => l.includes("no longer supported") && l.includes("0.0.0"))).toBe( + true, + ); + expect(currentLogs.some((l) => l.includes("no longer supported"))).toBe(false); + } finally { + rmSync(belowRoot, { recursive: true, force: true }); + rmSync(currentRoot, { recursive: true, force: true }); + } +}); diff --git a/scripts/generate-complete-framework-report.mjs b/scripts/generate-complete-framework-report.mjs index e998ae3f..977fe17c 100644 --- a/scripts/generate-complete-framework-report.mjs +++ b/scripts/generate-complete-framework-report.mjs @@ -174,11 +174,11 @@ add( "| Legacy/earlier approach | Current approach | Compatibility/status |", "| --- | --- | --- |", "| Compiler-owned/internal parsing imports | Canonical `@wrnexus/syntax` lexer/parser/AST/diagnostics | Compiler re-exports remain for compatibility; direct internals are deprecated. |", - "| Implicit component discovery everywhere | Explicit imports and generated contracts | Upgraded apps can retain `legacyComponentDiscovery`; unresolved symbols are reported rather than guessed. |", - '| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts remain behind compatibility configuration. |', + "| Implicit component discovery everywhere | Explicit imports and generated contracts | Unresolved symbols are reported rather than guessed; there is no compatibility opt-out. |", + '| `layout = "PublicLayout"` | Import layout and use `layout = PublicLayout` | String layouts are no longer supported; the config key that toggled them was removed. |', "| `@event changed = function` in props | `outputs { changed(payload: Type) }` | v0.6 migration converts declarations; ambiguous payloads become `unknown`. |", '| `$emit("changed", value)` and `event.detail` | `output.changed(value)` and direct `payload` | Static cases auto-migrated; dynamic emit names require review. |', - "| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; compatibility default can preserve ambiguous behavior. |", + "| Unclassified functions | `server function`, `client function`, or `shared function` | v0.6 classifies unambiguous cases; the legacy default runtime that preserved ambiguous behavior was removed. |", "| Manually copied CAPTCHA JS/script tags | Package-discovered client runtime/assets | v0.4 removes tags and archives old assets under `.wrnexus/legacy-assets/0.4.0`. |", "| Package components/routes/assets wired manually | Automatic package/plugin discovery and contribution registry | Current CLI/build/dev server inspect and consume contributions. |", "| Only scalar/quoted dynamic props | Native arrays/objects and JSX-style unquoted expressions | Quoted expression attributes remain supported. |", @@ -192,7 +192,7 @@ add( "| Per-component/global style placement inconsistencies | `style {}` promoted to document head | Current pipeline supports CSP, HMR and CSR navigation. |", "| Manually maintained API/component knowledge | Generated public API and component references plus validation gates | `check:public-api`, generated-type checks, UI visual contract and package audits detect drift. |", "", - "Compatibility flags visible in generated/upgraded config include `legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime`. New projects default legacy flags off; migration-created configs may enable them to preserve behavior until source modernization is complete.", + "`legacyEmit`, `legacyEventProps`, `legacyComponentDiscovery`, `stringLayouts`, and `functions.legacyDefaultRuntime` (and the other pre-1.0 compatibility keys) were removed before the first public release; a config that still sets any of them is rejected by validation rather than honored.", "", "## 7. Diagnostics, security, and correctness guarantees", "",