@marktiderman/genesis-forms 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -833,6 +833,81 @@ var structuredFieldTypes = [
833
833
  }
834
834
  ];
835
835
 
836
+ // src/photo.ts
837
+ var PHOTO_TYPE = "photo";
838
+ var UPLOAD_FIELD_TYPES = [PHOTO_TYPE];
839
+ var PHOTO_FORMATS = {
840
+ jpeg: ["image/jpeg"],
841
+ png: ["image/png"],
842
+ heic: ["image/heic", "image/heif"],
843
+ webp: ["image/webp"]
844
+ };
845
+ var FORMAT_NAMES = { jpeg: "JPEG", png: "PNG", heic: "HEIC", webp: "WebP" };
846
+ var ALL_FORMATS = Object.keys(PHOTO_FORMATS);
847
+ var DEFAULT_PHOTO_FILES = 3;
848
+ var MAX_PHOTO_FILES = 5;
849
+ var DEFAULT_PHOTO_SIZE_MB = 10;
850
+ var MAX_PHOTO_SIZE_MB = 25;
851
+ var MAX_PHOTO_ID_LENGTH = 200;
852
+ var MAX_PHOTO_NAME_LENGTH = 255;
853
+ var isPhotoFormat = (v) => typeof v === "string" && Object.prototype.hasOwnProperty.call(PHOTO_FORMATS, v);
854
+ var wholeIn = (v, min, max) => typeof v === "number" && Number.isInteger(v) && v >= min && v <= max;
855
+ function photoLimits(field2) {
856
+ const maxFiles = wholeIn(field2.props?.maxFiles, 1, MAX_PHOTO_FILES) ? field2.props.maxFiles : DEFAULT_PHOTO_FILES;
857
+ const maxSizeMb = wholeIn(field2.props?.maxSizeMb, 1, MAX_PHOTO_SIZE_MB) ? field2.props.maxSizeMb : DEFAULT_PHOTO_SIZE_MB;
858
+ const raw = field2.props?.formats;
859
+ const picked = Array.isArray(raw) ? ALL_FORMATS.filter((f2) => raw.includes(f2)) : [];
860
+ const formats = picked.length ? picked : [...ALL_FORMATS];
861
+ return {
862
+ maxFiles,
863
+ maxSizeMb,
864
+ maxBytes: maxSizeMb * 1024 * 1024,
865
+ formats,
866
+ mimeTypes: formats.flatMap((f2) => PHOTO_FORMATS[f2])
867
+ };
868
+ }
869
+ function isPhotoRef(v) {
870
+ if (!v || typeof v !== "object" || Array.isArray(v)) return false;
871
+ const r = v;
872
+ return typeof r.id === "string" && r.id.length > 0 && r.id.length <= MAX_PHOTO_ID_LENGTH && typeof r.name === "string" && r.name.length <= MAX_PHOTO_NAME_LENGTH && typeof r.type === "string" && typeof r.size === "number" && Number.isInteger(r.size) && r.size > 0;
873
+ }
874
+ function photoRefs(value) {
875
+ if (!Array.isArray(value)) return [];
876
+ const refs = [];
877
+ for (const r of value) if (isPhotoRef(r)) refs.push({ id: r.id, name: r.name, type: r.type, size: r.size });
878
+ return refs;
879
+ }
880
+ var plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
881
+ function photoFormatList(formats) {
882
+ const names = formats.map((f2) => FORMAT_NAMES[f2]);
883
+ return names.length > 1 ? `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}` : names[0] ?? "";
884
+ }
885
+ var photoTypeMessage = (field2) => `Use a ${photoFormatList(photoLimits(field2).formats)} photo.`;
886
+ var photoSizeMessage = (field2) => `Each photo must be ${photoLimits(field2).maxSizeMb} MB or smaller.`;
887
+ var photoCountMessage = (field2) => `Add at most ${plural2(photoLimits(field2).maxFiles, "photo")}.`;
888
+ var photoFieldType = {
889
+ type: PHOTO_TYPE,
890
+ label: "Photo",
891
+ category: "media",
892
+ value: { json: true },
893
+ operators: ["answered", "empty"],
894
+ a11y: { semantics: "group" },
895
+ isEmpty: (value) => value === void 0 || value === null || Array.isArray(value) && value.length === 0,
896
+ validate: (value, field2) => {
897
+ if (!Array.isArray(value) || !value.every(isPhotoRef)) return "Add a photo.";
898
+ const limits = photoLimits(field2);
899
+ if (value.length > limits.maxFiles) return photoCountMessage(field2);
900
+ const refs = value;
901
+ if (new Set(refs.map((r) => r.id)).size !== refs.length) return "Each photo can be added once.";
902
+ if (refs.some((r) => !limits.mimeTypes.includes(r.type.toLowerCase()))) return photoTypeMessage(field2);
903
+ if (refs.some((r) => r.size > limits.maxBytes)) return photoSizeMessage(field2);
904
+ return null;
905
+ },
906
+ normalize: (value) => Array.isArray(value) && value.every(isPhotoRef) ? photoRefs(value) : value,
907
+ summarize: (value) => photoRefs(value).map((r) => r.name || "Photo").join(", "),
908
+ samples: () => [[{ id: "sample-photo", name: "photo.jpg", type: "image/jpeg", size: 1024 }]]
909
+ };
910
+
836
911
  // src/fields.ts
