@traffical/node 0.8.0 → 0.10.0

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 (36) hide show
  1. package/README.md +16 -6
  2. package/dist/assignment-logger.test.js +10 -1
  3. package/dist/assignment-logger.test.js.map +1 -1
  4. package/dist/canonical-options.test.d.ts +2 -0
  5. package/dist/canonical-options.test.d.ts.map +1 -0
  6. package/dist/canonical-options.test.js +167 -0
  7. package/dist/canonical-options.test.js.map +1 -0
  8. package/dist/client.d.ts +111 -17
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +280 -50
  11. package/dist/client.js.map +1 -1
  12. package/dist/contract-0.7.0.test.d.ts +2 -0
  13. package/dist/contract-0.7.0.test.d.ts.map +1 -0
  14. package/dist/contract-0.7.0.test.js +171 -0
  15. package/dist/contract-0.7.0.test.js.map +1 -0
  16. package/dist/event-batcher.d.ts +58 -2
  17. package/dist/event-batcher.d.ts.map +1 -1
  18. package/dist/event-batcher.js +157 -23
  19. package/dist/event-batcher.js.map +1 -1
  20. package/dist/event-batcher.test.js +66 -0
  21. package/dist/event-batcher.test.js.map +1 -1
  22. package/dist/event-logger.test.js +63 -3
  23. package/dist/event-logger.test.js.map +1 -1
  24. package/dist/malformed-bundle.test.d.ts +12 -0
  25. package/dist/malformed-bundle.test.d.ts.map +1 -0
  26. package/dist/malformed-bundle.test.js +110 -0
  27. package/dist/malformed-bundle.test.js.map +1 -0
  28. package/dist/track-exposure.test.d.ts +12 -0
  29. package/dist/track-exposure.test.d.ts.map +1 -0
  30. package/dist/track-exposure.test.js +112 -0
  31. package/dist/track-exposure.test.js.map +1 -0
  32. package/dist/version.d.ts +1 -1
  33. package/dist/version.d.ts.map +1 -1
  34. package/dist/version.js +1 -1
  35. package/dist/version.js.map +1 -1
  36. package/package.json +5 -5
