|
| 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' | 'UNKNOWN'; |
| 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 |
0 commit comments