837
912
  var REGISTRY = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/field-types");
838
913
  var globalScope = globalThis;
@@ -875,6 +950,20 @@ function summarizeValue(value, field2) {
875
950
  if (Array.isArray(value)) return value.join(", ");
876
951
  return String(value);
877
952
  }
953
+ var OTHER_MAX_LENGTH = 200;
954
+ function otherOption(field2) {
955
+ const other = field2.props?.other;
956
+ if (other === true) return { label: "Other" };
957
+ if (other && typeof other === "object" && !Array.isArray(other)) {
958
+ const label = typeof other.label === "string" && other.label.trim() ? other.label.trim() : "Other";
959
+ return { label };
960
+ }
961
+ return null;
962
+ }
963
+ function isOtherValue(field2, value) {
964
+ return otherOption(field2) !== null && typeof value === "string" && !choiceOptions(field2).some((o) => o.value === value);
965
+ }
966
+ var typedOther = (field2, value) => isOtherValue(field2, value) && value.trim() !== "" && value.length <= OTHER_MAX_LENGTH;
878
967
  function choiceOptions(field2) {
879
968
  const raw = field2.props?.options;
880
969
  if (!Array.isArray(raw)) return [];
@@ -882,6 +971,42 @@ function choiceOptions(field2) {
882
971
  (o) => o && typeof o === "object" && !Array.isArray(o) && typeof o.value === "string" ? [{ value: o.value, label: typeof o.label === "string" ? o.label : void 0 }] : []
883
972
  );
884
973
  }
