Add examples/chat: WhatsApp-style local-first chat - #183
Draft
lucas-barake wants to merge 1 commit into
Draft
Conversation
…ver monorepo) A comprehensive example exercising the full effect-local API surface: multi-tab OPFS replicas, sync over WebSocket RPC with bearer auth, ephemeral typing/presence channels, SQL-windowed message pagination, and settlement-driven WhatsApp receipts (sent/delivered/read) plus a failed-message overlay with retry/discard. - shared/: branded-id domain models, deterministic mutation/query handlers used by both client and server, full-roster enforcement for the pinned group conversation, JSON-tuple read-state keys - server/: Node HTTP + WebSocket sync, /login with token auth, default-deny authorizer enforcing senderId/createdBy === principal - client/: React + atoms, no loading states (local-first render), optimistic sends with settlement-driven ticks, in-memory failed overlay fed by a settlement daemon streaming from the acknowledged floor (get.once + overlay-before-ack so teardown races replay idempotently instead of livelocking or dropping failure records) - test/: real-stack smoke suite (loopback WS, two SqlReplica clients) that rendezvouses on reactivity/settlement streams - no sleeps Root wiring: workspace glob, tsconfig project reference, vitest include, scoped oxlint overrides for platform adapters, dist-web ignore so the vite bundle stops clobbering composite declaration output, and a pre-commit snapshot linker that now covers nested workspace node_modules (examples/*/*), not just packages/*. Example packages set the DOM lib because workspace deps are consumed as TypeScript source whose files use web globals such as TextEncoder.
|
| GitGuardian id | GitGuardian status | Secret | Commit | Filename | |
|---|---|---|---|---|---|
| 36289973 | Triggered | Generic Password | 7719d63 | examples/chat/shared/src/domain.ts | View secret |
| 36289974 | Triggered | Generic Password | 7719d63 | examples/chat/shared/src/domain.ts | View secret |
🛠 Guidelines to remediate hardcoded secrets
- Understand the implications of revoking this secret by investigating where it is used in your code.
- Replace and store your secrets safely. Learn here the best practices.
- Revoke and rotate these secrets.
- If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.
To avoid such incidents in the future consider
- following these best practices for managing and storing secrets including API keys and other credentials
- install secret detection on pre-commit to catch secret before it leaves your machine and ease remediation.
🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A comprehensive, runnable example of the whole effect-local stack: a WhatsApp-like chat app as a pnpm monorepo under
examples/chat(shared/,server/,client/,test/). Four hard-coded users, real bearer-auth login, DMs + a pinned group, typing indicators, presence, and full read receipts — ✓ gray (server-acked), ✓✓ gray (delivered to peer), ✓✓ blue (read) — with WhatsApp-style failed-send handling (retry / discard). Local-first throughout: the app renders instantly from the local replica and shows no loading states anywhere.Layout and wiring:
shared/— domain models with branded ids (UserId/ConversationId/MessageId), deterministic mutation + query handlers used verbatim by both client and server, read-state keys as JSON tuples (conversation ids contain:), and the full-roster invariant for the pinnedgroup:everyoneconversation (its deterministic id is create-if-absent, so a partial or duplicate-substituted roster would otherwise pin the group forever).server/— Node HTTP + WebSocket sync,/loginissuing tokens, and a default-denyauthorizeMutationthat enforcessenderId/createdBy=== principal.client/— React + atoms over multi-tab OPFS replicas. Optimistic sends; ticks advance from the settlement stream, not from query state. Failed sends live in an in-memory overlay atom fed by a settlement daemon.test/— smoke suite over real stacks (loopback WebSockets, twoSqlReplicaclients, realAtomRegistry+ReplicaAtomwiring). All waits rendezvous on reactivity and settlement streams — no sleeps or polling.The settlement daemon (
client/src/settlementDaemon.ts) is the subtle part and got the most review attention. It streamssettlementsFor(SendMessage, { from: "acknowledged" })so rejections that settle during a MultiTab handoff or while the tab was closed still reach the failed overlay. Two ordering constraints are load-bearing:Both failure modes were reproduced in review (the livelock as a stack overflow) and are pinned by a regression test that forges
senderId(local accept, server terminal-reject) and asserts the overlay entry plus floor advancement.Limitations, documented in the README:
EphemeralHub'sAuthorizationInputdoesn't expose the published value, so the server can't bind ephemeral claims to the principal. Spoofable; needs a library-level fix.createdAt; clock skew can corrupt ticks/unread counts. A proper fix is server-assigned per-conversation sequences.discardQuarantined); the entry converges away via the settlement stream.Repo plumbing worth knowing about:
packages/*/node_modules; it now walks the tree and links nested workspacenode_modules(examples/*/*), otherwise the snapshot drowns in module-resolution diagnostics.lib: ["ES2023", "DOM"]because workspace deps are consumed as TypeScript source andpackages/localsources useTextEncoder(same reasonpackages/localsets it).dist-web/— vite'semptyOutDirwas wiping the composite project's declaration output indist/and desyncing incremental build state.