@wise/dynamic-flow-client 5.25.4 → 5.26.0-experimental-cadf60b

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 (34) hide show
  1. package/build/main.js +219 -14
  2. package/build/main.mjs +216 -9
  3. package/build/tsconfig.types.tsbuildinfo +1 -1
  4. package/build/types/controller/FlowController.d.ts.map +1 -1
  5. package/build/types/domain/components/UpdatableComponent.d.ts +16 -0
  6. package/build/types/domain/components/UpdatableComponent.d.ts.map +1 -0
  7. package/build/types/domain/components/step/StepDomainComponent.d.ts +3 -0
  8. package/build/types/domain/components/step/StepDomainComponent.d.ts.map +1 -1
  9. package/build/types/domain/features/domain-event-bus/DomainEventBus.d.ts +9 -0
  10. package/build/types/domain/features/domain-event-bus/DomainEventBus.d.ts.map +1 -0
  11. package/build/types/domain/features/domain-event-bus/index.d.ts +3 -0
  12. package/build/types/domain/features/domain-event-bus/index.d.ts.map +1 -0
  13. package/build/types/domain/features/domain-event-bus/types.d.ts +9 -0
  14. package/build/types/domain/features/domain-event-bus/types.d.ts.map +1 -0
  15. package/build/types/domain/features/eventNames.d.ts +1 -1
  16. package/build/types/domain/features/eventNames.d.ts.map +1 -1
  17. package/build/types/domain/features/updatable/getPerformUpdateQuery.d.ts +12 -0
  18. package/build/types/domain/features/updatable/getPerformUpdateQuery.d.ts.map +1 -0
  19. package/build/types/domain/mappers/layout/updatableLayoutToComponent.d.ts +6 -0
  20. package/build/types/domain/mappers/layout/updatableLayoutToComponent.d.ts.map +1 -0
  21. package/build/types/domain/mappers/mapLayoutToComponent.d.ts.map +1 -1
  22. package/build/types/domain/mappers/mapStepToComponent.d.ts +1 -1
  23. package/build/types/domain/mappers/mapStepToComponent.d.ts.map +1 -1
  24. package/build/types/domain/mappers/schema/types.d.ts +3 -1
  25. package/build/types/domain/mappers/schema/types.d.ts.map +1 -1
  26. package/build/types/domain/mappers/utils/behavior-utils.d.ts.map +1 -1
  27. package/build/types/domain/types.d.ts +4 -3
  28. package/build/types/domain/types.d.ts.map +1 -1
  29. package/build/types/renderers/mappers/componentToRendererProps.d.ts.map +1 -1
  30. package/build/types/renderers/mappers/updatableComponentToProps.d.ts +5 -0
  31. package/build/types/renderers/mappers/updatableComponentToProps.d.ts.map +1 -0
  32. package/build/types/test-utils/getMergedTestRenderers.d.ts +1 -1
  33. package/build/types/test-utils/getMergedTestRenderers.d.ts.map +1 -1
  34. package/package.json +2 -2
package/build/main.js CHANGED
@@ -870,6 +870,7 @@ var getChildren = (node) => {
870
870
  case "step":
871
871
  case "modal":
872
872
  case "section":
873
+ case "updatable":
873
874
  return node.childrenProps;
874
875
  case "columns":
875
876
  return [...node.startChildrenProps, ...node.endChildrenProps];
@@ -1377,12 +1378,6 @@ var normaliseBehavior = (behavior, stepActions) => {
1377
1378
  track: true
1378
1379
  });
1379
1380
  }
