@venncity/bi-events
v0.13.9
Published
Typed wrapper + generated catalog for Venn BI events. The only sanctioned event emitter — Segment lives inside via per-platform adapters (web / react-native / node).
Readme
@venncity/bi-events
The typed npm wrapper for Venn BI events — the only sanctioned way to emit an
event. It owns the typed track() API, the mandatory envelope, and contract
enforcement, and delegates the actual send to whatever analytics client an
app already has, via a Transport.
It is a new package (not an extension of the mobile in-repo wrapper or
@venncity/monitor-ui) because it is generated, version-pinned to the contract,
and must serve mobile and web behind one typed API.
What's generated vs hand-written
src/generated/index.ts— generated from the contract'sschema-generated/bygo run ./cmd/gen-npm. Event name → allowed action → required/optional traits, plus the frozenEVENTScatalog (keys, version, kind). Do not edit.src/client.ts,src/transport.ts— hand-written runtime:createBiEvents, envelope assembly, and contract checks.
Usage
import { createBiEvents } from "@venncity/bi-events";
const bi = createBiEvents({
app: "MobileApp", // product surface
deviceType: "ios",
appVersion: "6.16.0", // mobile: app version · web: release tag from env var
build: "20235",
namespace: "com.venn.mac", // mobile bundle id (omit on web)
transport, // see below
getIdentity: () => ({ anonymousId, userId, organizationId }),
});
// Fully typed: name → action → traits. Missing/extra/mistyped traits fail tsc.
bi.track("UnlockButton", "Clicked", { lockId: 496924, lockName: "Fitness Center" });track fills timestamp, message_id, and the identity fields, enforces
required traits, and — for Known events — throws if userId/organizationId
are missing. It then validates the full wire event against the generated JSON
Schema with ajv (draft 2020-12, incl. uuid/date-time formats). Disable
with validate: false, or handle failures via onValidationError (default:
throw). The embedded schemas live in src/generated/schemas.ts.
Transport adapters (delegate, don't replace)
Mobile (@segment/analytics-react-native):
import { getSegment } from "app/libs/monitoring/analytics/segment";
const transport = (e) => getSegment().track(e.event_key, e);Web (@segment/analytics-next):
import { analytics } from "~/lib/analytics";
const transport = (e) => { analytics.track(e.event_key, e); };The wire event's event_key (uuid) is the Segment event name; the whole wire
object is the properties payload. Each app keeps its own SDK setup — only the
emit path is centralized.
Regenerate
npm run gen # go run ./cmd/gen-schemas && go run ./cmd/gen-npm
npm run build # tsc -> dist
node test/smoke.jsReleasing (manual)
Published to npmjs.org under @venncity by a maintainer (currently
venncity-admin). CI (Jenkins) builds + tests but does not publish — npmjs
2FA makes hands-free CI publishing impractical without a dedicated bypass-2FA
token. To cut a release:
# bump clients/npm/package.json "version", commit, and tag vX.Y.Z in the repo
cd clients/npm
npm install # first time only, to get tsc + ajv
npm run release # = npm publish --access public (builds via prepublishOnly); add --otp=<code> if 2FA promptsConsumers pin by version (e.g. "@venncity/bi-events": "^0.1.0").
Canary (prerelease) releases
For testing an in-progress contract in an app without touching the latest
tag:
cd clients/npm
npm run release:canary # bumps to e.g. 0.1.2-canary.0 and publishes under the `canary` dist-taglatestis unaffected — normal^x.y.zconsumers don't pick it up.- Opt in explicitly:
yarn add @venncity/bi-events@canary(ornpm i …@canary), or pin the exact prerelease:@venncity/[email protected]. - It bumps the
versioninpackage.json(a prerelease). Don't commit that onmain— discard withgit checkout clients/npm/package.jsonafter, or run it from a branch. Re-running increments the suffix (-canary.1,-canary.2, …) so each canary is a unique, non-colliding version.
First adoption
mobile-app-v2 wires a Segment-delegating transport in
app/libs/monitoring/analytics/biEvents.ts (initBiEvents(bundleId) →
getBiEvents().track(...)), pulling identity from the selected context-option
atom and app_version from the app version util.
Not yet (follow-ups)
- Publish to the registry, pinned to the contract's git tag.
- Anonymous-vs-Known split at the type level (currently a runtime check).
- Web transport adapters (experience/resident/applicant) + the web
app_versionrelease-tag env var (see the Notion "Feasibility & Risks").
