tracklane 0.1.1 → 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.
- package/README.md +28 -5
- package/dist/browser-FeV5oRFZ.d.ts +628 -0
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +262 -6
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/server.d.ts +2 -2
- package/dist/server.js +289 -1
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/browser-usMUxJhU.d.ts +0 -126
- package/dist/server-DRda44Tb.d.ts +0 -166
- package/dist/vocabulary-fzM9ZuE1.d.ts +0 -129
package/README.md
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# tracklane
|
|
2
2
|
|
|
3
|
-
> One interface between your application and every tool
|
|
3
|
+
> One interface between your application and every analytics and advertising tool, in the browser
|
|
4
|
+
> and through their conversion APIs.
|
|
4
5
|
|
|
5
6
|
Every analytics and advertising tool arrives with its own SDK, its own event names and its own
|
|
6
7
|
payload format. Add a second one and the tracking call at each conversion point stops being a line
|
|
7
8
|
and becomes a block. Add a third and every one of those blocks has to change.
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
and
|
|
11
|
-
|
|
10
|
+
The server-side half is worse. Each vendor ships a separate conversion API with a different name
|
|
11
|
+
and a different contract: Google's Measurement Protocol, Meta's Conversions API, LinkedIn's
|
|
12
|
+
Conversions API, PostHog's Capture API. Each wants its own identifiers, its own hashing rules, and
|
|
13
|
+
its own way of tying a server event back to the browser event so the two are not counted twice.
|
|
14
|
+
|
|
15
|
+
`tracklane` gives your application one interface for both. Name what happened once, in GA4's
|
|
16
|
+
vocabulary, and it reaches every tool you configured, translated into that tool's event names,
|
|
17
|
+
payload shape and identifiers.
|
|
12
18
|
|
|
13
19
|
**Documentation:** https://tracklane.codar.me
|
|
14
20
|
|
|
@@ -40,6 +46,10 @@ consent('update', { ad_storage: 'granted', analytics_storage: 'granted' });
|
|
|
40
46
|
|
|
41
47
|
## Server
|
|
42
48
|
|
|
49
|
+
Each vendor's conversion API, behind the call you already wrote. The credentials are that vendor's
|
|
50
|
+
own: GA4's Measurement Protocol wants a `measurementId` alongside its `apiSecret`, exactly as its
|
|
51
|
+
own endpoint does.
|
|
52
|
+
|
|
43
53
|
```ts
|
|
44
54
|
import { createTracking, ga4 } from 'tracklane/server';
|
|
45
55
|
|
|
@@ -72,7 +82,20 @@ install vendor tags.
|
|
|
72
82
|
|
|
73
83
|
## Providers
|
|
74
84
|
|
|
75
|
-
|
|
85
|
+
Both surfaces, or it does not count as shipped. This table is checked against the source on every
|
|
86
|
+
build, so it is never ahead of the library.
|
|
87
|
+
|
|
88
|
+
<!-- providers:start -->
|
|
89
|
+
|
|
90
|
+
| Vendor | Conversion API | Status |
|
|
91
|
+
| -------- | -------------------- | ------ |
|
|
92
|
+
| GA4 | Measurement Protocol | shipped |
|
|
93
|
+
| Meta | Conversions API | shipped |
|
|
94
|
+
| LinkedIn | Conversions API | next |
|
|
95
|
+
| PostHog | Capture API | shipped |
|
|
96
|
+
| X | Conversions API | next |
|
|
97
|
+
|
|
98
|
+
<!-- providers:end -->
|
|
76
99
|
|
|
77
100
|
Any tool that receives events about what your users do can be a destination, and writing your own
|
|
78
101
|
uses the same public contract the built-in ones use, without a registry entry, an allow-list, or a
|
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
//#region src/providers/ga4.server.d.ts
|
|
2
|
+
/** Credentials for the GA4 Measurement Protocol. */
|
|
3
|
+
interface Ga4ServerCredentials {
|
|
4
|
+
/** The `G-` id of the data stream. */
|
|
5
|
+
measurementId: string;
|
|
6
|
+
/** Created under Admin → Data Streams → Measurement Protocol. */
|
|
7
|
+
apiSecret: string;
|
|
8
|
+
}
|
|
9
|
+
/** Optional configuration for the GA4 server provider. */
|
|
10
|
+
interface Ga4ServerConfig {
|
|
11
|
+
events?: Record<string, EventBinding<string>>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Google Analytics 4 on the server, through the Measurement Protocol.
|
|
15
|
+
*
|
|
16
|
+
* @param credentials - The measurement id and API secret.
|
|
17
|
+
* @param config - Rarely needed event name overrides.
|
|
18
|
+
* @returns A provider for `createTracking` from `tracklane/server`.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { createTracking, ga4 } from 'tracklane/server';
|
|
23
|
+
*
|
|
24
|
+
* const { track } = createTracking({
|
|
25
|
+
* providers: [ga4({ measurementId: 'G-XXXXXXX', apiSecret: process.env.GA4_SECRET! })],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* await track('purchase', order, { cookies: request.headers.get('cookie') });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
declare function ga4$1(credentials: Ga4ServerCredentials, config?: Ga4ServerConfig): ServerProvider;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/providers/meta.server.d.ts
|
|
34
|
+
/** Credentials for Meta's Conversions API. */
|
|
35
|
+
interface MetaServerCredentials {
|
|
36
|
+
/** The pixel (dataset) id events are posted to. */
|
|
37
|
+
pixelId: string;
|
|
38
|
+
/** A system user or page access token with `ads_management`. It is a credential. */
|
|
39
|
+
accessToken: string;
|
|
40
|
+
}
|
|
41
|
+
/** Optional configuration for the Meta server provider. */
|
|
42
|
+
interface MetaServerConfig {
|
|
43
|
+
/** How this pixel spells each canonical event, merged over the built-in map. */
|
|
44
|
+
events?: Record<string, EventBinding<string>>;
|
|
45
|
+
/**
|
|
46
|
+
* Where conversions happen by default. Meta requires `action_source` on
|
|
47
|
+
* every event, so there is always a value; `context.source` overrides it
|
|
48
|
+
* per call, which is what a webhook and a checkout need.
|
|
49
|
+
*/
|
|
50
|
+
actionSource?: ActionSource;
|
|
51
|
+
/** The Graph API version in the URL. Defaults to a pinned one. */
|
|
52
|
+
apiVersion?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Routes events to the Test Events tab in Events Manager instead of the
|
|
55
|
+
* live dataset. **Testing only**: Meta says to remove it before
|
|
56
|
+
* production, and it is the only instrument that shows what a payload
|
|
57
|
+
* actually became.
|
|
58
|
+
*/
|
|
59
|
+
testEventCode?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Meta's Limited Data Use marking, forwarded verbatim: `['LDU']` to enable
|
|
62
|
+
* it, `[]` to disable it.
|
|
63
|
+
*
|
|
64
|
+
* This is a processing marking that some US state laws require, not a
|
|
65
|
+
* consent declaration, which is why it is configuration on this factory and
|
|
66
|
+
* not a reading of `context.consent`. The browser half has no equivalent:
|
|
67
|
+
* `fbq('dataProcessingOptions', …)` is documented as running before
|
|
68
|
+
* `fbq('init')`, and the snippet is the host's.
|
|
69
|
+
*/
|
|
70
|
+
dataProcessingOptions?: readonly string[];
|
|
71
|
+
/** `0` lets Meta geolocate. Meaningless without `dataProcessingOptions`. */
|
|
72
|
+
dataProcessingOptionsCountry?: number;
|
|
73
|
+
/** `0` lets Meta geolocate. Meaningless without `dataProcessingOptions`. */
|
|
74
|
+
dataProcessingOptionsState?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Meta's Conversions API.
|
|
78
|
+
*
|
|
79
|
+
* @param credentials - The pixel id and an access token.
|
|
80
|
+
* @param config - Event overrides, the default action source, and Meta's own
|
|
81
|
+
* levers: the API version, a test event code, and Limited Data Use.
|
|
82
|
+
* @returns A provider for `createTracking` from `tracklane/server`.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { createTracking, meta } from 'tracklane/server';
|
|
87
|
+
*
|
|
88
|
+
* const { track } = createTracking({
|
|
89
|
+
* providers: [meta({ pixelId: '1234567890', accessToken: process.env.META_TOKEN! })],
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* await track('purchase', order, {
|
|
93
|
+
* user: { email: order.email },
|
|
94
|
+
* cookies: request.headers.get('cookie'),
|
|
95
|
+
* dedupId: order.id,
|
|
96
|
+
* ip,
|
|
97
|
+
* userAgent,
|
|
98
|
+
* });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
declare function meta$1(credentials: MetaServerCredentials, config?: MetaServerConfig): ServerProvider;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/providers/posthog.server.d.ts
|
|
104
|
+
/** Credentials for PostHog's Capture API. */
|
|
105
|
+
interface PosthogServerCredentials {
|
|
106
|
+
/** The project API key, the same one the browser SDK is initialised with (`phc_...`). */
|
|
107
|
+
apiKey: string;
|
|
108
|
+
/**
|
|
109
|
+
* Where this project ingests events, when it is not PostHog's default US
|
|
110
|
+
* Cloud instance — `https://eu.i.posthog.com` for the EU Cloud, or a
|
|
111
|
+
* self-hosted deployment's own origin. Defaults to `https://us.i.posthog.com`.
|
|
112
|
+
*/
|
|
113
|
+
host?: string;
|
|
114
|
+
}
|
|
115
|
+
/** Optional configuration for the PostHog server provider. */
|
|
116
|
+
interface PosthogServerConfig {
|
|
117
|
+
/**
|
|
118
|
+
* How this project spells each canonical event. Rarely needed: `capture()`
|
|
119
|
+
* accepts any string, so the canonical vocabulary already is PostHog's.
|
|
120
|
+
*/
|
|
121
|
+
events?: Record<string, EventBinding<string>>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* PostHog on the server, through the Capture API.
|
|
125
|
+
*
|
|
126
|
+
* @param credentials - The project API key, and the ingestion host for
|
|
127
|
+
* anything other than PostHog's default US Cloud instance.
|
|
128
|
+
* @param config - Rarely needed event name overrides.
|
|
129
|
+
* @returns A provider for `createTracking` from `tracklane/server`.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { createTracking, posthog } from 'tracklane/server';
|
|
134
|
+
*
|
|
135
|
+
* const { track } = createTracking({
|
|
136
|
+
* providers: [posthog({ apiKey: process.env.POSTHOG_API_KEY! })],
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* await track('purchase', order, {
|
|
140
|
+
* user: { userId: order.userId },
|
|
141
|
+
* cookies: request.headers.get('cookie'),
|
|
142
|
+
* });
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
declare function posthog$1(credentials: PosthogServerCredentials, config?: PosthogServerConfig): ServerProvider;
|
|
146
|
+
//#endregion
|
|
147
|
+
//#region src/vocabulary.d.ts
|
|
148
|
+
/**
|
|
149
|
+
* The vocabulary shared by the browser and server libraries.
|
|
150
|
+
*
|
|
151
|
+
* These two libraries never talk to each other. They share no runtime, no
|
|
152
|
+
* state, no instance in common. What they share is this: the names of
|
|
153
|
+
* events and the shape of the data that travels with them, so that
|
|
154
|
+
* `purchase` means the same thing on both sides. This is the only overlap
|
|
155
|
+
* that exists on purpose.
|
|
156
|
+
*/
|
|
157
|
+
/** One item inside an ecommerce event, spelled the way GA4 spells it. */
|
|
158
|
+
interface EventItem {
|
|
159
|
+
/** The SKU or catalogue id for this line. */
|
|
160
|
+
item_id?: string;
|
|
161
|
+
/** The product name for this line, shown in reports. */
|
|
162
|
+
item_name?: string;
|
|
163
|
+
/** The unit price for this line, before `quantity`. */
|
|
164
|
+
price?: number;
|
|
165
|
+
/** How many units of this line. */
|
|
166
|
+
quantity?: number;
|
|
167
|
+
[key: string]: unknown;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The business payload of an event.
|
|
171
|
+
*
|
|
172
|
+
* The named fields are the ones adapters read to build vendor-specific value
|
|
173
|
+
* and content slots. The index signature is deliberate: a business measures
|
|
174
|
+
* things this vocabulary does not name, and closing the bag would make the
|
|
175
|
+
* library the arbiter of what may be measured.
|
|
176
|
+
*/
|
|
177
|
+
interface EventData {
|
|
178
|
+
/** The order id. GA4 needs it, together with `items`, to record a purchase. */
|
|
179
|
+
transaction_id?: string;
|
|
180
|
+
/** The monetary amount, paired with `currency`. */
|
|
181
|
+
value?: number;
|
|
182
|
+
/** An ISO 4217 code. GA4 requires it whenever `value` is set. */
|
|
183
|
+
currency?: string;
|
|
184
|
+
/** The cart or order lines, required by GA4 alongside `transaction_id` for a purchase. */
|
|
185
|
+
items?: EventItem[];
|
|
186
|
+
[key: string]: unknown;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Who the person is.
|
|
190
|
+
*
|
|
191
|
+
* One identifier for the person, which every vendor receives under its own
|
|
192
|
+
* name. Values are always raw: hashing and normalisation are field formats
|
|
193
|
+
* each vendor defines, applied inside the adapter that owns the field.
|
|
194
|
+
*
|
|
195
|
+
* Everything else that identifies someone arrives through cookies on the
|
|
196
|
+
* server, read by whichever adapter knows the format.
|
|
197
|
+
*/
|
|
198
|
+
interface UserData {
|
|
199
|
+
/** One id for the person, translated per vendor. */
|
|
200
|
+
userId?: string;
|
|
201
|
+
/** Raw and unhashed. Adapters that need it hashed (Meta, LinkedIn) do that themselves. */
|
|
202
|
+
email?: string;
|
|
203
|
+
/** Raw, in whatever format the host has it. An adapter normalises it if its vendor needs to. */
|
|
204
|
+
phone?: string;
|
|
205
|
+
/** Raw, read only by vendors whose identity match uses names, such as Meta. */
|
|
206
|
+
firstName?: string;
|
|
207
|
+
/** Raw, read only by vendors whose identity match uses names, such as Meta. */
|
|
208
|
+
lastName?: string;
|
|
209
|
+
/** From the URL rather than a cookie, which is why it is named here. */
|
|
210
|
+
twclid?: string;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Consent signals, in Google's own spelling.
|
|
214
|
+
*
|
|
215
|
+
* Google's is the most granular surface any of the five vendors exposes, so
|
|
216
|
+
* every other vendor receives a *collapse* of it. Carrying the finest
|
|
217
|
+
* resolution and reducing per vendor means a projection never claims more
|
|
218
|
+
* than the host declared. Expanding a coarse answer into fine signals would
|
|
219
|
+
* invent information, and this library never does that.
|
|
220
|
+
*
|
|
221
|
+
* An absent signal produces no key. Treating silence as a denial would put
|
|
222
|
+
* words in the visitor's mouth, just as treating it as a grant would.
|
|
223
|
+
*/
|
|
224
|
+
interface ConsentState {
|
|
225
|
+
/** Governs Google's ad cookies. Browser only, since GA4's server-side API has no slot for it. */
|
|
226
|
+
ad_storage?: "granted" | "denied";
|
|
227
|
+
/** Governs Google's analytics cookies. Browser only, since GA4's server-side API has no slot. */
|
|
228
|
+
analytics_storage?: "granted" | "denied";
|
|
229
|
+
/** Whether user data may be sent for ads. GA4's server-side API reads this one, uppercased. */
|
|
230
|
+
ad_user_data?: "granted" | "denied";
|
|
231
|
+
/** Whether ads may be personalised. GA4's server-side API reads this one too, uppercased. */
|
|
232
|
+
ad_personalization?: "granted" | "denied";
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Where a conversion happened.
|
|
236
|
+
*
|
|
237
|
+
* Meta's vocabulary, adopted verbatim, because it is the only vendor of the
|
|
238
|
+
* five that documents the concept and requires the field on every server
|
|
239
|
+
* event. Inventing a taxonomy and translating it would be both incomplete
|
|
240
|
+
* for Meta and meaningless to everyone else.
|
|
241
|
+
*/
|
|
242
|
+
type ActionSource = "website" | "app" | "email" | "phone_call" | "chat" | "physical_store" | "system_generated" | "business_messaging" | "other";
|
|
243
|
+
/**
|
|
244
|
+
* How a canonical event name is spelled for one vendor, or `null` to say
|
|
245
|
+
* "never send this event here".
|
|
246
|
+
*/
|
|
247
|
+
type EventBinding<Target> = Target | null;
|
|
248
|
+
/** What a provider does with a canonical event it has no binding for. */
|
|
249
|
+
type ProviderDefault = "passthrough" | "ignore";
|
|
250
|
+
/**
|
|
251
|
+
* Reported out of band, never thrown at the call site.
|
|
252
|
+
*
|
|
253
|
+
* Fanning out to N vendors means no single call site can catch anything, so
|
|
254
|
+
* this is the replica of the `try/catch` a host would have written around a
|
|
255
|
+
* hand-made call. `severity` separates "this did not arrive" from "this
|
|
256
|
+
* arrived, and here is something you should know".
|
|
257
|
+
*
|
|
258
|
+
* It never carries event contents: the payload holds raw personal data by
|
|
259
|
+
* design, and no library-owned surface re-emits it into a host's logs.
|
|
260
|
+
*/
|
|
261
|
+
interface TrackingError {
|
|
262
|
+
/** Which vendor produced this. It matches the `name` on the provider that registered it. */
|
|
263
|
+
provider: string;
|
|
264
|
+
/** The canonical event name being sent when this happened. */
|
|
265
|
+
event: string;
|
|
266
|
+
/** `'error'` when the event did not arrive; `'warning'` when it arrived but is worth a look. */
|
|
267
|
+
severity: "error" | "warning";
|
|
268
|
+
/** What happened, in one line, excluding the payload: event data never appears here. */
|
|
269
|
+
message: string;
|
|
270
|
+
/** The original thrown value, where one exists, for a host that wants more than `message`. */
|
|
271
|
+
cause?: unknown;
|
|
272
|
+
}
|
|
273
|
+
//#endregion
|
|
274
|
+
//#region src/server.d.ts
|
|
275
|
+
/**
|
|
276
|
+
* Everything a conversion API needs beyond the event itself: who the person
|
|
277
|
+
* is, when it happened, and where it was observed.
|
|
278
|
+
*
|
|
279
|
+
* None of this is "what happened", which is why it does not travel in the
|
|
280
|
+
* event data: putting it there would leak identity into the pass-through
|
|
281
|
+
* event params of the vendors that forward the payload as-is.
|
|
282
|
+
*/
|
|
283
|
+
interface EventContext {
|
|
284
|
+
/** The person's identity, the server-side counterpart to `identify()` on the browser. */
|
|
285
|
+
user?: UserData;
|
|
286
|
+
/**
|
|
287
|
+
* The request's raw `Cookie` header, or an already-parsed map.
|
|
288
|
+
*
|
|
289
|
+
* These are cookies the vendors' own browser tags set. This library never
|
|
290
|
+
* writes one. On the server each adapter reads the cookies belonging to
|
|
291
|
+
* its own vendor: Meta forwards its value as-is, Google's holds several
|
|
292
|
+
* things at once and has to be opened. That knowledge lives in the
|
|
293
|
+
* adapters; the core only splits `name=value; name=value` into pairs.
|
|
294
|
+
*
|
|
295
|
+
* **It splits, it does not interpret.** Values are handed over exactly as
|
|
296
|
+
* they appear, with no percent-decoding. The cookies these vendors set do
|
|
297
|
+
* not need it; a host reading some other cookie should decode it itself.
|
|
298
|
+
*/
|
|
299
|
+
cookies?: string | Record<string, string> | null;
|
|
300
|
+
/** Ties this send to a browser hit. Never invented by the library. */
|
|
301
|
+
dedupId?: string;
|
|
302
|
+
/**
|
|
303
|
+
* When the conversion actually happened. Defaults to now.
|
|
304
|
+
*
|
|
305
|
+
* A webhook arrives after the fact and LinkedIn rejects conversions older
|
|
306
|
+
* than 90 days, so reporting "now" for everything would quietly distort
|
|
307
|
+
* attribution.
|
|
308
|
+
*/
|
|
309
|
+
timestamp?: number | Date;
|
|
310
|
+
/** Overrides the factory default. Meta requires it on every event. */
|
|
311
|
+
source?: ActionSource;
|
|
312
|
+
/** The page the conversion happened on. Meta's Conversions API reads it as `event_source_url`. */
|
|
313
|
+
url?: string;
|
|
314
|
+
/** The visitor's IP. Meta's Conversions API reads it as one of the identifiers in `user_data`. */
|
|
315
|
+
ip?: string;
|
|
316
|
+
/** The visitor's user agent. Meta's Conversions API reads it alongside `ip`. */
|
|
317
|
+
userAgent?: string;
|
|
318
|
+
/** What is known *about* the person: GA4 user properties, PostHog `$set`. */
|
|
319
|
+
traits?: Record<string, unknown>;
|
|
320
|
+
/**
|
|
321
|
+
* Travels inside the event, for the vendors that document such a field.
|
|
322
|
+
* Today that is GA4's Measurement Protocol, and it reads two of the
|
|
323
|
+
* signals; the others have no slot and ignore it.
|
|
324
|
+
*/
|
|
325
|
+
consent?: ConsentState;
|
|
326
|
+
}
|
|
327
|
+
/** The context an adapter receives, with the ambiguity resolved. */
|
|
328
|
+
interface ResolvedContext extends Omit<EventContext, "cookies" | "timestamp"> {
|
|
329
|
+
/** The request's cookies, already split into a `name → value` map. */
|
|
330
|
+
cookies: Record<string, string>;
|
|
331
|
+
/** Epoch milliseconds. Adapters convert to their vendor's spelling. */
|
|
332
|
+
timestamp: number;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* A destination for server events.
|
|
336
|
+
*
|
|
337
|
+
* `track` may throw, on a non-2xx or where a vendor documents a
|
|
338
|
+
* precondition the event cannot meet. The dispatcher isolates and reports
|
|
339
|
+
* it, so adapters stay honest and loud.
|
|
340
|
+
*
|
|
341
|
+
* `report` is for a caveat about a request that already succeeded. It does
|
|
342
|
+
* not throw: by then the send happened, and throwing would tell a host's
|
|
343
|
+
* retry logic to resend a delivered conversion.
|
|
344
|
+
*/
|
|
345
|
+
interface ServerProvider<Target = string> {
|
|
346
|
+
/** The vendor's fixed name. Registering one twice is an error. */
|
|
347
|
+
name: string;
|
|
348
|
+
/** What happens to a canonical event this vendor has no binding for. */
|
|
349
|
+
default: ProviderDefault;
|
|
350
|
+
/** How this vendor spells each canonical event. */
|
|
351
|
+
events?: Record<string, EventBinding<Target>>;
|
|
352
|
+
/** Sends one event to this vendor's conversion API. May throw, or call `report` for a caveat. */
|
|
353
|
+
track(name: Target, data: EventData, context: ResolvedContext, report: (message: string) => void): Promise<void>;
|
|
354
|
+
}
|
|
355
|
+
/** Options accepted by {@link createTracking}. */
|
|
356
|
+
interface ServerTrackingOptions {
|
|
357
|
+
/** The vendors to send every event to. Registering the same `name` twice throws. */
|
|
358
|
+
providers: readonly ServerProvider[];
|
|
359
|
+
/**
|
|
360
|
+
* The one diagnostic channel. Without it the library is silent, exactly as
|
|
361
|
+
* a hand-written vendor call is.
|
|
362
|
+
*/
|
|
363
|
+
onError?: (error: TrackingError) => void;
|
|
364
|
+
}
|
|
365
|
+
/** What {@link createTracking} returns. */
|
|
366
|
+
interface ServerTracking {
|
|
367
|
+
/**
|
|
368
|
+
* Sends one event to every configured conversion API.
|
|
369
|
+
*
|
|
370
|
+
* Resolves once every provider has settled and never rejects: one vendor
|
|
371
|
+
* being down is not the caller's failure to handle. Awaiting is about the
|
|
372
|
+
* request surviving a serverless runtime, not about delivery being
|
|
373
|
+
* guaranteed.
|
|
374
|
+
*/
|
|
375
|
+
track(name: string, data?: EventData, context?: EventContext): Promise<void>;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Creates the server tracking surface.
|
|
379
|
+
*
|
|
380
|
+
* There is no `identify` here and nothing is held between calls: a server
|
|
381
|
+
* instance is shared by every request, so a retained identity would attach
|
|
382
|
+
* one visitor to another visitor's conversion. Identity travels with the
|
|
383
|
+
* call, which is also what every conversion API expects.
|
|
384
|
+
*
|
|
385
|
+
* There is no `consent` call either. Consent commands are properties of a
|
|
386
|
+
* vendor's on-page tag, and a server request has no tag. The one vendor that
|
|
387
|
+
* accepts consent server-side reads it from the context.
|
|
388
|
+
*
|
|
389
|
+
* @param options - The providers to send to, and an optional error channel.
|
|
390
|
+
* @returns The `track` call.
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```ts
|
|
394
|
+
* import { createTracking, ga4 } from 'tracklane/server';
|
|
395
|
+
*
|
|
396
|
+
* const { track } = createTracking({
|
|
397
|
+
* providers: [ga4({ measurementId: 'G-XXXXXXX', apiSecret })],
|
|
398
|
+
* });
|
|
399
|
+
*
|
|
400
|
+
* await track('purchase', order, { cookies: request.headers.get('cookie') });
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
declare function createTracking$1(options: ServerTrackingOptions): ServerTracking;
|
|
404
|
+
//#endregion
|
|
405
|
+
//#region src/providers/ga4.browser.d.ts
|
|
406
|
+
/** Optional configuration for the GA4 browser provider. */
|
|
407
|
+
interface Ga4BrowserConfig {
|
|
408
|
+
/**
|
|
409
|
+
* How this property spells each canonical event. Rarely needed: the
|
|
410
|
+
* canonical vocabulary already is GA4's.
|
|
411
|
+
*/
|
|
412
|
+
events?: Record<string, EventBinding<string>>;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Google Analytics 4 in the browser, through `gtag.js`.
|
|
416
|
+
*
|
|
417
|
+
* **This provider does not load Google's tag.** The tag belongs on the page,
|
|
418
|
+
* installed the way Google documents it: a snippet in the HTML, or a tag
|
|
419
|
+
* manager. This library talks to the tag that is already there; it never
|
|
420
|
+
* injects a third-party script into your page, and it never issues the
|
|
421
|
+
* property configuration that would emit a second page view.
|
|
422
|
+
*
|
|
423
|
+
* Which also means the initial consent declaration is not ours to make:
|
|
424
|
+
* Google requires it before the property is configured, and the snippet that
|
|
425
|
+
* configures the property is yours. Use {@link ConsentState} through
|
|
426
|
+
* `consent()` for changes after that.
|
|
427
|
+
*
|
|
428
|
+
* @param measurementId - The `G-` id of the data stream, matching the tag on
|
|
429
|
+
* the page.
|
|
430
|
+
* @param config - Rarely needed event name overrides.
|
|
431
|
+
* @returns A provider for `createTracking` from `tracklane/browser`.
|
|
432
|
+
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```ts
|
|
435
|
+
* import { createTracking, ga4 } from 'tracklane/browser';
|
|
436
|
+
*
|
|
437
|
+
* // Google's own snippet is already in the page.
|
|
438
|
+
* const { track } = createTracking({ providers: [ga4('G-XXXXXXX')] });
|
|
439
|
+
*
|
|
440
|
+
* track('purchase', { transaction_id: 'T-1', value: 49.9, currency: 'BRL' });
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
declare function ga4(measurementId: string, config?: Ga4BrowserConfig): BrowserProvider;
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/providers/meta.browser.d.ts
|
|
446
|
+
/** Optional configuration for the Meta browser provider. */
|
|
447
|
+
interface MetaBrowserConfig {
|
|
448
|
+
/**
|
|
449
|
+
* How this pixel spells each canonical event, merged over the built-in map.
|
|
450
|
+
*
|
|
451
|
+
* This is how the eight standard events with no GA4 counterpart are
|
|
452
|
+
* reached, and how an event is kept out of Meta entirely with `null`.
|
|
453
|
+
*/
|
|
454
|
+
events?: Record<string, EventBinding<string>>;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* The Meta pixel in the browser, through `fbq`.
|
|
458
|
+
*
|
|
459
|
+
* **This provider does not load Meta's pixel.** The base code belongs on the
|
|
460
|
+
* page, installed the way
|
|
461
|
+
* [Meta documents it](https://developers.facebook.com/docs/meta-pixel/get-started):
|
|
462
|
+
* a snippet in the HTML, or a tag manager. This library talks to the pixel
|
|
463
|
+
* that is already there; it never injects a third-party script and never
|
|
464
|
+
* issues `fbq('init')`.
|
|
465
|
+
*
|
|
466
|
+
* Three consequences follow from that, and all three are Meta's rules rather
|
|
467
|
+
* than ours:
|
|
468
|
+
*
|
|
469
|
+
* - **There is no `identify` here.** Meta accepts advanced matching only in
|
|
470
|
+
* the base code ("be sure to place advanced matching parameters in the
|
|
471
|
+
* pixel base code or the values will not be treated as manual advanced
|
|
472
|
+
* matching values"), and the base code is yours. Put `em`, `ph`, `fn`, `ln`
|
|
473
|
+
* in your own `fbq('init')`, where the pixel normalises and hashes them for
|
|
474
|
+
* you. On the server, where there is no snippet, the same identity travels
|
|
475
|
+
* in `context.user` and is hashed by the adapter.
|
|
476
|
+
* - **The initial consent declaration is not ours.** Meta wants
|
|
477
|
+
* `fbq('consent', 'revoke')` before `init`, and by the time you hold what
|
|
478
|
+
* `createTracking` returns that moment has passed. Ours is every
|
|
479
|
+
* declaration after it, through `consent()`.
|
|
480
|
+
* - **Limited Data Use is not ours either.** `fbq('dataProcessingOptions', …)`
|
|
481
|
+
* is documented as running before `init`. It belongs in your snippet. The
|
|
482
|
+
* server half, which has no snippet, carries it on its factory.
|
|
483
|
+
*
|
|
484
|
+
* **There is no pixel id here**, and that is deliberate rather than an
|
|
485
|
+
* omission. `fbq('track')` reaches every pixel the page initialised, so an id
|
|
486
|
+
* passed in would address nothing and the parameter would promise something
|
|
487
|
+
* this adapter cannot deliver. Which pixels exist is declared by your snippet,
|
|
488
|
+
* in `fbq('init')`.
|
|
489
|
+
*
|
|
490
|
+
* @param config - Event name overrides, merged over the built-in map.
|
|
491
|
+
* @returns A provider for `createTracking` from `tracklane/browser`.
|
|
492
|
+
*
|
|
493
|
+
* @example
|
|
494
|
+
* ```ts
|
|
495
|
+
* import { createTracking, meta } from 'tracklane/browser';
|
|
496
|
+
*
|
|
497
|
+
* // Meta's own base code is already in the page, and it named the pixel.
|
|
498
|
+
* const { track } = createTracking({ providers: [meta()] });
|
|
499
|
+
*
|
|
500
|
+
* // fbq('track', 'Purchase', { value: 49.9, currency: 'BRL' }, { eventID: 'T-1' })
|
|
501
|
+
* track('purchase', { value: 49.9, currency: 'BRL' }, { dedupId: 'T-1' });
|
|
502
|
+
* ```
|
|
503
|
+
*/
|
|
504
|
+
declare function meta(config?: MetaBrowserConfig): BrowserProvider;
|
|
505
|
+
//#endregion
|
|
506
|
+
//#region src/providers/posthog.browser.d.ts
|
|
507
|
+
/** Optional configuration for the PostHog browser provider. */
|
|
508
|
+
interface PosthogBrowserConfig {
|
|
509
|
+
/**
|
|
510
|
+
* How this project spells each canonical event. Rarely needed: `capture()`
|
|
511
|
+
* accepts any string, so the canonical vocabulary already is PostHog's.
|
|
512
|
+
*/
|
|
513
|
+
events?: Record<string, EventBinding<string>>;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* PostHog in the browser, through `posthog-js`.
|
|
517
|
+
*
|
|
518
|
+
* **This provider does not load PostHog's snippet.** It belongs on the page,
|
|
519
|
+
* installed the way PostHog documents it: a snippet in the HTML, or a tag
|
|
520
|
+
* manager. This library talks to the instance that is already there; it
|
|
521
|
+
* never injects a third-party script into your page.
|
|
522
|
+
*
|
|
523
|
+
* @param config - Rarely needed event name overrides.
|
|
524
|
+
* @returns A provider for `createTracking` from `tracklane/browser`.
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* ```ts
|
|
528
|
+
* import { createTracking, posthog } from 'tracklane/browser';
|
|
529
|
+
*
|
|
530
|
+
* // PostHog's own snippet is already in the page.
|
|
531
|
+
* const { track } = createTracking({ providers: [posthog()] });
|
|
532
|
+
*
|
|
533
|
+
* track('purchase', { transaction_id: 'T-1', value: 49.9, currency: 'BRL' });
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
declare function posthog(config?: PosthogBrowserConfig): BrowserProvider;
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region src/browser.d.ts
|
|
539
|
+
/**
|
|
540
|
+
* Everything the browser needs beyond the event itself.
|
|
541
|
+
*
|
|
542
|
+
* There is one field, and that is the point: in the browser each vendor tag
|
|
543
|
+
* reads its own cookies and holds its own session, so the only thing left
|
|
544
|
+
* for the host to supply is the value that ties this hit to a server-side
|
|
545
|
+
* one.
|
|
546
|
+
*/
|
|
547
|
+
interface TrackOptions {
|
|
548
|
+
/**
|
|
549
|
+
* The value that lets a vendor recognise this hit and a server-side hit as
|
|
550
|
+
* one event. The library never invents it: the browser and the server do
|
|
551
|
+
* not share a process, so only a value the host already owns (an order id)
|
|
552
|
+
* can match on both sides.
|
|
553
|
+
*/
|
|
554
|
+
dedupId?: string;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* A destination for browser events.
|
|
558
|
+
*
|
|
559
|
+
* This is the whole contract. A provider written outside this package
|
|
560
|
+
* implements it exactly as the providers shipped here do.
|
|
561
|
+
*/
|
|
562
|
+
interface BrowserProvider<Target = string> {
|
|
563
|
+
/** The vendor's fixed name. Registering one twice is an error. */
|
|
564
|
+
name: string;
|
|
565
|
+
/** What happens to a canonical event this vendor has no binding for. */
|
|
566
|
+
default: ProviderDefault;
|
|
567
|
+
/** How this vendor spells each canonical event. */
|
|
568
|
+
events?: Record<string, EventBinding<Target>>;
|
|
569
|
+
/**
|
|
570
|
+
* Loads the vendor tag, at creation.
|
|
571
|
+
*
|
|
572
|
+
* **Make it idempotent.** A host that rebuilds its tracking, as
|
|
573
|
+
* `@tracklane/consent` does on every consent answer, creates again and
|
|
574
|
+
* installs again, including providers that were already there.
|
|
575
|
+
*/
|
|
576
|
+
install?(): void | Promise<void>;
|
|
577
|
+
/** Sends one event to this vendor. Throwing here reaches `onError`, not the caller. */
|
|
578
|
+
track(name: Target, data: EventData, options: TrackOptions): void;
|
|
579
|
+
/** Absent where the vendor keeps no user of its own. */
|
|
580
|
+
identify?(user: UserData, traits?: Record<string, unknown>): void;
|
|
581
|
+
/** Absent where the vendor documents no consent command. */
|
|
582
|
+
consent?(command: "default" | "update", state: ConsentState): void;
|
|
583
|
+
}
|
|
584
|
+
/** Options accepted by {@link createTracking}. */
|
|
585
|
+
interface BrowserTrackingOptions {
|
|
586
|
+
/** The vendors to send every event to. Registering the same `name` twice throws. */
|
|
587
|
+
providers: readonly BrowserProvider[];
|
|
588
|
+
/**
|
|
589
|
+
* The one diagnostic channel. Without it the library is silent, exactly as
|
|
590
|
+
* a hand-written vendor call is.
|
|
591
|
+
*/
|
|
592
|
+
onError?: (error: TrackingError) => void;
|
|
593
|
+
}
|
|
594
|
+
/** What {@link createTracking} returns. */
|
|
595
|
+
interface BrowserTracking {
|
|
596
|
+
/** Sends one event to every configured vendor. Never throws. */
|
|
597
|
+
track(name: string, data?: EventData, options?: TrackOptions): void;
|
|
598
|
+
/** Forwards the user to the vendors that hold one, and to nobody else. */
|
|
599
|
+
identify(user: UserData, traits?: Record<string, unknown>): void;
|
|
600
|
+
/**
|
|
601
|
+
* Forwards a consent declaration to every vendor that documents a command
|
|
602
|
+
* for it. It never decides, stores or withholds anything.
|
|
603
|
+
*/
|
|
604
|
+
consent(command: "default" | "update", state: ConsentState): void;
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Creates the browser tracking surface.
|
|
608
|
+
*
|
|
609
|
+
* Providers install eagerly, which is what pasting a vendor snippet does and
|
|
610
|
+
* what lets each vendor record its own initial page view. Every vendor
|
|
611
|
+
* shipped here queues its own commands until its script lands, so nothing is
|
|
612
|
+
* lost to loading and this library needs no queue of its own.
|
|
613
|
+
*
|
|
614
|
+
* @param options - The providers to send to, and an optional error channel.
|
|
615
|
+
* @returns The `track`, `identify` and `consent` calls.
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* import { createTracking, ga4 } from 'tracklane/browser';
|
|
620
|
+
*
|
|
621
|
+
* const { track } = createTracking({ providers: [ga4('G-XXXXXXX')] });
|
|
622
|
+
* track('purchase', { transaction_id: 'T-1', value: 49.9, currency: 'BRL' });
|
|
623
|
+
* ```
|
|
624
|
+
*/
|
|
625
|
+
declare function createTracking(options: BrowserTrackingOptions): BrowserTracking;
|
|
626
|
+
//#endregion
|
|
627
|
+
export { MetaServerCredentials as A, ProviderDefault as C, PosthogServerCredentials as D, PosthogServerConfig as E, Ga4ServerConfig as M, Ga4ServerCredentials as N, posthog$1 as O, ga4$1 as P, EventItem as S, UserData as T, createTracking$1 as _, createTracking as a, EventBinding as b, MetaBrowserConfig as c, ga4 as d, EventContext as f, ServerTrackingOptions as g, ServerTracking as h, TrackOptions as i, meta$1 as j, MetaServerConfig as k, meta as l, ServerProvider as m, BrowserTracking as n, PosthogBrowserConfig as o, ResolvedContext as p, BrowserTrackingOptions as r, posthog as s, BrowserProvider as t, Ga4BrowserConfig as u, ActionSource as v, TrackingError as w, EventData as x, ConsentState as y };
|
|
628
|
+
//# sourceMappingURL=browser-FeV5oRFZ.d.ts.map
|