@runbandit/sdk 2.0.0 → 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/README.md +3 -1
- package/dist/index.d.ts +169 -166
- package/dist/index.js +501 -56
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npm install @runbandit/sdk
|
|
|
14
14
|
import { BanditsClient } from '@runbandit/sdk'
|
|
15
15
|
|
|
16
16
|
const client = new BanditsClient({
|
|
17
|
-
apiUrl: 'https://
|
|
17
|
+
apiUrl: 'https://runbandit.com',
|
|
18
18
|
apiKey: 'your-api-key',
|
|
19
19
|
})
|
|
20
20
|
|
|
@@ -29,4 +29,6 @@ client.trackNamedEvent({
|
|
|
29
29
|
})
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
**Note:** The SDK automatically adds `/api` to your `apiUrl`, so use the base domain (e.g., `https://runbandit.com`) without the `/api` path. If your `apiUrl` already ends with `/api`, the SDK won't double-prefix it.
|
|
33
|
+
|
|
32
34
|
See the [SDK docs](https://runbandit.com/docs/sdk) for the full API.
|
package/dist/index.d.ts
CHANGED
|
@@ -63,7 +63,10 @@ interface Experiment {
|
|
|
63
63
|
createdAt: Date;
|
|
64
64
|
updatedAt: Date;
|
|
65
65
|
endAt?: Date;
|
|
66
|
+
placement?: Placement;
|
|
66
67
|
}
|
|
68
|
+
declare const PLACEMENTS: readonly ["hero_headline", "hero_subhead", "cta_primary", "banner_text", "badge_text"];
|
|
69
|
+
type Placement = (typeof PLACEMENTS)[number];
|
|
67
70
|
interface Treatment {
|
|
68
71
|
id: string;
|
|
69
72
|
experimentId: string;
|
|
@@ -77,11 +80,13 @@ interface Treatment {
|
|
|
77
80
|
interface AlgorithmResult {
|
|
78
81
|
treatmentId: string;
|
|
79
82
|
confidence: number;
|
|
83
|
+
probabilityBest?: number;
|
|
80
84
|
}
|
|
81
85
|
interface BanditResult {
|
|
82
86
|
assignmentId: string;
|
|
83
87
|
treatmentId: string;
|
|
84
88
|
confidence: number;
|
|
89
|
+
probabilityBest?: number;
|
|
85
90
|
metadata?: Record<string, any>;
|
|
86
91
|
}
|
|
87
92
|
interface ContextualFeatures {
|
|
@@ -108,33 +113,15 @@ interface VariantConfig {
|
|
|
108
113
|
content: string;
|
|
109
114
|
metadata?: TreatmentMetadata & Record<string, any>;
|
|
110
115
|
}
|
|
111
|
-
/**
|
|
112
|
-
* Bandit algorithm families.
|
|
113
|
-
*
|
|
114
|
-
* Every family is Thompson-style: a Bayesian posterior per arm, sampled at
|
|
115
|
-
* selection time. How much of the past that posterior remembers is controlled
|
|
116
|
-
* separately, by `ForgettingMode`.
|
|
117
|
-
*/
|
|
118
116
|
declare enum BanditAlgorithm {
|
|
119
117
|
THOMPSON_SAMPLING = "thompson_sampling",
|
|
120
118
|
CONTEXTUAL_LINEAR = "contextual_linear"
|
|
121
119
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
* restless - creative fatigues, seasons turn, traffic mix shifts - so the two
|
|
128
|
-
* non-stationary modes below down-weight or drop stale evidence.
|
|
129
|
-
*
|
|
130
|
-
* - NONE stationary; every observation counts forever
|
|
131
|
-
* - DISCOUNTED observation weight decays as 0.5^(age / halfLifeMs)
|
|
132
|
-
* - SLIDING_WINDOW observations outside the last `windowMs` are dropped entirely
|
|
133
|
-
*
|
|
134
|
-
* Both non-stationary modes measure age in wall-clock time rather than in
|
|
135
|
-
* observation count, so a quiet experiment forgets at the same rate as a busy
|
|
136
|
-
* one.
|
|
137
|
-
*/
|
|
120
|
+
declare enum Posterior {
|
|
121
|
+
BETA = "beta",
|
|
122
|
+
NORMAL_GAMMA = "normal_gamma"
|
|
123
|
+
}
|
|
124
|
+
declare function normalizePosterior(posterior: string | null | undefined): Posterior;
|
|
138
125
|
declare enum ForgettingMode {
|
|
139
126
|
NONE = "none",
|
|
140
127
|
DISCOUNTED = "discounted",
|
|
@@ -142,31 +129,13 @@ declare enum ForgettingMode {
|
|
|
142
129
|
}
|
|
143
130
|
interface ForgettingConfig {
|
|
144
131
|
mode: ForgettingMode;
|
|
145
|
-
/** Half-life in milliseconds. DISCOUNTED only. */
|
|
146
132
|
halfLifeMs?: number;
|
|
147
|
-
/** Window length in milliseconds. SLIDING_WINDOW only. */
|
|
148
133
|
windowMs?: number;
|
|
149
134
|
}
|
|
150
|
-
/** Default half-life for DISCOUNTED: one week. */
|
|
151
135
|
declare const DEFAULT_HALF_LIFE_MS: number;
|
|
152
|
-
/** Default window for SLIDING_WINDOW: two weeks. */
|
|
153
136
|
declare const DEFAULT_WINDOW_MS: number;
|
|
154
|
-
/** The stationary default applied when an experiment has no forgetting config. */
|
|
155
137
|
declare const STATIONARY: ForgettingConfig;
|
|
156
|
-
/**
|
|
157
|
-
* Fill in the defaults for a partially-specified forgetting config.
|
|
158
|
-
* An unrecognised or missing mode degrades to NONE rather than throwing, so a
|
|
159
|
-
* stale row written by an older deploy still selects treatments.
|
|
160
|
-
*/
|
|
161
138
|
declare function resolveForgetting(config?: Partial<ForgettingConfig> | null): ForgettingConfig;
|
|
162
|
-
/**
|
|
163
|
-
* Map a stored algorithm string onto a supported algorithm.
|
|
164
|
-
*
|
|
165
|
-
* Retired names (epsilon_greedy, ucb1) map onto standard Thompson Sampling,
|
|
166
|
-
* which reads the same successes/trials counters they did, so a running
|
|
167
|
-
* experiment keeps its learned history. contextual_neural maps onto
|
|
168
|
-
* contextual_linear, which is what it already fell back to at runtime.
|
|
169
|
-
*/
|
|
170
139
|
declare function normalizeAlgorithm(algorithm: string | null | undefined): BanditAlgorithm;
|
|
171
140
|
declare enum ContentType {
|
|
172
141
|
PLAIN_TEXT = "plain_text",
|
|
@@ -217,6 +186,7 @@ interface AssignmentRequest {
|
|
|
217
186
|
userId: string;
|
|
218
187
|
context?: ContextualFeatures;
|
|
219
188
|
sessionId?: string;
|
|
189
|
+
pageUrl?: string;
|
|
220
190
|
}
|
|
221
191
|
interface TrackNamedEventRequest {
|
|
222
192
|
name: string;
|
|
@@ -300,6 +270,7 @@ interface BundleAssignmentRequest {
|
|
|
300
270
|
userId: string;
|
|
301
271
|
context?: ContextualFeatures;
|
|
302
272
|
sessionId?: string;
|
|
273
|
+
pageUrl?: string;
|
|
303
274
|
}
|
|
304
275
|
interface BundleSlotAssignment {
|
|
305
276
|
assignmentId: string;
|
|
@@ -311,33 +282,14 @@ interface BundleAssignmentResponse {
|
|
|
311
282
|
bundleAssignmentId: string;
|
|
312
283
|
slots: Record<string, BundleSlotAssignment>;
|
|
313
284
|
}
|
|
314
|
-
/**
|
|
315
|
-
* One treatment's published allocation within a snapshot experiment.
|
|
316
|
-
*
|
|
317
|
-
* This is the wire contract between the snapshot publisher (dashboard API)
|
|
318
|
-
* and its only consumer (the SDK). Declared once here so both sides share
|
|
319
|
-
* the same shape instead of drifting apart.
|
|
320
|
-
*/
|
|
321
285
|
interface SnapshotTreatment {
|
|
322
286
|
id: string;
|
|
323
287
|
name?: string;
|
|
324
288
|
config: unknown;
|
|
325
|
-
/** Probability the next visitor is assigned this arm. Sums to ~1 per experiment. */
|
|
326
289
|
pct: number;
|
|
327
290
|
}
|
|
328
291
|
interface SnapshotExperiment {
|
|
329
|
-
/**
|
|
330
|
-
* 'snapshot' means the client may decide locally from `pct`. 'network' means
|
|
331
|
-
* it must call /api/assignments, because the experiment's state cannot be
|
|
332
|
-
* reduced to a static distribution.
|
|
333
|
-
*/
|
|
334
292
|
mode: 'snapshot' | 'network';
|
|
335
|
-
/**
|
|
336
|
-
* Assignment lifetime in SECONDS (the unit `assignments.expiresAt` uses).
|
|
337
|
-
* The client holds a decision this long so that repeated pageviews in one
|
|
338
|
-
* session report one impression, matching the server path's one assignment
|
|
339
|
-
* per (experiment, user).
|
|
340
|
-
*/
|
|
341
293
|
assignmentTtl: number;
|
|
342
294
|
treatments: SnapshotTreatment[];
|
|
343
295
|
}
|
|
@@ -356,35 +308,124 @@ declare function calculateConfidenceInterval(successes: number, trials: number,
|
|
|
356
308
|
};
|
|
357
309
|
declare function isValidEmail(email: string): boolean;
|
|
358
310
|
declare function sanitizeMetadata(metadata: Record<string, any>): Record<string, any>;
|
|
359
|
-
/**
|
|
360
|
-
* Generates a URL-safe subdomain from a company name
|
|
361
|
-
* @param name Company name
|
|
362
|
-
* @returns URL-safe subdomain
|
|
363
|
-
*/
|
|
364
311
|
declare function generateSubdomain(name: string): string;
|
|
365
|
-
/**
|
|
366
|
-
* Validates if a subdomain is properly formatted
|
|
367
|
-
* @param subdomain Subdomain to validate
|
|
368
|
-
* @returns true if valid, false otherwise
|
|
369
|
-
*/
|
|
370
312
|
declare function isValidSubdomain(subdomain: string): boolean;
|
|
371
|
-
/**
|
|
372
|
-
* Extracts subdomain from hostname
|
|
373
|
-
* @param hostname Full hostname (e.g., 'acme.example.com')
|
|
374
|
-
* @param baseDomain Base domain (e.g., 'example.com')
|
|
375
|
-
* @returns Subdomain or null if not found
|
|
376
|
-
*/
|
|
377
313
|
declare function extractSubdomain(hostname: string, baseDomain: string): string | null;
|
|
378
|
-
/**
|
|
379
|
-
* Builds full URL with subdomain
|
|
380
|
-
* @param subdomain Subdomain
|
|
381
|
-
* @param baseDomain Base domain
|
|
382
|
-
* @param path Optional path
|
|
383
|
-
* @param protocol Protocol (default: https)
|
|
384
|
-
* @returns Full URL
|
|
385
|
-
*/
|
|
386
314
|
declare function buildSubdomainUrl(subdomain: string, baseDomain: string, path?: string, protocol?: string): string;
|
|
387
315
|
|
|
316
|
+
declare const EMAIL_TOKEN_PARAM = "bt";
|
|
317
|
+
interface SigningKey {
|
|
318
|
+
version: number;
|
|
319
|
+
secret: string;
|
|
320
|
+
}
|
|
321
|
+
interface EmailTokenPayload {
|
|
322
|
+
c: string;
|
|
323
|
+
r: string;
|
|
324
|
+
k: number[];
|
|
325
|
+
l: number;
|
|
326
|
+
v?: number;
|
|
327
|
+
}
|
|
328
|
+
type VerifiedToken = {
|
|
329
|
+
valid: true;
|
|
330
|
+
payload: Required<EmailTokenPayload>;
|
|
331
|
+
} | {
|
|
332
|
+
valid: false;
|
|
333
|
+
reason: 'malformed' | 'unknown_key' | 'bad_signature';
|
|
334
|
+
};
|
|
335
|
+
declare function hashRecipient(recipientId: string, companySecret: string): Promise<string>;
|
|
336
|
+
declare function mintEmailToken(payload: EmailTokenPayload, key: SigningKey): Promise<string>;
|
|
337
|
+
declare function verifyEmailToken(token: string, keys: SigningKey[]): Promise<VerifiedToken>;
|
|
338
|
+
declare function appendEmailToken(url: string, token: string): string;
|
|
339
|
+
|
|
340
|
+
interface PlanArm {
|
|
341
|
+
treatmentId: string;
|
|
342
|
+
armIndex: number;
|
|
343
|
+
}
|
|
344
|
+
interface PlanSlot {
|
|
345
|
+
experimentId: string;
|
|
346
|
+
name: string;
|
|
347
|
+
arms: PlanArm[];
|
|
348
|
+
probabilities: number[];
|
|
349
|
+
configs: unknown[];
|
|
350
|
+
}
|
|
351
|
+
interface EmailPlan {
|
|
352
|
+
campaignId: string;
|
|
353
|
+
slots: PlanSlot[];
|
|
354
|
+
linkTable: string[];
|
|
355
|
+
salt: string;
|
|
356
|
+
signingKey: {
|
|
357
|
+
version: number;
|
|
358
|
+
secret: string;
|
|
359
|
+
};
|
|
360
|
+
refreshEvery: number;
|
|
361
|
+
expiresAt: number;
|
|
362
|
+
}
|
|
363
|
+
declare function uniformFor(salt: string, recipientId: string, slotIndex: number): Promise<number>;
|
|
364
|
+
declare function pickArm(probabilities: number[], uniform: number): number;
|
|
365
|
+
declare function sampleCombination(plan: EmailPlan, recipientId: string): Promise<number[]>;
|
|
366
|
+
declare function impressionManifest(plan: EmailPlan, recipientIds: string[]): Promise<Array<{
|
|
367
|
+
experimentId: string;
|
|
368
|
+
treatmentId: string;
|
|
369
|
+
count: number;
|
|
370
|
+
}>>;
|
|
371
|
+
|
|
372
|
+
interface SlotBinding {
|
|
373
|
+
$slot: string;
|
|
374
|
+
}
|
|
375
|
+
type Bound<T> = T | SlotBinding;
|
|
376
|
+
declare function isSlotBinding(value: unknown): value is SlotBinding;
|
|
377
|
+
interface HeadingBlock {
|
|
378
|
+
type: 'heading';
|
|
379
|
+
text: Bound<string>;
|
|
380
|
+
level?: 1 | 2 | 3;
|
|
381
|
+
align?: 'left' | 'center' | 'right';
|
|
382
|
+
color?: Bound<string>;
|
|
383
|
+
}
|
|
384
|
+
interface TextBlock {
|
|
385
|
+
type: 'text';
|
|
386
|
+
text: Bound<string>;
|
|
387
|
+
align?: 'left' | 'center' | 'right';
|
|
388
|
+
color?: Bound<string>;
|
|
389
|
+
}
|
|
390
|
+
interface ButtonBlock {
|
|
391
|
+
type: 'button';
|
|
392
|
+
text: Bound<string>;
|
|
393
|
+
href: Bound<string>;
|
|
394
|
+
backgroundColor?: Bound<string>;
|
|
395
|
+
textColor?: Bound<string>;
|
|
396
|
+
align?: 'left' | 'center' | 'right';
|
|
397
|
+
}
|
|
398
|
+
interface ImageBlock {
|
|
399
|
+
type: 'image';
|
|
400
|
+
src: Bound<string>;
|
|
401
|
+
alt: Bound<string>;
|
|
402
|
+
href?: Bound<string>;
|
|
403
|
+
width?: number;
|
|
404
|
+
}
|
|
405
|
+
interface DividerBlock {
|
|
406
|
+
type: 'divider';
|
|
407
|
+
color?: string;
|
|
408
|
+
}
|
|
409
|
+
interface SpacerBlock {
|
|
410
|
+
type: 'spacer';
|
|
411
|
+
height?: number;
|
|
412
|
+
}
|
|
413
|
+
type EmailBlock = HeadingBlock | TextBlock | ButtonBlock | ImageBlock | DividerBlock | SpacerBlock;
|
|
414
|
+
interface EmailDocument {
|
|
415
|
+
subject: Bound<string>;
|
|
416
|
+
preheader?: Bound<string>;
|
|
417
|
+
blocks: EmailBlock[];
|
|
418
|
+
backgroundColor?: string;
|
|
419
|
+
contentWidth?: number;
|
|
420
|
+
}
|
|
421
|
+
declare function extractDocumentSlots(document: EmailDocument): string[];
|
|
422
|
+
interface RenderedDocument {
|
|
423
|
+
subject: string;
|
|
424
|
+
html: string;
|
|
425
|
+
missingSlots: string[];
|
|
426
|
+
}
|
|
427
|
+
declare function renderEmailDocument(document: EmailDocument, slots: Record<string, string>): RenderedDocument;
|
|
428
|
+
|
|
388
429
|
interface BanditsClientConfig {
|
|
389
430
|
apiUrl: string;
|
|
390
431
|
apiKey: string;
|
|
@@ -407,11 +448,6 @@ declare class BanditsClient {
|
|
|
407
448
|
private flushTimer?;
|
|
408
449
|
constructor(config: BanditsClientConfig);
|
|
409
450
|
getAssignment(experimentId: string, userId: string, context?: ContextualFeatures): Promise<Assignment>;
|
|
410
|
-
/**
|
|
411
|
-
* Track a named event for session-based attribution (recommended)
|
|
412
|
-
* Events are matched to bandits by sessionId — no assignmentId needed
|
|
413
|
-
* Set immediate: true to fire synchronously (survives navigation)
|
|
414
|
-
*/
|
|
415
451
|
trackNamedEvent(event: {
|
|
416
452
|
name: string;
|
|
417
453
|
category?: string;
|
|
@@ -421,34 +457,13 @@ declare class BanditsClient {
|
|
|
421
457
|
immediate?: boolean;
|
|
422
458
|
}): void;
|
|
423
459
|
flush(): Promise<void>;
|
|
424
|
-
/**
|
|
425
|
-
* Link an anonymous user ID to a durable identity (e.g. email, account ID).
|
|
426
|
-
* All historical assignments and events for the anonymous ID are re-attributed
|
|
427
|
-
* to the identified user. Future calls to getAssignment() with either ID will
|
|
428
|
-
* resolve to the same assignment.
|
|
429
|
-
*
|
|
430
|
-
* Requires admin authentication — the client must be configured with a
|
|
431
|
-
* personal access token (PAT) instead of an API key, and companyId must
|
|
432
|
-
* be provided. This is a management operation, not an SDK client operation.
|
|
433
|
-
*
|
|
434
|
-
* @param anonymousId - The temporary/anonymous user ID (e.g. crypto.randomUUID())
|
|
435
|
-
* @param identifiedUserId - The permanent user ID (e.g. email, database ID)
|
|
436
|
-
* @param companyId - The company ID (required for PAT auth)
|
|
437
|
-
* @param mergeReason - Why the identification is happening
|
|
438
|
-
*/
|
|
439
460
|
identify(anonymousId: string, identifiedUserId: string, companyId: string, mergeReason?: 'user_signup' | 'email_verification' | 'social_login' | 'account_recovery' | 'manual'): Promise<void>;
|
|
440
|
-
|
|
441
|
-
* Record a funnel payout event.
|
|
442
|
-
* Call this when the terminal conversion happens (e.g., purchase).
|
|
443
|
-
*
|
|
444
|
-
* @param funnelId - The funnel ID
|
|
445
|
-
* @param userId - The user who converted
|
|
446
|
-
* @param value - The payout value (e.g., purchase amount in dollars)
|
|
447
|
-
*/
|
|
461
|
+
label(userId: string, labels: Record<string, string | boolean>): Promise<void>;
|
|
448
462
|
trackFunnelReward(funnelId: string, userId: string, value: number): Promise<void>;
|
|
449
463
|
getBundle(bundleId: string, userId: string, context?: ContextualFeatures): Promise<BundleAssignmentResponse>;
|
|
450
464
|
trackBundleReward(bundleId: string, userId: string, value: number): Promise<void>;
|
|
451
465
|
private generateId;
|
|
466
|
+
private normalizeApiUrl;
|
|
452
467
|
private getOrCreateSessionId;
|
|
453
468
|
private makeRequest;
|
|
454
469
|
private startBatchFlush;
|
|
@@ -464,28 +479,12 @@ interface SyncAssignment {
|
|
|
464
479
|
interface SnapshotClientConfig {
|
|
465
480
|
apiUrl: string;
|
|
466
481
|
publishableKey: string;
|
|
467
|
-
/** Defaults to window.localStorage. Injectable for tests and SSR. */
|
|
468
482
|
storage?: Storage;
|
|
469
|
-
/** Injectable clock. Defaults to Date.now. */
|
|
470
483
|
now?: () => number;
|
|
471
|
-
/** Session lifetime in ms. Must match the BanditsClient using the same storage. */
|
|
472
484
|
sessionTtl?: number;
|
|
473
|
-
/**
|
|
474
|
-
* A snapshot embedded in the page at render time. When present there is no
|
|
475
|
-
* cold start at all - the decision happens with zero network calls, which is
|
|
476
|
-
* the whole point of the design.
|
|
477
|
-
*/
|
|
478
485
|
inlineSnapshot?: CompanySnapshot;
|
|
479
486
|
fetchImpl?: typeof fetch;
|
|
480
487
|
}
|
|
481
|
-
/**
|
|
482
|
-
* Pick an arm from a published allocation.
|
|
483
|
-
*
|
|
484
|
-
* One uniform draw against the cumulative distribution. This is equivalent to
|
|
485
|
-
* Thompson sampling because `pct` *is* the distribution Thompson sampling
|
|
486
|
-
* draws from - the Monte Carlo ran server-side, once, instead of here on every
|
|
487
|
-
* pageview.
|
|
488
|
-
*/
|
|
489
488
|
declare function pickTreatment(treatments: SnapshotTreatment[], draw: number): SnapshotTreatment | null;
|
|
490
489
|
declare class SnapshotClient {
|
|
491
490
|
private config;
|
|
@@ -497,66 +496,70 @@ declare class SnapshotClient {
|
|
|
497
496
|
private sessionId;
|
|
498
497
|
private inFlight;
|
|
499
498
|
constructor(config: SnapshotClientConfig);
|
|
500
|
-
/**
|
|
501
|
-
* Decide an arm with no network call, or return null.
|
|
502
|
-
*
|
|
503
|
-
* Null means "this caller must fall back" - a cold cache, an expired one, an
|
|
504
|
-
* unknown experiment, or one whose state cannot be served from a static
|
|
505
|
-
* document. Returning null rather than the control is deliberate: the caller
|
|
506
|
-
* renders control itself, and no impression is reported for it. Reporting a
|
|
507
|
-
* forced-control impression would inflate the control arm's trials with
|
|
508
|
-
* decisions the bandit never made.
|
|
509
|
-
*/
|
|
510
499
|
getAssignmentSync(experimentId: string, userId: string): SyncAssignment | null;
|
|
511
|
-
/** Fetch a fresh snapshot and cache it. Safe to call on every pageview. */
|
|
512
500
|
refresh(): Promise<void>;
|
|
513
|
-
/**
|
|
514
|
-
* Post queued impressions.
|
|
515
|
-
*
|
|
516
|
-
* The queue is only cleared on success. A failed flush leaves the reports in
|
|
517
|
-
* storage, so a pageview that happened offline is still counted when the
|
|
518
|
-
* next one succeeds - the trial really did occur, and dropping it would bias
|
|
519
|
-
* the arm's denominator downward.
|
|
520
|
-
*/
|
|
521
501
|
flush(): Promise<void>;
|
|
522
502
|
private doFlush;
|
|
523
|
-
/** The session id these reports are attributed under. */
|
|
524
503
|
getSessionId(): string;
|
|
525
504
|
pendingReportCount(): number;
|
|
526
|
-
/** The cached snapshot, or null when absent or past its ttl. */
|
|
527
505
|
private freshSnapshot;
|
|
528
506
|
private readSnapshot;
|
|
529
507
|
private persistSnapshot;
|
|
530
508
|
private readQueue;
|
|
531
|
-
/** Stored decisions, pruned of expired entries. */
|
|
532
509
|
private readDecisions;
|
|
533
510
|
private writeDecisions;
|
|
534
511
|
private persistQueue;
|
|
535
512
|
}
|
|
536
513
|
|
|
537
|
-
/**
|
|
538
|
-
* Session identity, shared by every client in this package.
|
|
539
|
-
*
|
|
540
|
-
* Reward attribution joins `assignments` by `sessionId`, so a decision made by
|
|
541
|
-
* `SnapshotClient` and an event tracked by `BanditsClient` must agree on the
|
|
542
|
-
* id or the conversion never lands on the impression. One implementation,
|
|
543
|
-
* used by both, is the only way that rule stays true.
|
|
544
|
-
*/
|
|
545
514
|
declare const SESSION_ID_KEY = "bandits_session_id";
|
|
546
515
|
declare const SESSION_CREATED_AT_KEY = "bandits_session_created_at";
|
|
547
|
-
/** 24 hours, in milliseconds. */
|
|
548
516
|
declare const DEFAULT_SESSION_TTL_MS = 86400000;
|
|
549
517
|
interface SessionOptions {
|
|
550
|
-
/** Where the session lives. Null (no browser storage) means ephemeral. */
|
|
551
518
|
storage?: Storage | null;
|
|
552
|
-
/** Injectable clock, in milliseconds. */
|
|
553
519
|
now?: () => number;
|
|
554
520
|
ttlMs?: number;
|
|
555
521
|
}
|
|
556
|
-
/**
|
|
557
|
-
* Return the current session id, minting and persisting a new one when there
|
|
558
|
-
* is none or the stored one has aged past `ttlMs`.
|
|
559
|
-
*/
|
|
560
522
|
declare function getOrCreateSessionId(options?: SessionOptions): string;
|
|
561
523
|
|
|
562
|
-
|
|
524
|
+
interface RewriteOptions {
|
|
525
|
+
linkTable: string[];
|
|
526
|
+
tokens: string[];
|
|
527
|
+
}
|
|
528
|
+
interface RewriteResult {
|
|
529
|
+
html: string;
|
|
530
|
+
rewritten: number;
|
|
531
|
+
untracked: string[];
|
|
532
|
+
}
|
|
533
|
+
declare function extractEmailLinks(html: string): string[];
|
|
534
|
+
declare function rewriteEmailLinks(html: string, options: RewriteOptions): RewriteResult;
|
|
535
|
+
|
|
536
|
+
interface RenderEmailOptions {
|
|
537
|
+
slots: Record<string, string>;
|
|
538
|
+
linkTable: string[];
|
|
539
|
+
tokens: string[];
|
|
540
|
+
}
|
|
541
|
+
interface RenderedEmail {
|
|
542
|
+
html: string;
|
|
543
|
+
rewritten: number;
|
|
544
|
+
untracked: string[];
|
|
545
|
+
missingSlots: string[];
|
|
546
|
+
}
|
|
547
|
+
declare function renderEmail(templateHtml: string, options: RenderEmailOptions): RenderedEmail;
|
|
548
|
+
declare function extractSlotNames(templateHtml: string): string[];
|
|
549
|
+
|
|
550
|
+
interface PlanRenderResult extends RenderedEmail {
|
|
551
|
+
combination: number[];
|
|
552
|
+
tokens: string[];
|
|
553
|
+
}
|
|
554
|
+
declare function renderFromPlan(plan: EmailPlan, recipientId: string, templateHtml: string, companySecret: string): Promise<PlanRenderResult>;
|
|
555
|
+
declare function shouldRefreshPlan(plan: EmailPlan, recipientsRenderedSincePlan: number, now?: number): boolean;
|
|
556
|
+
declare function reportPlanImpressions(plan: EmailPlan, recipientIds: string[], options: {
|
|
557
|
+
apiUrl: string;
|
|
558
|
+
apiKey: string;
|
|
559
|
+
batchKey?: string;
|
|
560
|
+
}): Promise<{
|
|
561
|
+
recorded: boolean;
|
|
562
|
+
alreadyRecorded: boolean;
|
|
563
|
+
}>;
|
|
564
|
+
|
|
565
|
+
export { type APIResponse, type Account, type AlgorithmResult, type ApiKey, type Assignment, type AssignmentRequest, BanditAlgorithm, BanditsClient as BanditClient, type BanditConfig, type BanditResult, BanditsClient, type BanditsClientConfig, type Bound, type Bundle, type BundleAssignmentRequest, type BundleAssignmentResponse, type BundleMode, type BundleSlotAssignment, type ButtonBlock, type Company, type CompanySnapshot, ContentType, type ContextualFeatures, type CreateApiKeyRequest, type CreateApiKeyResponse, type CreateExperimentRequest, DEFAULT_HALF_LIFE_MS, DEFAULT_SESSION_TTL_MS, DEFAULT_WINDOW_MS, type DividerBlock, EMAIL_TOKEN_PARAM, type EmailBlock, type EmailDocument, type EmailPlan, type EmailTokenPayload, type Experiment, type ExperimentStats, ExperimentStatus, type ForgettingConfig, ForgettingMode, type Funnel, type FunnelRewardRequest, FunnelStatus, type HeadingBlock, type IdentifyMergeReason, type IdentifyUserRequest, type IdentifyUserResponse, type ImageBlock, PLACEMENTS, type Placement, type PlanArm, type PlanRenderResult, type PlanSlot, Posterior, type RenderEmailOptions, type RenderedDocument, type RenderedEmail, type ResolvedIdentity, type RewriteOptions, type RewriteResult, SESSION_CREATED_AT_KEY, SESSION_ID_KEY, STATIONARY, type Session, type SessionOptions, type SigningKey, type SlotBinding, SnapshotClient, type SnapshotClientConfig, type SnapshotExperiment, type SnapshotTreatment, type SpacerBlock, type SyncAssignment, type TextBlock, type TrackNamedEventRequest, type Treatment, type TreatmentMetadata, type TreatmentStats, type User, type VariantConfig, type VerifiedToken, appendEmailToken, buildSubdomainUrl, calculateConfidenceInterval, extractDocumentSlots, extractEmailLinks, extractSlotNames, extractSubdomain, generateId, generateSubdomain, getContentTypeCategory, getOrCreateSessionId, hashRecipient, impressionManifest, isSlotBinding, isValidEmail, isValidSubdomain, mintEmailToken, normalizeAlgorithm, normalizePosterior, normalizeTreatmentWeights, pickArm, pickTreatment, renderEmail, renderEmailDocument, renderFromPlan, reportPlanImpressions, resolveForgetting, rewriteEmailLinks, sampleCombination, sanitizeMetadata, shouldRefreshPlan, uniformFor, validateTreatmentWeights, verifyEmailToken };
|