diff --git a/cds/assets/cxl/binary-operator.drawio.svg b/cds/assets/cxl/binary-operator.drawio.svg new file mode 100644 index 0000000000..29809fd7f4 --- /dev/null +++ b/cds/assets/cxl/binary-operator.drawio.svg @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + * + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + / + + + + + + + + + + = + + + + + + + + + + != + + + + + + + + + + == + + + + + + + + + + <> + + + + + + + + + + > + + + + + + + + + + >= + + + + + + + + + + < + + + + + + + + + + <= + + + + + + + + + + AND + + + + + + + + + + OR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ || +
+
+
+
+ + || + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/cds/assets/cxl/expr.drawio.svg b/cds/assets/cxl/expr.drawio.svg new file mode 100644 index 0000000000..92e2eaaf15 --- /dev/null +++ b/cds/assets/cxl/expr.drawio.svg @@ -0,0 +1,870 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + literal value + + + + + + + + + + + + ref + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + ( + + + + + + + + + + , + + + + + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ( + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + as + + + + + + + + + + + + + + + type-ref + + + + + + + + + + + ) + + + + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + CAST + + + + + + + + + + + + + + + + + + + + + LIKE + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + NULL + + + + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + NOT + + + + + + + + + + + + + + + + + + BETWEEN + + + + + + + + + + NOT + + + + + + + + + + + + + + IS + + + + + + + + + + + + + + NOT + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + AND + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + NOT + + + + + + + + + + + + + + + + + + ( + + + + + + + + + + ) + + + + + + + + + + + expr + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT + + + + + + + + + + EXISTS + + + + + + + + + + + + + + + + + + IN + + + + + + + + + + + + + + CASE + + + + + + + + + + + + + + WHEN + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + THEN + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + ELSE + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + END + + + + + + + + + + + + + + + + + + + + + + + unary operator + + + + + + + + + + + + + + + + binary operator + + + + + + + + + + + + ref + + + + + + + + + + + + + + + + + + + + function + + + + + + + + + + \ No newline at end of file diff --git a/cds/assets/cxl/function-def.drawio.svg b/cds/assets/cxl/function-def.drawio.svg new file mode 100644 index 0000000000..e1379c1424 --- /dev/null +++ b/cds/assets/cxl/function-def.drawio.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + function name + + + + + + + + + + + + + + ( + + + + + + + + + + + + + + + + + + ) + + + + + + + + + + + expr + + + + + + + + + + + , + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cds/assets/cxl/infix-filter.drawio.svg b/cds/assets/cxl/infix-filter.drawio.svg new file mode 100644 index 0000000000..c46cbea71f --- /dev/null +++ b/cds/assets/cxl/infix-filter.drawio.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + WHERE + + + + + + + + + + + + + + + + + [ + + + + + + + + + + + + + + ] + + + + + + + + + + + + + + + expr + + + + + + + + + + + + + + + + + + + +
+
+
+ WHERE is implicitly part of each infix filter +
+
+
+
+ + WHERE is implic... + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/cds/assets/cxl/intro.drawio.svg b/cds/assets/cxl/intro.drawio.svg new file mode 100644 index 0000000000..8383fa431e --- /dev/null +++ b/cds/assets/cxl/intro.drawio.svg @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + Some shapes have tooltips with some explanation, try it out! 🚀 + + + + + + + Some shapes have tooltips with some explanation, try it out! 🚀 + + + alternative 1 + + + + + + + + + + alternative 2 + + + + + + + + + + + language construct + + + + + + + + + + + + + + + + start here 🚀 + + + + + + + + + + + + + + + + + + + 💡clickable + + + + + + + + + + + + + + + finish 🏁 + + + + + + + + + + + + + + + + + … + + + + + + + + + + + + + + Green Boxes indicate proprietary CAP expressions + + + they extend standard SQL Expressions in some way + + + + + + + + + + +
+
+
+ + Round shapes refer to terminal symbols such as + + + exists + + + or + + + null + + + , which can't be further divided + +
+
+
+
+ + Round shapes refer to te... + +
+
+
+ + + + + + + +
+
+
+ + language constructs can be broken down further and get their own syntax diagram + +
+
+
+
+ + language constructs can... + +
+
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/cds/assets/cxl/literal-value.drawio.svg b/cds/assets/cxl/literal-value.drawio.svg new file mode 100644 index 0000000000..0a4b5fae58 --- /dev/null +++ b/cds/assets/cxl/literal-value.drawio.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + string-literal + + + + + + + + + + null + + + + + + + + + + + + + + + + + + numeric-literal + + + + + + + + + + + + + + + + + + true + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + Date + + + + + + + + + + Time + + + + + + + + + + DateTime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TimeStamp + + + + + + + + + + + + + \ No newline at end of file diff --git a/cds/assets/cxl/ordering-term.drawio.svg b/cds/assets/cxl/ordering-term.drawio.svg new file mode 100644 index 0000000000..b349dda9fd --- /dev/null +++ b/cds/assets/cxl/ordering-term.drawio.svg @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + expr + + + + + + + + + + + ASC + + + + + + + + + + DESC + + + + + + + + + + NULLS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NULLS + + + + + + + + + + + + + + FIRST + + + + + + + + + + LAST + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cds/assets/cxl/ref.drawio.svg b/cds/assets/cxl/ref.drawio.svg new file mode 100644 index 0000000000..f479b81283 --- /dev/null +++ b/cds/assets/cxl/ref.drawio.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + a scalar element is a primitive field (e.g., String, Integer, Decimal, Boolean, Date/Time, UUID) without nested structure or associations + + + + + + + a scalar element is a primitive field (e.g., String, Integer, Decimal, Boolean, Date/Time, UUID) without nested structure or associations + + + leaf element + + + + + + + + + + structured element + + + + + + + + + + association + + + + + + + + + + + infix filter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ . +
+
+
+
+ + . + +
+
+
+ + + + + + + + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/cds/assets/cxl/unary-operator.drawio.svg b/cds/assets/cxl/unary-operator.drawio.svg new file mode 100644 index 0000000000..9158f4579c --- /dev/null +++ b/cds/assets/cxl/unary-operator.drawio.svg @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + NOT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/cds/cxl.md b/cds/cxl.md index f9734ff3d9..fd547bf3c7 100644 --- a/cds/cxl.md +++ b/cds/cxl.md @@ -1,12 +1,678 @@ --- +# layout: cds-ref +shorty: Expressions +synopsis: > + Specification of the CDS Expression Language (CXL) used to capture expressions in CDS. status: released --- -# CDS Expression Language (CXL) + -This document provides an overview of the CDS Expression Language (CXL) used in CDS models and queries. -CXL is essentially a standard subset of SQL expressions enhanced by [Path Expressions](../cds/cql#path-expressions) with [Infix Filters](../cds/cql#with-infix-filters). -The documentation is still **under construction** and will be released soon. +# CDS Expression Language (CXL) { #expressions } +The CDS Expression Language (`CXL`) is a language to express calculations, conditions, +and other expressions in the context of CDS models and queries. +**`CXL` is based on the SQL expression language**, so many syntax elements from SQL are also available in `CXL`. + +`CXL` can be used in various places: +- In [CQL](./cql#path-expressions) (select list, where clause, …) +- In [CDL](./cdl) + + In [calculated elements](./cdl#calculated-elements) + + In [annotations](./cdl.md#expressions-as-annotation-values) + +::: tip Expressions in CAP are materialized in the context of queries +No matter where `CXL` is used, it always manifests in queries. +For example, [a calculated element](./cdl#calculated-elements) defined in an entity will be resolved +to the respective calculation in the generated query when the entity is queried. +::: + + +## How to read this guide { #how-to } + + +In the following chapters we illustrate the `CXL` syntax based on simple and more complex examples. +For a complete reference of the syntax, there are clickable [syntax diagrams](https://en.wikipedia.org/wiki/Syntax_diagram) (aka railroad diagrams) for each language construct. + +### samples + +To try the samples by yourself, create a simple CAP app: + +```sh +cds init bookshop --add sample && cd bookshop +``` + +We encourage you to play around with the snippets. +Just create the sample app as described above and start a repl session within the newly created app by running: + +```sh +cds repl --run . +``` + +:::info All of the example expressions follow the same pattern: +1. A **`CXL`** is shown in the context of a query. +2. The resulting **`SQL`** is shown. + +:::code-group +```js [CQL] +> await cds.ql`SELECT from Books { title }` // [!code focus] +[ + { title: 'Wuthering Heights' }, + { title: 'Jane Eyre' }, + { title: 'The Raven' }, + { title: 'Eleonora' }, + { title: 'Catweazle' } +] +``` + +```sql [SQL] +SELECT title FROM sap_capire_bookshop_Books as Books +``` +::: + +### syntax diagrams + +Each language construct is illustrated by a clickable [syntax diagram](https://en.wikipedia.org/wiki/Syntax_diagram). + +They show the syntax of CAPs expression language as a sequence of building blocks. +By clicking on the individual blocks, you can get more information about the respective building block. + +The following diagram illustrates how to read the diagrams: + +
+ +
+
+ + +## expr { #expr } + +An expression can hold various elements, such as references, literals, function calls, operators, and more. A few examples, in the context of a select list: +```cds +select from Books { + 42 as answer, // literal + title, // reference ("ref") + price * quantity as totalPrice, // binary operator + substring(title, 1, 3) as shortTitle, // function call + author.name as authorName, // ref with path expression + chapters[number < 3] as earlyChapters, // ref with infix filter + exists chapters as hasChapters, // exists + count(chapters) as chapterCount, // aggregate function +} +``` + + +
+ +
+
+ +Expressions can be used in various places, for example in annotations: + +```cds +annotate AdminService.Authors:dateOfDeath with @assert: (case + when dateOfDeath > $now then 'Cannot be in the future' + when dateOfDeath < dateOfBirth then 'Enter a date after date of birth' +end); +``` + +Or in entity defintions for adding calculated elements: + +```cds +extend Authors with { + age = years_between(dateOfBirth, coalesce(dateOfDeath, $now)); +} +``` + +Or as part of a query: + +```cds +SELECT from Books { title } where genre.name = 'Fantasy' +``` + + +## ref (path expression) { #ref } + +A `ref` (short for reference) is used to refer to an element within the model. +It can be used to navigate along path segments. Such a navigation is often +referred to as a **path expression**. + +
+ +
+
+ +::: info Leaf elements +Leaf elements as opposed to associations and structured elements represent scalar values, such as strings, numbers, dates, as well as the array and map types. +They typically manifest as columns in database tables. +::: + +### simple element reference + +In its simplest form, a `ref` can be used to reference an element: + +:::code-group +```js [CQL] {1} +> await cds.ql`SELECT from Books { title }` // [!code focus] +[ + { title: 'Wuthering Heights' }, + { title: 'Jane Eyre' }, + { title: 'The Raven' }, + { title: 'Eleonora' }, + { title: 'Catweazle' } +] +``` + +```sql [SQL] +SELECT title FROM sap_capire_bookshop_Books as Books +``` +::: + +In this example, we select the `title` element from the `Books` entity. + +### path navigation {#path-navigation} + +A path expression can be used to navigate to any element of the associations target: + +:::code-group +```js [CQL] +> await cds.ql`SELECT from Books { title, author.name as author }` // [!code focus] +[ + { title: 'Wuthering Heights', author: 'Emily Brontë' }, + { title: 'Jane Eyre', author: 'Charlotte Brontë' }, + { title: 'The Raven', author: 'Edgar Allen Poe' }, + { title: 'Eleonora', author: 'Edgar Allen Poe' }, + { title: 'Catweazle', author: 'Richard Carpenter' } +] +``` + +```sql [SQL] +SELECT + title, + author.name AS author +FROM + sap_capire_bookshop_Books AS Books + LEFT JOIN sap_capire_bookshop_Authors AS author -- The table alias for association 'author' + ON author.ID = Books.author_ID; +``` +::: + +In this example, we select all books together with the name of their author. +The association `author` defined in the `Books` entity relates a book to it's author. + +When navigating along a to-many association to a leaf element, the result is flattened: + +:::code-group +```js [CQL] +> await cds.ql `SELECT from Authors { books.title as title, name as author }` // [!code focus] +[ + { title: 'Wuthering Heights', author: 'Emily Brontë' }, + { title: 'Jane Eyre', author: 'Charlotte Brontë' }, + { title: 'Eleonora', author: 'Edgar Allen Poe' }, + { title: 'The Raven', author: 'Edgar Allen Poe' }, + { title: 'Catweazle', author: 'Richard Carpenter' } +] +``` + +```sql [SQL] +SELECT + books.title as title, + name as author +FROM sap_capire_bookshop_Authors as Authors + LEFT JOIN sap_capire_bookshop_Books as books + ON books.author_ID = Authors.ID +``` +::: + +In this example, we select the book titles together with each author. +Since books is a to-many association, we get a flattened result: one entry per author and book title. + +In annotation expressions, the result should often only contain one entry per entry in the annotated entity. +This can be achieved using the [exists](#in-exists-predicate) predicate. + + +::: tip Associations are **forward-declared joins** +They provide a convenient way to navigate between related entities without having to define the join conditions manually. + +The join condition is defined **ahead of time** as part of the association. +Typically, this is a foreign key relationship between two entities, but other conditions are also possible. + + +```cds +entity Books { + key ID : Integer; + title : String; + author : Association to Authors; + // implicit: on author.ID = author_ID +} + +entity Authors { + key ID : Integer; + name : String; + books : Association to many Books on books.author = $self; + // for: on books.author_ID = $self.ID +} +``` + + +It is applied whenever the association is used in a path expression. + +```cds +SELECT from Books { title, author.name as author } +``` + +```sql +SELECT + title, + author.name AS author +FROM + sap_capire_bookshop_Books +LEFT JOIN + sap_capire_bookshop_Authors AS author -- Table alias for association 'author' + ON author.ID = author_ID; -- Join condition from association +``` + + +The condition can manifest in multiple ways: +- In the on condition of a join +- In the condition that correlates a subquery to a main query +- To select related entities with an additional query +::: + +### in `exists` predicate + +Path expressions can also be used after the `exists` keyword to check whether the set referenced by the path is empty. +This is especially useful for to-many relations. + +E.g., to select all authors that have written **at least** one book: + +:::code-group +```js [CQL] +> await cds.ql`SELECT from Authors { name } where exists books` // [!code focus] + +[ + { name: 'Emily Brontë' }, + { name: 'Charlotte Brontë' }, + { name: 'Edgar Allen Poe' }, + { name: 'Richard Carpenter' } +] +``` + +```sql [SQL] {3-7} +SELECT Authors.name +FROM sap_capire_bookshop_Authors as Authors +WHERE exists ( + SELECT 1 + FROM sap_capire_bookshop_Books as books + WHERE books.author_ID = Authors.ID + ) +``` +::: + +[Learn more about the `exists` predicate.](./cql.md#exists-predicate){.learn-more} + +## infix filter { #infix-filter } + +An infix in linguistics refer to a letter or group of letters that are added in the middle of a word to make a new word. + +If we apply this terminology to [path-expressions](#ref), an infix filter condition is an expression +that is applied to a path-segment of a [path-expression](#ref). +This allows to filter the target of an association based on certain criteria. + +
+ +
+
+ + + +### applied to `exists` predicate + +In this example, we want to select all authors that have written at least one book in the `Fantasy` genre: + +> REVISIT: this does work in the node runtime, but the compiler does not yet support it. How about java? + +:::code-group +```js [CQL] +> await cds.ql` + SELECT from Authors { name } + where exists books[genre.name = 'Fantasy']` // [!code focus] + +[ { name: 'Richard Carpenter' } ] +``` + +```sql [SQL] +SELECT Authors.name +FROM sap_capire_bookshop_Authors as Authors +WHERE exists ( + SELECT 1 + FROM sap_capire_bookshop_Books as books + inner JOIN sap_capire_bookshop_Genres as genre + ON genre.ID = books.genre_ID + WHERE books.author_ID = Authors.ID + and genre.name = 'Fantasy' + ) +``` +::: + +> Note how the infix filter condition `genre.name = 'Fantasy'` is applied to the +the `exists`-subquery for the `books` association in SQL. + +### applied to `from` clause + + + +:::code-group +```js [CQL] +> await cds.ql`SELECT from Books[price > 19.99]:author { name }` // [!code focus] +[ { name: 'Richard Carpenter' } ] +``` + +```sql [SQL] +SELECT Authors.name +FROM sap_capire_bookshop_Authors as Authors +WHERE exists ( + SELECT 1 + FROM sap_capire_bookshop_Books as Books + WHERE Books.author_ID = Authors.ID + and Books.price > ? +) +``` +::: + + +### in calculated element + +You can also use the infix filter notation to derive +another more specific association from an existing one. + +In the `Authors` entity in the `Books.cds` file add a new element `cheapBooks`: + +```cds {2} + books : Association to many Books on books.author = $self; + cheapBooks = books[price < 19.99]; // based on `books` association +``` + +Now we can use `cheapBooks` just like any other association. +E.g. to select the set of authors which have no cheap books: + +:::code-group +```js [CQL] +> await cds.ql`SELECT from Authors { name } where not exists cheapBooks` // [!code focus] +[ + { name: 'Richard Carpenter' } +] +``` + +```sql [SQL] +SELECT Authors.name +FROM sap_capire_bookshop_Authors as Authors +WHERE not exists ( + SELECT 1 + FROM sap_capire_bookshop_Books as cheapBooks + WHERE (cheapBooks.author_ID = Authors.ID) + and (cheapBooks.price < 19.99) -- here the infix filter condition is applied + ) +``` +::: + + +[Learn more about association-like calculated elements.](./cdl.md#association-like-calculated-elements){ .learn-more } + +We can also use `cheapBooks` in nested expands to get all cheap books of each author: + +::: code-group +```js [CQL] +> await cds.ql`SELECT from Authors { name, cheapBooks { title, price } }` // [!code focus] +[ + { + name: 'Emily Brontë', + cheapBooks: [ { title: 'Wuthering Heights', price: 11.11 } ] + }, + { + name: 'Charlotte Brontë', + cheapBooks: [ { title: 'Jane Eyre', price: 12.34 } ] + }, + { + name: 'Edgar Allen Poe', + cheapBooks: [ + { title: 'The Raven', price: 13.13 }, + { title: 'Eleonora', price: 14 } + ] + }, + { name: 'Richard Carpenter', cheapBooks: [] } +] +``` + +```sql [SQL] +SELECT Authors.name, +( + SELECT jsonb_group_array( + jsonb_insert('{}', '$."title"', title, '$."price"', price) + ) as _json_ + FROM ( + SELECT + Books.title, + Books.price + FROM sap_capire_bookshop_Books as Books + WHERE (Authors.ID = Books.author_ID) + and (Books.price < ?) + ) +) as cheapBooks +FROM sap_capire_bookshop_Authors as Authors +``` +::: + + +### between path segments + +Assuming you have the [calculated element](#in-calculated-element) age in place on the Authors entity: + +```cds +extend Authors with { + age = years_between(dateOfBirth, coalesce(dateOfDeath, $now)); +} +``` + +In this case we want to select all books where the author's name starts with `Emily` +and the author is younger than 40 years. + +:::code-group +```js [CQL] +> await cds.ql `SELECT from Books { title, author[age < 40].name as author }` // [!code focus] + +[ + { title: 'Wuthering Heights', author: 'Emily Brontë' }, + { title: 'Jane Eyre', author: 'Charlotte Brontë' }, + { title: 'The Raven', author: null }, + { title: 'Eleonora', author: null }, + { title: 'Catweazle', author: null } +] +``` + +```sql [SQL] +SELECT + title, + author.name as author +FROM + sap_capire_bookshop_Books as Books +LEFT JOIN + sap_capire_bookshop_Authors as author +ON + author.ID = Books.author_ID AND floor( + ( + ( + (cast(strftime('%Y', coalesce(author.dateOfDeath,session_context('$now'))) as Integer) - cast(strftime('%Y', author.dateOfBirth) as Integer)) * 12 + ) + ( + cast(strftime('%m', coalesce(author.dateOfDeath,session_context('$now'))) as Integer) - cast(strftime('%m', author.dateOfBirth) as Integer) + ) + ( + ( + case + when (cast(strftime('%Y%m', coalesce(author.dateOfDeath,session_context('$now'))) as Integer) < cast(strftime('%Y%m', author.dateOfBirth) as Integer)) then + (cast(strftime('%d%H%M%S%f0000', coalesce(author.dateOfDeath,session_context('$now'))) as Integer) > cast(strftime('%d%H%M%S%f0000', author.dateOfBirth) as Integer)) + else + (cast(strftime('%d%H%M%S%f0000', coalesce(author.dateOfDeath,session_context('$now'))) as Integer) < cast(strftime('%d%H%M%S%f0000', author.dateOfBirth) as Integer)) * -1 + end + ) + ) + ) / 12) < ? + ``` +::: + +The path expression `author[ age < 40 ].name` +navigates along the `author` association of the `Books` entity. + +The join for this path expression is generated as usual and enhanced with the infix filter condition `age < 40`. + + +## operators + +### unary operator { #unary-operator } + +
+
+
+ + +::: info A unary operator is an operator that operates on exactly one operand. + +E.g. in the expression `-price`, the `-` operator is a unary operator +that operates on the single operand `price`. It negates the value of `price`. +::: + +### binary operator { #binary-operator } + +
+
+
+ + +::: info A binary operator is an operator that operates on two operands. +E.g. in the expression `price * quantity`, the `*` operator is a binary operator +that multiplies the two factors `price` and `quantity`. +::: + +## literal value { #literal-value } + +
+
+
+ +[Learn more about literals.](./csn.md#literals){ .learn-more } + +## function { #function } + + +
+ +
+
+ + + +CAP supports a set of [standard functions](../guides/databases/index#standard-database-functions) that can be used in expressions. In addition, functions are passed through to the underlying database, allowing you to leverage database-specific functions as needed. + +CAP standard functions: +| Name | Description | +|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------| +| **String Functions** | | +| `concat(x, y, ...)` | Concatenates the given strings or numbers `x`, `y`, ... | +| `trim(x)` | Removes leading and trailing whitespaces from `x`. | +| `contains(x, y)` | Checks whether `x` contains `y` (case-sensitive). | +| `startswith(x, y)` | Checks whether `x` starts with `y` (case-sensitive). | +| `endswith(x, y)` | Checks whether `x` ends with `y` (case-sensitive). | +| `matchespattern(x, y)` | Checks whether `x` matches the regular expression `y`. | +| `indexof(x, y)`1 | Returns the index of the first occurrence of `y` in `x` (case-sensitive). | +| `substring(x, i, n?)`1 | Extracts a substring from `x` starting at index `i` (0-based) with an optional length `n`. | +| `length(x)` | Returns the length of the string `x`. | +| `tolower(x)` | Converts all characters in `x` to lowercase. | +| `toupper(x)` | Converts all characters in `x` to uppercase. | +| **Numeric Functions** | | +| `ceiling(x)` | Rounds the numeric parameter up to the nearest integer. | +| `floor(x)` | Rounds the numeric parameter down to the nearest integer. | +| `round(x)` | Rounds the numeric parameter to the nearest integer. The midpoint between two integers is rounded away from zero (e.g., `0.5` → `1` and `-0.5` → `-1`). | +| **Aggregate Functions** | | +| `min(x)` | Returns the minimum value of `x`. | +| `max(x)` | Returns the maximum value of `x`. | +| `sum(x)` | Returns the sum of all values of `x`. | +| `average(x)` | Returns the average (mean) value of `x`. | +| `count(x)` | Returns the count of non-null values of `x`. | +| `countdistinct(x)` | Returns the count of distinct non-null values of `x`. | + + + +## ordering term { #ordering-term } + +
+ +
+
+ +### ordered list of book titles by price + +:::code-group +```js [CQL] +> await cds.ql` + SELECT from Books { title, price } + order by price desc nulls last` // [!code focus] +[ + { title: 'Catweazle', price: 150 }, + { title: 'Eleonora', price: 14 }, + { title: 'The Raven', price: 13.13 }, + { title: 'Jane Eyre', price: 12.34 }, + { title: 'Wuthering Heights', price: 11.11 }, + { title: 'Untitled', price: null } +] +``` + +```sql [SQL] +SELECT + title, + price +FROM + sap_capire_bookshop_Books AS Books +ORDER BY price DESC NULLS LAST -- [!code focus] +``` +::: + +In this example, the ordering term sorts books by price in descending order and places rows with `null` prices at the end. + + +## type-ref { #type-ref } + +[Learn more about type references in CDL.](./cdl#type-references){ .learn-more } + + + -For the time being, please refer to the existing reference docs about [CDS Query Language (CQL)](../cds/cql), and to the [CDS Expression Notation (CXN)](../cds/cxn) documentation. diff --git a/package-lock.json b/package-lock.json index 3363b59f85..efcf8653f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "eslint": "^9.0.0", "eslint-plugin-vue": "^10.0.0", "fflate": "^0.8.2", + "markdown-it-mathjax3": "^4.3.2", "markdownlint-cli": ">=0.35.0", "markdownlint-rule-search-replace": "^1.1.1", "sass": "^1.62.1", @@ -3302,6 +3303,16 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3400,6 +3411,16 @@ "dev": true, "license": "MIT" }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -3682,6 +3703,45 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-1.6.0.tgz", + "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "css-select": "^4.3.0", + "css-what": "^6.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.3.1", + "domutils": "^2.8.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4042,6 +4102,36 @@ "node": ">=20" } }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4172,6 +4262,75 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -4315,6 +4474,19 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -4499,6 +4671,16 @@ "node": "*" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -5114,6 +5296,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -5410,6 +5622,36 @@ "node": ">=0.10.0" } }, + "node_modules/juice": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/juice/-/juice-8.1.0.tgz", + "integrity": "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio": "1.0.0-rc.10", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/katex": { "version": "0.16.27", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.27.tgz", @@ -5550,6 +5792,17 @@ "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/markdown-it-mathjax3": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/markdown-it-mathjax3/-/markdown-it-mathjax3-4.3.2.tgz", + "integrity": "sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "juice": "^8.0.0", + "mathjax-full": "^3.2.0" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -5698,6 +5951,19 @@ "node": ">= 0.4" } }, + "node_modules/mathjax-full": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.1.tgz", + "integrity": "sha512-aUz9o16MGZdeiIBwZjAfUBTiJb7LRqzZEl1YOZ8zQMGYIyh1/nxRebxKxjDe9L+xcZCr2OHdzoFBMcd6VnLv9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -5951,6 +6217,13 @@ "node": ">= 0.6" } }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==", + "dev": true, + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -5973,6 +6246,13 @@ "node": ">= 0.6" } }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -6640,6 +6920,13 @@ "dev": true, "license": "MIT" }, + "node_modules/mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6699,6 +6986,27 @@ "license": "MIT", "optional": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -6856,6 +7164,23 @@ "dev": true, "license": "MIT" }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^6.0.1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -7462,6 +7787,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "dev": true, + "license": "MIT (http://mootools.net/license.txt)", + "engines": { + "node": "*" + } + }, "node_modules/smol-toml": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", @@ -7506,6 +7841,38 @@ "node": ">=0.10.0" } }, + "node_modules/speech-rule-engine": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.1.2.tgz", + "integrity": "sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "0.9.8", + "commander": "13.1.0", + "wicked-good-xpath": "1.3.0" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -7653,6 +8020,13 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -7677,6 +8051,13 @@ "typescript": ">=4.8.4" } }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/twoslash": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/twoslash/-/twoslash-0.3.6.tgz", @@ -7885,6 +8266,16 @@ "node": ">= 0.4.0" } }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -8197,6 +8588,97 @@ "vue": "^3.0.0" } }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -8213,6 +8695,13 @@ "node": ">= 8" } }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", + "dev": true, + "license": "MIT" + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",