@wise/dynamic-flow-client 5.0.0 → 5.1.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 (25) hide show
  1. package/README.md +15 -30
  2. package/build/main.js +220 -108
  3. package/build/main.mjs +220 -108
  4. package/build/types/domain/components/step/StepDomainComponent.d.ts +4 -2
  5. package/build/types/domain/features/polling/getStepPolling.d.ts +3 -2
  6. package/build/types/domain/mappers/layout/alertLayoutToComponent.d.ts +1 -1
  7. package/build/types/domain/mappers/layout/decisionLayoutToComponent.d.ts +1 -1
  8. package/build/types/domain/mappers/layout/reviewLayoutToComponent.d.ts +1 -1
  9. package/build/types/domain/mappers/layout/statusListLayoutToComponent.d.ts +1 -1
  10. package/build/types/domain/mappers/mapStepToComponent.d.ts +1 -1
  11. package/build/types/domain/mappers/schema/types.d.ts +2 -1
  12. package/build/types/domain/mappers/utils/behavior-utils.d.ts +5 -2
  13. package/build/types/domain/mappers/utils/call-to-action-utils.d.ts +3 -2
  14. package/build/types/domain/mappers/utils/utils.d.ts +3 -2
  15. package/build/types/domain/prefetching/request-cache.d.ts +9 -0
  16. package/build/types/flow/executeSubmission.d.ts +4 -1
  17. package/build/types/flow/makeSafeHttpClient.d.ts +2 -0
  18. package/build/types/flow/makeSubmissionRequest.d.ts +3 -0
  19. package/package.json +28 -37
  20. package/build/types/domain/mappers/schema/tests/test-utils.d.ts +0 -34
  21. package/build/types/test-utils/NeptuneProviders.d.ts +0 -5
  22. package/build/types/test-utils/getRandomId.d.ts +0 -1
  23. package/build/types/test-utils/index.d.ts +0 -2
  24. package/build/types/test-utils/rtl-utils.d.ts +0 -2
  25. package/build/types/tests/renderers/TextInputRenderer.test.d.ts +0 -1
package/build/main.mjs CHANGED
@@ -1148,15 +1148,16 @@ var createAlertComponent = (alertProps) => __spreadValues({
1148
1148
  }, alertProps);
1149
1149
 
1150
1150
  // src/domain/mappers/utils/behavior-utils.ts
