@marktiderman/genesis-forms 0.4.2 → 0.6.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/README.md +195 -0
- package/dist/destinations.cjs +147 -7
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.d.cts +1 -1
- package/dist/destinations.d.ts +1 -1
- package/dist/destinations.js +147 -7
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-BkNuzlna.d.cts → engine-CpfOz8FN.d.ts} +33 -3
- package/dist/{engine-Dz9J5ej2.d.ts → engine-DC4YTPdr.d.cts} +33 -3
- package/dist/index.cjs +734 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -75
- package/dist/index.d.ts +142 -75
- package/dist/index.js +687 -70
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +721 -26
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +201 -4
- package/dist/server.d.ts +201 -4
- package/dist/server.js +702 -27
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +494 -26
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +4 -4
- package/dist/testing.d.ts +4 -4
- package/dist/testing.js +494 -26
- package/dist/testing.js.map +1 -1
- package/dist/{types-BBKBQ8sp.d.cts → types-oAckGR_Z.d.cts} +31 -1
- package/dist/{types-BBKBQ8sp.d.ts → types-oAckGR_Z.d.ts} +31 -1
- package/dist/validate-B5WKHbst.d.cts +322 -0
- package/dist/validate-GpTXPwyl.d.ts +322 -0
- package/package.json +1 -1
- package/dist/validate-BXXqi0fq.d.cts +0 -30
- package/dist/validate-BjNwF1UE.d.ts +0 -30
package/dist/index.cjs
CHANGED
|
@@ -835,6 +835,81 @@ var structuredFieldTypes = [
|
|
|
835
835
|
}
|
|
836
836
|
];
|
|
837
837
|
|
|
838
|
+
// src/photo.ts
|
|
839
|
+
var PHOTO_TYPE = "photo";
|
|
840
|
+
var UPLOAD_FIELD_TYPES = [PHOTO_TYPE];
|
|
841
|
+
var PHOTO_FORMATS = {
|
|
842
|
+
jpeg: ["image/jpeg"],
|
|
843
|
+
png: ["image/png"],
|
|
844
|
+
heic: ["image/heic", "image/heif"],
|
|
845
|
+
webp: ["image/webp"]
|
|
846
|
+
};
|
|
847
|
+
var FORMAT_NAMES = { jpeg: "JPEG", png: "PNG", heic: "HEIC", webp: "WebP" };
|
|
848
|
+
var ALL_FORMATS = Object.keys(PHOTO_FORMATS);
|
|
849
|
+
var DEFAULT_PHOTO_FILES = 3;
|
|
850
|
+
var MAX_PHOTO_FILES = 5;
|
|
851
|
+
var DEFAULT_PHOTO_SIZE_MB = 10;
|
|
852
|
+
var MAX_PHOTO_SIZE_MB = 25;
|
|
853
|
+
var MAX_PHOTO_ID_LENGTH = 200;
|
|
854
|
+
var MAX_PHOTO_NAME_LENGTH = 255;
|
|
855
|
+
var isPhotoFormat = (v) => typeof v === "string" && Object.prototype.hasOwnProperty.call(PHOTO_FORMATS, v);
|
|
856
|
+
var wholeIn = (v, min, max) => typeof v === "number" && Number.isInteger(v) && v >= min && v <= max;
|
|
857
|
+
function photoLimits(field2) {
|
|
858
|
+
const maxFiles = wholeIn(field2.props?.maxFiles, 1, MAX_PHOTO_FILES) ? field2.props.maxFiles : DEFAULT_PHOTO_FILES;
|
|
859
|
+
const maxSizeMb = wholeIn(field2.props?.maxSizeMb, 1, MAX_PHOTO_SIZE_MB) ? field2.props.maxSizeMb : DEFAULT_PHOTO_SIZE_MB;
|
|
860
|
+
const raw = field2.props?.formats;
|
|
861
|
+
const picked = Array.isArray(raw) ? ALL_FORMATS.filter((f2) => raw.includes(f2)) : [];
|
|
862
|
+
const formats = picked.length ? picked : [...ALL_FORMATS];
|
|
863
|
+
return {
|
|
864
|
+
maxFiles,
|
|
865
|
+
maxSizeMb,
|
|
866
|
+
maxBytes: maxSizeMb * 1024 * 1024,
|
|
867
|
+
formats,
|
|
868
|
+
mimeTypes: formats.flatMap((f2) => PHOTO_FORMATS[f2])
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
function isPhotoRef(v) {
|
|
872
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
873
|
+
const r = v;
|
|
874
|
+
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;
|
|
875
|
+
}
|
|
876
|
+
function photoRefs(value) {
|
|
877
|
+
if (!Array.isArray(value)) return [];
|
|
878
|
+
const refs = [];
|
|
879
|
+
for (const r of value) if (isPhotoRef(r)) refs.push({ id: r.id, name: r.name, type: r.type, size: r.size });
|
|
880
|
+
return refs;
|
|
881
|
+
}
|
|
882
|
+
var plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
883
|
+
function photoFormatList(formats) {
|
|
884
|
+
const names = formats.map((f2) => FORMAT_NAMES[f2]);
|
|
885
|
+
return names.length > 1 ? `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}` : names[0] ?? "";
|
|
886
|
+
}
|
|
887
|
+
var photoTypeMessage = (field2) => `Use a ${photoFormatList(photoLimits(field2).formats)} photo.`;
|
|
888
|
+
var photoSizeMessage = (field2) => `Each photo must be ${photoLimits(field2).maxSizeMb} MB or smaller.`;
|
|
889
|
+
var photoCountMessage = (field2) => `Add at most ${plural2(photoLimits(field2).maxFiles, "photo")}.`;
|
|
890
|
+
var photoFieldType = {
|
|
891
|
+
type: PHOTO_TYPE,
|
|
892
|
+
label: "Photo",
|
|
893
|
+
category: "media",
|
|
894
|
+
value: { json: true },
|
|
895
|
+
operators: ["answered", "empty"],
|
|
896
|
+
a11y: { semantics: "group" },
|
|
897
|
+
isEmpty: (value) => value === void 0 || value === null || Array.isArray(value) && value.length === 0,
|
|
898
|
+
validate: (value, field2) => {
|
|
899
|
+
if (!Array.isArray(value) || !value.every(isPhotoRef)) return "Add a photo.";
|
|
900
|
+
const limits = photoLimits(field2);
|
|
901
|
+
if (value.length > limits.maxFiles) return photoCountMessage(field2);
|
|
902
|
+
const refs = value;
|
|
903
|
+
if (new Set(refs.map((r) => r.id)).size !== refs.length) return "Each photo can be added once.";
|
|
904
|
+
if (refs.some((r) => !limits.mimeTypes.includes(r.type.toLowerCase()))) return photoTypeMessage(field2);
|
|
905
|
+
if (refs.some((r) => r.size > limits.maxBytes)) return photoSizeMessage(field2);
|
|
906
|
+
return null;
|
|
907
|
+
},
|
|
908
|
+
normalize: (value) => Array.isArray(value) && value.every(isPhotoRef) ? photoRefs(value) : value,
|
|
909
|
+
summarize: (value) => photoRefs(value).map((r) => r.name || "Photo").join(", "),
|
|
910
|
+
samples: () => [[{ id: "sample-photo", name: "photo.jpg", type: "image/jpeg", size: 1024 }]]
|
|
911
|
+
};
|
|
912
|
+
|
|
838
913
|
// src/fields.ts
|
|
839
914
|
var REGISTRY = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/field-types");
|
|
840
915
|
var globalScope = globalThis;
|
|
@@ -877,12 +952,127 @@ function summarizeValue(value, field2) {
|
|
|
877
952
|
if (Array.isArray(value)) return value.join(", ");
|
|
878
953
|
return String(value);
|
|
879
954
|
}
|
|
955
|
+
var OPTION_LIMITS = {
|
|
956
|
+
/** Characters in a description. */
|
|
957
|
+
description: 500,
|
|
958
|
+
/** Characters in an image URL. */
|
|
959
|
+
image: 2048,
|
|
960
|
+
/** Attributes on one option. */
|
|
961
|
+
attributes: 20,
|
|
962
|
+
/** Characters in an attribute's name. */
|
|
963
|
+
attributeName: 64,
|
|
964
|
+
/** Characters in one attribute value. */
|
|
965
|
+
attributeValue: 200,
|
|
966
|
+
/** Values in one attribute's list. */
|
|
967
|
+
attributeItems: 50
|
|
968
|
+
};
|
|
969
|
+
function isHttpsUrl(url) {
|
|
970
|
+
return typeof url === "string" && url.length <= OPTION_LIMITS.image && /^https:\/\/[^\s/?#@\\]+(?:[/?#][^\s]*)?$/i.test(url) && // No control characters anywhere (a line break would end a CSV row or an HTML attribute).
|
|
971
|
+
![...url].some((c) => c.charCodeAt(0) < 32 || c.charCodeAt(0) === 127);
|
|
972
|
+
}
|
|
973
|
+
function attributeName(name) {
|
|
974
|
+
return name.trim() !== "" && name.length <= OPTION_LIMITS.attributeName && !(name in Object.prototype);
|
|
975
|
+
}
|
|
976
|
+
function readAttributes(raw) {
|
|
977
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
978
|
+
const out = {};
|
|
979
|
+
let count = 0;
|
|
980
|
+
for (const name of Object.keys(raw)) {
|
|
981
|
+
if (count >= OPTION_LIMITS.attributes) break;
|
|
982
|
+
if (!attributeName(name)) continue;
|
|
983
|
+
const v = raw[name];
|
|
984
|
+
const fits = (x) => typeof x === "string" && x.length <= OPTION_LIMITS.attributeValue;
|
|
985
|
+
if (fits(v)) out[name] = v;
|
|
986
|
+
else if (Array.isArray(v) && v.length <= OPTION_LIMITS.attributeItems && v.every(fits)) out[name] = [...v];
|
|
987
|
+
else continue;
|
|
988
|
+
count++;
|
|
989
|
+
}
|
|
990
|
+
return count ? out : void 0;
|
|
991
|
+
}
|
|
992
|
+
function readOption(raw) {
|
|
993
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
994
|
+
const o = raw;
|
|
995
|
+
if (typeof o.value !== "string") return null;
|
|
996
|
+
const out = { value: o.value };
|
|
997
|
+
if (typeof o.label === "string") out.label = o.label;
|
|
998
|
+
if (typeof o.description === "string" && o.description.trim() && o.description.length <= OPTION_LIMITS.description) {
|
|
999
|
+
out.description = o.description;
|
|
1000
|
+
}
|
|
1001
|
+
if (isHttpsUrl(o.image)) out.image = o.image;
|
|
1002
|
+
const attributes = readAttributes(o.attributes);
|
|
1003
|
+
if (attributes) out.attributes = attributes;
|
|
1004
|
+
return out;
|
|
1005
|
+
}
|
|
1006
|
+
function optionJson(option) {
|
|
1007
|
+
const out = { value: option.value };
|
|
1008
|
+
if (option.label !== void 0) out.label = option.label;
|
|
1009
|
+
if (option.description !== void 0) out.description = option.description;
|
|
1010
|
+
if (option.image !== void 0) out.image = option.image;
|
|
1011
|
+
if (option.attributes !== void 0) out.attributes = { ...option.attributes };
|
|
1012
|
+
return out;
|
|
1013
|
+
}
|
|
1014
|
+
var OTHER_MAX_LENGTH = 200;
|
|
1015
|
+
function otherOption(field2) {
|
|
1016
|
+
const other = field2.props?.other;
|
|
1017
|
+
if (other === true) return { label: "Other" };
|
|
1018
|
+
if (other && typeof other === "object" && !Array.isArray(other)) {
|
|
1019
|
+
const label = typeof other.label === "string" && other.label.trim() ? other.label.trim() : "Other";
|
|
1020
|
+
return { label };
|
|
1021
|
+
}
|
|
1022
|
+
return null;
|
|
1023
|
+
}
|
|
1024
|
+
function isOtherValue(field2, value) {
|
|
1025
|
+
return otherOption(field2) !== null && typeof value === "string" && !choiceOptions(field2).some((o) => o.value === value);
|
|
1026
|
+
}
|
|
1027
|
+
var typedOther = (field2, value) => isOtherValue(field2, value) && value.trim() !== "" && value.length <= OTHER_MAX_LENGTH;
|
|
880
1028
|
function choiceOptions(field2) {
|
|
881
1029
|
const raw = field2.props?.options;
|
|
882
1030
|
if (!Array.isArray(raw)) return [];
|
|
883
|
-
return raw.flatMap(
|
|
884
|
-
|
|
885
|
-
|
|
1031
|
+
return raw.flatMap((o) => {
|
|
1032
|
+
const option = readOption(o);
|
|
1033
|
+
return option ? [option] : [];
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
function optionsSource(field2) {
|
|
1037
|
+
const raw = field2.props?.optionsFrom;
|
|
1038
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
1039
|
+
if (raw.source !== "host" || typeof raw.key !== "string" || !raw.key) return void 0;
|
|
1040
|
+
const out = { source: "host", key: raw.key };
|
|
1041
|
+
const filter = raw.filter;
|
|
1042
|
+
if (filter && typeof filter === "object" && !Array.isArray(filter) && typeof filter.attribute === "string" && filter.attribute && typeof filter.field === "string" && filter.field) {
|
|
1043
|
+
out.filter = { attribute: filter.attribute, field: filter.field };
|
|
1044
|
+
}
|
|
1045
|
+
return out;
|
|
1046
|
+
}
|
|
1047
|
+
function hostList(hostOptions, key) {
|
|
1048
|
+
if (!hostOptions || !Object.prototype.hasOwnProperty.call(hostOptions, key)) return [];
|
|
1049
|
+
const list = hostOptions[key];
|
|
1050
|
+
if (!Array.isArray(list)) return [];
|
|
1051
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1052
|
+
const out = [];
|
|
1053
|
+
for (const o of list) {
|
|
1054
|
+
const option = readOption(o);
|
|
1055
|
+
if (!option || !option.value || seen.has(option.value)) continue;
|
|
1056
|
+
seen.add(option.value);
|
|
1057
|
+
out.push(option);
|
|
1058
|
+
}
|
|
1059
|
+
return out;
|
|
1060
|
+
}
|
|
1061
|
+
function withHostOptions(definition, hostOptions) {
|
|
1062
|
+
let changed = false;
|
|
1063
|
+
const blocks = definition.blocks.map((block) => {
|
|
1064
|
+
if (block.kind !== "field") return block;
|
|
1065
|
+
const from = optionsSource(block);
|
|
1066
|
+
if (!from) return block;
|
|
1067
|
+
const own2 = choiceOptions(block);
|
|
1068
|
+
const taken = new Set(own2.map((o) => o.value));
|
|
1069
|
+
const added = hostList(hostOptions, from.key).filter((o) => !taken.has(o.value));
|
|
1070
|
+
if (!added.length) return block;
|
|
1071
|
+
changed = true;
|
|
1072
|
+
const options = [...own2, ...added].map(optionJson);
|
|
1073
|
+
return { ...block, props: { ...block.props, options } };
|
|
1074
|
+
});
|
|
1075
|
+
return changed ? { ...definition, blocks } : definition;
|
|
886
1076
|
}
|
|
887
1077
|
function optionLabel(field2, value) {
|
|
888
1078
|
const option = choiceOptions(field2).find((o) => o.value === value);
|
|
@@ -1002,7 +1192,7 @@ var builtins = [
|
|
|
1002
1192
|
value: { json: true },
|
|
1003
1193
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
1004
1194
|
a11y: { semantics: "group" },
|
|
1005
|
-
validate: (value, field2) => typeof value === "string" && choiceOptions(field2).some((o) => o.value === value) ? null : "Choose one of the options.",
|
|
1195
|
+
validate: (value, field2) => typeof value === "string" && (choiceOptions(field2).some((o) => o.value === value) || typedOther(field2, value)) ? null : "Choose one of the options.",
|
|
1006
1196
|
summarize: (value, field2) => optionLabel(field2, String(value))
|
|
1007
1197
|
},
|
|
1008
1198
|
{
|
|
@@ -1014,7 +1204,8 @@ var builtins = [
|
|
|
1014
1204
|
a11y: { semantics: "group" },
|
|
1015
1205
|
validate: (value, field2) => {
|
|
1016
1206
|
const allowed = new Set(choiceOptions(field2).map((o) => o.value));
|
|
1017
|
-
|
|
1207
|
+
const others = Array.isArray(value) ? value.filter((v) => typeof v === "string" && !allowed.has(v)) : [];
|
|
1208
|
+
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.";
|
|
1018
1209
|
},
|
|
1019
1210
|
summarize: (value, field2) => Array.isArray(value) ? value.map((v) => optionLabel(field2, String(v))).join(", ") : String(value)
|
|
1020
1211
|
},
|
|
@@ -1035,7 +1226,7 @@ var builtins = [
|
|
|
1035
1226
|
value: { json: true },
|
|
1036
1227
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
1037
1228
|
a11y: { semantics: "control" },
|
|
1038
|
-
validate: (value, field2) => typeof value === "string" && choiceOptions(field2).some((o) => o.value === value) ? null : "Choose one of the options.",
|
|
1229
|
+
validate: (value, field2) => typeof value === "string" && (choiceOptions(field2).some((o) => o.value === value) || typedOther(field2, value)) ? null : "Choose one of the options.",
|
|
1039
1230
|
summarize: (value, field2) => optionLabel(field2, String(value))
|
|
1040
1231
|
},
|
|
1041
1232
|
{
|
|
@@ -1145,7 +1336,48 @@ function dateLimits(field2) {
|
|
|
1145
1336
|
if (typeof max === "string" && isIsoDate(max)) out.max = max;
|
|
1146
1337
|
return out;
|
|
1147
1338
|
}
|
|
1148
|
-
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType]) registerFieldType(manifest);
|
|
1339
|
+
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType, photoFieldType]) registerFieldType(manifest);
|
|
1340
|
+
|
|
1341
|
+
// src/hidden.ts
|
|
1342
|
+
var MAX_HIDDEN_STRING_LENGTH = 1e3;
|
|
1343
|
+
var DECIMAL = /^-?\d+(\.\d+)?$/;
|
|
1344
|
+
function coerceHiddenValue(value, type) {
|
|
1345
|
+
if (typeof value === "string" && value.length > MAX_HIDDEN_STRING_LENGTH) return void 0;
|
|
1346
|
+
if (type === "string") {
|
|
1347
|
+
if (typeof value === "string") return value;
|
|
1348
|
+
if (typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
1349
|
+
return void 0;
|
|
1350
|
+
}
|
|
1351
|
+
if (type === "number") {
|
|
1352
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
1353
|
+
if (typeof value !== "string" || !DECIMAL.test(value)) return void 0;
|
|
1354
|
+
const n = Number(value);
|
|
1355
|
+
return Number.isFinite(n) ? n : void 0;
|
|
1356
|
+
}
|
|
1357
|
+
if (typeof value === "boolean") return value;
|
|
1358
|
+
if (value === "true") return true;
|
|
1359
|
+
if (value === "false") return false;
|
|
1360
|
+
return void 0;
|
|
1361
|
+
}
|
|
1362
|
+
function coerceHidden(definition, raw, options = {}) {
|
|
1363
|
+
const declared = new Map((definition.hidden ?? []).map((h) => [h.name, h]));
|
|
1364
|
+
const values = {};
|
|
1365
|
+
const ignored = [];
|
|
1366
|
+
if (!isPlainObject(raw)) return { values, ignored };
|
|
1367
|
+
for (const name of Object.keys(raw)) {
|
|
1368
|
+
const value = raw[name];
|
|
1369
|
+
if (value === void 0) continue;
|
|
1370
|
+
const spec = declared.get(name);
|
|
1371
|
+
if (!spec || options.sources && !options.sources.includes(spec.source)) {
|
|
1372
|
+
ignored.push(name);
|
|
1373
|
+
continue;
|
|
1374
|
+
}
|
|
1375
|
+
const coerced = coerceHiddenValue(value, spec.type);
|
|
1376
|
+
if (coerced === void 0) ignored.push(name);
|
|
1377
|
+
else Object.defineProperty(values, name, { value: coerced, enumerable: true, writable: true, configurable: true });
|
|
1378
|
+
}
|
|
1379
|
+
return { values, ignored };
|
|
1380
|
+
}
|
|
1149
1381
|
|
|
1150
1382
|
// src/logic.ts
|
|
1151
1383
|
function own(record, key) {
|
|
@@ -1278,6 +1510,225 @@ function fieldMap(def) {
|
|
|
1278
1510
|
return map;
|
|
1279
1511
|
}
|
|
1280
1512
|
|
|
1513
|
+
// src/options.ts
|
|
1514
|
+
var CHOICE_FIELD_TYPES = /* @__PURE__ */ new Set(["single-choice", "multi-choice", "dropdown"]);
|
|
1515
|
+
var isChoice = (block) => !!block && block.kind === "field" && CHOICE_FIELD_TYPES.has(block.type);
|
|
1516
|
+
function put(record, key, value) {
|
|
1517
|
+
Object.defineProperty(record, key, { value, enumerable: true, writable: true, configurable: true });
|
|
1518
|
+
}
|
|
1519
|
+
function optionFilter(field2) {
|
|
1520
|
+
return optionsSource(field2)?.filter;
|
|
1521
|
+
}
|
|
1522
|
+
function answerValues(answer) {
|
|
1523
|
+
const one = (v) => typeof v === "string" ? [v] : typeof v === "number" || typeof v === "boolean" ? [String(v)] : [];
|
|
1524
|
+
if (answer === void 0 || answer === null) return [];
|
|
1525
|
+
return Array.isArray(answer) ? answer.flatMap(one) : one(answer);
|
|
1526
|
+
}
|
|
1527
|
+
function optionMatches(option, attribute, answer) {
|
|
1528
|
+
const have = option.attributes && Object.prototype.hasOwnProperty.call(option.attributes, attribute) ? option.attributes[attribute] : void 0;
|
|
1529
|
+
if (have === void 0) return false;
|
|
1530
|
+
const wanted = answerValues(answer);
|
|
1531
|
+
return (Array.isArray(have) ? have : [have]).some((v) => wanted.includes(v));
|
|
1532
|
+
}
|
|
1533
|
+
function filterSource(definition, key) {
|
|
1534
|
+
return definition.blocks.find((b) => b.kind === "field" && b.key === key);
|
|
1535
|
+
}
|
|
1536
|
+
function visibleOptions(definition, field2, values) {
|
|
1537
|
+
const options = choiceOptions(field2);
|
|
1538
|
+
const filter = optionFilter(field2);
|
|
1539
|
+
if (!filter) return options;
|
|
1540
|
+
const source = filterSource(definition, filter.field);
|
|
1541
|
+
if (!source || source.id === field2.id) return options;
|
|
1542
|
+
const answer = own(values, source.id);
|
|
1543
|
+
if (isEmptyValue(answer, source)) return options;
|
|
1544
|
+
return options.filter((o) => optionMatches(o, filter.attribute, answer));
|
|
1545
|
+
}
|
|
1546
|
+
function answersOnPath(state) {
|
|
1547
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1548
|
+
for (const id of state.path) out[id] = own(state.values, id);
|
|
1549
|
+
return out;
|
|
1550
|
+
}
|
|
1551
|
+
var OUTSIDE_FILTER = "Choose one of the options.";
|
|
1552
|
+
function filteredChoiceError(definition, field2, value, values) {
|
|
1553
|
+
if (!optionFilter(field2) || isEmptyValue(value, field2)) return null;
|
|
1554
|
+
const all = new Set(choiceOptions(field2).map((o) => o.value));
|
|
1555
|
+
const shown2 = new Set(visibleOptions(definition, field2, values).map((o) => o.value));
|
|
1556
|
+
const picked = answerValues(value);
|
|
1557
|
+
return picked.some((v) => all.has(v) && !shown2.has(v)) ? OUTSIDE_FILTER : null;
|
|
1558
|
+
}
|
|
1559
|
+
function hasOptionFilters(definition) {
|
|
1560
|
+
return definition.blocks.some((b) => isChoice(b) && optionFilter(b) !== void 0);
|
|
1561
|
+
}
|
|
1562
|
+
function clearFilteredAnswers(definition, values, changedId) {
|
|
1563
|
+
const changed = definition.blocks.find((b) => b.kind === "field" && b.id === changedId);
|
|
1564
|
+
if (!changed) return values;
|
|
1565
|
+
const keys = /* @__PURE__ */ new Set([changed.key]);
|
|
1566
|
+
let out = values;
|
|
1567
|
+
for (const block of definition.blocks) {
|
|
1568
|
+
if (!isChoice(block)) continue;
|
|
1569
|
+
const filter = optionFilter(block);
|
|
1570
|
+
if (!filter || !keys.has(filter.field)) continue;
|
|
1571
|
+
const value = own(out, block.id);
|
|
1572
|
+
if (value === void 0 || isEmptyValue(value, block)) continue;
|
|
1573
|
+
const all = new Set(choiceOptions(block).map((o) => o.value));
|
|
1574
|
+
const shown2 = new Set(visibleOptions(definition, block, out).map((o) => o.value));
|
|
1575
|
+
const keep = (v) => typeof v !== "string" || !all.has(v) || shown2.has(v);
|
|
1576
|
+
let next = value;
|
|
1577
|
+
if (Array.isArray(value)) {
|
|
1578
|
+
const kept = value.filter(keep);
|
|
1579
|
+
if (kept.length !== value.length) next = kept.length ? kept : void 0;
|
|
1580
|
+
} else if (!keep(value)) next = void 0;
|
|
1581
|
+
if (next === value) continue;
|
|
1582
|
+
if (out === values) out = { ...values };
|
|
1583
|
+
put(out, block.id, next);
|
|
1584
|
+
keys.add(block.key);
|
|
1585
|
+
}
|
|
1586
|
+
return out;
|
|
1587
|
+
}
|
|
1588
|
+
var SEARCHABLE_OPTION_COUNT = 10;
|
|
1589
|
+
function isSearchable(field2, optionCount = choiceOptions(field2).length) {
|
|
1590
|
+
const setting = field2.props?.searchable;
|
|
1591
|
+
if (setting === true || setting === false) return setting;
|
|
1592
|
+
return optionCount > SEARCHABLE_OPTION_COUNT;
|
|
1593
|
+
}
|
|
1594
|
+
function fold(text) {
|
|
1595
|
+
return text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
1596
|
+
}
|
|
1597
|
+
function searchOptions(options, query) {
|
|
1598
|
+
const words = fold(query).split(/\s+/).filter(Boolean);
|
|
1599
|
+
if (!words.length) return [...options];
|
|
1600
|
+
return options.filter((o) => {
|
|
1601
|
+
const text = fold([o.label ?? "", o.value, o.description ?? ""].join(" "));
|
|
1602
|
+
return words.every((w) => text.includes(w));
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
var MAX_PREFILL_PARAM_LENGTH = 64;
|
|
1606
|
+
function isPrefillParam(param) {
|
|
1607
|
+
return typeof param === "string" && param.length <= MAX_PREFILL_PARAM_LENGTH && /^[A-Za-z0-9_.-]+$/.test(param);
|
|
1608
|
+
}
|
|
1609
|
+
function prefillSetting(field2) {
|
|
1610
|
+
if (!CHOICE_FIELD_TYPES.has(field2.type)) return void 0;
|
|
1611
|
+
const raw = field2.props?.prefill;
|
|
1612
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw) || !isPrefillParam(raw.param)) return void 0;
|
|
1613
|
+
return { param: raw.param, hideWhenPrefilled: raw.hideWhenPrefilled === true };
|
|
1614
|
+
}
|
|
1615
|
+
function paramValues(params, name) {
|
|
1616
|
+
let raw;
|
|
1617
|
+
if (typeof params.getAll === "function") {
|
|
1618
|
+
raw = params.getAll(name);
|
|
1619
|
+
} else if (Object.prototype.hasOwnProperty.call(params, name)) {
|
|
1620
|
+
raw = params[name];
|
|
1621
|
+
}
|
|
1622
|
+
const list = Array.isArray(raw) ? raw : raw === void 0 ? [] : [raw];
|
|
1623
|
+
return list.filter((v) => typeof v === "string" && v !== "" && v.length <= MAX_HIDDEN_STRING_LENGTH);
|
|
1624
|
+
}
|
|
1625
|
+
function prefillFromLink(definition, params, values = {}) {
|
|
1626
|
+
const out = {};
|
|
1627
|
+
if (!params || typeof params !== "object") return out;
|
|
1628
|
+
const seen = {};
|
|
1629
|
+
for (const id of Object.keys(values)) put(seen, id, values[id]);
|
|
1630
|
+
for (const block of definition.blocks) {
|
|
1631
|
+
if (!isChoice(block)) continue;
|
|
1632
|
+
const setting = prefillSetting(block);
|
|
1633
|
+
if (!setting || own(seen, block.id) !== void 0) continue;
|
|
1634
|
+
const raw = paramValues(params, setting.param);
|
|
1635
|
+
if (!raw.length) continue;
|
|
1636
|
+
const shown2 = new Set(visibleOptions(definition, block, seen).map((o) => o.value));
|
|
1637
|
+
let value;
|
|
1638
|
+
if (block.type === "multi-choice") {
|
|
1639
|
+
const picked = [];
|
|
1640
|
+
for (const r of raw) {
|
|
1641
|
+
for (const v of shown2.has(r) ? [r] : r.split(",").map((s) => s.trim())) {
|
|
1642
|
+
if (shown2.has(v) && !picked.includes(v)) picked.push(v);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
if (picked.length) value = picked;
|
|
1646
|
+
} else value = raw.find((v) => shown2.has(v));
|
|
1647
|
+
if (value === void 0) continue;
|
|
1648
|
+
put(out, block.id, value);
|
|
1649
|
+
put(seen, block.id, value);
|
|
1650
|
+
}
|
|
1651
|
+
return out;
|
|
1652
|
+
}
|
|
1653
|
+
function hiddenByPrefill(definition, state) {
|
|
1654
|
+
const hideable = /* @__PURE__ */ new Set();
|
|
1655
|
+
for (const b of definition.blocks) {
|
|
1656
|
+
if (isChoice(b) && prefillSetting(b)?.hideWhenPrefilled) hideable.add(b.id);
|
|
1657
|
+
}
|
|
1658
|
+
if (!hideable.size) return [];
|
|
1659
|
+
const prefilled = /* @__PURE__ */ new Set();
|
|
1660
|
+
for (const e of state.events) {
|
|
1661
|
+
if (e.type !== "answer" || !hideable.has(e.field)) continue;
|
|
1662
|
+
if (e.source === "prefill") prefilled.add(e.field);
|
|
1663
|
+
else prefilled.delete(e.field);
|
|
1664
|
+
}
|
|
1665
|
+
if (!prefilled.size) return [];
|
|
1666
|
+
const fields = new Map(definition.blocks.map((b) => [b.id, b]));
|
|
1667
|
+
return state.path.filter((id) => {
|
|
1668
|
+
if (!prefilled.has(id) || own(state.errors, id) !== void 0) return false;
|
|
1669
|
+
const field2 = fields.get(id);
|
|
1670
|
+
return !isEmptyValue(own(state.values, id), field2);
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
function respondentPath(definition, state) {
|
|
1674
|
+
const hidden = hiddenByPrefill(definition, state);
|
|
1675
|
+
if (!hidden.length) return [...state.path];
|
|
1676
|
+
const skip = new Set(hidden);
|
|
1677
|
+
return state.path.filter((id) => !skip.has(id));
|
|
1678
|
+
}
|
|
1679
|
+
function snapshotOf(byValue, value) {
|
|
1680
|
+
const option = byValue.get(value);
|
|
1681
|
+
if (option) return { value, label: option.label ?? value };
|
|
1682
|
+
return { value, label: value, other: true };
|
|
1683
|
+
}
|
|
1684
|
+
function answerSnapshots(definition, answers) {
|
|
1685
|
+
const out = {};
|
|
1686
|
+
for (const block of definition.blocks) {
|
|
1687
|
+
if (!isChoice(block) || !optionsSource(block)) continue;
|
|
1688
|
+
const value = own(answers, block.id);
|
|
1689
|
+
if (isEmptyValue(value, block)) continue;
|
|
1690
|
+
const byValue = new Map(choiceOptions(block).map((o) => [o.value, o]));
|
|
1691
|
+
let snapshot;
|
|
1692
|
+
if (Array.isArray(value)) {
|
|
1693
|
+
const items = value.filter((v) => typeof v === "string").map((v) => snapshotOf(byValue, v));
|
|
1694
|
+
if (items.length) snapshot = items;
|
|
1695
|
+
} else if (typeof value === "string") snapshot = snapshotOf(byValue, value);
|
|
1696
|
+
if (snapshot) put(out, block.id, snapshot);
|
|
1697
|
+
}
|
|
1698
|
+
return out;
|
|
1699
|
+
}
|
|
1700
|
+
function answersFromSnapshots(definition, snapshots) {
|
|
1701
|
+
const answers = {};
|
|
1702
|
+
const missing = {};
|
|
1703
|
+
for (const block of definition.blocks) {
|
|
1704
|
+
if (!isChoice(block)) continue;
|
|
1705
|
+
const raw = own(snapshots, block.id);
|
|
1706
|
+
const list = (Array.isArray(raw) ? raw : raw === void 0 ? [] : [raw]).flatMap((s) => {
|
|
1707
|
+
const o = readOption(s);
|
|
1708
|
+
if (!o) return [];
|
|
1709
|
+
const label = o.label ?? o.value;
|
|
1710
|
+
return [s.other === true ? { value: o.value, label, other: true } : { value: o.value, label }];
|
|
1711
|
+
});
|
|
1712
|
+
if (!list.length) continue;
|
|
1713
|
+
const options = choiceOptions(block);
|
|
1714
|
+
const byValue = new Set(options.map((o) => o.value));
|
|
1715
|
+
const allowsOther = block.props?.other !== void 0 && block.props?.other !== false;
|
|
1716
|
+
const found = [];
|
|
1717
|
+
const lost = [];
|
|
1718
|
+
for (const s of list) {
|
|
1719
|
+
let value;
|
|
1720
|
+
if (byValue.has(s.value)) value = s.value;
|
|
1721
|
+
else if (s.other) value = allowsOther ? s.value : void 0;
|
|
1722
|
+
else value = options.find((o) => fold(o.label ?? o.value) === fold(s.label))?.value;
|
|
1723
|
+
if (value === void 0) lost.push(s);
|
|
1724
|
+
else if (!found.includes(value)) found.push(value);
|
|
1725
|
+
}
|
|
1726
|
+
if (found.length) put(answers, block.id, block.type === "multi-choice" ? found : found[0]);
|
|
1727
|
+
if (lost.length) put(missing, block.id, lost);
|
|
1728
|
+
}
|
|
1729
|
+
return { answers, missing };
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1281
1732
|
// src/recall.ts
|
|
1282
1733
|
var TOKEN = /\{\{\s*(field|hidden):([A-Za-z0-9_-]+)\s*(?:\|([^}]*))?\}\}/g;
|
|
1283
1734
|
function recall(text, scope) {
|
|
@@ -1357,6 +1808,31 @@ function parseForm(input) {
|
|
|
1357
1808
|
const optionalBoolean = (v, path) => {
|
|
1358
1809
|
if (v !== void 0 && typeof v !== "boolean") issue(path, "Must be true or false.");
|
|
1359
1810
|
};
|
|
1811
|
+
const optionDetails = (o, at) => {
|
|
1812
|
+
if (o.description !== void 0 && (typeof o.description !== "string" || o.description.length > OPTION_LIMITS.description)) {
|
|
1813
|
+
issue(`${at}.description`, `Must be text of at most ${OPTION_LIMITS.description} characters.`);
|
|
1814
|
+
}
|
|
1815
|
+
if (o.image !== void 0 && !isHttpsUrl(o.image)) {
|
|
1816
|
+
issue(`${at}.image`, `Must be an https:// link of at most ${OPTION_LIMITS.image} characters.`);
|
|
1817
|
+
}
|
|
1818
|
+
if (o.attributes === void 0) return;
|
|
1819
|
+
const attributes = o.attributes;
|
|
1820
|
+
if (!isObject2(attributes) || Object.keys(attributes).length > OPTION_LIMITS.attributes) {
|
|
1821
|
+
issue(`${at}.attributes`, `Must be an object of at most ${OPTION_LIMITS.attributes} attributes.`);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
const fits = (x) => typeof x === "string" && x.length <= OPTION_LIMITS.attributeValue;
|
|
1825
|
+
for (const [name2, v] of Object.entries(attributes)) {
|
|
1826
|
+
if (!name2.trim() || name2.length > OPTION_LIMITS.attributeName || RESERVED.has(name2)) {
|
|
1827
|
+
issue(`${at}.attributes`, `${shown(name2)} isn't a usable attribute name (at most ${OPTION_LIMITS.attributeName} characters).`);
|
|
1828
|
+
} else if (!fits(v) && !(Array.isArray(v) && v.length <= OPTION_LIMITS.attributeItems && v.every(fits))) {
|
|
1829
|
+
issue(
|
|
1830
|
+
`${at}.attributes.${name2}`,
|
|
1831
|
+
`Must be text, or a list of at most ${OPTION_LIMITS.attributeItems} texts, each at most ${OPTION_LIMITS.attributeValue} characters.`
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1360
1836
|
const name = (v, path, what) => {
|
|
1361
1837
|
if (typeof v !== "string" || !v) {
|
|
1362
1838
|
issue(path, `${what} must be a non-empty string.`);
|
|
@@ -1445,6 +1921,22 @@ function parseForm(input) {
|
|
|
1445
1921
|
}
|
|
1446
1922
|
}
|
|
1447
1923
|
if (type === "consent") optionalText(props2.text, `${path}.text`);
|
|
1924
|
+
if (type === PHOTO_TYPE) {
|
|
1925
|
+
if (props2.maxFiles !== void 0 && !(isInt(props2.maxFiles) && props2.maxFiles >= 1 && props2.maxFiles <= MAX_PHOTO_FILES)) {
|
|
1926
|
+
issue(`${path}.maxFiles`, `Must be a whole number from 1 to ${MAX_PHOTO_FILES}.`);
|
|
1927
|
+
}
|
|
1928
|
+
if (props2.maxSizeMb !== void 0 && !(isInt(props2.maxSizeMb) && props2.maxSizeMb >= 1 && props2.maxSizeMb <= MAX_PHOTO_SIZE_MB)) {
|
|
1929
|
+
issue(`${path}.maxSizeMb`, `Must be a whole number from 1 to ${MAX_PHOTO_SIZE_MB}.`);
|
|
1930
|
+
}
|
|
1931
|
+
if (props2.formats !== void 0) {
|
|
1932
|
+
const formats = props2.formats;
|
|
1933
|
+
if (!Array.isArray(formats) || formats.length === 0 || !formats.every(isPhotoFormat)) {
|
|
1934
|
+
issue(`${path}.formats`, "Must be a list of one or more of jpeg, png, heic and webp.");
|
|
1935
|
+
} else if (new Set(formats).size !== formats.length) {
|
|
1936
|
+
issue(`${path}.formats`, "Name each format once.");
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1448
1940
|
};
|
|
1449
1941
|
if (!isObject2(input)) {
|
|
1450
1942
|
issue("", "A form definition must be an object.");
|
|
@@ -1465,6 +1957,7 @@ function parseForm(input) {
|
|
|
1465
1957
|
else {
|
|
1466
1958
|
optionalBoolean(input.settings.anonymous, "settings.anonymous");
|
|
1467
1959
|
optionalBoolean(input.settings.clearHiddenValues, "settings.clearHiddenValues");
|
|
1960
|
+
optionalBoolean(input.settings.saveProgress, "settings.saveProgress");
|
|
1468
1961
|
}
|
|
1469
1962
|
}
|
|
1470
1963
|
if (input.meta !== void 0 && (!isObject2(input.meta) || !isJson(input.meta))) {
|
|
@@ -1505,6 +1998,38 @@ function parseForm(input) {
|
|
|
1505
1998
|
issue(`${path}.type`, `Unknown field type "${String(block.type)}".`);
|
|
1506
1999
|
}
|
|
1507
2000
|
if (block.props !== void 0) {
|
|
2001
|
+
const from = isObject2(block.props) ? block.props.optionsFrom : void 0;
|
|
2002
|
+
if (from !== void 0) {
|
|
2003
|
+
if (!isObject2(from) || from.source !== "host" || typeof from.key !== "string" || !from.key) {
|
|
2004
|
+
issue(`${path}.props.optionsFrom`, 'Must be { source: "host", key } with a non-empty key.');
|
|
2005
|
+
} else if (typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
|
|
2006
|
+
warnings.push({ path: `${path}.props.optionsFrom`, message: "Only single choice, multiple choice and dropdown questions take options from the app." });
|
|
2007
|
+
}
|
|
2008
|
+
const filter = isObject2(from) ? from.filter : void 0;
|
|
2009
|
+
if (filter !== void 0) {
|
|
2010
|
+
const at = `${path}.props.optionsFrom.filter`;
|
|
2011
|
+
if (!isObject2(filter) || typeof filter.attribute !== "string" || !filter.attribute || filter.attribute.length > OPTION_LIMITS.attributeName || typeof filter.field !== "string" || !filter.field) {
|
|
2012
|
+
issue(at, `Must be { attribute, field }: an attribute name (at most ${OPTION_LIMITS.attributeName} characters) and an earlier question's key.`);
|
|
2013
|
+
} else if (!keys.has(filter.field)) {
|
|
2014
|
+
issue(`${at}.field`, `"${filter.field}" must be the key of an earlier question.`);
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
const prefill = isObject2(block.props) ? block.props.prefill : void 0;
|
|
2019
|
+
if (prefill !== void 0) {
|
|
2020
|
+
if (!isObject2(prefill) || !isPrefillParam(prefill.param)) {
|
|
2021
|
+
issue(
|
|
2022
|
+
`${path}.props.prefill`,
|
|
2023
|
+
`Must be { param, hideWhenPrefilled? }: a link parameter of letters, digits, "_", "-" or "." (at most ${MAX_PREFILL_PARAM_LENGTH} characters).`
|
|
2024
|
+
);
|
|
2025
|
+
} else {
|
|
2026
|
+
optionalBoolean(prefill.hideWhenPrefilled, `${path}.props.prefill.hideWhenPrefilled`);
|
|
2027
|
+
if (typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
|
|
2028
|
+
warnings.push({ path: `${path}.props.prefill`, message: "Only single choice, multiple choice and dropdown questions are filled in from a link." });
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
if (isObject2(block.props)) optionalBoolean(block.props.searchable, `${path}.props.searchable`);
|
|
1508
2033
|
if (!isObject2(block.props) || !isJson(block.props)) issue(`${path}.props`, "Must be a JSON object.");
|
|
1509
2034
|
else if (block.props.options !== void 0) {
|
|
1510
2035
|
const options = block.props.options;
|
|
@@ -1513,12 +2038,25 @@ function parseForm(input) {
|
|
|
1513
2038
|
options.forEach((o, j) => {
|
|
1514
2039
|
const at = `${path}.props.options[${j}]`;
|
|
1515
2040
|
if (!isObject2(o) || typeof o.value !== "string") issue(at, "Each option needs a string value.");
|
|
1516
|
-
else
|
|
2041
|
+
else {
|
|
2042
|
+
optionalText(o.label, `${at}.label`);
|
|
2043
|
+
optionDetails(o, at);
|
|
2044
|
+
}
|
|
1517
2045
|
});
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
2046
|
+
if (from === void 0) {
|
|
2047
|
+
choices.set(
|
|
2048
|
+
block.id,
|
|
2049
|
+
new Set(options.flatMap((o) => isObject2(o) && typeof o.value === "string" ? [o.value] : []))
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
const other = isObject2(block.props) ? block.props.other : void 0;
|
|
2055
|
+
if (other !== void 0) {
|
|
2056
|
+
if (other !== true && other !== false && !(isObject2(other) && (other.label === void 0 || typeof other.label === "string"))) {
|
|
2057
|
+
issue(`${path}.props.other`, "Must be true, false or { label }.");
|
|
2058
|
+
} else if (other !== false && typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
|
|
2059
|
+
warnings.push({ path: `${path}.props.other`, message: 'Only single choice, multiple choice and dropdown questions take an "Other" answer.' });
|
|
1522
2060
|
}
|
|
1523
2061
|
}
|
|
1524
2062
|
const { max, maxLength: maxLength2 } = isObject2(block.props) ? block.props : {};
|
|
@@ -1593,6 +2131,8 @@ function parseForm(input) {
|
|
|
1593
2131
|
} else if (ref.column !== void 0) issue(path, `"${ref.key}" is not a table, so it has no column "${ref.column}".`);
|
|
1594
2132
|
else if (targetType === "media-notes" || targetType === "text-highlights") {
|
|
1595
2133
|
warnings.push({ path, message: `"${ref.key}" holds notes, not numbers; the formula reads it as blank.` });
|
|
2134
|
+
} else if (targetType === PHOTO_TYPE) {
|
|
2135
|
+
warnings.push({ path, message: `"${ref.key}" holds photos, not numbers; the formula reads it as blank.` });
|
|
1596
2136
|
}
|
|
1597
2137
|
}
|
|
1598
2138
|
});
|
|
@@ -1812,9 +2352,30 @@ function field(type, key, options, props2) {
|
|
|
1812
2352
|
return block;
|
|
1813
2353
|
}
|
|
1814
2354
|
var props = (all) => Object.fromEntries(Object.entries(all).filter((e) => e[1] !== void 0));
|
|
1815
|
-
var choiceProps = (
|
|
1816
|
-
|
|
1817
|
-
|
|
2355
|
+
var choiceProps = (o) => {
|
|
2356
|
+
const out = {};
|
|
2357
|
+
if (o.options || !o.optionsFrom) {
|
|
2358
|
+
out.options = (o.options ?? []).map(
|
|
2359
|
+
(x) => props({
|
|
2360
|
+
value: x.value,
|
|
2361
|
+
label: x.label || void 0,
|
|
2362
|
+
description: x.description,
|
|
2363
|
+
image: x.image,
|
|
2364
|
+
attributes: x.attributes
|
|
2365
|
+
})
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
if (o.optionsFrom) {
|
|
2369
|
+
const { source, key, filter } = o.optionsFrom;
|
|
2370
|
+
out.optionsFrom = filter ? { source, key, filter: { attribute: filter.attribute, field: filter.field } } : { source, key };
|
|
2371
|
+
}
|
|
2372
|
+
if (o.prefill) {
|
|
2373
|
+
out.prefill = o.prefill.hideWhenPrefilled ? { param: o.prefill.param, hideWhenPrefilled: true } : { param: o.prefill.param };
|
|
2374
|
+
}
|
|
2375
|
+
if (o.searchable !== void 0) out.searchable = o.searchable;
|
|
2376
|
+
if (o.other) out.other = o.other === true ? true : props({ label: o.other.label });
|
|
2377
|
+
return out;
|
|
2378
|
+
};
|
|
1818
2379
|
var hiddenSpec = (type, o) => ({
|
|
1819
2380
|
type,
|
|
1820
2381
|
source: o.source ?? "url",
|
|
@@ -1838,11 +2399,11 @@ var f = {
|
|
|
1838
2399
|
),
|
|
1839
2400
|
/** A 0-to-10 Net Promoter Score. The end labels default to "Not likely" and "Very likely" in the renderer. */
|
|
1840
2401
|
nps: (key, o) => field("nps", key, o, props({ lowLabel: o.lowLabel || void 0, highLabel: o.highLabel || void 0 })),
|
|
1841
|
-
singleChoice: (key, o) => field("single-choice", key, o, choiceProps(o
|
|
1842
|
-
multiChoice: (key, o) => field("multi-choice", key, o, choiceProps(o
|
|
2402
|
+
singleChoice: (key, o) => field("single-choice", key, o, choiceProps(o)),
|
|
2403
|
+
multiChoice: (key, o) => field("multi-choice", key, o, choiceProps(o)),
|
|
1843
2404
|
yesNo: (key, o) => field("yes-no", key, o),
|
|
1844
2405
|
/** One option from a compact menu: for a long list (a country, a class), where single choice would be a wall of buttons. */
|
|
1845
|
-
dropdown: (key, o) => field("dropdown", key, o, choiceProps(o
|
|
2406
|
+
dropdown: (key, o) => field("dropdown", key, o, choiceProps(o)),
|
|
1846
2407
|
/** A number, optionally between `min` and `max` and on a `step` (from `min`, else 0): `step: 1` for whole numbers. */
|
|
1847
2408
|
number: (key, o) => field("number", key, o, props({ min: o.min, max: o.max, step: o.step })),
|
|
1848
2409
|
/** A calendar day, stored as `YYYY-MM-DD`, optionally between `min` and `max` (also `YYYY-MM-DD`). */
|
|
@@ -1894,6 +2455,17 @@ var f = {
|
|
|
1894
2455
|
o,
|
|
1895
2456
|
props({ text: o.text, maxHighlights: o.maxHighlights })
|
|
1896
2457
|
),
|
|
2458
|
+
/**
|
|
2459
|
+
* Up to a few photos (a picture of broken equipment). The answer holds the
|
|
2460
|
+
* references the host's upload returned, never the bytes; `formats` from
|
|
2461
|
+
* jpeg, png, heic and webp (default all four).
|
|
2462
|
+
*/
|
|
2463
|
+
photo: (key, o) => field(
|
|
2464
|
+
"photo",
|
|
2465
|
+
key,
|
|
2466
|
+
o,
|
|
2467
|
+
props({ maxFiles: o.maxFiles, maxSizeMb: o.maxSizeMb, formats: o.formats ? [...o.formats] : void 0 })
|
|
2468
|
+
),
|
|
1897
2469
|
/** Any registered field type, including plugins. */
|
|
1898
2470
|
custom: (type, key, o) => field(type, key, o, o.props),
|
|
1899
2471
|
heading: (id, text) => ({ kind: "content", id, type: "heading", text }),
|
|
@@ -2001,47 +2573,6 @@ ${list}`);
|
|
|
2001
2573
|
};
|
|
2002
2574
|
}
|
|
2003
2575
|
|
|
2004
|
-
// src/hidden.ts
|
|
2005
|
-
var MAX_HIDDEN_STRING_LENGTH = 1e3;
|
|
2006
|
-
var DECIMAL = /^-?\d+(\.\d+)?$/;
|
|
2007
|
-
function coerceHiddenValue(value, type) {
|
|
2008
|
-
if (typeof value === "string" && value.length > MAX_HIDDEN_STRING_LENGTH) return void 0;
|
|
2009
|
-
if (type === "string") {
|
|
2010
|
-
if (typeof value === "string") return value;
|
|
2011
|
-
if (typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
2012
|
-
return void 0;
|
|
2013
|
-
}
|
|
2014
|
-
if (type === "number") {
|
|
2015
|
-
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
2016
|
-
if (typeof value !== "string" || !DECIMAL.test(value)) return void 0;
|
|
2017
|
-
const n = Number(value);
|
|
2018
|
-
return Number.isFinite(n) ? n : void 0;
|
|
2019
|
-
}
|
|
2020
|
-
if (typeof value === "boolean") return value;
|
|
2021
|
-
if (value === "true") return true;
|
|
2022
|
-
if (value === "false") return false;
|
|
2023
|
-
return void 0;
|
|
2024
|
-
}
|
|
2025
|
-
function coerceHidden(definition, raw, options = {}) {
|
|
2026
|
-
const declared = new Map((definition.hidden ?? []).map((h) => [h.name, h]));
|
|
2027
|
-
const values = {};
|
|
2028
|
-
const ignored = [];
|
|
2029
|
-
if (!isPlainObject(raw)) return { values, ignored };
|
|
2030
|
-
for (const name of Object.keys(raw)) {
|
|
2031
|
-
const value = raw[name];
|
|
2032
|
-
if (value === void 0) continue;
|
|
2033
|
-
const spec = declared.get(name);
|
|
2034
|
-
if (!spec || options.sources && !options.sources.includes(spec.source)) {
|
|
2035
|
-
ignored.push(name);
|
|
2036
|
-
continue;
|
|
2037
|
-
}
|
|
2038
|
-
const coerced = coerceHiddenValue(value, spec.type);
|
|
2039
|
-
if (coerced === void 0) ignored.push(name);
|
|
2040
|
-
else Object.defineProperty(values, name, { value: coerced, enumerable: true, writable: true, configurable: true });
|
|
2041
|
-
}
|
|
2042
|
-
return { values, ignored };
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
2576
|
// src/validate.ts
|
|
2046
2577
|
var REQUIRED_MESSAGE = "This question needs an answer.";
|
|
2047
2578
|
function runCustom(validator, value) {
|
|
@@ -2077,7 +2608,8 @@ function readsAny(node, ids) {
|
|
|
2077
2608
|
function createForm(definition, options = {}) {
|
|
2078
2609
|
return build(definition, options).engine;
|
|
2079
2610
|
}
|
|
2080
|
-
function build(
|
|
2611
|
+
function build(given, options, replaying = false, budget) {
|
|
2612
|
+
const definition = withHostOptions(given, options.hostOptions);
|
|
2081
2613
|
const fields = fieldMap(definition);
|
|
2082
2614
|
const byKey = new Map([...fields.values()].map((f2) => [f2.key, f2]));
|
|
2083
2615
|
const blocks = new Map(definition.blocks.map((b) => [b.id, b]));
|
|
@@ -2123,6 +2655,7 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2123
2655
|
path: first.path,
|
|
2124
2656
|
current: first.path[0] ?? null,
|
|
2125
2657
|
history: [],
|
|
2658
|
+
furthest: first.path[0] ?? null,
|
|
2126
2659
|
errors: {},
|
|
2127
2660
|
status: "in_progress",
|
|
2128
2661
|
ending: null,
|
|
@@ -2143,16 +2676,34 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2143
2676
|
return at === void 0 ? event : { ...event, at };
|
|
2144
2677
|
};
|
|
2145
2678
|
const isVisibleIn = (s, id) => s.path.includes(id);
|
|
2679
|
+
const filters = hasOptionFilters(definition);
|
|
2680
|
+
const clearFiltered = (values, id) => filters ? clearFilteredAnswers(definition, values, id) : values;
|
|
2146
2681
|
const errorsFor = (s, ids) => {
|
|
2147
2682
|
const errors = {};
|
|
2683
|
+
let shown2;
|
|
2148
2684
|
for (const id of ids) {
|
|
2149
2685
|
const field2 = fields.get(id);
|
|
2150
2686
|
if (!field2 || !isVisibleIn(s, id)) continue;
|
|
2151
|
-
const
|
|
2687
|
+
const value = own(s.values, id);
|
|
2688
|
+
let message = validateField(field2, value, { custom: options.validators?.[id] });
|
|
2689
|
+
if (!message && filters) message = filteredChoiceError(definition, field2, value, shown2 ??= answersOnPath(s));
|
|
2152
2690
|
if (message) errors[id] = message;
|
|
2153
2691
|
}
|
|
2154
2692
|
return errors;
|
|
2155
2693
|
};
|
|
2694
|
+
const skipped = (s) => new Set(hiddenByPrefill(definition, s));
|
|
2695
|
+
const shownFrom = (s, from, skip) => {
|
|
2696
|
+
for (let i = from; i < s.path.length; i++) if (!skip.has(s.path[i])) return s.path[i];
|
|
2697
|
+
return void 0;
|
|
2698
|
+
};
|
|
2699
|
+
const showCurrent = () => {
|
|
2700
|
+
if (!state.current) return;
|
|
2701
|
+
const skip = skipped(state);
|
|
2702
|
+
if (!skip.has(state.current)) return;
|
|
2703
|
+
const at = state.path.indexOf(state.current);
|
|
2704
|
+
const shown2 = at < 0 ? void 0 : shownFrom(state, at, skip);
|
|
2705
|
+
if (shown2) state = { ...state, current: shown2, furthest: state.furthest === state.current ? shown2 : state.furthest };
|
|
2706
|
+
};
|
|
2156
2707
|
let unsettled = false;
|
|
2157
2708
|
let formulasStale = false;
|
|
2158
2709
|
const settleNow = (needValues = true) => {
|
|
@@ -2181,13 +2732,13 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2181
2732
|
const errors = { ...state.errors };
|
|
2182
2733
|
delete errors[id];
|
|
2183
2734
|
if (batch) {
|
|
2184
|
-
state.values = { ...state.values, [id]: value };
|
|
2735
|
+
state.values = clearFiltered({ ...state.values, [id]: value }, id);
|
|
2185
2736
|
state.errors = errors;
|
|
2186
2737
|
log(event);
|
|
2187
2738
|
unsettled = true;
|
|
2188
2739
|
return;
|
|
2189
2740
|
}
|
|
2190
|
-
const { values, path } = settle({ ...state.values, [id]: value }, state.hidden);
|
|
2741
|
+
const { values, path } = settle(clearFiltered({ ...state.values, [id]: value }, id), state.hidden);
|
|
2191
2742
|
emit({ ...state, values, path, errors, events: log(event) });
|
|
2192
2743
|
};
|
|
2193
2744
|
const engine = {
|
|
@@ -2218,19 +2769,21 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2218
2769
|
return false;
|
|
2219
2770
|
}
|
|
2220
2771
|
const at = state.path.indexOf(state.current);
|
|
2221
|
-
const target = state
|
|
2772
|
+
const target = shownFrom(state, at + 1, skipped(state));
|
|
2222
2773
|
if (!target) {
|
|
2223
2774
|
emit({ ...state, events });
|
|
2224
2775
|
return true;
|
|
2225
2776
|
}
|
|
2226
|
-
|
|
2777
|
+
const furthest = state.furthest && state.path.indexOf(state.furthest) >= state.path.indexOf(target) ? state.furthest : target;
|
|
2778
|
+
emit({ ...state, current: target, history: [...state.history, state.current], furthest, events });
|
|
2227
2779
|
return true;
|
|
2228
2780
|
},
|
|
2229
2781
|
back() {
|
|
2230
2782
|
if (state.status === "completed") return;
|
|
2231
2783
|
const history = [...state.history];
|
|
2784
|
+
const skip = skipped(state);
|
|
2232
2785
|
let previous = history.pop();
|
|
2233
|
-
while (previous && !state.path.includes(previous)) previous = history.pop();
|
|
2786
|
+
while (previous && (!state.path.includes(previous) || skip.has(previous))) previous = history.pop();
|
|
2234
2787
|
if (!previous) return;
|
|
2235
2788
|
emit({ ...state, current: previous, history, events: log(stamp({ type: "back" })) });
|
|
2236
2789
|
},
|
|
@@ -2258,15 +2811,27 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2258
2811
|
return out;
|
|
2259
2812
|
}
|
|
2260
2813
|
};
|
|
2261
|
-
|
|
2814
|
+
const fromLink = options.linkParams ? prefillFromLink(definition, options.linkParams, options.initialValues) : {};
|
|
2815
|
+
const initialValues = Object.keys(fromLink).length ? { ...fromLink, ...options.initialValues } : options.initialValues;
|
|
2816
|
+
if (initialValues) {
|
|
2262
2817
|
const wasBatch = batch;
|
|
2263
2818
|
batch = true;
|
|
2264
|
-
for (const [id, value] of Object.entries(
|
|
2819
|
+
for (const [id, value] of Object.entries(initialValues)) {
|
|
2265
2820
|
if (!fields.has(id) || value === void 0) continue;
|
|
2266
2821
|
answer(id, value, stamp({ type: "answer", field: id, value, source: "prefill" }));
|
|
2267
2822
|
}
|
|
2268
2823
|
settleNow();
|
|
2269
2824
|
batch = wasBatch;
|
|
2825
|
+
showCurrent();
|
|
2826
|
+
}
|
|
2827
|
+
const resumeAt = options.resumeAt;
|
|
2828
|
+
if (resumeAt && !replaying && state.path.includes(resumeAt)) {
|
|
2829
|
+
for (let i = 0; i < state.path.length && state.current !== resumeAt; i++) {
|
|
2830
|
+
if (!engine.next()) {
|
|
2831
|
+
state = { ...state, errors: {} };
|
|
2832
|
+
break;
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2270
2835
|
}
|
|
2271
2836
|
const apply = (event) => {
|
|
2272
2837
|
if (event.type === "answer") {
|
|
@@ -2276,18 +2841,22 @@ function build(definition, options, replaying = false, budget) {
|
|
|
2276
2841
|
}
|
|
2277
2842
|
} else if (event.type === "next") {
|
|
2278
2843
|
settleNow(false);
|
|
2844
|
+
showCurrent();
|
|
2279
2845
|
if (formulasStale && isCalculated(fields.get(state.current ?? ""))) settleNow(true);
|
|
2280
2846
|
engine.next();
|
|
2281
2847
|
} else if (event.type === "back") {
|
|
2282
2848
|
settleNow(false);
|
|
2849
|
+
showCurrent();
|
|
2283
2850
|
engine.back();
|
|
2284
2851
|
} else if (event.type === "submit") {
|
|
2285
2852
|
settleNow();
|
|
2853
|
+
showCurrent();
|
|
2286
2854
|
engine.submit();
|
|
2287
2855
|
}
|
|
2288
2856
|
};
|
|
2289
2857
|
const finish = () => {
|
|
2290
2858
|
settleNow();
|
|
2859
|
+
showCurrent();
|
|
2291
2860
|
batch = false;
|
|
2292
2861
|
state = { ...state, events: [...state.events] };
|
|
2293
2862
|
};
|
|
@@ -2298,7 +2867,12 @@ function replay(definition, events, options = {}) {
|
|
|
2298
2867
|
}
|
|
2299
2868
|
function replayWithin(definition, events, options, maxWork) {
|
|
2300
2869
|
const budget = void 0 ;
|
|
2301
|
-
const { engine, apply, finish } = build(
|
|
2870
|
+
const { engine, apply, finish } = build(
|
|
2871
|
+
definition,
|
|
2872
|
+
{ ...options, initialValues: void 0, linkParams: void 0, clock: () => void 0 },
|
|
2873
|
+
true,
|
|
2874
|
+
budget
|
|
2875
|
+
);
|
|
2302
2876
|
try {
|
|
2303
2877
|
for (const event of events) apply(event);
|
|
2304
2878
|
finish();
|
|
@@ -2307,6 +2881,33 @@ function replayWithin(definition, events, options, maxWork) {
|
|
|
2307
2881
|
return engine;
|
|
2308
2882
|
}
|
|
2309
2883
|
|
|
2884
|
+
// src/progress.ts
|
|
2885
|
+
function furthestReached(state, definition) {
|
|
2886
|
+
const at = (id) => id ? state.path.indexOf(id) : -1;
|
|
2887
|
+
let furthest = Math.max(at(state.current), at(state.furthest));
|
|
2888
|
+
for (const id of state.history) furthest = Math.max(furthest, at(id));
|
|
2889
|
+
const fields = fieldMap(definition);
|
|
2890
|
+
for (let i = state.path.length - 1; i > furthest; i--) {
|
|
2891
|
+
const field2 = fields.get(state.path[i]);
|
|
2892
|
+
if (field2 && !isEmptyValue(own(state.values, field2.id), field2)) {
|
|
2893
|
+
furthest = i;
|
|
2894
|
+
break;
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return furthest >= 0 ? state.path[furthest] : null;
|
|
2898
|
+
}
|
|
2899
|
+
function formProgress(engine) {
|
|
2900
|
+
const state = engine.getState();
|
|
2901
|
+
const furthest = furthestReached(state, engine.definition);
|
|
2902
|
+
const index = furthest ? state.path.indexOf(furthest) : -1;
|
|
2903
|
+
const fields = fieldMap(engine.definition);
|
|
2904
|
+
let fieldId = null;
|
|
2905
|
+
for (let i = index; i >= 0 && fieldId === null; i--) {
|
|
2906
|
+
if (fields.has(state.path[i])) fieldId = state.path[i];
|
|
2907
|
+
}
|
|
2908
|
+
return { step: index + 1, total: state.path.length, fieldId };
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2310
2911
|
// src/explain.ts
|
|
2311
2912
|
function explain(source, blockId, options = {}) {
|
|
2312
2913
|
const input = "getState" in source ? { definition: source.definition, values: source.getState().values, hidden: source.getState().hidden } : source;
|
|
@@ -2443,8 +3044,27 @@ function describeCondition(condition, def, fields = fieldMap(def)) {
|
|
|
2443
3044
|
return `${left} ${op} ${right}`;
|
|
2444
3045
|
}
|
|
2445
3046
|
|
|
3047
|
+
// src/carry.ts
|
|
3048
|
+
function carryAnswers(from, to, answers, extra = {}) {
|
|
3049
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
3050
|
+
for (const block of from.blocks) {
|
|
3051
|
+
if (block.kind === "field" && answers[block.id] !== void 0) byKey.set(block.key, answers[block.id]);
|
|
3052
|
+
}
|
|
3053
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
3054
|
+
if (value !== null && value !== void 0 && !byKey.has(key)) byKey.set(key, value);
|
|
3055
|
+
}
|
|
3056
|
+
const out = {};
|
|
3057
|
+
for (const block of to.blocks) {
|
|
3058
|
+
if (block.kind === "field" && byKey.has(block.key)) out[block.id] = byKey.get(block.key);
|
|
3059
|
+
}
|
|
3060
|
+
return out;
|
|
3061
|
+
}
|
|
3062
|
+
|
|
2446
3063
|
exports.CALCULATED_TYPE = CALCULATED_TYPE;
|
|
3064
|
+
exports.CHOICE_FIELD_TYPES = CHOICE_FIELD_TYPES;
|
|
2447
3065
|
exports.COMPUTED_FIELD_TYPES = COMPUTED_FIELD_TYPES;
|
|
3066
|
+
exports.DEFAULT_PHOTO_FILES = DEFAULT_PHOTO_FILES;
|
|
3067
|
+
exports.DEFAULT_PHOTO_SIZE_MB = DEFAULT_PHOTO_SIZE_MB;
|
|
2448
3068
|
exports.FORMULA_FUNCTIONS = FORMULA_FUNCTIONS;
|
|
2449
3069
|
exports.HIGHLIGHT_NOTE_MESSAGE = HIGHLIGHT_NOTE_MESSAGE;
|
|
2450
3070
|
exports.MAX_CALCULATED_DECIMALS = MAX_CALCULATED_DECIMALS;
|
|
@@ -2454,16 +3074,32 @@ exports.MAX_FORM_FORMULA_LENGTH = MAX_FORM_FORMULA_LENGTH;
|
|
|
2454
3074
|
exports.MAX_HIDDEN_STRING_LENGTH = MAX_HIDDEN_STRING_LENGTH;
|
|
2455
3075
|
exports.MAX_HIGHLIGHT_NOTE_LENGTH = MAX_HIGHLIGHT_NOTE_LENGTH;
|
|
2456
3076
|
exports.MAX_HIGHLIGHT_QUOTE_LENGTH = MAX_HIGHLIGHT_QUOTE_LENGTH;
|
|
3077
|
+
exports.MAX_PHOTO_FILES = MAX_PHOTO_FILES;
|
|
3078
|
+
exports.MAX_PHOTO_ID_LENGTH = MAX_PHOTO_ID_LENGTH;
|
|
3079
|
+
exports.MAX_PHOTO_NAME_LENGTH = MAX_PHOTO_NAME_LENGTH;
|
|
3080
|
+
exports.MAX_PHOTO_SIZE_MB = MAX_PHOTO_SIZE_MB;
|
|
3081
|
+
exports.MAX_PREFILL_PARAM_LENGTH = MAX_PREFILL_PARAM_LENGTH;
|
|
2457
3082
|
exports.MAX_SETTLE_ITEMS = MAX_SETTLE_ITEMS;
|
|
2458
3083
|
exports.MAX_TABLE_CELL_LENGTH = MAX_TABLE_CELL_LENGTH;
|
|
2459
3084
|
exports.MAX_TABLE_COLUMNS = MAX_TABLE_COLUMNS;
|
|
2460
3085
|
exports.MAX_TIMESTAMP_SECONDS = MAX_TIMESTAMP_SECONDS;
|
|
3086
|
+
exports.OPTION_LIMITS = OPTION_LIMITS;
|
|
3087
|
+
exports.OTHER_MAX_LENGTH = OTHER_MAX_LENGTH;
|
|
3088
|
+
exports.PHOTO_FORMATS = PHOTO_FORMATS;
|
|
3089
|
+
exports.PHOTO_TYPE = PHOTO_TYPE;
|
|
2461
3090
|
exports.REQUIRED_MESSAGE = REQUIRED_MESSAGE;
|
|
3091
|
+
exports.SEARCHABLE_OPTION_COUNT = SEARCHABLE_OPTION_COUNT;
|
|
2462
3092
|
exports.STRUCTURED_FIELD_TYPES = STRUCTURED_FIELD_TYPES;
|
|
3093
|
+
exports.UPLOAD_FIELD_TYPES = UPLOAD_FIELD_TYPES;
|
|
3094
|
+
exports.answerSnapshots = answerSnapshots;
|
|
3095
|
+
exports.answersFromSnapshots = answersFromSnapshots;
|
|
3096
|
+
exports.answersOnPath = answersOnPath;
|
|
2463
3097
|
exports.calculatedProps = calculatedProps;
|
|
2464
3098
|
exports.calculatedValue = calculatedValue;
|
|
2465
3099
|
exports.canonicalJson = canonicalJson;
|
|
3100
|
+
exports.carryAnswers = carryAnswers;
|
|
2466
3101
|
exports.choiceOptions = choiceOptions;
|
|
3102
|
+
exports.clearFilteredAnswers = clearFilteredAnswers;
|
|
2467
3103
|
exports.coerceHidden = coerceHidden;
|
|
2468
3104
|
exports.coerceHiddenValue = coerceHiddenValue;
|
|
2469
3105
|
exports.computePath = computePath;
|
|
@@ -2475,37 +3111,66 @@ exports.evaluate = evaluate;
|
|
|
2475
3111
|
exports.evaluateFormula = evaluateFormula;
|
|
2476
3112
|
exports.explain = explain;
|
|
2477
3113
|
exports.f = f;
|
|
3114
|
+
exports.filteredChoiceError = filteredChoiceError;
|
|
2478
3115
|
exports.fingerprint = fingerprint;
|
|
3116
|
+
exports.formProgress = formProgress;
|
|
2479
3117
|
exports.formatCalculated = formatCalculated;
|
|
2480
3118
|
exports.formatTimestamp = formatTimestamp;
|
|
2481
3119
|
exports.formulaInput = formulaInput;
|
|
2482
3120
|
exports.formulaNumber = formulaNumber;
|
|
2483
3121
|
exports.formulaRefs = formulaRefs;
|
|
3122
|
+
exports.furthestReached = furthestReached;
|
|
2484
3123
|
exports.getFieldType = getFieldType;
|
|
3124
|
+
exports.hasOptionFilters = hasOptionFilters;
|
|
3125
|
+
exports.hiddenByPrefill = hiddenByPrefill;
|
|
2485
3126
|
exports.isEmptyValue = isEmptyValue;
|
|
2486
3127
|
exports.isFormEvent = isFormEvent;
|
|
3128
|
+
exports.isHttpsUrl = isHttpsUrl;
|
|
2487
3129
|
exports.isIsoDate = isIsoDate;
|
|
2488
3130
|
exports.isJson = isJson;
|
|
3131
|
+
exports.isOtherValue = isOtherValue;
|
|
3132
|
+
exports.isPhotoFormat = isPhotoFormat;
|
|
3133
|
+
exports.isPhotoRef = isPhotoRef;
|
|
3134
|
+
exports.isPrefillParam = isPrefillParam;
|
|
3135
|
+
exports.isSearchable = isSearchable;
|
|
2489
3136
|
exports.isSensitive = isSensitive;
|
|
2490
3137
|
exports.listFieldTypes = listFieldTypes;
|
|
2491
3138
|
exports.listLimits = listLimits;
|
|
2492
3139
|
exports.normalizeValue = normalizeValue;
|
|
2493
3140
|
exports.numberLimits = numberLimits;
|
|
3141
|
+
exports.optionFilter = optionFilter;
|
|
3142
|
+
exports.optionJson = optionJson;
|
|
3143
|
+
exports.optionMatches = optionMatches;
|
|
3144
|
+
exports.optionsSource = optionsSource;
|
|
3145
|
+
exports.otherOption = otherOption;
|
|
2494
3146
|
exports.parseForm = parseForm;
|
|
2495
3147
|
exports.parseFormula = parseFormula;
|
|
2496
3148
|
exports.parseTimestamp = parseTimestamp;
|
|
3149
|
+
exports.photoCountMessage = photoCountMessage;
|
|
3150
|
+
exports.photoFormatList = photoFormatList;
|
|
3151
|
+
exports.photoLimits = photoLimits;
|
|
3152
|
+
exports.photoRefs = photoRefs;
|
|
3153
|
+
exports.photoSizeMessage = photoSizeMessage;
|
|
3154
|
+
exports.photoTypeMessage = photoTypeMessage;
|
|
3155
|
+
exports.prefillFromLink = prefillFromLink;
|
|
3156
|
+
exports.prefillSetting = prefillSetting;
|
|
2497
3157
|
exports.ratingDisplay = ratingDisplay;
|
|
3158
|
+
exports.readOption = readOption;
|
|
2498
3159
|
exports.recall = recall;
|
|
2499
3160
|
exports.registerFieldType = registerFieldType;
|
|
2500
3161
|
exports.renameFormulaKey = renameFormulaKey;
|
|
2501
3162
|
exports.replay = replay;
|
|
3163
|
+
exports.respondentPath = respondentPath;
|
|
2502
3164
|
exports.ruleVisible = ruleVisible;
|
|
2503
3165
|
exports.scaleLabels = scaleLabels;
|
|
3166
|
+
exports.searchOptions = searchOptions;
|
|
2504
3167
|
exports.steps = steps;
|
|
2505
3168
|
exports.summarizeValue = summarizeValue;
|
|
2506
3169
|
exports.tableColumns = tableColumns;
|
|
2507
3170
|
exports.tableLimits = tableLimits;
|
|
2508
3171
|
exports.validateField = validateField;
|
|
3172
|
+
exports.visibleOptions = visibleOptions;
|
|
2509
3173
|
exports.when = when;
|
|
3174
|
+
exports.withHostOptions = withHostOptions;
|
|
2510
3175
|
//# sourceMappingURL=index.cjs.map
|
|
2511
3176
|
//# sourceMappingURL=index.cjs.map
|