@stll/template-conditions 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ functions usable on both backend (Bun) and frontend (browser).
7
7
 
8
8
  Boolean conditions are evaluated through the canonical
9
9
  [`@stll/conditions`](https://github.com/stella/stella/tree/main/packages/conditions)
10
- AST and its single evaluator, so template `{{#if ...}}` conditionals share one
10
+ AST and its single evaluator, so template `{% if ... %}` conditionals share one
11
11
  set of operators and semantics with the rest of the system. This package owns
12
12
  the template surface: the marker grammar, the string parser, the named-condition
13
13
  resolver, the numeric expression evaluator, and the no-code builder serializer.
package/dist/index.d.ts CHANGED
@@ -6,6 +6,22 @@ declare const resolvePath: (path: string, data: Record<string, unknown>) => unkn
6
6
  //#region src/parse.d.ts
7
7
  declare const parseCondition: (expression: string) => ConditionNode$1 | null;
8
8
  //#endregion
9
+ //#region src/referenced-paths.d.ts
10
+ /**
11
+ * What a `{% if %}` expression reads. Structural, not evaluative: the paths a
12
+ * condition depends on, for callers that decide something about the condition
13
+ * itself rather than about a document being filled.
14
+ */
15
+ /**
16
+ * Every field path an expression reads, in source order.
17
+ *
18
+ * `null` says the expression does not answer the question: it did not parse,
19
+ * or it reads an operand whose own references cannot be enumerated. A caller
20
+ * deciding what a condition depends on must not read that as "depends on
21
+ * nothing".
22
+ */
23
+ declare const referencedConditionPaths: (expression: string) => string[] | null;
24
+ //#endregion
9
25
  //#region src/compute.d.ts
10
26
  /**
11
27
  * Arithmetic evaluator for computed template fields.
@@ -122,22 +138,47 @@ declare const serializeCondition: (node: ConditionNode$1) => string;
122
138
  //#endregion
123
139
  //#region src/markers.d.ts
124
140
  /**
125
- * Canonical grammar for `{{...}}` template markers — the single source of truth
126
- * shared by the fill pipeline (api), the editor highlighter (folio), and the
127
- * read-only preview (web).
141
+ * Canonical grammar for the docxtpl dialect of Jinja — the single source of
142
+ * truth shared by the fill pipeline (api), the editor highlighter (folio), and
143
+ * the read-only preview (web).
144
+ *
145
+ * Two spans carry meaning: an output marker `{{ ... }}` prints a value, and a
146
+ * tag `{% ... %}` opens or closes a block. A tag may carry a docxtpl placement
147
+ * prefix — `{%p ... %}` owns its paragraph, `{%tr ... %}` owns its table row —
148
+ * and a bare tag is placed by the leniency rules the consumer applies.
128
149
  *
129
150
  * Add a new directive to `MarkerMeta` and teach {@link classifyMarker} to
130
151
  * recognize it. Compile-time totality checks then require its ordered runtime
131
152
  * value and its block/inline disposition, while exhaustive consumer switches
132
153
  * fail until they handle it.
133
154
  *
134
- * No consumer should define its own `{{...}}` regex. The low-level pattern
155
+ * No consumer should define its own marker regex. The low-level pattern
135
156
  * factories below exist so handlers that mutate raw XML/text reuse the exact
136
157
  * same literals; recognizers should prefer the high-level scanner.
137
158
  */
159
+ /** A literal an author can write inside a filter or function call. */
160
+ type MarkerLiteral = string | number | boolean;
161
+ /** One argument of a filter call: positional, or `name=value`. */
162
+ type FilterArgument = {
163
+ kind: "positional";
164
+ value: MarkerLiteral;
165
+ } | {
166
+ kind: "keyword";
167
+ name: string;
168
+ value: MarkerLiteral;
169
+ };
170
+ /** One `| name` / `| name(args)` step of an output marker's filter chain. */
171
+ type FilterCall = {
172
+ name: FilterName;
173
+ args: readonly FilterArgument[];
174
+ };
175
+ /** The loop properties addressable inside a `{% for %}` body. */
176
+ declare const LOOP_PROPERTIES: readonly ["index", "index0", "first", "last", "length"];
177
+ type LoopProperty = (typeof LOOP_PROPERTIES)[number];
138
178
  type MarkerMeta = {
139
179
  kind: "placeholder";
140
180
  expr: string;
181
+ filters: readonly FilterCall[];
141
182
  } | {
142
183
  kind: "clause";
143
184
  name: string;
@@ -149,126 +190,241 @@ type MarkerMeta = {
149
190
  kind: "ref";
150
191
  key: string;
151
192
  } | {
152
- kind: "index";
153
- } | {
154
- kind: "count";
193
+ kind: "loop";
194
+ property: LoopProperty;
155
195
  } | {
156
196
  kind: "if";
157
197
  expr: string;
158
198
  } | {
159
- kind: "elseif";
199
+ kind: "elif";
160
200
  expr: string;
161
201
  } | {
162
202
  kind: "else";
163
203
  } | {
164
204
  kind: "endif";
165
205
  } | {
166
- kind: "each";
167
- expr: string;
206
+ kind: "for";
207
+ alias: string;
208
+ path: string;
209
+ /** Filters on the loop path configure the ARRAY: how many rows it takes
210
+ * and what to call it. */
211
+ filters: readonly FilterCall[];
168
212
  } | {
169
- kind: "endeach";
213
+ kind: "endfor";
170
214
  };
171
215
  type DirectiveKind = MarkerMeta["kind"];
172
- declare const DIRECTIVE_KINDS: readonly ["placeholder", "clause", "num", "ref", "index", "count", "if", "elseif", "else", "endif", "each", "endeach"];
216
+ declare const DIRECTIVE_KINDS: readonly ["placeholder", "clause", "num", "ref", "loop", "if", "elif", "else", "endif", "for", "endfor"];
173
217
  declare const DIRECTIVE_PLACEMENT: {
174
218
  readonly placeholder: "inline";
175
219
  readonly clause: "inline";
176
220
  readonly num: "inline";
177
221
  readonly ref: "inline";
178
- readonly index: "inline";
179
- readonly count: "inline";
222
+ readonly loop: "inline";
180
223
  readonly if: "block";
181
- readonly elseif: "block";
224
+ readonly elif: "block";
182
225
  readonly else: "block";
183
226
  readonly endif: "block";
184
- readonly each: "block";
185
- readonly endeach: "block";
227
+ readonly for: "block";
228
+ readonly endfor: "block";
186
229
  };
187
230
  type BlockDirectiveKind = { [TKind in DirectiveKind]: (typeof DIRECTIVE_PLACEMENT)[TKind] extends "block" ? TKind : never; }[DirectiveKind];
188
231
  /** Directives that occupy their own paragraph, in canonical grammar order. */
189
- declare const BLOCK_DIRECTIVE_KINDS: readonly ["if", "elseif", "else", "endif", "each", "endeach"];
232
+ declare const BLOCK_DIRECTIVE_KINDS: readonly ["if", "elif", "else", "endif", "for", "endfor"];
190
233
  declare const isBlockDirectiveKind: (kind: unknown) => kind is BlockDirectiveKind;
191
- /** Any `{{...}}` marker; group 1 is the inner text (untrimmed — callers trim). */
234
+ /**
235
+ * Every filter the dialect accepts on a fillable value. The names are the
236
+ * grammar's; which manifest property each one writes is decided by the field
237
+ * catalogue in the api, which is total over BOTH this list and the manifest
238
+ * keys, so neither side can gain an entry the other has not decided.
239
+ */
240
+ declare const FILTER_NAMES: readonly ["text", "number", "date", "checkbox", "select", "options_from", "label", "hint", "required", "pattern", "min", "max", "min_length", "max_length", "min_items", "max_items", "ai", "lookup", "matter", "contact", "party", "attorney", "firm", "formula", "condition"];
241
+ type FilterName = (typeof FILTER_NAMES)[number];
242
+ /** Normalise one marker's inner text: typographic quotes become straight
243
+ * quotes, fixed-width spaces become ordinary ones. */
244
+ declare const normalizeMarkerInner: (inner: string) => string;
245
+ /**
246
+ * What an output marker holds between its braces: anything but a brace or a
247
+ * double quote, plus whole double-quoted arguments, whose braces are content
248
+ * rather than the marker's own delimiters — `pattern("^[0-9]{5}$")` is a regex,
249
+ * not a nested marker. An apostrophe stays an ordinary character, so
250
+ * `label("Owner's")` reads as it always did.
251
+ *
252
+ * The alternatives cannot overlap (the plain one excludes every character the
253
+ * quoted one may start with) and a quoted run's end is forced by its body, so
254
+ * the scan stays linear on adversarial input: a `{{` with no closing `}}`
255
+ * costs one failed pass, not a polynomial retry.
256
+ */
257
+ declare const MARKER_OUTPUT_BODY: string;
258
+ /** What a tag holds between `{%` and `%}`: anything but a brace, and a `%`
259
+ * only where it does not close the tag. */
260
+ declare const MARKER_STATEMENT_BODY: string;
261
+ /**
262
+ * Any marker span: an output `{{...}}` (group `output`) or a tag `{%...%}`
263
+ * (group `statement`, with the docxtpl placement in group `prefix`).
264
+ */
192
265
  declare const markerPattern: () => RegExp;
193
- /** Inline field / clause / numbering marker (inner allows the directive sigils). */
266
+ /** Inline value marker, including its filter chain; group `name` is the path
267
+ * the value comes from and group `filters` the untouched chain. */
194
268
  declare const placeholderPattern: () => RegExp;
195
- /** `{{@clause:Name}}` / `{{@clause:Name:v3}}` — `name` and `modifier` groups. */
269
+ /** `{{ clause("Name") }}` / `{{ clause("Name", "v3") }}`. */
196
270
  declare const clauseSlotPattern: () => RegExp;
197
- /** `{{@num:Key}}` — the `key` group. */
271
+ /** `{{ num("key") }}` — the `key` group. */
198
272
  declare const numPattern: () => RegExp;
199
- /** `{{@ref:Key}}` — the `key` group. */
273
+ /** `{{ ref("key") }}` — the `key` group. */
200
274
  declare const refPattern: () => RegExp;
201
- /** `{{@index}}` — the 1-based position within the innermost enclosing loop. */
202
- declare const indexPattern: () => RegExp;
203
- /** `{{@count}}` — the item count of the innermost enclosing loop. */
204
- declare const countPattern: () => RegExp;
275
+ /** `{{ loop.index }}` and its siblings — the `property` group. */
276
+ declare const loopPattern: () => RegExp;
205
277
  /** Cheap presence test for any numbering marker (no capture). */
206
278
  declare const hasNumberingPattern: () => RegExp;
207
- /** Cheap presence test for any block directive (no capture). */
279
+ /** Cheap presence test for any tag (no capture). */
208
280
  declare const hasBlockDirectivePattern: () => RegExp;
209
- /** A whole line that is a single block directive — `tag` and `expr` groups. */
281
+ /** A whole line that is a single block tag — `prefix`, `tag` and `expr`. */
210
282
  declare const blockDirectiveLinePattern: () => RegExp;
211
283
  /** Whether `value` is a valid field path per the marker grammar (dotted
212
284
  * segments of letters/digits/underscore/dash — no brackets or spaces). */
213
285
  declare const isFieldPath: (value: string) => boolean;
214
286
  /** Whether `value` is safe to use as a dotted object path. */
215
287
  declare const isSafeFieldPath: (value: string) => boolean;
216
- /** Whether `value` is a valid name for authoring a `{{@clause:NAME}}` slot:
217
- * the intersection of the clause-slot parse grammar and the fill pipeline's
218
- * placeholder charset, so every accepted name round-trips through fill. */
288
+ /** Whether `value` is a valid name for authoring a `clause("NAME")` slot. */
219
289
  declare const isClauseSlotName: (value: string) => boolean;
290
+ /** Which brace pair carried the marker text. */
291
+ type MarkerForm = "output" | "statement";
292
+ /** Where a tag's docxtpl prefix says the tag belongs. `"none"` is a bare tag,
293
+ * placed by the consumer's leniency rules, and every output marker. */
294
+ type MarkerPrefix = "none" | "paragraph" | "row";
220
295
  /**
221
- * Classify the inner text of a `{{...}}` marker. Returns `null` when the text is
222
- * not a recognized directive (so a recognizer can leave stray braces alone).
296
+ * Classify the inner text of one marker. Returns `null` when the text is not a
297
+ * recognized directive (so a recognizer can leave stray braces alone).
223
298
  */
224
- declare const classifyMarker: (innerRaw: string) => MarkerMeta | null;
299
+ declare const classifyMarker: (innerRaw: string, form?: MarkerForm) => MarkerMeta | null;
225
300
  type ScannedMarker = {
226
- /** Offset of `{{` in the source text. */
301
+ /** Offset of the opening brace pair in the source text. */
227
302
  start: number;
228
- /** Offset just past `}}`. */
303
+ /** Offset just past the closing brace pair. */
229
304
  end: number;
230
- /** The full matched marker, e.g. `{{@num:scope}}`. */
305
+ /** The full matched marker, e.g. `{% endfor %}`. */
231
306
  raw: string;
232
- /** The (untrimmed) inner text. */
307
+ /** The normalized, trimmed inner text. */
233
308
  inner: string;
309
+ form: MarkerForm;
310
+ /** The docxtpl placement prefix the author wrote. */
311
+ prefix: MarkerPrefix;
234
312
  meta: MarkerMeta;
235
313
  };
236
314
  /**
237
- * Find every recognized `{{...}}` marker in `text`, in document order, with
238
- * offsets and classified metadata. Unrecognized `{{...}}` spans are skipped.
315
+ * Find every recognized marker in `text`, in document order, with offsets and
316
+ * classified metadata. Unrecognized spans are skipped.
239
317
  */
240
318
  declare const scanMarkers: (text: string) => ScannedMarker[];
241
- /** A `{{...}}` span that looks like a marker but classifies to nothing. */
319
+ /**
320
+ * Rewrite every `{{ ... }}` output marker in `text` through `replace`, which
321
+ * receives the whole marker and its raw inner text and returns the replacement.
322
+ * Tags are left untouched. The single scan is the same one the classifier uses,
323
+ * so a caller that rewrites marker text never needs its own regex.
324
+ */
325
+ declare const replaceOutputMarkers: (text: string, replace: (raw: string, inner: string) => string) => string;
326
+ /**
327
+ * The values-map key one marker substitutes from, or `null` when the marker is
328
+ * not a substitution target (a tag, or a `loop.*` token the loop expander
329
+ * resolves first). One function so the key discovery writes is the key the
330
+ * patcher reads.
331
+ */
332
+ declare const substitutionKey: (meta: MarkerMeta) => string | null;
333
+ /** The values-map key for one clause slot. */
334
+ declare const clauseSlotKey: (name: string, version?: string) => string;
335
+ /** A marker-shaped span that classifies to nothing. */
242
336
  type InvalidMarker = {
243
- /** Offset of `{{` in the source text. */
337
+ /** Offset of the opening brace pair in the source text. */
244
338
  start: number;
245
- /** Offset just past `}}`. */
339
+ /** Offset just past the closing brace pair. */
246
340
  end: number;
247
341
  /** The full matched span, e.g. `{{my field}}`. */
248
342
  raw: string;
249
- /** The (trimmed) inner text. */
343
+ /** The normalized, trimmed inner text. */
250
344
  inner: string;
345
+ form: MarkerForm;
251
346
  };
252
347
  /**
253
- * Find every `{{...}}` span whose inner text is NOT a recognized directive —
254
- * near-misses an author clearly meant as markers but that every recognizer
255
- * skips, so they print literally at fill time (e.g. `{{my field}}` with a
256
- * space, `{{@clause:}}` with no name). Offsets and raw text are shaped like
257
- * {@link scanMarkers} results, minus the classified `meta`. This is the exact
348
+ * Find every marker-shaped span whose inner text is NOT a recognized
349
+ * directive — near-misses an author clearly meant as markers but that every
350
+ * recognizer skips, so they print literally at fill time. This is the exact
258
351
  * complement of {@link scanMarkers}: a span is in one list or the other.
259
352
  */
260
353
  declare const scanInvalidMarkers: (text: string) => InvalidMarker[];
354
+ /** The loop alias the codemod (and the legacy-marker message) generates for a
355
+ * `{{#each path}}`: the singular of the last plain-word segment, else `item`. */
356
+ declare const legacyLoopAlias: (path: string) => string;
357
+ /** Translate an old-dialect condition expression to the Jinja surface:
358
+ * `!a` becomes `not a`, `a contains "x"` becomes `"x" in a`. */
359
+ declare const translateLegacyExpression: (expr: string) => string;
360
+ /**
361
+ * The Jinja marker that replaces one old-dialect marker's inner text, or
362
+ * `null` when the text is not old-dialect. Shared by the rejection message and
363
+ * the codemod, so the fix an author is told to make is the fix the script
364
+ * would have made.
365
+ */
366
+ declare const legacyMarkerReplacement: (innerRaw: string) => string | null;
367
+ /**
368
+ * Why a marker-shaped span misses the grammar in a way that names the
369
+ * authoring mistake rather than merely failing to classify. Consumers derive
370
+ * their own warning codes from {@link MARKER_DEFECT_KINDS} so the two never
371
+ * drift.
372
+ */
373
+ type MarkerDefectKind = "legacy_marker" | "unsupported_tag" | "unknown_filter" | "python_expression" | "bracket_index";
374
+ declare const MARKER_DEFECT_KINDS: readonly ["legacy_marker", "unsupported_tag", "unknown_filter", "python_expression", "bracket_index"];
375
+ /** One diagnosis of a rejected span: the kind plus the construct to quote. */
376
+ type MarkerDefect = {
377
+ kind: MarkerDefectKind;
378
+ /** The construct the author wrote that the grammar refuses. */
379
+ construct: string;
380
+ /** The Jinja that replaces it, when the mistake has one exact fix. */
381
+ replacement?: string;
382
+ };
383
+ /**
384
+ * Name the authoring mistake behind one marker's inner text, or `null` when
385
+ * the text is a recognized directive ({@link classifyMarker} accepts it) or an
386
+ * unrecognizable span with no specific diagnosis (`{{my field}}`).
387
+ */
388
+ declare const classifyMarkerDefect: (innerRaw: string, form?: MarkerForm) => MarkerDefect | null;
261
389
  /** Exhaustiveness guard — pass the discriminant in a `switch` default branch. */
262
390
  declare const assertNever: (value: never) => never;
263
391
  //#endregion
392
+ //#region src/row-blocks.d.ts
393
+ /** One end of a row block, addressed within the row that was scanned. */
394
+ type RowBlockMarker = {
395
+ /** Index into the row's cells. */
396
+ cellIndex: number;
397
+ /** Index into that cell's paragraphs. */
398
+ paragraphIndex: number;
399
+ marker: ScannedMarker;
400
+ };
401
+ type RowBlockPair = {
402
+ open: RowBlockMarker;
403
+ close: RowBlockMarker;
404
+ };
405
+ /**
406
+ * The one row block a table row declares in the lenient form, or `null`.
407
+ *
408
+ * `cells` is the row's cells in document order, each as its paragraph texts in
409
+ * document order.
410
+ *
411
+ * A row qualifies when exactly two block markers reach outside their own
412
+ * paragraph, they are a matching opener/closer pair, the closer sits in a LATER
413
+ * cell than the opener, the opener prefixes its cell and the closer suffixes
414
+ * its cell. Everything else — one half of a pair, two nested pairs, a stray
415
+ * `{% else %}`, a pair wrapping the paragraphs of a single cell — is left to the
416
+ * engine's existing structure errors rather than guessed at.
417
+ */
418
+ declare const detectRowBlockPair: (cells: readonly (readonly string[])[]) => RowBlockPair | null;
419
+ //#endregion
264
420
  //#region src/index.d.ts
265
421
  type NamedCondition = {
266
422
  name: string;
267
- /** The `{{#if}}` expression string. Authoritative unless `node` is set. */
423
+ /** The `{% if %}` expression string. Authoritative unless `node` is set. */
268
424
  expression: string;
269
425
  /**
270
426
  * The canonical AST, set when the condition cannot round-trip through the
271
- * expression string — i.e. it uses a `formula` operand the `{{#if}}` grammar
427
+ * expression string — i.e. it uses a `formula` operand the `{% if %}` grammar
272
428
  * has no syntax for. When present it is evaluated directly and `expression`
273
429
  * is ignored.
274
430
  */
@@ -282,16 +438,17 @@ type NamedCondition = {
282
438
  */
283
439
  declare const MAX_CONDITION_DEPTH = 50;
284
440
  /**
285
- * Evaluate a template `{{#if ...}}` condition. The expression is parsed into
441
+ * Evaluate a template `{% if ... %}` condition. The expression is parsed into
286
442
  * the canonical `@stll/conditions` AST and evaluated by the shared evaluator.
287
443
  *
288
- * Supports comparisons (`==`, `!=`, `>`, `<`, `>=`, `<=`), `contains`,
289
- * truthiness, negation (`!`), logical operators (`and`, `or`), parentheses,
290
- * dotted paths, numeric underscores, and named-condition references.
444
+ * Supports comparisons (`==`, `!=`, `>`, `<`, `>=`, `<=`), membership
445
+ * (`"x" in path`), presence (`is defined` / `is not defined`), truthiness,
446
+ * negation (`not`), logical operators (`and`, `or`), parentheses, dotted
447
+ * paths, numeric underscores, and named-condition references.
291
448
  *
292
449
  * An empty or unparseable expression is `false` ("no condition" never gates a
293
450
  * branch true).
294
451
  */
295
452
  declare const evaluateCondition: (expression: string, data: Record<string, unknown>, namedConditions?: readonly NamedCondition[], _resolved?: ReadonlySet<string>, _depth?: number) => boolean;
296
453
  //#endregion
297
- export { BLOCK_DIRECTIVE_KINDS, type BlockDirectiveKind, type CompareNode, type CompareOp, type Condition, type ConditionNode, DATE_FORMAT_EXAMPLE_ISO, DATE_FORMAT_STYLES, DIRECTIVE_KINDS, type DateFormatStyle, type DeterministicFieldConfig, type DirectiveKind, type FieldDateFormat, type GroupNode, type InvalidMarker, MAX_CONDITION_DEPTH, type MarkerMeta, NUMERIC_FUNCTION_NAMES, NamedCondition, type NumericFunctionName, type Operand, type PartConfig, type PredicateNode, type PredicateOp, type ScannedMarker, assertNever, blockDirectiveLinePattern, classifyMarker, clauseSlotPattern, conditionHasFormula, conditionNodeSchema, conditionSchema, countPattern, emptyCondition, evaluateCondition, evaluateNumericExpression, formatDate, hasBlockDirectivePattern, hasNumberingPattern, indexPattern, isBlockDirectiveKind, isClauseSlotName, isFieldPath, isSafeFieldPath, markerPattern, numPattern, parseCondition, placeholderPattern, refPattern, renderComposite, renderDeterministicFieldValue, resolvePath, scanInvalidMarkers, scanMarkers, serializeCondition };
454
+ export { BLOCK_DIRECTIVE_KINDS, type BlockDirectiveKind, type CompareNode, type CompareOp, type Condition, type ConditionNode, DATE_FORMAT_EXAMPLE_ISO, DATE_FORMAT_STYLES, DIRECTIVE_KINDS, type DateFormatStyle, type DeterministicFieldConfig, type DirectiveKind, FILTER_NAMES, type FieldDateFormat, type FilterArgument, type FilterCall, type FilterName, type GroupNode, type InvalidMarker, LOOP_PROPERTIES, type LoopProperty, MARKER_DEFECT_KINDS, MARKER_OUTPUT_BODY, MARKER_STATEMENT_BODY, MAX_CONDITION_DEPTH, type MarkerDefect, type MarkerDefectKind, type MarkerForm, type MarkerLiteral, type MarkerMeta, type MarkerPrefix, NUMERIC_FUNCTION_NAMES, NamedCondition, type NumericFunctionName, type Operand, type PartConfig, type PredicateNode, type PredicateOp, type RowBlockMarker, type RowBlockPair, type ScannedMarker, assertNever, blockDirectiveLinePattern, classifyMarker, classifyMarkerDefect, clauseSlotKey, clauseSlotPattern, conditionHasFormula, conditionNodeSchema, conditionSchema, detectRowBlockPair, emptyCondition, evaluateCondition, evaluateNumericExpression, formatDate, hasBlockDirectivePattern, hasNumberingPattern, isBlockDirectiveKind, isClauseSlotName, isFieldPath, isSafeFieldPath, legacyLoopAlias, legacyMarkerReplacement, loopPattern, markerPattern, normalizeMarkerInner, numPattern, parseCondition, placeholderPattern, refPattern, referencedConditionPaths, renderComposite, renderDeterministicFieldValue, replaceOutputMarkers, resolvePath, scanInvalidMarkers, scanMarkers, serializeCondition, substitutionKey, translateLegacyExpression };