@tracelog/lib 2.8.5-rc.106.8 → 2.8.5-rc.106.9
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/dist/browser/tracelog-shopify-pixel.iife.js +1 -1
- package/dist/browser/tracelog-shopify-pixel.iife.js.map +1 -1
- package/dist/browser/tracelog.esm.js +34 -41
- package/dist/browser/tracelog.esm.js.map +1 -1
- package/dist/browser/tracelog.js +1 -1
- package/dist/browser/tracelog.js.map +1 -1
- package/dist/pixel/index.cjs +2 -2
- package/dist/pixel/index.cjs.map +1 -1
- package/dist/pixel/index.d.mts +8 -1
- package/dist/pixel/index.d.ts +8 -1
- package/dist/pixel/index.js +2 -2
- package/dist/pixel/index.js.map +1 -1
- package/dist/public-api.cjs +1 -1
- package/dist/public-api.cjs.map +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
var TraceLogShopifyPixel=function(t){"use strict";
|
|
1
|
+
var TraceLogShopifyPixel=function(t){"use strict";const e=["cart_viewed","checkout_started","checkout_contact_info_submitted","checkout_address_info_submitted","checkout_shipping_info_submitted","payment_info_submitted","checkout_completed"],n=e.map(t=>`shopify_${t}`);function i(t,e){if(!Array.isArray(t))return null;for(const n of t)if(n.key===e&&"string"==typeof n.value&&n.value.length>0)return n.value;return null}function o(t){return"number"==typeof t&&Number.isFinite(t)?t:void 0}function c(t){return"string"==typeof t&&t.length>0?t:void 0}function r(t,e,n){void 0!==n&&(t[e]=n)}function u(t,e){const n=t.data?.checkout,i=t.data?.cart,u={};r(u,"shopify_client_id",c(t.clientId));const d=c(n?.token);if(void 0!==d&&(u.checkout_token=d),"checkout_completed"===e){r(u,"value",o(n?.totalPrice?.amount??n?.subtotalPrice?.amount)),r(u,"currency",c(n?.currencyCode??n?.totalPrice?.currencyCode));const t=n?.order?.id;"string"!=typeof t&&"number"!=typeof t||(u.orderId=String(t));const e=n?.lineItems??[],i=function(t){if(!t||0===t.length)return[];const e=t.slice(0,100);return e.map(t=>{const e={},n=t.variant?.id;return"string"!=typeof n&&"number"!=typeof n||(e.id=String(n)),r(e,"title",f(_(c(t.title??t.variant?.product?.title)),a)),r(e,"quantity",o(t.quantity)),r(e,"price",o(t.variant?.price?.amount??t.finalLinePrice?.amount)),r(e,"sku",f(_(c(t.variant?.sku??void 0)),l)),r(e,"vendor",f(_(c(t.variant?.product?.vendor)),a)),e})}(e);i.length>0&&(u.items=i,e.length>100&&(u.items_truncated=!0))}else if("cart_viewed"===e){r(u,"cart_total",o(i?.cost?.totalAmount?.amount??i?.totalAmount?.amount));r(u,"item_count",s(i?.lines)??s(i?.lineItems))}else if("checkout_started"===e){r(u,"cart_total",o(n?.totalPrice?.amount??n?.subtotalPrice?.amount)),r(u,"currency",c(n?.currencyCode??n?.totalPrice?.currencyCode));r(u,"item_count",s(n?.lineItems)??s(t.data?.checkoutLineItems))}else{r(u,"currency",c(n?.currencyCode??n?.totalPrice?.currencyCode));const t=n?.totalPrice?.amount;r(u,"cart_total",o(t))}return u}function s(t){if(!t||0===t.length)return;let e=0,n=!1;for(const i of t){const t=o(i.quantity);void 0!==t&&(n=!0,e+=t)}return n?e:t.length}const a=255,l=128,d=[/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,/javascript:/gi,/on\w+\s*=/gi,/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,/<embed\b[^>]*>/gi,/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi];function _(t){if(void 0===t)return;let e=t;for(const n of d)e=e.replace(n,"");return e}function f(t,e){if(void 0!==t)return t.length>e?t.slice(0,e):t}function m(t){const e=c(t.context?.window?.location?.href)??c(t.context?.document?.location?.href);return void 0!==e?function(t){const e=t.indexOf("?"),n=t.indexOf("#");let i=t.length;return-1!==e&&(i=e),-1!==n&&n<i&&(i=n),t.slice(0,i)}(e):"unknown"}function p(t,e){if(!t)return null;const n=t.data?.checkout?.attributes,o=t.data?.cart?.attributes,r=i(n,"tracelog_session_id")??i(o,"tracelog_session_id"),s=i(n,"tracelog_user_id")??i(o,"tracelog_user_id");if(null===r||null===s)return null;const a=function(t){if("string"!=typeof t||0===t.length)return Date.now();const e=Date.parse(t);return Number.isFinite(e)?e:Date.now()}(t.timestamp),l=function(t,e){return`${e}-001-${c(t.id)?.slice(-6)??Math.random().toString(36).slice(2,8)}`}(t,a),d=u(t,e);return{user_id:s,session_id:r,device:{type:"unknown",os:"unknown",browser:"unknown"},events:[{id:l,type:"custom",page_url:m(t),timestamp:a,custom_event:{name:`shopify_${e}`,metadata:d}}],_metadata:{client_version:"shopify-web-pixel-1",timestamp:a}}}function h(t,e){if(!t.projectId)return;const n=`https://ingest.tracelog.io/p/${encodeURIComponent(t.projectId)}/collect`;try{fetch(n,{method:"POST",headers:{"Content-Type":"application/json"},keepalive:!0,body:JSON.stringify(e)}).catch(()=>{})}catch{}}return t.SHOPIFY_EVENTS=e,t.SHOPIFY_PIXEL_EVENT_NAMES=n,t.mapEventToBody=p,t.registerShopifyPixel=function(t,e){const n=(t,n)=>{const i=p(n,t);i&&h(e,i)};t.analytics.subscribe("cart_viewed",t=>{n("cart_viewed",t)}),t.analytics.subscribe("checkout_started",t=>{n("checkout_started",t)}),t.analytics.subscribe("checkout_contact_info_submitted",t=>{n("checkout_contact_info_submitted",t)}),t.analytics.subscribe("checkout_address_info_submitted",t=>{n("checkout_address_info_submitted",t)}),t.analytics.subscribe("checkout_shipping_info_submitted",t=>{n("checkout_shipping_info_submitted",t)}),t.analytics.subscribe("payment_info_submitted",t=>{n("payment_info_submitted",t)}),t.analytics.subscribe("checkout_completed",t=>{n("checkout_completed",t)})},t.sendBatch=h,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),t}({});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracelog-shopify-pixel.iife.js","sources":["../../src/pixel/event-mapper.ts","../../src/pixel/pixel-sender.ts","../../src/pixel/shopify-pixel.ts"],"sourcesContent":["import type { PixelEventBody } from './pixel-sender';\n\nconst PIXEL_CLIENT_VERSION = 'shopify-web-pixel-1';\n\nconst MAX_LINE_ITEMS = 100;\n\nexport const SHOPIFY_EVENTS = [\n 'cart_viewed',\n 'checkout_started',\n 'checkout_contact_info_submitted',\n 'checkout_address_info_submitted',\n 'checkout_shipping_info_submitted',\n 'payment_info_submitted',\n 'checkout_completed',\n] as const;\n\nexport type ShopifyEventName = (typeof SHOPIFY_EVENTS)[number];\n\n/**\n * Spike-confirmed shape ([04-spike-report.md](../../docs/tasks/shopify-hybrid-capture/04-spike-report.md)):\n * `event.data.{cart,checkout}.attributes` is `Array<{key, value, __typename?}>`,\n * NOT a plain object. `__typename: 'NoteAttribute'` is added on `checkout_completed`\n * only — harmless because we lookup by `key`.\n */\ninterface ShopifyAttribute {\n key?: string;\n value?: string;\n __typename?: string;\n}\n\ninterface ShopifyMoney {\n amount?: number;\n currencyCode?: string;\n}\n\ninterface ShopifyLineItemVariantProduct {\n id?: string | number;\n title?: string;\n vendor?: string;\n}\n\ninterface ShopifyLineItemVariant {\n id?: string | number;\n sku?: string | null;\n price?: ShopifyMoney;\n product?: ShopifyLineItemVariantProduct;\n}\n\ninterface ShopifyLineItem {\n id?: string | number;\n title?: string;\n quantity?: number;\n variant?: ShopifyLineItemVariant;\n finalLinePrice?: ShopifyMoney;\n}\n\ninterface ShopifyCheckout {\n token?: string;\n currencyCode?: string;\n totalPrice?: ShopifyMoney;\n subtotalPrice?: ShopifyMoney;\n attributes?: ShopifyAttribute[];\n lineItems?: ShopifyLineItem[];\n order?: { id?: string | number };\n}\n\ninterface ShopifyCart {\n attributes?: ShopifyAttribute[];\n totalAmount?: ShopifyMoney;\n cost?: { totalAmount?: ShopifyMoney };\n lines?: ShopifyLineItem[];\n lineItems?: ShopifyLineItem[];\n}\n\ninterface ShopifyCheckoutLineItem {\n quantity?: number;\n}\n\ninterface ShopifyEvent {\n id?: string;\n timestamp?: string;\n clientId?: string;\n data?: {\n checkout?: ShopifyCheckout;\n cart?: ShopifyCart;\n checkoutLineItems?: ShopifyCheckoutLineItem[];\n };\n context?: {\n window?: { location?: { href?: string } };\n document?: { location?: { href?: string } };\n };\n}\n\nfunction attrLookup(attrs: ShopifyAttribute[] | undefined, key: string): string | null {\n if (!Array.isArray(attrs)) return null;\n for (const a of attrs) {\n if (a.key === key && typeof a.value === 'string' && a.value.length > 0) return a.value;\n }\n return null;\n}\n\nfunction safeNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction safeString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction setIfDefined<T>(target: Record<string, unknown>, key: string, value: T | undefined): void {\n if (value !== undefined) target[key] = value;\n}\n\nfunction buildMetadata(event: ShopifyEvent, name: ShopifyEventName): Record<string, unknown> {\n const checkout = event.data?.checkout;\n const cart = event.data?.cart;\n const meta: Record<string, unknown> = {};\n\n setIfDefined(meta, 'shopify_client_id', safeString(event.clientId));\n\n const checkoutToken = safeString(checkout?.token);\n if (checkoutToken !== undefined) meta['checkout_token'] = checkoutToken;\n\n if (name === 'checkout_completed') {\n const total = checkout?.totalPrice?.amount ?? checkout?.subtotalPrice?.amount;\n setIfDefined(meta, 'value', safeNumber(total));\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const orderId = checkout?.order?.id;\n if (typeof orderId === 'string' || typeof orderId === 'number') meta['orderId'] = String(orderId);\n const rawItems = checkout?.lineItems ?? [];\n const items = mapLineItems(rawItems);\n if (items.length > 0) {\n meta['items'] = items;\n if (rawItems.length > MAX_LINE_ITEMS) meta['items_truncated'] = true;\n }\n } else if (name === 'cart_viewed') {\n const cartTotal = cart?.cost?.totalAmount?.amount ?? cart?.totalAmount?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(cartTotal));\n const itemCount = countLineItems(cart?.lines) ?? countLineItems(cart?.lineItems);\n setIfDefined(meta, 'item_count', itemCount);\n } else if (name === 'checkout_started') {\n const total = checkout?.totalPrice?.amount ?? checkout?.subtotalPrice?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(total));\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const itemCount = countLineItems(checkout?.lineItems) ?? countLineItems(event.data?.checkoutLineItems);\n setIfDefined(meta, 'item_count', itemCount);\n } else {\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const total = checkout?.totalPrice?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(total));\n }\n\n return meta;\n}\n\nfunction countLineItems(items: { quantity?: number }[] | undefined): number | undefined {\n if (!items || items.length === 0) return undefined;\n let count = 0;\n let hasNumericQuantity = false;\n for (const item of items) {\n const q = safeNumber(item.quantity);\n if (q !== undefined) {\n hasNumericQuantity = true;\n count += q;\n }\n }\n // Fallback to row count only when no line had a numeric quantity. An explicit\n // sum of zero (e.g. `[{ quantity: 0 }]`) must be preserved, not replaced with\n // `items.length`, otherwise empty carts over-report `item_count`.\n return hasNumericQuantity ? count : items.length;\n}\n\n// Caps merchant-controlled free-text to keep payloads under the API's per-string DTO limit.\n// IDs, tokens, and currency codes are naturally bounded by Shopify and skip capping.\nconst MAX_TEXT_LEN = 255;\nconst MAX_SKU_LEN = 128;\n\n// Mirrors `src/constants/config.constants.ts` `XSS_PATTERNS`. Inlined because\n// the constants module pulls in unrelated dependencies that would blow the\n// pixel bundle's 5KB budget. Defense-in-depth: Shopify product titles/SKUs/\n// vendors are merchant-controlled; a compromised admin could inject HTML/JS\n// that lands in dashboards or logs.\nconst XSS_PATTERNS: readonly RegExp[] = [\n /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n /javascript:/gi,\n /on\\w+\\s*=/gi,\n /<iframe\\b[^<]*(?:(?!<\\/iframe>)<[^<]*)*<\\/iframe>/gi,\n /<embed\\b[^>]*>/gi,\n /<object\\b[^<]*(?:(?!<\\/object>)<[^<]*)*<\\/object>/gi,\n];\n\nfunction sanitize(value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n let out = value;\n for (const pattern of XSS_PATTERNS) out = out.replace(pattern, '');\n return out;\n}\n\nfunction cap(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\nfunction mapLineItems(items: ShopifyLineItem[] | undefined): Record<string, unknown>[] {\n if (!items || items.length === 0) return [];\n const capped = items.slice(0, MAX_LINE_ITEMS);\n return capped.map((item) => {\n const out: Record<string, unknown> = {};\n const variantId = item.variant?.id;\n if (typeof variantId === 'string' || typeof variantId === 'number') {\n out['id'] = String(variantId);\n }\n setIfDefined(out, 'title', cap(sanitize(safeString(item.title ?? item.variant?.product?.title)), MAX_TEXT_LEN));\n setIfDefined(out, 'quantity', safeNumber(item.quantity));\n setIfDefined(out, 'price', safeNumber(item.variant?.price?.amount ?? item.finalLinePrice?.amount));\n setIfDefined(out, 'sku', cap(sanitize(safeString(item.variant?.sku ?? undefined)), MAX_SKU_LEN));\n setIfDefined(out, 'vendor', cap(sanitize(safeString(item.variant?.product?.vendor)), MAX_TEXT_LEN));\n return out;\n });\n}\n\nfunction generateEventId(event: ShopifyEvent, ts: number): string {\n const suffix = safeString(event.id)?.slice(-6) ?? Math.random().toString(36).slice(2, 8);\n return `${ts}-001-${suffix}`;\n}\n\nfunction resolveTimestamp(value: string | undefined): number {\n if (typeof value !== 'string' || value.length === 0) return Date.now();\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : Date.now();\n}\n\nfunction stripUrlParams(href: string): string {\n // Pixel runs in the checkout sandbox where href can carry recovery tokens\n // (`?recovery=...`, `?key=...`). Path alone identifies the funnel stage; drop\n // query and hash to keep tokens out of telemetry. Mirrors the policy in\n // `src/utils/network/url.utils.ts` (which is too heavy to import into the\n // sub-5KB pixel bundle).\n const queryIdx = href.indexOf('?');\n const hashIdx = href.indexOf('#');\n let cutoff = href.length;\n if (queryIdx !== -1) cutoff = queryIdx;\n if (hashIdx !== -1 && hashIdx < cutoff) cutoff = hashIdx;\n return href.slice(0, cutoff);\n}\n\nfunction resolvePageUrl(event: ShopifyEvent): string {\n const href = safeString(event.context?.window?.location?.href) ?? safeString(event.context?.document?.location?.href);\n return href !== undefined ? stripUrlParams(href) : 'unknown';\n}\n\nexport function mapEventToBody(\n shopifyEvent: ShopifyEvent | null | undefined,\n shopifyEventName: ShopifyEventName,\n): PixelEventBody | null {\n if (!shopifyEvent) return null;\n\n const checkoutAttrs = shopifyEvent.data?.checkout?.attributes;\n const cartAttrs = shopifyEvent.data?.cart?.attributes;\n\n const sessionId = attrLookup(checkoutAttrs, 'tracelog_session_id') ?? attrLookup(cartAttrs, 'tracelog_session_id');\n const userId = attrLookup(checkoutAttrs, 'tracelog_user_id') ?? attrLookup(cartAttrs, 'tracelog_user_id');\n\n if (sessionId === null || userId === null) return null;\n\n const ts = resolveTimestamp(shopifyEvent.timestamp);\n const eventId = generateEventId(shopifyEvent, ts);\n const metadata = buildMetadata(shopifyEvent, shopifyEventName);\n\n return {\n user_id: userId,\n session_id: sessionId,\n device: { type: 'unknown', os: 'unknown', browser: 'unknown' },\n events: [\n {\n id: eventId,\n type: 'custom',\n page_url: resolvePageUrl(shopifyEvent),\n timestamp: ts,\n custom_event: {\n name: `shopify_${shopifyEventName}`,\n metadata,\n },\n },\n ],\n _metadata: {\n client_version: PIXEL_CLIENT_VERSION,\n timestamp: ts,\n },\n };\n}\n","/**\n * Standalone HTTP sender for the Shopify Web Pixel Extension bundle.\n *\n * Posts to the path-based ingress (`ingest.tracelog.io/p/<id>/collect`) — NOT\n * `api.tracelog.io/events/collect` — because the middleware has the only CORS\n * handler that accepts `Origin: null` from sandboxed iframes.\n *\n * Best-effort: failures are silently swallowed. The webhook (Task 03) carries\n * the revenue contract; pixel events are funnel-only and accept ~5-30% loss.\n */\n\nconst INGEST_HOST = 'https://ingest.tracelog.io';\n\nexport interface PixelSenderSettings {\n /** TraceLog project identifier (e.g. `t756edc0pnn17ha7`). Provided by Shopify init payload. */\n projectId: string;\n}\n\nexport interface PixelEventBody {\n user_id: string;\n session_id: string;\n device: { type: string; os: string; browser: string };\n events: Array<{\n id: string;\n type: 'custom';\n page_url: string;\n timestamp: number;\n custom_event: { name: string; metadata: Record<string, unknown> };\n }>;\n _metadata: { client_version: string; timestamp: number };\n}\n\nexport function sendBatch(settings: PixelSenderSettings, body: PixelEventBody): void {\n // Trust boundary is the Shopify extension settings form, but a misconfigured\n // (empty) projectId would yield `https://ingest.tracelog.io/p//collect` and\n // 404 every event. Drop silently so it can be diagnosed via Shopify pixel logs.\n if (!settings.projectId) return;\n\n // Encode in case the merchant pastes whitespace, slashes, or other unsafe\n // characters into the Shopify extension settings form.\n const url = `${INGEST_HOST}/p/${encodeURIComponent(settings.projectId)}/collect`;\n try {\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n keepalive: true,\n body: JSON.stringify(body),\n }).catch(() => {});\n } catch {\n // Pixel runs inside Shopify's strict sandbox; fetch() may be unavailable\n // or throw synchronously. Funnel events are best-effort — drop silently.\n }\n}\n","import { mapEventToBody, type ShopifyEventName } from './event-mapper';\nimport { sendBatch, type PixelSenderSettings } from './pixel-sender';\n\ninterface ShopifyAnalyticsApi {\n subscribe: (event: ShopifyEventName, callback: (event: unknown) => void) => void;\n}\n\ninterface ShopifyPixelApi {\n analytics: ShopifyAnalyticsApi;\n}\n\n/**\n * Registers the TraceLog Shopify Web Pixel: subscribes to the 7 standard\n * Customer Events and forwards each one to `ingest.tracelog.io/p/<id>/collect`.\n *\n * Each subscription is declared explicitly because Shopify's static analyzer\n * inspects the bundle for `analytics.subscribe('event_name', ...)` calls;\n * loop-based subscriptions are flagged and the pixel is treated as inactive.\n */\nexport function registerShopifyPixel(api: ShopifyPixelApi, settings: PixelSenderSettings): void {\n const handle = (eventName: ShopifyEventName, payload: unknown): void => {\n const body = mapEventToBody(payload as Parameters<typeof mapEventToBody>[0], eventName);\n if (!body) return;\n sendBatch(settings, body);\n };\n\n api.analytics.subscribe('cart_viewed', (event) => {\n handle('cart_viewed', event);\n });\n api.analytics.subscribe('checkout_started', (event) => {\n handle('checkout_started', event);\n });\n api.analytics.subscribe('checkout_contact_info_submitted', (event) => {\n handle('checkout_contact_info_submitted', event);\n });\n api.analytics.subscribe('checkout_address_info_submitted', (event) => {\n handle('checkout_address_info_submitted', event);\n });\n api.analytics.subscribe('checkout_shipping_info_submitted', (event) => {\n handle('checkout_shipping_info_submitted', event);\n });\n api.analytics.subscribe('payment_info_submitted', (event) => {\n handle('payment_info_submitted', event);\n });\n api.analytics.subscribe('checkout_completed', (event) => {\n handle('checkout_completed', event);\n });\n}\n"],"names":["attrLookup","attrs","key","Array","isArray","a","value","length","safeNumber","Number","isFinite","safeString","setIfDefined","target","buildMetadata","event","name","checkout","data","cart","meta","clientId","checkoutToken","token","totalPrice","amount","subtotalPrice","currencyCode","orderId","order","id","String","rawItems","lineItems","items","capped","slice","map","item","out","variantId","variant","cap","sanitize","title","product","MAX_TEXT_LEN","quantity","price","finalLinePrice","sku","MAX_SKU_LEN","vendor","mapLineItems","cost","totalAmount","countLineItems","lines","checkoutLineItems","total","count","hasNumericQuantity","q","XSS_PATTERNS","pattern","replace","max","resolvePageUrl","href","context","window","location","document","queryIdx","indexOf","hashIdx","cutoff","stripUrlParams","mapEventToBody","shopifyEvent","shopifyEventName","checkoutAttrs","attributes","cartAttrs","sessionId","userId","ts","Date","now","parsed","parse","resolveTimestamp","timestamp","eventId","Math","random","toString","generateEventId","metadata","user_id","session_id","device","type","os","browser","events","page_url","custom_event","_metadata","client_version","sendBatch","settings","body","projectId","url","encodeURIComponent","fetch","method","headers","keepalive","JSON","stringify","catch","api","handle","eventName","payload","analytics","subscribe"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tracelog-shopify-pixel.iife.js","sources":["../../src/pixel/event-mapper.ts","../../src/pixel/pixel-sender.ts","../../src/pixel/shopify-pixel.ts"],"sourcesContent":["import type { PixelEventBody } from './pixel-sender';\n\nconst PIXEL_CLIENT_VERSION = 'shopify-web-pixel-1';\n\nconst MAX_LINE_ITEMS = 100;\n\nexport const SHOPIFY_EVENTS = [\n 'cart_viewed',\n 'checkout_started',\n 'checkout_contact_info_submitted',\n 'checkout_address_info_submitted',\n 'checkout_shipping_info_submitted',\n 'payment_info_submitted',\n 'checkout_completed',\n] as const;\n\nexport type ShopifyEventName = (typeof SHOPIFY_EVENTS)[number];\n\n/**\n * Final event names emitted by `mapEventToBody()`. Exported so consumers\n * (tracelog-api event-catalog discovery, dashboards, contract validators)\n * can recognise pixel events without re-deriving the `shopify_` prefix.\n */\nexport const SHOPIFY_PIXEL_EVENT_NAMES = SHOPIFY_EVENTS.map((name) => `shopify_${name}` as const);\n\nexport type ShopifyPixelEventName = (typeof SHOPIFY_PIXEL_EVENT_NAMES)[number];\n\n/**\n * Spike-confirmed shape ([04-spike-report.md](../../docs/tasks/shopify-hybrid-capture/04-spike-report.md)):\n * `event.data.{cart,checkout}.attributes` is `Array<{key, value, __typename?}>`,\n * NOT a plain object. `__typename: 'NoteAttribute'` is added on `checkout_completed`\n * only — harmless because we lookup by `key`.\n */\ninterface ShopifyAttribute {\n key?: string;\n value?: string;\n __typename?: string;\n}\n\ninterface ShopifyMoney {\n amount?: number;\n currencyCode?: string;\n}\n\ninterface ShopifyLineItemVariantProduct {\n id?: string | number;\n title?: string;\n vendor?: string;\n}\n\ninterface ShopifyLineItemVariant {\n id?: string | number;\n sku?: string | null;\n price?: ShopifyMoney;\n product?: ShopifyLineItemVariantProduct;\n}\n\ninterface ShopifyLineItem {\n id?: string | number;\n title?: string;\n quantity?: number;\n variant?: ShopifyLineItemVariant;\n finalLinePrice?: ShopifyMoney;\n}\n\ninterface ShopifyCheckout {\n token?: string;\n currencyCode?: string;\n totalPrice?: ShopifyMoney;\n subtotalPrice?: ShopifyMoney;\n attributes?: ShopifyAttribute[];\n lineItems?: ShopifyLineItem[];\n order?: { id?: string | number };\n}\n\ninterface ShopifyCart {\n attributes?: ShopifyAttribute[];\n totalAmount?: ShopifyMoney;\n cost?: { totalAmount?: ShopifyMoney };\n lines?: ShopifyLineItem[];\n lineItems?: ShopifyLineItem[];\n}\n\ninterface ShopifyCheckoutLineItem {\n quantity?: number;\n}\n\ninterface ShopifyEvent {\n id?: string;\n timestamp?: string;\n clientId?: string;\n data?: {\n checkout?: ShopifyCheckout;\n cart?: ShopifyCart;\n checkoutLineItems?: ShopifyCheckoutLineItem[];\n };\n context?: {\n window?: { location?: { href?: string } };\n document?: { location?: { href?: string } };\n };\n}\n\nfunction attrLookup(attrs: ShopifyAttribute[] | undefined, key: string): string | null {\n if (!Array.isArray(attrs)) return null;\n for (const a of attrs) {\n if (a.key === key && typeof a.value === 'string' && a.value.length > 0) return a.value;\n }\n return null;\n}\n\nfunction safeNumber(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction safeString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction setIfDefined<T>(target: Record<string, unknown>, key: string, value: T | undefined): void {\n if (value !== undefined) target[key] = value;\n}\n\nfunction buildMetadata(event: ShopifyEvent, name: ShopifyEventName): Record<string, unknown> {\n const checkout = event.data?.checkout;\n const cart = event.data?.cart;\n const meta: Record<string, unknown> = {};\n\n setIfDefined(meta, 'shopify_client_id', safeString(event.clientId));\n\n const checkoutToken = safeString(checkout?.token);\n if (checkoutToken !== undefined) meta['checkout_token'] = checkoutToken;\n\n if (name === 'checkout_completed') {\n const total = checkout?.totalPrice?.amount ?? checkout?.subtotalPrice?.amount;\n setIfDefined(meta, 'value', safeNumber(total));\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const orderId = checkout?.order?.id;\n if (typeof orderId === 'string' || typeof orderId === 'number') meta['orderId'] = String(orderId);\n const rawItems = checkout?.lineItems ?? [];\n const items = mapLineItems(rawItems);\n if (items.length > 0) {\n meta['items'] = items;\n if (rawItems.length > MAX_LINE_ITEMS) meta['items_truncated'] = true;\n }\n } else if (name === 'cart_viewed') {\n const cartTotal = cart?.cost?.totalAmount?.amount ?? cart?.totalAmount?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(cartTotal));\n const itemCount = countLineItems(cart?.lines) ?? countLineItems(cart?.lineItems);\n setIfDefined(meta, 'item_count', itemCount);\n } else if (name === 'checkout_started') {\n const total = checkout?.totalPrice?.amount ?? checkout?.subtotalPrice?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(total));\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const itemCount = countLineItems(checkout?.lineItems) ?? countLineItems(event.data?.checkoutLineItems);\n setIfDefined(meta, 'item_count', itemCount);\n } else {\n setIfDefined(meta, 'currency', safeString(checkout?.currencyCode ?? checkout?.totalPrice?.currencyCode));\n const total = checkout?.totalPrice?.amount;\n setIfDefined(meta, 'cart_total', safeNumber(total));\n }\n\n return meta;\n}\n\nfunction countLineItems(items: { quantity?: number }[] | undefined): number | undefined {\n if (!items || items.length === 0) return undefined;\n let count = 0;\n let hasNumericQuantity = false;\n for (const item of items) {\n const q = safeNumber(item.quantity);\n if (q !== undefined) {\n hasNumericQuantity = true;\n count += q;\n }\n }\n // Fallback to row count only when no line had a numeric quantity. An explicit\n // sum of zero (e.g. `[{ quantity: 0 }]`) must be preserved, not replaced with\n // `items.length`, otherwise empty carts over-report `item_count`.\n return hasNumericQuantity ? count : items.length;\n}\n\n// Caps merchant-controlled free-text to keep payloads under the API's per-string DTO limit.\n// IDs, tokens, and currency codes are naturally bounded by Shopify and skip capping.\nconst MAX_TEXT_LEN = 255;\nconst MAX_SKU_LEN = 128;\n\n// Mirrors `src/constants/config.constants.ts` `XSS_PATTERNS`. Inlined because\n// the constants module pulls in unrelated dependencies that would blow the\n// pixel bundle's 5KB budget. Defense-in-depth: Shopify product titles/SKUs/\n// vendors are merchant-controlled; a compromised admin could inject HTML/JS\n// that lands in dashboards or logs.\nconst XSS_PATTERNS: readonly RegExp[] = [\n /<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi,\n /javascript:/gi,\n /on\\w+\\s*=/gi,\n /<iframe\\b[^<]*(?:(?!<\\/iframe>)<[^<]*)*<\\/iframe>/gi,\n /<embed\\b[^>]*>/gi,\n /<object\\b[^<]*(?:(?!<\\/object>)<[^<]*)*<\\/object>/gi,\n];\n\nfunction sanitize(value: string | undefined): string | undefined {\n if (value === undefined) return undefined;\n let out = value;\n for (const pattern of XSS_PATTERNS) out = out.replace(pattern, '');\n return out;\n}\n\nfunction cap(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\nfunction mapLineItems(items: ShopifyLineItem[] | undefined): Record<string, unknown>[] {\n if (!items || items.length === 0) return [];\n const capped = items.slice(0, MAX_LINE_ITEMS);\n return capped.map((item) => {\n const out: Record<string, unknown> = {};\n const variantId = item.variant?.id;\n if (typeof variantId === 'string' || typeof variantId === 'number') {\n out['id'] = String(variantId);\n }\n setIfDefined(out, 'title', cap(sanitize(safeString(item.title ?? item.variant?.product?.title)), MAX_TEXT_LEN));\n setIfDefined(out, 'quantity', safeNumber(item.quantity));\n setIfDefined(out, 'price', safeNumber(item.variant?.price?.amount ?? item.finalLinePrice?.amount));\n setIfDefined(out, 'sku', cap(sanitize(safeString(item.variant?.sku ?? undefined)), MAX_SKU_LEN));\n setIfDefined(out, 'vendor', cap(sanitize(safeString(item.variant?.product?.vendor)), MAX_TEXT_LEN));\n return out;\n });\n}\n\nfunction generateEventId(event: ShopifyEvent, ts: number): string {\n const suffix = safeString(event.id)?.slice(-6) ?? Math.random().toString(36).slice(2, 8);\n return `${ts}-001-${suffix}`;\n}\n\nfunction resolveTimestamp(value: string | undefined): number {\n if (typeof value !== 'string' || value.length === 0) return Date.now();\n const parsed = Date.parse(value);\n return Number.isFinite(parsed) ? parsed : Date.now();\n}\n\nfunction stripUrlParams(href: string): string {\n // Pixel runs in the checkout sandbox where href can carry recovery tokens\n // (`?recovery=...`, `?key=...`). Path alone identifies the funnel stage; drop\n // query and hash to keep tokens out of telemetry. Mirrors the policy in\n // `src/utils/network/url.utils.ts` (which is too heavy to import into the\n // sub-5KB pixel bundle).\n const queryIdx = href.indexOf('?');\n const hashIdx = href.indexOf('#');\n let cutoff = href.length;\n if (queryIdx !== -1) cutoff = queryIdx;\n if (hashIdx !== -1 && hashIdx < cutoff) cutoff = hashIdx;\n return href.slice(0, cutoff);\n}\n\nfunction resolvePageUrl(event: ShopifyEvent): string {\n const href = safeString(event.context?.window?.location?.href) ?? safeString(event.context?.document?.location?.href);\n return href !== undefined ? stripUrlParams(href) : 'unknown';\n}\n\nexport function mapEventToBody(\n shopifyEvent: ShopifyEvent | null | undefined,\n shopifyEventName: ShopifyEventName,\n): PixelEventBody | null {\n if (!shopifyEvent) return null;\n\n const checkoutAttrs = shopifyEvent.data?.checkout?.attributes;\n const cartAttrs = shopifyEvent.data?.cart?.attributes;\n\n const sessionId = attrLookup(checkoutAttrs, 'tracelog_session_id') ?? attrLookup(cartAttrs, 'tracelog_session_id');\n const userId = attrLookup(checkoutAttrs, 'tracelog_user_id') ?? attrLookup(cartAttrs, 'tracelog_user_id');\n\n if (sessionId === null || userId === null) return null;\n\n const ts = resolveTimestamp(shopifyEvent.timestamp);\n const eventId = generateEventId(shopifyEvent, ts);\n const metadata = buildMetadata(shopifyEvent, shopifyEventName);\n\n return {\n user_id: userId,\n session_id: sessionId,\n device: { type: 'unknown', os: 'unknown', browser: 'unknown' },\n events: [\n {\n id: eventId,\n type: 'custom',\n page_url: resolvePageUrl(shopifyEvent),\n timestamp: ts,\n custom_event: {\n name: `shopify_${shopifyEventName}`,\n metadata,\n },\n },\n ],\n _metadata: {\n client_version: PIXEL_CLIENT_VERSION,\n timestamp: ts,\n },\n };\n}\n","/**\n * Standalone HTTP sender for the Shopify Web Pixel Extension bundle.\n *\n * Posts to the path-based ingress (`ingest.tracelog.io/p/<id>/collect`) — NOT\n * `api.tracelog.io/events/collect` — because the middleware has the only CORS\n * handler that accepts `Origin: null` from sandboxed iframes.\n *\n * Best-effort: failures are silently swallowed. The webhook (Task 03) carries\n * the revenue contract; pixel events are funnel-only and accept ~5-30% loss.\n */\n\nconst INGEST_HOST = 'https://ingest.tracelog.io';\n\nexport interface PixelSenderSettings {\n /** TraceLog project identifier (e.g. `t756edc0pnn17ha7`). Provided by Shopify init payload. */\n projectId: string;\n}\n\nexport interface PixelEventBody {\n user_id: string;\n session_id: string;\n device: { type: string; os: string; browser: string };\n events: Array<{\n id: string;\n type: 'custom';\n page_url: string;\n timestamp: number;\n custom_event: { name: string; metadata: Record<string, unknown> };\n }>;\n _metadata: { client_version: string; timestamp: number };\n}\n\nexport function sendBatch(settings: PixelSenderSettings, body: PixelEventBody): void {\n // Trust boundary is the Shopify extension settings form, but a misconfigured\n // (empty) projectId would yield `https://ingest.tracelog.io/p//collect` and\n // 404 every event. Drop silently so it can be diagnosed via Shopify pixel logs.\n if (!settings.projectId) return;\n\n // Encode in case the merchant pastes whitespace, slashes, or other unsafe\n // characters into the Shopify extension settings form.\n const url = `${INGEST_HOST}/p/${encodeURIComponent(settings.projectId)}/collect`;\n try {\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n keepalive: true,\n body: JSON.stringify(body),\n }).catch(() => {});\n } catch {\n // Pixel runs inside Shopify's strict sandbox; fetch() may be unavailable\n // or throw synchronously. Funnel events are best-effort — drop silently.\n }\n}\n","import { mapEventToBody, type ShopifyEventName } from './event-mapper';\nimport { sendBatch, type PixelSenderSettings } from './pixel-sender';\n\ninterface ShopifyAnalyticsApi {\n subscribe: (event: ShopifyEventName, callback: (event: unknown) => void) => void;\n}\n\ninterface ShopifyPixelApi {\n analytics: ShopifyAnalyticsApi;\n}\n\n/**\n * Registers the TraceLog Shopify Web Pixel: subscribes to the 7 standard\n * Customer Events and forwards each one to `ingest.tracelog.io/p/<id>/collect`.\n *\n * Each subscription is declared explicitly because Shopify's static analyzer\n * inspects the bundle for `analytics.subscribe('event_name', ...)` calls;\n * loop-based subscriptions are flagged and the pixel is treated as inactive.\n */\nexport function registerShopifyPixel(api: ShopifyPixelApi, settings: PixelSenderSettings): void {\n const handle = (eventName: ShopifyEventName, payload: unknown): void => {\n const body = mapEventToBody(payload as Parameters<typeof mapEventToBody>[0], eventName);\n if (!body) return;\n sendBatch(settings, body);\n };\n\n api.analytics.subscribe('cart_viewed', (event) => {\n handle('cart_viewed', event);\n });\n api.analytics.subscribe('checkout_started', (event) => {\n handle('checkout_started', event);\n });\n api.analytics.subscribe('checkout_contact_info_submitted', (event) => {\n handle('checkout_contact_info_submitted', event);\n });\n api.analytics.subscribe('checkout_address_info_submitted', (event) => {\n handle('checkout_address_info_submitted', event);\n });\n api.analytics.subscribe('checkout_shipping_info_submitted', (event) => {\n handle('checkout_shipping_info_submitted', event);\n });\n api.analytics.subscribe('payment_info_submitted', (event) => {\n handle('payment_info_submitted', event);\n });\n api.analytics.subscribe('checkout_completed', (event) => {\n handle('checkout_completed', event);\n });\n}\n"],"names":["SHOPIFY_EVENTS","SHOPIFY_PIXEL_EVENT_NAMES","map","name","attrLookup","attrs","key","Array","isArray","a","value","length","safeNumber","Number","isFinite","safeString","setIfDefined","target","buildMetadata","event","checkout","data","cart","meta","clientId","checkoutToken","token","totalPrice","amount","subtotalPrice","currencyCode","orderId","order","id","String","rawItems","lineItems","items","capped","slice","item","out","variantId","variant","cap","sanitize","title","product","MAX_TEXT_LEN","quantity","price","finalLinePrice","sku","MAX_SKU_LEN","vendor","mapLineItems","cost","totalAmount","countLineItems","lines","checkoutLineItems","total","count","hasNumericQuantity","q","XSS_PATTERNS","pattern","replace","max","resolvePageUrl","href","context","window","location","document","queryIdx","indexOf","hashIdx","cutoff","stripUrlParams","mapEventToBody","shopifyEvent","shopifyEventName","checkoutAttrs","attributes","cartAttrs","sessionId","userId","ts","Date","now","parsed","parse","resolveTimestamp","timestamp","eventId","Math","random","toString","generateEventId","metadata","user_id","session_id","device","type","os","browser","events","page_url","custom_event","_metadata","client_version","sendBatch","settings","body","projectId","url","encodeURIComponent","fetch","method","headers","keepalive","JSON","stringify","catch","api","handle","eventName","payload","analytics","subscribe"],"mappings":"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"}
|
|
@@ -79,7 +79,7 @@ const p = {
|
|
|
79
79
|
/<iframe\b[^<]*(?:(?!<\/iframe>)<[^<]*)*<\/iframe>/gi,
|
|
80
80
|
/<embed\b[^>]*>/gi,
|
|
81
81
|
/<object\b[^<]*(?:(?!<\/object>)<[^<]*)*<\/object>/gi
|
|
82
|
-
], S = "tlog", X = `${S}:qa_mode`, Te = `${S}:uid`, rt = "tlog_mode", Ue = "qa",
|
|
82
|
+
], S = "tlog", X = `${S}:qa_mode`, Te = `${S}:uid`, rt = "tlog_mode", Ue = "qa", Fe = "qa_off", Ct = (r) => r ? `${S}:${r}:queue` : `${S}:queue`, Rt = (r) => r ? `${S}:${r}:rate_limit` : `${S}:rate_limit`, Nt = (r) => r ? `${S}:${r}:session` : `${S}:session`, Ot = (r) => r ? `${S}:${r}:broadcast` : `${S}:broadcast`, He = (r, e) => `${S}:${r}:session_counts:${e}`, xe = 10080 * 60 * 1e3, $e = `${S}:session_counts_last_cleanup`, Be = 3600 * 1e3, fe = (r) => r ? `${S}:${r}:identity` : `${S}:identity`, U = `${S}:pending_identity`;
|
|
83
83
|
var $ = /* @__PURE__ */ ((r) => (r.Localhost = "localhost:8080", r.Fail = "localhost:9999", r))($ || {}), A = /* @__PURE__ */ ((r) => (r.Mobile = "mobile", r.Tablet = "tablet", r.Desktop = "desktop", r.Unknown = "unknown", r))(A || {}), se = /* @__PURE__ */ ((r) => (r.EVENT = "event", r.QUEUE = "queue", r))(se || {});
|
|
84
84
|
class O extends Error {
|
|
85
85
|
constructor(e, t, s) {
|
|
@@ -164,9 +164,9 @@ const nt = "background: #ff9800; color: white; font-weight: bold; padding: 2px 8
|
|
|
164
164
|
const { error: s, data: n, showToClient: i = !1, style: o, visibility: l } = t ?? {}, c = s ? kt(e, s) : `[TraceLog] ${e}`, d = r === "error" ? "error" : r === "warn" ? "warn" : "log";
|
|
165
165
|
if (!Ut(l, i))
|
|
166
166
|
return;
|
|
167
|
-
const g =
|
|
168
|
-
|
|
169
|
-
}, Ut = (r, e) => r === "critical" ? !0 : r === "qa" || e ? Vt() : !1,
|
|
167
|
+
const g = Ft(l, o), I = n !== void 0 ? Ie(n) : void 0;
|
|
168
|
+
Ht(d, c, g, I);
|
|
169
|
+
}, Ut = (r, e) => r === "critical" ? !0 : r === "qa" || e ? Vt() : !1, Ft = (r, e) => e !== void 0 && e !== "" ? e : r === "critical" ? Dt : "", Ht = (r, e, t, s) => {
|
|
170
170
|
const n = t !== void 0 && t !== "", i = n ? `%c${e}` : e;
|
|
171
171
|
s !== void 0 ? n ? console[r](i, t, s) : console[r](i, s) : n ? console[r](i, t) : console[r](i);
|
|
172
172
|
}, Ie = (r) => {
|
|
@@ -319,10 +319,10 @@ const xt = () => {
|
|
|
319
319
|
return e === Ue ? (s = !0, sessionStorage.setItem(X, "true"), a("info", "QA Mode ACTIVE", {
|
|
320
320
|
visibility: "qa",
|
|
321
321
|
style: nt
|
|
322
|
-
})) : e ===
|
|
322
|
+
})) : e === Fe && (s = !1, sessionStorage.setItem(X, "false"), a("info", "QA Mode DISABLED", {
|
|
323
323
|
visibility: "qa",
|
|
324
324
|
style: it
|
|
325
|
-
})), (e === Ue || e ===
|
|
325
|
+
})), (e === Ue || e === Fe) && ts(), s ?? t === "true";
|
|
326
326
|
} catch {
|
|
327
327
|
return !1;
|
|
328
328
|
}
|
|
@@ -2887,7 +2887,7 @@ class vs extends _ {
|
|
|
2887
2887
|
loadSessionCounts(e) {
|
|
2888
2888
|
if (typeof window > "u" || typeof localStorage > "u")
|
|
2889
2889
|
return this.getInitialCounts();
|
|
2890
|
-
const t = this.get("userId") || "anonymous", s =
|
|
2890
|
+
const t = this.get("userId") || "anonymous", s = He(t, e);
|
|
2891
2891
|
try {
|
|
2892
2892
|
const n = localStorage.getItem(s);
|
|
2893
2893
|
if (!n)
|
|
@@ -2997,7 +2997,7 @@ class vs extends _ {
|
|
|
2997
2997
|
* @internal
|
|
2998
2998
|
*/
|
|
2999
2999
|
saveSessionCounts(e) {
|
|
3000
|
-
const t = this.get("userId") || "anonymous", s =
|
|
3000
|
+
const t = this.get("userId") || "anonymous", s = He(t, e);
|
|
3001
3001
|
try {
|
|
3002
3002
|
const n = {
|
|
3003
3003
|
...this.sessionEventCounts,
|
|
@@ -4184,7 +4184,6 @@ const Rs = "tracelog_session_id", Ns = "tracelog_user_id";
|
|
|
4184
4184
|
class Os extends _ {
|
|
4185
4185
|
visibilityHandler = null;
|
|
4186
4186
|
pageshowHandler = null;
|
|
4187
|
-
submitHandler = null;
|
|
4188
4187
|
lastSyncedKey = null;
|
|
4189
4188
|
activate() {
|
|
4190
4189
|
this.cleanupListeners(), this.syncCartAttribute(), this.setupListeners();
|
|
@@ -4221,30 +4220,24 @@ class Os extends _ {
|
|
|
4221
4220
|
}
|
|
4222
4221
|
}
|
|
4223
4222
|
/**
|
|
4224
|
-
* Sync triggers (theme-agnostic
|
|
4225
|
-
*
|
|
4226
|
-
*
|
|
4227
|
-
*
|
|
4228
|
-
*
|
|
4229
|
-
*
|
|
4230
|
-
*
|
|
4231
|
-
*
|
|
4232
|
-
* attribute write completes before the page navigates to /cart or /checkout.
|
|
4233
|
-
*
|
|
4234
|
-
* All triggers go through `syncCartAttribute()` which dedupes by
|
|
4235
|
-
* `sessionId|userId`, so spurious calls are cheap (early-return).
|
|
4223
|
+
* Sync triggers (theme-agnostic):
|
|
4224
|
+
* - `visibilitychange`: catches tab refocus (long sessions, OAuth round-trips).
|
|
4225
|
+
* - `pageshow` with `event.persisted === true`: catches bfcache restore so a
|
|
4226
|
+
* user returning from an external checkout / Shop Pay window picks up the
|
|
4227
|
+
* current sessionId before any further interaction.
|
|
4228
|
+
*
|
|
4229
|
+
* Both triggers go through `syncCartAttribute()` which dedupes by
|
|
4230
|
+
* `sessionId|userId`, so spurious calls cost nothing.
|
|
4236
4231
|
*/
|
|
4237
4232
|
setupListeners() {
|
|
4238
4233
|
this.visibilityHandler = () => {
|
|
4239
4234
|
document.hidden || this.syncCartAttribute();
|
|
4240
4235
|
}, document.addEventListener("visibilitychange", this.visibilityHandler), this.pageshowHandler = (e) => {
|
|
4241
4236
|
e.persisted && this.syncCartAttribute();
|
|
4242
|
-
}, window.addEventListener("pageshow", this.pageshowHandler)
|
|
4243
|
-
this.syncCartAttribute();
|
|
4244
|
-
}, document.addEventListener("submit", this.submitHandler, !0);
|
|
4237
|
+
}, window.addEventListener("pageshow", this.pageshowHandler);
|
|
4245
4238
|
}
|
|
4246
4239
|
cleanupListeners() {
|
|
4247
|
-
this.visibilityHandler && (document.removeEventListener("visibilitychange", this.visibilityHandler), this.visibilityHandler = null), this.pageshowHandler && (window.removeEventListener("pageshow", this.pageshowHandler), this.pageshowHandler = null)
|
|
4240
|
+
this.visibilityHandler && (document.removeEventListener("visibilitychange", this.visibilityHandler), this.visibilityHandler = null), this.pageshowHandler && (window.removeEventListener("pageshow", this.pageshowHandler), this.pageshowHandler = null);
|
|
4248
4241
|
}
|
|
4249
4242
|
}
|
|
4250
4243
|
class Ps {
|
|
@@ -5311,7 +5304,7 @@ const Vs = async (r) => typeof window > "u" || typeof document > "u" ? { session
|
|
|
5311
5304
|
throw new Error("[TraceLog] Cannot send events while TraceLog is being destroyed");
|
|
5312
5305
|
h.sendCustomEvent(r, e);
|
|
5313
5306
|
}
|
|
5314
|
-
},
|
|
5307
|
+
}, Fs = (r, e) => {
|
|
5315
5308
|
if (!(typeof window > "u" || typeof document > "u")) {
|
|
5316
5309
|
if (!h || R) {
|
|
5317
5310
|
k.push({ event: r, callback: e });
|
|
@@ -5319,7 +5312,7 @@ const Vs = async (r) => typeof window > "u" || typeof document > "u" ? { session
|
|
|
5319
5312
|
}
|
|
5320
5313
|
h.on(r, e);
|
|
5321
5314
|
}
|
|
5322
|
-
},
|
|
5315
|
+
}, Hs = (r, e) => {
|
|
5323
5316
|
if (!(typeof window > "u" || typeof document > "u")) {
|
|
5324
5317
|
if (!h) {
|
|
5325
5318
|
const t = k.findIndex((s) => s.event === r && s.callback === e);
|
|
@@ -5453,8 +5446,8 @@ const $s = (r) => {
|
|
|
5453
5446
|
}, Lr = {
|
|
5454
5447
|
init: Vs,
|
|
5455
5448
|
event: Us,
|
|
5456
|
-
on:
|
|
5457
|
-
off:
|
|
5449
|
+
on: Fs,
|
|
5450
|
+
off: Hs,
|
|
5458
5451
|
setTransformer: xs,
|
|
5459
5452
|
removeTransformer: $s,
|
|
5460
5453
|
setCustomHeaders: Bs,
|
|
@@ -5481,7 +5474,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5481
5474
|
}, y = function(r, e) {
|
|
5482
5475
|
var t = Pe(), s = "navigate";
|
|
5483
5476
|
return Et >= 0 ? s = "back-forward-cache" : t && (document.prerendering || de() > 0 ? s = "prerender" : document.wasDiscarded ? s = "restore" : t.type && (s = t.type.replace(/_/g, "-"))), { name: r, value: e === void 0 ? -1 : e, rating: "good", delta: 0, entries: [], id: "v4-".concat(Date.now(), "-").concat(Math.floor(8999999999999 * Math.random()) + 1e12), navigationType: s };
|
|
5484
|
-
},
|
|
5477
|
+
}, H = function(r, e, t) {
|
|
5485
5478
|
try {
|
|
5486
5479
|
if (PerformanceObserver.supportedEntryTypes.includes(r)) {
|
|
5487
5480
|
var s = new PerformanceObserver((function(n) {
|
|
@@ -5515,21 +5508,21 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5515
5508
|
return function() {
|
|
5516
5509
|
e || (r(), e = !0);
|
|
5517
5510
|
};
|
|
5518
|
-
},
|
|
5511
|
+
}, F = -1, et = function() {
|
|
5519
5512
|
return document.visibilityState !== "hidden" || document.prerendering ? 1 / 0 : 0;
|
|
5520
5513
|
}, ce = function(r) {
|
|
5521
|
-
document.visibilityState === "hidden" &&
|
|
5514
|
+
document.visibilityState === "hidden" && F > -1 && (F = r.type === "visibilitychange" ? r.timeStamp : 0, Js());
|
|
5522
5515
|
}, tt = function() {
|
|
5523
5516
|
addEventListener("visibilitychange", ce, !0), addEventListener("prerenderingchange", ce, !0);
|
|
5524
5517
|
}, Js = function() {
|
|
5525
5518
|
removeEventListener("visibilitychange", ce, !0), removeEventListener("prerenderingchange", ce, !0);
|
|
5526
5519
|
}, ke = function() {
|
|
5527
|
-
return
|
|
5520
|
+
return F < 0 && (F = et(), tt(), V((function() {
|
|
5528
5521
|
setTimeout((function() {
|
|
5529
|
-
|
|
5522
|
+
F = et(), tt();
|
|
5530
5523
|
}), 0);
|
|
5531
5524
|
}))), { get firstHiddenTime() {
|
|
5532
|
-
return
|
|
5525
|
+
return F;
|
|
5533
5526
|
} };
|
|
5534
5527
|
}, z = function(r) {
|
|
5535
5528
|
document.prerendering ? addEventListener("prerenderingchange", (function() {
|
|
@@ -5537,7 +5530,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5537
5530
|
}), !0) : r();
|
|
5538
5531
|
}, Ae = [1800, 3e3], St = function(r, e) {
|
|
5539
5532
|
e = e || {}, z((function() {
|
|
5540
|
-
var t, s = ke(), n = y("FCP"), i =
|
|
5533
|
+
var t, s = ke(), n = y("FCP"), i = H("paint", (function(o) {
|
|
5541
5534
|
o.forEach((function(l) {
|
|
5542
5535
|
l.name === "first-contentful-paint" && (i.disconnect(), l.startTime < s.firstHiddenTime && (n.value = Math.max(l.startTime - de(), 0), n.entries.push(l), t(!0)));
|
|
5543
5536
|
}));
|
|
@@ -5557,7 +5550,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5557
5550
|
n && d.startTime - g.startTime < 1e3 && d.startTime - f.startTime < 5e3 ? (n += d.value, i.push(d)) : (n = d.value, i = [d]);
|
|
5558
5551
|
}
|
|
5559
5552
|
})), n > s.value && (s.value = n, s.entries = i, t());
|
|
5560
|
-
}, l =
|
|
5553
|
+
}, l = H("layout-shift", o);
|
|
5561
5554
|
l && (t = w(r, s, Me, e.reportAllChanges), K((function() {
|
|
5562
5555
|
o(l.takeRecords()), t(!0);
|
|
5563
5556
|
})), V((function() {
|
|
@@ -5573,7 +5566,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5573
5566
|
}, It = function() {
|
|
5574
5567
|
return Le ? Tt : performance.interactionCount || 0;
|
|
5575
5568
|
}, tr = function() {
|
|
5576
|
-
"interactionCount" in performance || Le || (Le =
|
|
5569
|
+
"interactionCount" in performance || Le || (Le = H("event", er, { type: "event", buffered: !0, durationThreshold: 0 }));
|
|
5577
5570
|
}, L = [], te = /* @__PURE__ */ new Map(), vt = 0, sr = function() {
|
|
5578
5571
|
var r = Math.min(L.length - 1, Math.floor((It() - vt) / 50));
|
|
5579
5572
|
return L[r];
|
|
@@ -5608,7 +5601,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5608
5601
|
var c = sr();
|
|
5609
5602
|
c && c.latency !== n.value && (n.value = c.latency, n.entries = c.entries, s());
|
|
5610
5603
|
}));
|
|
5611
|
-
}, o =
|
|
5604
|
+
}, o = H("event", i, { durationThreshold: (t = e.durationThreshold) !== null && t !== void 0 ? t : 40 });
|
|
5612
5605
|
s = w(r, n, Ce, e.reportAllChanges), o && (o.observe({ type: "first-input", buffered: !0 }), K((function() {
|
|
5613
5606
|
i(o.takeRecords()), s(!0);
|
|
5614
5607
|
})), V((function() {
|
|
@@ -5621,7 +5614,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5621
5614
|
e.reportAllChanges || (c = c.slice(-1)), c.forEach((function(d) {
|
|
5622
5615
|
d.startTime < s.firstHiddenTime && (n.value = Math.max(d.startTime - de(), 0), n.entries = [d], t());
|
|
5623
5616
|
}));
|
|
5624
|
-
}, o =
|
|
5617
|
+
}, o = H("largest-contentful-paint", i);
|
|
5625
5618
|
if (o) {
|
|
5626
5619
|
t = w(r, n, Re, e.reportAllChanges);
|
|
5627
5620
|
var l = ue((function() {
|
|
@@ -5686,7 +5679,7 @@ var Le, C, G, pt, le, Et = -1, V = function(r) {
|
|
|
5686
5679
|
c.startTime < s.firstHiddenTime && (n.value = c.processingStart - c.startTime, n.entries.push(c), t(!0));
|
|
5687
5680
|
}, o = function(c) {
|
|
5688
5681
|
c.forEach(i);
|
|
5689
|
-
}, l =
|
|
5682
|
+
}, l = H("first-input", o);
|
|
5690
5683
|
t = w(r, n, Oe, e.reportAllChanges), l && (K(ue((function() {
|
|
5691
5684
|
o(l.takeRecords()), l.disconnect();
|
|
5692
5685
|
}))), V((function() {
|