add sume
This commit is contained in:
+207
@@ -0,0 +1,207 @@
|
||||
<!-- generated-by: gsd-doc-writer -->
|
||||
|
||||
# @araharah/web
|
||||
|
||||
The arah² Telegram Mini App — a MapLibre GL navigation shell built with Vite and TypeScript. It owns the three-source location acquisition chain, an in-memory-only session store, the Telegram WebApp integrations (theme slots, BackButton, safe-area insets), and the Rams chrome (status strip, boot/denied/signal-lost cards, recenter plate).
|
||||
|
||||
Part of the [arah²](../README.md) monorepo (workspaces: `theme`, `bot`, `web`). Private package — not published to a registry.
|
||||
|
||||
<!-- VERIFY: the deploy step mounts web/dist/ via the bot worker's ASSETS binding (see ../bot wrangler config) -->
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
Installed through the monorepo root (npm workspaces), not standalone:
|
||||
|
||||
```bash
|
||||
# from the repository root
|
||||
npm install
|
||||
```
|
||||
|
||||
`@araharah/theme` is a workspace dependency (`"@araharah/theme": "*"`), so the theme package must be built before this one:
|
||||
|
||||
```bash
|
||||
npm run build -w theme # emits theme/dist/rams.css + the basemap style
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
npm run web:dev # vite dev server on :5173 (strictPort)
|
||||
```
|
||||
|
||||
With no Telegram client present, `src/mock/telegram-mock.ts` installs a fake `window.Telegram.WebApp` automatically in dev (`import.meta.env.DEV`), so the shell boots to `LIVE` in a plain browser with the KL default fix. Without it, the app renders its denied card — still no blank screen.
|
||||
|
||||
Production build (what the Worker serves):
|
||||
|
||||
```bash
|
||||
npm run web:build # theme build + vite build -> web/dist
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scripts
|
||||
|
||||
Run from the package directory, or from the root with `npm run <script> -w web`.
|
||||
|
||||
| Command | Description |
|
||||
|---|---|
|
||||
| `npm run dev` | Vite dev server on port 5173 (`strictPort: true`, `vite.config.ts`). |
|
||||
| `npm run build` | Production build to `dist/` — the harness mock and fetch stubs are dead-code-eliminated. |
|
||||
| `npm run build:harness` | `vite build --mode harness` to `dist-harness/` — `__ARAHARAH_HARNESS__` is `true`, so the mock installer and runner-page stubs are compiled in. Never deployed. |
|
||||
| `npm test` | Zero-dependency runner (`node test/runner.mjs`) that auto-discovers `test/*.test.mjs`. |
|
||||
| `npm run typecheck` | `tsc --noEmit` over `src` (strict, `noUncheckedIndexedAccess`, `verbatimModuleSyntax`). |
|
||||
| `npm run capture` | Playwright harness (`scripts/capture.mjs`): serves `dist-harness/`, drives every boot state, writes `screenshots/`, and runs the runtime storage probe. |
|
||||
|
||||
`dist/` and `dist-harness/` must never share an outDir — the two builds would clobber each other and void the production-artifact gate.
|
||||
|
||||
---
|
||||
|
||||
## Module map
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.ts boot choreography — order is load-bearing
|
||||
├── shell.css the Rams chrome; imports @araharah/theme/dist/rams.css
|
||||
├── vite-env.d.ts ambient __ARAHARAH_HARNESS__ declaration
|
||||
├── telegram/
|
||||
│ ├── webapp.ts the one typed accessor for window.Telegram.WebApp (TelegramWebAppLike)
|
||||
│ ├── theme.ts Telegram theme signal -> [data-tg-theme] slot switch
|
||||
│ ├── safe-area.ts env(safe-area-inset-*) ∪ WebApp.safeAreaInset -> CSS custom properties
|
||||
│ └── backbutton.ts BackButton total wire (root press -> WebApp.close())
|
||||
├── session/
|
||||
│ ├── client.ts POST /api/session with the verbatim initData string
|
||||
│ └── store.ts memory-only token holder, 900s TTL — no storage API anywhere
|
||||
├── location/
|
||||
│ ├── chain.ts acquireFix(): the locked telegram -> webview -> chat-pin chain
|
||||
│ ├── telegram.ts LocationManager wrapper + Android stale-fix nudge
|
||||
│ ├── webview.ts navigator.geolocation (maximumAge: 0)
|
||||
│ ├── chat-pin.ts bounded poll of /api/location + ASK VIA CHAT trigger
|
||||
│ └── types.ts Fix, FixSource, LocationState, KL_CENTER
|
||||
├── map/
|
||||
│ ├── map.ts MapLibre v6 bootstrap; returns null when WebGL2 is absent
|
||||
│ ├── follow.ts following/free camera controller
|
||||
│ └── marker.ts position marker + accuracy circle + heading cone
|
||||
├── ui/
|
||||
│ ├── strip.ts status strip (validating/acquiring/live/lost/denied/awaiting)
|
||||
│ ├── cards.ts boot / denied / signal-lost cards + gantry blink
|
||||
│ └── recenter.ts follow/recenter plate
|
||||
├── i18n/strings.ts every user-facing string, en + ms (the copy gate)
|
||||
└── mock/telegram-mock.ts Mock-Telegram harness — excluded from dist by BA5
|
||||
```
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
MAIN["src/main.ts — boot choreography"]
|
||||
WA["src/telegram/webapp.ts"]
|
||||
TH["src/telegram/theme.ts"]
|
||||
SA["src/telegram/safe-area.ts"]
|
||||
BB["src/telegram/backbutton.ts"]
|
||||
SESS["src/session/client.ts"]
|
||||
STORE["src/session/store.ts"]
|
||||
CHAIN["src/location/chain.ts"]
|
||||
TG["src/location/telegram.ts"]
|
||||
WV["src/location/webview.ts"]
|
||||
PIN["src/location/chat-pin.ts"]
|
||||
TYPES["src/location/types.ts"]
|
||||
MAP["src/map/map.ts"]
|
||||
FOLLOW["src/map/follow.ts"]
|
||||
MARK["src/map/marker.ts"]
|
||||
STRIP["src/ui/strip.ts"]
|
||||
CARDS["src/ui/cards.ts"]
|
||||
REC["src/ui/recenter.ts"]
|
||||
STR["src/i18n/strings.ts"]
|
||||
MOCK["src/mock/telegram-mock.ts"]
|
||||
|
||||
MAIN --> WA
|
||||
MAIN --> TH
|
||||
MAIN --> SA
|
||||
MAIN --> BB
|
||||
MAIN --> SESS
|
||||
MAIN --> CHAIN
|
||||
MAIN --> MAP
|
||||
MAIN --> STRIP
|
||||
MAIN --> CARDS
|
||||
MAIN --> REC
|
||||
MAIN --> MOCK
|
||||
SESS --> STORE
|
||||
CHAIN --> TG
|
||||
CHAIN --> WV
|
||||
CHAIN --> PIN
|
||||
CHAIN --> TYPES
|
||||
TG --> TYPES
|
||||
WV --> TYPES
|
||||
PIN --> TYPES
|
||||
MAP --> FOLLOW
|
||||
MAP --> MARK
|
||||
FOLLOW --> TYPES
|
||||
MARK --> TYPES
|
||||
STRIP --> TYPES
|
||||
STRIP --> STR
|
||||
CARDS --> STR
|
||||
REC --> STR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage examples
|
||||
|
||||
Acquire a fix through the chain (this is exactly what `main.ts` does):
|
||||
|
||||
```ts
|
||||
import { acquireFix, isFixStale, fixAgeSeconds } from "./location/chain.ts";
|
||||
import { createSession, } from "./session/client.ts";
|
||||
import { getWebApp } from "./telegram/webapp.ts";
|
||||
import { getSession } from "./session/store.ts";
|
||||
|
||||
const wa = getWebApp(); // null off-Telegram — the chain handles null
|
||||
const session = await createSession(wa?.initData ?? "");
|
||||
if (!session.ok) throw new Error(session.reason); // "malformed" | "tampered" | "stale" | "network"
|
||||
|
||||
const { fix, state } = await acquireFix({ wa, getToken: getSession }, null);
|
||||
if (fix !== null && !isFixStale(fix, Date.now())) {
|
||||
console.log(fix.source, fixAgeSeconds(fix, Date.now()), "s");
|
||||
} else {
|
||||
console.log(state.kind); // "denied" | "awaiting-pin" | ...
|
||||
}
|
||||
```
|
||||
|
||||
Boot the map on its own (returns `null` rather than throwing on a WebGL2-less device):
|
||||
|
||||
```ts
|
||||
import { bootMap } from "./map/map.ts";
|
||||
import { createPositionMarker } from "./map/marker.ts";
|
||||
|
||||
const map = bootMap(document.getElementById("map")!);
|
||||
if (map !== null) {
|
||||
const marker = createPositionMarker();
|
||||
marker.marker.addTo(map);
|
||||
marker.update(map, fix);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
```bash
|
||||
npm run web:test # or: npm test (inside web/)
|
||||
npm run web:typecheck
|
||||
```
|
||||
|
||||
`test/runner.mjs` auto-discovers every `test/*.test.mjs` suite and drains the shared registry from `test/harness.mjs`. Suites cover the location chain (`location-chain.test.mjs`), the memory-only session store (`session.test.mjs`), theme slots (`theme.test.mjs`), the en/ms copy gates (`copy-gate.test.mjs`), the no-storage invariant (`no-storage.test.mjs`), and the production build artifact (`build-artifact.test.mjs` — CDN references, self-hosted fonts, hashed MapLibre worker, inlined Rams style, and BA5/BA6 asserting no mock installer or fetch stub ships in `dist/`).
|
||||
|
||||
The Playwright capture harness is a separate command (`npm run web:capture`): it serves `dist-harness/`, waits on the `RAMS-BASEMAP-READY` and `ARAHARAH-LIVE` console signals, and writes the state gallery into `screenshots/`.
|
||||
|
||||
---
|
||||
|
||||
## Related docs
|
||||
|
||||
- Root [README.md](../README.md) — monorepo overview and system diagram
|
||||
- [docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md) — component responsibilities across `theme` / `bot` / `web`
|
||||
- [docs/DEVELOPMENT.md](../docs/DEVELOPMENT.md) — local setup, build, and PR workflow
|
||||
- [docs/TESTING.md](../docs/TESTING.md) — the cross-package test strategy
|
||||
- [DESIGN.md](../DESIGN.md) and `theme/` — the Telemetry Rams design system this shell consumes
|
||||
Reference in New Issue
Block a user