@wise/dynamic-flow-client 5.5.1 → 5.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/main.mjs CHANGED
@@ -1307,9 +1307,10 @@ var createBoxComponent = (boxProps) => __spreadProps(__spreadValues({}, boxProps
1307
1307
  });
1308
1308
 
1309
1309
  // src/domain/mappers/utils/layout-utils.ts
1310
- var removeMarginFromLayout = (component) => {
1311
- return __spreadProps(__spreadValues({}, component), {
1312
- margin: "xs"
1310
+ var normaliseMarginInLastComponent = (container) => {
1311
+ return container.map((component, index) => {
1312
+ const shouldRemoveMargin = index === container.length - 1 && component.margin === void 0;
1313
+ return shouldRemoveMargin ? __spreadProps(__spreadValues({}, component), { margin: "xs" }) : component;
1313
1314
  });
1314
1315
  };
1315
1316
 
@@ -1322,16 +1323,9 @@ var boxLayoutToComponent = (uid, { border = false, components, control, margin,
1322
1323
  margin: margin != null ? margin : "md",
1323
1324
  width: width != null ? width : "xl",
1324
1325
  tags,
1325
- components: components.map((component, index) => {
1326
- const shouldRemoveMargin = index === components.length - 1 && component.margin === void 0;
1327
- const componentWithAdjustedMargin = shouldRemoveMargin ? removeMarginFromLayout(component) : component;
1328
- return mapLayoutToComponent(
1329
- `${uid}.box-${index}`,
1330
- componentWithAdjustedMargin,
1331
- mapperProps,
1332
- schemaComponents
1333
- );
1334
- })
1326
+ components: normaliseMarginInLastComponent(components).map(
1327
+ (component, index) => mapLayoutToComponent(`${uid}.box-${index}`, component, mapperProps, schemaComponents)
1328
+ )
1335
1329
  });
1336
1330
 
1337
1331
  // src/domain/components/ButtonComponent.ts
@@ -2293,6 +2287,35 @@ var modalToComponent = (uid, { content, title }, mapperProps, schemaComponents)
2293
2287
  mapperProps.onComponentUpdate
2294
2288
  );
2295
2289
 
2290
+ // src/domain/components/step/ExternalConfirmationComponent.ts
2291
+ var createExternalConfirmation = (uid, url, onComponentUpdate) => {
2292
+ const update = getInputUpdateFunction(onComponentUpdate);
2293
+ return {
2294
+ type: "external-confirmation",
2295
+ kind: "layout",
2296
+ uid,
2297
+ url,
2298
+ status: "initial",
2299
+ onSuccess() {
2300
+ update(this, (draft) => {
2301
+ draft.status = "success";
2302
+ });
2303
+ },
2304
+ onFailure() {
2305
+ if (this.status === "initial") {
2306
+ update(this, (draft) => {
2307
+ draft.status = "failure";
2308
+ });
2309
+ }
2310
+ },
2311
+ onCancel() {
2312
+ update(this, (draft) => {
2313
+ draft.status = "dismissed";
2314
+ });
2315
+ }
2316
+ };
2317
+ };
2318
+
2296
2319
  // src/utils/recursiveMerge.ts
2297
2320
  function recursiveMerge(valueA, valueB) {
2298
2321
  if (valueA === null) {
@@ -2332,526 +2355,162 @@ function mergeArrays(valueA, valueB) {
2332
2355
  );
2333
2356
  }
2334
2357
 
2335
- // src/controller/getErrorMessage.ts
2336
- var getErrorMessage = (error) => {
2337
- return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
2338
- };
2358
+ // src/utils/component-utils.ts
2359
+ var getSubmittableData = async (components) => Promise.all(components.map(async (component) => component.getSubmittableValue())).then(
2360
+ (values) => values.reduce((acc, value) => recursiveMerge(acc, value), null)
2361
+ );
2362
+ var getSubmittableDataSync = (components) => components.map((component) => component.getSubmittableValueSync()).reduce((acc, value) => recursiveMerge(acc, value), null);
2363
+ var getLocalValues = (components) => components.map((component) => component.getLocalValue()).reduce((acc, value) => recursiveMerge(acc, value), null);
2339
2364
 
2340
- // src/controller/response-utils.ts
2341
- import { validateActionResponse, validateSubflowResponse } from "@wise/dynamic-flow-types/spec";
2342
- var parseResponseBodyAsJsonElement = async (response) => {
2343
- assertResponseIsValid(response);
2344
- try {
2345
- return await response.json();
2346
- } catch (e) {
2347
- return null;
2348
- }
2349
- };
2350
- var parseResponseBodyAsText = async (response) => {
2351
- try {
2352
- return await response.text();
2353
- } catch (e) {
2354
- return null;
2355
- }
2365
+ // src/domain/components/step/StepDomainComponent.ts
2366
+ var createStepComponent = (stepProps) => {
2367
+ const _a = stepProps, { uid, stepPolling, stepRefreshAfter, stepPrefetch, onComponentUpdate } = _a, rest = __objRest(_a, ["uid", "stepPolling", "stepRefreshAfter", "stepPrefetch", "onComponentUpdate"]);
2368
+ const update = getInputUpdateFunction(onComponentUpdate);
2369
+ const component = __spreadProps(__spreadValues({
2370
+ uid
2371
+ }, rest), {
2372
+ type: "step",
2373
+ kind: "step",
2374
+ modals: [],
2375
+ requestCache: stepPrefetch.requestCache,
2376
+ dismissModal() {
2377
+ var _a2;
2378
+ (_a2 = this.modals.at(-1)) == null ? void 0 : _a2.close();
2379
+ },
2380
+ dismissAllModals() {
2381
+ this._update((draft) => {
2382
+ draft.modals = draft.modals.map((m) => __spreadProps(__spreadValues({}, m), { open: false }));
2383
+ });
2384
+ },
2385
+ showModal(modal) {
2386
+ this._update((draft) => {
2387
+ draft.modals = [...draft.modals, modal];
2388
+ });
2389
+ },
2390
+ _update(updateFn) {
2391
+ update(this, updateFn);
2392
+ },
2393
+ getChildren() {
2394
+ return this.externalConfirmation ? [...this.layoutComponents, this.externalConfirmation] : this.layoutComponents;
2395
+ },
2396
+ getModals() {
2397
+ return this.modals;
2398
+ },
2399
+ async getSubmittableValue() {
2400
+ return getSubmittableData(this.schemaComponents);
2401
+ },
2402
+ getSubmittableValueSync() {
2403
+ return getSubmittableDataSync(this.schemaComponents);
2404
+ },
2405
+ getLocalValue() {
2406
+ return getLocalValues(this.schemaComponents);
2407
+ },
2408
+ validate() {
2409
+ return this.schemaComponents.every(
2410
+ (inputComponent) => inputComponent.isSchemaReferencedInStep ? inputComponent.validate() : true
2411
+ );
2412
+ },
2413
+ setLoadingState(loadingState) {
2414
+ this._update((draft) => {
2415
+ draft.loadingState = loadingState;
2416
+ });
2417
+ },
2418
+ start() {
2419
+ stepPolling == null ? void 0 : stepPolling.start();
2420
+ stepRefreshAfter == null ? void 0 : stepRefreshAfter.start();
2421
+ stepPrefetch.start(this.getSubmittableValueSync());
2422
+ },
2423
+ stop() {
2424
+ stepPolling == null ? void 0 : stepPolling.stop();
2425
+ stepRefreshAfter == null ? void 0 : stepRefreshAfter.stop();
2426
+ stepPrefetch.stop();
2427
+ this._update((draft) => {
2428
+ draft.modals = [];
2429
+ });
2430
+ }
2431
+ });
2432
+ return component;
2356
2433
  };
2357
- function isActionResponseBody(body) {
2358
- return validateActionResponse(body).valid;
2359
- }
2360
- function assertActionResponseBody(body) {
2361
- if (!isObject(body) || !isObject(body.action)) {
2362
- throw new Error(
2363
- "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
2364
- );
2434
+
2435
+ // src/domain/features/polling/getStepPolling.ts
2436
+ var getStepPolling = ({
2437
+ pollingConfig,
2438
+ logEvent,
2439
+ onBehavior,
2440
+ onPoll,
2441
+ registerSubmissionBehavior
2442
+ }) => {
2443
+ const { interval, delay = interval, maxAttempts, url, onError } = pollingConfig;
2444
+ let abortController = new AbortController();
2445
+ let intervalRef = null;
2446
+ if (delay == null) {
2447
+ throw new Error("Polling configuration must include delay or interval");
2365
2448
  }
2366
- }
2367
- function assertModalResponseBody(body) {
2368
- if (isObject(body)) {
2369
- if ("content" in body && isArray(body.content)) {
2449
+ const onErrorBehavior = getDomainLayerBehavior(onError, [], registerSubmissionBehavior);
2450
+ let attempts = 0;
2451
+ const poll = () => {
2452
+ attempts += 1;
2453
+ abortController.abort();
2454
+ abortController = new AbortController();
2455
+ const { signal } = abortController;
2456
+ onPoll(url, onErrorBehavior, signal).then((result) => {
2457
+ if (result) {
2458
+ stop();
2459
+ return;
2460
+ }
2461
+ if (attempts >= maxAttempts && !signal.aborted) {
2462
+ void onBehavior(onErrorBehavior);
2463
+ stop();
2464
+ }
2465
+ }).catch(() => {
2466
+ });
2467
+ };
2468
+ const start = () => {
2469
+ attempts = 0;
2470
+ intervalRef = setInterval(poll, delay * 1e3);
2471
+ poll();
2472
+ };
2473
+ const stop = () => {
2474
+ if (!intervalRef) {
2475
+ logEvent("warning", "Attempted to stop polling but it was not started");
2370
2476
  return;
2371
2477
  }
2372
- }
2373
- throw new Error(
2374
- "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
2375
- );
2376
- }
2377
- function assertSubflowResponseBody(body) {
2378
- const { valid } = validateSubflowResponse(body);
2379
- if (valid) {
2380
- return;
2381
- }
2382
- throw new Error("Incorrect response body in subflow response.");
2383
- }
2384
- function isErrorResponseBody(body) {
2385
- return Boolean(
2386
- isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
2387
- );
2388
- }
2389
- function assertStepResponseBody(body) {
2390
- if (!isObject(body)) {
2391
- throw new Error("Incorrect response body in step response. Expected an object.");
2392
- }
2393
- }
2394
- var assertResponseIsValid = (response) => {
2395
- if (!isResponse(response)) {
2396
- throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
2397
- }
2398
- if (response.bodyUsed) {
2399
- throw new Error(
2400
- "The body of the provided Response object has already been used. Every request must respond with a new Response object."
2401
- );
2402
- }
2478
+ clearTimeout(intervalRef);
2479
+ abortController.abort();
2480
+ };
2481
+ return { start, stop };
2403
2482
  };
2404
- var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
2405
2483
 
2406
- // src/controller/handleErrorResponse.ts
2407
- var handleErrorResponse = async (response, actionId, trackEvent) => {
2408
- const body = await parseResponseBodyAsJsonElement(response.clone());
2409
- if (isErrorResponseBody(body)) {
2410
- const refreshUrl = body.refreshUrl || body.refreshFormUrl;
2411
- const { error, validation, analytics } = body;
2412
- trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
2413
- const errors = { error, validation };
2414
- return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
2415
- type: "error",
2416
- body: { errors, analytics },
2417
- httpError: { statusCode: response.status }
2418
- };
2484
+ // src/domain/features/refreshAfter/getStepRefreshAfter.ts
2485
+ var ONE_SECOND = 1e3;
2486
+ var getStepRefreshAfter = ({
2487
+ refreshAfter,
2488
+ logEvent,
2489
+ onBehavior
2490
+ }) => {
2491
+ let timeout = null;
2492
+ const targetTime = new Date(refreshAfter).getTime();
2493
+ if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
2494
+ throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
2419
2495
  }
2420
- trackEvent("Action Failed", { actionId, statusCode: response.status });
2421
- const errorMessage = await parseResponseBodyAsText(response);
2496
+ const start = () => {
2497
+ const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
2498
+ timeout = setTimeout(() => {
2499
+ void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
2500
+ }, timeLeft);
2501
+ };
2422
2502
  return {
2423
- type: "error",
2424
- httpError: {
2425
- message: errorMessage || void 0,
2426
- statusCode: response.status
2503
+ start,
2504
+ stop: () => {
2505
+ if (!timeout) {
2506
+ logEvent("warning", "Attempted to stop refreshAfter but it was not started");
2507
+ return;
2508
+ }
2509
+ clearTimeout(timeout);
2427
2510
  }
2428
2511
  };
2429
2512
  };
2430
2513
 
2431
- // src/controller/getResponseType.ts
2432
- var responseTypes = ["step", "action", "exit", "modal", "subflow"];
2433
- var getResponseType = (headers, body) => {
2434
- const headerResponseType = getResponseTypeFromHeader(headers);
2435
- if (headerResponseType) {
2436
- return headerResponseType;
2437
- }
2438
- if (isObject(body) && body.action) {
2439
- return "action";
2440
- }
2441
- return "step";
2442
- };
2443
- var getResponseTypeFromHeader = (headers) => {
2444
- if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
2445
- const type = headers.get("X-Df-Response-Type");
2446
- assertDFResponseType(type);
2447
- return type;
2448
- }
2449
- if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
2450
- return "exit";
2451
- }
2452
- return void 0;
2453
- };
2454
- function assertDFResponseType(type) {
2455
- if (!responseTypes.includes(type)) {
2456
- throw new Error(
2457
- "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'error', 'modal', 'subflow'."
2458
- );
2459
- }
2460
- }
2461
-
2462
- // src/controller/makeSafeHttpClient.ts
2463
- var makeSafeHttpClient = (httpClient) => async (...props) => {
2464
- try {
2465
- return await httpClient(...props);
2466
- } catch (e) {
2467
- return null;
2468
- }
2469
- };
2470
-
2471
- // src/controller/executeRequest.ts
2472
- var executeRequest = async (props) => {
2473
- const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
2474
- const { url, method, body } = request;
2475
- const response = await getCachedOrFetch(
2476
- [
2477
- url,
2478
- {
2479
- method,
2480
- body: body ? JSON.stringify(body) : void 0,
2481
- headers: { "Content-Type": "application/json" }
2482
- }
2483
- ],
2484
- requestCache,
2485
- httpClient
2486
- );
2487
- if (!response) {
2488
- const extra = { errorMessage: "Network Error" };
2489
- trackEvent("Request Failed", extra);
2490
- logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
2491
- return { type: "error" };
2492
- }
2493
- if (!response.ok) {
2494
- return handleErrorResponse(response, void 0, trackEvent);
2495
- }
2496
- const responseBody = await parseResponseBodyAsJsonElement(response);
2497
- const responseType = getResponseType(response.headers, responseBody);
2498
- if (exit) {
2499
- return { type: "complete", result: responseBody };
2500
- }
2501
- switch (responseType) {
2502
- case "step": {
2503
- const etag = response.headers.get("etag") || null;
2504
- assertStepResponseBody(responseBody);
2505
- return { type: "replace-step", step: responseBody, etag };
2506
- }
2507
- case "exit": {
2508
- return { type: "complete", result: responseBody };
2509
- }
2510
- case "action": {
2511
- assertActionResponseBody(responseBody);
2512
- return {
2513
- type: "behavior",
2514
- behavior: {
2515
- type: "action",
2516
- action: responseBody.action
2517
- }
2518
- };
2519
- }
2520
- case "subflow": {
2521
- assertSubflowResponseBody(responseBody);
2522
- return {
2523
- type: "behavior",
2524
- behavior: __spreadProps(__spreadValues({}, responseBody), {
2525
- type: "subflow",
2526
- onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
2527
- onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
2528
- })
2529
- };
2530
- }
2531
- case "modal": {
2532
- assertModalResponseBody(responseBody);
2533
- return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
2534
- }
2535
- default: {
2536
- throw new Error(`Unsupported response type: ${String(responseType)}`);
2537
- }
2538
- }
2539
- };
2540
- var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
2541
- const cachedPromise = requestCache.get(requestParams);
2542
- if (cachedPromise) {
2543
- const cachedResponse = await cachedPromise;
2544
- if (cachedResponse == null ? void 0 : cachedResponse.ok) {
2545
- return cachedResponse;
2546
- }
2547
- }
2548
- return makeSafeHttpClient(httpClient)(...requestParams);
2549
- };
2550
-
2551
- // src/controller/executeSubmission.ts
2552
- var executeSubmission = async (props) => {
2553
- const { httpClient, requestCache, trackEvent, logEvent } = props;
2554
- const triggerAction = async (action, model, isInitial) => {
2555
- var _a, _b;
2556
- const { exit, url, result = null, id: actionId } = action;
2557
- const trackSubmissionEvent = !isInitial ? trackEvent : () => {
2558
- };
2559
- trackSubmissionEvent("Action Triggered", { actionId });
2560
- if (exit && !url) {
2561
- trackSubmissionEvent("Action Succeeded", { actionId });
2562
- return { type: "complete", result };
2563
- }
2564
- try {
2565
- const command = await executeRequest({
2566
- exit,
2567
- request: createRequestFromAction(action, model),
2568
- requestCache,
2569
- httpClient,
2570
- trackEvent: (name, properties) => {
2571
- trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
2572
- },
2573
- logEvent
2574
- });
2575
- switch (command.type) {
2576
- case "error": {
2577
- trackSubmissionEvent("Action Failed", __spreadValues({
2578
- actionId,
2579
- statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
2580
- }, (_b = command.body) == null ? void 0 : _b.analytics));
2581
- return command;
2582
- }
2583
- case "behavior": {
2584
- if (command.behavior.type === "action") {
2585
- trackSubmissionEvent("Action Succeeded", { actionId });
2586
- return await executeRequest({
2587
- request: createRequestFromAction(command.behavior.action, null),
2588
- requestCache,
2589
- httpClient,
2590
- trackEvent,
2591
- logEvent
2592
- });
2593
- }
2594
- trackSubmissionEvent("Action Succeeded", { actionId });
2595
- return command;
2596
- }
2597
- case "complete": {
2598
- trackSubmissionEvent("Action Succeeded", { actionId });
2599
- return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
2600
- }
2601
- default: {
2602
- trackSubmissionEvent("Action Succeeded", { actionId });
2603
- return command;
2604
- }
2605
- }
2606
- } catch (error) {
2607
- const errorMessage = getErrorMessage(error);
2608
- trackSubmissionEvent("Action Failed", { actionId, errorMessage });
2609
- logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
2610
- throw error;
2611
- }
2612
- };
2613
- return triggerAction(props.action, props.model, props.isInitial);
2614
- };
2615
- var createRequestFromAction = (action, model) => {
2616
- var _a, _b, _c;
2617
- return __spreadProps(__spreadValues({}, action), {
2618
- url: (_a = action.url) != null ? _a : "",
2619
- method: (_b = action.method) != null ? _b : "POST",
2620
- body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
2621
- });
2622
- };
2623
-
2624
- // src/domain/components/step/ExternalConfirmationComponent.ts
2625
- var createExternalConfirmation = (uid, url, onComponentUpdate) => {
2626
- const update = getInputUpdateFunction(onComponentUpdate);
2627
- return {
2628
- type: "external-confirmation",
2629
- kind: "layout",
2630
- uid,
2631
- url,
2632
- status: "initial",
2633
- onSuccess() {
2634
- update(this, (draft) => {
2635
- draft.status = "success";
2636
- });
2637
- },
2638
- onFailure() {
2639
- if (this.status === "initial") {
2640
- update(this, (draft) => {
2641
- draft.status = "failure";
2642
- });
2643
- }
2644
- },
2645
- onCancel() {
2646
- update(this, (draft) => {
2647
- draft.status = "dismissed";
2648
- });
2649
- }
2650
- };
2651
- };
2652
-
2653
- // src/utils/component-utils.ts
2654
- var getSubmittableData = async (components) => Promise.all(components.map(async (component) => component.getSubmittableValue())).then(
2655
- (values) => values.reduce((acc, value) => recursiveMerge(acc, value), null)
2656
- );
2657
- var getSubmittableDataSync = (components) => components.map((component) => component.getSubmittableValueSync()).reduce((acc, value) => recursiveMerge(acc, value), null);
2658
- var getLocalValues = (components) => components.map((component) => component.getLocalValue()).reduce((acc, value) => recursiveMerge(acc, value), null);
2659
-
2660
- // src/domain/components/step/StepDomainComponent.ts
2661
- var createStepComponent = (stepProps) => {
2662
- const _a = stepProps, { uid, stepPolling, stepRefreshAfter, onComponentUpdate } = _a, rest = __objRest(_a, ["uid", "stepPolling", "stepRefreshAfter", "onComponentUpdate"]);
2663
- const update = getInputUpdateFunction(onComponentUpdate);
2664
- const component = __spreadProps(__spreadValues({
2665
- uid
2666
- }, rest), {
2667
- type: "step",
2668
- kind: "step",
2669
- modals: [],
2670
- dismissModal() {
2671
- var _a2;
2672
- (_a2 = this.modals.at(-1)) == null ? void 0 : _a2.close();
2673
- },
2674
- dismissAllModals() {
2675
- this._update((draft) => {
2676
- draft.modals = draft.modals.map((m) => __spreadProps(__spreadValues({}, m), { open: false }));
2677
- });
2678
- },
2679
- showModal(modal) {
2680
- this._update((draft) => {
2681
- draft.modals = [...draft.modals, modal];
2682
- });
2683
- },
2684
- _update(updateFn) {
2685
- update(this, updateFn);
2686
- },
2687
- getChildren() {
2688
- return this.externalConfirmation ? [...this.layoutComponents, this.externalConfirmation] : this.layoutComponents;
2689
- },
2690
- getModals() {
2691
- return this.modals;
2692
- },
2693
- async getSubmittableValue() {
2694
- return getSubmittableData(this.schemaComponents);
2695
- },
2696
- getSubmittableValueSync() {
2697
- return getSubmittableDataSync(this.schemaComponents);
2698
- },
2699
- getLocalValue() {
2700
- return getLocalValues(this.schemaComponents);
2701
- },
2702
- validate() {
2703
- return this.schemaComponents.every(
2704
- (inputComponent) => inputComponent.isSchemaReferencedInStep ? inputComponent.validate() : true
2705
- );
2706
- },
2707
- setLoadingState(loadingState) {
2708
- this._update((draft) => {
2709
- draft.loadingState = loadingState;
2710
- });
2711
- },
2712
- start() {
2713
- stepPolling == null ? void 0 : stepPolling.start();
2714
- stepRefreshAfter == null ? void 0 : stepRefreshAfter.start();
2715
- },
2716
- stop() {
2717
- stepPolling == null ? void 0 : stepPolling.stop();
2718
- stepRefreshAfter == null ? void 0 : stepRefreshAfter.stop();
2719
- this._update((draft) => {
2720
- draft.modals = [];
2721
- });
2722
- }
2723
- });
2724
- return component;
2725
- };
2726
-
2727
- // src/domain/features/polling/getStepPolling.ts
2728
- var getStepPolling = ({
2729
- pollingConfig,
2730
- logEvent,
2731
- onBehavior,
2732
- onPoll,
2733
- registerSubmissionBehavior
2734
- }) => {
2735
- const { interval, delay = interval, maxAttempts, url, onError } = pollingConfig;
2736
- let abortController = new AbortController();
2737
- let intervalRef = null;
2738
- if (delay == null) {
2739
- throw new Error("Polling configuration must include delay or interval");
2740
- }
2741
- const onErrorBehavior = getDomainLayerBehavior(onError, [], registerSubmissionBehavior);
2742
- let attempts = 0;
2743
- const poll = () => {
2744
- attempts += 1;
2745
- abortController.abort();
2746
- abortController = new AbortController();
2747
- const { signal } = abortController;
2748
- onPoll(url, onErrorBehavior, signal).then((result) => {
2749
- if (result) {
2750
- stop();
2751
- return;
2752
- }
2753
- if (attempts >= maxAttempts && !signal.aborted) {
2754
- void onBehavior(onErrorBehavior);
2755
- stop();
2756
- }
2757
- }).catch(() => {
2758
- });
2759
- };
2760
- const start = () => {
2761
- attempts = 0;
2762
- intervalRef = setInterval(poll, delay * 1e3);
2763
- poll();
2764
- };
2765
- const stop = () => {
2766
- if (!intervalRef) {
2767
- logEvent("warning", "Attempted to stop polling but it was not started");
2768
- return;
2769
- }
2770
- clearTimeout(intervalRef);
2771
- abortController.abort();
2772
- };
2773
- return { start, stop };
2774
- };
2775
-
2776
- // src/domain/features/refreshAfter/getStepRefreshAfter.ts
2777
- var ONE_SECOND = 1e3;
2778
- var getStepRefreshAfter = ({
2779
- refreshAfter,
2780
- logEvent,
2781
- onBehavior
2782
- }) => {
2783
- let timeout = null;
2784
- const targetTime = new Date(refreshAfter).getTime();
2785
- if (typeof refreshAfter !== "string" || Number.isNaN(targetTime)) {
2786
- throw new Error(`Invalid refreshAfter value: ${String(refreshAfter)}`);
2787
- }
2788
- const start = () => {
2789
- const timeLeft = Math.max(targetTime - Date.now(), ONE_SECOND);
2790
- timeout = setTimeout(() => {
2791
- void onBehavior({ type: "refresh", analytics: { schema: "refreshAfter" } });
2792
- }, timeLeft);
2793
- };
2794
- return {
2795
- start,
2796
- stop: () => {
2797
- if (!timeout) {
2798
- logEvent("warning", "Attempted to stop refreshAfter but it was not started");
2799
- return;
2800
- }
2801
- clearTimeout(timeout);
2802
- }
2803
- };
2804
- };
2805
-
2806
- // src/domain/prefetching/request-cache.ts
2807
- var makeRequestCacheWithParent = (parent) => {
2808
- const map = /* @__PURE__ */ new Map();
2809
- const cache = {
2810
- get: (requestParams) => {
2811
- var _a;
2812
- const key = makeKey(requestParams);
2813
- const promise = (_a = map.get(key)) != null ? _a : parent == null ? void 0 : parent.get(requestParams);
2814
- map.delete(key);
2815
- return promise;
2816
- },
2817
- set: (requestParams, responsePromise) => {
2818
- return map.set(makeKey(requestParams), responsePromise);
2819
- }
2820
- };
2821
- return cache;
2822
- };
2823
- var makeRequestCache = (initialValues = []) => {
2824
- const cache = makeRequestCacheWithParent(void 0);
2825
- initialValues.forEach(([requestParams, responsePromise]) => {
2826
- cache.set(requestParams, responsePromise);
2827
- });
2828
- return cache;
2829
- };
2830
- var normaliseRequestCache = (cache) => {
2831
- if (cache === void 0) {
2832
- return makeRequestCache();
2833
- }
2834
- if (isRequestCacheInstance(cache)) {
2835
- return cache;
2836
- }
2837
- return makeRequestCache(cache);
2838
- };
2839
- var isRequestCacheInstance = (cache) => {
2840
- return !cache || !Array.isArray(cache);
2841
- };
2842
- var makeKey = (requestParams) => {
2843
- var _a, _b;
2844
- const [input, init] = requestParams;
2845
- const url = typeof input === "string" || input instanceof URL ? input.toString() : input.url;
2846
- const key = JSON.stringify({
2847
- url,
2848
- method: (_a = init == null ? void 0 : init.method) != null ? _a : "GET",
2849
- headers: (init == null ? void 0 : init.headers) ? Array.from(new Headers(init.headers).entries()) : [],
2850
- body: (_b = init == null ? void 0 : init.body) != null ? _b : null
2851
- });
2852
- return key;
2853
- };
2854
-
2855
2514
  // src/domain/components/utils/isOrWasValid.ts
2856
2515
  var isOrWasValid = (getErrors, previous, current) => {
2857
2516
  const wasValid = getErrors(previous).length === 0 && previous !== null;
@@ -3330,6 +2989,72 @@ var getInitialValidationAsyncState = () => ({
3330
2989
  messages: {}
3331
2990
  });
3332
2991
 
2992
+ // src/controller/response-utils.ts
2993
+ import { validateActionResponse, validateSubflowResponse } from "@wise/dynamic-flow-types/spec";
2994
+ var parseResponseBodyAsJsonElement = async (response) => {
2995
+ assertResponseIsValid(response);
2996
+ try {
2997
+ return await response.json();
2998
+ } catch (e) {
2999
+ return null;
3000
+ }
3001
+ };
3002
+ var parseResponseBodyAsText = async (response) => {
3003
+ try {
3004
+ return await response.text();
3005
+ } catch (e) {
3006
+ return null;
3007
+ }
3008
+ };
3009
+ function isActionResponseBody(body) {
3010
+ return validateActionResponse(body).valid;
3011
+ }
3012
+ function assertActionResponseBody(body) {
3013
+ if (!isObject(body) || !isObject(body.action)) {
3014
+ throw new Error(
3015
+ "Incorrect response body in action response. Expected an object satisfying the type { action: Action }."
3016
+ );
3017
+ }
3018
+ }
3019
+ function assertModalResponseBody(body) {
3020
+ if (isObject(body)) {
3021
+ if ("content" in body && isArray(body.content)) {
3022
+ return;
3023
+ }
3024
+ }
3025
+ throw new Error(
3026
+ "Incorrect response body in modal response. Expected an object satisfying the type { title?: string, components: Layout[] }."
3027
+ );
3028
+ }
3029
+ function assertSubflowResponseBody(body) {
3030
+ const { valid } = validateSubflowResponse(body);
3031
+ if (valid) {
3032
+ return;
3033
+ }
3034
+ throw new Error("Incorrect response body in subflow response.");
3035
+ }
3036
+ function isErrorResponseBody(body) {
3037
+ return Boolean(
3038
+ isObject(body) && (body.refreshFormUrl || body.refreshUrl || body.validation || body.error || body.analytics)
3039
+ );
3040
+ }
3041
+ function assertStepResponseBody(body) {
3042
+ if (!isObject(body)) {
3043
+ throw new Error("Incorrect response body in step response. Expected an object.");
3044
+ }
3045
+ }
3046
+ var assertResponseIsValid = (response) => {
3047
+ if (!isResponse(response)) {
3048
+ throw new Error("Incorrect type of response from fetch. Expected object of type Response.");
3049
+ }
3050
+ if (response.bodyUsed) {
3051
+ throw new Error(
3052
+ "The body of the provided Response object has already been used. Every request must respond with a new Response object."
3053
+ );
3054
+ }
3055
+ };
3056
+ var isResponse = (response) => typeof response === "object" && response !== null && "clone" in response && "bodyUsed" in response;
3057
+
3333
3058
  // src/domain/features/utils/response-utils.ts
3334
3059
  var getAnalyticsFromErrorResponse = (json) => {
3335
3060
  if (!isErrorResponseBody(json)) {
@@ -5811,229 +5536,529 @@ var createTextInputComponent = (textInputProps, onComponentUpdate) => {
5811
5536
  return inputComponent;
5812
5537
  };
5813
5538
 
5814
- // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToTextInputComponent.ts
5815
- var stringSchemaToTextInputComponent = (schemaMapperProps, mapperProps) => {
5816
- const { schema, localValue, model, required = false, onPersistAsync } = schemaMapperProps;
5817
- const {
5818
- autocapitalization,
5819
- autocompleteHint,
5820
- control,
5821
- default: defaultValue,
5822
- displayFormat,
5823
- format,
5824
- maxLength,
5825
- minLength,
5826
- suggestions,
5827
- validationMessages
5828
- } = schema;
5829
- const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange, logEvent } = mapperProps;
5830
- const controlForLegacyFormat = getControlForLegacyFormat(format);
5831
- const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
5832
- const { performValidationAsync, validationAsyncState } = getValidationAsyncInitialState(
5833
- schemaMapperProps,
5834
- mapperProps
5835
- );
5836
- const validLocalValue = isString(localValue) ? localValue : null;
5837
- const validModel = isString(model) ? model : defaultValue != null ? defaultValue : null;
5838
- const value = onPersistAsync ? validLocalValue : validModel;
5839
- return createTextInputComponent(
5840
- __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
5841
- autocapitalization,
5842
- autoComplete: getAutocompleteString(autocompleteHint),
5843
- checks: schema.hidden ? [] : [
5844
- getRequiredCheck(required, errorMessageFunctions),
5845
- getAboveMaxLengthCheck(schema, errorMessageFunctions),
5846
- getBelowMinLengthCheck(schema, errorMessageFunctions),
5847
- getNotAdheringToPatternCheck(schema, errorMessageFunctions, { logEvent })
5848
- ],
5849
- control: control != null ? control : controlForLegacyFormat,
5850
- displayFormat,
5851
- maxLength,
5852
- minLength,
5853
- suggestions: mapStringSchemaSuggestions(suggestions, mapperProps.logEvent),
5854
- value,
5855
- validationAsyncState,
5856
- schemaOnChange: getSchemaOnChange(schema, onBehavior),
5857
- performValidationAsync,
5858
- onValueChange
5859
- }),
5860
- onComponentUpdate
5861
- );
5539
+ // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToTextInputComponent.ts
5540
+ var stringSchemaToTextInputComponent = (schemaMapperProps, mapperProps) => {
5541
+ const { schema, localValue, model, required = false, onPersistAsync } = schemaMapperProps;
5542
+ const {
5543
+ autocapitalization,
5544
+ autocompleteHint,
5545
+ control,
5546
+ default: defaultValue,
5547
+ displayFormat,
5548
+ format,
5549
+ maxLength,
5550
+ minLength,
5551
+ suggestions,
5552
+ validationMessages
5553
+ } = schema;
5554
+ const { getErrorMessageFunctions, onComponentUpdate, onBehavior, onValueChange, logEvent } = mapperProps;
5555
+ const controlForLegacyFormat = getControlForLegacyFormat(format);
5556
+ const errorMessageFunctions = getErrorMessageFunctions(validationMessages);
5557
+ const { performValidationAsync, validationAsyncState } = getValidationAsyncInitialState(
5558
+ schemaMapperProps,
5559
+ mapperProps
5560
+ );
5561
+ const validLocalValue = isString(localValue) ? localValue : null;
5562
+ const validModel = isString(model) ? model : defaultValue != null ? defaultValue : null;
5563
+ const value = onPersistAsync ? validLocalValue : validModel;
5564
+ return createTextInputComponent(
5565
+ __spreadProps(__spreadValues({}, mapCommonSchemaProps(schemaMapperProps)), {
5566
+ autocapitalization,
5567
+ autoComplete: getAutocompleteString(autocompleteHint),
5568
+ checks: schema.hidden ? [] : [
5569
+ getRequiredCheck(required, errorMessageFunctions),
5570
+ getAboveMaxLengthCheck(schema, errorMessageFunctions),
5571
+ getBelowMinLengthCheck(schema, errorMessageFunctions),
5572
+ getNotAdheringToPatternCheck(schema, errorMessageFunctions, { logEvent })
5573
+ ],
5574
+ control: control != null ? control : controlForLegacyFormat,
5575
+ displayFormat,
5576
+ maxLength,
5577
+ minLength,
5578
+ suggestions: mapStringSchemaSuggestions(suggestions, mapperProps.logEvent),
5579
+ value,
5580
+ validationAsyncState,
5581
+ schemaOnChange: getSchemaOnChange(schema, onBehavior),
5582
+ performValidationAsync,
5583
+ onValueChange
5584
+ }),
5585
+ onComponentUpdate
5586
+ );
5587
+ };
5588
+ var getControlForLegacyFormat = (format) => {
5589
+ if (format && ["numeric", "phone-number", "email", "password"].includes(format)) {
5590
+ return format;
5591
+ }
5592
+ return void 0;
5593
+ };
5594
+
5595
+ // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.ts
5596
+ var stringSchemaToComponent = (schemaMapperProps, mapperProps) => {
5597
+ const { schema } = schemaMapperProps;
5598
+ if (isStringSchemaWithBase64(schema)) {
5599
+ return stringSchemaToUploadInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5600
+ }
5601
+ switch (schema.format) {
5602
+ case "date":
5603
+ return stringSchemaToDateInputComponent(schemaMapperProps, mapperProps);
5604
+ default:
5605
+ return stringSchemaToTextInputComponent(schemaMapperProps, mapperProps);
5606
+ }
5607
+ };
5608
+ var isStringSchemaWithBase64 = (schema) => {
5609
+ return schema.format === "base64url" && !("persistAsync" in schema);
5610
+ };
5611
+
5612
+ // src/domain/mappers/mapSchemaToComponent.ts
5613
+ var mapSchemaToComponent = (schemaMapperProps, mapperProps) => {
5614
+ const { uid, schema } = schemaMapperProps;
5615
+ if (isConstSchema(schema)) {
5616
+ return constSchemaToComponent(uid, __spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5617
+ }
5618
+ if (isSchemaWithPersistAsync(schema)) {
5619
+ return persistAsyncSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5620
+ }
5621
+ if (isAllOfSchema(schema)) {
5622
+ return allOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5623
+ }
5624
+ if (isOneOfSchema(schema)) {
5625
+ return oneOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5626
+ }
5627
+ if (isBooleanSchema(schema)) {
5628
+ return booleanSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5629
+ }
5630
+ if (isObjectSchema(schema)) {
5631
+ const { format } = schema;
5632
+ if (format === "money") {
5633
+ return objectSchemaToMoneyInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5634
+ }
5635
+ if (format != null && Object.keys(schema.properties).length === 0) {
5636
+ return objectSchemaToFormattedValueComponent(
5637
+ __spreadProps(__spreadValues({}, schemaMapperProps), { schema: __spreadProps(__spreadValues({}, schema), { format }) }),
5638
+ mapperProps
5639
+ );
5640
+ }
5641
+ return objectSchemaToObjectComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5642
+ }
5643
+ if (isIntegerSchema(schema)) {
5644
+ return integerSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5645
+ }
5646
+ if (isNumberSchema(schema)) {
5647
+ return numberSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5648
+ }
5649
+ if (isStringSchema(schema)) {
5650
+ return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5651
+ }
5652
+ if (isArraySchema(schema)) {
5653
+ return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5654
+ }
5655
+ if (isBlobSchema(schema)) {
5656
+ if (!schemaMapperProps.onPersistAsync) {
5657
+ throw new Error(
5658
+ "Blob schemas can only be used as the schema of a persist async configuration."
5659
+ );
5660
+ }
5661
+ return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5662
+ }
5663
+ throw new Error("Not yet supported");
5664
+ };
5665
+
5666
+ // src/domain/mappers/mapStepSchemas.ts
5667
+ var mapStepSchemas = (uid, step, stepLocalValue, mapperProps, referencedSchemaIds) => {
5668
+ const isReferenced = (schemaId) => schemaId != null && referencedSchemaIds.includes(schemaId);
5669
+ return step.schemas.map((schema, i) => {
5670
+ var _a, _b;
5671
+ const schemaComponent = mapSchemaToComponent(
5672
+ {
5673
+ uid: `${uid}.schemas-${i}.${schema.$id}`,
5674
+ schemaId: schema.$id,
5675
+ schema,
5676
+ model: (_a = step.model) != null ? _a : null,
5677
+ localValue: stepLocalValue,
5678
+ validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
5679
+ required: true
5680
+ },
5681
+ mapperProps
5682
+ );
5683
+ return __spreadProps(__spreadValues({}, schemaComponent), {
5684
+ isSchemaReferencedInStep: isReferenced(schema.$id)
5685
+ });
5686
+ });
5687
+ };
5688
+
5689
+ // src/domain/mappers/mapToolbarToComponent.ts
5690
+ var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
5691
+ if (!toolbar) {
5692
+ return void 0;
5693
+ }
5694
+ const { onBehavior } = mapperProps;
5695
+ return __spreadProps(__spreadValues({}, toolbar), {
5696
+ type: "toolbar",
5697
+ uid: `${uid}.toolbar`,
5698
+ tags: toolbar.tags,
5699
+ items: toolbar.items.map((item) => {
5700
+ const context = item.context ? mapLegacyContext(item.context) : void 0;
5701
+ const behavior = getDomainLayerBehavior(
5702
+ { behavior: item.behavior },
5703
+ [],
5704
+ mapperProps.registerSubmissionBehavior
5705
+ );
5706
+ return __spreadProps(__spreadValues({}, item), {
5707
+ context: context ? mapLegacyContext(context) : void 0,
5708
+ disabled: !!item.disabled,
5709
+ tags: item.tags,
5710
+ onClick: () => {
5711
+ void onBehavior(behavior);
5712
+ }
5713
+ });
5714
+ })
5715
+ });
5716
+ };
5717
+
5718
+ // src/domain/mappers/utils/groupLayoutByPinned.ts
5719
+ var groupLayoutByPinned = (layouts) => {
5720
+ return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
5862
5721
  };
5863
- var getControlForLegacyFormat = (format) => {
5864
- if (format && ["numeric", "phone-number", "email", "password"].includes(format)) {
5865
- return format;
5722
+ var groupLayout = (acc, layout) => {
5723
+ if (layout.type === "button" && layout.pinOrder !== void 0) {
5724
+ return {
5725
+ pinned: [...acc.pinned, layout],
5726
+ nonPinned: acc.nonPinned
5727
+ };
5866
5728
  }
5867
- return void 0;
5729
+ if (hasColumns(layout)) {
5730
+ const leftChildren = groupLayoutByPinned(layout.left);
5731
+ const rightChildren = groupLayoutByPinned(layout.right);
5732
+ return {
5733
+ pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
5734
+ nonPinned: [
5735
+ ...acc.nonPinned,
5736
+ __spreadProps(__spreadValues({}, layout), {
5737
+ left: leftChildren.nonPinned,
5738
+ right: rightChildren.nonPinned
5739
+ })
5740
+ ]
5741
+ };
5742
+ }
5743
+ if (hasChildren(layout)) {
5744
+ const childComponents = groupLayoutByPinned(layout.components);
5745
+ return {
5746
+ pinned: [...acc.pinned, ...childComponents.pinned],
5747
+ nonPinned: [
5748
+ ...acc.nonPinned,
5749
+ __spreadProps(__spreadValues({}, layout), {
5750
+ components: childComponents.nonPinned
5751
+ })
5752
+ ]
5753
+ };
5754
+ }
5755
+ return {
5756
+ pinned: [...acc.pinned],
5757
+ nonPinned: [...acc.nonPinned, layout]
5758
+ };
5868
5759
  };
5760
+ var hasChildren = (component) => "components" in component;
5761
+ var hasColumns = (component) => "right" in component && "left" in component;
5869
5762
 
5870
- // src/domain/mappers/schema/stringSchemaToComponent/stringSchemaToComponent.ts
5871
- var stringSchemaToComponent = (schemaMapperProps, mapperProps) => {
5872
- const { schema } = schemaMapperProps;
5873
- if (isStringSchemaWithBase64(schema)) {
5874
- return stringSchemaToUploadInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5763
+ // src/controller/getErrorMessage.ts
5764
+ var getErrorMessage = (error) => {
5765
+ return error instanceof Error ? error.message : typeof error === "string" ? error : `Unknown Error: type is ${typeof error}`;
5766
+ };
5767
+
5768
+ // src/controller/handleErrorResponse.ts
5769
+ var handleErrorResponse = async (response, actionId, trackEvent) => {
5770
+ const body = await parseResponseBodyAsJsonElement(response.clone());
5771
+ if (isErrorResponseBody(body)) {
5772
+ const refreshUrl = body.refreshUrl || body.refreshFormUrl;
5773
+ const { error, validation, analytics } = body;
5774
+ trackEvent("Action Failed", __spreadProps(__spreadValues({}, analytics), { actionId, statusCode: response.status }));
5775
+ const errors = { error, validation };
5776
+ return refreshUrl ? { type: "refresh", body: { refreshUrl, errors } } : {
5777
+ type: "error",
5778
+ body: { errors, analytics },
5779
+ httpError: { statusCode: response.status }
5780
+ };
5875
5781
  }
5876
- switch (schema.format) {
5877
- case "date":
5878
- return stringSchemaToDateInputComponent(schemaMapperProps, mapperProps);
5879
- default:
5880
- return stringSchemaToTextInputComponent(schemaMapperProps, mapperProps);
5782
+ trackEvent("Action Failed", { actionId, statusCode: response.status });
5783
+ const errorMessage = await parseResponseBodyAsText(response);
5784
+ return {
5785
+ type: "error",
5786
+ httpError: {
5787
+ message: errorMessage || void 0,
5788
+ statusCode: response.status
5789
+ }
5790
+ };
5791
+ };
5792
+
5793
+ // src/controller/getResponseType.ts
5794
+ var responseTypes = ["step", "action", "exit", "modal", "subflow"];
5795
+ var getResponseType = (headers, body) => {
5796
+ const headerResponseType = getResponseTypeFromHeader(headers);
5797
+ if (headerResponseType) {
5798
+ return headerResponseType;
5881
5799
  }
5800
+ if (isObject(body) && body.action) {
5801
+ return "action";
5802
+ }
5803
+ return "step";
5882
5804
  };
5883
- var isStringSchemaWithBase64 = (schema) => {
5884
- return schema.format === "base64url" && !("persistAsync" in schema);
5805
+ var getResponseTypeFromHeader = (headers) => {
5806
+ if (headers == null ? void 0 : headers.has("X-Df-Response-Type")) {
5807
+ const type = headers.get("X-Df-Response-Type");
5808
+ assertDFResponseType(type);
5809
+ return type;
5810
+ }
5811
+ if (headers == null ? void 0 : headers.has("X-Df-Exit")) {
5812
+ return "exit";
5813
+ }
5814
+ return void 0;
5885
5815
  };
5886
-
5887
- // src/domain/mappers/mapSchemaToComponent.ts
5888
- var mapSchemaToComponent = (schemaMapperProps, mapperProps) => {
5889
- const { uid, schema } = schemaMapperProps;
5890
- if (isConstSchema(schema)) {
5891
- return constSchemaToComponent(uid, __spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5816
+ function assertDFResponseType(type) {
5817
+ if (!responseTypes.includes(type)) {
5818
+ throw new Error(
5819
+ "Unsupported X-Df-Response-Type. Allowed values are 'step', 'action', 'exit', 'error', 'modal', 'subflow'."
5820
+ );
5892
5821
  }
5893
- if (isSchemaWithPersistAsync(schema)) {
5894
- return persistAsyncSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5822
+ }
5823
+
5824
+ // src/controller/makeSafeHttpClient.ts
5825
+ var makeSafeHttpClient = (httpClient) => async (...props) => {
5826
+ try {
5827
+ return await httpClient(...props);
5828
+ } catch (e) {
5829
+ return null;
5895
5830
  }
5896
- if (isAllOfSchema(schema)) {
5897
- return allOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5831
+ };
5832
+
5833
+ // src/controller/executeRequest.ts
5834
+ var executeRequest = async (props) => {
5835
+ const { exit, request, requestCache, httpClient, trackEvent, logEvent } = props;
5836
+ const { url, method, body } = request;
5837
+ const response = await getCachedOrFetch(
5838
+ [
5839
+ url,
5840
+ {
5841
+ method,
5842
+ body: body ? JSON.stringify(body) : void 0,
5843
+ headers: { "Content-Type": "application/json" }
5844
+ }
5845
+ ],
5846
+ requestCache,
5847
+ httpClient
5848
+ );
5849
+ if (!response) {
5850
+ const extra = { errorMessage: "Network Error" };
5851
+ trackEvent("Request Failed", extra);
5852
+ logEvent("error", "Dynamic Flow - Request Failed Unexpectedly", extra);
5853
+ return { type: "error" };
5898
5854
  }
5899
- if (isOneOfSchema(schema)) {
5900
- return oneOfSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5855
+ if (!response.ok) {
5856
+ return handleErrorResponse(response, void 0, trackEvent);
5901
5857
  }
5902
- if (isBooleanSchema(schema)) {
5903
- return booleanSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5858
+ const responseBody = await parseResponseBodyAsJsonElement(response);
5859
+ const responseType = getResponseType(response.headers, responseBody);
5860
+ if (exit) {
5861
+ return { type: "complete", result: responseBody };
5904
5862
  }
5905
- if (isObjectSchema(schema)) {
5906
- const { format } = schema;
5907
- if (format === "money") {
5908
- return objectSchemaToMoneyInputComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5863
+ switch (responseType) {
5864
+ case "step": {
5865
+ const etag = response.headers.get("etag") || null;
5866
+ assertStepResponseBody(responseBody);
5867
+ return { type: "replace-step", step: responseBody, etag };
5909
5868
  }
5910
- if (format != null && Object.keys(schema.properties).length === 0) {
5911
- return objectSchemaToFormattedValueComponent(
5912
- __spreadProps(__spreadValues({}, schemaMapperProps), { schema: __spreadProps(__spreadValues({}, schema), { format }) }),
5913
- mapperProps
5914
- );
5869
+ case "exit": {
5870
+ return { type: "complete", result: responseBody };
5871
+ }
5872
+ case "action": {
5873
+ assertActionResponseBody(responseBody);
5874
+ return {
5875
+ type: "behavior",
5876
+ behavior: {
5877
+ type: "action",
5878
+ action: responseBody.action
5879
+ }
5880
+ };
5881
+ }
5882
+ case "subflow": {
5883
+ assertSubflowResponseBody(responseBody);
5884
+ return {
5885
+ type: "behavior",
5886
+ behavior: __spreadProps(__spreadValues({}, responseBody), {
5887
+ type: "subflow",
5888
+ onCompletion: responseBody.onCompletion ? normaliseBehavior(responseBody.onCompletion, []) : void 0,
5889
+ onError: responseBody.onError ? normaliseBehavior(responseBody.onError, []) : void 0
5890
+ })
5891
+ };
5892
+ }
5893
+ case "modal": {
5894
+ assertModalResponseBody(responseBody);
5895
+ return { type: "behavior", behavior: __spreadProps(__spreadValues({}, responseBody), { type: "modal" }) };
5896
+ }
5897
+ default: {
5898
+ throw new Error(`Unsupported response type: ${String(responseType)}`);
5915
5899
  }
5916
- return objectSchemaToObjectComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5917
- }
5918
- if (isIntegerSchema(schema)) {
5919
- return integerSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5920
- }
5921
- if (isNumberSchema(schema)) {
5922
- return numberSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5923
- }
5924
- if (isStringSchema(schema)) {
5925
- return stringSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5926
- }
5927
- if (isArraySchema(schema)) {
5928
- return arraySchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5929
5900
  }
5930
- if (isBlobSchema(schema)) {
5931
- if (!schemaMapperProps.onPersistAsync) {
5932
- throw new Error(
5933
- "Blob schemas can only be used as the schema of a persist async configuration."
5934
- );
5901
+ };
5902
+ var getCachedOrFetch = async (requestParams, requestCache, httpClient) => {
5903
+ const cachedPromise = requestCache.get(requestParams);
5904
+ if (cachedPromise) {
5905
+ const cachedResponse = await cachedPromise;
5906
+ if (cachedResponse == null ? void 0 : cachedResponse.ok) {
5907
+ return cachedResponse;
5935
5908
  }
5936
- return blobSchemaToComponent(__spreadProps(__spreadValues({}, schemaMapperProps), { schema }), mapperProps);
5937
5909
  }
5938
- throw new Error("Not yet supported");
5910
+ return makeSafeHttpClient(httpClient)(...requestParams);
5939
5911
  };
5940
5912
 
5941
- // src/domain/mappers/mapStepSchemas.ts
5942
- var mapStepSchemas = (uid, step, stepLocalValue, mapperProps, referencedSchemaIds) => {
5943
- const isReferenced = (schemaId) => schemaId != null && referencedSchemaIds.includes(schemaId);
5944
- return step.schemas.map((schema, i) => {
5913
+ // src/controller/executeSubmission.ts
5914
+ var executeSubmission = async (props) => {
5915
+ const { httpClient, requestCache, trackEvent, logEvent } = props;
5916
+ const triggerAction = async (action, model, isInitial) => {
5945
5917
  var _a, _b;
5946
- const schemaComponent = mapSchemaToComponent(
5947
- {
5948
- uid: `${uid}.schemas-${i}.${schema.$id}`,
5949
- schemaId: schema.$id,
5950
- schema,
5951
- model: (_a = step.model) != null ? _a : null,
5952
- localValue: stepLocalValue,
5953
- validationErrors: (_b = step.errors) == null ? void 0 : _b.validation,
5954
- required: true
5955
- },
5956
- mapperProps
5957
- );
5958
- return __spreadProps(__spreadValues({}, schemaComponent), {
5959
- isSchemaReferencedInStep: isReferenced(schema.$id)
5960
- });
5961
- });
5962
- };
5963
-
5964
- // src/domain/mappers/mapToolbarToComponent.ts
5965
- var mapToolbarToComponent = (uid, toolbar, mapperProps) => {
5966
- if (!toolbar) {
5967
- return void 0;
5968
- }
5969
- const { onBehavior } = mapperProps;
5970
- return __spreadProps(__spreadValues({}, toolbar), {
5971
- type: "toolbar",
5972
- uid: `${uid}.toolbar`,
5973
- tags: toolbar.tags,
5974
- items: toolbar.items.map((item) => {
5975
- const context = item.context ? mapLegacyContext(item.context) : void 0;
5976
- const behavior = getDomainLayerBehavior(
5977
- { behavior: item.behavior },
5978
- [],
5979
- mapperProps.registerSubmissionBehavior
5980
- );
5981
- return __spreadProps(__spreadValues({}, item), {
5982
- context: context ? mapLegacyContext(context) : void 0,
5983
- disabled: !!item.disabled,
5984
- tags: item.tags,
5985
- onClick: () => {
5986
- void onBehavior(behavior);
5987
- }
5918
+ const { exit, url, result = null, id: actionId } = action;
5919
+ const trackSubmissionEvent = !isInitial ? trackEvent : () => {
5920
+ };
5921
+ trackSubmissionEvent("Action Triggered", { actionId });
5922
+ if (exit && !url) {
5923
+ trackSubmissionEvent("Action Succeeded", { actionId });
5924
+ return { type: "complete", result };
5925
+ }
5926
+ try {
5927
+ const command = await executeRequest({
5928
+ exit,
5929
+ request: createRequestFromAction(action, model),
5930
+ requestCache,
5931
+ httpClient,
5932
+ trackEvent: (name, properties) => {
5933
+ trackSubmissionEvent(name, __spreadProps(__spreadValues({}, properties), { actionId }));
5934
+ },
5935
+ logEvent
5988
5936
  });
5989
- })
5937
+ switch (command.type) {
5938
+ case "error": {
5939
+ trackSubmissionEvent("Action Failed", __spreadValues({
5940
+ actionId,
5941
+ statusCode: (_a = command.httpError) == null ? void 0 : _a.statusCode
5942
+ }, (_b = command.body) == null ? void 0 : _b.analytics));
5943
+ return command;
5944
+ }
5945
+ case "behavior": {
5946
+ if (command.behavior.type === "action") {
5947
+ trackSubmissionEvent("Action Succeeded", { actionId });
5948
+ return await executeSubmission({
5949
+ action: command.behavior.action,
5950
+ isInitial: false,
5951
+ model: null,
5952
+ requestCache,
5953
+ httpClient,
5954
+ trackEvent,
5955
+ logEvent
5956
+ });
5957
+ }
5958
+ trackSubmissionEvent("Action Succeeded", { actionId });
5959
+ return command;
5960
+ }
5961
+ case "complete": {
5962
+ trackSubmissionEvent("Action Succeeded", { actionId });
5963
+ return __spreadProps(__spreadValues({}, command), { result: recursiveMerge(command.result, result) });
5964
+ }
5965
+ default: {
5966
+ trackSubmissionEvent("Action Succeeded", { actionId });
5967
+ return command;
5968
+ }
5969
+ }
5970
+ } catch (error) {
5971
+ const errorMessage = getErrorMessage(error);
5972
+ trackSubmissionEvent("Action Failed", { actionId, errorMessage });
5973
+ logEvent("error", "Dynamic Flow - Action Failed Unexpectedly", { actionId, errorMessage });
5974
+ throw error;
5975
+ }
5976
+ };
5977
+ return triggerAction(props.action, props.model, props.isInitial);
5978
+ };
5979
+ var createRequestFromAction = (action, model) => {
5980
+ var _a, _b, _c;
5981
+ return __spreadProps(__spreadValues({}, action), {
5982
+ url: (_a = action.url) != null ? _a : "",
5983
+ method: (_b = action.method) != null ? _b : "POST",
5984
+ body: action.method === "GET" ? void 0 : recursiveMerge(model, (_c = action.data) != null ? _c : null)
5990
5985
  });
5991
5986
  };
5992
5987
 
5993
- // src/domain/mappers/utils/groupLayoutByPinned.ts
5994
- var groupLayoutByPinned = (layouts) => {
5995
- return layouts.reduce(groupLayout, { pinned: [], nonPinned: [] });
5988
+ // src/domain/features/prefetch/request-cache.ts
5989
+ var makeRequestCacheWithParent = (parent) => {
5990
+ const map = /* @__PURE__ */ new Map();
5991
+ const cache = {
5992
+ get: (requestParams) => {
5993
+ var _a;
5994
+ const key = makeKey(requestParams);
5995
+ const promise = (_a = map.get(key)) != null ? _a : parent == null ? void 0 : parent.get(requestParams);
5996
+ map.delete(key);
5997
+ return promise;
5998
+ },
5999
+ set: (requestParams, responsePromise) => {
6000
+ return map.set(makeKey(requestParams), responsePromise);
6001
+ }
6002
+ };
6003
+ return cache;
5996
6004
  };
5997
- var groupLayout = (acc, layout) => {
5998
- if (layout.type === "button" && layout.pinOrder !== void 0) {
5999
- return {
6000
- pinned: [...acc.pinned, layout],
6001
- nonPinned: acc.nonPinned
6002
- };
6003
- }
6004
- if (hasColumns(layout)) {
6005
- const leftChildren = groupLayoutByPinned(layout.left);
6006
- const rightChildren = groupLayoutByPinned(layout.right);
6007
- return {
6008
- pinned: [...acc.pinned, ...leftChildren.pinned, ...rightChildren.pinned],
6009
- nonPinned: [
6010
- ...acc.nonPinned,
6011
- __spreadProps(__spreadValues({}, layout), {
6012
- left: leftChildren.nonPinned,
6013
- right: rightChildren.nonPinned
6014
- })
6015
- ]
6016
- };
6005
+ var makeRequestCache = (initialValues = []) => {
6006
+ const cache = makeRequestCacheWithParent(void 0);
6007
+ initialValues.forEach(([requestParams, responsePromise]) => {
6008
+ cache.set(requestParams, responsePromise);
6009
+ });
6010
+ return cache;
6011
+ };
6012
+ var normaliseRequestCache = (cache) => {
6013
+ if (cache === void 0) {
6014
+ return makeRequestCache();
6017
6015
  }
6018
- if (hasChildren(layout)) {
6019
- const childComponents = groupLayoutByPinned(layout.components);
6020
- return {
6021
- pinned: [...acc.pinned, ...childComponents.pinned],
6022
- nonPinned: [
6023
- ...acc.nonPinned,
6024
- __spreadProps(__spreadValues({}, layout), {
6025
- components: childComponents.nonPinned
6026
- })
6027
- ]
6028
- };
6016
+ if (isRequestCacheInstance(cache)) {
6017
+ return cache;
6029
6018
  }
6030
- return {
6031
- pinned: [...acc.pinned],
6032
- nonPinned: [...acc.nonPinned, layout]
6019
+ return makeRequestCache(cache);
6020
+ };
6021
+ var isRequestCacheInstance = (cache) => {
6022
+ return !cache || !Array.isArray(cache);
6023
+ };
6024
+ var makeKey = (requestParams) => {
6025
+ var _a, _b;
6026
+ const [input, init] = requestParams;
6027
+ const url = typeof input === "string" || input instanceof URL ? input.toString() : input.url;
6028
+ const key = JSON.stringify({
6029
+ url,
6030
+ method: (_a = init == null ? void 0 : init.method) != null ? _a : "GET",
6031
+ headers: (init == null ? void 0 : init.headers) ? Array.from(new Headers(init.headers).entries()) : [],
6032
+ body: (_b = init == null ? void 0 : init.body) != null ? _b : null
6033
+ });
6034
+ return key;
6035
+ };
6036
+
6037
+ // src/domain/features/prefetch/getStepPrefetch.ts
6038
+ var getStepPrefetch = (httpClient, flowRequestCache, submissionBehaviors) => {
6039
+ const requestCache = makeRequestCacheWithParent(flowRequestCache);
6040
+ const start = (model) => {
6041
+ submissionBehaviors.forEach((behavior) => {
6042
+ const request = behavior.type === "action" ? createRequestFromAction(behavior.action, model) : behavior.launchConfig.request;
6043
+ const requestParams = [
6044
+ request.url,
6045
+ {
6046
+ body: JSON.stringify(request.body),
6047
+ method: request.method,
6048
+ headers: { "Content-Type": "application/json" }
6049
+ }
6050
+ ];
6051
+ try {
6052
+ const responsePromise = httpClient(...requestParams).catch(() => null);
6053
+ requestCache.set(requestParams, responsePromise);
6054
+ } catch (e) {
6055
+ }
6056
+ });
6057
+ };
6058
+ const stop = () => {
6033
6059
  };
6060
+ return { requestCache, start, stop };
6034
6061
  };
6035
- var hasChildren = (component) => "components" in component;
6036
- var hasColumns = (component) => "right" in component && "left" in component;
6037
6062
 
6038
6063
  // src/domain/mappers/mapStepToComponent.ts
6039
6064
  var mapStepToComponent = (_a) => {
@@ -6061,14 +6086,12 @@ var mapStepToComponent = (_a) => {
6061
6086
  errors,
6062
6087
  external,
6063
6088
  key,
6064
- layout = [],
6065
6089
  navigation,
6066
6090
  polling,
6067
6091
  refreshAfter,
6068
6092
  title,
6069
6093
  tags
6070
6094
  } = step;
6071
- const requestCache = makeRequestCacheWithParent(flowRequestCache);
6072
6095
  const submissionBehaviors = [];
6073
6096
  const registerSubmissionBehavior = (behavior) => {
6074
6097
  if (behavior.type === "action" && behavior.action.prefetch) {
@@ -6092,6 +6115,7 @@ var mapStepToComponent = (_a) => {
6092
6115
  registerSubmissionBehavior
6093
6116
  }) : void 0;
6094
6117
  const stepRefreshAfter = refreshAfter ? getStepRefreshAfter({ refreshAfter, logEvent, onBehavior }) : void 0;
6118
+ const stepPrefetch = getStepPrefetch(restProps.httpClient, flowRequestCache, submissionBehaviors);
6095
6119
  const externalConfirmation = (external == null ? void 0 : external.url) ? createExternalConfirmation(`${uid}-external-confirmation`, external == null ? void 0 : external.url, onComponentUpdate) : void 0;
6096
6120
  const mapperProps = __spreadProps(__spreadValues({}, restProps), { trackEvent, onBehavior, registerSubmissionBehavior });
6097
6121
  const referencedSchemaIds = getReferencedSchemaId(step);
@@ -6102,21 +6126,12 @@ var mapStepToComponent = (_a) => {
6102
6126
  mapperProps,
6103
6127
  referencedSchemaIds
6104
6128
  );
6105
- const shouldPinButtons = features.isEnabled("pinnedButtons");
6106
- const { pinned, nonPinned } = shouldPinButtons ? groupLayoutByPinned(layout) : { pinned: [], nonPinned: layout };
6107
- const layoutComponents = nonPinned.map(
6129
+ const { layout, footer } = getLayoutAndFooter(step, features);
6130
+ const layoutComponents = layout.map(
6108
6131
  (layoutComponent, index) => mapLayoutToComponent(`${uid}.layout-${index}`, layoutComponent, mapperProps, schemaComponents)
6109
6132
  );
6110
- const footerComponents = pinned.map((footerComponent, index) => {
6111
- const shouldRemoveMargin = index === pinned.length - 1 && footerComponent.margin === void 0;
6112
- return shouldRemoveMargin ? removeMarginFromLayout(footerComponent) : footerComponent;
6113
- }).map(
6114
- (footerComponent, index) => mapLayoutToComponent(
6115
- `${uid}.footer-${index}`,
6116
- footerComponent,
6117
- mapperProps,
6118
- schemaComponents
6119
- )
6133
+ const footerComponents = normaliseMarginInLastComponent(footer).map(
6134
+ (footerComponent, index) => mapLayoutToComponent(`${uid}.footer-${index}`, footerComponent, mapperProps, schemaComponents)
6120
6135
  );
6121
6136
  const toolbar = mapToolbarToComponent(uid, step.toolbar, mapperProps);
6122
6137
  const stepComponent = createStepComponent({
@@ -6137,18 +6152,12 @@ var mapStepToComponent = (_a) => {
6137
6152
  title,
6138
6153
  tags,
6139
6154
  stackBehavior: (_a2 = navigation == null ? void 0 : navigation.stackBehavior) != null ? _a2 : "default",
6140
- requestCache,
6155
+ stepPrefetch,
6141
6156
  step,
6142
6157
  onComponentUpdate,
6143
6158
  trackEvent,
6144
6159
  onBehavior
6145
6160
  });
6146
- executePrefetch({
6147
- httpClient: mapperProps.httpClient,
6148
- model: stepComponent.getSubmittableValueSync(),
6149
- behaviors: submissionBehaviors,
6150
- requestCache
6151
- });
6152
6161
  return stepComponent;
6153
6162
  };
6154
6163
  var getReferencedSchemaId = (step) => {
@@ -6169,24 +6178,18 @@ var mapBackNavigation = (navigation, onBehavior, isNativeBackEnabled) => {
6169
6178
  }
6170
6179
  } : void 0;
6171
6180
  };
6172
- var executePrefetch = (props) => {
6173
- const { httpClient, behaviors: submissionBehaviors, model, requestCache } = props;
6174
- submissionBehaviors.forEach((behavior) => {
6175
- const request = behavior.type === "action" ? createRequestFromAction(behavior.action, model) : behavior.launchConfig.request;
6176
- const requestParams = [
6177
- request.url,
6178
- {
6179
- body: JSON.stringify(request.body),
6180
- method: request.method,
6181
- headers: { "Content-Type": "application/json" }
6182
- }
6183
- ];
6184
- try {
6185
- const responsePromise = httpClient(...requestParams).catch(() => null);
6186
- requestCache.set(requestParams, responsePromise);
6187
- } catch (e) {
6188
- }
6189
- });
6181
+ var getLayoutAndFooter = (step, features) => {
6182
+ var _a;
6183
+ if (step.footer) {
6184
+ const { layout: layout2 = [], footer = [] } = step;
6185
+ return { layout: layout2, footer };
6186
+ }
6187
+ if (features.isEnabled("pinnedButtons")) {
6188
+ const { pinned: footer, nonPinned: layout2 } = groupLayoutByPinned((_a = step.layout) != null ? _a : []);
6189
+ return { layout: layout2, footer };
6190
+ }
6191
+ const { layout = [] } = step;
6192
+ return { layout, footer: [] };
6190
6193
  };
6191
6194
 
6192
6195
  // src/getSubflowCallbacks.ts
@@ -6780,17 +6783,28 @@ var createFlowController = (props) => {
6780
6783
  return false;
6781
6784
  }
6782
6785
  };
6783
- trackEvent("Initiated");
6786
+ let initState = "initial";
6784
6787
  if (initialStep) {
6788
+ initState = "created";
6789
+ trackEvent("Initiated");
6785
6790
  createStep(initialStep, null);
6786
6791
  trackEvent("Step Shown", { isFirstStep: true });
6787
- } else if (initialAction) {
6788
- rootComponent.setLoadingState("submitting");
6789
- void onAction(__spreadValues({ method: "GET" }, initialAction), null);
6790
6792
  }
6791
- rootComponent.start();
6793
+ const start = () => {
6794
+ if (initState === "initial" && initialAction) {
6795
+ initState = "created";
6796
+ trackEvent("Initiated");
6797
+ rootComponent.setLoadingState("submitting");
6798
+ void onAction(__spreadValues({ method: "GET" }, initialAction), null);
6799
+ }
6800
+ if (initState === "created") {
6801
+ initState = "started";
6802
+ rootComponent.start();
6803
+ }
6804
+ };
6792
6805
  return {
6793
6806
  rootComponent,
6807
+ start,
6794
6808
  cancel: closeWithCancellation
6795
6809
  };
6796
6810
  };
@@ -8188,7 +8202,10 @@ var useRerender = () => {
8188
8202
  };
8189
8203
  var useFlowController = (props) => {
8190
8204
  const df = useMemo(() => createFlowController(props), []);
8191
- useEffect2(() => () => df.rootComponent.stop(), []);
8205
+ useEffect2(() => {
8206
+ df.start();
8207
+ return () => df.rootComponent.stop();
8208
+ }, []);
8192
8209
  return df;
8193
8210
  };
8194
8211