974
+ function optionsSource(field2) {
975
+ const raw = field2.props?.optionsFrom;
976
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
977
+ return raw.source === "host" && typeof raw.key === "string" && raw.key ? { source: "host", key: raw.key } : void 0;
978
+ }
979
+ function hostList(hostOptions, key) {
980
+ if (!hostOptions || !Object.prototype.hasOwnProperty.call(hostOptions, key)) return [];
981
+ const list = hostOptions[key];
982
+ if (!Array.isArray(list)) return [];
983
+ const seen = /* @__PURE__ */ new Set();
984
+ const out = [];
985
+ for (const o of list) {
986
+ if (!o || typeof o !== "object" || Array.isArray(o)) continue;
987
+ const { value, label } = o;
988
+ if (typeof value !== "string" || !value || seen.has(value)) continue;
989
+ seen.add(value);
990
+ out.push(typeof label === "string" ? { value, label } : { value });
991
+ }
992
+ return out;
993
+ }
994
+ function withHostOptions(definition, hostOptions) {
995
+ let changed = false;
996
+ const blocks = definition.blocks.map((block) => {
997
+ if (block.kind !== "field") return block;
998
+ const from = optionsSource(block);
999
+ if (!from) return block;
1000
+ const own2 = choiceOptions(block);
1001
+ const taken = new Set(own2.map((o) => o.value));
1002
+ const added = hostList(hostOptions, from.key).filter((o) => !taken.has(o.value));
1003
+ if (!added.length) return block;
1004
+ changed = true;
1005
+ const options = [...own2, ...added].map((o) => o.label === void 0 ? { value: o.value } : { value: o.value, label: o.label });
1006
+ return { ...block, props: { ...block.props, options } };
1007
+ });
1008
+ return changed ? { ...definition, blocks } : definition;
1009
+ }
885
1010
  function optionLabel(field2, value) {
886
1011
  const option = choiceOptions(field2).find((o) => o.value === value);
887
1012
  return option?.label ?? value;
@@ -1000,7 +1125,7 @@ var builtins = [
1000
1125
  value: { json: true },
1001
1126
  operators: ["eq", "neq", "in", "answered", "empty"],
1002
1127
  a11y: { semantics: "group" },
1003
- validate: (value, field2) => typeof value === "string" && choiceOptions(field2).some((o) => o.value === value) ? null : "Choose one of the options.",
1128
+ validate: (value, field2) => typeof value === "string" && (choiceOptions(field2).some((o) => o.value === value) || typedOther(field2, value)) ? null : "Choose one of the options.",
1004
1129
  summarize: (value, field2) => optionLabel(field2, String(value))
1005
1130
  },
1006
1131
  {
@@ -1012,7 +1137,8 @@ var builtins = [
1012
1137
  a11y: { semantics: "group" },
1013
1138
  validate: (value, field2) => {
1014
1139
  const allowed = new Set(choiceOptions(field2).map((o) => o.value));
1015
- return Array.isArray(value) && value.length <= allowed.size && new Set(value).size === value.length && value.every((v) => typeof v === "string" && allowed.has(v)) ? null : "Choose from the options.";
1140
+ const others = Array.isArray(value) ? value.filter((v) => typeof v === "string" && !allowed.has(v)) : [];
1141
+ return Array.isArray(value) && new Set(value).size === value.length && value.every((v) => typeof v === "string") && (others.length === 0 || others.length === 1 && typedOther(field2, others[0])) ? null : "Choose from the options.";
1016
1142
  },
1017
1143
  summarize: (value, field2) => Array.isArray(value) ? value.map((v) => optionLabel(field2, String(v))).join(", ") : String(value)
1018
1144
  },
@@ -1033,7 +1159,7 @@ var builtins = [
1033
1159
  value: { json: true },
1034
1160
  operators: ["eq", "neq", "in", "answered", "empty"],
1035
1161
  a11y: { semantics: "control" },
1036
- validate: (value, field2) => typeof value === "string" && choiceOptions(field2).some((o) => o.value === value) ? null : "Choose one of the options.",
1162
+ validate: (value, field2) => typeof value === "string" && (choiceOptions(field2).some((o) => o.value === value) || typedOther(field2, value)) ? null : "Choose one of the options.",
1037
1163
  summarize: (value, field2) => optionLabel(field2, String(value))
1038
1164
  },
1039
1165
  {
@@ -1143,7 +1269,7 @@ function dateLimits(field2) {
1143
1269
  if (typeof max === "string" && isIsoDate(max)) out.max = max;
1144
1270
  return out;
1145
1271
  }
1146
- for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType]) registerFieldType(manifest);
1272
+ for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType, photoFieldType]) registerFieldType(manifest);
1147
1273
 
1148
1274
  // src/logic.ts
1149
1275
  function own(record, key) {
@@ -1443,6 +1569,22 @@ function parseForm(input) {
1443
1569
  }
1444
1570
  }
1445
1571
  if (type === "consent") optionalText(props2.text, `${path}.text`);
