@legalplace/conditions-runner 2.2.20 → 2.3.1
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/CHANGELOG.md +28 -0
- package/dist/ComparatorResolver.d.ts +22 -0
- package/dist/ComparatorResolver.js +180 -0
- package/dist/ConditionsRunner.d.ts +1 -0
- package/dist/ConditionsRunner.js +23 -1
- package/dist/ConditionsRunner.test.js +281 -0
- package/dist/DataPopulator.js +3 -3
- package/dist/ScopedLeafEvaluator.d.ts +3 -0
- package/dist/ScopedLeafEvaluator.js +106 -0
- package/package.json +6 -6
- package/src/ComparatorResolver.ts +338 -0
- package/src/ConditionsRunner.test.ts +361 -1
- package/src/ConditionsRunner.ts +62 -1
- package/src/DataPopulator.ts +4 -4
- package/src/ScopedLeafEvaluator.ts +236 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,34 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [2.3.1](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/conditions-runner@2.3.0...@legalplace/conditions-runner@2.3.1) (2026-09-01)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* prevent LpLogic cleanString crash on null related-row values ([c07292d](https://git.legalplace.eu/legalplace/monorepo/commits/c07292d1ccdb615ee4ab885cafea10d6b08f479e))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [2.3.0](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/conditions-runner@2.2.20...@legalplace/conditions-runner@2.3.0) (2026-09-01)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* fix conditions on multiples and repeated outputs ([d77efbe](https://git.legalplace.eu/legalplace/monorepo/commits/d77efbe706da435c6e853176432f64954217d046))
|
|
23
|
+
* update InputsType import to align with new module structure ([61b0b47](https://git.legalplace.eu/legalplace/monorepo/commits/61b0b473168e298ded44eeeb7d041a52608dc038))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Features
|
|
27
|
+
|
|
28
|
+
* output image assets, documentStyle font sizes and table column widths ([8dfb115](https://git.legalplace.eu/legalplace/monorepo/commits/8dfb115d74cd3e5acbb11dd798efa749a8098001))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
6
34
|
## [2.2.20](https://git.legalplace.eu/legalplace/monorepo/compare/@legalplace/conditions-runner@2.2.19...@legalplace/conditions-runner@2.2.20) (2026-08-27)
|
|
7
35
|
|
|
8
36
|
**Note:** Version bump only for package @legalplace/conditions-runner
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Types } from "@legalplace/referencesparser";
|
|
2
|
+
import type { InputsType } from "@legalplace/types";
|
|
3
|
+
export declare const CONDITION_COMPARATOR_EMPTY_SENTINEL = "\uE000__LP_EMPTY__\uE000";
|
|
4
|
+
export declare const SCOPED_OPERATOR_REGEX: RegExp;
|
|
5
|
+
export type ComparatorResolverContext = {
|
|
6
|
+
optionId: number;
|
|
7
|
+
index: number;
|
|
8
|
+
conditionVariableId?: number;
|
|
9
|
+
conditionOptionId?: number;
|
|
10
|
+
evalVariableId?: number;
|
|
11
|
+
};
|
|
12
|
+
export type ComparatorResolverDeps = {
|
|
13
|
+
references: Types.ReferencesType;
|
|
14
|
+
ovc: InputsType;
|
|
15
|
+
};
|
|
16
|
+
export declare const getConditionVariableOptionId: (references: Types.ReferencesType, variableId: number) => number;
|
|
17
|
+
export declare const extractVarTagIds: (text: string) => number[];
|
|
18
|
+
export declare const extractLhsVariableId: (lhs: unknown) => number | undefined;
|
|
19
|
+
export declare const areVariablesSiblings: (references: Types.ReferencesType, variableA: number, variableB: number) => boolean;
|
|
20
|
+
export declare const parseConditionComparatorWithVariables: (text: string, optionId: number, index: number, deps: ComparatorResolverDeps, context?: Pick<ComparatorResolverContext, "conditionVariableId" | "conditionOptionId" | "evalVariableId">) => string;
|
|
21
|
+
export declare const resolveComparatorString: (value: string, context: ComparatorResolverContext, deps: ComparatorResolverDeps) => string;
|
|
22
|
+
export declare const resolveConditionComparators: (conditions: unknown, context: ComparatorResolverContext, deps: ComparatorResolverDeps) => unknown;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveConditionComparators = exports.resolveComparatorString = exports.parseConditionComparatorWithVariables = exports.areVariablesSiblings = exports.extractLhsVariableId = exports.extractVarTagIds = exports.getConditionVariableOptionId = exports.SCOPED_OPERATOR_REGEX = exports.CONDITION_COMPARATOR_EMPTY_SENTINEL = void 0;
|
|
4
|
+
const referencesparser_1 = require("@legalplace/referencesparser");
|
|
5
|
+
exports.CONDITION_COMPARATOR_EMPTY_SENTINEL = "\uE000__LP_EMPTY__\uE000";
|
|
6
|
+
const VAR_TAG_IN_TEXT_REGEX = /\[var:[0-9]+\]/i;
|
|
7
|
+
const UI_METADATA_KEYS = new Set(["scope", "groupMeta"]);
|
|
8
|
+
const RECOGNIZED_OPERATOR_KEYS = new Set((0, referencesparser_1.getRecognizedOperatorKeys)());
|
|
9
|
+
exports.SCOPED_OPERATOR_REGEX = /-(first|any)$/;
|
|
10
|
+
const getConditionVariableOptionId = (references, variableId) => {
|
|
11
|
+
var _a;
|
|
12
|
+
const parents = (_a = references.relations.variables[variableId]) === null || _a === void 0 ? void 0 : _a.parents;
|
|
13
|
+
return parents !== undefined && parents.length > 0 ? parents[0] : variableId;
|
|
14
|
+
};
|
|
15
|
+
exports.getConditionVariableOptionId = getConditionVariableOptionId;
|
|
16
|
+
const extractVarTagIds = (text) => {
|
|
17
|
+
const tagsMatch = text.match(/\[var:([0-9]+)\]/gi);
|
|
18
|
+
if (tagsMatch === null)
|
|
19
|
+
return [];
|
|
20
|
+
return [
|
|
21
|
+
...new Set(tagsMatch.map((tag) => {
|
|
22
|
+
const match = tag.match(/([0-9]+)/);
|
|
23
|
+
return match === null ? 0 : parseInt(match[0], 10);
|
|
24
|
+
})),
|
|
25
|
+
];
|
|
26
|
+
};
|
|
27
|
+
exports.extractVarTagIds = extractVarTagIds;
|
|
28
|
+
const extractLhsVariableId = (lhs) => {
|
|
29
|
+
if (typeof lhs !== "object" ||
|
|
30
|
+
lhs === null ||
|
|
31
|
+
!("var" in lhs) ||
|
|
32
|
+
typeof lhs.var !== "string") {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
const match = lhs.var.match(/^v\.([0-9]+)$/);
|
|
36
|
+
return match === null ? undefined : parseInt(match[1], 10);
|
|
37
|
+
};
|
|
38
|
+
exports.extractLhsVariableId = extractLhsVariableId;
|
|
39
|
+
const areVariablesSiblings = (references, variableA, variableB) => {
|
|
40
|
+
var _a, _b, _c, _d;
|
|
41
|
+
const rootA = (_b = (_a = references.relations.variables[variableA]) === null || _a === void 0 ? void 0 : _a.parents) === null || _b === void 0 ? void 0 : _b.slice(-1)[0];
|
|
42
|
+
const rootB = (_d = (_c = references.relations.variables[variableB]) === null || _c === void 0 ? void 0 : _c.parents) === null || _d === void 0 ? void 0 : _d.slice(-1)[0];
|
|
43
|
+
return rootA !== undefined && rootA === rootB;
|
|
44
|
+
};
|
|
45
|
+
exports.areVariablesSiblings = areVariablesSiblings;
|
|
46
|
+
const shouldUseConditionRowIndex = (references, variableId, optionId, context) => {
|
|
47
|
+
var _a, _b;
|
|
48
|
+
const contextVariableId = context === null || context === void 0 ? void 0 : context.contextVariableId;
|
|
49
|
+
if (contextVariableId !== undefined &&
|
|
50
|
+
(0, exports.areVariablesSiblings)(references, variableId, contextVariableId)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if ((context === null || context === void 0 ? void 0 : context.conditionOptionId) !== undefined) {
|
|
54
|
+
const parents = (_a = references.relations.variables[variableId]) === null || _a === void 0 ? void 0 : _a.parents;
|
|
55
|
+
if (parents !== undefined &&
|
|
56
|
+
parents.length > 0 &&
|
|
57
|
+
parents[0] === context.conditionOptionId) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const variableParents = (_b = references.relations.variables[variableId]) === null || _b === void 0 ? void 0 : _b.parents;
|
|
62
|
+
if (variableParents === undefined || variableParents.length === 0) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
const rootVariableParentId = variableParents[variableParents.length - 1];
|
|
66
|
+
const rootVariableParentRelations = references.relations.options[rootVariableParentId];
|
|
67
|
+
if (rootVariableParentRelations === undefined)
|
|
68
|
+
return false;
|
|
69
|
+
return (rootVariableParentRelations.children.options.includes(optionId) ||
|
|
70
|
+
rootVariableParentRelations.dependants.includes(optionId) ||
|
|
71
|
+
optionId === rootVariableParentId);
|
|
72
|
+
};
|
|
73
|
+
const normalizeConditionValue = (currentValue) => {
|
|
74
|
+
if (currentValue === null || currentValue === undefined)
|
|
75
|
+
return "";
|
|
76
|
+
if (typeof currentValue === "number")
|
|
77
|
+
return currentValue.toString();
|
|
78
|
+
if (typeof currentValue === "string")
|
|
79
|
+
return currentValue;
|
|
80
|
+
return "";
|
|
81
|
+
};
|
|
82
|
+
const parseConditionComparatorWithVariables = (text, optionId, index, deps, context) => {
|
|
83
|
+
var _a;
|
|
84
|
+
const variableIds = (0, exports.extractVarTagIds)(text);
|
|
85
|
+
if (variableIds.length === 0)
|
|
86
|
+
return text;
|
|
87
|
+
const rowContext = {
|
|
88
|
+
contextVariableId: (_a = context === null || context === void 0 ? void 0 : context.evalVariableId) !== null && _a !== void 0 ? _a : context === null || context === void 0 ? void 0 : context.conditionVariableId,
|
|
89
|
+
conditionOptionId: context === null || context === void 0 ? void 0 : context.conditionOptionId,
|
|
90
|
+
};
|
|
91
|
+
let parsedText = text;
|
|
92
|
+
variableIds.forEach((variableId) => {
|
|
93
|
+
var _a;
|
|
94
|
+
const values = (_a = deps.ovc.variables[variableId]) !== null && _a !== void 0 ? _a : [];
|
|
95
|
+
const useRow = shouldUseConditionRowIndex(deps.references, variableId, optionId, rowContext);
|
|
96
|
+
const cleanValues = useRow
|
|
97
|
+
? [normalizeConditionValue(values[index])]
|
|
98
|
+
: values.map((value) => normalizeConditionValue(value));
|
|
99
|
+
let resolved = cleanValues.length === 1 ? cleanValues[0] : cleanValues.join(", ");
|
|
100
|
+
if (resolved.trim().length === 0) {
|
|
101
|
+
resolved = exports.CONDITION_COMPARATOR_EMPTY_SENTINEL;
|
|
102
|
+
}
|
|
103
|
+
parsedText = parsedText.replace(new RegExp(`\\[var:${variableId}\\]`, "gi"), resolved);
|
|
104
|
+
});
|
|
105
|
+
return parsedText;
|
|
106
|
+
};
|
|
107
|
+
exports.parseConditionComparatorWithVariables = parseConditionComparatorWithVariables;
|
|
108
|
+
const resolveLeafContext = (lhs, context, deps) => {
|
|
109
|
+
const variableId = (0, exports.extractLhsVariableId)(lhs);
|
|
110
|
+
if (variableId === undefined)
|
|
111
|
+
return context;
|
|
112
|
+
return {
|
|
113
|
+
...context,
|
|
114
|
+
optionId: (0, exports.getConditionVariableOptionId)(deps.references, variableId),
|
|
115
|
+
evalVariableId: variableId,
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
const resolveComparatorString = (value, context, deps) => {
|
|
119
|
+
if (!VAR_TAG_IN_TEXT_REGEX.test(value))
|
|
120
|
+
return value;
|
|
121
|
+
return (0, exports.parseConditionComparatorWithVariables)(value, context.optionId, context.index, deps, {
|
|
122
|
+
evalVariableId: context.evalVariableId,
|
|
123
|
+
conditionVariableId: context.conditionVariableId,
|
|
124
|
+
conditionOptionId: context.conditionOptionId,
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
exports.resolveComparatorString = resolveComparatorString;
|
|
128
|
+
const walkConditionNode = (node, context, deps) => {
|
|
129
|
+
if (node === null || node === undefined)
|
|
130
|
+
return node;
|
|
131
|
+
if (Array.isArray(node)) {
|
|
132
|
+
return node.map((child) => walkConditionNode(child, context, deps));
|
|
133
|
+
}
|
|
134
|
+
if (typeof node !== "object")
|
|
135
|
+
return node;
|
|
136
|
+
const conditionNode = node;
|
|
137
|
+
const structuralKeys = Object.keys(conditionNode).filter((key) => !UI_METADATA_KEYS.has(key));
|
|
138
|
+
if (structuralKeys.length === 0)
|
|
139
|
+
return conditionNode;
|
|
140
|
+
const operatorKey = structuralKeys.find((key) => RECOGNIZED_OPERATOR_KEYS.has(key));
|
|
141
|
+
if (structuralKeys.length > 1 && operatorKey === undefined) {
|
|
142
|
+
return conditionNode;
|
|
143
|
+
}
|
|
144
|
+
const currentKey = operatorKey !== null && operatorKey !== void 0 ? operatorKey : structuralKeys[0];
|
|
145
|
+
if (currentKey === "and" || currentKey === "or") {
|
|
146
|
+
const children = conditionNode[currentKey];
|
|
147
|
+
if (!Array.isArray(children))
|
|
148
|
+
return conditionNode;
|
|
149
|
+
return {
|
|
150
|
+
...conditionNode,
|
|
151
|
+
[currentKey]: children.map((child) => walkConditionNode(child, context, deps)),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (RECOGNIZED_OPERATOR_KEYS.has(currentKey)) {
|
|
155
|
+
const args = conditionNode[currentKey];
|
|
156
|
+
if (!Array.isArray(args) || args.length < 2)
|
|
157
|
+
return conditionNode;
|
|
158
|
+
const resolvedArgs = [...args];
|
|
159
|
+
const comparator = resolvedArgs[1];
|
|
160
|
+
if (typeof comparator === "string") {
|
|
161
|
+
const baseContext = exports.SCOPED_OPERATOR_REGEX.test(currentKey)
|
|
162
|
+
? { ...context, index: 0 }
|
|
163
|
+
: context;
|
|
164
|
+
const leafContext = resolveLeafContext(resolvedArgs[0], baseContext, deps);
|
|
165
|
+
resolvedArgs[1] = (0, exports.resolveComparatorString)(comparator, leafContext, deps);
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
...conditionNode,
|
|
169
|
+
[currentKey]: resolvedArgs,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return conditionNode;
|
|
173
|
+
};
|
|
174
|
+
const resolveConditionComparators = (conditions, context, deps) => {
|
|
175
|
+
if (Array.isArray(conditions)) {
|
|
176
|
+
return conditions.map((condition) => walkConditionNode(condition, context, deps));
|
|
177
|
+
}
|
|
178
|
+
return walkConditionNode(conditions, context, deps);
|
|
179
|
+
};
|
|
180
|
+
exports.resolveConditionComparators = resolveConditionComparators;
|
|
@@ -15,5 +15,6 @@ declare class ConditionsRunner {
|
|
|
15
15
|
dataMap: ConditionDataMap;
|
|
16
16
|
conditions: ConditionV3;
|
|
17
17
|
}, id: number, index: number, conditionType: "options" | "optionValidator" | "variables" | "prefillers" | "variableValidator" | "sections" | "documents"): any;
|
|
18
|
+
private buildComparatorResolverContext;
|
|
18
19
|
}
|
|
19
20
|
export default ConditionsRunner;
|
package/dist/ConditionsRunner.js
CHANGED
|
@@ -4,7 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const lplogic_1 = __importDefault(require("@legalplace/lplogic"));
|
|
7
|
+
const referencesparser_1 = require("@legalplace/referencesparser");
|
|
7
8
|
const DataPopulator_1 = __importDefault(require("./DataPopulator"));
|
|
9
|
+
const ScopedLeafEvaluator_1 = require("./ScopedLeafEvaluator");
|
|
10
|
+
const ComparatorResolver_1 = require("./ComparatorResolver");
|
|
8
11
|
class ConditionsRunner {
|
|
9
12
|
constructor(references, ovc) {
|
|
10
13
|
this.executeConditionMemo = {};
|
|
@@ -77,9 +80,28 @@ class ConditionsRunner {
|
|
|
77
80
|
else if (conditionType === "sections" || conditionType === "documents") {
|
|
78
81
|
currentData = new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap, id, index).getData();
|
|
79
82
|
}
|
|
80
|
-
const
|
|
83
|
+
const resolverContext = this.buildComparatorResolverContext(id, index, conditionType);
|
|
84
|
+
const deps = { references: this.references, ovc: this.ovc };
|
|
85
|
+
const aggregatedConditions = (0, ScopedLeafEvaluator_1.aggregateScopedLeaves)((0, referencesparser_1.lowerScope)(condition.conditions), new DataPopulator_1.default(this, this.references, this.ovc, condition.dataMap).getData(), resolverContext, deps);
|
|
86
|
+
const resolvedConditions = (0, ComparatorResolver_1.resolveConditionComparators)(aggregatedConditions, resolverContext, deps);
|
|
87
|
+
const result = (0, lplogic_1.default)(resolvedConditions, currentData);
|
|
81
88
|
this.executeConditionMemo[memoizedKey] = result;
|
|
82
89
|
return result;
|
|
83
90
|
}
|
|
91
|
+
buildComparatorResolverContext(id, index, conditionType) {
|
|
92
|
+
if (conditionType === "options" || conditionType === "optionValidator") {
|
|
93
|
+
return { optionId: id, index, conditionOptionId: id };
|
|
94
|
+
}
|
|
95
|
+
if (conditionType === "variables" ||
|
|
96
|
+
conditionType === "prefillers" ||
|
|
97
|
+
conditionType === "variableValidator") {
|
|
98
|
+
return {
|
|
99
|
+
optionId: (0, ComparatorResolver_1.getConditionVariableOptionId)(this.references, id),
|
|
100
|
+
index,
|
|
101
|
+
conditionVariableId: id,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return { optionId: id, index };
|
|
105
|
+
}
|
|
84
106
|
}
|
|
85
107
|
exports.default = ConditionsRunner;
|
|
@@ -3,11 +3,292 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const referencesparser_1 = require("@legalplace/referencesparser");
|
|
6
7
|
const ConditionsRunner_1 = __importDefault(require("./ConditionsRunner"));
|
|
8
|
+
const ComparatorResolver_1 = require("./ComparatorResolver");
|
|
7
9
|
const inputs_type_json_1 = __importDefault(require("./fixtures/inputs-type.json"));
|
|
8
10
|
const references_json_1 = __importDefault(require("./fixtures/references.json"));
|
|
11
|
+
const emptyOptionRelations = {
|
|
12
|
+
options: [],
|
|
13
|
+
variables: [],
|
|
14
|
+
sections: {},
|
|
15
|
+
documents: [],
|
|
16
|
+
prefillers: [],
|
|
17
|
+
validators: { options: [], variables: [] },
|
|
18
|
+
parents: [],
|
|
19
|
+
children: { options: [], variables: [] },
|
|
20
|
+
dependants: [],
|
|
21
|
+
};
|
|
22
|
+
const buildRelatedDocumentReferences = () => ({
|
|
23
|
+
options: {
|
|
24
|
+
2: {
|
|
25
|
+
meta: {
|
|
26
|
+
id: 2,
|
|
27
|
+
type: "static",
|
|
28
|
+
label: "Partner",
|
|
29
|
+
step: "*",
|
|
30
|
+
multiple: { enabled: true },
|
|
31
|
+
},
|
|
32
|
+
options: [3, 4],
|
|
33
|
+
variables: [],
|
|
34
|
+
},
|
|
35
|
+
3: {
|
|
36
|
+
meta: { id: 3, type: "radio", label: "Yes", step: "*" },
|
|
37
|
+
options: [],
|
|
38
|
+
variables: [],
|
|
39
|
+
},
|
|
40
|
+
4: {
|
|
41
|
+
meta: { id: 4, type: "radio", label: "No", step: "*" },
|
|
42
|
+
options: [],
|
|
43
|
+
variables: [],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
variables: {},
|
|
47
|
+
outputs: {},
|
|
48
|
+
boxes: {},
|
|
49
|
+
documents: {
|
|
50
|
+
annex: {
|
|
51
|
+
name: "Annex",
|
|
52
|
+
params: { multiple: { enabled: true, repeatOption: 2 } },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
sections: {},
|
|
56
|
+
relations: {
|
|
57
|
+
options: {
|
|
58
|
+
2: {
|
|
59
|
+
...emptyOptionRelations,
|
|
60
|
+
children: { options: [3, 4], variables: [] },
|
|
61
|
+
},
|
|
62
|
+
3: { ...emptyOptionRelations, parents: [2] },
|
|
63
|
+
4: { ...emptyOptionRelations, parents: [2] },
|
|
64
|
+
},
|
|
65
|
+
variables: {},
|
|
66
|
+
sections: {},
|
|
67
|
+
},
|
|
68
|
+
conditions: {
|
|
69
|
+
options: {},
|
|
70
|
+
variables: {},
|
|
71
|
+
documents: {},
|
|
72
|
+
sections: { main: {} },
|
|
73
|
+
prefillers: {},
|
|
74
|
+
validators: { options: {}, variables: {} },
|
|
75
|
+
},
|
|
76
|
+
defaults: {},
|
|
77
|
+
evaluations: {},
|
|
78
|
+
optionTags: {},
|
|
79
|
+
variableTags: {},
|
|
80
|
+
});
|
|
81
|
+
const buildSiblingVariableReferences = () => ({
|
|
82
|
+
options: {
|
|
83
|
+
10: {
|
|
84
|
+
meta: {
|
|
85
|
+
id: 10,
|
|
86
|
+
type: "static",
|
|
87
|
+
label: "Repeat",
|
|
88
|
+
step: "*",
|
|
89
|
+
multiple: { enabled: true },
|
|
90
|
+
},
|
|
91
|
+
options: [],
|
|
92
|
+
variables: [3, 4],
|
|
93
|
+
},
|
|
94
|
+
20: {
|
|
95
|
+
meta: { id: 20, type: "static", label: "Other", step: "*" },
|
|
96
|
+
options: [],
|
|
97
|
+
variables: [5],
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
variables: {
|
|
101
|
+
3: { meta: { id: 3, type: "string", label: "Left" }, options: [] },
|
|
102
|
+
4: { meta: { id: 4, type: "string", label: "Right" }, options: [] },
|
|
103
|
+
5: { meta: { id: 5, type: "string", label: "Elsewhere" }, options: [] },
|
|
104
|
+
},
|
|
105
|
+
outputs: {},
|
|
106
|
+
boxes: {},
|
|
107
|
+
documents: {},
|
|
108
|
+
sections: {},
|
|
109
|
+
relations: {
|
|
110
|
+
options: {
|
|
111
|
+
10: {
|
|
112
|
+
...emptyOptionRelations,
|
|
113
|
+
children: { options: [], variables: [3, 4] },
|
|
114
|
+
},
|
|
115
|
+
20: {
|
|
116
|
+
...emptyOptionRelations,
|
|
117
|
+
children: { options: [], variables: [5] },
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
variables: {
|
|
121
|
+
3: { ...emptyOptionRelations, parents: [10] },
|
|
122
|
+
4: { ...emptyOptionRelations, parents: [10] },
|
|
123
|
+
5: { ...emptyOptionRelations, parents: [20] },
|
|
124
|
+
},
|
|
125
|
+
sections: {},
|
|
126
|
+
},
|
|
127
|
+
conditions: {
|
|
128
|
+
options: {},
|
|
129
|
+
variables: {},
|
|
130
|
+
documents: {},
|
|
131
|
+
sections: { main: {} },
|
|
132
|
+
prefillers: {},
|
|
133
|
+
validators: { options: {}, variables: {} },
|
|
134
|
+
},
|
|
135
|
+
defaults: {},
|
|
136
|
+
evaluations: {},
|
|
137
|
+
optionTags: {},
|
|
138
|
+
variableTags: {},
|
|
139
|
+
});
|
|
140
|
+
const relatedOvc = {
|
|
141
|
+
options: {
|
|
142
|
+
2: [true, true],
|
|
143
|
+
3: [true, false],
|
|
144
|
+
4: [false, true],
|
|
145
|
+
},
|
|
146
|
+
variables: {},
|
|
147
|
+
};
|
|
148
|
+
const siblingOvc = {
|
|
149
|
+
options: {
|
|
150
|
+
10: [true, true, true],
|
|
151
|
+
20: [true],
|
|
152
|
+
},
|
|
153
|
+
variables: {
|
|
154
|
+
3: ["same", "b", "c"],
|
|
155
|
+
4: ["same", "y", "c"],
|
|
156
|
+
5: ["solo-a", "solo-b"],
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
const mappedCondition = (conditions) => ({
|
|
160
|
+
dataMap: (0, referencesparser_1.extractConditionDataMap)(conditions),
|
|
161
|
+
conditions,
|
|
162
|
+
});
|
|
9
163
|
describe("ConditionsRunner class", () => {
|
|
10
164
|
it("should not raise error when instanciating class", () => {
|
|
11
165
|
expect(new ConditionsRunner_1.default(references_json_1.default, inputs_type_json_1.default)).toBeDefined();
|
|
12
166
|
});
|
|
167
|
+
it("slices related document conditions to the current occurrence", () => {
|
|
168
|
+
const runner = new ConditionsRunner_1.default(buildRelatedDocumentReferences(), relatedOvc);
|
|
169
|
+
const condition = mappedCondition({
|
|
170
|
+
selected: [{ var: "o.3" }],
|
|
171
|
+
scope: "related",
|
|
172
|
+
});
|
|
173
|
+
expect(runner.executeCondition(condition, 2, 0, "documents")).toBe(true);
|
|
174
|
+
expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
|
|
175
|
+
});
|
|
176
|
+
it("evaluates selected + first against the first row after id/index slicing", () => {
|
|
177
|
+
const runner = new ConditionsRunner_1.default(buildRelatedDocumentReferences(), relatedOvc);
|
|
178
|
+
const condition = mappedCondition({
|
|
179
|
+
selected: [{ var: "o.3" }],
|
|
180
|
+
scope: "first",
|
|
181
|
+
});
|
|
182
|
+
expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(true);
|
|
183
|
+
});
|
|
184
|
+
it("related sibling [var:N] uses the current occurrence index", () => {
|
|
185
|
+
const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), siblingOvc);
|
|
186
|
+
const condition = mappedCondition({
|
|
187
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
188
|
+
scope: "related",
|
|
189
|
+
});
|
|
190
|
+
expect(runner.executeCondition(condition, 3, 0, "variables")).toBe(true);
|
|
191
|
+
expect(runner.executeCondition(condition, 3, 1, "variables")).toBe(false);
|
|
192
|
+
expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
|
|
193
|
+
});
|
|
194
|
+
it("first-scoped sibling [var:N] uses occurrence 0", () => {
|
|
195
|
+
const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), siblingOvc);
|
|
196
|
+
const condition = mappedCondition({
|
|
197
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
198
|
+
scope: "first",
|
|
199
|
+
});
|
|
200
|
+
expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
it("all-scope sibling [var:N] is pairwise true when every row matches", () => {
|
|
203
|
+
const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
|
|
204
|
+
options: { 10: [true, true], 20: [true] },
|
|
205
|
+
variables: {
|
|
206
|
+
3: [1, 2],
|
|
207
|
+
4: [2, 3],
|
|
208
|
+
5: ["solo-a"],
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
const condition = mappedCondition({
|
|
212
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
213
|
+
scope: "all",
|
|
214
|
+
});
|
|
215
|
+
expect(runner.executeCondition(condition, 3, 0, "options")).toBe(true);
|
|
216
|
+
});
|
|
217
|
+
it("all-scope sibling [var:N] is pairwise false when a row fails", () => {
|
|
218
|
+
const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
|
|
219
|
+
options: { 10: [true, true], 20: [true] },
|
|
220
|
+
variables: {
|
|
221
|
+
3: [1, 5],
|
|
222
|
+
4: [2, 3],
|
|
223
|
+
5: ["solo-a"],
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
const condition = mappedCondition({
|
|
227
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
228
|
+
scope: "all",
|
|
229
|
+
});
|
|
230
|
+
expect(runner.executeCondition(condition, 3, 0, "options")).toBe(false);
|
|
231
|
+
});
|
|
232
|
+
it("any-scope sibling [var:N] succeeds on a later pairwise row", () => {
|
|
233
|
+
const runner = new ConditionsRunner_1.default(buildSiblingVariableReferences(), {
|
|
234
|
+
options: { 10: [true, true], 20: [true] },
|
|
235
|
+
variables: {
|
|
236
|
+
3: [5, 1],
|
|
237
|
+
4: [2, 10],
|
|
238
|
+
5: ["solo-a"],
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
const condition = mappedCondition({
|
|
242
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
243
|
+
scope: "any",
|
|
244
|
+
});
|
|
245
|
+
expect(runner.executeCondition(condition, 5, 0, "options")).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
it("joins non-sibling [var:N] values instead of slicing by row", () => {
|
|
248
|
+
const references = buildSiblingVariableReferences();
|
|
249
|
+
const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
|
|
250
|
+
"=": [{ var: "v.3" }, "[var:5]"],
|
|
251
|
+
scope: "related",
|
|
252
|
+
}), { optionId: 10, index: 2, conditionVariableId: 3 }, { references, ovc: siblingOvc });
|
|
253
|
+
expect(resolved).toEqual({
|
|
254
|
+
"=": [{ var: "v.3" }, "solo-a, solo-b"],
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
describe("resolveConditionComparators", () => {
|
|
259
|
+
const references = buildSiblingVariableReferences();
|
|
260
|
+
const deps = { references, ovc: siblingOvc };
|
|
261
|
+
it("related leaf: sibling [var:N] uses the current occurrence index", () => {
|
|
262
|
+
const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
|
|
263
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
264
|
+
scope: "related",
|
|
265
|
+
}), { optionId: 10, index: 2, conditionVariableId: 3 }, deps);
|
|
266
|
+
expect(resolved).toEqual({
|
|
267
|
+
"=": [{ var: "v.3" }, "c"],
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
it("first-scoped leaf: sibling [var:N] uses occurrence 0", () => {
|
|
271
|
+
const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
|
|
272
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
273
|
+
scope: "first",
|
|
274
|
+
}), { optionId: 10, index: 2, conditionVariableId: 3 }, deps);
|
|
275
|
+
expect(resolved).toEqual({
|
|
276
|
+
"=-first": [{ var: "v.3" }, "same"],
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
it("uses empty sentinel for missing sibling values", () => {
|
|
280
|
+
const resolved = (0, ComparatorResolver_1.resolveConditionComparators)((0, referencesparser_1.lowerScope)({
|
|
281
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
282
|
+
scope: "related",
|
|
283
|
+
}), { optionId: 10, index: 5, conditionVariableId: 3 }, {
|
|
284
|
+
references,
|
|
285
|
+
ovc: {
|
|
286
|
+
options: siblingOvc.options,
|
|
287
|
+
variables: { 3: ["a"], 4: ["b"], 5: [] },
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
expect(resolved).toEqual({
|
|
291
|
+
"=": [{ var: "v.3" }, ComparatorResolver_1.CONDITION_COMPARATOR_EMPTY_SENTINEL],
|
|
292
|
+
});
|
|
293
|
+
});
|
|
13
294
|
});
|
package/dist/DataPopulator.js
CHANGED
|
@@ -95,14 +95,14 @@ class DataPopulator {
|
|
|
95
95
|
getVariableValues(variableId) {
|
|
96
96
|
const value = this.ovc.variables[variableId] || [];
|
|
97
97
|
if (this.id === undefined || this.index === undefined) {
|
|
98
|
-
return value;
|
|
98
|
+
return this.cleanArrayFromNullValues([...value]);
|
|
99
99
|
}
|
|
100
100
|
const variableRootParent = this.getVariableRootParent(variableId);
|
|
101
101
|
const variableRootRelations = this.references.relations.options[variableRootParent];
|
|
102
102
|
if (variableRootRelations.children.options.includes(this.id) ||
|
|
103
103
|
variableRootRelations.dependants.includes(this.id) ||
|
|
104
104
|
this.id === variableRootParent) {
|
|
105
|
-
return [value[this.index]];
|
|
105
|
+
return this.cleanArrayFromNullValues([value[this.index]]);
|
|
106
106
|
}
|
|
107
107
|
return this.cleanArrayFromNullValues([...value]);
|
|
108
108
|
}
|
|
@@ -111,7 +111,7 @@ class DataPopulator {
|
|
|
111
111
|
return variableParents[variableParents.length - 1];
|
|
112
112
|
}
|
|
113
113
|
cleanArrayFromNullValues(arr) {
|
|
114
|
-
return arr.map((v) => (v === null ? "" : v));
|
|
114
|
+
return arr.map((v) => (v === null || v === undefined ? "" : v));
|
|
115
115
|
}
|
|
116
116
|
populateOptions() {
|
|
117
117
|
for (let i = 0; i < this.dataMap.o.length; i += 1) {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ConditionData } from "./types/conditions.d";
|
|
2
|
+
import { type ComparatorResolverContext, type ComparatorResolverDeps } from "./ComparatorResolver";
|
|
3
|
+
export declare const aggregateScopedLeaves: (tree: unknown, unslicedData: ConditionData, context: ComparatorResolverContext, deps: ComparatorResolverDeps) => unknown;
|