@wise/dynamic-flow-client 5.14.0 → 5.15.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.
Files changed (45) hide show
  1. package/README.md +4 -4
  2. package/build/main.js +963 -772
  3. package/build/main.mjs +1183 -992
  4. package/build/tsconfig.types.tsbuildinfo +1 -1
  5. package/build/types/common/capabilities/getHttpClientWithCapabilities.d.ts +9 -0
  6. package/build/types/common/capabilities/getHttpClientWithCapabilities.d.ts.map +1 -0
  7. package/build/types/common/capabilities/index.d.ts +2 -0
  8. package/build/types/common/capabilities/index.d.ts.map +1 -0
  9. package/build/types/controller/FlowController.d.ts.map +1 -1
  10. package/build/types/controller/executeSubflow.d.ts +22 -0
  11. package/build/types/controller/executeSubflow.d.ts.map +1 -0
  12. package/build/types/domain/components/RootDomainComponent.d.ts +5 -5
  13. package/build/types/domain/components/RootDomainComponent.d.ts.map +1 -1
  14. package/build/types/domain/components/SubflowComponent.d.ts +29 -0
  15. package/build/types/domain/components/SubflowComponent.d.ts.map +1 -0
  16. package/build/types/domain/features/eventNames.d.ts +1 -1
  17. package/build/types/domain/features/eventNames.d.ts.map +1 -1
  18. package/build/types/domain/features/prefetch/getStepPrefetch.d.ts +4 -5
  19. package/build/types/domain/features/prefetch/getStepPrefetch.d.ts.map +1 -1
  20. package/build/types/domain/mappers/mapStepToComponent.d.ts.map +1 -1
  21. package/build/types/domain/types.d.ts +2 -2
  22. package/build/types/domain/types.d.ts.map +1 -1
  23. package/build/types/index.d.ts +3 -2
  24. package/build/types/index.d.ts.map +1 -1
  25. package/build/types/renderers/mappers/componentToRendererProps.d.ts +1 -1
  26. package/build/types/renderers/mappers/componentToRendererProps.d.ts.map +1 -1
  27. package/build/types/renderers/mappers/rootComponentToProps.d.ts +2 -2
  28. package/build/types/renderers/mappers/rootComponentToProps.d.ts.map +1 -1
  29. package/build/types/renderers/mappers/subflowComponentToRendererProps.d.ts +8 -3
  30. package/build/types/renderers/mappers/subflowComponentToRendererProps.d.ts.map +1 -1
  31. package/build/types/renderers/subflow/getDynamicSubflowRenderer.d.ts +9 -0
  32. package/build/types/renderers/subflow/getDynamicSubflowRenderer.d.ts.map +1 -0
  33. package/build/types/renderers/subflow/getNativeSubflowRenderer.d.ts +3 -0
  34. package/build/types/renderers/subflow/getNativeSubflowRenderer.d.ts.map +1 -0
  35. package/build/types/test-utils/fetch-utils.d.ts.map +1 -1
  36. package/build/types/test-utils/getMergedTestRenderers.d.ts +1 -1
  37. package/build/types/test-utils/getMergedTestRenderers.d.ts.map +1 -1
  38. package/build/types/types.d.ts +2 -1
  39. package/build/types/types.d.ts.map +1 -1
  40. package/build/types/useDynamicFlow.d.ts.map +1 -1
  41. package/package.json +2 -3
  42. package/build/types/domain/components/SubflowDomainComponent.d.ts +0 -17
  43. package/build/types/domain/components/SubflowDomainComponent.d.ts.map +0 -1
  44. package/build/types/getSubflowCallbacks.d.ts +0 -15
  45. package/build/types/getSubflowCallbacks.d.ts.map +0 -1
package/build/main.mjs CHANGED
@@ -825,7 +825,8 @@ var getChildren = (node) => {
825
825
  case "input-text":
826
826
  case "input-upload":
827
827
  case "external-confirmation":
828
- case "subflow":
828
+ case "subflow-dynamic":
829
+ case "subflow-native":
829
830
  case "upsell":
830
831
  return [];
831
832
  }
@@ -911,6 +912,47 @@ var ErrorBoundary = class extends Component {
911
912
  };
912
913
  var ErrorBoundary_default = ErrorBoundary;
913
914
 
915
+ // src/common/capabilities/getHttpClientWithCapabilities.ts
916
+ var getHttpClientWithCapabilities = (httpClient, nativeSubflowHandlers = []) => {
917
+ const headers = getCapabilitiesHeader(nativeSubflowHandlers);
918
+ return async (input, init) => {
919
+ return headers && isRelativeOrSameOrigin(input) ? httpClient(input, mergeRequestInit(init != null ? init : {}, { headers })) : httpClient(input, init);
920
+ };
921
+ };
922
+ var getCapabilitiesHeader = (nativeSubflowHandlers) => {
923
+ const capabilities = nativeSubflowHandlers.map(getCapability);
924
+ return capabilities.length > 0 ? { "X-DF-Native-Capabilities": capabilities.join(";") } : null;
925
+ };
926
+ var getCapability = (handler) => {
927
+ return `${encode(handler.id)}${getCapabilityComments(handler)}`;
928
+ };
929
+ var getCapabilityComments = (handler) => {
930
+ var _a, _b;
931
+ const comments = Object.entries((_b = (_a = handler.getCapabilityComments) == null ? void 0 : _a.call(handler)) != null ? _b : {}).map(([key, value]) => {
932
+ return `${encode(key)}:${encode(value)}`;
933
+ });
934
+ return comments.length > 0 ? `(${comments.join(",")})` : "";
935
+ };
936
+ var isRelativeOrSameOrigin = (input) => {
937
+ var _a;
938
+ if (typeof input === "string") {
939
+ return isRelativePath2(input) || input.startsWith(`${(_a = window == null ? void 0 : window.location) == null ? void 0 : _a.origin}/`);
940
+ }
941
+ if (input instanceof Request) {
942
+ return isRelativeOrSameOrigin(input.url);
943
+ }
944
+ if (input instanceof URL) {
945
+ return isRelativeOrSameOrigin(input.toString());
946
+ }
947
+ return false;
948
+ };
949
+ var isRelativePath2 = (url = "") => {
950
+ return !["https://", "http://", "data:"].some(
951
+ (prefix) => url.startsWith(prefix) && url.length > prefix.length
952
+ );
953
+ };
954
+ var encode = (value) => encodeURIComponent(value).replace(/\(/g, "%28").replace(/\)/g, "%29");
955
+
914
956
  // src/domain/components/utils/component-utils.ts
915
957
  var getInputUpdateFunction = (onComponentUpdate) => {
916
958
  return (component, updateFn) => {
@@ -999,6 +1041,9 @@ var createRootDomainComponent = (onComponentUpdate, scrollToTop, backConfig, req
999
1041
  });
1000
1042
  },
