diff --git a/README.md b/README.md index 48600ec..6800298 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,15 @@ npm install @camunda/feel-builtins ## Usage +This package exports multiple collections of FEEL builtins: + +* **`camundaBuiltins`**: Collection of builtins of camunda scala FEEL. +* **`feelBuiltins`**: List of standard FEEL built-in functions (excluding Camunda-specific extensions). +* **`camundaExtensions`**: List of FEEL camunda extensions. +* **`camundaReservedNameBuiltins`**: Functions using reserved keywords in their name and need to be added to the parser context during parsing. + +You can feed built-ins as context into your favorite [FEEL editor](#feel-editor) or [validator](#feel-lint). + ### Feel Editor In your [FEEL editor](https://github.com/bpmn-io/feel-editor) you can use these builtins to establish the Camunda context: @@ -29,6 +38,17 @@ const editor = new FeelEditor({ }); ``` +If you only want standard FEEL functions, use `feelBuiltins` instead: + +```js +import { feelBuiltins } from '@camunda/feel-builtins'; + +const editor = new FeelEditor({ + container, + builtins: feelBuiltins +}); +``` + ### Feel Lint With [@bpmn-io/feel-lint](https://github.com/bpmn-io/feel-lint) you can also use the builtins to lint expressions in the Camunda world: diff --git a/dist/index.d.ts b/dist/index.d.ts index b2de2e7..37eef58 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,7 +1,24 @@ /** - * A collection of builtin of FEEL. + * Collection of builtins of camunda scala FEEL. */ export const camundaBuiltins: Builtin[]; + +/** + * List of standard FEEL built-in functions (excluding Camunda-specific extensions). + */ +export const feelBuiltins: Builtin[]; + +/** + * List of FEEL camunda extensions. + */ +export const camundaExtensions: Builtin[]; + +/** + * Camunda built-ins that use reserved keywords in their name and thus must + * be explicitly declared when parsing FEEL. + */ +export const camundaReservedNameBuiltins: Builtin[]; + export type Builtin = { /** * The name of the builtin function. @@ -12,11 +29,11 @@ export type Builtin = { */ info: string; /** - * type of the builtin, always 'function' for builtin functions. + * Type of the builtin, always 'function' for builtin functions. */ - type?: "function"; + type?: 'function'; /** - * function parameters. + * Function parameters. */ params?: Array<{ name: string; diff --git a/package-lock.json b/package-lock.json index 74c775f..ed51392 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.3.0", "license": "MIT", "devDependencies": { + "@bpmn-io/lezer-feel": "^2.2.1", "@types/chai": "^5.2.2", "@types/mocha": "^10.0.10", "@types/node": "^24.0.0", @@ -23,6 +24,21 @@ "typescript": "^5.8.3" } }, + "node_modules/@bpmn-io/lezer-feel": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@bpmn-io/lezer-feel/-/lezer-feel-2.2.1.tgz", + "integrity": "sha512-X/8DIoTIW+F9dI2Pr2M66tGT8q83ZXLAwVYqHVUWw9STXbbCs7Aluy6CxW8EpXD1rur15pbP1ZkXQDIFkx55rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/highlight": "^1.2.3", + "@lezer/lr": "^1.4.7", + "min-dash": "^5.0.0" + }, + "engines": { + "node": ">= 20.12.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -297,6 +313,33 @@ "node": ">=12" } }, + "node_modules/@lezer/common": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz", + "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.8.tgz", + "integrity": "sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -3009,6 +3052,13 @@ "node": ">= 0.10.0" } }, + "node_modules/min-dash": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/min-dash/-/min-dash-5.0.0.tgz", + "integrity": "sha512-EGuoBnVL7/Fnv2sqakpX5WGmZehZ3YMmLayT7sM8E9DRU74kkeyMg4Rik1lsOkR2GbFNeBca4/L+UfU6gF0Edw==", + "dev": true, + "license": "MIT" + }, "node_modules/minimatch": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", diff --git a/package.json b/package.json index 25c18dd..ef2f1a6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "compile-builtins": "node tasks/compileBuiltins.js" }, "devDependencies": { + "@bpmn-io/lezer-feel": "^2.2.1", "@types/chai": "^5.2.2", "@types/mocha": "^10.0.10", "@types/node": "^24.0.0", diff --git a/src/camundaBuiltins.js b/src/camundaBuiltins.js index aa16597..304d8fd 100644 --- a/src/camundaBuiltins.js +++ b/src/camundaBuiltins.js @@ -18,11 +18,11 @@ */ /** - * FEEL built-ins available with Camunda / feel-scala. + * List of standard FEEL built-in functions (excluding Camunda-specific extensions). * * @type { Builtin[] } */ -export const camundaBuiltins = [ +export const feelBuiltins = [ { "name": "not", "type": "function", @@ -33,58 +33,6 @@ export const camundaBuiltins = [ ], "info": "
Returns the logical negation of the given value.
\nFunction signature
\nnot(negand: boolean): boolean\n\nExamples
\nnot(true)\n// false\n\nnot(null)\n// null\n\n"
},
- {
- "name": "is defined",
- "type": "function",
- "params": [
- {
- "name": "value"
- }
- ],
- "info": "Camunda Extension
\nChecks if a given value is not null. If the value is null then the function returns false.\nOtherwise, the function returns true.
Function signature
\nis defined(value: Any): boolean\n\nExamples
\nis defined(1)\n// true\n\nis defined(null)\n// false\n\nis defined(x)\n// false - if no variable "x" exists\n\nis defined(x.y)\n// false - if no variable "x" exists or it doesn't have a property "y"\n\n:::caution Breaking change
\nThis function worked differently in previous versions. It returned true if the value was null.\nSince this version, the function returns false if the value is null.
:::
\n" - }, - { - "name": "get or else", - "type": "function", - "params": [ - { - "name": "value" - }, - { - "name": "default" - } - ], - "info": "Camunda Extension
\nReturn the provided value parameter if not null, otherwise return the default parameter
Function signature
\nget or else(value: Any, default: Any): Any\n\nExamples
\nget or else("this", "default")\n// "this"\n\nget or else(null, "default")\n// "default"\n\nget or else(null, null)\n// null\n\n"
- },
- {
- "name": "assert",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "condition"
- }
- ],
- "info": "Camunda Extension
\nVerify that the given condition is met. If the condition is true, the function returns the value.\nOtherwise, the evaluation fails with an error.
Function signature
\nassert(value: Any, condition: Any)\n\nExamples
\nassert(x, x != null)\n// "value" - if x is "value"\n// error - if x is null or doesn't exist\n\nassert(x, x >= 0)\n// 4 - if x is 4\n// error - if x is less than zero\n\n"
- },
- {
- "name": "assert",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "condition"
- },
- {
- "name": "cause"
- }
- ],
- "info": "Camunda Extension
\nVerify that the given condition is met. If the condition is true, the function returns the value.\nOtherwise, the evaluation fails with an error containing the given message.
Function signature
\nassert(value: Any, condition: Any, cause: String)\n\nExamples
\nassert(x, x != null, "'x' should not be null")\n// "value" - if x is "value"\n// error('x' should not be null) - if x is null or doesn't exist\n\nassert(x, x >= 0, "'x' should be positive")\n// 4 - if x is 4\n// error('x' should be positive) - if x is less than zero\n\n"
- },
{
"name": "get value",
"type": "function",
@@ -98,19 +46,6 @@ export const camundaBuiltins = [
],
"info": "Returns the value of the context entry with the given key.
\nFunction signature
\nget value(context: context, key: string): Any\n\nExamples
\nget value({foo: 123}, "foo")\n// 123\n\nget value({a: 1}, "b")\n// null\n\n"
},
- {
- "name": "get value",
- "type": "function",
- "params": [
- {
- "name": "context"
- },
- {
- "name": "keys"
- }
- ],
- "info": "Camunda Extension
\nReturns the value of the context entry for a context path defined by the given keys.
\nIf keys contains the keys [k1, k2] then it returns the value at the nested entry k1.k2 of the context.
If keys are empty or the nested entry defined by the keys doesn't exist in the context, it returns null.
Function signature
\nget value(context: context, keys: list<string>): Any\n\nExamples
\nget value({x:1, y: {z:0}}, ["y", "z"])\n// 0\n\nget value({x: {y: {z:0}}}, ["x", "y"])\n// {z:0}\n\nget value({a: {b: 3}}, ["b"])\n// null\n\n"
- },
{
"name": "get entries",
"type": "function",
@@ -121,22 +56,6 @@ export const camundaBuiltins = [
],
"info": "Returns the entries of the context as a list of key-value-pairs.
\nFunction signature
\nget entries(context: context): list<context>\n\nThe return value is a list of contexts. Each context contains two entries for "key" and "value".
\nExamples
\nget entries({foo: 123})\n// [{key: "foo", value: 123}]\n\n"
},
- {
- "name": "context put",
- "type": "function",
- "params": [
- {
- "name": "context"
- },
- {
- "name": "key"
- },
- {
- "name": "value"
- }
- ],
- "info": "Adds a new entry with the given key and value to the context. Returns a new context that includes the entry.
\nIf an entry for the same key already exists in the context, it overrides the value.
\nFunction signature
\ncontext put(context: context, key: string, value: Any): context\n\nExamples
\ncontext put({x:1}, "y", 2)\n// {x:1, y:2}\n\n:::info\nThe function context put() replaced the previous function put() (Camunda Extension). The\nprevious function is deprecated and should not be used anymore.\n:::
Adds a new entry with the given value to the context. The path of the entry is defined by the keys. Returns a new context that includes the entry.
\nIf keys contains the keys [k1, k2] then it adds the nested entry k1.k2 = value to the context.
If an entry for the same keys already exists in the context, it overrides the value.
\nIf keys are empty, it returns null.
Function signature
\ncontext put(context: context, keys: list<string>, value: Any): context\n\nExamples
\ncontext put({x:1}, ["y"], 2)\n// {x:1, y:2}\n\ncontext put({x:1, y: {z:0}}, ["y", "z"], 2)\n// {x:1, y: {z:2}}\n\ncontext put({x:1}, ["y", "z"], 2)\n// {x:1, y: {z:2}}\n\n"
},
- {
- "name": "context merge",
- "type": "function",
- "params": [
- {
- "name": "contexts"
- }
- ],
- "info": "Union the given contexts. Returns a new context that includes all entries of the given contexts.
\nIf an entry for the same key already exists in a context, it overrides the value. The entries are overridden in the same order as in the list of contexts.
\nFunction signature
\ncontext merge(contexts: list<context>): context\n\nExamples
\ncontext merge([{x:1}, {y:2}])\n// {x:1, y:2}\n\ncontext merge([{x:1, y: 0}, {y:2}])\n// {x:1, y:2}\n\n:::info\nThe function context merge() replaced the previous function put all() (Camunda Extension). The\nprevious function is deprecated and should not be used anymore.\n:::
Returns a date and time from the given components.
\nFunction signature
\ndate and time(date: date, time: time): date and time\n\ndate and time(date: date and time, time: time): date and time\n\nReturns a date and time value that consists of the date component of date combined with time.
Examples
\ndate and time(date("2012-12-24"),time("T23:59:00"))\n// date and time("2012-12-24T23:59:00")\n\ndate and time(date and time("2012-12-25T11:00:00"),time("T23:59:00"))\n// date and time("2012-12-25T23:59:00")\n\n"
},
- {
- "name": "date and time",
- "type": "function",
- "params": [
- {
- "name": "date"
- },
- {
- "name": "timezone"
- }
- ],
- "info": "Camunda Extension
\nReturns the given date and time value at the given timezone.
\nIf date has a different timezone than timezone then it adjusts the time to match the local time of timezone.
Function signature
\ndate and time(date: date and time, timezone: string): date and time\n\nExamples
\ndate and time(@"2020-07-31T14:27:30@Europe/Berlin", "America/Los_Angeles")\n// date and time("2020-07-31T05:27:30@America/Los_Angeles")\n\ndate and time(@"2020-07-31T14:27:30", "Z")\n// date and time("2020-07-31T12:27:30Z")\n\n"
- },
{
"name": "duration",
"type": "function",
@@ -619,16 +515,6 @@ export const camundaBuiltins = [
],
"info": "Returns the given list without duplicates.
\nFunction signature
\ndistinct values(list: list): list\n\nExamples
\ndistinct values([1,2,3,2,1])\n// [1,2,3]\n\n"
},
- {
- "name": "duplicate values",
- "type": "function",
- "params": [
- {
- "name": "list"
- }
- ],
- "info": "Camunda Extension
\nReturns all duplicate values of the given list.
\nFunction signature
\nduplicate values(list: list): list\n\nExamples
\nduplicate values([1,2,3,2,1])\n// [1,2]\n\n"
- },
{
"name": "flatten",
"type": "function",
@@ -675,128 +561,6 @@ export const camundaBuiltins = [
],
"info": "Joins a list of strings into a single string. This is similar to\nJava's joining\nfunction.
\nIf an item of the list is null, the item is ignored for the result string. If an item is\nneither a string nor null, the function returns null instead of a string.
The resulting string contains a delimiter between each element.
Function signature
\nstring join(list: list<string>, delimiter: string): string\n\nExamples
\nstring join(["a"], "X")\n// "a"\n\nstring join(["a","b","c"], ", ")\n// "a, b, c"\n\n"
},
- {
- "name": "string join",
- "type": "function",
- "params": [
- {
- "name": "list"
- },
- {
- "name": "delimiter"
- },
- {
- "name": "prefix"
- },
- {
- "name": "suffix"
- }
- ],
- "info": "Camunda Extension
\nJoins a list of strings into a single string. This is similar to\nJava's joining\nfunction.
\nIf an item of the list is null, the item is ignored for the result string. If an item is\nneither a string nor null, the function returns null instead of a string.
The resulting string starts with prefix, contains a delimiter between each element, and ends\nwith suffix.
Function signature
\nstring join(list: list<string>, delimiter: string, prefix: string, suffix: string): string\n\nExamples
\nstring join(["a","b","c"], ", ", "[", "]")\n// "[a, b, c]"\n\n"
- },
- {
- "name": "is empty",
- "type": "function",
- "params": [
- {
- "name": "list"
- }
- ],
- "info": "Camunda Extension
\nReturns true if the given list is empty. Otherwise, returns false.
Function signature
\nis empty(list: list): boolean\n\nExamples
\nis empty([])\n// true\n\nis empty([1,2,3])\n// false\n\n"
- },
- {
- "name": "partition",
- "type": "function",
- "params": [
- {
- "name": "list"
- },
- {
- "name": "size"
- }
- ],
- "info": "Camunda Extension
\nReturns consecutive sublists of a list, each of the same size (the final list may be smaller).
\nIf size is less than 0, it returns null.
Function signature
\npartition(list: list, size: number): list\n\nExamples
\npartition([1,2,3,4,5], 2)\n// [[1,2], [3,4], [5]]\n\npartition([], 2)\n// []\n\npartition([1,2], 0)\n// null\n\n"
- },
- {
- "name": "fromAi",
- "type": "function",
- "params": [
- {
- "name": "value"
- }
- ],
- "info": "Camunda Extension
\nReturns the unmodified value parameter.
The main use case of this function is for tool definitions used by the AI Agent connector.
\nSee the following function overloads for additional function parameters.
\nFunction signature
\nfromAi(value: Any): Any\n\nExamples
\nfromAi(toolCall.searchQuery)\n// toolCall.searchQuery contents\n\nfromAi(toolCall.userId)\n// toolCall.userId contents\n\n"
- },
- {
- "name": "fromAi",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "description"
- }
- ],
- "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional description parameter to provide a textual description of the value. The description must be null or a string constant.
Function signature
\nfromAi(value: Any, description: string): Any\n\nExamples
\nfromAi(toolCall.searchQuery, "The search query used to find the best match.")\n// toolCall.searchQuery contents\n\nfromAi(toolCall.searchQuery, null)\n// toolCall.searchQuery contents\n\n"
- },
- {
- "name": "fromAi",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "description"
- },
- {
- "name": "type"
- }
- ],
- "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional type parameter to provide type information about the value. The type must be null or a string constant.
Function signature
\nfromAi(value: Any, description: string, type: string): Any\n\nExamples
\nfromAi(toolCall.searchQuery, "The search query used to find the best match.", "string")\n// toolCall.searchQuery contents\n\nfromAi(toolCall.userId, "The user's ID", "number")\n// toolCall.userId contents\n\nfromAi(toolCall.userId, null, "number")\n// toolCall.userId contents\n\nfromAi(value: toolCall.userId, type: "number")\n// toolCall.userId contents\n\n"
- },
- {
- "name": "fromAi",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "description"
- },
- {
- "name": "type"
- },
- {
- "name": "schema"
- }
- ],
- "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional schema parameter to provide a (partial) JSON schema for the value.
null or a context (map) containing only constant values. For example, function calls within the schema are not supported.type and a schema, and it depends on the integration as to which value takes precedence. The AI Agent connector will override any type specified in the schema if the type parameter is also provided.Function signature
\nfromAi(value: Any, description: string, type: string, schema: context): Any\n\nExamples
\nfromAi(toolCall.documentType, "The document type to provide", "string", {\n enum: ["invoice", "receipt", "contract"]\n})\n// toolCall.documentType contents\n\nfromAi(value: toolCall.documentType, description: "The document type to provide", schema: {\n type: "string",\n enum: ["invoice", "receipt", "contract"]\n})\n// toolCall.documentType contents\n\nfromAi(toolCall.tags, "Tags to apply to the blog post", "array", {\n items: {\n type: "string"\n }\n})\n// toolCall.tags contents\n\n"
- },
- {
- "name": "fromAi",
- "type": "function",
- "params": [
- {
- "name": "value"
- },
- {
- "name": "description"
- },
- {
- "name": "type"
- },
- {
- "name": "schema"
- },
- {
- "name": "options"
- }
- ],
- "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional options parameter to provide additional options for the integration handling the value definition.
null or a context (map) containing only constant values. For example, function calls within options are not supported.Function signature
\nfromAi(value: Any, description: string, type: string, schema: context, options: context): Any\n\nExamples
\nfromAi(toolCall.documentType, "The document type to provide", "string", null, {\n required: false\n})\n// toolCall.documentType contents\n\nfromAi(value: toolCall.documentType, options: {\n required: false\n})\n// toolCall.documentType contents\n\n"
- },
{
"name": "decimal",
"type": "function",
@@ -981,12 +745,6 @@ export const camundaBuiltins = [
],
"info": "Returns true if the given is even. Otherwise, returns false.
Function signature
\neven(number: number): boolean\n\nExamples
\neven(5)\n// false\n\neven(2)\n// true\n\n"
},
- {
- "name": "random number",
- "type": "function",
- "params": [],
- "info": "Camunda Extension
\nReturns a random number between 0 and 1.
Function signature
\nrandom number(): number\n\nExamples
\nrandom number()\n// 0.9701618132579795\n\n"
- },
{
"name": "before",
"type": "function",
@@ -1540,115 +1298,365 @@ export const camundaBuiltins = [
"info": "Splits the given value into a list of substrings, breaking at each occurrence of the delimiter pattern.
Function signature
\nsplit(string: string, delimiter: string): list<string>\n\nThe delimiter is a string that contains a regular expression.
Examples
\nsplit("John Doe", "\\s" )\n// ["John", "Doe"]\n\nsplit("a;b;c;;", ";")\n// ["a", "b", "c", "", ""]\n\n"
},
{
- "name": "extract",
+ "name": "now",
"type": "function",
- "params": [
- {
- "name": "string"
- },
- {
- "name": "pattern"
- }
- ],
- "info": "Camunda Extension
\nReturns all matches of the pattern in the given string. Returns an empty list if the pattern doesn't\nmatch.
\nFunction signature
\nextract(string: string, pattern: string): list<string>\n\nThe pattern is a string that contains a regular expression.
Examples
\nextract("references are 1234, 1256, 1378", "12[0-9]*")\n// ["1234","1256"]\n\n"
+ "params": [],
+ "info": "Returns the current date and time including the timezone.
\nFunction signature
\nnow(): date and time\n\nExamples
\nnow()\n// date and time("2020-07-31T14:27:30@Europe/Berlin")\n\n"
},
{
- "name": "trim",
+ "name": "today",
+ "type": "function",
+ "params": [],
+ "info": "Returns the current date.
\nFunction signature
\ntoday(): date\n\nExamples
\ntoday()\n// date("2020-07-31")\n\n"
+ },
+ {
+ "name": "day of week",
"type": "function",
"params": [
{
- "name": "string"
+ "name": "date"
}
],
- "info": "Camunda Extension
\nReturns the given string without leading and trailing spaces.
\nFunction signature
\ntrim(string: string): string\n\nExamples
\ntrim(" hello world ")\n// "hello world"\n\ntrim("hello world ")\n// "hello world"\n\n"
+ "info": "Returns the day of the week according to the Gregorian calendar. Note that it always returns the English name of the day.
\nFunction signature
\nday of week(date: date): string\n\nday of week(date: date and time): string\n\nExamples
\nday of week(date("2019-09-17"))\n// "Tuesday"\n\nday of week(date and time("2019-09-17T12:00:00"))\n// "Tuesday"\n\n"
},
{
- "name": "uuid",
+ "name": "day of year",
"type": "function",
- "params": [],
- "info": "Camunda Extension
\nReturns a UUID (Universally Unique Identifier) with 36 characters.
\nFunction signature
\nuuid(): string\n\nExamples
\nuuid()\n// "7793aab1-d761-4d38-916b-b7270e309894"\n\n"
+ "params": [
+ {
+ "name": "date"
+ }
+ ],
+ "info": "Returns the Gregorian number of the day within the year.
\nFunction signature
\nday of year(date: date): number\n\nday of year(date: date and time): number\n\nExamples
\nday of year(date("2019-09-17"))\n// 260\n\nday of year(date and time("2019-09-17T12:00:00"))\n// 260\n\n"
},
{
- "name": "to base64",
+ "name": "week of year",
+ "type": "function",
+ "params": [
+ {
+ "name": "date"
+ }
+ ],
+ "info": "Returns the Gregorian number of the week within the year, according to ISO 8601.
\nFunction signature
\nweek of year(date: date): number\n\nweek of year(date: date and time): number\n\nExamples
\nweek of year(date("2019-09-17"))\n// 38\n\nweek of year(date and time("2019-09-17T12:00:00"))\n// 38\n\n"
+ },
+ {
+ "name": "month of year",
+ "type": "function",
+ "params": [
+ {
+ "name": "date"
+ }
+ ],
+ "info": "Returns the month of the year according to the Gregorian calendar. Note that it always returns the English name of the month.
\nFunction signature
\nmonth of year(date: date): string\n\nmonth of year(date: date and time): string\n\nExamples
\nmonth of year(date("2019-09-17"))\n// "September"\n\nmonth of year(date and time("2019-09-17T12:00:00"))\n// "September"\n\n"
+ },
+ {
+ "name": "abs",
+ "type": "function",
+ "params": [
+ {
+ "name": "n"
+ }
+ ],
+ "info": "Returns the absolute value of a given duration.
\nFunction signature
\nabs(n: days and time duration): days and time duration\n\nabs(n: years and months duration): years and months duration\n\nExamples
\nabs(duration("-PT5H"))\n// "duration("PT5H")"\n\nabs(duration("PT5H"))\n// "duration("PT5H")"\n\nabs(duration("-P2M"))\n// duration("P2M")\n\n"
+ }
+];
+
+/**
+ * List of FEEL camunda extensions.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaExtensions = [
+ {
+ "name": "is defined",
"type": "function",
"params": [
{
"name": "value"
}
],
- "info": "Camunda Extension
\nReturns the given string encoded in Base64 format.
\nFunction signature
\nto base64(value: string): string\n\nExamples
\nto base64("FEEL")\n// "RkVFTA=="\n\n"
+ "info": "Camunda Extension
\nChecks if a given value is not null. If the value is null then the function returns false.\nOtherwise, the function returns true.
Function signature
\nis defined(value: Any): boolean\n\nExamples
\nis defined(1)\n// true\n\nis defined(null)\n// false\n\nis defined(x)\n// false - if no variable "x" exists\n\nis defined(x.y)\n// false - if no variable "x" exists or it doesn't have a property "y"\n\n:::caution Breaking change
\nThis function worked differently in previous versions. It returned true if the value was null.\nSince this version, the function returns false if the value is null.
:::
\n" }, { - "name": "is blank", + "name": "get or else", "type": "function", "params": [ { - "name": "string" + "name": "value" + }, + { + "name": "default" } ], - "info": "Camunda Extension
\nReturns true if the given string is blank (empty or contains only whitespaces).
Function signature
\nis blank(string: string): boolean\n\nExamples
\nis blank("")\n// true\n\nis blank(" ")\n// true\n\nis blank("hello world")\n// false\n\n"
+ "info": "Camunda Extension
\nReturn the provided value parameter if not null, otherwise return the default parameter
Function signature
\nget or else(value: Any, default: Any): Any\n\nExamples
\nget or else("this", "default")\n// "this"\n\nget or else(null, "default")\n// "default"\n\nget or else(null, null)\n// null\n\n"
},
{
- "name": "now",
+ "name": "assert",
"type": "function",
- "params": [],
- "info": "Returns the current date and time including the timezone.
\nFunction signature
\nnow(): date and time\n\nExamples
\nnow()\n// date and time("2020-07-31T14:27:30@Europe/Berlin")\n\n"
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "condition"
+ }
+ ],
+ "info": "Camunda Extension
\nVerify that the given condition is met. If the condition is true, the function returns the value.\nOtherwise, the evaluation fails with an error.
Function signature
\nassert(value: Any, condition: Any)\n\nExamples
\nassert(x, x != null)\n// "value" - if x is "value"\n// error - if x is null or doesn't exist\n\nassert(x, x >= 0)\n// 4 - if x is 4\n// error - if x is less than zero\n\n"
},
{
- "name": "today",
+ "name": "assert",
"type": "function",
- "params": [],
- "info": "Returns the current date.
\nFunction signature
\ntoday(): date\n\nExamples
\ntoday()\n// date("2020-07-31")\n\n"
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "condition"
+ },
+ {
+ "name": "cause"
+ }
+ ],
+ "info": "Camunda Extension
\nVerify that the given condition is met. If the condition is true, the function returns the value.\nOtherwise, the evaluation fails with an error containing the given message.
Function signature
\nassert(value: Any, condition: Any, cause: String)\n\nExamples
\nassert(x, x != null, "'x' should not be null")\n// "value" - if x is "value"\n// error('x' should not be null) - if x is null or doesn't exist\n\nassert(x, x >= 0, "'x' should be positive")\n// 4 - if x is 4\n// error('x' should be positive) - if x is less than zero\n\n"
},
{
- "name": "day of week",
+ "name": "get value",
"type": "function",
"params": [
{
- "name": "date"
+ "name": "context"
+ },
+ {
+ "name": "keys"
}
],
- "info": "Returns the day of the week according to the Gregorian calendar. Note that it always returns the English name of the day.
\nFunction signature
\nday of week(date: date): string\n\nday of week(date: date and time): string\n\nExamples
\nday of week(date("2019-09-17"))\n// "Tuesday"\n\nday of week(date and time("2019-09-17T12:00:00"))\n// "Tuesday"\n\n"
+ "info": "Camunda Extension
\nReturns the value of the context entry for a context path defined by the given keys.
\nIf keys contains the keys [k1, k2] then it returns the value at the nested entry k1.k2 of the context.
If keys are empty or the nested entry defined by the keys doesn't exist in the context, it returns null.
Function signature
\nget value(context: context, keys: list<string>): Any\n\nExamples
\nget value({x:1, y: {z:0}}, ["y", "z"])\n// 0\n\nget value({x: {y: {z:0}}}, ["x", "y"])\n// {z:0}\n\nget value({a: {b: 3}}, ["b"])\n// null\n\n"
},
{
- "name": "day of year",
+ "name": "context put",
"type": "function",
"params": [
{
- "name": "date"
+ "name": "context"
+ },
+ {
+ "name": "key"
+ },
+ {
+ "name": "value"
}
],
- "info": "Returns the Gregorian number of the day within the year.
\nFunction signature
\nday of year(date: date): number\n\nday of year(date: date and time): number\n\nExamples
\nday of year(date("2019-09-17"))\n// 260\n\nday of year(date and time("2019-09-17T12:00:00"))\n// 260\n\n"
+ "info": "Adds a new entry with the given key and value to the context. Returns a new context that includes the entry.
\nIf an entry for the same key already exists in the context, it overrides the value.
\nFunction signature
\ncontext put(context: context, key: string, value: Any): context\n\nExamples
\ncontext put({x:1}, "y", 2)\n// {x:1, y:2}\n\n:::info\nThe function context put() replaced the previous function put() (Camunda Extension). The\nprevious function is deprecated and should not be used anymore.\n:::
Returns the Gregorian number of the week within the year, according to ISO 8601.
\nFunction signature
\nweek of year(date: date): number\n\nweek of year(date: date and time): number\n\nExamples
\nweek of year(date("2019-09-17"))\n// 38\n\nweek of year(date and time("2019-09-17T12:00:00"))\n// 38\n\n"
+ "info": "Union the given contexts. Returns a new context that includes all entries of the given contexts.
\nIf an entry for the same key already exists in a context, it overrides the value. The entries are overridden in the same order as in the list of contexts.
\nFunction signature
\ncontext merge(contexts: list<context>): context\n\nExamples
\ncontext merge([{x:1}, {y:2}])\n// {x:1, y:2}\n\ncontext merge([{x:1, y: 0}, {y:2}])\n// {x:1, y:2}\n\n:::info\nThe function context merge() replaced the previous function put all() (Camunda Extension). The\nprevious function is deprecated and should not be used anymore.\n:::
Returns the month of the year according to the Gregorian calendar. Note that it always returns the English name of the month.
\nFunction signature
\nmonth of year(date: date): string\n\nmonth of year(date: date and time): string\n\nExamples
\nmonth of year(date("2019-09-17"))\n// "September"\n\nmonth of year(date and time("2019-09-17T12:00:00"))\n// "September"\n\n"
+ "info": "Camunda Extension
\nReturns the given date and time value at the given timezone.
\nIf date has a different timezone than timezone then it adjusts the time to match the local time of timezone.
Function signature
\ndate and time(date: date and time, timezone: string): date and time\n\nExamples
\ndate and time(@"2020-07-31T14:27:30@Europe/Berlin", "America/Los_Angeles")\n// date and time("2020-07-31T05:27:30@America/Los_Angeles")\n\ndate and time(@"2020-07-31T14:27:30", "Z")\n// date and time("2020-07-31T12:27:30Z")\n\n"
},
{
- "name": "abs",
+ "name": "duplicate values",
"type": "function",
"params": [
{
- "name": "n"
+ "name": "list"
}
],
- "info": "Returns the absolute value of a given duration.
\nFunction signature
\nabs(n: days and time duration): days and time duration\n\nabs(n: years and months duration): years and months duration\n\nExamples
\nabs(duration("-PT5H"))\n// "duration("PT5H")"\n\nabs(duration("PT5H"))\n// "duration("PT5H")"\n\nabs(duration("-P2M"))\n// duration("P2M")\n\n"
+ "info": "Camunda Extension
\nReturns all duplicate values of the given list.
\nFunction signature
\nduplicate values(list: list): list\n\nExamples
\nduplicate values([1,2,3,2,1])\n// [1,2]\n\n"
+ },
+ {
+ "name": "string join",
+ "type": "function",
+ "params": [
+ {
+ "name": "list"
+ },
+ {
+ "name": "delimiter"
+ },
+ {
+ "name": "prefix"
+ },
+ {
+ "name": "suffix"
+ }
+ ],
+ "info": "Camunda Extension
\nJoins a list of strings into a single string. This is similar to\nJava's joining\nfunction.
\nIf an item of the list is null, the item is ignored for the result string. If an item is\nneither a string nor null, the function returns null instead of a string.
The resulting string starts with prefix, contains a delimiter between each element, and ends\nwith suffix.
Function signature
\nstring join(list: list<string>, delimiter: string, prefix: string, suffix: string): string\n\nExamples
\nstring join(["a","b","c"], ", ", "[", "]")\n// "[a, b, c]"\n\n"
+ },
+ {
+ "name": "is empty",
+ "type": "function",
+ "params": [
+ {
+ "name": "list"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns true if the given list is empty. Otherwise, returns false.
Function signature
\nis empty(list: list): boolean\n\nExamples
\nis empty([])\n// true\n\nis empty([1,2,3])\n// false\n\n"
+ },
+ {
+ "name": "partition",
+ "type": "function",
+ "params": [
+ {
+ "name": "list"
+ },
+ {
+ "name": "size"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns consecutive sublists of a list, each of the same size (the final list may be smaller).
\nIf size is less than 0, it returns null.
Function signature
\npartition(list: list, size: number): list\n\nExamples
\npartition([1,2,3,4,5], 2)\n// [[1,2], [3,4], [5]]\n\npartition([], 2)\n// []\n\npartition([1,2], 0)\n// null\n\n"
+ },
+ {
+ "name": "fromAi",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the unmodified value parameter.
The main use case of this function is for tool definitions used by the AI Agent connector.
\nSee the following function overloads for additional function parameters.
\nFunction signature
\nfromAi(value: Any): Any\n\nExamples
\nfromAi(toolCall.searchQuery)\n// toolCall.searchQuery contents\n\nfromAi(toolCall.userId)\n// toolCall.userId contents\n\n"
+ },
+ {
+ "name": "fromAi",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "description"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional description parameter to provide a textual description of the value. The description must be null or a string constant.
Function signature
\nfromAi(value: Any, description: string): Any\n\nExamples
\nfromAi(toolCall.searchQuery, "The search query used to find the best match.")\n// toolCall.searchQuery contents\n\nfromAi(toolCall.searchQuery, null)\n// toolCall.searchQuery contents\n\n"
+ },
+ {
+ "name": "fromAi",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "description"
+ },
+ {
+ "name": "type"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional type parameter to provide type information about the value. The type must be null or a string constant.
Function signature
\nfromAi(value: Any, description: string, type: string): Any\n\nExamples
\nfromAi(toolCall.searchQuery, "The search query used to find the best match.", "string")\n// toolCall.searchQuery contents\n\nfromAi(toolCall.userId, "The user's ID", "number")\n// toolCall.userId contents\n\nfromAi(toolCall.userId, null, "number")\n// toolCall.userId contents\n\nfromAi(value: toolCall.userId, type: "number")\n// toolCall.userId contents\n\n"
+ },
+ {
+ "name": "fromAi",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "description"
+ },
+ {
+ "name": "type"
+ },
+ {
+ "name": "schema"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional schema parameter to provide a (partial) JSON schema for the value.
null or a context (map) containing only constant values. For example, function calls within the schema are not supported.type and a schema, and it depends on the integration as to which value takes precedence. The AI Agent connector will override any type specified in the schema if the type parameter is also provided.Function signature
\nfromAi(value: Any, description: string, type: string, schema: context): Any\n\nExamples
\nfromAi(toolCall.documentType, "The document type to provide", "string", {\n enum: ["invoice", "receipt", "contract"]\n})\n// toolCall.documentType contents\n\nfromAi(value: toolCall.documentType, description: "The document type to provide", schema: {\n type: "string",\n enum: ["invoice", "receipt", "contract"]\n})\n// toolCall.documentType contents\n\nfromAi(toolCall.tags, "Tags to apply to the blog post", "array", {\n items: {\n type: "string"\n }\n})\n// toolCall.tags contents\n\n"
+ },
+ {
+ "name": "fromAi",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "description"
+ },
+ {
+ "name": "type"
+ },
+ {
+ "name": "schema"
+ },
+ {
+ "name": "options"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the unmodified value parameter.
In addition to the previous overload, it also accepts an optional options parameter to provide additional options for the integration handling the value definition.
null or a context (map) containing only constant values. For example, function calls within options are not supported.Function signature
\nfromAi(value: Any, description: string, type: string, schema: context, options: context): Any\n\nExamples
\nfromAi(toolCall.documentType, "The document type to provide", "string", null, {\n required: false\n})\n// toolCall.documentType contents\n\nfromAi(value: toolCall.documentType, options: {\n required: false\n})\n// toolCall.documentType contents\n\n"
+ },
+ {
+ "name": "random number",
+ "type": "function",
+ "params": [],
+ "info": "Camunda Extension
\nReturns a random number between 0 and 1.
Function signature
\nrandom number(): number\n\nExamples
\nrandom number()\n// 0.9701618132579795\n\n"
+ },
+ {
+ "name": "extract",
+ "type": "function",
+ "params": [
+ {
+ "name": "string"
+ },
+ {
+ "name": "pattern"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns all matches of the pattern in the given string. Returns an empty list if the pattern doesn't\nmatch.
\nFunction signature
\nextract(string: string, pattern: string): list<string>\n\nThe pattern is a string that contains a regular expression.
Examples
\nextract("references are 1234, 1256, 1378", "12[0-9]*")\n// ["1234","1256"]\n\n"
+ },
+ {
+ "name": "trim",
+ "type": "function",
+ "params": [
+ {
+ "name": "string"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the given string without leading and trailing spaces.
\nFunction signature
\ntrim(string: string): string\n\nExamples
\ntrim(" hello world ")\n// "hello world"\n\ntrim("hello world ")\n// "hello world"\n\n"
+ },
+ {
+ "name": "uuid",
+ "type": "function",
+ "params": [],
+ "info": "Camunda Extension
\nReturns a UUID (Universally Unique Identifier) with 36 characters.
\nFunction signature
\nuuid(): string\n\nExamples
\nuuid()\n// "7793aab1-d761-4d38-916b-b7270e309894"\n\n"
+ },
+ {
+ "name": "to base64",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns the given string encoded in Base64 format.
\nFunction signature
\nto base64(value: string): string\n\nExamples
\nto base64("FEEL")\n// "RkVFTA=="\n\n"
+ },
+ {
+ "name": "is blank",
+ "type": "function",
+ "params": [
+ {
+ "name": "string"
+ }
+ ],
+ "info": "Camunda Extension
\nReturns true if the given string is blank (empty or contains only whitespaces).
Function signature
\nis blank(string: string): boolean\n\nExamples
\nis blank("")\n// true\n\nis blank(" ")\n// true\n\nis blank("hello world")\n// false\n\n"
},
{
"name": "last day of month",
@@ -1660,4 +1668,32 @@ export const camundaBuiltins = [
],
"info": "Camunda Extension
\nTakes the month of the given date or date-time value and returns the last day of this month.
\nFunction signature
\nlast day of month(date: date): date\n\nlast day of month(date: date and time): date\n\nExamples
\nlast day of month(date("2022-10-01"))\n// date("2022-10-31"))\n\nlast day of month(date and time("2022-10-16T12:00:00"))\n// date("2022-10-31"))\n\n"
}
-];
\ No newline at end of file
+];
+
+/**
+ * Collection of builtins of camunda scala FEEL.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaBuiltins = [ ...feelBuiltins, ...camundaExtensions ];
+
+/**
+ * Functions using reserved keywords in their name and need to be added to the parser context.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaReservedNameBuiltins = [
+ {
+ "name": "get or else",
+ "type": "function",
+ "params": [
+ {
+ "name": "value"
+ },
+ {
+ "name": "default"
+ }
+ ],
+ "info": "Camunda Extension
\nReturn the provided value parameter if not null, otherwise return the default parameter
Function signature
\nget or else(value: Any, default: Any): Any\n\nExamples
\nget or else("this", "default")\n// "this"\n\nget or else(null, "default")\n// "default"\n\nget or else(null, null)\n// null\n\n"
+ }
+];
diff --git a/src/index.js b/src/index.js
index 503e70e..ecb3331 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1 +1 @@
-export { camundaBuiltins } from './camundaBuiltins.js';
+export { camundaBuiltins, feelBuiltins, camundaExtensions, camundaReservedNameBuiltins } from './camundaBuiltins.js';
diff --git a/tasks/camundaBuiltins.template.js b/tasks/camundaBuiltins.template.js
index ef2018c..dd9af18 100644
--- a/tasks/camundaBuiltins.template.js
+++ b/tasks/camundaBuiltins.template.js
@@ -18,8 +18,29 @@
*/
/**
- * FEEL built-ins available with Camunda / feel-scala.
+ * List of standard FEEL built-in functions (excluding Camunda-specific extensions).
*
* @type { Builtin[] }
*/
-export const camundaBuiltins = /** CAMUNDA_BUILTINS_PLACEHOLDER */ [];
\ No newline at end of file
+export const feelBuiltins = /** FEEL_BUILTINS_PLACEHOLDER */ [];
+
+/**
+ * List of FEEL camunda extensions.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaExtensions = /** CAMUNDA_EXTENSIONS_PLACEHOLDER */ [];
+
+/**
+ * Collection of builtins of camunda scala FEEL.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaBuiltins = [ ...feelBuiltins, ...camundaExtensions ];
+
+/**
+ * Functions using reserved keywords in their name and need to be added to the parser context.
+ *
+ * @type { Builtin[] }
+ */
+export const camundaReservedNameBuiltins = /** RESERVED_NAME_BUILTINS_PLACEHOLDER */ [];
diff --git a/tasks/compileBuiltins.js b/tasks/compileBuiltins.js
index 7c15d80..b5a9c4d 100644
--- a/tasks/compileBuiltins.js
+++ b/tasks/compileBuiltins.js
@@ -1,71 +1,28 @@
import { glob } from 'glob';
-import { marked } from 'marked';
-import { readFile, writeFile } from 'node:fs/promises';
-import { parseBuiltins } from './utils/parseBuiltins.js';
+import { categorizeBuiltins, logStatistics, parseBuiltins, parseMarkdownFile, writeBuiltinsFromTemplate } from './utils/index.js';
// paths relative to CWD
const MARKDOWN_SRC = './camunda-docs/docs/components/modeler/feel/builtin-functions/*.md';
const JS_SRC = './tasks/camundaBuiltins.template.js';
const JS_DEST = './src/camundaBuiltins.js';
-const CAMUNDA_BUILTINS_PLACEHOLDER = '/** CAMUNDA_BUILTINS_PLACEHOLDER */ []';
-
-/**
- * @typedef { import('./utils/parseBuiltins.js').BuiltinDescriptor } BuiltinDescriptor
- */
-
-/**
- * @param {string} fileName
- *
- * @return { Promise