From 5da8f2576420d25287a027b21acd83d66782b15a Mon Sep 17 00:00:00 2001 From: Rafael Gomes Date: Wed, 22 Jul 2026 00:55:00 -0300 Subject: [PATCH 1/3] feat(cli): add project-aware global launcher --- .github/dependabot.yml | 39 +++++++++++++++++ packages/cli/bin/dotreact.js | 9 +++- packages/cli/src/index.ts | 11 ++++- packages/cli/src/launcher.test.ts | 56 ++++++++++++++++++++++++ packages/cli/src/launcher.ts | 72 +++++++++++++++++++++++++++++++ scripts/smoke-published-cli.mjs | 13 ++++-- 6 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/launcher.test.ts create mode 100644 packages/cli/src/launcher.ts diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f6b65e2..5b510a8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,16 +5,47 @@ updates: schedule: interval: weekly open-pull-requests-limit: 5 + groups: + react-runtime: + patterns: + - react + - react-dom + - react-server-dom-webpack + update-types: + - minor + - patch + ignore: + - dependency-name: "@types/node" + update-types: + - version-update:semver-major + - dependency-name: concurrently + update-types: + - version-update:semver-major - package-ecosystem: npm directory: /docs schedule: interval: weekly open-pull-requests-limit: 3 + groups: + react-documentation: + patterns: + - react + - react-dom + update-types: + - minor + - patch - package-ecosystem: nuget directory: /dotnet schedule: interval: weekly open-pull-requests-limit: 5 + groups: + opentelemetry: + patterns: + - OpenTelemetry.* + update-types: + - minor + - patch - package-ecosystem: nuget directory: /examples/crud/Server schedule: @@ -25,8 +56,16 @@ updates: schedule: interval: weekly open-pull-requests-limit: 3 + groups: + official-actions: + patterns: + - actions/* - package-ecosystem: docker directory: / schedule: interval: weekly open-pull-requests-limit: 3 + ignore: + - dependency-name: node + update-types: + - version-update:semver-major diff --git a/packages/cli/bin/dotreact.js b/packages/cli/bin/dotreact.js index c74d2a0..680b7d2 100755 --- a/packages/cli/bin/dotreact.js +++ b/packages/cli/bin/dotreact.js @@ -1,3 +1,10 @@ #!/usr/bin/env node -import "../dist/index.js"; +import { launchDotReact } from "../dist/launcher.js"; + +try { + await launchDotReact(); +} catch (error) { + process.stderr.write(`[dotreact] ${error instanceof Error ? error.message : String(error)}\n`); + process.exitCode = 1; +} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 9b45058..5199ba4 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -561,6 +561,9 @@ async function doctor(): Promise { }; if (runtime.schemaVersion !== 1 || typeof runtime.frameworkVersion !== "string") throw new Error("Managed runtime metadata is invalid. Run dotreact upgrade from a valid framework checkout."); + const cliVersion = await frameworkVersion(); + if (runtime.frameworkVersion !== cliVersion) + throw new Error(`DotReact CLI ${cliVersion} cannot diagnose managed runtime ${runtime.frameworkVersion}. Use the project-local CLI.`); process.stdout.write(`[dotreact] Managed runtime ${runtime.frameworkVersion}.\n`); } else { const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies }; @@ -575,6 +578,9 @@ async function doctor(): Promise { if (versions.size !== 1) throw new Error("DotReact npm packages must use one aligned framework version. Run dotreact upgrade ."); const frameworkPackageVersion = [...versions][0]!; + const cliVersion = await frameworkVersion(); + if (frameworkPackageVersion !== cliVersion) + throw new Error(`DotReact CLI ${cliVersion} does not match project packages ${frameworkPackageVersion}. Run npm install and use the project-local CLI.`); const projectSource = await readFile(resolve(root, "Server/Server.csproj"), "utf8"); for (const packageName of ["DotReact.Hosting", "DotReact.AppGenerator"]) { const reference = new RegExp(` { function help(): void { process.stdout.write([ "DotReact CLI", + " dotreact version Show the effective CLI version", " dotreact create [--template starter|minimal]", " dotreact templates", " dotreact generate page [--dry-run]", @@ -947,7 +954,9 @@ function help(): void { } try { - if (command === "create") await create(args[0], optionValue(args, "--template")); + if (command === "version" || command === "--version" || command === "-v") + process.stdout.write(`${await frameworkVersion()}\n`); + else if (command === "create") await create(args[0], optionValue(args, "--template")); else if (command === "templates") process.stdout.write("Available templates:\n starter SSR, hydration, actions, Docker and Azure health checks\n minimal One SSR page, hydration, boundaries and production runtime\n"); else if (command === "generate" || command === "add") await generate(args); else if (command === "sync") await sync(args); diff --git a/packages/cli/src/launcher.test.ts b/packages/cli/src/launcher.test.ts new file mode 100644 index 0000000..73feef3 --- /dev/null +++ b/packages/cli/src/launcher.test.ts @@ -0,0 +1,56 @@ +import assert from "node:assert/strict"; +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { resolveProjectCli } from "./launcher.js"; + +function temporaryProject(): string { + return mkdtempSync(path.join(tmpdir(), "dotreact-launcher-")); +} + +test("global launcher selects the nearest explicitly declared project CLI", () => { + const root = temporaryProject(); + try { + const localBin = path.join(root, "node_modules", "@rafagouveia", "dotreact-cli", "bin", "dotreact.js"); + mkdirSync(path.dirname(localBin), { recursive: true }); + mkdirSync(path.join(root, "app", "reports"), { recursive: true }); + writeFileSync(path.join(root, "package.json"), JSON.stringify({ + devDependencies: { "@rafagouveia/dotreact-cli": "0.1.3" } + })); + writeFileSync(localBin, "#!/usr/bin/env node\n"); + assert.equal(resolveProjectCli(path.join(root, "app", "reports"), path.join(root, "global.js")), localBin); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + +test("global launcher does not execute undeclared or current package bins", () => { + const root = temporaryProject(); + try { + const localBin = path.join(root, "node_modules", "@rafagouveia", "dotreact-cli", "bin", "dotreact.js"); + mkdirSync(path.dirname(localBin), { recursive: true }); + writeFileSync(localBin, "#!/usr/bin/env node\n"); + writeFileSync(path.join(root, "package.json"), JSON.stringify({ dependencies: {} })); + assert.equal(resolveProjectCli(root, path.join(root, "global.js")), undefined); + + writeFileSync(path.join(root, "package.json"), JSON.stringify({ + dependencies: { "@rafagouveia/dotreact-cli": "0.1.3" } + })); + assert.equal(resolveProjectCli(root, localBin), undefined); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); + +test("global launcher fails closed when the declared project CLI is not installed", () => { + const root = temporaryProject(); + try { + writeFileSync(path.join(root, "package.json"), JSON.stringify({ + devDependencies: { "@rafagouveia/dotreact-cli": "0.1.3" } + })); + assert.throws(() => resolveProjectCli(root, path.join(root, "global.js")), /Run npm install/u); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); diff --git a/packages/cli/src/launcher.ts b/packages/cli/src/launcher.ts new file mode 100644 index 0000000..4356f98 --- /dev/null +++ b/packages/cli/src/launcher.ts @@ -0,0 +1,72 @@ +import { existsSync, readFileSync, realpathSync } from "node:fs"; +import { spawnSync } from "node:child_process"; +import { dirname, parse, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const cliPackageName = "@rafagouveia/dotreact-cli"; +const delegationGuard = "DOTREACT_CLI_DELEGATED"; +const currentBin = resolve(dirname(fileURLToPath(import.meta.url)), "../bin/dotreact.js"); + +interface PackageManifest { + readonly dependencies?: Readonly>; + readonly devDependencies?: Readonly>; + readonly optionalDependencies?: Readonly>; +} + +function canonicalPath(path: string): string { + try { + return realpathSync.native(path); + } catch { + return resolve(path); + } +} + +function declaresCli(manifest: PackageManifest): boolean { + return manifest.dependencies?.[cliPackageName] !== undefined + || manifest.devDependencies?.[cliPackageName] !== undefined + || manifest.optionalDependencies?.[cliPackageName] !== undefined; +} + +/** + * Finds the nearest explicitly declared project-local CLI. Merely placing a + * similarly named file below node_modules is not enough to make the global + * launcher execute it. + */ +export function resolveProjectCli(startDirectory: string, launcherBin = currentBin): string | undefined { + let directory = resolve(startDirectory); + const volumeRoot = parse(directory).root; + while (true) { + const packagePath = resolve(directory, "package.json"); + if (existsSync(packagePath)) { + try { + const manifest = JSON.parse(readFileSync(packagePath, "utf8")) as PackageManifest; + if (declaresCli(manifest)) { + const candidate = resolve(directory, "node_modules", "@rafagouveia", "dotreact-cli", "bin", "dotreact.js"); + if (existsSync(candidate) && canonicalPath(candidate) !== canonicalPath(launcherBin)) return candidate; + if (!existsSync(candidate) && process.env[delegationGuard] !== "1") + throw new Error(`DotReact CLI is declared by ${packagePath} but is not installed. Run npm install.`); + } + } catch (error) { + if (error instanceof Error && error.message.startsWith("DotReact CLI is declared")) throw error; + // The CLI command itself will provide the project diagnostic. A broken + // package manifest must never redirect execution to an arbitrary file. + } + } + if (directory === volumeRoot) return undefined; + directory = dirname(directory); + } +} + +export async function launchDotReact(): Promise { + const localCli = process.env[delegationGuard] === "1" ? undefined : resolveProjectCli(process.cwd()); + if (localCli !== undefined) { + const result = spawnSync(process.execPath, [localCli, ...process.argv.slice(2)], { + env: { ...process.env, [delegationGuard]: "1" }, + stdio: "inherit" + }); + if (result.error) throw result.error; + process.exitCode = result.status ?? 1; + return; + } + await import("./index.js"); +} diff --git a/scripts/smoke-published-cli.mjs b/scripts/smoke-published-cli.mjs index acbbe2f..b082562 100644 --- a/scripts/smoke-published-cli.mjs +++ b/scripts/smoke-published-cli.mjs @@ -38,8 +38,13 @@ await waitForPackage(); const smokeRoot = await mkdtemp(join(tmpdir(), "dotreact-published-cli-")); const projectDirectory = join(smokeRoot, "published-app"); +const globalPrefix = join(smokeRoot, "global-cli"); +const dotreact = process.platform === "win32" + ? join(globalPrefix, "dotreact.cmd") + : join(globalPrefix, "bin", "dotreact"); try { - run(npm, ["exec", "--yes", `--package=${cliPackage}`, "--", "dotreact", "create", "published-app", "--template", "minimal"], smokeRoot); + run(npm, ["install", "--global", cliPackage, `--prefix=${globalPrefix}`, `--registry=${registry}`], smokeRoot); + run(dotreact, ["create", "published-app", "--template", "minimal"], smokeRoot); if (existsSync(join(projectDirectory, "vendor"))) throw new Error("Published scaffolds must not contain vendored framework source."); run("dotnet", [ @@ -50,10 +55,10 @@ try { "--store-password-in-clear-text" ], projectDirectory); run(npm, ["install"], projectDirectory); - run(npm, ["exec", "--", "dotreact", "doctor"], projectDirectory); + run(dotreact, ["doctor"], projectDirectory); run(npm, ["run", "typecheck"], projectDirectory); - run(npm, ["run", "build"], projectDirectory); - process.stdout.write(`[dotreact] ${cliPackage} created, installed, diagnosed and built a clean project.\n`); + run(dotreact, ["build"], projectDirectory); + process.stdout.write(`[dotreact] ${cliPackage} direct command created, delegated, diagnosed and built a clean project.\n`); } finally { rmSync(smokeRoot, { recursive: true, force: true }); } From 273f9ef13aa23ef40216c9223ac245dfdba3d13c Mon Sep 17 00:00:00 2001 From: Rafael Gomes Date: Wed, 22 Jul 2026 01:12:19 -0300 Subject: [PATCH 2/3] feat(release): prepare direct CLI workflow 0.1.4 --- README.md | 35 ++++++---- docs/adr/005-drx-pages-and-server-actions.md | 6 +- .../006-drx-layouts-and-semantic-checking.md | 3 +- ...pp-router.md => 014-unified-app-router.md} | 7 +- docs/compatibility.md | 9 ++- docs/private-packages.md | 20 ++++-- docs/scripts/generate-english-catalog.mjs | 11 +++ docs/src/components.tsx | 2 +- docs/src/content.en.catalog.ts | 41 ++++++++--- docs/src/content.guides.tsx | 6 +- docs/src/content.tsx | 20 ++++-- dotnet/Directory.Build.props | 8 +-- .../DotReact.Hosting/DotReact.Hosting.csproj | 4 +- .../DotReact.AppGenerator.Tests.csproj | 2 +- .../DotReact.AspNetCore.Tests.csproj | 2 +- .../DotReact.TypeGen.Tests.csproj | 2 +- examples/crud/package.json | 14 ++-- examples/rsc/package.json | 4 +- examples/starter/README.md | 20 +++--- .../starter/docs/ai/06-WORKFLOW-RECIPES.md | 14 ++-- examples/starter/package.json | 16 ++--- package-lock.json | 68 +++++++++---------- package.json | 4 +- packages/adapter/package.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/index.test.ts | 16 ++--- packages/cli/src/index.ts | 2 +- packages/cli/src/launcher.test.ts | 50 ++++++++++++++ packages/cli/src/launcher.ts | 32 ++++++--- packages/client/package.json | 2 +- packages/font/package.json | 2 +- packages/image/package.json | 2 +- packages/protocol/package.json | 2 +- packages/renderer/package.json | 6 +- packages/rsc/package.json | 2 +- packages/script/package.json | 4 +- packages/vite/package.json | 4 +- scripts/package-npm.test.mjs | 4 +- 38 files changed, 288 insertions(+), 162 deletions(-) rename docs/adr/{006-unified-app-router.md => 014-unified-app-router.md} (92%) diff --git a/README.md b/README.md index c9a5116..95522d0 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ DotReact is an experimental full-stack framework that keeps ASP.NET Core in charge of HTTP, authentication, services, and data while React renders the UI on the server and hydrates it in the browser. -This repository contains a reusable pre-release framework, CLI scaffold and three +This repository contains a reusable experimental framework, CLI scaffold and three reference applications. The runtime boundary and production lifecycle are -implemented; coordinated private npm and NuGet packages are available at version `0.1.3`. +implemented; coordinated private npm and NuGet packages are available at version `0.1.4`. Public surfaces follow the repository's [compatibility and deprecation policy](COMPATIBILITY.md). The source is licensed under [MPL 2.0](LICENSE). Applications may use DotReact, @@ -86,22 +86,31 @@ the counter proves that React hydrated the same tree in the browser. ## Create a project -Using the published private CLI package: +Authenticate the private feed once, then install the CLI launcher once: ```powershell -npm exec --yes --package=@rafagouveia/dotreact-cli@0.1.3 -- dotreact create my-app --template starter +npm install --global @rafagouveia/dotreact-cli@0.1.4 --registry=https://npm.pkg.github.com +dotreact create my-app --template starter cd my-app npm install -npm run dev +dotreact doctor +dotreact dev ``` Generated projects pin the CLI, browser runtime, renderer, Vite integration, NuGet hosting libraries and local TypeGen tool to one exact DotReact version. +The global command automatically delegates to that project-local CLI, so a +machine can work on projects using different DotReact versions without drift. Framework source is not copied into product repositories. Use `dotreact upgrade ` to align every runtime surface before reinstalling and running the release gates. -Private prerelease packages are hosted under the `rafagouveia` GitHub Packages +CI should continue using package scripts or `npm exec -- dotreact`: it must not +depend on a mutable global installation. If the shell cannot find the launcher, +verify `Get-Command dotreact` after reopening it; the project-local fallback is +`npm exec -- dotreact doctor`. + +Private 0.x packages are hosted under the `rafagouveia` GitHub Packages namespace. Publishing remains manual and opt-in; normal CI never republishes a package. See [Private GitHub Packages](docs/private-packages.md) for package coordinates, token permissions, local archive validation and cost @@ -126,13 +135,13 @@ resulting lockfile before the first push. Inside a generated project, the complete lifecycle is: ```powershell -npm exec -- dotreact doctor -npm exec -- dotreact dev -npm exec -- dotreact generate page catalog/[slug] -npm exec -- dotreact generate action SaveCatalog -npm exec -- dotreact sync -npm exec -- dotreact check -npm exec -- dotreact build +dotreact doctor +dotreact dev +dotreact generate page catalog/[slug] +dotreact generate action SaveCatalog +dotreact sync +dotreact check +dotreact build dotreact workspace status dotreact workspace reveal dotreact workspace focus diff --git a/docs/adr/005-drx-pages-and-server-actions.md b/docs/adr/005-drx-pages-and-server-actions.md index e45fc29..8e7d5ad 100644 --- a/docs/adr/005-drx-pages-and-server-actions.md +++ b/docs/adr/005-drx-pages-and-server-actions.md @@ -2,7 +2,9 @@ ## Status -Accepted for the first opt-in vertical slice. +Superseded for new applications by ADR 014. This document preserves the first +opt-in slice and its security decisions; current scaffolds use the unified +`app/` router. ## Decision @@ -23,7 +25,7 @@ and emits three deterministic artifacts: TypeScript contracts, typed action clients and a backend manifest. The existing explicit TypeGen and `MapDotReactPage` APIs remain supported. -The Vite plugin scans only `src/pages/**/*.drx`, resolves canonical paths, +The original Vite plugin scanned only `src/pages/**/*.drx`, resolved canonical paths, rejects a pages root outside the application, ignores escaping symlinks and builds `virtual:dotreact/pages` from static imports. Request data is never used to construct a filesystem path or import specifier. diff --git a/docs/adr/006-drx-layouts-and-semantic-checking.md b/docs/adr/006-drx-layouts-and-semantic-checking.md index 5529e20..6fd5ec1 100644 --- a/docs/adr/006-drx-layouts-and-semantic-checking.md +++ b/docs/adr/006-drx-layouts-and-semantic-checking.md @@ -2,7 +2,8 @@ ## Status -Accepted for the second opt-in DRX slice. +Accepted. ADR 014 moved the canonical pages root from `src/pages` to `app/`; +the composition and semantic-checking guarantees in this record remain current. ## Decision diff --git a/docs/adr/006-unified-app-router.md b/docs/adr/014-unified-app-router.md similarity index 92% rename from docs/adr/006-unified-app-router.md rename to docs/adr/014-unified-app-router.md index da4bca7..4e86715 100644 --- a/docs/adr/006-unified-app-router.md +++ b/docs/adr/014-unified-app-router.md @@ -1,9 +1,10 @@ -# ADR 006: Unified app router +# ADR 014: Unified app router ## Status -Accepted for the 0.2 development line. The legacy `src/pages` plus attributed -loader model remains supported for compatibility during the deprecation window. +Accepted and shipped in the 0.1.x scaffold. The legacy `src/pages` plus +attributed loader model remains a compatibility surface, but it is not generated +for new applications. ## Context diff --git a/docs/compatibility.md b/docs/compatibility.md index 029a6b1..9a4fcd7 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -4,11 +4,14 @@ |---|---|---|---|---|---| | 0.1.x experimental | 10.x | 24.x recommended; 22.19+ supported | 19.2.x | 8.1.x | 7.0.x | -Protocol version 1 is currently required on both sides. Generated projects use -private GitHub npm and NuGet packages pinned to the same exact DotReact version; +Buffered SSR uses protocol v1 and streaming SSR negotiates protocol v2. The C# +host and Node renderer must come from the same coordinated DotReact release; +unknown protocol versions fail closed instead of falling back silently. +Generated projects use private GitHub npm and NuGet packages pinned to the same exact DotReact version; the local TypeGen tool is pinned to that version as well. `dotreact upgrade` aligns those three surfaces without rewriting application source. Every upgrade -must regenerate contracts and pass `dotreact check` before deployment. +must regenerate contracts and pass `dotreact check` before deployment. A +one-time global launcher delegates to the exact CLI installed by each project. Supported development hosts are Windows and Linux. The production container is Linux-based and targets Azure App Service custom containers and Azure Container diff --git a/docs/private-packages.md b/docs/private-packages.md index 930f5f6..9ccd130 100644 --- a/docs/private-packages.md +++ b/docs/private-packages.md @@ -1,6 +1,6 @@ # Private GitHub Packages -DotReact prerelease packages are published in the `rafagouveia` GitHub Packages +DotReact private 0.x packages are published in the `rafagouveia` GitHub Packages namespace. Publishing new immutable versions is manual and disabled by default. ## Package coordinates @@ -67,15 +67,27 @@ For another private repository, grant that repository access on each package's GitHub **Manage Actions access** page; `packages: read` alone cannot bypass a package that has not shared access with the consuming repository. -Create a project without cloning the framework repository: +Install the launcher once and create a project without cloning the framework +repository: ```powershell -npm exec --yes --package=@rafagouveia/dotreact-cli@0.1.3 -- dotreact create my-app --template starter +npm install --global @rafagouveia/dotreact-cli@0.1.4 --registry=https://npm.pkg.github.com +dotreact version +dotreact create my-app --template starter cd my-app npm install -npm exec -- dotreact doctor +dotreact doctor ``` +The global executable is only a launcher. Inside a project, it locates the +nearest `package.json` that explicitly declares `@rafagouveia/dotreact-cli` and +executes that installed local version. If dependencies are missing it fails +closed and asks for `npm install`; it never silently runs a different global +version. `npm exec -- dotreact doctor` remains the no-global fallback and is the +recommended form for CI. With nvm/fnm, global packages belong to the active Node +installation, so reinstall the launcher after switching Node versions if +`Get-Command dotreact` no longer resolves it. + ## Cost controls GitHub Free currently includes a private Packages allowance. Configure a GitHub diff --git a/docs/scripts/generate-english-catalog.mjs b/docs/scripts/generate-english-catalog.mjs index 55739b6..9f78d57 100644 --- a/docs/scripts/generate-english-catalog.mjs +++ b/docs/scripts/generate-english-catalog.mjs @@ -103,7 +103,14 @@ async function generate(expected, missing, stale, current) { } function normalizeTechnicalTranslation(source, translation) { + if (/^(?:dotreact(?:\s+.*)?|virtual:dotreact\/[A-Za-z0-9_./-]+|@rafagouveia\/dotreact-[A-Za-z0-9_-]+)$/u.test(source)) + return source; let result = translation; + result = result + .replaceAll("dotreat", "dotreact") + .replaceAll("doreact", "dotreact") + .replaceAll("Dotreat", "DotReact") + .replaceAll("Doreact", "DotReact"); if (/\bbanco|bancos\b/iu.test(source)) result = result.replace(/\bbank(?:ing)?\b/giu, "database").replace(/\bbanks\b/giu, "databases"); if (/\bhidrat/iu.test(source)) @@ -117,6 +124,9 @@ function normalizeTechnicalTranslation(source, translation) { function hasTerminologyError(source, translation) { if (typeof translation !== "string") return false; + if (/\b(?:doreact|dotreat)\b/iu.test(translation)) return true; + const protectedTokens = source.match(/(?:virtual:dotreact\/[A-Za-z0-9_./-]+|@rafagouveia\/dotreact-[A-Za-z0-9_-]+|\bdotreact(?:\s+[a-z][a-z-]*)?)/gu) ?? []; + if (protectedTokens.some(token => !translation.includes(token))) return true; if (/\bbanco|bancos\b/iu.test(source) && /\bbank(?:ing|s)?\b/iu.test(translation)) return true; if (/\bhidrat/iu.test(source) && /\bmoistur/iu.test(translation)) return true; if (/\bporta\b/iu.test(source) && /\bdoor\b/iu.test(translation)) return true; @@ -153,6 +163,7 @@ function shouldKeep(value) { if (/^\d+(?:\.\d+)?$/u.test(value)) return true; if (/^[A-Z0-9_.+/@:[\]()-]{2,}$/u.test(value)) return true; if (/^(?:C#|React|TypeScript|PowerShell|Terminal|Program\.cs|page\.drx|package\.json)$/u.test(value)) return true; + if (/^(?:dotreact(?:\s+.*)?|virtual:dotreact\/|@rafagouveia\/dotreact-)/u.test(value)) return true; if (/^(?:app|src|Server|Domain|Application|Infrastructure|Presentation|public|vendor)\//u.test(value)) return true; return false; } diff --git a/docs/src/components.tsx b/docs/src/components.tsx index 640c26c..570fbdb 100644 --- a/docs/src/components.tsx +++ b/docs/src/components.tsx @@ -115,7 +115,7 @@ export function CliLab() { - + ); } diff --git a/docs/src/content.en.catalog.ts b/docs/src/content.en.catalog.ts index b2f78ea..56d4796 100644 --- a/docs/src/content.en.catalog.ts +++ b/docs/src/content.en.catalog.ts @@ -79,6 +79,7 @@ export const englishText: Readonly> = { ". Ele executa antes da conclusão da request e pode redirecionar, reescrever ou alterar headers. No DotReact essa responsabilidade não fica no renderer: ela usa o pipeline ASP.NET Core, que é a entrada pública e possui DI, autenticação, autorização, rate limiting e observabilidade.": ". It executes before the request is complete and can redirect, rewrite or change headers. In DotReact, this responsibility does not rest with the renderer: it uses the ASP.NET Core pipeline, which is the public input and has DI, authentication, authorization, rate limiting and observability.", ". Ele tem duração absoluta e carrega apenas um ticket protegido. O React recebe somente o DTO público do usuário.": ". It has a fixed lifetime and carries only a protected ticket. React receives only the user's public DTO.", ". Em Azure, termine HTTPS no ingress e configure forwarded headers somente para proxies confiáveis.": ". In Azure, terminate HTTPS in the ingress and configure forwarded headers only for trusted proxies.", + ". Em CI, prefira os scripts do projeto ou": ". In CI, prefer project scripts or", ". Em desenvolvimento, ASP.NET faz proxy de": ". In development, ASP.NET proxy", ". Em múltiplas réplicas, persista e proteja o key ring do ASP.NET Core Data Protection para que autenticação e antiforgery sejam consistentes entre instâncias.": ". Across multiple replicas, persist and secure the ASP.NET Core Data Protection key ring so that authentication and antiforgery are consistent across instances.", ". Em produção, valide paths de": ". In production, validate paths", @@ -92,6 +93,8 @@ export const englishText: Readonly> = { ". O comando cria somente os arquivos públicos da feature e não altera o runtime.": ". The command only creates the feature's public files and does not change the runtime.", ". O entry React usa": ". entry React uses", ". O exemplo": ". The example", + ". O fallback sem instalação global é": ". Fallback without global installation is", + ". O global cria projetos; o local executa doctor, dev, build e generators sem drift.": ". The global creates projects; local runs doctor, dev, build and generators without drift.", ". O Hot Reload incrementa a revisão do backend e o router busca props SSR novas pela mesma origem.": ". Hot Reload increments the backend revision and the router searches for new SSR props from the same source.", ". O mesmo registry virtual é consumido no SSR e na hydration, evitando árvores diferentes.": ". The same virtual registry is consumed in SSR and hydration, avoiding different trees.", ". O nome antigo continua citado porque muitos projetos ainda possuem": ". The old name continues to be mentioned because many projects still have", @@ -126,6 +129,7 @@ export const englishText: Readonly> = { "(shop)/products/page": "(shop)/products/page", "[--routes] --url --out": "[--routes] --url --out", "[--static-only]": "[--static-only]", + "[--url]": "[--url]", "[AllowAnonymous]": "[AllowAnonymous]", "[Authorize(Policy = \"catalog.manage\")]": "[Authorize(Policy = \"catalog.manage\")]", "[Authorize(Policy = \"orders.write\")]": "[Authorize(Policy = \"orders.write\")]", @@ -147,7 +151,7 @@ export const englishText: Readonly> = { "@rafagouveia/dotreact-rsc": "@rafagouveia/dotreact-rsc", "@rafagouveia/dotreact-rsc experimental": "@rafagouveia/dotreact-rsc experimental", "@rafagouveia/dotreact-script": "@rafagouveia/dotreact-script", - "@rafagouveia/dotreact-vite": "@rafazouveia/dotreact-vite", + "@rafagouveia/dotreact-vite": "@rafagouveia/dotreact-vite", "*": "*", "*.server.ts": "*.server.ts", "/": "/", @@ -170,6 +174,7 @@ export const englishText: Readonly> = { " --kind error|not-found|loading|global-error": " --kind error|not-found|loading|global-error", " [--dry-run]": " [--dry-run]", "