@ukiahinsure/a2ui-react-adapter 0.1.7 → 0.1.8

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/dist/index.js CHANGED
@@ -29,6 +29,7 @@ var A2UIClientAdapter = class {
29
29
  #surfaceOrder = [];
30
30
  #structureBySurface = /* @__PURE__ */ new Map();
31
31
  #persistedListRowsByPath = /* @__PURE__ */ new Map();
32
+ #deletedListItemIdsByPath = /* @__PURE__ */ new Map();
32
33
  #version = 0;
33
34
  #lastSeq = 0;
34
35
  #disposed = false;
@@ -157,6 +158,7 @@ var A2UIClientAdapter = class {
157
158
  this.#surfaceOrder = [];
158
159
  this.#structureBySurface.clear();
159
160
  this.#persistedListRowsByPath.clear();
161
+ this.#deletedListItemIdsByPath.clear();
160
162
  previous.model.dispose();
161
163
  this.#notify();
162
164
  }
@@ -213,12 +215,17 @@ var A2UIClientAdapter = class {
213
215
  const rawMessages = envelope.kind === "snapshot" ? validated.data : mergePersistedListRowsIntoMessages(
214
216
  validated.data,
215
217
  envelope.surfaceId,
216
- this.#persistedListRowsByPath
218
+ this.#persistedListRowsByPath,
219
+ this.#deletedListItemIdsByPath
217
220
  );
218
- const actionCompleteMessages = ensureOptionalCoverageSkipActionMessages(
221
+ const listAddFreeMessages = removeListAddActionMessages(
219
222
  rawMessages,
220
223
  envelope.surfaceId
221
224
  );
225
+ const actionCompleteMessages = ensureOptionalCoverageSkipActionMessages(
226
+ listAddFreeMessages,
227
+ envelope.surfaceId
228
+ );
222
229
  const nextStructure = updateSurfaceStructures(
223
230
  actionCompleteMessages,
224
231
  envelope.kind === "snapshot" ? /* @__PURE__ */ new Map() : cloneSurfaceStructures(this.#structureBySurface)
@@ -264,9 +271,11 @@ var A2UIClientAdapter = class {
264
271
  this.#structureBySurface = nextStructure;
265
272
  if (envelope.kind === "snapshot") {
266
273
  this.#persistedListRowsByPath.clear();
274
+ this.#deletedListItemIdsByPath.clear();
267
275
  }
268
276
  rememberPersistedListRows(
269
277
  this.#persistedListRowsByPath,
278
+ this.#deletedListItemIdsByPath,
270
279
  candidate.model.getSurface(envelope.surfaceId)?.dataModel.get("/")
271
280
  );
272
281
  previous.model.dispose();
@@ -302,6 +311,9 @@ var A2UIClientAdapter = class {
302
311
  event.context
303
312
  );
304
313
  }
314
+ if (event.actionName === "flow.list.delete") {
315
+ this.#removeDeletedListRow(event.surfaceId, event.context);
316
+ }
305
317
  this.#options.onAction?.(event);
306
318
  for (const listener of this.#actionListeners) {
307
319
  listener(event);
@@ -327,6 +339,27 @@ var A2UIClientAdapter = class {
327
339
  );
328
340
  this.#notify();
329
341
  }
342
+ #removeDeletedListRow(surfaceId, context) {
343
+ const listPath = context.listPath;
344
+ const itemId = context.itemId;
345
+ if (typeof listPath !== "string" || listPath.length === 0 || typeof itemId !== "string" || itemId.length === 0) {
346
+ return;
347
+ }
348
+ const cachedRows = this.#persistedListRowsByPath.get(listPath) ?? [];
349
+ const nextRows = removeListRowByItemId(cachedRows, itemId);
350
+ this.#persistedListRowsByPath.set(listPath, nextRows);
351
+ rememberDeletedListItemId(this.#deletedListItemIdsByPath, listPath, itemId);
352
+ const surface = this.#processor.model.getSurface(surfaceId);
353
+ if (surface === void 0) {
354
+ return;
355
+ }
356
+ surface.dataModel.set(`/${listPath}`, cloneJsonArray(nextRows));
357
+ surface.dataModel.set(
358
+ `/${listPath.replaceAll(".", "/")}`,
359
+ cloneJsonArray(nextRows)
360
+ );
361
+ this.#notify();
362
+ }
330
363
  #snapshotMessages() {
331
364
  const messages = [];
332
365
  for (const surface of this.#processor.model.surfacesMap.values()) {
@@ -494,6 +527,59 @@ function ensureOptionalCoverageSkipActionMessages(messages, surfaceId) {
494
527
  }
495
528
  return messages;
496
529
  }
530
+ function removeListAddActionMessages(messages, surfaceId) {
531
+ for (let index = 0; index < messages.length; index += 1) {
532
+ const message = messages[index];
533
+ if (!isRecord(message) || !isRecord(message.updateComponents)) {
534
+ continue;
535
+ }
536
+ const update = message.updateComponents;
537
+ if (update.surfaceId !== surfaceId || !Array.isArray(update.components)) {
538
+ continue;
539
+ }
540
+ const addIds = listAddComponentIds(update.components);
541
+ if (addIds.size === 0) {
542
+ continue;
543
+ }
544
+ const nextMessages = [...messages];
545
+ nextMessages[index] = {
546
+ ...message,
547
+ updateComponents: {
548
+ ...update,
549
+ components: update.components.filter(
550
+ (component) => !(isRecord(component) && isNonEmptyString(component.id) && addIds.has(component.id))
551
+ ).map(
552
+ (component) => removeComponentChildren(component, addIds)
553
+ )
554
+ }
555
+ };
556
+ return nextMessages;
557
+ }
558
+ return messages;
559
+ }
560
+ function listAddComponentIds(components) {
561
+ const ids = /* @__PURE__ */ new Set();
562
+ for (const component of components) {
563
+ if (!isRecord(component) || !isNonEmptyString(component.id) || !hasActionName(component, "flow.list.add")) {
564
+ continue;
565
+ }
566
+ ids.add(component.id);
567
+ const child = component.child;
568
+ if (typeof child === "string" && child.length > 0) {
569
+ ids.add(child);
570
+ }
571
+ }
572
+ return ids;
573
+ }
574
+ function removeComponentChildren(component, removedIds) {
575
+ if (!isRecord(component) || !Array.isArray(component.children)) {
576
+ return component;
577
+ }
578
+ const children = component.children.filter(
579
+ (child) => typeof child !== "string" || !removedIds.has(child)
580
+ );
581
+ return children.length === component.children.length ? component : { ...component, children };
582
+ }
497
583
  function submitActionWithDefaultUnknown(submit, field) {
498
584
  if (!isRecord(submit.action) || !isRecord(submit.action.event)) {
499
585
  return submit;
@@ -770,8 +856,8 @@ function fieldFromPointer(path) {
770
856
  function fieldPointer(field) {
771
857
  return `/${field.replaceAll("~", "~0").replaceAll("/", "~1")}`;
772
858
  }
773
- function mergePersistedListRowsIntoMessages(messages, surfaceId, persistedRowsByPath) {
774
- if (persistedRowsByPath.size === 0) {
859
+ function mergePersistedListRowsIntoMessages(messages, surfaceId, persistedRowsByPath, deletedItemIdsByPath) {
860
+ if (persistedRowsByPath.size === 0 && deletedItemIdsByPath.size === 0) {
775
861
  return [...messages];
776
862
  }
777
863
  let mergedRoot = false;
@@ -786,7 +872,8 @@ function mergePersistedListRowsIntoMessages(messages, surfaceId, persistedRowsBy
786
872
  ...message.updateDataModel,
787
873
  value: mergePersistedListRowsIntoRoot(
788
874
  message.updateDataModel.value,
789
- persistedRowsByPath
875
+ persistedRowsByPath,
876
+ deletedItemIdsByPath
790
877
  )
791
878
  }
792
879
  };
@@ -801,25 +888,34 @@ function mergePersistedListRowsIntoMessages(messages, surfaceId, persistedRowsBy
801
888
  updateDataModel: {
802
889
  surfaceId,
803
890
  path: "/",
804
- value: mergePersistedListRowsIntoRoot({}, persistedRowsByPath)
891
+ value: mergePersistedListRowsIntoRoot(
892
+ {},
893
+ persistedRowsByPath,
894
+ deletedItemIdsByPath
895
+ )
805
896
  }
806
897
  }
807
898
  ];
808
899
  }
809
- function mergePersistedListRowsIntoRoot(root, persistedRowsByPath) {
900
+ function mergePersistedListRowsIntoRoot(root, persistedRowsByPath, deletedItemIdsByPath) {
810
901
  const merged = cloneJsonRecord(root);
811
- for (const [listPath, rows] of persistedRowsByPath) {
902
+ for (const listPath of /* @__PURE__ */ new Set([
903
+ ...persistedRowsByPath.keys(),
904
+ ...deletedItemIdsByPath.keys()
905
+ ])) {
906
+ const rows = persistedRowsByPath.get(listPath) ?? [];
812
907
  const dottedRows = merged[listPath];
813
908
  const nestedRows = getNestedValue(merged, listPath);
814
909
  const incomingRows = Array.isArray(dottedRows) ? dottedRows : Array.isArray(nestedRows) ? nestedRows : void 0;
815
910
  const shouldPreserveCachedRows = rows.length > 0 && (incomingRows === void 0 || incomingRows.length === 0 && (!hasListDraft(merged, listPath) || hasBlankListDraft(merged, listPath)));
816
911
  const effectiveRows = shouldPreserveCachedRows ? rows : incomingRows ?? rows;
817
- if (!Array.isArray(dottedRows) || shouldPreserveCachedRows) {
818
- merged[listPath] = cloneJsonArray(effectiveRows);
819
- }
820
- if (!Array.isArray(nestedRows) || shouldPreserveCachedRows) {
821
- setNestedValue(merged, listPath, cloneJsonArray(effectiveRows));
822
- }
912
+ const visibleRows = filterDeletedListRows(
913
+ effectiveRows,
914
+ deletedItemIdsByPath.get(listPath)
915
+ );
916
+ const normalizedRows = normalizeListRows(listPath, visibleRows, merged);
917
+ merged[listPath] = cloneJsonArray(normalizedRows);
918
+ setNestedValue(merged, listPath, cloneJsonArray(normalizedRows));
823
919
  }
824
920
  return merged;
825
921
  }
@@ -850,13 +946,65 @@ function isBlankDraftValue(value) {
850
946
  }
851
947
  return false;
852
948
  }
853
- function rememberPersistedListRows(persistedRowsByPath, root) {
949
+ function rememberPersistedListRows(persistedRowsByPath, deletedItemIdsByPath, root) {
854
950
  if (!isRecord(root)) {
855
951
  return;
856
952
  }
857
953
  for (const [listPath, rows] of listRowsFromRoot(root)) {
858
- persistedRowsByPath.set(listPath, cloneJsonArray(rows));
954
+ const visibleRows = filterDeletedListRows(
955
+ rows,
956
+ deletedItemIdsByPath.get(listPath)
957
+ );
958
+ persistedRowsByPath.set(
959
+ listPath,
960
+ normalizeListRows(listPath, visibleRows, root)
961
+ );
962
+ }
963
+ }
964
+ function normalizeListRows(listPath, rows, root) {
965
+ const clonedRows = cloneJsonArray(rows);
966
+ if (listPath !== "needs.providers") {
967
+ return clonedRows;
968
+ }
969
+ const rootProvider = root === void 0 ? void 0 : providerRowFromAliases(root);
970
+ if (clonedRows.length === 0 && rootProvider !== void 0) {
971
+ return [normalizeProviderRow(rootProvider)];
972
+ }
973
+ return clonedRows.map(
974
+ (row) => isRecord(row) ? normalizeProviderRow(row, rootProvider) : row
975
+ );
976
+ }
977
+ function normalizeProviderRow(row, fallback) {
978
+ const normalized = cloneJsonRecord(row);
979
+ const name = firstProviderName(normalized) ?? (fallback === void 0 ? void 0 : firstProviderName(fallback));
980
+ const location = firstProviderLocation(normalized) ?? (fallback === void 0 ? void 0 : firstProviderLocation(fallback));
981
+ if (normalized.primary_care_provider === void 0 && name !== void 0) {
982
+ normalized.primary_care_provider = name;
983
+ }
984
+ if (normalized.primary_care_provider_location === void 0 && location !== void 0) {
985
+ normalized.primary_care_provider_location = location;
986
+ }
987
+ if (name !== void 0 && location !== void 0 && typeof normalized.__displayText === "string" && !normalized.__displayText.includes(location)) {
988
+ normalized.__displayText = `${normalized.__displayText} | Provider Location: ${location}`;
989
+ } else if (normalized.__displayText === void 0 && (name !== void 0 || location !== void 0)) {
990
+ normalized.__displayText = [
991
+ name === void 0 ? void 0 : `Provider Name: ${name}`,
992
+ location === void 0 ? void 0 : `Provider Location: ${location}`
993
+ ].filter((part) => part !== void 0).join(" | ");
859
994
  }
995
+ return normalized;
996
+ }
997
+ function providerRowFromAliases(source) {
998
+ const name = firstProviderName(source);
999
+ const location = firstProviderLocation(source);
1000
+ if (name === void 0 && location === void 0) {
1001
+ return void 0;
1002
+ }
1003
+ return {
1004
+ itemId: "voice-primary-care-provider",
1005
+ ...name === void 0 ? {} : { primary_care_provider: name },
1006
+ ...location === void 0 ? {} : { primary_care_provider_location: location }
1007
+ };
860
1008
  }
861
1009
  function submittedProviderRowFromEvent(event) {
862
1010
  if (event.actionName !== "flow.submit") {
@@ -866,37 +1014,8 @@ function submittedProviderRowFromEvent(event) {
866
1014
  if (listPath !== "needs.providers" && !hasProviderSubmitFields(event.context)) {
867
1015
  return void 0;
868
1016
  }
869
- const name = firstTextField(event.context, [
870
- "primary_care_provider",
871
- "primaryCareProvider",
872
- "primary_provider",
873
- "primaryProvider",
874
- "primary_care_doctor",
875
- "primaryCareDoctor",
876
- "pcp",
877
- "providerName",
878
- "provider_name",
879
- "displayName",
880
- "display_name",
881
- "doctorName",
882
- "doctorname",
883
- "doctor_name",
884
- "name"
885
- ]);
886
- const location = firstTextField(event.context, [
887
- "primary_care_provider_location",
888
- "primaryCareProviderLocation",
889
- "provider_location",
890
- "providerLocation",
891
- "location_details",
892
- "locationDetails",
893
- "office_location",
894
- "officeLocation",
895
- "practice_location",
896
- "practiceLocation",
897
- "location",
898
- "address"
899
- ]);
1017
+ const name = firstProviderName(event.context);
1018
+ const location = firstProviderLocation(event.context);
900
1019
  if (name === void 0 && location === void 0) {
901
1020
  return void 0;
902
1021
  }
@@ -1088,6 +1207,122 @@ function upsertListRow(rows, row) {
1088
1207
  }
1089
1208
  return [...next, row];
1090
1209
  }
1210
+ function removeListRowByItemId(rows, itemId) {
1211
+ return rows.filter(
1212
+ (row) => !isRecord(row) || row.itemId !== itemId
1213
+ );
1214
+ }
1215
+ function rememberDeletedListItemId(deletedItemIdsByPath, listPath, itemId) {
1216
+ const deleted = deletedItemIdsByPath.get(listPath) ?? /* @__PURE__ */ new Set();
1217
+ deleted.add(itemId);
1218
+ deletedItemIdsByPath.set(listPath, deleted);
1219
+ }
1220
+ function filterDeletedListRows(rows, deletedItemIds) {
1221
+ const clonedRows = cloneJsonArray(rows);
1222
+ if (deletedItemIds === void 0 || deletedItemIds.size === 0) {
1223
+ return clonedRows;
1224
+ }
1225
+ return clonedRows.filter(
1226
+ (row) => !isRecord(row) || !deletedItemIds.has(stringRowItemId(row))
1227
+ );
1228
+ }
1229
+ function stringRowItemId(row) {
1230
+ const itemId = row.itemId;
1231
+ return typeof itemId === "string" ? itemId : "";
1232
+ }
1233
+ var PROVIDER_NAME_FIELDS = /* @__PURE__ */ new Set([
1234
+ "primary_care_provider",
1235
+ "primarycareprovider",
1236
+ "primary_care_provider_name",
1237
+ "primarycareprovidername",
1238
+ "primary_provider",
1239
+ "primaryprovider",
1240
+ "primary_provider_name",
1241
+ "primaryprovidername",
1242
+ "primary_care_doctor",
1243
+ "primarycaredoctor",
1244
+ "primary_care_doctor_name",
1245
+ "primarycaredoctorname",
1246
+ "pcp",
1247
+ "pcp_name",
1248
+ "pcpname",
1249
+ "provider",
1250
+ "provider_name",
1251
+ "providername",
1252
+ "display_name",
1253
+ "displayname",
1254
+ "doctor",
1255
+ "doctor_name",
1256
+ "doctorname",
1257
+ "name"
1258
+ ]);
1259
+ var PROVIDER_LOCATION_FIELDS = /* @__PURE__ */ new Set([
1260
+ "primary_care_provider_location",
1261
+ "primarycareproviderlocation",
1262
+ "primary_provider_location",
1263
+ "primaryproviderlocation",
1264
+ "primary_care_location",
1265
+ "primarycarelocation",
1266
+ "primary_care_doctor_location",
1267
+ "primarycaredoctorlocation",
1268
+ "pcp_location",
1269
+ "pcplocation",
1270
+ "provider_location",
1271
+ "providerlocation",
1272
+ "provider_location_details",
1273
+ "providerlocationdetails",
1274
+ "doctor_location",
1275
+ "doctorlocation",
1276
+ "location",
1277
+ "location_details",
1278
+ "locationdetails",
1279
+ "office_location",
1280
+ "officelocation",
1281
+ "practice_location",
1282
+ "practicelocation",
1283
+ "provider_address",
1284
+ "provideraddress",
1285
+ "doctor_address",
1286
+ "doctoraddress",
1287
+ "address",
1288
+ "city_state",
1289
+ "citystate"
1290
+ ]);
1291
+ function firstProviderName(source) {
1292
+ return firstTextByNormalizedField(source, PROVIDER_NAME_FIELDS);
1293
+ }
1294
+ function firstProviderLocation(source) {
1295
+ return firstTextByNormalizedField(source, PROVIDER_LOCATION_FIELDS);
1296
+ }
1297
+ function firstTextByNormalizedField(source, fields) {
1298
+ for (const [key, value] of Object.entries(source)) {
1299
+ if (!fields.has(normalizeFieldKey(key))) {
1300
+ continue;
1301
+ }
1302
+ const text = scalarText(value);
1303
+ if (text !== void 0) {
1304
+ return text;
1305
+ }
1306
+ }
1307
+ return void 0;
1308
+ }
1309
+ function scalarText(value) {
1310
+ if (typeof value === "string") {
1311
+ const text = value.trim();
1312
+ return text.length > 0 ? text : void 0;
1313
+ }
1314
+ if (!isRecord(value)) {
1315
+ return void 0;
1316
+ }
1317
+ return firstTextField(value, [
1318
+ "value",
1319
+ "displayValue",
1320
+ "display_value",
1321
+ "text",
1322
+ "answer",
1323
+ "label"
1324
+ ]);
1325
+ }
1091
1326
  function firstTextField(source, keys) {
1092
1327
  for (const key of keys) {
1093
1328
  const value = source[key];
@@ -1754,8 +1989,22 @@ function applyControlValidationAttributes(control, field) {
1754
1989
  }
1755
1990
  function attachControlValidationBehavior(control, field) {
1756
1991
  const maxLength = numericMaxLengthForField(field);
1992
+ const customValidation = customValidationForField(field);
1757
1993
  if (maxLength === void 0 || !isTextInput(control)) {
1758
- return void 0;
1994
+ if (customValidation === void 0 || !isTextInput(control)) {
1995
+ return void 0;
1996
+ }
1997
+ const validate = () => {
1998
+ applyCustomValidationMessage(control, customValidation);
1999
+ };
2000
+ control.addEventListener("input", validate);
2001
+ control.addEventListener("change", validate);
2002
+ validate();
2003
+ return () => {
2004
+ control.setCustomValidity("");
2005
+ control.removeEventListener("input", validate);
2006
+ control.removeEventListener("change", validate);
2007
+ };
1759
2008
  }
1760
2009
  const sanitize = () => {
1761
2010
  const next = control.value.replace(/\D/g, "").slice(0, maxLength);
@@ -1763,15 +2012,50 @@ function attachControlValidationBehavior(control, field) {
1763
2012
  control.value = next;
1764
2013
  control.dispatchEvent(new Event("input", { bubbles: true }));
1765
2014
  }
2015
+ if (customValidation !== void 0) {
2016
+ applyCustomValidationMessage(control, customValidation);
2017
+ }
1766
2018
  };
1767
2019
  control.addEventListener("input", sanitize);
1768
2020
  control.addEventListener("change", sanitize);
1769
2021
  sanitize();
1770
2022
  return () => {
2023
+ control.setCustomValidity("");
1771
2024
  control.removeEventListener("input", sanitize);
1772
2025
  control.removeEventListener("change", sanitize);
1773
2026
  };
1774
2027
  }
2028
+ function customValidationForField(field) {
2029
+ if (isZipValidationRegexp(field.validationRegexp)) {
2030
+ return {
2031
+ message: "Enter a valid 5-digit ZIP code.",
2032
+ pattern: regexpFromPattern(field.validationRegexp)
2033
+ };
2034
+ }
2035
+ if (isPhoneValidationRegexp(field.validationRegexp)) {
2036
+ return {
2037
+ message: "Enter a valid 10-digit US phone number.",
2038
+ pattern: regexpFromPattern(field.validationRegexp)
2039
+ };
2040
+ }
2041
+ return void 0;
2042
+ }
2043
+ function applyCustomValidationMessage(control, validation) {
2044
+ control.setCustomValidity("");
2045
+ if (control.value.length > 0 && (validation.pattern?.test(control.value) === false || !control.checkValidity())) {
2046
+ control.setCustomValidity(validation.message);
2047
+ }
2048
+ }
2049
+ function regexpFromPattern(pattern) {
2050
+ if (pattern === void 0) {
2051
+ return void 0;
2052
+ }
2053
+ try {
2054
+ return new RegExp(pattern);
2055
+ } catch {
2056
+ return void 0;
2057
+ }
2058
+ }
1775
2059
  function numericMaxLengthForField(field) {
1776
2060
  if (isPhoneValidationRegexp(field.validationRegexp)) {
1777
2061
  return 10;
@@ -2537,6 +2821,7 @@ function decodeRendererAction(event) {
2537
2821
  ([key]) => key !== "listPath" && key !== "itemId"
2538
2822
  );
2539
2823
  const fields2 = scalarFields(fieldEntries2, listPath);
2824
+ const targetStateId2 = listActionTargetStateId(event.sourceComponentId);
2540
2825
  if (action !== "flow.list.delete" && (fields2 === null || Object.keys(fields2).length === 0)) {
2541
2826
  return null;
2542
2827
  }
@@ -2544,6 +2829,7 @@ function decodeRendererAction(event) {
2544
2829
  action,
2545
2830
  payload: {
2546
2831
  listPath,
2832
+ ...targetStateId2 === void 0 ? {} : { targetStateId: targetStateId2 },
2547
2833
  ...typeof itemId === "string" ? { itemId } : {},
2548
2834
  ...fields2 !== null && Object.keys(fields2).length > 0 ? { fields: fields2 } : {}
2549
2835
  }
@@ -2566,6 +2852,14 @@ function decodeRendererAction(event) {
2566
2852
  }
2567
2853
  };
2568
2854
  }
2855
+ function listActionTargetStateId(sourceComponentId) {
2856
+ const markerIndex = sourceComponentId.indexOf(":list");
2857
+ if (markerIndex <= 0) {
2858
+ return void 0;
2859
+ }
2860
+ const stateId = sourceComponentId.slice(0, markerIndex);
2861
+ return stateId.length > 0 ? stateId : void 0;
2862
+ }
2569
2863
  function scalarFields(entries, listPath) {
2570
2864
  const fields = {};
2571
2865
  for (const [key, value] of entries) {