@rytass/bpm-core-nestjs-module 0.11.0 → 0.12.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.
Files changed (28) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/package.json +2 -2
  3. package/src/lib/condition/condition.service.js +4 -1
  4. package/src/lib/condition/condition.service.js.map +1 -1
  5. package/src/lib/form/form-schema.validator.js +277 -56
  6. package/src/lib/form/form-schema.validator.js.map +1 -1
  7. package/src/lib/form/form-table-reference.d.ts +28 -0
  8. package/src/lib/form/form-table-reference.js +53 -0
  9. package/src/lib/form/form-table-reference.js.map +1 -0
  10. package/src/lib/form-data-source/form-data-source-value-resolver.service.js +81 -13
  11. package/src/lib/form-data-source/form-data-source-value-resolver.service.js.map +1 -1
  12. package/src/lib/form-data-source/form-data-source.queries.d.ts +4 -0
  13. package/src/lib/form-data-source/form-data-source.queries.js +31 -0
  14. package/src/lib/form-data-source/form-data-source.queries.js.map +1 -1
  15. package/src/lib/form-data-source/form-data-source.service.d.ts +21 -0
  16. package/src/lib/form-data-source/form-data-source.service.js +70 -17
  17. package/src/lib/form-data-source/form-data-source.service.js.map +1 -1
  18. package/src/lib/form-data-source/form-data-source.types.d.ts +6 -0
  19. package/src/lib/form-data-source/form-data-source.types.js.map +1 -1
  20. package/src/lib/form-data-source/form-data-source.validation.d.ts +12 -1
  21. package/src/lib/form-data-source/form-data-source.validation.js +34 -5
  22. package/src/lib/form-data-source/form-data-source.validation.js.map +1 -1
  23. package/src/lib/template/template.service.js +34 -1
  24. package/src/lib/template/template.service.js.map +1 -1
  25. package/src/lib/workflow-engine/workflow-condition-evaluator.js +18 -0
  26. package/src/lib/workflow-engine/workflow-condition-evaluator.js.map +1 -1
  27. package/src/lib/workflow-engine/workflow-engine.service.js +119 -6
  28. package/src/lib/workflow-engine/workflow-engine.service.js.map +1 -1
@@ -21,6 +21,14 @@ function evaluateWorkflowEdgeCondition(edge, context) {
21
21
  return compareWorkflowValues(actualValue, operator, expectedValue);
22
22
  }
23
23
  function compareWorkflowValues(actualValue, operator, expectedValue) {
24
+ // A table value is a list of rows. Only IS_FILLED / IS_EMPTY (handled above,
25
+ // by row count) are defined for it; the designer offers nothing else, but a
26
+ // hand-written or imported definition can still carry a value comparison.
27
+ // Stringifying a row record would compare `[object Object]` and route the
28
+ // instance on a coincidence, so it never matches (ADR 16 §3.8).
29
+ if (isNonPrimitiveValue(actualValue)) {
30
+ return false;
31
+ }
24
32
  if (Array.isArray(actualValue)) {
25
33
  return compareArrayValue(actualValue, operator, expectedValue);
26
34
  }
@@ -67,6 +75,16 @@ function compareOrderedValue(actualValue, operator, expectedValue) {
67
75
  }
68
76
  return false;
69
77
  }
