@amboras-dev/snapchat-ads 0.1.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.
@@ -0,0 +1,27 @@
1
+ // src/get-config.ts
2
+ import { resolveBackendUrl, resolvePublishableKey, resolveStoreEnvironmentId } from "@amboras-dev/pixel-core";
3
+ async function getSnapchatConfig(options = {}) {
4
+ const baseUrl = options.baseUrl ?? resolveBackendUrl();
5
+ if (!baseUrl) return null;
6
+ const publishableKey = options.publishableApiKey ?? resolvePublishableKey();
7
+ const storeId = options.storeId ?? resolveStoreEnvironmentId();
8
+ const doFetch = options.fetchImpl ?? fetch;
9
+ try {
10
+ const res = await doFetch(`${baseUrl}/store/snapchat-ads/config`, {
11
+ headers: {
12
+ ...storeId ? { "X-Store-Environment-ID": storeId } : {},
13
+ ...publishableKey ? { "x-publishable-api-key": publishableKey } : {}
14
+ },
15
+ next: { revalidate: 60 }
16
+ });
17
+ if (!res.ok) return null;
18
+ return await res.json();
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ export {
25
+ getSnapchatConfig
26
+ };
27
+ //# sourceMappingURL=chunk-IP2VTHD6.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/get-config.ts"],"sourcesContent":["import { resolveBackendUrl, resolvePublishableKey, resolveStoreEnvironmentId } from '@amboras-dev/pixel-core'\nimport type { SnapchatPublicConfig } from './types'\n\ninterface GetSnapchatConfigOptions {\n baseUrl?: string\n publishableApiKey?: string\n storeId?: string\n /** Injected `fetch` for testing. Defaults to global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\n/**\n * Fetches the non-secret Snapchat config from Medusa. Returns `null` when\n * the backend URL is unknown or the endpoint is missing, unauthorized, or\n * errors. The Conversions API token is never returned by the server route,\n * so it never surfaces here.\n */\nexport async function getSnapchatConfig(\n options: GetSnapchatConfigOptions = {},\n): Promise<SnapchatPublicConfig | null> {\n const baseUrl = options.baseUrl ?? resolveBackendUrl()\n if (!baseUrl) return null\n const publishableKey = options.publishableApiKey ?? resolvePublishableKey()\n const storeId = options.storeId ?? resolveStoreEnvironmentId()\n const doFetch = options.fetchImpl ?? fetch\n\n try {\n const res = await doFetch(`${baseUrl}/store/snapchat-ads/config`, {\n headers: {\n ...(storeId ? { 'X-Store-Environment-ID': storeId } : {}),\n ...(publishableKey ? { 'x-publishable-api-key': publishableKey } : {}),\n },\n next: { revalidate: 60 },\n } as RequestInit)\n if (!res.ok) return null\n return (await res.json()) as SnapchatPublicConfig\n } catch {\n return null\n }\n}\n"],"mappings":";AAAA,SAAS,mBAAmB,uBAAuB,iCAAiC;AAiBpF,eAAsB,kBACpB,UAAoC,CAAC,GACC;AACtC,QAAM,UAAU,QAAQ,WAAW,kBAAkB;AACrD,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,iBAAiB,QAAQ,qBAAqB,sBAAsB;AAC1E,QAAM,UAAU,QAAQ,WAAW,0BAA0B;AAC7D,QAAM,UAAU,QAAQ,aAAa;AAErC,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,8BAA8B;AAAA,MAChE,SAAS;AAAA,QACP,GAAI,UAAU,EAAE,0BAA0B,QAAQ,IAAI,CAAC;AAAA,QACvD,GAAI,iBAAiB,EAAE,yBAAyB,eAAe,IAAI,CAAC;AAAA,MACtE;AAAA,MACA,MAAM,EAAE,YAAY,GAAG;AAAA,IACzB,CAAgB;AAChB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Every fixed string the package puts on the wire or in browser storage.
3
+ * Wire names are a cross-repo contract: Medusa's strict Zod schema and the
4
+ * orchestrator's storefront relay template expect exactly these values.
5
+ */
6
+ /**
7
+ * Snap standard event names. The pixel (`snaptr('track', <name>)`) and
8
+ * Conversions API v3 (`event_name`) use the SAME uppercase strings.
9
+ * https://developers.snap.com/api/marketing-api/Conversions-API/Parameters
10
+ */
11
+ declare const SNAP_EVENTS: {
12
+ readonly PAGE_VIEW: "PAGE_VIEW";
13
+ readonly VIEW_CONTENT: "VIEW_CONTENT";
14
+ readonly ADD_CART: "ADD_CART";
15
+ readonly START_CHECKOUT: "START_CHECKOUT";
16
+ readonly PURCHASE: "PURCHASE";
17
+ };
18
+ /** Storefront relay route written by the orchestrator's template. */
19
+ declare const CAPI_RELAY_ENDPOINT = "/api/snapchat/events";
20
+ declare const COOKIE_NAMES: {
21
+ /** Plugin-owned raw PII for the relay; Medusa hashes it. */
22
+ readonly PII: "amboras_snap_pii";
23
+ /** Plugin-owned copy of `ScCid`. */
24
+ readonly CLICK_ID: "amboras_snap_clid";
25
+ /** Snap's own first-party cookie (forwarded as `sc_cookie1`). */
26
+ readonly SCID: "_scid";
27
+ };
28
+
29
+ /**
30
+ * Non-secret Snapchat configuration exposed to the browser via
31
+ * `/store/snapchat-ads/config`. Never contains the Conversions API token -
32
+ * that lives only on the Medusa side and never leaves the server.
33
+ */
34
+ interface SnapchatPublicConfig {
35
+ pixel_id: string;
36
+ enabled: boolean;
37
+ track_page_view: boolean;
38
+ track_view_content: boolean;
39
+ track_add_cart: boolean;
40
+ track_start_checkout: boolean;
41
+ track_purchase: boolean;
42
+ }
43
+ /**
44
+ * Snap standard event names. The Snap Pixel (`snaptr('track', <name>)`) and
45
+ * Conversions API v3 (`event_name`) use the SAME uppercase strings, so the
46
+ * storefront sends the wire name end to end and Medusa forwards it as-is.
47
+ * https://developers.snap.com/api/marketing-api/Conversions-API/Parameters
48
+ */
49
+ type SnapchatEventName = (typeof SNAP_EVENTS)[keyof typeof SNAP_EVENTS];
50
+ /**
51
+ * Payload posted from the storefront to `/api/snapchat/events`. The proxy
52
+ * adds the PII cookie, client IP and User-Agent, then forwards to Medusa
53
+ * `/store/snapchat-ads/events` with the same `eventId`.
54
+ *
55
+ * Dedup contract with the browser pixel:
56
+ * - `eventId` == pixel `client_dedup_id` (48h window, every event).
57
+ * - `customData.orderId` == pixel `transaction_id` (30d window, PURCHASE).
58
+ */
59
+ interface SnapchatCapiEventPayload {
60
+ eventName: SnapchatEventName;
61
+ eventId: string;
62
+ eventSourceUrl: string;
63
+ userData: {
64
+ /** `ScCid` URL param captured on ad landing. Sent raw, never hashed. */
65
+ clickId?: string;
66
+ /** Value of Snap's first-party `_scid` cookie. Sent raw, never hashed. */
67
+ cookie1?: string;
68
+ };
69
+ customData?: {
70
+ value?: number;
71
+ currency?: string;
72
+ orderId?: string;
73
+ contentIds?: string[];
74
+ numItems?: number;
75
+ };
76
+ }
77
+
78
+ interface GetSnapchatConfigOptions {
79
+ baseUrl?: string;
80
+ publishableApiKey?: string;
81
+ storeId?: string;
82
+ /** Injected `fetch` for testing. Defaults to global `fetch`. */
83
+ fetchImpl?: typeof fetch;
84
+ }
85
+ /**
86
+ * Fetches the non-secret Snapchat config from Medusa. Returns `null` when
87
+ * the backend URL is unknown or the endpoint is missing, unauthorized, or
88
+ * errors. The Conversions API token is never returned by the server route,
89
+ * so it never surfaces here.
90
+ */
91
+ declare function getSnapchatConfig(options?: GetSnapchatConfigOptions): Promise<SnapchatPublicConfig | null>;
92
+
93
+ export { CAPI_RELAY_ENDPOINT as C, type SnapchatCapiEventPayload as S, type SnapchatEventName as a, COOKIE_NAMES as b, SNAP_EVENTS as c, type SnapchatPublicConfig as d, getSnapchatConfig as g };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Every fixed string the package puts on the wire or in browser storage.
3
+ * Wire names are a cross-repo contract: Medusa's strict Zod schema and the
4
+ * orchestrator's storefront relay template expect exactly these values.
5
+ */
6
+ /**
7
+ * Snap standard event names. The pixel (`snaptr('track', <name>)`) and
8
+ * Conversions API v3 (`event_name`) use the SAME uppercase strings.
9
+ * https://developers.snap.com/api/marketing-api/Conversions-API/Parameters
10
+ */
11
+ declare const SNAP_EVENTS: {
12
+ readonly PAGE_VIEW: "PAGE_VIEW";
13
+ readonly VIEW_CONTENT: "VIEW_CONTENT";
14
+ readonly ADD_CART: "ADD_CART";
15
+ readonly START_CHECKOUT: "START_CHECKOUT";
16
+ readonly PURCHASE: "PURCHASE";
17
+ };
18
+ /** Storefront relay route written by the orchestrator's template. */
19
+ declare const CAPI_RELAY_ENDPOINT = "/api/snapchat/events";
20
+ declare const COOKIE_NAMES: {
21
+ /** Plugin-owned raw PII for the relay; Medusa hashes it. */
22
+ readonly PII: "amboras_snap_pii";
23
+ /** Plugin-owned copy of `ScCid`. */
24
+ readonly CLICK_ID: "amboras_snap_clid";
25
+ /** Snap's own first-party cookie (forwarded as `sc_cookie1`). */
26
+ readonly SCID: "_scid";
27
+ };
28
+
29
+ /**
30
+ * Non-secret Snapchat configuration exposed to the browser via
31
+ * `/store/snapchat-ads/config`. Never contains the Conversions API token -
32
+ * that lives only on the Medusa side and never leaves the server.
33
+ */
34
+ interface SnapchatPublicConfig {
35
+ pixel_id: string;
36
+ enabled: boolean;
37
+ track_page_view: boolean;
38
+ track_view_content: boolean;
39
+ track_add_cart: boolean;
40
+ track_start_checkout: boolean;
41
+ track_purchase: boolean;
42
+ }
43
+ /**
44
+ * Snap standard event names. The Snap Pixel (`snaptr('track', <name>)`) and
45
+ * Conversions API v3 (`event_name`) use the SAME uppercase strings, so the
46
+ * storefront sends the wire name end to end and Medusa forwards it as-is.
47
+ * https://developers.snap.com/api/marketing-api/Conversions-API/Parameters
48
+ */
49
+ type SnapchatEventName = (typeof SNAP_EVENTS)[keyof typeof SNAP_EVENTS];
50
+ /**
51
+ * Payload posted from the storefront to `/api/snapchat/events`. The proxy
52
+ * adds the PII cookie, client IP and User-Agent, then forwards to Medusa
53
+ * `/store/snapchat-ads/events` with the same `eventId`.
54
+ *
55
+ * Dedup contract with the browser pixel:
56
+ * - `eventId` == pixel `client_dedup_id` (48h window, every event).
57
+ * - `customData.orderId` == pixel `transaction_id` (30d window, PURCHASE).
58
+ */
59
+ interface SnapchatCapiEventPayload {
60
+ eventName: SnapchatEventName;
61
+ eventId: string;
62
+ eventSourceUrl: string;
63
+ userData: {
64
+ /** `ScCid` URL param captured on ad landing. Sent raw, never hashed. */
65
+ clickId?: string;
66
+ /** Value of Snap's first-party `_scid` cookie. Sent raw, never hashed. */
67
+ cookie1?: string;
68
+ };
69
+ customData?: {
70
+ value?: number;
71
+ currency?: string;
72
+ orderId?: string;
73
+ contentIds?: string[];
74
+ numItems?: number;
75
+ };
76
+ }
77
+
78
+ interface GetSnapchatConfigOptions {
79
+ baseUrl?: string;
80
+ publishableApiKey?: string;
81
+ storeId?: string;
82
+ /** Injected `fetch` for testing. Defaults to global `fetch`. */
83
+ fetchImpl?: typeof fetch;
84
+ }
85
+ /**
86
+ * Fetches the non-secret Snapchat config from Medusa. Returns `null` when
87
+ * the backend URL is unknown or the endpoint is missing, unauthorized, or
88
+ * errors. The Conversions API token is never returned by the server route,
89
+ * so it never surfaces here.
90
+ */
91
+ declare function getSnapchatConfig(options?: GetSnapchatConfigOptions): Promise<SnapchatPublicConfig | null>;
92
+
93
+ export { CAPI_RELAY_ENDPOINT as C, type SnapchatCapiEventPayload as S, type SnapchatEventName as a, COOKIE_NAMES as b, SNAP_EVENTS as c, type SnapchatPublicConfig as d, getSnapchatConfig as g };
@@ -0,0 +1 @@
1
+ export { g as getSnapchatConfig } from './get-config-Dr_GV8ip.mjs';
@@ -0,0 +1 @@
1
+ export { g as getSnapchatConfig } from './get-config-Dr_GV8ip.js';
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/get-config.ts
21
+ var get_config_exports = {};
22
+ __export(get_config_exports, {
23
+ getSnapchatConfig: () => getSnapchatConfig
24
+ });
25
+ module.exports = __toCommonJS(get_config_exports);
26
+ var import_pixel_core = require("@amboras-dev/pixel-core");
27
+ async function getSnapchatConfig(options = {}) {
28
+ const baseUrl = options.baseUrl ?? (0, import_pixel_core.resolveBackendUrl)();
29
+ if (!baseUrl) return null;
30
+ const publishableKey = options.publishableApiKey ?? (0, import_pixel_core.resolvePublishableKey)();
31
+ const storeId = options.storeId ?? (0, import_pixel_core.resolveStoreEnvironmentId)();
32
+ const doFetch = options.fetchImpl ?? fetch;
33
+ try {
34
+ const res = await doFetch(`${baseUrl}/store/snapchat-ads/config`, {
35
+ headers: {
36
+ ...storeId ? { "X-Store-Environment-ID": storeId } : {},
37
+ ...publishableKey ? { "x-publishable-api-key": publishableKey } : {}
38
+ },
39
+ next: { revalidate: 60 }
40
+ });
41
+ if (!res.ok) return null;
42
+ return await res.json();
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+ // Annotate the CommonJS export names for ESM import in node:
48
+ 0 && (module.exports = {
49
+ getSnapchatConfig
50
+ });
51
+ //# sourceMappingURL=get-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/get-config.ts"],"sourcesContent":["import { resolveBackendUrl, resolvePublishableKey, resolveStoreEnvironmentId } from '@amboras-dev/pixel-core'\nimport type { SnapchatPublicConfig } from './types'\n\ninterface GetSnapchatConfigOptions {\n baseUrl?: string\n publishableApiKey?: string\n storeId?: string\n /** Injected `fetch` for testing. Defaults to global `fetch`. */\n fetchImpl?: typeof fetch\n}\n\n/**\n * Fetches the non-secret Snapchat config from Medusa. Returns `null` when\n * the backend URL is unknown or the endpoint is missing, unauthorized, or\n * errors. The Conversions API token is never returned by the server route,\n * so it never surfaces here.\n */\nexport async function getSnapchatConfig(\n options: GetSnapchatConfigOptions = {},\n): Promise<SnapchatPublicConfig | null> {\n const baseUrl = options.baseUrl ?? resolveBackendUrl()\n if (!baseUrl) return null\n const publishableKey = options.publishableApiKey ?? resolvePublishableKey()\n const storeId = options.storeId ?? resolveStoreEnvironmentId()\n const doFetch = options.fetchImpl ?? fetch\n\n try {\n const res = await doFetch(`${baseUrl}/store/snapchat-ads/config`, {\n headers: {\n ...(storeId ? { 'X-Store-Environment-ID': storeId } : {}),\n ...(publishableKey ? { 'x-publishable-api-key': publishableKey } : {}),\n },\n next: { revalidate: 60 },\n } as RequestInit)\n if (!res.ok) return null\n return (await res.json()) as SnapchatPublicConfig\n } catch {\n return null\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAAoF;AAiBpF,eAAsB,kBACpB,UAAoC,CAAC,GACC;AACtC,QAAM,UAAU,QAAQ,eAAW,qCAAkB;AACrD,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,iBAAiB,QAAQ,yBAAqB,yCAAsB;AAC1E,QAAM,UAAU,QAAQ,eAAW,6CAA0B;AAC7D,QAAM,UAAU,QAAQ,aAAa;AAErC,MAAI;AACF,UAAM,MAAM,MAAM,QAAQ,GAAG,OAAO,8BAA8B;AAAA,MAChE,SAAS;AAAA,QACP,GAAI,UAAU,EAAE,0BAA0B,QAAQ,IAAI,CAAC;AAAA,QACvD,GAAI,iBAAiB,EAAE,yBAAyB,eAAe,IAAI,CAAC;AAAA,MACtE;AAAA,MACA,MAAM,EAAE,YAAY,GAAG;AAAA,IACzB,CAAgB;AAChB,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ getSnapchatConfig
3
+ } from "./chunk-IP2VTHD6.mjs";
4
+ export {
5
+ getSnapchatConfig
6
+ };
7
+ //# sourceMappingURL=get-config.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,48 @@
1
+ import { S as SnapchatCapiEventPayload, a as SnapchatEventName } from './get-config-Dr_GV8ip.mjs';
2
+ export { C as CAPI_RELAY_ENDPOINT, b as COOKIE_NAMES, c as SNAP_EVENTS, d as SnapchatPublicConfig, g as getSnapchatConfig } from './get-config-Dr_GV8ip.mjs';
3
+
4
+ /**
5
+ * POST the event to the storefront's `/api/snapchat/events` proxy. The
6
+ * proxy enriches with the PII cookie + IP + User-Agent and forwards to
7
+ * Medusa's `/store/snapchat-ads/events` route, which queues it on the shared
8
+ * outbox. Intentionally UNGATED on analytics consent - CAPI operates on a
9
+ * different legal basis than the browser pixel. Merchant owns that call.
10
+ */
11
+ declare function sendSnapchatCapiEvent(payload: SnapchatCapiEventPayload): Promise<void>;
12
+ declare function trackPageView(): void;
13
+ declare function trackViewContent(productId: string, value?: number, currency?: string): void;
14
+ /**
15
+ * Fire `ADD_CART` for a single variant's quantity delta. `contentIds` is
16
+ * typically `[productId, variantId]`.
17
+ */
18
+ declare function trackAddCart(variantId: string, quantity: number, value?: number, currency?: string, contentIds?: string[]): void;
19
+ /** Deterministic per cart: a checkout refresh collapses into one event. */
20
+ declare function trackStartCheckout(cartId: string, itemCount: number, value?: number, currency?: string, contentIds?: string[]): void;
21
+ /**
22
+ * Order-complete tracker. Both channels use the SAME event name
23
+ * (`PURCHASE`) - unlike Pinterest there is no browser/server name split.
24
+ * Dedup is doubly anchored: `client_dedup_id` == `event_id` (48h) and pixel
25
+ * `transaction_id` == CAPI `order_id` (30d).
26
+ */
27
+ declare function trackPurchase(orderId: string, value: number, currency: string, itemCount?: number, contentIds?: string[]): void;
28
+
29
+ /**
30
+ * Event-ID builders for browser <-> CAPI dedup. Snap matches the pixel's
31
+ * `client_dedup_id` against the CAPI `event_id` within 48h, so the browser
32
+ * call and the CAPI relay for one user action must carry the same ID. Both
33
+ * are fired from the same function call, so the ID only has to be stable
34
+ * within that call - not across calls.
35
+ *
36
+ * Two flavours:
37
+ * - `makeEventId`: deterministic. Used where a repeat fire of the SAME
38
+ * action must collapse into one (START_CHECKOUT per cart, PURCHASE per
39
+ * order), e.g. a refresh on the thank-you page.
40
+ * - `makeUniqueEventId`: deterministic prefix + random suffix. Used for
41
+ * actions a shopper legitimately repeats (viewing a product again,
42
+ * adding the same variant twice, revisiting a page). A pure
43
+ * deterministic ID would make Snap drop every repeat within 48h.
44
+ */
45
+ declare function makeEventId(eventName: SnapchatEventName, primaryKey: string): string;
46
+ declare function makeUniqueEventId(eventName: SnapchatEventName, primaryKey: string): string;
47
+
48
+ export { SnapchatCapiEventPayload, SnapchatEventName, makeEventId, makeUniqueEventId, sendSnapchatCapiEvent, trackAddCart, trackPageView, trackPurchase, trackStartCheckout, trackViewContent };
@@ -0,0 +1,48 @@
1
+ import { S as SnapchatCapiEventPayload, a as SnapchatEventName } from './get-config-Dr_GV8ip.js';
2
+ export { C as CAPI_RELAY_ENDPOINT, b as COOKIE_NAMES, c as SNAP_EVENTS, d as SnapchatPublicConfig, g as getSnapchatConfig } from './get-config-Dr_GV8ip.js';
3
+
4
+ /**
5
+ * POST the event to the storefront's `/api/snapchat/events` proxy. The
6
+ * proxy enriches with the PII cookie + IP + User-Agent and forwards to
7
+ * Medusa's `/store/snapchat-ads/events` route, which queues it on the shared
8
+ * outbox. Intentionally UNGATED on analytics consent - CAPI operates on a
9
+ * different legal basis than the browser pixel. Merchant owns that call.
10
+ */
11
+ declare function sendSnapchatCapiEvent(payload: SnapchatCapiEventPayload): Promise<void>;
12
+ declare function trackPageView(): void;
13
+ declare function trackViewContent(productId: string, value?: number, currency?: string): void;
14
+ /**
15
+ * Fire `ADD_CART` for a single variant's quantity delta. `contentIds` is
16
+ * typically `[productId, variantId]`.
17
+ */
18
+ declare function trackAddCart(variantId: string, quantity: number, value?: number, currency?: string, contentIds?: string[]): void;
19
+ /** Deterministic per cart: a checkout refresh collapses into one event. */
20
+ declare function trackStartCheckout(cartId: string, itemCount: number, value?: number, currency?: string, contentIds?: string[]): void;
21
+ /**
22
+ * Order-complete tracker. Both channels use the SAME event name
23
+ * (`PURCHASE`) - unlike Pinterest there is no browser/server name split.
24
+ * Dedup is doubly anchored: `client_dedup_id` == `event_id` (48h) and pixel
25
+ * `transaction_id` == CAPI `order_id` (30d).
26
+ */
27
+ declare function trackPurchase(orderId: string, value: number, currency: string, itemCount?: number, contentIds?: string[]): void;
28
+
29
+ /**
30
+ * Event-ID builders for browser <-> CAPI dedup. Snap matches the pixel's
31
+ * `client_dedup_id` against the CAPI `event_id` within 48h, so the browser
32
+ * call and the CAPI relay for one user action must carry the same ID. Both
33
+ * are fired from the same function call, so the ID only has to be stable
34
+ * within that call - not across calls.
35
+ *
36
+ * Two flavours:
37
+ * - `makeEventId`: deterministic. Used where a repeat fire of the SAME
38
+ * action must collapse into one (START_CHECKOUT per cart, PURCHASE per
39
+ * order), e.g. a refresh on the thank-you page.
40
+ * - `makeUniqueEventId`: deterministic prefix + random suffix. Used for
41
+ * actions a shopper legitimately repeats (viewing a product again,
42
+ * adding the same variant twice, revisiting a page). A pure
43
+ * deterministic ID would make Snap drop every repeat within 48h.
44
+ */
45
+ declare function makeEventId(eventName: SnapchatEventName, primaryKey: string): string;
46
+ declare function makeUniqueEventId(eventName: SnapchatEventName, primaryKey: string): string;
47
+
48
+ export { SnapchatCapiEventPayload, SnapchatEventName, makeEventId, makeUniqueEventId, sendSnapchatCapiEvent, trackAddCart, trackPageView, trackPurchase, trackStartCheckout, trackViewContent };
package/dist/index.js ADDED
@@ -0,0 +1,251 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ CAPI_RELAY_ENDPOINT: () => CAPI_RELAY_ENDPOINT,
24
+ COOKIE_NAMES: () => COOKIE_NAMES,
25
+ SNAP_EVENTS: () => SNAP_EVENTS,
26
+ getSnapchatConfig: () => getSnapchatConfig,
27
+ makeEventId: () => makeEventId,
28
+ makeUniqueEventId: () => makeUniqueEventId,
29
+ sendSnapchatCapiEvent: () => sendSnapchatCapiEvent,
30
+ trackAddCart: () => trackAddCart,
31
+ trackPageView: () => trackPageView,
32
+ trackPurchase: () => trackPurchase,
33
+ trackStartCheckout: () => trackStartCheckout,
34
+ trackViewContent: () => trackViewContent
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/constants/index.ts
39
+ var SNAP_EVENTS = {
40
+ PAGE_VIEW: "PAGE_VIEW",
41
+ VIEW_CONTENT: "VIEW_CONTENT",
42
+ ADD_CART: "ADD_CART",
43
+ START_CHECKOUT: "START_CHECKOUT",
44
+ PURCHASE: "PURCHASE"
45
+ };
46
+ var EVENT_TOGGLES = {
47
+ PAGE_VIEW: "track_page_view",
48
+ VIEW_CONTENT: "track_view_content",
49
+ ADD_CART: "track_add_cart",
50
+ START_CHECKOUT: "track_start_checkout",
51
+ PURCHASE: "track_purchase"
52
+ };
53
+ var CONFIG_REFRESH_MS = 6e4;
54
+ var CAPI_RELAY_ENDPOINT = "/api/snapchat/events";
55
+ var COOKIE_NAMES = {
56
+ /** Plugin-owned raw PII for the relay; Medusa hashes it. */
57
+ PII: "amboras_snap_pii",
58
+ /** Plugin-owned copy of `ScCid`. */
59
+ CLICK_ID: "amboras_snap_clid",
60
+ /** Snap's own first-party cookie (forwarded as `sc_cookie1`). */
61
+ SCID: "_scid"
62
+ };
63
+ var COOKIE_MAX_AGE_SECONDS = {
64
+ PII: 30 * 24 * 60 * 60,
65
+ // Matches Snap's default 28-day click attribution window.
66
+ CLICK_ID: 28 * 24 * 60 * 60
67
+ };
68
+
69
+ // src/lib/snaptr.ts
70
+ function safeSnaptr(...args) {
71
+ if (typeof window === "undefined") return false;
72
+ const fn = window.snaptr;
73
+ if (typeof fn !== "function") return false;
74
+ try {
75
+ fn(...args);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ // src/lib/event-id.ts
83
+ function makeEventId(eventName, primaryKey) {
84
+ const safeKey = primaryKey.replace(/[^a-zA-Z0-9_-]/g, "_");
85
+ return `${eventName.toLowerCase()}_${safeKey}`;
86
+ }
87
+ function randomSuffix() {
88
+ const c = globalThis.crypto;
89
+ if (c?.randomUUID) return c.randomUUID().replace(/-/g, "").slice(0, 16);
90
+ return Math.random().toString(36).slice(2, 12) + Date.now().toString(36);
91
+ }
92
+ function makeUniqueEventId(eventName, primaryKey) {
93
+ return `${makeEventId(eventName, primaryKey)}_${randomSuffix()}`;
94
+ }
95
+
96
+ // src/lib/cookies.ts
97
+ var import_pixel_core = require("@amboras-dev/pixel-core");
98
+ function readScid() {
99
+ return (0, import_pixel_core.readCookie)(COOKIE_NAMES.SCID);
100
+ }
101
+ function readClickId() {
102
+ return (0, import_pixel_core.readCookie)(COOKIE_NAMES.CLICK_ID);
103
+ }
104
+
105
+ // src/get-config.ts
106
+ var import_pixel_core2 = require("@amboras-dev/pixel-core");
107
+ async function getSnapchatConfig(options = {}) {
108
+ const baseUrl = options.baseUrl ?? (0, import_pixel_core2.resolveBackendUrl)();
109
+ if (!baseUrl) return null;
110
+ const publishableKey = options.publishableApiKey ?? (0, import_pixel_core2.resolvePublishableKey)();
111
+ const storeId = options.storeId ?? (0, import_pixel_core2.resolveStoreEnvironmentId)();
112
+ const doFetch = options.fetchImpl ?? fetch;
113
+ try {
114
+ const res = await doFetch(`${baseUrl}/store/snapchat-ads/config`, {
115
+ headers: {
116
+ ...storeId ? { "X-Store-Environment-ID": storeId } : {},
117
+ ...publishableKey ? { "x-publishable-api-key": publishableKey } : {}
118
+ },
119
+ next: { revalidate: 60 }
120
+ });
121
+ if (!res.ok) return null;
122
+ return await res.json();
123
+ } catch {
124
+ return null;
125
+ }
126
+ }
127
+
128
+ // src/lib/runtime-config.ts
129
+ var cached = null;
130
+ function loadSnapchatConfig() {
131
+ const now = Date.now();
132
+ if (cached && now - cached.at < CONFIG_REFRESH_MS) return cached.read;
133
+ const read = getSnapchatConfig().then((config) => {
134
+ if (!config) cached = null;
135
+ return config;
136
+ });
137
+ cached = { at: now, read };
138
+ return read;
139
+ }
140
+ function activePixelId(config) {
141
+ if (!config || config.enabled !== true) return null;
142
+ return config.pixel_id || null;
143
+ }
144
+ function isEventEnabled(config, eventName) {
145
+ if (!activePixelId(config)) return false;
146
+ return config[EVENT_TOGGLES[eventName]] !== false;
147
+ }
148
+
149
+ // src/hooks/useSnapchatEvents.ts
150
+ function currentUrl() {
151
+ return typeof window !== "undefined" ? window.location.href : "";
152
+ }
153
+ function baseUserData() {
154
+ const out = {};
155
+ const clickId = readClickId();
156
+ const cookie1 = readScid();
157
+ if (clickId) out.clickId = clickId;
158
+ if (cookie1) out.cookie1 = cookie1;
159
+ return out;
160
+ }
161
+ function compact(input) {
162
+ const out = {};
163
+ for (const [k, v] of Object.entries(input)) {
164
+ if (v !== void 0) out[k] = v;
165
+ }
166
+ return out;
167
+ }
168
+ async function sendSnapchatCapiEvent(payload) {
169
+ try {
170
+ await fetch(CAPI_RELAY_ENDPOINT, {
171
+ method: "POST",
172
+ headers: { "Content-Type": "application/json" },
173
+ body: JSON.stringify(payload),
174
+ keepalive: true
175
+ });
176
+ } catch {
177
+ }
178
+ }
179
+ function fire(eventName, eventId, pixelParams, customData) {
180
+ const payload = {
181
+ eventName,
182
+ eventId,
183
+ eventSourceUrl: currentUrl(),
184
+ userData: baseUserData(),
185
+ ...customData ? { customData } : {}
186
+ };
187
+ const params = compact({ ...pixelParams, client_dedup_id: eventId });
188
+ void loadSnapchatConfig().then((config) => {
189
+ if (!isEventEnabled(config, eventName)) return;
190
+ safeSnaptr("track", eventName, params);
191
+ void sendSnapchatCapiEvent(payload);
192
+ });
193
+ }
194
+ function trackPageView() {
195
+ fire(SNAP_EVENTS.PAGE_VIEW, makeUniqueEventId(SNAP_EVENTS.PAGE_VIEW, "page"), {});
196
+ }
197
+ function trackViewContent(productId, value, currency) {
198
+ fire(
199
+ SNAP_EVENTS.VIEW_CONTENT,
200
+ makeUniqueEventId(SNAP_EVENTS.VIEW_CONTENT, productId),
201
+ { price: value, currency, item_ids: [productId] },
202
+ { value, currency, contentIds: [productId] }
203
+ );
204
+ }
205
+ function trackAddCart(variantId, quantity, value, currency, contentIds) {
206
+ const ids = contentIds?.length ? contentIds : [variantId];
207
+ fire(
208
+ SNAP_EVENTS.ADD_CART,
209
+ makeUniqueEventId(SNAP_EVENTS.ADD_CART, variantId),
210
+ { price: value, currency, item_ids: ids, number_items: quantity },
211
+ { value, currency, contentIds: ids, numItems: quantity }
212
+ );
213
+ }
214
+ function trackStartCheckout(cartId, itemCount, value, currency, contentIds) {
215
+ fire(
216
+ SNAP_EVENTS.START_CHECKOUT,
217
+ makeEventId(SNAP_EVENTS.START_CHECKOUT, cartId),
218
+ { price: value, currency, item_ids: contentIds?.length ? contentIds : void 0, number_items: itemCount },
219
+ { value, currency, contentIds, numItems: itemCount }
220
+ );
221
+ }
222
+ function trackPurchase(orderId, value, currency, itemCount, contentIds) {
223
+ fire(
224
+ SNAP_EVENTS.PURCHASE,
225
+ makeEventId(SNAP_EVENTS.PURCHASE, orderId),
226
+ {
227
+ price: value,
228
+ currency,
229
+ transaction_id: orderId,
230
+ item_ids: contentIds?.length ? contentIds : void 0,
231
+ number_items: itemCount
232
+ },
233
+ { value, currency, orderId, contentIds, numItems: itemCount }
234
+ );
235
+ }
236
+ // Annotate the CommonJS export names for ESM import in node:
237
+ 0 && (module.exports = {
238
+ CAPI_RELAY_ENDPOINT,
239
+ COOKIE_NAMES,
240
+ SNAP_EVENTS,
241
+ getSnapchatConfig,
242
+ makeEventId,
243
+ makeUniqueEventId,
244
+ sendSnapchatCapiEvent,
245
+ trackAddCart,
246
+ trackPageView,
247
+ trackPurchase,
248
+ trackStartCheckout,
249
+ trackViewContent
250
+ });
251
+ //# sourceMappingURL=index.js.map