@runbandit/sdk 2.0.1 → 2.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/dist/index.d.ts +6 -582
- package/dist/index.js +8 -60
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -51,7 +51,6 @@ var BanditsClient = class {
|
|
|
51
51
|
batchSize: 100,
|
|
52
52
|
flushInterval: 1e4,
|
|
53
53
|
sessionTtl: 864e5,
|
|
54
|
-
// 24 hours in ms
|
|
55
54
|
...config
|
|
56
55
|
};
|
|
57
56
|
this.sessionId = this.getOrCreateSessionId();
|
|
@@ -83,14 +82,10 @@ var BanditsClient = class {
|
|
|
83
82
|
treatmentId: assignment.treatmentId,
|
|
84
83
|
experimentId,
|
|
85
84
|
userId,
|
|
86
|
-
config: assignment.treatment?.config || {}
|
|
85
|
+
config: assignment.treatment?.config || {},
|
|
86
|
+
funnelId: assignment.funnelId
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Track a named event for session-based attribution (recommended)
|
|
91
|
-
* Events are matched to bandits by sessionId — no assignmentId needed
|
|
92
|
-
* Set immediate: true to fire synchronously (survives navigation)
|
|
93
|
-
*/
|
|
94
89
|
trackNamedEvent(event, opts) {
|
|
95
90
|
const request = {
|
|
96
91
|
name: event.name,
|
|
@@ -139,21 +134,6 @@ var BanditsClient = class {
|
|
|
139
134
|
throw error;
|
|
140
135
|
}
|
|
141
136
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Link an anonymous user ID to a durable identity (e.g. email, account ID).
|
|
144
|
-
* All historical assignments and events for the anonymous ID are re-attributed
|
|
145
|
-
* to the identified user. Future calls to getAssignment() with either ID will
|
|
146
|
-
* resolve to the same assignment.
|
|
147
|
-
*
|
|
148
|
-
* Requires admin authentication — the client must be configured with a
|
|
149
|
-
* personal access token (PAT) instead of an API key, and companyId must
|
|
150
|
-
* be provided. This is a management operation, not an SDK client operation.
|
|
151
|
-
*
|
|
152
|
-
* @param anonymousId - The temporary/anonymous user ID (e.g. crypto.randomUUID())
|
|
153
|
-
* @param identifiedUserId - The permanent user ID (e.g. email, database ID)
|
|
154
|
-
* @param companyId - The company ID (required for PAT auth)
|
|
155
|
-
* @param mergeReason - Why the identification is happening
|
|
156
|
-
*/
|
|
157
137
|
async identify(anonymousId, identifiedUserId, companyId, mergeReason) {
|
|
158
138
|
const response = await this.makeRequest(
|
|
159
139
|
"/identify",
|
|
@@ -169,14 +149,12 @@ var BanditsClient = class {
|
|
|
169
149
|
throw new Error(response.error || "Failed to identify user");
|
|
170
150
|
}
|
|
171
151
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
* @param value - The payout value (e.g., purchase amount in dollars)
|
|
179
|
-
*/
|
|
152
|
+
async label(userId, labels) {
|
|
153
|
+
const response = await this.makeRequest("/labels", "POST", { userId, labels });
|
|
154
|
+
if (!response.success) {
|
|
155
|
+
throw new Error(response.error || "Failed to record labels");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
180
158
|
async trackFunnelReward(funnelId, userId, value) {
|
|
181
159
|
const response = await this.makeRequest(
|
|
182
160
|
`/funnels/${funnelId}/reward`,
|
|
@@ -221,10 +199,6 @@ var BanditsClient = class {
|
|
|
221
199
|
generateId() {
|
|
222
200
|
return generateId();
|
|
223
201
|
}
|
|
224
|
-
/**
|
|
225
|
-
* Normalize the apiUrl by adding /api prefix if not present.
|
|
226
|
-
* Supports both 'https://runbandit.com' and 'https://runbandit.com/api'.
|
|
227
|
-
*/
|
|
228
202
|
normalizeApiUrl() {
|
|
229
203
|
let baseUrl = this.config.apiUrl.replace(/\/$/, "");
|
|
230
204
|
if (!baseUrl.endsWith("/api")) {
|
|
@@ -780,16 +754,6 @@ var SnapshotClient = class {
|
|
|
780
754
|
this.snapshot = this.readSnapshot();
|
|
781
755
|
}
|
|
782
756
|
}
|
|
783
|
-
/**
|
|
784
|
-
* Decide an arm with no network call, or return null.
|
|
785
|
-
*
|
|
786
|
-
* Null means "this caller must fall back" - a cold cache, an expired one, an
|
|
787
|
-
* unknown experiment, or one whose state cannot be served from a static
|
|
788
|
-
* document. Returning null rather than the control is deliberate: the caller
|
|
789
|
-
* renders control itself, and no impression is reported for it. Reporting a
|
|
790
|
-
* forced-control impression would inflate the control arm's trials with
|
|
791
|
-
* decisions the bandit never made.
|
|
792
|
-
*/
|
|
793
757
|
getAssignmentSync(experimentId, userId) {
|
|
794
758
|
const snapshot = this.freshSnapshot();
|
|
795
759
|
if (!snapshot) return null;
|
|
@@ -816,7 +780,6 @@ var SnapshotClient = class {
|
|
|
816
780
|
decisions[key] = {
|
|
817
781
|
clientAssignmentId,
|
|
818
782
|
treatmentId: treatment.id,
|
|
819
|
-
// expiresAt is milliseconds here; assignmentTtl arrives in seconds.
|
|
820
783
|
expiresAt: this.now() + ttlSeconds * 1e3
|
|
821
784
|
};
|
|
822
785
|
this.writeDecisions(decisions);
|
|
@@ -835,7 +798,6 @@ var SnapshotClient = class {
|
|
|
835
798
|
config: treatment.config
|
|
836
799
|
};
|
|
837
800
|
}
|
|
838
|
-
/** Fetch a fresh snapshot and cache it. Safe to call on every pageview. */
|
|
839
801
|
async refresh() {
|
|
840
802
|
const response = await this.fetchImpl(`${this.config.apiUrl}/snapshot`, {
|
|
841
803
|
method: "GET",
|
|
@@ -846,14 +808,6 @@ var SnapshotClient = class {
|
|
|
846
808
|
this.snapshot = snapshot;
|
|
847
809
|
this.persistSnapshot(snapshot);
|
|
848
810
|
}
|
|
849
|
-
/**
|
|
850
|
-
* Post queued impressions.
|
|
851
|
-
*
|
|
852
|
-
* The queue is only cleared on success. A failed flush leaves the reports in
|
|
853
|
-
* storage, so a pageview that happened offline is still counted when the
|
|
854
|
-
* next one succeeds - the trial really did occur, and dropping it would bias
|
|
855
|
-
* the arm's denominator downward.
|
|
856
|
-
*/
|
|
857
811
|
async flush() {
|
|
858
812
|
if (this.inFlight) return this.inFlight;
|
|
859
813
|
this.inFlight = this.doFlush().finally(() => {
|
|
@@ -881,14 +835,12 @@ var SnapshotClient = class {
|
|
|
881
835
|
this.queue = this.queue.slice(reports.length);
|
|
882
836
|
this.persistQueue();
|
|
883
837
|
}
|
|
884
|
-
/** The session id these reports are attributed under. */
|
|
885
838
|
getSessionId() {
|
|
886
839
|
return this.sessionId;
|
|
887
840
|
}
|
|
888
841
|
pendingReportCount() {
|
|
889
842
|
return this.queue.length;
|
|
890
843
|
}
|
|
891
|
-
/** The cached snapshot, or null when absent or past its ttl. */
|
|
892
844
|
freshSnapshot() {
|
|
893
845
|
if (!this.snapshot) return null;
|
|
894
846
|
if (this.snapshot.ttl === 0) return null;
|
|
@@ -921,7 +873,6 @@ var SnapshotClient = class {
|
|
|
921
873
|
return [];
|
|
922
874
|
}
|
|
923
875
|
}
|
|
924
|
-
/** Stored decisions, pruned of expired entries. */
|
|
925
876
|
readDecisions() {
|
|
926
877
|
if (!this.storage) return {};
|
|
927
878
|
try {
|
|
@@ -1130,9 +1081,6 @@ async function reportPlanImpressions(plan, recipientIds, options) {
|
|
|
1130
1081
|
headers: { "Content-Type": "application/json", "x-api-key": options.apiKey },
|
|
1131
1082
|
body: JSON.stringify({
|
|
1132
1083
|
campaignId: plan.campaignId,
|
|
1133
|
-
// Derived from the recipients this call covers, so a retry of the same
|
|
1134
|
-
// chunk collapses onto the same key without the caller needing to
|
|
1135
|
-
// invent and persist an idempotency token of its own.
|
|
1136
1084
|
batchKey: options.batchKey ?? await batchKeyFor(plan.campaignId, recipientIds),
|
|
1137
1085
|
counts
|
|
1138
1086
|
})
|