@kharon-labs/browser 0.0.0-stage → 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kharon Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,99 @@
1
- # Temporary Holding Version
1
+ # @kharon-labs/browser
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Personalize parts of any website with Kharon Labs. Mark the regions you want to personalize, keep
4
+ your own copy as the defaults, and the package swaps in the variant Kharon picks for each visitor,
5
+ then measures it against a holdout.
6
+
7
+ ```sh
8
+ npm install @kharon-labs/browser
9
+ ```
10
+
11
+ On a site without a bundler, use the script-tag build at `https://kharonlabs.com/sdk/v1.js`. On a
12
+ React site (including Next.js and React Native Web), use
13
+ [`@kharon-labs/react`](https://www.npmjs.com/package/@kharon-labs/react), which is built on this package.
14
+
15
+ ## Plain HTML
16
+
17
+ ```html
18
+ <head>
19
+ <!-- Optional: hides slots (never the page) until the swap, for at most 500 ms. -->
20
+ <style id="kharon-af">[data-ge-slot]{visibility:hidden!important}</style>
21
+ <script>window.__kharonAf=Date.now();setTimeout(function(){var s=document.getElementById("kharon-af");s&&s.parentNode.removeChild(s)},500)</script>
22
+ <script src="https://kharonlabs.com/sdk/v1.js" data-key="ge_pk_…" async></script>
23
+ </head>
24
+ <body>
25
+ <section data-ge-slot="hero">
26
+ <h1 data-ge-field="headline">Grow faster</h1>
27
+ <a data-ge-field="ctaLabel" data-ge-attr="href:ctaUrl" href="/demo">Book a demo</a>
28
+ </section>
29
+ </body>
30
+ ```
31
+
32
+ The anti-flicker snippet is also exported as `ANTI_FLICKER_SNIPPET`.
33
+
34
+ ## Bundlers
35
+
36
+ ```ts
37
+ import { init, setConsent, conversion } from "@kharon-labs/browser";
38
+
39
+ init({ key: "ge_pk_…" });
40
+ myCmp.onChange((ok) => setConsent(ok ? "granted" : "denied"));
41
+ conversion("demo_booked");
42
+ ```
43
+
44
+ | Function | What it does |
45
+ | --- | --- |
46
+ | `init({ key, apiBase?, consent?, timeoutMs?, pageId?, cookieDomain? })` | Resolves the page's slots once the DOM is ready and starts measuring if allowed. |
47
+ | `setConsent("granted" \| "denied" \| "unknown")` | Forward your consent tool's answer. Safe to call before `init`. |
48
+ | `conversion(goal, value?)` | Records a goal. Forms with `data-ge-goal="…"` record theirs on submit. |
49
+ | `signup()` | Records a `signup` conversion. Prefer `recordSignup` in `@kharon-labs/server`, which counts only signups that succeeded. |
50
+ | `refresh()` | Re-scans and re-resolves, e.g. after a client-side route change. |
51
+
52
+ ## Slots
53
+
54
+ - `data-ge-slot="id"` marks a slot; the id matches a slot in the console. Repeat an id to show the
55
+ same decision in several places.
56
+ - `data-ge-field="name"` writes a content field as text (`textContent`), never as HTML.
57
+ - `data-ge-attr="href:ctaUrl, title:ctaTitle"` writes fields into attributes. URL attributes accept
58
+ only `http(s)` and relative links; event handlers and `style` are never written.
59
+ - The markup's own text is the default. A field the variant doesn't set keeps it, and a slot the
60
+ console doesn't know yet keeps all of it.
61
+
62
+ ## Single-page apps
63
+
64
+ The package scans the page once, when the DOM is ready. After a client-side route change that mounts
65
+ new slots, call `refresh()`. On React Native Web, a `dataSet={{ geSlot: "hero" }}` prop renders
66
+ `data-ge-slot="hero"`; React Native's types don't declare `dataSet`, so add it with a module
67
+ augmentation (or use `@kharon-labs/react`, which needs no DOM attributes).
68
+
69
+ ## Guarantees
70
+
71
+ - **Defaults always render.** If we answer late (after `timeoutMs`, 500 ms), fail or are down, the
72
+ page keeps its defaults and slots are revealed. The package never retries.
73
+ - **Consent is decided by our server**, by the visitor's country and choice (opt-in in the EEA, UK
74
+ and Switzerland, GPC honored). Until it allows storing the visitor, the package writes no cookie
75
+ or storage and sends no analytics.
76
+ - **Holdouts.** Held-out visitors see the defaults; their impressions record the variant withheld.
77
+ - **Acquisition.** A session's first page view records how it arrived: `utm_*` tags, which ad
78
+ network's click id was present (never the id), a Kharon campaign link, and the referring site's
79
+ host. Never the URL's path or other parameters.
80
+
81
+ The visitor id is a first-party `ge_vid` cookie on your domain (one year; Safari caps script-set
82
+ cookies at seven days, `@kharon-labs/server` avoids that). A denial deletes it.
83
+
84
+ ## Size and support
85
+
86
+ About 4 KB gzip, no dependencies. ES2020 browsers; without `fetch` the page keeps its defaults, and
87
+ without `IntersectionObserver` impressions aren't recorded.
88
+
89
+ ## Your privacy policy
90
+
91
+ Adding Kharon Labs sets a first-party cookie and sends visit data to us, so your
92
+ privacy policy should say so. A paragraph to adapt (not legal advice):
93
+
94
+ > We use Kharon Labs to tailor parts of this website to the organization a visit comes from and to
95
+ > measure whether that helps. Kharon Labs processes your IP address, browser details, the page you
96
+ > visit and how you arrived, and infers your organization (not you personally). With your consent
97
+ > where required, it stores a random visitor ID in a first-party cookie (`ge_vid`) and records page
98
+ > views and interactions. See https://kharonlabs.com/privacy for what Kharon Labs collects and how
99
+ > long it keeps it.