@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/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
- for (const rule of routing.conditions) {
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 > 10 ? rawExitDuration / 1e3 : rawExitDuration;
7948
- const exitDelay = rawExitDelay > 10 ? rawExitDelay / 1e3 : 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 > 10 ? rawDuration / 1e3 : rawDuration;
7953
- const delay = rawDelay > 10 ? rawDelay / 1e3 : 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 > 1e4 ? rawAutoHide / 1e3 : rawAutoHide : null;
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 = "http://localhost:3002"
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
- async function fetchConfig() {
9646
- var _a;
10090
+ const resolvedBaseUrl = resolveJourneyBaseUrl(baseUrl);
10091
+ async function loadConfig() {
10092
+ var _a, _b;
9647
10093
  setState({ status: "loading" });
9648
10094
  try {
9649
- const url = `${baseUrl}/api/v1/journeys/${encodeURIComponent(journeyId)}`;
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
- (_a = body.error) != null ? _a : `Failed to fetch journey config (HTTP ${res.status})`
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
- fetchConfig();
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: "Failed to load journey" }),
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 JourneyRemote({
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 state = useJourneyConfig({ apiKey, journeyId, baseUrl });
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,