Skip to content

Commit

Permalink
Migrate generic prototype code to TypeScript (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexnj authored Feb 28, 2023
1 parent 5d67d30 commit 606987a
Show file tree
Hide file tree
Showing 19 changed files with 2,563 additions and 8,323 deletions.
6 changes: 0 additions & 6 deletions .babelrc

This file was deleted.

174 changes: 170 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,171 @@
.DS_Store
/target
/dist
# Logs

logs
_.log
npm-debug.log_
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)

report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json

# Runtime data

pids
_.pid
_.seed
\*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover

lib-cov

# Coverage directory used by tools like istanbul

coverage
\*.lcov

# nyc test coverage

.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)

.grunt

# Bower dependency directory (https://bower.io/)

bower_components

# node-waf configuration

.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)

build/Release

# Dependency directories

node_modules/
.cargo-ok
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)

web_modules/

# TypeScript cache

\*.tsbuildinfo

# Optional npm cache directory

.npm

# Optional eslint cache

.eslintcache

# Optional stylelint cache

.stylelintcache

# Microbundle cache

.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history

.node_repl_history

# Output of 'npm pack'

\*.tgz

# Yarn Integrity file

.yarn-integrity

# dotenv environment variable files

.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)

.cache
.parcel-cache

# Next.js build output

.next
out

# Nuxt.js build / generate output

.nuxt
dist

# Gatsby files

.cache/

# Comment in the public line in if your project uses Gatsby and not Next.js

# https://nextjs.org/blog/next-9-1#public-directory-support

# public

# vuepress build output

.vuepress/dist

# vuepress v2.x temp and cache directory

.temp
.cache

# Docusaurus cache and generated files

.docusaurus

# Serverless directories

.serverless/

# FuseBox cache

.fusebox/

# DynamoDB Local files

.dynamodb/

# TernJS port file

.tern-port

# Stores VSCode versions used for testing VSCode extensions

.vscode-test

# yarn v2

.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.\*

# wrangler project

.dev.vars
32 changes: 16 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ A proof of concept implementation of the performant client-side A/B testing spec

![architecture-diagram](/assets/images/arch.png)

To implement a proof of concept, we will use a CloudFlare Worker to apply the `PRE_UA` transforms. Alternatively, this could be any compute node that can act as a proxy — a connect middleware for an Express.js frontend application, a proxy or an Edge at the boundaries of an Origin, or a CDN with compute capabilities (like CF workers). The proxy should be able to intercept a request to the origin, minimally parse and modify the HTML and apply the transformations.
To implement a proof of concept, we will use a CloudFlare Worker to apply the `PRE_UA` transforms. Alternatively, this could be any compute node that can act as a proxy — a connect middleware for an Express.js frontend application, a proxy or an Edge at the boundaries of an Origin, or a CDN with compute capabilities (like CF workers). The proxy should be able to intercept a request to the origin, minimally parse and modify the HTML and apply the transformations.

Internally, CloudFlare production is [said to](https://blog.cloudflare.com/html-parsing-1/) use [lol-html](https://github.com/cloudflare/lol-html), a low-latency HTML parser that supports streaming. A non-CloudFlare version of this prototype will have to employ a similar mechanism with same performance characteristics for this to function at scale.
Internally, CloudFlare production is [said to](https://blog.cloudflare.com/html-parsing-1/) use [lol-html](https://github.com/cloudflare/lol-html), a low-latency HTML parser that supports streaming. A non-CloudFlare version of this prototype will have to employ a similar mechanism with same performance characteristics for this to function at scale.

For the A/B configuration provider, we will make a pseudo-API out of GitHub gists — this would allow us to test multiple sites and variations without redeploying the CloudFlare worker. This will also mimic the real world dependency of an API call federation off the Edge/CDN/Server. We will use stale-while-revalidate (SWR) policy for the configuration fetch, which should be acceptable for most websites’ real A/B tests.
For the A/B configuration provider, we will make a pseudo-API out of GitHub gists — this would allow us to test multiple sites and variations without redeploying the CloudFlare worker. This will also mimic the real world dependency of an API call federation off the Edge/CDN/Server. We will use stale-while-revalidate (SWR) policy for the configuration fetch, which should be acceptable for most websites’ real A/B tests.

In addition to that, the prototype edge also implements the following essential A/B testing functions:
* Figure out if an A/B test configuration is in place for the requested URI, via consulting the pseudo API.
Expand All @@ -22,7 +22,7 @@ In addition to that, the prototype edge also implements the following essential

## Demo

For a demo, we're going to A/B test ToDoMVC, that is built using React as a client-side SPA.
For a demo, we're going to A/B test ToDoMVC, that is built using React as a client-side SPA.

The following A/B test configuration consists of transformations that create a randomized experiment for 50% of the traffic:
* Change background-color of the page to _`beige`_. Since this is best done on the static markup, we'd like to do have this transformation applied before UA.
Expand All @@ -35,36 +35,36 @@ In addition, we'll also inject correct `<base>` tag to make relative URLs work,
```javascript
const experimentConfigJson = {
"control": {
"url": "https://todomvc.com/examples/react",
"url": "https://todomvc.com/examples/react",
"cache": {
}
},
"variants": [
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"]
]
},
},
{
"weight": 0.5,
"url": "https://todomvc.com/examples/react",
"url": "https://todomvc.com/examples/react",
"transformations": [
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<base href=\"https://todomvc.com/examples/react/\" target=\"_blank\">"],
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
[1 /* PRE_UA */, "head", 3 /* OP_PREPEND */,
"<link rel=\"canonical\" href=\"https://todomvc.com/examples/react/\" />"],
[1 /* PRE_UA */, "head", 4 /* OP_APPEND */,
[1 /* PRE_UA */, "head", 4 /* OP_APPEND */,
"<style>body{background:beige!important}</style>"],
[2 /* ON_UA */, "h1", 0 /* OP_CUSTOM_JS */,
[2 /* ON_UA */, "h1", 0 /* OP_CUSTOM_JS */,
"$.innerHTML=\"a/b test h1\";"],
[2 /* ON_UA */, ".new-todo", 0 /* OP_CUSTOM_JS */,
[2 /* ON_UA */, ".new-todo", 0 /* OP_CUSTOM_JS */,
"$.placeholder=\"What would you like to do today?\""],
[2 /* ON_UA */, ".todo-list>li", 0 /* OP_CUSTOM_JS */,
[2 /* ON_UA */, ".todo-list>li", 0 /* OP_CUSTOM_JS */,
"$.style.color=\"red\""]
]
}
Expand Down
Loading

0 comments on commit 606987a

Please sign in to comment.