Skip to content

Commit 878b6a2

Browse files
committed
feat: add attribution script pkg
1 parent 8706eff commit 878b6a2

14 files changed

Lines changed: 950 additions & 0 deletions

File tree

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# @refref/attribution-script
2+
3+
A lightweight, flexible, and TypeScript-first attribution script for tracking referral codes in web applications.
4+
5+
## Features
6+
7+
- Track referral codes from URL parameters
8+
- Store attribution data in cookies
9+
- Automatic form field population
10+
- Customizable cookie and form settings
11+
- TypeScript support with exported types
12+
- Automatic initialization on DOM ready
13+
- Works with all modern frameworks and plain HTML
14+
15+
## Installation
16+
17+
```bash
18+
npm install @refref/attribution-script
19+
# or
20+
yarn add @refref/attribution-script
21+
# or
22+
pnpm add @refref/attribution-script
23+
```
24+
25+
## Usage
26+
27+
### Basic Setup (TypeScript/ESM)
28+
29+
```typescript
30+
import RefRefAttribution, {
31+
AttributionConfig,
32+
} from "@refref/attribution-script";
33+
34+
const config: AttributionConfig = {
35+
cookieOptions: {
36+
enabled: true,
37+
domain: "yourdomain.com",
38+
path: "/",
39+
maxAge: 90 * 24 * 60 * 60, // 90 days
40+
secure: true,
41+
sameSite: "Lax",
42+
},
43+
formOptions: {
44+
codeField: "rfc", // Name of the hidden field for referral code
45+
},
46+
};
47+
48+
RefRefAttribution.init(config);
49+
```
50+
51+
### Form Integration
52+
53+
Add the `data-refref` attribute to your forms to enable automatic attribution tracking:
54+
55+
```html
56+
<form data-refref>
57+
<!-- Your form fields -->
58+
</form>
59+
```
60+
61+
Attach attribution tracking to all forms with `data-refref`:
62+
63+
```typescript
64+
RefRefAttribution.attachToAll();
65+
```
66+
67+
Or attach to a specific form:
68+
69+
```typescript
70+
const form = document.querySelector("#my-form") as HTMLFormElement;
71+
RefRefAttribution.attachTo(form);
72+
```
73+
74+
#### Injected Form Fields
75+
76+
- The script injects a hidden `<input>` field into each form with `data-refref`.
77+
- The field's `name` is set by the `codeField` option (default: `rfc`).
78+
- The field's `value` is set to the referral code (if present in URL or cookie).
79+
80+
### Getting the Attribution Code
81+
82+
```typescript
83+
const code = RefRefAttribution.getCode(); // Returns code from URL or cookie, or undefined if not found
84+
```
85+
86+
### Global Usage
87+
88+
If loaded in a browser environment, `RefRefAttribution` is also available on `window.RefRefAttribution`:
89+
90+
```javascript
91+
window.RefRefAttribution.getCode();
92+
```
93+
94+
## URL Parameters
95+
96+
The script automatically captures the following URL parameter:
97+
98+
| Parameter | Description |
99+
| --------- | ---------------------- |
100+
| `rfc` | Referral code (string) |
101+
102+
- Only the `rfc` parameter is tracked by default. You can customize the field name in forms via the `codeField` option.
103+
104+
## Cookie Storage
105+
106+
- The referral code is stored in a cookie with the key `refref-unique-code` (unless disabled).
107+
- Cookie options are fully configurable (see below).
108+
109+
## Configuration Options
110+
111+
### Cookie Options
112+
113+
| Option | Type | Default | Description |
114+
| -------- | --------------------------- | --------- | -------------------------------------- |
115+
| enabled | boolean | true | Enable/disable cookie storage |
116+
| domain | string | undefined | The domain to set the cookie on |
117+
| path | string | "/" | The path to set the cookie on |
118+
| maxAge | number | 90d | Cookie expiration in seconds (90 days) |
119+
| secure | boolean | true | Only send the cookie over HTTPS |
120+
| sameSite | "Strict" \| "Lax" \| "None" | "Lax" | Cookie same-site policy |
121+
122+
### Form Options
123+
124+
| Option | Type | Default | Description |
125+
| --------- | ------ | ------- | ------------------------------------------ |
126+
| codeField | string | "rfc" | Name of the hidden field for referral code |
127+
128+
## Automatic Initialization
129+
130+
The script automatically initializes when the DOM is ready. You can also manually initialize it:
131+
132+
```typescript
133+
RefRefAttribution.init();
134+
```
135+
136+
## TypeScript Support
137+
138+
All public APIs and configuration objects are fully typed. You can import types for strict usage:
139+
140+
```typescript
141+
import type {
142+
AttributionConfig,
143+
CookieOptions,
144+
FormOptions,
145+
} from "@refref/attribution-script";
146+
```
147+
148+
## Browser Support
149+
150+
The script is designed to work in browser environments and will log an error if used in a non-browser environment.
151+
152+
## Development
153+
154+
- Build: `pnpm build`
155+
- Test: `pnpm test`
156+
- Lint: `pnpm lint`
157+
- Preview: `pnpm preview`
158+
159+
## License
160+
161+
MIT
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>RefRef Attribution Script Demo</title>
7+
<style>
8+
body {
9+
font-family:
10+
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu,
11+
Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
12+
max-width: 800px;
13+
margin: 0 auto;
14+
padding: 20px;
15+
line-height: 1.6;
16+
}
17+
.form-container {
18+
border: 1px solid #ddd;
19+
padding: 20px;
20+
margin: 20px 0;
21+
border-radius: 8px;
22+
}
23+
form {
24+
display: flex;
25+
flex-direction: column;
26+
gap: 15px;
27+
}
28+
label {
29+
font-weight: 500;
30+
}
31+
input,
32+
button {
33+
padding: 8px;
34+
border: 1px solid #ccc;
35+
border-radius: 4px;
36+
}
37+
button {
38+
background: #0066cc;
39+
color: white;
40+
border: none;
41+
cursor: pointer;
42+
padding: 10px;
43+
}
44+
button:hover {
45+
background: #0052a3;
46+
}
47+
#attribution-data {
48+
background: #f5f5f5;
49+
padding: 15px;
50+
border-radius: 4px;
51+
margin-top: 20px;
52+
}
53+
.hidden-fields {
54+
margin-top: 15px;
55+
font-size: 0.9em;
56+
color: #666;
57+
}
58+
</style>
59+
</head>
60+
<body>
61+
<h1>RefRef Attribution Script Demo</h1>
62+
<p>
63+
This page demonstrates the RefRef attribution script functionality. Try
64+
accessing with different URL parameters:
65+
</p>
66+
<ul>
67+
<li><code>?rfc=TEST123</code> - Sets referral code</li>
68+
</ul>
69+
70+
<div class="form-container">
71+
<h2>Simple Sign Up Form</h2>
72+
<form data-refref id="signup-form">
73+
<div>
74+
<label for="name">Name:</label>
75+
<input type="text" id="name" name="name" required />
76+
</div>
77+
<div>
78+
<label for="email">Email:</label>
79+
<input type="email" id="email" name="email" required />
80+
</div>
81+
<button type="submit">Sign Up</button>
82+
<div class="hidden-fields">
83+
<strong>Hidden Fields:</strong>
84+
<pre id="signup-hidden-fields"></pre>
85+
</div>
86+
</form>
87+
</div>
88+
89+
<div class="form-container">
90+
<h2>Contact Form</h2>
91+
<form data-refref id="contact-form">
92+
<div>
93+
<label for="contact-name">Name:</label>
94+
<input type="text" id="contact-name" name="contact-name" required />
95+
</div>
96+
<div>
97+
<label for="contact-email">Email:</label>
98+
<input
99+
type="email"
100+
id="contact-email"
101+
name="contact-email"
102+
required
103+
/>
104+
</div>
105+
<div>
106+
<label for="message">Message:</label>
107+
<input type="text" id="message" name="message" required />
108+
</div>
109+
<button type="submit">Send Message</button>
110+
<div class="hidden-fields">
111+
<strong>Hidden Fields:</strong>
112+
<pre id="contact-hidden-fields"></pre>
113+
</div>
114+
</form>
115+
</div>
116+
117+
<div id="attribution-data">
118+
<h3>Current Attribution Data</h3>
119+
<pre id="current-data"></pre>
120+
</div>
121+
122+
<script src="../dist/index.js"></script>
123+
<script>
124+
document.addEventListener("DOMContentLoaded", () => {
125+
// Initialize RefRef
126+
const attribution = window.RefRef.init({
127+
cookieOptions: {
128+
domain: window.location.hostname,
129+
maxAge: 30 * 24 * 60 * 60, // 30 days
130+
},
131+
});
132+
133+
// Attach to all forms
134+
attribution.attachToAll();
135+
136+
// Function to display hidden fields
137+
function displayHiddenFields(formId, displayId) {
138+
const form = document.getElementById(formId);
139+
const hiddenFields = Array.from(
140+
form.querySelectorAll('input[type="hidden"]')
141+
)
142+
.map((field) => `${field.name}: ${field.value}`)
143+
.join("\n");
144+
document.getElementById(displayId).textContent = hiddenFields;
145+
}
146+
147+
// Display current attribution data
148+
function updateAttributionDisplay() {
149+
const data = attribution.getAttributionData();
150+
document.getElementById("current-data").textContent = JSON.stringify(
151+
data,
152+
null,
153+
2
154+
);
155+
}
156+
157+
// Update displays initially
158+
displayHiddenFields("signup-form", "signup-hidden-fields");
159+
displayHiddenFields("contact-form", "contact-hidden-fields");
160+
updateAttributionDisplay();
161+
162+
// Handle form submissions
163+
["signup-form", "contact-form"].forEach((formId) => {
164+
document.getElementById(formId).addEventListener("submit", (e) => {
165+
e.preventDefault();
166+
const formData = new FormData(e.target);
167+
const data = {};
168+
for (let [key, value] of formData.entries()) {
169+
data[key] = value;
170+
}
171+
console.log(`${formId} submission:`, data);
172+
alert("Form submitted! Check console for data");
173+
});
174+
});
175+
176+
// Update displays when attribution data changes
177+
setInterval(() => {
178+
displayHiddenFields("signup-form", "signup-hidden-fields");
179+
displayHiddenFields("contact-form", "contact-hidden-fields");
180+
updateAttributionDisplay();
181+
}, 1000);
182+
});
183+
</script>
184+
</body>
185+
</html>

0 commit comments

Comments
 (0)