@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.cjs
CHANGED
|
@@ -676,6 +676,8 @@ function JourneyProvider({
|
|
|
676
676
|
const { trigger } = react$1.useWebHaptics();
|
|
677
677
|
const onEventRef = React.useRef(onEvent);
|
|
678
678
|
const rawAnswersRef = React.useRef({});
|
|
679
|
+
const sessionStartEmittedRef = React.useRef(false);
|
|
680
|
+
const lastViewedStepIdRef = React.useRef(null);
|
|
679
681
|
const [discountCodeDialog, setDiscountCodeDialog] = React.useState(null);
|
|
680
682
|
React.useEffect(() => {
|
|
681
683
|
onEventRef.current = onEvent;
|
|
@@ -735,6 +737,39 @@ function JourneyProvider({
|
|
|
735
737
|
() => normalizedConfig.steps.flatMap(getStepVariables),
|
|
736
738
|
[normalizedConfig.steps]
|
|
737
739
|
);
|
|
740
|
+
React.useEffect(() => {
|
|
741
|
+
var _a, _b;
|
|
742
|
+
const currentStep = config.steps[currentStepIndex];
|
|
743
|
+
if (!currentStep) return;
|
|
744
|
+
const eventAnswers = getEventAnswers(
|
|
745
|
+
config.computedVariables,
|
|
746
|
+
rawAnswersRef.current
|
|
747
|
+
);
|
|
748
|
+
const strippedStep = stripOptionsFromEventStep(currentStep);
|
|
749
|
+
if (!sessionStartEmittedRef.current) {
|
|
750
|
+
sessionStartEmittedRef.current = true;
|
|
751
|
+
(_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, __spreadProps(__spreadValues({
|
|
752
|
+
type: "session_start",
|
|
753
|
+
step: strippedStep
|
|
754
|
+
}, eventAnswers), {
|
|
755
|
+
variables: allVariables
|
|
756
|
+
}));
|
|
757
|
+
}
|
|
758
|
+
if (lastViewedStepIdRef.current !== currentStep.id) {
|
|
759
|
+
lastViewedStepIdRef.current = currentStep.id;
|
|
760
|
+
(_b = onEventRef.current) == null ? void 0 : _b.call(onEventRef, __spreadProps(__spreadValues({
|
|
761
|
+
type: "step_view",
|
|
762
|
+
step: strippedStep
|
|
763
|
+
}, eventAnswers), {
|
|
764
|
+
variables: allVariables
|
|
765
|
+
}));
|
|
766
|
+
}
|
|
767
|
+
}, [
|
|
768
|
+
allVariables,
|
|
769
|
+
config.computedVariables,
|
|
770
|
+
config.steps,
|
|
771
|
+
currentStepIndex
|
|
772
|
+
]);
|
|
738
773
|
const setAnswer = React.useCallback((stepId, answer) => {
|
|
739
774
|
if (computedVariableIds.has(stepId)) return;
|
|
740
775
|
setRawAnswers((prev) => {
|
|
@@ -753,7 +788,8 @@ function JourneyProvider({
|
|
|
753
788
|
const currentStep = config.steps[currentStepIndex];
|
|
754
789
|
const routing = currentStep.routing;
|
|
755
790
|
if (routing) {
|
|
756
|
-
|
|
791
|
+
const conditions = Array.isArray(routing.conditions) ? routing.conditions : [];
|
|
792
|
+
for (const rule of conditions) {
|
|
757
793
|
if (evaluateRoutingRule(rule, currentAnswers)) {
|
|
758
794
|
const idx = config.steps.findIndex((s) => s.id === rule.goTo);
|
|
759
795
|
if (idx >= 0) return idx;
|
|
@@ -7944,13 +7980,13 @@ function MotionAnimatedBlock({
|
|
|
7944
7980
|
const exitMotion = exitPreset && exitPreset !== "none" ? EXIT_PRESETS[exitPreset] : void 0;
|
|
7945
7981
|
const rawExitDuration = (_b = exitAnim == null ? void 0 : exitAnim.duration) != null ? _b : DEFAULT_EXIT_DURATION_S;
|
|
7946
7982
|
const rawExitDelay = (_c = exitAnim == null ? void 0 : exitAnim.delay) != null ? _c : 0;
|
|
7947
|
-
const exitDuration = rawExitDuration
|
|
7948
|
-
const exitDelay = rawExitDelay
|
|
7983
|
+
const exitDuration = rawExitDuration;
|
|
7984
|
+
const exitDelay = rawExitDelay;
|
|
7949
7985
|
const motionConfig = preset !== "none" ? PRESETS[preset] : null;
|
|
7950
7986
|
const rawDuration = (_d = anim == null ? void 0 : anim.duration) != null ? _d : DEFAULT_DURATION_S;
|
|
7951
7987
|
const rawDelay = (_e = anim == null ? void 0 : anim.delay) != null ? _e : visibleIndex * DEFAULT_STAGGER_S;
|
|
7952
|
-
const duration = rawDuration
|
|
7953
|
-
const delay = rawDelay
|
|
7988
|
+
const duration = rawDuration;
|
|
7989
|
+
const delay = rawDelay;
|
|
7954
7990
|
const delayMs = delay * 1e3;
|
|
7955
7991
|
const [delayedIn, setDelayedIn] = React.useState(delayMs === 0);
|
|
7956
7992
|
React.useEffect(() => {
|
|
@@ -7959,7 +7995,7 @@ function MotionAnimatedBlock({
|
|
|
7959
7995
|
return () => clearTimeout(id);
|
|
7960
7996
|
}, [delayMs]);
|
|
7961
7997
|
const rawAutoHide = exitAnim == null ? void 0 : exitAnim.autoHideAfter;
|
|
7962
|
-
const autoHideS = rawAutoHide != null && rawAutoHide > 0 ? rawAutoHide
|
|
7998
|
+
const autoHideS = rawAutoHide != null && rawAutoHide > 0 ? rawAutoHide : null;
|
|
7963
7999
|
const [autoHidden, setAutoHidden] = React.useState(false);
|
|
7964
8000
|
React.useEffect(() => {
|
|
7965
8001
|
if (autoHideS == null) return;
|
|
@@ -9634,19 +9670,454 @@ function JourneyShell({ className, theme } = {}) {
|
|
|
9634
9670
|
) })
|
|
9635
9671
|
] });
|
|
9636
9672
|
}
|
|
9673
|
+
var JOURNEY_LIBRARY_NAME = "@founderhq/journeys";
|
|
9674
|
+
var JOURNEY_LIBRARY_VERSION = "0.4.0";
|
|
9675
|
+
function randomId(prefix) {
|
|
9676
|
+
const cryptoRef = globalThis.crypto;
|
|
9677
|
+
if (cryptoRef == null ? void 0 : cryptoRef.randomUUID) return `${prefix}_${cryptoRef.randomUUID()}`;
|
|
9678
|
+
return `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
9679
|
+
}
|
|
9680
|
+
function readJson(storage, key, fallback) {
|
|
9681
|
+
try {
|
|
9682
|
+
const raw = storage.getItem(key);
|
|
9683
|
+
return raw ? JSON.parse(raw) : fallback;
|
|
9684
|
+
} catch (e) {
|
|
9685
|
+
return fallback;
|
|
9686
|
+
}
|
|
9687
|
+
}
|
|
9688
|
+
function getStoredId(storage, key, prefix) {
|
|
9689
|
+
try {
|
|
9690
|
+
const existing = storage.getItem(key);
|
|
9691
|
+
if (existing) return existing;
|
|
9692
|
+
const next = randomId(prefix);
|
|
9693
|
+
storage.setItem(key, next);
|
|
9694
|
+
return next;
|
|
9695
|
+
} catch (e) {
|
|
9696
|
+
return randomId(prefix);
|
|
9697
|
+
}
|
|
9698
|
+
}
|
|
9699
|
+
function isRecord(value) {
|
|
9700
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9701
|
+
}
|
|
9702
|
+
function cleanObject(value) {
|
|
9703
|
+
if (!value || typeof value !== "object") return {};
|
|
9704
|
+
return __spreadValues({}, value);
|
|
9705
|
+
}
|
|
9706
|
+
var DEFAULT_REDACTED_URL_PARAMS = [
|
|
9707
|
+
"access_token",
|
|
9708
|
+
"api_key",
|
|
9709
|
+
"auth",
|
|
9710
|
+
"code",
|
|
9711
|
+
"email",
|
|
9712
|
+
"key",
|
|
9713
|
+
"otp",
|
|
9714
|
+
"password",
|
|
9715
|
+
"phone",
|
|
9716
|
+
"secret",
|
|
9717
|
+
"signature",
|
|
9718
|
+
"token"
|
|
9719
|
+
];
|
|
9720
|
+
function safeContextFactory(factory) {
|
|
9721
|
+
if (typeof factory !== "function") return void 0;
|
|
9722
|
+
try {
|
|
9723
|
+
const value = factory();
|
|
9724
|
+
return isRecord(value) ? value : void 0;
|
|
9725
|
+
} catch (e) {
|
|
9726
|
+
return void 0;
|
|
9727
|
+
}
|
|
9728
|
+
}
|
|
9729
|
+
function safeTimezone() {
|
|
9730
|
+
try {
|
|
9731
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
9732
|
+
} catch (e) {
|
|
9733
|
+
return void 0;
|
|
9734
|
+
}
|
|
9735
|
+
}
|
|
9736
|
+
function campaignContext(params) {
|
|
9737
|
+
var _a, _b, _c, _d, _e;
|
|
9738
|
+
return cleanObject({
|
|
9739
|
+
source: (_a = params.get("utm_source")) != null ? _a : void 0,
|
|
9740
|
+
medium: (_b = params.get("utm_medium")) != null ? _b : void 0,
|
|
9741
|
+
name: (_c = params.get("utm_campaign")) != null ? _c : void 0,
|
|
9742
|
+
term: (_d = params.get("utm_term")) != null ? _d : void 0,
|
|
9743
|
+
content: (_e = params.get("utm_content")) != null ? _e : void 0
|
|
9744
|
+
});
|
|
9745
|
+
}
|
|
9746
|
+
function redactedSearch(params, redactUrlParams) {
|
|
9747
|
+
if ([...params.keys()].length === 0) return "";
|
|
9748
|
+
const next = new URLSearchParams(params);
|
|
9749
|
+
const redactAll = redactUrlParams === true;
|
|
9750
|
+
const names = Array.isArray(redactUrlParams) && redactUrlParams.length > 0 ? redactUrlParams : DEFAULT_REDACTED_URL_PARAMS;
|
|
9751
|
+
const redactedNames = new Set(names.map((name) => name.toLowerCase()));
|
|
9752
|
+
for (const key of [...next.keys()]) {
|
|
9753
|
+
if (redactAll || redactedNames.has(key.toLowerCase())) {
|
|
9754
|
+
next.set(key, "[redacted]");
|
|
9755
|
+
}
|
|
9756
|
+
}
|
|
9757
|
+
const value = next.toString();
|
|
9758
|
+
return value ? `?${value}` : "";
|
|
9759
|
+
}
|
|
9760
|
+
function pageContext(redactUrlParams) {
|
|
9761
|
+
if (typeof window === "undefined") return void 0;
|
|
9762
|
+
const params = new URLSearchParams(window.location.search);
|
|
9763
|
+
const campaign = campaignContext(params);
|
|
9764
|
+
const search = redactedSearch(params, redactUrlParams);
|
|
9765
|
+
return cleanObject({
|
|
9766
|
+
url: `${window.location.origin}${window.location.pathname}${search}${window.location.hash}`,
|
|
9767
|
+
path: window.location.pathname,
|
|
9768
|
+
search: search || void 0,
|
|
9769
|
+
title: document.title,
|
|
9770
|
+
referrer: document.referrer,
|
|
9771
|
+
campaign
|
|
9772
|
+
});
|
|
9773
|
+
}
|
|
9774
|
+
function captureContext(capture, runtime) {
|
|
9775
|
+
var _a, _b;
|
|
9776
|
+
const customContext = cleanObject(__spreadValues(__spreadValues({}, (_a = capture.context) != null ? _a : {}), (_b = safeContextFactory(capture.contextFactory)) != null ? _b : {}));
|
|
9777
|
+
if (capture.captureContext === false) {
|
|
9778
|
+
return customContext;
|
|
9779
|
+
}
|
|
9780
|
+
const page = pageContext(capture.redactUrlParams);
|
|
9781
|
+
const navigatorRef = typeof window !== "undefined" ? window.navigator : void 0;
|
|
9782
|
+
const screenRef = typeof window !== "undefined" ? window.screen : void 0;
|
|
9783
|
+
return cleanObject(__spreadValues({
|
|
9784
|
+
source: { type: "embed", product: "journeys" },
|
|
9785
|
+
library: {
|
|
9786
|
+
name: JOURNEY_LIBRARY_NAME,
|
|
9787
|
+
version: JOURNEY_LIBRARY_VERSION
|
|
9788
|
+
},
|
|
9789
|
+
anonymousId: runtime.visitorId,
|
|
9790
|
+
sessionId: runtime.clientSessionId,
|
|
9791
|
+
locale: navigatorRef == null ? void 0 : navigatorRef.language,
|
|
9792
|
+
timezone: safeTimezone(),
|
|
9793
|
+
userAgent: navigatorRef == null ? void 0 : navigatorRef.userAgent,
|
|
9794
|
+
page,
|
|
9795
|
+
campaign: isRecord(page) ? page.campaign : void 0,
|
|
9796
|
+
screen: screenRef ? { width: screenRef.width, height: screenRef.height } : void 0
|
|
9797
|
+
}, customContext));
|
|
9798
|
+
}
|
|
9799
|
+
function toJsonRecord(value) {
|
|
9800
|
+
return isRecord(value) ? value : {};
|
|
9801
|
+
}
|
|
9802
|
+
function answerValue(answers, variable) {
|
|
9803
|
+
return Object.prototype.hasOwnProperty.call(answers, variable) ? answers[variable] : void 0;
|
|
9804
|
+
}
|
|
9805
|
+
function stepAnswerVariables(step) {
|
|
9806
|
+
var _a, _b, _c, _d, _e;
|
|
9807
|
+
if (step.type === "swipe_cards") {
|
|
9808
|
+
return ((_a = step.swipeCards) != null ? _a : []).map((card) => ({
|
|
9809
|
+
stepId: step.id,
|
|
9810
|
+
variable: card.variable,
|
|
9811
|
+
label: card.text
|
|
9812
|
+
}));
|
|
9813
|
+
}
|
|
9814
|
+
if (step.type === "input") {
|
|
9815
|
+
return ((_b = step.fields) != null ? _b : []).map((field) => {
|
|
9816
|
+
var _a2;
|
|
9817
|
+
return {
|
|
9818
|
+
stepId: step.id,
|
|
9819
|
+
variable: (_a2 = field.variable) != null ? _a2 : field.id,
|
|
9820
|
+
label: field.label,
|
|
9821
|
+
fieldType: field.type
|
|
9822
|
+
};
|
|
9823
|
+
});
|
|
9824
|
+
}
|
|
9825
|
+
return [
|
|
9826
|
+
{
|
|
9827
|
+
stepId: step.id,
|
|
9828
|
+
variable: (_c = step.variable) != null ? _c : step.id,
|
|
9829
|
+
label: (_e = (_d = step.question) != null ? _d : step.preface) != null ? _e : step.id
|
|
9830
|
+
}
|
|
9831
|
+
];
|
|
9832
|
+
}
|
|
9833
|
+
function buildAnswerSnapshot(config, answers) {
|
|
9834
|
+
const items = [];
|
|
9835
|
+
for (const step of config.steps) {
|
|
9836
|
+
for (const item of stepAnswerVariables(step)) {
|
|
9837
|
+
const value = answerValue(answers, item.variable);
|
|
9838
|
+
if (value === void 0) continue;
|
|
9839
|
+
items.push({
|
|
9840
|
+
stepId: item.stepId,
|
|
9841
|
+
variable: item.variable,
|
|
9842
|
+
label: item.label,
|
|
9843
|
+
value
|
|
9844
|
+
});
|
|
9845
|
+
}
|
|
9846
|
+
}
|
|
9847
|
+
return { items };
|
|
9848
|
+
}
|
|
9849
|
+
function buildRespondentSnapshot(config, answers) {
|
|
9850
|
+
var _a, _b;
|
|
9851
|
+
const snapshot = {};
|
|
9852
|
+
for (const step of config.steps) {
|
|
9853
|
+
if (step.type !== "input") continue;
|
|
9854
|
+
for (const field of (_a = step.fields) != null ? _a : []) {
|
|
9855
|
+
const variable = (_b = field.variable) != null ? _b : field.id;
|
|
9856
|
+
const value = answerValue(answers, variable);
|
|
9857
|
+
if (value !== void 0) snapshot[variable] = value;
|
|
9858
|
+
}
|
|
9859
|
+
}
|
|
9860
|
+
return snapshot;
|
|
9861
|
+
}
|
|
9862
|
+
function buildContactRef(config, answers) {
|
|
9863
|
+
var _a, _b;
|
|
9864
|
+
const contact = {};
|
|
9865
|
+
for (const step of config.steps) {
|
|
9866
|
+
if (step.type !== "input") continue;
|
|
9867
|
+
for (const field of (_a = step.fields) != null ? _a : []) {
|
|
9868
|
+
const variable = (_b = field.variable) != null ? _b : field.id;
|
|
9869
|
+
const value = answerValue(answers, variable);
|
|
9870
|
+
if (typeof value !== "string" || !value.trim()) continue;
|
|
9871
|
+
const normalizedVariable = variable.toLowerCase().replace(/[-_\s]/g, "");
|
|
9872
|
+
if (field.type === "email" || normalizedVariable.includes("email")) {
|
|
9873
|
+
contact.email = value;
|
|
9874
|
+
} else if (field.type === "tel" || normalizedVariable.includes("phone")) {
|
|
9875
|
+
contact.phone = value;
|
|
9876
|
+
} else if (normalizedVariable === "externalid") {
|
|
9877
|
+
contact.externalId = value;
|
|
9878
|
+
}
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
return Object.keys(contact).length > 0 ? contact : void 0;
|
|
9882
|
+
}
|
|
9883
|
+
function eventStepId(event) {
|
|
9884
|
+
if ("step" in event) return event.step.id;
|
|
9885
|
+
if (event.type === "navigate") return event.to.id;
|
|
9886
|
+
if (event.type === "purchase_intent") return event.stepId;
|
|
9887
|
+
return void 0;
|
|
9888
|
+
}
|
|
9889
|
+
function eventProperties(event) {
|
|
9890
|
+
if (event.type === "step_submit") {
|
|
9891
|
+
return { submitted: event.submitted };
|
|
9892
|
+
}
|
|
9893
|
+
if (event.type === "navigate") {
|
|
9894
|
+
return {
|
|
9895
|
+
fromStepId: event.from.id,
|
|
9896
|
+
toStepId: event.to.id,
|
|
9897
|
+
direction: event.direction
|
|
9898
|
+
};
|
|
9899
|
+
}
|
|
9900
|
+
if (event.type === "purchase_intent") {
|
|
9901
|
+
return __spreadValues({
|
|
9902
|
+
variable: event.variable,
|
|
9903
|
+
plan: event.plan
|
|
9904
|
+
}, event.discount ? { discount: event.discount } : {});
|
|
9905
|
+
}
|
|
9906
|
+
return {};
|
|
9907
|
+
}
|
|
9908
|
+
function toCaptureEvent(config, event, sequence, runtime) {
|
|
9909
|
+
const answers = "answers" in event ? event.answers : {};
|
|
9910
|
+
const payload = {
|
|
9911
|
+
id: createCaptureEventId(runtime, event, sequence),
|
|
9912
|
+
type: event.type,
|
|
9913
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9914
|
+
stepId: eventStepId(event),
|
|
9915
|
+
properties: eventProperties(event),
|
|
9916
|
+
answers: toJsonRecord(answers),
|
|
9917
|
+
computedVariables: "computedVariables" in event ? toJsonRecord(event.computedVariables) : void 0
|
|
9918
|
+
};
|
|
9919
|
+
if (event.type === "complete") {
|
|
9920
|
+
payload.respondentSnapshot = buildRespondentSnapshot(config, answers);
|
|
9921
|
+
payload.answerSnapshot = buildAnswerSnapshot(config, answers);
|
|
9922
|
+
payload.contact = buildContactRef(config, answers);
|
|
9923
|
+
}
|
|
9924
|
+
return payload;
|
|
9925
|
+
}
|
|
9926
|
+
function createCaptureEventId(runtime, event, sequence) {
|
|
9927
|
+
return `${runtime.clientSessionId}:${event.type}:${sequence.toString(
|
|
9928
|
+
36
|
|
9929
|
+
)}:${randomId("fhqe")}`;
|
|
9930
|
+
}
|
|
9931
|
+
function useJourneyCapture(params) {
|
|
9932
|
+
const onEventRef = React.useRef(params.onEvent);
|
|
9933
|
+
const configRef = React.useRef(params.config);
|
|
9934
|
+
const captureRef = React.useRef(params.capture);
|
|
9935
|
+
const runtimeRef = React.useRef(null);
|
|
9936
|
+
const queueRef = React.useRef([]);
|
|
9937
|
+
const sequenceRef = React.useRef(0);
|
|
9938
|
+
const flushingRef = React.useRef(false);
|
|
9939
|
+
onEventRef.current = params.onEvent;
|
|
9940
|
+
configRef.current = params.config;
|
|
9941
|
+
captureRef.current = params.capture;
|
|
9942
|
+
const persistQueue = React.useCallback(() => {
|
|
9943
|
+
const runtime = runtimeRef.current;
|
|
9944
|
+
if (!runtime || typeof window === "undefined") return;
|
|
9945
|
+
try {
|
|
9946
|
+
sessionStorage.setItem(
|
|
9947
|
+
runtime.queueKey,
|
|
9948
|
+
JSON.stringify(queueRef.current)
|
|
9949
|
+
);
|
|
9950
|
+
} catch (e) {
|
|
9951
|
+
}
|
|
9952
|
+
}, []);
|
|
9953
|
+
const ensureRuntime = React.useCallback((capture) => {
|
|
9954
|
+
if (typeof window === "undefined") return null;
|
|
9955
|
+
const existing = runtimeRef.current;
|
|
9956
|
+
if ((existing == null ? void 0 : existing.journeyId) === capture.journeyId) return existing;
|
|
9957
|
+
const visitorId = getStoredId(
|
|
9958
|
+
localStorage,
|
|
9959
|
+
"fhq_journey_visitor_id",
|
|
9960
|
+
"fhqv"
|
|
9961
|
+
);
|
|
9962
|
+
const clientSessionId = getStoredId(
|
|
9963
|
+
sessionStorage,
|
|
9964
|
+
`fhq_journey_session:${capture.journeyId}`,
|
|
9965
|
+
"fhqs"
|
|
9966
|
+
);
|
|
9967
|
+
const queueKey = `fhq_journey_queue:${capture.journeyId}:${clientSessionId}`;
|
|
9968
|
+
const runtime = {
|
|
9969
|
+
journeyId: capture.journeyId,
|
|
9970
|
+
visitorId,
|
|
9971
|
+
clientSessionId,
|
|
9972
|
+
queueKey
|
|
9973
|
+
};
|
|
9974
|
+
runtimeRef.current = runtime;
|
|
9975
|
+
queueRef.current = readJson(
|
|
9976
|
+
sessionStorage,
|
|
9977
|
+
queueKey,
|
|
9978
|
+
[]
|
|
9979
|
+
);
|
|
9980
|
+
return runtime;
|
|
9981
|
+
}, []);
|
|
9982
|
+
const flush = React.useCallback(async () => {
|
|
9983
|
+
var _a, _b, _c;
|
|
9984
|
+
const capture = captureRef.current;
|
|
9985
|
+
if (!capture || !configRef.current || flushingRef.current) return;
|
|
9986
|
+
const runtime = ensureRuntime(capture);
|
|
9987
|
+
if (!runtime || queueRef.current.length === 0) return;
|
|
9988
|
+
flushingRef.current = true;
|
|
9989
|
+
const batchSize = Math.max(1, (_a = capture.batchSize) != null ? _a : 10);
|
|
9990
|
+
const maxRetries = Math.max(1, (_b = capture.maxRetries) != null ? _b : 3);
|
|
9991
|
+
const batch = queueRef.current.slice(0, batchSize);
|
|
9992
|
+
const baseUrl = (_c = capture.baseUrl) != null ? _c : "https://getfounderhq.com";
|
|
9993
|
+
try {
|
|
9994
|
+
const response = await fetch(
|
|
9995
|
+
`${baseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
9996
|
+
capture.journeyId
|
|
9997
|
+
)}/capture`,
|
|
9998
|
+
{
|
|
9999
|
+
method: "POST",
|
|
10000
|
+
headers: {
|
|
10001
|
+
Authorization: `Bearer ${capture.apiKey}`,
|
|
10002
|
+
"Content-Type": "application/json"
|
|
10003
|
+
},
|
|
10004
|
+
body: JSON.stringify({
|
|
10005
|
+
clientSessionId: runtime.clientSessionId,
|
|
10006
|
+
visitorId: runtime.visitorId,
|
|
10007
|
+
context: captureContext(capture, runtime),
|
|
10008
|
+
events: batch.map((item) => item.event)
|
|
10009
|
+
}),
|
|
10010
|
+
keepalive: batch.length <= 5
|
|
10011
|
+
}
|
|
10012
|
+
);
|
|
10013
|
+
if (!response.ok) throw new Error(`Capture failed: ${response.status}`);
|
|
10014
|
+
queueRef.current = queueRef.current.slice(batch.length);
|
|
10015
|
+
} catch (e) {
|
|
10016
|
+
const failedIds = new Set(batch.map((item) => item.event.id));
|
|
10017
|
+
queueRef.current = queueRef.current.map(
|
|
10018
|
+
(item) => failedIds.has(item.event.id) ? __spreadProps(__spreadValues({}, item), { attempts: item.attempts + 1 }) : item
|
|
10019
|
+
).filter((item) => item.attempts < maxRetries);
|
|
10020
|
+
} finally {
|
|
10021
|
+
persistQueue();
|
|
10022
|
+
flushingRef.current = false;
|
|
10023
|
+
}
|
|
10024
|
+
}, [ensureRuntime, persistQueue]);
|
|
10025
|
+
React.useEffect(() => {
|
|
10026
|
+
var _a;
|
|
10027
|
+
const capture = captureRef.current;
|
|
10028
|
+
if (!capture || typeof window === "undefined") return;
|
|
10029
|
+
ensureRuntime(capture);
|
|
10030
|
+
const interval = window.setInterval(
|
|
10031
|
+
() => void flush(),
|
|
10032
|
+
Math.max(1e3, (_a = capture.flushIntervalMs) != null ? _a : 3e3)
|
|
10033
|
+
);
|
|
10034
|
+
const handleVisibility = () => {
|
|
10035
|
+
if (document.visibilityState === "hidden") void flush();
|
|
10036
|
+
};
|
|
10037
|
+
window.addEventListener("beforeunload", persistQueue);
|
|
10038
|
+
document.addEventListener("visibilitychange", handleVisibility);
|
|
10039
|
+
void flush();
|
|
10040
|
+
return () => {
|
|
10041
|
+
window.clearInterval(interval);
|
|
10042
|
+
window.removeEventListener("beforeunload", persistQueue);
|
|
10043
|
+
document.removeEventListener("visibilitychange", handleVisibility);
|
|
10044
|
+
persistQueue();
|
|
10045
|
+
};
|
|
10046
|
+
}, [ensureRuntime, flush, persistQueue, params.capture]);
|
|
10047
|
+
return React.useCallback(
|
|
10048
|
+
(event) => {
|
|
10049
|
+
var _a, _b;
|
|
10050
|
+
(_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, event);
|
|
10051
|
+
const capture = captureRef.current;
|
|
10052
|
+
const config = configRef.current;
|
|
10053
|
+
if (!capture || !config) return;
|
|
10054
|
+
const runtime = ensureRuntime(capture);
|
|
10055
|
+
if (!runtime) return;
|
|
10056
|
+
sequenceRef.current += 1;
|
|
10057
|
+
queueRef.current.push({
|
|
10058
|
+
event: toCaptureEvent(config, event, sequenceRef.current, runtime),
|
|
10059
|
+
attempts: 0
|
|
10060
|
+
});
|
|
10061
|
+
persistQueue();
|
|
10062
|
+
const batchSize = Math.max(1, (_b = capture.batchSize) != null ? _b : 10);
|
|
10063
|
+
if (queueRef.current.length >= batchSize) void flush();
|
|
10064
|
+
},
|
|
10065
|
+
[ensureRuntime, flush, persistQueue]
|
|
10066
|
+
);
|
|
10067
|
+
}
|
|
10068
|
+
var FOUNDERHQ_BASE_URL = "https://getfounderhq.com";
|
|
10069
|
+
function resolveJourneyBaseUrl(baseUrl) {
|
|
10070
|
+
if (!baseUrl) return FOUNDERHQ_BASE_URL;
|
|
10071
|
+
try {
|
|
10072
|
+
const parsed = new URL(baseUrl);
|
|
10073
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
10074
|
+
const isLocal = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
10075
|
+
return isLocal ? parsed.origin : FOUNDERHQ_BASE_URL;
|
|
10076
|
+
} catch (e) {
|
|
10077
|
+
return FOUNDERHQ_BASE_URL;
|
|
10078
|
+
}
|
|
10079
|
+
}
|
|
9637
10080
|
function useJourneyConfig({
|
|
9638
10081
|
apiKey,
|
|
9639
10082
|
journeyId,
|
|
9640
|
-
baseUrl
|
|
10083
|
+
baseUrl,
|
|
10084
|
+
config,
|
|
10085
|
+
capture = true
|
|
9641
10086
|
}) {
|
|
9642
10087
|
const [state, setState] = React.useState({ status: "loading" });
|
|
9643
10088
|
React.useEffect(() => {
|
|
9644
10089
|
let cancelled = false;
|
|
9645
|
-
|
|
9646
|
-
|
|
10090
|
+
const resolvedBaseUrl = resolveJourneyBaseUrl(baseUrl);
|
|
10091
|
+
async function loadConfig() {
|
|
10092
|
+
var _a, _b;
|
|
9647
10093
|
setState({ status: "loading" });
|
|
9648
10094
|
try {
|
|
9649
|
-
const
|
|
10095
|
+
const validateUrl = `${resolvedBaseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
10096
|
+
journeyId
|
|
10097
|
+
)}/validate`;
|
|
10098
|
+
const validateRes = await fetch(validateUrl, {
|
|
10099
|
+
method: "POST",
|
|
10100
|
+
headers: {
|
|
10101
|
+
Authorization: `Bearer ${apiKey}`,
|
|
10102
|
+
"Content-Type": "application/json"
|
|
10103
|
+
},
|
|
10104
|
+
body: JSON.stringify({ capture })
|
|
10105
|
+
});
|
|
10106
|
+
if (!validateRes.ok) {
|
|
10107
|
+
const body = await validateRes.json().catch(() => ({}));
|
|
10108
|
+
throw new Error(
|
|
10109
|
+
(_a = body.error) != null ? _a : `Journey is unavailable (HTTP ${validateRes.status})`
|
|
10110
|
+
);
|
|
10111
|
+
}
|
|
10112
|
+
if (config) {
|
|
10113
|
+
if (!cancelled) {
|
|
10114
|
+
setState({ status: "ready", config });
|
|
10115
|
+
}
|
|
10116
|
+
return;
|
|
10117
|
+
}
|
|
10118
|
+
const url = `${resolvedBaseUrl}/api/v1/journeys/${encodeURIComponent(
|
|
10119
|
+
journeyId
|
|
10120
|
+
)}`;
|
|
9650
10121
|
const res = await fetch(url, {
|
|
9651
10122
|
headers: {
|
|
9652
10123
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -9656,7 +10127,7 @@ function useJourneyConfig({
|
|
|
9656
10127
|
if (!res.ok) {
|
|
9657
10128
|
const body = await res.json().catch(() => ({}));
|
|
9658
10129
|
throw new Error(
|
|
9659
|
-
(
|
|
10130
|
+
(_b = body.error) != null ? _b : `Failed to fetch journey config (HTTP ${res.status})`
|
|
9660
10131
|
);
|
|
9661
10132
|
}
|
|
9662
10133
|
const data = await res.json();
|
|
@@ -9672,11 +10143,11 @@ function useJourneyConfig({
|
|
|
9672
10143
|
}
|
|
9673
10144
|
}
|
|
9674
10145
|
}
|
|
9675
|
-
|
|
10146
|
+
loadConfig();
|
|
9676
10147
|
return () => {
|
|
9677
10148
|
cancelled = true;
|
|
9678
10149
|
};
|
|
9679
|
-
}, [apiKey, journeyId, baseUrl]);
|
|
10150
|
+
}, [apiKey, journeyId, baseUrl, config, capture]);
|
|
9680
10151
|
return state;
|
|
9681
10152
|
}
|
|
9682
10153
|
function DefaultLoading() {
|
|
@@ -9725,15 +10196,16 @@ function DefaultError({ error }) {
|
|
|
9725
10196
|
textAlign: "center"
|
|
9726
10197
|
},
|
|
9727
10198
|
children: [
|
|
9728
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "1.125rem", fontWeight: 600 }, children: "
|
|
10199
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "1.125rem", fontWeight: 600 }, children: "This Journey is unavailable" }),
|
|
9729
10200
|
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.875rem", marginTop: "0.5rem", opacity: 0.7 }, children: error.message })
|
|
9730
10201
|
]
|
|
9731
10202
|
}
|
|
9732
10203
|
);
|
|
9733
10204
|
}
|
|
9734
|
-
function
|
|
10205
|
+
function Journey({
|
|
9735
10206
|
apiKey,
|
|
9736
10207
|
journeyId,
|
|
10208
|
+
config,
|
|
9737
10209
|
baseUrl,
|
|
9738
10210
|
storageKey,
|
|
9739
10211
|
onEvent,
|
|
@@ -9743,9 +10215,28 @@ function JourneyRemote({
|
|
|
9743
10215
|
initialOptions,
|
|
9744
10216
|
onDiscountCodeApply,
|
|
9745
10217
|
loadingComponent,
|
|
9746
|
-
errorComponent
|
|
10218
|
+
errorComponent,
|
|
10219
|
+
capture
|
|
9747
10220
|
}) {
|
|
9748
|
-
const
|
|
10221
|
+
const resolvedBaseUrl = resolveJourneyBaseUrl(baseUrl);
|
|
10222
|
+
const captureEnabled = capture !== false;
|
|
10223
|
+
const state = useJourneyConfig({
|
|
10224
|
+
apiKey,
|
|
10225
|
+
journeyId,
|
|
10226
|
+
baseUrl: resolvedBaseUrl,
|
|
10227
|
+
config,
|
|
10228
|
+
capture: captureEnabled
|
|
10229
|
+
});
|
|
10230
|
+
const captureOption = capture === false ? false : __spreadValues({
|
|
10231
|
+
apiKey,
|
|
10232
|
+
journeyId,
|
|
10233
|
+
baseUrl: resolvedBaseUrl
|
|
10234
|
+
}, capture != null ? capture : {});
|
|
10235
|
+
const capturedOnEvent = useJourneyCapture({
|
|
10236
|
+
config: state.status === "ready" ? state.config : null,
|
|
10237
|
+
capture: captureOption,
|
|
10238
|
+
onEvent
|
|
10239
|
+
});
|
|
9749
10240
|
if (state.status === "loading") {
|
|
9750
10241
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loadingComponent != null ? loadingComponent : /* @__PURE__ */ jsxRuntime.jsx(DefaultLoading, {}) });
|
|
9751
10242
|
}
|
|
@@ -9758,35 +10249,7 @@ function JourneyRemote({
|
|
|
9758
10249
|
config: state.config,
|
|
9759
10250
|
storageKey,
|
|
9760
10251
|
theme,
|
|
9761
|
-
onEvent,
|
|
9762
|
-
initialAnswers,
|
|
9763
|
-
initialOptions,
|
|
9764
|
-
onDiscountCodeApply,
|
|
9765
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(JourneyShell, { className, theme })
|
|
9766
|
-
}
|
|
9767
|
-
);
|
|
9768
|
-
}
|
|
9769
|
-
function Journey(props) {
|
|
9770
|
-
if ("apiKey" in props && props.apiKey) {
|
|
9771
|
-
return /* @__PURE__ */ jsxRuntime.jsx(JourneyRemote, __spreadValues({}, props));
|
|
9772
|
-
}
|
|
9773
|
-
const {
|
|
9774
|
-
config,
|
|
9775
|
-
storageKey,
|
|
9776
|
-
onEvent,
|
|
9777
|
-
className,
|
|
9778
|
-
theme,
|
|
9779
|
-
initialAnswers,
|
|
9780
|
-
initialOptions,
|
|
9781
|
-
onDiscountCodeApply
|
|
9782
|
-
} = props;
|
|
9783
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9784
|
-
JourneyProvider,
|
|
9785
|
-
{
|
|
9786
|
-
config,
|
|
9787
|
-
storageKey,
|
|
9788
|
-
theme,
|
|
9789
|
-
onEvent,
|
|
10252
|
+
onEvent: capturedOnEvent,
|
|
9790
10253
|
initialAnswers,
|
|
9791
10254
|
initialOptions,
|
|
9792
10255
|
onDiscountCodeApply,
|