Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@guardian/ab-core

Package Overview
Dependencies
Maintainers
6
Versions
32
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@guardian/ab-core - npm Package Compare versions

Comparing version 0.0.0-canary-20240429155350 to 0.0.0-canary-20240502162806

dist/@types/index.d.ts

93

dist/index.d.ts

@@ -1,91 +0,2 @@

type CoreAPIConfig = {
mvtMaxValue?: number;
mvtId: number;
pageIsSensitive: boolean;
abTestSwitches: Record<string, boolean>;
forcedTestVariants?: Participations;
forcedTestException?: ABTest['id'];
arrayOfTestObjects: ABTest[];
};
type CoreAPI = {
allRunnableTests: (tests: readonly ABTest[]) => ReadonlyArray<Runnable<ABTest>> | [];
runnableTest: (test: ABTest) => Runnable<ABTest & {
variantToRun: Variant;
}> | null;
firstRunnableTest: (tests: readonly ABTest[]) => Runnable<ABTest> | null;
isUserInVariant: (testId: ABTest['id'], variantId?: Variant['id']) => boolean;
};
type OphanAPIConfig = {
serverSideTests: ServerSideTests;
errorReporter: ErrorReporterFunc;
ophanRecord: OphanRecordFunction;
};
type OphanAPI = {
registerCompleteEvents: (tests: ReadonlyArray<Runnable<ABTest>>) => void;
registerImpressionEvents: (tests: ReadonlyArray<Runnable<ABTest>>) => void;
trackABTests: (tests: ReadonlyArray<Runnable<ABTest>>) => void;
};
type ABTestAPI = CoreAPI & OphanAPI;
type AbTestConfig = CoreAPIConfig & OphanAPIConfig;
interface OphanABEvent {
variantName: string;
complete: string | boolean;
campaignCodes?: readonly string[];
}
type OphanABPayload = Record<string, OphanABEvent>;
type OphanRecordFunction = (send: Record<string, OphanABPayload>) => void;
type ListenerFunction = (f: () => void) => void;
interface Variant {
id: string;
test: (x: Record<string, unknown>) => void;
campaignCode?: string;
canRun?: () => boolean;
impression?: ListenerFunction;
success?: ListenerFunction;
}
interface ABTest {
id: string;
start: string;
expiry: string;
author: string;
description: string;
audience: number;
audienceOffset: number;
successMeasure: string;
audienceCriteria: string;
showForSensitive?: boolean;
idealOutcome?: string;
dataLinkNames?: string;
variants: readonly Variant[];
canRun: () => boolean;
notInTest?: () => void;
}
type Participations = Record<string, {
variant: string;
}>;
type Runnable<ABTest> = ABTest & {
variantToRun: Variant;
};
type ServerSideTests = Record<string, string>;
/**
* Anything can be throw in JS, so you might want to narrow your return type
* with `error instanceof Error`
*/
type ErrorReporterFunc = (error: unknown) => void;
declare class AB implements ABTestAPI {
private readonly _core;
private readonly _ophan;
constructor({ abTestSwitches, arrayOfTestObjects, errorReporter, forcedTestException, forcedTestVariants, mvtId, mvtMaxValue, ophanRecord, pageIsSensitive, serverSideTests, }: AbTestConfig);
get allRunnableTests(): (tests: readonly ABTest[]) => readonly Runnable<ABTest>[] | [];
get firstRunnableTest(): (tests: readonly ABTest[]) => Runnable<ABTest> | null;
get runnableTest(): (test: ABTest) => Runnable<ABTest & {
variantToRun: Variant;
}> | null;
get isUserInVariant(): (testId: string, variantId?: string | undefined) => boolean;
get registerCompleteEvents(): (tests: readonly Runnable<ABTest>[]) => void;
get registerImpressionEvents(): (tests: readonly Runnable<ABTest>[]) => void;
get trackABTests(): (tests: readonly Runnable<ABTest>[]) => void;
}
export { AB, type ABTest, type ABTestAPI, type AbTestConfig, type CoreAPIConfig, type Participations, type Runnable, type Variant };
export { ABTest, ABTestAPI, AbTestConfig, CoreAPIConfig, Participations, Runnable, Variant } from './@types/index.js';
export { AB } from './ab.js';

@@ -1,204 +0,1 @@