package/README.md CHANGED
@@ -44,12 +44,22 @@ await client.track('purchase', {
44
44
  ```typescript
45
45
  const client = new TrafficalClient({
46
46
  // Required
47
+ orgId: 'org_...',
48
+ projectId: 'proj_...',
49
+ env: 'production',
47
50
  apiKey: 'sk_...',
48
-
51
+
49
52
  // 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
+ baseUrl: 'https://sdk.traffical.io', // Custom edge endpoint
54
+ refreshIntervalMs: 60_000, // Config refresh interval (default: 60000)
55
+ batchSize: 10, // Events per batch (default: 10)
56
+ // (legacy alias: eventBatchSize)
57
+ flushIntervalMs: 30_000, // Flush interval (default: 30000)
58
+ // (legacy alias: eventFlushIntervalMs)
59
+ configTimeoutMs: 10_000, // Config-fetch timeout (default: 10000)
60
+ eventsTimeoutMs: 10_000, // Event-delivery timeout (default: 10000)
61
+ resolveTimeoutMs: 5_000, // Server-resolve timeout (default: 5000)
62
+ // (legacy fallback for all three: requestTimeoutMs)
53
63
  });
54
64
  ```
55
65
 
@@ -106,8 +116,8 @@ The Node SDK automatically batches events for efficiency:
106
116
  ```typescript
107
117
  const client = new TrafficalClient({
108
118
  apiKey: 'sk_...',
109
- batchSize: 100, // Events per batch (default: 100)
110
- flushInterval: 5000, // Flush interval in ms (default: 5s)
119
+ batchSize: 10, // Events per batch (default: 10)
120
+ flushIntervalMs: 30000, // Flush interval in ms (default: 30s)
111
121
  });
112
122
 
113
123
  // Events are queued and sent in batches
@@ -68,9 +68,18 @@ describe("Node TrafficalClient assignmentLogger", () => {
68
68
  expect(entry.type).toBe("decision");
69
69
  expect(entry.policyId).toBe("pol_1");
70
70
  expect(entry.allocationName).toBe("treatment");
71
- expect(entry.decisionId).toBe("dec_server_1");
71
+ // Server mode now mints a FRESH decisionId per decide() (spec 0.7.0 S8)
72
+ // it no longer reuses the resolve response's decisionId across decisions.
73
+ expect(entry.decisionId).toMatch(/^dec_/);
74
+ expect(entry.decisionId).not.toBe("dec_server_1");
72
75
  expect(entry.anonymousId).toBeUndefined();
73
76
  expect(entry.id).toMatch(/^asn_/);
77
+ // Warehouse-native passthrough fields from the layer resolution.
78
+ expect(entry.bucket).toBe(500);
79
+ expect(entry.configVersion).toBe(serverResolveResponse.stateVersion);
80
+ // The server response carries no propensity or contextual model version.
81
+ expect(entry.probability).toBeUndefined();
82
+ expect(entry.modelVersion).toBeUndefined();
74
83
  await client.destroy();
75
84
  });
76
85
  test("decide() then trackExposure() produce two distinct rows (decision + exposure)", async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"assignment-logger.test.js","sourceRoot":"","sources":["../src/assignment-logger.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"assignment-logger.test.js","sourceRoot":"","sources":["../src/assignment-logger.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;AAEvC,MAAM,qBAAqB,GAAG;IAC5B,UAAU,EAAE,cAAc;IAC1B,WAAW,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;IACnC,QAAQ,EAAE;QACR,SAAS,EAAE,sBAAsB;QACjC,YAAY,EAAE,QAAQ;QACtB,MAAM,EAAE;YACN;gBACE,OAAO,EAAE,SAAS;gBAClB,MAAM,EAAE,GAAG;gBACX,QAAQ,EAAE,OAAO;gBACjB,YAAY,EAAE,SAAS;gBACvB,cAAc,EAAE,WAAW;aAC5B;SACF;KACF;IACD,YAAY,EAAE,sBAAsB;IACpC,kBAAkB,EAAE,KAAK;CAC1B,CAAC;AAEF,SAAS,cAAc;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE;QAChC,OAAO;YACL,EAAE,EAAE,IAAI;YACR,MAAM,EAAE,GAAG;YACX,UAAU,EAAE,IAAI;YAChB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,qBAAqB;YACvC,OAAO,EAAE,IAAI,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;SAChB,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,UAAU,CAAC,KAAK,GAAG,SAAoC,CAAC;IACxD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,GAAG;IACjB,KAAK,EAAE,UAAU;IACjB,SAAS,EAAE,WAAW;IACtB,GAAG,EAAE,YAAY;IACjB,MAAM,EAAE,mBAAmB;IAC3B,iBAAiB,EAAE,CAAC,CAAC;IACrB,cAAc,EAAE,KAAK;IACrB,cAAc,EAAE,QAAiB;IACjC,kBAAkB,EAAE,IAAI;CACzB,CAAC;AAEF,SAAS,CAAC,GAAG,EAAE;IACb,UAAU,CAAC,KAAK,GAAG,aAAa,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uCAAuC,EAAE,GAAG,EAAE;IACrD,IAAI,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACzF,cAAc,EAAE,CAAC;QACjB,MAAM,OAAO,GAAyB,EAAE,CAAC;QAEzC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,GAAG,UAAU;YACb,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;SACjD,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAE1B,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAEnF,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;QACxB,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC/C,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClD,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,iEAAiE;QACjE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;QACrE,yEAAyE;QACzE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,aAAa,EAAE,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,aAAa,EAAE,CAAC;QAE3C,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC/F,cAAc,EAAE,CAAC;QACjB,MAAM,OAAO,GAAyB,EAAE,CAAC;QAEzC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,GAAG,UAAU;YACb,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;SACjD,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAE1B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;YAC7B,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC7B,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE;SACjC,CAAC,CAAC;QACH,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAE/B,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAEhD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC5E,cAAc,EAAE,CAAC;QACjB,MAAM,OAAO,GAAyB,EAAE,CAAC;QAEzC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,GAAG,UAAU;YACb,gBAAgB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;SACjD,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,EAAE,CAAC;QAE1B,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACnF,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAEnF,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE1C,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=canonical-options.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-options.test.d.ts","sourceRoot":"","sources":["../src/canonical-options.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Canonical option-name aliases (spec 0.7.0 sdk-design-contract).
3
+ *
4
+ * The Node client accepts the canonical option names alongside the legacy
5
+ * ones, and a specific canonical option always wins when both are set:
6
+ * - batchSize (alias of eventBatchSize)
7
+ * - flushIntervalMs (alias of eventFlushIntervalMs)
8
+ * - configTimeoutMs / eventsTimeoutMs / resolveTimeoutMs
9
+ * (per-path split of the legacy single requestTimeoutMs)
10
+ */
11
+ import { describe, test, expect, afterEach, mock } from "bun:test";
12
+ import { TrafficalClient } from "./client.js";
13
+ const originalFetch = globalThis.fetch;
14
+ const originalWarn = console.warn;
15
+ afterEach(() => {
16
+ globalThis.fetch = originalFetch;
17
+ console.warn = originalWarn;
18
+ });
19
+ const localConfig = {
20
+ version: "2024-06-01T00:00:00.000Z",
21
+ orgId: "org_1",
22
+ projectId: "proj_1",
23
+ env: "test",
24
+ hashing: { unitKey: "userId", bucketCount: 1000 },
25
+ parameters: [
26
+ { key: "ui.color", type: "string", default: "#000", layerId: "layer_ui", namespace: "ui" },
27
+ ],
28
+ layers: [],
29
+ };
30
+ function baseOpts() {
31
+ return {
32
+ orgId: "org_1",
33
+ projectId: "proj_1",
34
+ env: "test",
35
+ apiKey: "pk",
36
+ localConfig,
37
+ refreshIntervalMs: -1,
38
+ trackDecisions: false,
39
+ // Never auto-flush on a timer unless a test overrides it.
40
+ eventFlushIntervalMs: 999999,
41
+ flushIntervalMs: 999999,
42
+ };
43
+ }
44
+ /** Mock fetch that records the /v1/events/batch POST bodies (config routes 404). */
45
+ function mockEventPosts(posted) {
46
+ globalThis.fetch = mock(async (url, init) => {
47
+ if (String(url).includes("/v1/events/batch")) {
48
+ const body = JSON.parse(String(init?.body ?? "{}"));
49
+ posted.push(body.events);
50
+ return new Response(JSON.stringify({ accepted: 1 }), { status: 200 });
51
+ }
52
+ return new Response("not found", { status: 404 });
53
+ });
54
+ }
55
+ const tick = () => new Promise((r) => setTimeout(r, 0));
56
+ describe("batchSize alias", () => {
57
+ test("canonical batchSize triggers auto-flush at the threshold", async () => {
58
+ const posted = [];
59
+ mockEventPosts(posted);
60
+ const client = new TrafficalClient({ ...baseOpts(), batchSize: 1 });
61
+ await client.initialize();
62
+ client.track("purchase", { orderId: "o1" }, { unitKey: "user-1" });
63
+ await tick();
64
+ expect(posted.flat().length).toBeGreaterThan(0);
65
+ await client.close();
66
+ });
67
+ test("legacy eventBatchSize still triggers auto-flush at the threshold", async () => {
68
+ const posted = [];
69
+ mockEventPosts(posted);
70
+ const client = new TrafficalClient({ ...baseOpts(), eventBatchSize: 1 });
71
+ await client.initialize();
72
+ client.track("purchase", { orderId: "o1" }, { unitKey: "user-1" });
73
+ await tick();
74
+ expect(posted.flat().length).toBeGreaterThan(0);
75
+ await client.close();
76
+ });
77
+ test("canonical batchSize wins over legacy eventBatchSize", async () => {
78
+ const posted = [];
79
+ mockEventPosts(posted);
80
+ // Canonical=1 (flush after 1) beats legacy=1000 (would never flush here).
81
+ const client = new TrafficalClient({ ...baseOpts(), batchSize: 1, eventBatchSize: 1000 });
82
+ await client.initialize();
83
+ client.track("purchase", { orderId: "o1" }, { unitKey: "user-1" });
84
+ await tick();
85
+ expect(posted.flat().length).toBeGreaterThan(0);
86
+ await client.close();
87
+ });
88
+ });
89
+ describe("flushIntervalMs alias", () => {
90
+ test("canonical flushIntervalMs wins over legacy eventFlushIntervalMs", async () => {
91
+ const posted = [];
92
+ mockEventPosts(posted);
93
+ // Canonical=10ms flushes on the timer; legacy=999999 would not within the window.
94
+ const client = new TrafficalClient({
95
+ ...baseOpts(),
96
+ flushIntervalMs: 10,
97
+ eventFlushIntervalMs: 999999,
98
+ batchSize: 1000, // keep batch threshold high so only the timer can flush
99
+ });
100
+ await client.initialize();
101
+ client.track("purchase", { orderId: "o1" }, { unitKey: "user-1" });
102
+ await new Promise((r) => setTimeout(r, 60));
103
+ expect(posted.flat().length).toBeGreaterThan(0);
104
+ await client.close();
105
+ });
106
+ });
107
+ /** A fetch that hangs forever but rejects with AbortError when aborted. */
108
+ function mockHungFetch(capture) {
109
+ globalThis.fetch = mock((url, init) => new Promise((_resolve, reject) => {
110
+ capture(String(url), init?.signal);
111
+ init?.signal?.addEventListener("abort", () => {
112
+ reject(new DOMException("The operation was aborted.", "AbortError"));
113
+ });
114
+ }));
115
+ }
116
+ describe("configTimeoutMs alias", () => {
117
+ test("canonical configTimeoutMs aborts a hung config fetch (canonical wins over legacy)", async () => {
118
+ const warnings = [];
119
+ console.warn = (...args) => warnings.push(args.map(String).join(" "));
120
+ let configSignal;
121
+ mockHungFetch((url, signal) => {
122
+ if (url.includes("/v1/config/"))
123
+ configSignal = signal;
124
+ });
125
+ const client = new TrafficalClient({
126
+ orgId: "org_1",
127
+ projectId: "proj_1",
128
+ env: "test",
129
+ apiKey: "pk",
130
+ trackDecisions: false,
131
+ refreshIntervalMs: -1,
132
+ disableCloudEvents: true,
133
+ // Canonical tiny timeout must win over the huge legacy value; otherwise
134
+ // initialize() would hang ~100s and this test would time out.
135
+ configTimeoutMs: 20,
136
+ requestTimeoutMs: 100000,
137
+ });
138
+ await client.initialize();
139
+ expect(warnings.some((w) => w.includes("Failed to fetch config"))).toBe(true);
140
+ expect(configSignal?.aborted).toBe(true);
141
+ await client.close();
142
+ });
143
+ });
144
+ describe("eventsTimeoutMs alias", () => {
145
+ test("canonical eventsTimeoutMs aborts a hung events POST (canonical wins over legacy)", async () => {
146
+ console.warn = () => { };
147
+ let eventsSignal;
148
+ mockHungFetch((url, signal) => {
149
+ if (url.includes("/v1/events/batch"))
150
+ eventsSignal = signal;
151
+ });
152
+ const client = new TrafficalClient({
153
+ ...baseOpts(),
154
+ batchSize: 1, // flush after one event
155
+ eventsTimeoutMs: 20, // canonical tiny value...
156
+ requestTimeoutMs: 100000, // ...must win over the huge legacy fallback
157
+ });
158
+ // localConfig is preloaded, so no config fetch is needed before tracking.
159
+ client.track("purchase", { orderId: "o1" }, { unitKey: "user-1" });
160
+ await new Promise((r) => setTimeout(r, 80));
161
+ expect(eventsSignal).toBeDefined();
162
+ expect(eventsSignal?.aborted).toBe(true);
163
+ // destroySync() avoids awaiting a final flush against the still-hung fetch.
164
+ client.destroySync();
165
+ });
166
+ });
167
+ //# sourceMappingURL=canonical-options.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-options.test.js","sourceRoot":"","sources":["../src/canonical-options.test.ts"],"names":[],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -11,7 +11,7 @@
11
11
  * - Batched event transport for efficiency
12
12
  * - Graceful degradation with local config and schema defaults
13
13
  */
14
- import { type Context, type DecisionResult, type ParameterValue, type TrafficalClientOptions as CoreClientOptions, type TrackOptions, type AssignmentLogger, type TrackableEventLogger, type TrackEventMap, type OnSchemaWarnings } from "@traffical/core";
14
+ import { type Context, type DecisionResult, type ParameterValue, type TrafficalClientOptions as CoreClientOptions, type TrackOptions, type TrackEventOptions, type DecideOptions, type GetParamsOptions, type AssignmentLogger, type TrackableEventLogger, type TrackEventMap, type OnSchemaWarnings } from "@traffical/core";
15
15
  /**
16
16
  * Options for the Node.js Traffical client.
17
17
  * Extends the core options with Node-specific settings.
@@ -30,12 +30,51 @@ export interface TrafficalClientOptions extends CoreClientOptions {
30
30
  decisionDeduplicationTtlMs?: number;
31
31
  /**
32
32
  * Event batch size - number of events before auto-flush (default: 10).
33
+ * @deprecated Use the canonical `batchSize` instead. `eventBatchSize` still works.
33
34
  */
34
35
  eventBatchSize?: number;
35
36
  /**
36
37
  * Event flush interval in milliseconds (default: 30000).
38
+ * @deprecated Use the canonical `flushIntervalMs` instead. `eventFlushIntervalMs` still works.
37
39
  */
38
40
  eventFlushIntervalMs?: number;
41
+ /** Events per delivery batch (default: 10). Canonical alias of `eventBatchSize`. */
42
+ batchSize?: number;
43
+ /** Event flush cadence in milliseconds (default: 30000). Canonical alias of `eventFlushIntervalMs`. */
44
+ flushIntervalMs?: number;
45
+ /**
46
+ * Maximum number of events buffered in memory before the oldest is dropped
47
+ * (default: 1000). Bounds memory in long-lived server processes.
48
+ */
49
+ eventMaxQueueSize?: number;
50
+ /**
51
+ * Timeout in milliseconds for SDK network requests — the config bundle
52
+ * fetch and event batch POSTs (default: 10000).
53
+ *
54
+ * On timeout the request is aborted and treated exactly like a network
55
+ * failure: config fetches fall back to the cached/local config, and event
56
+ * batches are re-queued for retry.
57
+ *
58
+ * @deprecated Use the per-path options `configTimeoutMs`, `eventsTimeoutMs`,
59
+ * and `resolveTimeoutMs` instead. `requestTimeoutMs` is still honored as the
60
+ * legacy fallback for all three when the specific option is not provided.
61
+ */
62
+ requestTimeoutMs?: number;
63
+ /**
64
+ * Timeout in milliseconds for the config-bundle fetch (default: 10000).
65
+ * Falls back to `requestTimeoutMs` when not set.
66
+ */
67
+ configTimeoutMs?: number;
68
+ /**
69
+ * Timeout in milliseconds for event-delivery POSTs (default: 10000).
70
+ * Falls back to `requestTimeoutMs` when not set.
71
+ */
72
+ eventsTimeoutMs?: number;
73
+ /**
74
+ * Timeout in milliseconds for server-resolve requests (POST /v1/resolve,
75
+ * default: 5000). Falls back to `requestTimeoutMs` when not set.
76
+ */
77
+ resolveTimeoutMs?: number;
39
78
  /**
40
79
  * Enable debug logging for events (default: false).
41
80
  */
@@ -61,6 +100,15 @@ export interface TrafficalClientOptions extends CoreClientOptions {
61
100
  * (same unit+policy+variant won't fire again within TTL). Default: true.
62
101
  */
63
102
  deduplicateAssignmentLogger?: boolean;
103
+ /**
104
+ * When true, exposure events are deduplicated per (unit, policy, allocation)
105
+ * within a session via in-memory LRU, mirroring the browser SDK. Default: true.
106
+ */
107
+ deduplicateExposures?: boolean;
108
+ /**
109
+ * Exposure deduplication session TTL in milliseconds (default: 30 minutes).
110
+ */
111
+ exposureSessionTtlMs?: number;
64
112
  /**
65
113
  * Optional callback for routing full events (exposure, track, decision)
66
114
  * to a customer-managed pipeline (e.g. Jitsu, Segment). Fires regardless
@@ -90,6 +138,7 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
90
138
  private readonly _options;
91
139
  private _state;
92
140
  private readonly _eventBatcher;
141
+ private readonly _requestTimeoutMs;
93
142
  private readonly _decisionDedup;
94
143
  private readonly _decisionClient;
95
144
  private readonly _assignmentLogger?;
@@ -97,21 +146,44 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
97
146
  private readonly _disableCloudEvents;
98
147
  /** In-memory LRU for assignment logger deduplication: key → expiry timestamp */
99
148
  private readonly _assignmentLoggerDedup;
149
+ /** In-memory LRU for exposure-event deduplication: key → expiry timestamp */
150
+ private readonly _exposureDedup;
151
+ /** Session TTL for exposure deduplication (ms) */
152
+ private readonly _exposureSessionTtlMs;
100
153
  /** Cache of recent decisions for attribution lookup on rewards */
101
154
  private readonly _decisionCache;
155
+ /** Serialized context of the last server-mode resolve (per-call throttle). */
156
+ private _lastResolveContextKey;
157
+ /** Resolves once the first config load attempt completes (fail-open). */
158
+ private _readyResolve;
159
+ private readonly _readyPromise;
102
160
  constructor(options: TrafficalClientOptions);
103
161
  /**
104
162
  * Initializes the client by fetching the config bundle.
105
163
  * This is called automatically by createTrafficalClient.
106
164
  */
107
165
  initialize(): Promise<void>;
166
+ /**
167
+ * Resolves once the first usable config has loaded (or the SDK has failed
168
+ * open on an unavailable/malformed bundle). Never rejects.
169
+ */
170
+ waitForReady(): Promise<void>;
171
+ /**
172
+ * Single teardown verb (spec 0.7.0 design contract). Stops background
173
+ * refresh and awaits a final event flush before returning.
174
+ */
175
+ close(): Promise<void>;
108
176
  /**
109
177
  * Stops background refresh and cleans up resources.
178
+ *
179
+ * @deprecated Use {@link close} instead — the canonical single teardown verb.
110
180
  */
111
181
  destroy(): Promise<void>;
112
182
  /**
113
183
  * Synchronous destroy for process exit handlers.
114
- * Use destroy() when possible for proper cleanup.
184
+ *
185
+ * @deprecated Use {@link close} instead. This best-effort variant does not
186
+ * await the final flush; prefer `await close()` where you can.
115
187
  */
116
188
  destroySync(): void;
117
189
  /**
@@ -122,6 +194,17 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
122
194
  * Gets the current config bundle version.
123
195
  */
124
196
  getConfigVersion(): string | null;
197
+ /**
198
+ * Returns the context field the bundle buckets on (the project's unit key),
199
+ * or null before the bundle has loaded. Adapters (e.g. an OpenFeature
200
+ * provider) map their targeting key onto this field.
201
+ */
202
+ getUnitKeyField(): string | null;
203
+ /**
204
+ * Returns the id of the layer a parameter belongs to, or null if the
205
+ * parameter is unknown / the bundle is not yet loaded.
206
+ */
207
+ getParameterLayerId(key: string): string | null;
125
208
  /**
126
209
  * Flush pending events immediately.
127
210
  */
@@ -135,25 +218,29 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
135
218
  * 3. Local config (if remote unavailable)
136
219
  * 4. Caller defaults
137
220
  */
138
- getParams<T extends Record<string, ParameterValue>>(options: {
139
- context: Context;
140
- defaults: T;
141
- }): T;
221
+ getParams<T extends Record<string, ParameterValue>>(context: Context, defaults: T): T;
222
+ /** @deprecated Pass `(context, defaults)` positionally (spec 0.7.0 contract). */
223
+ getParams<T extends Record<string, ParameterValue>>(options: GetParamsOptions<T>): T;
142
224
  /**
143
225
  * Makes a decision with full metadata for tracking.
144
226
  *
145
227
  * When trackDecisions is enabled (default), automatically sends a DecisionEvent
146
228
  * to the backend for intent-to-treat analysis.
147
229
  */
148
- decide<T extends Record<string, ParameterValue>>(options: {
149
- context: Context;
150
- defaults: T;
151
- }): DecisionResult;
230
+ decide<T extends Record<string, ParameterValue>>(context: Context, defaults: T): DecisionResult;
231
+ /** @deprecated Pass `(context, defaults)` positionally (spec 0.7.0 contract). */
232
+ decide<T extends Record<string, ParameterValue>>(options: DecideOptions<T>): DecisionResult;
152
233
  /**
153
- * Tracks an exposure event.
234
+ * Tracks an exposure event — the "user was actually shown this treatment"
235
+ * signal (treatment-on-the-treated).
154
236
  *
155
- * If the decision includes filtered context (from policies with contextLogging),
156
- * it will be included in the exposure event for contextual bandit training.
237
+ * Only layers the caller was actually exposed to are emitted: layers without
238
+ * a policy/allocation and `attributionOnly` layers (resolved for attribution
239
+ * but whose parameters weren't requested) are skipped, and each
240
+ * (unit, policy, allocation) is deduplicated per session so the same exposure
241
+ * isn't emitted twice. Mirrors the browser SDK. If the decision includes
242
+ * filtered context (from policies with contextLogging), it is included in the
243
+ * exposure event for contextual bandit training.
157
244
  */
158
245
  trackExposure(decision: DecisionResult): void;
159
246
  /**
@@ -169,10 +256,7 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
169
256
  * // Track with explicit decision attribution
170
257
  * client.track('checkout_complete', { value: 1 }, { decisionId: 'dec_xyz' });
171
258
  */
172
- track<E extends Extract<keyof TEvents, string>>(event: E, properties?: TEvents[E], options?: {
173
- decisionId?: string;
174
- unitKey?: string;
175
- }): void;
259
+ track<E extends Extract<keyof TEvents, string>>(event: E, properties?: TEvents[E], options?: TrackEventOptions): void;
176
260
  /**
177
261
  * @deprecated Use track() instead.
178
262
  * Tracks a reward event.
@@ -196,8 +280,18 @@ export declare class TrafficalClient<TEvents extends TrackEventMap = TrackEventM
196
280
  /**
197
281
  * Logs an offline warning (rate-limited).
198
282
  */
283
+ /**
284
+ * Server mode: threads the per-call context into a background /v1/resolve so
285
+ * the cached snapshot converges to the contexts actually being evaluated.
286
+ * Throttled by serialized context so repeated identical contexts (the common
287
+ * case) don't hammer the edge. Because decide()/getParams() are synchronous
288
+ * they cannot await this; the current call degrades to the last-good snapshot
289
+ * and subsequent calls pick up the refreshed resolution.
290
+ */
291
+ private _maybeResolveForContext;
199
292
  private _fetchServerResolve;
200
293
  private _logOfflineWarning;
294
+ private _logMalformedBundleWarning;
201
295
  /**
202
296
  * Routes a built event to the BYO event logger (if configured) and to the
203
297
  * Traffical edge batcher (unless cloud events are disabled).
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}