@replohq/sdk 1.14.1 → 1.16.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/_vendor/schemas/generated/consent.d.ts +13 -1
- package/_vendor/schemas/loaderKeys.mjs +1 -0
- package/analytics/analytics-provider.d.ts +2 -0
- package/analytics/analytics-provider.js +38 -8
- package/analytics/analytics-sink.d.ts +9 -0
- package/analytics/combine-sinks.js +2 -1
- package/analytics/get-analytics-sinks.js +9 -0
- package/analytics/sinks/intelligems-sink.d.ts +29 -0
- package/analytics/sinks/intelligems-sink.js +68 -0
- package/analytics/sinks/x-sink.d.ts +32 -0
- package/analytics/sinks/x-sink.js +126 -0
- package/cart/buy-now-action.d.ts +4 -0
- package/cart/buy-now-action.js +2 -1
- package/cart/cart-actions.d.ts +4 -0
- package/cart/cart-actions.js +2 -0
- package/cart/cart-provider.d.ts +2 -0
- package/cart/cart-provider.js +1 -0
- package/cart/gateways/cart-gateway.d.ts +4 -0
- package/cart/gateways/resolver-cart-gateway.js +2 -0
- package/cart/hooks/use-buy-now.d.ts +5 -0
- package/cart/hooks/use-buy-now.js +1 -0
- package/consent/replo-scripts.js +19 -5
- package/consent/script-registration-store.d.ts +9 -4
- package/consent/script-snippets.d.ts +9 -9
- package/consent/script-snippets.js +31 -5
- package/consent/types.d.ts +13 -1
- package/gateways/canopy-api.d.ts +4 -0
- package/gateways/canopy-api.js +1 -0
- package/lib/buildMetadata.js +3 -3
- package/loaders/loader-keys.d.ts +1 -0
- package/loaders/loader-keys.js +1 -0
- package/loaders/loader-schemas.d.ts +5 -0
- package/loaders/loader-schemas.js +9 -0
- package/loaders/shopify-integration-details-loader.d.ts +9 -0
- package/loaders/shopify-integration-details-loader.js +33 -0
- package/package.json +12 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ReploScriptType = "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "Cookiebot" | "snippet" | "custom" | "consentPlatform";
|
|
1
|
+
export type ReploScriptType = "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "X" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "Cookiebot" | "snippet" | "custom" | "consentPlatform";
|
|
2
2
|
export type ConsentCategory = "necessary" | "analytics" | "marketing" | "preferences" | "sale_of_data";
|
|
3
3
|
export type ConsentMode = "off" | "simple" | "per-category";
|
|
4
4
|
export type ScriptTagDescriptor = {
|
|
@@ -12,10 +12,22 @@ export type ScriptTagDescriptor = {
|
|
|
12
12
|
body: string;
|
|
13
13
|
module?: boolean;
|
|
14
14
|
};
|
|
15
|
+
export type XPixelEvents = {
|
|
16
|
+
pageView?: string;
|
|
17
|
+
productViewed?: string;
|
|
18
|
+
addToCart?: string;
|
|
19
|
+
startCheckout?: string;
|
|
20
|
+
purchase?: string;
|
|
21
|
+
};
|
|
15
22
|
export type ReploScriptEntry = {
|
|
16
23
|
type: "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge";
|
|
17
24
|
identifier: string;
|
|
18
25
|
requiredConsent?: ConsentCategory[];
|
|
26
|
+
} | {
|
|
27
|
+
type: "X";
|
|
28
|
+
identifier: string;
|
|
29
|
+
events?: Partial<XPixelEvents>;
|
|
30
|
+
requiredConsent?: ConsentCategory[];
|
|
19
31
|
} | {
|
|
20
32
|
type: "Cookiebot";
|
|
21
33
|
identifier: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '../../chunk-UJCSKKID.mjs';
|
|
2
2
|
|
|
3
3
|
var DATA_LOADER_KEYS = {
|
|
4
|
+
SHOPIFY_INTEGRATION_DETAILS: "shopify_storefront.integration_details",
|
|
4
5
|
SHOPIFY_PRODUCT: "shopify_storefront.product",
|
|
5
6
|
SHOPIFY_COLLECTION: "shopify_storefront.collection",
|
|
6
7
|
SHOPIFY_COLLECTION_PRODUCTS: "shopify_storefront.collection_products",
|
|
@@ -4,6 +4,8 @@ import type { AnalyticsSink } from "./analytics-sink";
|
|
|
4
4
|
export declare function AnalyticsProvider({ children, checkoutProvider, }: React.PropsWithChildren<{
|
|
5
5
|
checkoutProvider?: CheckoutProvider;
|
|
6
6
|
}>): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
/** Register one stable, consent-aware sink beneath ReploProvider. */
|
|
8
|
+
export declare function useAnalyticsSink(sink: AnalyticsSink): void;
|
|
7
9
|
/**
|
|
8
10
|
* Returns the AnalyticsSink from the nearest AnalyticsProvider.
|
|
9
11
|
* Throws if used outside of an AnalyticsProvider.
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createContext,
|
|
5
|
+
useCallback,
|
|
6
|
+
useContext,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState
|
|
11
|
+
} from "react";
|
|
4
12
|
import { useConsent } from "../consent/consent-store";
|
|
5
13
|
import { useRegisteredScripts } from "../consent/script-registration-store";
|
|
6
14
|
import { combineSinks } from "./combine-sinks";
|
|
@@ -13,9 +21,18 @@ function AnalyticsProvider({
|
|
|
13
21
|
}) {
|
|
14
22
|
const consent = useConsent();
|
|
15
23
|
const registeredScripts = useRegisteredScripts();
|
|
24
|
+
const [additionalSinks, setAdditionalSinks] = useState([]);
|
|
25
|
+
const registerSink = useCallback((sink) => {
|
|
26
|
+
setAdditionalSinks((current) => [...current, sink]);
|
|
27
|
+
return () => {
|
|
28
|
+
setAdditionalSinks((current) => {
|
|
29
|
+
return current.filter((registered) => registered !== sink);
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
16
33
|
const analytics = useMemo(() => {
|
|
17
|
-
return combineSinks(
|
|
18
|
-
getAnalyticsSinks({
|
|
34
|
+
return combineSinks([
|
|
35
|
+
...getAnalyticsSinks({
|
|
19
36
|
consent: {
|
|
20
37
|
cmp: consent.cmp,
|
|
21
38
|
mode: consent.mode,
|
|
@@ -24,15 +41,17 @@ function AnalyticsProvider({
|
|
|
24
41
|
},
|
|
25
42
|
registeredScripts,
|
|
26
43
|
checkoutProvider
|
|
27
|
-
})
|
|
28
|
-
|
|
44
|
+
}),
|
|
45
|
+
...additionalSinks
|
|
46
|
+
]);
|
|
29
47
|
}, [
|
|
30
48
|
consent.cmp,
|
|
31
49
|
consent.mode,
|
|
32
50
|
consent.categories,
|
|
33
51
|
consent.decidedAt,
|
|
34
52
|
registeredScripts,
|
|
35
|
-
checkoutProvider
|
|
53
|
+
checkoutProvider,
|
|
54
|
+
additionalSinks
|
|
36
55
|
]);
|
|
37
56
|
const analyticsRef = useRef(analytics);
|
|
38
57
|
analyticsRef.current = analytics;
|
|
@@ -51,7 +70,17 @@ function AnalyticsProvider({
|
|
|
51
70
|
navigationTracker.current?.cleanup();
|
|
52
71
|
};
|
|
53
72
|
}, []);
|
|
54
|
-
return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value: { analytics }, children });
|
|
73
|
+
return /* @__PURE__ */ jsx(AnalyticsContext.Provider, { value: { analytics, registerSink }, children });
|
|
74
|
+
}
|
|
75
|
+
function useAnalyticsSink(sink) {
|
|
76
|
+
const context = useContext(AnalyticsContext);
|
|
77
|
+
if (!context) {
|
|
78
|
+
throw new AnalyticsContextError({
|
|
79
|
+
message: "useAnalyticsSink must be used within <AnalyticsProvider>"
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const registerSink = context.registerSink;
|
|
83
|
+
useEffect(() => registerSink(sink), [registerSink, sink]);
|
|
55
84
|
}
|
|
56
85
|
class AnalyticsContextError extends Error {
|
|
57
86
|
constructor({ message }) {
|
|
@@ -74,5 +103,6 @@ function useAnalyticsOptional() {
|
|
|
74
103
|
export {
|
|
75
104
|
AnalyticsProvider,
|
|
76
105
|
useAnalytics,
|
|
77
|
-
useAnalyticsOptional
|
|
106
|
+
useAnalyticsOptional,
|
|
107
|
+
useAnalyticsSink
|
|
78
108
|
};
|
|
@@ -13,7 +13,16 @@ export type PurchasePayload = {
|
|
|
13
13
|
currency: string;
|
|
14
14
|
data: Record<string, any>;
|
|
15
15
|
};
|
|
16
|
+
export type AnalyticsProperty = string | number | boolean | null | AnalyticsProperty[] | {
|
|
17
|
+
[key: string]: AnalyticsProperty;
|
|
18
|
+
};
|
|
19
|
+
export interface CustomEventPayload {
|
|
20
|
+
event: string;
|
|
21
|
+
properties?: Record<string, AnalyticsProperty>;
|
|
22
|
+
}
|
|
16
23
|
export interface AnalyticsSink {
|
|
24
|
+
/** Explicit business goals; sinks without custom-event support may omit this. */
|
|
25
|
+
customEvent?(payload: CustomEventPayload): Promise<void>;
|
|
17
26
|
pageView(metadata?: PageMetadata): Promise<void>;
|
|
18
27
|
purchase(payload: PurchasePayload): Promise<void>;
|
|
19
28
|
click(payload: DataPayload): Promise<void>;
|
|
@@ -12,7 +12,7 @@ function combineSinks(sinks) {
|
|
|
12
12
|
sinks.map(async (sink) => {
|
|
13
13
|
try {
|
|
14
14
|
const sinkMethod = sink[method];
|
|
15
|
-
await sinkMethod
|
|
15
|
+
await sinkMethod?.apply(sink, args);
|
|
16
16
|
} catch {
|
|
17
17
|
if (typeof window !== "undefined") {
|
|
18
18
|
console.warn("[analytics] sink error", { method });
|
|
@@ -22,6 +22,7 @@ function combineSinks(sinks) {
|
|
|
22
22
|
);
|
|
23
23
|
};
|
|
24
24
|
return {
|
|
25
|
+
customEvent: (...args) => broadcast("customEvent", ...args),
|
|
25
26
|
pageView: (...args) => broadcast("pageView", ...args),
|
|
26
27
|
purchase: (...args) => broadcast("purchase", ...args),
|
|
27
28
|
click: (...args) => broadcast("click", ...args),
|
|
@@ -11,6 +11,7 @@ import { ReploSink } from "./sinks/replo-sink";
|
|
|
11
11
|
import { SnapchatSink } from "./sinks/snapchat-sink";
|
|
12
12
|
import { TikTokSink } from "./sinks/tiktok-sink";
|
|
13
13
|
import { WindowSink } from "./sinks/window-sink";
|
|
14
|
+
import { XSink } from "./sinks/x-sink";
|
|
14
15
|
import { gateShopifyOwnedFunnelEvents } from "./utils/shopify-funnel-gate";
|
|
15
16
|
const PROVIDER_SINK_FACTORIES = {
|
|
16
17
|
GA4: () => new GA4Sink(),
|
|
@@ -26,6 +27,14 @@ const SNIPPET_PROVIDER_SINK_FACTORIES = {
|
|
|
26
27
|
[snippetScriptId("Elevar")]: () => new ElevarSink()
|
|
27
28
|
};
|
|
28
29
|
function findSinkFactory(script) {
|
|
30
|
+
if (script.type === "X") {
|
|
31
|
+
return () => {
|
|
32
|
+
return new XSink({
|
|
33
|
+
pixelId: script.pixelId,
|
|
34
|
+
events: script.events
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
}
|
|
29
38
|
if (script.type === "snippet") {
|
|
30
39
|
return SNIPPET_PROVIDER_SINK_FACTORIES[script.id] ?? null;
|
|
31
40
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { AnalyticsSink, CustomEventPayload, DataPayload, PurchasePayload } from "../analytics-sink";
|
|
2
|
+
import type { PageMetadata } from "../utils/navigation-tracker";
|
|
3
|
+
export type IntelligemsEventMappings = {
|
|
4
|
+
[Method in Exclude<keyof AnalyticsSink, "customEvent">]?: (...args: Parameters<AnalyticsSink[Method]>) => CustomEventPayload | null;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Sends explicitly selected custom goals through the installed Intelligems SDK.
|
|
8
|
+
* Native page views, experiment assignment, and order attribution remain owned
|
|
9
|
+
* by useIgTrack and Shopify; duplicating them as custom events changes reports.
|
|
10
|
+
*/
|
|
11
|
+
export declare class IntelligemsSink implements AnalyticsSink {
|
|
12
|
+
private readonly options;
|
|
13
|
+
constructor(options: {
|
|
14
|
+
/** Read live SDK readiness; do not accept events before assignment resolves. */
|
|
15
|
+
isReady: () => boolean;
|
|
16
|
+
events?: IntelligemsEventMappings;
|
|
17
|
+
});
|
|
18
|
+
customEvent(payload: CustomEventPayload): Promise<void>;
|
|
19
|
+
private send;
|
|
20
|
+
pageView(metadata?: PageMetadata): Promise<void>;
|
|
21
|
+
purchase(payload: PurchasePayload): Promise<void>;
|
|
22
|
+
click(payload: DataPayload): Promise<void>;
|
|
23
|
+
productViewed(payload: DataPayload): Promise<void>;
|
|
24
|
+
addToCart(payload: DataPayload): Promise<void>;
|
|
25
|
+
removeFromCart(payload: DataPayload): Promise<void>;
|
|
26
|
+
viewCart(payload: DataPayload): Promise<void>;
|
|
27
|
+
startCheckout(payload: DataPayload): Promise<void>;
|
|
28
|
+
viewCollection(payload: DataPayload): Promise<void>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { consentStore, isConsentAllowed } from "../../consent/consent-store";
|
|
3
|
+
class IntelligemsSink {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
this.options = options;
|
|
6
|
+
}
|
|
7
|
+
async customEvent(payload) {
|
|
8
|
+
if (typeof window === "undefined" || !this.options.isReady()) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (!isConsentAllowed({
|
|
12
|
+
state: consentStore.getSnapshot(),
|
|
13
|
+
requiredConsent: ["analytics"]
|
|
14
|
+
})) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!payload.event.trim() || /[,']/.test(payload.event) || payload.event !== payload.event.trim()) {
|
|
18
|
+
throw new IntelligemsEventNameError(
|
|
19
|
+
"Intelligems event names must be nonempty, trimmed, and contain no commas or single quotes"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
if (!("igEvents" in window)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const transport = window.igEvents;
|
|
26
|
+
if (!transport || typeof transport !== "object" || Array.isArray(transport) || !("push" in transport) || typeof transport.push !== "function") {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
await transport.push(payload);
|
|
30
|
+
}
|
|
31
|
+
async send(payload) {
|
|
32
|
+
if (payload) {
|
|
33
|
+
await this.customEvent(payload);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async pageView(metadata) {
|
|
37
|
+
await this.send(this.options.events?.pageView?.(metadata));
|
|
38
|
+
}
|
|
39
|
+
async purchase(payload) {
|
|
40
|
+
await this.send(this.options.events?.purchase?.(payload));
|
|
41
|
+
}
|
|
42
|
+
async click(payload) {
|
|
43
|
+
await this.send(this.options.events?.click?.(payload));
|
|
44
|
+
}
|
|
45
|
+
async productViewed(payload) {
|
|
46
|
+
await this.send(this.options.events?.productViewed?.(payload));
|
|
47
|
+
}
|
|
48
|
+
async addToCart(payload) {
|
|
49
|
+
await this.send(this.options.events?.addToCart?.(payload));
|
|
50
|
+
}
|
|
51
|
+
async removeFromCart(payload) {
|
|
52
|
+
await this.send(this.options.events?.removeFromCart?.(payload));
|
|
53
|
+
}
|
|
54
|
+
async viewCart(payload) {
|
|
55
|
+
await this.send(this.options.events?.viewCart?.(payload));
|
|
56
|
+
}
|
|
57
|
+
async startCheckout(payload) {
|
|
58
|
+
await this.send(this.options.events?.startCheckout?.(payload));
|
|
59
|
+
}
|
|
60
|
+
async viewCollection(payload) {
|
|
61
|
+
await this.send(this.options.events?.viewCollection?.(payload));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class IntelligemsEventNameError extends Error {
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
IntelligemsSink
|
|
68
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { XPixelEvents } from "../../_vendor/schemas/generated/consent";
|
|
2
|
+
import type { AnalyticsSink, DataPayload, PurchasePayload } from "../analytics-sink";
|
|
3
|
+
import type { PageMetadata } from "../utils/navigation-tracker";
|
|
4
|
+
declare global {
|
|
5
|
+
interface Window {
|
|
6
|
+
twq?: (command: "config" | "event", ...args: unknown[]) => void;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Sends storefront events to X as the conversion events the merchant created
|
|
11
|
+
* in X Ads Events Manager. X has no named events in code, so an event without
|
|
12
|
+
* a mapped ID sends nothing. The consent-gated base code's `twq('config')`
|
|
13
|
+
* already records the site visit and landing page view on load.
|
|
14
|
+
*/
|
|
15
|
+
export declare class XSink implements AnalyticsSink {
|
|
16
|
+
private readonly events;
|
|
17
|
+
private send;
|
|
18
|
+
constructor({ pixelId, events }: {
|
|
19
|
+
pixelId: string;
|
|
20
|
+
events: XPixelEvents;
|
|
21
|
+
});
|
|
22
|
+
private track;
|
|
23
|
+
pageView(metadata: PageMetadata | undefined): Promise<void>;
|
|
24
|
+
purchase({ amount, currency, data }: PurchasePayload): Promise<void>;
|
|
25
|
+
click(): Promise<void>;
|
|
26
|
+
productViewed({ data }: DataPayload): Promise<void>;
|
|
27
|
+
addToCart({ data }: DataPayload): Promise<void>;
|
|
28
|
+
removeFromCart(): Promise<void>;
|
|
29
|
+
viewCart(): Promise<void>;
|
|
30
|
+
startCheckout({ data }: DataPayload): Promise<void>;
|
|
31
|
+
viewCollection(): Promise<void>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { filterUndefined } from "../../_vendor/replo-utils/lib/object.mjs";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { isXEventIdForPixel } from "../../consent/script-snippets";
|
|
4
|
+
import { legacyResourceId } from "../utils/analytics-products";
|
|
5
|
+
import { createEventQueue } from "../utils/analytics-utils";
|
|
6
|
+
const analyticsRecordSchema = z.record(z.string(), z.unknown());
|
|
7
|
+
const nonEmptyStringSchema = z.union([
|
|
8
|
+
z.string().min(1),
|
|
9
|
+
z.number().finite().transform(String)
|
|
10
|
+
]);
|
|
11
|
+
const finiteNumberSchema = z.union([
|
|
12
|
+
z.number().finite(),
|
|
13
|
+
z.string().trim().min(1).transform(Number).pipe(z.number().finite())
|
|
14
|
+
]);
|
|
15
|
+
function parseRecord(value) {
|
|
16
|
+
const result = analyticsRecordSchema.safeParse(value);
|
|
17
|
+
return result.success ? result.data : {};
|
|
18
|
+
}
|
|
19
|
+
function parseString(value) {
|
|
20
|
+
const result = nonEmptyStringSchema.safeParse(value);
|
|
21
|
+
return result.success ? result.data : void 0;
|
|
22
|
+
}
|
|
23
|
+
function parseNumber(value) {
|
|
24
|
+
const result = finiteNumberSchema.safeParse(value);
|
|
25
|
+
return result.success ? result.data : void 0;
|
|
26
|
+
}
|
|
27
|
+
function mapContent(value, defaultQuantity) {
|
|
28
|
+
const line = parseRecord(value);
|
|
29
|
+
const variant = parseRecord(line.variant);
|
|
30
|
+
const product = parseRecord(variant.product);
|
|
31
|
+
const productId = parseString(line.productId) ?? parseString(product.id);
|
|
32
|
+
if (!productId) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return filterUndefined({
|
|
36
|
+
content_id: legacyResourceId(productId),
|
|
37
|
+
content_name: parseString(line.productTitle) ?? parseString(product.title) ?? parseString(line.title),
|
|
38
|
+
content_price: parseNumber(line.price) ?? parseNumber(parseRecord(variant.price).amount),
|
|
39
|
+
num_items: parseNumber(line.quantity) ?? defaultQuantity
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function mapContents(lines) {
|
|
43
|
+
const result = z.array(z.unknown()).safeParse(lines);
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
return void 0;
|
|
46
|
+
}
|
|
47
|
+
const contents = result.data.map((line) => mapContent(line, void 0)).filter((content) => content !== null);
|
|
48
|
+
return contents.length > 0 ? contents : void 0;
|
|
49
|
+
}
|
|
50
|
+
function lineValue(content) {
|
|
51
|
+
if (content?.content_price === void 0) {
|
|
52
|
+
return void 0;
|
|
53
|
+
}
|
|
54
|
+
return content.content_price * (content.num_items ?? 1);
|
|
55
|
+
}
|
|
56
|
+
class XSink {
|
|
57
|
+
events;
|
|
58
|
+
send = createEventQueue(
|
|
59
|
+
() => typeof window !== "undefined" && Boolean(window.twq),
|
|
60
|
+
(eventId, parameters) => window.twq?.("event", eventId, parameters),
|
|
61
|
+
"X"
|
|
62
|
+
);
|
|
63
|
+
constructor({ pixelId, events }) {
|
|
64
|
+
this.events = Object.fromEntries(
|
|
65
|
+
Object.entries(events).flatMap(([event, eventId]) => {
|
|
66
|
+
const trimmed = eventId?.trim();
|
|
67
|
+
return trimmed && isXEventIdForPixel({ eventId: trimmed, pixelId }) ? [[event, trimmed]] : [];
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
track(event, parameters) {
|
|
72
|
+
const eventId = this.events[event];
|
|
73
|
+
if (eventId) {
|
|
74
|
+
this.send(eventId, filterUndefined(parameters));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async pageView(metadata) {
|
|
78
|
+
if (metadata?.navigationType === "initial") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.track("pageView", {});
|
|
82
|
+
}
|
|
83
|
+
async purchase({ amount, currency, data }) {
|
|
84
|
+
this.track("purchase", {
|
|
85
|
+
value: amount,
|
|
86
|
+
currency,
|
|
87
|
+
conversion_id: parseString(data.order_id) ?? parseString(data.orderId) ?? parseString(data.id),
|
|
88
|
+
contents: mapContents(data.lineItems)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async click() {
|
|
92
|
+
}
|
|
93
|
+
async productViewed({ data }) {
|
|
94
|
+
const content = mapContent(data, 1);
|
|
95
|
+
this.track("productViewed", {
|
|
96
|
+
value: lineValue(content),
|
|
97
|
+
currency: parseString(data.currency),
|
|
98
|
+
contents: content ? [content] : void 0
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async addToCart({ data }) {
|
|
102
|
+
const content = mapContent(data, 1);
|
|
103
|
+
this.track("addToCart", {
|
|
104
|
+
value: lineValue(content),
|
|
105
|
+
currency: parseString(data.currency),
|
|
106
|
+
contents: content ? [content] : void 0
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async removeFromCart() {
|
|
110
|
+
}
|
|
111
|
+
async viewCart() {
|
|
112
|
+
}
|
|
113
|
+
async startCheckout({ data }) {
|
|
114
|
+
const totalPrice = parseRecord(data.totalPrice);
|
|
115
|
+
this.track("startCheckout", {
|
|
116
|
+
value: parseNumber(totalPrice.amount),
|
|
117
|
+
currency: parseString(data.currencyCode) ?? parseString(totalPrice.currencyCode),
|
|
118
|
+
contents: mapContents(data.lineItems)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
async viewCollection() {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export {
|
|
125
|
+
XSink
|
|
126
|
+
};
|
package/cart/buy-now-action.d.ts
CHANGED
package/cart/buy-now-action.js
CHANGED
|
@@ -122,7 +122,8 @@ async function buyNowWithRecoveryAction(input) {
|
|
|
122
122
|
lines: cartLines,
|
|
123
123
|
skipStoringCart: true,
|
|
124
124
|
country: input.country,
|
|
125
|
-
discountCodes: discountCodes.length > 0 ? discountCodes : void 0
|
|
125
|
+
discountCodes: discountCodes.length > 0 ? discountCodes : void 0,
|
|
126
|
+
attributes: input.attributes
|
|
126
127
|
});
|
|
127
128
|
if (!cart?.checkoutUrl) {
|
|
128
129
|
throw new BuyNowCartCreationError({
|
package/cart/cart-actions.d.ts
CHANGED
|
@@ -32,6 +32,10 @@ export declare function createCartAction(options?: {
|
|
|
32
32
|
lines?: CartLinePayload[];
|
|
33
33
|
skipStoringCart?: boolean;
|
|
34
34
|
discountCodes?: string[];
|
|
35
|
+
attributes?: {
|
|
36
|
+
key: string;
|
|
37
|
+
value: string;
|
|
38
|
+
}[];
|
|
35
39
|
country?: StorefrontCountryCode;
|
|
36
40
|
}): Promise<Cart | null>;
|
|
37
41
|
/**
|
package/cart/cart-actions.js
CHANGED
|
@@ -65,6 +65,7 @@ async function createCartAction(options) {
|
|
|
65
65
|
lines,
|
|
66
66
|
skipStoringCart = false,
|
|
67
67
|
discountCodes,
|
|
68
|
+
attributes,
|
|
68
69
|
country
|
|
69
70
|
} = options ?? {};
|
|
70
71
|
const checkoutProvider = await getCheckoutProvider();
|
|
@@ -72,6 +73,7 @@ async function createCartAction(options) {
|
|
|
72
73
|
const cart = await cartGateway.createCart({
|
|
73
74
|
lines,
|
|
74
75
|
discountCodes,
|
|
76
|
+
attributes,
|
|
75
77
|
buyerIdentity: country ? { country } : void 0
|
|
76
78
|
});
|
|
77
79
|
if (cart && !skipStoringCart) {
|
package/cart/cart-provider.d.ts
CHANGED
|
@@ -66,6 +66,8 @@ export interface CartContextTypeInternal {
|
|
|
66
66
|
* For buy now functionality, use useBuyNow.
|
|
67
67
|
*/
|
|
68
68
|
export interface CartContextType {
|
|
69
|
+
/** Current cart ID for integrations that track checkout attribution. */
|
|
70
|
+
cartId: string | null;
|
|
69
71
|
/** Total number of items in cart */
|
|
70
72
|
itemsCount: number;
|
|
71
73
|
/** Whether the cart UI is open */
|
package/cart/cart-provider.js
CHANGED
|
@@ -11,6 +11,10 @@ export interface CartGateway {
|
|
|
11
11
|
createCart(options?: {
|
|
12
12
|
lines?: CartLinePayload[];
|
|
13
13
|
discountCodes?: string[];
|
|
14
|
+
attributes?: {
|
|
15
|
+
key: string;
|
|
16
|
+
value: string;
|
|
17
|
+
}[];
|
|
14
18
|
buyerIdentity?: CartBuyerIdentity;
|
|
15
19
|
}): Promise<Cart | null>;
|
|
16
20
|
addToCart(cartPayload: AddToCartPayload): Promise<Cart | null>;
|
|
@@ -11,6 +11,7 @@ class ResolverCartGateway {
|
|
|
11
11
|
async createCart({
|
|
12
12
|
lines,
|
|
13
13
|
discountCodes,
|
|
14
|
+
attributes,
|
|
14
15
|
buyerIdentity
|
|
15
16
|
} = {}) {
|
|
16
17
|
try {
|
|
@@ -26,6 +27,7 @@ class ResolverCartGateway {
|
|
|
26
27
|
sellingPlanId: line.sellingPlanId
|
|
27
28
|
})),
|
|
28
29
|
discountCodes,
|
|
30
|
+
attributes,
|
|
29
31
|
country: buyerIdentity?.country
|
|
30
32
|
});
|
|
31
33
|
} catch (error) {
|
|
@@ -81,5 +81,10 @@ export interface VariantBuyNowInfo {
|
|
|
81
81
|
export declare function useBuyNow(): {
|
|
82
82
|
buyNow: (variants: VariantBuyNowInfo[], options?: {
|
|
83
83
|
discountCodes?: string[];
|
|
84
|
+
/** Order-level Shopify cart attributes, including experiment attribution. */
|
|
85
|
+
attributes?: {
|
|
86
|
+
key: string;
|
|
87
|
+
value: string;
|
|
88
|
+
}[];
|
|
84
89
|
}) => Promise<BuyNowOutcome>;
|
|
85
90
|
};
|
|
@@ -35,6 +35,7 @@ function useBuyNow() {
|
|
|
35
35
|
properties: variant.properties ?? []
|
|
36
36
|
})),
|
|
37
37
|
discountCodes: options?.discountCodes ?? [],
|
|
38
|
+
attributes: options?.attributes ?? [],
|
|
38
39
|
pageUrl: window.location.href
|
|
39
40
|
};
|
|
40
41
|
const attemptStorageKey = `replo-checkout-attempt:${JSON.stringify(checkoutInput)}`;
|
package/consent/replo-scripts.js
CHANGED
|
@@ -57,7 +57,7 @@ function descriptorsFor(entry) {
|
|
|
57
57
|
if (entry.type === "consentPlatform" || entry.type === "Cookiebot") {
|
|
58
58
|
return [];
|
|
59
59
|
}
|
|
60
|
-
return buildScriptTags(
|
|
60
|
+
return buildScriptTags(entry);
|
|
61
61
|
}
|
|
62
62
|
class InjectedScript extends Component {
|
|
63
63
|
componentDidMount() {
|
|
@@ -89,17 +89,31 @@ class ScriptRegistration extends Component {
|
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
-
function
|
|
93
|
-
const requiredConsent = requiredConsentFor(entry);
|
|
92
|
+
function registrationFor(entry) {
|
|
94
93
|
const id = scriptId(entry);
|
|
94
|
+
const requiredConsent = requiredConsentFor(entry);
|
|
95
|
+
if (entry.type === "X") {
|
|
96
|
+
return {
|
|
97
|
+
id,
|
|
98
|
+
type: "X",
|
|
99
|
+
requiredConsent,
|
|
100
|
+
pixelId: entry.identifier,
|
|
101
|
+
events: entry.events ?? {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { id, type: entry.type, requiredConsent };
|
|
105
|
+
}
|
|
106
|
+
function ManagedScript({ entry }) {
|
|
107
|
+
const registration = registrationFor(entry);
|
|
108
|
+
const { id, requiredConsent } = registration;
|
|
95
109
|
const isBlockedByNativeConsent = useIsBlockedByNativeConsent(requiredConsent);
|
|
96
110
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
97
111
|
/* @__PURE__ */ jsx(
|
|
98
112
|
ScriptRegistration,
|
|
99
113
|
{
|
|
100
|
-
script:
|
|
114
|
+
script: registration
|
|
101
115
|
},
|
|
102
|
-
|
|
116
|
+
JSON.stringify(registration)
|
|
103
117
|
),
|
|
104
118
|
!isBlockedByNativeConsent && /* @__PURE__ */ jsx(
|
|
105
119
|
InjectedScript,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConsentCategory, ReploScriptType } from "../_vendor/schemas/generated/consent";
|
|
1
|
+
import type { ConsentCategory, ReploScriptType, XPixelEvents } from "../_vendor/schemas/generated/consent";
|
|
2
2
|
/**
|
|
3
3
|
* Tracks which managed scripts are currently mounted on the page. Each
|
|
4
4
|
* `ReploScripts` entry registers itself here on mount, so the analytics provider
|
|
@@ -6,11 +6,16 @@ import type { ConsentCategory, ReploScriptType } from "../_vendor/schemas/genera
|
|
|
6
6
|
* without importing a static list. Like the consent store, this is a singleton
|
|
7
7
|
* rather than React context so it works regardless of tree position.
|
|
8
8
|
*/
|
|
9
|
-
export
|
|
9
|
+
export type RegisteredScript = {
|
|
10
10
|
id: string;
|
|
11
|
-
type: ReploScriptType;
|
|
12
11
|
requiredConsent: ConsentCategory[];
|
|
13
|
-
}
|
|
12
|
+
} & ({
|
|
13
|
+
type: Exclude<ReploScriptType, "X">;
|
|
14
|
+
} | {
|
|
15
|
+
type: "X";
|
|
16
|
+
pixelId: string;
|
|
17
|
+
events: XPixelEvents;
|
|
18
|
+
});
|
|
14
19
|
/**
|
|
15
20
|
* Registration id of a `snippet` entry. Snippet providers have no distinct
|
|
16
21
|
* `type`, so both `ReploScripts` (registering) and the analytics sink factory
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import type { ConsentCategory, ReploScriptType, ScriptTagDescriptor } from "../_vendor/schemas/generated/consent";
|
|
1
|
+
import type { ConsentCategory, ReploScriptEntry, ReploScriptType, ScriptTagDescriptor } from "../_vendor/schemas/generated/consent";
|
|
2
2
|
type ProviderType = Exclude<ReploScriptType, "custom" | "snippet" | "consentPlatform" | "Cookiebot">;
|
|
3
3
|
/**
|
|
4
4
|
* Snippet providers don't synthesize tags (the user pastes the provider-issued
|
|
5
5
|
* snippet as the entry's `body`), but they still get a first-class default
|
|
6
6
|
* consent categorization like identifier providers.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
/** IDs for another pixel would credit a different advertiser. */
|
|
9
|
+
export declare function isXEventIdForPixel({ eventId, pixelId, }: {
|
|
10
|
+
eventId: string;
|
|
11
|
+
pixelId: string;
|
|
12
|
+
}): boolean;
|
|
13
|
+
/** The runtime injects these tags in order after consent allows tracking. */
|
|
14
|
+
export declare function buildScriptTags(entry: Extract<ReploScriptEntry, {
|
|
14
15
|
type: ProviderType;
|
|
15
|
-
|
|
16
|
-
}): ScriptTagDescriptor[];
|
|
16
|
+
}>): ScriptTagDescriptor[];
|
|
17
17
|
/**
|
|
18
18
|
* The default consent categories that gate an identifier-provider script when an
|
|
19
19
|
* entry does not specify `requiredConsent`. `snippet` and `custom` entries carry
|
|
@@ -69,6 +69,15 @@ gtag('config', '${id}');`
|
|
|
69
69
|
}
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
|
+
X: {
|
|
73
|
+
defaultConsent: ["marketing"],
|
|
74
|
+
buildTags: (id) => [
|
|
75
|
+
{
|
|
76
|
+
kind: "inline",
|
|
77
|
+
body: `!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');twq('config','${id}');`
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
},
|
|
72
81
|
Hotjar: {
|
|
73
82
|
defaultConsent: ["analytics"],
|
|
74
83
|
buildTags: (id) => [
|
|
@@ -140,16 +149,33 @@ gtag('config', '${id}');`
|
|
|
140
149
|
]
|
|
141
150
|
}
|
|
142
151
|
};
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
function isXEventIdForPixel({
|
|
153
|
+
eventId,
|
|
154
|
+
pixelId
|
|
146
155
|
}) {
|
|
147
|
-
|
|
156
|
+
const match = /^tw-([a-z0-9]+)-[a-z0-9]+$/i.exec(eventId);
|
|
157
|
+
return match?.[1]?.toLowerCase() === pixelId.toLowerCase();
|
|
158
|
+
}
|
|
159
|
+
function buildScriptTags(entry) {
|
|
160
|
+
const tags = PROVIDER_SNIPPETS[entry.type].buildTags(entry.identifier);
|
|
161
|
+
if (entry.type === "X") {
|
|
162
|
+
const pageViewId = entry.events?.pageView?.trim();
|
|
163
|
+
if (pageViewId && isXEventIdForPixel({ eventId: pageViewId, pixelId: entry.identifier })) {
|
|
164
|
+
return tags.map((tag) => {
|
|
165
|
+
return tag.kind === "inline" ? {
|
|
166
|
+
...tag,
|
|
167
|
+
body: `${tag.body}twq('event',${JSON.stringify(pageViewId)},{});`
|
|
168
|
+
} : tag;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return tags;
|
|
148
173
|
}
|
|
149
174
|
function defaultConsentFor(type) {
|
|
150
175
|
return PROVIDER_SNIPPETS[type].defaultConsent;
|
|
151
176
|
}
|
|
152
177
|
export {
|
|
153
178
|
buildScriptTags,
|
|
154
|
-
defaultConsentFor
|
|
179
|
+
defaultConsentFor,
|
|
180
|
+
isXEventIdForPixel
|
|
155
181
|
};
|
package/consent/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ReploScriptType = "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "Cookiebot" | "snippet" | "custom" | "consentPlatform";
|
|
1
|
+
export type ReploScriptType = "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "X" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "Cookiebot" | "snippet" | "custom" | "consentPlatform";
|
|
2
2
|
export type ConsentCategory = "necessary" | "analytics" | "marketing" | "preferences" | "sale_of_data";
|
|
3
3
|
export type ConsentMode = "off" | "simple" | "per-category";
|
|
4
4
|
declare module "react" {
|
|
@@ -16,10 +16,22 @@ export type ScriptTagDescriptor = {
|
|
|
16
16
|
kind: "inline";
|
|
17
17
|
body: string;
|
|
18
18
|
};
|
|
19
|
+
export type XPixelEvents = {
|
|
20
|
+
pageView?: string;
|
|
21
|
+
productViewed?: string;
|
|
22
|
+
addToCart?: string;
|
|
23
|
+
startCheckout?: string;
|
|
24
|
+
purchase?: string;
|
|
25
|
+
};
|
|
19
26
|
export type ReploScriptEntry = {
|
|
20
27
|
type: "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "Cookiebot";
|
|
21
28
|
identifier: string;
|
|
22
29
|
requiredConsent?: ConsentCategory[];
|
|
30
|
+
} | {
|
|
31
|
+
type: "X";
|
|
32
|
+
identifier: string;
|
|
33
|
+
events?: XPixelEvents;
|
|
34
|
+
requiredConsent?: ConsentCategory[];
|
|
23
35
|
} | {
|
|
24
36
|
type: "snippet";
|
|
25
37
|
id: string;
|
package/gateways/canopy-api.d.ts
CHANGED
|
@@ -72,6 +72,10 @@ declare class CanopyApiGateway {
|
|
|
72
72
|
sellingPlanId?: string | null;
|
|
73
73
|
}[];
|
|
74
74
|
discountCodes?: string[];
|
|
75
|
+
attributes?: {
|
|
76
|
+
key: string;
|
|
77
|
+
value: string;
|
|
78
|
+
}[];
|
|
75
79
|
country?: StorefrontCountryCode;
|
|
76
80
|
}): Promise<Cart>;
|
|
77
81
|
cartAddLines({ cartId, lines, }: {
|
package/gateways/canopy-api.js
CHANGED
|
@@ -195,6 +195,7 @@ If you're running locally, make sure the canopy-api cloudflare worker is running
|
|
|
195
195
|
return this._cartPost("/create", {
|
|
196
196
|
lines: options?.lines,
|
|
197
197
|
discountCodes: options?.discountCodes,
|
|
198
|
+
attributes: options?.attributes,
|
|
198
199
|
country: options?.country
|
|
199
200
|
});
|
|
200
201
|
}
|
package/lib/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-10-
|
|
5
|
+
"commit": "702e4d053e7d73e648c62dce85f5d14634d87bd8",
|
|
6
|
+
"builtAt": "2026-10-09T23:28:16.932Z"
|
|
7
7
|
};
|
package/loaders/loader-keys.d.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* `<integration>.<objectType>_<qualifier>`.
|
|
11
11
|
*/
|
|
12
12
|
export declare const DATA_LOADER_KEYS: {
|
|
13
|
+
readonly SHOPIFY_INTEGRATION_DETAILS: "shopify_storefront.integration_details";
|
|
13
14
|
readonly SHOPIFY_PRODUCT: "shopify_storefront.product";
|
|
14
15
|
readonly SHOPIFY_COLLECTION: "shopify_storefront.collection";
|
|
15
16
|
readonly SHOPIFY_COLLECTION_PRODUCTS: "shopify_storefront.collection_products";
|
package/loaders/loader-keys.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const DATA_LOADER_KEYS = {
|
|
2
|
+
SHOPIFY_INTEGRATION_DETAILS: "shopify_storefront.integration_details",
|
|
2
3
|
SHOPIFY_PRODUCT: "shopify_storefront.product",
|
|
3
4
|
SHOPIFY_COLLECTION: "shopify_storefront.collection",
|
|
4
5
|
SHOPIFY_COLLECTION_PRODUCTS: "shopify_storefront.collection_products",
|
|
@@ -1098,3 +1098,8 @@ export type ShopifyKachingBundlesLoaderOutput = z.infer<typeof shopifyKachingBun
|
|
|
1098
1098
|
export type StorefrontMetaobject = z.infer<typeof storefrontMetaobjectSchema>;
|
|
1099
1099
|
export type ShopifyMetaobjectLoaderOutput = z.infer<typeof shopifyMetaobjectLoaderOutputSchema>;
|
|
1100
1100
|
export type ShopifyMetaobjectsLoaderOutput = z.infer<typeof shopifyMetaobjectsLoaderOutputSchema>;
|
|
1101
|
+
export declare const shopifyIntegrationDetailsLoaderOutputSchema: z.ZodObject<{
|
|
1102
|
+
shopifyDomain: z.ZodString;
|
|
1103
|
+
storefrontAccessToken: z.ZodString;
|
|
1104
|
+
}, z.core.$strip>;
|
|
1105
|
+
export type ShopifyIntegrationDetails = z.infer<typeof shopifyIntegrationDetailsLoaderOutputSchema>;
|
|
@@ -362,6 +362,14 @@ const shopifyMetaobjectsLoaderOutputSchema = z.object({
|
|
|
362
362
|
const shopifyKachingBundlesLoaderOutputSchema = z.object({
|
|
363
363
|
bundles: z.record(z.string(), z.unknown()).nullable()
|
|
364
364
|
}).describe("ShopifyKachingBundles");
|
|
365
|
+
const shopifyIntegrationDetailsLoaderOutputSchema = z.object({
|
|
366
|
+
shopifyDomain: z.string().min(1).describe(
|
|
367
|
+
"The connected store's myshopify.com domain, without a protocol."
|
|
368
|
+
),
|
|
369
|
+
storefrontAccessToken: z.string().min(1).describe(
|
|
370
|
+
"Public Shopify Storefront API token intended for browser use. Never an Admin API or private Storefront token."
|
|
371
|
+
)
|
|
372
|
+
});
|
|
365
373
|
export {
|
|
366
374
|
contentfulAssetObjectSchema,
|
|
367
375
|
contentfulEntriesLoaderOutputSchema,
|
|
@@ -400,6 +408,7 @@ export {
|
|
|
400
408
|
sellingPlanOptionValueSchema,
|
|
401
409
|
sellingPlanPriceAdjustmentSchema,
|
|
402
410
|
sellingPlanSchema,
|
|
411
|
+
shopifyIntegrationDetailsLoaderOutputSchema,
|
|
403
412
|
shopifyKachingBundlesLoaderOutputSchema,
|
|
404
413
|
shopifyMetaobjectLoaderOutputSchema,
|
|
405
414
|
shopifyMetaobjectsLoaderOutputSchema,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { ShopifyIntegrationDetails } from "./loader-schemas";
|
|
3
|
+
type ShopifyIntegrationDetailsLoaderProps = {
|
|
4
|
+
loaderKey: string;
|
|
5
|
+
children: (data: ShopifyIntegrationDetails) => ReactNode;
|
|
6
|
+
fallback?: ReactNode;
|
|
7
|
+
};
|
|
8
|
+
export declare function ShopifyIntegrationDetailsLoader({ loaderKey, children, fallback, }: ShopifyIntegrationDetailsLoaderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Suspense } from "react";
|
|
4
|
+
import { useSuspenseQuery } from "@tanstack/react-query";
|
|
5
|
+
import { invokeLoader } from "./invoke-loader";
|
|
6
|
+
import { LoaderErrorBoundary } from "./loader-error-boundary";
|
|
7
|
+
import { shopifyIntegrationDetailsLoaderOutputSchema } from "./loader-schemas";
|
|
8
|
+
function ShopifyIntegrationDetailsLoaderInner({
|
|
9
|
+
loaderKey,
|
|
10
|
+
children
|
|
11
|
+
}) {
|
|
12
|
+
const { data } = useSuspenseQuery({
|
|
13
|
+
queryKey: [loaderKey, {}],
|
|
14
|
+
queryFn: async () => {
|
|
15
|
+
const loadedData = await invokeLoader({
|
|
16
|
+
loaderKey,
|
|
17
|
+
args: {}
|
|
18
|
+
});
|
|
19
|
+
return shopifyIntegrationDetailsLoaderOutputSchema.parse(loadedData);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
23
|
+
}
|
|
24
|
+
function ShopifyIntegrationDetailsLoader({
|
|
25
|
+
loaderKey,
|
|
26
|
+
children,
|
|
27
|
+
fallback
|
|
28
|
+
}) {
|
|
29
|
+
return /* @__PURE__ */ jsx(LoaderErrorBoundary, { fallback: fallback ?? null, resetKey: loaderKey, children: /* @__PURE__ */ jsx(Suspense, { fallback: fallback ?? null, children: /* @__PURE__ */ jsx(ShopifyIntegrationDetailsLoaderInner, { loaderKey, children }) }) });
|
|
30
|
+
}
|
|
31
|
+
export {
|
|
32
|
+
ShopifyIntegrationDetailsLoader
|
|
33
|
+
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replohq/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sdk",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.16.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-10-
|
|
8
|
+
"commit": "702e4d053e7d73e648c62dce85f5d14634d87bd8",
|
|
9
|
+
"builtAt": "2026-10-09T23:28:16.932Z"
|
|
10
10
|
},
|
|
11
11
|
"description": "Replo SDK — cart, analytics, and data loaders for agent-built Next.js sites.",
|
|
12
12
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
"types": "./analytics/analytics-provider.d.ts",
|
|
86
86
|
"default": "./analytics/analytics-provider.js"
|
|
87
87
|
},
|
|
88
|
+
"./analytics/sinks/intelligems-sink": {
|
|
89
|
+
"types": "./analytics/sinks/intelligems-sink.d.ts",
|
|
90
|
+
"default": "./analytics/sinks/intelligems-sink.js"
|
|
91
|
+
},
|
|
88
92
|
"./analytics/hooks/use-product-viewed-analytics": {
|
|
89
93
|
"types": "./analytics/hooks/use-product-viewed-analytics.d.ts",
|
|
90
94
|
"default": "./analytics/hooks/use-product-viewed-analytics.js"
|
|
@@ -165,6 +169,10 @@
|
|
|
165
169
|
"types": "./loaders/reviews-io-reviews-loader.d.ts",
|
|
166
170
|
"default": "./loaders/reviews-io-reviews-loader.js"
|
|
167
171
|
},
|
|
172
|
+
"./loaders/shopify-integration-details-loader": {
|
|
173
|
+
"types": "./loaders/shopify-integration-details-loader.d.ts",
|
|
174
|
+
"default": "./loaders/shopify-integration-details-loader.js"
|
|
175
|
+
},
|
|
168
176
|
"./loaders/smile-earning-rules-loader": {
|
|
169
177
|
"types": "./loaders/smile-earning-rules-loader.d.ts",
|
|
170
178
|
"default": "./loaders/smile-earning-rules-loader.js"
|