form0-core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/package.json +5 -2
- package/src/builtins/builtin-definitions.js +320 -0
- package/src/builtins/choice/choicelabel.js +2 -17
- package/src/builtins/choice/choicelabels.js +2 -17
- package/src/builtins/choice/choicevalue.js +2 -20
- package/src/builtins/choice/choicevalues.js +2 -17
- package/src/builtins/choice/hasother.js +2 -20
- package/src/builtins/choice/other.js +2 -20
- package/src/builtins/control/eval.js +78 -31
- package/src/builtins/control/setresult.js +29 -17
- package/src/builtins/date/dateadd.js +24 -0
- package/src/builtins/date/days.js +22 -0
- package/src/builtins/event/control/off.js +3 -27
- package/src/builtins/event/control/on.js +3 -19
- package/src/builtins/event/event-operations-collector.js +33 -9
- package/src/builtins/event/field/setvalue.js +3 -32
- package/src/builtins/event/ui/alert.js +3 -11
- package/src/builtins/logical/and.js +2 -17
- package/src/builtins/logical/array.js +2 -35
- package/src/builtins/logical/count.js +2 -20
- package/src/builtins/logical/counta.js +2 -20
- package/src/builtins/logical/countblank.js +2 -20
- package/src/builtins/logical/if.js +2 -20
- package/src/builtins/logical/or.js +2 -17
- package/src/builtins/math/abs.js +2 -20
- package/src/builtins/math/ceiling.js +2 -20
- package/src/builtins/math/cos.js +2 -20
- package/src/builtins/math/round.js +2 -23
- package/src/builtins/math/sin.js +2 -20
- package/src/builtins/registry.js +23 -40
- package/src/builtins/schema/datanames.js +9 -20
- package/src/builtins/schema/form.js +2 -20
- package/src/builtins/string/upper.js +2 -20
- package/src/engine/calculation-diagnostics.js +107 -0
- package/src/engine/calculation.js +65 -32
- package/src/engine/conditions.js +69 -3
- package/src/engine/evaluator.js +56 -18
- package/src/engine/event-operation-scope.js +61 -0
- package/src/engine/event-source.js +164 -0
- package/src/engine/events.js +111 -23
- package/src/engine/form-engine.js +61 -16
- package/src/engine/warning-system.js +7 -5
- package/src/schema/field-specs.js +2 -2
- package/src/security/validation.js +10 -8
- package/src/utilities/calculation-editor.js +42 -6
- package/src/utilities/date-utils.js +72 -0
- package/src/builtins/docs/choice/choicelabel.mdx +0 -110
- package/src/builtins/docs/choice/choicelabels.mdx +0 -230
- package/src/builtins/docs/choice/choicevalue.mdx +0 -109
- package/src/builtins/docs/choice/choicevalues.mdx +0 -196
- package/src/builtins/docs/choice/hasother.mdx +0 -133
- package/src/builtins/docs/choice/other.mdx +0 -150
- package/src/builtins/docs/control/setresult.mdx +0 -203
- package/src/builtins/docs/event/field/setvalue.mdx +0 -239
- package/src/builtins/docs/index.mdx +0 -234
- package/src/builtins/docs/logical/and.mdx +0 -141
- package/src/builtins/docs/logical/if.mdx +0 -94
- package/src/builtins/docs/logical/or.mdx +0 -191
- package/src/builtins/docs/overview.mdx +0 -255
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { COUNTBLANK_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin COUNTBLANK
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Array} values - An array of items to check
|
|
7
7
|
* @returns {number} The count of blank items in the array
|
|
8
|
-
* @example
|
|
9
|
-
* // Returns 3
|
|
10
|
-
* COUNTBLANK([null, null, '', 1])
|
|
11
|
-
* @example
|
|
12
|
-
* // Returns 2
|
|
13
|
-
* COUNTBLANK([undefined, '', 'a', 0])
|
|
14
|
-
* @example
|
|
15
|
-
* // Returns 0 (no blank values)
|
|
16
|
-
* COUNTBLANK([1, 2, 'a', true])
|
|
17
8
|
*/
|
|
18
|
-
export const COUNTBLANK_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'COUNTBLANK',
|
|
20
|
-
category: 'logical',
|
|
21
|
-
signature: 'COUNTBLANK(values)',
|
|
22
|
-
description: 'Count blank values.',
|
|
23
|
-
examples: ['COUNTBLANK(ARRAY($first_name, $last_name, $email))'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const COUNTBLANK = (values) => {
|
|
28
10
|
// Handle null/undefined input
|
|
29
11
|
if (!Array.isArray(values)) {
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { IF_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin IF
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {boolean} condition - The condition to evaluate
|
|
7
7
|
* @param {*} trueValue - Value returned if condition is true
|
|
8
8
|
* @param {*} falseValue - Value returned if condition is false
|
|
9
9
|
* @returns {*} Either trueValue or falseValue
|
|
10
|
-
* @example
|
|
11
|
-
* // Returns "Yes" if age >= 18, otherwise "No"
|
|
12
|
-
* IF(age >= 18, "Yes", "No")
|
|
13
|
-
* @example
|
|
14
|
-
* // Nested conditions for shipping cost
|
|
15
|
-
* IF(country === "US", IF(state === "CA", 8.99, 5.99), 15.99)
|
|
16
10
|
*/
|
|
17
|
-
export const IF_METADATA = defineBuiltinMetadata({
|
|
18
|
-
name: 'IF',
|
|
19
|
-
category: 'logical',
|
|
20
|
-
signature: 'IF(condition, trueValue, falseValue)',
|
|
21
|
-
description: 'Return one of two values based on a condition.',
|
|
22
|
-
examples: [
|
|
23
|
-
'IF($age >= 18, "adult", "minor")',
|
|
24
|
-
'IF($country === "US", IF($state === "CA", 8.99, 5.99), 15.99)',
|
|
25
|
-
],
|
|
26
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
27
|
-
});
|
|
28
|
-
|
|
29
11
|
export const IF = (cond, a, b) => (cond ? a : b);
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export { OR_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin OR
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {...*} args - Any number of arguments to evaluate
|
|
7
7
|
* @returns {boolean} True if at least one argument is truthy, false otherwise
|
|
8
|
-
* @example
|
|
9
|
-
* // Check if user has any premium feature
|
|
10
|
-
* OR(user.isPremium, user.isVip, user.hasSubscription)
|
|
11
|
-
* @example
|
|
12
|
-
* // Validation - either email or phone is required
|
|
13
|
-
* OR(email.length > 0, phone.length > 0)
|
|
14
8
|
*/
|
|
15
|
-
export const OR_METADATA = defineBuiltinMetadata({
|
|
16
|
-
name: 'OR',
|
|
17
|
-
category: 'logical',
|
|
18
|
-
signature: 'OR(...conditions)',
|
|
19
|
-
description: 'Return true when at least one argument is truthy.',
|
|
20
|
-
examples: ['OR($email, $phone)'],
|
|
21
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
9
|
export const OR = (...args) => args.some(Boolean);
|
package/src/builtins/math/abs.js
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export { ABS_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin ABS
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {number} value - The number of which to return the absolute value
|
|
7
7
|
* @returns {number} The absolute value of the input number
|
|
8
|
-
* @example
|
|
9
|
-
* // Returns 1
|
|
10
|
-
* ABS(-1)
|
|
11
|
-
* @example
|
|
12
|
-
* // Returns 42
|
|
13
|
-
* ABS(42)
|
|
14
|
-
* @example
|
|
15
|
-
* // Returns 3.14
|
|
16
|
-
* ABS(-3.14)
|
|
17
8
|
*/
|
|
18
|
-
export const ABS_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'ABS',
|
|
20
|
-
category: 'math',
|
|
21
|
-
signature: 'ABS(value)',
|
|
22
|
-
description: 'Return the absolute value of a number.',
|
|
23
|
-
examples: ['ABS($variance)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const ABS = (value) => Math.abs(value);
|
|
@@ -1,30 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
export { CEILING_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin CEILING
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {number} value - The value to round up to the nearest integer multiple of factor
|
|
7
7
|
* @param {number} [factor=1] - The number to whose multiples value will be rounded
|
|
8
8
|
* @returns {number} The value rounded up to the nearest multiple of factor
|
|
9
|
-
* @example
|
|
10
|
-
* // Returns 139.9
|
|
11
|
-
* CEILING(139.85, 0.1)
|
|
12
|
-
* @example
|
|
13
|
-
* // Returns 140
|
|
14
|
-
* CEILING(139.001)
|
|
15
|
-
* @example
|
|
16
|
-
* // Returns 15
|
|
17
|
-
* CEILING(12.3, 5)
|
|
18
9
|
*/
|
|
19
|
-
export const CEILING_METADATA = defineBuiltinMetadata({
|
|
20
|
-
name: 'CEILING',
|
|
21
|
-
category: 'math',
|
|
22
|
-
signature: 'CEILING(value, factor)',
|
|
23
|
-
description: 'Round a number up to the nearest multiple.',
|
|
24
|
-
examples: ['CEILING($amount, 5)'],
|
|
25
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
26
|
-
});
|
|
27
|
-
|
|
28
10
|
export const CEILING = (value, factor = 1) => {
|
|
29
11
|
if (factor === 0) {
|
|
30
12
|
throw new Error('CEILING factor cannot be zero');
|
package/src/builtins/math/cos.js
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export { COS_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin COS
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {number} value - The value for which to calculate the cosine
|
|
7
7
|
* @returns {number} The cosine of the input value
|
|
8
|
-
* @example
|
|
9
|
-
* // Returns 0.15425144988758405
|
|
10
|
-
* COS(30)
|
|
11
|
-
* @example
|
|
12
|
-
* // Returns 1
|
|
13
|
-
* COS(0)
|
|
14
|
-
* @example
|
|
15
|
-
* // Returns -1
|
|
16
|
-
* COS(Math.PI)
|
|
17
8
|
*/
|
|
18
|
-
export const COS_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'COS',
|
|
20
|
-
category: 'math',
|
|
21
|
-
signature: 'COS(value)',
|
|
22
|
-
description: 'Return the cosine of a value in radians.',
|
|
23
|
-
examples: ['COS($angle_radians)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const COS = (value) => Math.cos(value);
|
|
@@ -1,33 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
export { ROUND_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin ROUND
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {number} value - The numeric value to be rounded
|
|
7
7
|
* @param {number} places - The number of decimal places to round to
|
|
8
8
|
* @returns {number} The value rounded to the specified number of decimal places
|
|
9
|
-
* @example
|
|
10
|
-
* // Returns 179.8
|
|
11
|
-
* ROUND(179.848, 1)
|
|
12
|
-
* @example
|
|
13
|
-
* // Returns 900
|
|
14
|
-
* ROUND(918.268, -2)
|
|
15
|
-
* @example
|
|
16
|
-
* // Returns 3
|
|
17
|
-
* ROUND(3.14159, 0)
|
|
18
|
-
* @example
|
|
19
|
-
* // Returns 3.14
|
|
20
|
-
* ROUND(3.14159, 2)
|
|
21
9
|
*/
|
|
22
|
-
export const ROUND_METADATA = defineBuiltinMetadata({
|
|
23
|
-
name: 'ROUND',
|
|
24
|
-
category: 'math',
|
|
25
|
-
signature: 'ROUND(value, places)',
|
|
26
|
-
description: 'Round a number to a fixed number of decimal places.',
|
|
27
|
-
examples: ['ROUND($total, 2)'],
|
|
28
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
29
|
-
});
|
|
30
|
-
|
|
31
10
|
export const ROUND = (value, places) => {
|
|
32
11
|
if (typeof value !== 'number' || typeof places !== 'number') {
|
|
33
12
|
throw new Error('ROUND requires two numeric arguments');
|
package/src/builtins/math/sin.js
CHANGED
|
@@ -1,27 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
export { SIN_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin SIN
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {number} value - The value for which to calculate the sine
|
|
7
7
|
* @returns {number} The sine of the input value
|
|
8
|
-
* @example
|
|
9
|
-
* // Returns -0.9880316240928618
|
|
10
|
-
* SIN(30)
|
|
11
|
-
* @example
|
|
12
|
-
* // Returns 0
|
|
13
|
-
* SIN(0)
|
|
14
|
-
* @example
|
|
15
|
-
* // Returns 1
|
|
16
|
-
* SIN(Math.PI / 2)
|
|
17
8
|
*/
|
|
18
|
-
export const SIN_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'SIN',
|
|
20
|
-
category: 'math',
|
|
21
|
-
signature: 'SIN(value)',
|
|
22
|
-
description: 'Return the sine of a value in radians.',
|
|
23
|
-
examples: ['SIN($angle_radians)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const SIN = (value) => Math.sin(value);
|
package/src/builtins/registry.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { cloneBuiltinMetadata, BUILTIN_CONTEXTS } from './builtin-metadata.js';
|
|
2
|
+
import { BUILTIN_DEFINITIONS } from './builtin-definitions.js';
|
|
3
|
+
|
|
4
|
+
export {
|
|
5
|
+
BUILTIN_DEFINITIONS,
|
|
6
|
+
CALCULATION_BUILTIN_DEFINITIONS,
|
|
7
|
+
EVENT_BUILTIN_DEFINITIONS,
|
|
8
|
+
BUILTIN_DEFINITION_BY_NAME,
|
|
9
|
+
} from './builtin-definitions.js';
|
|
2
10
|
|
|
3
11
|
import { IF, IF_METADATA } from './logical/if.js';
|
|
4
12
|
import { AND, AND_METADATA } from './logical/and.js';
|
|
@@ -10,6 +18,7 @@ import { ARRAY, ARRAY_METADATA } from './logical/array.js';
|
|
|
10
18
|
import {
|
|
11
19
|
SETRESULT,
|
|
12
20
|
SETRESULT_METADATA,
|
|
21
|
+
__beginResultScope,
|
|
13
22
|
__consumeResult,
|
|
14
23
|
__resetResult,
|
|
15
24
|
} from './control/setresult.js';
|
|
@@ -27,13 +36,14 @@ import { CEILING, CEILING_METADATA } from './math/ceiling.js';
|
|
|
27
36
|
import { COS, COS_METADATA } from './math/cos.js';
|
|
28
37
|
import { SIN, SIN_METADATA } from './math/sin.js';
|
|
29
38
|
import { ROUND, ROUND_METADATA } from './math/round.js';
|
|
39
|
+
import { DATEADD, DATEADD_METADATA } from './date/dateadd.js';
|
|
40
|
+
import { DAYS, DAYS_METADATA } from './date/days.js';
|
|
30
41
|
import { UPPER, UPPER_METADATA } from './string/upper.js';
|
|
31
42
|
|
|
32
43
|
import { ALERT, ALERT_METADATA } from './event/ui/alert.js';
|
|
33
44
|
import { SETVALUE, SETVALUE_METADATA } from './event/field/setvalue.js';
|
|
34
45
|
import { ON, ON_METADATA } from './event/control/on.js';
|
|
35
46
|
import { OFF, OFF_METADATA } from './event/control/off.js';
|
|
36
|
-
import { __consumeEventOperations } from './event/event-operations-collector.js';
|
|
37
47
|
|
|
38
48
|
export { IF, IF_METADATA } from './logical/if.js';
|
|
39
49
|
export { AND, AND_METADATA } from './logical/and.js';
|
|
@@ -45,6 +55,7 @@ export { ARRAY, ARRAY_METADATA } from './logical/array.js';
|
|
|
45
55
|
export {
|
|
46
56
|
SETRESULT,
|
|
47
57
|
SETRESULT_METADATA,
|
|
58
|
+
__beginResultScope,
|
|
48
59
|
__consumeResult,
|
|
49
60
|
__resetResult,
|
|
50
61
|
} from './control/setresult.js';
|
|
@@ -67,15 +78,16 @@ export { CEILING, CEILING_METADATA } from './math/ceiling.js';
|
|
|
67
78
|
export { COS, COS_METADATA } from './math/cos.js';
|
|
68
79
|
export { SIN, SIN_METADATA } from './math/sin.js';
|
|
69
80
|
export { ROUND, ROUND_METADATA } from './math/round.js';
|
|
81
|
+
export { DATEADD, DATEADD_METADATA } from './date/dateadd.js';
|
|
82
|
+
export { DAYS, DAYS_METADATA } from './date/days.js';
|
|
70
83
|
export { UPPER, UPPER_METADATA } from './string/upper.js';
|
|
71
84
|
|
|
72
85
|
export { ALERT, ALERT_METADATA } from './event/ui/alert.js';
|
|
73
86
|
export { SETVALUE, SETVALUE_METADATA } from './event/field/setvalue.js';
|
|
74
87
|
export { ON, ON_METADATA } from './event/control/on.js';
|
|
75
88
|
export { OFF, OFF_METADATA } from './event/control/off.js';
|
|
76
|
-
export { __consumeEventOperations } from './event/event-operations-collector.js';
|
|
77
89
|
|
|
78
|
-
const
|
|
90
|
+
const RUNTIME_ENTRIES = Object.freeze([
|
|
79
91
|
{ implementation: IF, definition: IF_METADATA },
|
|
80
92
|
{ implementation: AND, definition: AND_METADATA },
|
|
81
93
|
{ implementation: OR, definition: OR_METADATA },
|
|
@@ -97,62 +109,33 @@ const COMMON_RUNTIME_ENTRIES = Object.freeze([
|
|
|
97
109
|
{ implementation: COS, definition: COS_METADATA },
|
|
98
110
|
{ implementation: SIN, definition: SIN_METADATA },
|
|
99
111
|
{ implementation: ROUND, definition: ROUND_METADATA },
|
|
112
|
+
{ implementation: DATEADD, definition: DATEADD_METADATA },
|
|
113
|
+
{ implementation: DAYS, definition: DAYS_METADATA },
|
|
100
114
|
{ implementation: UPPER, definition: UPPER_METADATA },
|
|
101
|
-
]);
|
|
102
|
-
|
|
103
|
-
const CALCULATION_ONLY_ENTRIES = Object.freeze([
|
|
104
115
|
{ implementation: SETRESULT, definition: SETRESULT_METADATA },
|
|
105
|
-
]);
|
|
106
|
-
|
|
107
|
-
const EVENT_ONLY_ENTRIES = Object.freeze([
|
|
108
116
|
{ implementation: ALERT, definition: ALERT_METADATA },
|
|
109
117
|
{ implementation: SETVALUE, definition: SETVALUE_METADATA },
|
|
110
118
|
{ implementation: ON, definition: ON_METADATA },
|
|
111
119
|
{ implementation: OFF, definition: OFF_METADATA },
|
|
112
120
|
]);
|
|
113
121
|
|
|
114
|
-
function buildBuiltinObject(
|
|
122
|
+
function buildBuiltinObject(context) {
|
|
115
123
|
return Object.freeze(
|
|
116
124
|
Object.fromEntries(
|
|
117
|
-
|
|
125
|
+
RUNTIME_ENTRIES.filter(({ definition }) => definition.contexts.includes(context)).map(
|
|
126
|
+
({ implementation, definition }) => [definition.name, implementation]
|
|
127
|
+
)
|
|
118
128
|
)
|
|
119
129
|
);
|
|
120
130
|
}
|
|
121
131
|
|
|
122
|
-
|
|
123
|
-
return Object.freeze(definitions.filter((definition) => definition.contexts.includes(context)));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export const calculationBuiltins = buildBuiltinObject([
|
|
127
|
-
...COMMON_RUNTIME_ENTRIES,
|
|
128
|
-
...CALCULATION_ONLY_ENTRIES,
|
|
129
|
-
]);
|
|
132
|
+
export const calculationBuiltins = buildBuiltinObject(BUILTIN_CONTEXTS.CALCULATION);
|
|
130
133
|
|
|
131
|
-
export const eventBuiltins = buildBuiltinObject(
|
|
134
|
+
export const eventBuiltins = buildBuiltinObject(BUILTIN_CONTEXTS.EVENT);
|
|
132
135
|
|
|
133
136
|
// Backward-compatible alias for calculation runtime helpers.
|
|
134
137
|
export const builtins = calculationBuiltins;
|
|
135
138
|
|
|
136
|
-
export const BUILTIN_DEFINITIONS = Object.freeze(
|
|
137
|
-
[...COMMON_RUNTIME_ENTRIES, ...CALCULATION_ONLY_ENTRIES, ...EVENT_ONLY_ENTRIES].map(
|
|
138
|
-
({ definition }) => definition
|
|
139
|
-
)
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
export const CALCULATION_BUILTIN_DEFINITIONS = filterDefinitionsByContext(
|
|
143
|
-
BUILTIN_DEFINITIONS,
|
|
144
|
-
BUILTIN_CONTEXTS.CALCULATION
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
export const EVENT_BUILTIN_DEFINITIONS = filterDefinitionsByContext(
|
|
148
|
-
BUILTIN_DEFINITIONS,
|
|
149
|
-
BUILTIN_CONTEXTS.EVENT
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
export const BUILTIN_DEFINITION_BY_NAME = new Map(
|
|
153
|
-
BUILTIN_DEFINITIONS.map((definition) => [definition.name, definition])
|
|
154
|
-
);
|
|
155
|
-
|
|
156
139
|
export function getBuiltinDefinitions() {
|
|
157
140
|
return BUILTIN_DEFINITIONS.map(cloneBuiltinMetadata);
|
|
158
141
|
}
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { DATANAMES_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin DATANAMES
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {string} type - Optional field type to filter by (e.g., 'TextField', 'RepeatableSection', etc.). Defaults to 'any' (all fields)
|
|
7
7
|
* @returns {Array<string>} Array of field data names
|
|
8
|
-
* @example
|
|
9
|
-
* // Get all field data names
|
|
10
|
-
* DATANAMES()
|
|
11
|
-
* @example
|
|
12
|
-
* // Get only RepeatableSection data names
|
|
13
|
-
* DATANAMES('RepeatableSection')
|
|
14
|
-
* @example
|
|
15
|
-
* // Get only TextField data names
|
|
16
|
-
* DATANAMES('TextField')
|
|
17
8
|
*/
|
|
18
|
-
export const DATANAMES_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'DATANAMES',
|
|
20
|
-
category: 'schema',
|
|
21
|
-
signature: "DATANAMES(type = 'any')",
|
|
22
|
-
description: 'Return form field data names, optionally filtered by field type.',
|
|
23
|
-
examples: ["DATANAMES('NumericField')"],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
// Global schema context for DATANAMES() - set during expression evaluation
|
|
28
10
|
let _dataNamesSchema = null;
|
|
29
11
|
|
|
@@ -32,7 +14,14 @@ let _dataNamesSchema = null;
|
|
|
32
14
|
* Called internally by the expression evaluator
|
|
33
15
|
*/
|
|
34
16
|
export function __setDataNamesContext(schema) {
|
|
17
|
+
const previousSchema = _dataNamesSchema;
|
|
35
18
|
_dataNamesSchema = schema;
|
|
19
|
+
let restored = false;
|
|
20
|
+
return () => {
|
|
21
|
+
if (restored) return;
|
|
22
|
+
restored = true;
|
|
23
|
+
_dataNamesSchema = previousSchema;
|
|
24
|
+
};
|
|
36
25
|
}
|
|
37
26
|
|
|
38
27
|
/**
|
|
@@ -1,28 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
export { FORM_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin FORM
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @returns {Object} The form schema object
|
|
7
|
-
* @example
|
|
8
|
-
* // Get form title
|
|
9
|
-
* FORM().title
|
|
10
|
-
* @example
|
|
11
|
-
* // Access field definitions
|
|
12
|
-
* FORM().elements[0].label
|
|
13
|
-
* @example
|
|
14
|
-
* // Check if form has RepeatableSections
|
|
15
|
-
* FORM().elements.some(el => el.type === 'RepeatableSection')
|
|
16
7
|
*/
|
|
17
|
-
export const FORM_METADATA = defineBuiltinMetadata({
|
|
18
|
-
name: 'FORM',
|
|
19
|
-
category: 'schema',
|
|
20
|
-
signature: 'FORM()',
|
|
21
|
-
description: 'Access the form definition. Reserved for a future implementation.',
|
|
22
|
-
examples: ['FORM()'],
|
|
23
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
24
|
-
});
|
|
25
|
-
|
|
26
8
|
export function FORM() {
|
|
27
9
|
// Placeholder for future implementation
|
|
28
10
|
// Will provide access to nested JSON form definition
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { UPPER_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin UPPER
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {string} value - The string to convert to uppercase
|
|
7
7
|
* @returns {string} The uppercase version of the input string
|
|
8
|
-
* @example
|
|
9
|
-
* // Returns "HELLO WORLD"
|
|
10
|
-
* UPPER("hello world")
|
|
11
|
-
* @example
|
|
12
|
-
* // Returns "FORM0"
|
|
13
|
-
* UPPER("form0")
|
|
14
|
-
* @example
|
|
15
|
-
* // Returns "ABC123"
|
|
16
|
-
* UPPER("abc123")
|
|
17
8
|
*/
|
|
18
|
-
export const UPPER_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'UPPER',
|
|
20
|
-
category: 'string',
|
|
21
|
-
signature: 'UPPER(value)',
|
|
22
|
-
description: 'Convert a string to uppercase.',
|
|
23
|
-
examples: ['UPPER($city_name)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const UPPER = (value) => {
|
|
28
10
|
if (value == null) {
|
|
29
11
|
return '';
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Engine-private, evaluation-scoped calculation reporting. Legacy warning handlers
|
|
3
|
+
* and throttling remain independent of this snapshot.
|
|
4
|
+
*/
|
|
5
|
+
export function createCalculationDiagnostics(options, observer, resolver, warningSystem) {
|
|
6
|
+
const consoleOverride = typeof options?.console === 'boolean' ? options.console : undefined;
|
|
7
|
+
const frames = [];
|
|
8
|
+
let snapshot = [];
|
|
9
|
+
const copy = (items) => JSON.parse(JSON.stringify(items));
|
|
10
|
+
|
|
11
|
+
function reportObserverFailure() {
|
|
12
|
+
if (consoleOverride ?? warningSystem.enableConsoleWarnings) {
|
|
13
|
+
console.warn('[form0] onDiagnostics observer failed.');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function record(fieldName, diagnostic) {
|
|
18
|
+
const frame = frames.at(-1);
|
|
19
|
+
if (!frame) return;
|
|
20
|
+
const item = {
|
|
21
|
+
code: diagnostic.code,
|
|
22
|
+
severity: diagnostic.severity || 'error',
|
|
23
|
+
message:
|
|
24
|
+
typeof diagnostic.message === 'string'
|
|
25
|
+
? diagnostic.message
|
|
26
|
+
: 'Unknown calculation runtime error.',
|
|
27
|
+
fieldName,
|
|
28
|
+
phase: diagnostic.phase,
|
|
29
|
+
suggestion: diagnostic.suggestion || null,
|
|
30
|
+
context: {
|
|
31
|
+
source: 'expression',
|
|
32
|
+
parentPath: [...(resolver.getFieldInfo(fieldName)?.parentPath || [])],
|
|
33
|
+
...diagnostic.context,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const key = JSON.stringify(item);
|
|
37
|
+
if (!frame.keys.has(key)) {
|
|
38
|
+
frame.keys.add(key);
|
|
39
|
+
frame.items.push(copy([item])[0]);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function emitWarning(warning, fieldNames = [warning.executionContext.fieldName]) {
|
|
44
|
+
const isScope = warning.type === 'FIELD_ACCESS_WARNING';
|
|
45
|
+
for (const fieldName of fieldNames) {
|
|
46
|
+
record(fieldName, {
|
|
47
|
+
code: isScope
|
|
48
|
+
? warning.reason === 'not_found'
|
|
49
|
+
? 'missing_reference'
|
|
50
|
+
: 'restricted_reference'
|
|
51
|
+
: warning.reason,
|
|
52
|
+
severity: warning.severity || 'warning',
|
|
53
|
+
phase: isScope ? 'scope' : 'dependency',
|
|
54
|
+
message: warning.message,
|
|
55
|
+
suggestion: warning.suggestion,
|
|
56
|
+
context: isScope
|
|
57
|
+
? { referencedFieldName: warning.fieldName }
|
|
58
|
+
: {
|
|
59
|
+
...(warning.fieldContext.fieldNames
|
|
60
|
+
? { fieldNames: [...warning.fieldContext.fieldNames] }
|
|
61
|
+
: {}),
|
|
62
|
+
...(warning.fieldContext.maxPasses
|
|
63
|
+
? { maxPasses: warning.fieldContext.maxPasses }
|
|
64
|
+
: {}),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
warningSystem.emitWarning(
|
|
69
|
+
warning,
|
|
70
|
+
consoleOverride === undefined ? undefined : { console: false }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
consoleOverride,
|
|
76
|
+
record,
|
|
77
|
+
emitWarning,
|
|
78
|
+
begin() {
|
|
79
|
+
const frame = { items: [], keys: new Set() };
|
|
80
|
+
frames.push(frame);
|
|
81
|
+
return frame;
|
|
82
|
+
},
|
|
83
|
+
abort(frame) {
|
|
84
|
+
frames.splice(frames.indexOf(frame), 1);
|
|
85
|
+
},
|
|
86
|
+
complete(frame) {
|
|
87
|
+
frames.splice(frames.indexOf(frame), 1);
|
|
88
|
+
snapshot = copy(frame.items);
|
|
89
|
+
if (consoleOverride === true) {
|
|
90
|
+
for (const item of frame.items) {
|
|
91
|
+
console.warn(`[form0] ${item.code} (${item.fieldName}): ${item.message}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (typeof observer === 'function') {
|
|
95
|
+
try {
|
|
96
|
+
const result = observer(copy(frame.items));
|
|
97
|
+
if (result && typeof result.then === 'function') {
|
|
98
|
+
Promise.resolve(result).catch(reportObserverFailure);
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
reportObserverFailure();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
getSnapshot: () => copy(snapshot),
|
|
106
|
+
};
|
|
107
|
+
}
|