78
+ /**
79
+ * True for a row record, or for a list holding any of them. Multi-select values
80
+ * (`readonly string[]`) stay comparable.
81
+ */
82
+ function isNonPrimitiveValue(value) {
83
+ if (Array.isArray(value)) {
84
+ return value.some((entry) => isNonPrimitiveValue(entry));
85
+ }
86
+ return typeof value === 'object' && value !== null;
87
+ }
70
88
  function isEmptyWorkflowValue(value) {
71
89
  if (value === null || typeof value === 'undefined') {
72
90
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-condition-evaluator.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/workflow-engine/workflow-condition-evaluator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-condition-evaluator.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/workflow-engine/workflow-condition-evaluator.ts"],"names":[],"mappings":"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"}
@@ -3314,6 +3314,15 @@ function writeValueAtPath(value, path, nextValue) {
3314
3314
  if (segments.length === 0) {
3315
3315
  return value;
3316
3316
  }
3317
+ // An index step is not understood by this writer — `items[0].qty` splits into
3318
+ // the literal segment `items[0]`, which would be created as a brand new
3319
+ // top-level key rather than reaching the row. That is not the no-op ADR 16
3320
+ // §3.8 asks for: it pollutes `formData` (and therefore the CEL context) with
3321
+ // a key no schema declares, and makes `readValueAtPath` start answering for
3322
+ // a path it should never resolve.
3323
+ if (segments.some((segment) => /[[\]]/u.test(segment))) {
3324
+ return value;
3325
+ }
3317
3326
  return writeNestedValue(value, segments, nextValue);
3318
3327
  }
3319
3328
  function writeNestedValue(value, segments, nextValue) {
@@ -3325,6 +3334,15 @@ function writeNestedValue(value, segments, nextValue) {
3325
3334
  return { ...value, [currentSegment]: nextValue };
3326
3335
  }
3327
3336
  const currentValue = value[currentSegment];
3337
+ // Descending through something that already holds a non-record value must be
3338
+ // a no-op, not an overwrite. `items.qty` against a table would otherwise
3339
+ // replace the whole row array with `{ qty: ... }`; the same guard protects a
3340
+ // scalar from being swallowed by a nested write (ADR 16 §3.8).
3341
+ if (typeof currentValue !== 'undefined' &&
3342
+ currentValue !== null &&
3343
+ !isRecord(currentValue)) {
3344
+ return value;
3345
+ }
3328
3346
  const nestedValue = isRecord(currentValue) ? currentValue : {};
3329
3347
  return {
3330
3348
  ...value,
@@ -3616,17 +3634,105 @@ function readCandidateStatusForClosedTask(taskStatus) {
3616
3634
  return workflow_engine_enums_1.TaskCandidateStatusEnum.CANCELLED;
3617
3635
  }
3618
3636
  function validateSubmittedFormData(schema, formData) {
3619
- const missingFields = schema.fields.filter((field) => {
3620
- const fieldValue = formData[field.fieldKey];
3621
- return (isSubmittedFormFieldVisible(field, schema.fields, formData) &&
3622
- isSubmittedFormFieldRequired(field, schema.fields, formData) &&
3623
- !isSubmittedFieldValuePresent(fieldValue));
3624
- });
3637
+ const visibleFields = schema.fields.filter((field) => isSubmittedFormFieldVisible(field, schema.fields, formData));
3638
+ // Tables answer "is this filled?" by row count and report per-row problems
3639
+ // with an instance path, so they get their own pass instead of the flat
3640
+ // "missing required fields" list (ADR 16 §3.7).
3641
+ const missingFields = visibleFields.filter((field) => !(0, form_1.isTableFieldDefinition)(field) &&
3642
+ isSubmittedFormFieldRequired(field, schema.fields, formData) &&
3643
+ !isSubmittedFieldValuePresent(formData[field.fieldKey]));
3625
3644
  if (missingFields.length > 0) {
3626
3645
  throw new common_1.BadRequestException(`Form data is missing required fields: ${missingFields
3627
3646
  .map((field) => field.label || field.fieldKey)
3628
3647
  .join(', ')}`);
3629
3648
  }
3649
+ const tableErrors = visibleFields
3650
+ .filter(form_1.isTableFieldDefinition)
3651
+ .flatMap((field) => validateSubmittedTableValue(field, formData[field.fieldKey], isSubmittedFormFieldRequired(field, schema.fields, formData)));
3652
+ if (tableErrors.length > 0) {
3653
+ throw new common_1.BadRequestException(tableErrors.join('; '));
3654
+ }
3655
+ }
3656
+ /**
3657
+ * Authoritative shape, row-count and per-row required checks for one table
3658
+ * value (ADR 16 §3.7). Depth stops here on purpose: like the flat fields, only
3659
+ * required / shape / row count are enforced server side; `minimum`, `maxLength`
3660
+ * and friends stay a frontend hint.
3661
+ */
3662
+ function validateSubmittedTableValue(field, value, required) {
3663
+ const path = `formData.${field.fieldKey}`;
3664
+ if (typeof value === 'undefined' || value === null) {
3665
+ return required || readTableMinRows(field) > 0
3666
+ ? [`${path} must include at least ${readTableRequiredRowCount(field)} row(s)`]
3667
+ : [];
3668
+ }
3669
+ if (!Array.isArray(value)) {
3670
+ return [`${path} must be an array of rows`];
3671
+ }
3672
+ const rowErrors = value.flatMap((row, index) => validateSubmittedTableRow(field, row, `${path}[${index}]`));
3673
+ // A malformed row cannot be counted against min/max meaningfully; report the
3674
+ // shape problem first so the submitter fixes the real cause.
3675
+ if (rowErrors.length > 0) {
3676
+ return rowErrors;
3677
+ }
3678
+ return validateSubmittedTableRowCount(field, value.length, required, path);
3679
+ }
3680
+ function validateSubmittedTableRowCount(field, rowCount, required, path) {
3681
+ const minRows = required
3682
+ ? Math.max(readTableMinRows(field), 1)
3683
+ : readTableMinRows(field);
3684
+ const maxRows = readTableMaxRows(field);
3685
+ return [
3686
+ ...(rowCount < minRows
3687
+ ? [`${path} must include at least ${minRows} row(s)`]
3688
+ : []),
3689
+ ...(rowCount > maxRows
3690
+ ? [`${path} must include at most ${maxRows} row(s)`]
3691
+ : []),
3692
+ ];
3693
+ }
3694
+ function validateSubmittedTableRow(field, row, path) {
3695
+ if (!isRecord(row)) {
3696
+ return [`${path} must be an object`];
3697
+ }
3698
+ const columnsByKey = new Map(field.columns.map((column) => [column.fieldKey, column]));
3699
+ const unknownKeyErrors = Object.keys(row)
3700
+ .filter((columnKey) => !columnsByKey.has(columnKey))
3701
+ .map((columnKey) => `${path}.${columnKey} is not a table column`);
3702
+ const cellErrors = Object.entries(row)
3703
+ .filter(([columnKey, cellValue]) => columnsByKey.has(columnKey) && !(0, form_1.isFormTableCellValue)(cellValue))
3704
+ .map(([columnKey]) => `${path}.${columnKey} is not a valid cell value`);
3705
+ const requiredErrors = field.columns
3706
+ .filter((column) => isSubmittedTableColumnRequired(column) &&
3707
+ !isSubmittedFieldValuePresent(readRowCellValue(row, column.fieldKey)))
3708
+ .map((column) => `${path}.${column.fieldKey} is required`);
3709
+ return [...unknownKeyErrors, ...cellErrors, ...requiredErrors];
3710
+ }
3711
+ /**
3712
+ * Own-property read. `constructor`, `toString` and friends all satisfy the
3713
+ * column key identifier rule, so a plain index would walk the prototype chain
3714
+ * and report an empty row as filled.
3715
+ */
3716
+ function readRowCellValue(row, columnKey) {
3717
+ return Object.prototype.hasOwnProperty.call(row, columnKey)
3718
+ ? row[columnKey]
3719
+ : undefined;
3720
+ }
3721
+ /**
3722
+ * Columns carry no `requiredWhen`: row-scoped conditions are out of scope for
3723
+ * V1 and the structural lint rejects them (ADR 16 §3.1).
3724
+ */
3725
+ function isSubmittedTableColumnRequired(column) {
3726
+ return column.required === true;
3727
+ }
3728
+ function readTableMinRows(field) {
3729
+ return typeof field.minRows === 'number' ? field.minRows : 0;
3730
+ }
3731
+ function readTableMaxRows(field) {
3732
+ return typeof field.maxRows === 'number' ? field.maxRows : form_1.FORM_TABLE_MAX_ROWS;
3733
+ }
3734
+ function readTableRequiredRowCount(field) {
3735
+ return Math.max(readTableMinRows(field), 1);
3630
3736
  }
3631
3737
  function readFormDefinitionSnapshotSchema(snapshot) {
3632
3738
  const schema = snapshot.schema;
@@ -3663,6 +3769,13 @@ function evaluateFormConditionExpression(expression, fields, values, fallback) {
3663
3769
  if (!field) {
3664
3770
  return fallback;
3665
3771
  }
3772
+ // A table value is a list of rows; comparing it with `==` / `>` has no
3773
+ // meaning, and the structural lint already rejects cell-level references.
3774
+ // Falling back keeps a table operand from silently deciding visibility
3775
+ // through a string comparison against row records (ADR 16 §3.8).
3776
+ if ((0, form_1.isTableFieldDefinition)(field)) {
3777
+ return fallback;
3778
+ }
3666
3779
  return evaluateFormConditionRule(rule, field, values[field.fieldKey]);
3667
3780
  }
3668
3781
  function parseFormConditionRule(expression) {