@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/README.md +67 -0
- package/dist/destinations.cjs +92 -4
- 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 +92 -4
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-Dz9J5ej2.d.ts → engine-BDq0XJ21.d.cts} +25 -2
- package/dist/{engine-BkNuzlna.d.cts → engine-Qzp6bX6O.d.ts} +25 -2
- package/dist/index.cjs +259 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -75
- package/dist/index.d.ts +120 -75
- package/dist/index.js +236 -17
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +187 -12
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +10 -3
- package/dist/server.d.ts +10 -3
- package/dist/server.js +187 -12
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +187 -12
- 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 +187 -12
- package/dist/testing.js.map +1 -1
- package/dist/{types-BBKBQ8sp.d.cts → types-BhYUbLJ-.d.cts} +21 -1
- package/dist/{types-BBKBQ8sp.d.ts → types-BhYUbLJ-.d.ts} +21 -1
- package/dist/validate-Bs1lyoOl.d.ts +131 -0
- package/dist/validate-BsZ0YTQS.d.cts +131 -0
- package/package.json +1 -1
- package/dist/validate-BXXqi0fq.d.cts +0 -30
- package/dist/validate-BjNwF1UE.d.ts +0 -30
package/dist/testing.cjs
CHANGED
|
@@ -721,6 +721,80 @@ var structuredFieldTypes = [
|
|
|
721
721
|
}
|
|
722
722
|
];
|
|
723
723
|
|
|
724
|
+
// src/photo.ts
|
|
725
|
+
var PHOTO_TYPE = "photo";
|
|
726
|
+
var PHOTO_FORMATS = {
|
|
727
|
+
jpeg: ["image/jpeg"],
|
|
728
|
+
png: ["image/png"],
|
|
729
|
+
heic: ["image/heic", "image/heif"],
|
|
730
|
+
webp: ["image/webp"]
|
|
731
|
+
};
|
|
732
|
+
var FORMAT_NAMES = { jpeg: "JPEG", png: "PNG", heic: "HEIC", webp: "WebP" };
|
|
733
|
+
var ALL_FORMATS = Object.keys(PHOTO_FORMATS);
|
|
734
|
+
var DEFAULT_PHOTO_FILES = 3;
|
|
735
|
+
var MAX_PHOTO_FILES = 5;
|
|
736
|
+
var DEFAULT_PHOTO_SIZE_MB = 10;
|
|
737
|
+
var MAX_PHOTO_SIZE_MB = 25;
|
|
738
|
+
var MAX_PHOTO_ID_LENGTH = 200;
|
|
739
|
+
var MAX_PHOTO_NAME_LENGTH = 255;
|
|
740
|
+
var isPhotoFormat = (v) => typeof v === "string" && Object.prototype.hasOwnProperty.call(PHOTO_FORMATS, v);
|
|
741
|
+
var wholeIn = (v, min, max) => typeof v === "number" && Number.isInteger(v) && v >= min && v <= max;
|
|
742
|
+
function photoLimits(field) {
|
|
743
|
+
const maxFiles = wholeIn(field.props?.maxFiles, 1, MAX_PHOTO_FILES) ? field.props.maxFiles : DEFAULT_PHOTO_FILES;
|
|
744
|
+
const maxSizeMb = wholeIn(field.props?.maxSizeMb, 1, MAX_PHOTO_SIZE_MB) ? field.props.maxSizeMb : DEFAULT_PHOTO_SIZE_MB;
|
|
745
|
+
const raw = field.props?.formats;
|
|
746
|
+
const picked = Array.isArray(raw) ? ALL_FORMATS.filter((f) => raw.includes(f)) : [];
|
|
747
|
+
const formats = picked.length ? picked : [...ALL_FORMATS];
|
|
748
|
+
return {
|
|
749
|
+
maxFiles,
|
|
750
|
+
maxSizeMb,
|
|
751
|
+
maxBytes: maxSizeMb * 1024 * 1024,
|
|
752
|
+
formats,
|
|
753
|
+
mimeTypes: formats.flatMap((f) => PHOTO_FORMATS[f])
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function isPhotoRef(v) {
|
|
757
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
758
|
+
const r = v;
|
|
759
|
+
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;
|
|
760
|
+
}
|
|
761
|
+
function photoRefs(value) {
|
|
762
|
+
if (!Array.isArray(value)) return [];
|
|
763
|
+
const refs = [];
|
|
764
|
+
for (const r of value) if (isPhotoRef(r)) refs.push({ id: r.id, name: r.name, type: r.type, size: r.size });
|
|
765
|
+
return refs;
|
|
766
|
+
}
|
|
767
|
+
var plural2 = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
|
|
768
|
+
function photoFormatList(formats) {
|
|
769
|
+
const names = formats.map((f) => FORMAT_NAMES[f]);
|
|
770
|
+
return names.length > 1 ? `${names.slice(0, -1).join(", ")} or ${names[names.length - 1]}` : names[0] ?? "";
|
|
771
|
+
}
|
|
772
|
+
var photoTypeMessage = (field) => `Use a ${photoFormatList(photoLimits(field).formats)} photo.`;
|
|
773
|
+
var photoSizeMessage = (field) => `Each photo must be ${photoLimits(field).maxSizeMb} MB or smaller.`;
|
|
774
|
+
var photoCountMessage = (field) => `Add at most ${plural2(photoLimits(field).maxFiles, "photo")}.`;
|
|
775
|
+
var photoFieldType = {
|
|
776
|
+
type: PHOTO_TYPE,
|
|
777
|
+
label: "Photo",
|
|
778
|
+
category: "media",
|
|
779
|
+
value: { json: true },
|
|
780
|
+
operators: ["answered", "empty"],
|
|
781
|
+
a11y: { semantics: "group" },
|
|
782
|
+
isEmpty: (value) => value === void 0 || value === null || Array.isArray(value) && value.length === 0,
|
|
783
|
+
validate: (value, field) => {
|
|
784
|
+
if (!Array.isArray(value) || !value.every(isPhotoRef)) return "Add a photo.";
|
|
785
|
+
const limits = photoLimits(field);
|
|
786
|
+
if (value.length > limits.maxFiles) return photoCountMessage(field);
|
|
787
|
+
const refs = value;
|
|
788
|
+
if (new Set(refs.map((r) => r.id)).size !== refs.length) return "Each photo can be added once.";
|
|
789
|
+
if (refs.some((r) => !limits.mimeTypes.includes(r.type.toLowerCase()))) return photoTypeMessage(field);
|
|
790
|
+
if (refs.some((r) => r.size > limits.maxBytes)) return photoSizeMessage(field);
|
|
791
|
+
return null;
|
|
792
|
+
},
|
|
793
|
+
normalize: (value) => Array.isArray(value) && value.every(isPhotoRef) ? photoRefs(value) : value,
|
|
794
|
+
summarize: (value) => photoRefs(value).map((r) => r.name || "Photo").join(", "),
|
|
795
|
+
samples: () => [[{ id: "sample-photo", name: "photo.jpg", type: "image/jpeg", size: 1024 }]]
|
|
796
|
+
};
|
|
797
|
+
|
|
724
798
|
// src/fields.ts
|
|
725
799
|
var REGISTRY = /* @__PURE__ */ Symbol.for("@marktiderman/genesis-forms/field-types");
|
|
726
800
|
var globalScope = globalThis;
|
|
@@ -757,6 +831,20 @@ function summarizeValue(value, field) {
|
|
|
757
831
|
if (Array.isArray(value)) return value.join(", ");
|
|
758
832
|
return String(value);
|
|
759
833
|
}
|
|
834
|
+
var OTHER_MAX_LENGTH = 200;
|
|
835
|
+
function otherOption(field) {
|
|
836
|
+
const other = field.props?.other;
|
|
837
|
+
if (other === true) return { label: "Other" };
|
|
838
|
+
if (other && typeof other === "object" && !Array.isArray(other)) {
|
|
839
|
+
const label = typeof other.label === "string" && other.label.trim() ? other.label.trim() : "Other";
|
|
840
|
+
return { label };
|
|
841
|
+
}
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
function isOtherValue(field, value) {
|
|
845
|
+
return otherOption(field) !== null && typeof value === "string" && !choiceOptions(field).some((o) => o.value === value);
|
|
846
|
+
}
|
|
847
|
+
var typedOther = (field, value) => isOtherValue(field, value) && value.trim() !== "" && value.length <= OTHER_MAX_LENGTH;
|
|
760
848
|
function choiceOptions(field) {
|
|
761
849
|
const raw = field.props?.options;
|
|
762
850
|
if (!Array.isArray(raw)) return [];
|
|
@@ -764,6 +852,42 @@ function choiceOptions(field) {
|
|
|
764
852
|
(o) => o && typeof o === "object" && !Array.isArray(o) && typeof o.value === "string" ? [{ value: o.value, label: typeof o.label === "string" ? o.label : void 0 }] : []
|
|
765
853
|
);
|
|
766
854
|
}
|
|
855
|
+
function optionsSource(field) {
|
|
856
|
+
const raw = field.props?.optionsFrom;
|
|
857
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
858
|
+
return raw.source === "host" && typeof raw.key === "string" && raw.key ? { source: "host", key: raw.key } : void 0;
|
|
859
|
+
}
|
|
860
|
+
function hostList(hostOptions, key) {
|
|
861
|
+
if (!hostOptions || !Object.prototype.hasOwnProperty.call(hostOptions, key)) return [];
|
|
862
|
+
const list = hostOptions[key];
|
|
863
|
+
if (!Array.isArray(list)) return [];
|
|
864
|
+
const seen = /* @__PURE__ */ new Set();
|
|
865
|
+
const out = [];
|
|
866
|
+
for (const o of list) {
|
|
867
|
+
if (!o || typeof o !== "object" || Array.isArray(o)) continue;
|
|
868
|
+
const { value, label } = o;
|
|
869
|
+
if (typeof value !== "string" || !value || seen.has(value)) continue;
|
|
870
|
+
seen.add(value);
|
|
871
|
+
out.push(typeof label === "string" ? { value, label } : { value });
|
|
872
|
+
}
|
|
873
|
+
return out;
|
|
874
|
+
}
|
|
875
|
+
function withHostOptions(definition, hostOptions) {
|
|
876
|
+
let changed = false;
|
|
877
|
+
const blocks = definition.blocks.map((block) => {
|
|
878
|
+
if (block.kind !== "field") return block;
|
|
879
|
+
const from = optionsSource(block);
|
|
880
|
+
if (!from) return block;
|
|
881
|
+
const own2 = choiceOptions(block);
|
|
882
|
+
const taken = new Set(own2.map((o) => o.value));
|
|
883
|
+
const added = hostList(hostOptions, from.key).filter((o) => !taken.has(o.value));
|
|
884
|
+
if (!added.length) return block;
|
|
885
|
+
changed = true;
|
|
886
|
+
const options = [...own2, ...added].map((o) => o.label === void 0 ? { value: o.value } : { value: o.value, label: o.label });
|
|
887
|
+
return { ...block, props: { ...block.props, options } };
|
|
888
|
+
});
|
|
889
|
+
return changed ? { ...definition, blocks } : definition;
|
|
890
|
+
}
|
|
767
891
|
function optionLabel(field, value) {
|
|
768
892
|
const option = choiceOptions(field).find((o) => o.value === value);
|
|
769
893
|
return option?.label ?? value;
|
|
@@ -882,7 +1006,7 @@ var builtins = [
|
|
|
882
1006
|
value: { json: true },
|
|
883
1007
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
884
1008
|
a11y: { semantics: "group" },
|
|
885
|
-
validate: (value, field) => typeof value === "string" && choiceOptions(field).some((o) => o.value === value) ? null : "Choose one of the options.",
|
|
1009
|
+
validate: (value, field) => typeof value === "string" && (choiceOptions(field).some((o) => o.value === value) || typedOther(field, value)) ? null : "Choose one of the options.",
|
|
886
1010
|
summarize: (value, field) => optionLabel(field, String(value))
|
|
887
1011
|
},
|
|
888
1012
|
{
|
|
@@ -894,7 +1018,8 @@ var builtins = [
|
|
|
894
1018
|
a11y: { semantics: "group" },
|
|
895
1019
|
validate: (value, field) => {
|
|
896
1020
|
const allowed = new Set(choiceOptions(field).map((o) => o.value));
|
|
897
|
-
|
|
1021
|
+
const others = Array.isArray(value) ? value.filter((v) => typeof v === "string" && !allowed.has(v)) : [];
|
|
1022
|
+
return Array.isArray(value) && new Set(value).size === value.length && value.every((v) => typeof v === "string") && (others.length === 0 || others.length === 1 && typedOther(field, others[0])) ? null : "Choose from the options.";
|
|
898
1023
|
},
|
|
899
1024
|
summarize: (value, field) => Array.isArray(value) ? value.map((v) => optionLabel(field, String(v))).join(", ") : String(value)
|
|
900
1025
|
},
|
|
@@ -915,7 +1040,7 @@ var builtins = [
|
|
|
915
1040
|
value: { json: true },
|
|
916
1041
|
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
917
1042
|
a11y: { semantics: "control" },
|
|
918
|
-
validate: (value, field) => typeof value === "string" && choiceOptions(field).some((o) => o.value === value) ? null : "Choose one of the options.",
|
|
1043
|
+
validate: (value, field) => typeof value === "string" && (choiceOptions(field).some((o) => o.value === value) || typedOther(field, value)) ? null : "Choose one of the options.",
|
|
919
1044
|
summarize: (value, field) => optionLabel(field, String(value))
|
|
920
1045
|
},
|
|
921
1046
|
{
|
|
@@ -1022,7 +1147,7 @@ function dateLimits(field) {
|
|
|
1022
1147
|
if (typeof max === "string" && isIsoDate(max)) out.max = max;
|
|
1023
1148
|
return out;
|
|
1024
1149
|
}
|
|
1025
|
-
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType]) registerFieldType(manifest);
|
|
1150
|
+
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType, photoFieldType]) registerFieldType(manifest);
|
|
1026
1151
|
|
|
1027
1152
|
// src/events.ts
|
|
1028
1153
|
function isPlainObject(value) {
|
|
@@ -1291,7 +1416,8 @@ function readsAny(node, ids) {
|
|
|
1291
1416
|
function createForm(definition, options = {}) {
|
|
1292
1417
|
return build(definition, options).engine;
|
|
1293
1418
|
}
|
|
1294
|
-
function build(
|
|
1419
|
+
function build(given, options, replaying = false, budget) {
|
|
1420
|
+
const definition = withHostOptions(given, options.hostOptions);
|
|
1295
1421
|
const fields = fieldMap(definition);
|
|
1296
1422
|
const byKey = new Map([...fields.values()].map((f) => [f.key, f]));
|
|
1297
1423
|
const blocks = new Map(definition.blocks.map((b) => [b.id, b]));
|
|
@@ -1337,6 +1463,7 @@ function build(definition, options, replaying = false, budget) {
|
|
|
1337
1463
|
path: first.path,
|
|
1338
1464
|
current: first.path[0] ?? null,
|
|
1339
1465
|
history: [],
|
|
1466
|
+
furthest: first.path[0] ?? null,
|
|
1340
1467
|
errors: {},
|
|
1341
1468
|
status: "in_progress",
|
|
1342
1469
|
ending: null,
|
|
@@ -1437,7 +1564,8 @@ function build(definition, options, replaying = false, budget) {
|
|
|
1437
1564
|
emit({ ...state, events });
|
|
1438
1565
|
return true;
|
|
1439
1566
|
}
|
|
1440
|
-
|
|
1567
|
+
const furthest = state.furthest && state.path.indexOf(state.furthest) >= state.path.indexOf(target) ? state.furthest : target;
|
|
1568
|
+
emit({ ...state, current: target, history: [...state.history, state.current], furthest, events });
|
|
1441
1569
|
return true;
|
|
1442
1570
|
},
|
|
1443
1571
|
back() {
|
|
@@ -1482,6 +1610,15 @@ function build(definition, options, replaying = false, budget) {
|
|
|
1482
1610
|
settleNow();
|
|
1483
1611
|
batch = wasBatch;
|
|
1484
1612
|
}
|
|
1613
|
+
const resumeAt = options.resumeAt;
|
|
1614
|
+
if (resumeAt && !replaying && state.path.includes(resumeAt)) {
|
|
1615
|
+
for (let i = 0; i < state.path.length && state.current !== resumeAt; i++) {
|
|
1616
|
+
if (!engine.next()) {
|
|
1617
|
+
state = { ...state, errors: {} };
|
|
1618
|
+
break;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1485
1622
|
const apply = (event) => {
|
|
1486
1623
|
if (event.type === "answer") {
|
|
1487
1624
|
if (fields.has(event.field)) {
|
|
@@ -1851,6 +1988,22 @@ function parseForm(input) {
|
|
|
1851
1988
|
}
|
|
1852
1989
|
}
|
|
1853
1990
|
if (type === "consent") optionalText(props.text, `${path}.text`);
|
|
1991
|
+
if (type === PHOTO_TYPE) {
|
|
1992
|
+
if (props.maxFiles !== void 0 && !(isInt(props.maxFiles) && props.maxFiles >= 1 && props.maxFiles <= MAX_PHOTO_FILES)) {
|
|
1993
|
+
issue(`${path}.maxFiles`, `Must be a whole number from 1 to ${MAX_PHOTO_FILES}.`);
|
|
1994
|
+
}
|
|
1995
|
+
if (props.maxSizeMb !== void 0 && !(isInt(props.maxSizeMb) && props.maxSizeMb >= 1 && props.maxSizeMb <= MAX_PHOTO_SIZE_MB)) {
|
|
1996
|
+
issue(`${path}.maxSizeMb`, `Must be a whole number from 1 to ${MAX_PHOTO_SIZE_MB}.`);
|
|
1997
|
+
}
|
|
1998
|
+
if (props.formats !== void 0) {
|
|
1999
|
+
const formats = props.formats;
|
|
2000
|
+
if (!Array.isArray(formats) || formats.length === 0 || !formats.every(isPhotoFormat)) {
|
|
2001
|
+
issue(`${path}.formats`, "Must be a list of one or more of jpeg, png, heic and webp.");
|
|
2002
|
+
} else if (new Set(formats).size !== formats.length) {
|
|
2003
|
+
issue(`${path}.formats`, "Name each format once.");
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
1854
2007
|
};
|
|
1855
2008
|
if (!isObject2(input)) {
|
|
1856
2009
|
issue("", "A form definition must be an object.");
|
|
@@ -1871,6 +2024,7 @@ function parseForm(input) {
|
|
|
1871
2024
|
else {
|
|
1872
2025
|
optionalBoolean(input.settings.anonymous, "settings.anonymous");
|
|
1873
2026
|
optionalBoolean(input.settings.clearHiddenValues, "settings.clearHiddenValues");
|
|
2027
|
+
optionalBoolean(input.settings.saveProgress, "settings.saveProgress");
|
|
1874
2028
|
}
|
|
1875
2029
|
}
|
|
1876
2030
|
if (input.meta !== void 0 && (!isObject2(input.meta) || !isJson(input.meta))) {
|
|
@@ -1911,6 +2065,14 @@ function parseForm(input) {
|
|
|
1911
2065
|
issue(`${path}.type`, `Unknown field type "${String(block.type)}".`);
|
|
1912
2066
|
}
|
|
1913
2067
|
if (block.props !== void 0) {
|
|
2068
|
+
const from = isObject2(block.props) ? block.props.optionsFrom : void 0;
|
|
2069
|
+
if (from !== void 0) {
|
|
2070
|
+
if (!isObject2(from) || from.source !== "host" || typeof from.key !== "string" || !from.key) {
|
|
2071
|
+
issue(`${path}.props.optionsFrom`, 'Must be { source: "host", key } with a non-empty key.');
|
|
2072
|
+
} else if (typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
|
|
2073
|
+
warnings.push({ path: `${path}.props.optionsFrom`, message: "Only single choice, multiple choice and dropdown questions take options from the app." });
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
1914
2076
|
if (!isObject2(block.props) || !isJson(block.props)) issue(`${path}.props`, "Must be a JSON object.");
|
|
1915
2077
|
else if (block.props.options !== void 0) {
|
|
1916
2078
|
const options = block.props.options;
|
|
@@ -1921,10 +2083,20 @@ function parseForm(input) {
|
|
|
1921
2083
|
if (!isObject2(o) || typeof o.value !== "string") issue(at, "Each option needs a string value.");
|
|
1922
2084
|
else optionalText(o.label, `${at}.label`);
|
|
1923
2085
|
});
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
2086
|
+
if (from === void 0) {
|
|
2087
|
+
choices.set(
|
|
2088
|
+
block.id,
|
|
2089
|
+
new Set(options.flatMap((o) => isObject2(o) && typeof o.value === "string" ? [o.value] : []))
|
|
2090
|
+
);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
const other = isObject2(block.props) ? block.props.other : void 0;
|
|
2095
|
+
if (other !== void 0) {
|
|
2096
|
+
if (other !== true && other !== false && !(isObject2(other) && (other.label === void 0 || typeof other.label === "string"))) {
|
|
2097
|
+
issue(`${path}.props.other`, "Must be true, false or { label }.");
|
|
2098
|
+
} else if (other !== false && typeof block.type === "string" && !CHOICE_TYPES.has(block.type)) {
|
|
2099
|
+
warnings.push({ path: `${path}.props.other`, message: 'Only single choice, multiple choice and dropdown questions take an "Other" answer.' });
|
|
1928
2100
|
}
|
|
1929
2101
|
}
|
|
1930
2102
|
const { max, maxLength: maxLength2 } = isObject2(block.props) ? block.props : {};
|
|
@@ -1999,6 +2171,8 @@ function parseForm(input) {
|
|
|
1999
2171
|
} else if (ref.column !== void 0) issue(path, `"${ref.key}" is not a table, so it has no column "${ref.column}".`);
|
|
2000
2172
|
else if (targetType === "media-notes" || targetType === "text-highlights") {
|
|
2001
2173
|
warnings.push({ path, message: `"${ref.key}" holds notes, not numbers; the formula reads it as blank.` });
|
|
2174
|
+
} else if (targetType === PHOTO_TYPE) {
|
|
2175
|
+
warnings.push({ path, message: `"${ref.key}" holds photos, not numbers; the formula reads it as blank.` });
|
|
2002
2176
|
}
|
|
2003
2177
|
}
|
|
2004
2178
|
});
|
|
@@ -2263,9 +2437,10 @@ function wellFormed(input) {
|
|
|
2263
2437
|
}
|
|
2264
2438
|
return { answers, events: known, hidden };
|
|
2265
2439
|
}
|
|
2266
|
-
function validateSubmission(
|
|
2267
|
-
const invalid = checkDefinition(
|
|
2440
|
+
function validateSubmission(published, submission, options = {}) {
|
|
2441
|
+
const invalid = checkDefinition(published);
|
|
2268
2442
|
if (invalid) return { ok: false, errors: { "": invalid } };
|
|
2443
|
+
const definition = withHostOptions(published, options.hostOptions);
|
|
2269
2444
|
if (!isPlainObject(submission)) return MALFORMED;
|
|
2270
2445
|
const maxEvents = options.maxEvents ?? MAX_EVENTS;
|
|
2271
2446
|
let sent = submission;
|