@team_cleave/gravity-zero-web-sdk
v0.2.6
Published
Gravity Zero analytics logging SDK for browsers.
Readme
Web SDK
TypeScript analytics SDK for browsers. Framework-agnostic — works with React, Vue, Svelte, Angular, or vanilla JS. Speaks the same envelope wire protocol as the Android and iOS SDKs.
Status: v0.1.0 — core pipeline.
Install
npm install @team_cleave/gravity-zero-web-sdkUsage
import { init, identify, track, flush } from "@team_cleave/gravity-zero-web-sdk";
init({
endpoint: "https://logs.bank.co.kr/v1/ingest",
tenantId: "bank_xyz",
apiKey: "...", // optional Bearer token
userIdHashSalt: "...", // per-tenant salt for user-id hashing
appVersion: "5.2.1", // browser has no native source; you supply it
});
identify("user_123"); // synchronous (void)
track("button_click", { button_id: "submit_payment" });
await flush(); // optional explicit flushAPI
| Method | Notes |
|--------|-------|
| init(config) | Idempotent. Never throws. |
| identify(userId) | void. Hashes salt + userId with SHA-256 locally; raw id never sent. |
| track(name, properties?) | name must match ^[a-z][a-z0-9_]{0,63}$. Oversized properties (>64KB) dropped. |
| flush() | Promise<void>. Forces a flush; otherwise flushes on interval/size. |
| reset() | Clears identity, rotates anonymous_id, starts a new session (logout). |
| shutdown() | Stops timers and lifecycle listeners. |
Architecture
Implements the shared 5-layer pipeline (see ../../docs/ARCHITECTURE.md),
mirroring the Android reference SDK:
| Layer | Implementation |
|-------|----------------|
| Public API | src/index.ts — singleton module, swallows all internal errors |
| Context | src/internal/context.ts — navigator/screen/Intl; anonymous_id in localStorage |
| Buffer | src/internal/buffer.ts — bounded FIFO, drops oldest on overflow |
| Persistence | src/internal/storage.ts — IndexedDB; recovered on init, deleted on flush success |
| Transport | src/internal/transport.ts — fetch + retry/backoff/jitter |
| Lifecycle | src/internal/lifecycle.ts — visibilitychange/pagehide → keepalive exit flush |
Notable web-specific choices
- Types are generated from the shared schema (
npm run generate:types, runs onprebuild), so the SDK can't drift fromschema/envelope-1.0.schema.json. identify()is synchronous despitecrypto.subtlebeing async: the hash is attached to the shared envelope context before the next flush, so it staysvoidlike the mobile SDKs.- Exit delivery uses
fetchwithkeepalive, notnavigator.sendBeacon— sendBeacon cannot set theAuthorization: Bearerheader the ingest endpoint requires. fetch keepalive survives unload and carries the auth header. device.model/manufacturerarenullandapp.versiondefaults to""(vs. Android's"unknown") — both intentional platform differences.context.networkis best-effort from the Network Information API (navigator.connection), which is widely unsupported:typeis one ofwifi/cellular/ethernet/none/unknown(offline →none, unsupported →unknown), andcarrieris alwaysnullon web.
Backend requirement: CORS
Browsers send a preflight OPTIONS and require Access-Control-* headers. The
ingest server must list this app's origin in its ALLOWED_ORIGINS env var. This
is operator config (set during tenant onboarding), not something the SDK controls.
Native mobile SDKs send no Origin and are unaffected.
Develop
npm install
npm run build # generate types + emit ESM/CJS/d.ts
npm test # vitest unit tests
npm run typecheckSee it run
npm run sample # build + serve the mock app at /sample, then open the
# printed localhost URLsample/ is the web counterpart of the Android/iOS sample apps: an interactive
mock bank app (config + init/identify/track/flush/reset, SPA-navigation
page_views, a live event log). On the allowlisted network it auto-prefills a
freshly-minted token (revoked on exit); otherwise paste one in the token field.
