@openpartner/sdk 0.2.0 → 0.3.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.
@@ -4,9 +4,9 @@ export { O as OpenPartnerError } from './shared-dh1vlldf.cjs';
4
4
  /**
5
5
  * Browser SDK — runs inside a merchant's web app. Captures the `cref`
6
6
  * attribution token from landing-page URLs and the first-party cookie
7
- * the click router sets, stashes it in localStorage so it survives
8
- * Safari / ITP cookie expiry and SPA navigations, and stitches to the
9
- * authenticated user on signup or login.
7
+ * the click router sets, stashes it in localStorage so it survives SPA
8
+ * navigations and later visits, and stitches to the authenticated user on
9
+ * signup or login.
10
10
  *
11
11
  * Typical integration:
12
12
  *
@@ -24,6 +24,25 @@ export { O as OpenPartnerError } from './shared-dh1vlldf.cjs';
24
24
  * to OpenPartner then needs no code for those checkouts. `stripe: false`
25
25
  * turns it off.
26
26
  *
27
+ * How long a referral lasts. Safari deletes storage that a page's own
28
+ * scripts wrote (localStorage, and cookies set through document.cookie)
29
+ * after 7 days without a visit, so a visitor who clicks, leaves for a
30
+ * fortnight and comes back to buy without signing up first arrives with
31
+ * nothing. A cookie the brand's own server sets on the landing response
32
+ * isn't capped that way: `referralCookieHeader` in `@openpartner/sdk/server`
33
+ * writes one, and this SDK reads it back (`op_ref`), on every subdomain it
34
+ * covers. Once identify() has run, the stitch lives server-side and no
35
+ * browser storage matters.
36
+ *
37
+ * Consent. `consent: 'pending'` holds the referral in memory until
38
+ * setConsent('granted'): nothing is read from or written to storage or
39
+ * cookies, no Stripe link is tagged and nothing is sent until then.
40
+ *
41
+ * Landing report. When a click first arrives from the URL the SDK tells
42
+ * OpenPartner (POST /attribution/landing), so Admin → Tracking health can
43
+ * count the clicks that never reached a page running it. `reportLanding:
44
+ * false` turns it off.
45
+ *
27
46
  * No bundler? The same SDK ships as a script tag that starts itself
28
47
  * (auto.ts), served at /op.js by an OpenPartner portal.
29
48
  */
@@ -33,6 +52,8 @@ interface StripeLinkingConfig {
33
52
  * in Stripe (e.g. "pay.yourbrand.com"). buy.stripe.com is always on. */
34
53
  linkHosts?: string[];
35
54
  }
