@runbandit/sdk 2.0.1 → 2.1.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.
- package/dist/index.d.ts +1 -581
- package/dist/index.js +6 -59
- 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();
|
|
@@ -86,11 +85,6 @@ var BanditsClient = class {
|
|
|
86
85
|
config: assignment.treatment?.config || {}
|
|
87
86
|
};
|
|
88
87
|
}
|
|
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
88
|
trackNamedEvent(event, opts) {
|
|
95
89
|
const request = {
|
|
96
90
|
name: event.name,
|
|
@@ -139,21 +133,6 @@ var BanditsClient = class {
|
|
|
139
133
|
throw error;
|
|
140
134
|
}
|
|
141
135
|
}
|
|
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
136
|
async identify(anonymousId, identifiedUserId, companyId, mergeReason) {
|
|
158
137
|
const response = await this.makeRequest(
|
|
159
138
|
"/identify",
|
|
@@ -169,14 +148,12 @@ var BanditsClient = class {
|
|
|
169
148
|
throw new Error(response.error || "Failed to identify user");
|
|
170
149
|
}
|
|
171
150
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
* @param value - The payout value (e.g., purchase amount in dollars)
|
|
179
|
-
*/
|
|
151
|
+
async label(userId, labels) {
|
|
152
|
+
const response = await this.makeRequest("/labels", "POST", { userId, labels });
|
|
153
|
+
if (!response.success) {
|
|
154
|
+
throw new Error(response.error || "Failed to record labels");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
180
157
|
async trackFunnelReward(funnelId, userId, value) {
|
|
181
158
|
const response = await this.makeRequest(
|
|
182
159
|
`/funnels/${funnelId}/reward`,
|
|
@@ -221,10 +198,6 @@ var BanditsClient = class {
|
|
|
221
198
|
generateId() {
|
|
222
199
|
return generateId();
|
|
223
200
|
}
|
|
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
201
|
normalizeApiUrl() {
|
|
229
202
|
let baseUrl = this.config.apiUrl.replace(/\/$/, "");
|
|
230
203
|
if (!baseUrl.endsWith("/api")) {
|
|
@@ -780,16 +753,6 @@ var SnapshotClient = class {
|
|
|
780
753
|
this.snapshot = this.readSnapshot();
|
|
781
754
|
}
|
|
782
755
|
}
|
|
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
756
|
getAssignmentSync(experimentId, userId) {
|
|
794
757
|
const snapshot = this.freshSnapshot();
|
|
795
758
|
if (!snapshot) return null;
|
|
@@ -816,7 +779,6 @@ var SnapshotClient = class {
|
|
|
816
779
|
decisions[key] = {
|
|
817
780
|
clientAssignmentId,
|
|
818
781
|
treatmentId: treatment.id,
|
|
819
|
-
// expiresAt is milliseconds here; assignmentTtl arrives in seconds.
|
|
820
782
|
expiresAt: this.now() + ttlSeconds * 1e3
|
|
821
783
|
};
|
|
822
784
|
this.writeDecisions(decisions);
|
|
@@ -835,7 +797,6 @@ var SnapshotClient = class {
|
|
|
835
797
|
config: treatment.config
|
|
836
798
|
};
|
|
837
799
|
}
|
|
838
|
-
/** Fetch a fresh snapshot and cache it. Safe to call on every pageview. */
|
|
839
800
|
async refresh() {
|
|
840
801
|
const response = await this.fetchImpl(`${this.config.apiUrl}/snapshot`, {
|
|
841
802
|
method: "GET",
|
|
@@ -846,14 +807,6 @@ var SnapshotClient = class {
|
|
|
846
807
|
this.snapshot = snapshot;
|
|
847
808
|
this.persistSnapshot(snapshot);
|
|
848
809
|
}
|
|
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
810
|
async flush() {
|
|
858
811
|
if (this.inFlight) return this.inFlight;
|
|
859
812
|
this.inFlight = this.doFlush().finally(() => {
|
|
@@ -881,14 +834,12 @@ var SnapshotClient = class {
|
|
|
881
834
|
this.queue = this.queue.slice(reports.length);
|
|
882
835
|
this.persistQueue();
|
|
883
836
|
}
|
|
884
|
-
/** The session id these reports are attributed under. */
|
|
885
837
|
getSessionId() {
|
|
886
838
|
return this.sessionId;
|
|
887
839
|
}
|
|
888
840
|
pendingReportCount() {
|
|
889
841
|
return this.queue.length;
|
|
890
842
|
}
|
|
891
|
-
/** The cached snapshot, or null when absent or past its ttl. */
|
|
892
843
|
freshSnapshot() {
|
|
893
844
|
if (!this.snapshot) return null;
|
|
894
845
|
if (this.snapshot.ttl === 0) return null;
|
|
@@ -921,7 +872,6 @@ var SnapshotClient = class {
|
|
|
921
872
|
return [];
|
|
922
873
|
}
|
|
923
874
|
}
|
|
924
|
-
/** Stored decisions, pruned of expired entries. */
|
|
925
875
|
readDecisions() {
|
|
926
876
|
if (!this.storage) return {};
|
|
927
877
|
try {
|
|
@@ -1130,9 +1080,6 @@ async function reportPlanImpressions(plan, recipientIds, options) {
|
|
|
1130
1080
|
headers: { "Content-Type": "application/json", "x-api-key": options.apiKey },
|
|
1131
1081
|
body: JSON.stringify({
|
|
1132
1082
|
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
1083
|
batchKey: options.batchKey ?? await batchKeyFor(plan.campaignId, recipientIds),
|
|
1137
1084
|
counts
|
|
1138
1085
|
})
|