Skip to content

Commit e87e32c

Browse files
docs(audience): add package and demo READMEs
Adds two READMEs: packages/audience/sdk/README.md Package-level usage doc. Install (npm + CDN), quick start example, public method list with short signatures, consent level behaviour table, auto-tracked event list, cookie reference, and a pointer to the demo. packages/audience/sdk/demo/README.md Demo harness usage doc. How to run (pnpm demo → serves localhost:3456), test publishable keys for dev + sandbox, a step-by-step 'what to try' script, environments table, troubleshooting for the common issues (bundle failing to load, 400/403 from the API, no BigQuery data), and a files layout section. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent ca6a102 commit e87e32c

2 files changed

Lines changed: 212 additions & 0 deletions

File tree

packages/audience/sdk/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# @imtbl/audience
2+
3+
Consent-aware event tracking and identity resolution for Immutable studios.
4+
5+
> **Pre-release.** This package is at version `0.0.0`. The API is stabilizing but breaking changes may still land before the first npm publish.
6+
7+
## Install
8+
9+
```sh
10+
npm install @imtbl/audience
11+
# or
12+
pnpm add @imtbl/audience
13+
# or
14+
yarn add @imtbl/audience
15+
```
16+
17+
Once published to npm, you'll be able to load the package via CDN (no bundler required):
18+
19+
```html
20+
<!-- Replace <version> with a specific release tag once @imtbl/audience is published. -->
21+
<script src="https://cdn.jsdelivr.net/npm/@imtbl/audience@<version>/dist/cdn/imtbl-audience.global.js"></script>
22+
<script>
23+
const audience = ImmutableAudience.Audience.init({
24+
publishableKey: 'pk_imapik-...',
25+
environment: 'sandbox',
26+
consent: 'anonymous',
27+
});
28+
</script>
29+
```
30+
31+
Until the first npm release, you can build the CDN bundle locally from this repo: `cd packages/audience/sdk && pnpm build`. The output is at `dist/cdn/imtbl-audience.global.js`. See `demo/README.md` for the interactive demo that loads it.
32+
33+
## Quickstart
34+
35+
```ts
36+
import { Audience, IdentityType } from '@imtbl/audience';
37+
38+
const audience = Audience.init({
39+
publishableKey: 'pk_imapik-...',
40+
environment: 'sandbox',
41+
consent: 'anonymous', // or 'none' until the user opts in
42+
debug: true,
43+
onError: (err) => {
44+
console.error('[audience]', err.code, err.status, err.responseBody);
45+
},
46+
});
47+
48+
// Track a page view
49+
audience.page({ section: 'marketplace' });
50+
51+
// Track a custom event
52+
audience.track('purchase_completed', { sku: 'pack-1', usd: 9.99 });
53+
54+
// Upgrade consent and identify the user
55+
audience.setConsent('full');
56+
audience.identify('player-7721', IdentityType.Passport, { plan: 'premium' });
57+
58+
// Link a previous identity
59+
audience.alias(
60+
{ id: '76561198012345', identityType: IdentityType.Steam },
61+
{ id: 'player-7721', identityType: IdentityType.Passport },
62+
);
63+
64+
// On logout
65+
audience.reset();
66+
67+
// On app unmount
68+
audience.shutdown();
69+
```
70+
71+
## API
72+
73+
### `Audience.init(config): Audience`
74+
75+
Creates and starts the SDK. `config` is an `AudienceConfig`:
76+
77+
| Field | Type | Required | Description |
78+
|---|---|---|---|
79+
| `publishableKey` | `string` | yes | Publishable API key from Immutable Hub (prefix: `pk_imapik-`). |
80+
| `environment` | `'dev' \| 'sandbox' \| 'production'` | yes | Backend to target. |
81+
| `consent` | `'none' \| 'anonymous' \| 'full'` | no | Initial consent level. Defaults to `'none'`. |
82+
| `debug` | `boolean` | no | Log every SDK call and flush to the browser console. |
83+
| `cookieDomain` | `string` | no | Cookie domain for cross-subdomain sharing (e.g. `.studio.com`). |
84+
| `flushInterval` | `number` | no | Queue flush interval in ms. Defaults to `5000`. |
85+
| `flushSize` | `number` | no | Batch size that triggers an automatic flush. Defaults to `20`. |
86+
| `onError` | `(err: AudienceError) => void` | no | Called when a flush or consent sync fails. |
87+
88+
### Methods
89+
90+
- **`page(properties?)`** — record a page view. Call on every route change.
91+
- **`track(eventName, properties?)`** — record a custom event.
92+
- **`identify(id, identityType, traits?)`** — tell the SDK who this player is. Requires `full` consent.
93+
- **`identify(traits)`** — traits-only overload for anonymous profile updates.
94+
- **`alias({id, identityType}, {id, identityType})`** — link two identities that belong to the same player.
95+
- **`setConsent(status)`** — update the consent level in response to a banner.
96+
- **`reset()`** — call on logout; rotates the anonymous ID and clears state.
97+
- **`flush()`** — force-send queued events.
98+
- **`shutdown()`** — stop the SDK and drain the queue.
99+
100+
## Identity types
101+
102+
The `identityType` argument to `identify()` and `alias()` must be one of:
103+
104+
| Value | Description |
105+
|---|---|
106+
| `passport` | Immutable Passport ID |
107+
| `steam` | Steam ID (64-bit) |
108+
| `epic` | Epic Games account ID |
109+
| `google` | Google account ID |
110+
| `apple` | Apple ID |
111+
| `discord` | Discord user ID |
112+
| `email` | Email address |
113+
| `custom` | Studio-defined custom ID |
114+
115+
Import the `IdentityType` enum to reference these at runtime:
116+
117+
```ts
118+
import { IdentityType } from '@imtbl/audience';
119+
120+
IdentityType.Passport; // 'passport'
121+
```
122+
123+
## Error handling
124+
125+
`AudienceConfig.onError` receives an `AudienceError` with these fields:
126+
127+
```ts
128+
class AudienceError extends Error {
129+
readonly code: 'FLUSH_FAILED' | 'CONSENT_SYNC_FAILED' | 'NETWORK_ERROR' | 'VALIDATION_REJECTED';
130+
readonly status: number; // HTTP status, 0 for network failure
131+
readonly endpoint: string; // full URL that failed
132+
readonly responseBody?: unknown; // parsed JSON body from the backend
133+
readonly cause?: unknown; // original fetch error on network failure
134+
}
135+
```
136+
137+
Errors are delivered asynchronously (after the failing flush completes). Throwing from `onError` is safe — the SDK catches and suppresses callback exceptions.
138+
139+
## Demo
140+
141+
There's an interactive demo under `demo/` that exercises every public method against the real backend. See `demo/README.md` for instructions.
142+
143+
## License
144+
145+
Apache-2.0
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# @imtbl/audience — Demo
2+
3+
Single-page interactive harness that exercises every public method on the `Audience` class against the real Immutable backend.
4+
5+
## Run
6+
7+
```sh
8+
cd packages/audience/sdk
9+
pnpm demo
10+
```
11+
12+
This runs `pnpm build` (ESM + CDN bundle + types) then serves the package root on `http://localhost:3456`. Open:
13+
14+
```
15+
http://localhost:3456/demo/
16+
```
17+
18+
Stop the server with `Ctrl+C`.
19+
20+
## Test publishable keys
21+
22+
These are test-only keys registered for audience tracking. Safe to commit and share.
23+
24+
| Environment | Key |
25+
|---|---|
26+
| `dev` | `pk_imapik-test-Xei06NzJZClzQogVXlKQ` |
27+
| `sandbox` | `pk_imapik-test-5ss4GpFy-n@$$Ye3LSox` |
28+
29+
## What to try
30+
31+
1. Paste a key, pick `sandbox`, set initial consent to `anonymous`, click **Init**.
32+
2. Watch the event log: you'll see `INIT`, `TRACK session_start`, and `FLUSH ok`. Check the browser DevTools Network tab — `POST /v1/audience/messages` should return 200.
33+
3. Click **Call page()** with no properties → `PAGE` entry + 200 response.
34+
4. Enter `{"section":"marketplace"}` in the page properties textarea → `PAGE {section: marketplace}`.
35+
5. Track a custom event with properties → `TRACK`.
36+
6. Set consent to `full``PUT /v1/audience/tracking-consent` returns 204.
37+
7. Identify a user (any made-up ID, type `passport`, optional traits) → status bar's User ID updates.
38+
8. Try Alias with a Steam ID → Passport ID → `ALIAS` entry.
39+
9. Click **Reset** → anonymous ID rotates, session end + start fire.
40+
10. Click **Shutdown** → session end fires, buttons flip off.
41+
42+
## Environments
43+
44+
| Env | API URL | Consent PUT |
45+
|---|---|---|
46+
| `dev` | `api.dev.immutable.com` | **known broken — returns 500.** Use `sandbox` to exercise consent sync. |
47+
| `sandbox` | `api.sandbox.immutable.com` | works |
48+
49+
## Troubleshooting
50+
51+
- **`window.ImmutableAudience is undefined`** in the demo page: the CDN bundle failed to load. Re-run `pnpm build` from `packages/audience/sdk` and confirm `dist/cdn/imtbl-audience.global.js` exists.
52+
- **`POST /v1/audience/messages` returns 400**: the publishable key format is wrong. Must start with `pk_imapik-`.
53+
- **`POST /v1/audience/messages` returns 403**: the key isn't registered for audience tracking on the backend. Use one of the keys in the table above.
54+
- **Identify button is a no-op**: consent is not `full`. Click **Set full** first.
55+
- **No events in BigQuery after 30s**: events go through SQS → Pub/Sub → BigQuery. BQ access requires `roles/bigquery.dataViewer` on `dev-im-cdp`. If you don't have it, the API ack (`POST /messages` 200) is your E2E confirmation.
56+
57+
## Files
58+
59+
```
60+
demo/
61+
index.html # single page, loads ../dist/cdn/imtbl-audience.global.js
62+
demo.js # vanilla ES2020, no modules; reads window.ImmutableAudience
63+
demo.css # light theme, hand-written CSS, no external deps
64+
README.md # this file
65+
```
66+
67+
Security: all user-controlled inputs (event names, traits, publishable keys) are rendered via `textContent` / `createElement`. No `innerHTML` anywhere on user data. CSP meta tag restricts `connect-src` to the dev and sandbox API origins.

0 commit comments

Comments
 (0)