55
+ /** Whether this visitor agreed to referral tracking. */
56
+ type ConsentState = 'granted' | 'pending' | 'denied';
36
57
  interface OpenPartnerConfig {
37
58
  /** Base URL of your OpenPartner API, e.g. "https://openpartner.example.com". */
38
59
  apiUrl: string;
@@ -53,6 +74,11 @@ interface OpenPartnerConfig {
53
74
  * (`client_reference_id: req.cookies.op_ref`). Off by default; the
54
75
  * script-tag build turns it on. `domain` shares it across subdomains
55
76
  * (".yourbrand.com") when visitors land on one and check out on another.
77
+ *
78
+ * The SDK reads this cookie back whether or not it writes it (`op_ref`
79
+ * unless `name` says otherwise): that's how a page on another subdomain,
80
+ * or a visit after Safari cleared localStorage, finds the referral, and
81
+ * how one your server set (`referralCookieHeader`) reaches identify().
56
82
  */
57
83
  referralCookie?: false | {
58
84
  name?: string;
@@ -60,17 +86,46 @@ interface OpenPartnerConfig {
60
86
  days?: number;
61
87
  };
62
88
  /**
63
- * Called when identify() fails. Default: log to console. Pass a noop
64
- * to suppress entirely or hook into your observability stack.
89
+ * Whether the visitor has agreed to referral tracking. Default
90
+ * `'granted'`: the SDK stores the referral as soon as it loads. Behind a
91
+ * consent banner, start at `'pending'` and call `setConsent()` from its
92
+ * callback. Until then the referral from the URL is held in memory only,
93
+ * so a visitor who agrees on the landing page is still credited.
94
+ * `'denied'` uses nothing.
95
+ */
96
+ consent?: ConsentState;
97
+ /**
98
+ * Tell OpenPartner when a partner click arrives on your site, so Admin →
99
+ * Tracking health can show clicks lost between the partner link and your
100
+ * page. Sends the click id once per click, nothing about the visitor.
101
+ * Default on; `false` turns it off.
102
+ */
103
+ reportLanding?: boolean;
104
+ /**
105
+ * Called when a call to OpenPartner fails: identify(), or a landing
106
+ * report OpenPartner answered with `click_not_found` (which usually means
107
+ * `apiUrl` names another program; other landing failures are dropped).
108
+ * Default: log to console. Pass a noop to suppress entirely or
109
+ * hook into your observability stack.
65
110
  */
66
111
  onError?: (err: unknown) => void;
67
112
  }
68
113
  declare class OpenPartner {
69
114
  private config;
70
115
  private observer;
116
+ private consent;
117
+ /** The URL's referral while consent is pending, held in memory only. */
118
+ private held;
119
+ /** This page's referral, kept here too: where localStorage throws it is
120
+ * the only copy until the cookie is written. */
121
+ private current;
122
+ /** The Stripe links and embeds this SDK tagged, and with what, so a
123
+ * withdrawn consent can take the tags back off. */
124
+ private tagged;
71
125
  private constructor();
72
126
  static init(config: OpenPartnerConfig): OpenPartner;
73
- /** The cref we're holding onto, if any. Useful for debugging. */
127
+ /** The cref we're holding onto, if any. Useful for debugging. Null until
128
+ * consent is granted. */
74
129
  getCref(): string | null;
75
130
  /**
76
131
  * Captured referral token for this visitor. Pass to Stripe Checkout
@@ -82,11 +137,22 @@ declare class OpenPartner {
82
137
  * // ...
83
138
  * });
84
139
  *
85
- * Returns null if the visitor didn't arrive via a partner link.
140
+ * Returns null if the visitor didn't arrive via a partner link, or
141
+ * hasn't agreed to tracking yet.
86
142
  */
87
143
  getReferral(): string | null;
88
- /** Explicitly discard the captured cref. */
144
+ /** Explicitly discard the captured cref, the referral cookie, and the
145
+ * references this SDK added to Stripe links and embeds. */
89
146
  clear(): void;
147
+ /**
148
+ * Record the visitor's answer to your consent banner. `'granted'` stores
149
+ * the referral (the one held since the page loaded, or one already
150
+ * stored), starts tagging Stripe links and sends the landing report.
151
+ * `'denied'` stops all of it, takes the references it added off Stripe
152
+ * links and embeds, and deletes what was stored. Calling it again is
153
+ * safe.
154
+ */
155
+ setConsent(state: 'granted' | 'denied'): void;
90
156
  /**
91
157
  * Add the referral to every Stripe Payment Link and Pricing Table / Buy
92
158
  * Button embed under `root` that doesn't carry a reference already.
@@ -95,9 +161,15 @@ declare class OpenPartner {
95
161
  * many it changed.
96
162
  */
97
163
  linkStripe(root?: ParentNode): number;
164
+ private remember;
165
+ /** Take back the references this SDK added. One the brand set, or one
166
+ * changed since, stays. */
167
+ private untagStripe;
98
168
  /** Stop watching the page for new Stripe links and embeds. */
99
169
  stopLinkingStripe(): void;
100
170
  private stripeLinkHosts;
171
+ /** Everything the SDK does once it may: store, mirror, link, report. */
172
+ private start;
101
173
  /**
102
174
  * Link what's on the page now, then anything added or re-pointed later
103
175
  * (SPAs, Stripe embeds mounted on demand). Only with a referral: a visitor
@@ -109,27 +181,41 @@ declare class OpenPartner {
109
181
  * element is parsed, before Stripe upgrades it.
110
182
  */
111
183
  private watchStripe;
184
+ /** The cref on the landing URL, if any. */
185
+ private queryCref;
112
186
  /**
113
- * Capture the cref from query string or cookie and stash in
114
- * localStorage. Runs on init so the value survives SPA navigations,
115
- * ITP cookie expiry, and subsequent page loads.
187
+ * Capture the cref from the URL (or the one held while consent was
188
+ * pending), else the router's cookie, else the referral cookie, and
189
+ * stash it in localStorage so it survives SPA navigations and later
190
+ * page loads. Returns the cref when it's a click arriving now, one this
191
+ * browser didn't have yet, for the landing report.
116
192
  */
117
193
  private captureCref;
118
- /** Mirror the referral into the brand's own first-party cookie (when
119
- * configured), refreshed on every page load that has one. */
194
+ private referralCookieName;
195
+ /**
196
+ * Mirror the referral into the brand's own first-party cookie (when
197
+ * configured). Left alone when it already holds this referral: in Safari
198
+ * rewriting it from script would trade a server-set cookie's full
199
+ * lifetime for the 7-day cap. A new click overwrites it.
200
+ */
120
201
  private writeReferralCookie;
121
202
  private readCookie;
203
+ private storageGet;
204
+ private storageSet;
205
+ private storageRemove;
206
+ /** Tell OpenPartner the click arrived. Best-effort, once per click. */
207
+ private reportLanding;
122
208
  /**
123
209
  * Stitch the current captured click to an authenticated user. Call
124
210
  * this on login, signup, or anywhere the user becomes known.
125
211
  *
126
- * No-op if no cref was captured. Errors are reported through
127
- * `config.onError` by default — attribution is best-effort from the
128
- * browser — but you can await this and try/catch if you want to
129
- * surface failures explicitly.
212
+ * No-op if no cref was captured, or consent isn't granted. Errors are
213
+ * reported through `config.onError` by default — attribution is
214
+ * best-effort from the browser — but you can await this and try/catch
215
+ * if you want to surface failures explicitly.
130
216
  */
131
217
  identify(userId: string): Promise<IdentifyResponse | null>;
132
218
  }
133
219
 
134
220
  export { IdentifyResponse, OpenPartner };
135
- export type { OpenPartnerConfig, StripeLinkingConfig };
221
+ export type { ConsentState, OpenPartnerConfig, StripeLinkingConfig };
package/dist/browser.d.ts CHANGED
@@ -4,9 +4,9 @@ export { O as OpenPartnerError } from './shared-dh1vlldf.js';
4
4
  /**
5
5
  * Browser SDK — runs inside a merchant's web app. Captures the `cref`
6
6
  * attribution token from landing-page URLs and the first-party cookie
7
- * the click router sets, stashes it in localStorage so it survives
8
- * Safari / ITP cookie expiry and SPA navigations, and stitches to the
9
- * authenticated user on signup or login.
7
+ * the click router sets, stashes it in localStorage so it survives SPA
8
+ * navigations and later visits, and stitches to the authenticated user on
9
+ * signup or login.
10
10
  *
11
11
  * Typical integration:
12
12
  *
@@ -24,6 +24,25 @@ export { O as OpenPartnerError } from './shared-dh1vlldf.js';
24
24
  * to OpenPartner then needs no code for those checkouts. `stripe: false`
25
25
  * turns it off.
26
26
  *
27
+ * How long a referral lasts. Safari deletes storage that a page's own
28
+ * scripts wrote (localStorage, and cookies set through document.cookie)
29
+ * after 7 days without a visit, so a visitor who clicks, leaves for a
30
+ * fortnight and comes back to buy without signing up first arrives with
31
+ * nothing. A cookie the brand's own server sets on the landing response
32
+ * isn't capped that way: `referralCookieHeader` in `@openpartner/sdk/server`
33
+ * writes one, and this SDK reads it back (`op_ref`), on every subdomain it
34
+ * covers. Once identify() has run, the stitch lives server-side and no
35
+ * browser storage matters.
36
+ *
37
+ * Consent. `consent: 'pending'` holds the referral in memory until
38
+ * setConsent('granted'): nothing is read from or written to storage or
39
+ * cookies, no Stripe link is tagged and nothing is sent until then.
40
+ *
41
+ * Landing report. When a click first arrives from the URL the SDK tells
42
+ * OpenPartner (POST /attribution/landing), so Admin → Tracking health can
43
+ * count the clicks that never reached a page running it. `reportLanding:
44
+ * false` turns it off.
45
+ *
27
46
  * No bundler? The same SDK ships as a script tag that starts itself
28
47
  * (auto.ts), served at /op.js by an OpenPartner portal.
29
48
  */
@@ -33,6 +52,8 @@ interface StripeLinkingConfig {
33
52
  * in Stripe (e.g. "pay.yourbrand.com"). buy.stripe.com is always on. */
34
53
  linkHosts?: string[];
35
54
  }
55
+ /** Whether this visitor agreed to referral tracking. */
56
+ type ConsentState = 'granted' | 'pending' | 'denied';
36
57
  interface OpenPartnerConfig {
37
58
  /** Base URL of your OpenPartner API, e.g. "https://openpartner.example.com". */
38
59
  apiUrl: string;
@@ -53,6 +74,11 @@ interface OpenPartnerConfig {
53
74
  * (`client_reference_id: req.cookies.op_ref`). Off by default; the
54
75
  * script-tag build turns it on. `domain` shares it across subdomains
55
76
  * (".yourbrand.com") when visitors land on one and check out on another.
77
+ *
78
+ * The SDK reads this cookie back whether or not it writes it (`op_ref`
79
+ * unless `name` says otherwise): that's how a page on another subdomain,
80
+ * or a visit after Safari cleared localStorage, finds the referral, and
81
+ * how one your server set (`referralCookieHeader`) reaches identify().
56
82
  */
57
83
  referralCookie?: false | {
58
84
  name?: string;
@@ -60,17 +86,46 @@ interface OpenPartnerConfig {
60
86
  days?: number;
61
87
  };
62
88
  /**
63
- * Called when identify() fails. Default: log to console. Pass a noop
64
- * to suppress entirely or hook into your observability stack.
89
+ * Whether the visitor has agreed to referral tracking. Default
90
+ * `'granted'`: the SDK stores the referral as soon as it loads. Behind a
91
+ * consent banner, start at `'pending'` and call `setConsent()` from its
92
+ * callback. Until then the referral from the URL is held in memory only,
93
+ * so a visitor who agrees on the landing page is still credited.
94
+ * `'denied'` uses nothing.
95
+ */
96
+ consent?: ConsentState;
97
+ /**
98
+ * Tell OpenPartner when a partner click arrives on your site, so Admin →
99
+ * Tracking health can show clicks lost between the partner link and your
100
+ * page. Sends the click id once per click, nothing about the visitor.
101
+ * Default on; `false` turns it off.
102
+ */
103
+ reportLanding?: boolean;
104
+ /**
105
+ * Called when a call to OpenPartner fails: identify(), or a landing
106
+ * report OpenPartner answered with `click_not_found` (which usually means
107
+ * `apiUrl` names another program; other landing failures are dropped).
108
+ * Default: log to console. Pass a noop to suppress entirely or
109
+ * hook into your observability stack.
65
110
  */
66
111
  onError?: (err: unknown) => void;
67
112
  }
68
113
  declare class OpenPartner {
69
114
  private config;
70
115
  private observer;
116
+ private consent;
117
+ /** The URL's referral while consent is pending, held in memory only. */
118
+ private held;
119
+ /** This page's referral, kept here too: where localStorage throws it is
120
+ * the only copy until the cookie is written. */
121
+ private current;
122
+ /** The Stripe links and embeds this SDK tagged, and with what, so a
123
+ * withdrawn consent can take the tags back off. */
124
+ private tagged;
71
125
  private constructor();
72
126
  static init(config: OpenPartnerConfig): OpenPartner;
73
- /** The cref we're holding onto, if any. Useful for debugging. */
127
+ /** The cref we're holding onto, if any. Useful for debugging. Null until
128
+ * consent is granted. */
74
129
  getCref(): string | null;
75
130
  /**
76
131
  * Captured referral token for this visitor. Pass to Stripe Checkout
@@ -82,11 +137,22 @@ declare class OpenPartner {
82
137
  * // ...
83
138
  * });
84
139
  *
85
- * Returns null if the visitor didn't arrive via a partner link.
140
+ * Returns null if the visitor didn't arrive via a partner link, or
141
+ * hasn't agreed to tracking yet.
86
142
  */
87
143
  getReferral(): string | null;
88
- /** Explicitly discard the captured cref. */
144
+ /** Explicitly discard the captured cref, the referral cookie, and the
145
+ * references this SDK added to Stripe links and embeds. */
89
146
  clear(): void;
147
+ /**
148
+ * Record the visitor's answer to your consent banner. `'granted'` stores
149
+ * the referral (the one held since the page loaded, or one already
150
+ * stored), starts tagging Stripe links and sends the landing report.
151
+ * `'denied'` stops all of it, takes the references it added off Stripe
152
+ * links and embeds, and deletes what was stored. Calling it again is
153
+ * safe.
154
+ */
155
+ setConsent(state: 'granted' | 'denied'): void;
90
156
  /**
91
157
  * Add the referral to every Stripe Payment Link and Pricing Table / Buy
92
158
  * Button embed under `root` that doesn't carry a reference already.
@@ -95,9 +161,15 @@ declare class OpenPartner {
95
161
  * many it changed.
96
162
  */
97
163
  linkStripe(root?: ParentNode): number;
164
+ private remember;
165
+ /** Take back the references this SDK added. One the brand set, or one
166
+ * changed since, stays. */
167
+ private untagStripe;
98
168
  /** Stop watching the page for new Stripe links and embeds. */
99
169
  stopLinkingStripe(): void;
100
170
  private stripeLinkHosts;
171
+ /** Everything the SDK does once it may: store, mirror, link, report. */
172
+ private start;
101
173
  /**
102
174
  * Link what's on the page now, then anything added or re-pointed later
103
175
  * (SPAs, Stripe embeds mounted on demand). Only with a referral: a visitor
@@ -109,27 +181,41 @@ declare class OpenPartner {
109
181
  * element is parsed, before Stripe upgrades it.
110
182
  */
111
183
  private watchStripe;
184
+ /** The cref on the landing URL, if any. */
185
+ private queryCref;
112
186
  /**
113
- * Capture the cref from query string or cookie and stash in
114
- * localStorage. Runs on init so the value survives SPA navigations,
115
- * ITP cookie expiry, and subsequent page loads.
187
+ * Capture the cref from the URL (or the one held while consent was
188
+ * pending), else the router's cookie, else the referral cookie, and
189
+ * stash it in localStorage so it survives SPA navigations and later
190
+ * page loads. Returns the cref when it's a click arriving now, one this
191
+ * browser didn't have yet, for the landing report.
116
192
  */
117
193
  private captureCref;
118
- /** Mirror the referral into the brand's own first-party cookie (when
119
- * configured), refreshed on every page load that has one. */
194
+ private referralCookieName;
195
+ /**
196
+ * Mirror the referral into the brand's own first-party cookie (when
197
+ * configured). Left alone when it already holds this referral: in Safari
198
+ * rewriting it from script would trade a server-set cookie's full
199
+ * lifetime for the 7-day cap. A new click overwrites it.
200
+ */
120
201
  private writeReferralCookie;
121
202
  private readCookie;
203
+ private storageGet;
204
+ private storageSet;
205
+ private storageRemove;
206
+ /** Tell OpenPartner the click arrived. Best-effort, once per click. */
207
+ private reportLanding;
122
208
  /**
123
209
  * Stitch the current captured click to an authenticated user. Call
124
210
  * this on login, signup, or anywhere the user becomes known.
125
211
  *
126
- * No-op if no cref was captured. Errors are reported through
127
- * `config.onError` by default — attribution is best-effort from the
128
- * browser — but you can await this and try/catch if you want to
129
- * surface failures explicitly.
212
+ * No-op if no cref was captured, or consent isn't granted. Errors are
213
+ * reported through `config.onError` by default — attribution is
214
+ * best-effort from the browser — but you can await this and try/catch
215
+ * if you want to surface failures explicitly.
130
216
  */
131
217
  identify(userId: string): Promise<IdentifyResponse | null>;
132
218
  }
133
219
 
134
220
  export { IdentifyResponse, OpenPartner };
135
- export type { OpenPartnerConfig, StripeLinkingConfig };
221
+ export type { ConsentState, OpenPartnerConfig, StripeLinkingConfig };