@varianceab/html-sdk 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,9 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Variance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # VarianceExperiment HTML SDK
2
+
3
+ A lightweight, client-side HTML SDK for rendering A/B test variants and sending experiment impressions to analytics. Supports **preview mode**, **shared caching**, and works with any `<variance-experiment>` / `<variance-variant>` setup.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Installation](#installation)
10
+ - [Usage](#usage)
11
+ - [Basic Example](#basic-example)
12
+ - [Preview Mode](#preview-mode)
13
+ - [API](#api)
14
+ - [Attributes](#attributes)
15
+ - [Methods](#methods)
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ Install via pnpm:
22
+
23
+ ```bash
24
+ pnpm add @varianceab/html-sdk
25
+ ```
26
+
27
+ Or include directly in your project:
28
+
29
+ ```html
30
+ <script src="https://unpkg.com/@varianceab/html-sdk"></script>
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ### Basic example
36
+
37
+ ```html
38
+ <variance-experiment id="exp_123" client-id="client_123">
39
+ <variance-variant id="var_123">
40
+ <h1>Welcome, visitor!</h1>
41
+ </variance-variant>
42
+ <variance-variant id="var_456">
43
+ <h1>Try our new feature!</h1>
44
+ </variance-variant>
45
+ </variance-experiment>
46
+ ```
47
+
48
+ - The component fetches the experiment data from the endpoint.
49
+ - The correct variant is automatically rendered.
50
+ - An analytics impression is sent automatically.
51
+
52
+ ### Preview Mode
53
+
54
+ Preview mode allows rendering a specific variant without sending analytics events or fetching experiment data.
55
+
56
+ ```html
57
+ <variance-experiment
58
+ id="exp_456"
59
+ client-id="client_123"
60
+ preview-mode="true"
61
+ variant-to-preview="var_456"
62
+ >
63
+ <variance-variant id="var_123">
64
+ <h1>Unseen variant</h1>
65
+ </variance-variant>
66
+ <variance-variant id="var_456">
67
+ <h1>Previewed variant</h1>
68
+ </variance-variant>
69
+ </variance-experiment>
70
+ ```
71
+
72
+ - `preview-mode="true"` requires `variant-to-preview` to be set.
73
+ - No network request is made in preview mode.
74
+ - No analytics impressions are sent.
75
+
76
+ ## API
77
+
78
+ ### Attributes
79
+
80
+ | Attribute | Type | Description |
81
+ | -------------------- | ------- | ----------------------------------------------------------------- |
82
+ | `id` | string | The experiment ID. |
83
+ | `client-id` | string | The client ID for fetching experiments. |
84
+ | `preview-mode` | boolean | If `true`, renders a specific variant for preview purposes. |
85
+ | `variant-to-preview` | string | The variant ID to preview (required when `preview-mode` is true). |
86
+
87
+ ### Methods
88
+
89
+ `sendImpression(experimentId: string, variantId: string)` – Sends an analytics event manually. Usually called automatically.
90
+
91
+ All other rendering is handled automatically; variants are rendered based on experiment data or preview mode.
92
+
@@ -0,0 +1,22 @@
1
+ export declare class VarianceExperiment extends HTMLElement {
2
+ static experimentCachePromise: Promise<any[]> | null;
3
+ static get observedAttributes(): string[];
4
+ experiments: any[];
5
+ hasSent: boolean;
6
+ connectedCallback(): void;
7
+ attributeChangedCallback(name: string): void;
8
+ get clientId(): string;
9
+ get previewMode(): boolean;
10
+ get experimentId(): string;
11
+ get variantToPreview(): string | null;
12
+ private ensureValidPreview;
13
+ fetchExperiments(): Promise<void>;
14
+ fetchAllExperiments(): Promise<any[]>;
15
+ sendImpression(experimentId: string, variantId: string): void;
16
+ render(): void;
17
+ private renderPreview;
18
+ private renderVariant;
19
+ private clear;
20
+ }
21
+ export declare class VarianceVariant extends HTMLElement {
22
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a="https://experiment-worker.christopherbeagles.workers.dev/sdk",o=1500;class i extends HTMLElement{constructor(){super(...arguments),this.experiments=[],this.hasSent=!1}static{this.experimentCachePromise=null}static get observedAttributes(){return["id","client-id","preview-mode","variant-to-preview"]}connectedCallback(){this.ensureValidPreview()||(this.previewMode?this.render():this.fetchExperiments())}attributeChangedCallback(e){if(!this.ensureValidPreview()){if(this.previewMode){this.render();return}e==="client-id"&&this.fetchExperiments()}}get clientId(){return this.getAttribute("client-id")??""}get previewMode(){return this.getAttribute("preview-mode")==="true"}get experimentId(){return this.getAttribute("id")??""}get variantToPreview(){return this.getAttribute("variant-to-preview")}ensureValidPreview(){if(!this.previewMode)return!1;const e=!this.variantToPreview;return e&&console.error('[variance-experiment] preview-mode="true" requires variant-to-preview'),e}async fetchExperiments(){if(this.previewMode){this.render();return}i.experimentCachePromise||(i.experimentCachePromise=this.fetchAllExperiments());try{this.experiments=await i.experimentCachePromise}catch(e){console.error(e),this.experiments=[]}finally{this.render()}}async fetchAllExperiments(){const e=new URL(a);e.searchParams.set("clientId",this.clientId);const t=new AbortController,r=setTimeout(()=>t.abort(),o);try{return await(await fetch(e.toString(),{signal:t.signal})).json()}catch(n){return console.error(n),[]}finally{clearTimeout(r)}}sendImpression(e,t){if(this.hasSent)return;this.hasSent=!0;const r={event:"experiment_impression",experiment_id:e,variant_id:t};window.dataLayer=window.dataLayer||[],window.gtag=window.gtag||function(){window.dataLayer.push(arguments)},window.gtag("event","experiment_impression",r),window.dataLayer.push(r)}render(){if(this.previewMode)return this.renderPreview(this.variantToPreview);const e=this.experiments.find(r=>r.id===this.experimentId);if(!e)return this.clear();const t=e?.variant?.id??null;t&&this.sendImpression(e.id,t),this.renderVariant(t)}renderPreview(e){this.hasSent=!0,this.renderVariant(e)}renderVariant(e){if(!e)return this.clear();const t=Array.from(this.children).find(r=>r instanceof HTMLElement&&r.tagName.toLowerCase()==="variance-variant"&&r.getAttribute("id")===e);this.innerHTML="",t&&this.appendChild(t.cloneNode(!0)),this.style.display="block"}clear(){this.innerHTML="",this.style.display="none"}}class s extends HTMLElement{}customElements.define("variance-experiment",i);customElements.define("variance-variant",s);exports.VarianceExperiment=i;exports.VarianceVariant=s;
2
+ //# sourceMappingURL=variance-html-sdk.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variance-html-sdk.cjs.js","sources":["../src/variance.ts"],"sourcesContent":["const ENDPOINT = \"https://experiment-worker.christopherbeagles.workers.dev/sdk\";\nconst TIMEOUT = 1500;\n\nexport class VarianceExperiment extends HTMLElement {\n static experimentCachePromise: Promise<any[]> | null = null;\n\n static get observedAttributes() {\n return [\"id\", \"client-id\", \"preview-mode\", \"variant-to-preview\"];\n }\n\n experiments: any[] = [];\n hasSent = false;\n\n connectedCallback() {\n if (this.ensureValidPreview()) return;\n\n this.previewMode ? this.render() : this.fetchExperiments();\n }\n\n attributeChangedCallback(name: string) {\n if (this.ensureValidPreview()) return;\n\n if (this.previewMode) {\n this.render();\n return;\n }\n\n if (name === \"client-id\") {\n this.fetchExperiments();\n }\n }\n\n /* --- Getters --- */\n get clientId() {\n return this.getAttribute(\"client-id\") ?? \"\";\n }\n get previewMode() {\n return this.getAttribute(\"preview-mode\") === \"true\";\n }\n get experimentId() {\n return this.getAttribute(\"id\") ?? \"\";\n }\n get variantToPreview() {\n return this.getAttribute(\"variant-to-preview\");\n }\n\n /* --- Validation --- */\n private ensureValidPreview() {\n if (!this.previewMode) return false;\n\n const missing = !this.variantToPreview;\n if (missing) {\n console.error(\n `[variance-experiment] preview-mode=\"true\" requires variant-to-preview`,\n );\n }\n return missing;\n }\n\n /* --- Fetching --- */\n async fetchExperiments() {\n if (this.previewMode) {\n this.render();\n return;\n }\n\n if (!VarianceExperiment.experimentCachePromise) {\n VarianceExperiment.experimentCachePromise = this.fetchAllExperiments();\n }\n\n try {\n this.experiments = await VarianceExperiment.experimentCachePromise;\n } catch (err) {\n console.error(err);\n this.experiments = [];\n } finally {\n this.render();\n }\n }\n\n async fetchAllExperiments(): Promise<any[]> {\n const url = new URL(ENDPOINT);\n url.searchParams.set(\"clientId\", this.clientId);\n\n const controller = new AbortController();\n const timeoutHandle = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const res = await fetch(url.toString(), { signal: controller.signal });\n return await res.json();\n } catch (err) {\n console.error(err);\n return [];\n } finally {\n clearTimeout(timeoutHandle);\n }\n }\n\n /* --- Analytics --- */\n sendImpression(experimentId: string, variantId: string) {\n if (this.hasSent) return;\n this.hasSent = true;\n\n const payload = {\n event: \"experiment_impression\",\n experiment_id: experimentId,\n variant_id: variantId,\n };\n\n window.dataLayer = window.dataLayer || [];\n window.gtag =\n window.gtag ||\n function () {\n window.dataLayer.push(arguments);\n };\n\n window.gtag(\"event\", \"experiment_impression\", payload);\n window.dataLayer.push(payload);\n }\n\n /* --- Rendering --- */\n render() {\n if (this.previewMode) {\n return this.renderPreview(this.variantToPreview!);\n }\n\n const experiment = this.experiments.find((e) => e.id === this.experimentId);\n if (!experiment) {\n return this.clear();\n }\n\n const variantId = experiment?.variant?.id ?? null;\n if (variantId) {\n this.sendImpression(experiment.id, variantId);\n }\n\n this.renderVariant(variantId);\n }\n\n private renderPreview(variantId: string) {\n this.hasSent = true; // block impressions\n this.renderVariant(variantId);\n }\n\n private renderVariant(variantId: string | null) {\n if (!variantId) return this.clear();\n\n // Capture original children before wiping\n const child = Array.from(this.children).find(\n (el) =>\n el instanceof HTMLElement &&\n el.tagName.toLowerCase() === \"variance-variant\" &&\n el.getAttribute(\"id\") === variantId,\n );\n\n this.innerHTML = \"\";\n\n if (child) {\n this.appendChild(child.cloneNode(true));\n }\n\n this.style.display = \"block\";\n }\n\n private clear() {\n this.innerHTML = \"\";\n this.style.display = \"none\";\n }\n}\n\nexport class VarianceVariant extends HTMLElement {}\n\ncustomElements.define(\"variance-experiment\", VarianceExperiment);\ncustomElements.define(\"variance-variant\", VarianceVariant);\n"],"names":["ENDPOINT","TIMEOUT","VarianceExperiment","name","missing","err","url","controller","timeoutHandle","experimentId","variantId","payload","experiment","e","child","el","VarianceVariant"],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ const s = "https://experiment-worker.christopherbeagles.workers.dev/sdk";
2
+ class i extends HTMLElement {
3
+ constructor() {
4
+ super(...arguments), this.experiments = [], this.hasSent = !1;
5
+ }
6
+ static {
7
+ this.experimentCachePromise = null;
8
+ }
9
+ static get observedAttributes() {
10
+ return ["id", "client-id", "preview-mode", "variant-to-preview"];
11
+ }
12
+ connectedCallback() {
13
+ this.ensureValidPreview() || (this.previewMode ? this.render() : this.fetchExperiments());
14
+ }
15
+ attributeChangedCallback(e) {
16
+ if (!this.ensureValidPreview()) {
17
+ if (this.previewMode) {
18
+ this.render();
19
+ return;
20
+ }
21
+ e === "client-id" && this.fetchExperiments();
22
+ }
23
+ }
24
+ /* --- Getters --- */
25
+ get clientId() {
26
+ return this.getAttribute("client-id") ?? "";
27
+ }
28
+ get previewMode() {
29
+ return this.getAttribute("preview-mode") === "true";
30
+ }
31
+ get experimentId() {
32
+ return this.getAttribute("id") ?? "";
33
+ }
34
+ get variantToPreview() {
35
+ return this.getAttribute("variant-to-preview");
36
+ }
37
+ /* --- Validation --- */
38
+ ensureValidPreview() {
39
+ if (!this.previewMode) return !1;
40
+ const e = !this.variantToPreview;
41
+ return e && console.error(
42
+ '[variance-experiment] preview-mode="true" requires variant-to-preview'
43
+ ), e;
44
+ }
45
+ /* --- Fetching --- */
46
+ async fetchExperiments() {
47
+ if (this.previewMode) {
48
+ this.render();
49
+ return;
50
+ }
51
+ i.experimentCachePromise || (i.experimentCachePromise = this.fetchAllExperiments());
52
+ try {
53
+ this.experiments = await i.experimentCachePromise;
54
+ } catch (e) {
55
+ console.error(e), this.experiments = [];
56
+ } finally {
57
+ this.render();
58
+ }
59
+ }
60
+ async fetchAllExperiments() {
61
+ const e = new URL(s);
62
+ e.searchParams.set("clientId", this.clientId);
63
+ const t = new AbortController(), r = setTimeout(() => t.abort(), 1500);
64
+ try {
65
+ return await (await fetch(e.toString(), { signal: t.signal })).json();
66
+ } catch (n) {
67
+ return console.error(n), [];
68
+ } finally {
69
+ clearTimeout(r);
70
+ }
71
+ }
72
+ /* --- Analytics --- */
73
+ sendImpression(e, t) {
74
+ if (this.hasSent) return;
75
+ this.hasSent = !0;
76
+ const r = {
77
+ event: "experiment_impression",
78
+ experiment_id: e,
79
+ variant_id: t
80
+ };
81
+ window.dataLayer = window.dataLayer || [], window.gtag = window.gtag || function() {
82
+ window.dataLayer.push(arguments);
83
+ }, window.gtag("event", "experiment_impression", r), window.dataLayer.push(r);
84
+ }
85
+ /* --- Rendering --- */
86
+ render() {
87
+ if (this.previewMode)
88
+ return this.renderPreview(this.variantToPreview);
89
+ const e = this.experiments.find((r) => r.id === this.experimentId);
90
+ if (!e)
91
+ return this.clear();
92
+ const t = e?.variant?.id ?? null;
93
+ t && this.sendImpression(e.id, t), this.renderVariant(t);
94
+ }
95
+ renderPreview(e) {
96
+ this.hasSent = !0, this.renderVariant(e);
97
+ }
98
+ renderVariant(e) {
99
+ if (!e) return this.clear();
100
+ const t = Array.from(this.children).find(
101
+ (r) => r instanceof HTMLElement && r.tagName.toLowerCase() === "variance-variant" && r.getAttribute("id") === e
102
+ );
103
+ this.innerHTML = "", t && this.appendChild(t.cloneNode(!0)), this.style.display = "block";
104
+ }
105
+ clear() {
106
+ this.innerHTML = "", this.style.display = "none";
107
+ }
108
+ }
109
+ class a extends HTMLElement {
110
+ }
111
+ customElements.define("variance-experiment", i);
112
+ customElements.define("variance-variant", a);
113
+ export {
114
+ i as VarianceExperiment,
115
+ a as VarianceVariant
116
+ };
117
+ //# sourceMappingURL=variance-html-sdk.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variance-html-sdk.es.js","sources":["../src/variance.ts"],"sourcesContent":["const ENDPOINT = \"https://experiment-worker.christopherbeagles.workers.dev/sdk\";\nconst TIMEOUT = 1500;\n\nexport class VarianceExperiment extends HTMLElement {\n static experimentCachePromise: Promise<any[]> | null = null;\n\n static get observedAttributes() {\n return [\"id\", \"client-id\", \"preview-mode\", \"variant-to-preview\"];\n }\n\n experiments: any[] = [];\n hasSent = false;\n\n connectedCallback() {\n if (this.ensureValidPreview()) return;\n\n this.previewMode ? this.render() : this.fetchExperiments();\n }\n\n attributeChangedCallback(name: string) {\n if (this.ensureValidPreview()) return;\n\n if (this.previewMode) {\n this.render();\n return;\n }\n\n if (name === \"client-id\") {\n this.fetchExperiments();\n }\n }\n\n /* --- Getters --- */\n get clientId() {\n return this.getAttribute(\"client-id\") ?? \"\";\n }\n get previewMode() {\n return this.getAttribute(\"preview-mode\") === \"true\";\n }\n get experimentId() {\n return this.getAttribute(\"id\") ?? \"\";\n }\n get variantToPreview() {\n return this.getAttribute(\"variant-to-preview\");\n }\n\n /* --- Validation --- */\n private ensureValidPreview() {\n if (!this.previewMode) return false;\n\n const missing = !this.variantToPreview;\n if (missing) {\n console.error(\n `[variance-experiment] preview-mode=\"true\" requires variant-to-preview`,\n );\n }\n return missing;\n }\n\n /* --- Fetching --- */\n async fetchExperiments() {\n if (this.previewMode) {\n this.render();\n return;\n }\n\n if (!VarianceExperiment.experimentCachePromise) {\n VarianceExperiment.experimentCachePromise = this.fetchAllExperiments();\n }\n\n try {\n this.experiments = await VarianceExperiment.experimentCachePromise;\n } catch (err) {\n console.error(err);\n this.experiments = [];\n } finally {\n this.render();\n }\n }\n\n async fetchAllExperiments(): Promise<any[]> {\n const url = new URL(ENDPOINT);\n url.searchParams.set(\"clientId\", this.clientId);\n\n const controller = new AbortController();\n const timeoutHandle = setTimeout(() => controller.abort(), TIMEOUT);\n\n try {\n const res = await fetch(url.toString(), { signal: controller.signal });\n return await res.json();\n } catch (err) {\n console.error(err);\n return [];\n } finally {\n clearTimeout(timeoutHandle);\n }\n }\n\n /* --- Analytics --- */\n sendImpression(experimentId: string, variantId: string) {\n if (this.hasSent) return;\n this.hasSent = true;\n\n const payload = {\n event: \"experiment_impression\",\n experiment_id: experimentId,\n variant_id: variantId,\n };\n\n window.dataLayer = window.dataLayer || [];\n window.gtag =\n window.gtag ||\n function () {\n window.dataLayer.push(arguments);\n };\n\n window.gtag(\"event\", \"experiment_impression\", payload);\n window.dataLayer.push(payload);\n }\n\n /* --- Rendering --- */\n render() {\n if (this.previewMode) {\n return this.renderPreview(this.variantToPreview!);\n }\n\n const experiment = this.experiments.find((e) => e.id === this.experimentId);\n if (!experiment) {\n return this.clear();\n }\n\n const variantId = experiment?.variant?.id ?? null;\n if (variantId) {\n this.sendImpression(experiment.id, variantId);\n }\n\n this.renderVariant(variantId);\n }\n\n private renderPreview(variantId: string) {\n this.hasSent = true; // block impressions\n this.renderVariant(variantId);\n }\n\n private renderVariant(variantId: string | null) {\n if (!variantId) return this.clear();\n\n // Capture original children before wiping\n const child = Array.from(this.children).find(\n (el) =>\n el instanceof HTMLElement &&\n el.tagName.toLowerCase() === \"variance-variant\" &&\n el.getAttribute(\"id\") === variantId,\n );\n\n this.innerHTML = \"\";\n\n if (child) {\n this.appendChild(child.cloneNode(true));\n }\n\n this.style.display = \"block\";\n }\n\n private clear() {\n this.innerHTML = \"\";\n this.style.display = \"none\";\n }\n}\n\nexport class VarianceVariant extends HTMLElement {}\n\ncustomElements.define(\"variance-experiment\", VarianceExperiment);\ncustomElements.define(\"variance-variant\", VarianceVariant);\n"],"names":["ENDPOINT","VarianceExperiment","name","missing","err","url","controller","timeoutHandle","experimentId","variantId","payload","experiment","e","child","el","VarianceVariant"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@varianceab/html-sdk",
3
+ "version": "0.0.1",
4
+ "private": false,
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://variance.tech",
8
+ "main": "dist/variance-html-sdk.cjs.js",
9
+ "module": "dist/variance-html-sdk.es.js",
10
+ "types": "dist/types/variance.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/variance-html-sdk.es.js",
14
+ "require": "./dist/variance-html-sdk.cjs.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "devDependencies": {
24
+ "eslint": "^9.37.0",
25
+ "happy-dom": "^20.0.10",
26
+ "typescript": "^5.9.3",
27
+ "vite": "^7.1.9",
28
+ "vitest": "^3.2.4"
29
+ },
30
+ "scripts": {
31
+ "build": "vite build && tsc --emitDeclarationOnly --outDir dist/types",
32
+ "build:watch": "tsc --watch",
33
+ "lint": "eslint src --ext .ts,.tsx",
34
+ "demo": "vite --config vite.config.demo.ts",
35
+ "test": "vitest"
36
+ }
37
+ }