@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
|
@@ -1,7 +1,185 @@
|
|
|
1
|
-
import { Types } from "@legalplace/referencesparser";
|
|
1
|
+
import { Types, extractConditionDataMap, lowerScope } from "@legalplace/referencesparser";
|
|
2
|
+
import type { ConditionV3 } from "@legalplace/models-v3-types";
|
|
2
3
|
import ConditionsRunner from "./ConditionsRunner";
|
|
4
|
+
import {
|
|
5
|
+
CONDITION_COMPARATOR_EMPTY_SENTINEL,
|
|
6
|
+
resolveConditionComparators,
|
|
7
|
+
} from "./ComparatorResolver";
|
|
3
8
|
import dummyInputs from "./fixtures/inputs-type.json";
|
|
4
9
|
import dummyReferences from "./fixtures/references.json";
|
|
10
|
+
import type { ConditionDataMap } from "./types/conditions.d";
|
|
11
|
+
|
|
12
|
+
const emptyOptionRelations = {
|
|
13
|
+
options: [],
|
|
14
|
+
variables: [],
|
|
15
|
+
sections: {},
|
|
16
|
+
documents: [] as string[],
|
|
17
|
+
prefillers: [],
|
|
18
|
+
validators: { options: [], variables: [] },
|
|
19
|
+
parents: [] as number[],
|
|
20
|
+
children: { options: [] as number[], variables: [] as number[] },
|
|
21
|
+
dependants: [] as number[],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Builds a minimal references graph: multiple #2 with radio children 3/4.
|
|
26
|
+
*/
|
|
27
|
+
const buildRelatedDocumentReferences = (): Types.ReferencesType =>
|
|
28
|
+
({
|
|
29
|
+
options: {
|
|
30
|
+
2: {
|
|
31
|
+
meta: {
|
|
32
|
+
id: 2,
|
|
33
|
+
type: "static",
|
|
34
|
+
label: "Partner",
|
|
35
|
+
step: "*",
|
|
36
|
+
multiple: { enabled: true },
|
|
37
|
+
},
|
|
38
|
+
options: [3, 4],
|
|
39
|
+
variables: [],
|
|
40
|
+
},
|
|
41
|
+
3: {
|
|
42
|
+
meta: { id: 3, type: "radio", label: "Yes", step: "*" },
|
|
43
|
+
options: [],
|
|
44
|
+
variables: [],
|
|
45
|
+
},
|
|
46
|
+
4: {
|
|
47
|
+
meta: { id: 4, type: "radio", label: "No", step: "*" },
|
|
48
|
+
options: [],
|
|
49
|
+
variables: [],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
variables: {},
|
|
53
|
+
outputs: {},
|
|
54
|
+
boxes: {},
|
|
55
|
+
documents: {
|
|
56
|
+
annex: {
|
|
57
|
+
name: "Annex",
|
|
58
|
+
params: { multiple: { enabled: true, repeatOption: 2 } },
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
sections: {},
|
|
62
|
+
relations: {
|
|
63
|
+
options: {
|
|
64
|
+
2: {
|
|
65
|
+
...emptyOptionRelations,
|
|
66
|
+
children: { options: [3, 4], variables: [] },
|
|
67
|
+
},
|
|
68
|
+
3: { ...emptyOptionRelations, parents: [2] },
|
|
69
|
+
4: { ...emptyOptionRelations, parents: [2] },
|
|
70
|
+
},
|
|
71
|
+
variables: {},
|
|
72
|
+
sections: {},
|
|
73
|
+
},
|
|
74
|
+
conditions: {
|
|
75
|
+
options: {},
|
|
76
|
+
variables: {},
|
|
77
|
+
documents: {},
|
|
78
|
+
sections: { main: {} },
|
|
79
|
+
prefillers: {},
|
|
80
|
+
validators: { options: {}, variables: {} },
|
|
81
|
+
},
|
|
82
|
+
defaults: {},
|
|
83
|
+
evaluations: {},
|
|
84
|
+
optionTags: {},
|
|
85
|
+
variableTags: {},
|
|
86
|
+
} as unknown as Types.ReferencesType);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Multiple #10 hosting sibling variables 3 and 4 (same root parent).
|
|
90
|
+
*/
|
|
91
|
+
const buildSiblingVariableReferences = (): Types.ReferencesType =>
|
|
92
|
+
({
|
|
93
|
+
options: {
|
|
94
|
+
10: {
|
|
95
|
+
meta: {
|
|
96
|
+
id: 10,
|
|
97
|
+
type: "static",
|
|
98
|
+
label: "Repeat",
|
|
99
|
+
step: "*",
|
|
100
|
+
multiple: { enabled: true },
|
|
101
|
+
},
|
|
102
|
+
options: [],
|
|
103
|
+
variables: [3, 4],
|
|
104
|
+
},
|
|
105
|
+
20: {
|
|
106
|
+
meta: { id: 20, type: "static", label: "Other", step: "*" },
|
|
107
|
+
options: [],
|
|
108
|
+
variables: [5],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
variables: {
|
|
112
|
+
3: { meta: { id: 3, type: "string", label: "Left" }, options: [] },
|
|
113
|
+
4: { meta: { id: 4, type: "string", label: "Right" }, options: [] },
|
|
114
|
+
5: { meta: { id: 5, type: "string", label: "Elsewhere" }, options: [] },
|
|
115
|
+
},
|
|
116
|
+
outputs: {},
|
|
117
|
+
boxes: {},
|
|
118
|
+
documents: {},
|
|
119
|
+
sections: {},
|
|
120
|
+
relations: {
|
|
121
|
+
options: {
|
|
122
|
+
10: {
|
|
123
|
+
...emptyOptionRelations,
|
|
124
|
+
children: { options: [], variables: [3, 4] },
|
|
125
|
+
},
|
|
126
|
+
20: {
|
|
127
|
+
...emptyOptionRelations,
|
|
128
|
+
children: { options: [], variables: [5] },
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
variables: {
|
|
132
|
+
3: { ...emptyOptionRelations, parents: [10] },
|
|
133
|
+
4: { ...emptyOptionRelations, parents: [10] },
|
|
134
|
+
5: { ...emptyOptionRelations, parents: [20] },
|
|
135
|
+
},
|
|
136
|
+
sections: {},
|
|
137
|
+
},
|
|
138
|
+
conditions: {
|
|
139
|
+
options: {},
|
|
140
|
+
variables: {},
|
|
141
|
+
documents: {},
|
|
142
|
+
sections: { main: {} },
|
|
143
|
+
prefillers: {},
|
|
144
|
+
validators: { options: {}, variables: {} },
|
|
145
|
+
},
|
|
146
|
+
defaults: {},
|
|
147
|
+
evaluations: {},
|
|
148
|
+
optionTags: {},
|
|
149
|
+
variableTags: {},
|
|
150
|
+
} as unknown as Types.ReferencesType);
|
|
151
|
+
|
|
152
|
+
const relatedOvc = {
|
|
153
|
+
options: {
|
|
154
|
+
2: [true, true],
|
|
155
|
+
3: [true, false],
|
|
156
|
+
4: [false, true],
|
|
157
|
+
},
|
|
158
|
+
variables: {},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const siblingOvc = {
|
|
162
|
+
options: {
|
|
163
|
+
10: [true, true, true],
|
|
164
|
+
20: [true],
|
|
165
|
+
},
|
|
166
|
+
variables: {
|
|
167
|
+
3: ["same", "b", "c"],
|
|
168
|
+
4: ["same", "y", "c"],
|
|
169
|
+
5: ["solo-a", "solo-b"],
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Builds executeCondition input from an authored ConditionV3 (scope on the node).
|
|
175
|
+
* @param conditions Authored condition tree
|
|
176
|
+
*/
|
|
177
|
+
const mappedCondition = (
|
|
178
|
+
conditions: ConditionV3
|
|
179
|
+
): { dataMap: ConditionDataMap; conditions: ConditionV3 } => ({
|
|
180
|
+
dataMap: extractConditionDataMap(conditions),
|
|
181
|
+
conditions,
|
|
182
|
+
});
|
|
5
183
|
|
|
6
184
|
describe("ConditionsRunner class", () => {
|
|
7
185
|
it("should not raise error when instanciating class", () => {
|
|
@@ -12,4 +190,186 @@ describe("ConditionsRunner class", () => {
|
|
|
12
190
|
)
|
|
13
191
|
).toBeDefined();
|
|
14
192
|
});
|
|
193
|
+
|
|
194
|
+
it("slices related document conditions to the current occurrence", () => {
|
|
195
|
+
const runner = new ConditionsRunner(
|
|
196
|
+
buildRelatedDocumentReferences(),
|
|
197
|
+
relatedOvc
|
|
198
|
+
);
|
|
199
|
+
const condition = mappedCondition({
|
|
200
|
+
selected: [{ var: "o.3" }],
|
|
201
|
+
scope: "related",
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
expect(runner.executeCondition(condition, 2, 0, "documents")).toBe(true);
|
|
205
|
+
expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(false);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("evaluates selected + first against the first row after id/index slicing", () => {
|
|
209
|
+
const runner = new ConditionsRunner(
|
|
210
|
+
buildRelatedDocumentReferences(),
|
|
211
|
+
relatedOvc
|
|
212
|
+
);
|
|
213
|
+
const condition = mappedCondition({
|
|
214
|
+
selected: [{ var: "o.3" }],
|
|
215
|
+
scope: "first",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
expect(runner.executeCondition(condition, 2, 1, "documents")).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("related sibling [var:N] uses the current occurrence index", () => {
|
|
222
|
+
const runner = new ConditionsRunner(
|
|
223
|
+
buildSiblingVariableReferences(),
|
|
224
|
+
siblingOvc
|
|
225
|
+
);
|
|
226
|
+
const condition = mappedCondition({
|
|
227
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
228
|
+
scope: "related",
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// index 0: "same" === "same"
|
|
232
|
+
expect(runner.executeCondition(condition, 3, 0, "variables")).toBe(true);
|
|
233
|
+
// index 1: "b" === "y"
|
|
234
|
+
expect(runner.executeCondition(condition, 3, 1, "variables")).toBe(false);
|
|
235
|
+
// index 2: "c" === "c" (not "same" from occurrence 0)
|
|
236
|
+
expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it("first-scoped sibling [var:N] uses occurrence 0", () => {
|
|
240
|
+
const runner = new ConditionsRunner(
|
|
241
|
+
buildSiblingVariableReferences(),
|
|
242
|
+
siblingOvc
|
|
243
|
+
);
|
|
244
|
+
const condition = mappedCondition({
|
|
245
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
246
|
+
scope: "first",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Outer index 2 still binds both sides to occurrence 0 → "same" === "same"
|
|
250
|
+
expect(runner.executeCondition(condition, 3, 2, "variables")).toBe(true);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it("all-scope sibling [var:N] is pairwise true when every row matches", () => {
|
|
254
|
+
const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
|
|
255
|
+
options: { 10: [true, true], 20: [true] },
|
|
256
|
+
variables: {
|
|
257
|
+
3: [1, 2],
|
|
258
|
+
4: [2, 3],
|
|
259
|
+
5: ["solo-a"],
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
const condition = mappedCondition({
|
|
263
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
264
|
+
scope: "all",
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(runner.executeCondition(condition, 3, 0, "options")).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("all-scope sibling [var:N] is pairwise false when a row fails", () => {
|
|
271
|
+
const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
|
|
272
|
+
options: { 10: [true, true], 20: [true] },
|
|
273
|
+
variables: {
|
|
274
|
+
3: [1, 5],
|
|
275
|
+
4: [2, 3],
|
|
276
|
+
5: ["solo-a"],
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
const condition = mappedCondition({
|
|
280
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
281
|
+
scope: "all",
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect(runner.executeCondition(condition, 3, 0, "options")).toBe(false);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("any-scope sibling [var:N] succeeds on a later pairwise row", () => {
|
|
288
|
+
const runner = new ConditionsRunner(buildSiblingVariableReferences(), {
|
|
289
|
+
options: { 10: [true, true], 20: [true] },
|
|
290
|
+
variables: {
|
|
291
|
+
3: [5, 1],
|
|
292
|
+
4: [2, 10],
|
|
293
|
+
5: ["solo-a"],
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
const condition = mappedCondition({
|
|
297
|
+
"<": [{ var: "v.3" }, "[var:4]"],
|
|
298
|
+
scope: "any",
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
expect(runner.executeCondition(condition, 5, 0, "options")).toBe(true);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it("joins non-sibling [var:N] values instead of slicing by row", () => {
|
|
305
|
+
const references = buildSiblingVariableReferences();
|
|
306
|
+
const resolved = resolveConditionComparators(
|
|
307
|
+
lowerScope({
|
|
308
|
+
"=": [{ var: "v.3" }, "[var:5]"],
|
|
309
|
+
scope: "related",
|
|
310
|
+
}),
|
|
311
|
+
{ optionId: 10, index: 2, conditionVariableId: 3 },
|
|
312
|
+
{ references, ovc: siblingOvc }
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(resolved).toEqual({
|
|
316
|
+
"=": [{ var: "v.3" }, "solo-a, solo-b"],
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe("resolveConditionComparators", () => {
|
|
322
|
+
const references = buildSiblingVariableReferences();
|
|
323
|
+
const deps = { references, ovc: siblingOvc };
|
|
324
|
+
|
|
325
|
+
it("related leaf: sibling [var:N] uses the current occurrence index", () => {
|
|
326
|
+
const resolved = resolveConditionComparators(
|
|
327
|
+
lowerScope({
|
|
328
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
329
|
+
scope: "related",
|
|
330
|
+
}),
|
|
331
|
+
{ optionId: 10, index: 2, conditionVariableId: 3 },
|
|
332
|
+
deps
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
expect(resolved).toEqual({
|
|
336
|
+
"=": [{ var: "v.3" }, "c"],
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("first-scoped leaf: sibling [var:N] uses occurrence 0", () => {
|
|
341
|
+
const resolved = resolveConditionComparators(
|
|
342
|
+
lowerScope({
|
|
343
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
344
|
+
scope: "first",
|
|
345
|
+
}),
|
|
346
|
+
{ optionId: 10, index: 2, conditionVariableId: 3 },
|
|
347
|
+
deps
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
expect(resolved).toEqual({
|
|
351
|
+
"=-first": [{ var: "v.3" }, "same"],
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("uses empty sentinel for missing sibling values", () => {
|
|
356
|
+
const resolved = resolveConditionComparators(
|
|
357
|
+
lowerScope({
|
|
358
|
+
"=": [{ var: "v.3" }, "[var:4]"],
|
|
359
|
+
scope: "related",
|
|
360
|
+
}),
|
|
361
|
+
{ optionId: 10, index: 5, conditionVariableId: 3 },
|
|
362
|
+
{
|
|
363
|
+
references,
|
|
364
|
+
ovc: {
|
|
365
|
+
options: siblingOvc.options,
|
|
366
|
+
variables: { 3: ["a"], 4: ["b"], 5: [] },
|
|
367
|
+
},
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
expect(resolved).toEqual({
|
|
372
|
+
"=": [{ var: "v.3" }, CONDITION_COMPARATOR_EMPTY_SENTINEL],
|
|
373
|
+
});
|
|
374
|
+
});
|
|
15
375
|
});
|
package/src/ConditionsRunner.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/* eslint-disable max-len */
|
|
2
2
|
import LpLogic from "@legalplace/lplogic";
|
|
3
3
|
import type { Types } from "@legalplace/referencesparser";
|
|
4
|
+
import { lowerScope } from "@legalplace/referencesparser";
|
|
4
5
|
import type { ConditionV3 } from "@legalplace/models-v3-types";
|
|
5
6
|
import type InputsType from "@legalplace/types/dist/inputs";
|
|
6
7
|
import DataPopulator from "./DataPopulator";
|
|
7
8
|
import type { ConditionDataMap } from "./types/conditions.d";
|
|
9
|
+
import { aggregateScopedLeaves } from "./ScopedLeafEvaluator";
|
|
10
|
+
import {
|
|
11
|
+
getConditionVariableOptionId,
|
|
12
|
+
resolveConditionComparators,
|
|
13
|
+
type ComparatorResolverContext,
|
|
14
|
+
} from "./ComparatorResolver";
|
|
8
15
|
|
|
9
16
|
class ConditionsRunner {
|
|
10
17
|
private references: Types.ReferencesType;
|
|
@@ -171,10 +178,64 @@ class ConditionsRunner {
|
|
|
171
178
|
).getData();
|
|
172
179
|
}
|
|
173
180
|
|
|
174
|
-
const
|
|
181
|
+
const resolverContext = this.buildComparatorResolverContext(
|
|
182
|
+
id,
|
|
183
|
+
index,
|
|
184
|
+
conditionType
|
|
185
|
+
);
|
|
186
|
+
const deps = { references: this.references, ovc: this.ovc };
|
|
187
|
+
|
|
188
|
+
// Pairwise sibling `[var:N]` (all/any/first) must run before string
|
|
189
|
+
// substitution collapses the tag to a single value.
|
|
190
|
+
const aggregatedConditions = aggregateScopedLeaves(
|
|
191
|
+
lowerScope(condition.conditions),
|
|
192
|
+
new DataPopulator(
|
|
193
|
+
this,
|
|
194
|
+
this.references,
|
|
195
|
+
this.ovc,
|
|
196
|
+
condition.dataMap
|
|
197
|
+
).getData(),
|
|
198
|
+
resolverContext,
|
|
199
|
+
deps
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const resolvedConditions = resolveConditionComparators(
|
|
203
|
+
aggregatedConditions,
|
|
204
|
+
resolverContext,
|
|
205
|
+
deps
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const result = LpLogic(resolvedConditions, currentData);
|
|
175
209
|
this.executeConditionMemo[memoizedKey] = result;
|
|
176
210
|
return result;
|
|
177
211
|
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Builds option/index context for comparator `[var:N]` substitution.
|
|
215
|
+
*/
|
|
216
|
+
private buildComparatorResolverContext(
|
|
217
|
+
id: number,
|
|
218
|
+
index: number,
|
|
219
|
+
conditionType: string
|
|
220
|
+
): ComparatorResolverContext {
|
|
221
|
+
if (conditionType === "options" || conditionType === "optionValidator") {
|
|
222
|
+
return { optionId: id, index, conditionOptionId: id };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
conditionType === "variables" ||
|
|
227
|
+
conditionType === "prefillers" ||
|
|
228
|
+
conditionType === "variableValidator"
|
|
229
|
+
) {
|
|
230
|
+
return {
|
|
231
|
+
optionId: getConditionVariableOptionId(this.references, id),
|
|
232
|
+
index,
|
|
233
|
+
conditionVariableId: id,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return { optionId: id, index };
|
|
238
|
+
}
|
|
178
239
|
}
|
|
179
240
|
|
|
180
241
|
export default ConditionsRunner;
|
package/src/DataPopulator.ts
CHANGED
|
@@ -169,7 +169,7 @@ class DataPopulator {
|
|
|
169
169
|
private getVariableValues(variableId: number): Readonly<(string | number)[]> {
|
|
170
170
|
const value = this.ovc.variables[variableId] || [];
|
|
171
171
|
if (this.id === undefined || this.index === undefined) {
|
|
172
|
-
return value;
|
|
172
|
+
return this.cleanArrayFromNullValues([...value]);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
175
|
// Getting variable parent option
|
|
@@ -181,7 +181,7 @@ class DataPopulator {
|
|
|
181
181
|
variableRootRelations.dependants.includes(this.id) ||
|
|
182
182
|
this.id === variableRootParent
|
|
183
183
|
) {
|
|
184
|
-
return [value[this.index]];
|
|
184
|
+
return this.cleanArrayFromNullValues([value[this.index]]);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
return this.cleanArrayFromNullValues([...value]);
|
|
@@ -203,9 +203,9 @@ class DataPopulator {
|
|
|
203
203
|
* @param arr Values array
|
|
204
204
|
*/
|
|
205
205
|
private cleanArrayFromNullValues(
|
|
206
|
-
arr: (string | number | null)[]
|
|
206
|
+
arr: (string | number | null | undefined)[]
|
|
207
207
|
): (string | number)[] {
|
|
208
|
-
return arr.map((v) => (v === null ? "" : v));
|
|
208
|
+
return arr.map((v) => (v === null || v === undefined ? "" : v));
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|