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

@the-control-group/react-split-test

Package Overview
Dependencies
Maintainers
3
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@the-control-group/react-split-test - npm Package Compare versions

Comparing version 1.0.0 to 2.0.0

lib/Experiment.d.ts

217

lib/Experiment.js

@@ -1,104 +0,117 @@

import React, { Component } from 'react';
import PropTypes from 'prop-types';
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importStar(require("react"));
const variationPromiseCache = new Map();
export default class Experiment extends Component {
static propTypes = {
children: PropTypes.node.isRequired,
id: PropTypes.string.isRequired,
session: PropTypes.bool,
onParticipate: PropTypes.func,
variationDecider: PropTypes.func
};
static defaultProps = {
onParticipate: () => {},
/**
* Stub A/B decider
* @param {React.node} experimentChildren - React children nodes of the experiment
* @return {Promise<variation id>} Selected variation ID
*/
variationDecider: experimentChildren => {
const variations = [];
React.Children.forEach(experimentChildren, c => {
if(c.props.isVariation) variations.push(c.props.id);
});
return Promise.resolve(variations[Math.floor(Math.random() * variations.length)]);
}
};
constructor(props) {
super(props);
this.cacheKey = `@tcg-split-test:${this.props.id}`;
this.storage = this.props.session ? sessionStorage : localStorage;
this.state = {
selectedVariation: this.chooseVariation()
};
}
chooseVariation() {
let cachedData;
try {
cachedData = JSON.parse(this.storage.getItem(this.cacheKey));
} catch (e) {
// No/invalid cache
}
if(cachedData) {
return cachedData.selectedVariation;
} else {
// Cache promises so only one decision is made per experiment ID
let variationDecider;
if(variationPromiseCache.has(this.props.id)) {
variationDecider = variationPromiseCache.get(this.props.id);
} else {
variationDecider = this.props.variationDecider(this.props.children);
variationPromiseCache.set(this.props.id, variationDecider);
}
variationDecider
.then(selectedVariation => {
const experimentData = {
id: this.props.id,
selectedVariation
};
// Since this is async, we need to check if another component with the same experiment
// has already made this decision in a potential race condition
let raceData;
try {
raceData = JSON.parse(this.storage.getItem(this.cacheKey));
} catch(e) {
// No/invalid data
}
if(!raceData) {
this.storage.setItem(this.cacheKey, JSON.stringify(experimentData));
this.props.onParticipate(experimentData);
// Remove the initial promise from the cache since subsequent renders/mounts will work off of the cache
variationPromiseCache.delete(this.props.id);
}
this.setState({
selectedVariation: raceData ? raceData.selectedVariation : selectedVariation
});
});
return null;
}
}
render() {
const { selectedVariation } = this.state;
return React.Children.map(this.props.children, child => {
if(child.props.isVariation && child.props.id !== selectedVariation) return;
return child;
});
}
class Experiment extends react_1.Component {
constructor(props) {
super(props);
this.cacheKey = `@tcg-split-test:${this.props.id}`;
this.storage = this.props.session ? sessionStorage : localStorage;
this.state = {
selectedVariation: this.chooseVariation()
};
}
chooseVariation() {
let cachedData;
const cachedString = this.storage.getItem(this.cacheKey);
if (cachedString) {
try {
cachedData = JSON.parse(cachedString);
}
catch (e) {
console.error('Invalid experiment cache data', e);
}
}
if (cachedData) {
return cachedData.selectedVariation;
}
else {
// Cache promises so only one decision is made per experiment ID
let variationDecider;
if (variationPromiseCache.has(this.props.id)) {
variationDecider = variationPromiseCache.get(this.props.id);
}
else {
variationDecider = this.props.variationDecider(this.props.children);
variationPromiseCache.set(this.props.id, variationDecider);
}
variationDecider
.then(selectedVariation => {
const experimentData = {
id: this.props.id,
selectedVariation
};
// Since this is async, we need to check if another component with the same experiment
// has already made this decision in a potential race condition
let raceData;
const cachedRaceString = this.storage.getItem(this.cacheKey);
if (cachedRaceString) {
try {
raceData = JSON.parse(cachedRaceString);
}
catch (e) {
console.error('Invalid experiment race cache data', e);
}
}
if (!raceData) {
this.storage.setItem(this.cacheKey, JSON.stringify(experimentData));
this.props.onParticipate(experimentData);
// Remove the initial promise from the cache since subsequent renders/mounts will work off of the cache
variationPromiseCache.delete(this.props.id);
}
this.setState({
selectedVariation: raceData ? raceData.selectedVariation : selectedVariation
});
});
return null;
}
}
render() {
const { selectedVariation } = this.state;
return react_1.default.Children.map(this.props.children, child => {
if (react_1.default.isValidElement(child) && child.props.isVariation && child.props.id !== selectedVariation)
return;
return child;
});
}
}
Experiment.defaultProps = {
onParticipate: () => { },
/**
* Stub A/B decider
* @param {React.node} experimentChildren - React children nodes of the experiment
* @return {Promise<variation id>} Selected variation ID
*/
variationDecider: (experimentChildren) => {
const variations = [];
react_1.default.Children.forEach(experimentChildren, c => {
if (react_1.default.isValidElement(c) && c.props.isVariation)
variations.push(c.props.id);
});
return Promise.resolve(variations[Math.floor(Math.random() * variations.length)]);
}
};
exports.default = Experiment;

@@ -1,15 +0,31 @@

export { default as Experiment } from './Experiment';
export { default as Variation } from './Variation';
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.clearAllExperimentCaches = exports.clearExperimentCache = exports.getCachedVariation = exports.Variation = exports.Experiment = void 0;
var Experiment_1 = require("./Experiment");
Object.defineProperty(exports, "Experiment", { enumerable: true, get: function () { return __importDefault(Experiment_1).default; } });
var Variation_1 = require("./Variation");
Object.defineProperty(exports, "Variation", { enumerable: true, get: function () { return __importDefault(Variation_1).default; } });
// These only work for persisted variations
export function getCachedVariation(id) {
return JSON.parse(localStorage.getItem(`@tcg-split-test:${id}`));
function getCachedVariation(id) {
const cachedVariation = localStorage.getItem(`@tcg-split-test:${id}`);
if (cachedVariation) {
try {
return JSON.parse(cachedVariation);
}
catch (e) {
console.error('Invalid variation data', e);
}
}
}
export function clearExperimentCache(id) {
localStorage.removeItem(`@tcg-split-test:${id}`);
exports.getCachedVariation = getCachedVariation;
function clearExperimentCache(id) {
localStorage.removeItem(`@tcg-split-test:${id}`);
}
export function clearAllExperimentCaches() {
Object.keys(localStorage).forEach(k => /^@tcg-split-test:/.test(k) && localStorage.removeItem(k));
exports.clearExperimentCache = clearExperimentCache;
function clearAllExperimentCaches() {
Object.keys(localStorage).forEach(k => /^@tcg-split-test:/.test(k) && localStorage.removeItem(k));
}
exports.clearAllExperimentCaches = clearAllExperimentCaches;

@@ -1,18 +0,11 @@

import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
const Variation = ({ children }) => (
<Fragment>{children}</Fragment>
);
Variation.propTypes = {
children: PropTypes.node.isRequired,
isVariation: PropTypes.bool,
id: PropTypes.string.isRequired
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const Variation = ({ children }) => (react_1.default.createElement(react_1.default.Fragment, null, children));
Variation.defaultProps = {
isVariation: true
isVariation: true
};
export default Variation;
exports.default = Variation;
{
"name": "@the-control-group/react-split-test",
"version": "1.0.0",
"version": "2.0.0",
"description": "A/B Split Testing Component for React",

@@ -10,7 +10,6 @@ "files": [

"scripts": {
"watch": "cpx \"src/**/*.{less,js}\" lib --watch",
"copy": "rm -rf lib/ && cpx \"src/*.js\" lib",
"prepublishOnly": "npm test && npm run copy",
"start": "react-devtools & webpack-dev-server --hot --mode development",
"test": "eslint src"
"prepublishOnly": "npm test && npm run build",
"start": "webpack serve --mode development",
"build": "tsc",
"test": "tsc --noEmit && eslint src"
},

@@ -35,25 +34,23 @@ "repository": {

"devDependencies": {
"@babel/cli": "^7.6.4",
"@babel/core": "^7.6.4",
"@babel/plugin-proposal-class-properties": "^7.5.5",
"@babel/preset-env": "^7.6.3",
"@babel/preset-react": "^7.6.3",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.0.6",
"cpx": "^1.5.0",
"eslint": "^6.5.1",
"eslint-plugin-react": "^7.16.0",
"html-webpack-plugin": "^3.2.0",
"prop-types": "^15.6.2",
"react": "^16.4.2",
"react-devtools": "^4.2.0",
"react-dom": "^16.10.2",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.2"
"@types/react": "^18.2.25",
"@types/react-dom": "^18.2.10",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"eslint": "^8.50.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"fork-ts-checker-webpack-plugin": "^9.0.0",
"html-webpack-plugin": "^5.5.3",
"npm-check-updates": "^16.14.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ts-loader": "^9.4.4",
"typescript": "^5.2.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
"peerDependencies": {
"react": ">=16",
"prop-types": ">=15"
"react": ">=17"
}
}
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