1572
+ if (type === PHOTO_TYPE) {
1573
+ if (props2.maxFiles !== void 0 && !(isInt(props2.maxFiles) && props2.maxFiles >= 1 && props2.maxFiles <= MAX_PHOTO_FILES)) {
1574
+ issue(`${path}.maxFiles`, `Must be a whole number from 1 to ${MAX_PHOTO_FILES}.`);
1575
+ }
1576
+ if (props2.maxSizeMb !== void 0 && !(isInt(props2.maxSizeMb) && props2.maxSizeMb >= 1 && props2.maxSizeMb <= MAX_PHOTO_SIZE_MB)) {
1577
+ issue(`${path}.maxSizeMb`, `Must be a whole number from 1 to ${MAX_PHOTO_SIZE_MB}.`);
1578
+ }
1579
+ if (props2.formats !== void 0) {
1580
+ const formats = props2.formats;
1581
+ if (!Array.isArray(formats) || formats.length === 0 || !formats.every(isPhotoFormat)) {
1582
+ issue(`${path}.formats`, "Must be a list of one or more of jpeg, png, heic and webp.");
1583
+ } else if (new Set(formats).size !== formats.length) {
1584
+ issue(`${path}.formats`, "Name each format once.");
1585
+ }
1586
+ }
1587
+ }
1446
1588
  };
1447
1589
  if (!isObject2(input)) {
1448
1590
  issue("", "A form definition must be an object.");
@@ -1463,6 +1605,7 @@ function parseForm(input) {
1463
1605
  else {
1464
1606
  optionalBoolean(input.settings.anonymous, "settings.anonymous");
1465
1607
  optionalBoolean(input.settings.clearHiddenValues, "settings.clearHiddenValues");
1608
+ optionalBoolean(input.settings.saveProgress, "settings.saveProgress");
1466
1609
  }
1467
1610
  }
1468
1611
  if (input.meta !== void 0 && (!isObject2(input.meta) || !isJson(input.meta))) {
@@ -1503,6 +1646,14 @@ function parseForm(input) {
1503
1646
  issue(`${path}.type`, `Unknown field type "${String(block.type)}".`);
1504
1647
  }
1505
1648
  if (block.props !== void 0) {
1649
+ const from = isObject2(block.props) ? block.props.optionsFrom : void 0;
1650
+ if (from !== void 0) {
1651
+ if (!isObject2(from) || from.source !== "host" || typeof from.key !== "string" || !from.key) {
1652
+ issue(`${path}.props.optionsFrom`, 'Must be { source: "host", key } with a non-empty key.');
1653
+ } else if (typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
1654
+ warnings.push({ path: `${path}.props.optionsFrom`, message: "Only single choice, multiple choice and dropdown questions take options from the app." });
1655
+ }
1656
+ }
1506
1657
  if (!isObject2(block.props) || !isJson(block.props)) issue(`${path}.props`, "Must be a JSON object.");
1507
1658
  else if (block.props.options !== void 0) {
1508
1659
  const options = block.props.options;
@@ -1513,10 +1664,20 @@ function parseForm(input) {
1513
1664
  if (!isObject2(o) || typeof o.value !== "string") issue(at, "Each option needs a string value.");
1514
1665
  else optionalText(o.label, `${at}.label`);
1515
1666
  });
1516
- choices.set(
1517
- block.id,
1518
- new Set(options.flatMap((o) => isObject2(o) && typeof o.value === "string" ? [o.value] : []))
1519
- );
1667
+ if (from === void 0) {
1668
+ choices.set(
1669
+ block.id,
1670
+ new Set(options.flatMap((o) => isObject2(o) && typeof o.value === "string" ? [o.value] : []))
1671
+ );
1672
+ }
1673
+ }
1674
+ }
1675
+ const other = isObject2(block.props) ? block.props.other : void 0;
1676
+ if (other !== void 0) {
1677
+ if (other !== true && other !== false && !(isObject2(other) && (other.label === void 0 || typeof other.label === "string"))) {
1678
+ issue(`${path}.props.other`, "Must be true, false or { label }.");
1679
+ } else if (other !== false && typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
1680
+ warnings.push({ path: `${path}.props.other`, message: 'Only single choice, multiple choice and dropdown questions take an "Other" answer.' });
1520
1681
  }
1521
1682
  }
1522
1683
  const { max, maxLength: maxLength2 } = isObject2(block.props) ? block.props : {};
@@ -1591,6 +1752,8 @@ function parseForm(input) {
1591
1752
  } else if (ref.column !== void 0) issue(path, `"${ref.key}" is not a table, so it has no column "${ref.column}".`);
1592
1753
  else if (targetType === "media-notes" || targetType === "text-highlights") {
1593
1754
  warnings.push({ path, message: `"${ref.key}" holds notes, not numbers; the formula reads it as blank.` });
1755
+ } else if (targetType === PHOTO_TYPE) {
1756
+ warnings.push({ path, message: `"${ref.key}" holds photos, not numbers; the formula reads it as blank.` });
1594
1757
  }
1595
1758
  }
