saasco-sdk 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dist/index.cjs +454 -305
- package/dist/index.d.cts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +454 -305
- package/dist/support.cjs +199 -192
- package/dist/support.js +127 -120
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,187 @@
|
|
|
2
2
|
import { isValid, parse } from "psl";
|
|
3
3
|
|
|
4
4
|
// package.json
|
|
5
|
-
var version = "0.2.
|
|
5
|
+
var version = "0.2.6";
|
|
6
|
+
|
|
7
|
+
// src/lib/deliverOnce.ts
|
|
8
|
+
var sent = /* @__PURE__ */ new Set();
|
|
9
|
+
var pending = /* @__PURE__ */ new Map();
|
|
10
|
+
function deliverOnce(key, send) {
|
|
11
|
+
if (!key || typeof window === "undefined") {
|
|
12
|
+
return Promise.resolve().then(send);
|
|
13
|
+
}
|
|
14
|
+
if (sent.has(key)) {
|
|
15
|
+
return Promise.resolve();
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
if (window.sessionStorage.getItem(key)) {
|
|
19
|
+
return Promise.resolve();
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
}
|
|
23
|
+
const existing = pending.get(key);
|
|
24
|
+
if (existing) {
|
|
25
|
+
return existing;
|
|
26
|
+
}
|
|
27
|
+
const task = Promise.resolve().then(send).then(() => {
|
|
28
|
+
sent.add(key);
|
|
29
|
+
try {
|
|
30
|
+
window.sessionStorage.setItem(key, "true");
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}).finally(() => pending.delete(key));
|
|
34
|
+
pending.set(key, task);
|
|
35
|
+
return task;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ../shared/src/lib/analyticsIngestError.ts
|
|
39
|
+
import { z } from "zod";
|
|
40
|
+
var IDENTIFY_INGEST_ERROR_SOURCE = "saasco-identify";
|
|
41
|
+
var ANALYTICS_INGEST_ERROR_SOURCE = "saasco-analytics";
|
|
42
|
+
var AnalyticsIngestErrorCodeSchema = z.enum([
|
|
43
|
+
"CREDENTIAL_INVALID",
|
|
44
|
+
"RESOURCE_FORBIDDEN",
|
|
45
|
+
"RESOURCE_UNSPECIFIED",
|
|
46
|
+
"ENVIRONMENT_UNREACHABLE",
|
|
47
|
+
"INGEST_FAILED"
|
|
48
|
+
]);
|
|
49
|
+
var AnalyticsIngestSurfaceSchema = z.enum(["identify", "events"]);
|
|
50
|
+
var AnalyticsIngestErrorSchema = z.object({
|
|
51
|
+
affected: z.string(),
|
|
52
|
+
code: AnalyticsIngestErrorCodeSchema,
|
|
53
|
+
message: z.string(),
|
|
54
|
+
projectId: z.string().nullable(),
|
|
55
|
+
recovery: z.string(),
|
|
56
|
+
resource: z.string().nullable(),
|
|
57
|
+
surface: AnalyticsIngestSurfaceSchema
|
|
58
|
+
});
|
|
59
|
+
var IDENTIFY_AFFECTED = "Saasco identification only: CRM contact properties and identity stitching. Page views, charts, and the host application UI are not this failure.";
|
|
60
|
+
var ANALYTICS_EVENT_AFFECTED = "Saasco event tracking only. The host application UI is not this failure.";
|
|
61
|
+
function analyticsIngestError({
|
|
62
|
+
code,
|
|
63
|
+
message,
|
|
64
|
+
projectId,
|
|
65
|
+
resource,
|
|
66
|
+
surface
|
|
67
|
+
}) {
|
|
68
|
+
const copy = ingestCopy({ code, resource, surface });
|
|
69
|
+
return {
|
|
70
|
+
affected: surface === "identify" ? IDENTIFY_AFFECTED : ANALYTICS_EVENT_AFFECTED,
|
|
71
|
+
code,
|
|
72
|
+
message: message ?? copy.message,
|
|
73
|
+
projectId,
|
|
74
|
+
recovery: copy.recovery,
|
|
75
|
+
resource,
|
|
76
|
+
surface
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function ingestCopy({
|
|
80
|
+
code,
|
|
81
|
+
resource,
|
|
82
|
+
surface
|
|
83
|
+
}) {
|
|
84
|
+
const named = resource ? `\`${resource}\`` : "the requested analytics resource";
|
|
85
|
+
const didNotComplete = surface === "identify" ? "Identification did not complete." : "Event tracking did not complete.";
|
|
86
|
+
switch (code) {
|
|
87
|
+
case "CREDENTIAL_INVALID": {
|
|
88
|
+
return {
|
|
89
|
+
message: "Saasco analytics credentials are invalid or missing. This is not an application login or permissions problem.",
|
|
90
|
+
recovery: "Check TINYBIRD_ANALYTICS_TOKEN on the Saasco ingest worker. Do not change host-app auth. Charts and UI styling are unrelated."
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case "RESOURCE_FORBIDDEN": {
|
|
94
|
+
return {
|
|
95
|
+
message: `Saasco analytics is not allowed to use ${named}. ${didNotComplete}`,
|
|
96
|
+
recovery: surface === "identify" ? `Grant the ingest worker access to ${named} (PIPES:READ or DATASOURCES:APPEND). Page tracking may still work.` : `Grant the ingest worker access to ${named} (PIPES:READ or DATASOURCES:APPEND).`
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case "RESOURCE_UNSPECIFIED": {
|
|
100
|
+
return {
|
|
101
|
+
message: `Saasco analytics was denied access without a named pipe or datasource. ${didNotComplete}`,
|
|
102
|
+
recovery: "This is a Saasco ingest configuration issue: the worker could not name the Tinybird resource it tried to use. Host-app login is unrelated."
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
case "ENVIRONMENT_UNREACHABLE": {
|
|
106
|
+
return {
|
|
107
|
+
message: surface === "identify" ? "The Saasco ingest host could not be reached, so identification was not sent." : "The Saasco ingest host could not be reached, so this event was not sent.",
|
|
108
|
+
recovery: "If `proxy` points at localhost, start the analytics worker (`pnpm worker:dev`) or use https://t.saasco.com/. This does not mean the host app is broken."
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
default: {
|
|
112
|
+
return {
|
|
113
|
+
message: surface === "identify" ? "Saasco identification could not be completed." : "Saasco event tracking could not be completed.",
|
|
114
|
+
recovery: "Retry. If it keeps failing, this is a Saasco analytics ingest issue, not host-app login or chart configuration."
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function ingestErrorSource(surface) {
|
|
120
|
+
return surface === "identify" ? IDENTIFY_INGEST_ERROR_SOURCE : ANALYTICS_INGEST_ERROR_SOURCE;
|
|
121
|
+
}
|
|
122
|
+
function redactSecrets(text) {
|
|
123
|
+
return text.replaceAll(/Bearer\s+\S+/giu, "Bearer [redacted]").replaceAll(/\bp\.[A-Za-z0-9._-]+/gu, "[redacted]").replaceAll(/token=[^&\s]+/giu, "token=[redacted]");
|
|
124
|
+
}
|
|
125
|
+
function isUnsafeIngestMessage(text) {
|
|
126
|
+
return /Bearer \[redacted\]|token=\[redacted\]/iu.test(text) || /token you have provided|doesn't have access to this resource|not enough permissions|invalid token/iu.test(
|
|
127
|
+
text
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/lib/formatAnalyticsRequestFailure.ts
|
|
132
|
+
function formatAnalyticsRequestFailure(input) {
|
|
133
|
+
const surface = input.path === "identify" ? "identify" : "events";
|
|
134
|
+
const projectId = input.projectId ?? null;
|
|
135
|
+
if (input.reason === "http") {
|
|
136
|
+
const parsed = AnalyticsIngestErrorSchema.safeParse(input.body);
|
|
137
|
+
if (parsed.success) {
|
|
138
|
+
return parsed.data;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (input.reason === "unreachable") {
|
|
142
|
+
return analyticsIngestError({
|
|
143
|
+
code: "ENVIRONMENT_UNREACHABLE",
|
|
144
|
+
projectId,
|
|
145
|
+
resource: null,
|
|
146
|
+
surface
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const legacy = input.body && typeof input.body === "object" && !Array.isArray(input.body) && "message" in input.body && typeof input.body.message === "string" ? input.body.message : "";
|
|
150
|
+
const safeMessage = redactSecrets(legacy).trim();
|
|
151
|
+
return analyticsIngestError({
|
|
152
|
+
code: "INGEST_FAILED",
|
|
153
|
+
message: safeMessage && !isUnsafeIngestMessage(safeMessage) ? safeMessage : void 0,
|
|
154
|
+
projectId,
|
|
155
|
+
resource: null,
|
|
156
|
+
surface
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function reportIngestFailure({
|
|
160
|
+
failure,
|
|
161
|
+
logger,
|
|
162
|
+
path,
|
|
163
|
+
seen
|
|
164
|
+
}) {
|
|
165
|
+
const key = `${path}:${failure.code}:${failure.resource ?? ""}`;
|
|
166
|
+
if (!seen.has(key)) {
|
|
167
|
+
seen.add(key);
|
|
168
|
+
logger.warn(...analyticsFailureLogLines(failure, path));
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
success: false,
|
|
172
|
+
message: failure.message
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function analyticsFailureLogLines(failure, path) {
|
|
176
|
+
const lines = [
|
|
177
|
+
`${ingestErrorSource(failure.surface)} ${path} failed (${failure.code})`,
|
|
178
|
+
failure.message,
|
|
179
|
+
failure.resource ? `Resource: ${failure.resource}` : null,
|
|
180
|
+
failure.projectId ? `Project: ${failure.projectId}` : null,
|
|
181
|
+
`Affected: ${failure.affected}`,
|
|
182
|
+
`Recovery: ${failure.recovery}`
|
|
183
|
+
];
|
|
184
|
+
return lines.filter((line) => line !== null);
|
|
185
|
+
}
|
|
6
186
|
|
|
7
187
|
// src/lib/timezones.ts
|
|
8
188
|
var timezones = {
|
|
@@ -482,12 +662,10 @@ var AnalyticsLogger = class {
|
|
|
482
662
|
);
|
|
483
663
|
}
|
|
484
664
|
/**
|
|
485
|
-
*
|
|
665
|
+
* Warnings always print. Ingest failures use this so they stay visible when
|
|
666
|
+
* the default log level is ERROR, without `console.error` (Next.js overlays it).
|
|
486
667
|
*/
|
|
487
668
|
warn(...args) {
|
|
488
|
-
if (this.config.level < 1 /* WARN */) {
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
669
|
const icon = "\u25CD";
|
|
492
670
|
const message = `${icon} ${this.config.label}`;
|
|
493
671
|
console.warn(
|
|
@@ -633,6 +811,50 @@ function resolveAdParams(params) {
|
|
|
633
811
|
|
|
634
812
|
// src/lib/integrations/facebook-pixel.ts
|
|
635
813
|
import { sha256 } from "js-sha256";
|
|
814
|
+
|
|
815
|
+
// src/lib/integrations/loadPixelScript.ts
|
|
816
|
+
var loads = /* @__PURE__ */ new Map();
|
|
817
|
+
function loadPixelScript(src) {
|
|
818
|
+
const existingLoad = loads.get(src);
|
|
819
|
+
if (existingLoad) {
|
|
820
|
+
return existingLoad;
|
|
821
|
+
}
|
|
822
|
+
const existingScript = Array.from(document.scripts).find(
|
|
823
|
+
(script2) => script2.src === src
|
|
824
|
+
);
|
|
825
|
+
const script = existingScript ?? document.createElement("script");
|
|
826
|
+
const task = new Promise((resolve, reject) => {
|
|
827
|
+
const cleanup = () => {
|
|
828
|
+
clearTimeout(timeout);
|
|
829
|
+
script.removeEventListener("load", loaded);
|
|
830
|
+
script.removeEventListener("error", failed);
|
|
831
|
+
};
|
|
832
|
+
const loaded = () => {
|
|
833
|
+
cleanup();
|
|
834
|
+
resolve();
|
|
835
|
+
};
|
|
836
|
+
const failed = () => {
|
|
837
|
+
cleanup();
|
|
838
|
+
script.remove();
|
|
839
|
+
reject(new Error(`Pixel script unavailable: ${src}`));
|
|
840
|
+
};
|
|
841
|
+
const timeout = setTimeout(failed, 1e4);
|
|
842
|
+
script.addEventListener("load", loaded);
|
|
843
|
+
script.addEventListener("error", failed);
|
|
844
|
+
if (!existingScript) {
|
|
845
|
+
script.async = true;
|
|
846
|
+
script.src = src;
|
|
847
|
+
document.head.append(script);
|
|
848
|
+
}
|
|
849
|
+
}).catch((error) => {
|
|
850
|
+
loads.delete(src);
|
|
851
|
+
throw error;
|
|
852
|
+
});
|
|
853
|
+
loads.set(src, task);
|
|
854
|
+
return task;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/lib/integrations/facebook-pixel.ts
|
|
636
858
|
var isBrowser2 = typeof window !== "undefined";
|
|
637
859
|
var isServer2 = !isBrowser2;
|
|
638
860
|
var standardFacebookEvents = [
|
|
@@ -674,70 +896,32 @@ function createFacebookPixelIntegration(config, debug) {
|
|
|
674
896
|
return;
|
|
675
897
|
}
|
|
676
898
|
logger.info("Initializing Facebook Pixel", { pixelId: config.pixelId });
|
|
677
|
-
if (window.fbq
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
try {
|
|
684
|
-
(function init(f, b, e, v, n, t, s) {
|
|
685
|
-
if (f.fbq) {
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
688
|
-
n = f.fbq = function fbq2() {
|
|
689
|
-
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments);
|
|
690
|
-
};
|
|
691
|
-
if (!f._fbq) {
|
|
692
|
-
f._fbq = n;
|
|
693
|
-
}
|
|
694
|
-
n.push = n;
|
|
695
|
-
n.loaded = true;
|
|
696
|
-
n.version = "2.0";
|
|
697
|
-
n.queue = [];
|
|
698
|
-
t = b.createElement(e);
|
|
699
|
-
t.async = true;
|
|
700
|
-
t.src = v;
|
|
701
|
-
t.addEventListener("load", () => {
|
|
702
|
-
isPixelReady = true;
|
|
703
|
-
logger.info("Facebook Pixel script loaded successfully");
|
|
704
|
-
resolve();
|
|
705
|
-
});
|
|
706
|
-
t.addEventListener("error", () => {
|
|
707
|
-
logger.error("Failed to load Facebook Pixel script");
|
|
708
|
-
reject(new Error("Failed to load Facebook Pixel script"));
|
|
709
|
-
});
|
|
710
|
-
s = b.getElementsByTagName(e)[0];
|
|
711
|
-
s.parentNode.insertBefore(t, s);
|
|
712
|
-
})(
|
|
713
|
-
window,
|
|
714
|
-
document,
|
|
715
|
-
"script",
|
|
716
|
-
"https://connect.facebook.net/en_US/fbevents.js"
|
|
717
|
-
);
|
|
718
|
-
const { fbq } = window;
|
|
719
|
-
if (!fbq) {
|
|
720
|
-
reject(new Error("Facebook Pixel failed to initialize"));
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
fbq.disablePushState = true;
|
|
724
|
-
fbq.allowDuplicatePageViews = true;
|
|
725
|
-
if (config.automaticConfiguration === false) {
|
|
726
|
-
logger.debug("Disabling automatic configuration");
|
|
727
|
-
fbq("set", "autoConfig", false, config.pixelId);
|
|
899
|
+
if (!window.fbq) {
|
|
900
|
+
const fbq2 = (...args) => {
|
|
901
|
+
if (fbq2.callMethod) {
|
|
902
|
+
fbq2.callMethod(...args);
|
|
903
|
+
} else {
|
|
904
|
+
fbq2.queue.push(args);
|
|
728
905
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
906
|
+
};
|
|
907
|
+
fbq2.queue = [];
|
|
908
|
+
fbq2.push = fbq2;
|
|
909
|
+
fbq2.loaded = true;
|
|
910
|
+
fbq2.version = "2.0";
|
|
911
|
+
window.fbq = fbq2;
|
|
912
|
+
window._fbq = fbq2;
|
|
913
|
+
}
|
|
914
|
+
const { fbq } = window;
|
|
915
|
+
fbq.disablePushState = true;
|
|
916
|
+
fbq.allowDuplicatePageViews = true;
|
|
917
|
+
if (config.automaticConfiguration === false) {
|
|
918
|
+
fbq("set", "autoConfig", false, config.pixelId);
|
|
919
|
+
}
|
|
920
|
+
fbq("init", config.pixelId);
|
|
921
|
+
if (!fbq.callMethod) {
|
|
922
|
+
await loadPixelScript("https://connect.facebook.net/en_US/fbevents.js");
|
|
923
|
+
}
|
|
924
|
+
isPixelReady = true;
|
|
741
925
|
},
|
|
742
926
|
track: (eventName, properties, _context) => {
|
|
743
927
|
const { fbq } = window;
|
|
@@ -745,9 +929,11 @@ function createFacebookPixelIntegration(config, debug) {
|
|
|
745
929
|
logger.warn("Facebook Pixel not ready, skipping track event", {
|
|
746
930
|
eventName
|
|
747
931
|
});
|
|
748
|
-
|
|
932
|
+
throw new Error("Facebook Pixel not ready");
|
|
749
933
|
}
|
|
750
934
|
const fbEventName = getFacebookEventName(eventName, config.eventMapping);
|
|
935
|
+
const eventId = properties?.["event_id"];
|
|
936
|
+
const eventOptions = typeof eventId === "string" && eventId.trim().length > 0 ? { eventID: eventId } : void 0;
|
|
751
937
|
const eventsWithParams = ["Purchase", "StartTrial", "Subscribe"];
|
|
752
938
|
const isStandardEvent = standardFacebookEvents.includes(
|
|
753
939
|
fbEventName
|
|
@@ -775,10 +961,18 @@ function createFacebookPixelIntegration(config, debug) {
|
|
|
775
961
|
event: fbEventName,
|
|
776
962
|
params: fbParams
|
|
777
963
|
});
|
|
778
|
-
|
|
964
|
+
if (eventOptions) {
|
|
965
|
+
fbq(trackType, fbEventName, fbParams, eventOptions);
|
|
966
|
+
} else {
|
|
967
|
+
fbq(trackType, fbEventName, fbParams);
|
|
968
|
+
}
|
|
779
969
|
} else {
|
|
780
970
|
logger.info("Tracking Facebook Pixel event", { event: fbEventName });
|
|
781
|
-
|
|
971
|
+
if (eventOptions) {
|
|
972
|
+
fbq(trackType, fbEventName, {}, eventOptions);
|
|
973
|
+
} else {
|
|
974
|
+
fbq(trackType, fbEventName);
|
|
975
|
+
}
|
|
782
976
|
}
|
|
783
977
|
},
|
|
784
978
|
identify: (userId, properties, _context) => {
|
|
@@ -1132,12 +1326,12 @@ var IntegrationManager = class {
|
|
|
1132
1326
|
globalQueue = [];
|
|
1133
1327
|
config;
|
|
1134
1328
|
logger;
|
|
1135
|
-
initTime;
|
|
1136
1329
|
currentEnvironment;
|
|
1137
1330
|
flushTimer;
|
|
1138
1331
|
unloadHandler;
|
|
1139
1332
|
constructor(config = {}) {
|
|
1140
1333
|
this.config = {
|
|
1334
|
+
deduplicationNamespace: config.deduplicationNamespace ?? "default",
|
|
1141
1335
|
flushInterval: config.flushInterval ?? 5e3,
|
|
1142
1336
|
loggerLevel: config.loggerLevel ?? 0 /* ERROR */,
|
|
1143
1337
|
maxIntegrationWaitTime: config.maxIntegrationWaitTime ?? 1e4,
|
|
@@ -1147,7 +1341,6 @@ var IntegrationManager = class {
|
|
|
1147
1341
|
label: "Saasco Integrations Debug",
|
|
1148
1342
|
level: this.config.loggerLevel
|
|
1149
1343
|
});
|
|
1150
|
-
this.initTime = Date.now();
|
|
1151
1344
|
this.currentEnvironment = typeof window === "undefined" ? "server" : "client";
|
|
1152
1345
|
this.setupPeriodicFlushing();
|
|
1153
1346
|
this.setupUnloadHandler();
|
|
@@ -1224,6 +1417,8 @@ var IntegrationManager = class {
|
|
|
1224
1417
|
*/
|
|
1225
1418
|
send(partial) {
|
|
1226
1419
|
const envelope = {
|
|
1420
|
+
delivered: /* @__PURE__ */ new Set(),
|
|
1421
|
+
pending: /* @__PURE__ */ new Set(),
|
|
1227
1422
|
context: { ...this.context },
|
|
1228
1423
|
id: uuid(),
|
|
1229
1424
|
name: partial.name,
|
|
@@ -1231,65 +1426,55 @@ var IntegrationManager = class {
|
|
|
1231
1426
|
timestamp: Date.now(),
|
|
1232
1427
|
type: partial.type
|
|
1233
1428
|
};
|
|
1234
|
-
if (
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
this.logger.warn("Queue full \u2192 dropped oldest");
|
|
1238
|
-
}
|
|
1239
|
-
this.globalQueue.push(envelope);
|
|
1240
|
-
this.logger.debug(`Queued (${this.globalQueue.length})`, envelope);
|
|
1241
|
-
return;
|
|
1429
|
+
if (this.globalQueue.length >= this.config.maxQueueSize) {
|
|
1430
|
+
this.globalQueue.shift();
|
|
1431
|
+
this.logger.warn("Queue full \u2192 dropped oldest");
|
|
1242
1432
|
}
|
|
1243
|
-
this.
|
|
1433
|
+
this.globalQueue.push(envelope);
|
|
1434
|
+
this.flush();
|
|
1244
1435
|
}
|
|
1245
|
-
async deliver(evt) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
await integration.identify(
|
|
1264
|
-
evt.context.distinctId,
|
|
1265
|
-
evt.properties,
|
|
1266
|
-
evt.context
|
|
1267
|
-
);
|
|
1268
|
-
}
|
|
1269
|
-
} catch (error) {
|
|
1270
|
-
this.logger.error(
|
|
1271
|
-
`deliver error in ${integration.name} (${evt.type})`,
|
|
1272
|
-
error
|
|
1436
|
+
async deliver(evt, state) {
|
|
1437
|
+
const { integration } = state;
|
|
1438
|
+
evt.pending.add(integration.name);
|
|
1439
|
+
try {
|
|
1440
|
+
if (evt.type === "track" && integration.track && evt.name) {
|
|
1441
|
+
const eventId = evt.properties?.["event_id"];
|
|
1442
|
+
const key = typeof eventId === "string" && eventId.trim() ? `saasco:${this.config.deduplicationNamespace}:${integration.name}:${evt.name}:${eventId}` : void 0;
|
|
1443
|
+
const { track } = integration;
|
|
1444
|
+
const { name } = evt;
|
|
1445
|
+
await deliverOnce(
|
|
1446
|
+
key,
|
|
1447
|
+
() => track.call(integration, name, evt.properties, evt.context)
|
|
1448
|
+
);
|
|
1449
|
+
} else if (evt.type === "identify" && integration.identify) {
|
|
1450
|
+
await integration.identify(
|
|
1451
|
+
evt.context.distinctId,
|
|
1452
|
+
evt.properties,
|
|
1453
|
+
evt.context
|
|
1273
1454
|
);
|
|
1274
1455
|
}
|
|
1456
|
+
evt.delivered.add(integration.name);
|
|
1457
|
+
} catch (error) {
|
|
1458
|
+
this.logger.error(
|
|
1459
|
+
`deliver error in ${integration.name} (${evt.type})`,
|
|
1460
|
+
error
|
|
1461
|
+
);
|
|
1462
|
+
} finally {
|
|
1463
|
+
evt.pending.delete(integration.name);
|
|
1275
1464
|
}
|
|
1276
1465
|
}
|
|
1277
|
-
/**
|
|
1278
|
-
*
|
|
1279
|
-
*/
|
|
1466
|
+
/** Each ready destination drains its own copy; a slow/blocked pixel cannot
|
|
1467
|
+
* discard another pixel's queued events or hold up the ready destinations. */
|
|
1280
1468
|
flush() {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
for (const evt of toSend) {
|
|
1291
|
-
this.deliver(evt);
|
|
1292
|
-
}
|
|
1469
|
+
this.globalQueue = this.globalQueue.filter((evt) => {
|
|
1470
|
+
for (const state of this.integrations.values()) {
|
|
1471
|
+
const { name } = state.integration;
|
|
1472
|
+
if (state.status === "ready" && !evt.delivered.has(name) && !evt.pending.has(name)) {
|
|
1473
|
+
void this.deliver(evt, state);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return evt.delivered.size < this.integrations.size && Date.now() - evt.timestamp < this.config.maxIntegrationWaitTime;
|
|
1477
|
+
});
|
|
1293
1478
|
}
|
|
1294
1479
|
readyCount() {
|
|
1295
1480
|
let n = 0;
|
|
@@ -1300,18 +1485,6 @@ var IntegrationManager = class {
|
|
|
1300
1485
|
}
|
|
1301
1486
|
return n;
|
|
1302
1487
|
}
|
|
1303
|
-
isReady() {
|
|
1304
|
-
const timeElapsed = Date.now() - this.initTime;
|
|
1305
|
-
if (this.readyCount() < this.integrations.size && timeElapsed < this.config.maxIntegrationWaitTime) {
|
|
1306
|
-
return false;
|
|
1307
|
-
}
|
|
1308
|
-
if (this.readyCount() < this.integrations.size) {
|
|
1309
|
-
this.logger.info(
|
|
1310
|
-
`proceeding with ${this.readyCount()}/${this.integrations.size} integrations ready after ${timeElapsed}ms wait`
|
|
1311
|
-
);
|
|
1312
|
-
}
|
|
1313
|
-
return true;
|
|
1314
|
-
}
|
|
1315
1488
|
/**
|
|
1316
1489
|
* Setup periodic flushing if enabled
|
|
1317
1490
|
*/
|
|
@@ -1395,50 +1568,20 @@ function createPinterestTagIntegration(config, debug) {
|
|
|
1395
1568
|
return;
|
|
1396
1569
|
}
|
|
1397
1570
|
logger.info("Initializing Pinterest Tag", { tagId: config.tagId });
|
|
1398
|
-
if (window.pintrk
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1571
|
+
if (!window.pintrk) {
|
|
1572
|
+
const pintrk = (...args) => pintrk.queue.push(args);
|
|
1573
|
+
pintrk.queue = [];
|
|
1574
|
+
pintrk.version = "3.0";
|
|
1575
|
+
window.pintrk = pintrk;
|
|
1402
1576
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
window.pintrk = function pintrk(...args) {
|
|
1408
|
-
window.pintrk.queue.push(args);
|
|
1409
|
-
};
|
|
1410
|
-
const n = window.pintrk;
|
|
1411
|
-
n.queue = [];
|
|
1412
|
-
n.version = "3.0";
|
|
1413
|
-
const t = document.createElement("script");
|
|
1414
|
-
t.async = true;
|
|
1415
|
-
t.src = e;
|
|
1416
|
-
t.addEventListener("load", () => {
|
|
1417
|
-
window.pintrk("load", config.tagId);
|
|
1418
|
-
window.pintrk("page");
|
|
1419
|
-
setTimeout(() => {
|
|
1420
|
-
logger.debug("Pinterest Tag initialized");
|
|
1421
|
-
isTagReady = true;
|
|
1422
|
-
resolve();
|
|
1423
|
-
}, 500);
|
|
1424
|
-
});
|
|
1425
|
-
t.addEventListener("error", () => {
|
|
1426
|
-
logger.error("Failed to load Pinterest Tag script");
|
|
1427
|
-
reject(new Error("Failed to load Pinterest Tag script"));
|
|
1428
|
-
});
|
|
1429
|
-
const r = document.getElementsByTagName("script")[0];
|
|
1430
|
-
r.parentNode.insertBefore(t, r);
|
|
1431
|
-
}
|
|
1432
|
-
})("https://s.pinimg.com/ct/core.js");
|
|
1433
|
-
} catch (error) {
|
|
1434
|
-
logger.error("Failed to initialize Pinterest Tag", error);
|
|
1435
|
-
reject(error);
|
|
1436
|
-
}
|
|
1437
|
-
});
|
|
1577
|
+
await loadPixelScript("https://s.pinimg.com/ct/core.js");
|
|
1578
|
+
window.pintrk("load", config.tagId);
|
|
1579
|
+
window.pintrk("page");
|
|
1580
|
+
isTagReady = true;
|
|
1438
1581
|
},
|
|
1439
1582
|
track: (eventName, properties) => {
|
|
1440
1583
|
if (!isTagReady || !window.pintrk) {
|
|
1441
|
-
|
|
1584
|
+
throw new Error("Pinterest Tag not ready");
|
|
1442
1585
|
}
|
|
1443
1586
|
const pinterestEventName = getPinterestEventName(
|
|
1444
1587
|
eventName,
|
|
@@ -1567,8 +1710,8 @@ function createTikTokPixelIntegration(config, debug) {
|
|
|
1567
1710
|
return;
|
|
1568
1711
|
}
|
|
1569
1712
|
logger.info("Initializing TikTok Pixel", { pixelId: config.pixelId });
|
|
1570
|
-
if (window.ttq) {
|
|
1571
|
-
|
|
1713
|
+
if (window.ttq?.ready) {
|
|
1714
|
+
await new Promise((resolve) => window.ttq.ready(resolve));
|
|
1572
1715
|
isPixelReady = true;
|
|
1573
1716
|
return;
|
|
1574
1717
|
}
|
|
@@ -1620,28 +1763,15 @@ function createTikTokPixelIntegration(config, debug) {
|
|
|
1620
1763
|
ttq._t[e] = Date.now();
|
|
1621
1764
|
ttq._o ||= {};
|
|
1622
1765
|
ttq._o[e] = n || {};
|
|
1623
|
-
|
|
1624
|
-
script.type = "text/javascript";
|
|
1625
|
-
script.async = true;
|
|
1626
|
-
script.src = `${r}?sdkid=${e}&lib=${t}`;
|
|
1627
|
-
script.addEventListener("load", () => {
|
|
1766
|
+
loadPixelScript(`${r}?sdkid=${e}&lib=${t}`).then(() => {
|
|
1628
1767
|
isPixelReady = true;
|
|
1629
1768
|
resolve();
|
|
1630
|
-
});
|
|
1631
|
-
script.addEventListener("error", () => {
|
|
1632
|
-
reject(new Error("Failed to load TikTok Pixel script"));
|
|
1633
|
-
});
|
|
1634
|
-
const firstScript = document.getElementsByTagName("script")[0];
|
|
1635
|
-
firstScript.parentNode.insertBefore(script, firstScript);
|
|
1769
|
+
}).catch(reject);
|
|
1636
1770
|
};
|
|
1637
1771
|
ttq.load(
|
|
1638
1772
|
config.pixelId,
|
|
1639
1773
|
config.testMode ? { test_mode: true } : void 0
|
|
1640
1774
|
);
|
|
1641
|
-
if (window.ttq && typeof window.ttq === "function") {
|
|
1642
|
-
isPixelReady = true;
|
|
1643
|
-
resolve();
|
|
1644
|
-
}
|
|
1645
1775
|
})(window, document, "ttq");
|
|
1646
1776
|
} catch (error) {
|
|
1647
1777
|
reject(error);
|
|
@@ -1650,8 +1780,8 @@ function createTikTokPixelIntegration(config, debug) {
|
|
|
1650
1780
|
},
|
|
1651
1781
|
track: (eventName, properties, _context) => {
|
|
1652
1782
|
const { ttq } = window;
|
|
1653
|
-
if (!isPixelReady || !ttq) {
|
|
1654
|
-
|
|
1783
|
+
if (!isPixelReady || !ttq?.track) {
|
|
1784
|
+
throw new Error("TikTok Pixel not ready");
|
|
1655
1785
|
}
|
|
1656
1786
|
const ttEventName = getTikTokEventName(eventName, config.eventMapping);
|
|
1657
1787
|
if (ttEventName === "PageView") {
|
|
@@ -1659,7 +1789,10 @@ function createTikTokPixelIntegration(config, debug) {
|
|
|
1659
1789
|
return;
|
|
1660
1790
|
}
|
|
1661
1791
|
const ttParams = mapTikTokEventParameters(ttEventName, properties);
|
|
1662
|
-
|
|
1792
|
+
const eventId = properties?.["event_id"];
|
|
1793
|
+
if (typeof eventId === "string" && eventId.trim().length > 0) {
|
|
1794
|
+
ttq.track?.(ttEventName, ttParams ?? {}, { event_id: eventId });
|
|
1795
|
+
} else if (ttParams && Object.keys(ttParams).length > 0) {
|
|
1663
1796
|
ttq.track?.(ttEventName, ttParams);
|
|
1664
1797
|
} else {
|
|
1665
1798
|
ttq.track?.(ttEventName);
|
|
@@ -2179,6 +2312,15 @@ function migrateFromLocalStorage() {
|
|
|
2179
2312
|
if (isServer5) {
|
|
2180
2313
|
return;
|
|
2181
2314
|
}
|
|
2315
|
+
let storage;
|
|
2316
|
+
try {
|
|
2317
|
+
storage = globalThis.localStorage ?? null;
|
|
2318
|
+
} catch {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
if (!storage) {
|
|
2322
|
+
return;
|
|
2323
|
+
}
|
|
2182
2324
|
const keysToMigrate = [
|
|
2183
2325
|
`${PREF}-session-id`,
|
|
2184
2326
|
`${PREF}-anonymous-id`,
|
|
@@ -2188,20 +2330,23 @@ function migrateFromLocalStorage() {
|
|
|
2188
2330
|
];
|
|
2189
2331
|
for (const key of keysToMigrate) {
|
|
2190
2332
|
try {
|
|
2191
|
-
const value =
|
|
2333
|
+
const value = storage.getItem(key);
|
|
2192
2334
|
if (value) {
|
|
2193
2335
|
const item = JSON.parse(value);
|
|
2194
2336
|
const now = Date.now();
|
|
2195
2337
|
if (item.expiry > now) {
|
|
2196
2338
|
const remainingTtl = item.expiry - now;
|
|
2197
2339
|
setCookie(key, JSON.stringify(item.value), remainingTtl);
|
|
2198
|
-
|
|
2340
|
+
storage.removeItem(key);
|
|
2199
2341
|
} else {
|
|
2200
|
-
|
|
2342
|
+
storage.removeItem(key);
|
|
2201
2343
|
}
|
|
2202
2344
|
}
|
|
2203
2345
|
} catch {
|
|
2204
|
-
|
|
2346
|
+
try {
|
|
2347
|
+
storage.removeItem(key);
|
|
2348
|
+
} catch {
|
|
2349
|
+
}
|
|
2205
2350
|
}
|
|
2206
2351
|
}
|
|
2207
2352
|
}
|
|
@@ -2432,7 +2577,8 @@ var Saasco = class {
|
|
|
2432
2577
|
};
|
|
2433
2578
|
this.integrationManager = new IntegrationManager({
|
|
2434
2579
|
loggerLevel,
|
|
2435
|
-
maxQueueSize: 1e3
|
|
2580
|
+
maxQueueSize: 1e3,
|
|
2581
|
+
deduplicationNamespace: this.config.projectId
|
|
2436
2582
|
});
|
|
2437
2583
|
this.integrationManager.setContext({});
|
|
2438
2584
|
void (async () => {
|
|
@@ -2455,6 +2601,7 @@ var Saasco = class {
|
|
|
2455
2601
|
// here so an `identify()` that fires before the widget bundle finishes
|
|
2456
2602
|
// loading is replayed once it does.
|
|
2457
2603
|
lastSupportIdentity = null;
|
|
2604
|
+
ingestFailureKeys = /* @__PURE__ */ new Set();
|
|
2458
2605
|
init() {
|
|
2459
2606
|
if (this.isInitialized) {
|
|
2460
2607
|
this.logger.info(
|
|
@@ -2506,48 +2653,52 @@ var Saasco = class {
|
|
|
2506
2653
|
);
|
|
2507
2654
|
return;
|
|
2508
2655
|
}
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2656
|
+
await Promise.all(
|
|
2657
|
+
this.config.integrations.map(async (integrationConfig) => {
|
|
2658
|
+
try {
|
|
2659
|
+
if (integrationConfig.type === "facebook-pixel") {
|
|
2660
|
+
this.logger.info(
|
|
2661
|
+
"Registering Facebook Pixel integration with ID:",
|
|
2662
|
+
integrationConfig.config.pixelId
|
|
2663
|
+
);
|
|
2664
|
+
const fbIntegration = createFacebookPixelIntegration(
|
|
2665
|
+
integrationConfig.config,
|
|
2666
|
+
integrationConfig.debug
|
|
2667
|
+
);
|
|
2668
|
+
await this.integrationManager.registerIntegration(fbIntegration);
|
|
2669
|
+
} else if (integrationConfig.type === "pinterest-tag") {
|
|
2670
|
+
this.logger.info(
|
|
2671
|
+
"Registering Pinterest Tag integration with ID:",
|
|
2672
|
+
integrationConfig.config.tagId
|
|
2673
|
+
);
|
|
2674
|
+
const pinterestIntegration = createPinterestTagIntegration(
|
|
2675
|
+
integrationConfig.config,
|
|
2676
|
+
integrationConfig.debug
|
|
2677
|
+
);
|
|
2678
|
+
await this.integrationManager.registerIntegration(
|
|
2679
|
+
pinterestIntegration
|
|
2680
|
+
);
|
|
2681
|
+
} else if (integrationConfig.type === "tiktok-pixel") {
|
|
2682
|
+
this.logger.info(
|
|
2683
|
+
"Registering TikTok Pixel integration with ID:",
|
|
2684
|
+
integrationConfig.config.pixelId
|
|
2685
|
+
);
|
|
2686
|
+
const tiktokIntegration = createTikTokPixelIntegration(
|
|
2687
|
+
integrationConfig.config,
|
|
2688
|
+
integrationConfig.debug
|
|
2689
|
+
);
|
|
2690
|
+
await this.integrationManager.registerIntegration(
|
|
2691
|
+
tiktokIntegration
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2694
|
+
} catch (error) {
|
|
2534
2695
|
this.logger.info(
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
);
|
|
2538
|
-
const tiktokIntegration = createTikTokPixelIntegration(
|
|
2539
|
-
integrationConfig.config,
|
|
2540
|
-
integrationConfig.debug
|
|
2696
|
+
`Failed to register ${integrationConfig.type} integration:`,
|
|
2697
|
+
error
|
|
2541
2698
|
);
|
|
2542
|
-
await this.integrationManager.registerIntegration(tiktokIntegration);
|
|
2543
2699
|
}
|
|
2544
|
-
}
|
|
2545
|
-
|
|
2546
|
-
`Failed to register ${integrationConfig.type} integration:`,
|
|
2547
|
-
error
|
|
2548
|
-
);
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2700
|
+
})
|
|
2701
|
+
);
|
|
2551
2702
|
}
|
|
2552
2703
|
track(actionOrPayload, propertiesOrNothing, contextOrNothing) {
|
|
2553
2704
|
if (!this.config.projectId) {
|
|
@@ -2604,7 +2755,19 @@ var Saasco = class {
|
|
|
2604
2755
|
});
|
|
2605
2756
|
}
|
|
2606
2757
|
this.integrationManager.track(action, properties);
|
|
2607
|
-
|
|
2758
|
+
const eventId = properties?.["event_id"];
|
|
2759
|
+
if (!isBrowser5 || typeof eventId !== "string" || !eventId.trim()) {
|
|
2760
|
+
return this.doRequest("events", requestData);
|
|
2761
|
+
}
|
|
2762
|
+
return deliverOnce(
|
|
2763
|
+
`saasco:${this.config.projectId}:ingest:${action}:${eventId}`,
|
|
2764
|
+
async () => {
|
|
2765
|
+
const result = await this.doRequest("events", requestData);
|
|
2766
|
+
if (!result.success) {
|
|
2767
|
+
throw new Error(result.message);
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
).then(() => ({ success: true, message: "Event dispatched" })).catch((error) => ({ success: false, message: String(error) }));
|
|
2608
2771
|
}
|
|
2609
2772
|
/**
|
|
2610
2773
|
* The page method lets you record page views on your website
|
|
@@ -2704,7 +2867,10 @@ var Saasco = class {
|
|
|
2704
2867
|
},
|
|
2705
2868
|
context: context || {}
|
|
2706
2869
|
};
|
|
2707
|
-
this.logger.info("identify",
|
|
2870
|
+
this.logger.info("identify", {
|
|
2871
|
+
distinctId,
|
|
2872
|
+
projectId: this.config.projectId
|
|
2873
|
+
});
|
|
2708
2874
|
if (!this.config.enabled) {
|
|
2709
2875
|
return Promise.resolve({
|
|
2710
2876
|
success: true,
|
|
@@ -2740,6 +2906,13 @@ var Saasco = class {
|
|
|
2740
2906
|
message: "Analytics is disabled"
|
|
2741
2907
|
};
|
|
2742
2908
|
}
|
|
2909
|
+
const projectId = requestData.projectId ?? null;
|
|
2910
|
+
const fail = (input) => reportIngestFailure({
|
|
2911
|
+
failure: formatAnalyticsRequestFailure({ path, projectId, ...input }),
|
|
2912
|
+
logger: this.logger,
|
|
2913
|
+
path,
|
|
2914
|
+
seen: this.ingestFailureKeys
|
|
2915
|
+
});
|
|
2743
2916
|
let response;
|
|
2744
2917
|
try {
|
|
2745
2918
|
response = await fetch(url, {
|
|
@@ -2749,46 +2922,22 @@ var Saasco = class {
|
|
|
2749
2922
|
},
|
|
2750
2923
|
body: JSON.stringify(requestData)
|
|
2751
2924
|
});
|
|
2752
|
-
} catch
|
|
2753
|
-
|
|
2754
|
-
this.logger.warn(
|
|
2755
|
-
`Network request failed`,
|
|
2756
|
-
`
|
|
2757
|
-
Error Message: "${message}"`,
|
|
2758
|
-
`
|
|
2759
|
-
Url: "${url}"`
|
|
2760
|
-
);
|
|
2761
|
-
return {
|
|
2762
|
-
success: false,
|
|
2763
|
-
message
|
|
2764
|
-
};
|
|
2925
|
+
} catch {
|
|
2926
|
+
return fail({ reason: "unreachable" });
|
|
2765
2927
|
}
|
|
2928
|
+
let responseBody;
|
|
2766
2929
|
try {
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
}
|
|
2773
|
-
return {
|
|
2774
|
-
success: true,
|
|
2775
|
-
message: responseBody.message ?? ""
|
|
2776
|
-
};
|
|
2777
|
-
} catch (error) {
|
|
2778
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2779
|
-
this.logger.error(
|
|
2780
|
-
`
|
|
2781
|
-
Error Message: "${message}"`,
|
|
2782
|
-
`
|
|
2783
|
-
Path: "/${path}"`,
|
|
2784
|
-
`
|
|
2785
|
-
Request Data: ${JSON.stringify(requestData, null, 2)}`
|
|
2786
|
-
);
|
|
2787
|
-
return {
|
|
2788
|
-
success: false,
|
|
2789
|
-
message
|
|
2790
|
-
};
|
|
2930
|
+
responseBody = await response.json();
|
|
2931
|
+
} catch {
|
|
2932
|
+
return fail({ reason: "http" });
|
|
2933
|
+
}
|
|
2934
|
+
if (!response.ok) {
|
|
2935
|
+
return fail({ reason: "http", body: responseBody });
|
|
2791
2936
|
}
|
|
2937
|
+
return {
|
|
2938
|
+
success: true,
|
|
2939
|
+
message: responseBody && typeof responseBody === "object" && "message" in responseBody && typeof responseBody.message === "string" ? responseBody.message : ""
|
|
2940
|
+
};
|
|
2792
2941
|
}
|
|
2793
2942
|
/**
|
|
2794
2943
|
* If autoPageTracking is enabled, this will automatically track page views
|
|
@@ -3016,31 +3165,31 @@ function getLoggerLevel(config) {
|
|
|
3016
3165
|
}
|
|
3017
3166
|
|
|
3018
3167
|
// ../shared/src/lib/types/browserContext.ts
|
|
3019
|
-
import { z } from "zod";
|
|
3020
|
-
var BrowserContextSchema =
|
|
3021
|
-
$href:
|
|
3022
|
-
$locale:
|
|
3023
|
-
$location:
|
|
3024
|
-
$pathname:
|
|
3025
|
-
$referrer:
|
|
3026
|
-
$referringDomain:
|
|
3027
|
-
$screenDPI:
|
|
3028
|
-
$screenHeight:
|
|
3029
|
-
$screenWidth:
|
|
3030
|
-
$title:
|
|
3031
|
-
$userAgent:
|
|
3032
|
-
$utmAdId:
|
|
3033
|
-
$utmAdSource:
|
|
3034
|
-
$utmCampaign:
|
|
3035
|
-
$utmCampaignId:
|
|
3036
|
-
$utmContent:
|
|
3037
|
-
$utmCreativeFormat:
|
|
3038
|
-
$utmId:
|
|
3039
|
-
$utmMarketingTactic:
|
|
3040
|
-
$utmMedium:
|
|
3041
|
-
$utmSource:
|
|
3042
|
-
$utmSourcePlatform:
|
|
3043
|
-
$utmTerm:
|
|
3168
|
+
import { z as z2 } from "zod";
|
|
3169
|
+
var BrowserContextSchema = z2.object({
|
|
3170
|
+
$href: z2.string(),
|
|
3171
|
+
$locale: z2.string(),
|
|
3172
|
+
$location: z2.string(),
|
|
3173
|
+
$pathname: z2.string(),
|
|
3174
|
+
$referrer: z2.string(),
|
|
3175
|
+
$referringDomain: z2.string(),
|
|
3176
|
+
$screenDPI: z2.number(),
|
|
3177
|
+
$screenHeight: z2.number(),
|
|
3178
|
+
$screenWidth: z2.number(),
|
|
3179
|
+
$title: z2.string(),
|
|
3180
|
+
$userAgent: z2.string(),
|
|
3181
|
+
$utmAdId: z2.string().nullable(),
|
|
3182
|
+
$utmAdSource: z2.string().nullable(),
|
|
3183
|
+
$utmCampaign: z2.string().nullable(),
|
|
3184
|
+
$utmCampaignId: z2.string().nullable(),
|
|
3185
|
+
$utmContent: z2.string().nullable(),
|
|
3186
|
+
$utmCreativeFormat: z2.string().nullable(),
|
|
3187
|
+
$utmId: z2.string().nullable(),
|
|
3188
|
+
$utmMarketingTactic: z2.string().nullable(),
|
|
3189
|
+
$utmMedium: z2.string().nullable(),
|
|
3190
|
+
$utmSource: z2.string().nullable(),
|
|
3191
|
+
$utmSourcePlatform: z2.string().nullable(),
|
|
3192
|
+
$utmTerm: z2.string().nullable()
|
|
3044
3193
|
});
|
|
3045
3194
|
export {
|
|
3046
3195
|
BrowserContextSchema,
|