@marktiderman/genesis-forms 0.3.1 → 0.4.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 +75 -1
- package/dist/destinations.cjs +159 -6
- package/dist/destinations.cjs.map +1 -1
- package/dist/destinations.js +159 -6
- package/dist/destinations.js.map +1 -1
- package/dist/{engine-CPLtKVew.d.cts → engine-BkNuzlna.d.cts} +2 -1
- package/dist/{engine-DOrPgFDw.d.ts → engine-Dz9J5ej2.d.ts} +2 -1
- package/dist/index.cjs +755 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +195 -4
- package/dist/index.d.ts +195 -4
- package/dist/index.js +736 -23
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +669 -18
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +5 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.js +669 -18
- package/dist/server.js.map +1 -1
- package/dist/testing.cjs +671 -18
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +671 -18
- package/dist/testing.js.map +1 -1
- package/package.json +1 -1
package/dist/destinations.js
CHANGED
|
@@ -112,6 +112,39 @@ async function sha256Hex(text) {
|
|
|
112
112
|
return Array.from(new Uint8Array(digest), (b) => b.toString(16).padStart(2, "0")).join("");
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
// src/calculated.ts
|
|
116
|
+
var CALCULATED_TYPE = "calculated";
|
|
117
|
+
var MAX_CALCULATED_DECIMALS = 10;
|
|
118
|
+
function calculatedProps(field) {
|
|
119
|
+
const p = field.props ?? {};
|
|
120
|
+
const out = { formula: typeof p.formula === "string" ? p.formula : "" };
|
|
121
|
+
if (p.format === "number" || p.format === "percent" || p.format === "text") out.format = p.format;
|
|
122
|
+
if (typeof p.decimals === "number" && Number.isInteger(p.decimals) && p.decimals >= 0 && p.decimals <= MAX_CALCULATED_DECIMALS) {
|
|
123
|
+
out.decimals = p.decimals;
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
function formatCalculated(value, field) {
|
|
128
|
+
if (value === void 0 || value === null) return "";
|
|
129
|
+
const { format, decimals } = calculatedProps(field);
|
|
130
|
+
const fixed = (n) => decimals === void 0 ? String(Number(n.toFixed(6))) : n.toFixed(decimals);
|
|
131
|
+
if (typeof value === "number") return format === "percent" ? `${fixed(value * 100)}%` : fixed(value);
|
|
132
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
133
|
+
return typeof value === "string" ? value : "";
|
|
134
|
+
}
|
|
135
|
+
var numberOps = ["eq", "neq", "lt", "lte", "gt", "gte", "in", "answered", "empty"];
|
|
136
|
+
var calculatedFieldType = {
|
|
137
|
+
type: CALCULATED_TYPE,
|
|
138
|
+
label: "Calculated",
|
|
139
|
+
category: "advanced",
|
|
140
|
+
value: { json: true },
|
|
141
|
+
operators: numberOps,
|
|
142
|
+
a11y: { semantics: "control" },
|
|
143
|
+
// The engine works the value out; any number, text or true / false it made is valid.
|
|
144
|
+
validate: (value) => typeof value === "number" || typeof value === "string" || typeof value === "boolean" ? null : "This value is worked out from other answers.",
|
|
145
|
+
summarize: (value, field) => formatCalculated(value, field)
|
|
146
|
+
};
|
|
147
|
+
|
|
115
148
|
// src/structured.ts
|
|
116
149
|
var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
117
150
|
function intProp(field, name, fallback, floor = 1) {
|
|
@@ -343,7 +376,7 @@ function intRange(field, min, maxDefault) {
|
|
|
343
376
|
const max = typeof field.props?.max === "number" ? field.props.max : maxDefault;
|
|
344
377
|
return { min, max };
|
|
345
378
|
}
|
|
346
|
-
var
|
|
379
|
+
var numberOps2 = ["eq", "neq", "lt", "lte", "gt", "gte", "in", "answered", "empty"];
|
|
347
380
|
var textOps = ["eq", "neq", "contains", "answered", "empty"];
|
|
348
381
|
function maxLength(field, fallback) {
|
|
349
382
|
return typeof field.props?.maxLength === "number" ? field.props.maxLength : fallback;
|
|
@@ -421,23 +454,30 @@ var builtins = [
|
|
|
421
454
|
label: "Rating",
|
|
422
455
|
category: "scale",
|
|
423
456
|
value: { json: true },
|
|
424
|
-
operators:
|
|
457
|
+
operators: numberOps2,
|
|
425
458
|
a11y: { semantics: "group" },
|
|
426
459
|
validate: (value, field) => {
|
|
427
460
|
const { min, max } = intRange(field, 1, 5);
|
|
428
461
|
return Number.isInteger(value) && value >= min && value <= max ? null : `Choose a rating from ${min} to ${max}.`;
|
|
429
462
|
},
|
|
430
|
-
summarize: (value, field) =>
|
|
463
|
+
summarize: (value, field) => {
|
|
464
|
+
const { max } = intRange(field, 1, 5);
|
|
465
|
+
const end = scaleEndLabel(field, value, 1, max);
|
|
466
|
+
return end ? `${value}/${max} (${end})` : `${value}/${max}`;
|
|
467
|
+
}
|
|
431
468
|
},
|
|
432
469
|
{
|
|
433
470
|
type: "nps",
|
|
434
471
|
label: "Net Promoter Score",
|
|
435
472
|
category: "scale",
|
|
436
473
|
value: { json: true },
|
|
437
|
-
operators:
|
|
474
|
+
operators: numberOps2,
|
|
438
475
|
a11y: { semantics: "group" },
|
|
439
476
|
validate: (value) => Number.isInteger(value) && value >= 0 && value <= 10 ? null : "Choose a score from 0 to 10.",
|
|
440
|
-
summarize: (value) =>
|
|
477
|
+
summarize: (value, field) => {
|
|
478
|
+
const end = scaleEndLabel(field, value, 0, 10);
|
|
479
|
+
return end ? `${value}/10 (${end})` : `${value}/10`;
|
|
480
|
+
}
|
|
441
481
|
},
|
|
442
482
|
{
|
|
443
483
|
type: "single-choice",
|
|
@@ -471,9 +511,122 @@ var builtins = [
|
|
|
471
511
|
a11y: { semantics: "group" },
|
|
472
512
|
validate: (value) => typeof value === "boolean" ? null : "Choose yes or no.",
|
|
473
513
|
summarize: (value) => value ? "Yes" : "No"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
type: "dropdown",
|
|
517
|
+
label: "Dropdown",
|
|
518
|
+
category: "choice",
|
|
519
|
+
value: { json: true },
|
|
520
|
+
operators: ["eq", "neq", "in", "answered", "empty"],
|
|
521
|
+
a11y: { semantics: "control" },
|
|
522
|
+
validate: (value, field) => typeof value === "string" && choiceOptions(field).some((o) => o.value === value) ? null : "Choose one of the options.",
|
|
523
|
+
summarize: (value, field) => optionLabel(field, String(value))
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
type: "number",
|
|
527
|
+
label: "Number",
|
|
528
|
+
category: "text",
|
|
529
|
+
value: { json: true },
|
|
530
|
+
operators: numberOps2,
|
|
531
|
+
a11y: { semantics: "control", inputMode: "decimal" },
|
|
532
|
+
validate: (value, field) => {
|
|
533
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "Enter a number.";
|
|
534
|
+
const { min, max, step } = numberLimits(field);
|
|
535
|
+
if (min !== void 0 && max !== void 0 && (value < min || value > max)) {
|
|
536
|
+
return `Enter a number from ${min} to ${max}.`;
|
|
537
|
+
}
|
|
538
|
+
if (min !== void 0 && value < min) return `Enter ${min} or more.`;
|
|
539
|
+
if (max !== void 0 && value > max) return `Enter ${max} or less.`;
|
|
540
|
+
if (step !== void 0 && !onStep(value, min ?? 0, step)) {
|
|
541
|
+
return step === 1 && Number.isInteger(min ?? 0) ? "Enter a whole number." : `Enter a number in steps of ${step}.`;
|
|
542
|
+
}
|
|
543
|
+
return null;
|
|
544
|
+
},
|
|
545
|
+
summarize: (value) => String(value),
|
|
546
|
+
samples: (field) => {
|
|
547
|
+
const { min, max } = numberLimits(field);
|
|
548
|
+
return [min, max, 0, 1].filter((v) => v !== void 0);
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
type: "date",
|
|
553
|
+
label: "Date",
|
|
554
|
+
category: "date",
|
|
555
|
+
value: { json: true },
|
|
556
|
+
operators: ["eq", "neq", "answered", "empty"],
|
|
557
|
+
a11y: { semantics: "control" },
|
|
558
|
+
validate: (value, field) => {
|
|
559
|
+
if (typeof value !== "string" || !isIsoDate(value.trim())) return "Enter a date like 2026-10-22.";
|
|
560
|
+
const date = value.trim();
|
|
561
|
+
const { min, max } = dateLimits(field);
|
|
562
|
+
if (min && max && (date < min || date > max)) return `Choose a date from ${min} to ${max}.`;
|
|
563
|
+
if (min && date < min) return `Choose ${min} or later.`;
|
|
564
|
+
if (max && date > max) return `Choose ${max} or earlier.`;
|
|
565
|
+
return null;
|
|
566
|
+
},
|
|
567
|
+
normalize: (value) => typeof value === "string" ? value.trim() : value,
|
|
568
|
+
samples: (field) => {
|
|
569
|
+
const { min, max } = dateLimits(field);
|
|
570
|
+
return [min ?? max ?? "2000-01-01"];
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
type: "consent",
|
|
575
|
+
label: "Consent checkbox",
|
|
576
|
+
category: "choice",
|
|
577
|
+
value: { json: true },
|
|
578
|
+
operators: ["answered", "empty"],
|
|
579
|
+
a11y: { semantics: "group" },
|
|
580
|
+
// Unchecked is unanswered, so a required consent must be checked.
|
|
581
|
+
isEmpty: (value) => value === void 0 || value === null || value === false,
|
|
582
|
+
validate: (value) => value === true ? null : "Check the box to agree.",
|
|
583
|
+
summarize: () => "Agreed",
|
|
584
|
+
samples: () => [true]
|
|
474
585
|
}
|
|
475
586
|
];
|
|
476
|
-
|
|
587
|
+
function scaleLabels(field) {
|
|
588
|
+
const out = {};
|
|
589
|
+
const low = field.props?.lowLabel;
|
|
590
|
+
const high = field.props?.highLabel;
|
|
591
|
+
if (typeof low === "string" && low.trim()) out.low = low.trim();
|
|
592
|
+
if (typeof high === "string" && high.trim()) out.high = high.trim();
|
|
593
|
+
return out;
|
|
594
|
+
}
|
|
595
|
+
function scaleEndLabel(field, value, min, max) {
|
|
596
|
+
const { low, high } = scaleLabels(field);
|
|
597
|
+
return value === min ? low : value === max ? high : void 0;
|
|
598
|
+
}
|
|
599
|
+
var finite = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
600
|
+
function numberLimits(field) {
|
|
601
|
+
const out = {};
|
|
602
|
+
const min = finite(field.props?.min);
|
|
603
|
+
const max = finite(field.props?.max);
|
|
604
|
+
const step = finite(field.props?.step);
|
|
605
|
+
if (min !== void 0) out.min = min;
|
|
606
|
+
if (max !== void 0) out.max = max;
|
|
607
|
+
if (step !== void 0 && step > 0) out.step = step;
|
|
608
|
+
return out;
|
|
609
|
+
}
|
|
610
|
+
function onStep(value, base, step) {
|
|
611
|
+
const n = (value - base) / step;
|
|
612
|
+
return Math.abs(n - Math.round(n)) < 1e-9 * Math.max(1, Math.abs(n));
|
|
613
|
+
}
|
|
614
|
+
function isIsoDate(value) {
|
|
615
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
|
|
616
|
+
const [y, m, d] = value.split("-").map(Number);
|
|
617
|
+
if (y < 1 || m < 1 || m > 12 || d < 1) return false;
|
|
618
|
+
const days = [31, y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
619
|
+
return d <= days[m - 1];
|
|
620
|
+
}
|
|
621
|
+
function dateLimits(field) {
|
|
622
|
+
const out = {};
|
|
623
|
+
const min = field.props?.min;
|
|
624
|
+
const max = field.props?.max;
|
|
625
|
+
if (typeof min === "string" && isIsoDate(min)) out.min = min;
|
|
626
|
+
if (typeof max === "string" && isIsoDate(max)) out.max = max;
|
|
627
|
+
return out;
|
|
628
|
+
}
|
|
629
|
+
for (const manifest of [...builtins, ...structuredFieldTypes, calculatedFieldType]) registerFieldType(manifest);
|
|
477
630
|
|
|
478
631
|
// src/logic.ts
|
|
479
632
|
function own(record, key) {
|