@founderhq/journeys 0.3.66 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -6
- package/dist/_tsup-dts-rollup.d.cts +2294 -0
- package/dist/_tsup-dts-rollup.d.ts +2294 -0
- package/dist/index.cjs +509 -46
- package/dist/index.d.cts +140 -1446
- package/dist/index.d.ts +140 -1446
- package/dist/index.js +509 -46
- package/dist/styles.css +2 -2
- package/package.json +13 -10
package/dist/index.js
CHANGED
|
@@ -651,6 +651,8 @@ function JourneyProvider({
|
|
|
651
651
|
const { trigger } = useWebHaptics();
|
|
652
652
|
const onEventRef = useRef(onEvent);
|
|
653
653
|
const rawAnswersRef = useRef({});
|
|
654
|
+
const sessionStartEmittedRef = useRef(false);
|
|
655
|
+
const lastViewedStepIdRef = useRef(null);
|
|
654
656
|
const [discountCodeDialog, setDiscountCodeDialog] = useState(null);
|
|
655
657
|
useEffect(() => {
|
|
656
658
|
onEventRef.current = onEvent;
|
|
@@ -710,6 +712,39 @@ function JourneyProvider({
|
|
|
710
712
|
() => normalizedConfig.steps.flatMap(getStepVariables),
|
|
711
713
|
[normalizedConfig.steps]
|
|
712
714
|
);
|
|
715
|
+
useEffect(() => {
|
|
716
|
+
var _a, _b;
|
|
717
|
+
const currentStep = config.steps[currentStepIndex];
|
|
718
|
+
if (!currentStep) return;
|
|
719
|
+
const eventAnswers = getEventAnswers(
|
|
720
|
+
config.computedVariables,
|
|
721
|
+
rawAnswersRef.current
|
|
722
|
+
);
|
|
723
|
+
const strippedStep = stripOptionsFromEventStep(currentStep);
|
|
724
|
+
if (!sessionStartEmittedRef.current) {
|
|
725
|
+
sessionStartEmittedRef.current = true;
|
|
726
|
+
(_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, __spreadProps(__spreadValues({
|
|
727
|
+
type: "session_start",
|
|
728
|
+
step: strippedStep
|
|
729
|
+
}, eventAnswers), {
|
|
730
|
+
variables: allVariables
|
|
731
|
+
}));
|
|
732
|
+
}
|
|
733
|
+
if (lastViewedStepIdRef.current !== currentStep.id) {
|
|
734
|
+
lastViewedStepIdRef.current = currentStep.id;
|
|
735
|
+
(_b = onEventRef.current) == null ? void 0 : _b.call(onEventRef, __spreadProps(__spreadValues({
|
|
736
|
+
type: "step_view",
|
|
737
|
+
step: strippedStep
|
|
738
|
+
}, eventAnswers), {
|
|
739
|
+
variables: allVariables
|
|
740
|
+
}));
|
|
741
|
+
}
|
|
742
|
+
}, [
|
|
743
|
+
allVariables,
|
|
744
|
+
config.computedVariables,
|
|
745
|
+
config.steps,
|
|
746
|
+
currentStepIndex
|
|
747
|
+
]);
|
|
713
748
|
const setAnswer = useCallback((stepId, answer) => {
|
|
714
749
|
if (computedVariableIds.has(stepId)) return;
|
|
715
750
|
setRawAnswers((prev) => {
|
|
@@ -728,7 +763,8 @@ function JourneyProvider({
|
|
|
728
763
|
const currentStep = config.steps[currentStepIndex];
|
|
729
764
|
const routing = currentStep.routing;
|
|
730
765
|
if (routing) {
|
|
731
|
-
|
|
766
|
+
const conditions = Array.isArray(routing.conditions) ? routing.conditions : [];
|
|
767
|
+
for (const rule of conditions) {
|
|
732
768
|
if (evaluateRoutingRule(rule, currentAnswers)) {
|
|
733
769
|
const idx = config.steps.findIndex((s) => s.id === rule.goTo);
|
|
734
770
|
if (idx >= 0) return idx;
|
|
@@ -7919,13 +7955,13 @@ function MotionAnimatedBlock({
|
|
|
7919
7955
|
const exitMotion = exitPreset && exitPreset !== "none" ? EXIT_PRESETS[exitPreset] : void 0;
|
|
7920
7956
|
const rawExitDuration = (_b = exitAnim == null ? void 0 : exitAnim.duration) != null ? _b : DEFAULT_EXIT_DURATION_S;
|
|
7921
7957
|
const rawExitDelay = (_c = exitAnim == null ? void 0 : exitAnim.delay) != null ? _c : 0;
|
|
7922
|
-
const exitDuration = rawExitDuration
|
|
7923
|
-
const exitDelay = rawExitDelay
|
|
7958
|
+
const exitDuration = rawExitDuration;
|
|
7959
|
+
const exitDelay = rawExitDelay;
|
|
7924
7960
|
const motionConfig = preset !== "none" ? PRESETS[preset] : null;
|
|
7925
7961
|
const rawDuration = (_d = anim == null ? void 0 : anim.duration) != null ? _d : DEFAULT_DURATION_S;
|
|
7926
7962
|
const rawDelay = (_e = anim == null ? void 0 : anim.delay) != null ? _e : visibleIndex * DEFAULT_STAGGER_S;
|
|
7927
|
-
const duration = rawDuration
|
|
7928
|
-
const delay = rawDelay
|
|
7963
|
+
const duration = rawDuration;
|
|
7964
|
+
const delay = rawDelay;
|
|
7929
7965
|
const delayMs = delay * 1e3;
|
|
7930
7966
|
const [delayedIn, setDelayedIn] = useState(delayMs === 0);
|
|
7931
7967
|
useEffect(() => {
|
|
@@ -7934,7 +7970,7 @@ function MotionAnimatedBlock({
|
|
|
7934
7970
|
return () => clearTimeout(id);
|
|
7935
7971
|
}, [delayMs]);
|
|
7936
7972
|
const rawAutoHide = exitAnim == null ? void 0 : exitAnim.autoHideAfter;
|
|
7937
|
-
const autoHideS = rawAutoHide != null && rawAutoHide > 0 ? rawAutoHide
|
|
7973
|
+
const autoHideS = rawAutoHide != null && rawAutoHide > 0 ? rawAutoHide : null;
|
|
7938
7974
|
const [autoHidden, setAutoHidden] = useState(false);
|
|
7939
7975
|
useEffect(() => {
|
|
7940
7976
|
if (autoHideS == null) return;
|
|
@@ -9609,19 +9645,454 @@ function JourneyShell({ className, theme } = {}) {
|
|
|
9609
9645
|
) })
|
|
9610
9646
|
] });
|
|
9611
9647
|
}
|
|
9648
|
+
var JOURNEY_LIBRARY_NAME = "@founderhq/journeys";
|
|
9649
|
+
var JOURNEY_LIBRARY_VERSION = "0.4.0";
|
|
9650
|
+
function randomId(prefix) {
|
|
9651
|
+
const cryptoRef = globalThis.crypto;
|
|
9652
|
+
if (cryptoRef == null ? void 0 : cryptoRef.randomUUID) return `${prefix}_${cryptoRef.randomUUID()}`;
|
|
9653
|
+
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
9654
|
+
}
|
|
9655
|
+
function readJson(storage, key, fallback) {
|
|
9656
|
+
try {
|
|
9657
|
+
const raw = storage.getItem(key);
|
|
9658
|
+
return raw ? JSON.parse(raw) : fallback;
|
|
9659
|
+
} catch (e) {
|
|
9660
|
+
return fallback;
|
|
9661
|
+
}
|
|
9662
|
+
}
|
|
9663
|
+
function getStoredId(storage, key, prefix) {
|
|
9664
|
+
try {
|
|
9665
|
+
const existing = storage.getItem(key);
|
|
9666
|
+
if (existing) return existing;
|
|
9667
|
+
const next = randomId(prefix);
|
|
9668
|
+
storage.setItem(key, next);
|
|
9669
|
+
return next;
|
|
9670
|
+
} catch (e) {
|
|
9671
|
+
return randomId(prefix);
|
|
9672
|
+
}
|
|
9673
|
+
}
|
|
9674
|
+
function isRecord(value) {
|
|
9675
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9676
|
+
}
|
|
9677
|
+
function cleanObject(value) {
|
|
9678
|
+
if (!value || typeof value !== "object") return {};
|
|
9679
|
+
return __spreadValues({}, value);
|
|
9680
|
+
}
|
|
9681
|
+
var DEFAULT_REDACTED_URL_PARAMS = [
|
|
9682
|
+
"access_token",
|
|
9683
|
+
"api_key",
|
|
9684
|
+
"auth",
|
|
9685
|
+
"code",
|
|
9686
|
+
"email",
|
|
9687
|
+
"key",
|
|
9688
|
+
"otp",
|
|
9689
|
+
"password",
|
|
9690
|
+
"phone",
|
|
9691
|
+
"secret",
|
|
9692
|
+
"signature",
|
|
9693
|
+
"token"
|
|
9694
|
+
];
|
|
9695
|
+
function safeContextFactory(factory) {
|
|
9696
|
+
if (typeof factory !== "function") return void 0;
|
|
9697
|
+
try {
|
|
9698
|
+
const value = factory();
|
|
9699
|
+
return isRecord(value) ? value : void 0;
|
|
9700
|
+
} catch (e) {
|
|
9701
|
+
return void 0;
|
|
9702
|
+
}
|
|
9703
|
+
}
|
|
9704
|
+
function safeTimezone() {
|
|
9705
|
+
try {
|
|
9706
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
9707
|
+
} catch (e) {
|
|
9708
|
+
return void 0;
|
|
9709
|
+
}
|
|
9710
|
+
}
|
|
9711
|
+
function campaignContext(params) {
|
|
9712
|
+
var _a, _b, _c, _d, _e;
|
|
9713
|
+
return cleanObject({
|
|
9714
|
+
source: (_a = params.get("utm_source")) != null ? _a : void 0,
|
|
9715
|
+
medium: (_b = params.get("utm_medium")) != null ? _b : void 0,
|
|
9716
|
+
name: (_c = params.get("utm_campaign")) != null ? _c : void 0,
|
|
9717
|
+
term: (_d = params.get("utm_term")) != null ? _d : void 0,
|
|
9718
|
+
content: (_e = params.get("utm_content")) != null ? _e : void 0
|
|
9719
|
+
});
|
|
9720
|
+
}
|
|
9721
|
+
function redactedSearch(params, redactUrlParams) {
|
|
9722
|
+
if ([...params.keys()].length === 0) return "";
|
|
9723
|
+
const next = new URLSearchParams(params);
|
|
9724
|
+
const redactAll = redactUrlParams === true;
|
|
9725
|
+
const names = Array.isArray(redactUrlParams) && redactUrlParams.length > 0 ? redactUrlParams : DEFAULT_REDACTED_URL_PARAMS;
|
|
9726
|
+
const redactedNames = new Set(names.map((name) => name.toLowerCase()));
|
|
9727
|
+
for (const key of [...next.keys()]) {
|
|
9728
|
+
if (redactAll || redactedNames.has(key.toLowerCase())) {
|
|
9729
|
+
next.set(key, "[redacted]");
|
|
9730
|
+
}
|
|
9731
|
+
}
|
|
9732
|
+
const value = next.toString();
|
|
9733
|
+
return value ? `?${value}` : "";
|
|
9734
|
+
}
|
|
9735
|
+
function pageContext(redactUrlParams) {
|
|
9736
|
+
if (typeof window === "undefined") return void 0;
|
|
9737
|
+
const params = new URLSearchParams(window.location.search);
|
|
9738
|
+
const campaign = campaignContext(params);
|
|
9739
|
+
const search = redactedSearch(params, redactUrlParams);
|
|
9740
|
+
return cleanObject({
|
|
9741
|
+
url: `${window.location.origin}${window.location.pathname}${search}${window.location.hash}`,
|
|
9742
|
+
path: window.location.pathname,
|
|
9743
|
+
search: search || void 0,
|
|
9744
|
+
title: document.title,
|
|
9745
|
+
referrer: document.referrer,
|
|
9746
|
+
campaign
|
|
9747
|
+
});
|
|
9748
|
+
}
|
|
9749
|
+
function captureContext(capture, runtime) {
|
|
9750
|
+
var _a, _b;
|
|
9751
|
+
const customContext = cleanObject(__spreadValues(__spreadValues({}, (_a = capture.context) != null ? _a : {}), (_b = safeContextFactory(capture.contextFactory)) != null ? _b : {}));
|
|
9752
|
+
if (capture.captureContext === false) {
|
|
9753
|
+
return customContext;
|
|
9754
|
+
}
|
|
9755
|
+
const page = pageContext(capture.redactUrlParams);
|
|
9756
|
+
const navigatorRef = typeof window !== "undefined" ? window.navigator : void 0;
|
|
9757
|
+
const screenRef = typeof window !== "undefined" ? window.screen : void 0;
|
|
9758
|
+
return cleanObject(__spreadValues({
|
|
9759
|
+
source: { type: "embed", product: "journeys" },
|
|
9760
|
+
library: {
|
|
9761
|
+
name: JOURNEY_LIBRARY_NAME,
|
|
9762
|
+
version: JOURNEY_LIBRARY_VERSION
|
|
9763
|
+
},
|
|
9764
|
+
anonymousId: runtime.visitorId,
|
|
9765
|
+
sessionId: runtime.clientSessionId,
|
|
9766
|
+
locale: navigatorRef == null ? void 0 : navigatorRef.language,
|
|
9767
|
+
timezone: safeTimezone(),
|
|
9768
|
+
userAgent: navigatorRef == null ? void 0 : navigatorRef.userAgent,
|
|
9769
|
+
page,
|
|
9770
|
+
campaign: isRecord(page) ? page.campaign : void 0,
|
|
9771
|
+
screen: screenRef ? { width: screenRef.width, height: screenRef.height } : void 0
|
|
9772
|
+
}, customContext));
|
|
9773
|
+
}
|
|
9774
|
+
function toJsonRecord(value) {
|
|
9775
|
+
return isRecord(value) ? value : {};
|
|
9776
|
+
}
|
|
9777
|
+
function answerValue(answers, variable) {
|
|
9778
|
+
return Object.prototype.hasOwnProperty.call(answers, variable) ? answers[variable] : void 0;
|
|
9779
|
+
}
|
|
9780
|
+
function stepAnswerVariables(step) {
|
|
9781
|
+
var _a, _b, _c, _d, _e;
|
|
9782
|
+
if (step.type === "swipe_cards") {
|
|
9783
|
+
return ((_a = step.swipeCards) != null ? _a : []).map((card) => ({
|
|
9784
|
+
stepId: step.id,
|
|
9785
|
+
variable: card.variable,
|
|
9786
|
+
label: card.text
|
|
9787
|
+
}));
|
|
9788
|
+
}
|
|
9789
|
+
if (step.type === "input") {
|
|
9790
|
+
return ((_b = step.fields) != null ? _b : []).map((field) => {
|
|
9791
|
+
var _a2;
|
|
9792
|
+
return {
|
|
9793
|
+
stepId: step.id,
|
|
9794
|
+
variable: (_a2 = field.variable) != null ? _a2 : field.id,
|
|
9795
|
+
label: field.label,
|
|
9796
|
+
fieldType: field.type
|
|
9797
|
+
};
|
|
9798
|
+
});
|
|
9799
|
+
}
|
|
9800
|
+
return [
|
|
9801
|
+
{
|
|
9802
|
+
stepId: step.id,
|
|
9803
|
+
variable: (_c = step.variable) != null ? _c : step.id,
|
|
9804
|
+
label: (_e = (_d = step.question) != null ? _d : step.preface) != null ? _e : step.id
|
|
9805
|
+
}
|
|
9806
|
+
];
|
|
9807
|
+
}
|
|
9808
|
+
function buildAnswerSnapshot(config, answers) {
|
|
9809
|
+
const items = [];
|
|
9810
|
+
for (const step of config.steps) {
|
|
9811
|
+
for (const item of stepAnswerVariables(step)) {
|
|
9812
|
+
const value = answerValue(answers, item.variable);
|
|
9813
|
+
if (value === void 0) continue;
|
|
9814
|
+
items.push({
|
|
9815
|
+
stepId: item.stepId,
|
|
9816
|
+
variable: item.variable,
|
|
9817
|
+
label: item.label,
|
|
9818
|
+
value
|
|
9819
|
+
});
|
|
9820
|
+
}
|
|
9821
|
+
}
|
|
9822
|
+
return { items };
|
|
9823
|
+
}
|
|
9824
|
+
function buildRespondentSnapshot(config, answers) {
|
|
9825
|
+
var _a, _b;
|
|
9826
|
+
const snapshot = {};
|
|
9827
|
+
for (const step of config.steps) {
|
|
9828
|
+
if (step.type !== "input") continue;
|
|
9829
|
+
for (const field of (_a = step.fields) != null ? _a : []) {
|
|
9830
|
+
const variable = (_b = field.variable) != null ? _b : field.id;
|
|
9831
|
+
const value = answerValue(answers, variable);
|
|
9832
|
+
if (value !== void 0) snapshot[variable] = value;
|
|
9833
|
+
}
|
|
9834
|
+
}
|
|
9835
|
+
return snapshot;
|
|
9836
|
+
}
|
|
9837
|
+
function buildContactRef(config, answers) {
|
|
9838
|
+
var _a, _b;
|
|
9839
|
+
const contact = {};
|
|
9840
|
+
for (const step of config.steps) {
|
|
9841
|
+
if (step.type !== "input") continue;
|
|
9842
|
+
for (const field of (_a = step.fields) != null ? _a : []) {
|
|
9843
|
+
const variable = (_b = field.variable) != null ? _b : field.id;
|
|
9844
|
+
const value = answerValue(answers, variable);
|
|
9845
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
9846
|
+
const normalizedVariable = variable.toLowerCase().replace(/[-_\s]/g, "");
|
|
9847
|
+
if (field.type === "email" || normalizedVariable.includes("email")) {
|
|
9848
|
+
contact.email = value;
|
|
9849
|
+
} else if (field.type === "tel" || normalizedVariable.includes("phone")) {
|
|
9850
|
+
contact.phone = value;
|
|
9851
|
+
} else if (normalizedVariable === "externalid") {
|
|
9852
|
+
contact.externalId = value;
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9855
|
+
}
|
|
9856
|
+
return Object.keys(contact).length > 0 ? contact : void 0;
|
|
9857
|
+
}
|
|
9858
|
+
function eventStepId(event) {
|
|
9859
|
+
if ("step" in event) return event.step.id;
|
|
9860
|
+
if (event.type === "navigate") return event.to.id;
|
|
9861
|
+
if (event.type === "purchase_intent") return event.stepId;
|
|
9862
|
+
return void 0;
|
|
9863
|
+
}
|
|
9864
|
+
function eventProperties(event) {
|
|
9865
|
+
if (event.type === "step_submit") {
|
|
9866
|
+
return { submitted: event.submitted };
|
|
9867
|
+
}
|
|
9868
|
+
if (event.type === "navigate") {
|
|
9869
|
+
return {
|
|
9870
|
+
fromStepId: event.from.id,
|
|
9871
|
+
toStepId: event.to.id,
|
|
9872
|
+
direction: event.direction
|
|
9873
|
+
};
|
|
9874
|
+
}
|
|
9875
|
+
if (event.type === "purchase_intent") {
|
|
9876
|
+
return __spreadValues({
|
|
9877
|
+
variable: event.variable,
|
|
9878
|
+
plan: event.plan
|
|
9879
|
+
}, event.discount ? { discount: event.discount } : {});
|
|
9880
|
+
}
|
|
9881
|
+
return {};
|
|
9882
|
+
}
|
|
9883
|
+
function toCaptureEvent(config, event, sequence, runtime) {
|
|
9884
|
+
const answers = "answers" in event ? event.answers : {};
|
|
9885
|
+
const payload = {
|
|
9886
|
+
id: createCaptureEventId(runtime, event, sequence),
|
|
9887
|
+
type: event.type,
|
|
9888
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9889
|
+
stepId: eventStepId(event),
|
|
9890
|
+
properties: eventProperties(event),
|
|
9891
|
+
answers: toJsonRecord(answers),
|
|
9892
|
+
computedVariables: "computedVariables" in event ? toJsonRecord(event.computedVariables) : void 0
|
|
9893
|
+
};
|
|
9894
|
+
if (event.type === "complete") {
|
|
9895
|
+
payload.respondentSnapshot = buildRespondentSnapshot(config, answers);
|
|
9896
|
+
payload.answerSnapshot = buildAnswerSnapshot(config, answers);
|
|
9897
|
+
payload.contact = buildContactRef(config, answers);
|
|
9898
|
+
}
|
|
9899
|
+
return payload;
|
|
9900
|
+
}
|
|
9901
|
+
function createCaptureEventId(runtime, event, sequence) {
|
|
9902
|
+
return `${runtime.clientSessionId}:${event.type}:${sequence.toString(
|
|
9903
|
+
36
|
|
9904
|
+
)}:${randomId("fhqe")}`;
|
|
9905
|
+
}
|
|
9906
|
+
function useJourneyCapture(params) {
|
|
9907
|
+
const onEventRef = useRef(params.onEvent);
|
|
9908
|
+
const configRef = useRef(params.config);
|
|
9909
|
+
const captureRef = useRef(params.capture);
|
|
9910
|
+
const runtimeRef = useRef(null);
|
|
9911
|
+
const queueRef = useRef([]);
|
|
9912
|
+
const sequenceRef = useRef(0);
|
|
9913
|
+
const flushingRef = useRef(false);
|
|
9914
|
+
onEventRef.current = params.onEvent;
|
|
9915
|
+
configRef.current = params.config;
|
|
9916
|
+
captureRef.current = params.capture;
|
|
9917
|
+
const persistQueue = useCallback(() => {
|
|
9918
|
+
const runtime = runtimeRef.current;
|
|
9919
|
+
if (!runtime || typeof window === "undefined") return;
|
|
9920
|
+
try {
|
|
9921
|
+
sessionStorage.setItem(
|
|
9922
|
+
runtime.queueKey,
|
|
9923
|
+
JSON.stringify(queueRef.current)
|
|
9924
|
+
);
|
|
9925
|
+
} catch (e) {
|
|
9926
|
+
}
|
|
9927
|
+
}, []);
|
|
9928
|
+
const ensureRuntime = useCallback((capture) => {
|
|
9929
|
+
if (typeof window === "undefined") return null;
|
|
9930
|
+
const existing = runtimeRef.current;
|
|
9931
|
+
if ((existing == null ? void 0 : existing.journeyId) === capture.journeyId) return existing;
|
|
9932
|
+
const visitorId = getStoredId(
|
|
9933
|
+
localStorage,
|
|
9934
|
+
"fhq_journey_visitor_id",
|
|
9935
|
+
"fhqv"
|
|
9936
|
+
);
|
|
9937
|
+
const clientSessionId = getStoredId(
|
|
9938
|
+
sessionStorage,
|
|
9939
|
+
`fhq_journey_session:${capture.journeyId}`,
|
|
9940
|
+
"fhqs"
|
|
9941
|
+
);
|
|
9942
|
+
const queueKey = `fhq_journey_queue:${capture.journeyId}:${clientSessionId}`;
|
|
9943
|
+
const runtime = {
|
|
9944
|
+
journeyId: capture.journeyId,
|
|
9945
|
+
visitorId,
|
|
9946
|
+
clientSessionId,
|
|
9947
|
+
queueKey
|
|
9948
|
+
};
|
|
9949
|
+
runtimeRef.current = runtime;
|
|
9950
|
+
queueRef.current = readJson(
|
|
9951
|
+
sessionStorage,
|
|
9952
|
+
queueKey,
|
|
9953
|
+
[]
|
|
9954
|
+
);
|
|
9955
|
+
return runtime;
|
|
9956
|
+
}, []);
|
|
9957
|
+
const flush = useCallback(async () => {
|
|
9958
|
+
var _a, _b, _c;
|
|
9959
|
+
const capture = captureRef.current;
|
|
9960
|
+
if (!capture || !configRef.current || flushingRef.current) return;
|
|
9961
|
+
const runtime = ensureRuntime(capture);
|
|
9962
|
+
if (!runtime || queueRef.current.length === 0) return;
|
|
9963
|
+
flushingRef.current = true;
|
|
9964
|
+
const batchSize = Math.max(1, (_a = capture.batchSize) != null ? _a : 10);
|
|
9965
|
+
const maxRetries = Math.max(1, (_b = capture.maxRetries) != null ? _b : 3);
|
|
9966
|
+
const batch = queueRef.current.slice(0, batchSize);
|
|
9967
|
+
const baseUrl = (_c = capture.baseUrl) != null ? _c : "https://getfounderhq.com";
|
|
9968
|
+
try {
|
|
9969
|
+
const response = await fetch(
|
|
9970
|
+
`${baseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
9971
|
+
capture.journeyId
|
|
9972
|
+
)}/capture`,
|
|
9973
|
+
{
|
|
9974
|
+
method: "POST",
|
|
9975
|
+
headers: {
|
|
9976
|
+
Authorization: `Bearer ${capture.apiKey}`,
|
|
9977
|
+
"Content-Type": "application/json"
|
|
9978
|
+
},
|
|
9979
|
+
body: JSON.stringify({
|
|
9980
|
+
clientSessionId: runtime.clientSessionId,
|
|
9981
|
+
visitorId: runtime.visitorId,
|
|
9982
|
+
context: captureContext(capture, runtime),
|
|
9983
|
+
events: batch.map((item) => item.event)
|
|
9984
|
+
}),
|
|
9985
|
+
keepalive: batch.length <= 5
|
|
9986
|
+
}
|
|
9987
|
+
);
|
|
9988
|
+
if (!response.ok) throw new Error(`Capture failed: ${response.status}`);
|
|
9989
|
+
queueRef.current = queueRef.current.slice(batch.length);
|
|
9990
|
+
} catch (e) {
|
|
9991
|
+
const failedIds = new Set(batch.map((item) => item.event.id));
|
|
9992
|
+
queueRef.current = queueRef.current.map(
|
|
9993
|
+
(item) => failedIds.has(item.event.id) ? __spreadProps(__spreadValues({}, item), { attempts: item.attempts + 1 }) : item
|
|
9994
|
+
).filter((item) => item.attempts < maxRetries);
|
|
9995
|
+
} finally {
|
|
9996
|
+
persistQueue();
|
|
9997
|
+
flushingRef.current = false;
|
|
9998
|
+
}
|
|
9999
|
+
}, [ensureRuntime, persistQueue]);
|
|
10000
|
+
useEffect(() => {
|
|
10001
|
+
var _a;
|
|
10002
|
+
const capture = captureRef.current;
|
|
10003
|
+
if (!capture || typeof window === "undefined") return;
|
|
10004
|
+
ensureRuntime(capture);
|
|
10005
|
+
const interval = window.setInterval(
|
|
10006
|
+
() => void flush(),
|
|
10007
|
+
Math.max(1e3, (_a = capture.flushIntervalMs) != null ? _a : 3e3)
|
|
10008
|
+
);
|
|
10009
|
+
const handleVisibility = () => {
|
|
10010
|
+
if (document.visibilityState === "hidden") void flush();
|
|
10011
|
+
};
|
|
10012
|
+
window.addEventListener("beforeunload", persistQueue);
|
|
10013
|
+
document.addEventListener("visibilitychange", handleVisibility);
|
|
10014
|
+
void flush();
|
|
10015
|
+
return () => {
|
|
10016
|
+
window.clearInterval(interval);
|
|
10017
|
+
window.removeEventListener("beforeunload", persistQueue);
|
|
10018
|
+
document.removeEventListener("visibilitychange", handleVisibility);
|
|
10019
|
+
persistQueue();
|
|
10020
|
+
};
|
|
10021
|
+
}, [ensureRuntime, flush, persistQueue, params.capture]);
|
|
10022
|
+
return useCallback(
|
|
10023
|
+
(event) => {
|
|
10024
|
+
var _a, _b;
|
|
10025
|
+
(_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, event);
|
|
10026
|
+
const capture = captureRef.current;
|
|
10027
|
+
const config = configRef.current;
|
|
10028
|
+
if (!capture || !config) return;
|
|
10029
|
+
const runtime = ensureRuntime(capture);
|
|
10030
|
+
if (!runtime) return;
|
|
10031
|
+
sequenceRef.current += 1;
|
|
10032
|
+
queueRef.current.push({
|
|
10033
|
+
event: toCaptureEvent(config, event, sequenceRef.current, runtime),
|
|
10034
|
+
attempts: 0
|
|
10035
|
+
});
|
|
10036
|
+
persistQueue();
|
|
10037
|
+
const batchSize = Math.max(1, (_b = capture.batchSize) != null ? _b : 10);
|
|
10038
|
+
if (queueRef.current.length >= batchSize) void flush();
|
|
10039
|
+
},
|
|
10040
|
+
[ensureRuntime, flush, persistQueue]
|
|
10041
|
+
);
|
|
10042
|
+
}
|
|
10043
|
+
var FOUNDERHQ_BASE_URL = "https://getfounderhq.com";
|
|
10044
|
+
function resolveJourneyBaseUrl(baseUrl) {
|
|
10045
|
+
if (!baseUrl) return FOUNDERHQ_BASE_URL;
|
|
10046
|
+
try {
|
|
10047
|
+
const parsed = new URL(baseUrl);
|
|
10048
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
10049
|
+
const isLocal = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
10050
|
+
return isLocal ? parsed.origin : FOUNDERHQ_BASE_URL;
|
|
10051
|
+
} catch (e) {
|
|
10052
|
+
return FOUNDERHQ_BASE_URL;
|
|
10053
|
+
}
|
|
10054
|
+
}
|
|
9612
10055
|
function useJourneyConfig({
|
|
9613
10056
|
apiKey,
|
|
9614
10057
|
journeyId,
|
|
9615
|
-
baseUrl
|
|
10058
|
+
baseUrl,
|
|
10059
|
+
config,
|
|
10060
|
+
capture = true
|
|
9616
10061
|
}) {
|
|
9617
10062
|
const [state, setState] = useState({ status: "loading" });
|
|
9618
10063
|
useEffect(() => {
|
|
9619
10064
|
let cancelled = false;
|
|
9620
|
-
|
|
9621
|
-
|
|
10065
|
+
const resolvedBaseUrl = resolveJourneyBaseUrl(baseUrl);
|
|
10066
|
+
async function loadConfig() {
|
|
10067
|
+
var _a, _b;
|
|
9622
10068
|
setState({ status: "loading" });
|
|
9623
10069
|
try {
|
|
9624
|
-
const
|
|
10070
|
+
const validateUrl = `${resolvedBaseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
10071
|
+
journeyId
|
|
10072
|
+
)}/validate`;
|
|
10073
|
+
const validateRes = await fetch(validateUrl, {
|
|
10074
|
+
method: "POST",
|
|
10075
|
+
headers: {
|
|
10076
|
+
Authorization: `Bearer ${apiKey}`,
|
|
10077
|
+
"Content-Type": "application/json"
|
|
10078
|
+
},
|
|
10079
|
+
body: JSON.stringify({ capture })
|
|
10080
|
+
});
|
|
10081
|
+
if (!validateRes.ok) {
|
|
10082
|
+
const body = await validateRes.json().catch(() => ({}));
|
|
10083
|
+
throw new Error(
|
|
10084
|
+
(_a = body.error) != null ? _a : `Journey is unavailable (HTTP ${validateRes.status})`
|
|
10085
|
+
);
|
|
10086
|
+
}
|
|
10087
|
+
if (config) {
|
|
10088
|
+
if (!cancelled) {
|
|
10089
|
+
setState({ status: "ready", config });
|
|
10090
|
+
}
|
|
10091
|
+
return;
|
|
10092
|
+
}
|
|
10093
|
+
const url = `${resolvedBaseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
10094
|
+
journeyId
|
|
10095
|
+
)}`;
|
|
9625
10096
|
const res = await fetch(url, {
|
|
9626
10097
|
headers: {
|
|
9627
10098
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -9631,7 +10102,7 @@ function useJourneyConfig({
|
|
|
9631
10102
|
if (!res.ok) {
|
|
9632
10103
|
const body = await res.json().catch(() => ({}));
|
|
9633
10104
|
throw new Error(
|
|
9634
|
-
(
|
|
10105
|
+
(_b = body.error) != null ? _b : `Failed to fetch journey config (HTTP ${res.status})`
|
|
9635
10106
|
);
|
|
9636
10107
|
}
|
|
9637
10108
|
const data = await res.json();
|
|
@@ -9647,11 +10118,11 @@ function useJourneyConfig({
|
|
|
9647
10118
|
}
|
|
9648
10119
|
}
|
|
9649
10120
|
}
|
|
9650
|
-
|
|
10121
|
+
loadConfig();
|
|
9651
10122
|
return () => {
|
|
9652
10123
|
cancelled = true;
|
|
9653
10124
|
};
|
|
9654
|
-
}, [apiKey, journeyId, baseUrl]);
|
|
10125
|
+
}, [apiKey, journeyId, baseUrl, config, capture]);
|
|
9655
10126
|
return state;
|
|
9656
10127
|
}
|
|
9657
10128
|
function DefaultLoading() {
|
|
@@ -9700,15 +10171,16 @@ function DefaultError({ error }) {
|
|
|
9700
10171
|
textAlign: "center"
|
|
9701
10172
|
},
|
|
9702
10173
|
children: [
|
|
9703
|
-
/* @__PURE__ */ jsx("p", { style: { fontSize: "1.125rem", fontWeight: 600 }, children: "
|
|
10174
|
+
/* @__PURE__ */ jsx("p", { style: { fontSize: "1.125rem", fontWeight: 600 }, children: "This Journey is unavailable" }),
|
|
9704
10175
|
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", marginTop: "0.5rem", opacity: 0.7 }, children: error.message })
|
|
9705
10176
|
]
|
|
9706
10177
|
}
|
|
9707
10178
|
);
|
|
9708
10179
|
}
|
|
9709
|
-
function
|
|
10180
|
+
function Journey({
|
|
9710
10181
|
apiKey,
|
|
9711
10182
|
journeyId,
|
|
10183
|
+
config,
|
|
9712
10184
|
baseUrl,
|
|
9713
10185
|
storageKey,
|
|
9714
10186
|
onEvent,
|
|
@@ -9718,9 +10190,28 @@ function JourneyRemote({
|
|
|
9718
10190
|
initialOptions,
|
|
9719
10191
|
onDiscountCodeApply,
|
|
9720
10192
|
loadingComponent,
|
|
9721
|
-
errorComponent
|
|
10193
|
+
errorComponent,
|
|
10194
|
+
capture
|
|
9722
10195
|
}) {
|
|
9723
|
-
const
|
|
10196
|
+
const resolvedBaseUrl = resolveJourneyBaseUrl(baseUrl);
|
|
10197
|
+
const captureEnabled = capture !== false;
|
|
10198
|
+
const state = useJourneyConfig({
|
|
10199
|
+
apiKey,
|
|
10200
|
+
journeyId,
|
|
10201
|
+
baseUrl: resolvedBaseUrl,
|
|
10202
|
+
config,
|
|
10203
|
+
capture: captureEnabled
|
|
10204
|
+
});
|
|
10205
|
+
const captureOption = capture === false ? false : __spreadValues({
|
|
10206
|
+
apiKey,
|
|
10207
|
+
journeyId,
|
|
10208
|
+
baseUrl: resolvedBaseUrl
|
|
10209
|
+
}, capture != null ? capture : {});
|
|
10210
|
+
const capturedOnEvent = useJourneyCapture({
|
|
10211
|
+
config: state.status === "ready" ? state.config : null,
|
|
10212
|
+
capture: captureOption,
|
|
10213
|
+
onEvent
|
|
10214
|
+
});
|
|
9724
10215
|
if (state.status === "loading") {
|
|
9725
10216
|
return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent != null ? loadingComponent : /* @__PURE__ */ jsx(DefaultLoading, {}) });
|
|
9726
10217
|
}
|
|
@@ -9733,35 +10224,7 @@ function JourneyRemote({
|
|
|
9733
10224
|
config: state.config,
|
|
9734
10225
|
storageKey,
|
|
9735
10226
|
theme,
|
|
9736
|
-
onEvent,
|
|
9737
|
-
initialAnswers,
|
|
9738
|
-
initialOptions,
|
|
9739
|
-
onDiscountCodeApply,
|
|
9740
|
-
children: /* @__PURE__ */ jsx(JourneyShell, { className, theme })
|
|
9741
|
-
}
|
|
9742
|
-
);
|
|
9743
|
-
}
|
|
9744
|
-
function Journey(props) {
|
|
9745
|
-
if ("apiKey" in props && props.apiKey) {
|
|
9746
|
-
return /* @__PURE__ */ jsx(JourneyRemote, __spreadValues({}, props));
|
|
9747
|
-
}
|
|
9748
|
-
const {
|
|
9749
|
-
config,
|
|
9750
|
-
storageKey,
|
|
9751
|
-
onEvent,
|
|
9752
|
-
className,
|
|
9753
|
-
theme,
|
|
9754
|
-
initialAnswers,
|
|
9755
|
-
initialOptions,
|
|
9756
|
-
onDiscountCodeApply
|
|
9757
|
-
} = props;
|
|
9758
|
-
return /* @__PURE__ */ jsx(
|
|
9759
|
-
JourneyProvider,
|
|
9760
|
-
{
|
|
9761
|
-
config,
|
|
9762
|
-
storageKey,
|
|
9763
|
-
theme,
|
|
9764
|
-
onEvent,
|
|
10227
|
+
onEvent: capturedOnEvent,
|
|
9765
10228
|
initialAnswers,
|
|
9766
10229
|
initialOptions,
|
|
9767
10230
|
onDiscountCodeApply,
|