@marktiderman/genesis-forms 0.1.0 → 0.2.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 +8 -4
- package/dist/{engine-LsPRTzt3.d.cts → engine-Bj8-mMVb.d.cts} +6 -6
- package/dist/{engine-Cp5t2_Am.d.ts → engine-Coc-gQIB.d.ts} +6 -6
- package/dist/index.cjs +95 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -23
- package/dist/index.d.ts +30 -23
- package/dist/index.js +95 -5
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +108 -12
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -11
- package/dist/server.d.ts +11 -11
- package/dist/server.js +108 -12
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +108 -12
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +3 -3
- package/dist/testing.d.ts +3 -3
- package/dist/testing.js +108 -12
- package/dist/testing.js.map +1 -1
- package/dist/{validate-j8Z188mF.d.cts → validate-BbackAjB.d.cts} +8 -1
- package/dist/{validate-j8Z188mF.d.ts → validate-BbackAjB.d.ts} +8 -1
- package/package.json +3 -3
package/dist/server.cjs
CHANGED
|
@@ -230,6 +230,7 @@ function compactEvents(events) {
|
|
|
230
230
|
|
|
231
231
|
// src/hidden.ts
|
|
232
232
|
var MAX_HIDDEN_STRING_LENGTH = 1e3;
|
|
233
|
+
var DECIMAL = /^-?\d+(\.\d+)?$/;
|
|
233
234
|
function coerceHiddenValue(value, type) {
|
|
234
235
|
if (typeof value === "string" && value.length > MAX_HIDDEN_STRING_LENGTH) return void 0;
|
|
235
236
|
if (type === "string") {
|
|
@@ -239,7 +240,7 @@ function coerceHiddenValue(value, type) {
|
|
|
239
240
|
}
|
|
240
241
|
if (type === "number") {
|
|
241
242
|
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
242
|
-
if (typeof value !== "string" ||
|
|
243
|
+
if (typeof value !== "string" || !DECIMAL.test(value)) return void 0;
|
|
243
244
|
const n = Number(value);
|
|
244
245
|
return Number.isFinite(n) ? n : void 0;
|
|
245
246
|
}
|
|
@@ -564,7 +565,7 @@ function build(definition, options, replaying = false) {
|
|
|
564
565
|
for (const id of state.path) {
|
|
565
566
|
const field = fields.get(id);
|
|
566
567
|
const value = own(state.values, id);
|
|
567
|
-
if (field && value
|
|
568
|
+
if (field && !isEmptyValue(value, field)) out[id] = normalizeValue(value, field);
|
|
568
569
|
}
|
|
569
570
|
return out;
|
|
570
571
|
}
|
|
@@ -599,7 +600,30 @@ function replay(definition, events, options = {}) {
|
|
|
599
600
|
}
|
|
600
601
|
|
|
601
602
|
// src/parse.ts
|
|
603
|
+
function shown(v) {
|
|
604
|
+
let text;
|
|
605
|
+
try {
|
|
606
|
+
text = JSON.stringify(v);
|
|
607
|
+
} catch {
|
|
608
|
+
text = void 0;
|
|
609
|
+
}
|
|
610
|
+
text ??= typeof v === "bigint" ? `${v}n` : typeof v;
|
|
611
|
+
return text.length > 60 ? `${text.slice(0, 57)}...` : text;
|
|
612
|
+
}
|
|
602
613
|
var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
614
|
+
var isInt = (v) => typeof v === "number" && Number.isInteger(v);
|
|
615
|
+
var LITERAL_TYPE = {
|
|
616
|
+
"short-text": "string",
|
|
617
|
+
"long-text": "string",
|
|
618
|
+
email: "string",
|
|
619
|
+
phone: "string",
|
|
620
|
+
rating: "number",
|
|
621
|
+
nps: "number",
|
|
622
|
+
"single-choice": "string",
|
|
623
|
+
"multi-choice": "string",
|
|
624
|
+
"yes-no": "boolean"
|
|
625
|
+
};
|
|
626
|
+
var ORDERING = /* @__PURE__ */ new Set(["lt", "lte", "gt", "gte"]);
|
|
603
627
|
var OPERATORS = /* @__PURE__ */ new Set([
|
|
604
628
|
"eq",
|
|
605
629
|
"neq",
|
|
@@ -672,6 +696,7 @@ function parseForm(input) {
|
|
|
672
696
|
const keys = /* @__PURE__ */ new Map();
|
|
673
697
|
const fieldTypes = /* @__PURE__ */ new Map();
|
|
674
698
|
const order = /* @__PURE__ */ new Map();
|
|
699
|
+
const choices = /* @__PURE__ */ new Map();
|
|
675
700
|
input.blocks.forEach((block, i) => {
|
|
676
701
|
const path = `blocks[${i}]`;
|
|
677
702
|
if (!isObject(block) || typeof block.id !== "string" || !block.id) {
|
|
@@ -705,8 +730,19 @@ function parseForm(input) {
|
|
|
705
730
|
if (!isObject(o) || typeof o.value !== "string") issue(at, "Each option needs a string value.");
|
|
706
731
|
else optionalText(o.label, `${at}.label`);
|
|
707
732
|
});
|
|
733
|
+
choices.set(
|
|
734
|
+
block.id,
|
|
735
|
+
new Set(options.flatMap((o) => isObject(o) && typeof o.value === "string" ? [o.value] : []))
|
|
736
|
+
);
|
|
708
737
|
}
|
|
709
738
|
}
|
|
739
|
+
const { max, maxLength: maxLength2 } = isObject(block.props) ? block.props : {};
|
|
740
|
+
if (block.type === "rating" && max !== void 0 && !(isInt(max) && max >= 2 && max <= 10)) {
|
|
741
|
+
issue(`${path}.props.max`, "Must be a whole number from 2 to 10.");
|
|
742
|
+
}
|
|
743
|
+
if (maxLength2 !== void 0 && !(isInt(maxLength2) && maxLength2 > 0)) {
|
|
744
|
+
issue(`${path}.props.maxLength`, "Must be a whole number above 0.");
|
|
745
|
+
}
|
|
710
746
|
}
|
|
711
747
|
if (validKey) {
|
|
712
748
|
if (keys.has(block.key)) issue(`${path}.key`, `Duplicate key "${String(block.key)}".`);
|
|
@@ -723,6 +759,7 @@ function parseForm(input) {
|
|
|
723
759
|
}
|
|
724
760
|
});
|
|
725
761
|
const hiddenNames = /* @__PURE__ */ new Set();
|
|
762
|
+
const hiddenTypes = /* @__PURE__ */ new Map();
|
|
726
763
|
if (input.hidden !== void 0 && !Array.isArray(input.hidden)) issue("hidden", "Must be an array.");
|
|
727
764
|
(Array.isArray(input.hidden) ? input.hidden : []).forEach((h, i) => {
|
|
728
765
|
if (!isObject(h) || typeof h.name !== "string" || !h.name) {
|
|
@@ -738,7 +775,9 @@ function parseForm(input) {
|
|
|
738
775
|
issue(`hidden[${i}].type`, "Must be string, number or boolean.");
|
|
739
776
|
}
|
|
740
777
|
optionalBoolean(h.sensitive, `hidden[${i}].sensitive`);
|
|
778
|
+
optionalBoolean(h.identifying, `hidden[${i}].identifying`);
|
|
741
779
|
hiddenNames.add(h.name);
|
|
780
|
+
if (typeof h.type === "string") hiddenTypes.set(h.name, h.type);
|
|
742
781
|
});
|
|
743
782
|
const endingIds = /* @__PURE__ */ new Set();
|
|
744
783
|
if (input.endings !== void 0 && !Array.isArray(input.endings)) issue("endings", "Must be an array.");
|
|
@@ -787,8 +826,45 @@ function parseForm(input) {
|
|
|
787
826
|
if (typeof c.left.field === "string") {
|
|
788
827
|
const manifest = getFieldType(fieldTypes.get(c.left.field) ?? "");
|
|
789
828
|
if (manifest && OPERATORS.has(c.op) && !manifest.operators.includes(c.op)) {
|
|
790
|
-
issue(`${path}.op`, `"${c.op}" does not apply to a ${manifest.type} field.`);
|
|
829
|
+
return issue(`${path}.op`, `"${c.op}" does not apply to a ${manifest.type} field.`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
checkLiterals(c.op, c.left, c.right, path);
|
|
833
|
+
};
|
|
834
|
+
const checkLiterals = (op, left, right, path) => {
|
|
835
|
+
if (op === "answered" || op === "empty" || !OPERATORS.has(op)) return;
|
|
836
|
+
const literal = isObject(right) && "value" in right;
|
|
837
|
+
if (right !== void 0 && !literal) return;
|
|
838
|
+
let what = "the left side";
|
|
839
|
+
let type;
|
|
840
|
+
let options;
|
|
841
|
+
if (typeof left.field === "string" && fieldTypes.has(left.field)) {
|
|
842
|
+
const fieldType = fieldTypes.get(left.field);
|
|
843
|
+
what = `"${keyOf(left.field)}"`;
|
|
844
|
+
type = LITERAL_TYPE[fieldType];
|
|
845
|
+
if (fieldType === "single-choice" || fieldType === "multi-choice") options = choices.get(left.field);
|
|
846
|
+
} else if (typeof left.hidden === "string" && hiddenTypes.has(left.hidden)) {
|
|
847
|
+
what = `hidden input "${left.hidden}"`;
|
|
848
|
+
type = hiddenTypes.get(left.hidden);
|
|
849
|
+
}
|
|
850
|
+
const outcome = op === "neq" ? "always true" : "never true";
|
|
851
|
+
let values;
|
|
852
|
+
if (op === "in") {
|
|
853
|
+
if (!literal || !Array.isArray(right.value)) {
|
|
854
|
+
return void warnings.push({ path: `${path}.right`, message: "`in` needs a list of values; it never matches." });
|
|
855
|
+
}
|
|
856
|
+
values = right.value;
|
|
857
|
+
} else if (!literal) return;
|
|
858
|
+
else values = [right.value];
|
|
859
|
+
if (ORDERING.has(op)) {
|
|
860
|
+
if (type !== void 0 && type !== "number") {
|
|
861
|
+
return void warnings.push({ path: `${path}.op`, message: `"${op}" compares numbers, but ${what} holds a ${type}; it is never true.` });
|
|
791
862
|
}
|
|
863
|
+
type = "number";
|
|
864
|
+
}
|
|
865
|
+
for (const v of values) {
|
|
866
|
+
const message = type !== void 0 && typeof v !== type ? `${what} holds a ${type}, but ${shown(v)} is not one; this comparison is ${outcome}.` : options && typeof v === "string" && !options.has(v) ? `${shown(v)} is not an option of ${what}; this comparison is ${outcome}.` : null;
|
|
867
|
+
if (message) warnings.push({ path: `${path}.right`, message });
|
|
792
868
|
}
|
|
793
869
|
};
|
|
794
870
|
const keyOf = (id) => [...keys].find(([, v]) => v === id)?.[0] ?? id;
|
|
@@ -865,6 +941,18 @@ function parseForm(input) {
|
|
|
865
941
|
}
|
|
866
942
|
}
|
|
867
943
|
});
|
|
944
|
+
const extra = [["description", input.description]];
|
|
945
|
+
(Array.isArray(input.endings) ? input.endings : []).forEach((e, i) => {
|
|
946
|
+
if (isObject(e)) extra.push([`endings[${i}]`, e.title], [`endings[${i}]`, e.description]);
|
|
947
|
+
});
|
|
948
|
+
for (const [path, t] of extra) {
|
|
949
|
+
if (typeof t !== "string") continue;
|
|
950
|
+
for (const ref of recallRefs(t)) {
|
|
951
|
+
if (ref.kind === "hidden" ? !hiddenNames.has(ref.name) : !keys.has(ref.name)) {
|
|
952
|
+
warnings.push({ path, message: `Recalls unknown ${ref.kind === "hidden" ? "hidden input" : "field"} "${ref.name}"; it shows its fallback text.` });
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
868
956
|
return issues.length ? fail() : { ok: true, form: input, warnings };
|
|
869
957
|
}
|
|
870
958
|
|
|
@@ -901,18 +989,16 @@ function validateSubmission(definition, submission, options = {}) {
|
|
|
901
989
|
if (invalid) return { ok: false, errors: { "": invalid } };
|
|
902
990
|
const input = wellFormed(submission);
|
|
903
991
|
if (!input) return MALFORMED;
|
|
904
|
-
const ignored =
|
|
992
|
+
const ignored = /* @__PURE__ */ new Set();
|
|
905
993
|
const client = coerceHidden(definition, input.hidden, { sources: ["url", "host"] });
|
|
906
|
-
for (const name of client.ignored) ignored.
|
|
994
|
+
for (const name of client.ignored) ignored.add(`hidden.${name}`);
|
|
907
995
|
const hidden = client.values;
|
|
908
996
|
const context = coerceHidden(definition, options.context, { sources: ["server"] }).values;
|
|
909
997
|
const fields = fieldMap(definition);
|
|
910
|
-
const dropped = /* @__PURE__ */ new Set();
|
|
911
998
|
const accept = (id) => {
|
|
912
|
-
if (!fields.has(id)) return void ignored.
|
|
999
|
+
if (!fields.has(id)) return void ignored.add(id);
|
|
913
1000
|
if (!withheld(definition, fields.get(id))) return true;
|
|
914
|
-
|
|
915
|
-
dropped.add(id);
|
|
1001
|
+
ignored.add(id);
|
|
916
1002
|
return false;
|
|
917
1003
|
};
|
|
918
1004
|
const engineHidden = { ...hidden, ...context };
|
|
@@ -965,15 +1051,25 @@ function validateSubmission(definition, submission, options = {}) {
|
|
|
965
1051
|
if (message === null) answers[id] = normalizeValue(value, field);
|
|
966
1052
|
}
|
|
967
1053
|
}
|
|
1054
|
+
const store = (values, report) => {
|
|
1055
|
+
if (definition.settings?.anonymous !== true) return values;
|
|
1056
|
+
const identifying = new Set((definition.hidden ?? []).filter((h) => h.identifying === true).map((h) => h.name));
|
|
1057
|
+
const kept = {};
|
|
1058
|
+
for (const [name, value] of Object.entries(values)) {
|
|
1059
|
+
if (!identifying.has(name)) kept[name] = value;
|
|
1060
|
+
else if (report) ignored.add(`hidden.${name}`);
|
|
1061
|
+
}
|
|
1062
|
+
return kept;
|
|
1063
|
+
};
|
|
968
1064
|
return {
|
|
969
1065
|
ok: true,
|
|
970
1066
|
submission: {
|
|
971
1067
|
status: complete ? "completed" : "draft",
|
|
972
1068
|
answers,
|
|
973
|
-
hidden,
|
|
974
|
-
context,
|
|
1069
|
+
hidden: store(hidden, true),
|
|
1070
|
+
context: store(context, false),
|
|
975
1071
|
ending: complete ? state.ending : null,
|
|
976
|
-
ignored
|
|
1072
|
+
ignored: [...ignored]
|
|
977
1073
|
}
|
|
978
1074
|
};
|
|
979
1075
|
}
|