form0-core 0.1.1 → 0.1.3
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/LICENSE +1 -1
- package/README.md +14 -0
- package/package.json +2 -2
- package/src/builtins/builtin-metadata.js +44 -0
- package/src/builtins/choice/choicelabel.js +11 -0
- package/src/builtins/choice/choicelabels.js +11 -0
- package/src/builtins/choice/choicevalue.js +11 -0
- package/src/builtins/choice/choicevalues.js +11 -0
- package/src/builtins/choice/hasother.js +11 -0
- package/src/builtins/choice/other.js +11 -0
- package/src/builtins/control/eval.js +10 -0
- package/src/builtins/control/setresult.js +11 -0
- package/src/builtins/event/control/off.js +10 -0
- package/src/builtins/event/control/on.js +10 -0
- package/src/builtins/event/field/setvalue.js +10 -0
- package/src/builtins/event/ui/alert.js +10 -0
- package/src/builtins/logical/and.js +11 -0
- package/src/builtins/logical/array.js +11 -0
- package/src/builtins/logical/count.js +11 -0
- package/src/builtins/logical/counta.js +11 -0
- package/src/builtins/logical/countblank.js +12 -3
- package/src/builtins/logical/if.js +11 -0
- package/src/builtins/logical/or.js +11 -0
- package/src/builtins/math/abs.js +11 -0
- package/src/builtins/math/ceiling.js +11 -0
- package/src/builtins/math/cos.js +12 -1
- package/src/builtins/math/round.js +13 -2
- package/src/builtins/math/sin.js +12 -1
- package/src/builtins/registry.js +130 -92
- package/src/builtins/schema/datanames.js +10 -0
- package/src/builtins/schema/form.js +11 -0
- package/src/builtins/string/upper.js +12 -1
- package/src/index.js +6 -0
- package/src/utilities/calculation-editor.js +501 -0
- package/src/utilities/record-transformer.js +32 -11
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -35,3 +35,17 @@ npm install form0-core
|
|
|
35
35
|
## Security
|
|
36
36
|
|
|
37
37
|
See `SECURITY.md` for security modes and configuration.
|
|
38
|
+
|
|
39
|
+
## Requirements
|
|
40
|
+
|
|
41
|
+
- Node.js 18+
|
|
42
|
+
|
|
43
|
+
## Related repositories
|
|
44
|
+
|
|
45
|
+
- [form0-cli](https://github.com/paqu-io/form0-cli) - Command-line interface
|
|
46
|
+
- [form0-react](https://github.com/paqu-io/form0-react) - React components
|
|
47
|
+
- [form0-react-native](https://github.com/paqu-io/form0-react-native) - React Native components
|
|
48
|
+
|
|
49
|
+
## Contributing
|
|
50
|
+
|
|
51
|
+
Contributions are welcome! Please feel free to submit [issues](https://github.com/paqu-io/form0-core/issues) and [pull requests](https://github.com/paqu-io/form0-core/pulls).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "form0-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/"
|
|
6
6
|
},
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"main": "src/index.js",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "node tests/form0-core.test.js && node tests/security-test.js",
|
|
11
|
+
"test": "node tests/form0-core.test.js && node tests/builtin-metadata.test.js && node tests/calculation-editor.test.js && node tests/security-test.js",
|
|
12
12
|
"postinstall": "node scripts/postinstall.cjs",
|
|
13
13
|
"format": "prettier --write \"**/*.{js,jsx,json,ts,tsx,md}\"",
|
|
14
14
|
"format:check": "prettier --check \"**/*.{js,jsx,json,ts,tsx,md}\""
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {'calculation' | 'event'} BuiltinContext
|
|
3
|
+
*
|
|
4
|
+
* @typedef {Object} BuiltinMetadata
|
|
5
|
+
* @property {string} name
|
|
6
|
+
* @property {string} category
|
|
7
|
+
* @property {string} signature
|
|
8
|
+
* @property {string} description
|
|
9
|
+
* @property {string[]} examples
|
|
10
|
+
* @property {BuiltinContext[]} contexts
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export const BUILTIN_CONTEXTS = Object.freeze({
|
|
14
|
+
CALCULATION: 'calculation',
|
|
15
|
+
EVENT: 'event',
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create immutable metadata for a builtin implementation.
|
|
20
|
+
*
|
|
21
|
+
* @param {BuiltinMetadata} definition
|
|
22
|
+
* @returns {BuiltinMetadata}
|
|
23
|
+
*/
|
|
24
|
+
export function defineBuiltinMetadata(definition) {
|
|
25
|
+
return Object.freeze({
|
|
26
|
+
...definition,
|
|
27
|
+
examples: Object.freeze([...(definition.examples ?? [])]),
|
|
28
|
+
contexts: Object.freeze([...(definition.contexts ?? [])]),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Return a mutable clone of builtin metadata for external consumers.
|
|
34
|
+
*
|
|
35
|
+
* @param {BuiltinMetadata} definition
|
|
36
|
+
* @returns {BuiltinMetadata}
|
|
37
|
+
*/
|
|
38
|
+
export function cloneBuiltinMetadata(definition) {
|
|
39
|
+
return {
|
|
40
|
+
...definition,
|
|
41
|
+
examples: [...definition.examples],
|
|
42
|
+
contexts: [...definition.contexts],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin CHOICELABEL
|
|
3
5
|
* @description Retrieves the currently selected choice field label, preserving the type (if the label is a number preserves the number type, otherwise string). If no selection it returns null.
|
|
@@ -10,6 +12,15 @@
|
|
|
10
12
|
* // Use in display logic
|
|
11
13
|
* "You selected: " + CHOICELABEL($city)
|
|
12
14
|
*/
|
|
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
|
+
|
|
13
24
|
export const CHOICELABEL = (choiceField) => {
|
|
14
25
|
// Handle null/undefined input
|
|
15
26
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin CHOICELABELS
|
|
3
5
|
* @description Retrieves an array of all selected choice field labels from a MultiChoiceField, preserving the type of each label. Returns an empty array if no selections.
|
|
@@ -10,6 +12,15 @@
|
|
|
10
12
|
* // Display selected colors
|
|
11
13
|
* "Selected colors: " + CHOICELABELS($colors).join(", ")
|
|
12
14
|
*/
|
|
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
|
+
|
|
13
24
|
export const CHOICELABELS = (multiChoiceField) => {
|
|
14
25
|
// Handle null/undefined input
|
|
15
26
|
if (!multiChoiceField || typeof multiChoiceField !== 'object') {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin CHOICEVALUE
|
|
3
5
|
* @description Retrieves the currently selected choice field value, preserving the type (if the value is a number preserves the number type, otherwise string). If no selection it returns null.
|
|
@@ -10,6 +12,15 @@
|
|
|
10
12
|
* // Use in conditional logic
|
|
11
13
|
* IF(CHOICEVALUE($city) === "bogota", "Welcome to Bogotá!", "Welcome!")
|
|
12
14
|
*/
|
|
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: ['CHOICEVALUE($city)'],
|
|
21
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
+
});
|
|
23
|
+
|
|
13
24
|
export const CHOICEVALUE = (choiceField) => {
|
|
14
25
|
// Handle null/undefined input
|
|
15
26
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin CHOICEVALUES
|
|
3
5
|
* @description Retrieves an array of all selected choice field values from a MultiChoiceField, preserving the type of each value. Returns an empty array if no selections.
|
|
@@ -10,6 +12,15 @@
|
|
|
10
12
|
* // Check if "red" is selected
|
|
11
13
|
* CHOICEVALUES($colors).includes("red")
|
|
12
14
|
*/
|
|
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)'],
|
|
21
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
+
});
|
|
23
|
+
|
|
13
24
|
export const CHOICEVALUES = (multiChoiceField) => {
|
|
14
25
|
// Handle null/undefined input
|
|
15
26
|
if (!multiChoiceField || typeof multiChoiceField !== 'object') {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin HASOTHER
|
|
3
5
|
* @description Returns true if user entered an other option, false otherwise. Works with both SingleChoiceField and MultiChoiceField.
|
|
@@ -13,6 +15,15 @@
|
|
|
13
15
|
* // Use in conditional logic
|
|
14
16
|
* IF(HASOTHER($city), "Custom city: " + OTHER($city), "Selected city: " + CHOICELABEL($city))
|
|
15
17
|
*/
|
|
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
|
+
|
|
16
27
|
export const HASOTHER = (choiceField) => {
|
|
17
28
|
// Handle null/undefined input
|
|
18
29
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin OTHER
|
|
3
5
|
* @description Retrieves the other label if user entered the other option, otherwise null. Works with both SingleChoiceField and MultiChoiceField.
|
|
@@ -13,6 +15,15 @@
|
|
|
13
15
|
* // Use in conditional logic
|
|
14
16
|
* IF(HASOTHER($city), "Custom city: " + OTHER($city), "No custom city entered")
|
|
15
17
|
*/
|
|
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
|
+
|
|
16
27
|
export const OTHER = (choiceField) => {
|
|
17
28
|
// Handle null/undefined input
|
|
18
29
|
if (!choiceField || typeof choiceField !== 'object') {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { validateExpression } from '../../security/validation.js';
|
|
2
2
|
import { SAFE_SECURITY_CONFIG } from '../../security/config.js';
|
|
3
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @builtin EVAL
|
|
@@ -16,6 +17,15 @@ import { SAFE_SECURITY_CONFIG } from '../../security/config.js';
|
|
|
16
17
|
* // Computed field references
|
|
17
18
|
* EVAL('$city' + '_suffix')
|
|
18
19
|
*/
|
|
20
|
+
export const EVAL_METADATA = defineBuiltinMetadata({
|
|
21
|
+
name: 'EVAL',
|
|
22
|
+
category: 'control',
|
|
23
|
+
signature: 'EVAL(expression)',
|
|
24
|
+
description: 'Evaluate a dynamically built expression string in a restricted context.',
|
|
25
|
+
examples: ['EVAL("$" + dynamicFieldName)'],
|
|
26
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
27
|
+
});
|
|
28
|
+
|
|
19
29
|
// Global context for EVAL() - set during expression evaluation
|
|
20
30
|
let _evalContext = null;
|
|
21
31
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
// Global state for result management
|
|
2
4
|
let _resultSet = false;
|
|
3
5
|
let _resultValue;
|
|
@@ -14,6 +16,15 @@ let _resultValue;
|
|
|
14
16
|
* // Set a conditional result
|
|
15
17
|
* SETRESULT(IF(isEligible, discount, 0))
|
|
16
18
|
*/
|
|
19
|
+
export const SETRESULT_METADATA = defineBuiltinMetadata({
|
|
20
|
+
name: 'SETRESULT',
|
|
21
|
+
category: 'control',
|
|
22
|
+
signature: 'SETRESULT(value)',
|
|
23
|
+
description: 'Set the final return value for multiline calculations.',
|
|
24
|
+
examples: ['SETRESULT($price * $quantity)'],
|
|
25
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
26
|
+
});
|
|
27
|
+
|
|
17
28
|
export const SETRESULT = (value) => {
|
|
18
29
|
_resultSet = true;
|
|
19
30
|
_resultValue = value;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __collectEventOperation } from '../event-operations-collector.js';
|
|
2
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../../builtin-metadata.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* OFF builtin for form events
|
|
@@ -26,6 +27,15 @@ import { __collectEventOperation } from '../event-operations-collector.js';
|
|
|
26
27
|
* // Remove all field event handlers
|
|
27
28
|
* OFF('change', 'city');
|
|
28
29
|
*/
|
|
30
|
+
export const OFF_METADATA = defineBuiltinMetadata({
|
|
31
|
+
name: 'OFF',
|
|
32
|
+
category: 'event',
|
|
33
|
+
signature: 'OFF(eventType, fieldKeyOrCallback, callback)',
|
|
34
|
+
description: 'Remove event handlers within form event code.',
|
|
35
|
+
examples: ["OFF('change', 'city', specificCallback)"],
|
|
36
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
37
|
+
});
|
|
38
|
+
|
|
29
39
|
export function OFF(eventType, fieldKeyOrCallback, callback) {
|
|
30
40
|
let fieldKey = '*';
|
|
31
41
|
let actualCallback = undefined;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __collectEventOperation } from '../event-operations-collector.js';
|
|
2
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../../builtin-metadata.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* ON builtin for form events
|
|
@@ -18,6 +19,15 @@ import { __collectEventOperation } from '../event-operations-collector.js';
|
|
|
18
19
|
* // Register field-specific event handler
|
|
19
20
|
* ON('change', 'city', function(event) { ALERT('City changed!'); });
|
|
20
21
|
*/
|
|
22
|
+
export const ON_METADATA = defineBuiltinMetadata({
|
|
23
|
+
name: 'ON',
|
|
24
|
+
category: 'event',
|
|
25
|
+
signature: 'ON(eventType, fieldKeyOrCallback, callback)',
|
|
26
|
+
description: 'Register an event handler within form event code.',
|
|
27
|
+
examples: ["ON('change', 'city', function(event) { ALERT('City changed!'); })"],
|
|
28
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
29
|
+
});
|
|
30
|
+
|
|
21
31
|
export function ON(eventType, fieldKeyOrCallback, callback) {
|
|
22
32
|
let fieldKey = '*';
|
|
23
33
|
let actualCallback = fieldKeyOrCallback;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __collectEventOperation } from '../event-operations-collector.js';
|
|
2
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../../builtin-metadata.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* SETVALUE builtin for form events
|
|
@@ -30,6 +31,15 @@ import { __collectEventOperation } from '../event-operations-collector.js';
|
|
|
30
31
|
* // Set MultiChoiceField value
|
|
31
32
|
* SETVALUE('field_dataname', ['value1', 'value2', 'value3'])
|
|
32
33
|
*/
|
|
34
|
+
export const SETVALUE_METADATA = defineBuiltinMetadata({
|
|
35
|
+
name: 'SETVALUE',
|
|
36
|
+
category: 'event',
|
|
37
|
+
signature: 'SETVALUE(fieldDataName, valueToSet)',
|
|
38
|
+
description: 'Set a field value from a form event handler.',
|
|
39
|
+
examples: ["SETVALUE('field_dataname', 'value_to_set')"],
|
|
40
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
41
|
+
});
|
|
42
|
+
|
|
33
43
|
export function SETVALUE(fieldDataName, valueToSet) {
|
|
34
44
|
// Create operation descriptor
|
|
35
45
|
const operation = {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { __collectEventOperation } from '../event-operations-collector.js';
|
|
2
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../../builtin-metadata.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* ALERT builtin for form events
|
|
@@ -7,6 +8,15 @@ import { __collectEventOperation } from '../event-operations-collector.js';
|
|
|
7
8
|
* @param {string} message - The message to display in the alert (optional)
|
|
8
9
|
* @returns {Object} Operation descriptor for platform execution (for backward compatibility)
|
|
9
10
|
*/
|
|
11
|
+
export const ALERT_METADATA = defineBuiltinMetadata({
|
|
12
|
+
name: 'ALERT',
|
|
13
|
+
category: 'event',
|
|
14
|
+
signature: "ALERT(title, message = '')",
|
|
15
|
+
description: 'Display an alert from a form event handler.',
|
|
16
|
+
examples: ["ALERT('Saved', 'The record was saved successfully.')"],
|
|
17
|
+
contexts: [BUILTIN_CONTEXTS.EVENT],
|
|
18
|
+
});
|
|
19
|
+
|
|
10
20
|
export function ALERT(title, message = '') {
|
|
11
21
|
// Create operation descriptor
|
|
12
22
|
const operation = {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin AND
|
|
3
5
|
* @description Logical AND operation that returns true only if all arguments are truthy
|
|
@@ -10,4 +12,13 @@
|
|
|
10
12
|
* // Multiple conditions
|
|
11
13
|
* AND(name.length > 0, email.includes("@"), age >= 13)
|
|
12
14
|
*/
|
|
15
|
+
export const AND_METADATA = defineBuiltinMetadata({
|
|
16
|
+
name: 'AND',
|
|
17
|
+
category: 'logical',
|
|
18
|
+
signature: 'AND(...conditions)',
|
|
19
|
+
description: 'Return true only when all arguments are truthy.',
|
|
20
|
+
examples: ['AND($age >= 18, $country === "it")'],
|
|
21
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
22
|
+
});
|
|
23
|
+
|
|
13
24
|
export const AND = (...args) => args.every(Boolean);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin ARRAY
|
|
3
5
|
* @description Returns an array from its arguments. Can parse string representations of arrays (with single or double quotes) and comma-separated values.
|
|
@@ -28,6 +30,15 @@
|
|
|
28
30
|
* // For complex parsing, JSON.parse() can be used:
|
|
29
31
|
* // EVAL('JSON.parse("[1,2,3]")')
|
|
30
32
|
*/
|
|
33
|
+
export const ARRAY_METADATA = defineBuiltinMetadata({
|
|
34
|
+
name: 'ARRAY',
|
|
35
|
+
category: 'logical',
|
|
36
|
+
signature: 'ARRAY(...values)',
|
|
37
|
+
description: 'Build an array from individual arguments.',
|
|
38
|
+
examples: ['ARRAY($city, $country)'],
|
|
39
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
40
|
+
});
|
|
41
|
+
|
|
31
42
|
export const ARRAY = (...args) => {
|
|
32
43
|
// Handle empty arguments
|
|
33
44
|
if (args.length === 0) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin COUNT
|
|
3
5
|
* @description Returns a count of the number of numeric values in a dataset
|
|
@@ -13,6 +15,15 @@
|
|
|
13
15
|
* // Returns 3 (mixed array)
|
|
14
16
|
* COUNT([1, 'a', 2, null, 3])
|
|
15
17
|
*/
|
|
18
|
+
export const COUNT_METADATA = defineBuiltinMetadata({
|
|
19
|
+
name: 'COUNT',
|
|
20
|
+
category: 'logical',
|
|
21
|
+
signature: 'COUNT(values)',
|
|
22
|
+
description: 'Count numeric values.',
|
|
23
|
+
examples: ['COUNT(ARRAY($score_1, $score_2, $score_3))'],
|
|
24
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
+
});
|
|
26
|
+
|
|
16
27
|
export const COUNT = (values) => {
|
|
17
28
|
// Handle null/undefined input
|
|
18
29
|
if (!Array.isArray(values)) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin COUNTA
|
|
3
5
|
* @description Returns a count of values in a dataset
|
|
@@ -13,6 +15,15 @@
|
|
|
13
15
|
* // Returns 4 (counts all items including null)
|
|
14
16
|
* COUNTA([1, 'a', null, true])
|
|
15
17
|
*/
|
|
18
|
+
export const COUNTA_METADATA = defineBuiltinMetadata({
|
|
19
|
+
name: 'COUNTA',
|
|
20
|
+
category: 'logical',
|
|
21
|
+
signature: 'COUNTA(values)',
|
|
22
|
+
description: 'Count non-empty values.',
|
|
23
|
+
examples: ['COUNTA(ARRAY($first_name, $last_name, $email))'],
|
|
24
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
25
|
+
});
|
|
26
|
+
|
|
16
27
|
export const COUNTA = (values) => {
|
|
17
28
|
// Handle null/undefined input
|
|
18
29
|
if (!Array.isArray(values)) {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin COUNTBLANK
|
|
3
5
|
* @description Returns the number of blank values in a dataset
|
|
@@ -13,6 +15,15 @@
|
|
|
13
15
|
* // Returns 0 (no blank values)
|
|
14
16
|
* COUNTBLANK([1, 2, 'a', true])
|
|
15
17
|
*/
|
|
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
|
+
|
|
16
27
|
export const COUNTBLANK = (values) => {
|
|
17
28
|
// Handle null/undefined input
|
|
18
29
|
if (!Array.isArray(values)) {
|
|
@@ -20,7 +31,5 @@ export const COUNTBLANK = (values) => {
|
|
|
20
31
|
}
|
|
21
32
|
|
|
22
33
|
// Count null, undefined, and empty strings as blank
|
|
23
|
-
return values.filter(
|
|
24
|
-
(value) => value === null || value === undefined || value === ''
|
|
25
|
-
).length;
|
|
34
|
+
return values.filter((value) => value === null || value === undefined || value === '').length;
|
|
26
35
|
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin IF
|
|
3
5
|
* @description Conditional logic function that returns one of two values based on a condition
|
|
@@ -12,4 +14,13 @@
|
|
|
12
14
|
* // Nested conditions for shipping cost
|
|
13
15
|
* IF(country === "US", IF(state === "CA", 8.99, 5.99), 15.99)
|
|
14
16
|
*/
|
|
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: ['IF($age >= 18, "adult", "minor")'],
|
|
23
|
+
contexts: [BUILTIN_CONTEXTS.CALCULATION, BUILTIN_CONTEXTS.EVENT],
|
|
24
|
+
});
|
|
25
|
+
|
|
15
26
|
export const IF = (cond, a, b) => (cond ? a : b);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin OR
|
|
3
5
|
* @description Logical OR operation that returns true if at least one argument is truthy
|
|
@@ -10,4 +12,13 @@
|
|
|
10
12
|
* // Validation - either email or phone is required
|
|
11
13
|
* OR(email.length > 0, phone.length > 0)
|
|
12
14
|
*/
|
|
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
|
+
|
|
13
24
|
export const OR = (...args) => args.some(Boolean);
|
package/src/builtins/math/abs.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin ABS
|
|
3
5
|
* @description Returns the absolute value of a number
|
|
@@ -13,4 +15,13 @@
|
|
|
13
15
|
* // Returns 3.14
|
|
14
16
|
* ABS(-3.14)
|
|
15
17
|
*/
|
|
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
|
+
|
|
16
27
|
export const ABS = (value) => Math.abs(value);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin CEILING
|
|
3
5
|
* @description Rounds a value up to the nearest integer multiple of factor
|
|
@@ -14,6 +16,15 @@
|
|
|
14
16
|
* // Returns 15
|
|
15
17
|
* CEILING(12.3, 5)
|
|
16
18
|
*/
|
|
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
|
+
|
|
17
28
|
export const CEILING = (value, factor = 1) => {
|
|
18
29
|
if (factor === 0) {
|
|
19
30
|
throw new Error('CEILING factor cannot be zero');
|
package/src/builtins/math/cos.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin COS
|
|
3
5
|
* @description Returns the cosine of a value, in radians
|
|
@@ -13,4 +15,13 @@
|
|
|
13
15
|
* // Returns -1
|
|
14
16
|
* COS(Math.PI)
|
|
15
17
|
*/
|
|
16
|
-
export const
|
|
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
|
+
export const COS = (value) => Math.cos(value);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin ROUND
|
|
3
5
|
* @description Rounds a number to a specified number of decimal places according to standard rounding rules
|
|
@@ -17,11 +19,20 @@
|
|
|
17
19
|
* // Returns 3.14
|
|
18
20
|
* ROUND(3.14159, 2)
|
|
19
21
|
*/
|
|
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
|
+
|
|
20
31
|
export const ROUND = (value, places) => {
|
|
21
32
|
if (typeof value !== 'number' || typeof places !== 'number') {
|
|
22
33
|
throw new Error('ROUND requires two numeric arguments');
|
|
23
34
|
}
|
|
24
|
-
|
|
35
|
+
|
|
25
36
|
const multiplier = Math.pow(10, places);
|
|
26
37
|
return Math.round(value * multiplier) / multiplier;
|
|
27
|
-
};
|
|
38
|
+
};
|
package/src/builtins/math/sin.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { BUILTIN_CONTEXTS, defineBuiltinMetadata } from '../builtin-metadata.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @builtin SIN
|
|
3
5
|
* @description Returns the sine of a value, in radians
|
|
@@ -13,4 +15,13 @@
|
|
|
13
15
|
* // Returns 1
|
|
14
16
|
* SIN(Math.PI / 2)
|
|
15
17
|
*/
|
|
16
|
-
export const
|
|
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
|
+
export const SIN = (value) => Math.sin(value);
|