1151
- var getDomainLayerBehavior = ({
1152
- action,
1153
- behavior
1154
- }, stepActions) => {
1155
- if (behavior) {
1156
- return normaliseBehavior(behavior, stepActions);
1151
+ var getDomainLayerBehavior = ({ action, behavior: specBehavior }, stepActions, registerSubmissionBehavior) => {
1152
+ if (specBehavior) {
1153
+ const behavior = normaliseBehavior(specBehavior, stepActions);
1154
+ registerSubmissionBehavior == null ? void 0 : registerSubmissionBehavior(behavior);
1155
+ return behavior;
1157
1156
  }
1158
1157
  if (action) {
1159
- return actionToBehavior(action, stepActions);
1158
+ const behavior = actionToBehavior(action, stepActions);
1159
+ registerSubmissionBehavior == null ? void 0 : registerSubmissionBehavior(behavior);
1160
+ return behavior;
1160
1161
  }
1161
1162
  return { type: "none" };
1162
1163
  };
@@ -1186,14 +1187,17 @@ var getActionByReference = ($ref, actions = []) => {
1186
1187
  };
1187
1188
 
1188
1189
  // src/domain/mappers/utils/call-to-action-utils.ts
1189
- var getDomainLayerCallToAction = (callToAction, onBehavior, stepActions) => {
1190
+ var getDomainLayerCallToAction = (callToAction, mapperProps) => {
1191
+ var _a;
1190
1192
  if (!callToAction) {
1191
1193
  return void 0;
1192
1194
  }
1195
+ const { step, onBehavior, registerSubmissionBehavior } = mapperProps;
1196
+ const stepActions = (_a = step.actions) != null ? _a : [];
1193
1197
  const { title = "", accessibilityDescription, action, behavior } = callToAction;
1194
1198
  return getCallToAction(
1195
1199
  { title, accessibilityDescription },
1196
- getDomainLayerBehavior({ action, behavior }, stepActions),
1200
+ getDomainLayerBehavior({ action, behavior }, stepActions, registerSubmissionBehavior),
1197
1201
  onBehavior
1198
1202
  );
1199
1203
  };
@@ -1287,9 +1291,8 @@ var alertLayoutToComponent = (uid, {
1287
1291
  callToAction,
1288
1292
  analyticsId,
1289
1293
  tags
1290
- }, { onBehavior, step }) => {
1291
- var _a;
1292
- const cta = callToAction ? getDomainLayerCallToAction(callToAction, onBehavior, (_a = step.actions) != null ? _a : []) : void 0;
1294
+ }, mapperProps) => {
1295
+ const cta = callToAction ? getDomainLayerCallToAction(callToAction, mapperProps) : void 0;
1293
1296
  return createAlertComponent({
1294
1297
  uid,
1295
1298
  analyticsId,
@@ -1378,7 +1381,11 @@ var buttonLayoutToComponentWithBehavior = (uid, button, mapperProps) => {
1378
1381
  analyticsId,
1379
1382
  tags
1380
1383
  } = button;
1381
- const behavior = getDomainLayerBehavior(button, (_a = mapperProps.step.actions) != null ? _a : []);
1384
+ const behavior = getDomainLayerBehavior(
1385
+ button,
1386
+ (_a = mapperProps.step.actions) != null ? _a : [],
1387
+ mapperProps.registerSubmissionBehavior
1388
+ );
1382
1389
  const onClick = () => {
1383
1390
  void onBehavior(behavior);
1384
1391
  };
@@ -1522,9 +1529,10 @@ var mapInlineAlert = (alert) => {
1522
1529
  context: alert.context ? mapLegacyContext(alert.context) : "neutral"
1523
1530
  } : void 0;
1524
1531
  };
1525
- var mapAdditionalInfo = (info, onBehavior) => {
1532
+ var mapAdditionalInfo = (info, mapperProps) => {
1533
+ const { onBehavior, registerSubmissionBehavior } = mapperProps;
1526
1534
  if (info) {
1527
- const behavior = getDomainLayerBehavior(info, []);
1535
+ const behavior = getDomainLayerBehavior(info, [], registerSubmissionBehavior);
1528
1536
  return {
1529
1537
  text: info.text,
1530
1538
  href: behavior.type === "link" ? behavior.url : void 0,
@@ -1551,20 +1559,21 @@ var decisionLayoutToComponent = (uid, {
1551
1559
  options,
1552
1560
  tags,
1553
1561
  title
1554
- }, { onBehavior, step }) => createDecisionComponent({
1562
+ }, mapperProps) => createDecisionComponent({
1555
1563
  uid,
1556
1564
  analyticsId,
1557
1565
  control,
1558
1566
  margin,
1559
- options: options.map((option) => mapOption(option, onBehavior, step.actions)),
1567
+ options: options.map((option) => mapOption(option, mapperProps)),
1560
1568
  tags,
1561
1569
  title
1562
1570
  });
1563
- var mapOption = (option, onBehavior, stepActions = []) => {
1564
- var _a;
1565
- const behavior = getDomainLayerBehavior(option, stepActions);
1571
+ var mapOption = (option, mapperProps) => {
1572
+ var _a, _b;
1573
+ const { step, onBehavior, registerSubmissionBehavior } = mapperProps;
1574
+ const behavior = getDomainLayerBehavior(option, (_a = step.actions) != null ? _a : [], registerSubmissionBehavior);
1566
1575
  return __spreadProps(__spreadValues({}, option), {
1567
- disabled: (_a = option.disabled) != null ? _a : false,
1576
+ disabled: (_b = option.disabled) != null ? _b : false,
1568
1577
  href: behavior.type === "link" ? behavior.url : void 0,
1569
1578
  media: getDomainLayerMedia(option),
1570
1579
  inlineAlert: mapInlineAlert(option.inlineAlert),
@@ -1753,21 +1762,18 @@ var createListComponent = (listProps) => __spreadValues({
1753
1762
 
1754
1763
  // src/domain/mappers/layout/listLayoutToComponent.ts
1755
1764
  var listLayoutToComponent = (uid, { analyticsId, callToAction, control, items, margin = "md", tags, title }, mapperProps) => {
1756
- var _a;
1757
- const { step, onBehavior } = mapperProps;
1758
1765
  return createListComponent({
1759
1766
  uid,
1760
1767
  analyticsId,
1761
1768
  control,
1762
1769
  items: items.map((item) => mapItem(item, mapperProps)),
1763
- callToAction: getDomainLayerCallToAction(callToAction, onBehavior, (_a = step.actions) != null ? _a : []),
1770
+ callToAction: getDomainLayerCallToAction(callToAction, mapperProps),
1764
1771
  margin,
1765
1772
  tags,
1766
1773
  title
1767
1774
  });
1768
1775
  };
1769
1776
  var mapItem = (item, mapperProps) => {
1770
- var _b;
1771
1777
  const _a = item, {
1772
1778
  value,
1773
1779
  subvalue,
@@ -1791,14 +1797,13 @@ var mapItem = (item, mapperProps) => {
1791
1797
  "additionalInfo",
1792
1798
  "inlineAlert"
1793
1799
  ]);
1794
- const { step, onBehavior } = mapperProps;
1795
1800
  return __spreadProps(__spreadValues({}, rest), {
1796
1801
  description: description != null ? description : subtitle,
1797
1802
  media: getDomainLayerMedia({ icon, image, media }),
1798
1803
  supportingValues: mapSupportingValues(item),
1799
- additionalInfo: mapAdditionalInfo(additionalInfo, onBehavior),
1804
+ additionalInfo: mapAdditionalInfo(additionalInfo, mapperProps),
1800
1805
  inlineAlert: mapInlineAlert(inlineAlert),
1801
- callToAction: getDomainLayerCallToAction(callToAction, onBehavior, (_b = step.actions) != null ? _b : []),
1806
+ callToAction: getDomainLayerCallToAction(callToAction, mapperProps),
1802
1807
  tags: mapTags(rest)
1803
1808
  });
1804
1809
  };
@@ -1910,31 +1915,30 @@ var reviewLayoutToComponent = (uid, {
1910
1915
  tags,
1911
1916
  orientation,
1912
1917
  action
1913
- }, { onBehavior, step }) => createReviewComponent({
1918
+ }, mapperProps) => createReviewComponent({
1914
1919
  uid,
1915
1920
  analyticsId,
1916
- callToAction: getCallToAction2({ onBehavior, callToAction, action, stepActions: step.actions }),
1921
+ callToAction: getCallToAction2({ mapperProps, callToAction, action }),
1917
1922
  control: getOrientationControl({ control, orientation }),
1918
- fields: fields.map((field) => mapReviewField(field, { onBehavior, step })),
1923
+ fields: fields.map((field) => mapReviewField(field, mapperProps)),
1919
1924
  margin,
1920
1925
  tags,
1921
1926
  title
1922
1927
  });
1923
1928
  var getCallToAction2 = ({
1924
- onBehavior,
1925
1929
  callToAction,
1926
1930
  action,
1927
- stepActions = []
1931
+ mapperProps
1928
1932
  }) => {
1929
1933
  if (callToAction) {
1930
- return getDomainLayerCallToAction(callToAction, onBehavior, stepActions);
1934
+ return getDomainLayerCallToAction(callToAction, mapperProps);
1931
1935
  }
1932
1936
  if (action == null ? void 0 : action.title) {
1933
1937
  return {
1934
1938
  type: "action",
1935
1939
  title: action.title,
1936
1940
  onClick: () => {
1937
- void onBehavior({ type: "action", action });
1941
+ void mapperProps.onBehavior({ type: "action", action });
1938
1942
  }
1939
1943
  };
1940
1944
  }
@@ -1952,17 +1956,16 @@ var getOrientationControl = ({
1952
1956
  }
1953
1957
  return void 0;
1954
1958
  };
1955
- var mapReviewField = (field, { onBehavior, step }) => {
1956
- var _a, _b;
1959
+ var mapReviewField = (field, mapperProps) => {
1960
+ var _a;
1957
1961
  return __spreadProps(__spreadValues({}, field), {
1958
1962
  media: getDomainLayerMedia(field),
1959
1963
  help: (_a = field.help) == null ? void 0 : _a.markdown,
1960
1964
  inlineAlert: mapInlineAlert(field.inlineAlert),
1961
- additionalInfo: mapAdditionalInfo(field.additionalInfo, onBehavior),
1965
+ additionalInfo: mapAdditionalInfo(field.additionalInfo, mapperProps),
1962
1966
  callToAction: getCallToAction2({
1963
- onBehavior,
1964
1967
  callToAction: field.callToAction,
1965
- stepActions: (_b = step.actions) != null ? _b : []
1968
+ mapperProps
1966
1969
  }),
1967
1970
  tags: mapTags(field)
1968
1971
  });
@@ -2159,17 +2162,12 @@ var searchLayoutToComponent = (uid, {
2159
2162
  };
2160
2163
 
2161
2164
  // src/domain/mappers/layout/statusListLayoutToComponent.ts
2162
- var statusListLayoutToComponent = (uid, { analyticsId, control, items, margin = "md", tags, title }, { onBehavior, step }) => createStatusListComponent({
2165
+ var statusListLayoutToComponent = (uid, { analyticsId, control, items, margin = "md", tags, title }, mapperProps) => createStatusListComponent({
2163
2166
  uid,
2164
2167
  analyticsId,
2165
2168
  control,
2166
2169
  items: items.map((item) => {
2167
- var _a;
2168
- const callToAction = getDomainLayerCallToAction(
2169
- item.callToAction,
2170
- onBehavior,
2171
- (_a = step.actions) != null ? _a : []
2172
- );
2170
+ const callToAction = getDomainLayerCallToAction(item.callToAction, mapperProps);
2173
2171
  return __spreadProps(__spreadValues({}, item), { callToAction, tags: mapTags(item) });
2174
2172
  }),
2175
2173
  margin,
@@ -2190,7 +2188,6 @@ var createSectionComponent = (props) => {
2190
2188
 
2191
2189
  // src/domain/mappers/layout/sectionLayoutToComponent.ts
2192
2190
  var sectionLayoutToComponent = (uid, { analyticsId, control, title, components, callToAction, margin = "md", tags }, mapperProps, schemaComponents) => {
2193
- var _a;
2194
2191
  return createSectionComponent({
2195
2192
  uid,
2196
2193
  analyticsId,
@@ -2200,11 +2197,7 @@ var sectionLayoutToComponent = (uid, { analyticsId, control, title, components,
2200
2197
  (component, index) => mapLayoutToComponent(`${uid}.section-${index}`, component, mapperProps, schemaComponents)
2201
2198
  ),
2202
2199
  margin,
2203
- callToAction: getDomainLayerCallToAction(
2204
- callToAction,
2205
- mapperProps.onBehavior,
2206
- (_a = mapperProps.step.actions) != null ? _a : []
2207
- ),
2200
+ callToAction: getDomainLayerCallToAction(callToAction, mapperProps),
2208
2201
  tags
2209
2202
  });
2210
2203
  };
@@ -2333,34 +2326,39 @@ var modalToComponent = (uid, { content, title }, mapperProps, schemaComponents)
2333
2326
  mapperProps.updateComponent
2334
2327
  );
2335
2328
 
2336
- // src/domain/components/step/ExternalConfirmationComponent.ts
2337
- var createExternalConfirmation = (uid, url, updateComponent) => {
2338
- const update = getInputUpdateFunction(updateComponent);
2329
+ // src/domain/prefetching/request-cache.ts
2330
+ var makeRequestCache = () => {
2331
+ const cache = /* @__PURE__ */ new Map();
2339
2332
  return {
2340
- type: "external-confirmation",
2341
- kind: "layout",
2342
- uid,
2343
- url,
2344
- status: "initial",
2345
- onSuccess() {
2346
- update(this, (draft) => {
2347
- draft.status = "success";
2348
- });
2333
+ has: (...requestParams) => {
2334
+ return cache.has(makeKey(...requestParams));
2349
2335
  },
2350
- onFailure() {
2351
- if (this.status === "initial") {
2352
- update(this, (draft) => {
2353
- draft.status = "failure";
2354
- });
2355
- }
2336
+ get: (...requestParams) => {
2337
+ return cache.get(makeKey(...requestParams));
2356
2338
  },
2357
- onCancel() {
2358
- update(this, (draft) => {
2359
- draft.status = "dismissed";
2360
- });
2339
+ delete: (...requestParams) => {
2340
+ return cache.delete(makeKey(...requestParams));
2341
+ },
2342
+ set: (...[input, init, response]) => {
2343
+ return cache.set(makeKey(input, init), response);
2344
+ },
2345
+ clear: () => {
2346
+ cache.clear();
2361
2347
  }
2362
2348
  };
2363
2349
  };
2350
+ var makeKey = (...requestParams) => {
2351
+ var _a, _b;
2352
+ const [input, init] = requestParams;
2353
+ const url = typeof input === "string" || input instanceof URL ? input.toString() : input.url;
2354
+ const key = JSON.stringify({
2355
+ url,
2356
+ method: (_a = init == null ? void 0 : init.method) != null ? _a : "GET",
2357
+ headers: (init == null ? void 0 : init.headers) ? Array.from(new Headers(init.headers).entries()) : [],
2358
+ body: (_b = init == null ? void 0 : init.body) != null ? _b : null
2359
+ });
2360
+ return key;
2361
+ };
2364
2362
 
2365
2363
  // src/utils/recursiveMerge.ts
2366
2364
  function recursiveMerge(valueA, valueB) {
@@ -2401,6 +2399,57 @@ function mergeArrays(valueA, valueB) {
2401
2399
  );
2402
2400
  }
2403
2401
 
2402
+ // src/flow/makeSubmissionRequest.ts
2403
+ var makeSubmissionRequest = (action, model) => {
2404
+ var _a, _b;
2405
+ return [
2406
+ (_a = action.url) != null ? _a : "",
2407
+ {
2408
+ method: (_b = action.method) != null ? _b : "POST",
2409
+ body: makeRequestBody(action, model),
2410
+ headers: { "Content-Type": "application/json" }
2411
+ }
2412
+ ];
2413
+ };
2414
+ var makeRequestBody = (action, model) => {
2415
+ var _a, _b;
2416
+ const method = (_a = action.method) != null ? _a : "POST";
2417
+ if (method === "GET") {
2418
+ return void 0;
2419
+ }
2420
+ const payload = recursiveMerge(model, (_b = action.data) != null ? _b : null);
2421
+ return payload !== null ? JSON.stringify(payload) : null;
2422
+ };
2423
+
2424
+ // src/domain/components/step/ExternalConfirmationComponent.ts
2425
+ var createExternalConfirmation = (uid, url, updateComponent) => {
2426
+ const update = getInputUpdateFunction(updateComponent);
2427
+ return {
2428
+ type: "external-confirmation",
2429
+ kind: "layout",
2430
+ uid,
2431
+ url,
2432
+ status: "initial",
2433
+ onSuccess() {
2434
+ update(this, (draft) => {
2435
+ draft.status = "success";
2436
+ });
2437
+ },
2438
+ onFailure() {
2439
+ if (this.status === "initial") {
2440
+ update(this, (draft) => {
2441
+ draft.status = "failure";
2442
+ });
2443
+ }
2444
+ },
2445
+ onCancel() {
2446
+ update(this, (draft) => {
2447
+ draft.status = "dismissed";
2448
+ });
2449
+ }
2450
+ };
2451
+ };
2452
+
2404
2453
  // src/utils/component-utils.ts
2405
2454
  var getSubmittableData = async (components) => Promise.all(components.map(async (component) => component.getSubmittableValue())).then(
2406
2455
  (values) => values.reduce((acc, value) => recursiveMerge(acc, value), null)
@@ -2480,7 +2529,8 @@ var getStepPolling = ({
2480
2529
  pollingConfig,
2481
2530
  logEvent,
2482
2531
  onBehavior,
2483
- onPoll
2532
+ onPoll,
2533
+ registerSubmissionBehavior
2484
2534
  }) => {
2485
2535
  const { interval, delay = interval, maxAttempts, url, onError } = pollingConfig;
2486
2536
  let abortController = new AbortController();
@@ -2488,7 +2538,7 @@ var getStepPolling = ({
2488
2538
  if (delay == null) {
2489
2539
  throw new Error("Polling configuration must include delay or interval");
2490
2540
  }
2491
- const onErrorBehavior = getDomainLayerBehavior(onError, []);
2541
+ const onErrorBehavior = getDomainLayerBehavior(onError, [], registerSubmissionBehavior);
2492
2542
  let attempts = 0;
2493
2543
  const poll = () => {
2494
2544
  attempts += 1;
@@ -4803,11 +4853,12 @@ var objectSchemaToMoneyInputComponent = (schemaMapperProps, mapperProps) => {
4803
4853
  );
4804
4854
  };
4805
4855
  var createMoneyInputSubComponents = (schemaMapperProps, mapperProps) => {
4806
- const { schema: objectSchema } = schemaMapperProps;
4856
+ const { schema: objectSchema, model } = schemaMapperProps;
4807
4857
  const amountKey = getAmountSchemaKey(objectSchema, mapperProps);
4808
4858
  const currencyKey = getCurrencySchemaKey(objectSchema, mapperProps);
4809
4859
  const currencySchema = objectSchema.properties[currencyKey];
4810
4860
  const customSchemaMapperProps = __spreadProps(__spreadValues({}, schemaMapperProps), {
4861
+ model: getSanitizedModel(amountKey, model),
4811
4862
  schema: isOneOfSchema(currencySchema) ? objectSchema : replaceKeyInObjectSchema(objectSchema, currencyKey, { oneOf: [currencySchema] })
4812
4863
  });
4813
4864
  const componentMap = createComponentMap(customSchemaMapperProps, mapperProps, "money");
@@ -4819,6 +4870,18 @@ var createMoneyInputSubComponents = (schemaMapperProps, mapperProps) => {
4819
4870
  const checks = getMinMaxChecks(objectSchema, amountKey, mapperProps);
4820
4871
  return { amountKey, amountComponent, currencyKey, currencyComponent, extraValues, checks };
4821
4872
  };
4873
+ var isNumberString = (value) => {
4874
+ return typeof value === "string" && !Number.isNaN(Number.parseFloat(value)) && /^\d*(?:\.\d*)?$/.test(value);
4875
+ };
4876
+ var getSanitizedModel = (key, value) => {
4877
+ if (isObject(value)) {
4878
+ const amount = value[key];
4879
+ if (!isNumberString(amount)) {
4880
+ return __spreadProps(__spreadValues({}, value), { [key]: null });
4881
+ }
4882
+ }
4883
+ return value;
4884
+ };
4822
4885
  var getAmountSchemaKey = ({ displayOrder, properties }, mapperProps) => {
4823
4886
  var _a;
4824
4887
  const entry = displayOrder.map((key) => ({ key, schema: properties[key] })).find(({ schema }) => isAmountSchema(schema));
@@ -5620,7 +5683,11 @@ var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
5620
5683
  tags: toolbar.tags,
5621
5684
  items: toolbar.items.map((item) => {
5622
5685
  const context = item.context ? mapLegacyContext(item.context) : void 0;
5623
- const behavior = getDomainLayerBehavior({ behavior: item.behavior }, []);
5686
+ const behavior = getDomainLayerBehavior(
5687
+ { behavior: item.behavior },
5688
+ [],
5689
+ mapperProps.registerSubmissionBehavior
5690
+ );
5624
5691
  return __spreadProps(__spreadValues({}, item), {
5625
5692
  context: context ? mapLegacyContext(context) : void 0,
5626
5693
  disabled: !!item.disabled,
@@ -5716,16 +5783,29 @@ var mapStepToComponent = (_a) => {
5716
5783
  title,
5717
5784
  tags
5718
5785
  } = step;
5786
+ const submissionRequestsCache = makeRequestCache();
5787
+ const submissionBehaviors = [];
5788
+ const registerSubmissionBehavior = (behavior) => {
5789
+ if (behavior.type === "action" && behavior.action.prefetch) {
5790
+ submissionBehaviors.push(behavior);
5791
+ }
5792
+ };
5719
5793
  const back = mapBackNavigation(navigation, onBehavior, Boolean(features.nativeBack));
5720
5794
  const stepId = id || key;
5721
5795
  if (stepId === void 0) {
5722
5796
  throw new Error("Step must have an id or a key");
5723
5797
  }
5724
5798
  const uid = `${rootUid}.${stepId != null ? stepId : "step"}`;
5725
- const stepPolling = polling ? getStepPolling({ pollingConfig: polling, logEvent, onBehavior, onPoll }) : void 0;
5799
+ const stepPolling = polling ? getStepPolling({
5800
+ pollingConfig: polling,
5801
+ logEvent,
5802
+ onBehavior,
5803
+ onPoll,
5804
+ registerSubmissionBehavior
5805
+ }) : void 0;
5726
5806
  const stepRefreshAfter = refreshAfter ? getStepRefreshAfter({ refreshAfter, logEvent, onBehavior }) : void 0;
5727
5807
  const externalConfirmation = (external == null ? void 0 : external.url) ? createExternalConfirmation(`${uid}-external-confirmation`, external == null ? void 0 : external.url, updateComponent) : void 0;
5728
- const mapperProps = __spreadProps(__spreadValues({}, restProps), { trackEvent, onBehavior });
5808
+ const mapperProps = __spreadProps(__spreadValues({}, restProps), { trackEvent, onBehavior, registerSubmissionBehavior });
5729
5809
  const referencedSchemaIds = getReferencedSchemaId(step);
5730
5810
  const schemaComponents = mapStepSchemas(
5731
5811
  uid,
@@ -5769,11 +5849,18 @@ var mapStepToComponent = (_a) => {
5769
5849
  title: !features.hideStepTitle ? title : void 0,
5770
5850
  tags,
5771
5851
  stackBehavior: (_a2 = navigation == null ? void 0 : navigation.stackBehavior) != null ? _a2 : "default",
5852
+ submissionRequestsCache,
5772
5853
  step,
5773
5854
  updateComponent,
5774
5855
  trackEvent,
5775
5856
  onBehavior
5776
5857
  });
5858
+ executePrefetch({
5859
+ httpClient: mapperProps.httpClient,
5860
+ model: stepComponent.getSubmittableValueSync(),
5861
+ behaviors: submissionBehaviors,
5862
+ requestsCache: submissionRequestsCache
5863
+ });
5777
5864
  return stepComponent;
5778
5865
  };
5779
5866
  var getReferencedSchemaId = (step) => {
@@ -5794,6 +5881,22 @@ var mapBackNavigation = (navigation, onBehavior, isNativeBackEnabled) => {
5794
5881
  }
5795
5882
  } : void 0;
5796
5883
  };
5884
+ var executePrefetch = (props) => {
5885
+ const {
5886
+ httpClient,
5887
+ behaviors: submissionBehaviors,
5888
+ model,
5889
+ requestsCache: submissionRequestsCache
5890
+ } = props;
5891
+ submissionBehaviors.forEach((behavior) => {
5892
+ const requestParams = makeSubmissionRequest(behavior.action, model);
5893
+ try {
5894
+ const responsePromise = httpClient(...requestParams).catch(() => null);
5895
+ submissionRequestsCache.set(...requestParams, responsePromise);
5896
+ } catch (e) {
5897
+ }
5898
+ });
5899
+ };
5797
5900
 
5798
5901
  // src/flow/getResponseType.ts
5799
5902
  var responseTypes = ["step", "action", "exit", "modal"];
@@ -5922,11 +6025,20 @@ var executeRefresh = async (props) => {
5922
6025
  }
5923
6026
  };
5924
6027
 
6028
+ // src/flow/makeSafeHttpClient.ts
6029
+ var makeSafeHttpClient = (httpClient) => async (...props) => {
6030
+ try {
6031
+ return await httpClient(...props);
6032
+ } catch (e) {
6033
+ return null;
6034
+ }
6035
+ };
6036
+
5925
6037
  // src/flow/executeSubmission.ts
5926
6038
  var executeSubmission = async (props) => {
5927
- const { httpClient, trackEvent, logEvent } = props;
6039
+ const { httpClient, requestCache, trackEvent, logEvent } = props;
5928
6040
  const triggerAction = async (action, model, isInitial) => {
5929
- const { exit, url, method = "POST", result = null, id: actionId } = action;
6041
+ const { exit, url, result = null, id: actionId } = action;
5930
6042
  const trackSubmissionEvent = !isInitial ? trackEvent : () => {
5931
6043
  };
5932
6044
  trackSubmissionEvent("Action Triggered", { actionId });
@@ -5934,19 +6046,8 @@ var executeSubmission = async (props) => {
5934
6046
  trackSubmissionEvent("Action Succeeded", { actionId });
5935
6047
  return { type: "complete", result };
5936
6048
  }
5937
- const makeRequestBody = () => {
5938
- var _a;
5939
- if (method === "GET") {
5940
- return void 0;
5941
- }
5942
- const payload = recursiveMerge(model, (_a = action.data) != null ? _a : null);
5943
- return payload !== null ? JSON.stringify(payload) : null;
5944
- };
5945
- const response = await getSafeHttpClient(httpClient)(url != null ? url : "", {
5946
- method,
5947
- body: makeRequestBody(),
5948
- headers: { "Content-Type": "application/json" }
5949
- });
6049
+ const requestParams = makeSubmissionRequest(action, model);
6050
+ const response = await getCachedOrFetch(requestParams, requestCache, httpClient);
5950
6051
  if (!response) {
5951
6052
  const extra = { actionId, errorMessage: "Network Error" };
5952
6053
  trackEvent("Action Failed", extra);
@@ -6019,12 +6120,18 @@ var executeSubmission = async (props) => {
6019
6120
  };
6020
6121
  return triggerAction(props.action, props.model, props.isInitial);
6021
6122
  };
6022
- var getSafeHttpClient = (httpClient) => async (input, init) => {
6023
- try {
6024
- return await httpClient(input, init);
6025
- } catch (e) {
6026
- return null;
6123
+ var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
6124
+ if (requestCache == null ? void 0 : requestCache.has(...requestParams)) {
6125
+ const cachedPromise = requestCache.get(...requestParams);
6126
+ requestCache.delete(...requestParams);
6127
+ if (cachedPromise) {
6128
+ const cachedResponse = await cachedPromise;
6129
+ if (cachedResponse == null ? void 0 : cachedResponse.ok) {
6130
+ return cachedResponse;
6131
+ }
6132
+ }
6027
6133
  }
6134
+ return makeSafeHttpClient(httpClient)(...requestParams);
6028
6135
  };
6029
6136
 
6030
6137
  // src/renderers/getSchemaErrorMessageFunction.ts
@@ -6476,10 +6583,14 @@ function useDynamicFlowCore(props) {
6476
6583
  modalToComponent(
6477
6584
  rootComponentRef.current.uid,
6478
6585
  behavior,
6479
- __spreadValues({
6586
+ __spreadProps(__spreadValues({
6480
6587
  step: currentStep,
6481
6588
  stepLocalValue: rootComponentRef.current.getLocalValue()
6482
- }, getMapperProps()),
6589
+ }, getMapperProps()), {
6590
+ registerSubmissionBehavior: () => {
6591
+ }
6592
+ // this means actions in modal responses won't have prefetching
6593
+ }),
6483
6594
  rootComponentRef.current.getSchemaComponents()
6484
6595
  )
6485
6596
  );
@@ -6498,13 +6609,14 @@ function useDynamicFlowCore(props) {
6498
6609
  }, []);
6499
6610
  const onAction = useCallback2(
6500
6611
  async (action, model) => {
6501
- var _a2, _b, _c, _d, _e, _f, _g, _h;
6612
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i;
6502
6613
  try {
6503
6614
  rootComponentRef.current.setLoadingState("submitting");
6504
6615
  const command = await executeSubmission({
6505
6616
  action,
6506
6617
  model,
6507
6618
  isInitial: !rootComponentRef.current.hasStep(),
6619
+ requestCache: (_a2 = rootComponentRef.current.getStep()) == null ? void 0 : _a2.submissionRequestsCache,
6508
6620
  httpClient,
6509
6621
  trackEvent: trackCoreEvent,
6510
6622
  logEvent
@@ -6525,7 +6637,7 @@ function useDynamicFlowCore(props) {
6525
6637
  case "error": {
6526
6638
  rootComponentRef.current.setLoadingState("idle");
6527
6639
  const genericErrorMessage = getErrorMessageFunctions().genericErrorWithRetry();
6528
- const errors = (_b = (_a2 = command.body) == null ? void 0 : _a2.errors) != null ? _b : { error: genericErrorMessage };
6640
+ const errors = (_c = (_b = command.body) == null ? void 0 : _b.errors) != null ? _c : { error: genericErrorMessage };
6529
6641
  const currentStep = rootComponentRef.current.getStep();
6530
6642
  if (currentStep) {
6531
6643
  updateStep(
@@ -6539,16 +6651,16 @@ function useDynamicFlowCore(props) {
6539
6651
  external: void 0
6540
6652
  // and no external, to avoid retriggering it
6541
6653
  }),
6542
- (_d = (_c = rootComponentRef.current.getStep()) == null ? void 0 : _c.etag) != null ? _d : null
6654
+ (_e = (_d = rootComponentRef.current.getStep()) == null ? void 0 : _d.etag) != null ? _e : null
6543
6655
  );
6544
6656
  } else {
6545
- const errorMessage = ((_f = (_e = command.body) == null ? void 0 : _e.errors) == null ? void 0 : _f.error) || ((_g = command.httpError) == null ? void 0 : _g.message) || "Initial request failed";
6657
+ const errorMessage = ((_g = (_f = command.body) == null ? void 0 : _f.errors) == null ? void 0 : _g.error) || ((_h = command.httpError) == null ? void 0 : _h.message) || "Initial request failed";
6546
6658
  closeWithError(
6547
6659
  new Error(errorMessage, {
6548
6660
  cause: `method: ${action.method}, url: ${action.url}`
6549
6661
  }),
6550
6662
  {},
6551
- (_h = command.httpError) == null ? void 0 : _h.statusCode
6663
+ (_i = command.httpError) == null ? void 0 : _i.statusCode
6552
6664
  );
6553
6665
  }
6554
6666
  break;
@@ -1,6 +1,7 @@
1
1
  import type { BaseComponent, DomainComponent, LayoutComponent, LoadingState, LocalValue, OnBehavior, SchemaComponent, UpdateComponent } from '../../types';
2
- import { Model } from '@wise/dynamic-flow-types/spec';
3
2
  import type { NavigationStackBehavior, Step } from '@wise/dynamic-flow-types/spec';
3
+ import { Model } from '@wise/dynamic-flow-types/spec';
4
+ import { SubmissionRequestCache } from '../../prefetching/request-cache';
4
5
  import type { AnalyticsEventDispatcher } from '../../features/events';
5
6
  import type { StepPolling } from '../../features/polling/getStepPolling';
6
7
  import { StepRefreshAfter } from '../../features/refreshAfter/getStepRefreshAfter';
@@ -26,6 +27,7 @@ export type StepDomainComponent = BaseComponent & {
26
27
  title?: string;
27
28
  modals: ModalComponent[];
28
29
  tags?: string[];
30
+ submissionRequestsCache: SubmissionRequestCache;
29
31
  dismissModal: () => void;
30
32
  dismissAllModals: () => void;
31
33
  showModal: (modal: ModalComponent) => void;
@@ -45,7 +47,7 @@ export type BackNavigation = {
45
47
  title?: string;
46
48
  onClick: () => void;
47
49
  };
48
- export declare const createStepComponent: (stepProps: Pick<StepDomainComponent, "uid" | "back" | "toolbar" | "layoutComponents" | "schemaComponents" | "footerComponents" | "control" | "description" | "error" | "externalConfirmation" | "loadingState" | "stackBehavior" | "etag" | "step" | "title" | "tags" | "trackEvent" | "onBehavior"> & {
50
+ export declare const createStepComponent: (stepProps: Pick<StepDomainComponent, "uid" | "back" | "toolbar" | "layoutComponents" | "schemaComponents" | "footerComponents" | "control" | "description" | "error" | "externalConfirmation" | "loadingState" | "stackBehavior" | "etag" | "step" | "title" | "tags" | "submissionRequestsCache" | "trackEvent" | "onBehavior"> & {
49
51
  stepPolling?: StepPolling;
50
52
  stepRefreshAfter?: StepRefreshAfter;
51
53
  updateComponent: UpdateComponent;
@@ -1,14 +1,15 @@
1
1
  import type { Polling } from '@wise/dynamic-flow-types/spec';
2
- import type { OnBehavior, OnPoll } from '../../types';
2
+ import type { Behavior, OnBehavior, OnPoll } from '../../types';
3
3
  import { LoggingEventDispatcher } from '../events';
4
4
  export type StepPollingProps = {
5
5
  pollingConfig: Polling;
6
6
  logEvent: LoggingEventDispatcher;
7
7
  onBehavior: OnBehavior;
8
8
  onPoll: OnPoll;
9
+ registerSubmissionBehavior: (behavior: Behavior) => void;
9
10
  };
10
11
  export type StepPolling = {
11
12
  start: () => void;
12
13
  stop: () => void;
13
14
  };
14
- export declare const getStepPolling: ({ pollingConfig, logEvent, onBehavior, onPoll, }: StepPollingProps) => StepPolling;
15
+ export declare const getStepPolling: ({ pollingConfig, logEvent, onBehavior, onPoll, registerSubmissionBehavior, }: StepPollingProps) => StepPolling;