@traffical/js-client 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +209 -0
  2. package/dist/client.d.ts +167 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +429 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/error-boundary.d.ts +47 -0
  7. package/dist/error-boundary.d.ts.map +1 -0
  8. package/dist/error-boundary.js +118 -0
  9. package/dist/error-boundary.js.map +1 -0
  10. package/dist/event-logger.d.ts +75 -0
  11. package/dist/event-logger.d.ts.map +1 -0
  12. package/dist/event-logger.js +186 -0
  13. package/dist/event-logger.js.map +1 -0
  14. package/dist/exposure-dedup.d.ts +49 -0
  15. package/dist/exposure-dedup.d.ts.map +1 -0
  16. package/dist/exposure-dedup.js +94 -0
  17. package/dist/exposure-dedup.js.map +1 -0
  18. package/dist/global.d.ts +38 -0
  19. package/dist/global.d.ts.map +1 -0
  20. package/dist/global.js +67 -0
  21. package/dist/global.js.map +1 -0
  22. package/dist/index.d.ts +41 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +46 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/plugins/decision-tracking.d.ts +68 -0
  27. package/dist/plugins/decision-tracking.d.ts.map +1 -0
  28. package/dist/plugins/decision-tracking.js +83 -0
  29. package/dist/plugins/decision-tracking.js.map +1 -0
  30. package/dist/plugins/dom-binding.d.ts +48 -0
  31. package/dist/plugins/dom-binding.d.ts.map +1 -0
  32. package/dist/plugins/dom-binding.js +211 -0
  33. package/dist/plugins/dom-binding.js.map +1 -0
  34. package/dist/plugins/index.d.ts +70 -0
  35. package/dist/plugins/index.d.ts.map +1 -0
  36. package/dist/plugins/index.js +194 -0
  37. package/dist/plugins/index.js.map +1 -0
  38. package/dist/plugins/types.d.ts +62 -0
  39. package/dist/plugins/types.d.ts.map +1 -0
  40. package/dist/plugins/types.js +7 -0
  41. package/dist/plugins/types.js.map +1 -0
  42. package/dist/stable-id.d.ts +44 -0
  43. package/dist/stable-id.d.ts.map +1 -0
  44. package/dist/stable-id.js +129 -0
  45. package/dist/stable-id.js.map +1 -0
  46. package/dist/storage.d.ts +41 -0
  47. package/dist/storage.d.ts.map +1 -0
  48. package/dist/storage.js +144 -0
  49. package/dist/storage.js.map +1 -0
  50. package/dist/traffical.min.js +3 -0
  51. package/dist/traffical.min.js.map +7 -0
  52. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,209 @@
