@wise/dynamic-flow-client 5.14.1 → 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 -771
  3. package/build/main.mjs +1184 -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.js CHANGED
@@ -59,6 +59,7 @@ __export(index_exports, {
59
59
  DynamicFlow: () => DynamicFlowCore,
60
60
  eventNames: () => eventNames,
61
61
  findRendererPropsByType: () => findRendererPropsByType,
62
+ getDynamicSubflowRenderer: () => getDynamicSubflowRenderer,
62
63
  makeHttpClient: () => makeHttpClient,
63
64
  makeRequestCache: () => makeRequestCache,
64
65
  translations: () => i18n_default,
@@ -855,7 +856,8 @@ var getChildren = (node) => {
855
856
  case "input-text":
856
857
  case "input-upload":
857
858
  case "external-confirmation":
858
- case "subflow":
859
+ case "subflow-dynamic":
860
+ case "subflow-native":
859
861
  case "upsell":
860
862
  return [];
861
863
  }
@@ -941,6 +943,47 @@ var ErrorBoundary = class extends import_react.Component {
941
943
  };
942
944
  var ErrorBoundary_default = ErrorBoundary;
943
945
 
946
+ // src/common/capabilities/getHttpClientWithCapabilities.ts
947
+ var getHttpClientWithCapabilities = (httpClient, nativeSubflowHandlers = []) => {
948
+ const headers = getCapabilitiesHeader(nativeSubflowHandlers);
949
+ return async (input, init) => {
950
+ return headers && isRelativeOrSameOrigin(input) ? httpClient(input, mergeRequestInit(init != null ? init : {}, { headers })) : httpClient(input, init);
951
+ };
952
+ };
953
+ var getCapabilitiesHeader = (nativeSubflowHandlers) => {
954
+ const capabilities = nativeSubflowHandlers.map(getCapability);
955
+ return capabilities.length > 0 ? { "X-DF-Native-Capabilities": capabilities.join(";") } : null;
956
+ };
957
+ var getCapability = (handler) => {
958
+ return `${encode(handler.id)}${getCapabilityComments(handler)}`;
959
+ };
960
+ var getCapabilityComments = (handler) => {
961
+ var _a, _b;
962
+ const comments = Object.entries((_b = (_a = handler.getCapabilityComments) == null ? void 0 : _a.call(handler)) != null ? _b : {}).map(([key, value]) => {
963
+ return `${encode(key)}:${encode(value)}`;
964
+ });
965
+ return comments.length > 0 ? `(${comments.join(",")})` : "";
966
+ };
967
+ var isRelativeOrSameOrigin = (input) => {
968
+ var _a;
969
+ if (typeof input === "string") {
970
+ return isRelativePath2(input) || input.startsWith(`${(_a = window == null ? void 0 : window.location) == null ? void 0 : _a.origin}/`);
971
+ }
972
+ if (input instanceof Request) {
973
+ return isRelativeOrSameOrigin(input.url);
974
+ }
975
+ if (input instanceof URL) {
976
+ return isRelativeOrSameOrigin(input.toString());
977
+ }
978
+ return false;
979
+ };
980
+ var isRelativePath2 = (url = "") => {
981
+ return !["https://", "http://", "data:"].some(
982
+ (prefix) => url.startsWith(prefix) && url.length > prefix.length
983
+ );
984
+ };
985
+ var encode = (value) => encodeURIComponent(value).replace(/\(/g, "%28").replace(/\)/g, "%29");
986
+
944
987
  // src/domain/components/utils/component-utils.ts
945
988
  var getInputUpdateFunction = (onComponentUpdate) => {
946
989
  return (component, updateFn) => {
@@ -1029,6 +1072,9 @@ var createRootDomainComponent = (onComponentUpdate, scrollToTop, backConfig, req
1029
1072
  });
1030
1073
  },
1031
1074
  closeSubflow() {
1075
+ if (!this.subflow) {
1076
+ return;
1077
+ }
1032
1078
  this._update((draft) => {
1033
1079
  draft.subflow = null;
1034
1080
  });
@@ -1119,24 +1165,6 @@ var shouldScrollOnStepUpdate = (newStep, currentStep) => {
1119
1165
  return newStep.error != null && newStep.error !== currentStep.error;
1120
1166
  };
1121
1167
 
1122
- // src/domain/components/utils/getRandomId.ts
1123
- var getRandomId = () => Math.random().toString(36).substring(2);
1124
-
1125
- // src/domain/components/SubflowDomainComponent.ts
1126
- var createSubflowDomainComponent = (subflowProps, onComponentUpdate) => {
1127
- const update = getInputUpdateFunction(onComponentUpdate);
1128
- const subflowComponent = __spreadProps(__spreadValues({
1129
- uid: `subflow-${getRandomId()}`,
1130
- type: "subflow",
1131
- kind: "layout"
1132
- }, subflowProps), {
1133
- _update(updateFn) {
1134
- update(this, updateFn);
1135
- }
1136
- });
1137
- return subflowComponent;
1138
- };
1139
-
1140
1168
  // src/domain/features/prefetch/request-cache.ts
1141
1169
  var makeRequestCacheWithParent = (parent) => {
1142
1170
  const map = /* @__PURE__ */ new Map();
@@ -1209,6 +1237,9 @@ var createModalContentComponent = (modalProps, onComponentUpdate) => {
1209
1237
  return modalContentComponent;
1210
1238
  };
1211
1239
 
1240
+ // src/domain/components/utils/getRandomId.ts
1241
+ var getRandomId = () => Math.random().toString(36).substring(2);
1242
+
1212
1243
  // src/domain/components/AlertComponent.ts
1213
1244
  var createAlertComponent = (alertProps) => __spreadValues({
1214
1245
  type: "alert",
@@ -2695,120 +2726,461 @@ var getStepPolling = ({
2695
2726
  return { start, stop };
2696
2727
  };
2697
2728
 
2698
- // src/domain/features/refreshAfter/getStepRefreshAfter.ts
2699
- var ONE_SECOND = 1e3;
2700
- var getStepRefreshAfter = ({
2701
- refreshAfter,
2702
- logEvent,
2703
- onBehavior
2704
- }) => {
2705
- let timeout = null;
2706
- const targetTime = new Date(refreshAfter).getTime();
2707
- if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
2708
- throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
2709
- }
2710
- const start = () => {
2711
- const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
2712
- timeout = setTimeout(() => {
2713
- void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
2714
- }, timeLeft);
2715
- };
2716
- return {
2717
- start,
2718
- stop: () => {
2719
- if (!timeout) {
2720
- logEvent("warning", "Attempted to stop refreshAfter but it was not started");
2721
- return;
2722
- }
2723
- clearTimeout(timeout);
2724
- }
2725
- };
2729
+ // src/controller/getErrorMessage.ts
2730
+ var getErrorMessage = (error) => {
2731
+ return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
2726
2732
  };
2727
2733
 
2728
- // src/domain/components/utils/isOrWasValid.ts
2729
- var isOrWasValid = (getErrors, previous, current) => {
2730
- const wasValid = getErrors(previous).length === 0 && previous !== null;
2731
- const isValid = getErrors(current).length === 0;
2732
- return wasValid || isValid;
2734
+ // src/controller/response-utils.ts
2735
+ var import_spec = require("@wise/dynamic-flow-types/spec");
2736
+ var parseResponseBodyAsJsonElement = async (response) => {
2737
+ assertResponseIsValid(response);
2738
+ try {
2739
+ return await response.json();
2740
+ } catch (e) {
2741
+ return null;
2742
+ }
2733
2743
  };
2734
-
2735
- // src/domain/features/schema-on-change/getDebouncedSchemaOnChange.ts
2736
- var debounceDelay = 1e3;
2737
- var getDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
2738
- if (!onChange) {
2739
- return void 0;
2744
+ var parseResponseBodyAsText = async (response) => {
2745
+ try {
2746
+ return await response.text();
2747
+ } catch (e) {
2748
+ return null;
2740
2749
  }
2741
- const performOnChange = onChange.behaviorType === "refresh" ? getSelectiveDebouncedSchemaOnChange(onChange, getValidationErrors) : debounce(onChange, debounceDelay);
2742
- return performOnChange;
2743
2750
  };
2744
- var getSelectiveDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
2745
- let behaviorControl = { abort: () => {
2746
- } };
2747
- let timeoutId = null;
2748
- const clearTimer = () => {
2749
- if (timeoutId) {
2750
- clearTimeout(timeoutId);
2751
- }
2752
- timeoutId = null;
2753
- };
2754
- const flush = () => {
2755
- if (timeoutId !== null) {
2756
- void onChange().then((bc) => {
2757
- behaviorControl = bc;
2758
- });
2759
- clearTimer();
2760
- }
2761
- };
2762
- const debouncedOnChange = (prevValue, updatedValue) => {
2763
- var _a;
2764
- const valid = isOrWasValid(getValidationErrors, prevValue, updatedValue);
2765
- const isPending = timeoutId !== null;
2766
- if (valid || isPending) {
2767
- (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
2768
- clearTimer();
2769
- timeoutId = setTimeout(() => flush(), debounceDelay);
2751
+ function isActionResponseBody(body) {
2752
+ return (0, import_spec.validateActionResponse)(body).valid;
2753
+ }
2754
+ function assertActionResponseBody(body) {
2755
+ if (!isObject(body) || !isObject(body.action)) {
2756
+ throw new Error(
2757
+ "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
2758
+ );
2759
+ }
2760
+ }
2761
+ function assertModalResponseBody(body) {
2762
+ if (isObject(body)) {
2763
+ if ("content" in body && isArray(body.content)) {
2764
+ return;
2770
2765
  }
2771
- };
2772
- debouncedOnChange.cancel = () => {
2773
- var _a;
2774
- clearTimer();
2775
- (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
2776
- };
2777
- debouncedOnChange.flush = () => {
2778
- flush();
2779
- };
2780
- return debouncedOnChange;
2766
+ }
2767
+ throw new Error(
2768
+ "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
2769
+ );
2770
+ }
2771
+ function assertSubflowResponseBody(body) {
2772
+ const { valid } = (0, import_spec.validateSubflowResponse)(body);
2773
+ if (valid) {
2774
+ return;
2775
+ }
2776
+ throw new Error("Incorrect response body in subflow response.");
2777
+ }
2778
+ function isErrorResponseBody(body) {
2779
+ return Boolean(
2780
+ isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
2781
+ );
2782
+ }
2783
+ function assertStepResponseBody(body) {
2784
+ if (!isObject(body)) {
2785
+ throw new Error("Incorrect response body in step response. Expected an object.");
2786
+ }
2787
+ }
2788
+ var assertResponseIsValid = (response) => {
2789
+ if (!isResponse(response)) {
2790
+ throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
2791
+ }
2792
+ if (response.bodyUsed) {
2793
+ throw new Error(
2794
+ "The body of the provided Response object has already been used. Every request must respond with a new Response object."
2795
+ );
2796
+ }
2781
2797
  };
2798
+ var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
2782
2799
 
2783
- // src/domain/features/validation/validation-functions.ts
2784
- var validateComponents = (components) => components.reduce((acc, component) => component.validate() && acc, true);
2785
- var getLocalValueValidator = (checks) => (currentValue) => checks.map((check) => check(currentValue)).filter(isString);
2786
-
2787
- // src/domain/features/utils/http-utils.ts
2788
- function constructPayload({
2789
- value,
2790
- signal,
2791
- requestConfig
2792
- }) {
2793
- const isFile2 = value instanceof File;
2794
- const { method, param } = requestConfig;
2795
- const body = isFile2 ? wrapInFormData(param, value) : JSON.stringify({ [param]: value });
2796
- const headers = __spreadValues({}, !isFile2 && { "Content-Type": "application/json" });
2800
+ // src/controller/handleErrorResponse.ts
2801
+ var handleErrorResponse = async (response, actionId, trackEvent) => {
2802
+ const body = await parseResponseBodyAsJsonElement(response.clone());
2803
+ if (isErrorResponseBody(body)) {
2804
+ const refreshUrl = body.refreshUrl || body.refreshFormUrl;
2805
+ const { error, validation, analytics } = body;
2806
+ trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
2807
+ const errors = { error, validation };
2808
+ return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
2809
+ type: "error",
2810
+ body: { errors, analytics },
2811
+ httpError: { statusCode: response.status }
2812
+ };
2813
+ }
2814
+ trackEvent("Action Failed", { actionId, statusCode: response.status });
2815
+ const errorMessage = await parseResponseBodyAsText(response);
2797
2816
  return {
2798
- method,
2799
- headers,
2800
- body,
2801
- signal
2817
+ type: "error",
2818
+ httpError: {
2819
+ message: errorMessage || void 0,
2820
+ statusCode: response.status
2821
+ }
2802
2822
  };
2803
- }
2804
- var wrapInFormData = (key, file) => {
2805
- const formData = new FormData();
2806
- formData.append(key, file);
2807
- return formData;
2808
2823
  };
2809
- var abortAndResetController = (abortController) => {
2810
- abortController.abort();
2811
- return new AbortController();
2824
+
2825
+ // src/controller/getResponseType.ts
2826
+ var responseTypes = ["step", "action", "exit", "modal", "subflow", "no-op"];
2827
+ var getResponseType = (headers, body) => {
2828
+ const headerResponseType = getResponseTypeFromHeader(headers);
2829
+ if (headerResponseType) {
2830
+ return headerResponseType;
2831
+ }
2832
+ if (isObject(body) && body.action) {
2833
+ return "action";
2834
+ }
2835
+ return "step";
2836
+ };
2837
+ var getResponseTypeFromHeader = (headers) => {
2838
+ if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
2839
+ const type = headers.get("X-Df-Response-Type");
2840
+ assertDFResponseType(type);
2841
+ return type;
2842
+ }
2843
+ if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
2844
+ return "exit";
2845
+ }
2846
+ return void 0;
2847
+ };
2848
+ function assertDFResponseType(type) {
2849
+ if (!responseTypes.includes(type)) {
2850
+ throw new Error(
2851
+ "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'modal', 'subflow', 'no-op'."
2852
+ );
2853
+ }
2854
+ }
2855
+
2856
+ // src/controller/makeSafeHttpClient.ts
2857
+ var makeSafeHttpClient = (httpClient) => async (...props) => {
2858
+ try {
2859
+ return await httpClient(...props);
2860
+ } catch (e) {
2861
+ return null;
2862
+ }
2863
+ };
2864
+
2865
+ // src/controller/executeRequest.ts
2866
+ var executeRequest = async (props) => {
2867
+ const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
2868
+ const { url, method, body } = request;
2869
+ const response = await getCachedOrFetch(
2870
+ [
2871
+ url,
2872
+ {
2873
+ method,
2874
+ body: body ? JSON.stringify(body) : void 0,
2875
+ headers: { "Content-Type": "application/json" }
2876
+ }
2877
+ ],
2878
+ requestCache,
2879
+ httpClient
2880
+ );
2881
+ if (!response) {
2882
+ const extra = { errorMessage: "Network Error" };
2883
+ trackEvent("Request Failed", extra);
2884
+ logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
2885
+ return { type: "error" };
2886
+ }
2887
+ if (!response.ok) {
2888
+ return handleErrorResponse(response, void 0, trackEvent);
2889
+ }
2890
+ const responseBody = await parseResponseBodyAsJsonElement(response);
2891
+ const responseType = getResponseType(response.headers, responseBody);
2892
+ if (exit) {
2893
+ return { type: "complete", result: responseBody };
2894
+ }
2895
+ switch (responseType) {
2896
+ case "step": {
2897
+ const etag = response.headers.get("etag") || null;
2898
+ assertStepResponseBody(responseBody);
2899
+ return { type: "replace-step", step: responseBody, etag };
2900
+ }
2901
+ case "exit": {
2902
+ return { type: "complete", result: responseBody };
2903
+ }
2904
+ case "action": {
2905
+ assertActionResponseBody(responseBody);
2906
+ return {
2907
+ type: "behavior",
2908
+ behavior: {
2909
+ type: "action",
2910
+ action: responseBody.action
2911
+ }
2912
+ };
2913
+ }
2914
+ case "subflow": {
2915
+ assertSubflowResponseBody(responseBody);
2916
+ return {
2917
+ type: "behavior",
2918
+ behavior: __spreadProps(__spreadValues({}, responseBody), {
2919
+ type: "subflow",
2920
+ onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
2921
+ onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
2922
+ })
2923
+ };
2924
+ }
2925
+ case "modal": {
2926
+ assertModalResponseBody(responseBody);
2927
+ return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
2928
+ }
2929
+ case "no-op": {
2930
+ return { type: "no-op" };
2931
+ }
2932
+ default: {
2933
+ throw new Error(`Unsupported response type: ${String(responseType)}`);
2934
+ }
2935
+ }
2936
+ };
2937
+ var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
2938
+ const cachedPromise = requestCache.get(requestParams);
2939
+ if (cachedPromise) {
2940
+ const cachedResponse = await cachedPromise;
2941
+ if (cachedResponse == null ? void 0 : cachedResponse.ok) {
2942
+ return cachedResponse;
2943
+ }
2944
+ }
2945
+ return makeSafeHttpClient(httpClient)(...requestParams);
2946
+ };
2947
+
2948
+ // src/controller/executeSubmission.ts
2949
+ var executeSubmission = async (props) => {
2950
+ const { httpClient, requestCache, trackEvent, logEvent } = props;
2951
+ const triggerAction = async (action, model, isInitial) => {
2952
+ var _a, _b;
2953
+ const { exit, url, result = null, id: actionId } = action;
2954
+ const trackSubmissionEvent = !isInitial ? trackEvent : () => {
2955
+ };
2956
+ trackSubmissionEvent("Action Triggered", { actionId });
2957
+ if (exit && !url) {
2958
+ trackSubmissionEvent("Action Succeeded", { actionId });
2959
+ return { type: "complete", result };
2960
+ }
2961
+ try {
2962
+ const command = await executeRequest({
2963
+ exit,
2964
+ request: createRequestFromAction(action, model),
2965
+ requestCache,
2966
+ httpClient,
2967
+ trackEvent: (name, properties) => {
2968
+ trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
2969
+ },
2970
+ logEvent
2971
+ });
2972
+ switch (command.type) {
2973
+ case "error": {
2974
+ trackSubmissionEvent("Action Failed", __spreadValues({
2975
+ actionId,
2976
+ statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
2977
+ }, (_b = command.body) == null ? void 0 : _b.analytics));
2978
+ return command;
2979
+ }
2980
+ case "behavior": {
2981
+ if (command.behavior.type === "action") {
2982
+ trackSubmissionEvent("Action Succeeded", { actionId });
2983
+ return await executeSubmission({
2984
+ action: command.behavior.action,
2985
+ isInitial: false,
2986
+ model: null,
2987
+ requestCache,
2988
+ httpClient,
2989
+ trackEvent,
2990
+ logEvent
2991
+ });
2992
+ }
2993
+ trackSubmissionEvent("Action Succeeded", { actionId });
2994
+ return command;
2995
+ }
2996
+ case "complete": {
2997
+ trackSubmissionEvent("Action Succeeded", { actionId });
2998
+ return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
2999
+ }
3000
+ case "no-op":
3001
+ default: {
3002
+ trackSubmissionEvent("Action Succeeded", { actionId });
3003
+ return command;
3004
+ }
3005
+ }
3006
+ } catch (error) {
3007
+ const errorMessage = getErrorMessage(error);
3008
+ trackSubmissionEvent("Action Failed", { actionId, errorMessage });
3009
+ logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
3010
+ throw error;
3011
+ }
3012
+ };
3013
+ return triggerAction(props.action, props.model, props.isInitial);
3014
+ };
3015
+ var createRequestFromAction = (action, model) => {
3016
+ var _a, _b, _c;
3017
+ return __spreadProps(__spreadValues({}, action), {
3018
+ url: (_a = action.url) != null ? _a : "",
3019
+ method: (_b = action.method) != null ? _b : "POST",
3020
+ body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
3021
+ });
3022
+ };
3023
+
3024
+ // src/domain/features/prefetch/getStepPrefetch.ts
3025
+ var getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
3026
+ const requestCache = makeRequestCacheWithParent(flowRequestCache);
3027
+ const keys = /* @__PURE__ */ new Set();
3028
+ const start = (model) => {
3029
+ if (keys.size > 0) {
3030
+ return;
3031
+ }
3032
+ submissionBehaviors.forEach((behavior) => {
3033
+ const request = getPrefetchableRequest(behavior, model);
3034
+ if (request) {
3035
+ const requestParams = [
3036
+ request.url,
3037
+ {
3038
+ body: JSON.stringify(request.body),
3039
+ method: request.method,
3040
+ headers: { "Content-Type": "application/json" }
3041
+ }
3042
+ ];
3043
+ try {
3044
+ const key = makeRequestCacheKey(requestParams);
3045
+ if (keys.has(key)) {
3046
+ return;
3047
+ }
3048
+ const responsePromise = httpClient(...requestParams).catch(() => null);
3049
+ requestCache.set(requestParams, responsePromise);
3050
+ keys.add(key);
3051
+ } catch (e) {
3052
+ }
3053
+ }
3054
+ });
3055
+ };
3056
+ const stop = () => {
3057
+ };
3058
+ return { requestCache, start, stop };
3059
+ };
3060
+ var getPrefetchableRequest = (behavior, model) => {
3061
+ if (behavior.type === "action" && behavior.action.prefetch) {
3062
+ return createRequestFromAction(behavior.action, model);
3063
+ }
3064
+ if (behavior.type === "subflow" && behavior.launchConfig.type === "dynamic" && behavior.launchConfig.request.prefetch) {
3065
+ return behavior.launchConfig.request;
3066
+ }
3067
+ return null;
3068
+ };
3069
+
3070
+ // src/domain/features/refreshAfter/getStepRefreshAfter.ts
3071
+ var ONE_SECOND = 1e3;
3072
+ var getStepRefreshAfter = ({
3073
+ refreshAfter,
3074
+ logEvent,
3075
+ onBehavior
3076
+ }) => {
3077
+ let timeout = null;
3078
+ const targetTime = new Date(refreshAfter).getTime();
3079
+ if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
3080
+ throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
3081
+ }
3082
+ const start = () => {
3083
+ const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
3084
+ timeout = setTimeout(() => {
3085
+ void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
3086
+ }, timeLeft);
3087
+ };
3088
+ return {
3089
+ start,
3090
+ stop: () => {
3091
+ if (!timeout) {
3092
+ logEvent("warning", "Attempted to stop refreshAfter but it was not started");
3093
+ return;
3094
+ }
3095
+ clearTimeout(timeout);
3096
+ }
3097
+ };
3098
+ };
3099
+
3100
+ // src/domain/components/utils/isOrWasValid.ts
3101
+ var isOrWasValid = (getErrors, previous, current) => {
3102
+ const wasValid = getErrors(previous).length === 0 && previous !== null;
3103
+ const isValid = getErrors(current).length === 0;
3104
+ return wasValid || isValid;
3105
+ };
3106
+
3107
+ // src/domain/features/schema-on-change/getDebouncedSchemaOnChange.ts
3108
+ var debounceDelay = 1e3;
3109
+ var getDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
3110
+ if (!onChange) {
3111
+ return void 0;
3112
+ }
3113
+ const performOnChange = onChange.behaviorType === "refresh" ? getSelectiveDebouncedSchemaOnChange(onChange, getValidationErrors) : debounce(onChange, debounceDelay);
3114
+ return performOnChange;
3115
+ };
3116
+ var getSelectiveDebouncedSchemaOnChange = (onChange, getValidationErrors) => {
3117
+ let behaviorControl = { abort: () => {
3118
+ } };
3119
+ let timeoutId = null;
3120
+ const clearTimer = () => {
3121
+ if (timeoutId) {
3122
+ clearTimeout(timeoutId);
3123
+ }
3124
+ timeoutId = null;
3125
+ };
3126
+ const flush = () => {
3127
+ if (timeoutId !== null) {
3128
+ void onChange().then((bc) => {
3129
+ behaviorControl = bc;
3130
+ });
3131
+ clearTimer();
3132
+ }
3133
+ };
3134
+ const debouncedOnChange = (prevValue, updatedValue) => {
3135
+ var _a;
3136
+ const valid = isOrWasValid(getValidationErrors, prevValue, updatedValue);
3137
+ const isPending = timeoutId !== null;
3138
+ if (valid || isPending) {
3139
+ (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
3140
+ clearTimer();
3141
+ timeoutId = setTimeout(() => flush(), debounceDelay);
3142
+ }
3143
+ };
3144
+ debouncedOnChange.cancel = () => {
3145
+ var _a;
3146
+ clearTimer();
3147
+ (_a = behaviorControl.abort) == null ? void 0 : _a.call(behaviorControl);
3148
+ };
3149
+ debouncedOnChange.flush = () => {
3150
+ flush();
3151
+ };
3152
+ return debouncedOnChange;
3153
+ };
3154
+
3155
+ // src/domain/features/validation/validation-functions.ts
3156
+ var validateComponents = (components) => components.reduce((acc, component) => component.validate() && acc, true);
3157
+ var getLocalValueValidator = (checks) => (currentValue) => checks.map((check) => check(currentValue)).filter(isString);
3158
+
3159
+ // src/domain/features/utils/http-utils.ts
3160
+ function constructPayload({
3161
+ value,
3162
+ signal,
3163
+ requestConfig
3164
+ }) {
3165
+ const isFile2 = value instanceof File;
3166
+ const { method, param } = requestConfig;
3167
+ const body = isFile2 ? wrapInFormData(param, value) : JSON.stringify({ [param]: value });
3168
+ const headers = __spreadValues({}, !isFile2 && { "Content-Type": "application/json" });
3169
+ return {
3170
+ method,
3171
+ headers,
3172
+ body,
3173
+ signal
3174
+ };
3175
+ }
3176
+ var wrapInFormData = (key, file) => {
3177
+ const formData = new FormData();
3178
+ formData.append(key, file);
3179
+ return formData;
3180
+ };
3181
+ var abortAndResetController = (abortController) => {
3182
+ abortController.abort();
3183
+ return new AbortController();
2812
3184
  };
2813
3185
 
2814
3186
  // src/domain/features/validationAsync/getComponentValidationAsync.ts
@@ -3199,75 +3571,9 @@ var autocompleteTokenMap = {
3199
3571
  // src/domain/features/validationAsync/getInitialValidationAsyncState.ts
3200
3572
  var getInitialValidationAsyncState = () => ({
3201
3573
  abortController: new AbortController(),
3202
- lastSubmitted: null,
3203
- messages: {}
3204
- });
3205
-
3206
- // src/controller/response-utils.ts
3207
- var import_spec = require("@wise/dynamic-flow-types/spec");
3208
- var parseResponseBodyAsJsonElement = async (response) => {
3209
- assertResponseIsValid(response);
3210
- try {
3211
- return await response.json();
3212
- } catch (e) {
3213
- return null;
3214
- }
3215
- };
3216
- var parseResponseBodyAsText = async (response) => {
3217
- try {
3218
- return await response.text();
3219
- } catch (e) {
3220
- return null;
3221
- }
3222
- };
3223
- function isActionResponseBody(body) {
3224
- return (0, import_spec.validateActionResponse)(body).valid;
3225
- }
3226
- function assertActionResponseBody(body) {
3227
- if (!isObject(body) || !isObject(body.action)) {
3228
- throw new Error(
3229
- "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
3230
- );
3231
- }
3232
- }
3233
- function assertModalResponseBody(body) {
3234
- if (isObject(body)) {
3235
- if ("content" in body && isArray(body.content)) {
3236
- return;
3237
- }
3238
- }
3239
- throw new Error(
3240
- "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
3241
- );
3242
- }
3243
- function assertSubflowResponseBody(body) {
3244
- const { valid } = (0, import_spec.validateSubflowResponse)(body);
3245
- if (valid) {
3246
- return;
3247
- }
3248
- throw new Error("Incorrect response body in subflow response.");
3249
- }
3250
- function isErrorResponseBody(body) {
3251
- return Boolean(
3252
- isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
3253
- );
3254
- }
3255
- function assertStepResponseBody(body) {
3256
- if (!isObject(body)) {
3257
- throw new Error("Incorrect response body in step response. Expected an object.");
3258
- }
3259
- }
3260
- var assertResponseIsValid = (response) => {
3261
- if (!isResponse(response)) {
3262
- throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
3263
- }
3264
- if (response.bodyUsed) {
3265
- throw new Error(
3266
- "The body of the provided Response object has already been used. Every request must respond with a new Response object."
3267
- );
3268
- }
3269
- };
3270
- var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
3574
+ lastSubmitted: null,
3575
+ messages: {}
3576
+ });
3271
3577
 
3272
3578
  // src/domain/features/utils/response-utils.ts
3273
3579
  var getAnalyticsFromErrorResponse = (json) => {
@@ -5868,381 +6174,117 @@ var mapSchemaToComponent = (schemaMapperProps, mapperProps) => {
5868
6174
  if (isNumberSchema(schema)) {
5869
6175
  return numberSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5870
6176
  }
5871
- if (isStringSchema(schema)) {
5872
- return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5873
- }
5874
- if (isArraySchema(schema)) {
5875
- return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5876
- }
5877
- if (isBlobSchema(schema)) {
5878
- if (!schemaMapperProps.onPersistAsync) {
5879
- throw new Error(
5880
- "Blob schemas can only be used as the schema of a persist async configuration."
5881
- );
5882
- }
5883
- return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5884
- }
5885
- throw new Error("Not yet supported");
5886
- };
5887
-
5888
- // src/domain/mappers/mapStepSchemas.ts
5889
- var mapStepSchemas = (uid, step, stepLocalValue, mapperProps) => {
5890
- return step.schemas.map(
5891
- (schema, i) => {
5892
- var _a, _b;
5893
- return mapSchemaToComponent(
5894
- {
5895
- uid: `${uid}.schemas-${i}.${schema.$id}`,
5896
- schemaId: schema.$id,
5897
- schema,
5898
- model: (_a = step.model) != null ? _a : null,
5899
- localValue: stepLocalValue,
5900
- validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
5901
- required: true
5902
- },
5903
- mapperProps
5904
- );
5905
- }
5906
- );
5907
- };
5908
-
5909
- // src/domain/mappers/mapToolbarToComponent.ts
5910
- var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
5911
- if (!toolbar) {
5912
- return void 0;
5913
- }
5914
- const { onBehavior } = mapperProps;
5915
- return __spreadProps(__spreadValues({}, toolbar), {
5916
- type: "toolbar",
5917
- uid: `${uid}.toolbar`,
5918
- tags: toolbar.tags,
5919
- items: toolbar.items.map((item) => {
5920
- const context = item.context ? mapLegacyContext(item.context) : void 0;
5921
- const behavior = getDomainLayerBehavior(
5922
- { behavior: item.behavior },
5923
- [],
5924
- mapperProps.registerSubmissionBehavior
5925
- );
5926
- return __spreadProps(__spreadValues({}, item), {
5927
- context: context ? mapLegacyContext(context) : void 0,
5928
- disabled: !!item.disabled,
5929
- tags: item.tags,
5930
- onClick: () => {
5931
- void onBehavior(behavior);
5932
- }
5933
- });
5934
- })
5935
- });
5936
- };
5937
-
5938
- // src/domain/mappers/utils/groupLayoutByPinned.ts
5939
- var groupLayoutByPinned = (layouts) => {
5940
- return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
5941
- };
5942
- var groupLayout = (acc, layout) => {
5943
- if (layout.type === "button" && layout.pinOrder !== void 0) {
5944
- return {
5945
- pinned: [...acc.pinned, layout],
5946
- nonPinned: acc.nonPinned
5947
- };
5948
- }
5949
- if (hasColumns(layout)) {
5950
- const leftChildren = groupLayoutByPinned(layout.left);
5951
- const rightChildren = groupLayoutByPinned(layout.right);
5952
- return {
5953
- pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
5954
- nonPinned: [
5955
- ...acc.nonPinned,
5956
- __spreadProps(__spreadValues({}, layout), {
5957
- left: leftChildren.nonPinned,
5958
- right: rightChildren.nonPinned
5959
- })
5960
- ]
5961
- };
5962
- }
5963
- if (hasChildren(layout)) {
5964
- const childComponents = groupLayoutByPinned(layout.components);
5965
- return {
5966
- pinned: [...acc.pinned, ...childComponents.pinned],
5967
- nonPinned: [
5968
- ...acc.nonPinned,
5969
- __spreadProps(__spreadValues({}, layout), {
5970
- components: childComponents.nonPinned
5971
- })
5972
- ]
5973
- };
5974
- }
5975
- return {
5976
- pinned: [...acc.pinned],
5977
- nonPinned: [...acc.nonPinned, layout]
5978
- };
5979
- };
5980
- var hasChildren = (component) => "components" in component;
5981
- var hasColumns = (component) => "right" in component && "left" in component;
5982
-
5983
- // src/controller/getErrorMessage.ts
5984
- var getErrorMessage = (error) => {
5985
- return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
5986
- };
5987
-
5988
- // src/controller/handleErrorResponse.ts
5989
- var handleErrorResponse = async (response, actionId, trackEvent) => {
5990
- const body = await parseResponseBodyAsJsonElement(response.clone());
5991
- if (isErrorResponseBody(body)) {
5992
- const refreshUrl = body.refreshUrl || body.refreshFormUrl;
5993
- const { error, validation, analytics } = body;
5994
- trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
5995
- const errors = { error, validation };
5996
- return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
5997
- type: "error",
5998
- body: { errors, analytics },
5999
- httpError: { statusCode: response.status }
6000
- };
6001
- }
6002
- trackEvent("Action Failed", { actionId, statusCode: response.status });
6003
- const errorMessage = await parseResponseBodyAsText(response);
6004
- return {
6005
- type: "error",
6006
- httpError: {
6007
- message: errorMessage || void 0,
6008
- statusCode: response.status
6009
- }
6010
- };
6011
- };
6012
-
6013
- // src/controller/getResponseType.ts
6014
- var responseTypes = ["step", "action", "exit", "modal", "subflow", "no-op"];
6015
- var getResponseType = (headers, body) => {
6016
- const headerResponseType = getResponseTypeFromHeader(headers);
6017
- if (headerResponseType) {
6018
- return headerResponseType;
6019
- }
6020
- if (isObject(body) && body.action) {
6021
- return "action";
6022
- }
6023
- return "step";
6024
- };
6025
- var getResponseTypeFromHeader = (headers) => {
6026
- if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
6027
- const type = headers.get("X-Df-Response-Type");
6028
- assertDFResponseType(type);
6029
- return type;
6030
- }
6031
- if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
6032
- return "exit";
6033
- }
6034
- return void 0;
6035
- };
6036
- function assertDFResponseType(type) {
6037
- if (!responseTypes.includes(type)) {
6038
- throw new Error(
6039
- "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'modal', 'subflow', 'no-op'."
6040
- );
6041
- }
6042
- }
6043
-
6044
- // src/controller/makeSafeHttpClient.ts
6045
- var makeSafeHttpClient = (httpClient) => async (...props) => {
6046
- try {
6047
- return await httpClient(...props);
6048
- } catch (e) {
6049
- return null;
6050
- }
6051
- };
6052
-
6053
- // src/controller/executeRequest.ts
6054
- var executeRequest = async (props) => {
6055
- const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
6056
- const { url, method, body } = request;
6057
- const response = await getCachedOrFetch(
6058
- [
6059
- url,
6060
- {
6061
- method,
6062
- body: body ? JSON.stringify(body) : void 0,
6063
- headers: { "Content-Type": "application/json" }
6064
- }
6065
- ],
6066
- requestCache,
6067
- httpClient
6068
- );
6069
- if (!response) {
6070
- const extra = { errorMessage: "Network Error" };
6071
- trackEvent("Request Failed", extra);
6072
- logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
6073
- return { type: "error" };
6074
- }
6075
- if (!response.ok) {
6076
- return handleErrorResponse(response, void 0, trackEvent);
6077
- }
6078
- const responseBody = await parseResponseBodyAsJsonElement(response);
6079
- const responseType = getResponseType(response.headers, responseBody);
6080
- if (exit) {
6081
- return { type: "complete", result: responseBody };
6082
- }
6083
- switch (responseType) {
6084
- case "step": {
6085
- const etag = response.headers.get("etag") || null;
6086
- assertStepResponseBody(responseBody);
6087
- return { type: "replace-step", step: responseBody, etag };
6088
- }
6089
- case "exit": {
6090
- return { type: "complete", result: responseBody };
6091
- }
6092
- case "action": {
6093
- assertActionResponseBody(responseBody);
6094
- return {
6095
- type: "behavior",
6096
- behavior: {
6097
- type: "action",
6098
- action: responseBody.action
6099
- }
6100
- };
6101
- }
6102
- case "subflow": {
6103
- assertSubflowResponseBody(responseBody);
6104
- return {
6105
- type: "behavior",
6106
- behavior: __spreadProps(__spreadValues({}, responseBody), {
6107
- type: "subflow",
6108
- onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
6109
- onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
6110
- })
6111
- };
6112
- }
6113
- case "modal": {
6114
- assertModalResponseBody(responseBody);
6115
- return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
6116
- }
6117
- case "no-op": {
6118
- return { type: "no-op" };
6119
- }
6120
- default: {
6121
- throw new Error(`Unsupported response type: ${String(responseType)}`);
6122
- }
6123
- }
6124
- };
6125
- var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
6126
- const cachedPromise = requestCache.get(requestParams);
6127
- if (cachedPromise) {
6128
- const cachedResponse = await cachedPromise;
6129
- if (cachedResponse == null ? void 0 : cachedResponse.ok) {
6130
- return cachedResponse;
6177
+ if (isStringSchema(schema)) {
6178
+ return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6179
+ }
6180
+ if (isArraySchema(schema)) {
6181
+ return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6182
+ }
6183
+ if (isBlobSchema(schema)) {
6184
+ if (!schemaMapperProps.onPersistAsync) {
6185
+ throw new Error(
6186
+ "Blob schemas can only be used as the schema of a persist async configuration."
6187
+ );
6131
6188
  }
6189
+ return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
6132
6190
  }
6133
- return makeSafeHttpClient(httpClient)(...requestParams);
6191
+ throw new Error("Not yet supported");
6134
6192
  };
6135
6193
 
6136
- // src/controller/executeSubmission.ts
6137
- var executeSubmission = async (props) => {
6138
- const { httpClient, requestCache, trackEvent, logEvent } = props;
6139
- const triggerAction = async (action, model, isInitial) => {
6140
- var _a, _b;
6141
- const { exit, url, result = null, id: actionId } = action;
6142
- const trackSubmissionEvent = !isInitial ? trackEvent : () => {
6143
- };
6144
- trackSubmissionEvent("Action Triggered", { actionId });
6145
- if (exit && !url) {
6146
- trackSubmissionEvent("Action Succeeded", { actionId });
6147
- return { type: "complete", result };
6148
- }
6149
- try {
6150
- const command = await executeRequest({
6151
- exit,
6152
- request: createRequestFromAction(action, model),
6153
- requestCache,
6154
- httpClient,
6155
- trackEvent: (name, properties) => {
6156
- trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
6194
+ // src/domain/mappers/mapStepSchemas.ts
6195
+ var mapStepSchemas = (uid, step, stepLocalValue, mapperProps) => {
6196
+ return step.schemas.map(
6197
+ (schema, i) => {
6198
+ var _a, _b;
6199
+ return mapSchemaToComponent(
6200
+ {
6201
+ uid: `${uid}.schemas-${i}.${schema.$id}`,
6202
+ schemaId: schema.$id,
6203
+ schema,
6204
+ model: (_a = step.model) != null ? _a : null,
6205
+ localValue: stepLocalValue,
6206
+ validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
6207
+ required: true
6157
6208
  },
6158
- logEvent
6159
- });
6160
- switch (command.type) {
6161
- case "error": {
6162
- trackSubmissionEvent("Action Failed", __spreadValues({
6163
- actionId,
6164
- statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
6165
- }, (_b = command.body) == null ? void 0 : _b.analytics));
6166
- return command;
6167
- }
6168
- case "behavior": {
6169
- if (command.behavior.type === "action") {
6170
- trackSubmissionEvent("Action Succeeded", { actionId });
6171
- return await executeSubmission({
6172
- action: command.behavior.action,
6173
- isInitial: false,
6174
- model: null,
6175
- requestCache,
6176
- httpClient,
6177
- trackEvent,
6178
- logEvent
6179
- });
6180
- }
6181
- trackSubmissionEvent("Action Succeeded", { actionId });
6182
- return command;
6183
- }
6184
- case "complete": {
6185
- trackSubmissionEvent("Action Succeeded", { actionId });
6186
- return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
6187
- }
6188
- case "no-op":
6189
- default: {
6190
- trackSubmissionEvent("Action Succeeded", { actionId });
6191
- return command;
6192
- }
6193
- }
6194
- } catch (error) {
6195
- const errorMessage = getErrorMessage(error);
6196
- trackSubmissionEvent("Action Failed", { actionId, errorMessage });
6197
- logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
6198
- throw error;
6209
+ mapperProps
6210
+ );
6199
6211
  }
6200
- };
6201
- return triggerAction(props.action, props.model, props.isInitial);
6212
+ );
6202
6213
  };
6203
- var createRequestFromAction = (action, model) => {
6204
- var _a, _b, _c;
6205
- return __spreadProps(__spreadValues({}, action), {
6206
- url: (_a = action.url) != null ? _a : "",
6207
- method: (_b = action.method) != null ? _b : "POST",
6208
- body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
6214
+
6215
+ // src/domain/mappers/mapToolbarToComponent.ts
6216
+ var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
6217
+ if (!toolbar) {
6218
+ return void 0;
6219
+ }
6220
+ const { onBehavior } = mapperProps;
6221
+ return __spreadProps(__spreadValues({}, toolbar), {
6222
+ type: "toolbar",
6223
+ uid: `${uid}.toolbar`,
6224
+ tags: toolbar.tags,
6225
+ items: toolbar.items.map((item) => {
6226
+ const context = item.context ? mapLegacyContext(item.context) : void 0;
6227
+ const behavior = getDomainLayerBehavior(
6228
+ { behavior: item.behavior },
6229
+ [],
6230
+ mapperProps.registerSubmissionBehavior
6231
+ );
6232
+ return __spreadProps(__spreadValues({}, item), {
6233
+ context: context ? mapLegacyContext(context) : void 0,
6234
+ disabled: !!item.disabled,
6235
+ tags: item.tags,
6236
+ onClick: () => {
6237
+ void onBehavior(behavior);
6238
+ }
6239
+ });
6240
+ })
6209
6241
  });
6210
6242
  };
6211
6243
 
6212
- // src/domain/features/prefetch/getStepPrefetch.ts
6213
- var getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
6214
- const requestCache = makeRequestCacheWithParent(flowRequestCache);
6215
- const keys = /* @__PURE__ */ new Set();
6216
- const start = (model) => {
6217
- if (keys.size > 0) {
6218
- return;
6219
- }
6220
- submissionBehaviors.forEach((behavior) => {
6221
- const request = behavior.type === "action" ? createRequestFromAction(behavior.action, model) : behavior.launchConfig.request;
6222
- const requestParams = [
6223
- request.url,
6224
- {
6225
- body: JSON.stringify(request.body),
6226
- method: request.method,
6227
- headers: { "Content-Type": "application/json" }
6228
- }
6229
- ];
6230
- try {
6231
- const key = makeRequestCacheKey(requestParams);
6232
- if (keys.has(key)) {
6233
- return;
6234
- }
6235
- const responsePromise = httpClient(...requestParams).catch(() => null);
6236
- requestCache.set(requestParams, responsePromise);
6237
- keys.add(key);
6238
- } catch (e) {
6239
- }
6240
- });
6241
- };
6242
- const stop = () => {
6244
+ // src/domain/mappers/utils/groupLayoutByPinned.ts
6245
+ var groupLayoutByPinned = (layouts) => {
6246
+ return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
6247
+ };
6248
+ var groupLayout = (acc, layout) => {
6249
+ if (layout.type === "button" && layout.pinOrder !== void 0) {
6250
+ return {
6251
+ pinned: [...acc.pinned, layout],
6252
+ nonPinned: acc.nonPinned
6253
+ };
6254
+ }
6255
+ if (hasColumns(layout)) {
6256
+ const leftChildren = groupLayoutByPinned(layout.left);
6257
+ const rightChildren = groupLayoutByPinned(layout.right);
6258
+ return {
6259
+ pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
6260
+ nonPinned: [
6261
+ ...acc.nonPinned,
6262
+ __spreadProps(__spreadValues({}, layout), {
6263
+ left: leftChildren.nonPinned,
6264
+ right: rightChildren.nonPinned
6265
+ })
6266
+ ]
6267
+ };
6268
+ }
6269
+ if (hasChildren(layout)) {
6270
+ const childComponents = groupLayoutByPinned(layout.components);
6271
+ return {
6272
+ pinned: [...acc.pinned, ...childComponents.pinned],
6273
+ nonPinned: [
6274
+ ...acc.nonPinned,
6275
+ __spreadProps(__spreadValues({}, layout), {
6276
+ components: childComponents.nonPinned
6277
+ })
6278
+ ]
6279
+ };
6280
+ }
6281
+ return {
6282
+ pinned: [...acc.pinned],
6283
+ nonPinned: [...acc.nonPinned, layout]
6243
6284
  };
6244
- return { requestCache, start, stop };
6245
6285
  };
6286
+ var hasChildren = (component) => "components" in component;
6287
+ var hasColumns = (component) => "right" in component && "left" in component;
6246
6288
 
6247
6289
  // src/domain/mappers/mapStepToComponent.ts
6248
6290
  var mapStepToComponent = (_a) => {
@@ -6278,12 +6320,7 @@ var mapStepToComponent = (_a) => {
6278
6320
  } = step;
6279
6321
  const submissionBehaviors = [];
6280
6322
  const registerSubmissionBehavior = (behavior) => {
6281
- if (behavior.type === "action" && behavior.action.prefetch) {
6282
- submissionBehaviors.push(behavior);
6283
- }
6284
- if (behavior.type === "subflow" && behavior.launchConfig.type === "dynamic" && behavior.launchConfig.request.prefetch) {
6285
- submissionBehaviors.push(behavior);
6286
- }
6323
+ submissionBehaviors.push(behavior);
6287
6324
  };
6288
6325
  const back = mapBackNavigation(navigation, onBehavior, features.isEnabled("nativeBack"));
6289
6326
  const stepId = id || key;
@@ -6381,64 +6418,6 @@ var getLayoutAndFooter = (step, features) => {
6381
6418
  return { layout, footer: [] };
6382
6419
  };
6383
6420
 
6384
- // src/getSubflowCallbacks.ts
6385
- var getSubflowCallbacks = ({
6386
- behavior,
6387
- close,
6388
- restart,
6389
- onBehavior,
6390
- onError,
6391
- onEvent
6392
- }) => {
6393
- const {
6394
- onCompletion: onCompletionBehavior,
6395
- onError: onErrorBehavior,
6396
- referrerId,
6397
- resultKey
6398
- } = behavior;
6399
- return {
6400
- onCompletion: (result) => {
6401
- var _a;
6402
- restart();
6403
- if (!onCompletionBehavior) {
6404
- close();
6405
- return;
6406
- }
6407
- if (onCompletionBehavior.type === "action") {
6408
- const newActionData = recursiveMerge(
6409
- (_a = onCompletionBehavior.action.data) != null ? _a : null,
6410
- resultKey ? { [resultKey]: result } : result
6411
- );
6412
- void onBehavior(__spreadProps(__spreadValues({}, onCompletionBehavior), {
6413
- action: __spreadProps(__spreadValues({}, onCompletionBehavior.action), { data: newActionData })
6414
- }));
6415
- return;
6416
- }
6417
- void onBehavior(onCompletionBehavior);
6418
- close();
6419
- },
6420
- onError: (error, status) => {
6421
- if (!onErrorBehavior) {
6422
- onError(error, {}, status);
6423
- close();
6424
- return;
6425
- }
6426
- restart();
6427
- void onBehavior(onErrorBehavior);
6428
- close();
6429
- },
6430
- onEvent: (name, properties) => {
6431
- onEvent(name, __spreadValues({
6432
- referrerId
6433
- }, properties));
6434
- },
6435
- onCancellation: () => {
6436
- restart();
6437
- close();
6438
- }
6439
- };
6440
- };
6441
-
6442
6421
  // src/utils/analyse-step.ts
6443
6422
  var import_spec2 = require("@wise/dynamic-flow-types/spec");
6444
6423
  var analyseStep = (step, logEvent) => {
@@ -6567,6 +6546,136 @@ var executeRefresh = async (props) => {
6567
6546
  }
6568
6547
  };
6569
6548
 
6549
+ // src/domain/components/SubflowComponent.ts
6550
+ var createSubflowComponent = (subflowProps, onComponentUpdate) => {
6551
+ switch (subflowProps.subflowType) {
6552
+ case "dynamic":
6553
+ return createDynamicSubflowComponent(subflowProps, onComponentUpdate);
6554
+ case "native":
6555
+ return createNativeSubflowComponent(subflowProps);
6556
+ }
6557
+ };
6558
+ var createDynamicSubflowComponent = (subflowProps, onComponentUpdate) => {
6559
+ const update = getInputUpdateFunction(onComponentUpdate);
6560
+ const subflowComponent = __spreadProps(__spreadValues({
6561
+ uid: `subflow-dynamic-${getRandomId()}`,
6562
+ type: "subflow",
6563
+ subflowType: "dynamic",
6564
+ kind: "layout"
6565
+ }, subflowProps), {
6566
+ _update(updateFn) {
6567
+ update(this, updateFn);
6568
+ }
6569
+ });
6570
+ return subflowComponent;
6571
+ };
6572
+ var createNativeSubflowComponent = (subflowProps) => {
6573
+ const subflowComponent = __spreadValues({
6574
+ uid: `subflow-native-${getRandomId()}`,
6575
+ type: "subflow",
6576
+ subflowType: "native",
6577
+ kind: "layout"
6578
+ }, subflowProps);
6579
+ return subflowComponent;
6580
+ };
6581
+
6582
+ // src/controller/executeSubflow.ts
6583
+ var executeSubflow = async ({
6584
+ behavior,
6585
+ rootComponent,
6586
+ trackEvent,
6587
+ onEvent,
6588
+ onChange
6589
+ }) => {
6590
+ rootComponent.setLoadingState("submitting");
6591
+ const { launchConfig } = behavior;
6592
+ const trackSubflowEvent = (eventName, properties) => {
6593
+ if (behavior.launchConfig.type === "native") {
6594
+ trackEvent(`Native Subflow ${eventName}`, __spreadValues({
6595
+ referrerId,
6596
+ nativeSubflowId: behavior.launchConfig.id
6597
+ }, properties));
6598
+ }
6599
+ };
6600
+ const { onCompletion, onError: onErrorBehavior, referrerId, resultKey } = behavior;
6601
+ return new Promise((resolve) => {
6602
+ var _a, _b;
6603
+ const restart = () => {
6604
+ rootComponent.setLoadingState("idle");
6605
+ rootComponent.start();
6606
+ };
6607
+ const close = () => {
6608
+ var _a2;
6609
+ if (((_a2 = rootComponent.subflow) == null ? void 0 : _a2.uid) === component.uid) {
6610
+ rootComponent.closeSubflow();
6611
+ }
6612
+ };
6613
+ const callbacks = {
6614
+ onCompletion: (result) => {
6615
+ var _a2;
6616
+ trackSubflowEvent("Succeeded");
6617
+ restart();
6618
+ if (!onCompletion) {
6619
+ close();
6620
+ resolve({ type: "no-op" });
6621
+ return;
6622
+ }
6623
+ if (onCompletion.type === "action") {
6624
+ const newActionData = recursiveMerge(
6625
+ (_a2 = onCompletion.action.data) != null ? _a2 : null,
6626
+ resultKey ? { [resultKey]: result } : result
6627
+ );
6628
+ resolve({
6629
+ type: "behavior",
6630
+ behavior: __spreadProps(__spreadValues({}, onCompletion), {
6631
+ action: __spreadProps(__spreadValues({}, onCompletion.action), { data: newActionData })
6632
+ })
6633
+ });
6634
+ return;
6635
+ }
6636
+ resolve({ type: "behavior", behavior: onCompletion });
6637
+ close();
6638
+ },
6639
+ onError: (error, status) => {
6640
+ trackSubflowEvent("Failed", { error, status });
6641
+ if (!onErrorBehavior) {
6642
+ resolve({ type: "error", error, status });
6643
+ close();
6644
+ return;
6645
+ }
6646
+ restart();
6647
+ resolve({ type: "behavior", behavior: onErrorBehavior });
6648
+ close();
6649
+ },
6650
+ onEvent: (name, properties) => onEvent == null ? void 0 : onEvent(name, __spreadValues({ referrerId }, properties)),
6651
+ onCancellation: () => {
6652
+ trackSubflowEvent("Cancelled");
6653
+ restart();
6654
+ close();
6655
+ }
6656
+ };
6657
+ const component = launchConfig.type === "dynamic" ? createSubflowComponent(
6658
+ __spreadProps(__spreadValues({}, callbacks), {
6659
+ subflowType: "dynamic",
6660
+ initialRequest: launchConfig.request,
6661
+ requestCache: (_b = (_a = rootComponent.getStep()) == null ? void 0 : _a.requestCache) != null ? _b : rootComponent.requestCache,
6662
+ presentation: launchConfig.presentation
6663
+ }),
6664
+ onChange
6665
+ ) : createSubflowComponent(
6666
+ __spreadProps(__spreadValues({}, callbacks), {
6667
+ subflowType: "native",
6668
+ id: launchConfig.id,
6669
+ payload: launchConfig.payload
6670
+ }),
6671
+ onChange
6672
+ );
6673
+ rootComponent.stop();
6674
+ rootComponent.addSubflow(component);
6675
+ trackSubflowEvent("Triggered");
6676
+ });
6677
+ };
6678
+
6570
6679
  // src/controller/getRequestAbortController.ts
6571
6680
  var getRequestAbortController = () => {
6572
6681
  let abortController = new AbortController();
@@ -6596,8 +6705,6 @@ var getStepCounter = () => {
6596
6705
  };
6597
6706
 
6598
6707
  // src/controller/FlowController.ts
6599
- var noop2 = () => {
6600
- };
6601
6708
  var createFlowController = (props) => {
6602
6709
  const _a = props, {
6603
6710
  flowId,
@@ -6605,9 +6712,9 @@ var createFlowController = (props) => {
6605
6712
  initialStep,
6606
6713
  backConfig,
6607
6714
  features,
6715
+ nativeSubflowHandlers,
6608
6716
  getErrorMessageFunctions,
6609
6717
  scrollToTop,
6610
- httpClient,
6611
6718
  onChange,
6612
6719
  onCancellation,
6613
6720
  onCompletion,
@@ -6623,9 +6730,9 @@ var createFlowController = (props) => {
6623
6730
  "initialStep",
6624
6731
  "backConfig",
6625
6732
  "features",
6733
+ "nativeSubflowHandlers",
6626
6734
  "getErrorMessageFunctions",
6627
6735
  "scrollToTop",
6628
- "httpClient",
6629
6736
  "onChange",
6630
6737
  "onCancellation",
6631
6738
  "onCompletion",
@@ -6636,6 +6743,7 @@ var createFlowController = (props) => {
6636
6743
  "onLog",
6637
6744
  "onValueChange"
6638
6745
  ]);
6746
+ const httpClient = getHttpClientWithCapabilities(props.httpClient, nativeSubflowHandlers);
6639
6747
  const rootComponent = createRootDomainComponent(
6640
6748
  onChange,
6641
6749
  scrollToTop,
@@ -6731,7 +6839,7 @@ var createFlowController = (props) => {
6731
6839
  onCancellation == null ? void 0 : onCancellation();
6732
6840
  };
6733
6841
  const onBehavior = async (behavior) => {
6734
- var _a2, _b, _c;
6842
+ var _a2;
6735
6843
  switch (behavior.type) {
6736
6844
  case "back": {
6737
6845
  if (behavior.action) {
@@ -6823,35 +6931,23 @@ var createFlowController = (props) => {
6823
6931
  rootComponent.dismissModal();
6824
6932
  break;
6825
6933
  case "subflow": {
6826
- rootComponent.setLoadingState("submitting");
6827
- const { launchConfig } = behavior;
6828
- const { request, presentation } = launchConfig;
6829
- const callbacks = getSubflowCallbacks({
6934
+ const command = await executeSubflow({
6830
6935
  behavior,
6831
- close: () => {
6832
- var _a3;
6833
- if (((_a3 = rootComponent.subflow) == null ? void 0 : _a3.uid) === component.uid) {
6834
- rootComponent.closeSubflow();
6835
- }
6836
- },
6837
- onBehavior,
6838
- onError: closeWithError,
6839
- onEvent: onEvent != null ? onEvent : noop2,
6840
- restart: () => {
6841
- rootComponent.setLoadingState("idle");
6842
- rootComponent.start();
6843
- }
6844
- });
6845
- const component = createSubflowDomainComponent(
6846
- __spreadProps(__spreadValues({}, callbacks), {
6847
- initialRequest: request,
6848
- requestCache: (_c = (_b = rootComponent.getStep()) == null ? void 0 : _b.requestCache) != null ? _c : rootComponent.requestCache,
6849
- presentation
6850
- }),
6936
+ rootComponent,
6937
+ trackEvent,
6938
+ onEvent,
6851
6939
  onChange
6852
- );
6853
- rootComponent.stop();
6854
- rootComponent.addSubflow(component);
6940
+ });
6941
+ switch (command.type) {
6942
+ case "behavior":
6943
+ void onBehavior(command.behavior);
6944
+ break;
6945
+ case "error":
6946
+ closeWithError(command.error, {}, command.status);
6947
+ break;
6948
+ case "no-op":
6949
+ break;
6950
+ }
6855
6951
  break;
6856
6952
  }
6857
6953
  case "none":
@@ -7054,29 +7150,29 @@ var CoreContainerRenderer = {
7054
7150
  render: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children })
7055
7151
  };
7056
7152
 
7057
- // src/renderers/EmptyLoadingStateRenderer.tsx
7058
- var import_jsx_runtime4 = require("react/jsx-runtime");
7059
- var EmptyLoadingStateRenderer = {
7060
- canRenderType: "loading-state",
7061
- render: () => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, {})
7062
- };
7063
-
7064
7153
  // src/renderers/CoreRootRenderer.tsx
7065
- var import_jsx_runtime5 = require("react/jsx-runtime");
7154
+ var import_jsx_runtime4 = require("react/jsx-runtime");
7066
7155
  var CoreRootRenderer = {
7067
7156
  canRenderType: "root",
7068
7157
  render: ({ children, childrenProps, subflow }) => {
7069
7158
  if ((subflow == null ? void 0 : subflow.presentation.type) === "push") {
7070
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children: subflow.children });
7159
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: subflow.children });
7071
7160
  }
7072
7161
  const [stepProps] = findRendererPropsByType(childrenProps, "step");
7073
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { id: stepProps == null ? void 0 : stepProps.id, className: "dynamic-flow-step", children: [
7162
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { id: stepProps == null ? void 0 : stepProps.id, className: "dynamic-flow-step", children: [
7074
7163
  children,
7075
7164
  (subflow == null ? void 0 : subflow.presentation.type) === "modal" ? subflow.children : void 0
7076
7165
  ] });
7077
7166
  }
7078
7167
  };
7079
7168
 
7169
+ // src/renderers/EmptyLoadingStateRenderer.tsx
7170
+ var import_jsx_runtime5 = require("react/jsx-runtime");
7171
+ var EmptyLoadingStateRenderer = {
7172
+ canRenderType: "loading-state",
7173
+ render: () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, {})
7174
+ };
7175
+
7080
7176
  // src/renderers/getRenderFunction.tsx
7081
7177
  var import_jsx_runtime6 = require("react/jsx-runtime");
7082
7178
  var getRenderFunction = (renderers) => {
@@ -7952,76 +8048,6 @@ var reviewComponentToProps = (component, rendererMapperProps) => __spreadValues(
7952
8048
  "tags"
7953
8049
  )), rendererMapperProps);
7954
8050
 
7955
- // src/renderers/stepComponentToProps.ts
7956
- var stepComponentToProps = (component, rendererMapperProps) => {
7957
- const { uid, analyticsId, back, control, description, error, step, title, tags, onBehavior } = component;
7958
- const _a = rendererMapperProps, { showBack } = _a, restMapperProps = __objRest(_a, ["showBack"]);
7959
- const childrenProps = component.getChildren().map((c) => componentToRendererProps(c, restMapperProps));
7960
- const footerProps = component.footerComponents.map(
7961
- (c) => componentToRendererProps(c, restMapperProps)
7962
- );
7963
- return __spreadValues({
7964
- type: "step",
7965
- id: step.id,
7966
- analyticsId,
7967
- uid,
7968
- back: showBack ? back : void 0,
7969
- control,
7970
- description,
7971
- toolbar: getToolbarProps(component.toolbar, restMapperProps),
7972
- error,
7973
- __rawStep: step,
7974
- title: restMapperProps.features.isEnabled("hideStepTitle") ? "" : title,
7975
- tags,
7976
- children: childrenProps.map(rendererMapperProps.render),
7977
- childrenProps,
7978
- footer: footerProps.map(rendererMapperProps.render),
7979
- footerProps,
7980
- __onAction: (action) => {
7981
- void onBehavior({ type: "action", action });
7982
- }
7983
- }, restMapperProps);
7984
- };
7985
- var getToolbarProps = (toolbar, rendererMapperProps) => {
7986
- if (!toolbar) {
7987
- return void 0;
7988
- }
7989
- return __spreadValues(__spreadProps(__spreadValues({}, pick(toolbar, "uid", "control", "type", "tags")), {
7990
- type: "toolbar",
7991
- items: toolbar.items.map((item) => __spreadProps(__spreadValues({}, item), {
7992
- disabled: item.disabled || rendererMapperProps.stepLoadingState !== "idle"
7993
- }))
7994
- }), rendererMapperProps);
7995
- };
7996
-
7997
- // src/renderers/mappers/rootComponentToProps.ts
7998
- var rootComponentToProps = (rootComponent, rendererMapperProps) => {
7999
- const childrenProps = [
8000
- __spreadValues({ type: "loading-state", uid: "loading-state" }, rendererMapperProps),
8001
- ...rootComponent.getChildren().map((child) => {
8002
- if (child.type === "step") {
8003
- return stepComponentToProps(child, __spreadProps(__spreadValues({}, rendererMapperProps), {
8004
- showBack: rootComponent.canPerformBack()
8005
- }));
8006
- }
8007
- return componentToRendererProps(child, rendererMapperProps);
8008
- })
8009
- ];
8010
- const subflow = rootComponent.subflow ? {
8011
- presentation: rootComponent.subflow.presentation,
8012
- children: rendererMapperProps.render(
8013
- componentToRendererProps(rootComponent.subflow, rendererMapperProps)
8014
- )
8015
- } : void 0;
8016
- return __spreadValues({
8017
- type: "root",
8018
- uid: rootComponent.uid,
8019
- children: childrenProps.map(rendererMapperProps.render),
8020
- childrenProps,
8021
- subflow
8022
- }, rendererMapperProps);
8023
- };
8024
-
8025
8051
  // src/renderers/mappers/searchComponentToProps.ts
8026
8052
  var searchComponentToProps = (component, rendererMapperProps) => {
8027
8053
  const { uid, control, emptyMessage, error, hint, isLoading, margin, query, title, tags } = component;
@@ -8229,19 +8255,6 @@ var upsellComponentToProps = (component, rendererMapperProps) => __spreadValues(
8229
8255
  onDismiss: component.onDismiss ? component.onDismiss.bind(component) : void 0
8230
8256
  }), rendererMapperProps);
8231
8257
 
8232
- // src/renderers/mappers/subflowComponentToRendererProps.ts
8233
- var subflowComponentToRendererProps = (component, rendererMapperProps) => {
8234
- return __spreadValues(__spreadProps(__spreadValues({
8235
- uid: component.uid,
8236
- type: "subflow"
8237
- }, pick(component, "requestCache", "presentation", "initialRequest")), {
8238
- onCancellation: component.onCancellation.bind(component),
8239
- onCompletion: component.onCompletion.bind(component),
8240
- onError: component.onError.bind(component),
8241
- onEvent: component.onEvent.bind(component)
8242
- }), rendererMapperProps);
8243
- };
8244
-
8245
8258
  // src/renderers/mappers/componentToRendererProps.ts
8246
8259
  var componentToRendererProps = (component, mapperProps) => {
8247
8260
  if (isHiddenComponent(component)) {
@@ -8266,8 +8279,6 @@ var componentToRendererProps = (component, mapperProps) => {
8266
8279
  };
8267
8280
  var getComponentProps = (component, rendererMapperProps) => {
8268
8281
  switch (component.type) {
8269
- case "root":
8270
- return rootComponentToProps(component, rendererMapperProps);
8271
8282
  case "alert":
8272
8283
  return alertComponentToProps(component, rendererMapperProps);
8273
8284
  case "all-of":
@@ -8342,8 +8353,6 @@ var getComponentProps = (component, rendererMapperProps) => {
8342
8353
  return selectInputComponentToProps(component, rendererMapperProps);
8343
8354
  case "status-list":
8344
8355
  return statusListComponentToProps(component, rendererMapperProps);
8345
- case "subflow":
8346
- return subflowComponentToRendererProps(component, rendererMapperProps);
8347
8356
  case "tabs":
8348
8357
  return tabsComponentToProps(component, rendererMapperProps);
8349
8358
  case "text":
@@ -8356,8 +8365,12 @@ var getComponentProps = (component, rendererMapperProps) => {
8356
8365
  return upsellComponentToProps(component, rendererMapperProps);
8357
8366
  case "persist-async":
8358
8367
  return persistAsyncComponentToProps(component, rendererMapperProps);
8368
+ case "root":
8369
+ throw new Error("Root components are only mapped from rootComponentToProps");
8359
8370
  case "step":
8360
- throw Error("Step components are only mapped from rootComponentToProps");
8371
+ throw new Error("Step components are only mapped from rootComponentToProps");
8372
+ case "subflow":
8373
+ throw new Error("Subflow components are only mapped from rootComponentToProps");
8361
8374
  default:
8362
8375
  throw new Error("Unknown component type");
8363
8376
  }
@@ -8370,6 +8383,142 @@ var getComponentAlertProps = (component, rendererMapperProps) => "alert" in comp
8370
8383
  markdown: component.alert.content
8371
8384
  }, rendererMapperProps) : null;
8372
8385
 
8386
+ // src/renderers/stepComponentToProps.ts
8387
+ var stepComponentToProps = (component, rendererMapperProps) => {
8388
+ const { uid, analyticsId, back, control, description, error, step, title, tags, onBehavior } = component;
8389
+ const _a = rendererMapperProps, { showBack } = _a, restMapperProps = __objRest(_a, ["showBack"]);
8390
+ const childrenProps = component.getChildren().map((c) => componentToRendererProps(c, restMapperProps));
8391
+ const footerProps = component.footerComponents.map(
8392
+ (c) => componentToRendererProps(c, restMapperProps)
8393
+ );
8394
+ return __spreadValues({
8395
+ type: "step",
8396
+ id: step.id,
8397
+ analyticsId,
8398
+ uid,
8399
+ back: showBack ? back : void 0,
8400
+ control,
8401
+ description,
8402
+ toolbar: getToolbarProps(component.toolbar, restMapperProps),
8403
+ error,
8404
+ __rawStep: step,
8405
+ title: restMapperProps.features.isEnabled("hideStepTitle") ? "" : title,
8406
+ tags,
8407
+ children: childrenProps.map(rendererMapperProps.render),
8408
+ childrenProps,
8409
+ footer: footerProps.map(rendererMapperProps.render),
8410
+ footerProps,
8411
+ __onAction: (action) => {
8412
+ void onBehavior({ type: "action", action });
8413
+ }
8414
+ }, restMapperProps);
8415
+ };
8416
+ var getToolbarProps = (toolbar, rendererMapperProps) => {
8417
+ if (!toolbar) {
8418
+ return void 0;
8419
+ }
8420
+ return __spreadValues(__spreadProps(__spreadValues({}, pick(toolbar, "uid", "control", "type", "tags")), {
8421
+ type: "toolbar",
8422
+ items: toolbar.items.map((item) => __spreadProps(__spreadValues({}, item), {
8423
+ disabled: item.disabled || rendererMapperProps.stepLoadingState !== "idle"
8424
+ }))
8425
+ }), rendererMapperProps);
8426
+ };
8427
+
8428
+ // src/renderers/mappers/subflowComponentToRendererProps.ts
8429
+ var subflowComponentToRendererProps = (subflow, rendererMapperProps, nativeSubflowHandlers) => {
8430
+ if (subflow.subflowType === "native") {
8431
+ const handler = nativeSubflowHandlers.find((h) => h.id === subflow.id);
8432
+ if (!handler) {
8433
+ throw new Error(
8434
+ `No native subflow handler found for subflow with id "${subflow.id}". Make sure to provide it in the "nativeSubflowHandlers" prop of DynamicFlowCore.`
8435
+ );
8436
+ }
8437
+ const presentationMode = handler.getPresentationMode(subflow.payload);
8438
+ return {
8439
+ presentation: { type: presentationMode },
8440
+ children: rendererMapperProps.render(
8441
+ nativeSubflowComponentToRendererProps(subflow, rendererMapperProps)
8442
+ )
8443
+ };
8444
+ }
8445
+ if (subflow.subflowType === "dynamic") {
8446
+ return {
8447
+ presentation: subflow.presentation,
8448
+ children: rendererMapperProps.render(
8449
+ dynamicSubflowComponentToRendererProps(subflow, rendererMapperProps)
8450
+ )
8451
+ };
8452
+ }
8453
+ };
8454
+ var dynamicSubflowComponentToRendererProps = (component, rendererMapperProps) => {
8455
+ return __spreadValues(__spreadProps(__spreadValues({
8456
+ uid: component.uid,
8457
+ type: "subflow-dynamic"
8458
+ }, pick(component, "requestCache", "presentation", "initialRequest")), {
8459
+ onCancellation: component.onCancellation.bind(component),
8460
+ onCompletion: component.onCompletion.bind(component),
8461
+ onError: component.onError.bind(component),
8462
+ onEvent: component.onEvent.bind(component)
8463
+ }), rendererMapperProps);
8464
+ };
8465
+ var nativeSubflowComponentToRendererProps = (component, rendererMapperProps) => {
8466
+ return __spreadValues({
8467
+ uid: component.uid,
8468
+ type: "subflow-native",
8469
+ id: component.id,
8470
+ payload: component.payload,
8471
+ onCancellation: component.onCancellation.bind(component),
8472
+ onCompletion: component.onCompletion.bind(component),
8473
+ onError: component.onError.bind(component),
8474
+ onEvent: component.onEvent.bind(component)
8475
+ }, rendererMapperProps);
8476
+ };
8477
+
8478
+ // src/renderers/mappers/rootComponentToProps.ts
8479
+ var rootComponentToProps = (rootComponent, rendererMapperProps, nativeSubflowHandlers) => {
8480
+ const childrenProps = [
8481
+ __spreadValues({ type: "loading-state", uid: "loading-state" }, rendererMapperProps),
8482
+ ...rootComponent.getChildren().map((child) => {
8483
+ if (child.type === "step") {
8484
+ return stepComponentToProps(child, __spreadProps(__spreadValues({}, rendererMapperProps), {
8485
+ showBack: rootComponent.canPerformBack()
8486
+ }));
8487
+ }
8488
+ return componentToRendererProps(child, rendererMapperProps);
8489
+ })
8490
+ ];
8491
+ const subflow = rootComponent.subflow ? subflowComponentToRendererProps(
8492
+ rootComponent.subflow,
8493
+ rendererMapperProps,
8494
+ nativeSubflowHandlers
8495
+ ) : void 0;
8496
+ return __spreadValues({
8497
+ type: "root",
8498
+ uid: rootComponent.uid,
8499
+ children: childrenProps.map(rendererMapperProps.render),
8500
+ childrenProps,
8501
+ subflow
8502
+ }, rendererMapperProps);
8503
+ };
8504
+
8505
+ // src/renderers/subflow/getNativeSubflowRenderer.tsx
8506
+ var import_jsx_runtime7 = require("react/jsx-runtime");
8507
+ var getNativeSubflowRenderer = (handlers) => {
8508
+ return {
8509
+ canRenderType: "subflow-native",
8510
+ render: (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(NativeSubflow, __spreadProps(__spreadValues({}, props), { handlers }))
8511
+ };
8512
+ };
8513
+ var NativeSubflow = (propsWithHandler) => {
8514
+ const _a = propsWithHandler, { handlers } = _a, props = __objRest(_a, ["handlers"]);
8515
+ const handler = handlers.find((it) => it.id === props.id);
8516
+ if (!handler) {
8517
+ throw new Error(`No handler found for native subflow with id ${props.id}`);
8518
+ }
8519
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_jsx_runtime7.Fragment, { children: handler.render(props) });
8520
+ };
8521
+
8373
8522
  // src/utils/getScrollToTop.ts
8374
8523
  var getScrollToTop = (normalisedFlowId, className2) => (behavior) => {
8375
8524
  var _a;
@@ -8398,12 +8547,12 @@ function useStableCallback(handler) {
8398
8547
  }
8399
8548
 
8400
8549
  // src/useDynamicFlow.tsx
8401
- var import_jsx_runtime7 = require("react/jsx-runtime");
8550
+ var import_jsx_runtime8 = require("react/jsx-runtime");
8402
8551
  var className = "dynamic-flow";
8403
- var noop3 = () => {
8552
+ var noop2 = () => {
8404
8553
  };
8405
8554
  function useDynamicFlow(props) {
8406
- var _a;
8555
+ var _a, _b;
8407
8556
  const { flowId, renderers } = props;
8408
8557
  const normalisedFlowId = normaliseFlowId(flowId);
8409
8558
  const scrollToTop = (0, import_react3.useMemo)(
@@ -8423,7 +8572,7 @@ function useDynamicFlow(props) {
8423
8572
  const onError = useStableCallback(props.onError);
8424
8573
  const onEvent = useStableCallback(props.onEvent);
8425
8574
  const onLog = useStableCallback(props.onLog);
8426
- const onValueChange = noop3;
8575
+ const onValueChange = noop2;
8427
8576
  const { formatMessage, locale } = (0, import_react_intl7.useIntl)();
8428
8577
  const getErrorMessageFunctions = (0, import_react3.useMemo)(
8429
8578
  () => getSchemaErrorMessageFunction(formatMessage, locale),
@@ -8449,33 +8598,45 @@ function useDynamicFlow(props) {
8449
8598
  scrollToTop
8450
8599
  }));
8451
8600
  const render = (0, import_react3.useMemo)(
8452
- () => getRenderFunction([
8453
- CoreRootRenderer,
8454
- CoreContainerRenderer,
8455
- ...renderers,
8456
- EmptyLoadingStateRenderer
8457
- ]),
8601
+ () => {
8602
+ var _a2;
8603
+ return getRenderFunction([
8604
+ CoreRootRenderer,
8605
+ CoreContainerRenderer,
8606
+ getNativeSubflowRenderer((_a2 = props.nativeSubflowHandlers) != null ? _a2 : []),
8607
+ // renderers above this cannot be overridden by the user
8608
+ ...renderers,
8609
+ EmptyLoadingStateRenderer
8610
+ // this can be overridden by the user
8611
+ ]);
8612
+ },
8613
+ // we don't want to react to changes in the nativeSubflowHandlers. they should be set once
8614
+ // eslint-disable-next-line react-hooks/exhaustive-deps
8458
8615
  [renderers]
8459
8616
  );
8460
- const tree = componentToRendererProps(rootComponent, {
8461
- features,
8462
- render,
8463
- httpClient,
8464
- trackEvent: (_a = rootComponent.getTrackEvent()) != null ? _a : (() => {
8465
- }),
8466
- stepLoadingState: rootComponent.getLoadingState()
8467
- });
8617
+ const tree = rootComponentToProps(
8618
+ rootComponent,
8619
+ {
8620
+ features,
8621
+ render,
8622
+ httpClient,
8623
+ trackEvent: (_a = rootComponent.getTrackEvent()) != null ? _a : (() => {
8624
+ }),
8625
+ stepLoadingState: rootComponent.getLoadingState()
8626
+ },
8627
+ (_b = props.nativeSubflowHandlers) != null ? _b : []
8628
+ );
8468
8629
  return {
8469
8630
  controller: {
8470
8631
  getSubmittableValue: async () => rootComponent.getSubmittableValue(),
8471
8632
  validate: () => rootComponent.validate(),
8472
8633
  getCurrentStep: () => {
8473
- var _a2, _b;
8474
- return (_b = (_a2 = rootComponent.getStep()) == null ? void 0 : _a2.step) != null ? _b : null;
8634
+ var _a2, _b2;
8635
+ return (_b2 = (_a2 = rootComponent.getStep()) == null ? void 0 : _a2.step) != null ? _b2 : null;
8475
8636
  },
8476
8637
  cancel
8477
8638
  },
8478
- view: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
8639
+ view: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
8479
8640
  ErrorBoundary_default,
8480
8641
  {
8481
8642
  onError: (error) => {
@@ -8485,7 +8646,7 @@ function useDynamicFlow(props) {
8485
8646
  errorMessage: getErrorMessage(error)
8486
8647
  });
8487
8648
  },
8488
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { id: normalisedFlowId, className, children: render(tree) })
8649
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { id: normalisedFlowId, className, children: render(tree) })
8489
8650
  }
8490
8651
  )
8491
8652
  };
@@ -8583,5 +8744,36 @@ var eventNames = [
8583
8744
  "Polling Failed",
8584
8745
  "ValidationAsync Triggered",
8585
8746
  "ValidationAsync Succeeded",
8586
- "ValidationAsync Failed"
8747
+ "ValidationAsync Failed",
8748
+ "Native Subflow Triggered",
8749
+ "Native Subflow Succeeded",
8750
+ "Native Subflow Failed",
8751
+ "Native Subflow Cancelled"
8587
8752
  ];
8753
+
8754
+ // src/renderers/subflow/getDynamicSubflowRenderer.tsx
8755
+ var import_jsx_runtime9 = require("react/jsx-runtime");
8756
+ var getDynamicSubflowRenderer = ({
8757
+ Component: Component2,
8758
+ canRender
8759
+ }) => {
8760
+ return {
8761
+ canRenderType: "subflow-dynamic",
8762
+ canRender,
8763
+ render: (props) => {
8764
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
8765
+ Component2,
8766
+ {
8767
+ presentation: props.presentation,
8768
+ requestCache: props.requestCache,
8769
+ initialRequest: props.initialRequest,
8770
+ onCompletion: props.onCompletion,
8771
+ onError: props.onError,
8772
+ onCancellation: props.onCancellation,
8773
+ onEvent: props.onEvent
8774
+ },
8775
+ props.uid
8776
+ );
8777
+ }
8778
+ };
8779
+ };