1380
- if (behavior.type === "dispatch-event") {
1381
- return {
1382
- type: "none",
1383
- track: false
1384
- };
1385
- }
1386
1381
  if (behavior.type === "link") {
1387
1382
  return __spreadProps(__spreadValues({}, behavior), {
1388
1383
  type: "link",
@@ -3100,6 +3095,157 @@ var mapTabs = (tabs, uid, mapperProps, schemaComponents) => tabs.map(({ componen
3100
3095
  )
3101
3096
  }));
3102
3097
 
3098
+ // src/domain/components/UpdatableComponent.ts
3099
+ var createUpdatableComponent = (props, query, onComponentUpdate, subscribe, unsubscribe) => {
3100
+ const updateComponent = getInputUpdateFunction(onComponentUpdate);
3101
+ let abortController = new AbortController();
3102
+ const triggerUpdate = (data) => {
3103
+ abortController.abort();
3104
+ abortController = new AbortController();
3105
+ const { signal } = abortController;
3106
+ query({ data, signal }).then((state) => {
3107
+ component.update(state);
3108
+ }).catch(() => {
3109
+ });
3110
+ };
3111
+ const component = __spreadProps(__spreadValues({}, props), {
3112
+ type: "updatable",
3113
+ kind: "layout",
3114
+ getChildren() {
3115
+ return component.state.components;
3116
+ },
3117
+ update(newState) {
3118
+ if (!newState) {
3119
+ return;
3120
+ }
3121
+ component.getChildren().forEach((c) => c.stop());
3122
+ updateComponent(component, (draft) => {
3123
+ draft.state = newState;
3124
+ });
3125
+ component.getChildren().forEach((c) => c.start());
3126
+ },
3127
+ start() {
3128
+ subscribe(triggerUpdate);
3129
+ },
3130
+ stop() {
3131
+ unsubscribe(triggerUpdate);
3132
+ abortController.abort();
3133
+ }
3134
+ });
3135
+ return component;
3136
+ };
3137
+
3138
+ // src/domain/features/updatable/getPerformUpdateQuery.ts
3139
+ var import_spec = require("@wise/dynamic-flow-types/spec");
3140
+ var getPerformUpdateQuery = (httpClient, baseRequest, fallbackLayout, mapState, trackEvent) => {
3141
+ return async (props) => {
3142
+ const { signal } = props;
3143
+ trackEvent("Updatable Update Triggered");
3144
+ try {
3145
+ const response = await performRequest2(baseRequest, props, httpClient);
3146
+ if (response.ok) {
3147
+ const results = await parseResponse3(response);
3148
+ if (signal.aborted || !results.components) {
3149
+ return null;
3150
+ }
3151
+ const mappedState = mapState(results);
3152
+ trackEvent("Updatable Update Succeeded");
3153
+ return mappedState;
3154
+ }
3155
+ throw Error("error response", { cause: response });
3156
+ } catch (e) {
3157
+ if (signal.aborted) {
3158
+ return null;
3159
+ }
3160
+ trackEvent("Updatable Update Failed");
3161
+ return fallbackLayout != null ? fallbackLayout : null;
3162
+ }
3163
+ };
3164
+ };
3165
+ var performRequest2 = (request, queryProps, httpClient) => {
3166
+ const { url, method, body, headers } = request;
3167
+ return httpClient(url, {
3168
+ method,
3169
+ headers: __spreadProps(__spreadValues({}, headers), { "Content-Type": "application/json" }),
3170
+ signal: queryProps.signal,
3171
+ body: methodSupportsBody(method) ? JSON.stringify(recursiveMerge(body != null ? body : null, queryProps.data)) : void 0
3172
+ });
3173
+ };
3174
+ var parseResponse3 = async (response) => {
3175
+ if (response.ok) {
3176
+ const body = await response.json();
3177
+ const r = (0, import_spec.validateUpdatableResponse)(body);
3178
+ if (r.valid) {
3179
+ return body;
3180
+ }
3181
+ throw Error(`invalid response body: ${r.errors}`);
3182
+ }
3183
+ throw Error("error response");
3184
+ };
3185
+ var methodSupportsBody = (method) => ["POST", "PUT", "PATCH"].includes(method.toUpperCase());
3186
+
3187
+ // src/domain/mappers/layout/updatableLayoutToComponent.ts
3188
+ var updatableLayoutToComponent = (uid, {
3189
+ analyticsId,
3190
+ baseRequest,
3191
+ control,
3192
+ margin,
3193
+ tags,
3194
+ trigger,
3195
+ initialState,
3196
+ onError
3197
+ }, mapperProps, schemaComponents) => {
3198
+ const { httpClient, onComponentUpdate } = mapperProps;
3199
+ const mapLayout = (layout, index) => mapLayoutToComponent(`${uid}-updatable-${index}`, layout, mapperProps, schemaComponents);
3200
+ const mapState = (state) => {
3201
+ var _a;
3202
+ return {
3203
+ components: ((_a = state == null ? void 0 : state.components) != null ? _a : []).map((component2, index) => mapLayout(component2, index))
3204
+ };
3205
+ };
3206
+ const trackEvent = (name, data) => mapperProps.trackEvent(name, __spreadProps(__spreadValues({}, data), { analyticsId }));
3207
+ const fallbackLayout = (onError == null ? void 0 : onError.fallbackLayout) ? mapState({ components: onError.fallbackLayout }) : null;
3208
+ const queryFunction = getPerformUpdateQuery(
3209
+ httpClient,
3210
+ baseRequest,
3211
+ fallbackLayout,
3212
+ mapState,
3213
+ trackEvent
3214
+ );
3215
+ const { subscribe, unsubscribe } = getTriggerFunction(trigger, mapperProps);
3216
+ const component = createUpdatableComponent(
3217
+ {
3218
+ uid,
3219
+ analyticsId,
3220
+ control,
3221
+ margin: margin != null ? margin : "md",
3222
+ tags,
3223
+ state: mapState(initialState)
3224
+ },
3225
+ queryFunction,
3226
+ onComponentUpdate,
3227
+ subscribe,
3228
+ unsubscribe
3229
+ );
3230
+ return component;
3231
+ };
3232
+ var getTriggerFunction = (trigger, mapperProps) => {
3233
+ const { stepEventSubscriber } = mapperProps;
3234
+ if (trigger.type === "on-event") {
3235
+ return {
3236
+ subscribe: (handler) => {
3237
+ stepEventSubscriber.addEventListener(trigger.eventName, handler);
3238
+ },
3239
+ unsubscribe: (handler) => {
3240
+ stepEventSubscriber.removeEventListener(trigger.eventName, handler);
3241
+ }
3242
+ };
3243
+ }
3244
+ return { subscribe() {
3245
+ }, unsubscribe() {
3246
+ } };
3247
+ };
3248
+
3103
3249
  // src/domain/components/UpsellComponent.ts
3104
3250
  var createUpsellComponent = (props, onComponentUpdate) => {
3105
3251
  const update = getInputUpdateFunction(onComponentUpdate);
@@ -3195,6 +3341,8 @@ var mapLayoutToComponent = (uid, layout, mapperProps, schemaComponents) => {
3195
3341
  return tabsLayoutToComponent(uid, layout, mapperProps, schemaComponents);
3196
3342
  case "upsell":
3197
3343
  return upsellLayoutToComponent(uid, layout, mapperProps);
3344
+ case "updatable":
3345
+ return updatableLayoutToComponent(uid, layout, mapperProps, schemaComponents);
3198
3346
  default:
3199
3347
  throw new Error(`Invalid layout type in ${JSON.stringify(layout)}`);
3200
3348
  }
@@ -3392,6 +3540,7 @@ var createStepComponent = (props) => {
3392
3540
  });
3393
3541
  },
3394
3542
  start() {
3543
+ getAllSubcomponents(component).forEach((child) => child.start());
3395
3544
  if (component.state === "initial") {
3396
3545
  onLoad == null ? void 0 : onLoad();
3397
3546
  }
@@ -3401,7 +3550,9 @@ var createStepComponent = (props) => {
3401
3550
  stepPolling == null ? void 0 : stepPolling.start();
3402
3551
  stepRefreshAfter == null ? void 0 : stepRefreshAfter.start();
3403
3552
  stepPrefetch.start(component.getSubmittableValueSync());
3404
- getAllSubcomponents(component).forEach((child) => child.start());
3553
+ },
3554
+ dispatchStepEvent(eventName) {
3555
+ props.eventPublisher.dispatchEvent(eventName, component.getSubmittableValueSync());
3405
3556
  },
3406
3557
  stop() {
3407
3558
  stepPolling == null ? void 0 : stepPolling.stop();
@@ -3447,6 +3598,32 @@ var validateInputComponent = (component, inputComponent, isStrict, logEvent) =>
3447
3598
  return isSchemaReferenced || isStrict ? isValid : true;
3448
3599
  };
3449
3600
 
3601
+ // src/domain/features/domain-event-bus/DomainEventBus.ts
3602
+ var DomainEventBus = class {
3603
+ constructor() {
3604
+ this.listeners = /* @__PURE__ */ new Map();
3605
+ }
3606
+ dispatchEvent(eventName, data) {
3607
+ const eventListeners = this.listeners.get(eventName);
3608
+ if (eventListeners) {
3609
+ eventListeners.forEach((callback) => callback(data));
3610
+ }
3611
+ }
3612
+ addEventListener(eventName, callback) {
3613
+ var _a;
3614
+ if (!this.listeners.has(eventName)) {
3615
+ this.listeners.set(eventName, /* @__PURE__ */ new Set());
3616
+ }
3617
+ (_a = this.listeners.get(eventName)) == null ? void 0 : _a.add(callback);
3618
+ }
3619
+ removeEventListener(eventName, callback) {
3620
+ const eventListeners = this.listeners.get(eventName);
3621
+ if (eventListeners) {
3622
+ eventListeners.delete(callback);
3623
+ }
3624
+ }
3625
+ };
3626
+
3450
3627
  // src/domain/features/polling/getStepPolling.ts
3451
3628
  var getStepPolling = ({
3452
3629
  pollingConfig,
@@ -3509,7 +3686,7 @@ var getErrorMessage = (error) => {
3509
3686
  };
3510
3687
 
3511
3688
  // src/controller/response-utils.ts
3512
- var import_spec = require("@wise/dynamic-flow-types/spec");
3689
+ var import_spec2 = require("@wise/dynamic-flow-types/spec");
3513
3690
  var parseResponseBodyAsJsonElement = async (response) => {
3514
3691
  assertResponseIsValid(response);
3515
3692
  try {
@@ -3526,7 +3703,7 @@ var parseResponseBodyAsText = async (response) => {
3526
3703
  }
3527
3704
  };
3528
3705
  function isActionResponseBody(body) {
3529
- return (0, import_spec.validateActionResponse)(body).valid;
3706
+ return (0, import_spec2.validateActionResponse)(body).valid;
3530
3707
  }
3531
3708
  function assertActionResponseBody(body) {
3532
3709
  if (!isObject(body) || !isObject(body.action)) {
@@ -3546,7 +3723,7 @@ function assertModalResponseBody(body) {
3546
3723
  );
3547
3724
  }
3548
3725
  function assertSubflowResponseBody(body) {
3549
- const { valid } = (0, import_spec.validateSubflowResponse)(body);
3726
+ const { valid } = (0, import_spec2.validateSubflowResponse)(body);
3550
3727
  if (valid) {
3551
3728
  return;
3552
3729
  }
@@ -7315,8 +7492,10 @@ var mapStepToComponent = (_a) => {
7315
7492
  const registerSubmissionBehavior = (behavior) => {
7316
7493
  submissionBehaviors.push(behavior);
7317
7494
  };
7495
+ const stepEventBus = new DomainEventBus();
7318
7496
  const mapperProps = __spreadProps(__spreadValues({}, restProps), {
7319
7497
  features,
7498
+ stepEventSubscriber: stepEventBus,
7320
7499
  trackEvent,
7321
7500
  onBehavior,
7322
7501
  registerSubmissionBehavior
@@ -7377,6 +7556,7 @@ var mapStepToComponent = (_a) => {
7377
7556
  step,
7378
7557
  referencedSchemaIds,
7379
7558
  features,
7559
+ eventPublisher: stepEventBus,
7380
7560
  onComponentUpdate,
7381
7561
  trackEvent,
7382
7562
  logEvent,
@@ -7437,13 +7617,13 @@ var getLayoutAndFooter = (step, features) => {
7437
7617
  };
7438
7618
 
7439
7619
  // src/utils/analyse-step.ts
7440
- var import_spec2 = require("@wise/dynamic-flow-types/spec");
7620
+ var import_spec3 = require("@wise/dynamic-flow-types/spec");
7441
7621
  var analyseStep = (step, logEvent) => {
7442
7622
  validateStepTypes(step, logEvent);
7443
7623
  };
7444
7624
  var validateStepTypes = (step, logEvent) => {
7445
7625
  var _a;
7446
- const validationResult = (0, import_spec2.validateStep)(step);
7626
+ const validationResult = (0, import_spec3.validateStep)(step);
7447
7627
  if (!validationResult.valid) {
7448
7628
  logEvent("info", "The provided step does not conform to the specification.", {
7449
7629
  stepId: (_a = step.id) != null ? _a : step.key,
@@ -7979,7 +8159,7 @@ var createFlowController = (props) => {
7979
8159
  onCancellation == null ? void 0 : onCancellation();
7980
8160
  };
7981
8161
  const onBehavior = async (behavior) => {
7982
- var _a;
8162
+ var _a, _b;
7983
8163
  trackBehaviorTriggered(trackEvent, behavior);
7984
8164
  switch (behavior.type) {
7985
8165
  case "delay": {
@@ -8081,6 +8261,10 @@ var createFlowController = (props) => {
8081
8261
  step: currentStep,
8082
8262
  stepLocalValue: rootComponent.getLocalValue()
8083
8263
  }, getMapperProps()), {
8264
+ stepEventSubscriber: { addEventListener: () => {
8265
+ }, removeEventListener: () => {
8266
+ } },
8267
+ // IMPORTANT! this means that components in modal responses won't be able to listen to step events
8084
8268
  registerSubmissionBehavior: () => {
8085
8269
  }
8086
8270
  // this means actions in modal responses won't have prefetching
@@ -8115,6 +8299,10 @@ var createFlowController = (props) => {
8115
8299
  }
8116
8300
  break;
8117
8301
  }
8302
+ case "dispatch-event": {
8303
+ (_b = rootComponent.getStep()) == null ? void 0 : _b.dispatchStepEvent(behavior.eventName);
8304
+ break;
8305
+ }
8118
8306
  case "none":
8119
8307
  break;
8120
8308
  }
@@ -9398,6 +9586,17 @@ var uploadInputComponentToProps = (component, rendererMapperProps) => {
9398
9586
  });
9399
9587
  };
9400
9588
 
9589
+ // src/renderers/mappers/updatableComponentToProps.ts
9590
+ var updatableComponentToProps = (component, rendererMapperProps) => {
9591
+ const childrenProps = component.state.components.map(
9592
+ (c) => componentToRendererProps(c, rendererMapperProps)
9593
+ );
9594
+ return __spreadProps(__spreadValues(__spreadValues({}, pick(component, "uid", "analyticsId", "type", "control", "margin", "tags")), rendererMapperProps), {
9595
+ children: childrenProps.map(rendererMapperProps.render),
9596
+ childrenProps
9597
+ });
9598
+ };
9599
+
9401
9600
  // src/renderers/mappers/upsellComponentToProps.ts
9402
9601
  var upsellComponentToProps = (component, rendererMapperProps) => __spreadValues(__spreadProps(__spreadValues({}, pick(component, "uid", "analyticsId", "type", "control", "margin", "media", "tags", "text")), {
9403
9602
  callToAction: mapCallToAction(component.callToAction),
@@ -9514,6 +9713,8 @@ var getComponentProps = (component, rendererMapperProps) => {
9514
9713
  return uploadInputComponentToProps(component, rendererMapperProps);
9515
9714
  case "upsell":
9516
9715
  return upsellComponentToProps(component, rendererMapperProps);
9716
+ case "updatable":
9717
+ return updatableComponentToProps(component, rendererMapperProps);
9517
9718
  case "persist-async":
9518
9719
  return persistAsyncComponentToProps(component, rendererMapperProps);
9519
9720
  case "root":
@@ -9916,7 +10117,11 @@ var eventNames = [
9916
10117
  "Collection Filter Failed",
9917
10118
  "Collection Pagination Triggered",
9918
10119
  "Collection Pagination Succeeded",
9919
- "Collection Pagination Failed"
10120
+ "Collection Pagination Failed",
10121
+ "Updatable Update Triggered",
10122
+ // agree on these names
10123
+ "Updatable Update Succeeded",
10124
+ "Updatable Update Failed"
9920
10125
  ];
9921
10126
 
9922
10127
  // src/renderers/subflow/getDynamicSubflowRenderer.tsx
package/build/main.mjs CHANGED
@@ -839,6 +839,7 @@ var getChildren = (node) => {
839
839
  case "step":
840
840
  case "modal":
841
841
  case "section":
842
+ case "updatable":
842
843
  return node.childrenProps;
843
844
  case "columns":
844
845
  return [...node.startChildrenProps, ...node.endChildrenProps];
@@ -1346,12 +1347,6 @@ var normaliseBehavior = (behavior, stepActions) => {
1346
1347
  track: true
1347
1348
  });
1348
1349
  }
1349
- if (behavior.type === "dispatch-event") {
1350
- return {
1351
- type: "none",
1352
- track: false
1353
- };
1354
- }
1355
1350
  if (behavior.type === "link") {
1356
1351
  return __spreadProps(__spreadValues({}, behavior), {
1357
1352
  type: "link",
@@ -3069,6 +3064,159 @@ var mapTabs = (tabs, uid, mapperProps, schemaComponents) => tabs.map(({ componen
3069
3064
  )
3070
3065
  }));
3071
3066
 
3067
+ // src/domain/components/UpdatableComponent.ts
3068
+ var createUpdatableComponent = (props, query, onComponentUpdate, subscribe, unsubscribe) => {
3069
+ const updateComponent = getInputUpdateFunction(onComponentUpdate);
3070
+ let abortController = new AbortController();
3071
+ const triggerUpdate = (data) => {
3072
+ abortController.abort();
3073
+ abortController = new AbortController();
3074
+ const { signal } = abortController;
3075
+ query({ data, signal }).then((state) => {
3076
+ component.update(state);
3077
+ }).catch(() => {
3078
+ });
3079
+ };
3080
+ const component = __spreadProps(__spreadValues({}, props), {
3081
+ type: "updatable",
3082
+ kind: "layout",
3083
+ getChildren() {
3084
+ return component.state.components;
3085
+ },
3086
+ update(newState) {
3087
+ if (!newState) {
3088
+ return;
3089
+ }
3090
+ component.getChildren().forEach((c) => c.stop());
3091
+ updateComponent(component, (draft) => {
3092
+ draft.state = newState;
3093
+ });
3094
+ component.getChildren().forEach((c) => c.start());
3095
+ },
3096
+ start() {
3097
+ subscribe(triggerUpdate);
3098
+ },
3099
+ stop() {
3100
+ unsubscribe(triggerUpdate);
3101
+ abortController.abort();
3102
+ }
3103
+ });
3104
+ return component;
3105
+ };
3106
+
3107
+ // src/domain/features/updatable/getPerformUpdateQuery.ts
3108
+ import {
3109
+ validateUpdatableResponse
3110
+ } from "@wise/dynamic-flow-types/spec";
3111
+ var getPerformUpdateQuery = (httpClient, baseRequest, fallbackLayout, mapState, trackEvent) => {
3112
+ return async (props) => {
3113
+ const { signal } = props;
3114
+ trackEvent("Updatable Update Triggered");
3115
+ try {
3116
+ const response = await performRequest2(baseRequest, props, httpClient);
3117
+ if (response.ok) {
3118
+ const results = await parseResponse3(response);
3119
+ if (signal.aborted || !results.components) {
3120
+ return null;
3121
+ }
3122
+ const mappedState = mapState(results);
3123
+ trackEvent("Updatable Update Succeeded");
3124
+ return mappedState;
3125
+ }
3126
+ throw Error("error response", { cause: response });
3127
+ } catch (e) {
3128
+ if (signal.aborted) {
3129
+ return null;
3130
+ }
3131
+ trackEvent("Updatable Update Failed");
3132
+ return fallbackLayout != null ? fallbackLayout : null;
3133
+ }
3134
+ };
3135
+ };
3136
+ var performRequest2 = (request, queryProps, httpClient) => {
3137
+ const { url, method, body, headers } = request;
3138
+ return httpClient(url, {
3139
+ method,
3140
+ headers: __spreadProps(__spreadValues({}, headers), { "Content-Type": "application/json" }),
3141
+ signal: queryProps.signal,
3142
+ body: methodSupportsBody(method) ? JSON.stringify(recursiveMerge(body != null ? body : null, queryProps.data)) : void 0
3143
+ });
3144
+ };
3145
+ var parseResponse3 = async (response) => {
3146
+ if (response.ok) {
3147
+ const body = await response.json();
3148
+ const r = validateUpdatableResponse(body);
3149
+ if (r.valid) {
3150
+ return body;
3151
+ }
3152
+ throw Error(`invalid response body: ${r.errors}`);
3153
+ }
3154
+ throw Error("error response");
3155
+ };
3156
+ var methodSupportsBody = (method) => ["POST", "PUT", "PATCH"].includes(method.toUpperCase());
3157
+
3158
+ // src/domain/mappers/layout/updatableLayoutToComponent.ts
3159
+ var updatableLayoutToComponent = (uid, {
3160
+ analyticsId,
3161
+ baseRequest,
3162
+ control,
3163
+ margin,
3164
+ tags,
3165
+ trigger,
3166
+ initialState,
3167
+ onError
3168
+ }, mapperProps, schemaComponents) => {
3169
+ const { httpClient, onComponentUpdate } = mapperProps;
3170
+ const mapLayout = (layout, index) => mapLayoutToComponent(`${uid}-updatable-${index}`, layout, mapperProps, schemaComponents);
3171
+ const mapState = (state) => {
3172
+ var _a;
3173
+ return {
3174
+ components: ((_a = state == null ? void 0 : state.components) != null ? _a : []).map((component2, index) => mapLayout(component2, index))
3175
+ };
3176
+ };
3177
+ const trackEvent = (name, data) => mapperProps.trackEvent(name, __spreadProps(__spreadValues({}, data), { analyticsId }));
3178
+ const fallbackLayout = (onError == null ? void 0 : onError.fallbackLayout) ? mapState({ components: onError.fallbackLayout }) : null;
3179
+ const queryFunction = getPerformUpdateQuery(
3180
+ httpClient,
3181
+ baseRequest,
3182
+ fallbackLayout,
3183
+ mapState,
3184
+ trackEvent
3185
+ );
3186
+ const { subscribe, unsubscribe } = getTriggerFunction(trigger, mapperProps);
3187
+ const component = createUpdatableComponent(
3188
+ {
3189
+ uid,
3190
+ analyticsId,
3191
+ control,
3192
+ margin: margin != null ? margin : "md",
3193
+ tags,
3194
+ state: mapState(initialState)
3195
+ },
3196
+ queryFunction,
3197
+ onComponentUpdate,
3198
+ subscribe,
3199
+ unsubscribe
3200
+ );
3201
+ return component;
3202
+ };
3203
+ var getTriggerFunction = (trigger, mapperProps) => {
3204
+ const { stepEventSubscriber } = mapperProps;
3205
+ if (trigger.type === "on-event") {
3206
+ return {
3207
+ subscribe: (handler) => {
3208
+ stepEventSubscriber.addEventListener(trigger.eventName, handler);
3209
+ },
3210
+ unsubscribe: (handler) => {
3211
+ stepEventSubscriber.removeEventListener(trigger.eventName, handler);
3212
+ }
3213
+ };
3214
+ }
3215
+ return { subscribe() {
3216
+ }, unsubscribe() {
3217
+ } };
3218
+ };
3219
+
3072
3220
  // src/domain/components/UpsellComponent.ts
3073
3221
  var createUpsellComponent = (props, onComponentUpdate) => {
3074
3222
  const update = getInputUpdateFunction(onComponentUpdate);
@@ -3164,6 +3312,8 @@ var mapLayoutToComponent = (uid, layout, mapperProps, schemaComponents) => {
3164
3312
  return tabsLayoutToComponent(uid, layout, mapperProps, schemaComponents);
3165
3313
  case "upsell":
3166
3314
  return upsellLayoutToComponent(uid, layout, mapperProps);
3315
+ case "updatable":
3316
+ return updatableLayoutToComponent(uid, layout, mapperProps, schemaComponents);
3167
3317
  default:
3168
3318
  throw new Error(`Invalid layout type in ${JSON.stringify(layout)}`);
3169
3319
  }
@@ -3361,6 +3511,7 @@ var createStepComponent = (props) => {
3361
3511
  });
3362
3512
  },
3363
3513
  start() {
3514
+ getAllSubcomponents(component).forEach((child) => child.start());
3364
3515
  if (component.state === "initial") {
3365
3516
  onLoad == null ? void 0 : onLoad();
3366
3517
  }
@@ -3370,7 +3521,9 @@ var createStepComponent = (props) => {
3370
3521
  stepPolling == null ? void 0 : stepPolling.start();
3371
3522
  stepRefreshAfter == null ? void 0 : stepRefreshAfter.start();
3372
3523
  stepPrefetch.start(component.getSubmittableValueSync());
3373
- getAllSubcomponents(component).forEach((child) => child.start());
3524
+ },
3525
+ dispatchStepEvent(eventName) {
3526
+ props.eventPublisher.dispatchEvent(eventName, component.getSubmittableValueSync());
3374
3527
  },
3375
3528
  stop() {
3376
3529
  stepPolling == null ? void 0 : stepPolling.stop();
@@ -3416,6 +3569,32 @@ var validateInputComponent = (component, inputComponent, isStrict, logEvent) =>
3416
3569
  return isSchemaReferenced || isStrict ? isValid : true;
3417
3570
  };
3418
3571
 
3572
+ // src/domain/features/domain-event-bus/DomainEventBus.ts
3573
+ var DomainEventBus = class {
3574
+ constructor() {
3575
+ this.listeners = /* @__PURE__ */ new Map();
3576
+ }
3577
+ dispatchEvent(eventName, data) {
3578
+ const eventListeners = this.listeners.get(eventName);
3579
+ if (eventListeners) {
3580
+ eventListeners.forEach((callback) => callback(data));
3581
+ }
3582
+ }
3583
+ addEventListener(eventName, callback) {
3584
+ var _a;
3585
+ if (!this.listeners.has(eventName)) {
3586
+ this.listeners.set(eventName, /* @__PURE__ */ new Set());
3587
+ }
3588
+ (_a = this.listeners.get(eventName)) == null ? void 0 : _a.add(callback);
3589
+ }
3590
+ removeEventListener(eventName, callback) {
3591
+ const eventListeners = this.listeners.get(eventName);
3592
+ if (eventListeners) {
3593
+ eventListeners.delete(callback);
3594
+ }
3595
+ }
3596
+ };
3597
+
3419
3598
  // src/domain/features/polling/getStepPolling.ts
3420
3599
  var getStepPolling = ({
3421
3600
  pollingConfig,
@@ -7284,8 +7463,10 @@ var mapStepToComponent = (_a) => {
7284
7463
  const registerSubmissionBehavior = (behavior) => {
7285
7464
  submissionBehaviors.push(behavior);
7286
7465
  };
7466
+ const stepEventBus = new DomainEventBus();
7287
7467
  const mapperProps = __spreadProps(__spreadValues({}, restProps), {
7288
7468
  features,
7469
+ stepEventSubscriber: stepEventBus,
7289
7470
  trackEvent,
7290
7471
  onBehavior,
7291
7472
  registerSubmissionBehavior
@@ -7346,6 +7527,7 @@ var mapStepToComponent = (_a) => {
7346
7527
  step,
7347
7528
  referencedSchemaIds,
7348
7529
  features,
7530
+ eventPublisher: stepEventBus,
7349
7531
  onComponentUpdate,
7350
7532
  trackEvent,
7351
7533
  logEvent,
@@ -7948,7 +8130,7 @@ var createFlowController = (props) => {
7948
8130
  onCancellation == null ? void 0 : onCancellation();
7949
8131
  };
7950
8132
  const onBehavior = async (behavior) => {
7951
- var _a;
8133
+ var _a, _b;
7952
8134
  trackBehaviorTriggered(trackEvent, behavior);
7953
8135
  switch (behavior.type) {
7954
8136
  case "delay": {
@@ -8050,6 +8232,10 @@ var createFlowController = (props) => {
8050
8232
  step: currentStep,
8051
8233
  stepLocalValue: rootComponent.getLocalValue()
8052
8234
  }, getMapperProps()), {
8235
+ stepEventSubscriber: { addEventListener: () => {
8236
+ }, removeEventListener: () => {
8237
+ } },
8238
+ // IMPORTANT! this means that components in modal responses won't be able to listen to step events
8053
8239
  registerSubmissionBehavior: () => {
8054
8240
  }
8055
8241
  // this means actions in modal responses won't have prefetching
@@ -8084,6 +8270,10 @@ var createFlowController = (props) => {
8084
8270
  }
8085
8271
  break;
8086
8272
  }
8273
+ case "dispatch-event": {
8274
+ (_b = rootComponent.getStep()) == null ? void 0 : _b.dispatchStepEvent(behavior.eventName);
8275
+ break;
8276
+ }
8087
8277
  case "none":
8088
8278
  break;
8089
8279
  }
@@ -9367,6 +9557,17 @@ var uploadInputComponentToProps = (component, rendererMapperProps) => {
9367
9557
  });
9368
9558
  };
9369
9559
 
9560
+ // src/renderers/mappers/updatableComponentToProps.ts
9561
+ var updatableComponentToProps = (component, rendererMapperProps) => {
9562
+ const childrenProps = component.state.components.map(
9563
+ (c) => componentToRendererProps(c, rendererMapperProps)
9564
+ );
9565
+ return __spreadProps(__spreadValues(__spreadValues({}, pick(component, "uid", "analyticsId", "type", "control", "margin", "tags")), rendererMapperProps), {
9566
+ children: childrenProps.map(rendererMapperProps.render),
9567
+ childrenProps
9568
+ });
9569
+ };
9570
+
9370
9571
  // src/renderers/mappers/upsellComponentToProps.ts
9371
9572
  var upsellComponentToProps = (component, rendererMapperProps) => __spreadValues(__spreadProps(__spreadValues({}, pick(component, "uid", "analyticsId", "type", "control", "margin", "media", "tags", "text")), {
9372
9573
  callToAction: mapCallToAction(component.callToAction),
@@ -9483,6 +9684,8 @@ var getComponentProps = (component, rendererMapperProps) => {
9483
9684
  return uploadInputComponentToProps(component, rendererMapperProps);
9484
9685
  case "upsell":
9485
9686
  return upsellComponentToProps(component, rendererMapperProps);
9687
+ case "updatable":
9688
+ return updatableComponentToProps(component, rendererMapperProps);
9486
9689
  case "persist-async":
9487
9690
  return persistAsyncComponentToProps(component, rendererMapperProps);
9488
9691
  case "root":
@@ -9885,7 +10088,11 @@ var eventNames = [
9885
10088
  "Collection Filter Failed",
9886
10089
  "Collection Pagination Triggered",
9887
10090
  "Collection Pagination Succeeded",
9888
- "Collection Pagination Failed"
10091
+ "Collection Pagination Failed",
10092
+ "Updatable Update Triggered",
10093
+ // agree on these names
10094
+ "Updatable Update Succeeded",
10095
+ "Updatable Update Failed"
9889
10096
  ];
9890
10097
 
9891
10098
  // src/renderers/subflow/getDynamicSubflowRenderer.tsx