1596
1759
  });
@@ -1810,9 +1973,15 @@ function field(type, key, options, props2) {
1810
1973
  return block;
1811
1974
  }
1812
1975
  var props = (all) => Object.fromEntries(Object.entries(all).filter((e) => e[1] !== void 0));
1813
- var choiceProps = (options) => ({
1814
- options: options.map((o) => o.label ? { value: o.value, label: o.label } : { value: o.value })
1815
- });
1976
+ var choiceProps = (o) => {
1977
+ const out = {};
1978
+ if (o.options || !o.optionsFrom) {
1979
+ out.options = (o.options ?? []).map((x) => x.label ? { value: x.value, label: x.label } : { value: x.value });
1980
+ }
1981
+ if (o.optionsFrom) out.optionsFrom = { source: o.optionsFrom.source, key: o.optionsFrom.key };
1982
+ if (o.other) out.other = o.other === true ? true : props({ label: o.other.label });
1983
+ return out;
1984
+ };
1816
1985
  var hiddenSpec = (type, o) => ({
1817
1986
  type,
1818
1987
  source: o.source ?? "url",
@@ -1836,11 +2005,11 @@ var f = {
1836
2005
  ),
1837
2006
  /** A 0-to-10 Net Promoter Score. The end labels default to "Not likely" and "Very likely" in the renderer. */
1838
2007
  nps: (key, o) => field("nps", key, o, props({ lowLabel: o.lowLabel || void 0, highLabel: o.highLabel || void 0 })),
1839
- singleChoice: (key, o) => field("single-choice", key, o, choiceProps(o.options)),
1840
- multiChoice: (key, o) => field("multi-choice", key, o, choiceProps(o.options)),
2008
+ singleChoice: (key, o) => field("single-choice", key, o, choiceProps(o)),
2009
+ multiChoice: (key, o) => field("multi-choice", key, o, choiceProps(o)),
1841
2010
  yesNo: (key, o) => field("yes-no", key, o),
1842
2011
  /** One option from a compact menu: for a long list (a country, a class), where single choice would be a wall of buttons. */
1843
- dropdown: (key, o) => field("dropdown", key, o, choiceProps(o.options)),
2012
+ dropdown: (key, o) => field("dropdown", key, o, choiceProps(o)),
1844
2013
  /** A number, optionally between `min` and `max` and on a `step` (from `min`, else 0): `step: 1` for whole numbers. */
1845
2014
  number: (key, o) => field("number", key, o, props({ min: o.min, max: o.max, step: o.step })),
1846
2015
  /** A calendar day, stored as `YYYY-MM-DD`, optionally between `min` and `max` (also `YYYY-MM-DD`). */
@@ -1892,6 +2061,17 @@ var f = {
1892
2061
  o,
1893
2062
  props({ text: o.text, maxHighlights: o.maxHighlights })
1894
2063
  ),
2064
+ /**
2065
+ * Up to a few photos (a picture of broken equipment). The answer holds the
2066
+ * references the host's upload returned, never the bytes; `formats` from
2067
+ * jpeg, png, heic and webp (default all four).
2068
+ */
2069
+ photo: (key, o) => field(
2070
+ "photo",
2071
+ key,
2072
+ o,
2073
+ props({ maxFiles: o.maxFiles, maxSizeMb: o.maxSizeMb, formats: o.formats ? [...o.formats] : void 0 })
2074
+ ),
1895
2075
  /** Any registered field type, including plugins. */
1896
2076
  custom: (type, key, o) => field(type, key, o, o.props),
1897
2077
  heading: (id, text) => ({ kind: "content", id, type: "heading", text }),
@@ -2075,7 +2255,8 @@ function readsAny(node, ids) {
2075
2255
  function createForm(definition, options = {}) {
2076
2256
  return build(definition, options).engine;
2077
2257
  }
2078
- function build(definition, options, replaying = false, budget) {
2258
+ function build(given, options, replaying = false, budget) {
2259
+ const definition = withHostOptions(given, options.hostOptions);
2079
2260
  const fields = fieldMap(definition);
2080
2261
  const byKey = new Map([...fields.values()].map((f2) => [f2.key, f2]));
2081
2262
  const blocks = new Map(definition.blocks.map((b) => [b.id, b]));
@@ -2121,6 +2302,7 @@ function build(definition, options, replaying = false, budget) {
2121
2302
  path: first.path,
2122
2303
  current: first.path[0] ?? null,
2123
2304
  history: [],
2305
+ furthest: first.path[0] ?? null,
2124
2306
  errors: {},
2125
2307
  status: "in_progress",
2126
2308
  ending: null,
@@ -2221,7 +2403,8 @@ function build(definition, options, replaying = false, budget) {
2221
2403
  emit({ ...state, events });
2222
2404
  return true;
2223
2405
  }
2224
- emit({ ...state, current: target, history: [...state.history, state.current], events });
2406
+ const furthest = state.furthest && state.path.indexOf(state.furthest) >= state.path.indexOf(target) ? state.furthest : target;
2407
+ emit({ ...state, current: target, history: [...state.history, state.current], furthest, events });
2225
2408
  return true;
2226
2409
  },
2227
2410
  back() {
@@ -2266,6 +2449,15 @@ function build(definition, options, replaying = false, budget) {
2266
2449
  settleNow();
2267
2450
  batch = wasBatch;
2268
2451
  }
2452
+ const resumeAt = options.resumeAt;
2453
+ if (resumeAt && !replaying && state.path.includes(resumeAt)) {
2454
+ for (let i = 0; i < state.path.length && state.current !== resumeAt; i++) {
2455
+ if (!engine.next()) {
2456
+ state = { ...state, errors: {} };
2457
+ break;
2458
+ }
2459
+ }
2460
+ }
2269
2461
  const apply = (event) => {
2270
2462
  if (event.type === "answer") {
2271
2463
  if (fields.has(event.field)) {
@@ -2305,6 +2497,33 @@ function replayWithin(definition, events, options, maxWork) {
2305
2497
  return engine;
2306
2498
  }
2307
2499
 
2500
+ // src/progress.ts
2501
+ function furthestReached(state, definition) {
2502
+ const at = (id) => id ? state.path.indexOf(id) : -1;
2503
+ let furthest = Math.max(at(state.current), at(state.furthest));
2504
+ for (const id of state.history) furthest = Math.max(furthest, at(id));
2505
+ const fields = fieldMap(definition);
2506
+ for (let i = state.path.length - 1; i > furthest; i--) {
2507
+ const field2 = fields.get(state.path[i]);
2508
+ if (field2 && !isEmptyValue(own(state.values, field2.id), field2)) {
2509
+ furthest = i;
2510
+ break;
2511
+ }
2512
+ }
2513
+ return furthest >= 0 ? state.path[furthest] : null;
2514
+ }
2515
+ function formProgress(engine) {
2516
+ const state = engine.getState();
2517
+ const furthest = furthestReached(state, engine.definition);
2518
+ const index = furthest ? state.path.indexOf(furthest) : -1;
2519
+ const fields = fieldMap(engine.definition);
2520
+ let fieldId = null;
2521
+ for (let i = index; i >= 0 && fieldId === null; i--) {
2522
+ if (fields.has(state.path[i])) fieldId = state.path[i];
2523
+ }
2524
+ return { step: index + 1, total: state.path.length, fieldId };
2525
+ }
2526
+
2308
2527
  // src/explain.ts
2309
2528
  function explain(source, blockId, options = {}) {
2310
2529
  const input = "getState" in source ? { definition: source.definition, values: source.getState().values, hidden: source.getState().hidden } : source;
@@ -2441,6 +2660,6 @@ function describeCondition(condition, def, fields = fieldMap(def)) {
2441
2660
  return `${left} ${op} ${right}`;
2442
2661
  }
2443
2662
 
2444
- export { CALCULATED_TYPE, COMPUTED_FIELD_TYPES, FORMULA_FUNCTIONS, HIGHLIGHT_NOTE_MESSAGE, MAX_CALCULATED_DECIMALS, MAX_FORMULA_ITEMS, MAX_FORMULA_LENGTH, MAX_FORM_FORMULA_LENGTH, MAX_HIDDEN_STRING_LENGTH, MAX_HIGHLIGHT_NOTE_LENGTH, MAX_HIGHLIGHT_QUOTE_LENGTH, MAX_SETTLE_ITEMS, MAX_TABLE_CELL_LENGTH, MAX_TABLE_COLUMNS, MAX_TIMESTAMP_SECONDS, REQUIRED_MESSAGE, STRUCTURED_FIELD_TYPES, calculatedProps, calculatedValue, canonicalJson, choiceOptions, coerceHidden, coerceHiddenValue, computePath, createForm, dateLimits, defineForm, describeCondition, evaluate, evaluateFormula, explain, f, fingerprint, formatCalculated, formatTimestamp, formulaInput, formulaNumber, formulaRefs, getFieldType, isEmptyValue, isFormEvent, isIsoDate, isJson, isSensitive, listFieldTypes, listLimits, normalizeValue, numberLimits, parseForm, parseFormula, parseTimestamp, ratingDisplay, recall, registerFieldType, renameFormulaKey, replay, ruleVisible, scaleLabels, steps, summarizeValue, tableColumns, tableLimits, validateField, when };
2663
+ export { CALCULATED_TYPE, COMPUTED_FIELD_TYPES, DEFAULT_PHOTO_FILES, DEFAULT_PHOTO_SIZE_MB, FORMULA_FUNCTIONS, HIGHLIGHT_NOTE_MESSAGE, MAX_CALCULATED_DECIMALS, MAX_FORMULA_ITEMS, MAX_FORMULA_LENGTH, MAX_FORM_FORMULA_LENGTH, MAX_HIDDEN_STRING_LENGTH, MAX_HIGHLIGHT_NOTE_LENGTH, MAX_HIGHLIGHT_QUOTE_LENGTH, MAX_PHOTO_FILES, MAX_PHOTO_ID_LENGTH, MAX_PHOTO_NAME_LENGTH, MAX_PHOTO_SIZE_MB, MAX_SETTLE_ITEMS, MAX_TABLE_CELL_LENGTH, MAX_TABLE_COLUMNS, MAX_TIMESTAMP_SECONDS, OTHER_MAX_LENGTH, PHOTO_FORMATS, PHOTO_TYPE, REQUIRED_MESSAGE, STRUCTURED_FIELD_TYPES, UPLOAD_FIELD_TYPES, calculatedProps, calculatedValue, canonicalJson, choiceOptions, coerceHidden, coerceHiddenValue, computePath, createForm, dateLimits, defineForm, describeCondition, evaluate, evaluateFormula, explain, f, fingerprint, formProgress, formatCalculated, formatTimestamp, formulaInput, formulaNumber, formulaRefs, furthestReached, getFieldType, isEmptyValue, isFormEvent, isIsoDate, isJson, isOtherValue, isPhotoFormat, isPhotoRef, isSensitive, listFieldTypes, listLimits, normalizeValue, numberLimits, optionsSource, otherOption, parseForm, parseFormula, parseTimestamp, photoCountMessage, photoFormatList, photoLimits, photoRefs, photoSizeMessage, photoTypeMessage, ratingDisplay, recall, registerFieldType, renameFormulaKey, replay, ruleVisible, scaleLabels, steps, summarizeValue, tableColumns, tableLimits, validateField, when, withHostOptions };
2445
2664
  //# sourceMappingURL=index.js.map
2446
2665
  //# sourceMappingURL=index.js.map