Skip to content

About

A contract-first React Native (Expo) client: types generated from service contracts, honest mocks, live tests against real services, and one approval card for human and AI-proposed changes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Contract-first React Native — an Expo client for AI and domain services

ci Expo TypeScript license

A React Native (Expo) app for the library services in this set: search the catalog and borrow a copy, or ask the AI assistant — and confirm every change on the same card, whether you asked for it or the assistant proposed it.

Borrowing from the catalog: the confirmation card Borrowed: availability follows the loan An injected instruction arrives as a warned card

Live mode on iOS against the Python library API and the assistant API. Right: a book title carrying “ignore all previous instructions…” made the assistant propose a loan for m_attacker — it arrives as a warned card and runs only if the person confirms.

It exists to show three things a mobile client in a multi-service system needs, and how to make each one fail loudly:

How Fails when
Types from contracts src/api/*.gen.ts are generated from the services' committed OpenAPI documents; calls go through typed openapi-fetch clients a service changes its contract and the app has not followed (npm run contracts:check) · the app calls a path or field that does not exist (tsc)
Mock-first, but honest mocks Without a service URL the app runs on in-memory mocks; every mock response satisfies the generated schema type a mock drifts from the real API (tsc)
The same client against real services npm run test:live drives the generated clients against running services; CI starts them from their own repositories the wiring works on mocks but not on the wire
Human approval in the UI One ConfirmCard for all changes; an assistant proposal the user never asked for arrives suspicious and shows the service's reasons a change could run without a tap (screen tests)

The live test earned its place on day one: Expo SDK 57's global fetch rejects the Request objects openapi-fetch sends ("Unsupported BodyInit type"). The mock-backed screen tests could not see it — the mocks read the Request themselves. The app now bridges it (expoFetch in src/api/clients.ts).

The services

Tab Talks to Contract
Catalog the library API — Python or .NET; one spec, one contract contracts/openapi.json in either
Assistant the tool-calling assistant's HTTP API contracts/openapi.json there

Borrowing from the catalog needs a copy id; the library API gained GET /catalog/books/{isbn}/copies for this app — spec first (feature 003, rule CAT-R3), then both implementations, then the regenerated types here.

Run it

npm install
npm start                      # mock mode: no services needed

# live mode
EXPO_PUBLIC_LIBRARY_URL=http://localhost:8000 \
EXPO_PUBLIC_ASSISTANT_URL=http://localhost:8001 npm start

npm run check                  # lint, strict tsc, screen tests, contract gate
npm run test:live              # generated clients against running services (env as above)

Layout

Path
src/api/ Generated types, typed clients, contract-checked mocks
src/ui/ConfirmCard.tsx The one approval surface
src/features/catalog/ Search → copies → borrow
src/features/assistant/ Conversation with proposals as cards
src/__tests__/ Screen flows on mocks; live.test.ts against real services
scripts/contracts.sh Generate / check types from both published contracts

Part of a set: llm-tool-calling-assistant · spec-driven-ddd-python · spec-driven-ddd-dotnet · gitops-reference · ai-native-engineering. By Hasan Özkan · LinkedIn

About

A contract-first React Native (Expo) client: types generated from service contracts, honest mocks, live tests against real services, and one approval card for human and AI-proposed changes.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages