@founderhq/journeys 0.3.61 → 0.3.62

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 CHANGED
@@ -63,9 +63,28 @@ In the journey config, reference the injected value with a whole-value template:
63
63
 
64
64
  Keys declared in `computedVariables` are ignored in `initialAnswers`; computed values always come from their formulas.
65
65
 
66
+ ## Injecting Runtime Options (`initialOptions`)
67
+
68
+ Pass `initialOptions` to override option lists at render time without storing large dynamic lists in the journey config. Keys are answer keys: top-level steps use `step.variable ?? step.id`, and `single_select` / `multi_select` blocks use `props.variable`.
69
+
70
+ ```tsx
71
+ <Journey
72
+ apiKey="fos_..."
73
+ journeyId="abc-123"
74
+ initialOptions={{
75
+ country: countries.map((country) => ({
76
+ id: country.code,
77
+ label: country.name,
78
+ })),
79
+ }}
80
+ />
81
+ ```
82
+
83
+ When a key is present in `initialOptions`, that list is used even if it is empty. When no key is present, the inspector-configured options remain the fallback.
84
+
66
85
  ## Event Payloads
67
86
 
68
- `onEvent` payloads keep user-provided answers and computed values separate:
87
+ `onEvent` payloads keep user-provided answers and computed values separate. `step_submit` and `navigate` events include the rendered step config, but option arrays are omitted from those event configs so large `initialOptions` lists are not copied into every payload.
69
88
 
70
89
  ```ts
71
90
  onEvent={(event) => {
package/dist/index.cjs CHANGED
@@ -537,6 +537,131 @@ function normalizeStepOrder(config) {
537
537
  }
538
538
  return __spreadProps(__spreadValues({}, config), { steps: ordered });
539
539
  }
540
+ function getRuntimeOptionOverride(overrides, key) {
541
+ if (!overrides || !key) return void 0;
542
+ if (!Object.prototype.hasOwnProperty.call(overrides, key)) return void 0;
543
+ return overrides[key];
544
+ }
545
+ function applyRuntimeOptionOverridesToBlocks(blocks, overrides) {
546
+ if (!overrides) return blocks;
547
+ let changed = false;
548
+ const nextBlocks = blocks.map((block) => {
549
+ let next = block;
550
+ if (block.type === "single_select" || block.type === "multi_select") {
551
+ const options = getRuntimeOptionOverride(overrides, block.props.variable);
552
+ if (options !== void 0) {
553
+ next = __spreadProps(__spreadValues({}, block), {
554
+ props: __spreadProps(__spreadValues({}, block.props), { options })
555
+ });
556
+ changed = true;
557
+ }
558
+ }
559
+ if (block.type === "columns") {
560
+ let columnsChanged = false;
561
+ const columns = block.props.columns.map((column) => {
562
+ const columnBlocks = applyRuntimeOptionOverridesToBlocks(
563
+ column.blocks,
564
+ overrides
565
+ );
566
+ if (columnBlocks === column.blocks) return column;
567
+ columnsChanged = true;
568
+ return __spreadProps(__spreadValues({}, column), { blocks: columnBlocks });
569
+ });
570
+ if (columnsChanged) {
571
+ next = __spreadProps(__spreadValues({}, block), {
572
+ props: __spreadProps(__spreadValues({}, block.props), { columns })
573
+ });
574
+ changed = true;
575
+ }
576
+ }
577
+ return next;
578
+ });
579
+ return changed ? nextBlocks : blocks;
580
+ }
581
+ function applyRuntimeOptionOverrides(config, overrides) {
582
+ if (!overrides || Object.keys(overrides).length === 0) return config;
583
+ let changed = false;
584
+ const steps = config.steps.map((step) => {
585
+ var _a;
586
+ let next = step;
587
+ const stepUsesOptions = step.type === "single_select" || step.type === "multi_select" || step.type === "counter_select";
588
+ const options = stepUsesOptions ? getRuntimeOptionOverride(overrides, (_a = step.variable) != null ? _a : step.id) : void 0;
589
+ if (options !== void 0) {
590
+ next = __spreadProps(__spreadValues({}, step), { options });
591
+ changed = true;
592
+ }
593
+ if (step.blocks) {
594
+ const blocks = applyRuntimeOptionOverridesToBlocks(
595
+ step.blocks,
596
+ overrides
597
+ );
598
+ if (blocks !== step.blocks) {
599
+ next = __spreadProps(__spreadValues({}, next), { blocks });
600
+ changed = true;
601
+ }
602
+ }
603
+ return next;
604
+ });
605
+ return changed ? __spreadProps(__spreadValues({}, config), { steps }) : config;
606
+ }
607
+ function stripOptionsFromEventBlocks(blocks) {
608
+ let changed = false;
609
+ const nextBlocks = blocks.map((block) => {
610
+ let next = block;
611
+ if (block.type === "single_select" || block.type === "multi_select") {
612
+ const props = __spreadValues({}, block.props);
613
+ delete props.options;
614
+ next = __spreadProps(__spreadValues({}, block), { props });
615
+ changed = true;
616
+ }
617
+ if (block.type === "columns") {
618
+ let columnsChanged = false;
619
+ const columns = block.props.columns.map((column) => {
620
+ const columnBlocks = stripOptionsFromEventBlocks(column.blocks);
621
+ if (columnBlocks === column.blocks) return column;
622
+ columnsChanged = true;
623
+ return __spreadProps(__spreadValues({}, column), { blocks: columnBlocks });
624
+ });
625
+ if (columnsChanged) {
626
+ next = __spreadProps(__spreadValues({}, block), {
627
+ props: __spreadProps(__spreadValues({}, block.props), { columns })
628
+ });
629
+ changed = true;
630
+ }
631
+ }
632
+ return next;
633
+ });
634
+ return changed ? nextBlocks : blocks;
635
+ }
636
+ function stripOptionsFromEventStep(step) {
637
+ var _a;
638
+ let changed = false;
639
+ let next = step;
640
+ if (step.options !== void 0) {
641
+ next = __spreadValues({}, next);
642
+ delete next.options;
643
+ changed = true;
644
+ }
645
+ if ((_a = step.fields) == null ? void 0 : _a.some((field) => field.options !== void 0)) {
646
+ next = __spreadProps(__spreadValues({}, next), {
647
+ fields: step.fields.map((field) => {
648
+ if (field.options === void 0) return field;
649
+ const nextField = __spreadValues({}, field);
650
+ delete nextField.options;
651
+ return nextField;
652
+ })
653
+ });
654
+ changed = true;
655
+ }
656
+ if (step.blocks) {
657
+ const blocks = stripOptionsFromEventBlocks(step.blocks);
658
+ if (blocks !== step.blocks) {
659
+ next = __spreadProps(__spreadValues({}, next), { blocks });
660
+ changed = true;
661
+ }
662
+ }
663
+ return changed ? next : step;
664
+ }
540
665
  var JourneyContext = React.createContext(null);
541
666
  function JourneyProvider({
542
667
  config: rawConfig,
@@ -544,6 +669,7 @@ function JourneyProvider({
544
669
  theme,
545
670
  onEvent,
546
671
  initialAnswers,
672
+ initialOptions,
547
673
  onDiscountCodeApply,
548
674
  children
549
675
  }) {
@@ -554,7 +680,14 @@ function JourneyProvider({
554
680
  React.useEffect(() => {
555
681
  onEventRef.current = onEvent;
556
682
  }, [onEvent]);
557
- const config = React.useMemo(() => normalizeStepOrder(rawConfig), [rawConfig]);
683
+ const normalizedConfig = React.useMemo(
684
+ () => normalizeStepOrder(rawConfig),
685
+ [rawConfig]
686
+ );
687
+ const config = React.useMemo(
688
+ () => applyRuntimeOptionOverrides(normalizedConfig, initialOptions),
689
+ [normalizedConfig, initialOptions]
690
+ );
558
691
  const computedVariableIds = React.useMemo(
559
692
  () => getComputedVariableIds(config.computedVariables),
560
693
  [config.computedVariables]
@@ -599,8 +732,8 @@ function JourneyProvider({
599
732
  rawAnswersRef.current = rawAnswers;
600
733
  }, [rawAnswers]);
601
734
  const allVariables = React.useMemo(
602
- () => config.steps.flatMap(getStepVariables),
603
- [config.steps]
735
+ () => normalizedConfig.steps.flatMap(getStepVariables),
736
+ [normalizedConfig.steps]
604
737
  );
605
738
  const setAnswer = React.useCallback((stepId, answer) => {
606
739
  if (computedVariableIds.has(stepId)) return;
@@ -669,7 +802,7 @@ function JourneyProvider({
669
802
  const submitted = getSubmittedAnswer(currentStep, latestAnswers);
670
803
  (_c = onEventRef.current) == null ? void 0 : _c.call(onEventRef, __spreadProps(__spreadValues({
671
804
  type: "step_submit",
672
- step: currentStep,
805
+ step: stripOptionsFromEventStep(currentStep),
673
806
  submitted
674
807
  }, eventAnswers), {
675
808
  variables: allVariables
@@ -679,8 +812,8 @@ function JourneyProvider({
679
812
  navigateToIndex(nextIndex);
680
813
  (_d = onEventRef.current) == null ? void 0 : _d.call(onEventRef, __spreadValues({
681
814
  type: "navigate",
682
- from: currentStep,
683
- to: config.steps[nextIndex],
815
+ from: stripOptionsFromEventStep(currentStep),
816
+ to: stripOptionsFromEventStep(config.steps[nextIndex]),
684
817
  direction: "forward"
685
818
  }, eventAnswers));
686
819
  } else {
@@ -712,13 +845,18 @@ function JourneyProvider({
712
845
  navigateToIndex(idx);
713
846
  (_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, __spreadValues({
714
847
  type: "navigate",
715
- from: fromStep,
716
- to: config.steps[idx],
848
+ from: stripOptionsFromEventStep(fromStep),
849
+ to: stripOptionsFromEventStep(config.steps[idx]),
717
850
  direction: idx > currentStepIndex ? "forward" : "backward"
718
851
  }, getEventAnswers(config.computedVariables, rawAnswersRef.current)));
719
852
  }
720
853
  },
721
- [config.computedVariables, config.steps, currentStepIndex, navigateToIndex]
854
+ [
855
+ config.computedVariables,
856
+ config.steps,
857
+ currentStepIndex,
858
+ navigateToIndex
859
+ ]
722
860
  );
723
861
  const goBack = React.useCallback(() => {
724
862
  var _a;
@@ -754,8 +892,8 @@ function JourneyProvider({
754
892
  setDirection(dir);
755
893
  (_b2 = onEventRef.current) == null ? void 0 : _b2.call(onEventRef, __spreadValues({
756
894
  type: "navigate",
757
- from: config.steps[prev],
758
- to: config.steps[step],
895
+ from: stripOptionsFromEventStep(config.steps[prev]),
896
+ to: stripOptionsFromEventStep(config.steps[step]),
759
897
  direction: dir
760
898
  }, getEventAnswers(config.computedVariables, rawAnswersRef.current)));
761
899
  return step;
@@ -763,7 +901,11 @@ function JourneyProvider({
763
901
  };
764
902
  window.addEventListener("popstate", handlePopState);
765
903
  return () => window.removeEventListener("popstate", handlePopState);
766
- }, [config.allowBackNavigation, config.computedVariables, config.steps]);
904
+ }, [
905
+ config.allowBackNavigation,
906
+ config.computedVariables,
907
+ config.steps
908
+ ]);
767
909
  const getStepAnswer = React.useCallback(
768
910
  (stepId) => {
769
911
  var _a;
@@ -783,17 +925,22 @@ function JourneyProvider({
783
925
  );
784
926
  const firePurchaseIntent = React.useCallback(
785
927
  (variable, plan, discount) => {
786
- var _a, _b;
787
- const currentStep = config.steps[currentStepIndex];
788
- (_b = onEventRef.current) == null ? void 0 : _b.call(onEventRef, __spreadValues(__spreadProps(__spreadValues({
928
+ var _a, _b, _c;
929
+ const currentStep = (_a = normalizedConfig.steps[currentStepIndex]) != null ? _a : config.steps[currentStepIndex];
930
+ (_c = onEventRef.current) == null ? void 0 : _c.call(onEventRef, __spreadValues(__spreadProps(__spreadValues({
789
931
  type: "purchase_intent",
790
932
  variable,
791
933
  plan
792
934
  }, discount ? { discount } : {}), {
793
- stepId: (_a = currentStep == null ? void 0 : currentStep.id) != null ? _a : ""
935
+ stepId: (_b = currentStep == null ? void 0 : currentStep.id) != null ? _b : ""
794
936
  }), getEventAnswers(config.computedVariables, rawAnswersRef.current)));
795
937
  },
796
- [config.computedVariables, config.steps, currentStepIndex]
938
+ [
939
+ config.computedVariables,
940
+ config.steps,
941
+ currentStepIndex,
942
+ normalizedConfig.steps
943
+ ]
797
944
  );
798
945
  const openDiscountCodeDialog = React.useCallback(
799
946
  (state2) => {
@@ -9301,6 +9448,7 @@ function JourneyRemote({
9301
9448
  className,
9302
9449
  theme,
9303
9450
  initialAnswers,
9451
+ initialOptions,
9304
9452
  onDiscountCodeApply,
9305
9453
  loadingComponent,
9306
9454
  errorComponent
@@ -9320,6 +9468,7 @@ function JourneyRemote({
9320
9468
  theme,
9321
9469
  onEvent,
9322
9470
  initialAnswers,
9471
+ initialOptions,
9323
9472
  onDiscountCodeApply,
9324
9473
  children: /* @__PURE__ */ jsxRuntime.jsx(JourneyShell, { className, theme })
9325
9474
  }
@@ -9336,6 +9485,7 @@ function Journey(props) {
9336
9485
  className,
9337
9486
  theme,
9338
9487
  initialAnswers,
9488
+ initialOptions,
9339
9489
  onDiscountCodeApply
9340
9490
  } = props;
9341
9491
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -9346,6 +9496,7 @@ function Journey(props) {
9346
9496
  theme,
9347
9497
  onEvent,
9348
9498
  initialAnswers,
9499
+ initialOptions,
9349
9500
  onDiscountCodeApply,
9350
9501
  children: /* @__PURE__ */ jsxRuntime.jsx(JourneyShell, { className, theme })
9351
9502
  }
package/dist/index.d.cts CHANGED
@@ -11,6 +11,7 @@ type StepOption = {
11
11
  /** Optional image URL used in gravity bin physics visualization */
12
12
  image?: string;
13
13
  };
14
+ type JourneyOptionOverrides = Record<string, StepOption[]>;
14
15
  type SwipeCardItem = {
15
16
  variable: string;
16
17
  text: string;
@@ -1257,6 +1258,8 @@ type JourneyCommonProps = {
1257
1258
  theme?: string;
1258
1259
  /** Seed answers available at first render. Useful for runtime-injected data (e.g. live pricing plans referenced via `"${pricingPlans}"` templates). Keys declared in `computedVariables` are ignored because formulas own those values. */
1259
1260
  initialAnswers?: JourneyAnswers;
1261
+ /** Runtime option lists keyed by answer key (`step.variable ?? step.id`, or select block `props.variable`). Values override inspector-configured options for rendering only. */
1262
+ initialOptions?: JourneyOptionOverrides;
1260
1263
  /** Consumer-owned discount validation/repricing callback. Journeys only handles UI/state. */
1261
1264
  onDiscountCodeApply?: (request: DiscountCodeApplyRequest) => Promise<DiscountCodeApplyResult> | DiscountCodeApplyResult;
1262
1265
  /** Custom loading component shown while fetching config */
@@ -1343,11 +1346,15 @@ type JourneyProviderProps = {
1343
1346
  * `computedVariables` are ignored here because formulas own those values. Refreshed prices
1344
1347
  * always win over a stale snapshot in storage. */
1345
1348
  initialAnswers?: JourneyAnswers;
1349
+ /** Runtime option lists keyed by answer key (`step.variable ?? step.id`, or select block
1350
+ * `props.variable`). These override editor-configured options in the rendered journey only;
1351
+ * they are not persisted in answers. Event step configs omit option arrays. */
1352
+ initialOptions?: JourneyOptionOverrides;
1346
1353
  /** Consumer-owned discount validation/repricing callback. Journeys only handles UI/state. */
1347
1354
  onDiscountCodeApply?: (request: DiscountCodeApplyRequest) => Promise<DiscountCodeApplyResult> | DiscountCodeApplyResult;
1348
1355
  children: ReactNode;
1349
1356
  };
1350
- declare function JourneyProvider({ config: rawConfig, storageKey, theme, onEvent, initialAnswers, onDiscountCodeApply, children, }: JourneyProviderProps): react_jsx_runtime.JSX.Element;
1357
+ declare function JourneyProvider({ config: rawConfig, storageKey, theme, onEvent, initialAnswers, initialOptions, onDiscountCodeApply, children, }: JourneyProviderProps): react_jsx_runtime.JSX.Element;
1351
1358
  declare function useJourneyState(): JourneyState;
1352
1359
  declare function useJourneyActions(): JourneyActions;
1353
1360
 
@@ -1427,4 +1434,4 @@ declare function GravityBin({ items, height, gravity, restitution, friction, ico
1427
1434
 
1428
1435
  declare function TimelineBlock({ items, iconPlacement, labelPlacement, nodeSize, lineThickness, itemSpacing, titleDescGap, lineStyle, lineColor, lineOpacity, startLine, endLine, animated, entryAnimation, staggerDelay, className, }: TimelineBlockProps): react_jsx_runtime.JSX.Element;
1429
1436
 
1430
- export { type AccordionBlockProps, type AccordionItem, type AnimationPreset, type AppliedDiscountAnswer, type AppliedDiscountPricing, type Arc, type AvatarGroupBlockProps, type AvatarItem, BLOCK_META, type BadgeBlockProps, type BadgeItem, type BadgeVariant, type BeforeAfterBlockProps, type BeforeAfterSide, type BlobConfig, type BlockAnimation, type BlockCondition, type BlockConfig, type BlockExitAnimation, type BlockPropsMap, type BlockType, type ButtonAction, type ButtonBlockProps, type CalloutBlockProps, type CardBlockProps, type CarouselBlockProps, type CarouselSlide, type ChartLine, type ChartLineEndDot, type ChecklistBlockProps, type ChecklistItem, type CircularProgressBlockProps, type ColumnConfig, type ColumnsBlockProps, type ComparisonBar, type ComparisonBarBlockProps, type ComputedVariable, type ContinuousScrollTimelineEvent, type CounterBlockProps, type CounterStyle, type DecoratedTextBlockProps, type DeviceFrameBlockProps, type DiscountCodeApplyRequest, type DiscountCodeApplyResult, DiscountCodeDialog, type DiscountCodeDialogState, type DiscountCodeSummaryLine, type DividerBlockProps, type ExitAnimationPreset, type FeatureRowBlockProps, type FeatureRowIconBg, type FetchState, type FloatingLabelBlockProps, GravityBin, type GravityBinBlockProps, type GravityBinItem, type GravityBinProps, type HeadingBlockProps, type IconBlockProps, type ImageBlockProps, Journey, type JourneyAnswers, type JourneyConfig, JourneyContext, type JourneyEvent, type JourneyEventComputedVariables, type JourneyProps, JourneyProvider, type JourneyProviderProps, JourneyShell, type LineChartBlockProps, type ListBlockProps, type LottieBlockProps, type MediaSlide, type MetricBlockProps, type MetricVariant, type MultiSelectBlockProps, type NavigationDirection, type NotificationColor, type NotificationItem, type NotificationStackBlockProps, type PageLayout, type PauseScrollTimelineEvent, type PricingPlan, type PricingPlanFeature, type PricingPlanPeriod, type PricingPlansBlockProps, type PricingStyle, type ProgressBarBlockProps, type ProgressSegment, type QuoteBlockProps, type QuoteSlide, type RoutingRule, type ScrollTimelineBehavior, type ScrollTimelineEvent, type ScrollToTimelineEvent, SegmentedProgress, type SelectedPlanAnswer, type SingleSelectBlockProps, type SliderStyle, type SpacerBlockProps, type StatBlockProps, type StepAnswer, type StepBackground, type StepComponentProps, type StepConfig, type StepField, type StepOption, StepRenderer, type StepRouting, type StepScrollTimeline, type StepType, type StepValidation, type SwipeCardAlign, type SwipeCardItem, type SwipeIconStyle, type SwipeLabels, type TableBlockProps, type TableCell, type TableColumn, type TableRowLabel, type TextBlockProps, type TextSegment, TimelineBlock, type TimelineBlockProps, type TimelineColor, type TimelineItem, type VideoBlockProps, type WidgetStyle, arePageInputsValid, evaluateCondition, hasVisibleProgress, normalizeStepOrder, resolveComputedVariableValues, resolveComputedVariables, useJourneyActions, useJourneyConfig, useJourneyState };
1437
+ export { type AccordionBlockProps, type AccordionItem, type AnimationPreset, type AppliedDiscountAnswer, type AppliedDiscountPricing, type Arc, type AvatarGroupBlockProps, type AvatarItem, BLOCK_META, type BadgeBlockProps, type BadgeItem, type BadgeVariant, type BeforeAfterBlockProps, type BeforeAfterSide, type BlobConfig, type BlockAnimation, type BlockCondition, type BlockConfig, type BlockExitAnimation, type BlockPropsMap, type BlockType, type ButtonAction, type ButtonBlockProps, type CalloutBlockProps, type CardBlockProps, type CarouselBlockProps, type CarouselSlide, type ChartLine, type ChartLineEndDot, type ChecklistBlockProps, type ChecklistItem, type CircularProgressBlockProps, type ColumnConfig, type ColumnsBlockProps, type ComparisonBar, type ComparisonBarBlockProps, type ComputedVariable, type ContinuousScrollTimelineEvent, type CounterBlockProps, type CounterStyle, type DecoratedTextBlockProps, type DeviceFrameBlockProps, type DiscountCodeApplyRequest, type DiscountCodeApplyResult, DiscountCodeDialog, type DiscountCodeDialogState, type DiscountCodeSummaryLine, type DividerBlockProps, type ExitAnimationPreset, type FeatureRowBlockProps, type FeatureRowIconBg, type FetchState, type FloatingLabelBlockProps, GravityBin, type GravityBinBlockProps, type GravityBinItem, type GravityBinProps, type HeadingBlockProps, type IconBlockProps, type ImageBlockProps, Journey, type JourneyAnswers, type JourneyConfig, JourneyContext, type JourneyEvent, type JourneyEventComputedVariables, type JourneyOptionOverrides, type JourneyProps, JourneyProvider, type JourneyProviderProps, JourneyShell, type LineChartBlockProps, type ListBlockProps, type LottieBlockProps, type MediaSlide, type MetricBlockProps, type MetricVariant, type MultiSelectBlockProps, type NavigationDirection, type NotificationColor, type NotificationItem, type NotificationStackBlockProps, type PageLayout, type PauseScrollTimelineEvent, type PricingPlan, type PricingPlanFeature, type PricingPlanPeriod, type PricingPlansBlockProps, type PricingStyle, type ProgressBarBlockProps, type ProgressSegment, type QuoteBlockProps, type QuoteSlide, type RoutingRule, type ScrollTimelineBehavior, type ScrollTimelineEvent, type ScrollToTimelineEvent, SegmentedProgress, type SelectedPlanAnswer, type SingleSelectBlockProps, type SliderStyle, type SpacerBlockProps, type StatBlockProps, type StepAnswer, type StepBackground, type StepComponentProps, type StepConfig, type StepField, type StepOption, StepRenderer, type StepRouting, type StepScrollTimeline, type StepType, type StepValidation, type SwipeCardAlign, type SwipeCardItem, type SwipeIconStyle, type SwipeLabels, type TableBlockProps, type TableCell, type TableColumn, type TableRowLabel, type TextBlockProps, type TextSegment, TimelineBlock, type TimelineBlockProps, type TimelineColor, type TimelineItem, type VideoBlockProps, type WidgetStyle, arePageInputsValid, evaluateCondition, hasVisibleProgress, normalizeStepOrder, resolveComputedVariableValues, resolveComputedVariables, useJourneyActions, useJourneyConfig, useJourneyState };
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ type StepOption = {
11
11
  /** Optional image URL used in gravity bin physics visualization */
12
12
  image?: string;
13
13
  };
14
+ type JourneyOptionOverrides = Record<string, StepOption[]>;
14
15
  type SwipeCardItem = {
15
16
  variable: string;
16
17
  text: string;
@@ -1257,6 +1258,8 @@ type JourneyCommonProps = {
1257
1258
  theme?: string;
1258
1259
  /** Seed answers available at first render. Useful for runtime-injected data (e.g. live pricing plans referenced via `"${pricingPlans}"` templates). Keys declared in `computedVariables` are ignored because formulas own those values. */
1259
1260
  initialAnswers?: JourneyAnswers;
1261
+ /** Runtime option lists keyed by answer key (`step.variable ?? step.id`, or select block `props.variable`). Values override inspector-configured options for rendering only. */
1262
+ initialOptions?: JourneyOptionOverrides;
1260
1263
  /** Consumer-owned discount validation/repricing callback. Journeys only handles UI/state. */
1261
1264
  onDiscountCodeApply?: (request: DiscountCodeApplyRequest) => Promise<DiscountCodeApplyResult> | DiscountCodeApplyResult;
1262
1265
  /** Custom loading component shown while fetching config */
@@ -1343,11 +1346,15 @@ type JourneyProviderProps = {
1343
1346
  * `computedVariables` are ignored here because formulas own those values. Refreshed prices
1344
1347
  * always win over a stale snapshot in storage. */
1345
1348
  initialAnswers?: JourneyAnswers;
1349
+ /** Runtime option lists keyed by answer key (`step.variable ?? step.id`, or select block
1350
+ * `props.variable`). These override editor-configured options in the rendered journey only;
1351
+ * they are not persisted in answers. Event step configs omit option arrays. */
1352
+ initialOptions?: JourneyOptionOverrides;
1346
1353
  /** Consumer-owned discount validation/repricing callback. Journeys only handles UI/state. */
1347
1354
  onDiscountCodeApply?: (request: DiscountCodeApplyRequest) => Promise<DiscountCodeApplyResult> | DiscountCodeApplyResult;
1348
1355
  children: ReactNode;
1349
1356
  };
1350
- declare function JourneyProvider({ config: rawConfig, storageKey, theme, onEvent, initialAnswers, onDiscountCodeApply, children, }: JourneyProviderProps): react_jsx_runtime.JSX.Element;
1357
+ declare function JourneyProvider({ config: rawConfig, storageKey, theme, onEvent, initialAnswers, initialOptions, onDiscountCodeApply, children, }: JourneyProviderProps): react_jsx_runtime.JSX.Element;
1351
1358
  declare function useJourneyState(): JourneyState;
1352
1359
  declare function useJourneyActions(): JourneyActions;
1353
1360
 
@@ -1427,4 +1434,4 @@ declare function GravityBin({ items, height, gravity, restitution, friction, ico
1427
1434
 
1428
1435
  declare function TimelineBlock({ items, iconPlacement, labelPlacement, nodeSize, lineThickness, itemSpacing, titleDescGap, lineStyle, lineColor, lineOpacity, startLine, endLine, animated, entryAnimation, staggerDelay, className, }: TimelineBlockProps): react_jsx_runtime.JSX.Element;
1429
1436
 
1430
- export { type AccordionBlockProps, type AccordionItem, type AnimationPreset, type AppliedDiscountAnswer, type AppliedDiscountPricing, type Arc, type AvatarGroupBlockProps, type AvatarItem, BLOCK_META, type BadgeBlockProps, type BadgeItem, type BadgeVariant, type BeforeAfterBlockProps, type BeforeAfterSide, type BlobConfig, type BlockAnimation, type BlockCondition, type BlockConfig, type BlockExitAnimation, type BlockPropsMap, type BlockType, type ButtonAction, type ButtonBlockProps, type CalloutBlockProps, type CardBlockProps, type CarouselBlockProps, type CarouselSlide, type ChartLine, type ChartLineEndDot, type ChecklistBlockProps, type ChecklistItem, type CircularProgressBlockProps, type ColumnConfig, type ColumnsBlockProps, type ComparisonBar, type ComparisonBarBlockProps, type ComputedVariable, type ContinuousScrollTimelineEvent, type CounterBlockProps, type CounterStyle, type DecoratedTextBlockProps, type DeviceFrameBlockProps, type DiscountCodeApplyRequest, type DiscountCodeApplyResult, DiscountCodeDialog, type DiscountCodeDialogState, type DiscountCodeSummaryLine, type DividerBlockProps, type ExitAnimationPreset, type FeatureRowBlockProps, type FeatureRowIconBg, type FetchState, type FloatingLabelBlockProps, GravityBin, type GravityBinBlockProps, type GravityBinItem, type GravityBinProps, type HeadingBlockProps, type IconBlockProps, type ImageBlockProps, Journey, type JourneyAnswers, type JourneyConfig, JourneyContext, type JourneyEvent, type JourneyEventComputedVariables, type JourneyProps, JourneyProvider, type JourneyProviderProps, JourneyShell, type LineChartBlockProps, type ListBlockProps, type LottieBlockProps, type MediaSlide, type MetricBlockProps, type MetricVariant, type MultiSelectBlockProps, type NavigationDirection, type NotificationColor, type NotificationItem, type NotificationStackBlockProps, type PageLayout, type PauseScrollTimelineEvent, type PricingPlan, type PricingPlanFeature, type PricingPlanPeriod, type PricingPlansBlockProps, type PricingStyle, type ProgressBarBlockProps, type ProgressSegment, type QuoteBlockProps, type QuoteSlide, type RoutingRule, type ScrollTimelineBehavior, type ScrollTimelineEvent, type ScrollToTimelineEvent, SegmentedProgress, type SelectedPlanAnswer, type SingleSelectBlockProps, type SliderStyle, type SpacerBlockProps, type StatBlockProps, type StepAnswer, type StepBackground, type StepComponentProps, type StepConfig, type StepField, type StepOption, StepRenderer, type StepRouting, type StepScrollTimeline, type StepType, type StepValidation, type SwipeCardAlign, type SwipeCardItem, type SwipeIconStyle, type SwipeLabels, type TableBlockProps, type TableCell, type TableColumn, type TableRowLabel, type TextBlockProps, type TextSegment, TimelineBlock, type TimelineBlockProps, type TimelineColor, type TimelineItem, type VideoBlockProps, type WidgetStyle, arePageInputsValid, evaluateCondition, hasVisibleProgress, normalizeStepOrder, resolveComputedVariableValues, resolveComputedVariables, useJourneyActions, useJourneyConfig, useJourneyState };
1437
+ export { type AccordionBlockProps, type AccordionItem, type AnimationPreset, type AppliedDiscountAnswer, type AppliedDiscountPricing, type Arc, type AvatarGroupBlockProps, type AvatarItem, BLOCK_META, type BadgeBlockProps, type BadgeItem, type BadgeVariant, type BeforeAfterBlockProps, type BeforeAfterSide, type BlobConfig, type BlockAnimation, type BlockCondition, type BlockConfig, type BlockExitAnimation, type BlockPropsMap, type BlockType, type ButtonAction, type ButtonBlockProps, type CalloutBlockProps, type CardBlockProps, type CarouselBlockProps, type CarouselSlide, type ChartLine, type ChartLineEndDot, type ChecklistBlockProps, type ChecklistItem, type CircularProgressBlockProps, type ColumnConfig, type ColumnsBlockProps, type ComparisonBar, type ComparisonBarBlockProps, type ComputedVariable, type ContinuousScrollTimelineEvent, type CounterBlockProps, type CounterStyle, type DecoratedTextBlockProps, type DeviceFrameBlockProps, type DiscountCodeApplyRequest, type DiscountCodeApplyResult, DiscountCodeDialog, type DiscountCodeDialogState, type DiscountCodeSummaryLine, type DividerBlockProps, type ExitAnimationPreset, type FeatureRowBlockProps, type FeatureRowIconBg, type FetchState, type FloatingLabelBlockProps, GravityBin, type GravityBinBlockProps, type GravityBinItem, type GravityBinProps, type HeadingBlockProps, type IconBlockProps, type ImageBlockProps, Journey, type JourneyAnswers, type JourneyConfig, JourneyContext, type JourneyEvent, type JourneyEventComputedVariables, type JourneyOptionOverrides, type JourneyProps, JourneyProvider, type JourneyProviderProps, JourneyShell, type LineChartBlockProps, type ListBlockProps, type LottieBlockProps, type MediaSlide, type MetricBlockProps, type MetricVariant, type MultiSelectBlockProps, type NavigationDirection, type NotificationColor, type NotificationItem, type NotificationStackBlockProps, type PageLayout, type PauseScrollTimelineEvent, type PricingPlan, type PricingPlanFeature, type PricingPlanPeriod, type PricingPlansBlockProps, type PricingStyle, type ProgressBarBlockProps, type ProgressSegment, type QuoteBlockProps, type QuoteSlide, type RoutingRule, type ScrollTimelineBehavior, type ScrollTimelineEvent, type ScrollToTimelineEvent, SegmentedProgress, type SelectedPlanAnswer, type SingleSelectBlockProps, type SliderStyle, type SpacerBlockProps, type StatBlockProps, type StepAnswer, type StepBackground, type StepComponentProps, type StepConfig, type StepField, type StepOption, StepRenderer, type StepRouting, type StepScrollTimeline, type StepType, type StepValidation, type SwipeCardAlign, type SwipeCardItem, type SwipeIconStyle, type SwipeLabels, type TableBlockProps, type TableCell, type TableColumn, type TableRowLabel, type TextBlockProps, type TextSegment, TimelineBlock, type TimelineBlockProps, type TimelineColor, type TimelineItem, type VideoBlockProps, type WidgetStyle, arePageInputsValid, evaluateCondition, hasVisibleProgress, normalizeStepOrder, resolveComputedVariableValues, resolveComputedVariables, useJourneyActions, useJourneyConfig, useJourneyState };
package/dist/index.js CHANGED
@@ -512,6 +512,131 @@ function normalizeStepOrder(config) {
512
512
  }
513
513
  return __spreadProps(__spreadValues({}, config), { steps: ordered });
514
514
  }
515
+ function getRuntimeOptionOverride(overrides, key) {
516
+ if (!overrides || !key) return void 0;
517
+ if (!Object.prototype.hasOwnProperty.call(overrides, key)) return void 0;
518
+ return overrides[key];
519
+ }
520
+ function applyRuntimeOptionOverridesToBlocks(blocks, overrides) {
521
+ if (!overrides) return blocks;
522
+ let changed = false;
523
+ const nextBlocks = blocks.map((block) => {
524
+ let next = block;
525
+ if (block.type === "single_select" || block.type === "multi_select") {
526
+ const options = getRuntimeOptionOverride(overrides, block.props.variable);
527
+ if (options !== void 0) {
528
+ next = __spreadProps(__spreadValues({}, block), {
529
+ props: __spreadProps(__spreadValues({}, block.props), { options })
530
+ });
531
+ changed = true;
532
+ }
533
+ }
534
+ if (block.type === "columns") {
535
+ let columnsChanged = false;
536
+ const columns = block.props.columns.map((column) => {
537
+ const columnBlocks = applyRuntimeOptionOverridesToBlocks(
538
+ column.blocks,
539
+ overrides
540
+ );
541
+ if (columnBlocks === column.blocks) return column;
542
+ columnsChanged = true;
543
+ return __spreadProps(__spreadValues({}, column), { blocks: columnBlocks });
544
+ });
545
+ if (columnsChanged) {
546
+ next = __spreadProps(__spreadValues({}, block), {
547
+ props: __spreadProps(__spreadValues({}, block.props), { columns })
548
+ });
549
+ changed = true;
550
+ }
551
+ }
552
+ return next;
553
+ });
554
+ return changed ? nextBlocks : blocks;
555
+ }
556
+ function applyRuntimeOptionOverrides(config, overrides) {
557
+ if (!overrides || Object.keys(overrides).length === 0) return config;
558
+ let changed = false;
559
+ const steps = config.steps.map((step) => {
560
+ var _a;
561
+ let next = step;
562
+ const stepUsesOptions = step.type === "single_select" || step.type === "multi_select" || step.type === "counter_select";
563
+ const options = stepUsesOptions ? getRuntimeOptionOverride(overrides, (_a = step.variable) != null ? _a : step.id) : void 0;
564
+ if (options !== void 0) {
565
+ next = __spreadProps(__spreadValues({}, step), { options });
566
+ changed = true;
567
+ }
568
+ if (step.blocks) {
569
+ const blocks = applyRuntimeOptionOverridesToBlocks(
570
+ step.blocks,
571
+ overrides
572
+ );
573
+ if (blocks !== step.blocks) {
574
+ next = __spreadProps(__spreadValues({}, next), { blocks });
575
+ changed = true;
576
+ }
577
+ }
578
+ return next;
579
+ });
580
+ return changed ? __spreadProps(__spreadValues({}, config), { steps }) : config;
581
+ }
582
+ function stripOptionsFromEventBlocks(blocks) {
583
+ let changed = false;
584
+ const nextBlocks = blocks.map((block) => {
585
+ let next = block;
586
+ if (block.type === "single_select" || block.type === "multi_select") {
587
+ const props = __spreadValues({}, block.props);
588
+ delete props.options;
589
+ next = __spreadProps(__spreadValues({}, block), { props });
590
+ changed = true;
591
+ }
592
+ if (block.type === "columns") {
593
+ let columnsChanged = false;
594
+ const columns = block.props.columns.map((column) => {
595
+ const columnBlocks = stripOptionsFromEventBlocks(column.blocks);
596
+ if (columnBlocks === column.blocks) return column;
597
+ columnsChanged = true;
598
+ return __spreadProps(__spreadValues({}, column), { blocks: columnBlocks });
599
+ });
600
+ if (columnsChanged) {
601
+ next = __spreadProps(__spreadValues({}, block), {
602
+ props: __spreadProps(__spreadValues({}, block.props), { columns })
603
+ });
604
+ changed = true;
605
+ }
606
+ }
607
+ return next;
608
+ });
609
+ return changed ? nextBlocks : blocks;
610
+ }
611
+ function stripOptionsFromEventStep(step) {
612
+ var _a;
613
+ let changed = false;
614
+ let next = step;
615
+ if (step.options !== void 0) {
616
+ next = __spreadValues({}, next);
617
+ delete next.options;
618
+ changed = true;
619
+ }
620
+ if ((_a = step.fields) == null ? void 0 : _a.some((field) => field.options !== void 0)) {
621
+ next = __spreadProps(__spreadValues({}, next), {
622
+ fields: step.fields.map((field) => {
623
+ if (field.options === void 0) return field;
624
+ const nextField = __spreadValues({}, field);
625
+ delete nextField.options;
626
+ return nextField;
627
+ })
628
+ });
629
+ changed = true;
630
+ }
631
+ if (step.blocks) {
632
+ const blocks = stripOptionsFromEventBlocks(step.blocks);
633
+ if (blocks !== step.blocks) {
634
+ next = __spreadProps(__spreadValues({}, next), { blocks });
635
+ changed = true;
636
+ }
637
+ }
638
+ return changed ? next : step;
639
+ }
515
640
  var JourneyContext = createContext(null);
516
641
  function JourneyProvider({
517
642
  config: rawConfig,
@@ -519,6 +644,7 @@ function JourneyProvider({
519
644
  theme,
520
645
  onEvent,
521
646
  initialAnswers,
647
+ initialOptions,
522
648
  onDiscountCodeApply,
523
649
  children
524
650
  }) {
@@ -529,7 +655,14 @@ function JourneyProvider({
529
655
  useEffect(() => {
530
656
  onEventRef.current = onEvent;
531
657
  }, [onEvent]);
532
- const config = useMemo(() => normalizeStepOrder(rawConfig), [rawConfig]);
658
+ const normalizedConfig = useMemo(
659
+ () => normalizeStepOrder(rawConfig),
660
+ [rawConfig]
661
+ );
662
+ const config = useMemo(
663
+ () => applyRuntimeOptionOverrides(normalizedConfig, initialOptions),
664
+ [normalizedConfig, initialOptions]
665
+ );
533
666
  const computedVariableIds = useMemo(
534
667
  () => getComputedVariableIds(config.computedVariables),
535
668
  [config.computedVariables]
@@ -574,8 +707,8 @@ function JourneyProvider({
574
707
  rawAnswersRef.current = rawAnswers;
575
708
  }, [rawAnswers]);
576
709
  const allVariables = useMemo(
577
- () => config.steps.flatMap(getStepVariables),
578
- [config.steps]
710
+ () => normalizedConfig.steps.flatMap(getStepVariables),
711
+ [normalizedConfig.steps]
579
712
  );
580
713
  const setAnswer = useCallback((stepId, answer) => {
581
714
  if (computedVariableIds.has(stepId)) return;
@@ -644,7 +777,7 @@ function JourneyProvider({
644
777
  const submitted = getSubmittedAnswer(currentStep, latestAnswers);
645
778
  (_c = onEventRef.current) == null ? void 0 : _c.call(onEventRef, __spreadProps(__spreadValues({
646
779
  type: "step_submit",
647
- step: currentStep,
780
+ step: stripOptionsFromEventStep(currentStep),
648
781
  submitted
649
782
  }, eventAnswers), {
650
783
  variables: allVariables
@@ -654,8 +787,8 @@ function JourneyProvider({
654
787
  navigateToIndex(nextIndex);
655
788
  (_d = onEventRef.current) == null ? void 0 : _d.call(onEventRef, __spreadValues({
656
789
  type: "navigate",
657
- from: currentStep,
658
- to: config.steps[nextIndex],
790
+ from: stripOptionsFromEventStep(currentStep),
791
+ to: stripOptionsFromEventStep(config.steps[nextIndex]),
659
792
  direction: "forward"
660
793
  }, eventAnswers));
661
794
  } else {
@@ -687,13 +820,18 @@ function JourneyProvider({
687
820
  navigateToIndex(idx);
688
821
  (_a = onEventRef.current) == null ? void 0 : _a.call(onEventRef, __spreadValues({
689
822
  type: "navigate",
690
- from: fromStep,
691
- to: config.steps[idx],
823
+ from: stripOptionsFromEventStep(fromStep),
824
+ to: stripOptionsFromEventStep(config.steps[idx]),
692
825
  direction: idx > currentStepIndex ? "forward" : "backward"
693
826
  }, getEventAnswers(config.computedVariables, rawAnswersRef.current)));
694
827
  }
695
828
  },
696
- [config.computedVariables, config.steps, currentStepIndex, navigateToIndex]
829
+ [
830
+ config.computedVariables,
831
+ config.steps,
832
+ currentStepIndex,
833
+ navigateToIndex
834
+ ]
697
835
  );
698
836
  const goBack = useCallback(() => {
699
837
  var _a;
@@ -729,8 +867,8 @@ function JourneyProvider({
729
867
  setDirection(dir);
730
868
  (_b2 = onEventRef.current) == null ? void 0 : _b2.call(onEventRef, __spreadValues({
731
869
  type: "navigate",
732
- from: config.steps[prev],
733
- to: config.steps[step],
870
+ from: stripOptionsFromEventStep(config.steps[prev]),
871
+ to: stripOptionsFromEventStep(config.steps[step]),
734
872
  direction: dir
735
873
  }, getEventAnswers(config.computedVariables, rawAnswersRef.current)));
736
874
  return step;
@@ -738,7 +876,11 @@ function JourneyProvider({
738
876
  };
739
877
  window.addEventListener("popstate", handlePopState);
740
878
  return () => window.removeEventListener("popstate", handlePopState);
741
- }, [config.allowBackNavigation, config.computedVariables, config.steps]);
879
+ }, [
880
+ config.allowBackNavigation,
881
+ config.computedVariables,
882
+ config.steps
883
+ ]);
742
884
  const getStepAnswer = useCallback(
743
885
  (stepId) => {
744
886
  var _a;
@@ -758,17 +900,22 @@ function JourneyProvider({
758
900
  );
759
901
  const firePurchaseIntent = useCallback(
760
902
  (variable, plan, discount) => {
761
- var _a, _b;
762
- const currentStep = config.steps[currentStepIndex];
763
- (_b = onEventRef.current) == null ? void 0 : _b.call(onEventRef, __spreadValues(__spreadProps(__spreadValues({
903
+ var _a, _b, _c;
904
+ const currentStep = (_a = normalizedConfig.steps[currentStepIndex]) != null ? _a : config.steps[currentStepIndex];
905
+ (_c = onEventRef.current) == null ? void 0 : _c.call(onEventRef, __spreadValues(__spreadProps(__spreadValues({
764
906
  type: "purchase_intent",
765
907
  variable,
766
908
  plan
767
909
  }, discount ? { discount } : {}), {
768
- stepId: (_a = currentStep == null ? void 0 : currentStep.id) != null ? _a : ""
910
+ stepId: (_b = currentStep == null ? void 0 : currentStep.id) != null ? _b : ""
769
911
  }), getEventAnswers(config.computedVariables, rawAnswersRef.current)));
770
912
  },
771
- [config.computedVariables, config.steps, currentStepIndex]
913
+ [
914
+ config.computedVariables,
915
+ config.steps,
916
+ currentStepIndex,
917
+ normalizedConfig.steps
918
+ ]
772
919
  );
773
920
  const openDiscountCodeDialog = useCallback(
774
921
  (state2) => {
@@ -9276,6 +9423,7 @@ function JourneyRemote({
9276
9423
  className,
9277
9424
  theme,
9278
9425
  initialAnswers,
9426
+ initialOptions,
9279
9427
  onDiscountCodeApply,
9280
9428
  loadingComponent,
9281
9429
  errorComponent
@@ -9295,6 +9443,7 @@ function JourneyRemote({
9295
9443
  theme,
9296
9444
  onEvent,
9297
9445
  initialAnswers,
9446
+ initialOptions,
9298
9447
  onDiscountCodeApply,
9299
9448
  children: /* @__PURE__ */ jsx(JourneyShell, { className, theme })
9300
9449
  }
@@ -9311,6 +9460,7 @@ function Journey(props) {
9311
9460
  className,
9312
9461
  theme,
9313
9462
  initialAnswers,
9463
+ initialOptions,
9314
9464
  onDiscountCodeApply
9315
9465
  } = props;
9316
9466
  return /* @__PURE__ */ jsx(
@@ -9321,6 +9471,7 @@ function Journey(props) {
9321
9471
  theme,
9322
9472
  onEvent,
9323
9473
  initialAnswers,
9474
+ initialOptions,
9324
9475
  onDiscountCodeApply,
9325
9476
  children: /* @__PURE__ */ jsx(JourneyShell, { className, theme })
9326
9477
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@founderhq/journeys",
3
- "version": "0.3.61",
3
+ "version": "0.3.62",
4
4
  "description": "Config-driven interactive journey/questionnaire engine for React",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",