Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,51 @@ updates:
schedule:
interval: weekly
open-pull-requests-limit: 5
groups:
react-runtime:
patterns:
- react
- react-dom
- react-server-dom-webpack
- "@types/react"
- "@types/react-dom"
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
- "@types/react"
- "@types/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:
Expand All @@ -25,8 +60,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
35 changes: 22 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 <version>` 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
Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions docs/adr/005-drx-pages-and-server-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion docs/adr/006-drx-layouts-and-semantic-checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand Down
9 changes: 6 additions & 3 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 16 additions & 4 deletions docs/private-packages.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions docs/scripts/generate-english-catalog.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion docs/src/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function CliLab() {
<label>{locale === "pt" ? "Projeto" : "Project"}
<input value={name} onChange={event => setName(event.target.value)} />
</label>
<CodeBlock label="Terminal" code={`npm run create -- create ${safeName} --template ${template}\ncd ${safeName}\nnpm install\ndotreact doctor\ndotreact dev`} />
<CodeBlock label="Terminal" code={`dotreact create ${safeName} --template ${template}\ncd ${safeName}\nnpm install\ndotreact doctor\ndotreact dev`} />
</div>
);
}
Expand Down
Loading
Loading