const isExpired = (testExpiry) => {
const currentTime = (/* @__PURE__ */ new Date()).valueOf();
const theTestExpiry = new Date(testExpiry).setHours(23, 59, 59, 59);
return currentTime > theTestExpiry;
};
const initCore = ({
mvtMaxValue = 1e6,
mvtId,
pageIsSensitive,
abTestSwitches,
forcedTestVariants,
forcedTestException,
arrayOfTestObjects = []
}) => {
const variantCanBeRun = (variant) => {
const isInTest = variant.id !== "notintest";
if (variant.canRun) {
return variant.canRun() && isInTest;
} else {
return isInTest;
}
};
const testCanBeRun = (test) => {
const expired = isExpired(test.expiry);
const testShouldShowForSensitive = !!test.showForSensitive;
const isTestOn = abTestSwitches[`ab${test.id}`] && !!abTestSwitches[`ab${test.id}`];
const canTestBeRun = test.canRun();
return (pageIsSensitive ? testShouldShowForSensitive : true) && !!isTestOn && !expired && canTestBeRun;
};
const computeVariantFromMvtCookie = (test) => {
const smallestTestId = mvtMaxValue * test.audienceOffset;
const largestTestId = smallestTestId + mvtMaxValue * test.audience;
if (mvtId && mvtId > smallestTestId && mvtId <= largestTestId) {
return test.variants[mvtId % test.variants.length] ?? null;
}
return null;
};
const getForcedTestVariant = (test, forcedTestVariants2) => {
const testId = test.id;
const getVariantFromIds = (test2, variantId) => test2.variants.find((variant) => variant.id === variantId) ?? false;
const forcedTest = forcedTestVariants2?.[testId]?.variant;
return forcedTest ? getVariantFromIds(test, forcedTest) : false;
};
const runnableTest = (test) => {
const fromCookie = computeVariantFromMvtCookie(test);
const variantFromForcedTest = getForcedTestVariant(
test,
forcedTestVariants
);
const forcedOutOfTest = forcedTestException === test.id;
const variantToRun = variantFromForcedTest || fromCookie;
if (!forcedOutOfTest && (variantFromForcedTest || testCanBeRun(test)) && // We ignore the test's canRun if the test is forced
variantToRun && (variantFromForcedTest || variantCanBeRun(variantToRun))) {
return {
...test,
variantToRun
};
}
return null;
};
const allRunnableTests = (tests) => tests.reduce((prev, currentValue) => {
const rt = runnableTest(currentValue);
return rt ? [...prev, rt] : prev;
}, []);
const firstRunnableTest = (tests) => tests.map((test) => runnableTest(test)).find((rt) => rt !== null) ?? null;
const isUserInVariant = (testId, variantId) => allRunnableTests(arrayOfTestObjects).some(
(runnableTest2) => {
return runnableTest2.id === testId && runnableTest2.variantToRun.id === variantId;
}
);
return {
allRunnableTests,
runnableTest,
firstRunnableTest,
isUserInVariant
};
};
const submit = (payload, ophanRecord) => ophanRecord({
abTestRegister: payload
});
const makeABEvent = (variant, complete) => {
const event = {
variantName: variant.id,
complete
};
if (variant.campaignCode) {
event.campaignCodes = [variant.campaignCode];
}
return event;
};
const defersImpression = (test) => test.variants.every(
(variant) => typeof variant.impression === "function"
);
const buildOphanSubmitter = (test, variant, complete, ophanRecord) => {
const data = {
[test.id]: makeABEvent(variant, complete)
};
return () => submit(data, ophanRecord);
};
const registerCompleteEvent = (complete, errorReporter, ophanRecord) => (test) => {
const variant = test.variantToRun;
const listener = complete ? variant.success : variant.impression;
if (!listener) {
return;
}
try {
listener(buildOphanSubmitter(test, variant, complete, ophanRecord));
} catch (error) {
errorReporter(error);
}
};
const buildOphanPayload = (tests, errorReporter, serverSideTestObj) => {
try {
const log = {};
const serverSideTests = Object.keys(serverSideTestObj).filter(
(test) => !!serverSideTestObj[test]
);
tests.filter((test) => !defersImpression(test)).forEach((test) => {
log[test.id] = makeABEvent(test.variantToRun, false);
});
serverSideTests.forEach((test) => {
const serverSideVariant = {
id: "inTest",
test: () => void 0
};
log[`ab${test}`] = makeABEvent(serverSideVariant, false);
});
return log;
} catch (error) {
errorReporter(error);
return {};
}
};
const initOphan = ({
serverSideTests,
errorReporter,
ophanRecord
}) => ({
registerCompleteEvents: (tests) => tests.forEach(registerCompleteEvent(true, errorReporter, ophanRecord)),
registerImpressionEvents: (tests) => tests.filter(defersImpression).forEach(registerCompleteEvent(false, errorReporter, ophanRecord)),
trackABTests: (tests) => submit(
buildOphanPayload(tests, errorReporter, serverSideTests),
ophanRecord
)
});
class AB {
_core;
_ophan;
constructor({
abTestSwitches,
arrayOfTestObjects,
errorReporter,
forcedTestException,
forcedTestVariants,
mvtId,
mvtMaxValue,
ophanRecord,
pageIsSensitive,
serverSideTests
}) {
this._core = initCore({
abTestSwitches,
arrayOfTestObjects,
forcedTestException,
forcedTestVariants,
mvtId,
mvtMaxValue,
pageIsSensitive
});
this._ophan = initOphan({
errorReporter,
ophanRecord,
serverSideTests
});
}
// CoreAPI
get allRunnableTests() {
return this._core.allRunnableTests;
}
get firstRunnableTest() {
return this._core.firstRunnableTest;
}
get runnableTest() {
return this._core.runnableTest;
}
get isUserInVariant() {
return this._core.isUserInVariant;
}
// OphanAPI
get registerCompleteEvents() {
return this._ophan.registerCompleteEvents;
}
get registerImpressionEvents() {
return this._ophan.registerImpressionEvents;
}
get trackABTests() {
return this._ophan.trackABTests;
}
}
export { AB };
export { AB } from './ab.js';
{
"name": "@guardian/ab-core",
"version": "0.0.0-canary-20240429155350",
"version": "0.0.0-canary-20240502162806",
"private": false,

@@ -20,3 +20,3 @@ "description": "A client-side library for A/B & multivariate testing",

"devDependencies": {
"pkgroll": "2.0.2",
"rollup": "^4.16.4",
"tslib": "2.6.2",

@@ -85,3 +85,3 @@ "typescript": "5.3.3"

"scripts": {
"build": "rm -rf dist && pkgroll",
"build": "rm -rf dist && rollup -c",
"dev": "jest --watch",

@@ -88,0 +88,0 @@ "fix": "pnpm lint --fix",

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc