form0-core 0.3.1 → 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/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 +105 -19
- 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 -258
package/README.md
CHANGED
|
@@ -81,6 +81,69 @@ console.log(engine.getState());
|
|
|
81
81
|
The engine API exposes `eval()` for calculations, conditions, and validation; `trigger()` for form
|
|
82
82
|
events; and `getState()` for current values and evaluated field state.
|
|
83
83
|
|
|
84
|
+
Schema event callbacks retain their authored source, so ordinary functions and arrows defined in
|
|
85
|
+
`form.events.code` also work on Hermes. Each dispatch uses current scoped field values; initialization
|
|
86
|
+
runs once. Local initialization variables are not captured. Callbacks supplied by host helpers still
|
|
87
|
+
require executable `toString()` output; unavailable source produces an event warning.
|
|
88
|
+
|
|
89
|
+
## Calculation diagnostics
|
|
90
|
+
|
|
91
|
+
Diagnostics are collected automatically, including when console reporting is disabled:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
const engine = createFormEngine({ schema });
|
|
95
|
+
console.log(engine.getDiagnostics()); // [] before the first evaluation
|
|
96
|
+
engine.eval();
|
|
97
|
+
console.log(engine.getDiagnostics()); // latest evaluation, returned as a defensive copy
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Configure reporting in application code:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const engine = createFormEngine({
|
|
104
|
+
schema,
|
|
105
|
+
diagnostics: { console: false },
|
|
106
|
+
onDiagnostics(snapshot) {
|
|
107
|
+
for (const diagnostic of snapshot) {
|
|
108
|
+
reportCalculationDiagnostic(diagnostic);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
engine.eval();
|
|
114
|
+
// For a schema calculating quotient from divisor, correct the input and evaluate again:
|
|
115
|
+
engine.getState().values.divisor = 4;
|
|
116
|
+
engine.eval(); // replaces the snapshot; recovered failures disappear
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`onDiagnostics` runs once after every completed evaluation, including identical snapshots and
|
|
120
|
+
`[]`. Observer exceptions cannot interrupt evaluation and are reported according to the console
|
|
121
|
+
policy. Each diagnostic has `code`, `severity`, `message`, `fieldName`, `phase`, `suggestion`, and
|
|
122
|
+
`context`. Context identifies the schema parent path, related/referenced fields, and `expression`
|
|
123
|
+
versus `EVAL` origin. The engine adds no expression source, field values, or stack traces.
|
|
124
|
+
|
|
125
|
+
Identical diagnostics are deduplicated within an evaluation. Distinct failures are retained,
|
|
126
|
+
including failures observed in intermediate stabilization passes even if a later pass succeeds.
|
|
127
|
+
The snapshot records that evaluation; it is not a history or just the final unresolved errors.
|
|
128
|
+
Legitimate `null`, `undefined`, `NaN`, and `Infinity` results do not produce errors by themselves.
|
|
129
|
+
Diagnostics stay separate from `state.errors` and do not change submission validity.
|
|
130
|
+
|
|
131
|
+
Omit `diagnostics.console` to preserve existing defaults and supplied `WarningSystem` settings.
|
|
132
|
+
`false` silences calculation-owned reporting, including `EVAL()` warnings and debug logs; `true`
|
|
133
|
+
prints diagnostic reporting in development and production. Event reporting retains its existing
|
|
134
|
+
behavior. `WarningSystem` remains compatible.
|
|
135
|
+
|
|
136
|
+
The append-only `runtimeDiagnostics` option is deprecated but remains supported for compatibility.
|
|
137
|
+
For new integrations, omit that option and use `engine.getDiagnostics()` for the latest evaluation,
|
|
138
|
+
or `onDiagnostics(snapshot)` to receive every completed evaluation, including recovery (`[]`).
|
|
139
|
+
This is not a drop-in replacement for the legacy array: snapshots use the structured diagnostic
|
|
140
|
+
contract above and replace prior results rather than accumulating them. If you need a history,
|
|
141
|
+
store snapshots in application code. No removal version is scheduled.
|
|
142
|
+
|
|
143
|
+
See [calculation diagnostics](https://docs.form0.dev/core/engine/calculations-dependencies#seeing-errors-and-warnings)
|
|
144
|
+
for stable codes and the reporting contract. CLI and renderer diagnostics adoption is separate
|
|
145
|
+
from this core API.
|
|
146
|
+
|
|
84
147
|
## Schema and record ownership
|
|
85
148
|
|
|
86
149
|
`form0-core` owns behavioral schema concerns such as fields, conditions, calculations, events, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form0-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/"
|
|
6
6
|
},
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"SECURITY.md"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "node tests/form0-core.test.js && node tests/conditions.test.js && node tests/numeric-field-schema.test.js && node tests/dataset-descriptors.test.js && node tests/structured-record.test.js && node tests/title-fields.test.js && node tests/builtin-metadata.test.js && node tests/calculation-expression-utils.test.js && node tests/calculation-editor.test.js && node tests/form-event-editor.test.js && node tests/form-authoring.test.js && node tests/security-test.js",
|
|
15
|
+
"test": "node tests/form0-core.test.js && node tests/conditions.test.js && node tests/numeric-field-schema.test.js && node tests/dataset-descriptors.test.js && node tests/structured-record.test.js && node tests/title-fields.test.js && node tests/builtin-metadata.test.js && node tests/builtin-import-isolation.test.js && node tests/builtin-catalog-examples.test.js && node tests/date-builtins.test.js && node tests/calculation-expression-utils.test.js && node tests/calculation-editor.test.js && node tests/form-event-editor.test.js && node tests/form-authoring.test.js && node tests/calculation-dependency-engine.test.js && node tests/calculation-diagnostics.characterization.test.js && node tests/calculation-diagnostics.test.js && node tests/calculation-missing-result.test.js && node tests/evaluator-reentry.test.js && node tests/event-compatibility.characterization.test.js && node tests/event-hermes.test.js && node tests/event-async-isolation.test.js && node tests/security-test.js",
|
|
16
16
|
"format": "prettier --write \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\" \"scripts/**/*.{js,mjs}\" package.json README.md SECURITY.md SUPPORT.md CONTRIBUTING.md eslint.config.js .github/workflows/ci.yml .github/workflows/publish.yml",
|
|
17
17
|
"format:check": "prettier --check \"src/**/*.{js,jsx}\" \"tests/**/*.{js,jsx}\" \"scripts/**/*.{js,mjs}\" package.json README.md SECURITY.md SUPPORT.md CONTRIBUTING.md eslint.config.js .github/workflows/ci.yml .github/workflows/publish.yml",
|
|
18
18
|
"lint:check": "eslint src tests",
|
|
@@ -63,5 +63,8 @@
|
|
|
63
63
|
"globals": "^17.12.0",
|
|
64
64
|
"ignore": "^7.0.5",
|
|
65
65
|
"prettier": "^3.5.3"
|
|
66
|
+
},
|
|
67
|
+
"dependencies": {
|
|
68
|
+
"acorn": "8.18.0"
|
|
66
69
|
}
|
|
67
70
|
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from './builtin-metadata.js';
|
|
2
|
+
|
|
3
|
+
// Authoritative builtin metadata. Keep this catalog independent of implementations
|
|
4
|
+
// so security validation can inspect names and contexts without loading EVAL().
|
|
5
|
+
|
|
6
|
+
export const IF_METADATA = defineBuiltinMetadata({
|
|
7
|
+
name: 'IF',
|
|
8
|
+
category: 'logical',
|
|
9
|
+
signature: 'IF(condition, trueValue, falseValue)',
|
|
10
|
+
description: 'Return one of two values based on a condition.',
|
|
11
|
+
examples: [
|
|
12
|
+
'IF($age >= 18, "adult", "minor")',
|
|
13
|
+
'IF($country === "US", IF($state === "CA", 8.99, 5.99), 15.99)',
|
|
14
|
+
],
|
|
15
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export const AND_METADATA = defineBuiltinMetadata({
|
|
19
|
+
name: 'AND',
|
|
20
|
+
category: 'logical',
|
|
21
|
+
signature: 'AND(...conditions)',
|
|
22
|
+
description: 'Return true only when all arguments are truthy.',
|
|
23
|
+
examples: ['AND($age >= 18, $country === "it")'],
|
|
24
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const OR_METADATA = defineBuiltinMetadata({
|
|
28
|
+
name: 'OR',
|
|
29
|
+
category: 'logical',
|
|
30
|
+
signature: 'OR(...conditions)',
|
|
31
|
+
description: 'Return true when at least one argument is truthy.',
|
|
32
|
+
examples: ['OR($email, $phone)'],
|
|
33
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export const COUNT_METADATA = defineBuiltinMetadata({
|
|
37
|
+
name: 'COUNT',
|
|
38
|
+
category: 'logical',
|
|
39
|
+
signature: 'COUNT(values)',
|
|
40
|
+
description: 'Count numeric values.',
|
|
41
|
+
examples: ['COUNT(ARRAY($score_1, $score_2, $score_3))'],
|
|
42
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const COUNTA_METADATA = defineBuiltinMetadata({
|
|
46
|
+
name: 'COUNTA',
|
|
47
|
+
category: 'logical',
|
|
48
|
+
signature: 'COUNTA(values)',
|
|
49
|
+
description: 'Count non-empty values.',
|
|
50
|
+
examples: ['COUNTA(ARRAY($first_name, $last_name, $email))'],
|
|
51
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export const COUNTBLANK_METADATA = defineBuiltinMetadata({
|
|
55
|
+
name: 'COUNTBLANK',
|
|
56
|
+
category: 'logical',
|
|
57
|
+
signature: 'COUNTBLANK(values)',
|
|
58
|
+
description: 'Count blank values.',
|
|
59
|
+
examples: ['COUNTBLANK(ARRAY($first_name, $last_name, $email))'],
|
|
60
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const ARRAY_METADATA = defineBuiltinMetadata({
|
|
64
|
+
name: 'ARRAY',
|
|
65
|
+
category: 'logical',
|
|
66
|
+
signature: 'ARRAY(...values)',
|
|
67
|
+
description: 'Build an array from individual arguments.',
|
|
68
|
+
examples: ['ARRAY($city, $country)'],
|
|
69
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export const EVAL_METADATA = defineBuiltinMetadata({
|
|
73
|
+
name: 'EVAL',
|
|
74
|
+
category: 'control',
|
|
75
|
+
signature: 'EVAL(expression)',
|
|
76
|
+
description: 'Evaluate a dynamically built expression string in a restricted context.',
|
|
77
|
+
examples: ['EVAL("$" + dynamicFieldName)'],
|
|
78
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const CHOICEVALUE_METADATA = defineBuiltinMetadata({
|
|
82
|
+
name: 'CHOICEVALUE',
|
|
83
|
+
category: 'choice',
|
|
84
|
+
signature: 'CHOICEVALUE(fieldValue)',
|
|
85
|
+
description: 'Return the selected choice value from a choice field.',
|
|
86
|
+
examples: [
|
|
87
|
+
'CHOICEVALUE($city)',
|
|
88
|
+
'IF(CHOICEVALUE($city) === "bogota", "Welcome to Bogota!", "Welcome!")',
|
|
89
|
+
],
|
|
90
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const CHOICELABEL_METADATA = defineBuiltinMetadata({
|
|
94
|
+
name: 'CHOICELABEL',
|
|
95
|
+
category: 'choice',
|
|
96
|
+
signature: 'CHOICELABEL(fieldValue)',
|
|
97
|
+
description: 'Return the selected choice label from a choice field.',
|
|
98
|
+
examples: ['CHOICELABEL($city)'],
|
|
99
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export const HASOTHER_METADATA = defineBuiltinMetadata({
|
|
103
|
+
name: 'HASOTHER',
|
|
104
|
+
category: 'choice',
|
|
105
|
+
signature: 'HASOTHER(fieldValue)',
|
|
106
|
+
description: 'Return true when the field has an "other" value.',
|
|
107
|
+
examples: ['HASOTHER($city)'],
|
|
108
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
export const OTHER_METADATA = defineBuiltinMetadata({
|
|
112
|
+
name: 'OTHER',
|
|
113
|
+
category: 'choice',
|
|
114
|
+
signature: 'OTHER(fieldValue)',
|
|
115
|
+
description: 'Return the "other" label from a choice field.',
|
|
116
|
+
examples: ['OTHER($city)'],
|
|
117
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const CHOICEVALUES_METADATA = defineBuiltinMetadata({
|
|
121
|
+
name: 'CHOICEVALUES',
|
|
122
|
+
category: 'choice',
|
|
123
|
+
signature: 'CHOICEVALUES(fieldValue)',
|
|
124
|
+
description: 'Return all selected values from a MultiChoiceField.',
|
|
125
|
+
examples: ['CHOICEVALUES($colors)', 'CHOICEVALUES($colors).includes("red")'],
|
|
126
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
export const CHOICELABELS_METADATA = defineBuiltinMetadata({
|
|
130
|
+
name: 'CHOICELABELS',
|
|
131
|
+
category: 'choice',
|
|
132
|
+
signature: 'CHOICELABELS(fieldValue)',
|
|
133
|
+
description: 'Return all selected labels from a MultiChoiceField.',
|
|
134
|
+
examples: ['CHOICELABELS($colors)'],
|
|
135
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
export const FORM_METADATA = defineBuiltinMetadata({
|
|
139
|
+
name: 'FORM',
|
|
140
|
+
category: 'schema',
|
|
141
|
+
signature: 'FORM()',
|
|
142
|
+
description: 'Access the form definition. Reserved for a future implementation.',
|
|
143
|
+
examples: ['FORM()'],
|
|
144
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
export const DATANAMES_METADATA = defineBuiltinMetadata({
|
|
148
|
+
name: 'DATANAMES',
|
|
149
|
+
category: 'schema',
|
|
150
|
+
signature: "DATANAMES(type = 'any')",
|
|
151
|
+
description: 'Return form field data names, optionally filtered by field type.',
|
|
152
|
+
examples: ["DATANAMES('NumericField')"],
|
|
153
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
export const ABS_METADATA = defineBuiltinMetadata({
|
|
157
|
+
name: 'ABS',
|
|
158
|
+
category: 'math',
|
|
159
|
+
signature: 'ABS(value)',
|
|
160
|
+
description: 'Return the absolute value of a number.',
|
|
161
|
+
examples: ['ABS($variance)'],
|
|
162
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export const CEILING_METADATA = defineBuiltinMetadata({
|
|
166
|
+
name: 'CEILING',
|
|
167
|
+
category: 'math',
|
|
168
|
+
signature: 'CEILING(value, factor)',
|
|
169
|
+
description: 'Round a number up to the nearest multiple.',
|
|
170
|
+
examples: ['CEILING($amount, 5)'],
|
|
171
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
export const COS_METADATA = defineBuiltinMetadata({
|
|
175
|
+
name: 'COS',
|
|
176
|
+
category: 'math',
|
|
177
|
+
signature: 'COS(value)',
|
|
178
|
+
description: 'Return the cosine of a value in radians.',
|
|
179
|
+
examples: ['COS($angle_radians)'],
|
|
180
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
export const SIN_METADATA = defineBuiltinMetadata({
|
|
184
|
+
name: 'SIN',
|
|
185
|
+
category: 'math',
|
|
186
|
+
signature: 'SIN(value)',
|
|
187
|
+
description: 'Return the sine of a value in radians.',
|
|
188
|
+
examples: ['SIN($angle_radians)'],
|
|
189
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
export const ROUND_METADATA = defineBuiltinMetadata({
|
|
193
|
+
name: 'ROUND',
|
|
194
|
+
category: 'math',
|
|
195
|
+
signature: 'ROUND(value, places)',
|
|
196
|
+
description: 'Round a number to a fixed number of decimal places.',
|
|
197
|
+
examples: ['ROUND($total, 2)'],
|
|
198
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
export const DATEADD_METADATA = defineBuiltinMetadata({
|
|
202
|
+
name: 'DATEADD',
|
|
203
|
+
category: 'date',
|
|
204
|
+
signature: 'DATEADD(date, days)',
|
|
205
|
+
description:
|
|
206
|
+
'Add whole days to a YYYY-MM-DD date from 0001-01-01 through 9999-12-31. Returns null when an argument is blank.',
|
|
207
|
+
examples: ['DATEADD($start_date, 30)', 'DATEADD($due_date, -7)'],
|
|
208
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
export const DAYS_METADATA = defineBuiltinMetadata({
|
|
212
|
+
name: 'DAYS',
|
|
213
|
+
category: 'date',
|
|
214
|
+
signature: 'DAYS(endDate, startDate)',
|
|
215
|
+
description:
|
|
216
|
+
'Return the days between YYYY-MM-DD dates from 0001-01-01 through 9999-12-31. Returns null when an argument is blank.',
|
|
217
|
+
examples: ['DAYS($end_date, $start_date)'],
|
|
218
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
export const UPPER_METADATA = defineBuiltinMetadata({
|
|
222
|
+
name: 'UPPER',
|
|
223
|
+
category: 'string',
|
|
224
|
+
signature: 'UPPER(value)',
|
|
225
|
+
description: 'Convert a string to uppercase.',
|
|
226
|
+
examples: ['UPPER($city_name)'],
|
|
227
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
export const SETRESULT_METADATA = defineBuiltinMetadata({
|
|
231
|
+
name: 'SETRESULT',
|
|
232
|
+
category: 'control',
|
|
233
|
+
signature: 'SETRESULT(value)',
|
|
234
|
+
description: 'Set the final return value for multiline calculations.',
|
|
235
|
+
examples: ['SETRESULT($price * $quantity)'],
|
|
236
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION],
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
export const ALERT_METADATA = defineBuiltinMetadata({
|
|
240
|
+
name: 'ALERT',
|
|
241
|
+
category: 'event',
|
|
242
|
+
signature: "ALERT(title, message = '')",
|
|
243
|
+
description: 'Display an alert from a form event handler.',
|
|
244
|
+
examples: ["ALERT('Saved', 'The record was saved successfully.')"],
|
|
245
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
export const SETVALUE_METADATA = defineBuiltinMetadata({
|
|
249
|
+
name: 'SETVALUE',
|
|
250
|
+
category: 'event',
|
|
251
|
+
signature: 'SETVALUE(fieldDataName, valueToSet)',
|
|
252
|
+
description: 'Set a field value from a form event handler.',
|
|
253
|
+
examples: ["SETVALUE('field_dataname', 'value_to_set')"],
|
|
254
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export const ON_METADATA = defineBuiltinMetadata({
|
|
258
|
+
name: 'ON',
|
|
259
|
+
category: 'event',
|
|
260
|
+
signature: 'ON(eventType, fieldKeyOrCallback, callback)',
|
|
261
|
+
description: 'Register an event handler within form event code.',
|
|
262
|
+
examples: ["ON('change', 'city', function(event) { ALERT('City changed!'); })"],
|
|
263
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
export const OFF_METADATA = defineBuiltinMetadata({
|
|
267
|
+
name: 'OFF',
|
|
268
|
+
category: 'event',
|
|
269
|
+
signature: 'OFF(eventType, fieldKeyOrCallback, callback)',
|
|
270
|
+
description: 'Remove event handlers within form event code.',
|
|
271
|
+
examples: ["OFF('change', 'city', specificCallback)"],
|
|
272
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
export const BUILTIN_DEFINITIONS = Object.freeze([
|
|
276
|
+
IF_METADATA,
|
|
277
|
+
AND_METADATA,
|
|
278
|
+
OR_METADATA,
|
|
279
|
+
COUNT_METADATA,
|
|
280
|
+
COUNTA_METADATA,
|
|
281
|
+
COUNTBLANK_METADATA,
|
|
282
|
+
ARRAY_METADATA,
|
|
283
|
+
EVAL_METADATA,
|
|
284
|
+
CHOICEVALUE_METADATA,
|
|
285
|
+
CHOICELABEL_METADATA,
|
|
286
|
+
HASOTHER_METADATA,
|
|
287
|
+
OTHER_METADATA,
|
|
288
|
+
CHOICEVALUES_METADATA,
|
|
289
|
+
CHOICELABELS_METADATA,
|
|
290
|
+
FORM_METADATA,
|
|
291
|
+
DATANAMES_METADATA,
|
|
292
|
+
ABS_METADATA,
|
|
293
|
+
CEILING_METADATA,
|
|
294
|
+
COS_METADATA,
|
|
295
|
+
SIN_METADATA,
|
|
296
|
+
ROUND_METADATA,
|
|
297
|
+
DATEADD_METADATA,
|
|
298
|
+
DAYS_METADATA,
|
|
299
|
+
UPPER_METADATA,
|
|
300
|
+
SETRESULT_METADATA,
|
|
301
|
+
ALERT_METADATA,
|
|
302
|
+
SETVALUE_METADATA,
|
|
303
|
+
ON_METADATA,
|
|
304
|
+
OFF_METADATA,
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
function filterDefinitionsByContext(context) {
|
|
308
|
+
return Object.freeze(
|
|
309
|
+
BUILTIN_DEFINITIONS.filter((definition) => definition.contexts.includes(context))
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export const CALCULATION_BUILTIN_DEFINITIONS = filterDefinitionsByContext(
|
|
314
|
+
BUILTIN_CONTEXTS.CALCULATION
|
|
315
|
+
);
|
|
316
|
+
export const EVENT_BUILTIN_DEFINITIONS = filterDefinitionsByContext(BUILTIN_CONTEXTS.EVENT);
|
|
317
|
+
|
|
318
|
+
export const BUILTIN_DEFINITION_BY_NAME = new Map(
|
|
319
|
+
BUILTIN_DEFINITIONS.map((definition) => [definition.name, definition])
|
|
320
|
+
);
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { CHOICELABEL_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin CHOICELABEL
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} choiceField - The choice field object with choice and other arrays
|
|
7
7
|
* @returns {*} The selected choice label with preserved type, or null if no selection
|
|
8
|
-
* @example
|
|
9
|
-
* // Get the selected city label
|
|
10
|
-
* CHOICELABEL($city)
|
|
11
|
-
* @example
|
|
12
|
-
* // Use in display logic
|
|
13
|
-
* "You selected: " + CHOICELABEL($city)
|
|
14
8
|
*/
|
|
15
|
-
export const CHOICELABEL_METADATA = defineBuiltinMetadata({
|
|
16
|
-
name: 'CHOICELABEL',
|
|
17
|
-
category: 'choice',
|
|
18
|
-
signature: 'CHOICELABEL(fieldValue)',
|
|
19
|
-
description: 'Return the selected choice label from a choice field.',
|
|
20
|
-
examples: ['CHOICELABEL($city)'],
|
|
21
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
9
|
export const CHOICELABEL = (choiceField) => {
|
|
25
10
|
// Handle null/undefined input
|
|
26
11
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { CHOICELABELS_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin CHOICELABELS
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} multiChoiceField - The multi choice field object with choices and other arrays
|
|
7
7
|
* @returns {Array} Array of selected choice labels with preserved types
|
|
8
|
-
* @example
|
|
9
|
-
* // Get all selected color labels
|
|
10
|
-
* CHOICELABELS($colors)
|
|
11
|
-
* @example
|
|
12
|
-
* // Display selected colors
|
|
13
|
-
* "Selected colors: " + CHOICELABELS($colors).join(", ")
|
|
14
8
|
*/
|
|
15
|
-
export const CHOICELABELS_METADATA = defineBuiltinMetadata({
|
|
16
|
-
name: 'CHOICELABELS',
|
|
17
|
-
category: 'choice',
|
|
18
|
-
signature: 'CHOICELABELS(fieldValue)',
|
|
19
|
-
description: 'Return all selected labels from a MultiChoiceField.',
|
|
20
|
-
examples: ['CHOICELABELS($colors)'],
|
|
21
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
9
|
export const CHOICELABELS = (multiChoiceField) => {
|
|
25
10
|
// Handle null/undefined input
|
|
26
11
|
if (!multiChoiceField || typeof multiChoiceField !== 'object') {
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { CHOICEVALUE_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin CHOICEVALUE
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} choiceField - The choice field object with choice and other arrays
|
|
7
7
|
* @returns {*} The selected choice value with preserved type, or null if no selection
|
|
8
|
-
* @example
|
|
9
|
-
* // Get the selected city value
|
|
10
|
-
* CHOICEVALUE($city)
|
|
11
|
-
* @example
|
|
12
|
-
* // Use in conditional logic
|
|
13
|
-
* IF(CHOICEVALUE($city) === "bogota", "Welcome to Bogotá!", "Welcome!")
|
|
14
8
|
*/
|
|
15
|
-
export const CHOICEVALUE_METADATA = defineBuiltinMetadata({
|
|
16
|
-
name: 'CHOICEVALUE',
|
|
17
|
-
category: 'choice',
|
|
18
|
-
signature: 'CHOICEVALUE(fieldValue)',
|
|
19
|
-
description: 'Return the selected choice value from a choice field.',
|
|
20
|
-
examples: [
|
|
21
|
-
'CHOICEVALUE($city)',
|
|
22
|
-
'IF(CHOICEVALUE($city) === "bogota", "Welcome to Bogota!", "Welcome!")',
|
|
23
|
-
],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const CHOICEVALUE = (choiceField) => {
|
|
28
10
|
// Handle null/undefined input
|
|
29
11
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { CHOICEVALUES_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin CHOICEVALUES
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} multiChoiceField - The multi choice field object with choices and other arrays
|
|
7
7
|
* @returns {Array} Array of selected choice values with preserved types
|
|
8
|
-
* @example
|
|
9
|
-
* // Get all selected color values
|
|
10
|
-
* CHOICEVALUES($colors)
|
|
11
|
-
* @example
|
|
12
|
-
* // Check if "red" is selected
|
|
13
|
-
* CHOICEVALUES($colors).includes("red")
|
|
14
8
|
*/
|
|
15
|
-
export const CHOICEVALUES_METADATA = defineBuiltinMetadata({
|
|
16
|
-
name: 'CHOICEVALUES',
|
|
17
|
-
category: 'choice',
|
|
18
|
-
signature: 'CHOICEVALUES(fieldValue)',
|
|
19
|
-
description: 'Return all selected values from a MultiChoiceField.',
|
|
20
|
-
examples: ['CHOICEVALUES($colors)', 'CHOICEVALUES($colors).includes("red")'],
|
|
21
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
-
});
|
|
23
|
-
|
|
24
9
|
export const CHOICEVALUES = (multiChoiceField) => {
|
|
25
10
|
// Handle null/undefined input
|
|
26
11
|
if (!multiChoiceField || typeof multiChoiceField !== 'object') {
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { HASOTHER_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin HASOTHER
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} choiceField - The choice field object (SingleChoiceField with choice array or MultiChoiceField with choices array) and other array
|
|
7
7
|
* @returns {boolean} True if user entered an other option, false otherwise
|
|
8
|
-
* @example
|
|
9
|
-
* // Check if user entered other option in single choice field
|
|
10
|
-
* HASOTHER($city)
|
|
11
|
-
* @example
|
|
12
|
-
* // Check if user entered other option in multi choice field
|
|
13
|
-
* HASOTHER($colors)
|
|
14
|
-
* @example
|
|
15
|
-
* // Use in conditional logic
|
|
16
|
-
* IF(HASOTHER($city), "Custom city: " + OTHER($city), "Selected city: " + CHOICELABEL($city))
|
|
17
8
|
*/
|
|
18
|
-
export const HASOTHER_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'HASOTHER',
|
|
20
|
-
category: 'choice',
|
|
21
|
-
signature: 'HASOTHER(fieldValue)',
|
|
22
|
-
description: 'Return true when the field has an "other" value.',
|
|
23
|
-
examples: ['HASOTHER($city)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const HASOTHER = (choiceField) => {
|
|
28
10
|
// Handle null/undefined input
|
|
29
11
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export { OTHER_METADATA } from '../builtin-definitions.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @builtin OTHER
|
|
5
|
-
*
|
|
5
|
+
* Name, signature, description, examples, and contexts: see builtin-definitions.js.
|
|
6
6
|
* @param {Object} choiceField - The choice field object (SingleChoiceField with choice array or MultiChoiceField with choices array) and other array
|
|
7
7
|
* @returns {string|null} The other label if user entered an other option, null otherwise
|
|
8
|
-
* @example
|
|
9
|
-
* // Get the other value from single choice field
|
|
10
|
-
* OTHER($city)
|
|
11
|
-
* @example
|
|
12
|
-
* // Get the other value from multi choice field
|
|
13
|
-
* OTHER($colors)
|
|
14
|
-
* @example
|
|
15
|
-
* // Use in conditional logic
|
|
16
|
-
* IF(HASOTHER($city), "Custom city: " + OTHER($city), "No custom city entered")
|
|
17
8
|
*/
|
|
18
|
-
export const OTHER_METADATA = defineBuiltinMetadata({
|
|
19
|
-
name: 'OTHER',
|
|
20
|
-
category: 'choice',
|
|
21
|
-
signature: 'OTHER(fieldValue)',
|
|
22
|
-
description: 'Return the "other" label from a choice field.',
|
|
23
|
-
examples: ['OTHER($city)'],
|
|
24
|
-
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
-
});
|
|
26
|
-
|
|
27
9
|
export const OTHER = (choiceField) => {
|
|
28
10
|
// Handle null/undefined input
|
|
29
11
|
if (!choiceField || typeof choiceField !== 'object') {
|