Register an author, add books to their shelf, and watch the author list update without reloading. This is the Library domain from the shared Arc tutorial, implemented with Arc for TypeScript, Chronicle, Express, and the published Arc React client. The Chronicle integration is experimental. This is a development demonstration, not an authentication template.
From the repository root (Node 22.19+, Yarn 4, and Docker):
yarn install
docker compose -f Samples/Library/docker-compose.yml up -d
yarn workspace @cratis/arc.sample.library devOpen http://127.0.0.1:5173. Choose Add author on the Cratis Components DataPage, register an author in its CommandDialog, select the author, and add a book to their shelf. The observable author list and book shelf update as Chronicle projections catch up. Stop the dev workers with Ctrl+C, then stop only this sample's compose project with docker compose -f Samples/Library/docker-compose.yml down. This removes its kernel and its bundled development MongoDB; data is not retained after the container is removed. The backend listens on 127.0.0.1:3000; Vite proxies /api and /.cratis to it.
The development image bundles MongoDB for Chronicle's event store and read-model sink; no separate MongoDB server is needed. appsettings.json supplies Cratis.Chronicle.connectionString (chronicle://localhost:35000) and eventStore (Library). Set CHRONICLE_URL to override the connection string when running against another owned development kernel. There is no in-memory or direct MongoDB mode in this sample; Tasks demonstrates Arc without Chronicle. Chronicle appends and projections are asynchronous: a successful command can precede an updated query. An observable GET can answer 202 before its first result. The paged snapshot loads the small catalog before Arc pages it; do not use it for an unbounded production list.
The backend assigns a fixed Librarian principal inside the loopback-only Express host to demonstrate @roles('Librarian'). It does not authenticate visitors. Do not expose this host publicly or copy that principal into production; connect a host-verified identity before deployment.
-
A full registration slice looks like this:
Features/Authors/Registration/ ├── Registration.ts ├── for_RegisterAuthor/when_registering/with_librarian_role.ts ├── RegisterAuthor.proxy.ts ├── RegisterAuthorForm.tsx └── for_RegisterAuthorForm/when_submitting/with_a_valid_name.tsxThe listing slice has the same layout:
Listing.ts,for_Author/when_paging/, its*.proxy.tsfiles,AuthorCatalog.tsx, andfor_AuthorCatalog/when_showing_a_page/. -
Features/Authors/Registration/Registration.tsholdsRegisterAuthor,AuthorRegistered, andUniqueAuthorName.handle()returns the event, and@key()identifies its event source. Chronicle enforces name uniqueness across author streams at append time.AuthorName.tskeeps the concept and its validator together. -
Features/Authors/Listing/Listing.tsmaps the event to a projectedAuthor. Its Arc queries load the Chronicle read models and subscribe to changes for the observable list. The book registration and listing slices follow the same pattern underFeatures/Books/. -
Features/generatedMetadata.tsand each slice's*.proxy.tsfiles are produced byarc-proxygeneratorand committed beside their backend modules, as in Arc for .NET. Change the decorated source, then runyarn workspace @cratis/arc.sample.library generate-proxies; never edit generated files. The generator uses--use-proxy-file-suffix, writes no barrels inFeatures, and removes only stale owned files. -
Features/Authors/Listing/AuthorCatalog.tsxpasses the co-located./AllAuthors.proxyobservable query toDataPagefrom@cratis/components/DataPage. ItsColumnrenders author names;MenuItemopens the registration dialog; selecting a row showsAuthorBooksin the details pane.Features/Authors/Registration/RegisterAuthorForm.tsxbindsInputTextFieldfrom@cratis/components/CommandFormto./RegisterAuthor.proxyinside aCommandDialog. The dialog seeds the required id, executes the command, and closes on success.Web/src/App.tsxmountsCratisComponentsProviderinsideArc;Web/src/main.tsximports the Components tokens, styles, and theme. The backend tsconfig excludes browser files, the web tsconfig includes components and frontend specs, and Vite serves slices outsideWeb. The web and frontend-spec Vite configs deduplicate React, React DOM,@cratis/arc,@cratis/arc.react, and@cratis/componentsacross the app shell and slice imports. The slice package (Samples/Library/package.json) declares the frontend packages imported fromFeatures/;Web/package.jsondeclares the app shell's own dependencies. Components has no PrimeReact peer.
yarn workspace @cratis/arc.sample.library build
yarn test --project library-frontend
yarn test
bash Samples/Library/run-integration.shThe root Vitest workspace includes vite.config.mts for backend for_*/when_*/*.ts specs in Node and vite.frontend.config.mts for co-located frontend for_*/when_*/*.tsx specs in jsdom. Both projects use the shared Chai setup; frontend specs stub the query hook and generated command execution, then exercise Components interactions with Testing Library. Backend compilation and discovery ignore for_* folders and .tsx files; Web/tsconfig.json type-checks the frontend specs along with the components and proxies.
The integration script creates and removes only its own Chronicle development container, with bundled MongoDB. It exercises generated proxies over Express: author registration, uniqueness rejection, projected listings, book registration, and a live observable update. The ChronicleCommandScenario slice specs assert event appends without a kernel; only the live run checks the kernel's constraint and projection behavior. The browser UI is not exercised by the script.