Headless CMS. Angular frontend in `frontend/`, ASP.NET Core backend in `backend/`.
## Frontend
- Angular app in `frontend/`, source under `src/app`:
- `framework/` — generic, reusable UI components and utilities (no domain knowledge).
- `shared/` — Squidex-specific services, state stores and components.
- `features/` — the actual screens (apps, assets, content, rules, schemas, settings, teams, ...).
- `shell/` — app frame, navigation, layout.
- State is handled with the state store pattern from `framework/state.ts` (immutable value objects + `State<T>` subclasses), not with a third-party store library.
- Commands:
```bash
npm start
```
```bash
npm test
```
```bash
npm run lint
```
### Best Practices
- i18n texts live in `backend/i18n`, translations are generated into the frontend — do not edit generated translation files by hand.
- Do not write JsDoc comments.
## Backend
- .NET solution `backend/Squidex.slnx`. Projects under `backend/src`, tests under `backend/tests`, optional integrations under `backend/extensions`.
- Event-sourced domain: aggregates emit events from `Squidex.Domain.Apps.Events`, state is projected into MongoDB or EF Core (`Squidex.Data.MongoDb`, `Squidex.Data.EntityFramework`).
- Run tests with the filter below — some tests need external setup (real databases, Docker/Testcontainers) and will fail without it:
### Tests
Some tests need test setup or test containers which are slow. Run the tests like this to skip these tests.
```bash
dotnet test --filter "Category!=Dependencies & Category!=TestContainer"
```
### Best Practices
- Code style is enforced by StyleCop (`backend/stylecop.json`) and `.editorconfig` — follow the surrounding file's conventions.
- Do not write XML comments.
## Shared best practices
- Do write precise short comments and only when needed.
- Do not comment a class or a method, only put comments inside functions or above variables.
The file was touched (a variable rename and whitespace tidy-up), but the blocking call
is unchanged — it just moved from line 134 to 129:
```csharp
var scheme = GetSchemeCoreAsync(name, default).Result;
```
`Get(string? name)` is an options-resolution hook invoked from the auth pipeline, so
each call parks a thread-pool thread on a DB round trip. Under load this is a classic
thread-pool starvation source, and it deadlocks outright if any sync context is ever
installed.
`Get(string? name)` blocks a thread-pool thread on a DB round trip. **Accepted as-is —
this is not an important path** (dynamic OIDC scheme resolution, only reached for
team-level auth domains, not on ordinary API traffic), so the starvation risk does not
justify the rework. Left documented rather than deleted so it is not re-reported as a
new finding.
Same pattern, lower blast radius:
If it ever does move onto a hot path, the fix is to cache scheme results synchronously
(populated by an async initializer / background refresh) so `Get` can return without
blocking.
Same pattern elsewhere, also low blast radius:
- `Squidex.Domain.Apps.Entities/Contents/DomainObject/Guards/ScriptingExtensions.cs:144` — `.Wait()` on full content validation inside a script callback.