@shared/contracts is the canonical contract layer for a distributed, offline-first health platform. It defines every API schema, entity type, sync rule, conflict resolution policy, and health metric configuration shared between the mobile frontend and cloud backend. The package produces identical runtime behavior in Node.js and React Native — same Zod validation, same merge outcomes, same SHA-256 hashes, same cursor semantics. One definition, two enforcement layers, zero semantic drift.
| Language | |
| Validation | |
| Target Runtimes | |
| Cryptography | |
| Testing |
Every API request and response is validated by the same Zod schema on both client and server. There is exactly one definition for each entity type, metric code, and sync cursor format. If the shape changes, it changes in one place.
Conflict resolution rules are declarative configuration, not imperative code scattered across services. Each entity type has a published ENTITY_CONFLICT_CONFIG with field-level merge policies. Both frontend and backend import and apply the same rules.
Monotonic state transitions (e.g., session status: ACTIVE → PAUSED → COMPLETED), allowed health metric units, precision policies — these are encoded as TypeScript types and Zod refinements, not documentation that can drift.
Every export is a pure function, frozen configuration object, or Zod schema. No side effects, no I/O, no runtime dependencies beyond zod. The package works identically in Node.js and React Native.
graph LR
subgraph Mobile Frontend
UI[UI Components] --> FServ[Application Services]
FServ --> FDB[Local SQLite DB]
end
subgraph Cloud Backend
Ctrl[API Controllers] --> BServ[Business Logic]
BServ --> BDB[PostgreSQL]
end
subgraph Shared Contracts
ZOD[API Schemas · Zod]
ENT[Entity Definitions]
SYNC[Sync Configuration]
HC[Health Config]
MOD[Domain Models]
end
FServ -->|validates requests| ZOD
FServ -->|sync logic| SYNC
FServ -->|metric config| HC
FDB -->|schema alignment| ENT
Ctrl -->|validates payloads| ZOD
BServ -->|sync logic| SYNC
BServ -->|metric config| HC
BServ -->|domain types| MOD
BDB -->|schema alignment| ENT
style ZOD fill:#FFFFE0,stroke:#333
style ENT fill:#FFFFE0,stroke:#333
style SYNC fill:#FFFFE0,stroke:#333
style HC fill:#FFFFE0,stroke:#333
style MOD fill:#FFFFE0,stroke:#333
Request/response schemas and DTOs for every API surface, validated at both client and server with the same Zod definitions.
| Contract | Purpose |
|---|---|
health.contract.ts |
Batch health sample upload/response schemas, metric validation, payload hashing, local DB schemas |
health-projection.contract.ts |
Rollup, sleep summary, session impact, product impact DTOs with ProjectionFreshnessMeta |
journal.contract.ts |
Journal entry and effect schemas with mood, tags, version-based conflict detection |
session-telemetry.contract.ts |
Time-series metric data with 1m/5m resolution bucketing |
prediction.contract.ts |
Inventory depletion forecasts with confidence tiers and data quality classification |
sync-lease.contract.ts |
Admission control schemas for rate-limited sync operations |
sync-etag.contract.ts |
FNV-1a weak ETag generation for sync state validation |
onboarding.contract.ts |
Onboarding preference storage with question versioning |
Declarative configuration driving bidirectional offline-first sync between SQLite (mobile) and PostgreSQL (backend). Entity ordering, cursor management, conflict resolution, and merge logic are all configuration — not imperative code.
| Module | Purpose |
|---|---|
entity-types.ts |
9 canonical entity types with sync order hierarchy (10–90) and legacy name mapping |
conflict-configs.ts |
Per-entity conflict resolution: field-level policies for sessions, consumptions, journals, purchases, devices |
conflict-strategies.ts |
6 entity strategies (SERVER_WINS, MERGE, ...) and 11 field policies (LOCAL_WINS, MONOTONIC, MERGE_ARRAYS, MAX_VALUE, ...) |
cursor.ts |
Composite cursor encode/decode with schema versioning and monotonic advancement validation |
entity-merger.ts |
Pure merge functions driven by ENTITY_CONFLICT_CONFIG — deterministic on any runtime |
conflict-resolution.ts |
Conflict outcome types: ADOPT, REBASE, MANUAL |
product-field-mask.ts |
23-field allowlist for product sync shaping |
session-window.ts |
Canonical session semantics: idle timeout, hit accumulation, status transitions |
relation-graph.ts |
Foreign key relationships and cascade rules for sync dependency ordering |
Canonical metric definitions, validation rules, cryptographic hashing, and projection state management for a 32-metric health data pipeline.
| Module | Purpose |
|---|---|
metric-types.ts |
32 HealthMetricCode definitions across 8 categories — units, bounds, value kinds, aggregation rules |
payload-hash.ts |
Cross-platform SHA-256 hashing with order-invariant canonicalization for request idempotency |
precision-policy.ts |
Per-metric decimal precision rules and value rounding enforcement |
freshness-types.ts |
Projection state machine: READY, COMPUTING, STALE, FAILED, NO_DATA with factory functions |
sleep-clustering.ts |
Gap-based sleep session clustering with nap vs. night detection |
sleep-utils.ts |
Night boundary anchoring (18:00–12:00 local), timezone-aware date computation |
source-resolution.ts |
Multi-device source deduplication and priority ranking |
unit-normalization.ts |
Cross-platform unit aliasing and conversion |
Health data uploads must be idempotent across retries on unreliable mobile networks. computeBatchPayloadHash canonicalizes samples and deletions into a deterministic byte sequence — sorting by composite key, normalizing undefined/null values, applying recursive key ordering — then produces an identical SHA-256 hash on both Node.js (crypto) and React Native (expo-crypto) via globalThis.crypto.subtle. The server detects retried requests by hash match, returning the cached response instead of reprocessing.
When a mobile client and backend server both modify the same entity offline, they must independently reach identical merge outcomes. ENTITY_CONFLICT_CONFIG defines per-entity, per-field merge policies (LOCAL_WINS, SERVER_WINS, MONOTONIC, MERGE_ARRAYS, MAX_VALUE). The EntityMerger applies these policies as pure functions — no I/O, no service dependencies. Both sides import the same configuration and execute the same logic. The outcome is reproducible from the config alone.
Cursor-based sync pagination must never regress, or data is silently lost. The composite cursor system encodes per-entity cursor state (timestamps + UUIDs) with a schema version field, validates monotonic advancement on every decode, and rejects backward cursor movement with typed errors (CursorBackwardError). Schema versioning enables cursor format evolution without breaking existing clients.
| Domain | Scope | Key Exports |
|---|---|---|
| API Contracts | Request/response schemas for all REST endpoints | BatchUpsertSamplesRequestSchema, JournalEntrySchema, SyncLeaseRequestSchema |
| Health Configuration | 32 metric definitions, units, bounds, and payload hashing | HEALTH_METRIC_CONFIG, computeBatchPayloadHash, PrecisionPolicy |
| Sync Configuration | Entity ordering, cursor management, and conflict resolution | ENTITY_CONFLICT_CONFIG, encodeCompositeCursor, EntityMerger |
| Health Projections | Read-model DTOs for aggregated health data | toHealthRollupDayDto, toSleepNightSummaryDto, FreshnessMeta |
| Domain Models | Shared entity type definitions and enums | JournalEntry, JournalEffect, EffectPolarity |
| Pattern | Implementation |
|---|---|
| Runtime Schema Validation | Zod .parse() / .safeParse() at every trust boundary with actionable error messages |
| Frozen Configuration | Object.freeze() on all shared config — prevents mutation across Node.js module cache |
| Pure Functions | All merge, hash, and cursor utilities are stateless and side-effect free |
| Cross-Platform Crypto | globalThis.crypto.subtle abstraction — identical hashes on Node.js and React Native |
| Order-Invariant Hashing | Samples sorted by composite key before SHA-256 — field order never affects the hash |
| Monotonic State Machines | MONOTONIC field policy enforces forward-only state transitions (e.g., ACTIVE → COMPLETED) |
| Cursor Schema Versioning | Version field in encoded cursors enables format evolution without breaking clients |
| Metadata Size Guards | Health sample metadata enforces 20 key max, 3-level depth, 4KB ceiling |
| Backward Compatibility | Legacy model name mappings and configVersion field for graceful schema evolution |
| Array Deduplication | MERGE_ARRAYS policy unions arrays with automatic deduplication |
| Type-Safe Error Codes | Exhaustive error code enums with retryable/non-retryable classification |
| Canonical Entity Ordering | Sync order (10–90) encoded in configuration, respecting foreign key dependencies |
// Backend: Validate incoming API request
import { BatchUpsertSamplesRequestSchema } from '@shared/contracts';
const validated = BatchUpsertSamplesRequestSchema.parse(req.body);
// Mobile: Conflict resolution during sync
import { ENTITY_CONFLICT_CONFIG, CONFLICT_STRATEGY } from '@shared/contracts';
const sessionConfig = ENTITY_CONFLICT_CONFIG.sessions;
// sessionConfig.defaultStrategy === CONFLICT_STRATEGY.MERGE
// Both: Cursor encoding for sync pagination
import { encodeCompositeCursor, decodeCompositeCursor } from '@shared/contracts';
const cursor = encodeCompositeCursor(entityCursors);
// Both: Payload hash for idempotent health uploads
import { computeBatchPayloadHash } from '@shared/contracts';
const hash = await computeBatchPayloadHash({ samples, deleted, configVersion });
// Both: Health metric validation
import { isHealthMetricCode, HEALTH_METRIC_CONFIG } from '@shared/contracts';
if (isHealthMetricCode('heart_rate')) {
const config = HEALTH_METRIC_CONFIG['heart_rate'];
// config.canonicalUnit === 'bpm', config.valueKind === 'SCALAR_NUM'
}
// Both: Projection freshness state
import { createStaleFreshnessMeta, getFreshnessUiState } from '@shared/contracts';
const meta = createStaleFreshnessMeta(existingMeta, 'NEW_SAMPLES');
const ui = getFreshnessUiState(meta.status);
// ui.showStaleBadge === true, ui.showData === truesrc/
├── contracts/ # API request/response schemas and DTOs
│ ├── health.contract.ts # Health sample schemas, batch upsert, payload hashing
│ ├── health-projection.contract.ts # Projection DTOs, freshness meta, response summaries
│ ├── journal.contract.ts # Journal entry/effect schemas
│ ├── session-telemetry.contract.ts # Time-series telemetry data
│ ├── prediction.contract.ts # Inventory prediction schemas
│ ├── sync-etag.contract.ts # ETag generation for sync
│ ├── sync-lease.contract.ts # Sync lease admission control
│ ├── onboarding.contract.ts # Onboarding preferences
│ └── onboarding-questions.ts # Question definitions
├── health-config/ # Health data configuration & utilities
│ ├── metric-types.ts # 32 HealthMetricCode definitions with units and bounds
│ ├── payload-hash.ts # Cross-platform SHA-256 payload hashing
│ ├── precision-policy.ts # Per-metric decimal precision
│ ├── freshness-types.ts # Projection state machine and factory functions
│ ├── sleep-clustering.ts # Sleep session clustering logic
│ ├── sleep-utils.ts # Night boundary and anchor date utilities
│ ├── source-resolution.ts # Multi-device source deduplication
│ └── unit-normalization.ts # Unit aliasing and conversion
├── models/ # Shared data model types
│ └── journal.model.ts # Journal entry and effect models
├── sync-config/ # Sync engine configuration & logic
│ ├── entity-types.ts # 9 canonical entity types and sync order
│ ├── conflict-configs.ts # Per-entity conflict resolution rules
│ ├── conflict-strategies.ts # Strategy and field policy enums
│ ├── conflict-resolution.ts # Conflict outcome types
│ ├── cursor.ts # Composite cursor encode/decode/advance
│ ├── entity-merger.ts # Pure config-driven merge functions
│ ├── product-field-mask.ts # Product field allowlist
│ ├── relation-graph.ts # Entity dependency graph
│ └── session-window.ts # Session window semantics
└── index.ts # Central re-export hub
TypeScript types are erased at runtime. API payloads arrive as unknown — they need runtime validation. Zod provides both: z.infer<typeof Schema> gives the static type, and Schema.parse(data) validates at the trust boundary. One definition, two enforcement layers.
When the mobile client and backend server both resolve conflicts, they must agree on the outcome. Shared configuration (ENTITY_CONFLICT_CONFIG) ensures deterministic, reproducible results. Tests can verify conflict resolution without running either service.
Object.freeze prevents accidental mutation of shared state. A service importing ENTITY_CONFLICT_CONFIG cannot accidentally modify it for all other consumers. This is especially important in Node.js where module caching means all importers share the same object reference.
computeBatchPayloadHash produces identical SHA-256 hashes on both client and server for the same payload. This enables request-level idempotency: the server detects retried requests by matching the hash, returning the cached response instead of reprocessing.
This package is one of four repositories that compose the full platform:
| Repository | Role |
|---|---|
| ESP32-S3 Edge Firmware Platform | BLE sensor firmware on ESP32-S3 with FreeRTOS |
| Offline-First Mobile Platform | React Native app with native iOS/Android runtime integrations |
| Cloud-Native Backend Platform | Event-driven Node.js backend with PostgreSQL and BullMQ |
| Cross-Platform Shared Contracts (this repo) | Canonical TypeScript contracts shared across all platform layers |