@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 +154 -0
- package/dist/client.d.ts +200 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +419 -0
- package/dist/client.js.map +1 -0
- package/dist/event-batcher.d.ts +74 -0
- package/dist/event-batcher.d.ts.map +1 -0
- package/dist/event-batcher.js +165 -0
- package/dist/event-batcher.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
- package/src/client.ts +564 -0
- package/src/event-batcher.ts +210 -0
- package/src/index.ts +17 -0
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
|
+
|
package/dist/client.d.ts
ADDED
|
@@ -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"}
|