@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/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { F as FieldBlock, C as ContentBlock, P as PageBreakBlock, a as FormDefinition, H as HiddenInput, b as FormLayout, L as LogicRule, E as Ending, c as FormSettings, J as Json, d as Condition, B as Block, O as Operator, e as FieldValidator } from './validate-
|
|
2
|
-
export { f as FormEvent, g as Operand, R as REQUIRED_MESSAGE, S as StandardSchemaV1, v as validateField } from './validate-
|
|
3
|
-
import { F as FormEngine } from './engine-
|
|
4
|
-
export { C as CreateFormOptions, a as FormState, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-
|
|
1
|
+
import { F as FieldBlock, C as ContentBlock, P as PageBreakBlock, a as FormDefinition, H as HiddenInput, b as FormLayout, L as LogicRule, E as Ending, c as FormSettings, J as Json, d as Condition, B as Block, O as Operator, e as FieldValidator } from './validate-BbackAjB.cjs';
|
|
2
|
+
export { f as FormEvent, g as Operand, R as REQUIRED_MESSAGE, S as StandardSchemaV1, v as validateField } from './validate-BbackAjB.cjs';
|
|
3
|
+
import { F as FormEngine } from './engine-Bj8-mMVb.cjs';
|
|
4
|
+
export { C as CreateFormOptions, a as FormState, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-Bj8-mMVb.cjs';
|
|
5
5
|
|
|
6
6
|
/** A field block that remembers its key and value type, for answer inference. */
|
|
7
7
|
type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
@@ -11,15 +11,15 @@ type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
|
11
11
|
};
|
|
12
12
|
interface FieldOptions {
|
|
13
13
|
label: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
required?: boolean;
|
|
14
|
+
description?: string | undefined;
|
|
15
|
+
placeholder?: string | undefined;
|
|
16
|
+
required?: boolean | undefined;
|
|
17
17
|
/** Personal or secret data (see `isSensitive`). */
|
|
18
|
-
sensitive?: boolean;
|
|
18
|
+
sensitive?: boolean | undefined;
|
|
19
19
|
/** Identifies the respondent (name, email, phone). Not asked when `settings.anonymous` is true. */
|
|
20
|
-
identifying?: boolean;
|
|
20
|
+
identifying?: boolean | undefined;
|
|
21
21
|
/** Defaults to the key. Set it before renaming a key on a live form, so answers stay linked. */
|
|
22
|
-
id?: string;
|
|
22
|
+
id?: string | undefined;
|
|
23
23
|
}
|
|
24
24
|
interface ChoiceFieldOptions<O extends string> extends FieldOptions {
|
|
25
25
|
options: readonly {
|
|
@@ -28,20 +28,22 @@ interface ChoiceFieldOptions<O extends string> extends FieldOptions {
|
|
|
28
28
|
}[];
|
|
29
29
|
}
|
|
30
30
|
interface HiddenOptions {
|
|
31
|
-
source?: HiddenInput["source"];
|
|
32
|
-
sensitive?: boolean;
|
|
31
|
+
source?: HiddenInput["source"] | undefined;
|
|
32
|
+
sensitive?: boolean | undefined;
|
|
33
|
+
/** Identifies the respondent. Not stored when `settings.anonymous` is true. */
|
|
34
|
+
identifying?: boolean | undefined;
|
|
33
35
|
}
|
|
34
36
|
declare const f: {
|
|
35
37
|
shortText: <K extends string>(key: K, o: FieldOptions & {
|
|
36
|
-
maxLength?: number;
|
|
38
|
+
maxLength?: number | undefined;
|
|
37
39
|
}) => FieldSpec<K, string>;
|
|
38
40
|
longText: <K extends string>(key: K, o: FieldOptions & {
|
|
39
|
-
maxLength?: number;
|
|
41
|
+
maxLength?: number | undefined;
|
|
40
42
|
}) => FieldSpec<K, string>;
|
|
41
43
|
email: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, string>;
|
|
42
44
|
phone: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, string>;
|
|
43
45
|
rating: <K extends string>(key: K, o: FieldOptions & {
|
|
44
|
-
max?: number;
|
|
46
|
+
max?: number | undefined;
|
|
45
47
|
}) => FieldSpec<K, number>;
|
|
46
48
|
nps: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, number>;
|
|
47
49
|
singleChoice: <K extends string, O extends string>(key: K, o: ChoiceFieldOptions<O>) => FieldSpec<K, O>;
|
|
@@ -56,19 +58,22 @@ declare const f: {
|
|
|
56
58
|
pageBreak: (id: string, title?: string) => PageBreakBlock;
|
|
57
59
|
hidden: {
|
|
58
60
|
string: (o?: HiddenOptions) => {
|
|
59
|
-
sensitive?: true | undefined;
|
|
60
61
|
type: "string";
|
|
61
|
-
source: "
|
|
62
|
+
source: HiddenInput["source"];
|
|
63
|
+
sensitive?: true;
|
|
64
|
+
identifying?: true;
|
|
62
65
|
};
|
|
63
66
|
number: (o?: HiddenOptions) => {
|
|
64
|
-
sensitive?: true | undefined;
|
|
65
67
|
type: "number";
|
|
66
|
-
source: "
|
|
68
|
+
source: HiddenInput["source"];
|
|
69
|
+
sensitive?: true;
|
|
70
|
+
identifying?: true;
|
|
67
71
|
};
|
|
68
72
|
boolean: (o?: HiddenOptions) => {
|
|
69
|
-
sensitive?: true | undefined;
|
|
70
73
|
type: "boolean";
|
|
71
|
-
source: "
|
|
74
|
+
source: HiddenInput["source"];
|
|
75
|
+
sensitive?: true;
|
|
76
|
+
identifying?: true;
|
|
72
77
|
};
|
|
73
78
|
};
|
|
74
79
|
};
|
|
@@ -111,7 +116,8 @@ declare namespace when {
|
|
|
111
116
|
type HiddenSpec = {
|
|
112
117
|
type: HiddenInput["type"];
|
|
113
118
|
source: HiddenInput["source"];
|
|
114
|
-
sensitive?: boolean;
|
|
119
|
+
sensitive?: boolean | undefined;
|
|
120
|
+
identifying?: boolean | undefined;
|
|
115
121
|
};
|
|
116
122
|
type HiddenValue<T extends HiddenSpec["type"]> = T extends "number" ? number : T extends "boolean" ? boolean : string;
|
|
117
123
|
type AnyBlock = FieldSpec<string, unknown> | ContentBlock | PageBreakBlock;
|
|
@@ -187,7 +193,8 @@ interface CoercedHidden {
|
|
|
187
193
|
* Convert one raw value to a hidden input's declared type, or `undefined` when
|
|
188
194
|
* it cannot be. Only string, number and boolean primitives are accepted.
|
|
189
195
|
* - `string`: a string (at most 1000 characters), or a number or boolean as text.
|
|
190
|
-
* - `number`: a finite number, or a
|
|
196
|
+
* - `number`: a finite number, or a plain decimal string such as `"12"`, `"-3"`
|
|
197
|
+
* or `"2.5"`. No spaces, hex, exponents or `""`.
|
|
191
198
|
* - `boolean`: `true`/`false`, or the strings `"true"`/`"false"`.
|
|
192
199
|
*/
|
|
193
200
|
declare function coerceHiddenValue(value: unknown, type: HiddenInput["type"]): Json | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { F as FieldBlock, C as ContentBlock, P as PageBreakBlock, a as FormDefinition, H as HiddenInput, b as FormLayout, L as LogicRule, E as Ending, c as FormSettings, J as Json, d as Condition, B as Block, O as Operator, e as FieldValidator } from './validate-
|
|
2
|
-
export { f as FormEvent, g as Operand, R as REQUIRED_MESSAGE, S as StandardSchemaV1, v as validateField } from './validate-
|
|
3
|
-
import { F as FormEngine } from './engine-
|
|
4
|
-
export { C as CreateFormOptions, a as FormState, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-
|
|
1
|
+
import { F as FieldBlock, C as ContentBlock, P as PageBreakBlock, a as FormDefinition, H as HiddenInput, b as FormLayout, L as LogicRule, E as Ending, c as FormSettings, J as Json, d as Condition, B as Block, O as Operator, e as FieldValidator } from './validate-BbackAjB.js';
|
|
2
|
+
export { f as FormEvent, g as Operand, R as REQUIRED_MESSAGE, S as StandardSchemaV1, v as validateField } from './validate-BbackAjB.js';
|
|
3
|
+
import { F as FormEngine } from './engine-Coc-gQIB.js';
|
|
4
|
+
export { C as CreateFormOptions, a as FormState, c as createForm, i as isFormEvent, b as isJson, r as replay } from './engine-Coc-gQIB.js';
|
|
5
5
|
|
|
6
6
|
/** A field block that remembers its key and value type, for answer inference. */
|
|
7
7
|
type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
@@ -11,15 +11,15 @@ type FieldSpec<K extends string = string, V = unknown> = FieldBlock & {
|
|
|
11
11
|
};
|
|
12
12
|
interface FieldOptions {
|
|
13
13
|
label: string;
|
|
14
|
-
description?: string;
|
|
15
|
-
placeholder?: string;
|
|
16
|
-
required?: boolean;
|
|
14
|
+
description?: string | undefined;
|
|
15
|
+
placeholder?: string | undefined;
|
|
16
|
+
required?: boolean | undefined;
|
|
17
17
|
/** Personal or secret data (see `isSensitive`). */
|
|
18
|
-
sensitive?: boolean;
|
|
18
|
+
sensitive?: boolean | undefined;
|
|
19
19
|
/** Identifies the respondent (name, email, phone). Not asked when `settings.anonymous` is true. */
|
|
20
|
-
identifying?: boolean;
|
|
20
|
+
identifying?: boolean | undefined;
|
|
21
21
|
/** Defaults to the key. Set it before renaming a key on a live form, so answers stay linked. */
|
|
22
|
-
id?: string;
|
|
22
|
+
id?: string | undefined;
|
|
23
23
|
}
|
|
24
24
|
interface ChoiceFieldOptions<O extends string> extends FieldOptions {
|
|
25
25
|
options: readonly {
|
|
@@ -28,20 +28,22 @@ interface ChoiceFieldOptions<O extends string> extends FieldOptions {
|
|
|
28
28
|
}[];
|
|
29
29
|
}
|
|
30
30
|
interface HiddenOptions {
|
|
31
|
-
source?: HiddenInput["source"];
|
|
32
|
-
sensitive?: boolean;
|
|
31
|
+
source?: HiddenInput["source"] | undefined;
|
|
32
|
+
sensitive?: boolean | undefined;
|
|
33
|
+
/** Identifies the respondent. Not stored when `settings.anonymous` is true. */
|
|
34
|
+
identifying?: boolean | undefined;
|
|
33
35
|
}
|
|
34
36
|
declare const f: {
|
|
35
37
|
shortText: <K extends string>(key: K, o: FieldOptions & {
|
|
36
|
-
maxLength?: number;
|
|
38
|
+
maxLength?: number | undefined;
|
|
37
39
|
}) => FieldSpec<K, string>;
|
|
38
40
|
longText: <K extends string>(key: K, o: FieldOptions & {
|
|
39
|
-
maxLength?: number;
|
|
41
|
+
maxLength?: number | undefined;
|
|
40
42
|
}) => FieldSpec<K, string>;
|
|
41
43
|
email: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, string>;
|
|
42
44
|
phone: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, string>;
|
|
43
45
|
rating: <K extends string>(key: K, o: FieldOptions & {
|
|
44
|
-
max?: number;
|
|
46
|
+
max?: number | undefined;
|
|
45
47
|
}) => FieldSpec<K, number>;
|
|
46
48
|
nps: <K extends string>(key: K, o: FieldOptions) => FieldSpec<K, number>;
|
|
47
49
|
singleChoice: <K extends string, O extends string>(key: K, o: ChoiceFieldOptions<O>) => FieldSpec<K, O>;
|
|
@@ -56,19 +58,22 @@ declare const f: {
|
|
|
56
58
|
pageBreak: (id: string, title?: string) => PageBreakBlock;
|
|
57
59
|
hidden: {
|
|
58
60
|
string: (o?: HiddenOptions) => {
|
|
59
|
-
sensitive?: true | undefined;
|
|
60
61
|
type: "string";
|
|
61
|
-
source: "
|
|
62
|
+
source: HiddenInput["source"];
|
|
63
|
+
sensitive?: true;
|
|
64
|
+
identifying?: true;
|
|
62
65
|
};
|
|
63
66
|
number: (o?: HiddenOptions) => {
|
|
64
|
-
sensitive?: true | undefined;
|
|
65
67
|
type: "number";
|
|
66
|
-
source: "
|
|
68
|
+
source: HiddenInput["source"];
|
|
69
|
+
sensitive?: true;
|
|
70
|
+
identifying?: true;
|
|
67
71
|
};
|
|
68
72
|
boolean: (o?: HiddenOptions) => {
|
|
69
|
-
sensitive?: true | undefined;
|
|
70
73
|
type: "boolean";
|
|
71
|
-
source: "
|
|
74
|
+
source: HiddenInput["source"];
|
|
75
|
+
sensitive?: true;
|
|
76
|
+
identifying?: true;
|
|
72
77
|
};
|
|
73
78
|
};
|
|
74
79
|
};
|
|
@@ -111,7 +116,8 @@ declare namespace when {
|
|
|
111
116
|
type HiddenSpec = {
|
|
112
117
|
type: HiddenInput["type"];
|
|
113
118
|
source: HiddenInput["source"];
|
|
114
|
-
sensitive?: boolean;
|
|
119
|
+
sensitive?: boolean | undefined;
|
|
120
|
+
identifying?: boolean | undefined;
|
|
115
121
|
};
|
|
116
122
|
type HiddenValue<T extends HiddenSpec["type"]> = T extends "number" ? number : T extends "boolean" ? boolean : string;
|
|
117
123
|
type AnyBlock = FieldSpec<string, unknown> | ContentBlock | PageBreakBlock;
|
|
@@ -187,7 +193,8 @@ interface CoercedHidden {
|
|
|
187
193
|
* Convert one raw value to a hidden input's declared type, or `undefined` when
|
|
188
194
|
* it cannot be. Only string, number and boolean primitives are accepted.
|
|
189
195
|
* - `string`: a string (at most 1000 characters), or a number or boolean as text.
|
|
190
|
-
* - `number`: a finite number, or a
|
|
196
|
+
* - `number`: a finite number, or a plain decimal string such as `"12"`, `"-3"`
|
|
197
|
+
* or `"2.5"`. No spaces, hex, exponents or `""`.
|
|
191
198
|
* - `boolean`: `true`/`false`, or the strings `"true"`/`"false"`.
|
|
192
199
|
*/
|
|
193
200
|
declare function coerceHiddenValue(value: unknown, type: HiddenInput["type"]): Json | undefined;
|
package/dist/index.js
CHANGED
|
@@ -397,7 +397,30 @@ function recallRefs(text) {
|
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
// src/parse.ts
|
|
400
|
+
function shown(v) {
|
|
401
|
+
let text;
|
|
402
|
+
try {
|
|
403
|
+
text = JSON.stringify(v);
|
|
404
|
+
} catch {
|
|
405
|
+
text = void 0;
|
|
406
|
+
}
|
|
407
|
+
text ??= typeof v === "bigint" ? `${v}n` : typeof v;
|
|
408
|
+
return text.length > 60 ? `${text.slice(0, 57)}...` : text;
|
|
409
|
+
}
|
|
400
410
|
var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
411
|
+
var isInt = (v) => typeof v === "number" && Number.isInteger(v);
|
|
412
|
+
var LITERAL_TYPE = {
|
|
413
|
+
"short-text": "string",
|
|
414
|
+
"long-text": "string",
|
|
415
|
+
email: "string",
|
|
416
|
+
phone: "string",
|
|
417
|
+
rating: "number",
|
|
418
|
+
nps: "number",
|
|
419
|
+
"single-choice": "string",
|
|
420
|
+
"multi-choice": "string",
|
|
421
|
+
"yes-no": "boolean"
|
|
422
|
+
};
|
|
423
|
+
var ORDERING = /* @__PURE__ */ new Set(["lt", "lte", "gt", "gte"]);
|
|
401
424
|
var OPERATORS = /* @__PURE__ */ new Set([
|
|
402
425
|
"eq",
|
|
403
426
|
"neq",
|
|
@@ -470,6 +493,7 @@ function parseForm(input) {
|
|
|
470
493
|
const keys = /* @__PURE__ */ new Map();
|
|
471
494
|
const fieldTypes = /* @__PURE__ */ new Map();
|
|
472
495
|
const order = /* @__PURE__ */ new Map();
|
|
496
|
+
const choices = /* @__PURE__ */ new Map();
|
|
473
497
|
input.blocks.forEach((block, i) => {
|
|
474
498
|
const path = `blocks[${i}]`;
|
|
475
499
|
if (!isObject(block) || typeof block.id !== "string" || !block.id) {
|
|
@@ -503,8 +527,19 @@ function parseForm(input) {
|
|
|
503
527
|
if (!isObject(o) || typeof o.value !== "string") issue(at, "Each option needs a string value.");
|
|
504
528
|
else optionalText(o.label, `${at}.label`);
|
|
505
529
|
});
|
|
530
|
+
choices.set(
|
|
531
|
+
block.id,
|
|
532
|
+
new Set(options.flatMap((o) => isObject(o) && typeof o.value === "string" ? [o.value] : []))
|
|
533
|
+
);
|
|
506
534
|
}
|
|
507
535
|
}
|
|
536
|
+
const { max, maxLength: maxLength2 } = isObject(block.props) ? block.props : {};
|
|
537
|
+
if (block.type === "rating" && max !== void 0 && !(isInt(max) && max >= 2 && max <= 10)) {
|
|
538
|
+
issue(`${path}.props.max`, "Must be a whole number from 2 to 10.");
|
|
539
|
+
}
|
|
540
|
+
if (maxLength2 !== void 0 && !(isInt(maxLength2) && maxLength2 > 0)) {
|
|
541
|
+
issue(`${path}.props.maxLength`, "Must be a whole number above 0.");
|
|
542
|
+
}
|
|
508
543
|
}
|
|
509
544
|
if (validKey) {
|
|
510
545
|
if (keys.has(block.key)) issue(`${path}.key`, `Duplicate key "${String(block.key)}".`);
|
|
@@ -521,6 +556,7 @@ function parseForm(input) {
|
|
|
521
556
|
}
|
|
522
557
|
});
|
|
523
558
|
const hiddenNames = /* @__PURE__ */ new Set();
|
|
559
|
+
const hiddenTypes = /* @__PURE__ */ new Map();
|
|
524
560
|
if (input.hidden !== void 0 && !Array.isArray(input.hidden)) issue("hidden", "Must be an array.");
|
|
525
561
|
(Array.isArray(input.hidden) ? input.hidden : []).forEach((h, i) => {
|
|
526
562
|
if (!isObject(h) || typeof h.name !== "string" || !h.name) {
|
|
@@ -536,7 +572,9 @@ function parseForm(input) {
|
|
|
536
572
|
issue(`hidden[${i}].type`, "Must be string, number or boolean.");
|
|
537
573
|
}
|
|
538
574
|
optionalBoolean(h.sensitive, `hidden[${i}].sensitive`);
|
|
575
|
+
optionalBoolean(h.identifying, `hidden[${i}].identifying`);
|
|
539
576
|
hiddenNames.add(h.name);
|
|
577
|
+
if (typeof h.type === "string") hiddenTypes.set(h.name, h.type);
|
|
540
578
|
});
|
|
541
579
|
const endingIds = /* @__PURE__ */ new Set();
|
|
542
580
|
if (input.endings !== void 0 && !Array.isArray(input.endings)) issue("endings", "Must be an array.");
|
|
@@ -585,8 +623,45 @@ function parseForm(input) {
|
|
|
585
623
|
if (typeof c.left.field === "string") {
|
|
586
624
|
const manifest = getFieldType(fieldTypes.get(c.left.field) ?? "");
|
|
587
625
|
if (manifest && OPERATORS.has(c.op) && !manifest.operators.includes(c.op)) {
|
|
588
|
-
issue(`${path}.op`, `"${c.op}" does not apply to a ${manifest.type} field.`);
|
|
626
|
+
return issue(`${path}.op`, `"${c.op}" does not apply to a ${manifest.type} field.`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
checkLiterals(c.op, c.left, c.right, path);
|
|
630
|
+
};
|
|
631
|
+
const checkLiterals = (op, left, right, path) => {
|
|
632
|
+
if (op === "answered" || op === "empty" || !OPERATORS.has(op)) return;
|
|
633
|
+
const literal = isObject(right) && "value" in right;
|
|
634
|
+
if (right !== void 0 && !literal) return;
|
|
635
|
+
let what = "the left side";
|
|
636
|
+
let type;
|
|
637
|
+
let options;
|
|
638
|
+
if (typeof left.field === "string" && fieldTypes.has(left.field)) {
|
|
639
|
+
const fieldType = fieldTypes.get(left.field);
|
|
640
|
+
what = `"${keyOf(left.field)}"`;
|
|
641
|
+
type = LITERAL_TYPE[fieldType];
|
|
642
|
+
if (fieldType === "single-choice" || fieldType === "multi-choice") options = choices.get(left.field);
|
|
643
|
+
} else if (typeof left.hidden === "string" && hiddenTypes.has(left.hidden)) {
|
|
644
|
+
what = `hidden input "${left.hidden}"`;
|
|
645
|
+
type = hiddenTypes.get(left.hidden);
|
|
646
|
+
}
|
|
647
|
+
const outcome = op === "neq" ? "always true" : "never true";
|
|
648
|
+
let values;
|
|
649
|
+
if (op === "in") {
|
|
650
|
+
if (!literal || !Array.isArray(right.value)) {
|
|
651
|
+
return void warnings.push({ path: `${path}.right`, message: "`in` needs a list of values; it never matches." });
|
|
652
|
+
}
|
|
653
|
+
values = right.value;
|
|
654
|
+
} else if (!literal) return;
|
|
655
|
+
else values = [right.value];
|
|
656
|
+
if (ORDERING.has(op)) {
|
|
657
|
+
if (type !== void 0 && type !== "number") {
|
|
658
|
+
return void warnings.push({ path: `${path}.op`, message: `"${op}" compares numbers, but ${what} holds a ${type}; it is never true.` });
|
|
589
659
|
}
|
|
660
|
+
type = "number";
|
|
661
|
+
}
|
|
662
|
+
for (const v of values) {
|
|
663
|
+
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;
|
|
664
|
+
if (message) warnings.push({ path: `${path}.right`, message });
|
|
590
665
|
}
|
|
591
666
|
};
|
|
592
667
|
const keyOf = (id) => [...keys].find(([, v]) => v === id)?.[0] ?? id;
|
|
@@ -663,6 +738,18 @@ function parseForm(input) {
|
|
|
663
738
|
}
|
|
664
739
|
}
|
|
665
740
|
});
|
|
741
|
+
const extra = [["description", input.description]];
|
|
742
|
+
(Array.isArray(input.endings) ? input.endings : []).forEach((e, i) => {
|
|
743
|
+
if (isObject(e)) extra.push([`endings[${i}]`, e.title], [`endings[${i}]`, e.description]);
|
|
744
|
+
});
|
|
745
|
+
for (const [path, t] of extra) {
|
|
746
|
+
if (typeof t !== "string") continue;
|
|
747
|
+
for (const ref of recallRefs(t)) {
|
|
748
|
+
if (ref.kind === "hidden" ? !hiddenNames.has(ref.name) : !keys.has(ref.name)) {
|
|
749
|
+
warnings.push({ path, message: `Recalls unknown ${ref.kind === "hidden" ? "hidden input" : "field"} "${ref.name}"; it shows its fallback text.` });
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
666
753
|
return issues.length ? fail() : { ok: true, form: input, warnings };
|
|
667
754
|
}
|
|
668
755
|
|
|
@@ -689,7 +776,8 @@ var choiceProps = (options) => ({
|
|
|
689
776
|
var hiddenSpec = (type, o) => ({
|
|
690
777
|
type,
|
|
691
778
|
source: o.source ?? "url",
|
|
692
|
-
...o.sensitive ? { sensitive: true } : {}
|
|
779
|
+
...o.sensitive ? { sensitive: true } : {},
|
|
780
|
+
...o.identifying ? { identifying: true } : {}
|
|
693
781
|
});
|
|
694
782
|
var f = {
|
|
695
783
|
shortText: (key, o) => field("short-text", key, o, o.maxLength ? { maxLength: o.maxLength } : void 0),
|
|
@@ -784,7 +872,8 @@ function defineForm(input) {
|
|
|
784
872
|
name,
|
|
785
873
|
type: spec.type,
|
|
786
874
|
source: spec.source,
|
|
787
|
-
...spec.sensitive ? { sensitive: true } : {}
|
|
875
|
+
...spec.sensitive ? { sensitive: true } : {},
|
|
876
|
+
...spec.identifying ? { identifying: true } : {}
|
|
788
877
|
}));
|
|
789
878
|
}
|
|
790
879
|
if (input.logic?.length) definition.logic = input.logic.map((rule, i) => rule.build(resolve2, `r${i + 1}`));
|
|
@@ -809,6 +898,7 @@ ${list}`);
|
|
|
809
898
|
|
|
810
899
|
// src/hidden.ts
|
|
811
900
|
var MAX_HIDDEN_STRING_LENGTH = 1e3;
|
|
901
|
+
var DECIMAL = /^-?\d+(\.\d+)?$/;
|
|
812
902
|
function coerceHiddenValue(value, type) {
|
|
813
903
|
if (typeof value === "string" && value.length > MAX_HIDDEN_STRING_LENGTH) return void 0;
|
|
814
904
|
if (type === "string") {
|
|
@@ -818,7 +908,7 @@ function coerceHiddenValue(value, type) {
|
|
|
818
908
|
}
|
|
819
909
|
if (type === "number") {
|
|
820
910
|
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
821
|
-
if (typeof value !== "string" ||
|
|
911
|
+
if (typeof value !== "string" || !DECIMAL.test(value)) return void 0;
|
|
822
912
|
const n = Number(value);
|
|
823
913
|
return Number.isFinite(n) ? n : void 0;
|
|
824
914
|
}
|
|
@@ -999,7 +1089,7 @@ function build(definition, options, replaying = false) {
|
|
|
999
1089
|
for (const id of state.path) {
|
|
1000
1090
|
const field2 = fields.get(id);
|
|
1001
1091
|
const value = own(state.values, id);
|
|
1002
|
-
if (field2 && value
|
|
1092
|
+
if (field2 && !isEmptyValue(value, field2)) out[id] = normalizeValue(value, field2);
|
|
1003
1093
|
}
|
|
1004
1094
|
return out;
|
|
1005
1095
|
}
|