1001
1043
  closeSubflow() {
1044
+ if (!this.subflow) {
1045
+ return;
1046
+ }
1002
1047
  this._update((draft) => {
1003
1048
  draft.subflow = null;
1004
1049
  });
@@ -1089,24 +1134,6 @@ var shouldScrollOnStepUpdate = (newStep, currentStep) => {
1089
1134
  return newStep.error != null && newStep.error !== currentStep.error;
1090
1135
  };
1091
1136
 
1092
- // src/domain/components/utils/getRandomId.ts
1093
- var getRandomId = () => Math.random().toString(36).substring(2);
1094
-
1095
- // src/domain/components/SubflowDomainComponent.ts
1096
- var createSubflowDomainComponent = (subflowProps, onComponentUpdate) => {
1097
- const update = getInputUpdateFunction(onComponentUpdate);
1098
- const subflowComponent = __spreadProps(__spreadValues({
1099
- uid: `subflow-${getRandomId()}`,
1100
- type: "subflow",
1101
- kind: "layout"
1102
- }, subflowProps), {
1103
- _update(updateFn) {
1104
- update(this, updateFn);
1105
- }
1106
- });
1107
- return subflowComponent;
1108
- };
1109
-
1110
1137
  // src/domain/features/prefetch/request-cache.ts
1111
1138
  var makeRequestCacheWithParent = (parent) => {
1112
1139
  const map = /* @__PURE__ */ new Map();
@@ -1179,6 +1206,9 @@ var createModalContentComponent = (modalProps, onComponentUpdate) => {
1179
1206
  return modalContentComponent;
1180
1207
  };
1181
1208
 
1209
+ // src/domain/components/utils/getRandomId.ts
1210
+ var getRandomId = () => Math.random().toString(36).substring(2);
1211
+
1182
1212
  // src/domain/components/AlertComponent.ts
1183
1213
  var createAlertComponent = (alertProps) => __spreadValues({
1184
1214
  type: "alert",
@@ -2665,120 +2695,461 @@ var getStepPolling = ({
2665
2695
  return { start, stop };
2666
2696
  };
2667
2697
 
2668
- // src/domain/features/refreshAfter/getStepRefreshAfter.ts
2669
- var ONE_SECOND = 1e3;
2670
- var getStepRefreshAfter = ({
2671
- refreshAfter,
2672
- logEvent,
2673
- onBehavior
2674
- }) => {
2675
- let timeout = null;
2676
- const targetTime = new Date(refreshAfter).getTime();
2677
- if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
2678
- throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
2679
- }
2680
- const start = () => {
2681
- const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
2682
- timeout = setTimeout(() => {
2683
- void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
2684
- }, timeLeft);
2685
- };
2686
- return {
2687
- start,
2688
- stop: () => {
2689
- if (!timeout) {
2690
- logEvent("warning", "Attempted to stop refreshAfter but it was not started");
2691
- return;
2692
- }
2693
- clearTimeout(timeout);
2694
- }
2695
- };
2698
+ // src/controller/getErrorMessage.ts
2699
+ var getErrorMessage = (error) => {
2700
+ return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
2696
2701
  };
2697
2702
 
2698
- // src/domain/components/utils/isOrWasValid.ts
2699
- var isOrWasValid = (getErrors, previous, current) => {
2700
- const wasValid = getErrors(previous).length === 0 && previous !== null;
2701
- const isValid = getErrors(current).length === 0;
2702
- return wasValid || isValid;
2703
+ // src/controller/response-utils.ts
2704
+ import { validateActionResponse, validateSubflowResponse } from "@wise/dynamic-flow-types/spec";
2705
+ var parseResponseBodyAsJsonElement = async (response) => {
2706
+ assertResponseIsValid(response);
2707
+ try {
2708
+ return await response.json();
2709
+ } catch (e) {
2710
+ return null;
2711
+ }
2703
2712
  };
2704
-
2705
- // src/domain/features/schema-on-change/getDebouncedSchemaOnChange.ts
2706
- var debounceDelay = 1e3;
2707
- var getDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
2708
- if (!onChange) {
2709
- return void 0;
2713
+ var parseResponseBodyAsText = async (response) => {
2714
+ try {
2715
+ return await response.text();
2716
+ } catch (e) {
2717
+ return null;
2710
2718
  }
2711
- const performOnChange = onChange.behaviorType === "refresh" ? getSelectiveDebouncedSchemaOnChange(onChange, getValidationErrors) : debounce(onChange, debounceDelay);
2712
- return performOnChange;
2713
2719
  };
2714
- var getSelectiveDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
2715
- let behaviorControl = { abort: () => {
2716
- } };
2717
- let timeoutId = null;
2718
- const clearTimer = () => {
2719
- if (timeoutId) {
2720
- clearTimeout(timeoutId);
2721
- }
2722
- timeoutId = null;
2723
- };
2724
- const flush = () => {
2725
- if (timeoutId !== null) {
2726
- void onChange().then((bc) => {
2727
- behaviorControl = bc;
2728
- });
2729
- clearTimer();
2730
- }
2731
- };
2732
- const debouncedOnChange = (prevValue, updatedValue) => {
2733
- var _a;
2734
- const valid = isOrWasValid(getValidationErrors, prevValue, updatedValue);
2735
- const isPending = timeoutId !== null;
2736
- if (valid || isPending) {
2737
- (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
2738
- clearTimer();
2739
- timeoutId = setTimeout(() => flush(), debounceDelay);
2720
+ function isActionResponseBody(body) {
2721
+ return validateActionResponse(body).valid;
2722
+ }
2723
+ function assertActionResponseBody(body) {
2724
+ if (!isObject(body) || !isObject(body.action)) {
2725
+ throw new Error(
2726
+ "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
2727
+ );
2728
+ }
2729
+ }
2730
+ function assertModalResponseBody(body) {
2731
+ if (isObject(body)) {
2732
+ if ("content" in body && isArray(body.content)) {
2733
+ return;
2740
2734
  }
2741
- };
2742
- debouncedOnChange.cancel = () => {
2743
- var _a;
2744
- clearTimer();
2745
- (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
2746
- };
2747
- debouncedOnChange.flush = () => {
2748
- flush();
2749
- };
2750
- return debouncedOnChange;
2735
+ }
2736
+ throw new Error(
2737
+ "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
2738
+ );
2739
+ }
2740
+ function assertSubflowResponseBody(body) {
2741
+ const { valid } = validateSubflowResponse(body);
2742
+ if (valid) {
2743
+ return;
2744
+ }
2745
+ throw new Error("Incorrect response body in subflow response.");
2746
+ }
2747
+ function isErrorResponseBody(body) {
2748
+ return Boolean(
2749
+ isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
2750
+ );
2751
+ }
2752
+ function assertStepResponseBody(body) {
2753
+ if (!isObject(body)) {
2754
+ throw new Error("Incorrect response body in step response. Expected an object.");
2755
+ }
2756
+ }
2757
+ var assertResponseIsValid = (response) => {
2758
+ if (!isResponse(response)) {
2759
+ throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
2760
+ }
2761
+ if (response.bodyUsed) {
2762
+ throw new Error(
2763
+ "The body of the provided Response object has already been used. Every request must respond with a new Response object."
2764
+ );
2765
+ }
2751
2766
  };
2767
+ var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
2752
2768
 
2753
- // src/domain/features/validation/validation-functions.ts
2754
- var validateComponents = (components) => components.reduce((acc, component) => component.validate() && acc, true);
2755
- var getLocalValueValidator = (checks) => (currentValue) => checks.map((check) => check(currentValue)).filter(isString);
2756
-
2757
- // src/domain/features/utils/http-utils.ts
2758
- function constructPayload({
2759
- value,
2760
- signal,
2761
- requestConfig
2762
- }) {
2763
- const isFile2 = value instanceof File;
2764
- const { method, param } = requestConfig;
2765
- const body = isFile2 ? wrapInFormData(param, value) : JSON.stringify({ [param]: value });
2766
- const headers = __spreadValues({}, !isFile2 && { "Content-Type": "application/json" });
2769
+ // src/controller/handleErrorResponse.ts
2770
+ var handleErrorResponse = async (response, actionId, trackEvent) => {
2771
+ const body = await parseResponseBodyAsJsonElement(response.clone());
2772
+ if (isErrorResponseBody(body)) {
2773
+ const refreshUrl = body.refreshUrl || body.refreshFormUrl;
2774
+ const { error, validation, analytics } = body;
2775
+ trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
2776
+ const errors = { error, validation };
2777
+ return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
2778
+ type: "error",
2779
+ body: { errors, analytics },
2780
+ httpError: { statusCode: response.status }
2781
+ };
2782
+ }
2783
+ trackEvent("Action Failed", { actionId, statusCode: response.status });
2784
+ const errorMessage = await parseResponseBodyAsText(response);
2767
2785
  return {
2768
- method,
2769
- headers,
2770
- body,
2771
- signal
2786
+ type: "error",
2787
+ httpError: {
2788
+ message: errorMessage || void 0,
2789
+ statusCode: response.status
2790
+ }
2772
2791
  };
2773
- }
2774
- var wrapInFormData = (key, file) => {
2775
- const formData = new FormData();
2776
- formData.append(key, file);
2777
- return formData;
2778
2792
  };
2779
- var abortAndResetController = (abortController) => {
2780
- abortController.abort();
2781
- return new AbortController();
2793
+
2794
+ // src/controller/getResponseType.ts
2795
+ var responseTypes = ["step", "action", "exit", "modal", "subflow", "no-op"];
2796
+ var getResponseType = (headers, body) => {
2797
+ const headerResponseType = getResponseTypeFromHeader(headers);
2798
+ if (headerResponseType) {
2799
+ return headerResponseType;
2800
+ }
2801
+ if (isObject(body) && body.action) {
2802
+ return "action";
2803
+ }
2804
+ return "step";
2805
+ };
2806
+ var getResponseTypeFromHeader = (headers) => {
2807
+ if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
2808
+ const type = headers.get("X-Df-Response-Type");
2809
+ assertDFResponseType(type);
2810
+ return type;
2811
+ }
2812
+ if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
2813
+ return "exit";
2814
+ }
2815
+ return void 0;
2816
+ };
2817
+ function assertDFResponseType(type) {
2818
+ if (!responseTypes.includes(type)) {
2819
+ throw new Error(
2820
+ "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'modal', 'subflow', 'no-op'."
2821
+ );
2822
+ }
2823
+ }
2824
+
2825
+ // src/controller/makeSafeHttpClient.ts
2826
+ var makeSafeHttpClient = (httpClient) => async (...props) => {
2827
+ try {
2828
+ return await httpClient(...props);
2829
+ } catch (e) {
2830
+ return null;
2831
+ }
2832
+ };
2833
+
2834
+ // src/controller/executeRequest.ts
2835
+ var executeRequest = async (props) => {
2836
+ const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
2837
+ const { url, method, body } = request;
2838
+ const response = await getCachedOrFetch(
2839
+ [
2840
+ url,
2841
+ {
2842
+ method,
2843
+ body: body ? JSON.stringify(body) : void 0,
2844
+ headers: { "Content-Type": "application/json" }
2845
+ }
2846
+ ],
2847
+ requestCache,
2848
+ httpClient
2849
+ );
2850
+ if (!response) {
2851
+ const extra = { errorMessage: "Network Error" };
2852
+ trackEvent("Request Failed", extra);
2853
+ logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
2854
+ return { type: "error" };
2855
+ }
2856
+ if (!response.ok) {
2857
+ return handleErrorResponse(response, void 0, trackEvent);
2858
+ }
2859
+ const responseBody = await parseResponseBodyAsJsonElement(response);
2860
+ const responseType = getResponseType(response.headers, responseBody);
2861
+ if (exit) {
2862
+ return { type: "complete", result: responseBody };
2863
+ }
2864
+ switch (responseType) {
2865
+ case "step": {
2866
+ const etag = response.headers.get("etag") || null;
2867
+ assertStepResponseBody(responseBody);
2868
+ return { type: "replace-step", step: responseBody, etag };
2869
+ }
2870
+ case "exit": {
2871
+ return { type: "complete", result: responseBody };
2872
+ }
2873
+ case "action": {
2874
+ assertActionResponseBody(responseBody);
2875
+ return {
2876
+ type: "behavior",
2877
+ behavior: {
2878
+ type: "action",
2879
+ action: responseBody.action
2880
+ }
2881
+ };
2882
+ }
2883
+ case "subflow": {
2884
+ assertSubflowResponseBody(responseBody);
2885
+ return {
2886
+ type: "behavior",
2887
+ behavior: __spreadProps(__spreadValues({}, responseBody), {
2888
+ type: "subflow",
2889
+ onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
2890
+ onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
2891
+ })
2892
+ };
2893
+ }
2894
+ case "modal": {
2895
+ assertModalResponseBody(responseBody);
2896
+ return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
2897
+ }
2898
+ case "no-op": {
2899
+ return { type: "no-op" };
2900
+ }
2901
+ default: {
2902
+ throw new Error(`Unsupported response type: ${String(responseType)}`);
2903
+ }
2904
+ }
2905
+ };
2906
+ var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
2907
+ const cachedPromise = requestCache.get(requestParams);
2908
+ if (cachedPromise) {
2909
+ const cachedResponse = await cachedPromise;
2910
+ if (cachedResponse == null ? void 0 : cachedResponse.ok) {
2911
+ return cachedResponse;
2912
+ }
2913
+ }
2914
+ return makeSafeHttpClient(httpClient)(...requestParams);
2915
+ };
2916
+
2917
+ // src/controller/executeSubmission.ts
2918
+ var executeSubmission = async (props) => {
2919
+ const { httpClient, requestCache, trackEvent, logEvent } = props;
2920
+ const triggerAction = async (action, model, isInitial) => {
2921
+ var _a, _b;
2922
+ const { exit, url, result = null, id: actionId } = action;
2923
+ const trackSubmissionEvent = !isInitial ? trackEvent : () => {
2924
+ };
2925
+ trackSubmissionEvent("Action Triggered", { actionId });
2926
+ if (exit && !url) {
2927
+ trackSubmissionEvent("Action Succeeded", { actionId });
2928
+ return { type: "complete", result };
2929
+ }
2930
+ try {
2931
+ const command = await executeRequest({
2932
+ exit,
2933
+ request: createRequestFromAction(action, model),
2934
+ requestCache,
2935
+ httpClient,
2936
+ trackEvent: (name, properties) => {
2937
+ trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
2938
+ },
2939
+ logEvent
2940
+ });
2941
+ switch (command.type) {
2942
+ case "error": {
2943
+ trackSubmissionEvent("Action Failed", __spreadValues({
2944
+ actionId,
2945
+ statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
2946
+ }, (_b = command.body) == null ? void 0 : _b.analytics));
2947
+ return command;
2948
+ }
2949
+ case "behavior": {
2950
+ if (command.behavior.type === "action") {
2951
+ trackSubmissionEvent("Action Succeeded", { actionId });
2952
+ return await executeSubmission({
2953
+ action: command.behavior.action,
2954
+ isInitial: false,
2955
+ model: null,
2956
+ requestCache,
2957
+ httpClient,
2958
+ trackEvent,
2959
+ logEvent
2960
+ });
2961
+ }
2962
+ trackSubmissionEvent("Action Succeeded", { actionId });
2963
+ return command;
2964
+ }
2965
+ case "complete": {
2966
+ trackSubmissionEvent("Action Succeeded", { actionId });
2967
+ return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
2968
+ }
2969
+ case "no-op":
2970
+ default: {
2971
+ trackSubmissionEvent("Action Succeeded", { actionId });
2972
+ return command;
2973
+ }
2974
+ }
2975
+ } catch (error) {
2976
+ const errorMessage = getErrorMessage(error);
2977
+ trackSubmissionEvent("Action Failed", { actionId, errorMessage });
2978
+ logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
2979
+ throw error;
2980
+ }
2981
+ };
2982
+ return triggerAction(props.action, props.model, props.isInitial);
2983
+ };
2984
+ var createRequestFromAction = (action, model) => {
2985
+ var _a, _b, _c;
2986
+ return __spreadProps(__spreadValues({}, action), {
2987
+ url: (_a = action.url) != null ? _a : "",
2988
+ method: (_b = action.method) != null ? _b : "POST",
2989
+ body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
2990
+ });
2991
+ };
2992
+
2993
+ // src/domain/features/prefetch/getStepPrefetch.ts
2994
+ var getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
2995
+ const requestCache = makeRequestCacheWithParent(flowRequestCache);
2996
+ const keys = /* @__PURE__ */ new Set();
2997
+ const start = (model) => {
2998
+ if (keys.size > 0) {
2999
+ return;
3000
+ }
3001
+ submissionBehaviors.forEach((behavior) => {
3002
+ const request = getPrefetchableRequest(behavior, model);
3003
+ if (request) {
3004
+ const requestParams = [
3005
+ request.url,
3006
+ {
3007
+ body: JSON.stringify(request.body),
3008
+ method: request.method,
3009
+ headers: { "Content-Type": "application/json" }
3010
+ }
3011
+ ];
3012
+ try {
3013
+ const key = makeRequestCacheKey(requestParams);
3014
+ if (keys.has(key)) {
3015
+ return;
3016
+ }
3017
+ const responsePromise = httpClient(...requestParams).catch(() => null);
3018
+ requestCache.set(requestParams, responsePromise);
3019
+ keys.add(key);
3020
+ } catch (e) {
3021
+ }
3022
+ }
3023
+ });
3024
+ };
3025
+ const stop = () => {
3026
+ };
3027
+ return { requestCache, start, stop };
3028
+ };
3029
+ var getPrefetchableRequest = (behavior, model) => {
3030
+ if (behavior.type === "action" && behavior.action.prefetch) {
3031
+ return createRequestFromAction(behavior.action, model);
3032
+ }
3033
+ if (behavior.type === "subflow" && behavior.launchConfig.type === "dynamic" && behavior.launchConfig.request.prefetch) {
3034
+ return behavior.launchConfig.request;
3035
+ }
3036
+ return null;
3037
+ };
3038
+
3039
+ // src/domain/features/refreshAfter/getStepRefreshAfter.ts
3040
+ var ONE_SECOND = 1e3;
3041
+ var getStepRefreshAfter = ({
3042
+ refreshAfter,
3043
+ logEvent,
3044
+ onBehavior
3045
+ }) => {
3046
+ let timeout = null;
3047
+ const targetTime = new Date(refreshAfter).getTime();
3048
+ if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
3049
+ throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
3050
+ }
3051
+ const start = () => {
3052
+ const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
3053
+ timeout = setTimeout(() => {
3054
+ void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
3055
+ }, timeLeft);
3056
+ };
3057
+ return {
3058
+ start,
3059
+ stop: () => {
3060
+ if (!timeout) {
3061
+ logEvent("warning", "Attempted to stop refreshAfter but it was not started");
3062
+ return;
3063
+ }
3064
+ clearTimeout(timeout);
3065
+ }
3066
+ };
3067
+ };
3068
+
3069
+ // src/domain/components/utils/isOrWasValid.ts
3070
+ var isOrWasValid = (getErrors, previous, current) => {
3071
+ const wasValid = getErrors(previous).length === 0 && previous !== null;
3072
+ const isValid = getErrors(current).length === 0;
3073
+ return wasValid || isValid;
3074
+ };
3075
+
3076
+ // src/domain/features/schema-on-change/getDebouncedSchemaOnChange.ts
3077
+ var debounceDelay = 1e3;
3078
+ var getDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
3079
+ if (!onChange) {
3080
+ return void 0;
3081
+ }
3082
+ const performOnChange = onChange.behaviorType === "refresh" ? getSelectiveDebouncedSchemaOnChange(onChange, getValidationErrors) : debounce(onChange, debounceDelay);
3083
+ return performOnChange;
3084
+ };
3085
+ var getSelectiveDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
3086
+ let behaviorControl = { abort: () => {
3087
+ } };
3088
+ let timeoutId = null;
3089
+ const clearTimer = () => {
3090
+ if (timeoutId) {
3091
+ clearTimeout(timeoutId);
3092
+ }
3093
+ timeoutId = null;
3094
+ };
3095
+ const flush = () => {
3096
+ if (timeoutId !== null) {
3097
+ void onChange().then((bc) => {
3098
+ behaviorControl = bc;
3099
+ });
3100
+ clearTimer();
3101
+ }
3102
+ };
3103
+ const debouncedOnChange = (prevValue, updatedValue) => {
3104
+ var _a;
3105
+ const valid = isOrWasValid(getValidationErrors, prevValue, updatedValue);
3106
+ const isPending = timeoutId !== null;
3107
+ if (valid || isPending) {
3108
+ (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
3109
+ clearTimer();
3110
+ timeoutId = setTimeout(() => flush(), debounceDelay);
3111
+ }
3112
+ };
3113
+ debouncedOnChange.cancel = () => {
3114
+ var _a;
3115
+ clearTimer();
3116
+ (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
3117
+ };
3118
+ debouncedOnChange.flush = () => {
3119
+ flush();
3120
+ };
3121
+ return debouncedOnChange;
3122
+ };
3123
+
3124
+ // src/domain/features/validation/validation-functions.ts
3125
+ var validateComponents = (components) => components.reduce((acc, component) => component.validate() && acc, true);
3126
+ var getLocalValueValidator = (checks) => (currentValue) => checks.map((check) => check(currentValue)).filter(isString);
3127
+
3128
+ // src/domain/features/utils/http-utils.ts
3129
+ function constructPayload({
3130
+ value,
3131
+ signal,
3132
+ requestConfig
3133
+ }) {
3134
+ const isFile2 = value instanceof File;
3135
+ const { method, param } = requestConfig;
3136
+ const body = isFile2 ? wrapInFormData(param, value) : JSON.stringify({ [param]: value });
3137
+ const headers = __spreadValues({}, !isFile2 && { "Content-Type": "application/json" });
3138
+ return {
3139
+ method,
3140
+ headers,
3141
+ body,
3142
+ signal
3143
+ };
3144
+ }
3145
+ var wrapInFormData = (key, file) => {
3146
+ const formData = new FormData();
3147
+ formData.append(key, file);
3148
+ return formData;
3149
+ };
3150
+ var abortAndResetController = (abortController) => {
3151
+ abortController.abort();
3152
+ return new AbortController();
2782
3153
  };
2783
3154
 
2784
3155
  // src/domain/features/validationAsync/getComponentValidationAsync.ts
@@ -3173,72 +3544,6 @@ var getInitialValidationAsyncState = () => ({
3173
3544
  messages: {}
3174
3545
  });
3175
3546
 
3176
- // src/controller/response-utils.ts
3177
- import { validateActionResponse, validateSubflowResponse } from "@wise/dynamic-flow-types/spec";
3178
- var parseResponseBodyAsJsonElement = async (response) => {
3179
- assertResponseIsValid(response);
3180
- try {
3181
- return await response.json();
3182
- } catch (e) {
3183
- return null;
3184
- }
3185
- };
3186
- var parseResponseBodyAsText = async (response) => {
3187
- try {
3188
- return await response.text();
3189
- } catch (e) {
3190
- return null;
3191
- }
3192
- };
3193
- function isActionResponseBody(body) {
3194
- return validateActionResponse(body).valid;
3195
- }
3196
- function assertActionResponseBody(body) {
3197
- if (!isObject(body) || !isObject(body.action)) {
3198
- throw new Error(
3199
- "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
3200
- );
3201
- }
3202
- }
3203
- function assertModalResponseBody(body) {
3204
- if (isObject(body)) {
3205
- if ("content" in body && isArray(body.content)) {
3206
- return;
3207
- }
3208
- }
3209
- throw new Error(
3210
- "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
3211
- );
3212
- }
3213
- function assertSubflowResponseBody(body) {
3214
- const { valid } = validateSubflowResponse(body);
3215
- if (valid) {
3216
- return;
3217
- }
3218
- throw new Error("Incorrect response body in subflow response.");
3219
- }
3220
- function isErrorResponseBody(body) {
3221
- return Boolean(
3222
- isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
3223
- );
3224
- }
3225
- function assertStepResponseBody(body) {
3226
- if (!isObject(body)) {
3227
- throw new Error("Incorrect response body in step response. Expected an object.");
3228
- }
3229
- }
3230
- var assertResponseIsValid = (response) => {
3231
- if (!isResponse(response)) {
3232
- throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
3233
- }
3234
- if (response.bodyUsed) {
3235
- throw new Error(
3236
- "The body of the provided Response object has already been used. Every request must respond with a new Response object."
3237
- );
3238
- }
3239
- };
3240
- var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
3241
-
3242
3547
  // src/domain/features/utils/response-utils.ts
3243
3548
  var getAnalyticsFromErrorResponse = (json) => {
3244
3549
  if (!isErrorResponseBody(json)) {
@@ -5578,195 +5883,8 @@ var stringSchemaToDateInputComponent = (schemaMapperProps, mapperProps) => {
5578
5883
  getAboveMaximumDateCheck(schema, errorMessageFunctions),
5579
5884
  getBelowMinimumDateCheck(schema, errorMessageFunctions)
5580
5885
  ],
5581
- minimumDate,
5582
- maximumDate,
5583
- suggestions: mapStringSchemaSuggestions(suggestions, mapperProps.logEvent),
5584
- value,
5585
- validationAsyncState,
5586
- schemaOnChange: getSchemaOnChange(schema, onBehavior),
5587
- performValidationAsync,
5588
- onValueChange
5589
- }),
5590
- onComponentUpdate
5591
- );
5592
- };
5593
- var getValidDate = (model) => {
5594
- if (!isString(model)) {
5595
- return null;
5596
- }
5597
- if (/^\d{4}-\d{2}$/.test(model)) {
5598
- return model;
5599
- }
5600
- try {
5601
- const date = new Date(model);
5602
- const stringDate = date.toISOString();
5603
- return stringDate.split("T")[0];
5604
- } catch (e) {
5605
- return null;
5606
- }
5607
- };
5608
-
5609
- // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToUploadInputComponent.ts
5610
- var stringSchemaToUploadInputComponent = (schemaMapperProps, mapperProps) => {
5611
- var _a;
5612
- const { schema, localValue, model, required = false } = schemaMapperProps;
5613
- const { accepts, autocompleteHint, cameraConfig, hidden, maxSize, source, validationMessages } = schema;
5614
- const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange } = mapperProps;
5615
- const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
5616
- const validLocalValue = isFile(localValue) ? localValue : null;
5617
- const value = (_a = getFileFromModel(model)) != null ? _a : validLocalValue;
5618
- const checks = hidden ? [] : [
5619
- getRequiredCheck(required, errorMessageFunctions),
5620
- getFileSizeCheck(schema, errorMessageFunctions),
5621
- getFileTypeCheck(schema, errorMessageFunctions)
5622
- ];
5623
- return createUploadInputComponent(
5624
- __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
5625
- accepts,
5626
- autoComplete: getAutocompleteString(autocompleteHint),
5627
- cameraConfig,
5628
- checks,
5629
- format: "base64",
5630
- maxSize,
5631
- source,
5632
- value,
5633
- schemaOnChange: getSchemaOnChange(schema, onBehavior),
5634
- onValueChange
5635
- }),
5636
- onComponentUpdate
5637
- );
5638
- };
5639
- var getFileFromModel = (model) => isString(model) ? base64dataUrltoFile(model, "") : null;
5640
-
5641
- // src/domain/components/TextInputComponent.ts
5642
- var createTextInputComponent = (textInputProps, onComponentUpdate) => {
5643
- const _a = textInputProps, {
5644
- uid,
5645
- id,
5646
- checks,
5647
- schemaOnChange,
5648
- performValidationAsync,
5649
- onValueChange,
5650
- onPersistAsync,
5651
- summariser,
5652
- value
5653
- } = _a, rest = __objRest(_a, [
5654
- "uid",
5655
- "id",
5656
- "checks",
5657
- "schemaOnChange",
5658
- "performValidationAsync",
5659
- "onValueChange",
5660
- "onPersistAsync",
5661
- "summariser",
5662
- "value"
5663
- ]);
5664
- const update = getInputUpdateFunction(onComponentUpdate);
5665
- const getValidationErrors = getLocalValueValidator(checks);
5666
- const performOnChange = getDebouncedSchemaOnChange(schemaOnChange, getValidationErrors);
5667
- const validateAsync = performValidationAsync ? getDebouncedComponentValidationAsync(update, performValidationAsync) : void 0;
5668
- const inputComponent = __spreadValues({
5669
- type: "text",
5670
- kind: "input",
5671
- uid,
5672
- id,
5673
- value,
5674
- _update(updateFn) {
5675
- update(this, updateFn);
5676
- },
5677
- onBlur() {
5678
- const isValid = this.validate();
5679
- performOnChange == null ? void 0 : performOnChange.flush();
5680
- if (isValid) {
5681
- void (onPersistAsync == null ? void 0 : onPersistAsync().catch(() => {
5682
- }));
5683
- validateAsync == null ? void 0 : validateAsync.flush();
5684
- }
5685
- },
5686
- onFocus() {
5687
- },
5688
- // Noop
5689
- onChange(updatedValue) {
5690
- const prevValue = this.value;
5691
- this._update((draft) => {
5692
- draft.errors = [];
5693
- draft.validationAsyncState.messages = {};
5694
- draft.value = updatedValue;
5695
- });
5696
- performOnChange == null ? void 0 : performOnChange(prevValue, updatedValue);
5697
- const isValid = getValidationErrors(updatedValue).length === 0;
5698
- if (isValid) {
5699
- validateAsync == null ? void 0 : validateAsync(this, updatedValue);
5700
- }
5701
- if (!updatedValue) {
5702
- validateAsync == null ? void 0 : validateAsync.cancel();
5703
- }
5704
- onValueChange();
5705
- },
5706
- async getSubmittableValue() {
5707
- return this.getSubmittableValueSync();
5708
- },
5709
- getSubmittableValueSync() {
5710
- return this.getLocalValue() || null;
5711
- },
5712
- getSummary() {
5713
- return summariser(this.getLocalValue());
5714
- },
5715
- getLocalValue() {
5716
- return this.value;
5717
- },
5718
- validate() {
5719
- const errors = getValidationErrors(this.getLocalValue());
5720
- this._update((draft) => {
5721
- draft.errors = errors;
5722
- });
5723
- return errors.length === 0;
5724
- }
5725
- }, rest);
5726
- return inputComponent;
5727
- };
5728
-
5729
- // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToTextInputComponent.ts
5730
- var stringSchemaToTextInputComponent = (schemaMapperProps, mapperProps) => {
5731
- const { schema, localValue, model, required = false, onPersistAsync } = schemaMapperProps;
5732
- const {
5733
- autocapitalization,
5734
- autocompleteHint,
5735
- control,
5736
- default: defaultValue,
5737
- displayFormat,
5738
- format,
5739
- maxLength,
5740
- minLength,
5741
- pattern,
5742
- suggestions,
5743
- validationMessages
5744
- } = schema;
5745
- const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange, logEvent } = mapperProps;
5746
- const controlForLegacyFormat = getControlForLegacyFormat(format);
5747
- const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
5748
- const { performValidationAsync, validationAsyncState } = getValidationAsyncInitialState(
5749
- schemaMapperProps,
5750
- mapperProps
5751
- );
5752
- const validLocalValue = isString(localValue) ? localValue : null;
5753
- const validModel = isString(model) ? model : defaultValue != null ? defaultValue : null;
5754
- const value = onPersistAsync ? validLocalValue : validModel;
5755
- return createTextInputComponent(
5756
- __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
5757
- autocapitalization,
5758
- autoComplete: getAutocompleteString(autocompleteHint),
5759
- checks: schema.hidden ? [] : [
5760
- getRequiredCheck(required, errorMessageFunctions),
5761
- getAboveMaxLengthCheck(schema, errorMessageFunctions),
5762
- getBelowMinLengthCheck(schema, errorMessageFunctions),
5763
- getNotAdheringToPatternCheck(schema, errorMessageFunctions, { logEvent })
5764
- ],
5765
- control: control != null ? control : controlForLegacyFormat,
5766
- displayFormat,
5767
- maxLength,
5768
- minLength,
5769
- pattern,
5886
+ minimumDate,
5887
+ maximumDate,
5770
5888
  suggestions: mapStringSchemaSuggestions(suggestions, mapperProps.logEvent),
5771
5889
  value,
5772
5890
  validationAsyncState,
@@ -5777,442 +5895,365 @@ var stringSchemaToTextInputComponent = (schemaMapperProps, mapperProps) => {
5777
5895
  onComponentUpdate
5778
5896
  );
5779
5897
  };
5780
- var getControlForLegacyFormat = (format) => {
5781
- if (format && ["numeric", "phone-number", "email", "password"].includes(format)) {
5782
- return format;
5783
- }
5784
- return void 0;
5785
- };
5786
-
5787
- // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.ts
5788
- var stringSchemaToComponent = (schemaMapperProps, mapperProps) => {
5789
- const { schema } = schemaMapperProps;
5790
- if (isStringSchemaWithBase64(schema)) {
5791
- return stringSchemaToUploadInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5792
- }
5793
- switch (schema.format) {
5794
- case "date":
5795
- return stringSchemaToDateInputComponent(schemaMapperProps, mapperProps);
5796
- default:
5797
- return stringSchemaToTextInputComponent(schemaMapperProps, mapperProps);
5798
- }
5799
- };
5800
- var isStringSchemaWithBase64 = (schema) => {
5801
- return schema.format === "base64url" && !("persistAsync" in schema);
5802
- };
5803
-
5804
- // src/domain/mappers/mapSchemaToComponent.ts
5805
- var mapSchemaToComponent = (schemaMapperProps, mapperProps) => {
5806
- const { uid, schema } = schemaMapperProps;
5807
- if (isConstSchema(schema)) {
5808
- return constSchemaToComponent(uid, __spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5809
- }
5810
- if (isSchemaWithPersistAsync(schema)) {
5811
- return persistAsyncSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5812
- }
5813
- if (isAllOfSchema(schema)) {
5814
- return allOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5815
- }
5816
- if (isOneOfSchema(schema)) {
5817
- return oneOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5818
- }
5819
- if (isBooleanSchema(schema)) {
5820
- return booleanSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5821
- }
5822
- if (isObjectSchema(schema)) {
5823
- const { format } = schema;
5824
- if (format === "money") {
5825
- return objectSchemaToMoneyInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5826
- }
5827
- if (format != null && Object.keys(schema.properties).length === 0) {
5828
- return objectSchemaToFormattedValueComponent(
5829
- __spreadProps(__spreadValues({}, schemaMapperProps), { schema: __spreadProps(__spreadValues({}, schema), { format }) }),
5830
- mapperProps
5831
- );
5832
- }
5833
- return objectSchemaToObjectComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5834
- }
5835
- if (isIntegerSchema(schema)) {
5836
- return integerSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5837
- }
5838
- if (isNumberSchema(schema)) {
5839
- return numberSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5840
- }
5841
- if (isStringSchema(schema)) {
5842
- return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5898
+ var getValidDate = (model) => {
5899
+ if (!isString(model)) {
5900
+ return null;
5843
5901
  }
5844
- if (isArraySchema(schema)) {
5845
- return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5902
+ if (/^\d{4}-\d{2}$/.test(model)) {
5903
+ return model;
5846
5904
  }
5847
- if (isBlobSchema(schema)) {
5848
- if (!schemaMapperProps.onPersistAsync) {
5849
- throw new Error(
5850
- "Blob schemas can only be used as the schema of a persist async configuration."
5851
- );
5852
- }
5853
- return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5905
+ try {
5906
+ const date = new Date(model);
5907
+ const stringDate = date.toISOString();
5908
+ return stringDate.split("T")[0];
5909
+ } catch (e) {
5910
+ return null;
5854
5911
  }
5855
- throw new Error("Not yet supported");
5856
5912
  };
5857
5913
 
5858
- // src/domain/mappers/mapStepSchemas.ts
5859
- var mapStepSchemas = (uid, step, stepLocalValue, mapperProps) => {
5860
- return step.schemas.map(
5861
- (schema, i) => {
5862
- var _a, _b;
5863
- return mapSchemaToComponent(
5864
- {
5865
- uid: `${uid}.schemas-${i}.${schema.$id}`,
5866
- schemaId: schema.$id,
5867
- schema,
5868
- model: (_a = step.model) != null ? _a : null,
5869
- localValue: stepLocalValue,
5870
- validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
5871
- required: true
5872
- },
5873
- mapperProps
5874
- );
5875
- }
5914
+ // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToUploadInputComponent.ts
5915
+ var stringSchemaToUploadInputComponent = (schemaMapperProps, mapperProps) => {
5916
+ var _a;
5917
+ const { schema, localValue, model, required = false } = schemaMapperProps;
5918
+ const { accepts, autocompleteHint, cameraConfig, hidden, maxSize, source, validationMessages } = schema;
5919
+ const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange } = mapperProps;
5920
+ const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
5921
+ const validLocalValue = isFile(localValue) ? localValue : null;
5922
+ const value = (_a = getFileFromModel(model)) != null ? _a : validLocalValue;
5923
+ const checks = hidden ? [] : [
5924
+ getRequiredCheck(required, errorMessageFunctions),
5925
+ getFileSizeCheck(schema, errorMessageFunctions),
5926
+ getFileTypeCheck(schema, errorMessageFunctions)
5927
+ ];
5928
+ return createUploadInputComponent(
5929
+ __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
5930
+ accepts,
5931
+ autoComplete: getAutocompleteString(autocompleteHint),
5932
+ cameraConfig,
5933
+ checks,
5934
+ format: "base64",
5935
+ maxSize,
5936
+ source,
5937
+ value,
5938
+ schemaOnChange: getSchemaOnChange(schema, onBehavior),
5939
+ onValueChange
5940
+ }),
5941
+ onComponentUpdate
5876
5942
  );
5877
5943
  };
5944
+ var getFileFromModel = (model) => isString(model) ? base64dataUrltoFile(model, "") : null;
5878
5945
 
5879
- // src/domain/mappers/mapToolbarToComponent.ts
5880
- var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
5881
- if (!toolbar) {
5882
- return void 0;
5883
- }
5884
- const { onBehavior } = mapperProps;
5885
- return __spreadProps(__spreadValues({}, toolbar), {
5886
- type: "toolbar",
5887
- uid: `${uid}.toolbar`,
5888
- tags: toolbar.tags,
5889
- items: toolbar.items.map((item) => {
5890
- const context = item.context ? mapLegacyContext(item.context) : void 0;
5891
- const behavior = getDomainLayerBehavior(
5892
- { behavior: item.behavior },
5893
- [],
5894
- mapperProps.registerSubmissionBehavior
5895
- );
5896
- return __spreadProps(__spreadValues({}, item), {
5897
- context: context ? mapLegacyContext(context) : void 0,
5898
- disabled: !!item.disabled,
5899
- tags: item.tags,
5900
- onClick: () => {
5901
- void onBehavior(behavior);
5902
- }
5903
- });
5904
- })
5905
- });
5906
- };
5907
-
5908
- // src/domain/mappers/utils/groupLayoutByPinned.ts
5909
- var groupLayoutByPinned = (layouts) => {
5910
- return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
5911
- };
5912
- var groupLayout = (acc, layout) => {
5913
- if (layout.type === "button" && layout.pinOrder !== void 0) {
5914
- return {
5915
- pinned: [...acc.pinned, layout],
5916
- nonPinned: acc.nonPinned
5917
- };
5918
- }
5919
- if (hasColumns(layout)) {
5920
- const leftChildren = groupLayoutByPinned(layout.left);
5921
- const rightChildren = groupLayoutByPinned(layout.right);
5922
- return {
5923
- pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
5924
- nonPinned: [
5925
- ...acc.nonPinned,
5926
- __spreadProps(__spreadValues({}, layout), {
5927
- left: leftChildren.nonPinned,
5928
- right: rightChildren.nonPinned
5929
- })
5930
- ]
5931
- };
5932
- }
5933
- if (hasChildren(layout)) {
5934
- const childComponents = groupLayoutByPinned(layout.components);
5935
- return {
5936
- pinned: [...acc.pinned, ...childComponents.pinned],
5937
- nonPinned: [
5938
- ...acc.nonPinned,
5939
- __spreadProps(__spreadValues({}, layout), {
5940
- components: childComponents.nonPinned
5941
- })
5942
- ]
5943
- };
5944
- }
5945
- return {
5946
- pinned: [...acc.pinned],
5947
- nonPinned: [...acc.nonPinned, layout]
5948
- };
5946
+ // src/domain/components/TextInputComponent.ts
5947
+ var createTextInputComponent = (textInputProps, onComponentUpdate) => {
5948
+ const _a = textInputProps, {
5949
+ uid,
5950
+ id,
5951
+ checks,
5952
+ schemaOnChange,
5953
+ performValidationAsync,
5954
+ onValueChange,
5955
+ onPersistAsync,
5956
+ summariser,
5957
+ value
5958
+ } = _a, rest = __objRest(_a, [
5959
+ "uid",
5960
+ "id",
5961
+ "checks",
5962
+ "schemaOnChange",
5963
+ "performValidationAsync",
5964
+ "onValueChange",
5965
+ "onPersistAsync",
5966
+ "summariser",
5967
+ "value"
5968
+ ]);
5969
+ const update = getInputUpdateFunction(onComponentUpdate);
5970
+ const getValidationErrors = getLocalValueValidator(checks);
5971
+ const performOnChange = getDebouncedSchemaOnChange(schemaOnChange, getValidationErrors);
5972
+ const validateAsync = performValidationAsync ? getDebouncedComponentValidationAsync(update, performValidationAsync) : void 0;
5973
+ const inputComponent = __spreadValues({
5974
+ type: "text",
5975
+ kind: "input",
5976
+ uid,
5977
+ id,
5978
+ value,
5979
+ _update(updateFn) {
5980
+ update(this, updateFn);
5981
+ },
5982
+ onBlur() {
5983
+ const isValid = this.validate();
5984
+ performOnChange == null ? void 0 : performOnChange.flush();
5985
+ if (isValid) {
5986
+ void (onPersistAsync == null ? void 0 : onPersistAsync().catch(() => {
5987
+ }));
5988
+ validateAsync == null ? void 0 : validateAsync.flush();
5989
+ }
5990
+ },
5991
+ onFocus() {
5992
+ },
5993
+ // Noop
5994
+ onChange(updatedValue) {
5995
+ const prevValue = this.value;
5996
+ this._update((draft) => {
5997
+ draft.errors = [];
5998
+ draft.validationAsyncState.messages = {};
5999
+ draft.value = updatedValue;
6000
+ });
6001
+ performOnChange == null ? void 0 : performOnChange(prevValue, updatedValue);
6002
+ const isValid = getValidationErrors(updatedValue).length === 0;
6003
+ if (isValid) {
6004
+ validateAsync == null ? void 0 : validateAsync(this, updatedValue);
6005
+ }
6006
+ if (!updatedValue) {
6007
+ validateAsync == null ? void 0 : validateAsync.cancel();
6008
+ }
6009
+ onValueChange();
6010
+ },
6011
+ async getSubmittableValue() {
6012
+ return this.getSubmittableValueSync();
6013
+ },
6014
+ getSubmittableValueSync() {
6015
+ return this.getLocalValue() || null;
6016
+ },
6017
+ getSummary() {
6018
+ return summariser(this.getLocalValue());
6019
+ },
6020
+ getLocalValue() {
6021
+ return this.value;
6022
+ },
6023
+ validate() {
6024
+ const errors = getValidationErrors(this.getLocalValue());
6025
+ this._update((draft) => {
6026
+ draft.errors = errors;
6027
+ });
6028
+ return errors.length === 0;
6029
+ }
6030
+ }, rest);
6031
+ return inputComponent;
5949
6032
  };
5950
- var hasChildren = (component) => "components" in component;
5951
- var hasColumns = (component) => "right" in component && "left" in component;
5952
6033
 
5953
- // src/controller/getErrorMessage.ts
5954
- var getErrorMessage = (error) => {
5955
- return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
6034
+ // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToTextInputComponent.ts
6035
+ var stringSchemaToTextInputComponent = (schemaMapperProps, mapperProps) => {
6036
+ const { schema, localValue, model, required = false, onPersistAsync } = schemaMapperProps;
6037
+ const {
6038
+ autocapitalization,
6039
+ autocompleteHint,
6040
+ control,
6041
+ default: defaultValue,
6042
+ displayFormat,
6043
+ format,
6044
+ maxLength,
6045
+ minLength,
6046
+ pattern,
6047
+ suggestions,
6048
+ validationMessages
6049
+ } = schema;
6050
+ const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange, logEvent } = mapperProps;
6051
+ const controlForLegacyFormat = getControlForLegacyFormat(format);
6052
+ const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
6053
+ const { performValidationAsync, validationAsyncState } = getValidationAsyncInitialState(
6054
+ schemaMapperProps,
6055
+ mapperProps
6056
+ );
6057
+ const validLocalValue = isString(localValue) ? localValue : null;
6058
+ const validModel = isString(model) ? model : defaultValue != null ? defaultValue : null;
6059
+ const value = onPersistAsync ? validLocalValue : validModel;
6060
+ return createTextInputComponent(
6061
+ __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
6062
+ autocapitalization,
6063
+ autoComplete: getAutocompleteString(autocompleteHint),
6064
+ checks: schema.hidden ? [] : [
6065
+ getRequiredCheck(required, errorMessageFunctions),
6066
+ getAboveMaxLengthCheck(schema, errorMessageFunctions),
6067
+ getBelowMinLengthCheck(schema, errorMessageFunctions),
6068
+ getNotAdheringToPatternCheck(schema, errorMessageFunctions, { logEvent })
6069
+ ],
6070
+ control: control != null ? control : controlForLegacyFormat,
6071
+ displayFormat,
6072
+ maxLength,
6073
+ minLength,
6074
+ pattern,
6075
+ suggestions: mapStringSchemaSuggestions(suggestions, mapperProps.logEvent),
6076
+ value,
6077
+ validationAsyncState,
6078
+ schemaOnChange: getSchemaOnChange(schema, onBehavior),
6079
+ performValidationAsync,
6080
+ onValueChange
6081
+ }),
6082
+ onComponentUpdate
6083
+ );
5956
6084
  };
5957
-
5958
- // src/controller/handleErrorResponse.ts
5959
- var handleErrorResponse = async (response, actionId, trackEvent) => {
5960
- const body = await parseResponseBodyAsJsonElement(response.clone());
5961
- if (isErrorResponseBody(body)) {
5962
- const refreshUrl = body.refreshUrl || body.refreshFormUrl;
5963
- const { error, validation, analytics } = body;
5964
- trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
5965
- const errors = { error, validation };
5966
- return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
5967
- type: "error",
5968
- body: { errors, analytics },
5969
- httpError: { statusCode: response.status }
5970
- };
6085
+ var getControlForLegacyFormat = (format) => {
6086
+ if (format && ["numeric", "phone-number", "email", "password"].includes(format)) {
6087
+ return format;
5971
6088
  }
5972
- trackEvent("Action Failed", { actionId, statusCode: response.status });
5973
- const errorMessage = await parseResponseBodyAsText(response);
5974
- return {
5975
- type: "error",
5976
- httpError: {
5977
- message: errorMessage || void 0,
5978
- statusCode: response.status
5979
- }
5980
- };
6089
+ return void 0;
5981
6090
  };
5982
6091
 
5983
- // src/controller/getResponseType.ts
5984
- var responseTypes = ["step", "action", "exit", "modal", "subflow", "no-op"];
5985
- var getResponseType = (headers, body) => {
5986
- const headerResponseType = getResponseTypeFromHeader(headers);
5987
- if (headerResponseType) {
5988
- return headerResponseType;
6092
+ // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.ts
6093
+ var stringSchemaToComponent = (schemaMapperProps, mapperProps) => {
6094
+ const { schema } = schemaMapperProps;
6095
+ if (isStringSchemaWithBase64(schema)) {
6096
+ return stringSchemaToUploadInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5989
6097
  }
5990
- if (isObject(body) && body.action) {
5991
- return "action";
6098
+ switch (schema.format) {
6099
+ case "date":
6100
+ return stringSchemaToDateInputComponent(schemaMapperProps, mapperProps);
6101
+ default:
6102
+ return stringSchemaToTextInputComponent(schemaMapperProps, mapperProps);
5992
6103
  }
5993
- return "step";
5994
6104
  };
5995
- var getResponseTypeFromHeader = (headers) => {
5996
- if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
5997
- const type = headers.get("X-Df-Response-Type");
5998
- assertDFResponseType(type);
5999
- return type;
6000
- }
6001
- if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
6002
- return "exit";
6003
- }
6004
- return void 0;
6105
+ var isStringSchemaWithBase64 = (schema) => {
6106
+ return schema.format === "base64url" && !("persistAsync" in schema);
6005
6107
  };
6006
- function assertDFResponseType(type) {
6007
- if (!responseTypes.includes(type)) {
6008
- throw new Error(
6009
- "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'modal', 'subflow', 'no-op'."
6010
- );
6011
- }
6012
- }
6013
6108
 
6014
- // src/controller/makeSafeHttpClient.ts
6015
- var makeSafeHttpClient = (httpClient) => async (...props) => {
6016
- try {
6017
- return await httpClient(...props);
6018
- } catch (e) {
6019
- return null;
6109
+ // src/domain/mappers/mapSchemaToComponent.ts
6110
+ var mapSchemaToComponent = (schemaMapperProps, mapperProps) => {
6111
+ const { uid, schema } = schemaMapperProps;
6112
+ if (isConstSchema(schema)) {
6113
+ return constSchemaToComponent(uid, __spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6020
6114
  }
6021
- };
6022
-
6023
- // src/controller/executeRequest.ts
6024
- var executeRequest = async (props) => {
6025
- const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
6026
- const { url, method, body } = request;
6027
- const response = await getCachedOrFetch(
6028
- [
6029
- url,
6030
- {
6031
- method,
6032
- body: body ? JSON.stringify(body) : void 0,
6033
- headers: { "Content-Type": "application/json" }
6034
- }
6035
- ],
6036
- requestCache,
6037
- httpClient
6038
- );
6039
- if (!response) {
6040
- const extra = { errorMessage: "Network Error" };
6041
- trackEvent("Request Failed", extra);
6042
- logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
6043
- return { type: "error" };
6115
+ if (isSchemaWithPersistAsync(schema)) {
6116
+ return persistAsyncSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6044
6117
  }
6045
- if (!response.ok) {
6046
- return handleErrorResponse(response, void 0, trackEvent);
6118
+ if (isAllOfSchema(schema)) {
6119
+ return allOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6047
6120
  }
6048
- const responseBody = await parseResponseBodyAsJsonElement(response);
6049
- const responseType = getResponseType(response.headers, responseBody);
6050
- if (exit) {
6051
- return { type: "complete", result: responseBody };
6121
+ if (isOneOfSchema(schema)) {
6122
+ return oneOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6052
6123
  }
6053
- switch (responseType) {
6054
- case "step": {
6055
- const etag = response.headers.get("etag") || null;
6056
- assertStepResponseBody(responseBody);
6057
- return { type: "replace-step", step: responseBody, etag };
6058
- }
6059
- case "exit": {
6060
- return { type: "complete", result: responseBody };
6061
- }
6062
- case "action": {
6063
- assertActionResponseBody(responseBody);
6064
- return {
6065
- type: "behavior",
6066
- behavior: {
6067
- type: "action",
6068
- action: responseBody.action
6069
- }
6070
- };
6071
- }
6072
- case "subflow": {
6073
- assertSubflowResponseBody(responseBody);
6074
- return {
6075
- type: "behavior",
6076
- behavior: __spreadProps(__spreadValues({}, responseBody), {
6077
- type: "subflow",
6078
- onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
6079
- onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
6080
- })
6081
- };
6082
- }
6083
- case "modal": {
6084
- assertModalResponseBody(responseBody);
6085
- return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
6086
- }
6087
- case "no-op": {
6088
- return { type: "no-op" };
6124
+ if (isBooleanSchema(schema)) {
6125
+ return booleanSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6126
+ }
6127
+ if (isObjectSchema(schema)) {
6128
+ const { format } = schema;
6129
+ if (format === "money") {
6130
+ return objectSchemaToMoneyInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6089
6131
  }
6090
- default: {
6091
- throw new Error(`Unsupported response type: ${String(responseType)}`);
6132
+ if (format != null && Object.keys(schema.properties).length === 0) {
6133
+ return objectSchemaToFormattedValueComponent(
6134
+ __spreadProps(__spreadValues({}, schemaMapperProps), { schema: __spreadProps(__spreadValues({}, schema), { format }) }),
6135
+ mapperProps
6136
+ );
6092
6137
  }
6138
+ return objectSchemaToObjectComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6139
+ }
6140
+ if (isIntegerSchema(schema)) {
6141
+ return integerSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6093
6142
  }
6094
- };
6095
- var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
6096
- const cachedPromise = requestCache.get(requestParams);
6097
- if (cachedPromise) {
6098
- const cachedResponse = await cachedPromise;
6099
- if (cachedResponse == null ? void 0 : cachedResponse.ok) {
6100
- return cachedResponse;
6143
+ if (isNumberSchema(schema)) {
6144
+ return numberSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6145
+ }
6146
+ if (isStringSchema(schema)) {
6147
+ return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6148
+ }
6149
+ if (isArraySchema(schema)) {
6150
+ return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6151
+ }
6152
+ if (isBlobSchema(schema)) {
6153
+ if (!schemaMapperProps.onPersistAsync) {
6154
+ throw new Error(
6155
+ "Blob schemas can only be used as the schema of a persist async configuration."
6156
+ );
6101
6157
  }
6158
+ return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6102
6159
  }
6103
- return makeSafeHttpClient(httpClient)(...requestParams);
6160
+ throw new Error("Not yet supported");
6104
6161
  };
6105
6162
 
6106
- // src/controller/executeSubmission.ts
6107
- var executeSubmission = async (props) => {
6108
- const { httpClient, requestCache, trackEvent, logEvent } = props;
6109
- const triggerAction = async (action, model, isInitial) => {
6110
- var _a, _b;
6111
- const { exit, url, result = null, id: actionId } = action;
6112
- const trackSubmissionEvent = !isInitial ? trackEvent : () => {
6113
- };
6114
- trackSubmissionEvent("Action Triggered", { actionId });
6115
- if (exit && !url) {
6116
- trackSubmissionEvent("Action Succeeded", { actionId });
6117
- return { type: "complete", result };
6118
- }
6119
- try {
6120
- const command = await executeRequest({
6121
- exit,
6122
- request: createRequestFromAction(action, model),
6123
- requestCache,
6124
- httpClient,
6125
- trackEvent: (name, properties) => {
6126
- trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
6163
+ // src/domain/mappers/mapStepSchemas.ts
6164
+ var mapStepSchemas = (uid, step, stepLocalValue, mapperProps) => {
6165
+ return step.schemas.map(
6166
+ (schema, i) => {
6167
+ var _a, _b;
6168
+ return mapSchemaToComponent(
6169
+ {
6170
+ uid: `${uid}.schemas-${i}.${schema.$id}`,
6171
+ schemaId: schema.$id,
6172
+ schema,
6173
+ model: (_a = step.model) != null ? _a : null,
6174
+ localValue: stepLocalValue,
6175
+ validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
6176
+ required: true
6127
6177
  },
6128
- logEvent
6129
- });
6130
- switch (command.type) {
6131
- case "error": {
6132
- trackSubmissionEvent("Action Failed", __spreadValues({
6133
- actionId,
6134
- statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
6135
- }, (_b = command.body) == null ? void 0 : _b.analytics));
6136
- return command;
6137
- }
6138
- case "behavior": {
6139
- if (command.behavior.type === "action") {
6140
- trackSubmissionEvent("Action Succeeded", { actionId });
6141
- return await executeSubmission({
6142
- action: command.behavior.action,
6143
- isInitial: false,
6144
- model: null,
6145
- requestCache,
6146
- httpClient,
6147
- trackEvent,
6148
- logEvent
6149
- });
6150
- }
6151
- trackSubmissionEvent("Action Succeeded", { actionId });
6152
- return command;
6153
- }
6154
- case "complete": {
6155
- trackSubmissionEvent("Action Succeeded", { actionId });
6156
- return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
6157
- }
6158
- case "no-op":
6159
- default: {
6160
- trackSubmissionEvent("Action Succeeded", { actionId });
6161
- return command;
6162
- }
6163
- }
6164
- } catch (error) {
6165
- const errorMessage = getErrorMessage(error);
6166
- trackSubmissionEvent("Action Failed", { actionId, errorMessage });
6167
- logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
6168
- throw error;
6178
+ mapperProps
6179
+ );
6169
6180
  }
6170
- };
6171
- return triggerAction(props.action, props.model, props.isInitial);
6181
+ );
6172
6182
  };
6173
- var createRequestFromAction = (action, model) => {
6174
- var _a, _b, _c;
6175
- return __spreadProps(__spreadValues({}, action), {
6176
- url: (_a = action.url) != null ? _a : "",
6177
- method: (_b = action.method) != null ? _b : "POST",
6178
- body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
6183
+
6184
+ // src/domain/mappers/mapToolbarToComponent.ts
6185
+ var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
6186
+ if (!toolbar) {
6187
+ return void 0;
6188
+ }
6189
+ const { onBehavior } = mapperProps;
6190
+ return __spreadProps(__spreadValues({}, toolbar), {
6191
+ type: "toolbar",
6192
+ uid: `${uid}.toolbar`,
6193
+ tags: toolbar.tags,
6194
+ items: toolbar.items.map((item) => {
6195
+ const context = item.context ? mapLegacyContext(item.context) : void 0;
6196
+ const behavior = getDomainLayerBehavior(
6197
+ { behavior: item.behavior },
6198
+ [],
6199
+ mapperProps.registerSubmissionBehavior
6200
+ );
6201
+ return __spreadProps(__spreadValues({}, item), {
6202
+ context: context ? mapLegacyContext(context) : void 0,
6203
+ disabled: !!item.disabled,
6204
+ tags: item.tags,
6205
+ onClick: () => {
6206
+ void onBehavior(behavior);
6207
+ }
6208
+ });
6209
+ })
6179
6210
  });
6180
6211
  };
6181
6212
 
6182
- // src/domain/features/prefetch/getStepPrefetch.ts
6183
- var getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
6184
- const requestCache = makeRequestCacheWithParent(flowRequestCache);
6185
- const keys = /* @__PURE__ */ new Set();
6186
- const start = (model) => {
6187
- if (keys.size > 0) {
6188
- return;
6189
- }
6190
- submissionBehaviors.forEach((behavior) => {
6191
- const request = behavior.type === "action" ? createRequestFromAction(behavior.action, model) : behavior.launchConfig.request;
6192
- const requestParams = [
6193
- request.url,
6194
- {
6195
- body: JSON.stringify(request.body),
6196
- method: request.method,
6197
- headers: { "Content-Type": "application/json" }
6198
- }
6199
- ];
6200
- try {
6201
- const key = makeRequestCacheKey(requestParams);
6202
- if (keys.has(key)) {
6203
- return;
6204
- }
6205
- const responsePromise = httpClient(...requestParams).catch(() => null);
6206
- requestCache.set(requestParams, responsePromise);
6207
- keys.add(key);
6208
- } catch (e) {
6209
- }
6210
- });
6211
- };
6212
- const stop = () => {
6213
+ // src/domain/mappers/utils/groupLayoutByPinned.ts
6214
+ var groupLayoutByPinned = (layouts) => {
6215
+ return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
6216
+ };
6217
+ var groupLayout = (acc, layout) => {
6218
+ if (layout.type === "button" && layout.pinOrder !== void 0) {
6219
+ return {
6220
+ pinned: [...acc.pinned, layout],
6221
+ nonPinned: acc.nonPinned
6222
+ };
6223
+ }
6224
+ if (hasColumns(layout)) {
6225
+ const leftChildren = groupLayoutByPinned(layout.left);
6226
+ const rightChildren = groupLayoutByPinned(layout.right);
6227
+ return {
6228
+ pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
6229
+ nonPinned: [
6230
+ ...acc.nonPinned,
6231
+ __spreadProps(__spreadValues({}, layout), {
6232
+ left: leftChildren.nonPinned,
6233
+ right: rightChildren.nonPinned
6234
+ })
6235
+ ]
6236
+ };
6237
+ }
6238
+ if (hasChildren(layout)) {
6239
+ const childComponents = groupLayoutByPinned(layout.components);
6240
+ return {
6241
+ pinned: [...acc.pinned, ...childComponents.pinned],
6242
+ nonPinned: [
6243
+ ...acc.nonPinned,
6244
+ __spreadProps(__spreadValues({}, layout), {
6245
+ components: childComponents.nonPinned
6246
+ })
6247
+ ]
6248
+ };
6249
+ }
6250
+ return {
6251
+ pinned: [...acc.pinned],
6252
+ nonPinned: [...acc.nonPinned, layout]
6213
6253
  };
6214
- return { requestCache, start, stop };
6215
6254
  };
6255
+ var hasChildren = (component) => "components" in component;
6256
+ var hasColumns = (component) => "right" in component && "left" in component;
6216
6257
 
6217
6258
  // src/domain/mappers/mapStepToComponent.ts
6218
6259
  var mapStepToComponent = (_a) => {
@@ -6248,12 +6289,7 @@ var mapStepToComponent = (_a) => {
6248
6289
  } = step;
6249
6290
  const submissionBehaviors = [];
6250
6291
  const registerSubmissionBehavior = (behavior) => {
6251
- if (behavior.type === "action" && behavior.action.prefetch) {
6252
- submissionBehaviors.push(behavior);
6253
- }
6254
- if (behavior.type === "subflow" && behavior.launchConfig.type === "dynamic" && behavior.launchConfig.request.prefetch) {
6255
- submissionBehaviors.push(behavior);
6256
- }
6292
+ submissionBehaviors.push(behavior);
6257
6293
  };
6258
6294
  const back = mapBackNavigation(navigation, onBehavior, features.isEnabled("nativeBack"));
6259
6295
  const stepId = id || key;
@@ -6351,64 +6387,6 @@ var getLayoutAndFooter = (step, features) => {
6351
6387
  return { layout, footer: [] };
6352
6388
  };
6353
6389
 
6354
- // src/getSubflowCallbacks.ts
6355
- var getSubflowCallbacks = ({
6356
- behavior,
6357
- close,
6358
- restart,
6359
- onBehavior,
6360
- onError,
6361
- onEvent
6362
- }) => {
6363
- const {
6364
- onCompletion: onCompletionBehavior,
6365
- onError: onErrorBehavior,
6366
- referrerId,
6367
- resultKey
6368
- } = behavior;
6369
- return {
6370
- onCompletion: (result) => {
6371
- var _a;
6372
- restart();
6373
- if (!onCompletionBehavior) {
6374
- close();
6375
- return;
6376
- }
6377
- if (onCompletionBehavior.type === "action") {
6378
- const newActionData = recursiveMerge(
6379
- (_a = onCompletionBehavior.action.data) != null ? _a : null,
6380
- resultKey ? { [resultKey]: result } : result
6381
- );
6382
- void onBehavior(__spreadProps(__spreadValues({}, onCompletionBehavior), {
6383
- action: __spreadProps(__spreadValues({}, onCompletionBehavior.action), { data: newActionData })
6384
- }));
6385
- return;
6386
- }
6387
- void onBehavior(onCompletionBehavior);
6388
- close();
6389
- },
6390
- onError: (error, status) => {
6391
- if (!onErrorBehavior) {
6392
- onError(error, {}, status);
6393
- close();
6394
- return;
6395
- }
6396
- restart();
6397
- void onBehavior(onErrorBehavior);
6398
- close();
6399
- },
6400
- onEvent: (name, properties) => {
6401
- onEvent(name, __spreadValues({
6402
- referrerId
6403
- }, properties));
6404
- },
6405
- onCancellation: () => {
6406
- restart();
6407
- close();
6408
- }
6409
- };
6410
- };
6411
-
6412
6390
  // src/utils/analyse-step.ts
6413
6391
  import { validateStep } from "@wise/dynamic-flow-types/spec";
6414
6392
  var analyseStep = (step, logEvent) => {
@@ -6537,6 +6515,136 @@ var executeRefresh = async (props) => {
6537
6515
  }
6538
6516
  };
6539
6517
 
6518
+ // src/domain/components/SubflowComponent.ts
6519
+ var createSubflowComponent = (subflowProps, onComponentUpdate) => {
6520
+ switch (subflowProps.subflowType) {
6521
+ case "dynamic":
6522
+ return createDynamicSubflowComponent(subflowProps, onComponentUpdate);
6523
+ case "native":
6524
+ return createNativeSubflowComponent(subflowProps);
6525
+ }
6526
+ };
6527
+ var createDynamicSubflowComponent = (subflowProps, onComponentUpdate) => {
6528
+ const update = getInputUpdateFunction(onComponentUpdate);
6529
+ const subflowComponent = __spreadProps(__spreadValues({
6530
+ uid: `subflow-dynamic-${getRandomId()}`,
6531
+ type: "subflow",
6532
+ subflowType: "dynamic",
6533
+ kind: "layout"
6534
+ }, subflowProps), {
6535
+ _update(updateFn) {
6536
+ update(this, updateFn);
6537
+ }
6538
+ });
6539
+ return subflowComponent;
6540
+ };
6541
+ var createNativeSubflowComponent = (subflowProps) => {
6542
+ const subflowComponent = __spreadValues({
6543
+ uid: `subflow-native-${getRandomId()}`,
6544
+ type: "subflow",
6545
+ subflowType: "native",
6546
+ kind: "layout"
6547
+ }, subflowProps);
6548
+ return subflowComponent;
6549
+ };
6550
+
6551
+ // src/controller/executeSubflow.ts
6552
+ var executeSubflow = async ({
6553
+ behavior,
6554
+ rootComponent,
6555
+ trackEvent,
6556
+ onEvent,
6557
+ onChange
6558
+ }) => {
6559
+ rootComponent.setLoadingState("submitting");
6560
+ const { launchConfig } = behavior;
6561
+ const trackSubflowEvent = (eventName, properties) => {
6562
+ if (behavior.launchConfig.type === "native") {
6563
+ trackEvent(`Native Subflow ${eventName}`, __spreadValues({
6564
+ referrerId,
6565
+ nativeSubflowId: behavior.launchConfig.id
6566
+ }, properties));
6567
+ }
6568
+ };
6569
+ const { onCompletion, onError: onErrorBehavior, referrerId, resultKey } = behavior;
6570
+ return new Promise((resolve) => {
6571
+ var _a, _b;
6572
+ const restart = () => {
6573
+ rootComponent.setLoadingState("idle");
6574
+ rootComponent.start();
6575
+ };
6576
+ const close = () => {
6577
+ var _a2;
6578
+ if (((_a2 = rootComponent.subflow) == null ? void 0 : _a2.uid) === component.uid) {
6579
+ rootComponent.closeSubflow();
6580
+ }
6581
+ };
6582
+ const callbacks = {
6583
+ onCompletion: (result) => {
6584
+ var _a2;
6585
+ trackSubflowEvent("Succeeded");
6586
+ restart();
6587
+ if (!onCompletion) {
6588
+ close();
6589
+ resolve({ type: "no-op" });
6590
+ return;
6591
+ }
6592
+ if (onCompletion.type === "action") {
6593
+ const newActionData = recursiveMerge(
6594
+ (_a2 = onCompletion.action.data) != null ? _a2 : null,
6595
+ resultKey ? { [resultKey]: result } : result
6596
+ );
6597
+ resolve({
6598
+ type: "behavior",
6599
+ behavior: __spreadProps(__spreadValues({}, onCompletion), {
6600
+ action: __spreadProps(__spreadValues({}, onCompletion.action), { data: newActionData })
6601
+ })
6602
+ });
6603
+ return;
6604
+ }
6605
+ resolve({ type: "behavior", behavior: onCompletion });
6606
+ close();
6607
+ },
6608
+ onError: (error, status) => {
6609
+ trackSubflowEvent("Failed", { error, status });
6610
+ if (!onErrorBehavior) {
6611
+ resolve({ type: "error", error, status });
6612
+ close();
6613
+ return;
6614
+ }
6615
+ restart();
6616
+ resolve({ type: "behavior", behavior: onErrorBehavior });
6617
+ close();
6618
+ },
6619
+ onEvent: (name, properties) => onEvent == null ? void 0 : onEvent(name, __spreadValues({ referrerId }, properties)),
6620
+ onCancellation: () => {
6621
+ trackSubflowEvent("Cancelled");
6622
+ restart();
6623
+ close();
6624
+ }
6625
+ };
6626
+ const component = launchConfig.type === "dynamic" ? createSubflowComponent(
6627
+ __spreadProps(__spreadValues({}, callbacks), {
6628
+ subflowType: "dynamic",
6629
+ initialRequest: launchConfig.request,
6630
+ requestCache: (_b = (_a = rootComponent.getStep()) == null ? void 0 : _a.requestCache) != null ? _b : rootComponent.requestCache,
6631
+ presentation: launchConfig.presentation
6632
+ }),
6633
+ onChange
6634
+ ) : createSubflowComponent(
6635
+ __spreadProps(__spreadValues({}, callbacks), {
6636
+ subflowType: "native",
6637
+ id: launchConfig.id,
6638
+ payload: launchConfig.payload
6639
+ }),
6640
+ onChange
6641
+ );
6642
+ rootComponent.stop();
6643
+ rootComponent.addSubflow(component);
6644
+ trackSubflowEvent("Triggered");
6645
+ });
6646
+ };
6647
+
6540
6648
  // src/controller/getRequestAbortController.ts
6541
6649
  var getRequestAbortController = () => {
6542
6650
  let abortController = new AbortController();
@@ -6566,8 +6674,6 @@ var getStepCounter = () => {
6566
6674
  };
6567
6675
 
6568
6676
  // src/controller/FlowController.ts
6569
- var noop2 = () => {
6570
- };
6571
6677
  var createFlowController = (props) => {
6572
6678
  const _a = props, {
6573
6679
  flowId,
@@ -6575,9 +6681,9 @@ var createFlowController = (props) => {
6575
6681
  initialStep,
6576
6682
  backConfig,
6577
6683
  features,
6684
+ nativeSubflowHandlers,
6578
6685
  getErrorMessageFunctions,
6579
6686
  scrollToTop,
6580
- httpClient,
6581
6687
  onChange,
6582
6688
  onCancellation,
6583
6689
  onCompletion,
@@ -6593,9 +6699,9 @@ var createFlowController = (props) => {
6593
6699
  "initialStep",
6594
6700
  "backConfig",
6595
6701
  "features",
6702
+ "nativeSubflowHandlers",
6596
6703
  "getErrorMessageFunctions",
6597
6704
  "scrollToTop",
6598
- "httpClient",
6599
6705
  "onChange",
6600
6706
  "onCancellation",
6601
6707
  "onCompletion",
@@ -6606,6 +6712,7 @@ var createFlowController = (props) => {
6606
6712
  "onLog",
6607
6713
  "onValueChange"
6608
6714
  ]);
6715
+ const httpClient = getHttpClientWithCapabilities(props.httpClient, nativeSubflowHandlers);
6609
6716
  const rootComponent = createRootDomainComponent(
6610
6717
  onChange,
6611
6718
  scrollToTop,
@@ -6701,7 +6808,7 @@ var createFlowController = (props) => {
6701
6808
  onCancellation == null ? void 0 : onCancellation();
6702
6809
  };
6703
6810
  const onBehavior = async (behavior) => {
6704
- var _a2, _b, _c;
6811
+ var _a2;
6705
6812
  switch (behavior.type) {
6706
6813
  case "back": {
6707
6814
  if (behavior.action) {
@@ -6793,35 +6900,23 @@ var createFlowController = (props) => {
6793
6900
  rootComponent.dismissModal();
6794
6901
  break;
6795
6902
  case "subflow": {
6796
- rootComponent.setLoadingState("submitting");
6797
- const { launchConfig } = behavior;
6798
- const { request, presentation } = launchConfig;
6799
- const callbacks = getSubflowCallbacks({
6903
+ const command = await executeSubflow({
6800
6904
  behavior,
6801
- close: () => {
6802
- var _a3;
6803
- if (((_a3 = rootComponent.subflow) == null ? void 0 : _a3.uid) === component.uid) {
6804
- rootComponent.closeSubflow();
6805
- }
6806
- },
6807
- onBehavior,
6808
- onError: closeWithError,
6809
- onEvent: onEvent != null ? onEvent : noop2,
6810
- restart: () => {
6811
- rootComponent.setLoadingState("idle");
6812
- rootComponent.start();
6813
- }
6814
- });
6815
- const component = createSubflowDomainComponent(
6816
- __spreadProps(__spreadValues({}, callbacks), {
6817
- initialRequest: request,
6818
- requestCache: (_c = (_b = rootComponent.getStep()) == null ? void 0 : _b.requestCache) != null ? _c : rootComponent.requestCache,
6819
- presentation
6820
- }),
6905
+ rootComponent,
6906
+ trackEvent,
6907
+ onEvent,
6821
6908
  onChange
6822
- );
6823
- rootComponent.stop();
6824
- rootComponent.addSubflow(component);
6909
+ });
6910
+ switch (command.type) {
6911
+ case "behavior":
6912
+ void onBehavior(command.behavior);
6913
+ break;
6914
+ case "error":
6915
+ closeWithError(command.error, {}, command.status);
6916
+ break;
6917
+ case "no-op":
6918
+ break;
6919
+ }
6825
6920
  break;
6826
6921
  }
6827
6922
  case "none":
@@ -6899,7 +6994,6 @@ var createFlowController = (props) => {
6899
6994
  }
6900
6995
  case "behavior": {
6901
6996
  void onBehavior(command.behavior);
6902
- rootComponent.setLoadingState("idle");
6903
6997
  break;
6904
6998
  }
6905
6999
  case "no-op": {
@@ -7025,20 +7119,13 @@ var CoreContainerRenderer = {
7025
7119
  render: ({ children }) => /* @__PURE__ */ jsx3(Fragment2, { children })
7026
7120
  };
7027
7121
 
7028
- // src/renderers/EmptyLoadingStateRenderer.tsx
7029
- import { Fragment as Fragment3, jsx as jsx4 } from "react/jsx-runtime";
7030
- var EmptyLoadingStateRenderer = {
7031
- canRenderType: "loading-state",
7032
- render: () => /* @__PURE__ */ jsx4(Fragment3, {})
7033
- };
7034
-
7035
7122
  // src/renderers/CoreRootRenderer.tsx
7036
- import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
7123
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
7037
7124
  var CoreRootRenderer = {
7038
7125
  canRenderType: "root",
7039
7126
  render: ({ children, childrenProps, subflow }) => {
7040
7127
  if ((subflow == null ? void 0 : subflow.presentation.type) === "push") {
7041
- return /* @__PURE__ */ jsx5(Fragment4, { children: subflow.children });
7128
+ return /* @__PURE__ */ jsx4(Fragment3, { children: subflow.children });
7042
7129
  }
7043
7130
  const [stepProps] = findRendererPropsByType(childrenProps, "step");
7044
7131
  return /* @__PURE__ */ jsxs2("div", { id: stepProps == null ? void 0 : stepProps.id, className: "dynamic-flow-step", children: [
@@ -7048,6 +7135,13 @@ var CoreRootRenderer = {
7048
7135
  }
7049
7136
  };
7050
7137
 
7138
+ // src/renderers/EmptyLoadingStateRenderer.tsx
7139
+ import { Fragment as Fragment4, jsx as jsx5 } from "react/jsx-runtime";
7140
+ var EmptyLoadingStateRenderer = {
7141
+ canRenderType: "loading-state",
7142
+ render: () => /* @__PURE__ */ jsx5(Fragment4, {})
7143
+ };
7144
+
7051
7145
  // src/renderers/getRenderFunction.tsx
7052
7146
  import { Fragment as Fragment5, jsx as jsx6 } from "react/jsx-runtime";
7053
7147
  var getRenderFunction = (renderers) => {
@@ -7923,76 +8017,6 @@ var reviewComponentToProps = (component, rendererMapperProps) => __spreadValues(
7923
8017
  "tags"
7924
8018
  )), rendererMapperProps);
7925
8019
 
7926
- // src/renderers/stepComponentToProps.ts
7927
- var stepComponentToProps = (component, rendererMapperProps) => {
7928
- const { uid, analyticsId, back, control, description, error, step, title, tags, onBehavior } = component;
7929
- const _a = rendererMapperProps, { showBack } = _a, restMapperProps = __objRest(_a, ["showBack"]);
7930
- const childrenProps = component.getChildren().map((c) => componentToRendererProps(c, restMapperProps));
7931
- const footerProps = component.footerComponents.map(
7932
- (c) => componentToRendererProps(c, restMapperProps)
7933
- );
7934
- return __spreadValues({
7935
- type: "step",
7936
- id: step.id,
7937
- analyticsId,
7938
- uid,
7939
- back: showBack ? back : void 0,
7940
- control,
7941
- description,
7942
- toolbar: getToolbarProps(component.toolbar, restMapperProps),
7943
- error,
7944
- __rawStep: step,
7945
- title: restMapperProps.features.isEnabled("hideStepTitle") ? "" : title,
7946
- tags,
7947
- children: childrenProps.map(rendererMapperProps.render),
7948
- childrenProps,
7949
- footer: footerProps.map(rendererMapperProps.render),
7950
- footerProps,
7951
- __onAction: (action) => {
7952
- void onBehavior({ type: "action", action });
7953
- }
7954
- }, restMapperProps);
7955
- };
7956
- var getToolbarProps = (toolbar, rendererMapperProps) => {
7957
- if (!toolbar) {
7958
- return void 0;
7959
- }
7960
- return __spreadValues(__spreadProps(__spreadValues({}, pick(toolbar, "uid", "control", "type", "tags")), {
7961
- type: "toolbar",
7962
- items: toolbar.items.map((item) => __spreadProps(__spreadValues({}, item), {
7963
- disabled: item.disabled || rendererMapperProps.stepLoadingState !== "idle"
7964
- }))
7965
- }), rendererMapperProps);
7966
- };
7967
-
7968
- // src/renderers/mappers/rootComponentToProps.ts
7969
- var rootComponentToProps = (rootComponent, rendererMapperProps) => {
7970
- const childrenProps = [
7971
- __spreadValues({ type: "loading-state", uid: "loading-state" }, rendererMapperProps),
7972
- ...rootComponent.getChildren().map((child) => {
7973
- if (child.type === "step") {
7974
- return stepComponentToProps(child, __spreadProps(__spreadValues({}, rendererMapperProps), {
7975
- showBack: rootComponent.canPerformBack()
7976
- }));
7977
- }
7978
- return componentToRendererProps(child, rendererMapperProps);
7979
- })
7980
- ];
7981
- const subflow = rootComponent.subflow ? {
7982
- presentation: rootComponent.subflow.presentation,
7983
- children: rendererMapperProps.render(
7984
- componentToRendererProps(rootComponent.subflow, rendererMapperProps)
7985
- )
7986
- } : void 0;
7987
- return __spreadValues({
7988
- type: "root",
7989
- uid: rootComponent.uid,
7990
- children: childrenProps.map(rendererMapperProps.render),
7991
- childrenProps,
7992
- subflow
7993
- }, rendererMapperProps);
7994
- };
7995
-
7996
8020
  // src/renderers/mappers/searchComponentToProps.ts
7997
8021
  var searchComponentToProps = (component, rendererMapperProps) => {
7998
8022
  const { uid, control, emptyMessage, error, hint, isLoading, margin, query, title, tags } = component;
@@ -8200,19 +8224,6 @@ var upsellComponentToProps = (component, rendererMapperProps) => __spreadValues(
8200
8224
  onDismiss: component.onDismiss ? component.onDismiss.bind(component) : void 0
8201
8225
  }), rendererMapperProps);
8202
8226
 
8203
- // src/renderers/mappers/subflowComponentToRendererProps.ts
8204
- var subflowComponentToRendererProps = (component, rendererMapperProps) => {
8205
- return __spreadValues(__spreadProps(__spreadValues({
8206
- uid: component.uid,
8207
- type: "subflow"
8208
- }, pick(component, "requestCache", "presentation", "initialRequest")), {
8209
- onCancellation: component.onCancellation.bind(component),
8210
- onCompletion: component.onCompletion.bind(component),
8211
- onError: component.onError.bind(component),
8212
- onEvent: component.onEvent.bind(component)
8213
- }), rendererMapperProps);
8214
- };
8215
-
8216
8227
  // src/renderers/mappers/componentToRendererProps.ts
8217
8228
  var componentToRendererProps = (component, mapperProps) => {
8218
8229
  if (isHiddenComponent(component)) {
@@ -8237,8 +8248,6 @@ var componentToRendererProps = (component, mapperProps) => {
8237
8248
  };
8238
8249
  var getComponentProps = (component, rendererMapperProps) => {
8239
8250
  switch (component.type) {
8240
- case "root":
8241
- return rootComponentToProps(component, rendererMapperProps);
8242
8251
  case "alert":
8243
8252
  return alertComponentToProps(component, rendererMapperProps);
8244
8253
  case "all-of":
@@ -8313,8 +8322,6 @@ var getComponentProps = (component, rendererMapperProps) => {
8313
8322
  return selectInputComponentToProps(component, rendererMapperProps);
8314
8323
  case "status-list":
8315
8324
  return statusListComponentToProps(component, rendererMapperProps);
8316
- case "subflow":
8317
- return subflowComponentToRendererProps(component, rendererMapperProps);
8318
8325
  case "tabs":
8319
8326
  return tabsComponentToProps(component, rendererMapperProps);
8320
8327
  case "text":
@@ -8327,8 +8334,12 @@ var getComponentProps = (component, rendererMapperProps) => {
8327
8334
  return upsellComponentToProps(component, rendererMapperProps);
8328
8335
  case "persist-async":
8329
8336
  return persistAsyncComponentToProps(component, rendererMapperProps);
8337
+ case "root":
8338
+ throw new Error("Root components are only mapped from rootComponentToProps");
8330
8339
  case "step":
8331
- throw Error("Step components are only mapped from rootComponentToProps");
8340
+ throw new Error("Step components are only mapped from rootComponentToProps");
8341
+ case "subflow":
8342
+ throw new Error("Subflow components are only mapped from rootComponentToProps");
8332
8343
  default:
8333
8344
  throw new Error("Unknown component type");
8334
8345
  }
@@ -8341,6 +8352,142 @@ var getComponentAlertProps = (component, rendererMapperProps) => "alert" in comp
8341
8352
  markdown: component.alert.content
8342
8353
  }, rendererMapperProps) : null;
8343
8354
 
8355
+ // src/renderers/stepComponentToProps.ts
8356
+ var stepComponentToProps = (component, rendererMapperProps) => {
8357
+ const { uid, analyticsId, back, control, description, error, step, title, tags, onBehavior } = component;
8358
+ const _a = rendererMapperProps, { showBack } = _a, restMapperProps = __objRest(_a, ["showBack"]);
8359
+ const childrenProps = component.getChildren().map((c) => componentToRendererProps(c, restMapperProps));
8360
+ const footerProps = component.footerComponents.map(
8361
+ (c) => componentToRendererProps(c, restMapperProps)
8362
+ );
8363
+ return __spreadValues({
8364
+ type: "step",
8365
+ id: step.id,
8366
+ analyticsId,
8367
+ uid,
8368
+ back: showBack ? back : void 0,
8369
+ control,
8370
+ description,
8371
+ toolbar: getToolbarProps(component.toolbar, restMapperProps),
8372
+ error,
8373
+ __rawStep: step,
8374
+ title: restMapperProps.features.isEnabled("hideStepTitle") ? "" : title,
8375
+ tags,
8376
+ children: childrenProps.map(rendererMapperProps.render),
8377
+ childrenProps,
8378
+ footer: footerProps.map(rendererMapperProps.render),
8379
+ footerProps,
8380
+ __onAction: (action) => {
8381
+ void onBehavior({ type: "action", action });
8382
+ }
8383
+ }, restMapperProps);
8384
+ };
8385
+ var getToolbarProps = (toolbar, rendererMapperProps) => {
8386
+ if (!toolbar) {
8387
+ return void 0;
8388
+ }
8389
+ return __spreadValues(__spreadProps(__spreadValues({}, pick(toolbar, "uid", "control", "type", "tags")), {
8390
+ type: "toolbar",
8391
+ items: toolbar.items.map((item) => __spreadProps(__spreadValues({}, item), {
8392
+ disabled: item.disabled || rendererMapperProps.stepLoadingState !== "idle"
8393
+ }))
8394
+ }), rendererMapperProps);
8395
+ };
8396
+
8397
+ // src/renderers/mappers/subflowComponentToRendererProps.ts
8398
+ var subflowComponentToRendererProps = (subflow, rendererMapperProps, nativeSubflowHandlers) => {
8399
+ if (subflow.subflowType === "native") {
8400
+ const handler = nativeSubflowHandlers.find((h) => h.id === subflow.id);
8401
+ if (!handler) {
8402
+ throw new Error(
8403
+ `No native subflow handler found for subflow with id "${subflow.id}". Make sure to provide it in the "nativeSubflowHandlers" prop of DynamicFlowCore.`
8404
+ );
8405
+ }
8406
+ const presentationMode = handler.getPresentationMode(subflow.payload);
8407
+ return {
8408
+ presentation: { type: presentationMode },
8409
+ children: rendererMapperProps.render(
8410
+ nativeSubflowComponentToRendererProps(subflow, rendererMapperProps)
8411
+ )
8412
+ };
8413
+ }
8414
+ if (subflow.subflowType === "dynamic") {
8415
+ return {
8416
+ presentation: subflow.presentation,
8417
+ children: rendererMapperProps.render(
8418
+ dynamicSubflowComponentToRendererProps(subflow, rendererMapperProps)
8419
+ )
8420
+ };
8421
+ }
8422
+ };
8423
+ var dynamicSubflowComponentToRendererProps = (component, rendererMapperProps) => {
8424
+ return __spreadValues(__spreadProps(__spreadValues({
8425
+ uid: component.uid,
8426
+ type: "subflow-dynamic"
8427
+ }, pick(component, "requestCache", "presentation", "initialRequest")), {
8428
+ onCancellation: component.onCancellation.bind(component),
8429
+ onCompletion: component.onCompletion.bind(component),
8430
+ onError: component.onError.bind(component),
8431
+ onEvent: component.onEvent.bind(component)
8432
+ }), rendererMapperProps);
8433
+ };
8434
+ var nativeSubflowComponentToRendererProps = (component, rendererMapperProps) => {
8435
+ return __spreadValues({
8436
+ uid: component.uid,
8437
+ type: "subflow-native",
8438
+ id: component.id,
8439
+ payload: component.payload,
8440
+ onCancellation: component.onCancellation.bind(component),
8441
+ onCompletion: component.onCompletion.bind(component),
8442
+ onError: component.onError.bind(component),
8443
+ onEvent: component.onEvent.bind(component)
8444
+ }, rendererMapperProps);
8445
+ };
8446
+
8447
+ // src/renderers/mappers/rootComponentToProps.ts
8448
+ var rootComponentToProps = (rootComponent, rendererMapperProps, nativeSubflowHandlers) => {
8449
+ const childrenProps = [
8450
+ __spreadValues({ type: "loading-state", uid: "loading-state" }, rendererMapperProps),
8451
+ ...rootComponent.getChildren().map((child) => {
8452
+ if (child.type === "step") {
8453
+ return stepComponentToProps(child, __spreadProps(__spreadValues({}, rendererMapperProps), {
8454
+ showBack: rootComponent.canPerformBack()
8455
+ }));
8456
+ }
8457
+ return componentToRendererProps(child, rendererMapperProps);
8458
+ })
8459
+ ];
8460
+ const subflow = rootComponent.subflow ? subflowComponentToRendererProps(
8461
+ rootComponent.subflow,
8462
+ rendererMapperProps,
8463
+ nativeSubflowHandlers
8464
+ ) : void 0;
8465
+ return __spreadValues({
8466
+ type: "root",
8467
+ uid: rootComponent.uid,
8468
+ children: childrenProps.map(rendererMapperProps.render),
8469
+ childrenProps,
8470
+ subflow
8471
+ }, rendererMapperProps);
8472
+ };
8473
+
8474
+ // src/renderers/subflow/getNativeSubflowRenderer.tsx
8475
+ import { Fragment as Fragment6, jsx as jsx7 } from "react/jsx-runtime";
8476
+ var getNativeSubflowRenderer = (handlers) => {
8477
+ return {
8478
+ canRenderType: "subflow-native",
8479
+ render: (props) => /* @__PURE__ */ jsx7(NativeSubflow, __spreadProps(__spreadValues({}, props), { handlers }))
8480
+ };
8481
+ };
8482
+ var NativeSubflow = (propsWithHandler) => {
8483
+ const _a = propsWithHandler, { handlers } = _a, props = __objRest(_a, ["handlers"]);
8484
+ const handler = handlers.find((it) => it.id === props.id);
8485
+ if (!handler) {
8486
+ throw new Error(`No handler found for native subflow with id ${props.id}`);
8487
+ }
8488
+ return /* @__PURE__ */ jsx7(Fragment6, { children: handler.render(props) });
8489
+ };
8490
+
8344
8491
  // src/utils/getScrollToTop.ts
8345
8492
  var getScrollToTop = (normalisedFlowId, className2) => (behavior) => {
8346
8493
  var _a;
@@ -8369,12 +8516,12 @@ function useStableCallback(handler) {
8369
8516
  }
8370
8517
 
8371
8518
  // src/useDynamicFlow.tsx
8372
- import { jsx as jsx7 } from "react/jsx-runtime";
8519
+ import { jsx as jsx8 } from "react/jsx-runtime";
8373
8520
  var className = "dynamic-flow";
8374
- var noop3 = () => {
8521
+ var noop2 = () => {
8375
8522
  };
8376
8523
  function useDynamicFlow(props) {
8377
- var _a;
8524
+ var _a, _b;
8378
8525
  const { flowId, renderers } = props;
8379
8526
  const normalisedFlowId = normaliseFlowId(flowId);
8380
8527
  const scrollToTop = useMemo(
@@ -8394,7 +8541,7 @@ function useDynamicFlow(props) {
8394
8541
  const onError = useStableCallback(props.onError);
8395
8542
  const onEvent = useStableCallback(props.onEvent);
8396
8543
  const onLog = useStableCallback(props.onLog);
8397
- const onValueChange = noop3;
8544
+ const onValueChange = noop2;
8398
8545
  const { formatMessage, locale } = useIntl2();
8399
8546
  const getErrorMessageFunctions = useMemo(
8400
8547
  () => getSchemaErrorMessageFunction(formatMessage, locale),
@@ -8420,33 +8567,45 @@ function useDynamicFlow(props) {
8420
8567
  scrollToTop
8421
8568
  }));
8422
8569
  const render = useMemo(
8423
- () => getRenderFunction([
8424
- CoreRootRenderer,
8425
- CoreContainerRenderer,
8426
- ...renderers,
8427
- EmptyLoadingStateRenderer
8428
- ]),
8570
+ () => {
8571
+ var _a2;
8572
+ return getRenderFunction([
8573
+ CoreRootRenderer,
8574
+ CoreContainerRenderer,
8575
+ getNativeSubflowRenderer((_a2 = props.nativeSubflowHandlers) != null ? _a2 : []),
8576
+ // renderers above this cannot be overridden by the user
8577
+ ...renderers,
8578
+ EmptyLoadingStateRenderer
8579
+ // this can be overridden by the user
8580
+ ]);
8581
+ },
8582
+ // we don't want to react to changes in the nativeSubflowHandlers. they should be set once
8583
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8429
8584
  [renderers]
8430
8585
  );
8431
- const tree = componentToRendererProps(rootComponent, {
8432
- features,
8433
- render,
8434
- httpClient,
8435
- trackEvent: (_a = rootComponent.getTrackEvent()) != null ? _a : (() => {
8436
- }),
8437
- stepLoadingState: rootComponent.getLoadingState()
8438
- });
8586
+ const tree = rootComponentToProps(
8587
+ rootComponent,
8588
+ {
8589
+ features,
8590
+ render,
8591
+ httpClient,
8592
+ trackEvent: (_a = rootComponent.getTrackEvent()) != null ? _a : (() => {
8593
+ }),
8594
+ stepLoadingState: rootComponent.getLoadingState()
8595
+ },
8596
+ (_b = props.nativeSubflowHandlers) != null ? _b : []
8597
+ );
8439
8598
  return {
8440
8599
  controller: {
8441
8600
  getSubmittableValue: async () => rootComponent.getSubmittableValue(),
8442
8601
  validate: () => rootComponent.validate(),
8443
8602
  getCurrentStep: () => {
8444
- var _a2, _b;
8445
- return (_b = (_a2 = rootComponent.getStep()) == null ? void 0 : _a2.step) != null ? _b : null;
8603
+ var _a2, _b2;
8604
+ return (_b2 = (_a2 = rootComponent.getStep()) == null ? void 0 : _a2.step) != null ? _b2 : null;
8446
8605
  },
8447
8606
  cancel
8448
8607
  },
8449
- view: /* @__PURE__ */ jsx7(
8608
+ view: /* @__PURE__ */ jsx8(
8450
8609
  ErrorBoundary_default,
8451
8610
  {
8452
8611
  onError: (error) => {
@@ -8456,7 +8615,7 @@ function useDynamicFlow(props) {
8456
8615
  errorMessage: getErrorMessage(error)
8457
8616
  });
8458
8617
  },
8459
- children: /* @__PURE__ */ jsx7("div", { id: normalisedFlowId, className, children: render(tree) })
8618
+ children: /* @__PURE__ */ jsx8("div", { id: normalisedFlowId, className, children: render(tree) })
8460
8619
  }
8461
8620
  )
8462
8621
  };
@@ -8554,12 +8713,44 @@ var eventNames = [
8554
8713
  "Polling Failed",
8555
8714
  "ValidationAsync Triggered",
8556
8715
  "ValidationAsync Succeeded",
8557
- "ValidationAsync Failed"
8716
+ "ValidationAsync Failed",
8717
+ "Native Subflow Triggered",
8718
+ "Native Subflow Succeeded",
8719
+ "Native Subflow Failed",
8720
+ "Native Subflow Cancelled"
8558
8721
  ];
8722
+
8723
+ // src/renderers/subflow/getDynamicSubflowRenderer.tsx
8724
+ import { jsx as jsx9 } from "react/jsx-runtime";
8725
+ var getDynamicSubflowRenderer = ({
8726
+ Component: Component2,
8727
+ canRender
8728
+ }) => {
8729
+ return {
8730
+ canRenderType: "subflow-dynamic",
8731
+ canRender,
8732
+ render: (props) => {
8733
+ return /* @__PURE__ */ jsx9(
8734
+ Component2,
8735
+ {
8736
+ presentation: props.presentation,
8737
+ requestCache: props.requestCache,
8738
+ initialRequest: props.initialRequest,
8739
+ onCompletion: props.onCompletion,
8740
+ onError: props.onError,
8741
+ onCancellation: props.onCancellation,
8742
+ onEvent: props.onEvent
8743
+ },
8744
+ props.uid
8745
+ );
8746
+ }
8747
+ };
8748
+ };
8559
8749
  export {
8560
8750
  DynamicFlowCore as DynamicFlow,
8561
8751
  eventNames,
8562
8752
  findRendererPropsByType,
8753
+ getDynamicSubflowRenderer,
8563
8754
  makeHttpClient,
8564
8755
  makeRequestCache,
8565
8756
  i18n_default as translations,