brainerce 1.56.0 → 1.57.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 +13 -0
- package/dist/index.d.mts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +90 -6
- package/dist/index.mjs +90 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2457,6 +2457,10 @@ const checkout = await client.setCheckoutCustomer(checkoutId, {
|
|
|
2457
2457
|
notes: 'Please leave the package at the door', // Optional order note (max 2000 chars)
|
|
2458
2458
|
// analyticsClientId / analyticsSessionId: auto-attached if you called
|
|
2459
2459
|
// loadGoogleAnalytics() — no need to pass these yourself.
|
|
2460
|
+
// trafficReferrerHost / trafficUtm*: auto-attached from the SDK's own
|
|
2461
|
+
// traffic-attribution capture (external referrer + utm, 30-day window) so
|
|
2462
|
+
// the dashboard can report "orders from ChatGPT / Google / …". Nothing to
|
|
2463
|
+
// wire up; pass explicitly only to override.
|
|
2460
2464
|
});
|
|
2461
2465
|
```
|
|
2462
2466
|
|
|
@@ -4094,6 +4098,15 @@ const cart = await client.createCart();
|
|
|
4094
4098
|
await client.addToCart(cart.id, { productId: 'prod_abc', quantity: 1 });
|
|
4095
4099
|
```
|
|
4096
4100
|
|
|
4101
|
+
### Traffic attribution (automatic, zero-config)
|
|
4102
|
+
|
|
4103
|
+
The SDK also records where each visit came from — the external referrer host
|
|
4104
|
+
and any `utm_source`/`utm_medium`/`utm_campaign` — as a **last non-direct
|
|
4105
|
+
touch** (`brainerce_attr` in localStorage, 30-day window). The captured values
|
|
4106
|
+
are auto-attached to `setCheckoutCustomer()` / `setShippingAddress()` and end
|
|
4107
|
+
up on the order, powering the dashboard's "orders from ChatGPT / Google / …"
|
|
4108
|
+
reporting. Nothing to configure; a value you pass explicitly always wins.
|
|
4109
|
+
|
|
4097
4110
|
What this does:
|
|
4098
4111
|
|
|
4099
4112
|
- Idempotently injects `gtag.js` and initializes `dataLayer` (skips injection if you're already loading `gtag.js` yourself — safe to call either way).
|
package/dist/index.d.mts
CHANGED
|
@@ -3294,6 +3294,16 @@ interface SetCheckoutCustomerDto {
|
|
|
3294
3294
|
*/
|
|
3295
3295
|
analyticsClientId?: string;
|
|
3296
3296
|
analyticsSessionId?: string;
|
|
3297
|
+
/**
|
|
3298
|
+
* Traffic attribution (last non-direct touch) — auto-attached by the SDK
|
|
3299
|
+
* from its `brainerce_attr` capture (external referrer host + utm params,
|
|
3300
|
+
* 30-day window). Lets the dashboard report "orders from ChatGPT/Google/…".
|
|
3301
|
+
* Pass explicitly to override; omit to use the captured values.
|
|
3302
|
+
*/
|
|
3303
|
+
trafficReferrerHost?: string;
|
|
3304
|
+
trafficUtmSource?: string;
|
|
3305
|
+
trafficUtmMedium?: string;
|
|
3306
|
+
trafficUtmCampaign?: string;
|
|
3297
3307
|
}
|
|
3298
3308
|
/**
|
|
3299
3309
|
* Shipping address with customer email (required for checkout).
|
|
@@ -3327,6 +3337,16 @@ interface SetShippingAddressDto {
|
|
|
3327
3337
|
*/
|
|
3328
3338
|
analyticsClientId?: string;
|
|
3329
3339
|
analyticsSessionId?: string;
|
|
3340
|
+
/**
|
|
3341
|
+
* Traffic attribution (last non-direct touch) — auto-attached by the SDK
|
|
3342
|
+
* from its `brainerce_attr` capture (external referrer host + utm params,
|
|
3343
|
+
* 30-day window). Lets the dashboard report "orders from ChatGPT/Google/…".
|
|
3344
|
+
* Pass explicitly to override; omit to use the captured values.
|
|
3345
|
+
*/
|
|
3346
|
+
trafficReferrerHost?: string;
|
|
3347
|
+
trafficUtmSource?: string;
|
|
3348
|
+
trafficUtmMedium?: string;
|
|
3349
|
+
trafficUtmCampaign?: string;
|
|
3330
3350
|
/**
|
|
3331
3351
|
* The `placeId` of the autocomplete suggestion the shopper picked (from
|
|
3332
3352
|
* `addressAutocomplete()`). Send it whenever the address came from the
|
|
@@ -6847,6 +6867,26 @@ declare class BrainerceClient {
|
|
|
6847
6867
|
* No-op (returns `dto` unchanged) if `loadGoogleAnalytics()` was never
|
|
6848
6868
|
* called, or if it hasn't resolved any ids by the time this is awaited.
|
|
6849
6869
|
*/
|
|
6870
|
+
/** localStorage key holding the last non-direct-touch attribution blob. */
|
|
6871
|
+
private readonly TRAFFIC_ATTR_KEY;
|
|
6872
|
+
/** Attribution older than this is stale and never forwarded (classic 30-day window). */
|
|
6873
|
+
private static readonly TRAFFIC_ATTR_MAX_AGE_MS;
|
|
6874
|
+
/**
|
|
6875
|
+
* Record the visit's traffic origin — LAST NON-DIRECT TOUCH semantics: an
|
|
6876
|
+
* external referrer or any utm_source overwrites the stored blob; a direct
|
|
6877
|
+
* or internal navigation keeps the previous touch. Runs once per client
|
|
6878
|
+
* construction (i.e. per page load in browser storefronts) and must never
|
|
6879
|
+
* throw — attribution is telemetry, the storefront always wins.
|
|
6880
|
+
*/
|
|
6881
|
+
private captureTrafficAttribution;
|
|
6882
|
+
/** The stored attribution as request-body fields, or null when absent/stale. */
|
|
6883
|
+
private getTrafficAttribution;
|
|
6884
|
+
/**
|
|
6885
|
+
* Merge the stored traffic attribution onto a request body — only for
|
|
6886
|
+
* fields the caller didn't set explicitly (explicit values always win),
|
|
6887
|
+
* mirroring `withAnalyticsStitchIds`. No-op outside the browser.
|
|
6888
|
+
*/
|
|
6889
|
+
private withTrafficAttribution;
|
|
6850
6890
|
private withAnalyticsStitchIds;
|
|
6851
6891
|
/**
|
|
6852
6892
|
* Drop the fields `getAddressDetails()` returns that no address endpoint
|
package/dist/index.d.ts
CHANGED
|
@@ -3294,6 +3294,16 @@ interface SetCheckoutCustomerDto {
|
|
|
3294
3294
|
*/
|
|
3295
3295
|
analyticsClientId?: string;
|
|
3296
3296
|
analyticsSessionId?: string;
|
|
3297
|
+
/**
|
|
3298
|
+
* Traffic attribution (last non-direct touch) — auto-attached by the SDK
|
|
3299
|
+
* from its `brainerce_attr` capture (external referrer host + utm params,
|
|
3300
|
+
* 30-day window). Lets the dashboard report "orders from ChatGPT/Google/…".
|
|
3301
|
+
* Pass explicitly to override; omit to use the captured values.
|
|
3302
|
+
*/
|
|
3303
|
+
trafficReferrerHost?: string;
|
|
3304
|
+
trafficUtmSource?: string;
|
|
3305
|
+
trafficUtmMedium?: string;
|
|
3306
|
+
trafficUtmCampaign?: string;
|
|
3297
3307
|
}
|
|
3298
3308
|
/**
|
|
3299
3309
|
* Shipping address with customer email (required for checkout).
|
|
@@ -3327,6 +3337,16 @@ interface SetShippingAddressDto {
|
|
|
3327
3337
|
*/
|
|
3328
3338
|
analyticsClientId?: string;
|
|
3329
3339
|
analyticsSessionId?: string;
|
|
3340
|
+
/**
|
|
3341
|
+
* Traffic attribution (last non-direct touch) — auto-attached by the SDK
|
|
3342
|
+
* from its `brainerce_attr` capture (external referrer host + utm params,
|
|
3343
|
+
* 30-day window). Lets the dashboard report "orders from ChatGPT/Google/…".
|
|
3344
|
+
* Pass explicitly to override; omit to use the captured values.
|
|
3345
|
+
*/
|
|
3346
|
+
trafficReferrerHost?: string;
|
|
3347
|
+
trafficUtmSource?: string;
|
|
3348
|
+
trafficUtmMedium?: string;
|
|
3349
|
+
trafficUtmCampaign?: string;
|
|
3330
3350
|
/**
|
|
3331
3351
|
* The `placeId` of the autocomplete suggestion the shopper picked (from
|
|
3332
3352
|
* `addressAutocomplete()`). Send it whenever the address came from the
|
|
@@ -6847,6 +6867,26 @@ declare class BrainerceClient {
|
|
|
6847
6867
|
* No-op (returns `dto` unchanged) if `loadGoogleAnalytics()` was never
|
|
6848
6868
|
* called, or if it hasn't resolved any ids by the time this is awaited.
|
|
6849
6869
|
*/
|
|
6870
|
+
/** localStorage key holding the last non-direct-touch attribution blob. */
|
|
6871
|
+
private readonly TRAFFIC_ATTR_KEY;
|
|
6872
|
+
/** Attribution older than this is stale and never forwarded (classic 30-day window). */
|
|
6873
|
+
private static readonly TRAFFIC_ATTR_MAX_AGE_MS;
|
|
6874
|
+
/**
|
|
6875
|
+
* Record the visit's traffic origin — LAST NON-DIRECT TOUCH semantics: an
|
|
6876
|
+
* external referrer or any utm_source overwrites the stored blob; a direct
|
|
6877
|
+
* or internal navigation keeps the previous touch. Runs once per client
|
|
6878
|
+
* construction (i.e. per page load in browser storefronts) and must never
|
|
6879
|
+
* throw — attribution is telemetry, the storefront always wins.
|
|
6880
|
+
*/
|
|
6881
|
+
private captureTrafficAttribution;
|
|
6882
|
+
/** The stored attribution as request-body fields, or null when absent/stale. */
|
|
6883
|
+
private getTrafficAttribution;
|
|
6884
|
+
/**
|
|
6885
|
+
* Merge the stored traffic attribution onto a request body — only for
|
|
6886
|
+
* fields the caller didn't set explicitly (explicit values always win),
|
|
6887
|
+
* mirroring `withAnalyticsStitchIds`. No-op outside the browser.
|
|
6888
|
+
*/
|
|
6889
|
+
private withTrafficAttribution;
|
|
6850
6890
|
private withAnalyticsStitchIds;
|
|
6851
6891
|
/**
|
|
6852
6892
|
* Drop the fields `getAddressDetails()` returns that no address endpoint
|
package/dist/index.js
CHANGED
|
@@ -299,6 +299,14 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
299
299
|
* This is needed because Stripe redirects lose in-memory state.
|
|
300
300
|
*/
|
|
301
301
|
this.ACTIVE_CHECKOUT_KEY = "brainerce_active_checkout";
|
|
302
|
+
/**
|
|
303
|
+
* Merge the resolved GA4 stitch ids onto a request body — only for fields
|
|
304
|
+
* the caller didn't already set explicitly (explicit values always win).
|
|
305
|
+
* No-op (returns `dto` unchanged) if `loadGoogleAnalytics()` was never
|
|
306
|
+
* called, or if it hasn't resolved any ids by the time this is awaited.
|
|
307
|
+
*/
|
|
308
|
+
/** localStorage key holding the last non-direct-touch attribution blob. */
|
|
309
|
+
this.TRAFFIC_ATTR_KEY = "brainerce_attr";
|
|
302
310
|
// -------------------- Contact Forms (schema) --------------------
|
|
303
311
|
/**
|
|
304
312
|
* List active contact forms configured for the store.
|
|
@@ -704,6 +712,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
704
712
|
this.onCartReset = options.onCartReset;
|
|
705
713
|
this.hydrateSessionCart();
|
|
706
714
|
this.detectRecoverCartFromUrl();
|
|
715
|
+
this.captureTrafficAttribution();
|
|
707
716
|
}
|
|
708
717
|
// -------------------- Locale --------------------
|
|
709
718
|
/**
|
|
@@ -1526,11 +1535,82 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
1526
1535
|
}
|
|
1527
1536
|
}
|
|
1528
1537
|
/**
|
|
1529
|
-
*
|
|
1530
|
-
*
|
|
1531
|
-
*
|
|
1532
|
-
*
|
|
1538
|
+
* Record the visit's traffic origin — LAST NON-DIRECT TOUCH semantics: an
|
|
1539
|
+
* external referrer or any utm_source overwrites the stored blob; a direct
|
|
1540
|
+
* or internal navigation keeps the previous touch. Runs once per client
|
|
1541
|
+
* construction (i.e. per page load in browser storefronts) and must never
|
|
1542
|
+
* throw — attribution is telemetry, the storefront always wins.
|
|
1543
|
+
*/
|
|
1544
|
+
captureTrafficAttribution() {
|
|
1545
|
+
try {
|
|
1546
|
+
if (typeof window === "undefined" || !window.localStorage) return;
|
|
1547
|
+
const params = new URLSearchParams(window.location.search);
|
|
1548
|
+
const utmSource = params.get("utm_source") || void 0;
|
|
1549
|
+
const utmMedium = params.get("utm_medium") || void 0;
|
|
1550
|
+
const utmCampaign = params.get("utm_campaign") || void 0;
|
|
1551
|
+
let referrerHost;
|
|
1552
|
+
if (document.referrer) {
|
|
1553
|
+
try {
|
|
1554
|
+
const ref = new URL(document.referrer);
|
|
1555
|
+
if (ref.hostname && ref.hostname !== window.location.hostname) {
|
|
1556
|
+
referrerHost = ref.hostname.toLowerCase().replace(/^www\./, "");
|
|
1557
|
+
}
|
|
1558
|
+
} catch {
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
if (!referrerHost && !utmSource) return;
|
|
1562
|
+
window.localStorage.setItem(
|
|
1563
|
+
this.TRAFFIC_ATTR_KEY,
|
|
1564
|
+
JSON.stringify({ referrerHost, utmSource, utmMedium, utmCampaign, at: Date.now() })
|
|
1565
|
+
);
|
|
1566
|
+
} catch {
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
/** The stored attribution as request-body fields, or null when absent/stale. */
|
|
1570
|
+
getTrafficAttribution() {
|
|
1571
|
+
try {
|
|
1572
|
+
if (typeof window === "undefined" || !window.localStorage) return null;
|
|
1573
|
+
const raw = window.localStorage.getItem(this.TRAFFIC_ATTR_KEY);
|
|
1574
|
+
if (!raw) return null;
|
|
1575
|
+
const blob = JSON.parse(raw);
|
|
1576
|
+
if (!blob || typeof blob !== "object") return null;
|
|
1577
|
+
if (typeof blob.at !== "number" || Date.now() - blob.at > _BrainerceClient.TRAFFIC_ATTR_MAX_AGE_MS) {
|
|
1578
|
+
return null;
|
|
1579
|
+
}
|
|
1580
|
+
const out = {};
|
|
1581
|
+
if (typeof blob.referrerHost === "string" && blob.referrerHost) {
|
|
1582
|
+
out.trafficReferrerHost = blob.referrerHost.slice(0, 253);
|
|
1583
|
+
}
|
|
1584
|
+
if (typeof blob.utmSource === "string" && blob.utmSource) {
|
|
1585
|
+
out.trafficUtmSource = blob.utmSource.slice(0, 150);
|
|
1586
|
+
}
|
|
1587
|
+
if (typeof blob.utmMedium === "string" && blob.utmMedium) {
|
|
1588
|
+
out.trafficUtmMedium = blob.utmMedium.slice(0, 150);
|
|
1589
|
+
}
|
|
1590
|
+
if (typeof blob.utmCampaign === "string" && blob.utmCampaign) {
|
|
1591
|
+
out.trafficUtmCampaign = blob.utmCampaign.slice(0, 150);
|
|
1592
|
+
}
|
|
1593
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
1594
|
+
} catch {
|
|
1595
|
+
return null;
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Merge the stored traffic attribution onto a request body — only for
|
|
1600
|
+
* fields the caller didn't set explicitly (explicit values always win),
|
|
1601
|
+
* mirroring `withAnalyticsStitchIds`. No-op outside the browser.
|
|
1533
1602
|
*/
|
|
1603
|
+
withTrafficAttribution(dto) {
|
|
1604
|
+
const attr = this.getTrafficAttribution();
|
|
1605
|
+
if (!attr) return dto;
|
|
1606
|
+
return {
|
|
1607
|
+
...dto ?? {},
|
|
1608
|
+
trafficReferrerHost: dto?.trafficReferrerHost ?? attr.trafficReferrerHost,
|
|
1609
|
+
trafficUtmSource: dto?.trafficUtmSource ?? attr.trafficUtmSource,
|
|
1610
|
+
trafficUtmMedium: dto?.trafficUtmMedium ?? attr.trafficUtmMedium,
|
|
1611
|
+
trafficUtmCampaign: dto?.trafficUtmCampaign ?? attr.trafficUtmCampaign
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1534
1614
|
async withAnalyticsStitchIds(dto) {
|
|
1535
1615
|
if (!this._ga4StitchPromise) return dto;
|
|
1536
1616
|
try {
|
|
@@ -5387,7 +5467,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5387
5467
|
* ```
|
|
5388
5468
|
*/
|
|
5389
5469
|
async setCheckoutCustomer(checkoutId, data) {
|
|
5390
|
-
const body = await this.withAnalyticsStitchIds(data);
|
|
5470
|
+
const body = this.withTrafficAttribution(await this.withAnalyticsStitchIds(data));
|
|
5391
5471
|
if (this.isVibeCodedMode()) {
|
|
5392
5472
|
return this.vibeCodedRequest(
|
|
5393
5473
|
"PATCH",
|
|
@@ -5489,7 +5569,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5489
5569
|
* ```
|
|
5490
5570
|
*/
|
|
5491
5571
|
async setShippingAddress(checkoutId, address) {
|
|
5492
|
-
const body =
|
|
5572
|
+
const body = this.withTrafficAttribution(
|
|
5573
|
+
await this.withAnalyticsStitchIds(this.stripResolvedOnlyAddressFields(address))
|
|
5574
|
+
);
|
|
5493
5575
|
if (this.isVibeCodedMode()) {
|
|
5494
5576
|
return this.vibeCodedRequest(
|
|
5495
5577
|
"PATCH",
|
|
@@ -9716,6 +9798,8 @@ _BrainerceClient.RESOLVED_ONLY_ADDRESS_FIELDS = [
|
|
|
9716
9798
|
"lng",
|
|
9717
9799
|
"formattedAddress"
|
|
9718
9800
|
];
|
|
9801
|
+
/** Attribution older than this is stale and never forwarded (classic 30-day window). */
|
|
9802
|
+
_BrainerceClient.TRAFFIC_ATTR_MAX_AGE_MS = 30 * 24 * 3600 * 1e3;
|
|
9719
9803
|
var BrainerceClient = _BrainerceClient;
|
|
9720
9804
|
var BrainerceError = class extends Error {
|
|
9721
9805
|
constructor(message, statusCode, details) {
|
package/dist/index.mjs
CHANGED
|
@@ -212,6 +212,14 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
212
212
|
* This is needed because Stripe redirects lose in-memory state.
|
|
213
213
|
*/
|
|
214
214
|
this.ACTIVE_CHECKOUT_KEY = "brainerce_active_checkout";
|
|
215
|
+
/**
|
|
216
|
+
* Merge the resolved GA4 stitch ids onto a request body — only for fields
|
|
217
|
+
* the caller didn't already set explicitly (explicit values always win).
|
|
218
|
+
* No-op (returns `dto` unchanged) if `loadGoogleAnalytics()` was never
|
|
219
|
+
* called, or if it hasn't resolved any ids by the time this is awaited.
|
|
220
|
+
*/
|
|
221
|
+
/** localStorage key holding the last non-direct-touch attribution blob. */
|
|
222
|
+
this.TRAFFIC_ATTR_KEY = "brainerce_attr";
|
|
215
223
|
// -------------------- Contact Forms (schema) --------------------
|
|
216
224
|
/**
|
|
217
225
|
* List active contact forms configured for the store.
|
|
@@ -617,6 +625,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
617
625
|
this.onCartReset = options.onCartReset;
|
|
618
626
|
this.hydrateSessionCart();
|
|
619
627
|
this.detectRecoverCartFromUrl();
|
|
628
|
+
this.captureTrafficAttribution();
|
|
620
629
|
}
|
|
621
630
|
// -------------------- Locale --------------------
|
|
622
631
|
/**
|
|
@@ -1439,11 +1448,82 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
1439
1448
|
}
|
|
1440
1449
|
}
|
|
1441
1450
|
/**
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1444
|
-
*
|
|
1445
|
-
*
|
|
1451
|
+
* Record the visit's traffic origin — LAST NON-DIRECT TOUCH semantics: an
|
|
1452
|
+
* external referrer or any utm_source overwrites the stored blob; a direct
|
|
1453
|
+
* or internal navigation keeps the previous touch. Runs once per client
|
|
1454
|
+
* construction (i.e. per page load in browser storefronts) and must never
|
|
1455
|
+
* throw — attribution is telemetry, the storefront always wins.
|
|
1456
|
+
*/
|
|
1457
|
+
captureTrafficAttribution() {
|
|
1458
|
+
try {
|
|
1459
|
+
if (typeof window === "undefined" || !window.localStorage) return;
|
|
1460
|
+
const params = new URLSearchParams(window.location.search);
|
|
1461
|
+
const utmSource = params.get("utm_source") || void 0;
|
|
1462
|
+
const utmMedium = params.get("utm_medium") || void 0;
|
|
1463
|
+
const utmCampaign = params.get("utm_campaign") || void 0;
|
|
1464
|
+
let referrerHost;
|
|
1465
|
+
if (document.referrer) {
|
|
1466
|
+
try {
|
|
1467
|
+
const ref = new URL(document.referrer);
|
|
1468
|
+
if (ref.hostname && ref.hostname !== window.location.hostname) {
|
|
1469
|
+
referrerHost = ref.hostname.toLowerCase().replace(/^www\./, "");
|
|
1470
|
+
}
|
|
1471
|
+
} catch {
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
if (!referrerHost && !utmSource) return;
|
|
1475
|
+
window.localStorage.setItem(
|
|
1476
|
+
this.TRAFFIC_ATTR_KEY,
|
|
1477
|
+
JSON.stringify({ referrerHost, utmSource, utmMedium, utmCampaign, at: Date.now() })
|
|
1478
|
+
);
|
|
1479
|
+
} catch {
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
/** The stored attribution as request-body fields, or null when absent/stale. */
|
|
1483
|
+
getTrafficAttribution() {
|
|
1484
|
+
try {
|
|
1485
|
+
if (typeof window === "undefined" || !window.localStorage) return null;
|
|
1486
|
+
const raw = window.localStorage.getItem(this.TRAFFIC_ATTR_KEY);
|
|
1487
|
+
if (!raw) return null;
|
|
1488
|
+
const blob = JSON.parse(raw);
|
|
1489
|
+
if (!blob || typeof blob !== "object") return null;
|
|
1490
|
+
if (typeof blob.at !== "number" || Date.now() - blob.at > _BrainerceClient.TRAFFIC_ATTR_MAX_AGE_MS) {
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
const out = {};
|
|
1494
|
+
if (typeof blob.referrerHost === "string" && blob.referrerHost) {
|
|
1495
|
+
out.trafficReferrerHost = blob.referrerHost.slice(0, 253);
|
|
1496
|
+
}
|
|
1497
|
+
if (typeof blob.utmSource === "string" && blob.utmSource) {
|
|
1498
|
+
out.trafficUtmSource = blob.utmSource.slice(0, 150);
|
|
1499
|
+
}
|
|
1500
|
+
if (typeof blob.utmMedium === "string" && blob.utmMedium) {
|
|
1501
|
+
out.trafficUtmMedium = blob.utmMedium.slice(0, 150);
|
|
1502
|
+
}
|
|
1503
|
+
if (typeof blob.utmCampaign === "string" && blob.utmCampaign) {
|
|
1504
|
+
out.trafficUtmCampaign = blob.utmCampaign.slice(0, 150);
|
|
1505
|
+
}
|
|
1506
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
1507
|
+
} catch {
|
|
1508
|
+
return null;
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Merge the stored traffic attribution onto a request body — only for
|
|
1513
|
+
* fields the caller didn't set explicitly (explicit values always win),
|
|
1514
|
+
* mirroring `withAnalyticsStitchIds`. No-op outside the browser.
|
|
1446
1515
|
*/
|
|
1516
|
+
withTrafficAttribution(dto) {
|
|
1517
|
+
const attr = this.getTrafficAttribution();
|
|
1518
|
+
if (!attr) return dto;
|
|
1519
|
+
return {
|
|
1520
|
+
...dto ?? {},
|
|
1521
|
+
trafficReferrerHost: dto?.trafficReferrerHost ?? attr.trafficReferrerHost,
|
|
1522
|
+
trafficUtmSource: dto?.trafficUtmSource ?? attr.trafficUtmSource,
|
|
1523
|
+
trafficUtmMedium: dto?.trafficUtmMedium ?? attr.trafficUtmMedium,
|
|
1524
|
+
trafficUtmCampaign: dto?.trafficUtmCampaign ?? attr.trafficUtmCampaign
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1447
1527
|
async withAnalyticsStitchIds(dto) {
|
|
1448
1528
|
if (!this._ga4StitchPromise) return dto;
|
|
1449
1529
|
try {
|
|
@@ -5300,7 +5380,7 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5300
5380
|
* ```
|
|
5301
5381
|
*/
|
|
5302
5382
|
async setCheckoutCustomer(checkoutId, data) {
|
|
5303
|
-
const body = await this.withAnalyticsStitchIds(data);
|
|
5383
|
+
const body = this.withTrafficAttribution(await this.withAnalyticsStitchIds(data));
|
|
5304
5384
|
if (this.isVibeCodedMode()) {
|
|
5305
5385
|
return this.vibeCodedRequest(
|
|
5306
5386
|
"PATCH",
|
|
@@ -5402,7 +5482,9 @@ var _BrainerceClient = class _BrainerceClient {
|
|
|
5402
5482
|
* ```
|
|
5403
5483
|
*/
|
|
5404
5484
|
async setShippingAddress(checkoutId, address) {
|
|
5405
|
-
const body =
|
|
5485
|
+
const body = this.withTrafficAttribution(
|
|
5486
|
+
await this.withAnalyticsStitchIds(this.stripResolvedOnlyAddressFields(address))
|
|
5487
|
+
);
|
|
5406
5488
|
if (this.isVibeCodedMode()) {
|
|
5407
5489
|
return this.vibeCodedRequest(
|
|
5408
5490
|
"PATCH",
|
|
@@ -9629,6 +9711,8 @@ _BrainerceClient.RESOLVED_ONLY_ADDRESS_FIELDS = [
|
|
|
9629
9711
|
"lng",
|
|
9630
9712
|
"formattedAddress"
|
|
9631
9713
|
];
|
|
9714
|
+
/** Attribution older than this is stale and never forwarded (classic 30-day window). */
|
|
9715
|
+
_BrainerceClient.TRAFFIC_ATTR_MAX_AGE_MS = 30 * 24 * 3600 * 1e3;
|
|
9632
9716
|
var BrainerceClient = _BrainerceClient;
|
|
9633
9717
|
var BrainerceError = class extends Error {
|
|
9634
9718
|
constructor(message, statusCode, details) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.57.0",
|
|
4
4
|
"description": "Official SDK for building e-commerce storefronts with Brainerce Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|