Repository navigation
BuildFlow
🌐 This page in: English · Português
This document describes the eQuantic.UI build flow, demonstrating how the framework maintains zero external dependencies for the consumer.
┌─────────────────────────────────────────────────────────────────────────────┐
│ DEVELOPMENT (source tree) │
└─────────────────────────────────────────────────────────────────────────────┘
src/eQuantic.UI.Runtime/src/**/*.ts src/eQuantic.UI.Sdk/Resources/boot.ts
│ │
└──────────────────┬───────────────────────────┘
▼
┌──────────────────────────────────┐
│ dotnet build │
│ (eQuantic.UI.Server) │
│ │
│ ResolveBunForServer target: │
│ └─ the embedded bun from the │
│ Runtime.{Os}{Arch} project │
│ (extracted from .zip, +x) │
│ │
│ BundleRuntime target, before │
│ every build: │
│ └─ "$(_BunPath)" build boot.ts │
│ --outfile wwwroot/runtime.js │
└──────────────────────────────────┘
│
▼
wwwroot/runtime.js ──── the ONE bundle: embedded in Server.dll and served at
│ /_equantic/runtime.js, and the file a source-tree build
│ copies beside an app's modules
▼
┌──────────────────────────────────┐
│ dotnet pack │
│ (every packable project) │
│ │
│ Server: CollectServedRuntime │
│ └─ wwwroot/runtime.js → │
│ tools/runtime/runtime.js │
│ Sdk: CollectBuildOutput │
│ └─ eqc + eqicon → │
│ tools/net10.0/ │
└──────────────────────────────────┘
│
▼
*.nupkg — bin/Release/ in CI, or a local feed (see below); the version comes from
Directory.Build.props (or the release tag), never from a file in the tree
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONSUMER (client project) │
└─────────────────────────────────────────────────────────────────────────────┘
MyApp.csproj
└─ <Project Sdk="eQuantic.UI.Sdk"> ← that line is the whole build pipeline
global.json
└─ { "msbuild-sdks": { "eQuantic.UI.Sdk": "<version>" } } ← the ONE place the version lives
(no PackageReference: the SDK adds its own)
│
▼
┌──────────────────────────────────┐
│ dotnet restore │
│ │
│ NuGet installs packages: │
│ ├─ eQuantic.UI.Sdk │
│ ├─ eQuantic.UI.Primitives │
│ ├─ eQuantic.UI.Code │
│ ├─ eQuantic.UI.Components │
│ ├─ eQuantic.UI.Charts │
│ ├─ eQuantic.UI.Web │
│ ├─ eQuantic.UI.Server │ ◄── the runtime it serves, also as
│ │ └─ tools/runtime/runtime.js │ a file for the SDK to copy
│ ├─ eQuantic.UI.Generators │ (analyzer)
│ └─ eQuantic.UI.Runtime.OsxArm64 │ ◄── Bun embedded here! ONE package,
│ └─ tools/bun/bun-darwin.zip │ chosen by OS + architecture
└──────────────────────────────────┘
> **Which Bun?** `src/eQuantic.UI.Runtime/bun-toolchain.json` holds the version and a SHA-256 per
> platform, checked on every test run. The digests prove the committed bytes are the ones the
> release published; the version is verified by extracting the host's binary and running it, because
> a manifest nobody compares against the thing it describes drifts from it.
>
> All six platform packages (`Osx64`, `OsxArm64`, `Win64`, `WinArm64`, `Linux64`, `LinuxArm64`) carry
> the same build; a different Bun per architecture is how you get a bundle that only fails on one
> person's machine.
>
> Updating it: replace the `.zip` files, run the tests once with `EQ_UPDATE_BUN_MANIFEST=1`, and read
> the diff. A version that moved with digests that did not is a mistake, and so is the reverse.
│
▼
┌────────────────────────────────────────┐
│ dotnet build │
│ │
│ wwwroot/_equantic/** is not Content: │
│ this build writes it (step 11) │
│ │
│ In the order the targets run: │
│ │
│ 1. EQuanticVectors │ (when Assets/ exists)
│ └─ eqicon: *.svg → C#, before the │
│ compile reads it │
│ │
│ 2. CoreCompile │
│ └─ the app's C#, .NET's own target │
│ │
│ 3. ResolveBunZipPath │
│ └─ $(PkgeQuantic_UI_Runtime_ │
│ OsxArm64)/tools/bun/*.zip │
│ │
│ 4. EnsureBunExtracted │
│ ├─ Unzip if needed │
│ └─ chmod +x (Unix) │
│ │
│ 5. ResolveBunPath │
│ └─ Defines $(BunPath) │
│ │
│ 6. InstallBunPackages │
│ ├─ <BunPackage> → bun add │
│ └─ Symlink node_modules │
│ │
│ 7. EQuanticRequireCompiler │
│ └─ eqc missing → EQ4002 │
│ │
│ 8. CompileEQuanticUI │
│ └─ dotnet eqc.dll ... --refs │
│ --bun "$(BunPath)" │
│ --source-maps │
│ $(EQuanticSourceMaps) │
│ │
│ 9. CopyEQuanticRuntime │ ◄── runtime.js deployment
│ └─ the served runtime.js, │
│ from the Server package, │
│ to wwwroot/_equantic/ │
│ │
│ 10. EQuanticWebAppIcon │ (when an AppIcon is declared)
│ └─ eqicon → │
│ wwwroot/_equantic/icons │
│ │
│ 11. _EQuanticOutputAsStaticWebAssets │ ◄── the static web assets hook
│ └─ wwwroot/_equantic/ as 8 to 10 │
│ wrote it, maps left out, │
│ defined as static web assets │
│ │
│ then the static web assets pipeline │
│ compresses them and writes the │
│ manifests: this build's files │
└────────────────────────────────────────┘
│
▼
wwwroot/_equantic/
├─ runtime.js (the Server's bundle, byte for byte)
├─ *.js (compiled components, and the chunks they share)
└─ *.js.map (Debug only, by default)
│
▼
┌──────────────────────────────────┐
│ dotnet run │
│ │
│ Server serves: │
│ ├─ runtime.js (from Server.dll) │
│ └─ *.js (from wwwroot/_equantic)│
└──────────────────────────────────┘
│
▼
Browser loads application
| Component | Bun Source |
|---|---|
Runtime (-t:TestRuntime) |
eQuantic.UI.Runtime.{Os}{Arch}/tools/bun/ (source tree) |
| Server (package build) |
eQuantic.UI.Runtime.{Os}{Arch}/tools/bun/ (source tree) |
| SDK (consumer) |
$(PkgeQuantic_UI_Runtime_{Os}{Arch})/tools/bun/ (NuGet cache) |
| SDK (source tree) |
eQuantic.UI.Runtime.{Os}{Arch}/tools/bun/ beside the SDK's own files |
The consumer only needs:
- .NET SDK 10.0
-
dotnet restore+dotnet build
No Node.js, npm, or global Bun installation required.
A build from the repository's own tree — the samples, a scaffold under test — never goes
through a package. (The website is NOT one of these any more: equantic-web consumes released
packages through global.json, like any other consumer. The section after next is how a repo
outside this tree gets the no-package loop anyway.) Sdk.props sees the source tree beside it and swaps every PackageReference for
a ProjectReference; Sdk.targets runs the eqc and eqicon the graph just built, and copies
runtime.js from src/eQuantic.UI.Server/wwwroot/runtime.js, which the Server project's
BundleRuntime writes before every build of the Server. There is no local feed to fill, no cache to
clear, and no pack step between an edit and the sample that shows it:
dotnet build samples/DefaultUIDashboardThe mechanism — IsEQuanticDevMode, the tool edges, _EqSourceTree — is described in
Package Architecture. The section below is
for the OTHER question: does the change survive the real consumer path?
An app developed alongside the SDK — a consumer repo, not a sample — wants the same thing the samples
get: edit the framework, rebuild the app, see it. It can have that, and the mechanism is the one the
samples already use rather than anything new. _DevRootDir is relative to where Sdk.props PHYSICALLY
SITS, so importing the SDK by path from anywhere lands the probe inside the tree and dev mode turns on.
Four things about this are not guessable, and each one was measured rather than reasoned about.
Dev mode reaches only the project that imports the SDK. In a solution of a head plus libraries, switching the head alone gives you the worst half of the deal: the app RUNS against your working tree while the libraries still COMPILE against the pinned package, so the moment you change an API — the thing the loop exists for — the libraries compile clean and the app fails at run time with a missing member. Every project that references eQuantic.UI has to fork.
The property has to be set before the SDK import, so Directory.Build.props is too late for the
head. Directory.Build.props is imported by Microsoft.NET.Sdk's own props, which the eQuantic SDK
imports at its top — by the time it is read, the head's conditional <Import> has already been
evaluated and skipped. This fails SILENTLY: you get released packages while believing you are on the
tree, and you find out while debugging something else.
A shared props file imported by both the head and Directory.Build.props needs a sentinel, or
MSBuild raises MSB4011 for the second import.
The released-package arm needs global.json. Without msbuild-sdks pinning a version, the
fallback fails with MSB4236: The SDK could not be found and a resolver message that does not mention
the missing pin.
The whole shape, four files:
<!-- EQuanticDev.props — the one definition, read by the head AND by Directory.Build.props -->
<Project>
<PropertyGroup>
<_EqDevPropsImported>true</_EqDevPropsImported>
</PropertyGroup>
<PropertyGroup Condition="'$(EQuanticUIDevRoot)' == ''">
<_EqDevCandidate>$(MSBuildThisFileDirectory)../equantic-ui</_EqDevCandidate>
<EQuanticUIDevRoot Condition="Exists('$(_EqDevCandidate)/src/eQuantic.UI.Primitives/eQuantic.UI.Primitives.csproj')">$(_EqDevCandidate)</EQuanticUIDevRoot>
</PropertyGroup>
</Project><!-- Directory.Build.props -->
<Project>
<Import Project="$(MSBuildThisFileDirectory)EQuanticDev.props" Condition="'$(_EqDevPropsImported)' != 'true'" />
...
</Project><!-- The HEAD: a bare <Project>, because the Sdk attribute cannot carry a Condition -->
<Project>
<Import Project="$(MSBuildThisFileDirectory)../../EQuanticDev.props" />
<Import Project="$(EQuanticUIDevRoot)/src/eQuantic.UI.Sdk.Native/Sdk/Sdk.props" Condition="'$(EQuanticUIDevRoot)' != ''" />
<Import Project="Sdk.props" Sdk="eQuantic.UI.Sdk.Native" Condition="'$(EQuanticUIDevRoot)' == ''" />
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<Import Project="$(EQuanticUIDevRoot)/src/eQuantic.UI.Sdk.Native/Sdk/Sdk.targets" Condition="'$(EQuanticUIDevRoot)' != ''" />
<Import Project="Sdk.targets" Sdk="eQuantic.UI.Sdk.Native" Condition="'$(EQuanticUIDevRoot)' == ''" />
</Project><!-- Every LIBRARY that references eQuantic.UI forks the same way -->
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup Condition="'$(EQuanticUIDevRoot)' == ''">
<PackageReference Include="eQuantic.UI.Primitives" Version="$(EQuanticUIVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(EQuanticUIDevRoot)' != ''">
<ProjectReference Include="$(EQuanticUIDevRoot)/src/eQuantic.UI.Primitives/eQuantic.UI.Primitives.csproj" />
</ItemGroup>
</Project>Check it took, rather than assuming — the failure is silent:
dotnet msbuild src/App/App.csproj -getProperty:IsEQuanticDevMode # true
dotnet msbuild src/Lib/Lib.csproj -t:ResolveReferences -getItem:ReferencePath | grep Primitives
# → .../equantic-ui/src/eQuantic.UI.Primitives/bin/... and NOT ~/.nuget/packages/What it costs: the first build compiles the whole framework from source, and a broken tree becomes a
broken app build. The Exists(...) guard is what keeps CI and a clean clone on released packages.
Adding your own project to your own solution can drag this repo into it. dotnet sln add reads
the csproj, sees the dev-mode ProjectReferences and pulls eQuantic.UI.Primitives, Components
and the icon pack into YOUR .sln. Harmless until someone builds the fallback arm, where those
paths do not exist and the solution stops loading. Check the .sln after adding a project, or add
with --in-root and keep the references out.
Since 0.2.0-preview.45
Validate a framework change in a REAL app — packages, global.json, restore, the whole consumer
path — without cutting a version or running a pipeline. The first Track W consumer proved its
migration this way: byte-identical frames from the package build and from the source build.
In the framework checkout:
dotnet pack -c Release -p:PackageVersion=1.0.0-dev -o artifacts/local-feed1.0.0-dev never collides with a published version, both SDKs derive the version of every sibling
package from their own, and neither the samples nor the tests pack — the feed is all product. In the app:
-
global.json:{ "msbuild-sdks": { "eQuantic.UI.Sdk.Native": "1.0.0-dev" } }(oreQuantic.UI.Sdk) -
nuget.config:<clear />, then the local feed FIRST, nuget.org after - the project file:
Sdk="eQuantic.UI.Sdk.Native", no version attribute
After every repack, clear the cached copy and restore again — NuGet never re-downloads a version it
already has. ClearEQuanticCache lives in the framework's Directory.Build.targets, so run it from
that checkout (it reaches every project through the solution); it removes every equantic.* package
from the NuGet cache reactively (a hand-kept list once missed all the native packages):
dotnet msbuild -t:ClearEQuanticCachedotnet restore --forceHeadless proof for a native app, no window needed: --Photon:ScreenshotPath out.png.
| File | Responsibility |
|---|---|
Sdk/Sdk.props |
Adds the framework packages — or, beside a source tree, the framework PROJECTS and the two tool edges (IsEQuanticDevMode) |
Sdk/Sdk.targets |
Finds the tools (_EqSourceTree: the tree's build or the package's tools/), resolves Bun, installs <BunPackage> items, compiles components (with the source maps EQuanticSourceMaps asks for, and none in a publish), copies runtime.js, writes vectors and the app icon, defines its output folder as static web assets; fails by name when a tool is missing (EQ4001, EQ4002) |
Runtime.csproj |
The runtime's TypeScript and its proof: -t:TestRuntime runs bun install --frozen-lockfile, tsc and vitest run through the embedded bun. It bundles nothing and is not packed (IsPackable=false) |
Server.csproj |
The runtime's one writer: bundles boot.ts → wwwroot/runtime.js before every build, embeds it (served at /_equantic/runtime.js) and packs the same file as tools/runtime/runtime.js
|
Runtime.{Os}{Arch}.csproj |
Packages the zipped Bun executable for one OS and architecture |
Sdk.csproj |
Packs Sdk/, and collects eqc and eqicon into tools/net10.0/ from the tool projects it references as edges |
In the order they run in a dotnet build:
-
EQuanticRequireIconTool / EQuanticVectors - When
Assets/exists, before the C# compile: fails withEQ4001ifeqicon.dllis missing, otherwise writesAssets/**/*.svgasVectors.g.cs - CoreCompile - The app's C# compile, the .NET SDK's own target; everything below runs after it
- ResolveBunZipPath - Finds the Bun .zip in the NuGet cache (or beside the SDK in a source tree)
- EnsureBunExtracted - Extracts the executable if needed
-
ResolveBunPath - Defines
$(BunPath)for later use -
InstallBunPackages - Installs
<BunPackage>items viabun add(see BunPackage) -
EQuanticRequireCompiler - Fails with
EQ4002wheneqc.dllis not whereEqcCliPathsays - CompileEQuanticUI - Transpiles C# → TypeScript → JavaScript
-
CopyEQuanticRuntime - Copies the served runtime.js from the Server package (beside a source tree, the Server project's
wwwroot/runtime.js) to wwwroot/_equantic/, and deletes anequantic.cssan earlier SDK left there - EQuanticWebAppIcon - When an app icon is declared: writes the icon sizes and the web manifest
-
_EQuanticOutputAsStaticWebAssets - Defines
wwwroot/_equantic/as static web assets from what 8 to 10 wrote, the maps left out
Steps 3 to 10 run where they do because of the last one. _EQuanticOutputAsStaticWebAssets hangs on
$(GenerateComputedBuildStaticWebAssetsDependsOn), the static web assets pipeline's own hook for
assets a build generates, which runs after CoreCompile and before the pipeline resolves its inputs,
and it depends on the compiler, the runtime copy and the app icon. CompileEQuanticUI still declares
BeforeTargets="Build", and by then it has already run.
The folder is not Content for the same reason. The pipeline registers what wwwroot/ holds while
the project evaluates, and the build then rewrites this folder: a file registered and then deleted
failed the next publish's compression. That was every map a Release build removes, and every shared
chunk bun renames when a component two pages share changes, in the most ordinary sequence there is:
edit, then publish. Defined from disk once its writers have run, the folder reaches the compression,
the manifests and the publish as this build wrote it, and only that. A --no-build publish loads the
build's manifest instead, so it carries the files the last build wrote. With
EnableEQuanticUICompilation=false neither half applies, and the folder's files stay ordinary
Content.
-
_EQuanticKeepSourceMapsOutOfPublish - After
ComputeResolvedFilesToPublishList: takes every.mapunderwwwroot/_equantic/out ofResolvedFileToPublish, whateverEQuanticSourceMapssays. The maps are not among the web assets above either, so this is the second line
- ResolveBunForRuntime - Finds the embedded Bun in the source tree
-
TestRuntime -
bun install --frozen-lockfile, thentscandvitest run. Nothing is bundled here
- ResolveBunForServer - Finds the embedded Bun in the source tree
- BundleRuntime - Compiles boot.ts → wwwroot/runtime.js, before every build
- CollectServedRuntime - At pack time, puts the wwwroot/runtime.js the build wrote under tools/runtime/runtime.js, and never bundles again
eQuantic.UI follows a self-contained package architecture where each package manages its own artifacts. The SDK acts as an orchestrator, referencing other packages via NuGet's $(Pkg*) properties.
Before (Problematic):
SDK Package ❌
├─ Embedded runtime.js (copied from Server)
└─ Embedded *.cs files (copied from Components)
Problems: tight coupling, version conflicts, artifact duplication
After (Correct):
Server Package ✅
└─ tools/runtime/runtime.js (the runtime it serves, as a file)
Components Package ✅
└─ tools/source/*.cs (self-contained)
SDK Package ✅
└─ References other packages via $(PkgeQuantic_UI_*)
Benefits: decoupling, correct versioning, no duplication
1. Packaging (Development)
Every build of eQuantic.UI.Server writes the bundle, and the compiler embeds it; at pack time a
target collects the same file. A static <Content Condition="Exists(…)"> would be evaluated before
the bundle exists on a clean build, and the target collects what the build wrote without bundling
again, so a pack with no build cannot write a second bundle after the embed:
<!-- eQuantic.UI.Server.csproj -->
<PropertyGroup>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);CollectServedRuntime</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<ItemGroup>
<!-- a BUILD OUTPUT, never a committed file -->
<None Remove="wwwroot/runtime.js" />
<EmbeddedResource Include="wwwroot/runtime.js">
<LogicalName>eQuantic.UI.Server.runtime.js</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Target Name="CollectServedRuntime">
<ItemGroup>
<TfmSpecificPackageFile Include="wwwroot/runtime.js" PackagePath="tools/runtime/runtime.js" />
</ItemGroup>
<Error Condition="!Exists('wwwroot/runtime.js')"
Text="eQuantic.UI.Server: wwwroot/runtime.js was not produced, and the package would ship no runtime." />
</Target>
<Target Name="BundleRuntime" BeforeTargets="BeforeBuild" DependsOnTargets="ResolveBunForServer">
<MakeDir Directories="wwwroot" />
<Exec Command=""$(_BunPath)" build ../eQuantic.UI.Sdk/Resources/boot.ts --outfile wwwroot/runtime.js --minify-syntax --minify-whitespace" />
</Target>The package that serves the runtime is the one that ships it, so what an app is served and what its build copies are one file.
2. Deployment (Consumer Build)
During dotnet build, the SDK's CopyEQuanticRuntime target executes:
<!-- Sdk/Sdk.targets -->
<Target Name="CopyEQuanticRuntime" AfterTargets="CompileEQuanticUI"
Condition="'$(EnableEQuanticUICompilation)' == 'true'">
<PropertyGroup>
<!-- The Server package's file, via NuGet property -->
<_RuntimeSourcePath Condition="'$(PkgeQuantic_UI_Server)' != ''">$(PkgeQuantic_UI_Server)/tools/runtime/runtime.js</_RuntimeSourcePath>
<!-- Beside a source tree: the Server project's own build output -->
<_RuntimeSourcePath
Condition="'$(_RuntimeSourcePath)' == '' And Exists('$(MSBuildThisFileDirectory)../../eQuantic.UI.Server/wwwroot/runtime.js')">$(MSBuildThisFileDirectory)../../eQuantic.UI.Server/wwwroot/runtime.js</_RuntimeSourcePath>
<_RuntimeDestPath>$(MSBuildProjectDirectory)/$(EQuanticOutputPath)runtime.js</_RuntimeDestPath>
</PropertyGroup>
<Error Text="eQuantic.UI: the served runtime was not found. It ships inside the eQuantic.UI.Server package (version $(EQuanticUIVersion)) at tools/runtime/runtime.js; try 'dotnet restore --force'."
Condition="'$(_RuntimeSourcePath)' == '' Or !Exists('$(_RuntimeSourcePath)')" />
<Copy SourceFiles="$(_RuntimeSourcePath)" DestinationFiles="$(_RuntimeDestPath)" SkipUnchangedFiles="true" />
<!-- the base stylesheet an earlier SDK copied here, which no page linked -->
<Delete Files="$(MSBuildProjectDirectory)/$(EQuanticOutputPath)equantic.css" />
</Target>A running app never reads this copy: the Server answers /_equantic/runtime.js with the bundle it
embeds, ahead of the static files. The copy is for the tools that load an app's modules outside a
server (the VS Code preview reads wwwroot/_equantic/runtime.js), which is why it is that bundle,
byte for byte.
1. Packaging (Development)
During dotnet pack of eQuantic.UI.Components:
<!-- eQuantic.UI.Components.csproj -->
<ItemGroup>
<Content Include="**\*.cs" Exclude="obj\**;bin\**" PackagePath="tools\source\" />
<Content Include="SdkResources*.resx" PackagePath="tools\source\" />
</ItemGroup>The Components package embeds its own C# source files for compiler type resolution.
2. Compilation (Consumer Build)
During dotnet build, the SDK's CompileEQuanticUI target executes:
<!-- Sdk/Sdk.targets -->
<Target Name="CompileEQuanticUI" BeforeTargets="Build"
DependsOnTargets="ResolveReferences;FindReferenceAssembliesForReferences;GenerateGlobalUsings">
<PropertyGroup>
<!-- Resolve from Components package via NuGet property -->
<_StandardComponentsDir Condition="'$(PkgeQuantic_UI_Components)' != ''">$(PkgeQuantic_UI_Components)/tools/source</_StandardComponentsDir>
<!-- Fallback to source tree (development only) -->
<_DevComponentsDir>$(MSBuildThisFileDirectory)../../eQuantic.UI.Components</_DevComponentsDir>
<_StandardComponentsDir
Condition="'$(_StandardComponentsDir)' == '' And Exists('$(_DevComponentsDir)')">$(_DevComponentsDir)</_StandardComponentsDir>
<_EqcSourceDirs>$(MSBuildProjectDirectory);$(_StandardComponentsDir)</_EqcSourceDirs>
</PropertyGroup>
<Error Text="eQuantic.UI: Standard components not found. Ensure the eQuantic.UI.Components package (version $(EQuanticUIVersion)) is installed."
Condition="'$(_StandardComponentsDir)' == '' Or !Exists('$(_StandardComponentsDir)')" />
<!-- --refs: the exact assemblies csc compiles against, so eqc's semantic model matches the real build -->
<Exec Command="dotnet $(EqcCliPath) "$(_EqcSourceDirs)" $(MSBuildProjectDirectory)/$(EQuanticOutputPath) --refs "$(_EqcRefsFile)" ..." />
</Target>BeforeTargets="Build" is the latest the target can run. In a build it runs earlier, right after
CoreCompile, because the target that defines the output folder as static web assets depends on it
(see the execution order above).
- Decoupling: SDK doesn't embed artifacts from other packages
- Correct Versioning: the SDK reads the artifacts of whatever version NuGet resolved — it never carries a copy that can disagree with the installed package
- No Duplication: Each artifact exists only in its source package
- Flexibility: Packages evolve independently without tight coupling
-
Clear Interface: SDK references packages via well-defined NuGet properties (
$(Pkg*)) - Development Fallback: Source tree paths work for framework development
The runtime is ONE file: the Server's BundleRuntime hands bun a single --outfile, and a page
reaches it as one bare module, @equantic/runtime, which the shell's import map points at
/_equantic/runtime.js.
Why Single Bundle?
- Simplified Deployment: Only one file to copy (runtime.js)
- No Chunk Management: no part of the runtime is split into a file of its own that a page would have to find
- Reliable Distribution: Guaranteed that all runtime features (logger, error overlay) are included
-
Size: a budget, not a number quoted here.
ServedRuntimeBudgetTestsgzips the bytes the Server embeds (CompressionLevel.SmallestSize) and compares them with the record intests/eQuantic.UI.Server.Tests/Budgets/served-runtime.json: growth past 1% fails, and so does a shrink past 5%, which locks a win in.EQ_UPDATE_RUNTIME_BUDGET=1writes the record, and the commit that does says what moved it
The page modules are the opposite case. eqc runs bun with --splitting, so what two or more modules
share is a chunk named after one of them with a content hash (NotFoundScreen-<hash>.js in the
dashboard sample is its console shell, not a second type of that name), and it loads once, with the
first page that needs it. Their sizes are reported rather than gated, since a page module is the
app's own code: CI writes each one's size, raw and gzipped, to the job summary of every pull request.
🌐 English · Português
🏁 Start here
🏗️ Architecture
- Architecture Overview
- Write-Once Components
- Declarative Surface
- Package Architecture
- Components
- Styling
- Localization
- Analytics & GTM
📱 Write-once
⚙️ Compilation
⚡ Runtime
🔌 Server
🎨 Ecosystem
🚀 Development