1
+ # @traffical/js-client
2
+
3
+ JavaScript SDK for browser environments with error boundaries, exposure deduplication, and smart event batching.
4
+
5
+ ## Installation
6
+
7
+ ### NPM
8
+
9
+ ```bash
10
+ npm install @traffical/js-client
11
+ # or
12
+ bun add @traffical/js-client
13
+ ```
14
+
15
+ ### CDN
16
+
17
+ ```html
18
+ <script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ### NPM / ES Modules
24
+
25
+ ```typescript
26
+ import { createTrafficalClient } from '@traffical/js-client';
27
+
28
+ const traffical = await createTrafficalClient({
29
+ orgId: 'org_xxx',
30
+ projectId: 'proj_xxx',
31
+ env: 'production',
32
+ apiKey: 'pk_xxx',
33
+ });
34
+
35
+ // Get parameters
36
+ const params = traffical.getParams({
37
+ context: { userId: 'user_123' },
38
+ defaults: {
39
+ 'ui.hero.title': 'Welcome',
40
+ 'ui.hero.color': '#000',
41
+ },
42
+ });
43
+
44
+ // Make decision with tracking metadata
45
+ const decision = traffical.decide({
46
+ context: { userId: 'user_123' },
47
+ defaults: { 'ui.hero.title': 'Welcome' },
48
+ });
49
+
50
+ // Track exposure (automatically deduplicated)
51
+ traffical.trackExposure(decision);
52
+
53
+ // Track user events
54
+ traffical.track('purchase', { value: 99.99, orderId: 'ord_123' });
55
+ traffical.track('add_to_cart', { itemId: 'sku_456' });
56
+
57
+ // Track with explicit decision attribution
58
+ traffical.track('checkout_complete', { value: 1 }, { decisionId: decision.decisionId });
59
+ ```
60
+
61
+ ### CDN / Script Tag
62
+
63
+ ```html
64
+ <script src="https://cdn.traffical.io/js-client/v1/traffical.min.js"></script>
65
+ <script>
66
+ Traffical.init({
67
+ orgId: 'org_xxx',
68
+ projectId: 'proj_xxx',
69
+ env: 'production',
70
+ apiKey: 'pk_xxx',
71
+ }).then(function(traffical) {
72
+ var params = traffical.getParams({
73
+ context: { userId: 'user_123' },
74
+ defaults: { 'ui.hero.title': 'Welcome' },
75
+ });
76
+ console.log(params);
77
+ });
78
+ </script>
79
+ ```
80
+
81
+ ### Google Tag Manager
82
+
83
+ ```html
84
+ <script>
85
+ (function() {
86
+ var s = document.createElement('script');
87
+ s.src = 'https://cdn.traffical.io/js-client/v1/traffical.min.js';
88
+ s.onload = function() {
89
+ Traffical.init({
90
+ orgId: '{{Traffical Org ID}}',
91
+ projectId: '{{Traffical Project ID}}',
92
+ env: '{{Traffical Environment}}',
93
+ apiKey: '{{Traffical API Key}}',
94
+ });
95
+ };
96
+ document.head.appendChild(s);
97
+ })();
98
+ </script>
99
+ ```
100
+
101
+ ## Configuration Options
102
+
103
+ ```typescript
104
+ createTrafficalClient({
105
+ // Required
106
+ orgId: string,
107
+ projectId: string,
108
+ env: string,
109
+ apiKey: string,
110
+
111
+ // Optional
112
+ baseUrl?: string, // Default: https://sdk.traffical.io
113
+ refreshIntervalMs?: number, // Config refresh interval (default: 60000)
114
+ localConfig?: ConfigBundle, // Offline fallback config
115
+ eventBatchSize?: number, // Events per batch (default: 10)
116
+ eventFlushIntervalMs?: number, // Flush interval (default: 30000)
117
+ exposureSessionTtlMs?: number, // Dedup session TTL (default: 1800000)
118
+ plugins?: TrafficalPlugin[], // Plugins to register
119
+ });
120
+ ```
121
+
122
+ ## Features
123
+
124
+ - **Error Boundary** - SDK errors never crash your app
125
+ - **Exposure Deduplication** - Same user/variant = 1 exposure per session
126
+ - **Smart Batching** - Events batched and flushed efficiently
127
+ - **Beacon on Unload** - Events sent reliably on page close
128
+ - **Auto Stable ID** - Anonymous user identification via localStorage/cookie
129
+ - **Plugin System** - Extensible via plugins
130
+ - **DOM Binding Plugin** - Auto-apply parameters to DOM elements
131
+
132
+ ## Plugins
133
+
134
+ ### DOM Binding Plugin
135
+
136
+ Automatically applies parameter values to DOM elements based on bindings configured via the Traffical Visual Editor.
137
+
138
+ ```typescript
139
+ import { createTrafficalClient, createDOMBindingPlugin } from '@traffical/js-client';
140
+
141
+ const traffical = await createTrafficalClient({
142
+ orgId: 'org_xxx',
143
+ projectId: 'proj_xxx',
144
+ env: 'production',
145
+ apiKey: 'pk_xxx',
146
+ plugins: [
147
+ createDOMBindingPlugin({
148
+ observeMutations: true, // Watch for DOM changes (SPA support)
149
+ debounceMs: 100, // Debounce reapplication
150
+ }),
151
+ ],
152
+ });
153
+
154
+ // Parameters are automatically applied to DOM elements
155
+ // when getParams() or decide() is called
156
+ const params = traffical.getParams({
157
+ context: { userId: 'user_123' },
158
+ defaults: { 'hero.headline': 'Welcome' },
159
+ });
160
+
161
+ // Access the plugin for manual control
162
+ const bindingPlugin = traffical.getPlugin('dom-binding');
163
+ bindingPlugin?.applyBindings(); // Re-apply bindings
164
+ bindingPlugin?.getBindings(); // Get current bindings
165
+ ```
166
+
167
+ The plugin:
168
+ - Receives bindings from the config bundle via `onConfigUpdate`
169
+ - Applies parameter values to DOM elements via `onResolve` and `onDecision`
170
+ - Supports URL pattern matching (regex) for page-specific bindings
171
+ - Uses MutationObserver for SPA support
172
+ - Supports multiple property types: `innerHTML`, `textContent`, `src`, `href`, `style.*`
173
+
174
+ ## Development
175
+
176
+ ### Build
177
+
178
+ ```bash
179
+ # Install dependencies (from sdk/ root)
180
+ cd ../
181
+ bun install
182
+
183
+ # Build ESM + IIFE
184
+ cd js-client
185
+ bun run build
186
+
187
+ # Type check only
188
+ bun run typecheck
189
+ ```
190
+
191
+ ### Output
192
+
193
+ | File | Format | Use Case |
194
+ |------|--------|----------|
195
+ | `dist/index.js` | ESM | npm / bundlers |
196
+ | `dist/traffical.min.js` | IIFE | CDN / script tag |
197
+
198
+ ### Release to CDN
199
+
200
+ ```bash
201
+ # Requires wrangler CLI with R2 access configured
202
+ ./scripts/release-cdn.sh
203
+ ```
204
+
205
+ This uploads to:
206
+ - `cdn.traffical.io/js-client/v{VERSION}/` - Immutable, 1 year cache
207
+ - `cdn.traffical.io/js-client/v{MAJOR}/` - Latest major, 1 hour cache
208
+ - `cdn.traffical.io/js-client/latest/` - Latest, 5 minute cache
209
+
@@ -0,0 +1,167 @@
1
+ /**
2
+ * TrafficalClient - JavaScript SDK for browser environments.
3
+ *
4
+ * Features:
5
+ * - Same API as Node SDK: getParams(), decide(), trackExposure(), track()
6
+ * - Error boundary wrapping (P0)
7
+ * - Exposure deduplication (P0)
8
+ * - Smart event batching with beacon on unload (P1)
9
+ * - Plugin system (P2)
10
+ * - Auto stable ID for anonymous users
11
+ */
12
+ import { type ConfigBundle, type Context, type DecisionResult, type ParameterValue } from "@traffical/core";
13
+ import { type ErrorBoundaryOptions } from "./error-boundary.js";
14
+ import { type StorageProvider } from "./storage.js";
15
+ import { type TrafficalPlugin } from "./plugins/index.js";
16
+ export interface TrafficalClientOptions {
17
+ /** Organization ID */
18
+ orgId: string;
19
+ /** Project ID */
20
+ projectId: string;
21
+ /** Environment (e.g., "production", "staging") */
22
+ env: string;
23
+ /** API key for authentication */
24
+ apiKey: string;
25
+ /** Base URL for the SDK API (edge worker) */
26
+ baseUrl?: string;
27
+ /** Local config bundle for offline fallback */
28
+ localConfig?: ConfigBundle;
29
+ /** Refresh interval in milliseconds (default: 60000) */
30
+ refreshIntervalMs?: number;
31
+ /** Error boundary options */
32
+ errorBoundary?: ErrorBoundaryOptions;
33
+ /** Event batching options */
34
+ eventBatchSize?: number;
35
+ eventFlushIntervalMs?: number;
36
+ /** Exposure deduplication session TTL */
37
+ exposureSessionTtlMs?: number;
38
+ /**
39
+ * Whether to automatically track decision events (default: true).
40
+ * When enabled, every call to decide() automatically sends a DecisionEvent
41
+ * to the backend, enabling intent-to-treat analysis.
42
+ */
43
+ trackDecisions?: boolean;
44
+ /**
45
+ * Decision deduplication TTL in milliseconds (default: 1 hour).
46
+ * Same user+assignment combination won't be tracked again within this window.
47
+ */
48
+ decisionDeduplicationTtlMs?: number;
49
+ /** Plugins to register on init */
50
+ plugins?: TrafficalPlugin[];
51
+ /** Custom storage provider (default: localStorage) */
52
+ storage?: StorageProvider;
53
+ /** Disable automatic stable ID generation */
54
+ disableAutoStableId?: boolean;
55
+ }
56
+ export declare class TrafficalClient {
57
+ private readonly _options;
58
+ private _state;
59
+ private readonly _errorBoundary;
60
+ private readonly _storage;
61
+ private readonly _eventLogger;
62
+ private readonly _exposureDedup;
63
+ private readonly _stableId;
64
+ private readonly _plugins;
65
+ /** Cache of recent decisions for attribution lookup when track() is called */
66
+ private readonly _decisionCache;
67
+ constructor(options: TrafficalClientOptions);
68
+ /**
69
+ * Initializes the client by fetching the config bundle.
70
+ */
71
+ initialize(): Promise<void>;
72
+ /**
73
+ * Check if the client is initialized.
74
+ */
75
+ get isInitialized(): boolean;
76
+ /**
77
+ * Stops background refresh and cleans up resources.
78
+ */
79
+ destroy(): void;
80
+ /**
81
+ * Manually refreshes the config bundle.
82
+ */
83
+ refreshConfig(): Promise<void>;
84
+ /**
85
+ * Gets the current config bundle version.
86
+ */
87
+ getConfigVersion(): string | null;
88
+ /**
89
+ * Resolves parameters with defaults as fallback.
90
+ */
91
+ getParams<T extends Record<string, ParameterValue>>(options: {
92
+ context: Context;
93
+ defaults: T;
94
+ }): T;
95
+ /**
96
+ * Makes a decision with full metadata for tracking.
97
+ */
98
+ decide<T extends Record<string, ParameterValue>>(options: {
99
+ context: Context;
100
+ defaults: T;
101
+ }): DecisionResult;
102
+ /**
103
+ * Tracks an exposure event.
104
+ * Automatically deduplicates exposures for the same user/variant.
105
+ */
106
+ trackExposure(decision: DecisionResult): void;
107
+ /**
108
+ * Tracks a user event.
109
+ *
110
+ * @param eventName - The event name (e.g., 'purchase', 'add_to_cart')
111
+ * @param properties - Optional event properties (including value for optimization)
112
+ * @param options - Optional tracking options (decisionId, unitKey)
113
+ *
114
+ * @example
115
+ * // Track a purchase with revenue
116
+ * client.track('purchase', { value: 99.99, orderId: 'ord_123' });
117
+ *
118
+ * // Track a simple event
119
+ * client.track('add_to_cart', { itemId: 'sku_456' });
120
+ *
121
+ * // Track with explicit decision attribution
122
+ * client.track('checkout_complete', { value: 1 }, { decisionId: decision.decisionId });
123
+ */
124
+ track(eventName: string, properties?: Record<string, unknown>, options?: {
125
+ decisionId?: string;
126
+ unitKey?: string;
127
+ }): void;
128
+ /**
129
+ * Flush pending events immediately.
130
+ */
131
+ flushEvents(): Promise<void>;
132
+ /**
133
+ * Register a plugin.
134
+ */
135
+ use(plugin: TrafficalPlugin): this;
136
+ /**
137
+ * Get a registered plugin by name.
138
+ */
139
+ getPlugin(name: string): TrafficalPlugin | undefined;
140
+ /**
141
+ * Get the stable ID for the current user.
142
+ */
143
+ getStableId(): string;
144
+ /**
145
+ * Set a custom stable ID (e.g., when user logs in).
146
+ */
147
+ setStableId(id: string): void;
148
+ private _getEffectiveBundle;
149
+ private _enrichContext;
150
+ private _fetchConfig;
151
+ private _startBackgroundRefresh;
152
+ private _logOfflineWarning;
153
+ /**
154
+ * Caches a decision for attribution lookup when track() is called.
155
+ * Maintains a bounded cache to prevent memory leaks.
156
+ */
157
+ private _cacheDecision;
158
+ }
159
+ /**
160
+ * Creates and initializes a Traffical client.
161
+ */
162
+ export declare function createTrafficalClient(options: TrafficalClientOptions): Promise<TrafficalClient>;
163
+ /**
164
+ * Creates a Traffical client without initializing (synchronous).
165
+ */
166
+ export declare function createTrafficalClientSync(options: TrafficalClientOptions): TrafficalClient;
167
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,cAAc,EAUpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAI/E,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EAAiB,KAAK,eAAe,EAAgC,MAAM,oBAAoB,CAAC;AAkBvG,MAAM,WAAW,sBAAsB;IACrC,sBAAsB;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,YAAY,CAAC;IAC3B,wDAAwD;IACxD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,aAAa,CAAC,EAAE,oBAAoB,CAAC;IACrC,6BAA6B;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,yCAAyC;IACzC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,kCAAkC;IAClC,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,sDAAsD;IACtD,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,6CAA6C;IAC7C,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAeD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAEU;IAEnC,OAAO,CAAC,MAAM,CAOZ;IAEF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IACzC,8EAA8E;IAC9E,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;gBAE7D,OAAO,EAAE,sBAAsB;IAwE3C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAejC;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;OAEG;IACH,OAAO,IAAI,IAAI;IAkBf;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC;;OAEG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAQjC;;OAEG;IACH,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,CAAC,CAAA;KAAE,GAAG,CAAC;IAiBlG;;OAEG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,CAAC,CAAA;KAAE,GAAG,cAAc;IAoC5G;;;OAGG;IACH,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IA2C7C;;;;;;;;;;;;;;;;OAgBG;IACH,KAAK,CACH,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAClD,IAAI;IAmDP;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAUlC;;OAEG;IACH,GAAG,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAKlC;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAQpD;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ7B,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,cAAc;YAeR,YAAY;IAsC1B,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,kBAAkB;IAU1B;;;OAGG;IACH,OAAO,CAAC,cAAc;CAWvB;AAMD;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,CAIrG;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAE1F"}