@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.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
- * Record a funnel payout event.
174
- * Call this when the terminal conversion happens (e.g., purchase).
175
- *
176
- * @param funnelId - The funnel ID
177
- * @param userId - The user who converted
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
  })