@traffical/node 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.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # @traffical/node
2
+
3
+ Traffical SDK for Node.js - server-side parameter resolution with caching and event tracking.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @traffical/node
9
+ # or
10
+ bun add @traffical/node
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { TrafficalClient } from '@traffical/node';
17
+
18
+ const client = new TrafficalClient({
19
+ apiKey: 'sk_...', // Server-side API key
20
+ });
21
+
22
+ // Get parameters for a user
23
+ const decision = await client.decide({
24
+ userId: 'user-123',
25
+ country: 'US',
26
+ });
27
+
28
+ console.log(decision.params);
29
+ // { 'button.color': '#007bff', 'pricing.discount': 0.1 }
30
+
31
+ // Track an event
32
+ await client.track('purchase', {
33
+ decisionId: decision.decisionId,
34
+ unitKey: 'user-123',
35
+ properties: {
36
+ amount: 99.99,
37
+ currency: 'USD',
38
+ },
39
+ });
40
+ ```
41
+
42
+ ## Configuration
43
+
44
+ ```typescript
45
+ const client = new TrafficalClient({
46
+ // Required
47
+ apiKey: 'sk_...',
48
+
49
+ // Optional
50
+ apiBase: 'https://api.traffical.io', // Custom API endpoint
51
+ cacheTtl: 60_000, // Config cache TTL in ms (default: 60s)
52
+ timeout: 5_000, // Request timeout in ms (default: 5s)
53
+ });
54
+ ```
55
+
56
+ ## API Reference
57
+
58
+ ### `decide(context)`
59
+
60
+ Resolves parameters for a given context.
61
+
62
+ ```typescript
63
+ const decision = await client.decide({
64
+ userId: 'user-123', // Required: unit key for bucketing
65
+ country: 'US', // Optional: targeting context
66
+ plan: 'premium', // Optional: more context
67
+ });
68
+
69
+ // Returns:
70
+ {
71
+ decisionId: 'dec_...', // Unique ID for this decision
72
+ params: { // Resolved parameters
73
+ 'feature.enabled': true,
74
+ 'ui.theme': 'dark',
75
+ },
76
+ exposures: [...] // Which experiments the user is in
77
+ }
78
+ ```
79
+
80
+ ### `track(event, options)`
81
+
82
+ Tracks a user event for analytics.
83
+
84
+ ```typescript
85
+ await client.track('purchase', {
86
+ decisionId: decision.decisionId,
87
+ unitKey: 'user-123',
88
+ properties: {
89
+ amount: 99.99,
90
+ },
91
+ });
92
+ ```
93
+
94
+ ### `refresh()`
95
+
96
+ Forces a refresh of the cached configuration.
97
+
98
+ ```typescript
99
+ await client.refresh();
100
+ ```
101
+
102
+ ## Event Batching
103
+
104
+ The Node SDK automatically batches events for efficiency:
105
+
106
+ ```typescript
107
+ const client = new TrafficalClient({
108
+ apiKey: 'sk_...',
109
+ batchSize: 100, // Events per batch (default: 100)
110
+ flushInterval: 5000, // Flush interval in ms (default: 5s)
111
+ });
112
+
113
+ // Events are queued and sent in batches
114
+ client.track('page_view', { ... });
115
+ client.track('click', { ... });
116
+
117
+ // Force flush before shutdown
118
+ await client.flush();
119
+ ```
120
+
121
+ ## Error Handling
122
+
123
+ ```typescript
124
+ try {
125
+ const decision = await client.decide({ userId: 'user-123' });
126
+ } catch (error) {
127
+ if (error.code === 'NETWORK_ERROR') {
128
+ // Handle network issues - use defaults
129
+ }
130
+ if (error.code === 'INVALID_API_KEY') {
131
+ // Handle auth issues
132
+ }
133
+ }
134
+ ```
135
+
136
+ ## TypeScript
137
+
138
+ Full TypeScript support with type inference:
139
+
140
+ ```typescript
141
+ import type { Context, DecisionResult } from '@traffical/node';
142
+
143
+ const context: Context = {
144
+ userId: 'user-123',
145
+ plan: 'premium',
146
+ };
147
+
148
+ const decision: DecisionResult = await client.decide(context);
149
+ ```
150
+
151
+ ## License
152
+
153
+ MIT
154
+
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Traffical Node.js SDK Client
3
+ *
4
+ * HTTP client with caching, background refresh, and graceful degradation.
5
+ * Wraps the pure core-ts resolution engine.
6
+ *
7
+ * Features:
8
+ * - ETag-based caching for efficient config fetches
9
+ * - Background refresh for keeping config up-to-date
10
+ * - Automatic decision tracking for intent-to-treat analysis
11
+ * - Batched event transport for efficiency
12
+ * - Graceful degradation with local config and schema defaults
13
+ */
14
+ import { type Context, type DecisionResult, type ParameterValue, type TrafficalClientOptions as CoreClientOptions, type TrackOptions } from "@traffical/core";
15
+ /**
16
+ * Options for the Node.js Traffical client.
17
+ * Extends the core options with Node-specific settings.
18
+ */
19
+ export interface TrafficalClientOptions extends CoreClientOptions {
20
+ /**
21
+ * Whether to automatically track decision events (default: true).
22
+ * When enabled, every call to decide() automatically sends a DecisionEvent
23
+ * to the backend, enabling intent-to-treat analysis.
24
+ */
25
+ trackDecisions?: boolean;
26
+ /**
27
+ * Decision deduplication TTL in milliseconds (default: 1 hour).
28
+ * Same user+assignment combination won't be tracked again within this window.
29
+ */
30
+ decisionDeduplicationTtlMs?: number;
31
+ /**
32
+ * Event batch size - number of events before auto-flush (default: 10).
33
+ */
34
+ eventBatchSize?: number;
35
+ /**
36
+ * Event flush interval in milliseconds (default: 30000).
37
+ */
38
+ eventFlushIntervalMs?: number;
39
+ /**
40
+ * Enable debug logging for events (default: false).
41
+ */
42
+ debugEvents?: boolean;
43
+ }
44
+ /**
45
+ * TrafficalClient - the main SDK client for Node.js environments.
46
+ *
47
+ * Features:
48
+ * - ETag-based caching for efficient config fetches
49
+ * - Background refresh for keeping config up-to-date
50
+ * - Automatic decision tracking for intent-to-treat analysis
51
+ * - Batched event transport for efficiency
52
+ * - Graceful degradation with local config and schema defaults
53
+ * - Rate-limited offline warnings
54
+ */
55
+ export declare class TrafficalClient {
56
+ private readonly _options;
57
+ private _state;
58
+ private readonly _eventBatcher;
59
+ private readonly _decisionDedup;
60
+ /** Cache of recent decisions for attribution lookup on rewards */
61
+ private readonly _decisionCache;
62
+ constructor(options: TrafficalClientOptions);
63
+ /**
64
+ * Initializes the client by fetching the config bundle.
65
+ * This is called automatically by createTrafficalClient.
66
+ */
67
+ initialize(): Promise<void>;
68
+ /**
69
+ * Stops background refresh and cleans up resources.
70
+ */
71
+ destroy(): Promise<void>;
72
+ /**
73
+ * Synchronous destroy for process exit handlers.
74
+ * Use destroy() when possible for proper cleanup.
75
+ */
76
+ destroySync(): void;
77
+ /**
78
+ * Manually refreshes the config bundle.
79
+ */
80
+ refreshConfig(): Promise<void>;
81
+ /**
82
+ * Gets the current config bundle version.
83
+ */
84
+ getConfigVersion(): string | null;
85
+ /**
86
+ * Flush pending events immediately.
87
+ */
88
+ flushEvents(): Promise<void>;
89
+ /**
90
+ * Resolves parameters with defaults as fallback.
91
+ *
92
+ * Resolution priority (highest wins):
93
+ * 1. Policy overrides (from remote bundle)
94
+ * 2. Parameter defaults (from remote bundle)
95
+ * 3. Local config (if remote unavailable)
96
+ * 4. Caller defaults
97
+ */
98
+ getParams<T extends Record<string, ParameterValue>>(options: {
99
+ context: Context;
100
+ defaults: T;
101
+ }): T;
102
+ /**
103
+ * Makes a decision with full metadata for tracking.
104
+ *
105
+ * When trackDecisions is enabled (default), automatically sends a DecisionEvent
106
+ * to the backend for intent-to-treat analysis.
107
+ */
108
+ decide<T extends Record<string, ParameterValue>>(options: {
109
+ context: Context;
110
+ defaults: T;
111
+ }): DecisionResult;
112
+ /**
113
+ * Tracks an exposure event.
114
+ *
115
+ * If the decision includes filtered context (from policies with contextLogging),
116
+ * it will be included in the exposure event for contextual bandit training.
117
+ */
118
+ trackExposure(decision: DecisionResult): void;
119
+ /**
120
+ * Tracks a user event.
121
+ *
122
+ * @example
123
+ * // Track a purchase with revenue
124
+ * client.track('purchase', { value: 99.99, orderId: 'ord_123' });
125
+ *
126
+ * // Track a simple event
127
+ * client.track('add_to_cart', { itemId: 'sku_456' });
128
+ *
129
+ * // Track with explicit decision attribution
130
+ * client.track('checkout_complete', { value: 1 }, { decisionId: 'dec_xyz' });
131
+ */
132
+ track(event: string, properties?: Record<string, unknown>, options?: {
133
+ decisionId?: string;
134
+ unitKey?: string;
135
+ }): void;
136
+ /**
137
+ * @deprecated Use track() instead.
138
+ * Tracks a reward event.
139
+ * If decisionId is provided and the decision is cached, attribution is auto-populated.
140
+ */
141
+ trackReward(options: TrackOptions): void;
142
+ /**
143
+ * Gets the effective bundle: remote > local > null
144
+ */
145
+ private _getEffectiveBundle;
146
+ /**
147
+ * Fetches the config bundle from the edge worker.
148
+ * Uses ETag for efficient caching.
149
+ */
150
+ private _fetchConfig;
151
+ /**
152
+ * Starts background refresh timer.
153
+ */
154
+ private _startBackgroundRefresh;
155
+ /**
156
+ * Logs an offline warning (rate-limited).
157
+ */
158
+ private _logOfflineWarning;
159
+ /**
160
+ * Tracks a decision event (internal).
161
+ * Called automatically when trackDecisions is enabled.
162
+ */
163
+ private _trackDecision;
164
+ /**
165
+ * Caches a decision for attribution lookup when trackReward is called.
166
+ * Maintains a bounded cache to prevent memory leaks.
167
+ */
168
+ private _cacheDecision;
169
+ /**
170
+ * Gets attribution info from cached decision if available.
171
+ */
172
+ private _getAttributionFromCache;
173
+ }
174
+ /**
175
+ * Creates and initializes a Traffical client.
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * const traffical = await createTrafficalClient({
180
+ * orgId: "org_123",
181
+ * projectId: "proj_456",
182
+ * env: "production",
183
+ * apiKey: "sk_...",
184
+ * });
185
+ *
186
+ * const params = traffical.getParams({
187
+ * context: { userId: "user_789" },
188
+ * defaults: {
189
+ * "ui.button.color": "#000",
190
+ * },
191
+ * });
192
+ * ```
193
+ */
194
+ export declare function createTrafficalClient(options: TrafficalClientOptions): Promise<TrafficalClient>;
195
+ /**
196
+ * Creates a Traffical client without initializing (synchronous).
197
+ * Useful when you want to control initialization timing.
198
+ */
199
+ export declare function createTrafficalClientSync(options: TrafficalClientOptions): TrafficalClient;
200
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAEL,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,sBAAsB,IAAI,iBAAiB,EAChD,KAAK,YAAY,EAUlB,MAAM,iBAAiB,CAAC;AAoBzB;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAmBD;;;;;;;;;;GAUG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAQvB;IAEF,OAAO,CAAC,MAAM,CAOZ;IAEF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,kEAAkE;IAClE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0C;gBAE7D,OAAO,EAAE,sBAAsB;IAoC3C;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B;;;OAGG;IACH,WAAW,IAAI,IAAI;IASnB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAIpC;;OAEG;IACH,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlC;;;;;;;;OAQG;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;IAKlG;;;;;OAKG;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;IAiB5G;;;;;OAKG;IACH,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IA2B7C;;;;;;;;;;;;OAYG;IACH,KAAK,CACH,KAAK,EAAE,MAAM,EACb,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;IA0BP;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAWxC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;OAGG;YACW,YAAY;IAqC1B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAiB/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAyCtB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,wBAAwB;CAoBjC;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,CAAC,CAI1B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAEjB"}