@oneblink/sdk-core 10.2.0 → 10.2.1-beta.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/dist/calculationService.js +5 -12
- package/dist/calculationService.js.map +1 -1
- package/dist/conditionalLogicService/conditionallyShowOption.js +4 -5
- package/dist/conditionalLogicService/conditionallyShowOption.js.map +1 -1
- package/dist/form-elements-regex.js +1 -0
- package/dist/form-elements-regex.js.map +1 -1
- package/dist/paymentService/getPaymentProvider.js +1 -0
- package/dist/paymentService/getPaymentProvider.js.map +1 -1
- package/dist/replaceCustomValues.js +3 -0
- package/dist/replaceCustomValues.js.map +1 -1
- package/package.json +2 -2
|
@@ -10,13 +10,6 @@ const isUnenteredValue = (value) => {
|
|
|
10
10
|
const isObjectWithValue = (obj) => {
|
|
11
11
|
return typeof obj === 'object' && obj !== null && 'value' in obj;
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
* Escape special RegExp characters in a string so it can be used as a literal
|
|
15
|
-
* pattern.
|
|
16
|
-
*/
|
|
17
|
-
function escapeRegExp(value) {
|
|
18
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
19
|
-
}
|
|
20
13
|
/**
|
|
21
14
|
* Workaround for `.toFixed()` not rounding floating point numbers correctly.
|
|
22
15
|
*/
|
|
@@ -29,8 +22,7 @@ function findFormElementByNamePath(formElements, elementName) {
|
|
|
29
22
|
const parts = elementName.split('|');
|
|
30
23
|
let searchElements = formElements;
|
|
31
24
|
let found;
|
|
32
|
-
for (
|
|
33
|
-
const name = parts[i];
|
|
25
|
+
for (const [i, name] of parts.entries()) {
|
|
34
26
|
// Match flattenFormElements: page/section are transparent for naming, but
|
|
35
27
|
// form/repeatableSet/infoPage require an explicit parent path segment.
|
|
36
28
|
found = flattenFormElements(searchElements).find((element) => 'name' in element && element.name === name);
|
|
@@ -127,6 +119,7 @@ function resolveElementValue({ submission, nestedElementNames, formElements, par
|
|
|
127
119
|
let isNestedRepeatableSet = false;
|
|
128
120
|
const nestedElementValues = elementValue.reduce((nestedElementValues, entry) => {
|
|
129
121
|
if (entry) {
|
|
122
|
+
// eslint-disable-next-line security/detect-object-injection -- key is a form element name
|
|
130
123
|
const nextElementValue = entry[nextElementName];
|
|
131
124
|
if (Array.isArray(nextElementValue)) {
|
|
132
125
|
if (nextElementValue.length) {
|
|
@@ -161,9 +154,10 @@ function resolveElementValue({ submission, nestedElementNames, formElements, par
|
|
|
161
154
|
// element name, find its value in the object and return it for the next
|
|
162
155
|
// iteration to handle.
|
|
163
156
|
if (typeof elementValue === 'object') {
|
|
164
|
-
const formFormElement = findFormElement(formElements, (e) => e.type === 'form' && e.name === nestedElementNames
|
|
157
|
+
const formFormElement = findFormElement(formElements, (e) => e.type === 'form' && e.name === nestedElementNames.at(index));
|
|
165
158
|
const nextElementName = nestedElementNames[index + 1];
|
|
166
159
|
if (formFormElement && nextElementName) {
|
|
160
|
+
// eslint-disable-next-line security/detect-object-injection -- key is a form element name
|
|
167
161
|
return elementValue[nextElementName];
|
|
168
162
|
}
|
|
169
163
|
}
|
|
@@ -339,7 +333,6 @@ export function evaluateExpression({ expression, submission, formElements, parse
|
|
|
339
333
|
return value;
|
|
340
334
|
});
|
|
341
335
|
const code = elementNames.reduce((code, elementName, index) => {
|
|
342
|
-
const regex = new RegExp(escapeRegExp(`{ELEMENT:${elementName}}`), 'g');
|
|
343
336
|
const replacement = `a${index}`;
|
|
344
337
|
exprParser.registerProperty(replacement, (submissionScope) => resolveElementValue({
|
|
345
338
|
submission: submissionScope,
|
|
@@ -347,7 +340,7 @@ export function evaluateExpression({ expression, submission, formElements, parse
|
|
|
347
340
|
formElements,
|
|
348
341
|
parseDayOnlyDate,
|
|
349
342
|
}));
|
|
350
|
-
return code.
|
|
343
|
+
return code.replaceAll(`{ELEMENT:${elementName}}`, replacement);
|
|
351
344
|
}, expression);
|
|
352
345
|
let parsed;
|
|
353
346
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calculationService.js","sourceRoot":"","sources":["../src/calculationService.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,mBAAmB,CAAA;AAChD,gFAAgF;AAChF,6EAA6E;AAC7E,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,OAAO,IAAI,gBAAgB,CAAA;AAErE,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,iBAAiB,GAElB,MAAM,oCAAoC,CAAA;AAS3C,MAAM,gBAAgB,GAAG,CAAC,KAA0B,EAAE,EAAE;IACtD,OAAO,CAAC,KAAK,IAAI,KAAK,KAAK,CAAC,CAAA;AAC9B,CAAC,CAAA;AAED,MAAM,iBAAiB,GAAG,CAAC,GAAY,EAA6B,EAAE;IACpE,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,IAAI,GAAG,CAAA;AAClE,CAAC,CAAA;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;AACrD,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,MAAc,EAAE,QAAgB;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAA;IACzC,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC,GAAG,UAAU,CAAA;IAClE,OAAO,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;AACxC,CAAC;AAED,SAAS,yBAAyB,CAChC,YAAqC,EACrC,WAAmB;IAEnB,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACpC,IAAI,cAAc,GAAG,YAAY,CAAA;IACjC,IAAI,KAAwC,CAAA;IAE5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QACrB,0EAA0E;QAC1E,uEAAuE;QACvE,KAAK,GAAG,mBAAmB,CAAC,cAAc,CAAC,CAAC,IAAI,CAC9C,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,IAAI,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CACxD,CAAA;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAM;QACR,CAAC;QAED,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC9C,OAAM;YACR,CAAC;YACD,cAAc,GAAG,KAAK,CAAC,QAAQ,CAAA;QACjC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mCAAmC,CAAC,EAClD,UAAU,EACV,YAAY,GAIb;IACC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,YAAY,GAAa,EAAE,CAAA;IACjC,qBAAqB,CAAC,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE;QACpD,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,MAAM,uBAAuB,GAAa,EAAE,CAAA;IAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC1B,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACrB,IAAI,CAAC,yBAAyB,CAAC,YAAY,EAAE,WAAW,CAAC,EAAE,CAAC;YAC1D,uBAAuB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,uBAAuB,CAAA;AAChC,CAAC;AAED,SAAS,mBAAmB,CAAC,EAC3B,UAAU,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,GAMjB;IACC,MAAM,kBAAkB,GAAG,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAA;IAC5D,OAAO,kBAAkB,CAAC,MAAM,CAC9B,CAAC,YAAiC,EAAE,WAAmB,EAAE,KAAa,EAAE,EAAE;QACxE,qCAAqC;QACrC,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,OAAO,YAAY,CAAA;QACrB,CAAC;QAED,8DAA8D;QAC9D,2EAA2E;QAC3E,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,qEAAqE;YACrE,sEAAsE;YACtE,oEAAoE;YACpE,2CAA2C;YAC3C,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YACtD,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,UAAU,GAAG,gBAAgB,CAAC,YAAY,CAAC,CAAA;gBACjD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;oBACxC,OAAO,UAAU,CAAC,OAAO,EAAE,CAAA;gBAC7B,CAAC;YACH,CAAC;iBAAM,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;gBAC9C,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;gBACzC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;oBACxC,OAAO,UAAU,CAAC,OAAO,EAAE,CAAA;gBAC7B,CAAC;YACH,CAAC;YAED,OAAO,UAAU,CAAC,YAAY,CAAC,CAAA;QACjC,CAAC;QAED,IAAI,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAChC,sEAAsE;YACtE,WAAW;YACX,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;gBACzB,OAAO,GAAG,CAAA;YACZ,CAAC;YAED,gEAAgE;YAChE,sEAAsE;YACtE,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;YACpE,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;gBACzD,OAAO,aAAa,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAA;YACnE,CAAC;YAED,mEAAmE;YACnE,sEAAsE;YACtE,qEAAqE;YACrE,iEAAiE;YACjE,MAAM,eAAe,GAAG,kBAAkB,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YAErD,IAAI,qBAAqB,GAAG,KAAK,CAAA;YACjC,MAAM,mBAAmB,GAAG,YAAY,CAAC,MAAM,CAC7C,CAAC,mBAAmB,EAAE,KAAK,EAAE,EAAE;gBAC7B,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,gBAAgB,GAAG,KAAK,CAAC,eAAe,CAAC,CAAA;oBAC/C,IAAI,KAAK,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC;wBACpC,IAAI,gBAAgB,CAAC,MAAM,EAAE,CAAC;4BAC5B,mBAAmB,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAA;4BAC7C,qBAAqB,GAAG,IAAI,CAAA;wBAC9B,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,mBAAmB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAA;oBAC5C,CAAC;gBACH,CAAC;gBACD,OAAO,mBAAmB,CAAA;YAC5B,CAAC,EACD,EAAe,CAChB,CAAA;YAED,mEAAmE;YACnE,kBAAkB;YAClB,IAAI,qBAAqB,EAAE,CAAC;gBAC1B,OAAO,mBAAmB,CAAA;YAC5B,CAAC;YAED,OAAO,mBAAmB,CAAC,MAAM,CAC/B,CAAC,KAAa,EAAE,kBAAuC,EAAE,EAAE;gBACzD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,OAAO,GAAG,CAAA;gBACZ,CAAC;gBACD,MAAM,KAAK,GAAG,UAAU,CAAC,kBAA4B,CAAC,CAAA;gBACtD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,OAAO,GAAG,CAAA;gBACZ,CAAC;gBACD,OAAO,KAAK,GAAG,KAAK,CAAA;YACtB,CAAC,EACD,CAAC,CACF,CAAA;QACH,CAAC;QAED,uEAAuE;QACvE,qEAAqE;QACrE,wEAAwE;QACxE,uBAAuB;QACvB,IAAI,OAAO,YAAY,KAAK,QAAQ,EAAE,CAAC;YACrC,MAAM,eAAe,GAAG,eAAe,CACrC,YAAY,EACZ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,CAAC,KAAK,CAAC,CACjE,CAAA;YACD,MAAM,eAAe,GAAG,kBAAkB,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YACrD,IAAI,eAAe,IAAI,eAAe,EAAE,CAAC;gBACvC,OAAQ,YAAwC,CAAC,eAAe,CAAC,CAAA;YACnE,CAAC;QACH,CAAC;QAED,yEAAyE;QACzE,IACE,iBAAiB,CAAC,YAAY,CAAC;YAC/B,OAAO,YAAY,CAAC,KAAK,KAAK,QAAQ,EACtC,CAAC;YACD,OAAO,UAAU,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;QACvC,CAAC;QAED,wEAAwE;QACxE,0BAA0B;QAC1B,OAAO,GAAG,CAAA;IACZ,CAAC,EACD,kBAAkB,CACnB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4HG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,UAAU,EACV,UAAU,EACV,YAAY,EACZ,gBAAgB,GAiBjB;IACC,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,IAAI,KAAK,CAAC,yBAAyB,CAAC;SAC5C,CAAA;IACH,CAAC;IAED,MAAM,YAAY,GAAa,EAAE,CAAA;IACjC,qBAAqB,CAAC,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE;QACpD,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAChC,CAAC,CAAC,CAAA;IAEF,MAAM,UAAU,GAAG,IAAI,gBAAgB,EAEpC,CAAA;IACH,UAAU,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAa,EAAE,SAAiB,EAAE,EAAE;QACxE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO,UAAU,CAAC,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAA;QACnD,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACF,UAAU,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC,KAAa,EAAE,EAAE;QAC1D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC1B,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACF,UAAU,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,KAAa,EAAE,EAAE;QACxD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,CAAC,CAAA;IACF,UAAU,CAAC,gBAAgB,CACzB,QAAQ,EACR,CAAC,KAA0B,EAAE,YAAoB,EAAE,EAAE;QACnD,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,YAAY,IAAI,CAAC,CAAA;QAC1B,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC,CACF,CAAA;IAED,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE;QAC5D,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,YAAY,CAAC,YAAY,WAAW,GAAG,CAAC,EAAE,GAAG,CAAC,CAAA;QACvE,MAAM,WAAW,GAAG,IAAI,KAAK,EAAE,CAAA;QAC/B,UAAU,CAAC,gBAAgB,CACzB,WAAW,EACX,CAAC,eAA+D,EAAE,EAAE,CAClE,mBAAmB,CAAC;YAClB,UAAU,EAAE,eAAe;YAC3B,kBAAkB,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC;YAC1C,YAAY;YACZ,gBAAgB;SACjB,CAAC,CACL,CAAA;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;IACzC,CAAC,EAAE,UAAU,CAAC,CAAA;IAEd,IAAI,MAAM,CAAA;IACV,IAAI,CAAC;QACH,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;SACjE,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACtC,2EAA2E;IAC3E,yCAAyC;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,CAAA;IAC1C,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,oEAAoE;IACpE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAA;AACnC,CAAC","sourcesContent":["import morphExpressions from 'morph-expressions'\n// morph-expressions is CommonJS and assigns `exports.default`, which Node's ESM\n// interop exposes as the whole `module.exports` rather than the constructor.\nconst ExpressionParser = morphExpressions.default ?? morphExpressions\nimport { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport {\n findFormElement,\n flattenFormElements,\n matchElementsTagRegex,\n} from './formElementsService.js'\nimport {\n DATE_ONLY_PATTERN,\n type ParseDayOnlyDate,\n} from './conditionalLogicService/types.js'\n\nexport type { ParseDayOnlyDate } from './conditionalLogicService/types.js'\n\nexport type EvaluateExpressionResult =\n | { type: 'RESULT'; value: number }\n | { type: 'MISSING_VALUES' }\n | { type: 'INVALID_EXPRESSION'; error: Error }\n\nconst isUnenteredValue = (value: unknown | undefined) => {\n return !value && value !== 0\n}\n\nconst isObjectWithValue = (obj: unknown): obj is { value: unknown } => {\n return typeof obj === 'object' && obj !== null && 'value' in obj\n}\n\n/**\n * Escape special RegExp characters in a string so it can be used as a literal\n * pattern.\n */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n\n/**\n * Workaround for `.toFixed()` not rounding floating point numbers correctly.\n */\nfunction roundToFixed(number: number, decimals: number) {\n const multiplier = Math.pow(10, decimals)\n const roundedNumber = Math.round(number * multiplier) / multiplier\n return roundedNumber.toFixed(decimals)\n}\n\nfunction findFormElementByNamePath(\n formElements: FormTypes.FormElement[],\n elementName: string,\n): FormTypes.FormElement | undefined {\n const parts = elementName.split('|')\n let searchElements = formElements\n let found: FormTypes.FormElement | undefined\n\n for (let i = 0; i < parts.length; i++) {\n const name = parts[i]\n // Match flattenFormElements: page/section are transparent for naming, but\n // form/repeatableSet/infoPage require an explicit parent path segment.\n found = flattenFormElements(searchElements).find(\n (element) => 'name' in element && element.name === name,\n )\n if (!found) {\n return\n }\n\n if (i < parts.length - 1) {\n if (!('elements' in found) || !found.elements) {\n return\n }\n searchElements = found.elements\n }\n }\n\n return found\n}\n\n/**\n * Find `{ELEMENT:...}` references in an expression whose name paths do not\n * exist in the form definition.\n *\n * Intended for server-side payment validation. Skip this on the client during\n * interactive calculation evaluation — it walks the form definition for every\n * referenced element and is unnecessary for display.\n *\n * @returns Missing element name paths (e.g. `Amount` or `Items|Amount`), or an\n * empty array when every referenced element exists.\n */\nexport function findMissingFormElementsInExpression({\n expression,\n formElements,\n}: {\n expression: string\n formElements: FormTypes.FormElement[]\n}): string[] {\n if (!expression) {\n return []\n }\n\n const elementNames: string[] = []\n matchElementsTagRegex(expression, ({ elementName }) => {\n elementNames.push(elementName)\n })\n\n const missingFormElementNames: string[] = []\n const seen = new Set<string>()\n for (const elementName of elementNames) {\n if (seen.has(elementName)) {\n continue\n }\n seen.add(elementName)\n if (!findFormElementByNamePath(formElements, elementName)) {\n missingFormElementNames.push(elementName)\n }\n }\n return missingFormElementNames\n}\n\nfunction resolveElementValue({\n submission,\n nestedElementNames,\n formElements,\n parseDayOnlyDate,\n}: {\n submission: SubmissionTypes.S3SubmissionData['submission']\n nestedElementNames: string[]\n formElements: FormTypes.FormElement[]\n parseDayOnlyDate: ParseDayOnlyDate\n}): unknown {\n const defaultAccumulator = submission[nestedElementNames[0]]\n return nestedElementNames.reduce(\n (elementValue: unknown | undefined, elementName: string, index: number) => {\n // Numbers can just be returned as is\n if (typeof elementValue === 'number') {\n return elementValue\n }\n\n // Attempt to get a number from the element value as a string.\n // NaN is accounted for in the calculation so we can return that from here.\n if (typeof elementValue === 'string') {\n // Day-only (`YYYY-MM-DD`) strings are parsed via parseDayOnlyDate so\n // callers control timezone. Other date strings use `new Date(value)`.\n // Numeric strings (e.g. `\"10\"`) fall through to parseFloat — `Date`\n // can otherwise treat them as valid dates.\n const isDayOnly = DATE_ONLY_PATTERN.test(elementValue)\n if (isDayOnly) {\n const parsedDate = parseDayOnlyDate(elementValue)\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate.getTime()\n }\n } else if (Number.isNaN(Number(elementValue))) {\n const parsedDate = new Date(elementValue)\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate.getTime()\n }\n }\n\n return parseFloat(elementValue)\n }\n\n if (Array.isArray(elementValue)) {\n // If there are no entries, return NaN to prevent the calculation from\n // running.\n if (!elementValue.length) {\n return NaN\n }\n\n // An array could be an element that allows multiple values e.g.\n // checkboxes. If that is the case, add them all together and move on.\n const elementValues = elementValue.map((entry) => parseFloat(entry))\n if (elementValues.every((entry) => !Number.isNaN(entry))) {\n return elementValues.reduce((number, entry) => number + entry, 0)\n }\n\n // Otherwise attempt to process it as a repeatable set. If we found\n // another repeatable set to process, pass it to the next element name\n // to iterate over the entries. If we are processing the entries in a\n // repeatable set, we can sum the number elements in the entries.\n const nextElementName = nestedElementNames[index + 1]\n\n let isNestedRepeatableSet = false\n const nestedElementValues = elementValue.reduce(\n (nestedElementValues, entry) => {\n if (entry) {\n const nextElementValue = entry[nextElementName]\n if (Array.isArray(nextElementValue)) {\n if (nextElementValue.length) {\n nestedElementValues.push(...nextElementValue)\n isNestedRepeatableSet = true\n }\n } else {\n nestedElementValues.push(nextElementValue)\n }\n }\n return nestedElementValues\n },\n [] as unknown[],\n )\n\n // If the nested element values are all arrays, pass them on to the\n // next iteration.\n if (isNestedRepeatableSet) {\n return nestedElementValues\n }\n\n return nestedElementValues.reduce(\n (total: number, nestedElementValue: unknown | undefined) => {\n if (Number.isNaN(total)) {\n return NaN\n }\n const value = parseFloat(nestedElementValue as string)\n if (Number.isNaN(value)) {\n return NaN\n }\n return total + value\n },\n 0,\n )\n }\n\n // If the value is an object, take the element name and check to see if\n // this element is a nested form element. If so, take the next nested\n // element name, find its value in the object and return it for the next\n // iteration to handle.\n if (typeof elementValue === 'object') {\n const formFormElement = findFormElement(\n formElements,\n (e) => e.type === 'form' && e.name === nestedElementNames[index],\n )\n const nextElementName = nestedElementNames[index + 1]\n if (formFormElement && nextElementName) {\n return (elementValue as Record<string, unknown>)[nextElementName]\n }\n }\n\n // \"compliance\" form element has an object value with a \"value\" property.\n if (\n isObjectWithValue(elementValue) &&\n typeof elementValue.value === 'string'\n ) {\n return parseFloat(elementValue.value)\n }\n\n // We did not find a number value from the known elements, assume we are\n // at the end of the line.\n return NaN\n },\n defaultAccumulator,\n )\n}\n\n/**\n * Evaluate a OneBlink calculation expression against form submission data.\n *\n * Day-only (`YYYY-MM-DD`) date parsing is injected via `parseDayOnlyDate` so\n * callers control timezone-aware behaviour (client vs server), matching the\n * pattern used by `conditionalLogicService`. Other date strings use\n * `new Date(value)`.\n *\n * #### Expression syntax\n *\n * Expressions are mathematical / logical formulas. Form element values are\n * referenced with `{ELEMENT:<elementName>}` tags (including nested paths via\n * `|`, e.g. `{ELEMENT:Children|Age}`).\n *\n * ##### Literals\n *\n * - **Numbers** — e.g. `0`, `5`, `5.4`\n * - **Strings** — single or double quoted, e.g. `\"abc\"`, `'abc'`\n * - **Booleans** — `true`, `false`\n *\n * ##### Arithmetic operators\n *\n * | Operator | Description | Example |\n * | -------- | ------------------------------------ | -------------- |\n * | `+` | Addition (also unary plus) | `1 + 2` → `3` |\n * | `-` | Subtraction (also unary minus) | `1 - 2` → `-1` |\n * | `*` | Multiplication | `4 * 2` → `8` |\n * | `/` | Division | `4 / 2` → `2` |\n * | `%` | Remainder | `8 % 3` → `2` |\n * | `++` | Unary increment | `++2` → `3` |\n * | `--` | Unary decrement | `--2` → `1` |\n *\n * Standard operator precedence applies. Use parentheses to override it, e.g.\n * `3 * (2 + 1)` → `9`.\n *\n * ##### Comparison operators\n *\n * | Operator | Description | Example |\n * | -------- | --------------------- | ------------------- |\n * | `==` | Equal | `2 == 2` → `true` |\n * | `!=` | Not equal | `2 != 3` → `true` |\n * | `>` | Greater than | `2 > 1` → `true` |\n * | `>=` | Greater than or equal | `2 >= 2` → `true` |\n * | `<` | Less than | `2 < 3` → `true` |\n * | `<=` | Less than or equal | `2 <= 2` → `true` |\n *\n * ##### Logical operators\n *\n * | Operator | Description | Example |\n * | -------- | ------------------------------------ | -------------------------- |\n * | `&&` | Logical AND (returns last operand) | `true && false` → `false` |\n * | `\\|\\|` | Logical OR (returns first truthy) | `false \\|\\| true` → `true` |\n * | `!` | Logical NOT | `!false` → `true` |\n *\n * ##### Element references\n *\n * - `{ELEMENT:Number}` — value of the root element named `Number`\n * - `{ELEMENT:Set\\|Child}` — value of `Child` within repeatable set / nested\n * form element `Set`\n * - Nested paths may include multiple segments:\n * `{ELEMENT:Parents\\|Children\\|Age}`\n *\n * How values are coerced for evaluation:\n *\n * - Numbers are used as-is\n * - Day-only (`YYYY-MM-DD`) strings are parsed via `parseDayOnlyDate`; other\n * non-numeric date strings use `new Date(value)`. Valid dates use\n * `Date#getTime()`, otherwise the string is parsed as a float via\n * `parseFloat`\n * - Arrays of numeric strings (e.g. checkboxes) are summed\n * - Repeatable set entries sum the referenced nested numeric values\n * - Empty arrays yield `NaN` (result type `MISSING_VALUES`)\n * - Compliance element objects use their `value` property\n *\n * ##### Built-in functions\n *\n * | Function | Description | Example |\n * | -------------------------------- | --------------------------------------------------------------------------- | ------------------------------- |\n * | `ROUND(value, precision)` | Round `value` to `precision` decimal places (correct floating-point rounds) | `ROUND(1.255, 2)` → `1.26` |\n * | `ROUND_DOWN(value)` | Round down to the nearest integer (`Math.floor`) | `ROUND_DOWN(1.9)` → `1` |\n * | `ROUND_UP(value)` | Round up to the nearest integer (`Math.ceil`) | `ROUND_UP(1.1)` → `2` |\n * | `ISNULL(value, defaultValue)` | If `value` is unentered (`null`/`undefined`/`\"\"`), return `defaultValue` or `0`; otherwise return `value` | `ISNULL({ELEMENT:A}, 10)` |\n *\n * `ROUND`, `ROUND_DOWN`, and `ROUND_UP` return `null` when `value` is `NaN` or\n * not finite.\n *\n * ##### Examples\n *\n * ```js\n * const result = calculationService.evaluateExpression({\n * expression: '{ELEMENT:Quantity} * {ELEMENT:Price}',\n * submission: { Quantity: 3, Price: 12.5 },\n * formElements: form.elements,\n * // Client: local timezone. Server: organisation timezone.\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // result === { type: 'RESULT', value: 37.5 }\n * ```\n *\n * ```js\n * calculationService.evaluateExpression({\n * expression: 'ROUND({ELEMENT:Amount} * 1.1, 2)',\n * submission: { Amount: 100 },\n * formElements: form.elements,\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // { type: 'RESULT', value: 110 }\n * ```\n *\n * ```js\n * calculationService.evaluateExpression({\n * expression: 'ISNULL({ELEMENT:Optional}, 0) + {ELEMENT:Required}',\n * submission: { Required: 5 },\n * formElements: form.elements,\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // { type: 'RESULT', value: 5 }\n * ```\n *\n * @param options\n * @returns A discriminated result describing the outcome of evaluation. Empty\n * expressions and parse failures return\n * `{ type: 'INVALID_EXPRESSION', error }`. Unexpected runtime errors are\n * rethrown.\n */\nexport function evaluateExpression({\n expression,\n submission,\n formElements,\n parseDayOnlyDate,\n}: {\n /** The calculation expression string to evaluate */\n expression: string\n /** Form submission data used to resolve `{ELEMENT:...}` references */\n submission: SubmissionTypes.S3SubmissionData['submission']\n /**\n * Form elements definition. Used to resolve nested `form` element values\n * referenced in the expression and to validate `{ELEMENT:...}` references.\n */\n formElements: FormTypes.FormElement[]\n /**\n * Parse `YYYY-MM-DD` strings when resolving date element values. sdk-core\n * only calls this for day-only values; other date strings use\n * `new Date(value)`.\n */\n parseDayOnlyDate: ParseDayOnlyDate\n}): EvaluateExpressionResult {\n if (!expression) {\n return {\n type: 'INVALID_EXPRESSION',\n error: new Error('Expression is required.'),\n }\n }\n\n const elementNames: string[] = []\n matchElementsTagRegex(expression, ({ elementName }) => {\n elementNames.push(elementName)\n })\n\n const exprParser = new ExpressionParser<\n SubmissionTypes.S3SubmissionData['submission']\n >()\n exprParser.registerFunction('ROUND', (value: number, precision: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return parseFloat(roundToFixed(value, precision))\n }\n return null\n })\n exprParser.registerFunction('ROUND_DOWN', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.floor(value)\n }\n return null\n })\n exprParser.registerFunction('ROUND_UP', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.ceil(value)\n }\n return null\n })\n exprParser.registerFunction(\n 'ISNULL',\n (value: unknown | undefined, defaultValue: number) => {\n if (isUnenteredValue(value)) {\n return defaultValue || 0\n }\n return value\n },\n )\n\n const code = elementNames.reduce((code, elementName, index) => {\n const regex = new RegExp(escapeRegExp(`{ELEMENT:${elementName}}`), 'g')\n const replacement = `a${index}`\n exprParser.registerProperty(\n replacement,\n (submissionScope: SubmissionTypes.S3SubmissionData['submission']) =>\n resolveElementValue({\n submission: submissionScope,\n nestedElementNames: elementName.split('|'),\n formElements,\n parseDayOnlyDate,\n }),\n )\n return code.replace(regex, replacement)\n }, expression)\n\n let parsed\n try {\n parsed = exprParser.parse(code.trim())\n } catch (error) {\n return {\n type: 'INVALID_EXPRESSION',\n error: error instanceof Error ? error : new Error(String(error)),\n }\n }\n\n const result = parsed.eval(submission)\n // Match historical behaviour: any non-NaN number (including Infinity) is a\n // usable calculation result for display.\n if (typeof result === 'number' && !Number.isNaN(result)) {\n return { type: 'RESULT', value: result }\n }\n\n // Successfully parsed expressions that do not yield a number (e.g. `true`,\n // non-numeric element values) previously returned `undefined` with no error\n // in the form UI. Treat them the same as missing submission values.\n return { type: 'MISSING_VALUES' }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"calculationService.js","sourceRoot":"","sources":["../src/calculationService.ts"],"names":[],"mappings":"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","sourcesContent":["import morphExpressions from 'morph-expressions'\n// morph-expressions is CommonJS and assigns `exports.default`, which Node's ESM\n// interop exposes as the whole `module.exports` rather than the constructor.\nconst ExpressionParser = morphExpressions.default ?? morphExpressions\nimport { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport {\n findFormElement,\n flattenFormElements,\n matchElementsTagRegex,\n} from './formElementsService.js'\nimport {\n DATE_ONLY_PATTERN,\n type ParseDayOnlyDate,\n} from './conditionalLogicService/types.js'\n\nexport type { ParseDayOnlyDate } from './conditionalLogicService/types.js'\n\nexport type EvaluateExpressionResult =\n | { type: 'RESULT'; value: number }\n | { type: 'MISSING_VALUES' }\n | { type: 'INVALID_EXPRESSION'; error: Error }\n\nconst isUnenteredValue = (value: unknown | undefined) => {\n return !value && value !== 0\n}\n\nconst isObjectWithValue = (obj: unknown): obj is { value: unknown } => {\n return typeof obj === 'object' && obj !== null && 'value' in obj\n}\n\n/**\n * Workaround for `.toFixed()` not rounding floating point numbers correctly.\n */\nfunction roundToFixed(number: number, decimals: number) {\n const multiplier = Math.pow(10, decimals)\n const roundedNumber = Math.round(number * multiplier) / multiplier\n return roundedNumber.toFixed(decimals)\n}\n\nfunction findFormElementByNamePath(\n formElements: FormTypes.FormElement[],\n elementName: string,\n): FormTypes.FormElement | undefined {\n const parts = elementName.split('|')\n let searchElements = formElements\n let found: FormTypes.FormElement | undefined\n\n for (const [i, name] of parts.entries()) {\n // Match flattenFormElements: page/section are transparent for naming, but\n // form/repeatableSet/infoPage require an explicit parent path segment.\n found = flattenFormElements(searchElements).find(\n (element) => 'name' in element && element.name === name,\n )\n if (!found) {\n return\n }\n\n if (i < parts.length - 1) {\n if (!('elements' in found) || !found.elements) {\n return\n }\n searchElements = found.elements\n }\n }\n\n return found\n}\n\n/**\n * Find `{ELEMENT:...}` references in an expression whose name paths do not\n * exist in the form definition.\n *\n * Intended for server-side payment validation. Skip this on the client during\n * interactive calculation evaluation — it walks the form definition for every\n * referenced element and is unnecessary for display.\n *\n * @returns Missing element name paths (e.g. `Amount` or `Items|Amount`), or an\n * empty array when every referenced element exists.\n */\nexport function findMissingFormElementsInExpression({\n expression,\n formElements,\n}: {\n expression: string\n formElements: FormTypes.FormElement[]\n}): string[] {\n if (!expression) {\n return []\n }\n\n const elementNames: string[] = []\n matchElementsTagRegex(expression, ({ elementName }) => {\n elementNames.push(elementName)\n })\n\n const missingFormElementNames: string[] = []\n const seen = new Set<string>()\n for (const elementName of elementNames) {\n if (seen.has(elementName)) {\n continue\n }\n seen.add(elementName)\n if (!findFormElementByNamePath(formElements, elementName)) {\n missingFormElementNames.push(elementName)\n }\n }\n return missingFormElementNames\n}\n\nfunction resolveElementValue({\n submission,\n nestedElementNames,\n formElements,\n parseDayOnlyDate,\n}: {\n submission: SubmissionTypes.S3SubmissionData['submission']\n nestedElementNames: string[]\n formElements: FormTypes.FormElement[]\n parseDayOnlyDate: ParseDayOnlyDate\n}): unknown {\n const defaultAccumulator = submission[nestedElementNames[0]]\n return nestedElementNames.reduce(\n (elementValue: unknown | undefined, elementName: string, index: number) => {\n // Numbers can just be returned as is\n if (typeof elementValue === 'number') {\n return elementValue\n }\n\n // Attempt to get a number from the element value as a string.\n // NaN is accounted for in the calculation so we can return that from here.\n if (typeof elementValue === 'string') {\n // Day-only (`YYYY-MM-DD`) strings are parsed via parseDayOnlyDate so\n // callers control timezone. Other date strings use `new Date(value)`.\n // Numeric strings (e.g. `\"10\"`) fall through to parseFloat — `Date`\n // can otherwise treat them as valid dates.\n const isDayOnly = DATE_ONLY_PATTERN.test(elementValue)\n if (isDayOnly) {\n const parsedDate = parseDayOnlyDate(elementValue)\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate.getTime()\n }\n } else if (Number.isNaN(Number(elementValue))) {\n const parsedDate = new Date(elementValue)\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate.getTime()\n }\n }\n\n return parseFloat(elementValue)\n }\n\n if (Array.isArray(elementValue)) {\n // If there are no entries, return NaN to prevent the calculation from\n // running.\n if (!elementValue.length) {\n return NaN\n }\n\n // An array could be an element that allows multiple values e.g.\n // checkboxes. If that is the case, add them all together and move on.\n const elementValues = elementValue.map((entry) => parseFloat(entry))\n if (elementValues.every((entry) => !Number.isNaN(entry))) {\n return elementValues.reduce((number, entry) => number + entry, 0)\n }\n\n // Otherwise attempt to process it as a repeatable set. If we found\n // another repeatable set to process, pass it to the next element name\n // to iterate over the entries. If we are processing the entries in a\n // repeatable set, we can sum the number elements in the entries.\n const nextElementName = nestedElementNames[index + 1]\n\n let isNestedRepeatableSet = false\n const nestedElementValues = elementValue.reduce(\n (nestedElementValues, entry) => {\n if (entry) {\n // eslint-disable-next-line security/detect-object-injection -- key is a form element name\n const nextElementValue = entry[nextElementName]\n if (Array.isArray(nextElementValue)) {\n if (nextElementValue.length) {\n nestedElementValues.push(...nextElementValue)\n isNestedRepeatableSet = true\n }\n } else {\n nestedElementValues.push(nextElementValue)\n }\n }\n return nestedElementValues\n },\n [] as unknown[],\n )\n\n // If the nested element values are all arrays, pass them on to the\n // next iteration.\n if (isNestedRepeatableSet) {\n return nestedElementValues\n }\n\n return nestedElementValues.reduce(\n (total: number, nestedElementValue: unknown | undefined) => {\n if (Number.isNaN(total)) {\n return NaN\n }\n const value = parseFloat(nestedElementValue as string)\n if (Number.isNaN(value)) {\n return NaN\n }\n return total + value\n },\n 0,\n )\n }\n\n // If the value is an object, take the element name and check to see if\n // this element is a nested form element. If so, take the next nested\n // element name, find its value in the object and return it for the next\n // iteration to handle.\n if (typeof elementValue === 'object') {\n const formFormElement = findFormElement(\n formElements,\n (e) => e.type === 'form' && e.name === nestedElementNames.at(index),\n )\n const nextElementName = nestedElementNames[index + 1]\n if (formFormElement && nextElementName) {\n // eslint-disable-next-line security/detect-object-injection -- key is a form element name\n return (elementValue as Record<string, unknown>)[nextElementName]\n }\n }\n\n // \"compliance\" form element has an object value with a \"value\" property.\n if (\n isObjectWithValue(elementValue) &&\n typeof elementValue.value === 'string'\n ) {\n return parseFloat(elementValue.value)\n }\n\n // We did not find a number value from the known elements, assume we are\n // at the end of the line.\n return NaN\n },\n defaultAccumulator,\n )\n}\n\n/**\n * Evaluate a OneBlink calculation expression against form submission data.\n *\n * Day-only (`YYYY-MM-DD`) date parsing is injected via `parseDayOnlyDate` so\n * callers control timezone-aware behaviour (client vs server), matching the\n * pattern used by `conditionalLogicService`. Other date strings use\n * `new Date(value)`.\n *\n * #### Expression syntax\n *\n * Expressions are mathematical / logical formulas. Form element values are\n * referenced with `{ELEMENT:<elementName>}` tags (including nested paths via\n * `|`, e.g. `{ELEMENT:Children|Age}`).\n *\n * ##### Literals\n *\n * - **Numbers** — e.g. `0`, `5`, `5.4`\n * - **Strings** — single or double quoted, e.g. `\"abc\"`, `'abc'`\n * - **Booleans** — `true`, `false`\n *\n * ##### Arithmetic operators\n *\n * | Operator | Description | Example |\n * | -------- | ------------------------------------ | -------------- |\n * | `+` | Addition (also unary plus) | `1 + 2` → `3` |\n * | `-` | Subtraction (also unary minus) | `1 - 2` → `-1` |\n * | `*` | Multiplication | `4 * 2` → `8` |\n * | `/` | Division | `4 / 2` → `2` |\n * | `%` | Remainder | `8 % 3` → `2` |\n * | `++` | Unary increment | `++2` → `3` |\n * | `--` | Unary decrement | `--2` → `1` |\n *\n * Standard operator precedence applies. Use parentheses to override it, e.g.\n * `3 * (2 + 1)` → `9`.\n *\n * ##### Comparison operators\n *\n * | Operator | Description | Example |\n * | -------- | --------------------- | ------------------- |\n * | `==` | Equal | `2 == 2` → `true` |\n * | `!=` | Not equal | `2 != 3` → `true` |\n * | `>` | Greater than | `2 > 1` → `true` |\n * | `>=` | Greater than or equal | `2 >= 2` → `true` |\n * | `<` | Less than | `2 < 3` → `true` |\n * | `<=` | Less than or equal | `2 <= 2` → `true` |\n *\n * ##### Logical operators\n *\n * | Operator | Description | Example |\n * | -------- | ------------------------------------ | -------------------------- |\n * | `&&` | Logical AND (returns last operand) | `true && false` → `false` |\n * | `\\|\\|` | Logical OR (returns first truthy) | `false \\|\\| true` → `true` |\n * | `!` | Logical NOT | `!false` → `true` |\n *\n * ##### Element references\n *\n * - `{ELEMENT:Number}` — value of the root element named `Number`\n * - `{ELEMENT:Set\\|Child}` — value of `Child` within repeatable set / nested\n * form element `Set`\n * - Nested paths may include multiple segments:\n * `{ELEMENT:Parents\\|Children\\|Age}`\n *\n * How values are coerced for evaluation:\n *\n * - Numbers are used as-is\n * - Day-only (`YYYY-MM-DD`) strings are parsed via `parseDayOnlyDate`; other\n * non-numeric date strings use `new Date(value)`. Valid dates use\n * `Date#getTime()`, otherwise the string is parsed as a float via\n * `parseFloat`\n * - Arrays of numeric strings (e.g. checkboxes) are summed\n * - Repeatable set entries sum the referenced nested numeric values\n * - Empty arrays yield `NaN` (result type `MISSING_VALUES`)\n * - Compliance element objects use their `value` property\n *\n * ##### Built-in functions\n *\n * | Function | Description | Example |\n * | -------------------------------- | --------------------------------------------------------------------------- | ------------------------------- |\n * | `ROUND(value, precision)` | Round `value` to `precision` decimal places (correct floating-point rounds) | `ROUND(1.255, 2)` → `1.26` |\n * | `ROUND_DOWN(value)` | Round down to the nearest integer (`Math.floor`) | `ROUND_DOWN(1.9)` → `1` |\n * | `ROUND_UP(value)` | Round up to the nearest integer (`Math.ceil`) | `ROUND_UP(1.1)` → `2` |\n * | `ISNULL(value, defaultValue)` | If `value` is unentered (`null`/`undefined`/`\"\"`), return `defaultValue` or `0`; otherwise return `value` | `ISNULL({ELEMENT:A}, 10)` |\n *\n * `ROUND`, `ROUND_DOWN`, and `ROUND_UP` return `null` when `value` is `NaN` or\n * not finite.\n *\n * ##### Examples\n *\n * ```js\n * const result = calculationService.evaluateExpression({\n * expression: '{ELEMENT:Quantity} * {ELEMENT:Price}',\n * submission: { Quantity: 3, Price: 12.5 },\n * formElements: form.elements,\n * // Client: local timezone. Server: organisation timezone.\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // result === { type: 'RESULT', value: 37.5 }\n * ```\n *\n * ```js\n * calculationService.evaluateExpression({\n * expression: 'ROUND({ELEMENT:Amount} * 1.1, 2)',\n * submission: { Amount: 100 },\n * formElements: form.elements,\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // { type: 'RESULT', value: 110 }\n * ```\n *\n * ```js\n * calculationService.evaluateExpression({\n * expression: 'ISNULL({ELEMENT:Optional}, 0) + {ELEMENT:Required}',\n * submission: { Required: 5 },\n * formElements: form.elements,\n * parseDayOnlyDate: (value) => new Date(`${value}T00:00:00.000Z`),\n * })\n * // { type: 'RESULT', value: 5 }\n * ```\n *\n * @param options\n * @returns A discriminated result describing the outcome of evaluation. Empty\n * expressions and parse failures return\n * `{ type: 'INVALID_EXPRESSION', error }`. Unexpected runtime errors are\n * rethrown.\n */\nexport function evaluateExpression({\n expression,\n submission,\n formElements,\n parseDayOnlyDate,\n}: {\n /** The calculation expression string to evaluate */\n expression: string\n /** Form submission data used to resolve `{ELEMENT:...}` references */\n submission: SubmissionTypes.S3SubmissionData['submission']\n /**\n * Form elements definition. Used to resolve nested `form` element values\n * referenced in the expression and to validate `{ELEMENT:...}` references.\n */\n formElements: FormTypes.FormElement[]\n /**\n * Parse `YYYY-MM-DD` strings when resolving date element values. sdk-core\n * only calls this for day-only values; other date strings use\n * `new Date(value)`.\n */\n parseDayOnlyDate: ParseDayOnlyDate\n}): EvaluateExpressionResult {\n if (!expression) {\n return {\n type: 'INVALID_EXPRESSION',\n error: new Error('Expression is required.'),\n }\n }\n\n const elementNames: string[] = []\n matchElementsTagRegex(expression, ({ elementName }) => {\n elementNames.push(elementName)\n })\n\n const exprParser = new ExpressionParser<\n SubmissionTypes.S3SubmissionData['submission']\n >()\n exprParser.registerFunction('ROUND', (value: number, precision: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return parseFloat(roundToFixed(value, precision))\n }\n return null\n })\n exprParser.registerFunction('ROUND_DOWN', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.floor(value)\n }\n return null\n })\n exprParser.registerFunction('ROUND_UP', (value: number) => {\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n return Math.ceil(value)\n }\n return null\n })\n exprParser.registerFunction(\n 'ISNULL',\n (value: unknown | undefined, defaultValue: number) => {\n if (isUnenteredValue(value)) {\n return defaultValue || 0\n }\n return value\n },\n )\n\n const code = elementNames.reduce((code, elementName, index) => {\n const replacement = `a${index}`\n exprParser.registerProperty(\n replacement,\n (submissionScope: SubmissionTypes.S3SubmissionData['submission']) =>\n resolveElementValue({\n submission: submissionScope,\n nestedElementNames: elementName.split('|'),\n formElements,\n parseDayOnlyDate,\n }),\n )\n return code.replaceAll(`{ELEMENT:${elementName}}`, replacement)\n }, expression)\n\n let parsed\n try {\n parsed = exprParser.parse(code.trim())\n } catch (error) {\n return {\n type: 'INVALID_EXPRESSION',\n error: error instanceof Error ? error : new Error(String(error)),\n }\n }\n\n const result = parsed.eval(submission)\n // Match historical behaviour: any non-NaN number (including Infinity) is a\n // usable calculation result for display.\n if (typeof result === 'number' && !Number.isNaN(result)) {\n return { type: 'RESULT', value: result }\n }\n\n // Successfully parsed expressions that do not yield a number (e.g. `true`,\n // non-numeric element values) previously returned `undefined` with no error\n // in the form UI. Treat them the same as missing submission values.\n return { type: 'MISSING_VALUES' }\n}\n"]}
|
|
@@ -164,9 +164,8 @@ export default function conditionallyShowOption({ formElementsCtrl, elementToEva
|
|
|
164
164
|
return 'SHOW';
|
|
165
165
|
let isLoadingDynamicDEPENDENCY = false;
|
|
166
166
|
let isLoadingStaticDEPENDENCY = false;
|
|
167
|
-
|
|
168
|
-
for (
|
|
169
|
-
const predicate = validPredicates[i];
|
|
167
|
+
let showCount = 0;
|
|
168
|
+
for (const predicate of validPredicates) {
|
|
170
169
|
const predicateResult = conditionallyShowOptionByPredicate({
|
|
171
170
|
formElementsCtrl,
|
|
172
171
|
predicate,
|
|
@@ -175,7 +174,7 @@ export default function conditionallyShowOption({ formElementsCtrl, elementToEva
|
|
|
175
174
|
switch (predicateResult) {
|
|
176
175
|
case 'SHOW': {
|
|
177
176
|
if (elementToEvaluate.requiresAllConditionallyShowOptionsPredicates) {
|
|
178
|
-
|
|
177
|
+
showCount++;
|
|
179
178
|
break;
|
|
180
179
|
}
|
|
181
180
|
// if match any return show
|
|
@@ -192,7 +191,7 @@ export default function conditionallyShowOption({ formElementsCtrl, elementToEva
|
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
if (elementToEvaluate.requiresAllConditionallyShowOptionsPredicates &&
|
|
195
|
-
|
|
194
|
+
showCount === validPredicates.length) {
|
|
196
195
|
return 'SHOW';
|
|
197
196
|
}
|
|
198
197
|
if (isLoadingDynamicDEPENDENCY) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditionallyShowOption.js","sourceRoot":"","sources":["../../src/conditionalLogicService/conditionallyShowOption.ts"],"names":[],"mappings":"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","sourcesContent":["import { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport evaluateConditionalOptionsPredicate from './evaluateConditionalOptionsPredicate.js'\nimport { FormElementsCtrl } from './types.js'\nimport { typeCastService } from '../index.js'\nimport conditionallyShowElement from './conditionallyShowElement.js'\n\nexport type ShouldShowOption =\n | 'SHOW'\n | 'HIDE'\n | 'LOADING_DYNAMIC_DEPENDENCY'\n | 'LOADING_STATIC_DEPENDENCY'\n\nconst handleAttributePredicate = ({\n predicate,\n model,\n predicateElement,\n}: {\n predicate: FormTypes.ChoiceElementOptionAttribute\n model: SubmissionTypes.S3SubmissionData['submission'] | undefined\n predicateElement: FormTypes.FormElementWithOptions\n}) => {\n const values = model?.[predicateElement.name]\n if (!values) return true\n\n if (\n Array.isArray(values) &&\n (!values.length ||\n !values.filter((value) => typeof value !== 'undefined').length)\n ) {\n return true\n }\n\n return evaluateConditionalOptionsPredicate({\n predicate: {\n type: 'OPTIONS',\n elementId: predicate.elementId,\n optionIds: predicate.optionIds,\n },\n predicateValue: values,\n predicateElement,\n })\n}\n\nconst conditionallyShowOptionByPredicate = ({\n formElementsCtrl,\n predicate,\n elementsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n predicate: FormTypes.ChoiceElementOptionAttribute\n elementsEvaluated: string[]\n}): ShouldShowOption => {\n // Validate the predicate data, if it is invalid,\n // we will always show the field\n if (\n !predicate ||\n !predicate.elementId ||\n !predicate.optionIds ||\n !predicate.optionIds.length\n ) {\n return 'SHOW'\n }\n const predicateElement = formElementsCtrl.flattenedElements.find(\n (element) => {\n return element.id === predicate.elementId\n },\n )\n\n // If we cant find the element used for the predicate,\n // we can check to see if the element being evaluated\n // is in a repeatable set and the predicate element is\n // in a parent list of elements.\n if (!predicateElement) {\n if (formElementsCtrl.parentFormElementsCtrl) {\n return conditionallyShowOptionByPredicate({\n formElementsCtrl: formElementsCtrl.parentFormElementsCtrl,\n predicate,\n elementsEvaluated,\n })\n } else {\n return 'HIDE'\n }\n }\n\n const optionsPredicateElement =\n typeCastService.formElements.toOptionsElement(predicateElement)\n if (!optionsPredicateElement) {\n return 'HIDE'\n }\n // If the predicate element does not have any options to evaluate,\n // we will show the element.\n // Unless the predicate element has dynamic options and\n // options have not been fetched yet.\n const isLoadingPredicateElementsOptions = !Array.isArray(\n optionsPredicateElement.options,\n )\n if (isLoadingPredicateElementsOptions) {\n if (optionsPredicateElement.optionsType === 'DYNAMIC') {\n return 'LOADING_DYNAMIC_DEPENDENCY'\n } else {\n return 'LOADING_STATIC_DEPENDENCY'\n }\n }\n\n const everyOptionIsShowing = predicate.optionIds.every((id) => {\n const predicateOption = optionsPredicateElement.options?.find(\n (o) => o.id === id,\n )\n if (!predicateOption) return 'HIDE'\n\n return conditionallyShowOption({\n formElementsCtrl: {\n model: formElementsCtrl.model,\n flattenedElements: [],\n },\n elementToEvaluate: optionsPredicateElement,\n optionToEvaluate: predicateOption,\n optionsEvaluated: elementsEvaluated,\n })\n })\n\n if (!everyOptionIsShowing) {\n return 'HIDE'\n }\n\n // Check to see if the model has one of the valid values to show the element\n const shouldShow = handleAttributePredicate({\n predicate,\n model: formElementsCtrl.model,\n predicateElement: optionsPredicateElement,\n })\n return shouldShow ? 'SHOW' : 'HIDE'\n}\n\nconst isAttributeFilterValid = ({\n formElementsCtrl,\n predicate,\n elementsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n predicate: FormTypes.ChoiceElementOptionAttribute\n elementsEvaluated: string[]\n}): boolean => {\n const predicateElement = formElementsCtrl.flattenedElements.find(\n (element) => {\n return element.id === predicate.elementId\n },\n )\n\n // If we cant find the element used for the predicate,\n // we can check to see if the element being evaluated\n // is in a repeatable set and the predicate element is\n // in a parent list of elements.\n if (!predicateElement) {\n if (formElementsCtrl.parentFormElementsCtrl) {\n return isAttributeFilterValid({\n formElementsCtrl: formElementsCtrl.parentFormElementsCtrl,\n predicate,\n elementsEvaluated,\n })\n } else {\n return false\n }\n }\n\n // now we have the model and predicate element, verify that the predicate element\n // is not hidden\n if (\n // Will never be a page, just making typescript happy :)\n predicateElement.type === 'page' ||\n predicateElement.type === 'section' ||\n !conditionallyShowElement({\n formElementsCtrl,\n elementToEvaluate: predicateElement,\n elementsEvaluated: [],\n })\n ) {\n return false\n }\n\n // verify that at least one option is selected\n const values = formElementsCtrl.model?.[predicateElement.name]\n if (!values) return false\n // if the model value is an array, verify that it has a selection\n if (\n Array.isArray(values) &&\n (!values.length ||\n !values.filter((value) => typeof value !== 'undefined').length)\n ) {\n return false\n }\n\n return true\n}\n\nexport default function conditionallyShowOption({\n formElementsCtrl,\n elementToEvaluate,\n optionToEvaluate,\n optionsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n elementToEvaluate: FormTypes.FormElementWithOptions\n optionToEvaluate: FormTypes.ChoiceElementOption\n optionsEvaluated: string[]\n}): ShouldShowOption {\n // If the element does not have the `conditionallyShow` flag set,\n // we can always show the option.\n\n if (\n !elementToEvaluate.conditionallyShowOptions ||\n !optionToEvaluate ||\n !optionToEvaluate.attributes ||\n !Array.isArray(optionToEvaluate.attributes) ||\n !optionToEvaluate.attributes.length\n ) {\n return 'SHOW'\n }\n\n // Check to see if this element has already been used to evaluate\n // if the option should be shown based on parent element conditional logic\n if (optionsEvaluated.some((optionId) => optionId === optionToEvaluate.id)) {\n throw new Error(\n 'Your conditional logic has caused an infinite loop. Check the following Fields to ensure element A does not rely on element B if element B also relies on element A.',\n )\n } else {\n optionsEvaluated.push(optionToEvaluate.id)\n }\n const validPredicates = (optionToEvaluate.attributes || []).filter(\n (predicate) => {\n return isAttributeFilterValid({\n formElementsCtrl,\n predicate,\n elementsEvaluated: optionsEvaluated,\n })\n },\n )\n if (!validPredicates.length) return 'SHOW'\n\n let isLoadingDynamicDEPENDENCY = false\n let isLoadingStaticDEPENDENCY = false\n const showResults = Array(validPredicates.length).fill(false)\n for (let i = 0; i < validPredicates.length; i++) {\n const predicate = validPredicates[i]\n const predicateResult = conditionallyShowOptionByPredicate({\n formElementsCtrl,\n predicate,\n elementsEvaluated: optionsEvaluated,\n })\n switch (predicateResult) {\n case 'SHOW': {\n if (elementToEvaluate.requiresAllConditionallyShowOptionsPredicates) {\n showResults[i] = true\n break\n }\n // if match any return show\n return 'SHOW'\n }\n case 'LOADING_DYNAMIC_DEPENDENCY': {\n isLoadingDynamicDEPENDENCY = true\n break\n }\n case 'LOADING_STATIC_DEPENDENCY': {\n isLoadingStaticDEPENDENCY = true\n break\n }\n }\n }\n if (\n elementToEvaluate.requiresAllConditionallyShowOptionsPredicates &&\n showResults.every((result) => result)\n ) {\n return 'SHOW'\n }\n if (isLoadingDynamicDEPENDENCY) {\n return 'LOADING_DYNAMIC_DEPENDENCY'\n } else if (isLoadingStaticDEPENDENCY) {\n return 'LOADING_STATIC_DEPENDENCY'\n } else {\n return 'HIDE'\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"conditionallyShowOption.js","sourceRoot":"","sources":["../../src/conditionalLogicService/conditionallyShowOption.ts"],"names":[],"mappings":"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","sourcesContent":["import { FormTypes, SubmissionTypes } from '@oneblink/types'\nimport evaluateConditionalOptionsPredicate from './evaluateConditionalOptionsPredicate.js'\nimport { FormElementsCtrl } from './types.js'\nimport { typeCastService } from '../index.js'\nimport conditionallyShowElement from './conditionallyShowElement.js'\n\nexport type ShouldShowOption =\n | 'SHOW'\n | 'HIDE'\n | 'LOADING_DYNAMIC_DEPENDENCY'\n | 'LOADING_STATIC_DEPENDENCY'\n\nconst handleAttributePredicate = ({\n predicate,\n model,\n predicateElement,\n}: {\n predicate: FormTypes.ChoiceElementOptionAttribute\n model: SubmissionTypes.S3SubmissionData['submission'] | undefined\n predicateElement: FormTypes.FormElementWithOptions\n}) => {\n const values = model?.[predicateElement.name]\n if (!values) return true\n\n if (\n Array.isArray(values) &&\n (!values.length ||\n !values.filter((value) => typeof value !== 'undefined').length)\n ) {\n return true\n }\n\n return evaluateConditionalOptionsPredicate({\n predicate: {\n type: 'OPTIONS',\n elementId: predicate.elementId,\n optionIds: predicate.optionIds,\n },\n predicateValue: values,\n predicateElement,\n })\n}\n\nconst conditionallyShowOptionByPredicate = ({\n formElementsCtrl,\n predicate,\n elementsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n predicate: FormTypes.ChoiceElementOptionAttribute\n elementsEvaluated: string[]\n}): ShouldShowOption => {\n // Validate the predicate data, if it is invalid,\n // we will always show the field\n if (\n !predicate ||\n !predicate.elementId ||\n !predicate.optionIds ||\n !predicate.optionIds.length\n ) {\n return 'SHOW'\n }\n const predicateElement = formElementsCtrl.flattenedElements.find(\n (element) => {\n return element.id === predicate.elementId\n },\n )\n\n // If we cant find the element used for the predicate,\n // we can check to see if the element being evaluated\n // is in a repeatable set and the predicate element is\n // in a parent list of elements.\n if (!predicateElement) {\n if (formElementsCtrl.parentFormElementsCtrl) {\n return conditionallyShowOptionByPredicate({\n formElementsCtrl: formElementsCtrl.parentFormElementsCtrl,\n predicate,\n elementsEvaluated,\n })\n } else {\n return 'HIDE'\n }\n }\n\n const optionsPredicateElement =\n typeCastService.formElements.toOptionsElement(predicateElement)\n if (!optionsPredicateElement) {\n return 'HIDE'\n }\n // If the predicate element does not have any options to evaluate,\n // we will show the element.\n // Unless the predicate element has dynamic options and\n // options have not been fetched yet.\n const isLoadingPredicateElementsOptions = !Array.isArray(\n optionsPredicateElement.options,\n )\n if (isLoadingPredicateElementsOptions) {\n if (optionsPredicateElement.optionsType === 'DYNAMIC') {\n return 'LOADING_DYNAMIC_DEPENDENCY'\n } else {\n return 'LOADING_STATIC_DEPENDENCY'\n }\n }\n\n const everyOptionIsShowing = predicate.optionIds.every((id) => {\n const predicateOption = optionsPredicateElement.options?.find(\n (o) => o.id === id,\n )\n if (!predicateOption) return 'HIDE'\n\n return conditionallyShowOption({\n formElementsCtrl: {\n model: formElementsCtrl.model,\n flattenedElements: [],\n },\n elementToEvaluate: optionsPredicateElement,\n optionToEvaluate: predicateOption,\n optionsEvaluated: elementsEvaluated,\n })\n })\n\n if (!everyOptionIsShowing) {\n return 'HIDE'\n }\n\n // Check to see if the model has one of the valid values to show the element\n const shouldShow = handleAttributePredicate({\n predicate,\n model: formElementsCtrl.model,\n predicateElement: optionsPredicateElement,\n })\n return shouldShow ? 'SHOW' : 'HIDE'\n}\n\nconst isAttributeFilterValid = ({\n formElementsCtrl,\n predicate,\n elementsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n predicate: FormTypes.ChoiceElementOptionAttribute\n elementsEvaluated: string[]\n}): boolean => {\n const predicateElement = formElementsCtrl.flattenedElements.find(\n (element) => {\n return element.id === predicate.elementId\n },\n )\n\n // If we cant find the element used for the predicate,\n // we can check to see if the element being evaluated\n // is in a repeatable set and the predicate element is\n // in a parent list of elements.\n if (!predicateElement) {\n if (formElementsCtrl.parentFormElementsCtrl) {\n return isAttributeFilterValid({\n formElementsCtrl: formElementsCtrl.parentFormElementsCtrl,\n predicate,\n elementsEvaluated,\n })\n } else {\n return false\n }\n }\n\n // now we have the model and predicate element, verify that the predicate element\n // is not hidden\n if (\n // Will never be a page, just making typescript happy :)\n predicateElement.type === 'page' ||\n predicateElement.type === 'section' ||\n !conditionallyShowElement({\n formElementsCtrl,\n elementToEvaluate: predicateElement,\n elementsEvaluated: [],\n })\n ) {\n return false\n }\n\n // verify that at least one option is selected\n const values = formElementsCtrl.model?.[predicateElement.name]\n if (!values) return false\n // if the model value is an array, verify that it has a selection\n if (\n Array.isArray(values) &&\n (!values.length ||\n !values.filter((value) => typeof value !== 'undefined').length)\n ) {\n return false\n }\n\n return true\n}\n\nexport default function conditionallyShowOption({\n formElementsCtrl,\n elementToEvaluate,\n optionToEvaluate,\n optionsEvaluated,\n}: {\n formElementsCtrl: FormElementsCtrl\n elementToEvaluate: FormTypes.FormElementWithOptions\n optionToEvaluate: FormTypes.ChoiceElementOption\n optionsEvaluated: string[]\n}): ShouldShowOption {\n // If the element does not have the `conditionallyShow` flag set,\n // we can always show the option.\n\n if (\n !elementToEvaluate.conditionallyShowOptions ||\n !optionToEvaluate ||\n !optionToEvaluate.attributes ||\n !Array.isArray(optionToEvaluate.attributes) ||\n !optionToEvaluate.attributes.length\n ) {\n return 'SHOW'\n }\n\n // Check to see if this element has already been used to evaluate\n // if the option should be shown based on parent element conditional logic\n if (optionsEvaluated.some((optionId) => optionId === optionToEvaluate.id)) {\n throw new Error(\n 'Your conditional logic has caused an infinite loop. Check the following Fields to ensure element A does not rely on element B if element B also relies on element A.',\n )\n } else {\n optionsEvaluated.push(optionToEvaluate.id)\n }\n const validPredicates = (optionToEvaluate.attributes || []).filter(\n (predicate) => {\n return isAttributeFilterValid({\n formElementsCtrl,\n predicate,\n elementsEvaluated: optionsEvaluated,\n })\n },\n )\n if (!validPredicates.length) return 'SHOW'\n\n let isLoadingDynamicDEPENDENCY = false\n let isLoadingStaticDEPENDENCY = false\n let showCount = 0\n for (const predicate of validPredicates) {\n const predicateResult = conditionallyShowOptionByPredicate({\n formElementsCtrl,\n predicate,\n elementsEvaluated: optionsEvaluated,\n })\n switch (predicateResult) {\n case 'SHOW': {\n if (elementToEvaluate.requiresAllConditionallyShowOptionsPredicates) {\n showCount++\n break\n }\n // if match any return show\n return 'SHOW'\n }\n case 'LOADING_DYNAMIC_DEPENDENCY': {\n isLoadingDynamicDEPENDENCY = true\n break\n }\n case 'LOADING_STATIC_DEPENDENCY': {\n isLoadingStaticDEPENDENCY = true\n break\n }\n }\n }\n if (\n elementToEvaluate.requiresAllConditionallyShowOptionsPredicates &&\n showCount === validPredicates.length\n ) {\n return 'SHOW'\n }\n if (isLoadingDynamicDEPENDENCY) {\n return 'LOADING_DYNAMIC_DEPENDENCY'\n } else if (isLoadingStaticDEPENDENCY) {\n return 'LOADING_STATIC_DEPENDENCY'\n } else {\n return 'HIDE'\n }\n}\n"]}
|
|
@@ -74,6 +74,7 @@ export function matchElementsTagRegex(options, matchHandler) {
|
|
|
74
74
|
: NestedElementRegex;
|
|
75
75
|
// Fresh RegExp per call so re-entrant matchElementsTagRegex (and concurrent
|
|
76
76
|
// callers) do not share mutable lastIndex on the exported /g patterns.
|
|
77
|
+
// eslint-disable-next-line security/detect-non-literal-regexp -- clones one of the static literal regexes above
|
|
77
78
|
const regex = new RegExp(regexTemplate.source, regexTemplate.flags);
|
|
78
79
|
while ((matches = regex.exec(text)) !== null) {
|
|
79
80
|
if (matches?.length < 3)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"form-elements-regex.js","sourceRoot":"","sources":["../src/form-elements-regex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAA;AACzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,+BAA+B,CAAA;AAC9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,8BAA8B,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAgBK,EACL,YAGU;IAEV,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAA;IACjE,MAAM,qBAAqB,GACzB,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAA;IAChE,MAAM,kBAAkB,GACtB,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAA;IAC7D,IAAI,OAAO,CAAA;IACX,MAAM,aAAa,GAAG,kBAAkB;QACtC,CAAC,CAAC,qBAAqB;YACrB,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,iCAAiC;QACrC,CAAC,CAAC,qBAAqB;YACrB,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,kBAAkB,CAAA;IACxB,4EAA4E;IAC5E,uEAAuE;IACvE,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IACnE,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7C,IAAI,OAAO,EAAE,MAAM,GAAG,CAAC;YAAE,SAAQ;QAEjC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QAC9B,YAAY,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACzD,CAAC;AACH,CAAC","sourcesContent":["/**\n * The regex used for matching `{ELEMENT:<elementName>}` tags in the OneBlink\n * platform. Ignores nested elements based on the '|' character. Will match\n * `\"{ELEMENT:Parent_Name}\"` but will NOT match `\"{ELEMENT:Children|Name}\"`.\n */\nexport const RootElementRegex = /({ELEMENT:)([^|}]+)(})/g\n/**\n * The regex used for matching `{ELEMENT:<elementName>|<nestedElementName>}`\n * tags in the OneBlink platform. Includes nested elements based on the '|'\n * character. {ELEMENT:Children|Name} would be matched. Will match\n * `\"{ELEMENT:Parent_Name}\"` and `\"{ELEMENT:Children|Name}\"`.\n */\nexport const NestedElementRegex = /({ELEMENT:)([^}]+)(})/g\n\n/**\n * The regex used for matching `{ELEMENT_VALUE:<elementName>}` tags in the OneBlink\n * platform. Ignores nested elements based on the '|' character. Will match\n * `\"{ELEMENT_VALUE:Parent_Name}\"` but will NOT match `\"{ELEMENT_VALUE:Children|Name}\"`.\n */\nexport const RootSubmissionValueElementRegex = /({ELEMENT_VALUE:)([^|}]+)(})/g\n/**\n * The regex used for matching `{ELEMENT_VALUE:<elementName>|<nestedElementName>}`\n * tags in the OneBlink platform. Includes nested elements based on the '|'\n * character. {ELEMENT_VALUE:Children|Name} would be matched. Will match\n * `\"{ELEMENT_VALUE:Parent_Name}\"` and `\"{ELEMENT_VALUE:Children|Name}\"`.\n */\nexport const NestedSubmissionValueElementRegex = /({ELEMENT_VALUE:)([^}]+)(})/g\n\n/**\n * Takes a string and calls a provided handler function for each found instance\n * of `{ELEMENT:<elementName>}` in the string. Used to replace values in\n * OneBlink calculation and info (HTML) elements.\n *\n * #### Example\n *\n * ```js\n * formElementsService.matchElementsTagRegex(\n * myString,\n * ({ elementName, elementMatch }) => {\n * const v = submission[elementName]\n * myString = myString.replace(elementMatch, v)\n * },\n * )\n * ```\n *\n * Or\n *\n * ```js\n * formElementsService.matchElementsTagRegex(\n * {\n * text: myString,\n * excludeNestedElements: true,\n * },\n * ({ elementName, elementMatch }) => {\n * const v = submission[elementName]\n * myString = myString.replace(elementMatch, v)\n * },\n * )\n * ```\n *\n * @param options\n * @param handler\n * @returns\n */\nexport function matchElementsTagRegex(\n options:\n | string\n | {\n text: string\n /**\n * Determine if only root level elements should be matched.\n *\n * `false` will match `\"{ELEMENT:Parent_Name}\"` and\n * `\"{ELEMENT:Children|Name}\"`.\n *\n * `true` will match `\"{ELEMENT:Parent_Name}\"` but will NOT replace\n * `{ELEMENT:Children|Name}`.\n */\n excludeNestedElements: boolean\n /** Use the submission value instead of labels for elements with optionsets */\n useSubmissionValue?: boolean\n },\n matchHandler: (options: {\n elementName: string\n elementMatch: string\n }) => void,\n) {\n const text = typeof options === 'string' ? options : options.text\n const excludeNestedElements =\n typeof options !== 'string' && !!options.excludeNestedElements\n const useSubmissionValue =\n typeof options !== 'string' && !!options.useSubmissionValue\n let matches\n const regexTemplate = useSubmissionValue\n ? excludeNestedElements\n ? RootSubmissionValueElementRegex\n : NestedSubmissionValueElementRegex\n : excludeNestedElements\n ? RootElementRegex\n : NestedElementRegex\n // Fresh RegExp per call so re-entrant matchElementsTagRegex (and concurrent\n // callers) do not share mutable lastIndex on the exported /g patterns.\n const regex = new RegExp(regexTemplate.source, regexTemplate.flags)\n while ((matches = regex.exec(text)) !== null) {\n if (matches?.length < 3) continue\n\n const elementName = matches[2]\n matchHandler({ elementName, elementMatch: matches[0] })\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"form-elements-regex.js","sourceRoot":"","sources":["../src/form-elements-regex.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAA;AACzD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,wBAAwB,CAAA;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,+BAA+B,CAAA;AAC9E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,8BAA8B,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAgBK,EACL,YAGU;IAEV,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAA;IACjE,MAAM,qBAAqB,GACzB,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAA;IAChE,MAAM,kBAAkB,GACtB,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAA;IAC7D,IAAI,OAAO,CAAA;IACX,MAAM,aAAa,GAAG,kBAAkB;QACtC,CAAC,CAAC,qBAAqB;YACrB,CAAC,CAAC,+BAA+B;YACjC,CAAC,CAAC,iCAAiC;QACrC,CAAC,CAAC,qBAAqB;YACrB,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,kBAAkB,CAAA;IACxB,4EAA4E;IAC5E,uEAAuE;IACvE,gHAAgH;IAChH,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAA;IACnE,OAAO,CAAC,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7C,IAAI,OAAO,EAAE,MAAM,GAAG,CAAC;YAAE,SAAQ;QAEjC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;QAC9B,YAAY,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IACzD,CAAC;AACH,CAAC","sourcesContent":["/**\n * The regex used for matching `{ELEMENT:<elementName>}` tags in the OneBlink\n * platform. Ignores nested elements based on the '|' character. Will match\n * `\"{ELEMENT:Parent_Name}\"` but will NOT match `\"{ELEMENT:Children|Name}\"`.\n */\nexport const RootElementRegex = /({ELEMENT:)([^|}]+)(})/g\n/**\n * The regex used for matching `{ELEMENT:<elementName>|<nestedElementName>}`\n * tags in the OneBlink platform. Includes nested elements based on the '|'\n * character. {ELEMENT:Children|Name} would be matched. Will match\n * `\"{ELEMENT:Parent_Name}\"` and `\"{ELEMENT:Children|Name}\"`.\n */\nexport const NestedElementRegex = /({ELEMENT:)([^}]+)(})/g\n\n/**\n * The regex used for matching `{ELEMENT_VALUE:<elementName>}` tags in the OneBlink\n * platform. Ignores nested elements based on the '|' character. Will match\n * `\"{ELEMENT_VALUE:Parent_Name}\"` but will NOT match `\"{ELEMENT_VALUE:Children|Name}\"`.\n */\nexport const RootSubmissionValueElementRegex = /({ELEMENT_VALUE:)([^|}]+)(})/g\n/**\n * The regex used for matching `{ELEMENT_VALUE:<elementName>|<nestedElementName>}`\n * tags in the OneBlink platform. Includes nested elements based on the '|'\n * character. {ELEMENT_VALUE:Children|Name} would be matched. Will match\n * `\"{ELEMENT_VALUE:Parent_Name}\"` and `\"{ELEMENT_VALUE:Children|Name}\"`.\n */\nexport const NestedSubmissionValueElementRegex = /({ELEMENT_VALUE:)([^}]+)(})/g\n\n/**\n * Takes a string and calls a provided handler function for each found instance\n * of `{ELEMENT:<elementName>}` in the string. Used to replace values in\n * OneBlink calculation and info (HTML) elements.\n *\n * #### Example\n *\n * ```js\n * formElementsService.matchElementsTagRegex(\n * myString,\n * ({ elementName, elementMatch }) => {\n * const v = submission[elementName]\n * myString = myString.replace(elementMatch, v)\n * },\n * )\n * ```\n *\n * Or\n *\n * ```js\n * formElementsService.matchElementsTagRegex(\n * {\n * text: myString,\n * excludeNestedElements: true,\n * },\n * ({ elementName, elementMatch }) => {\n * const v = submission[elementName]\n * myString = myString.replace(elementMatch, v)\n * },\n * )\n * ```\n *\n * @param options\n * @param handler\n * @returns\n */\nexport function matchElementsTagRegex(\n options:\n | string\n | {\n text: string\n /**\n * Determine if only root level elements should be matched.\n *\n * `false` will match `\"{ELEMENT:Parent_Name}\"` and\n * `\"{ELEMENT:Children|Name}\"`.\n *\n * `true` will match `\"{ELEMENT:Parent_Name}\"` but will NOT replace\n * `{ELEMENT:Children|Name}`.\n */\n excludeNestedElements: boolean\n /** Use the submission value instead of labels for elements with optionsets */\n useSubmissionValue?: boolean\n },\n matchHandler: (options: {\n elementName: string\n elementMatch: string\n }) => void,\n) {\n const text = typeof options === 'string' ? options : options.text\n const excludeNestedElements =\n typeof options !== 'string' && !!options.excludeNestedElements\n const useSubmissionValue =\n typeof options !== 'string' && !!options.useSubmissionValue\n let matches\n const regexTemplate = useSubmissionValue\n ? excludeNestedElements\n ? RootSubmissionValueElementRegex\n : NestedSubmissionValueElementRegex\n : excludeNestedElements\n ? RootElementRegex\n : NestedElementRegex\n // Fresh RegExp per call so re-entrant matchElementsTagRegex (and concurrent\n // callers) do not share mutable lastIndex on the exported /g patterns.\n // eslint-disable-next-line security/detect-non-literal-regexp -- clones one of the static literal regexes above\n const regex = new RegExp(regexTemplate.source, regexTemplate.flags)\n while ((matches = regex.exec(text)) !== null) {\n if (matches?.length < 3) continue\n\n const elementName = matches[2]\n matchHandler({ elementName, elementMatch: matches[0] })\n }\n}\n"]}
|
|
@@ -15,6 +15,7 @@ const paymentProvidersMap = {
|
|
|
15
15
|
* @returns
|
|
16
16
|
*/
|
|
17
17
|
export default function getPaymentProvider(type) {
|
|
18
|
+
// eslint-disable-next-line security/detect-object-injection -- type is a typed payment type union
|
|
18
19
|
return paymentProvidersMap[type];
|
|
19
20
|
}
|
|
20
21
|
//# sourceMappingURL=getPaymentProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getPaymentProvider.js","sourceRoot":"","sources":["../../src/paymentService/getPaymentProvider.ts"],"names":[],"mappings":"AAEA,OAAO,qBAAqB,MAAM,uBAAuB,CAAA;AACzD,OAAO,oBAAoB,MAAM,sBAAsB,CAAA;AACvD,OAAO,wBAAwB,MAAM,0BAA0B,CAAA;AAC/D,OAAO,iCAAiC,MAAM,mCAAmC,CAAA;AAEjF,MAAM,mBAAmB,GAGrB;IACF,MAAM,EAAE,qBAAqB;IAC7B,MAAM,EAAE,oBAAoB;IAC5B,WAAW,EAAE,wBAAwB;IACrC,oBAAoB,EAAE,iCAAiC;CACxD,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACxC,IAAmD;IAEnD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { SubmissionTypes } from '@oneblink/types'\r\nimport { PaymentProvider } from './types.js'\r\nimport bpointPaymentProvider from './providers/bpoint.js'\r\nimport cpPayPaymentProvider from './providers/cpPay.js'\r\nimport nswGovPayPaymentProvider from './providers/nswGovPay.js'\r\nimport westpacQuickStreamPaymentProvider from './providers/westpacQuickStream.js'\r\n\r\nconst paymentProvidersMap: Record<\r\n SubmissionTypes.FormSubmissionPayment['type'],\r\n PaymentProvider<SubmissionTypes.FormSubmissionPayment>\r\n> = {\r\n BPOINT: bpointPaymentProvider,\r\n CP_PAY: cpPayPaymentProvider,\r\n NSW_GOV_PAY: nswGovPayPaymentProvider,\r\n WESTPAC_QUICK_STREAM: westpacQuickStreamPaymentProvider,\r\n}\r\n\r\n/**\r\n * Resolve the pure payment provider implementation for a payment type.\r\n *\r\n * @param type\r\n * @returns\r\n */\r\nexport default function getPaymentProvider(\r\n type: SubmissionTypes.FormSubmissionPayment['type'],\r\n): PaymentProvider<SubmissionTypes.FormSubmissionPayment> {\r\n return paymentProvidersMap[type]\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"getPaymentProvider.js","sourceRoot":"","sources":["../../src/paymentService/getPaymentProvider.ts"],"names":[],"mappings":"AAEA,OAAO,qBAAqB,MAAM,uBAAuB,CAAA;AACzD,OAAO,oBAAoB,MAAM,sBAAsB,CAAA;AACvD,OAAO,wBAAwB,MAAM,0BAA0B,CAAA;AAC/D,OAAO,iCAAiC,MAAM,mCAAmC,CAAA;AAEjF,MAAM,mBAAmB,GAGrB;IACF,MAAM,EAAE,qBAAqB;IAC7B,MAAM,EAAE,oBAAoB;IAC5B,WAAW,EAAE,wBAAwB;IACrC,oBAAoB,EAAE,iCAAiC;CACxD,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACxC,IAAmD;IAEnD,kGAAkG;IAClG,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAA;AAClC,CAAC","sourcesContent":["import { SubmissionTypes } from '@oneblink/types'\r\nimport { PaymentProvider } from './types.js'\r\nimport bpointPaymentProvider from './providers/bpoint.js'\r\nimport cpPayPaymentProvider from './providers/cpPay.js'\r\nimport nswGovPayPaymentProvider from './providers/nswGovPay.js'\r\nimport westpacQuickStreamPaymentProvider from './providers/westpacQuickStream.js'\r\n\r\nconst paymentProvidersMap: Record<\r\n SubmissionTypes.FormSubmissionPayment['type'],\r\n PaymentProvider<SubmissionTypes.FormSubmissionPayment>\r\n> = {\r\n BPOINT: bpointPaymentProvider,\r\n CP_PAY: cpPayPaymentProvider,\r\n NSW_GOV_PAY: nswGovPayPaymentProvider,\r\n WESTPAC_QUICK_STREAM: westpacQuickStreamPaymentProvider,\r\n}\r\n\r\n/**\r\n * Resolve the pure payment provider implementation for a payment type.\r\n *\r\n * @param type\r\n * @returns\r\n */\r\nexport default function getPaymentProvider(\r\n type: SubmissionTypes.FormSubmissionPayment['type'],\r\n): PaymentProvider<SubmissionTypes.FormSubmissionPayment> {\r\n // eslint-disable-next-line security/detect-object-injection -- type is a typed payment type union\r\n return paymentProvidersMap[type]\r\n}\r\n"]}
|
|
@@ -105,6 +105,7 @@ export function getElementSubmissionValue({ elementId, propertyName, submission,
|
|
|
105
105
|
});
|
|
106
106
|
}
|
|
107
107
|
function getElementSubmissionValueByName({ propertyName, formElements, submission, }) {
|
|
108
|
+
// eslint-disable-next-line security/detect-object-injection -- key is a form element name
|
|
108
109
|
const unknownValue = submission[propertyName];
|
|
109
110
|
if (unknownValue === undefined || unknownValue === null) {
|
|
110
111
|
return undefined;
|
|
@@ -629,6 +630,7 @@ export function processInjectablesInCustomResource({ resource, submission, formE
|
|
|
629
630
|
matches.forEach((hasNestedFormElements, repeatableSetElementName) => {
|
|
630
631
|
if (hasNestedFormElements) {
|
|
631
632
|
// Attempt to create a new resource for each entry in the repeatable set.
|
|
633
|
+
// eslint-disable-next-line security/detect-object-injection -- key is a form element name
|
|
632
634
|
const entries = submission?.[repeatableSetElementName];
|
|
633
635
|
if (Array.isArray(entries)) {
|
|
634
636
|
const repeatableSetElement = findFormElement(formElements, (formElement) => {
|
|
@@ -676,6 +678,7 @@ export function processInjectablesInCustomResource({ resource, submission, formE
|
|
|
676
678
|
valueMatches.forEach((hasNestedFormElements, repeatableSetElementName) => {
|
|
677
679
|
if (hasNestedFormElements) {
|
|
678
680
|
// Attempt to create a new resource for each entry in the repeatable set.
|
|
681
|
+
// eslint-disable-next-line security/detect-object-injection -- key is a form element name
|
|
679
682
|
const entries = submission?.[repeatableSetElementName];
|
|
680
683
|
if (Array.isArray(entries)) {
|
|
681
684
|
const repeatableSetElement = findFormElement(formElements, (formElement) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replaceCustomValues.js","sourceRoot":"","sources":["../src/replaceCustomValues.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n APINSWTypes,\n CivicaTypes,\n FormTypes,\n GeoscapeTypes,\n GoogleTypes,\n MiscTypes,\n PointTypes,\n ScheduledTasksTypes,\n SubmissionTypes,\n} from '@oneblink/types'\nimport { getABNNumberFromABNRecord } from './abnService.js'\nimport {\n RootElementRegex,\n NestedElementRegex,\n matchElementsTagRegex,\n RootSubmissionValueElementRegex,\n NestedSubmissionValueElementRegex,\n} from './form-elements-regex.js'\nimport { findFormElement, flattenFormElements } from './formElementsService.js'\n\nexport type ReplaceInjectablesFormatters = {\n /** A function to format any date/times. */\n formatDateTime: (value: string) => string\n /** A function to format any dates. */\n formatDate: (value: string) => string\n /** A function to format any times. */\n formatTime: (value: string) => string\n /** A function to format any numbers. */\n formatNumber: (value: number) => string\n /** A function to format any numbers as currency. */\n formatCurrency: (value: number) => string\n}\n\nexport type ReplaceInjectablesBaseOptions = ReplaceInjectablesFormatters & {\n submission: SubmissionTypes.S3SubmissionData['submission']\n userProfile: MiscTypes.UserProfile | undefined\n task: ScheduledTasksTypes.Task | undefined\n taskGroup: ScheduledTasksTypes.TaskGroup | undefined\n taskGroupInstance: ScheduledTasksTypes.TaskGroupInstance | undefined\n /**\n * Determine if only root level elements should be replaced.\n *\n * `false` will replace `\"{ELEMENT:Parent_Name}\"` and\n * `\"{ELEMENT:Children|Name}\"`.\n *\n * `true` will replace `\"{ELEMENT:Parent_Name}\"` but will NOT replace\n * `{ELEMENT:Children|Name}`.\n */\n excludeNestedElements?: boolean\n}\n\nexport type ReplaceInjectablesOptions = ReplaceInjectablesBaseOptions & {\n form: FormTypes.Form\n submissionId: string\n submissionTimestamp: string\n externalId: string | undefined\n previousApprovalId: string | undefined\n}\n\nconst SUBMISSION_VALUES: Array<{\n string: string\n value: (options: ReplaceInjectablesOptions) => string | undefined\n}> = [\n {\n string: '{INFO_PAGE_ID}',\n value: ({ form }: ReplaceInjectablesOptions) => form.id.toString(),\n },\n {\n string: '{INFO_PAGE_NAME}',\n value: ({ form }: ReplaceInjectablesOptions) => form.name,\n },\n {\n string: '{FORM_ID}',\n value: ({ form }: ReplaceInjectablesOptions) => form.id.toString(),\n },\n {\n string: '{FORM_NAME}',\n value: ({ form }: ReplaceInjectablesOptions) => form.name,\n },\n {\n string: '{DATE}',\n value: ({\n submissionTimestamp,\n formatDateTime,\n }: ReplaceInjectablesOptions) => {\n if (!submissionTimestamp) {\n return undefined\n }\n return formatDateTime(submissionTimestamp)\n },\n },\n {\n string: '{TIMESTAMP}',\n value: ({ submissionTimestamp }: ReplaceInjectablesOptions) =>\n submissionTimestamp,\n },\n {\n string: '{SUBMISSION_ID}',\n value: ({ submissionId }: ReplaceInjectablesOptions) => submissionId,\n },\n {\n string: '{EXTERNAL_ID}',\n value: ({ externalId }: ReplaceInjectablesOptions) => externalId,\n },\n {\n string: '{PREVIOUS_APPROVAL_ID}',\n value: ({ previousApprovalId }: ReplaceInjectablesOptions) =>\n previousApprovalId,\n },\n]\n\nconst ELEMENT_VALUES: Array<{\n string: string\n value: (options: ReplaceInjectablesBaseOptions) => string | undefined\n}> = [\n {\n string: '{USER:email}',\n value: ({ userProfile }) => userProfile?.email,\n },\n {\n string: '{USER:username}',\n value: ({ userProfile }) => userProfile?.username,\n },\n {\n string: '{TASK_NAME}',\n value: ({ task }) => task?.name,\n },\n {\n string: '{TASK_GROUP_NAME}',\n value: ({ taskGroup }) => taskGroup?.name,\n },\n {\n string: '{TASK_GROUP_INSTANCE_LABEL}',\n value: ({ taskGroupInstance }) => taskGroupInstance?.label,\n },\n]\n\n/**\n * Function to get the display value of a property in submission, if elementId\n * is provided propertyName will be ignored\n *\n * #### Example\n *\n * ```typescript\n * const result = submissionService.getElementSubmissionValue({\n * propertyName: 'search',\n * submission: {\n * search: 'Entered By User',\n * },\n * formElements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * formatNumber: (value) => Number(value).toString(),\n * formatCurrency: (value) => Number(value).toFixed(2),\n * })\n * ```\n *\n * @param options\n * @returns\n */\nexport function getElementSubmissionValue({\n propertyName,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n propertyName: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions):\n | {\n element: FormTypes.FormElement | undefined\n value: unknown\n }\n | undefined\nexport function getElementSubmissionValue({\n elementId,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n elementId: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions):\n | {\n element: FormTypes.FormElement | undefined\n value: unknown\n }\n | undefined\nexport function getElementSubmissionValue({\n elementId,\n propertyName,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n elementId?: string\n propertyName?: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions) {\n if (elementId === undefined && propertyName === undefined) {\n return undefined\n }\n\n let result: ReturnType<typeof getElementSubmissionValueByName> | undefined =\n undefined\n if (elementId) {\n result = getElementSubmissionValueById({\n elementId,\n formElements,\n submission,\n })\n } else if (propertyName) {\n result = getElementSubmissionValueByName({\n propertyName,\n formElements,\n submission,\n })\n }\n if (!result) {\n return\n }\n return formatValue({\n element: result.formElement,\n unknownValue: result.unknownValue,\n useSubmissionValue,\n formatDate,\n formatTime,\n formatDateTime,\n formatCurrency,\n formatNumber,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n })\n}\n\nfunction getElementSubmissionValueByName({\n propertyName,\n formElements,\n submission,\n}: {\n propertyName: string\n formElements: FormTypes.FormElement[]\n submission: SubmissionTypes.S3SubmissionData['submission']\n}):\n | { formElement: FormTypes.FormElement | undefined; unknownValue: unknown }\n | undefined {\n const unknownValue = submission[propertyName]\n if (unknownValue === undefined || unknownValue === null) {\n return undefined\n }\n\n const formElement = findFormElement(\n formElements,\n (element) =>\n element.type !== 'page' &&\n element.type !== 'section' &&\n element.name === propertyName,\n )\n\n return { formElement, unknownValue }\n}\n\nfunction getElementSubmissionValueById({\n elementId,\n formElements,\n submission,\n}: {\n elementId: string\n formElements: FormTypes.FormElement[]\n submission: SubmissionTypes.S3SubmissionData['submission']\n}): { formElement: FormTypes.FormElement; unknownValue: unknown } | undefined {\n // make sure submission is an object\n if (Object(submission) !== submission) {\n return undefined\n }\n const flattenedElements = flattenFormElements(formElements)\n\n let unknown: unknown = undefined\n let formElement: FormTypes.FormElement | undefined = undefined\n for (const element of flattenedElements) {\n if (elementId === element.id) {\n if ('name' in element) {\n unknown = submission[element.name]\n formElement = element\n }\n break\n }\n if (element.type === 'form') {\n const newSubmissionData = submission[element.name]\n\n const result = getElementSubmissionValueById({\n elementId,\n formElements: element.elements ?? [],\n submission:\n newSubmissionData as SubmissionTypes.S3SubmissionData['submission'],\n })\n if (result) {\n unknown = result.unknownValue\n formElement = result.formElement\n break\n }\n }\n }\n if (unknown === undefined || unknown === null || formElement === undefined) {\n return undefined\n }\n\n return { unknownValue: unknown, formElement }\n}\n\nfunction formatValue({\n element,\n unknownValue,\n useSubmissionValue,\n formatDate,\n formatTime,\n formatDateTime,\n formatCurrency,\n formatNumber,\n ...baseOptions\n}: {\n element: FormTypes.FormElement | undefined\n unknownValue: unknown\n useSubmissionValue?: boolean\n} & Omit<ReplaceInjectablesBaseOptions, 'submission'>) {\n switch (element?.type) {\n case 'datetime': {\n const value = unknownValue as string\n return { element: element, value: formatDateTime(value) }\n }\n case 'date': {\n const value = unknownValue as string\n return { element: element, value: formatDate(value) }\n }\n case 'time': {\n const value = unknownValue as string\n return { element: element, value: formatTime(value) }\n }\n case 'radio':\n case 'autocomplete': {\n const value = unknownValue as string\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const option = element.options?.find((opt) => opt.value === value)\n return { element: element, value: option?.label || value }\n }\n\n case 'checkboxes': {\n const value = unknownValue as string[]\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const options = element.options\n const selectedOptionLabels: string[] = value.reduce(\n (labels: string[], selectedOption: string) => {\n const foundOption = options?.find((o) => o.value === selectedOption)\n if (foundOption) labels.push(foundOption.label)\n return labels\n },\n [],\n )\n return {\n element: element,\n value: selectedOptionLabels.length ? selectedOptionLabels : undefined,\n }\n }\n case 'compliance': {\n const value = unknownValue as {\n value?: string\n }\n if (useSubmissionValue) {\n return { element: element, value: value.value }\n }\n const option = (element.options || []).find(\n (option: FormTypes.ChoiceElementOption) => option.value === value.value,\n )\n return {\n element: element,\n value: {\n ...value,\n value: option?.label || value.value,\n },\n }\n }\n case 'select': {\n if (element.multi) {\n const value = unknownValue as string[]\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const options = element.options\n const selectedOptionLabels: string[] = value.reduce(\n (labels: string[], selectedOption: string) => {\n const foundOption = options?.find((o) => o.value === selectedOption)\n if (foundOption) labels.push(foundOption.label)\n return labels\n },\n [],\n )\n return {\n element: element,\n value: selectedOptionLabels.length ? selectedOptionLabels : undefined,\n }\n } else {\n const value = unknownValue as string\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const option = element.options?.find((opt) => opt.value === value)\n return { element: element, value: option?.label }\n }\n }\n case 'boolean': {\n const value = unknownValue as boolean\n return { element: element, value: value ? 'Yes' : 'No' }\n }\n case 'calculation':\n case 'number': {\n const getValue = (value: number) => {\n if (element.displayAsCurrency) {\n return formatCurrency(value)\n }\n if (element.type === 'calculation') {\n return formatNumber(value)\n }\n return value\n }\n\n const value = unknownValue as number\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n const formattedValue = getValue(value)\n return { element, value: formattedValue }\n }\n return undefined\n }\n case 'pointCadastralParcel': {\n const value = unknownValue as PointTypes.PointCadastralParcelResponse\n return {\n element: element,\n value: value?.parcelBundle?.[0]?.parcelId,\n }\n }\n case 'pointAddress':\n case 'geoscapeAddress': {\n const value = unknownValue as\n | PointTypes.PointAddress\n | GeoscapeTypes.GeoscapeAddress\n return {\n element: element,\n value: value?.addressDetails?.formattedAddress || value?.addressId,\n }\n }\n case 'pointAddressV3': {\n const value =\n unknownValue as PointTypes.PointAddressV3GetAddressDetailsResponse\n return {\n element: element,\n value:\n value.properties?.formattedAddress || value.properties?.addressId,\n }\n }\n case 'googleAddress': {\n const value = unknownValue as GoogleTypes.GoogleMapsAddress\n return {\n element: element,\n value: value.formatted_address || value.place_id,\n }\n }\n case 'civicaStreetName': {\n const value = unknownValue as CivicaTypes.CivicaStreetName\n return { element: element, value: value?.formattedStreet }\n }\n case 'civicaNameRecord': {\n const value = unknownValue as CivicaTypes.CivicaNameRecord\n return {\n element: element,\n value:\n [value?.title, value?.givenName1, value?.familyName]\n .filter((t) => t)\n .join(' ') || value?.emailAddress,\n }\n }\n case 'abn': {\n const value = unknownValue as MiscTypes.ABNRecord\n return {\n element: element,\n value: value ? getABNNumberFromABNRecord(value) : undefined,\n }\n }\n case 'apiNSWLiquorLicence': {\n const value = unknownValue as APINSWTypes.LiquorLicenceDetails | undefined\n return {\n element: element,\n value:\n `${value?.licenceDetail?.licenceNumber} | ${value?.licenceDetail?.licenceName}`.trim(),\n }\n }\n case 'freshdeskDependentField': {\n const value =\n unknownValue as FormTypes.FreshdeskDependentFieldElementValue\n return {\n element,\n value:\n [value.category, value.subCategory, value.item]\n .filter((text) => !!text)\n .join(' / ') || undefined,\n }\n }\n case 'repeatableSet': {\n const entrySummary = element.entrySummary\n if (!entrySummary) {\n return { element, value: unknownValue }\n }\n\n if (!Array.isArray(unknownValue)) {\n return { element, value: '' }\n }\n\n const value = unknownValue\n .map((entry: unknown) => {\n if (typeof entry !== 'object' || entry === null) {\n return ''\n }\n const { text } = replaceInjectablesWithElementValues(entrySummary, {\n ...baseOptions,\n submission: entry as SubmissionTypes.S3SubmissionData['submission'],\n formElements: element.elements,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n })\n return text\n })\n // Each item is HTML produced from entrySummary (not plain labels). Joining\n // with '' stacks adjacent markup; it is not concatenating words (e.g. \"DogCatFrog\").\n .join('')\n return {\n element: element,\n value,\n }\n }\n default: {\n return { element: element, value: unknownValue }\n }\n }\n}\n\n/**\n * Replace the `{ELEMENT:<elementName>}` values in text while a form is being\n * filled out. The replacements are suppose to be user friendly and for display\n * purposes, e.g. dates should be displayed in the user's desired format and\n * timezone.\n *\n * #### Example\n *\n * ```js\n * const { text: url } =\n * submissionService.replaceInjectablesWithElementValues(\n * 'https://example.com/path?search{ELEMENT:search}',\n * {\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * formatNumber: (value) => Number(value).toString(),\n * formatCurrency: (value) => Number(value).toFixed(2),\n * submission: {\n * search: 'Entered By User',\n * },\n * elements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * userProfile: {\n * userId: 'abc123',\n * username: 'john-user',\n * email: 'john.user@domain.com',\n * },\n * },\n * )\n * ```\n *\n * @param text\n * @param options\n * @returns\n */\nexport function replaceInjectablesWithElementValues(\n text: string,\n options: {\n formElements: FormTypes.FormElement[]\n } & ReplaceInjectablesBaseOptions,\n): {\n /**\n * The text passed in with all injectables replaced with their corresponding\n * values or empty strings.\n */\n text: string\n /**\n * Determine if replaceable strings could all be replaced. The `text` property\n * returned will replace all injectable with empty strings if the value cannot\n * be found. Use this boolean to either use the replaced string with missing\n * data or implement some other logic to handle missing injectables.\n */\n hadAllInjectablesReplaced: boolean\n} {\n let hadAllInjectablesReplaced = true\n text = ELEMENT_VALUES.reduce((newString, customValue) => {\n if (!newString.includes(customValue.string)) {\n return newString\n }\n const replacedValue = customValue.value(options)\n if (!replacedValue) {\n hadAllInjectablesReplaced = false\n }\n return newString.replaceAll(customValue.string, replacedValue || '')\n }, text)\n\n const matchesElement = text.match(\n options.excludeNestedElements ? RootElementRegex : NestedElementRegex,\n )\n if (matchesElement) {\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: !!options.excludeNestedElements,\n },\n ({ elementName, elementMatch }) => {\n const result = getElementSubmissionValue({\n propertyName: elementName,\n ...options,\n })\n\n const hasNoValue = result === undefined || result.value === undefined\n if (hasNoValue) {\n hadAllInjectablesReplaced = false\n }\n\n text = text.replace(\n elementMatch,\n hasNoValue ? '' : (result.value as string),\n )\n },\n )\n }\n\n const valueMatchesElement = text.match(\n options.excludeNestedElements\n ? RootSubmissionValueElementRegex\n : NestedSubmissionValueElementRegex,\n )\n if (valueMatchesElement) {\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: !!options.excludeNestedElements,\n useSubmissionValue: true,\n },\n ({ elementName, elementMatch }) => {\n const result = getElementSubmissionValue({\n propertyName: elementName,\n ...options,\n useSubmissionValue: true,\n })\n const hasNoValue = result === undefined || result.value === undefined\n if (hasNoValue) {\n hadAllInjectablesReplaced = false\n }\n\n text = text.replace(\n elementMatch,\n hasNoValue ? '' : (result.value as string),\n )\n },\n )\n }\n\n return {\n text,\n hadAllInjectablesReplaced,\n }\n}\n\n/**\n * Replace the `{ELEMENT:<elementName>}` values in text after a successful form\n * submission as well as other replaceable parameters e.g. `submissionId`. The\n * replacements are suppose to be user friendly and for display purposes, e.g.\n * dates should be displayed in the user's desired format and timezone.\n *\n * #### Example\n *\n * ```js\n * const result = submissionService.replaceInjectablesWithSubmissionValues(\n * 'https://example.com/path?submissionId={SUBMISSION_ID}&externalId={EXTERNAL_ID}&search{ELEMENT:search}',\n * {\n * submissionId: 'abc-123',\n * submissionTimestamp: '2021-07-02T02:19:13.670Z',\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * submission: {\n * search: 'Entered By User',\n * },\n * form: {\n * id: 1,\n * name: 'Form',\n * organisationId: '',\n * formsAppEnvironmentId: 1,\n * formsAppIds: [],\n * isAuthenticated: false,\n * isMultiPage: false,\n * isInfoPage: false,\n * postSubmissionAction: 'FORMS_LIBRARY',\n * cancelAction: 'FORMS_LIBRARY',\n * submissionEvents: [],\n * tags: [],\n * elements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * },\n * },\n * )\n * ```\n *\n * @param text\n * @param options\n * @returns\n */\nexport function replaceInjectablesWithSubmissionValues(\n text: string,\n options: ReplaceInjectablesOptions,\n): {\n /**\n * The text passed in with all injectables replaced with their corresponding\n * values or empty strings.\n */\n text: string\n /**\n * Determine if replaceable strings could all be replaced. The `text` property\n * returned will replace all injectable with empty strings if the value cannot\n * be found. Use this boolean to either use the replaced string with missing\n * data or implement some other logic to handle missing injectables.\n */\n hadAllInjectablesReplaced: boolean\n} {\n const result = replaceInjectablesWithElementValues(text, {\n formElements: options.form.elements,\n ...options,\n })\n let hadAllInjectablesReplaced = result.hadAllInjectablesReplaced\n const replacedText = SUBMISSION_VALUES.reduce((newString, customValue) => {\n if (!newString.includes(customValue.string)) {\n return newString\n }\n const replacedValue = customValue.value(options)\n if (!replacedValue) {\n hadAllInjectablesReplaced = false\n }\n return newString.replaceAll(customValue.string, replacedValue || '')\n }, result.text)\n\n return {\n text: replacedText,\n hadAllInjectablesReplaced,\n }\n}\n\n/**\n * Process a resource with injectable element values to turn a single resource\n * (could be a single) into multiple resources. e.g.\n * `\"{ELEMENT:Children|Child_Name} {ELEMENT:Family_Name}\"` with the following\n * submission data:\n *\n * ```json\n * {\n * \"Family_Name\": \"Smith\",\n * \"Children\": [\n * {\n * \"Child_Name\": \"John\"\n * },\n * {\n * \"Child_Name\": \"Jane\"\n * }\n * ]\n * }\n * ```\n *\n * Would result in the following resources:\n *\n * - `\"John Smith\"`\n * - `\"Jane Smith\"`\n *\n * #### Example\n *\n * ```js\n * const emailAddresses = processInjectablesInCustomResource({\n * resource: '{ELEMENT:People|Email_Address}',\n * submission: {\n * People: [\n * {\n * Email_Address: 'user@oneblink.io',\n * },\n * {\n * Email_Address: 'admin@oneblink.io',\n * },\n * ],\n * },\n * formElements: [\n * {\n * id: '18dcd3e0-6e2f-462e-803b-e24562d9fa6d',\n * type: 'repeatableSet',\n * name: 'People',\n * label: 'People',\n * elements: [\n * {\n * id: 'd0902113-3f77-4070-adbd-ca3ae95ce091',\n * type: 'email',\n * name: 'Email_Address',\n * label: 'Email_Address',\n * },\n * ],\n * },\n * ],\n * replaceRootInjectables: (resource, submission, formElements) => {\n * const { text } = replaceInjectablesWithElementValues(resource, {\n * submission,\n * formElements,\n * excludeNestedElements: true,\n * // other options\n * })\n * return text\n * },\n * })\n * // emailAddresses === [\"user@oneblink.io\", \"admin@oneblink.io\"]\n * ```\n *\n * @param options\n * @returns\n */\nexport function processInjectablesInCustomResource<T>({\n resource,\n submission,\n formElements,\n replaceRootInjectables,\n prepareNestedInjectables = (resource, prepare) =>\n prepare(String(resource)) as T,\n}: {\n /** The resource that contains properties that support injection or a string */\n resource: T\n /** The form submission data to process */\n submission: SubmissionTypes.S3SubmissionData['submission']\n /** The form elements to process */\n formElements: FormTypes.FormElement[]\n /**\n * A function to inject values, this allows custom formatters to be used.\n * Return `undefined` to prevent the injection from recursively continuing.\n *\n * @param resource The current resource that contains properties that support\n * injection or a string\n * @param submission The current form submission data to process (may be an\n * entry in a repeatable set)\n * @param formElements The current form elements to process (may be the\n * elements from a repeatable set)\n * @returns\n */\n replaceRootInjectables: (\n resource: T,\n submission: SubmissionTypes.S3SubmissionData['submission'],\n formElements: FormTypes.FormElement[],\n ) =>\n | [injectedText: string, resourceKey: string, newResource: T]\n | string\n | undefined\n /**\n * An optional function to replace nested injectables when creating multiple\n * resources from repeatable sets. Only required if the `resource` param is\n * not a `string`.\n *\n * @param resource The current resource that contains properties that support\n * injection or a string\n * @param prepare A function to prepare the resource string(s) for another\n * iteration\n * @returns\n */\n prepareNestedInjectables?: (\n resource: T,\n preparer: (resourceText: string) => string,\n ) => T\n}): Map<string, T> {\n const newResources: Map<string, T> = new Map<string, T>()\n\n const injectorResult = replaceRootInjectables(\n resource,\n submission,\n formElements,\n )\n if (!injectorResult) {\n return newResources\n }\n\n const [text, resourceKey, newResource] =\n typeof injectorResult === 'string'\n ? [injectorResult, injectorResult, injectorResult as T]\n : injectorResult\n\n // Find nested form elements\n const matches: Map<string, boolean> = new Map()\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: false,\n },\n ({ elementName }) => {\n const [repeatableSetElementName, ...elementNames] = elementName.split('|')\n matches.set(repeatableSetElementName, !!elementNames.length)\n },\n )\n\n if (matches.size) {\n matches.forEach((hasNestedFormElements, repeatableSetElementName) => {\n if (hasNestedFormElements) {\n // Attempt to create a new resource for each entry in the repeatable set.\n const entries = submission?.[repeatableSetElementName]\n if (Array.isArray(entries)) {\n const repeatableSetElement = findFormElement(\n formElements,\n (formElement) => {\n return (\n 'name' in formElement &&\n formElement.name === repeatableSetElementName\n )\n },\n )\n if (\n repeatableSetElement &&\n 'elements' in repeatableSetElement &&\n Array.isArray(repeatableSetElement.elements)\n ) {\n for (const entry of entries) {\n const replacedResource = prepareNestedInjectables(\n newResource,\n (resourceText) => {\n return resourceText.replaceAll(\n `{ELEMENT:${repeatableSetElementName}|`,\n '{ELEMENT:',\n )\n },\n )\n const nestedResources = processInjectablesInCustomResource<T>({\n resource: replacedResource,\n submission: entry,\n formElements: repeatableSetElement.elements,\n replaceRootInjectables,\n prepareNestedInjectables,\n })\n if (nestedResources.size) {\n nestedResources.forEach((nestedResource, nestedResourceKey) => {\n if (!newResources.has(nestedResourceKey)) {\n newResources.set(nestedResourceKey, nestedResource)\n }\n })\n }\n }\n }\n }\n }\n })\n }\n\n // Find nested form elements using ELEMENT_VALUE:\n const valueMatches: Map<string, boolean> = new Map()\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: false,\n useSubmissionValue: true,\n },\n ({ elementName }) => {\n const [repeatableSetElementName, ...elementNames] = elementName.split('|')\n valueMatches.set(repeatableSetElementName, !!elementNames.length)\n },\n )\n\n if (valueMatches.size) {\n valueMatches.forEach((hasNestedFormElements, repeatableSetElementName) => {\n if (hasNestedFormElements) {\n // Attempt to create a new resource for each entry in the repeatable set.\n const entries = submission?.[repeatableSetElementName]\n if (Array.isArray(entries)) {\n const repeatableSetElement = findFormElement(\n formElements,\n (formElement) => {\n return (\n 'name' in formElement &&\n formElement.name === repeatableSetElementName\n )\n },\n )\n if (\n repeatableSetElement &&\n 'elements' in repeatableSetElement &&\n Array.isArray(repeatableSetElement.elements)\n ) {\n for (const entry of entries) {\n const replacedResource = prepareNestedInjectables(\n newResource,\n (resourceText) => {\n return resourceText.replaceAll(\n `{ELEMENT_VALUE:${repeatableSetElementName}|`,\n '{ELEMENT_VALUE:',\n )\n },\n )\n const nestedResources = processInjectablesInCustomResource<T>({\n resource: replacedResource,\n submission: entry,\n formElements: repeatableSetElement.elements,\n replaceRootInjectables,\n prepareNestedInjectables,\n })\n if (nestedResources.size) {\n nestedResources.forEach((nestedResource, nestedResourceKey) => {\n if (!newResources.has(nestedResourceKey)) {\n newResources.set(nestedResourceKey, nestedResource)\n }\n })\n }\n }\n }\n }\n }\n })\n }\n\n if (!matches.size && !valueMatches.size) {\n newResources.set(resourceKey, newResource)\n }\n\n return newResources\n}\n"]}
|
|
1
|
+
{"version":3,"file":"replaceCustomValues.js","sourceRoot":"","sources":["../src/replaceCustomValues.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n APINSWTypes,\n CivicaTypes,\n FormTypes,\n GeoscapeTypes,\n GoogleTypes,\n MiscTypes,\n PointTypes,\n ScheduledTasksTypes,\n SubmissionTypes,\n} from '@oneblink/types'\nimport { getABNNumberFromABNRecord } from './abnService.js'\nimport {\n RootElementRegex,\n NestedElementRegex,\n matchElementsTagRegex,\n RootSubmissionValueElementRegex,\n NestedSubmissionValueElementRegex,\n} from './form-elements-regex.js'\nimport { findFormElement, flattenFormElements } from './formElementsService.js'\n\nexport type ReplaceInjectablesFormatters = {\n /** A function to format any date/times. */\n formatDateTime: (value: string) => string\n /** A function to format any dates. */\n formatDate: (value: string) => string\n /** A function to format any times. */\n formatTime: (value: string) => string\n /** A function to format any numbers. */\n formatNumber: (value: number) => string\n /** A function to format any numbers as currency. */\n formatCurrency: (value: number) => string\n}\n\nexport type ReplaceInjectablesBaseOptions = ReplaceInjectablesFormatters & {\n submission: SubmissionTypes.S3SubmissionData['submission']\n userProfile: MiscTypes.UserProfile | undefined\n task: ScheduledTasksTypes.Task | undefined\n taskGroup: ScheduledTasksTypes.TaskGroup | undefined\n taskGroupInstance: ScheduledTasksTypes.TaskGroupInstance | undefined\n /**\n * Determine if only root level elements should be replaced.\n *\n * `false` will replace `\"{ELEMENT:Parent_Name}\"` and\n * `\"{ELEMENT:Children|Name}\"`.\n *\n * `true` will replace `\"{ELEMENT:Parent_Name}\"` but will NOT replace\n * `{ELEMENT:Children|Name}`.\n */\n excludeNestedElements?: boolean\n}\n\nexport type ReplaceInjectablesOptions = ReplaceInjectablesBaseOptions & {\n form: FormTypes.Form\n submissionId: string\n submissionTimestamp: string\n externalId: string | undefined\n previousApprovalId: string | undefined\n}\n\nconst SUBMISSION_VALUES: Array<{\n string: string\n value: (options: ReplaceInjectablesOptions) => string | undefined\n}> = [\n {\n string: '{INFO_PAGE_ID}',\n value: ({ form }: ReplaceInjectablesOptions) => form.id.toString(),\n },\n {\n string: '{INFO_PAGE_NAME}',\n value: ({ form }: ReplaceInjectablesOptions) => form.name,\n },\n {\n string: '{FORM_ID}',\n value: ({ form }: ReplaceInjectablesOptions) => form.id.toString(),\n },\n {\n string: '{FORM_NAME}',\n value: ({ form }: ReplaceInjectablesOptions) => form.name,\n },\n {\n string: '{DATE}',\n value: ({\n submissionTimestamp,\n formatDateTime,\n }: ReplaceInjectablesOptions) => {\n if (!submissionTimestamp) {\n return undefined\n }\n return formatDateTime(submissionTimestamp)\n },\n },\n {\n string: '{TIMESTAMP}',\n value: ({ submissionTimestamp }: ReplaceInjectablesOptions) =>\n submissionTimestamp,\n },\n {\n string: '{SUBMISSION_ID}',\n value: ({ submissionId }: ReplaceInjectablesOptions) => submissionId,\n },\n {\n string: '{EXTERNAL_ID}',\n value: ({ externalId }: ReplaceInjectablesOptions) => externalId,\n },\n {\n string: '{PREVIOUS_APPROVAL_ID}',\n value: ({ previousApprovalId }: ReplaceInjectablesOptions) =>\n previousApprovalId,\n },\n]\n\nconst ELEMENT_VALUES: Array<{\n string: string\n value: (options: ReplaceInjectablesBaseOptions) => string | undefined\n}> = [\n {\n string: '{USER:email}',\n value: ({ userProfile }) => userProfile?.email,\n },\n {\n string: '{USER:username}',\n value: ({ userProfile }) => userProfile?.username,\n },\n {\n string: '{TASK_NAME}',\n value: ({ task }) => task?.name,\n },\n {\n string: '{TASK_GROUP_NAME}',\n value: ({ taskGroup }) => taskGroup?.name,\n },\n {\n string: '{TASK_GROUP_INSTANCE_LABEL}',\n value: ({ taskGroupInstance }) => taskGroupInstance?.label,\n },\n]\n\n/**\n * Function to get the display value of a property in submission, if elementId\n * is provided propertyName will be ignored\n *\n * #### Example\n *\n * ```typescript\n * const result = submissionService.getElementSubmissionValue({\n * propertyName: 'search',\n * submission: {\n * search: 'Entered By User',\n * },\n * formElements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * formatNumber: (value) => Number(value).toString(),\n * formatCurrency: (value) => Number(value).toFixed(2),\n * })\n * ```\n *\n * @param options\n * @returns\n */\nexport function getElementSubmissionValue({\n propertyName,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n propertyName: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions):\n | {\n element: FormTypes.FormElement | undefined\n value: unknown\n }\n | undefined\nexport function getElementSubmissionValue({\n elementId,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n elementId: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions):\n | {\n element: FormTypes.FormElement | undefined\n value: unknown\n }\n | undefined\nexport function getElementSubmissionValue({\n elementId,\n propertyName,\n submission,\n formElements,\n useSubmissionValue,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n}: {\n elementId?: string\n propertyName?: string\n formElements: FormTypes.FormElement[]\n useSubmissionValue?: boolean\n} & ReplaceInjectablesBaseOptions) {\n if (elementId === undefined && propertyName === undefined) {\n return undefined\n }\n\n let result: ReturnType<typeof getElementSubmissionValueByName> | undefined =\n undefined\n if (elementId) {\n result = getElementSubmissionValueById({\n elementId,\n formElements,\n submission,\n })\n } else if (propertyName) {\n result = getElementSubmissionValueByName({\n propertyName,\n formElements,\n submission,\n })\n }\n if (!result) {\n return\n }\n return formatValue({\n element: result.formElement,\n unknownValue: result.unknownValue,\n useSubmissionValue,\n formatDate,\n formatTime,\n formatDateTime,\n formatCurrency,\n formatNumber,\n userProfile,\n task,\n taskGroup,\n taskGroupInstance,\n excludeNestedElements,\n })\n}\n\nfunction getElementSubmissionValueByName({\n propertyName,\n formElements,\n submission,\n}: {\n propertyName: string\n formElements: FormTypes.FormElement[]\n submission: SubmissionTypes.S3SubmissionData['submission']\n}):\n | { formElement: FormTypes.FormElement | undefined; unknownValue: unknown }\n | undefined {\n // eslint-disable-next-line security/detect-object-injection -- key is a form element name\n const unknownValue = submission[propertyName]\n if (unknownValue === undefined || unknownValue === null) {\n return undefined\n }\n\n const formElement = findFormElement(\n formElements,\n (element) =>\n element.type !== 'page' &&\n element.type !== 'section' &&\n element.name === propertyName,\n )\n\n return { formElement, unknownValue }\n}\n\nfunction getElementSubmissionValueById({\n elementId,\n formElements,\n submission,\n}: {\n elementId: string\n formElements: FormTypes.FormElement[]\n submission: SubmissionTypes.S3SubmissionData['submission']\n}): { formElement: FormTypes.FormElement; unknownValue: unknown } | undefined {\n // make sure submission is an object\n if (Object(submission) !== submission) {\n return undefined\n }\n const flattenedElements = flattenFormElements(formElements)\n\n let unknown: unknown = undefined\n let formElement: FormTypes.FormElement | undefined = undefined\n for (const element of flattenedElements) {\n if (elementId === element.id) {\n if ('name' in element) {\n unknown = submission[element.name]\n formElement = element\n }\n break\n }\n if (element.type === 'form') {\n const newSubmissionData = submission[element.name]\n\n const result = getElementSubmissionValueById({\n elementId,\n formElements: element.elements ?? [],\n submission:\n newSubmissionData as SubmissionTypes.S3SubmissionData['submission'],\n })\n if (result) {\n unknown = result.unknownValue\n formElement = result.formElement\n break\n }\n }\n }\n if (unknown === undefined || unknown === null || formElement === undefined) {\n return undefined\n }\n\n return { unknownValue: unknown, formElement }\n}\n\nfunction formatValue({\n element,\n unknownValue,\n useSubmissionValue,\n formatDate,\n formatTime,\n formatDateTime,\n formatCurrency,\n formatNumber,\n ...baseOptions\n}: {\n element: FormTypes.FormElement | undefined\n unknownValue: unknown\n useSubmissionValue?: boolean\n} & Omit<ReplaceInjectablesBaseOptions, 'submission'>) {\n switch (element?.type) {\n case 'datetime': {\n const value = unknownValue as string\n return { element: element, value: formatDateTime(value) }\n }\n case 'date': {\n const value = unknownValue as string\n return { element: element, value: formatDate(value) }\n }\n case 'time': {\n const value = unknownValue as string\n return { element: element, value: formatTime(value) }\n }\n case 'radio':\n case 'autocomplete': {\n const value = unknownValue as string\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const option = element.options?.find((opt) => opt.value === value)\n return { element: element, value: option?.label || value }\n }\n\n case 'checkboxes': {\n const value = unknownValue as string[]\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const options = element.options\n const selectedOptionLabels: string[] = value.reduce(\n (labels: string[], selectedOption: string) => {\n const foundOption = options?.find((o) => o.value === selectedOption)\n if (foundOption) labels.push(foundOption.label)\n return labels\n },\n [],\n )\n return {\n element: element,\n value: selectedOptionLabels.length ? selectedOptionLabels : undefined,\n }\n }\n case 'compliance': {\n const value = unknownValue as {\n value?: string\n }\n if (useSubmissionValue) {\n return { element: element, value: value.value }\n }\n const option = (element.options || []).find(\n (option: FormTypes.ChoiceElementOption) => option.value === value.value,\n )\n return {\n element: element,\n value: {\n ...value,\n value: option?.label || value.value,\n },\n }\n }\n case 'select': {\n if (element.multi) {\n const value = unknownValue as string[]\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const options = element.options\n const selectedOptionLabels: string[] = value.reduce(\n (labels: string[], selectedOption: string) => {\n const foundOption = options?.find((o) => o.value === selectedOption)\n if (foundOption) labels.push(foundOption.label)\n return labels\n },\n [],\n )\n return {\n element: element,\n value: selectedOptionLabels.length ? selectedOptionLabels : undefined,\n }\n } else {\n const value = unknownValue as string\n if (useSubmissionValue) {\n return { element: element, value: value }\n }\n const option = element.options?.find((opt) => opt.value === value)\n return { element: element, value: option?.label }\n }\n }\n case 'boolean': {\n const value = unknownValue as boolean\n return { element: element, value: value ? 'Yes' : 'No' }\n }\n case 'calculation':\n case 'number': {\n const getValue = (value: number) => {\n if (element.displayAsCurrency) {\n return formatCurrency(value)\n }\n if (element.type === 'calculation') {\n return formatNumber(value)\n }\n return value\n }\n\n const value = unknownValue as number\n if (!Number.isNaN(value) && Number.isFinite(value)) {\n const formattedValue = getValue(value)\n return { element, value: formattedValue }\n }\n return undefined\n }\n case 'pointCadastralParcel': {\n const value = unknownValue as PointTypes.PointCadastralParcelResponse\n return {\n element: element,\n value: value?.parcelBundle?.[0]?.parcelId,\n }\n }\n case 'pointAddress':\n case 'geoscapeAddress': {\n const value = unknownValue as\n | PointTypes.PointAddress\n | GeoscapeTypes.GeoscapeAddress\n return {\n element: element,\n value: value?.addressDetails?.formattedAddress || value?.addressId,\n }\n }\n case 'pointAddressV3': {\n const value =\n unknownValue as PointTypes.PointAddressV3GetAddressDetailsResponse\n return {\n element: element,\n value:\n value.properties?.formattedAddress || value.properties?.addressId,\n }\n }\n case 'googleAddress': {\n const value = unknownValue as GoogleTypes.GoogleMapsAddress\n return {\n element: element,\n value: value.formatted_address || value.place_id,\n }\n }\n case 'civicaStreetName': {\n const value = unknownValue as CivicaTypes.CivicaStreetName\n return { element: element, value: value?.formattedStreet }\n }\n case 'civicaNameRecord': {\n const value = unknownValue as CivicaTypes.CivicaNameRecord\n return {\n element: element,\n value:\n [value?.title, value?.givenName1, value?.familyName]\n .filter((t) => t)\n .join(' ') || value?.emailAddress,\n }\n }\n case 'abn': {\n const value = unknownValue as MiscTypes.ABNRecord\n return {\n element: element,\n value: value ? getABNNumberFromABNRecord(value) : undefined,\n }\n }\n case 'apiNSWLiquorLicence': {\n const value = unknownValue as APINSWTypes.LiquorLicenceDetails | undefined\n return {\n element: element,\n value:\n `${value?.licenceDetail?.licenceNumber} | ${value?.licenceDetail?.licenceName}`.trim(),\n }\n }\n case 'freshdeskDependentField': {\n const value =\n unknownValue as FormTypes.FreshdeskDependentFieldElementValue\n return {\n element,\n value:\n [value.category, value.subCategory, value.item]\n .filter((text) => !!text)\n .join(' / ') || undefined,\n }\n }\n case 'repeatableSet': {\n const entrySummary = element.entrySummary\n if (!entrySummary) {\n return { element, value: unknownValue }\n }\n\n if (!Array.isArray(unknownValue)) {\n return { element, value: '' }\n }\n\n const value = unknownValue\n .map((entry: unknown) => {\n if (typeof entry !== 'object' || entry === null) {\n return ''\n }\n const { text } = replaceInjectablesWithElementValues(entrySummary, {\n ...baseOptions,\n submission: entry as SubmissionTypes.S3SubmissionData['submission'],\n formElements: element.elements,\n formatDate,\n formatDateTime,\n formatTime,\n formatNumber,\n formatCurrency,\n })\n return text\n })\n // Each item is HTML produced from entrySummary (not plain labels). Joining\n // with '' stacks adjacent markup; it is not concatenating words (e.g. \"DogCatFrog\").\n .join('')\n return {\n element: element,\n value,\n }\n }\n default: {\n return { element: element, value: unknownValue }\n }\n }\n}\n\n/**\n * Replace the `{ELEMENT:<elementName>}` values in text while a form is being\n * filled out. The replacements are suppose to be user friendly and for display\n * purposes, e.g. dates should be displayed in the user's desired format and\n * timezone.\n *\n * #### Example\n *\n * ```js\n * const { text: url } =\n * submissionService.replaceInjectablesWithElementValues(\n * 'https://example.com/path?search{ELEMENT:search}',\n * {\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * formatNumber: (value) => Number(value).toString(),\n * formatCurrency: (value) => Number(value).toFixed(2),\n * submission: {\n * search: 'Entered By User',\n * },\n * elements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * userProfile: {\n * userId: 'abc123',\n * username: 'john-user',\n * email: 'john.user@domain.com',\n * },\n * },\n * )\n * ```\n *\n * @param text\n * @param options\n * @returns\n */\nexport function replaceInjectablesWithElementValues(\n text: string,\n options: {\n formElements: FormTypes.FormElement[]\n } & ReplaceInjectablesBaseOptions,\n): {\n /**\n * The text passed in with all injectables replaced with their corresponding\n * values or empty strings.\n */\n text: string\n /**\n * Determine if replaceable strings could all be replaced. The `text` property\n * returned will replace all injectable with empty strings if the value cannot\n * be found. Use this boolean to either use the replaced string with missing\n * data or implement some other logic to handle missing injectables.\n */\n hadAllInjectablesReplaced: boolean\n} {\n let hadAllInjectablesReplaced = true\n text = ELEMENT_VALUES.reduce((newString, customValue) => {\n if (!newString.includes(customValue.string)) {\n return newString\n }\n const replacedValue = customValue.value(options)\n if (!replacedValue) {\n hadAllInjectablesReplaced = false\n }\n return newString.replaceAll(customValue.string, replacedValue || '')\n }, text)\n\n const matchesElement = text.match(\n options.excludeNestedElements ? RootElementRegex : NestedElementRegex,\n )\n if (matchesElement) {\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: !!options.excludeNestedElements,\n },\n ({ elementName, elementMatch }) => {\n const result = getElementSubmissionValue({\n propertyName: elementName,\n ...options,\n })\n\n const hasNoValue = result === undefined || result.value === undefined\n if (hasNoValue) {\n hadAllInjectablesReplaced = false\n }\n\n text = text.replace(\n elementMatch,\n hasNoValue ? '' : (result.value as string),\n )\n },\n )\n }\n\n const valueMatchesElement = text.match(\n options.excludeNestedElements\n ? RootSubmissionValueElementRegex\n : NestedSubmissionValueElementRegex,\n )\n if (valueMatchesElement) {\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: !!options.excludeNestedElements,\n useSubmissionValue: true,\n },\n ({ elementName, elementMatch }) => {\n const result = getElementSubmissionValue({\n propertyName: elementName,\n ...options,\n useSubmissionValue: true,\n })\n const hasNoValue = result === undefined || result.value === undefined\n if (hasNoValue) {\n hadAllInjectablesReplaced = false\n }\n\n text = text.replace(\n elementMatch,\n hasNoValue ? '' : (result.value as string),\n )\n },\n )\n }\n\n return {\n text,\n hadAllInjectablesReplaced,\n }\n}\n\n/**\n * Replace the `{ELEMENT:<elementName>}` values in text after a successful form\n * submission as well as other replaceable parameters e.g. `submissionId`. The\n * replacements are suppose to be user friendly and for display purposes, e.g.\n * dates should be displayed in the user's desired format and timezone.\n *\n * #### Example\n *\n * ```js\n * const result = submissionService.replaceInjectablesWithSubmissionValues(\n * 'https://example.com/path?submissionId={SUBMISSION_ID}&externalId={EXTERNAL_ID}&search{ELEMENT:search}',\n * {\n * submissionId: 'abc-123',\n * submissionTimestamp: '2021-07-02T02:19:13.670Z',\n * formatDate: (value) => new Date(value).toDateString(),\n * formatDateTime: (value) => new Date(value).toString(),\n * formatTime: (value) => new Date(value).toTimeString(),\n * submission: {\n * search: 'Entered By User',\n * },\n * form: {\n * id: 1,\n * name: 'Form',\n * organisationId: '',\n * formsAppEnvironmentId: 1,\n * formsAppIds: [],\n * isAuthenticated: false,\n * isMultiPage: false,\n * isInfoPage: false,\n * postSubmissionAction: 'FORMS_LIBRARY',\n * cancelAction: 'FORMS_LIBRARY',\n * submissionEvents: [],\n * tags: [],\n * elements: [\n * {\n * id: 'd4135b47-9004-4d75-aeb3-d2f6232da111',\n * name: 'search',\n * type: 'text',\n * label: 'Search',\n * readOnly: false,\n * required: false,\n * conditionallyShow: false,\n * requiresAllConditionallyShowPredicates: false,\n * isElementLookup: false,\n * isDataLookup: false,\n * },\n * ],\n * },\n * },\n * )\n * ```\n *\n * @param text\n * @param options\n * @returns\n */\nexport function replaceInjectablesWithSubmissionValues(\n text: string,\n options: ReplaceInjectablesOptions,\n): {\n /**\n * The text passed in with all injectables replaced with their corresponding\n * values or empty strings.\n */\n text: string\n /**\n * Determine if replaceable strings could all be replaced. The `text` property\n * returned will replace all injectable with empty strings if the value cannot\n * be found. Use this boolean to either use the replaced string with missing\n * data or implement some other logic to handle missing injectables.\n */\n hadAllInjectablesReplaced: boolean\n} {\n const result = replaceInjectablesWithElementValues(text, {\n formElements: options.form.elements,\n ...options,\n })\n let hadAllInjectablesReplaced = result.hadAllInjectablesReplaced\n const replacedText = SUBMISSION_VALUES.reduce((newString, customValue) => {\n if (!newString.includes(customValue.string)) {\n return newString\n }\n const replacedValue = customValue.value(options)\n if (!replacedValue) {\n hadAllInjectablesReplaced = false\n }\n return newString.replaceAll(customValue.string, replacedValue || '')\n }, result.text)\n\n return {\n text: replacedText,\n hadAllInjectablesReplaced,\n }\n}\n\n/**\n * Process a resource with injectable element values to turn a single resource\n * (could be a single) into multiple resources. e.g.\n * `\"{ELEMENT:Children|Child_Name} {ELEMENT:Family_Name}\"` with the following\n * submission data:\n *\n * ```json\n * {\n * \"Family_Name\": \"Smith\",\n * \"Children\": [\n * {\n * \"Child_Name\": \"John\"\n * },\n * {\n * \"Child_Name\": \"Jane\"\n * }\n * ]\n * }\n * ```\n *\n * Would result in the following resources:\n *\n * - `\"John Smith\"`\n * - `\"Jane Smith\"`\n *\n * #### Example\n *\n * ```js\n * const emailAddresses = processInjectablesInCustomResource({\n * resource: '{ELEMENT:People|Email_Address}',\n * submission: {\n * People: [\n * {\n * Email_Address: 'user@oneblink.io',\n * },\n * {\n * Email_Address: 'admin@oneblink.io',\n * },\n * ],\n * },\n * formElements: [\n * {\n * id: '18dcd3e0-6e2f-462e-803b-e24562d9fa6d',\n * type: 'repeatableSet',\n * name: 'People',\n * label: 'People',\n * elements: [\n * {\n * id: 'd0902113-3f77-4070-adbd-ca3ae95ce091',\n * type: 'email',\n * name: 'Email_Address',\n * label: 'Email_Address',\n * },\n * ],\n * },\n * ],\n * replaceRootInjectables: (resource, submission, formElements) => {\n * const { text } = replaceInjectablesWithElementValues(resource, {\n * submission,\n * formElements,\n * excludeNestedElements: true,\n * // other options\n * })\n * return text\n * },\n * })\n * // emailAddresses === [\"user@oneblink.io\", \"admin@oneblink.io\"]\n * ```\n *\n * @param options\n * @returns\n */\nexport function processInjectablesInCustomResource<T>({\n resource,\n submission,\n formElements,\n replaceRootInjectables,\n prepareNestedInjectables = (resource, prepare) =>\n prepare(String(resource)) as T,\n}: {\n /** The resource that contains properties that support injection or a string */\n resource: T\n /** The form submission data to process */\n submission: SubmissionTypes.S3SubmissionData['submission']\n /** The form elements to process */\n formElements: FormTypes.FormElement[]\n /**\n * A function to inject values, this allows custom formatters to be used.\n * Return `undefined` to prevent the injection from recursively continuing.\n *\n * @param resource The current resource that contains properties that support\n * injection or a string\n * @param submission The current form submission data to process (may be an\n * entry in a repeatable set)\n * @param formElements The current form elements to process (may be the\n * elements from a repeatable set)\n * @returns\n */\n replaceRootInjectables: (\n resource: T,\n submission: SubmissionTypes.S3SubmissionData['submission'],\n formElements: FormTypes.FormElement[],\n ) =>\n | [injectedText: string, resourceKey: string, newResource: T]\n | string\n | undefined\n /**\n * An optional function to replace nested injectables when creating multiple\n * resources from repeatable sets. Only required if the `resource` param is\n * not a `string`.\n *\n * @param resource The current resource that contains properties that support\n * injection or a string\n * @param prepare A function to prepare the resource string(s) for another\n * iteration\n * @returns\n */\n prepareNestedInjectables?: (\n resource: T,\n preparer: (resourceText: string) => string,\n ) => T\n}): Map<string, T> {\n const newResources: Map<string, T> = new Map<string, T>()\n\n const injectorResult = replaceRootInjectables(\n resource,\n submission,\n formElements,\n )\n if (!injectorResult) {\n return newResources\n }\n\n const [text, resourceKey, newResource] =\n typeof injectorResult === 'string'\n ? [injectorResult, injectorResult, injectorResult as T]\n : injectorResult\n\n // Find nested form elements\n const matches: Map<string, boolean> = new Map()\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: false,\n },\n ({ elementName }) => {\n const [repeatableSetElementName, ...elementNames] = elementName.split('|')\n matches.set(repeatableSetElementName, !!elementNames.length)\n },\n )\n\n if (matches.size) {\n matches.forEach((hasNestedFormElements, repeatableSetElementName) => {\n if (hasNestedFormElements) {\n // Attempt to create a new resource for each entry in the repeatable set.\n // eslint-disable-next-line security/detect-object-injection -- key is a form element name\n const entries = submission?.[repeatableSetElementName]\n if (Array.isArray(entries)) {\n const repeatableSetElement = findFormElement(\n formElements,\n (formElement) => {\n return (\n 'name' in formElement &&\n formElement.name === repeatableSetElementName\n )\n },\n )\n if (\n repeatableSetElement &&\n 'elements' in repeatableSetElement &&\n Array.isArray(repeatableSetElement.elements)\n ) {\n for (const entry of entries) {\n const replacedResource = prepareNestedInjectables(\n newResource,\n (resourceText) => {\n return resourceText.replaceAll(\n `{ELEMENT:${repeatableSetElementName}|`,\n '{ELEMENT:',\n )\n },\n )\n const nestedResources = processInjectablesInCustomResource<T>({\n resource: replacedResource,\n submission: entry,\n formElements: repeatableSetElement.elements,\n replaceRootInjectables,\n prepareNestedInjectables,\n })\n if (nestedResources.size) {\n nestedResources.forEach((nestedResource, nestedResourceKey) => {\n if (!newResources.has(nestedResourceKey)) {\n newResources.set(nestedResourceKey, nestedResource)\n }\n })\n }\n }\n }\n }\n }\n })\n }\n\n // Find nested form elements using ELEMENT_VALUE:\n const valueMatches: Map<string, boolean> = new Map()\n matchElementsTagRegex(\n {\n text,\n excludeNestedElements: false,\n useSubmissionValue: true,\n },\n ({ elementName }) => {\n const [repeatableSetElementName, ...elementNames] = elementName.split('|')\n valueMatches.set(repeatableSetElementName, !!elementNames.length)\n },\n )\n\n if (valueMatches.size) {\n valueMatches.forEach((hasNestedFormElements, repeatableSetElementName) => {\n if (hasNestedFormElements) {\n // Attempt to create a new resource for each entry in the repeatable set.\n // eslint-disable-next-line security/detect-object-injection -- key is a form element name\n const entries = submission?.[repeatableSetElementName]\n if (Array.isArray(entries)) {\n const repeatableSetElement = findFormElement(\n formElements,\n (formElement) => {\n return (\n 'name' in formElement &&\n formElement.name === repeatableSetElementName\n )\n },\n )\n if (\n repeatableSetElement &&\n 'elements' in repeatableSetElement &&\n Array.isArray(repeatableSetElement.elements)\n ) {\n for (const entry of entries) {\n const replacedResource = prepareNestedInjectables(\n newResource,\n (resourceText) => {\n return resourceText.replaceAll(\n `{ELEMENT_VALUE:${repeatableSetElementName}|`,\n '{ELEMENT_VALUE:',\n )\n },\n )\n const nestedResources = processInjectablesInCustomResource<T>({\n resource: replacedResource,\n submission: entry,\n formElements: repeatableSetElement.elements,\n replaceRootInjectables,\n prepareNestedInjectables,\n })\n if (nestedResources.size) {\n nestedResources.forEach((nestedResource, nestedResourceKey) => {\n if (!newResources.has(nestedResourceKey)) {\n newResources.set(nestedResourceKey, nestedResource)\n }\n })\n }\n }\n }\n }\n }\n })\n }\n\n if (!matches.size && !valueMatches.size) {\n newResources.set(resourceKey, newResource)\n }\n\n return newResources\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneblink/sdk-core",
|
|
3
3
|
"description": "OneBlink SDK for JavaScript (works in Browsers and NodeJS)",
|
|
4
|
-
"version": "10.2.
|
|
4
|
+
"version": "10.2.1-beta.1",
|
|
5
5
|
"author": "OneBlink <developers@oneblink.io> (https://oneblink.io)",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/oneblink/sdk-core-js/issues"
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@eslint/eslintrc": "^3.3.3",
|
|
14
14
|
"@eslint/js": "^9.39.2",
|
|
15
|
-
"@microsoft/eslint-plugin-sdl": "^1.1.0",
|
|
16
15
|
"@microsoft/microsoft-graph-types": "^2.43.1",
|
|
17
16
|
"@oneblink/release-cli": "^5.0.1",
|
|
18
17
|
"@oneblink/types": "github:oneblink/types",
|
|
@@ -24,6 +23,7 @@
|
|
|
24
23
|
"eslint": "^9.39.2",
|
|
25
24
|
"eslint-config-prettier": "^10.1.8",
|
|
26
25
|
"eslint-plugin-prettier": "^5.5.4",
|
|
26
|
+
"eslint-plugin-security": "^4.2.1",
|
|
27
27
|
"fixpack": "^4.0.0",
|
|
28
28
|
"globals": "^17.0.0",
|
|
29
29
|
"lodash": "^4.17.23",
|