Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audit annotations

An annotation attaches business metadata to part of an expression: a label, a message and an identifier. jlens uses annotations to make audit trees and explanations readable. Annotations are written as JSONata comments, so an annotated expression is still valid JSONata and evaluates identically in any other implementation.

/** @label "Authorized customer" @message "The customer login must be on the approved list." */
customer.login in $approved
and
/** @label "Minimum total" @id "RULE-042" */
$sum(items.(unitPrice * quantity)) > 50000

Syntax

An annotation is a block comment that starts with /**. The empty comment /**/ and ordinary /* ... */ comments are not annotations.

ElementMeaning
@label "…"Short name shown in audit trees and explanations
@message "…"Longer explanation of what the expression checks
@id "…"Stable identifier, for referring to this check from other systems
text before the first tagFree-form description

Tag values are JSON string literals, so \", \n and é escapes work. Leading * characters on continuation lines are ignored, so multi-line comments can be written JSDoc-style:

/**
 * Customers must be registered partners.
 * @label "Authorized customer"
 * @id "IMP-001"
 */
customer.login in $approved

When several annotations precede the same expression, they are merged and later tags win.

What an annotation covers

An annotation covers the operand that follows it. It extends to the right as far as the surrounding operators allow, but never past and or or, nor past operators that bind more loosely: ? (the conditional), := and ... In practice:

ExpressionAnnotated part
/** A */ x.y in $list and zx.y in $list
a and /** A */ b > 1 or cb > 1
/** A */ $total > 100 ? "big" : "small"$total > 100 (the condition)
a - /** A */ b + cb (the operand of -)
$f(/** A */ items.price, 2)items.price (the argument)
/** A */ (x or y)the whole parenthesised block
$x := /** A */ $sum(values)$sum(values) (the bound value)

Annotations never change how an expression is parsed. The syntax tree of an annotated expression is the same as the tree of the expression with its comments removed; only the annotation metadata is added to one node.

To label a whole and/or chain, wrap it in parentheses, as the rule in fixtures/shipping-rule.jsonata does:

/** @label "Order category check" */
(
  /** @label "Electronics group" */ ('electronic' in items.type and $electronics > $total * 0.3)
  or
  /** @label "Blue items group" */ ('blue' in items.color and $blueTax < $averageTax)
)

Warnings

Annotation problems are warnings: they are reported, but they never stop compilation or evaluation.

CodeWhen
W0101The annotation precedes something that is not an expression, such as an operator, a closing bracket, or the end of the input
W0102Unknown tag, such as @severity
W0103A tag value is not a quoted JSON string; the rest of the line is used as the value

Run jlens check to see them:

$ jlens check '/** @label "Total" @severity "high" */ $sum(items.price) > 100'
warning[W0102]: Unknown annotation tag @severity; expected @label, @message or @id
 --> <expr>:1:20
  |
1 | /** @label "Total" @severity "high" */ $sum(items.price) > 100
  |                    ^^^^^^^^^
<expr>: 0 error(s), 1 warning(s)

In the API

After parsing, the annotation is available on the annotated node as Node::annotation. It also appears in the JSON form of the syntax tree (jlens ast):

{ "type": "binary", "op": "in", "annotation": { "label": "Authorized customer", "span": { … } } }

The audit guide explains how annotations appear in audit trees.