cursorful-video-1786851116656.mp4
Kasir tetap jalan. Sinkron saat online.
COMPOS adalah point-of-sale offline-first untuk merchant yang harus tetap berjualan
saat koneksi tidak stabil, lalu sinkron otomatis ketika internet kembali.
Why COMPOS · Guarantees · Sync flow · Demo · Architecture · Quick start · Deploy · Docs
Important
“Transaksi berhasil saat offline” berarti sale, item snapshot, stock projection, dan local outbox sudah tersimpan atomically di device. Backend settlement baru terjadi ketika koneksi sehat. Transport boleh mengirim ulang, tetapi stable transaction ID dan idempotency membuat business effect tetap exactly-once.
Koneksi counter UMKM, bazar, atau pop-up store tidak selalu stabil. POS yang bergantung penuh ke request backend bisa membuat antrean berhenti tepat ketika kasir sedang ramai. COMPOS membalik default tersebut: transaksi diselesaikan secara lokal dulu, lalu cloud mengejar state device ketika koneksi kembali.
Project ini bukan mockup checkout. Repository-nya mencakup durable browser persistence, sync engine, merchant-scoped Admin, separate Owner PWA, immutable transaction ledger, reporting read models, reconciliation, PostgreSQL outbox worker, dan automated failure scenarios.
| Guarantee | Implementasi nyata |
|---|---|
| Atomic local checkout | Transaction, item snapshots, outbox, stock projection, dan draft cleanup ditulis dalam satu Dexie transaction. |
| Durable queue | Confirmed sale tetap ada setelah reload, logout, auth expiry, network loss, dan abandoned SYNCING recovery. |
| Stable identity | Setiap sale memakai client-generated UUIDv7 yang tidak berubah saat retry atau berpindah batch. |
| Idempotent settlement | ID + payload sama menjadi ALREADY_PROCESSED; reuse ID dengan payload berbeda ditolak tanpa overwrite history. |
| Partial batch isolation | Maksimal 25 due records dikirim per batch; satu item gagal tidak menggagalkan hasil item lain atau mengubah order. |
| Immutable history | Settled transaction tidak diedit; correction dan audit event bersifat append-only. |
| Eventual inventory | Sale diterima lebih dulu, lalu worker menerapkan stock movement secara idempotent dan membuka discrepancy. |
| Offline authorization lease | Checkout lokal boleh lanjut maksimal 72 jam sejak online authentication terakhir tanpa menghapus queued data. |
flowchart LR
Sale["Kasir confirm sale"] --> Local[("IndexedDB transaction + outbox")]
Local --> Receipt["Receipt lokal"]
Receipt --> Connection{"Connection sehat?"}
Connection -- "Belum" --> Retry["Queued + exponential backoff"]
Retry --> Connection
Connection -- "Ya" --> Batch["Ordered batch, max 25"]
Batch --> Idempotency{"Stable transaction ID"}
Idempotency --> Ledger[("PostgreSQL immutable ledger")]
Ledger --> BackendOutbox["Backend outbox event"]
BackendOutbox --> Worker["Inventory worker"]
Worker --> Projection["Stock projection + discrepancy"]
Ledger --> ReportEvent["Reporting event"]
ReportEvent --> ReadModel["Daily sales read model"]
ReadModel --> Owner["COMPOS Owner PWA"]
Browser scheduler bereaksi pada startup, browser online event, manual reconnect, health probe, dan
interval. Sync service hanya mengambil outbox yang sudah due, single-flight, dan tidak menghapus
queue ketika token expired. Detail state transition, lost-response handling, dan retry policy ada di
Sync Protocol.
cursorful-video-1786851116656.mp4
Demo utama yang perlu dibuktikan:
- Login dan aktivasi device ketika online.
- Matikan koneksi, lakukan checkout, lalu reload browser.
- Receipt dan transaksi tetap ada dengan status queued.
- Pulihkan koneksi dan lihat automatic settlement tanpa duplicate.
- Login sebagai Admin untuk mencoba operator management, catalog pricing, correction, dan inventory reconciliation.
apps/
operator-web/ React 19 + Vite PWA, Dexie, Zustand, shadcn primitives
owner-web/ React 19 + Vite PWA, online-first reporting dan insight history
api/ Fastify API, PostgreSQL repositories, multi-lane outbox worker
packages/
contracts/ Canonical Zod wire schemas dan inferred TypeScript DTOs
docs/ Product, architecture, operations, testing, dan ADR playbook
| Layer | Technology |
|---|---|
| Operator application | React 19, TypeScript, Vite 7, Tailwind CSS 4, PWA, Dexie, Zustand |
| API | Fastify 5, Zod contracts, JWT dengan server-side session jti |
| Durable local state | IndexedDB melalui Dexie |
| Canonical persistence | PostgreSQL 17, explicit SQL repositories, transactional outbox |
| Background processing | Satu Node.js worker; lane inventory, reporting, dan insight |
| Quality | Vitest, fake IndexedDB, PostgreSQL integration suite, Playwright |
| Tooling | pnpm workspace, Oxlint, Oxfmt, GitHub Actions |
Beberapa keputusan penting sengaja konservatif: PWA dipilih sebelum React Native, PostgreSQL outbox dipilih sebelum RabbitMQ, dan raw typed repositories dipertahankan sebelum menambah ORM. Alasan dan trade-off lengkap ada di Architecture Decision Records.
Requirements: Node.js 22+, pnpm 10, dan Docker Desktop/Compose.
git clone https://github.com/myudak/compos.git
cd compos
pnpm install --frozen-lockfile
pnpm db:up
pnpm db:reset
pnpm devBuka Operator di http://localhost:5173 dan Owner di
http://localhost:5174/owner/. pnpm dev menjalankan kedua PWA, API
di port 3001, serta worker dengan lane inventory, reporting, dan insight.
| Role | Merchant | Operator | PIN |
|---|---|---|---|
| Kasir | KEDAI-NUSA |
RANI |
1234 |
| Admin | KEDAI-NUSA |
ADMIN |
9999 |
| Owner | KEDAI-NUSA |
OWNER |
7777 |
Device activation code: COMPOS-DEMO.
Warning
pnpm db:reset menghapus schema PostgreSQL lokal. Guard bawaan hanya mengizinkan database bernama
operator_pos atau operator_pos_*; jangan pernah menjalankannya ke production database.
Render Blueprint membuat satu same-origin Fastify + PWA service, satu independent inventory worker, dan satu PostgreSQL database di region Singapore. Migration berjalan concurrency-safe saat API dan worker start; deterministic demo seed hanya berjalan pada first deploy.
Caution
Ini adalah isolated demo sandbox, bukan production template. Inventory worker memakai
paid starter instance. Free Render PostgreSQL kedaluwarsa setelah 30 hari dan tidak menyediakan
backup. Demo credentials di atas bersifat publik. Review estimasi biaya sebelum approve, lalu hapus
seluruh Render project setelah selesai mencoba.
Panduan first deploy, smoke test, troubleshooting, dan teardown ada di Render Demo Deployment.
| Command | Bukti yang dijalankan |
|---|---|
pnpm format:check |
Repository mengikuti canonical formatting |
pnpm lint |
Oxlint type-aware dan maintainability limits |
pnpm typecheck |
Strict TypeScript untuk contracts, web, dan API |
pnpm test |
Unit + fake IndexedDB integration |
pnpm test:integration |
Real PostgreSQL, auth/admin, idempotency, lost response, worker replay |
pnpm test:load |
Mixed load 50 merchant: sync, Admin, reporting, insight |
pnpm test:load:500 |
Capacity profile eksplisit 500 merchant selama lima menit |
pnpm test:e2e |
Production-build Playwright scenarios |
pnpm docs:check |
Seluruh local Markdown link dan image reference |
pnpm run ci |
Seluruh repository quality gates dalam satu command |
| GitHub Actions | Full repository quality gate dengan PostgreSQL service |
Build hijau bukan klaim production-ready. Secrets management, rate limiting/WAF, accessibility, load testing, monitoring, encrypted backup/PITR, dan restore drill tetap wajib sebelum menangani merchant sungguhan.
- Project Playbook — pintu masuk seluruh product dan engineering knowledge.
- Product Principles — problem, scope, invariants, dan success criteria.
- Traceability Matrix — requirement ke code, API, test, dan demo step.
- System Architecture — components, trust boundary, dan data flow.
- Scaling Strategy — workload budgets, evidence, dan scale triggers.
- Database Design — canonical ledger, sessions, audit, dan outbox schema.
- Testing Strategy — unit, integration, failure injection, dan E2E.
- Development Guide — setup, environment, dan workspace boundaries.
- Deployment Plan — sandbox versus production topology.
- Operations Runbook — health signals dan incident response.
