@bluprynt/forms-core 4.0.1 → 4.1.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/dist/index.cjs CHANGED
@@ -110,6 +110,18 @@ const resolveRelativeDate = (relative, now) => {
110
110
  *
111
111
  * **Date handling**: condition values that are relative date expressions
112
112
  * (e.g. `"+7d"`) are resolved against `ctx.now` before comparison.
113
+ *
114
+ * **Multiselect handling**: when `ctx.fieldTypes` reports the referenced field
115
+ * as `multiselect`, its array value is compared as a set instead of a scalar:
116
+ * `set` means "at least one option chosen", `eq`/`ne` compare set membership
117
+ * ignoring order and duplicates, `in`/`notin` test whether *any* chosen option
118
+ * appears in the condition's list, and the ordering operators are always
119
+ * `false`. Fields of every other type -- including `array` -- keep the original
120
+ * scalar semantics.
121
+ *
122
+ * **Object-valued fields** (`file`, `sustainability`) go through the scalar path
123
+ * too, so only `set`/`notset` are meaningful: any object present counts as
124
+ * `set`, and `eq`/`ne`/`in`/`notin` compare by reference and so never match.
113
125
  */
114
126
  var ConditionEvaluator = class {
115
127
  /**
@@ -128,6 +140,7 @@ var ConditionEvaluator = class {
128
140
  evalSimple(cond, ctx) {
129
141
  if (ctx.visibilityMap && ctx.visibilityMap.get(cond.field) === false) return cond.op === "notset";
130
142
  const fieldValue = ctx.values[String(cond.field)];
143
+ if (ctx.fieldTypes?.get(cond.field) === "multiselect") return this.evalMultiselect(cond, fieldValue);
131
144
  switch (cond.op) {
132
145
  case "set": return fieldValue !== null && fieldValue !== void 0 && fieldValue !== "";
133
146
  case "notset": return fieldValue === null || fieldValue === void 0 || fieldValue === "";
@@ -142,6 +155,32 @@ var ConditionEvaluator = class {
142
155
  default: return false;
143
156
  }
144
157
  }
158
+ evalMultiselect(cond, fieldValue) {
159
+ const selected = Array.isArray(fieldValue) ? fieldValue : [];
160
+ const candidates = cond.value;
161
+ switch (cond.op) {
162
+ case "set": return selected.length > 0;
163
+ case "notset": return selected.length === 0;
164
+ case "eq": return this.sameSet(selected, candidates);
165
+ case "ne": return !this.sameSet(selected, candidates);
166
+ case "in": return Array.isArray(candidates) && selected.some((item) => candidates.includes(item));
167
+ case "notin": return Array.isArray(candidates) && !selected.some((item) => candidates.includes(item));
168
+ default: return false;
169
+ }
170
+ }
171
+ /**
172
+ * Compares the chosen options against a condition value as sets, ignoring
173
+ * order and duplicates. A scalar condition value is read as a one-element
174
+ * set, so `{ op: 'eq', value: 'a' }` means "`a` is the only option chosen".
175
+ */
176
+ sameSet(selected, expected) {
177
+ const expectedItems = Array.isArray(expected) ? expected : [expected];
178
+ const selectedSet = new Set(selected);
179
+ const expectedSet = new Set(expectedItems);
180
+ if (selectedSet.size !== expectedSet.size) return false;
181
+ for (const item of expectedSet) if (!selectedSet.has(item)) return false;
182
+ return true;
183
+ }
145
184
  resolveIfDate(value, now) {
146
185
  if (isRelativeDate(value)) return resolveRelativeDate(value, now);
147
186
  return value;
@@ -402,6 +441,93 @@ var DependencyGraph = class DependencyGraph {
402
441
  }
403
442
  };
404
443
  //#endregion
444
+ //#region src/validators/array-obj-validator.ts
445
+ const isRow = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
446
+ /**
447
+ * Validates an `array_obj` field: the row list itself, then every sub-field of
448
+ * every row.
449
+ *
450
+ * Sub-field errors keep the container's `fieldId` -- matching the key the
451
+ * `fieldErrors` map stores them under -- and carry `itemIndex` (the row) plus
452
+ * `itemFieldId` (the sub-field). A row-level `TYPE` error carries `itemIndex`
453
+ * only.
454
+ */
455
+ var ArrayObjValidator = class {
456
+ validate(ctx) {
457
+ const { fieldId, value, now } = ctx;
458
+ const { items, validateField } = ctx;
459
+ const validation = ctx.validation;
460
+ const errors = [];
461
+ if (value === null || value === void 0) return errors;
462
+ if (!Array.isArray(value)) {
463
+ errors.push({
464
+ fieldId,
465
+ rule: "TYPE",
466
+ message: "Must be an array",
467
+ params: { expectedType: "array" }
468
+ });
469
+ return errors;
470
+ }
471
+ if (validation?.minItems !== void 0 && value.length < validation.minItems) errors.push({
472
+ fieldId,
473
+ rule: "MIN_ITEMS",
474
+ message: `Must have at least ${validation.minItems} items`,
475
+ params: {
476
+ minItems: validation.minItems,
477
+ actual: value.length
478
+ }
479
+ });
480
+ if (validation?.maxItems !== void 0 && value.length > validation.maxItems) errors.push({
481
+ fieldId,
482
+ rule: "MAX_ITEMS",
483
+ message: `Must have at most ${validation.maxItems} items`,
484
+ params: {
485
+ maxItems: validation.maxItems,
486
+ actual: value.length
487
+ }
488
+ });
489
+ if (!items) return errors;
490
+ for (let i = 0; i < value.length; i++) {
491
+ const raw = value[i];
492
+ if (raw !== null && raw !== void 0 && !isRow(raw)) {
493
+ errors.push({
494
+ fieldId,
495
+ rule: "TYPE",
496
+ message: "Must be an object",
497
+ params: { expectedType: "object" },
498
+ itemIndex: i
499
+ });
500
+ continue;
501
+ }
502
+ const row = isRow(raw) ? raw : {};
503
+ for (const subField of items) {
504
+ if (subField.type === "static") continue;
505
+ const fakeEntry = {
506
+ id: subField.id,
507
+ type: subField.type,
508
+ condition: void 0,
509
+ validation: subField.validation,
510
+ parentId: fieldId,
511
+ options: subField.options,
512
+ item: void 0,
513
+ items: void 0,
514
+ label: subField.label,
515
+ title: void 0
516
+ };
517
+ const subErrors = validateField(subField.id, row[String(subField.id)], fakeEntry, now);
518
+ errors.push(...subErrors.map((err) => ({
519
+ ...err,
520
+ fieldId,
521
+ itemIndex: i,
522
+ itemFieldId: subField.id,
523
+ message: `${subField.label}: ${err.message}`
524
+ })));
525
+ }
526
+ }
527
+ return errors;
528
+ }
529
+ };
530
+ //#endregion
405
531
  //#region src/validators/array-validator.ts
406
532
  var ArrayValidator = class {
407
533
  validate(ctx) {
@@ -437,7 +563,7 @@ var ArrayValidator = class {
437
563
  actual: value.length
438
564
  }
439
565
  });
440
- if (item) for (let i = 0; i < value.length; i++) {
566
+ if (item && item.type !== "static") for (let i = 0; i < value.length; i++) {
441
567
  const fakeEntry = {
442
568
  id: fieldId,
443
569
  type: item.type,
@@ -446,6 +572,7 @@ var ArrayValidator = class {
446
572
  parentId: void 0,
447
573
  options: item.options,
448
574
  item: void 0,
575
+ items: void 0,
449
576
  label: item.label,
450
577
  title: void 0
451
578
  };
@@ -459,6 +586,32 @@ var ArrayValidator = class {
459
586
  }
460
587
  };
461
588
  //#endregion
589
+ //#region src/validators/blockchain-validator.ts
590
+ var BlockchainValidator = class {
591
+ validate(ctx) {
592
+ const { fieldId, value } = ctx;
593
+ const validation = ctx.validation;
594
+ const errors = [];
595
+ const isEmpty = value === null || value === void 0 || value === "";
596
+ if (validation?.required && isEmpty) {
597
+ errors.push({
598
+ fieldId,
599
+ rule: "REQUIRED",
600
+ message: "Value is required"
601
+ });
602
+ return errors;
603
+ }
604
+ if (isEmpty) return errors;
605
+ if (typeof value !== "string") errors.push({
606
+ fieldId,
607
+ rule: "TYPE",
608
+ message: "Must be a blockchain address string",
609
+ params: { expectedType: "blockchain" }
610
+ });
611
+ return errors;
612
+ }
613
+ };
614
+ //#endregion
462
615
  //#region src/validators/boolean-validator.ts
463
616
  var BooleanValidator = class {
464
617
  validate(ctx) {
@@ -556,6 +709,51 @@ var FileValidator = class {
556
709
  }
557
710
  };
558
711
  //#endregion
712
+ //#region src/validators/multiselect-validator.ts
713
+ /**
714
+ * Validates `multiselect` fields, whose value is an array of the chosen
715
+ * options' `value`s.
716
+ *
717
+ * An absent value and an empty array both count as "nothing selected", so both
718
+ * fail `required`. Option membership is reported as a single field-level
719
+ * `INVALID_OPTION` error rather than one error per offending element: the
720
+ * viewer filters out errors carrying an `itemIndex` on the non-array render
721
+ * path, so indexed errors would never be displayed.
722
+ */
723
+ var MultiselectValidator = class {
724
+ validate(ctx) {
725
+ const { fieldId, value } = ctx;
726
+ const validation = ctx.validation;
727
+ const options = ctx.options;
728
+ const errors = [];
729
+ const isEmpty = value === null || value === void 0 || Array.isArray(value) && value.length === 0;
730
+ if (validation?.required && isEmpty) {
731
+ errors.push({
732
+ fieldId,
733
+ rule: "REQUIRED",
734
+ message: "Value is required"
735
+ });
736
+ return errors;
737
+ }
738
+ if (isEmpty) return errors;
739
+ if (!Array.isArray(value)) {
740
+ errors.push({
741
+ fieldId,
742
+ rule: "TYPE",
743
+ message: "Must be an array",
744
+ params: { expectedType: "multiselect" }
745
+ });
746
+ return errors;
747
+ }
748
+ if (options && value.some((selected) => !options.some((opt) => opt.value === selected))) errors.push({
749
+ fieldId,
750
+ rule: "INVALID_OPTION",
751
+ message: "Value is not a valid option"
752
+ });
753
+ return errors;
754
+ }
755
+ };
756
+ //#endregion
559
757
  //#region src/validators/number-validator.ts
560
758
  var NumberValidator = class {
561
759
  validate(ctx) {
@@ -683,6 +881,53 @@ var StringValidator = class {
683
881
  }
684
882
  };
685
883
  //#endregion
884
+ //#region src/validators/sustainability-validator.ts
885
+ const OPTIONAL_TEXT_PROPERTIES = ["ccri", "cmc"];
886
+ var SustainabilityValidator = class {
887
+ validate(ctx) {
888
+ const { fieldId, value } = ctx;
889
+ const validation = ctx.validation;
890
+ const errors = [];
891
+ if (value === null || value === void 0) {
892
+ if (validation?.required) errors.push({
893
+ fieldId,
894
+ rule: "REQUIRED",
895
+ message: "Value is required"
896
+ });
897
+ return errors;
898
+ }
899
+ if (typeof value !== "object" || Array.isArray(value)) {
900
+ errors.push({
901
+ fieldId,
902
+ rule: "TYPE",
903
+ message: "Must be a valid sustainability object",
904
+ params: { expectedType: "sustainability" }
905
+ });
906
+ return errors;
907
+ }
908
+ const record = value;
909
+ const address = record.address ?? "";
910
+ if (typeof address !== "string" || OPTIONAL_TEXT_PROPERTIES.some((key) => !isOptionalText(record[key]))) {
911
+ errors.push({
912
+ fieldId,
913
+ rule: "TYPE",
914
+ message: "Must be a valid sustainability object",
915
+ params: { expectedType: "sustainability" }
916
+ });
917
+ return errors;
918
+ }
919
+ if (validation?.required && address === "") errors.push({
920
+ fieldId,
921
+ rule: "REQUIRED",
922
+ message: "Value is required"
923
+ });
924
+ return errors;
925
+ }
926
+ };
927
+ function isOptionalText(value) {
928
+ return value === void 0 || value === null || typeof value === "string";
929
+ }
930
+ //#endregion
686
931
  //#region src/field-validator.ts
687
932
  /**
688
933
  * Validates form values against the schema's validation rules.
@@ -699,9 +944,19 @@ var StringValidator = class {
699
944
  * - `date` -- `required`, `minDate`, `maxDate`. Relative date boundaries
700
945
  * are resolved against `now`.
701
946
  * - `select` -- `required`, plus the value must be one of the defined options.
947
+ * - `multiselect` -- `required` (an absent value and an empty array both fail),
948
+ * plus every selected value must be one of the defined options.
949
+ * - `static` -- never validated; static blocks hold no value.
950
+ * - `blockchain` -- `required` only. The CAIP address format is never checked.
951
+ * - `sustainability` -- `required` (the value's `address` must be a non-empty
952
+ * string) plus a shape check; `ccri` and `cmc` are optional text.
702
953
  * - `array` -- `minItems`, `maxItems`, plus each item is validated
703
954
  * individually according to the array's {@link ArrayItemDef}. Item-level
704
955
  * errors carry an `itemIndex`.
956
+ * - `array_obj` -- `minItems`, `maxItems`, plus every sub-field of every row is
957
+ * validated according to the field's {@link ArrayObjItemDef} list. Sub-field
958
+ * errors carry an `itemIndex` (the row) and an `itemFieldId` (the sub-field),
959
+ * while `fieldId` stays the container's id.
705
960
  *
706
961
  * For all types, if `required` fails, no further rules are checked for that
707
962
  * field (early return). If the value is empty/absent and `required` is not
@@ -721,8 +976,12 @@ var FieldValidator = class {
721
976
  boolean: new BooleanValidator(),
722
977
  date: new DateValidator(),
723
978
  select: new SelectValidator(),
979
+ multiselect: new MultiselectValidator(),
724
980
  array: new ArrayValidator(),
725
- file: new FileValidator()
981
+ array_obj: new ArrayObjValidator(),
982
+ file: new FileValidator(),
983
+ blockchain: new BlockchainValidator(),
984
+ sustainability: new SustainabilityValidator()
726
985
  };
727
986
  }
728
987
  /**
@@ -763,6 +1022,17 @@ var FieldValidator = class {
763
1022
  };
764
1023
  return validator.validate(ctx);
765
1024
  }
1025
+ if (entry.type === "array_obj") {
1026
+ const ctx = {
1027
+ fieldId,
1028
+ value,
1029
+ validation: entry.validation,
1030
+ now,
1031
+ items: entry.items,
1032
+ validateField: this.validateField.bind(this)
1033
+ };
1034
+ return validator.validate(ctx);
1035
+ }
766
1036
  return validator.validate({
767
1037
  fieldId,
768
1038
  value,
@@ -817,11 +1087,17 @@ var FormDefinitionEditor = class {
817
1087
  }
818
1088
  /**
819
1089
  * Returns the next available numeric id (max existing + 1).
1090
+ *
1091
+ * `array_obj` sub-field ids share the form-wide id space, so they count
1092
+ * here too -- otherwise a new top-level field could collide with one.
820
1093
  */
821
1094
  nextId() {
822
1095
  let max = 0;
823
1096
  this.walkAll(this.definition.content, (item) => {
824
1097
  if (item.id > max) max = item.id;
1098
+ if (item.type === "array_obj") {
1099
+ for (const subField of item.items ?? []) if (subField.id > max) max = subField.id;
1100
+ }
825
1101
  });
826
1102
  return max + 1;
827
1103
  }
@@ -960,6 +1236,7 @@ var FormDefinitionEditor = class {
960
1236
  const item = this.findItem(id);
961
1237
  if (!item) throw new Error(`Item with id ${id} not found`);
962
1238
  if (item.type === "section") throw new Error("Sections do not have validation");
1239
+ if (item.type === "static") throw new Error("Static fields do not have validation");
963
1240
  const field = item;
964
1241
  if (validation === void 0) delete field.validation;
965
1242
  else field.validation = validation;
@@ -976,12 +1253,12 @@ var FormDefinitionEditor = class {
976
1253
  return this;
977
1254
  }
978
1255
  /**
979
- * Sets the select options for a `select` field.
1256
+ * Sets the options for a `select` or `multiselect` field.
980
1257
  */
981
1258
  setOptions(id, options) {
982
1259
  const item = this.findItem(id);
983
1260
  if (!item) throw new Error(`Item with id ${id} not found`);
984
- if (item.type !== "select") throw new Error(`Field ${id} is not a select field`);
1261
+ if (item.type !== "select" && item.type !== "multiselect") throw new Error(`Field ${id} is not a select or multiselect field`);
985
1262
  const field = item;
986
1263
  field.options = options;
987
1264
  return this;
@@ -998,6 +1275,94 @@ var FormDefinitionEditor = class {
998
1275
  return this;
999
1276
  }
1000
1277
  /**
1278
+ * Replaces the whole sub-field list of an `array_obj` field.
1279
+ *
1280
+ * @throws If the id is not found or does not refer to an `array_obj` field.
1281
+ */
1282
+ setArrayItems(id, itemDefs) {
1283
+ this.assertArrayObjField(id).items = itemDefs;
1284
+ return this;
1285
+ }
1286
+ /**
1287
+ * Returns the sub-field list of an `array_obj` field.
1288
+ *
1289
+ * @throws If the id is not found or does not refer to an `array_obj` field.
1290
+ */
1291
+ getArrayItems(id) {
1292
+ return this.assertArrayObjField(id).items ?? [];
1293
+ }
1294
+ /**
1295
+ * Sets or clears the layout hint of an `array_obj` field.
1296
+ *
1297
+ * Presentation metadata only -- the engine never reads it. Passing
1298
+ * `undefined` removes the key, which reads as `'list'`.
1299
+ *
1300
+ * @throws If the id is not found or does not refer to an `array_obj` field.
1301
+ */
1302
+ setArrayKind(id, kind) {
1303
+ const field = this.assertArrayObjField(id);
1304
+ if (kind === void 0) delete field.kind;
1305
+ else field.kind = kind;
1306
+ return this;
1307
+ }
1308
+ /**
1309
+ * Appends a sub-field to an `array_obj` field. The sub-field's `id` is
1310
+ * auto-assigned when omitted.
1311
+ *
1312
+ * @returns The id of the sub-field that was added.
1313
+ * @throws If the id is not found, does not refer to an `array_obj` field,
1314
+ * or the requested sub-field id is already taken.
1315
+ */
1316
+ addArrayItemField(id, subField) {
1317
+ const field = this.assertArrayObjField(id);
1318
+ const subFieldId = subField.id ?? this.nextId();
1319
+ this.assertIdAvailable(subFieldId);
1320
+ field.items?.push({
1321
+ ...subField,
1322
+ id: subFieldId
1323
+ });
1324
+ return subFieldId;
1325
+ }
1326
+ /**
1327
+ * Merges properties into an existing `array_obj` sub-field.
1328
+ *
1329
+ * Like {@link updateField}, omitted properties are kept rather than
1330
+ * removed -- pass a property explicitly as `undefined` to clear it.
1331
+ *
1332
+ * @throws If the field or the sub-field is not found.
1333
+ */
1334
+ updateArrayItemField(id, subFieldId, patch) {
1335
+ const subField = this.assertArrayObjField(id).items?.find((f) => f.id === subFieldId);
1336
+ if (!subField) throw new Error(`Sub-field with id ${subFieldId} not found in field ${id}`);
1337
+ Object.assign(subField, patch, { id: subFieldId });
1338
+ return this;
1339
+ }
1340
+ /**
1341
+ * Removes a sub-field from an `array_obj` field.
1342
+ *
1343
+ * @throws If the field or the sub-field is not found.
1344
+ */
1345
+ removeArrayItemField(id, subFieldId) {
1346
+ const field = this.assertArrayObjField(id);
1347
+ const index = field.items?.findIndex((f) => f.id === subFieldId) ?? -1;
1348
+ if (index < 0) throw new Error(`Sub-field with id ${subFieldId} not found in field ${id}`);
1349
+ field.items?.splice(index, 1);
1350
+ return this;
1351
+ }
1352
+ /**
1353
+ * Moves a sub-field of an `array_obj` field to another position.
1354
+ *
1355
+ * @throws If the field is not found or either index is out of range.
1356
+ */
1357
+ moveArrayItemField(id, fromIndex, toIndex) {
1358
+ const items = this.assertArrayObjField(id).items ?? [];
1359
+ if (fromIndex < 0 || fromIndex >= items.length) throw new Error(`Index ${fromIndex} out of range`);
1360
+ if (toIndex < 0 || toIndex >= items.length) throw new Error(`Index ${toIndex} out of range`);
1361
+ const [moved] = items.splice(fromIndex, 1);
1362
+ if (moved) items.splice(toIndex, 0, moved);
1363
+ return this;
1364
+ }
1365
+ /**
1001
1366
  * Sets the label for a field.
1002
1367
  */
1003
1368
  setLabel(id, label) {
@@ -1009,6 +1374,18 @@ var FormDefinitionEditor = class {
1009
1374
  return this;
1010
1375
  }
1011
1376
  /**
1377
+ * Sets the displayed text of a `static` field.
1378
+ *
1379
+ * @throws If the id is not found or does not refer to a static field.
1380
+ */
1381
+ setText(id, text) {
1382
+ const item = this.findItem(id);
1383
+ if (!item) throw new Error(`Item with id ${id} not found`);
1384
+ if (item.type !== "static") throw new Error(`Field ${id} is not a static field`);
1385
+ item.text = text;
1386
+ return this;
1387
+ }
1388
+ /**
1012
1389
  * Sets the description for a field or section.
1013
1390
  */
1014
1391
  setFieldDescription(id, description) {
@@ -1019,6 +1396,41 @@ var FormDefinitionEditor = class {
1019
1396
  return this;
1020
1397
  }
1021
1398
  /**
1399
+ * Sets the placeholder for a field or section.
1400
+ *
1401
+ * Passing `undefined` removes the property. Unlike {@link updateField},
1402
+ * which merges and therefore cannot clear, this genuinely deletes the key.
1403
+ */
1404
+ setPlaceholder(id, placeholder) {
1405
+ return this.setMetadata(id, "placeholder", placeholder);
1406
+ }
1407
+ /**
1408
+ * Sets the answer guidelines for a field or section.
1409
+ *
1410
+ * Passing `undefined` removes the property.
1411
+ */
1412
+ setAnswerGuidelines(id, answerGuidelines) {
1413
+ return this.setMetadata(id, "answer_guidelines", answerGuidelines);
1414
+ }
1415
+ /**
1416
+ * Sets the external reference id for a field or section.
1417
+ *
1418
+ * Passing `undefined` removes the property.
1419
+ */
1420
+ setReferenceId(id, referenceId) {
1421
+ return this.setMetadata(id, "reference_id", referenceId);
1422
+ }
1423
+ /**
1424
+ * Assigns or deletes one optional string property on a field or section.
1425
+ */
1426
+ setMetadata(id, property, value) {
1427
+ const item = this.findItem(id);
1428
+ if (!item) throw new Error(`Item with id ${id} not found`);
1429
+ if (value === void 0) delete item[property];
1430
+ else item[property] = value;
1431
+ return this;
1432
+ }
1433
+ /**
1022
1434
  * Returns a deep clone of the current form definition.
1023
1435
  */
1024
1436
  toJSON() {
@@ -1033,6 +1445,30 @@ var FormDefinitionEditor = class {
1033
1445
  }
1034
1446
  assertIdAvailable(id) {
1035
1447
  if (this.findItem(id)) throw new Error(`Item with id ${id} already exists`);
1448
+ if (this.findArrayObjItem(id)) throw new Error(`Item with id ${id} already exists`);
1449
+ }
1450
+ /**
1451
+ * Finds an `array_obj` sub-field anywhere in the definition by its id.
1452
+ */
1453
+ findArrayObjItem(id) {
1454
+ let found;
1455
+ this.walkAll(this.definition.content, (item) => {
1456
+ if (found || item.type !== "array_obj") return;
1457
+ const subField = (item.items ?? []).find((f) => f.id === id);
1458
+ if (subField) found = {
1459
+ field: item,
1460
+ subField
1461
+ };
1462
+ });
1463
+ return found;
1464
+ }
1465
+ assertArrayObjField(id) {
1466
+ const item = this.findItem(id);
1467
+ if (!item) throw new Error(`Item with id ${id} not found`);
1468
+ if (item.type !== "array_obj") throw new Error(`Field ${id} is not an array_obj field`);
1469
+ const field = item;
1470
+ if (!field.items) field.items = [];
1471
+ return field;
1036
1472
  }
1037
1473
  insertItem(item, parentId, index) {
1038
1474
  const target = this.getTargetContent(parentId);
@@ -1244,6 +1680,11 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1244
1680
  "properties": { "required": { "type": "boolean" } },
1245
1681
  "additionalProperties": false
1246
1682
  },
1683
+ "multiselectValidation": {
1684
+ "type": "object",
1685
+ "properties": { "required": { "type": "boolean" } },
1686
+ "additionalProperties": false
1687
+ },
1247
1688
  "arrayValidation": {
1248
1689
  "type": "object",
1249
1690
  "properties": {
@@ -1258,11 +1699,35 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1258
1699
  },
1259
1700
  "additionalProperties": false
1260
1701
  },
1702
+ "arrayObjValidation": {
1703
+ "type": "object",
1704
+ "properties": {
1705
+ "minItems": {
1706
+ "type": "integer",
1707
+ "minimum": 0
1708
+ },
1709
+ "maxItems": {
1710
+ "type": "integer",
1711
+ "minimum": 1
1712
+ }
1713
+ },
1714
+ "additionalProperties": false
1715
+ },
1261
1716
  "fileValidation": {
1262
1717
  "type": "object",
1263
1718
  "properties": { "required": { "type": "boolean" } },
1264
1719
  "additionalProperties": false
1265
1720
  },
1721
+ "blockchainValidation": {
1722
+ "type": "object",
1723
+ "properties": { "required": { "type": "boolean" } },
1724
+ "additionalProperties": false
1725
+ },
1726
+ "sustainabilityValidation": {
1727
+ "type": "object",
1728
+ "properties": { "required": { "type": "boolean" } },
1729
+ "additionalProperties": false
1730
+ },
1266
1731
  "selectOption": {
1267
1732
  "type": "object",
1268
1733
  "required": ["value", "label"],
@@ -1279,6 +1744,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1279
1744
  "type": { "const": "string" },
1280
1745
  "label": { "type": "string" },
1281
1746
  "description": { "type": "string" },
1747
+ "placeholder": { "type": "string" },
1748
+ "answer_guidelines": { "type": "string" },
1749
+ "reference_id": { "type": "string" },
1282
1750
  "validation": { "$ref": "#/$defs/stringValidation" }
1283
1751
  },
1284
1752
  "additionalProperties": false
@@ -1290,6 +1758,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1290
1758
  "type": { "const": "number" },
1291
1759
  "label": { "type": "string" },
1292
1760
  "description": { "type": "string" },
1761
+ "placeholder": { "type": "string" },
1762
+ "answer_guidelines": { "type": "string" },
1763
+ "reference_id": { "type": "string" },
1293
1764
  "validation": { "$ref": "#/$defs/numberValidation" }
1294
1765
  },
1295
1766
  "additionalProperties": false
@@ -1301,6 +1772,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1301
1772
  "type": { "const": "boolean" },
1302
1773
  "label": { "type": "string" },
1303
1774
  "description": { "type": "string" },
1775
+ "placeholder": { "type": "string" },
1776
+ "answer_guidelines": { "type": "string" },
1777
+ "reference_id": { "type": "string" },
1304
1778
  "validation": { "$ref": "#/$defs/booleanValidation" }
1305
1779
  },
1306
1780
  "additionalProperties": false
@@ -1312,6 +1786,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1312
1786
  "type": { "const": "date" },
1313
1787
  "label": { "type": "string" },
1314
1788
  "description": { "type": "string" },
1789
+ "placeholder": { "type": "string" },
1790
+ "answer_guidelines": { "type": "string" },
1791
+ "reference_id": { "type": "string" },
1315
1792
  "validation": { "$ref": "#/$defs/dateValidation" }
1316
1793
  },
1317
1794
  "additionalProperties": false
@@ -1327,6 +1804,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1327
1804
  "type": { "const": "select" },
1328
1805
  "label": { "type": "string" },
1329
1806
  "description": { "type": "string" },
1807
+ "placeholder": { "type": "string" },
1808
+ "answer_guidelines": { "type": "string" },
1809
+ "reference_id": { "type": "string" },
1330
1810
  "options": {
1331
1811
  "type": "array",
1332
1812
  "minItems": 1,
@@ -1343,19 +1823,335 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1343
1823
  "type": { "const": "file" },
1344
1824
  "label": { "type": "string" },
1345
1825
  "description": { "type": "string" },
1826
+ "placeholder": { "type": "string" },
1827
+ "answer_guidelines": { "type": "string" },
1828
+ "reference_id": { "type": "string" },
1346
1829
  "validation": { "$ref": "#/$defs/fileValidation" }
1347
1830
  },
1348
1831
  "additionalProperties": false
1349
1832
  },
1350
- "arrayItem": { "oneOf": [
1351
- { "$ref": "#/$defs/arrayItemString" },
1352
- { "$ref": "#/$defs/arrayItemNumber" },
1353
- { "$ref": "#/$defs/arrayItemBoolean" },
1354
- { "$ref": "#/$defs/arrayItemDate" },
1355
- { "$ref": "#/$defs/arrayItemSelect" },
1356
- { "$ref": "#/$defs/arrayItemFile" }
1357
- ] },
1358
- "stringField": {
1833
+ "arrayItemBlockchain": {
1834
+ "type": "object",
1835
+ "required": ["type", "label"],
1836
+ "properties": {
1837
+ "type": { "const": "blockchain" },
1838
+ "label": { "type": "string" },
1839
+ "description": { "type": "string" },
1840
+ "placeholder": { "type": "string" },
1841
+ "answer_guidelines": { "type": "string" },
1842
+ "reference_id": { "type": "string" },
1843
+ "validation": { "$ref": "#/$defs/blockchainValidation" }
1844
+ },
1845
+ "additionalProperties": false
1846
+ },
1847
+ "arrayItemSustainability": {
1848
+ "type": "object",
1849
+ "required": ["type", "label"],
1850
+ "properties": {
1851
+ "type": { "const": "sustainability" },
1852
+ "label": { "type": "string" },
1853
+ "description": { "type": "string" },
1854
+ "placeholder": { "type": "string" },
1855
+ "answer_guidelines": { "type": "string" },
1856
+ "reference_id": { "type": "string" },
1857
+ "validation": { "$ref": "#/$defs/sustainabilityValidation" }
1858
+ },
1859
+ "additionalProperties": false
1860
+ },
1861
+ "arrayItemMultiselect": {
1862
+ "type": "object",
1863
+ "required": [
1864
+ "type",
1865
+ "label",
1866
+ "options"
1867
+ ],
1868
+ "properties": {
1869
+ "type": { "const": "multiselect" },
1870
+ "label": { "type": "string" },
1871
+ "description": { "type": "string" },
1872
+ "placeholder": { "type": "string" },
1873
+ "answer_guidelines": { "type": "string" },
1874
+ "reference_id": { "type": "string" },
1875
+ "options": {
1876
+ "type": "array",
1877
+ "minItems": 1,
1878
+ "items": { "$ref": "#/$defs/selectOption" }
1879
+ },
1880
+ "validation": { "$ref": "#/$defs/multiselectValidation" }
1881
+ },
1882
+ "additionalProperties": false
1883
+ },
1884
+ "arrayItemStatic": {
1885
+ "type": "object",
1886
+ "required": ["type", "text"],
1887
+ "properties": {
1888
+ "type": { "const": "static" },
1889
+ "text": { "type": "string" },
1890
+ "label": { "type": "string" },
1891
+ "description": { "type": "string" },
1892
+ "placeholder": { "type": "string" },
1893
+ "answer_guidelines": { "type": "string" },
1894
+ "reference_id": { "type": "string" }
1895
+ },
1896
+ "additionalProperties": false
1897
+ },
1898
+ "arrayItem": { "oneOf": [
1899
+ { "$ref": "#/$defs/arrayItemString" },
1900
+ { "$ref": "#/$defs/arrayItemNumber" },
1901
+ { "$ref": "#/$defs/arrayItemBoolean" },
1902
+ { "$ref": "#/$defs/arrayItemDate" },
1903
+ { "$ref": "#/$defs/arrayItemSelect" },
1904
+ { "$ref": "#/$defs/arrayItemFile" },
1905
+ { "$ref": "#/$defs/arrayItemBlockchain" },
1906
+ { "$ref": "#/$defs/arrayItemSustainability" },
1907
+ { "$ref": "#/$defs/arrayItemMultiselect" },
1908
+ { "$ref": "#/$defs/arrayItemStatic" }
1909
+ ] },
1910
+ "arrayObjItemString": {
1911
+ "type": "object",
1912
+ "required": [
1913
+ "id",
1914
+ "type",
1915
+ "label"
1916
+ ],
1917
+ "properties": {
1918
+ "id": {
1919
+ "type": "integer",
1920
+ "minimum": 1
1921
+ },
1922
+ "type": { "const": "string" },
1923
+ "label": { "type": "string" },
1924
+ "description": { "type": "string" },
1925
+ "placeholder": { "type": "string" },
1926
+ "answer_guidelines": { "type": "string" },
1927
+ "reference_id": { "type": "string" },
1928
+ "validation": { "$ref": "#/$defs/stringValidation" }
1929
+ },
1930
+ "additionalProperties": false
1931
+ },
1932
+ "arrayObjItemNumber": {
1933
+ "type": "object",
1934
+ "required": [
1935
+ "id",
1936
+ "type",
1937
+ "label"
1938
+ ],
1939
+ "properties": {
1940
+ "id": {
1941
+ "type": "integer",
1942
+ "minimum": 1
1943
+ },
1944
+ "type": { "const": "number" },
1945
+ "label": { "type": "string" },
1946
+ "description": { "type": "string" },
1947
+ "placeholder": { "type": "string" },
1948
+ "answer_guidelines": { "type": "string" },
1949
+ "reference_id": { "type": "string" },
1950
+ "validation": { "$ref": "#/$defs/numberValidation" }
1951
+ },
1952
+ "additionalProperties": false
1953
+ },
1954
+ "arrayObjItemBoolean": {
1955
+ "type": "object",
1956
+ "required": [
1957
+ "id",
1958
+ "type",
1959
+ "label"
1960
+ ],
1961
+ "properties": {
1962
+ "id": {
1963
+ "type": "integer",
1964
+ "minimum": 1
1965
+ },
1966
+ "type": { "const": "boolean" },
1967
+ "label": { "type": "string" },
1968
+ "description": { "type": "string" },
1969
+ "placeholder": { "type": "string" },
1970
+ "answer_guidelines": { "type": "string" },
1971
+ "reference_id": { "type": "string" },
1972
+ "validation": { "$ref": "#/$defs/booleanValidation" }
1973
+ },
1974
+ "additionalProperties": false
1975
+ },
1976
+ "arrayObjItemDate": {
1977
+ "type": "object",
1978
+ "required": [
1979
+ "id",
1980
+ "type",
1981
+ "label"
1982
+ ],
1983
+ "properties": {
1984
+ "id": {
1985
+ "type": "integer",
1986
+ "minimum": 1
1987
+ },
1988
+ "type": { "const": "date" },
1989
+ "label": { "type": "string" },
1990
+ "description": { "type": "string" },
1991
+ "placeholder": { "type": "string" },
1992
+ "answer_guidelines": { "type": "string" },
1993
+ "reference_id": { "type": "string" },
1994
+ "validation": { "$ref": "#/$defs/dateValidation" }
1995
+ },
1996
+ "additionalProperties": false
1997
+ },
1998
+ "arrayObjItemSelect": {
1999
+ "type": "object",
2000
+ "required": [
2001
+ "id",
2002
+ "type",
2003
+ "label",
2004
+ "options"
2005
+ ],
2006
+ "properties": {
2007
+ "id": {
2008
+ "type": "integer",
2009
+ "minimum": 1
2010
+ },
2011
+ "type": { "const": "select" },
2012
+ "label": { "type": "string" },
2013
+ "description": { "type": "string" },
2014
+ "placeholder": { "type": "string" },
2015
+ "answer_guidelines": { "type": "string" },
2016
+ "reference_id": { "type": "string" },
2017
+ "options": {
2018
+ "type": "array",
2019
+ "minItems": 1,
2020
+ "items": { "$ref": "#/$defs/selectOption" }
2021
+ },
2022
+ "validation": { "$ref": "#/$defs/selectValidation" }
2023
+ },
2024
+ "additionalProperties": false
2025
+ },
2026
+ "arrayObjItemMultiselect": {
2027
+ "type": "object",
2028
+ "required": [
2029
+ "id",
2030
+ "type",
2031
+ "label",
2032
+ "options"
2033
+ ],
2034
+ "properties": {
2035
+ "id": {
2036
+ "type": "integer",
2037
+ "minimum": 1
2038
+ },
2039
+ "type": { "const": "multiselect" },
2040
+ "label": { "type": "string" },
2041
+ "description": { "type": "string" },
2042
+ "placeholder": { "type": "string" },
2043
+ "answer_guidelines": { "type": "string" },
2044
+ "reference_id": { "type": "string" },
2045
+ "options": {
2046
+ "type": "array",
2047
+ "minItems": 1,
2048
+ "items": { "$ref": "#/$defs/selectOption" }
2049
+ },
2050
+ "validation": { "$ref": "#/$defs/multiselectValidation" }
2051
+ },
2052
+ "additionalProperties": false
2053
+ },
2054
+ "arrayObjItemFile": {
2055
+ "type": "object",
2056
+ "required": [
2057
+ "id",
2058
+ "type",
2059
+ "label"
2060
+ ],
2061
+ "properties": {
2062
+ "id": {
2063
+ "type": "integer",
2064
+ "minimum": 1
2065
+ },
2066
+ "type": { "const": "file" },
2067
+ "label": { "type": "string" },
2068
+ "description": { "type": "string" },
2069
+ "placeholder": { "type": "string" },
2070
+ "answer_guidelines": { "type": "string" },
2071
+ "reference_id": { "type": "string" },
2072
+ "validation": { "$ref": "#/$defs/fileValidation" }
2073
+ },
2074
+ "additionalProperties": false
2075
+ },
2076
+ "arrayObjItemBlockchain": {
2077
+ "type": "object",
2078
+ "required": [
2079
+ "id",
2080
+ "type",
2081
+ "label"
2082
+ ],
2083
+ "properties": {
2084
+ "id": {
2085
+ "type": "integer",
2086
+ "minimum": 1
2087
+ },
2088
+ "type": { "const": "blockchain" },
2089
+ "label": { "type": "string" },
2090
+ "description": { "type": "string" },
2091
+ "placeholder": { "type": "string" },
2092
+ "answer_guidelines": { "type": "string" },
2093
+ "reference_id": { "type": "string" },
2094
+ "validation": { "$ref": "#/$defs/blockchainValidation" }
2095
+ },
2096
+ "additionalProperties": false
2097
+ },
2098
+ "arrayObjItemSustainability": {
2099
+ "type": "object",
2100
+ "required": [
2101
+ "id",
2102
+ "type",
2103
+ "label"
2104
+ ],
2105
+ "properties": {
2106
+ "id": {
2107
+ "type": "integer",
2108
+ "minimum": 1
2109
+ },
2110
+ "type": { "const": "sustainability" },
2111
+ "label": { "type": "string" },
2112
+ "description": { "type": "string" },
2113
+ "placeholder": { "type": "string" },
2114
+ "answer_guidelines": { "type": "string" },
2115
+ "reference_id": { "type": "string" },
2116
+ "validation": { "$ref": "#/$defs/sustainabilityValidation" }
2117
+ },
2118
+ "additionalProperties": false
2119
+ },
2120
+ "arrayObjItemStatic": {
2121
+ "type": "object",
2122
+ "required": [
2123
+ "id",
2124
+ "type",
2125
+ "text"
2126
+ ],
2127
+ "properties": {
2128
+ "id": {
2129
+ "type": "integer",
2130
+ "minimum": 1
2131
+ },
2132
+ "type": { "const": "static" },
2133
+ "text": { "type": "string" },
2134
+ "label": { "type": "string" },
2135
+ "description": { "type": "string" },
2136
+ "placeholder": { "type": "string" },
2137
+ "answer_guidelines": { "type": "string" },
2138
+ "reference_id": { "type": "string" }
2139
+ },
2140
+ "additionalProperties": false
2141
+ },
2142
+ "arrayObjItem": { "oneOf": [
2143
+ { "$ref": "#/$defs/arrayObjItemString" },
2144
+ { "$ref": "#/$defs/arrayObjItemNumber" },
2145
+ { "$ref": "#/$defs/arrayObjItemBoolean" },
2146
+ { "$ref": "#/$defs/arrayObjItemDate" },
2147
+ { "$ref": "#/$defs/arrayObjItemSelect" },
2148
+ { "$ref": "#/$defs/arrayObjItemMultiselect" },
2149
+ { "$ref": "#/$defs/arrayObjItemFile" },
2150
+ { "$ref": "#/$defs/arrayObjItemBlockchain" },
2151
+ { "$ref": "#/$defs/arrayObjItemSustainability" },
2152
+ { "$ref": "#/$defs/arrayObjItemStatic" }
2153
+ ] },
2154
+ "stringField": {
1359
2155
  "type": "object",
1360
2156
  "required": [
1361
2157
  "id",
@@ -1370,6 +2166,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1370
2166
  "type": { "const": "string" },
1371
2167
  "label": { "type": "string" },
1372
2168
  "description": { "type": "string" },
2169
+ "placeholder": { "type": "string" },
2170
+ "answer_guidelines": { "type": "string" },
2171
+ "reference_id": { "type": "string" },
1373
2172
  "condition": { "$ref": "#/$defs/condition" },
1374
2173
  "validation": { "$ref": "#/$defs/stringValidation" }
1375
2174
  },
@@ -1390,6 +2189,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1390
2189
  "type": { "const": "number" },
1391
2190
  "label": { "type": "string" },
1392
2191
  "description": { "type": "string" },
2192
+ "placeholder": { "type": "string" },
2193
+ "answer_guidelines": { "type": "string" },
2194
+ "reference_id": { "type": "string" },
1393
2195
  "condition": { "$ref": "#/$defs/condition" },
1394
2196
  "validation": { "$ref": "#/$defs/numberValidation" }
1395
2197
  },
@@ -1410,6 +2212,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1410
2212
  "type": { "const": "boolean" },
1411
2213
  "label": { "type": "string" },
1412
2214
  "description": { "type": "string" },
2215
+ "placeholder": { "type": "string" },
2216
+ "answer_guidelines": { "type": "string" },
2217
+ "reference_id": { "type": "string" },
1413
2218
  "condition": { "$ref": "#/$defs/condition" },
1414
2219
  "validation": { "$ref": "#/$defs/booleanValidation" }
1415
2220
  },
@@ -1430,6 +2235,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1430
2235
  "type": { "const": "date" },
1431
2236
  "label": { "type": "string" },
1432
2237
  "description": { "type": "string" },
2238
+ "placeholder": { "type": "string" },
2239
+ "answer_guidelines": { "type": "string" },
2240
+ "reference_id": { "type": "string" },
1433
2241
  "condition": { "$ref": "#/$defs/condition" },
1434
2242
  "validation": { "$ref": "#/$defs/dateValidation" }
1435
2243
  },
@@ -1451,6 +2259,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1451
2259
  "type": { "const": "select" },
1452
2260
  "label": { "type": "string" },
1453
2261
  "description": { "type": "string" },
2262
+ "placeholder": { "type": "string" },
2263
+ "answer_guidelines": { "type": "string" },
2264
+ "reference_id": { "type": "string" },
1454
2265
  "condition": { "$ref": "#/$defs/condition" },
1455
2266
  "options": {
1456
2267
  "type": "array",
@@ -1461,6 +2272,58 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1461
2272
  },
1462
2273
  "additionalProperties": false
1463
2274
  },
2275
+ "multiselectField": {
2276
+ "type": "object",
2277
+ "required": [
2278
+ "id",
2279
+ "type",
2280
+ "label",
2281
+ "options"
2282
+ ],
2283
+ "properties": {
2284
+ "id": {
2285
+ "type": "integer",
2286
+ "minimum": 1
2287
+ },
2288
+ "type": { "const": "multiselect" },
2289
+ "label": { "type": "string" },
2290
+ "description": { "type": "string" },
2291
+ "placeholder": { "type": "string" },
2292
+ "answer_guidelines": { "type": "string" },
2293
+ "reference_id": { "type": "string" },
2294
+ "condition": { "$ref": "#/$defs/condition" },
2295
+ "options": {
2296
+ "type": "array",
2297
+ "minItems": 1,
2298
+ "items": { "$ref": "#/$defs/selectOption" }
2299
+ },
2300
+ "validation": { "$ref": "#/$defs/multiselectValidation" }
2301
+ },
2302
+ "additionalProperties": false
2303
+ },
2304
+ "staticField": {
2305
+ "type": "object",
2306
+ "required": [
2307
+ "id",
2308
+ "type",
2309
+ "text"
2310
+ ],
2311
+ "properties": {
2312
+ "id": {
2313
+ "type": "integer",
2314
+ "minimum": 1
2315
+ },
2316
+ "type": { "const": "static" },
2317
+ "text": { "type": "string" },
2318
+ "label": { "type": "string" },
2319
+ "description": { "type": "string" },
2320
+ "placeholder": { "type": "string" },
2321
+ "answer_guidelines": { "type": "string" },
2322
+ "reference_id": { "type": "string" },
2323
+ "condition": { "$ref": "#/$defs/condition" }
2324
+ },
2325
+ "additionalProperties": false
2326
+ },
1464
2327
  "arrayField": {
1465
2328
  "type": "object",
1466
2329
  "required": [
@@ -1477,12 +2340,48 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1477
2340
  "type": { "const": "array" },
1478
2341
  "label": { "type": "string" },
1479
2342
  "description": { "type": "string" },
2343
+ "placeholder": { "type": "string" },
2344
+ "answer_guidelines": { "type": "string" },
2345
+ "reference_id": { "type": "string" },
1480
2346
  "condition": { "$ref": "#/$defs/condition" },
1481
2347
  "item": { "$ref": "#/$defs/arrayItem" },
1482
2348
  "validation": { "$ref": "#/$defs/arrayValidation" }
1483
2349
  },
1484
2350
  "additionalProperties": false
1485
2351
  },
2352
+ "arrayObjField": {
2353
+ "type": "object",
2354
+ "required": [
2355
+ "id",
2356
+ "type",
2357
+ "label",
2358
+ "items"
2359
+ ],
2360
+ "properties": {
2361
+ "id": {
2362
+ "type": "integer",
2363
+ "minimum": 1
2364
+ },
2365
+ "type": { "const": "array_obj" },
2366
+ "label": { "type": "string" },
2367
+ "description": { "type": "string" },
2368
+ "placeholder": { "type": "string" },
2369
+ "answer_guidelines": { "type": "string" },
2370
+ "reference_id": { "type": "string" },
2371
+ "condition": { "$ref": "#/$defs/condition" },
2372
+ "items": {
2373
+ "type": "array",
2374
+ "minItems": 1,
2375
+ "items": { "$ref": "#/$defs/arrayObjItem" }
2376
+ },
2377
+ "kind": {
2378
+ "type": "string",
2379
+ "enum": ["list", "table"]
2380
+ },
2381
+ "validation": { "$ref": "#/$defs/arrayObjValidation" }
2382
+ },
2383
+ "additionalProperties": false
2384
+ },
1486
2385
  "fileField": {
1487
2386
  "type": "object",
1488
2387
  "required": [
@@ -1498,19 +2397,73 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1498
2397
  "type": { "const": "file" },
1499
2398
  "label": { "type": "string" },
1500
2399
  "description": { "type": "string" },
2400
+ "placeholder": { "type": "string" },
2401
+ "answer_guidelines": { "type": "string" },
2402
+ "reference_id": { "type": "string" },
1501
2403
  "condition": { "$ref": "#/$defs/condition" },
1502
2404
  "validation": { "$ref": "#/$defs/fileValidation" }
1503
2405
  },
1504
2406
  "additionalProperties": false
1505
2407
  },
2408
+ "blockchainField": {
2409
+ "type": "object",
2410
+ "required": [
2411
+ "id",
2412
+ "type",
2413
+ "label"
2414
+ ],
2415
+ "properties": {
2416
+ "id": {
2417
+ "type": "integer",
2418
+ "minimum": 1
2419
+ },
2420
+ "type": { "const": "blockchain" },
2421
+ "label": { "type": "string" },
2422
+ "description": { "type": "string" },
2423
+ "placeholder": { "type": "string" },
2424
+ "answer_guidelines": { "type": "string" },
2425
+ "reference_id": { "type": "string" },
2426
+ "condition": { "$ref": "#/$defs/condition" },
2427
+ "validation": { "$ref": "#/$defs/blockchainValidation" }
2428
+ },
2429
+ "additionalProperties": false
2430
+ },
2431
+ "sustainabilityField": {
2432
+ "type": "object",
2433
+ "required": [
2434
+ "id",
2435
+ "type",
2436
+ "label"
2437
+ ],
2438
+ "properties": {
2439
+ "id": {
2440
+ "type": "integer",
2441
+ "minimum": 1
2442
+ },
2443
+ "type": { "const": "sustainability" },
2444
+ "label": { "type": "string" },
2445
+ "description": { "type": "string" },
2446
+ "placeholder": { "type": "string" },
2447
+ "answer_guidelines": { "type": "string" },
2448
+ "reference_id": { "type": "string" },
2449
+ "condition": { "$ref": "#/$defs/condition" },
2450
+ "validation": { "$ref": "#/$defs/sustainabilityValidation" }
2451
+ },
2452
+ "additionalProperties": false
2453
+ },
1506
2454
  "fieldItem": { "oneOf": [
1507
2455
  { "$ref": "#/$defs/stringField" },
1508
2456
  { "$ref": "#/$defs/numberField" },
1509
2457
  { "$ref": "#/$defs/booleanField" },
1510
2458
  { "$ref": "#/$defs/dateField" },
1511
2459
  { "$ref": "#/$defs/selectField" },
2460
+ { "$ref": "#/$defs/multiselectField" },
1512
2461
  { "$ref": "#/$defs/arrayField" },
1513
- { "$ref": "#/$defs/fileField" }
2462
+ { "$ref": "#/$defs/arrayObjField" },
2463
+ { "$ref": "#/$defs/fileField" },
2464
+ { "$ref": "#/$defs/staticField" },
2465
+ { "$ref": "#/$defs/blockchainField" },
2466
+ { "$ref": "#/$defs/sustainabilityField" }
1514
2467
  ] },
1515
2468
  "section": {
1516
2469
  "type": "object",
@@ -1528,6 +2481,9 @@ const validateFn = new ajv_dist_2020.default({ allErrors: true }).compile({
1528
2481
  "type": { "const": "section" },
1529
2482
  "title": { "type": "string" },
1530
2483
  "description": { "type": "string" },
2484
+ "placeholder": { "type": "string" },
2485
+ "answer_guidelines": { "type": "string" },
2486
+ "reference_id": { "type": "string" },
1531
2487
  "condition": { "$ref": "#/$defs/condition" },
1532
2488
  "content": {
1533
2489
  "type": "array",
@@ -1567,17 +2523,44 @@ const itemRequiredProperties = new Map([
1567
2523
  "label",
1568
2524
  "options"
1569
2525
  ])],
2526
+ ["multiselect", new Set([
2527
+ "id",
2528
+ "type",
2529
+ "label",
2530
+ "options"
2531
+ ])],
1570
2532
  ["array", new Set([
1571
2533
  "id",
1572
2534
  "type",
1573
2535
  "label",
1574
2536
  "item"
1575
2537
  ])],
2538
+ ["array_obj", new Set([
2539
+ "id",
2540
+ "type",
2541
+ "label",
2542
+ "items"
2543
+ ])],
1576
2544
  ["file", new Set([
1577
2545
  "id",
1578
2546
  "type",
1579
2547
  "label"
1580
2548
  ])],
2549
+ ["blockchain", new Set([
2550
+ "id",
2551
+ "type",
2552
+ "label"
2553
+ ])],
2554
+ ["sustainability", new Set([
2555
+ "id",
2556
+ "type",
2557
+ "label"
2558
+ ])],
2559
+ ["static", new Set([
2560
+ "id",
2561
+ "type",
2562
+ "text"
2563
+ ])],
1581
2564
  ["section", new Set([
1582
2565
  "id",
1583
2566
  "type",
@@ -1591,6 +2574,9 @@ const itemAllowedProperties = new Map([
1591
2574
  "type",
1592
2575
  "label",
1593
2576
  "description",
2577
+ "placeholder",
2578
+ "answer_guidelines",
2579
+ "reference_id",
1594
2580
  "condition",
1595
2581
  "validation"
1596
2582
  ])],
@@ -1599,6 +2585,9 @@ const itemAllowedProperties = new Map([
1599
2585
  "type",
1600
2586
  "label",
1601
2587
  "description",
2588
+ "placeholder",
2589
+ "answer_guidelines",
2590
+ "reference_id",
1602
2591
  "condition",
1603
2592
  "validation"
1604
2593
  ])],
@@ -1607,6 +2596,9 @@ const itemAllowedProperties = new Map([
1607
2596
  "type",
1608
2597
  "label",
1609
2598
  "description",
2599
+ "placeholder",
2600
+ "answer_guidelines",
2601
+ "reference_id",
1610
2602
  "condition",
1611
2603
  "validation"
1612
2604
  ])],
@@ -1615,6 +2607,9 @@ const itemAllowedProperties = new Map([
1615
2607
  "type",
1616
2608
  "label",
1617
2609
  "description",
2610
+ "placeholder",
2611
+ "answer_guidelines",
2612
+ "reference_id",
1618
2613
  "condition",
1619
2614
  "validation"
1620
2615
  ])],
@@ -1623,6 +2618,21 @@ const itemAllowedProperties = new Map([
1623
2618
  "type",
1624
2619
  "label",
1625
2620
  "description",
2621
+ "placeholder",
2622
+ "answer_guidelines",
2623
+ "reference_id",
2624
+ "condition",
2625
+ "options",
2626
+ "validation"
2627
+ ])],
2628
+ ["multiselect", new Set([
2629
+ "id",
2630
+ "type",
2631
+ "label",
2632
+ "description",
2633
+ "placeholder",
2634
+ "answer_guidelines",
2635
+ "reference_id",
1626
2636
  "condition",
1627
2637
  "options",
1628
2638
  "validation"
@@ -1632,23 +2642,78 @@ const itemAllowedProperties = new Map([
1632
2642
  "type",
1633
2643
  "label",
1634
2644
  "description",
2645
+ "placeholder",
2646
+ "answer_guidelines",
2647
+ "reference_id",
1635
2648
  "condition",
1636
2649
  "item",
1637
2650
  "validation"
1638
2651
  ])],
2652
+ ["array_obj", new Set([
2653
+ "id",
2654
+ "type",
2655
+ "label",
2656
+ "description",
2657
+ "placeholder",
2658
+ "answer_guidelines",
2659
+ "reference_id",
2660
+ "condition",
2661
+ "items",
2662
+ "kind",
2663
+ "validation"
2664
+ ])],
1639
2665
  ["file", new Set([
1640
2666
  "id",
1641
2667
  "type",
1642
2668
  "label",
1643
2669
  "description",
2670
+ "placeholder",
2671
+ "answer_guidelines",
2672
+ "reference_id",
2673
+ "condition",
2674
+ "validation"
2675
+ ])],
2676
+ ["blockchain", new Set([
2677
+ "id",
2678
+ "type",
2679
+ "label",
2680
+ "description",
2681
+ "placeholder",
2682
+ "answer_guidelines",
2683
+ "reference_id",
2684
+ "condition",
2685
+ "validation"
2686
+ ])],
2687
+ ["sustainability", new Set([
2688
+ "id",
2689
+ "type",
2690
+ "label",
2691
+ "description",
2692
+ "placeholder",
2693
+ "answer_guidelines",
2694
+ "reference_id",
1644
2695
  "condition",
1645
2696
  "validation"
1646
2697
  ])],
2698
+ ["static", new Set([
2699
+ "id",
2700
+ "type",
2701
+ "text",
2702
+ "label",
2703
+ "description",
2704
+ "placeholder",
2705
+ "answer_guidelines",
2706
+ "reference_id",
2707
+ "condition"
2708
+ ])],
1647
2709
  ["section", new Set([
1648
2710
  "id",
1649
2711
  "type",
1650
2712
  "title",
1651
2713
  "description",
2714
+ "placeholder",
2715
+ "answer_guidelines",
2716
+ "reference_id",
1652
2717
  "condition",
1653
2718
  "content"
1654
2719
  ])]
@@ -1666,12 +2731,17 @@ const itemAllowedProperties = new Map([
1666
2731
  * ### Semantic validation (`validate`)
1667
2732
  * Checks for logical issues that go beyond JSON schema validity:
1668
2733
  * 1. **Duplicate IDs** (`DUPLICATE_ID`) -- every content item id must be unique.
2734
+ * `array_obj` sub-field ids share that id space and are checked with it.
1669
2735
  * 2. **Nesting depth** (`NESTING_DEPTH`) -- sections may not be nested more
1670
2736
  * than 3 levels deep.
1671
2737
  * 3. **Unknown field references** (`UNKNOWN_FIELD_REF`) -- conditions must
1672
2738
  * only reference field ids that exist in the registry.
1673
- * 4. **Condition references section** (`CONDITION_REFS_SECTION`) -- conditions
1674
- * must not reference section ids, because sections have no values.
2739
+ * 4. **Condition references a valueless item** (`CONDITION_REFS_SECTION`,
2740
+ * `CONDITION_REFS_STATIC`) -- conditions must not reference section or
2741
+ * static ids, because neither holds a value.
2742
+ * 4b. **Condition references an `array_obj` sub-field** (`CONDITION_REFS_ARRAY_OBJ_ITEM`)
2743
+ * -- a sub-field holds one value per row, so a form-level condition cannot
2744
+ * say which row it means.
1675
2745
  * 5. **Constraint contradictions** (`INVALID_MIN_MAX`) -- e.g. `minLength > maxLength`,
1676
2746
  * `min > max`, `minDate > maxDate` (absolute dates only), `minItems > maxItems`.
1677
2747
  * 6. **Invalid regex** (`INVALID_REGEX`) -- string field `pattern` values must
@@ -1869,22 +2939,51 @@ var FormDefinitionValidator = class {
1869
2939
  const issues = [];
1870
2940
  this.checkDuplicateIds(definition.content, issues);
1871
2941
  this.checkNestingDepth(definition.content, 0, issues);
1872
- this.checkConditionRefs(registry, issues);
1873
- this.checkConditionRefsSection(registry, issues);
2942
+ const arrayObjItemIds = this.collectArrayObjItemIds(definition.content);
2943
+ this.checkConditionRefsArrayObjItem(registry, arrayObjItemIds, issues);
2944
+ this.checkConditionRefs(registry, arrayObjItemIds, issues);
2945
+ this.checkConditionRefsValueless(registry, issues);
1874
2946
  this.checkConstraintContradictions(registry, issues);
1875
2947
  this.checkInvalidRegex(registry, issues);
1876
2948
  return issues;
1877
2949
  }
1878
2950
  checkDuplicateIds(content, issues) {
1879
2951
  const seen = /* @__PURE__ */ new Set();
1880
- this.walkItems(content, (item) => {
1881
- if (seen.has(item.id)) issues.push({
2952
+ const record = (id) => {
2953
+ if (seen.has(id)) issues.push({
1882
2954
  code: "DUPLICATE_ID",
1883
- message: `Duplicate id: ${item.id}`,
1884
- itemId: item.id
2955
+ message: `Duplicate id: ${id}`,
2956
+ itemId: id
1885
2957
  });
1886
- else seen.add(item.id);
2958
+ else seen.add(id);
2959
+ };
2960
+ this.walkItems(content, (item) => {
2961
+ record(item.id);
2962
+ if (item.type === "array_obj") for (const subField of item.items ?? []) record(subField.id);
2963
+ });
2964
+ }
2965
+ collectArrayObjItemIds(content) {
2966
+ const ids = /* @__PURE__ */ new Map();
2967
+ this.walkItems(content, (item) => {
2968
+ if (item.type !== "array_obj") return;
2969
+ for (const subField of item.items ?? []) if (!ids.has(subField.id)) ids.set(subField.id, item.id);
1887
2970
  });
2971
+ return ids;
2972
+ }
2973
+ checkConditionRefsArrayObjItem(registry, arrayObjItemIds, issues) {
2974
+ if (arrayObjItemIds.size === 0) return;
2975
+ for (const [id, entry] of registry) {
2976
+ if (!entry.condition) continue;
2977
+ for (const ref of DependencyGraph.extractFieldRefs(entry.condition)) {
2978
+ const containerId = arrayObjItemIds.get(ref);
2979
+ if (containerId === void 0) continue;
2980
+ issues.push({
2981
+ code: "CONDITION_REFS_ARRAY_OBJ_ITEM",
2982
+ message: `Condition references sub-field ${ref} of array_obj field ${containerId}, whose value is per-row and not addressable (in item ${id})`,
2983
+ itemId: id
2984
+ });
2985
+ }
2986
+ }
1888
2987
  }
1889
2988
  checkNestingDepth(content, depth, issues) {
1890
2989
  for (const item of content) if (item.type === "section") if (depth >= 3) issues.push({
@@ -1894,28 +2993,37 @@ var FormDefinitionValidator = class {
1894
2993
  });
1895
2994
  else this.checkNestingDepth(item.content, depth + 1, issues);
1896
2995
  }
1897
- checkConditionRefs(registry, issues) {
2996
+ checkConditionRefs(registry, arrayObjItemIds, issues) {
1898
2997
  for (const [id, entry] of registry) {
1899
2998
  if (!entry.condition) continue;
1900
2999
  const refs = DependencyGraph.extractFieldRefs(entry.condition);
1901
- for (const ref of refs) if (!registry.has(ref)) issues.push({
1902
- code: "UNKNOWN_FIELD_REF",
1903
- message: `Condition references unknown field: ${ref} (in item ${id})`,
1904
- itemId: id
1905
- });
3000
+ for (const ref of refs) {
3001
+ if (arrayObjItemIds.has(ref)) continue;
3002
+ if (!registry.has(ref)) issues.push({
3003
+ code: "UNKNOWN_FIELD_REF",
3004
+ message: `Condition references unknown field: ${ref} (in item ${id})`,
3005
+ itemId: id
3006
+ });
3007
+ }
1906
3008
  }
1907
3009
  }
1908
- checkConditionRefsSection(registry, issues) {
3010
+ checkConditionRefsValueless(registry, issues) {
1909
3011
  for (const [id, entry] of registry) {
1910
3012
  if (!entry.condition) continue;
1911
3013
  const refs = DependencyGraph.extractFieldRefs(entry.condition);
1912
3014
  for (const ref of refs) {
1913
3015
  const refEntry = registry.get(ref);
1914
- if (refEntry && refEntry.type === "section") issues.push({
3016
+ if (!refEntry) continue;
3017
+ if (refEntry.type === "section") issues.push({
1915
3018
  code: "CONDITION_REFS_SECTION",
1916
3019
  message: `Condition references section ${ref}, which has no value (in item ${id})`,
1917
3020
  itemId: id
1918
3021
  });
3022
+ if (refEntry.type === "static") issues.push({
3023
+ code: "CONDITION_REFS_STATIC",
3024
+ message: `Condition references static field ${ref}, which has no value (in item ${id})`,
3025
+ itemId: id
3026
+ });
1919
3027
  }
1920
3028
  }
1921
3029
  }
@@ -1956,7 +3064,8 @@ var FormDefinitionValidator = class {
1956
3064
  }
1957
3065
  break;
1958
3066
  }
1959
- case "array": {
3067
+ case "array":
3068
+ case "array_obj": {
1960
3069
  const v = entry.validation;
1961
3070
  if (v.minItems !== void 0 && v.maxItems !== void 0 && v.maxItems < v.minItems) issues.push({
1962
3071
  code: "INVALID_MIN_MAX",
@@ -2042,6 +3151,7 @@ var VisibilityResolver = class {
2042
3151
  registry;
2043
3152
  conditionEvaluator;
2044
3153
  topologicalOrder;
3154
+ fieldTypes;
2045
3155
  /**
2046
3156
  * @param registry - The engine's field registry.
2047
3157
  * @param conditionEvaluator - Evaluator for condition trees.
@@ -2051,6 +3161,7 @@ var VisibilityResolver = class {
2051
3161
  this.registry = registry;
2052
3162
  this.conditionEvaluator = conditionEvaluator;
2053
3163
  this.topologicalOrder = topologicalOrder;
3164
+ this.fieldTypes = new Map([...registry].map(([id, entry]) => [id, entry.type]));
2054
3165
  }
2055
3166
  /**
2056
3167
  * Determines whether a single field or section is visible.
@@ -2077,7 +3188,8 @@ var VisibilityResolver = class {
2077
3188
  if (entry.condition) {
2078
3189
  if (!this.conditionEvaluator.evalCondition(entry.condition, {
2079
3190
  values,
2080
- now
3191
+ now,
3192
+ fieldTypes: this.fieldTypes
2081
3193
  })) return false;
2082
3194
  }
2083
3195
  if (entry.parentId !== void 0) return this.isVisible(entry.parentId, values, now);
@@ -2115,7 +3227,8 @@ var VisibilityResolver = class {
2115
3227
  const visible = this.conditionEvaluator.evalCondition(entry.condition, {
2116
3228
  values,
2117
3229
  visibilityMap: result,
2118
- now
3230
+ now,
3231
+ fieldTypes: this.fieldTypes
2119
3232
  });
2120
3233
  result.set(id, visible);
2121
3234
  } else result.set(id, true);
@@ -2382,14 +3495,16 @@ var FormEngine = class FormEngine {
2382
3495
  }
2383
3496
  static walkContent(content, parentId, registry, contentOrder) {
2384
3497
  for (const item of content) {
3498
+ const isValueField = item.type !== "section" && item.type !== "static";
2385
3499
  const entry = {
2386
3500
  id: item.id,
2387
3501
  type: item.type,
2388
3502
  condition: item.condition,
2389
- validation: item.type !== "section" ? item.validation : void 0,
3503
+ validation: isValueField ? item.validation : void 0,
2390
3504
  parentId,
2391
- options: item.type === "select" ? item.options : void 0,
3505
+ options: item.type === "select" || item.type === "multiselect" ? item.options : void 0,
2392
3506
  item: item.type === "array" ? item.item : void 0,
3507
+ items: item.type === "array_obj" ? item.items : void 0,
2393
3508
  label: item.type !== "section" ? item.label : void 0,
2394
3509
  title: item.type === "section" ? item.title : void 0
2395
3510
  };
@@ -2400,6 +3515,108 @@ var FormEngine = class FormEngine {
2400
3515
  }
2401
3516
  };
2402
3517
  //#endregion
3518
+ //#region src/suggestions.ts
3519
+ /**
3520
+ * Pure helpers for reading and writing the `suggestions` block of a
3521
+ * {@link FormDocument}.
3522
+ *
3523
+ * Every function is total over legacy documents: `doc.suggestions` may be
3524
+ * `undefined` (documents written before suggestions existed) and each helper
3525
+ * treats that as "no suggestions". Writers never mutate their input -- they
3526
+ * return a new document -- and they drop the `suggestions` key entirely once it
3527
+ * would be empty, so a document that never had suggestions round-trips
3528
+ * unchanged.
3529
+ *
3530
+ * These helpers deliberately do **not** check that `fieldId` refers to an
3531
+ * existing, non-section field. That check belongs to the general-purpose
3532
+ * authoring API ({@link FormValuesEditor}); callers that already resolved a
3533
+ * field -- such as the viewer's render loop -- would only pay for it twice.
3534
+ */
3535
+ /**
3536
+ * Returns the suggestion recorded for a field.
3537
+ *
3538
+ * @param doc - The form document to read from.
3539
+ * @param fieldId - Numeric id of the field.
3540
+ * @returns The suggestion, or `undefined` when the field has none.
3541
+ */
3542
+ const getSuggestion = (doc, fieldId) => doc.suggestions?.[String(fieldId)];
3543
+ /**
3544
+ * Records a suggestion for a field, replacing any existing one.
3545
+ *
3546
+ * @param doc - The form document to update.
3547
+ * @param fieldId - Numeric id of the field.
3548
+ * @param suggestion - The suggestion to store. Deep-copied before storing.
3549
+ * @returns A new document carrying the suggestion.
3550
+ * @throws If `confidence` is not a finite number within `0..1`.
3551
+ */
3552
+ const setSuggestion = (doc, fieldId, suggestion) => {
3553
+ const { confidence } = suggestion;
3554
+ if (!Number.isFinite(confidence) || confidence < 0 || confidence > 1) throw new Error(`Suggestion confidence must be a number between 0 and 1, received ${confidence}`);
3555
+ return {
3556
+ ...doc,
3557
+ suggestions: {
3558
+ ...doc.suggestions,
3559
+ [String(fieldId)]: deepCopy(suggestion)
3560
+ }
3561
+ };
3562
+ };
3563
+ /**
3564
+ * Removes the suggestion recorded for a field, leaving the field's value alone.
3565
+ *
3566
+ * When the removed entry was the last one, the `suggestions` key is dropped
3567
+ * from the document rather than left as an empty object.
3568
+ *
3569
+ * @param doc - The form document to update.
3570
+ * @param fieldId - Numeric id of the field.
3571
+ * @returns A new document without that suggestion, or `doc` itself when there
3572
+ * was nothing to remove.
3573
+ */
3574
+ const removeSuggestion = (doc, fieldId) => {
3575
+ const key = String(fieldId);
3576
+ if (!doc.suggestions || !(key in doc.suggestions)) return doc;
3577
+ const { [key]: _removed, ...rest } = doc.suggestions;
3578
+ if (Object.keys(rest).length === 0) {
3579
+ const { suggestions: _dropped, ...withoutSuggestions } = doc;
3580
+ return withoutSuggestions;
3581
+ }
3582
+ return {
3583
+ ...doc,
3584
+ suggestions: rest
3585
+ };
3586
+ };
3587
+ /**
3588
+ * Copies a field's suggested value into the document's values, keeping the
3589
+ * suggestion in place so the origin of the answer stays visible.
3590
+ *
3591
+ * The value is deep-copied: array and object suggestions must not become
3592
+ * aliases of the stored value, or later in-place edits (e.g. appending an array
3593
+ * item) would silently rewrite the suggestion too.
3594
+ *
3595
+ * @param doc - The form document to update.
3596
+ * @param fieldId - Numeric id of the field.
3597
+ * @returns A new document with the value applied, or `doc` itself when the
3598
+ * field has no suggestion.
3599
+ */
3600
+ const applySuggestion = (doc, fieldId) => {
3601
+ const suggestion = getSuggestion(doc, fieldId);
3602
+ if (!suggestion) return doc;
3603
+ return {
3604
+ ...doc,
3605
+ values: {
3606
+ ...doc.values,
3607
+ [String(fieldId)]: deepCopy(suggestion.value)
3608
+ }
3609
+ };
3610
+ };
3611
+ /**
3612
+ * Returns all suggestions on a document as a plain object, never `undefined`.
3613
+ *
3614
+ * @param doc - The form document to read from.
3615
+ * @returns A copy of the suggestions map. Empty when the document has none.
3616
+ */
3617
+ const getSuggestions = (doc) => ({ ...doc.suggestions });
3618
+ const deepCopy = (value) => value === void 0 ? value : JSON.parse(JSON.stringify(value));
3619
+ //#endregion
2403
3620
  //#region src/form-values-editor.ts
2404
3621
  /**
2405
3622
  * Mutable editor for building and modifying form values against a {@link FormDefinition}.
@@ -2471,13 +3688,16 @@ var FormValuesEditor = class {
2471
3688
  * If the field currently has no value, it is initialized to an empty array
2472
3689
  * before appending.
2473
3690
  *
2474
- * @param fieldId - Numeric id of the array field.
2475
- * @param value - The value to append. Defaults to `undefined`.
3691
+ * @param fieldId - Numeric id of the `array` or `array_obj` field.
3692
+ * @param value - The value to append. Defaults to `undefined` for an
3693
+ * `array` field and to an empty row (`{}`) for an `array_obj` field.
2476
3694
  * @returns `this` for chaining.
2477
3695
  * @throws If `fieldId` is not an array field.
2478
3696
  */
2479
3697
  addArrayItem(fieldId, value) {
2480
- this.getOrInitArray(fieldId).push(value);
3698
+ const arr = this.getOrInitArray(fieldId);
3699
+ const entry = this.engine.getFieldDef(fieldId);
3700
+ arr.push(value === void 0 && entry?.type === "array_obj" ? {} : value);
2481
3701
  return this;
2482
3702
  }
2483
3703
  /**
@@ -2527,6 +3747,114 @@ var FormValuesEditor = class {
2527
3747
  return this;
2528
3748
  }
2529
3749
  /**
3750
+ * Returns the value of one sub-field within one row of an `array_obj` field.
3751
+ *
3752
+ * @param fieldId - Numeric id of the `array_obj` field.
3753
+ * @param index - Zero-based row index.
3754
+ * @param subFieldId - Numeric id of the sub-field.
3755
+ * @returns The sub-field value, or `undefined` if not set.
3756
+ * @throws If the field is not an `array_obj`, the row index is out of
3757
+ * bounds, or the sub-field does not belong to the field.
3758
+ */
3759
+ getArrayObjValue(fieldId, index, subFieldId) {
3760
+ return this.assertArrayObjRow(fieldId, index, subFieldId)[String(subFieldId)];
3761
+ }
3762
+ /**
3763
+ * Sets the value of one sub-field within one row of an `array_obj` field.
3764
+ *
3765
+ * @param fieldId - Numeric id of the `array_obj` field.
3766
+ * @param index - Zero-based row index.
3767
+ * @param subFieldId - Numeric id of the sub-field.
3768
+ * @param value - The value to set.
3769
+ * @returns `this` for chaining.
3770
+ * @throws If the field is not an `array_obj`, the row index is out of
3771
+ * bounds, or the sub-field does not belong to the field or is static.
3772
+ */
3773
+ setArrayObjValue(fieldId, index, subFieldId, value) {
3774
+ this.assertArrayObjRow(fieldId, index, subFieldId)[String(subFieldId)] = value;
3775
+ return this;
3776
+ }
3777
+ /**
3778
+ * Removes one sub-field's value from one row of an `array_obj` field.
3779
+ *
3780
+ * @returns `this` for chaining.
3781
+ * @throws Under the same conditions as {@link setArrayObjValue}.
3782
+ */
3783
+ clearArrayObjValue(fieldId, index, subFieldId) {
3784
+ delete this.assertArrayObjRow(fieldId, index, subFieldId)[String(subFieldId)];
3785
+ return this;
3786
+ }
3787
+ /**
3788
+ * Returns the suggestion recorded for a field.
3789
+ *
3790
+ * @param fieldId - Numeric id of the field.
3791
+ * @returns The suggestion, or `undefined` if the field has none.
3792
+ */
3793
+ getSuggestion(fieldId) {
3794
+ return getSuggestion(this.doc, fieldId);
3795
+ }
3796
+ /**
3797
+ * Returns every suggestion on the document, keyed by stringified field id.
3798
+ *
3799
+ * @returns A copy of the suggestions map. Empty when there are none.
3800
+ */
3801
+ getSuggestions() {
3802
+ return getSuggestions(this.doc);
3803
+ }
3804
+ /**
3805
+ * Records a suggestion for a field, replacing any existing one. The field's
3806
+ * value is left untouched.
3807
+ *
3808
+ * @param fieldId - Numeric id of the field.
3809
+ * @param suggestion - The suggestion to store.
3810
+ * @returns `this` for chaining.
3811
+ * @throws If `fieldId` is unknown, references a section, or `confidence`
3812
+ * falls outside `0..1`.
3813
+ */
3814
+ setSuggestion(fieldId, suggestion) {
3815
+ this.assertField(fieldId);
3816
+ this.doc = setSuggestion(this.doc, fieldId, suggestion);
3817
+ return this;
3818
+ }
3819
+ /**
3820
+ * Removes the suggestion recorded for a field, leaving its value untouched.
3821
+ *
3822
+ * @param fieldId - Numeric id of the field.
3823
+ * @returns `this` for chaining.
3824
+ */
3825
+ clearSuggestion(fieldId) {
3826
+ this.doc = removeSuggestion(this.doc, fieldId);
3827
+ return this;
3828
+ }
3829
+ /**
3830
+ * Accepts a field's suggestion: copies the suggested value into the field's
3831
+ * value and **keeps** the suggestion, so its source stays visible.
3832
+ *
3833
+ * No-op when the field has no suggestion.
3834
+ *
3835
+ * @param fieldId - Numeric id of the field.
3836
+ * @returns `this` for chaining.
3837
+ * @throws If `fieldId` is unknown or references a section.
3838
+ */
3839
+ acceptSuggestion(fieldId) {
3840
+ this.assertField(fieldId);
3841
+ this.doc = applySuggestion(this.doc, fieldId);
3842
+ return this;
3843
+ }
3844
+ /**
3845
+ * Rejects a field's suggestion: removes it entirely, leaving the field's
3846
+ * value untouched.
3847
+ *
3848
+ * No-op when the field has no suggestion.
3849
+ *
3850
+ * @param fieldId - Numeric id of the field.
3851
+ * @returns `this` for chaining.
3852
+ */
3853
+ rejectSuggestion(fieldId) {
3854
+ this.doc = removeSuggestion(this.doc, fieldId);
3855
+ return this;
3856
+ }
3857
+ /**
2530
3858
  * Sets the `submittedAt` timestamp on the document.
2531
3859
  *
2532
3860
  * @param submittedAt - ISO 8601 timestamp string.
@@ -2582,6 +3910,7 @@ var FormValuesEditor = class {
2582
3910
  const entry = this.engine.getFieldDef(fieldId);
2583
3911
  if (!entry) throw new Error(`Field with id ${fieldId} not found`);
2584
3912
  if (entry.type === "section") throw new Error(`Item ${fieldId} is a section, not a field`);
3913
+ if (entry.type === "static") throw new Error(`Item ${fieldId} is a static field and holds no value`);
2585
3914
  }
2586
3915
  /**
2587
3916
  * Asserts that `fieldId` is an array field and returns the current array value.
@@ -2590,7 +3919,7 @@ var FormValuesEditor = class {
2590
3919
  assertArray(fieldId) {
2591
3920
  const entry = this.engine.getFieldDef(fieldId);
2592
3921
  if (!entry) throw new Error(`Field with id ${fieldId} not found`);
2593
- if (entry.type !== "array") throw new Error(`Field ${fieldId} is not an array field`);
3922
+ if (entry.type !== "array" && entry.type !== "array_obj") throw new Error(`Field ${fieldId} is not an array field`);
2594
3923
  const val = this.doc.values[String(fieldId)];
2595
3924
  if (!Array.isArray(val)) throw new Error(`Field ${fieldId} does not currently hold an array value`);
2596
3925
  return val;
@@ -2601,7 +3930,7 @@ var FormValuesEditor = class {
2601
3930
  getOrInitArray(fieldId) {
2602
3931
  const entry = this.engine.getFieldDef(fieldId);
2603
3932
  if (!entry) throw new Error(`Field with id ${fieldId} not found`);
2604
- if (entry.type !== "array") throw new Error(`Field ${fieldId} is not an array field`);
3933
+ if (entry.type !== "array" && entry.type !== "array_obj") throw new Error(`Field ${fieldId} is not an array field`);
2605
3934
  const key = String(fieldId);
2606
3935
  let val = this.doc.values[key];
2607
3936
  if (!Array.isArray(val)) {
@@ -2610,6 +3939,26 @@ var FormValuesEditor = class {
2610
3939
  }
2611
3940
  return val;
2612
3941
  }
3942
+ /**
3943
+ * Asserts that `fieldId` is an `array_obj` field holding a row at `index`
3944
+ * with a writable sub-field `subFieldId`, and returns that row.
3945
+ * The row is created in place when it is absent or not an object.
3946
+ */
3947
+ assertArrayObjRow(fieldId, index, subFieldId) {
3948
+ const entry = this.engine.getFieldDef(fieldId);
3949
+ if (!entry) throw new Error(`Field with id ${fieldId} not found`);
3950
+ if (entry.type !== "array_obj") throw new Error(`Field ${fieldId} is not an array_obj field`);
3951
+ const subField = (entry.items ?? []).find((f) => f.id === subFieldId);
3952
+ if (!subField) throw new Error(`Sub-field ${subFieldId} does not belong to field ${fieldId}`);
3953
+ if (subField.type === "static") throw new Error(`Sub-field ${subFieldId} is a static field and holds no value`);
3954
+ const arr = this.assertArray(fieldId);
3955
+ if (index < 0 || index >= arr.length) throw new Error(`Index ${index} is out of bounds for array field ${fieldId} (length ${arr.length})`);
3956
+ const row = arr[index];
3957
+ if (typeof row === "object" && row !== null && !Array.isArray(row)) return row;
3958
+ const created = {};
3959
+ arr[index] = created;
3960
+ return created;
3961
+ }
2613
3962
  };
2614
3963
  //#endregion
2615
3964
  exports.ConditionEvaluator = ConditionEvaluator;
@@ -2621,3 +3970,8 @@ exports.FormDefinitionValidator = FormDefinitionValidator;
2621
3970
  exports.FormEngine = FormEngine;
2622
3971
  exports.FormValuesEditor = FormValuesEditor;
2623
3972
  exports.VisibilityResolver = VisibilityResolver;
3973
+ exports.applySuggestion = applySuggestion;
3974
+ exports.getSuggestion = getSuggestion;
3975
+ exports.getSuggestions = getSuggestions;
3976
+ exports.removeSuggestion = removeSuggestion;
3977
+ exports.setSuggestion = setSuggestion;