Frontend Architecture¶
The frontend is a React 19 TypeScript SPA built with Vite. It has no routing library โ navigation is a local state enum. It has no global state management library โ state lives in component hooks, with localStorage as the only persistence layer.
Project structure¶
packages/frontend/src/
โโโ components/
โ โโโ ChainBuilder/
โ โ โโโ ChainBuilder.tsx main orchestration component
โ โ โโโ ChainComposer.tsx drag-drop chain builder (dnd-kit)
โ โ โโโ ChainConfig.tsx configuration + execution panel
โ โ โโโ ChainResults.tsx execution results display
โ โ โโโ DmnCard.tsx individual DMN card
โ โ โโโ DmnList.tsx available DMNs panel
โ โ โโโ ExecutionProgress.tsx step-by-step progress indicator
โ โ โโโ InputForm.tsx dynamic input form generation
โ โ โโโ ExportChain.tsx export modal (JSON / BPMN 2.0)
โ โ โโโ SemanticView.tsx semantic analysis tab
โ โ โโโ ValidationBadge.tsx governance status badge
โ โ โโโ ValidationPanel.tsx chain validation status display
โ โโโ BpmnModeler/
โ โ โโโ BpmnModeler.tsx main orchestrator
โ โ โโโ BpmnCanvas.tsx bpmn-js canvas wrapper
โ โ โโโ BpmnProperties.tsx properties panel
โ โ โโโ ProcessList.tsx process management sidebar
โ โ โโโ DmnTemplateSelector.tsx DMN/DRD dropdown for BusinessRuleTask
โ โโโ DsoExplorer/
โ โ โโโ DsoExplorer.tsx DSO Explorer shell (default export only)
โ โ โโโ QualityProfileTab.tsx quality profile tab (Scorecard/Matrix)
โ โ โโโ shared.tsx shared components โ only Section
โ โ โโโ tokens.ts shared constants, the Tone type, toneForRatio()
โ โโโ GraphView.tsx D3.js RDF graph visualisation
โ โโโ ResultsTable.tsx SPARQL results table + CSV export
โ โโโ Changelog.tsx version history display
โโโ services/
โ โโโ sparqlService.ts SPARQL query execution + result parsing
โ โโโ templateService.ts localStorage CRUD for templates + processes
โโโ utils/
โ โโโ exportService.ts JSON + BPMN 2.0 export logic
โ โโโ exportFormats.ts export format definitions
โ โโโ bpmnTemplates.ts default BPMN XML templates
โ โโโ ronlAttributes.ts ronl:* process attributes โ escaped on write, decoded on read
โ โโโ constants.ts sample queries, preset endpoints
โโโ types/
โ โโโ index.ts core TypeScript interfaces
โ โโโ chainBuilder.types.ts chain builder specific types
โ โโโ export.types.ts export types
โโโ changelog.json version history data (JSON)
DsoExplorer/ splits what its two tabs share by kind: shared.tsx exports only
components, and tokens.ts holds the constants, type and helper that
DsoExplorer.tsx and QualityProfileTab.tsx both import. A .tsx module that
exports anything besides components cannot be hot-swapped by React Fast Refresh,
which the react-refresh/only-export-components lint rule enforces.
tutorial.json sat beside changelog.json until v2026.09.2, which removed the
in-app tutorial and its 596 lines of content along with the Tutorial component
and ViewMode.TUTORIAL.
Key TypeScript interfaces¶
DMN model:
interface DmnModel {
id: string;
identifier: string;
title: string;
description?: string;
inputs: DmnVariable[];
outputs: DmnVariable[];
organisation?: string;
// Governance metadata
validationStatus?: 'validated' | 'in-review' | 'not-validated';
validatedByName?: string;
validatedAt?: string;
validationNote?: string;
// Vendor metadata
vendorCount?: number;
vendors?: VendorService[];
}
interface DmnVariable {
id: string;
identifier: string;
title: string;
type: 'Integer' | 'String' | 'Boolean' | 'Date' | 'Double';
}
Chain template (localStorage schema):
interface ChainTemplate {
id: string;
name: string;
description?: string;
endpoint: string;
chain: string[]; // ordered DMN identifiers
testData?: Record<string, unknown>;
type: 'sequential' | 'drd';
// DRD-specific
isDrd?: boolean;
drdDeploymentId?: string;
drdEntryPointId?: string;
drdOriginalChain?: string[];
}
Drag-and-drop (dnd-kit)¶
The Chain Composer uses @dnd-kit/core for drag detection and @dnd-kit/sortable for reordering within the composer. DMN cards in the Available DMNs list are Draggable; the Chain Composer area is a Droppable. Cards already in the composer use SortableContext for reordering.
SPARQL service¶
sparqlService.ts sends every query through the backend, POST /v1/triplydb/query, whatever endpoint the user chose. Until v2026.09.5 it fetched the typed endpoint straight from the browser and fell back to the third-party api.allorigins.win proxy when CORS refused; both paths are gone, so no query passes through a third party, the backend's outbound guard applies to every one, and the Content-Security-Policy's connect-src can name a single origin. When the backend refuses an endpoint, its problem-details detail is shown to the user; a non-JSON error page, such as a proxy's HTML 502, shows as Query failed (502). rather than a JSON parse error. Result parsing handles both standard application/sparql-results+json and variations in binding formats.
The connection badge always reads Proxied via Backend. The Local Jena endpoint preset is offered only in development builds, the only place the backend admits local endpoints, and the default endpoint is chosen by URL rather than by list position, so it stays DMN Discovery in every build.
Styling¶
Tailwind CSS 3.4 is built with the application, through PostCSS and autoprefixer, from an entry stylesheet imported in main.tsx and a content scan over index.html and the source. Until v2026.09.5, index.html loaded the Tailwind Play CDN on every page load in acceptance and production โ third-party JavaScript executing in the application's origin, on an unversioned URL that cannot be pinned with an integrity hash. v3 was kept deliberately, so every class means exactly what the CDN served. An unused import map naming six packages on esm.sh was removed at the same time.
The inline <style> block that used to sit in index.html lives in src/index.css, so the Content-Security-Policy needs no 'unsafe-inline' for <style> elements. The error banner's fade-down and the Settings panel's slide-in are two keyframes in tailwind.config.js, which motion-reduce turns off.
Content-Security-Policy¶
vite/cspPlugin.ts writes staticwebapp.config.json into the build output during build:acc and build:prod, where Azure Static Web Apps reads it. The policy is served as Content-Security-Policy-Report-Only, built from the same VITE_API_BASE_URL the app uses, so each environment's connect-src names that environment's backend. Every allowed source is there because something in the built app needs it: Google Fonts for the Inter typeface, TriplyDB's assets API for organisation logos in img-src, and data: for the BPMN icon font and diagram images. Violations are reported to the backend's POST /v1/csp-reports. See Deployment for how it is shipped.
Template service (localStorage)¶
templateService.ts provides the localStorage interface for both chain templates and BPMN processes. All storage keys are prefixed with linkeddata-explorer-. Templates are namespaced by endpoint URL so switching endpoints does not surface templates from another dataset.
Key functions:
getUserTemplates(endpoint: string): ChainTemplate[]
saveTemplate(endpoint: string, template: ChainTemplate): void
deleteTemplate(endpoint: string, templateId: string): void
getBpmnProcesses(): BpmnProcess[]
saveBpmnProcess(process: BpmnProcess): void
Environment variables (Vite)¶
All environment variables are prefixed VITE_ and read at build time:
| Variable | Default (development) | Description |
|---|---|---|
VITE_API_BASE_URL |
http://localhost:3001 |
Backend API base URL |
VITE_CPSV_EDITOR_URL |
http://localhost:3002 |
The CPSV Editor the DSO โ DMN publish handoff deep-links to. Run it on a non-3000 port locally, since LDE's dev server also uses 3000 |
Build targets: npm run build:prod (production), npm run build:acc (acceptance), npm run dev (development). The deploy workflows use build:acc and build:prod; each mode reads its own .env.<mode> file.
VITE_OPERATON_BASE_URL was removed in v2026.09.6
It named, display-only, the Operaton the BPMN deploy modal deploys to and the Cockpit
link in exported instructions โ while the backend's OPERATON_BASE_URL decided where a
process actually landed. A build-time copy of a value the backend owns can drift from it,
so the modal and the exported README now ask
GET /v1/dmns/process/deploy-target instead, through
services/deployTargetService.ts. The variable is gone from all three .env files.