@omgbase/oqx 0.10.2 โ†’ 0.12.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/CHANGELOG.md CHANGED
@@ -4,6 +4,168 @@ All notable changes to `@omgbase/oqx` are recorded here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/) (pre-1.0: a minor bump may break).
6
6
 
7
+ ## [0.12.0] - 2026-09-25
8
+
9
+ **OQX is now a language with a specification and two implementations.** The
10
+ spec lives at `spec/oqx` in the monorepo: `GRAMMAR.md`, `SEMANTICS.md`, and
11
+ 835 executable fixtures in 28 files that this package and the Rust
12
+ crate `oqx` both run. **Versioning changed accordingly:** the language version
13
+ is `spec/oqx/VERSION` (`0.12`) and every implementation's version is
14
+ `<language>.<patch>`, so `@omgbase/oqx 0.12.x` and the `oqx` crate `0.12.x`
15
+ both mean "the 0.12 language"; the patch digit is per implementation. The
16
+ package exports `LANGUAGE_VERSION`.
17
+
18
+ Everything below follows one principle, applied without exception: **least
19
+ surprise.** Where the reference behaved by JavaScript accident, it now does
20
+ what a careful user would predict.
21
+
22
+ ### Changed (breaking) โ€” semantics
23
+ - **Identity is structural.** `distinct` over unprojected rows and `follow`
24
+ cycle detection/dedup use the `id` property when present, else the row's
25
+ structural value (deep equality, key order ignored). Previously identities
26
+ were stringified, so every id-less object collided as `[object Object]`
27
+ (`count distinct { }` over two different objects gave 1; `follow` over
28
+ id-less nodes marked every successor a cycle) and `1` equalled `"1"`.
29
+ - **No cross-type ordering.** `< <= > >=` and range coverage order only
30
+ number/number and string/string; every other pair, including booleans and
31
+ anything absent, is false. `1 < "2"` and `"3" in 1..5` were true.
32
+ - **Strings order and measure by Unicode code point**, not UTF-16 code unit:
33
+ `"๐Ÿ˜€".size()` is 1 and astral characters sort after the BMP.
34
+ - **Own enumerable properties only.** Property reads (`name`, `.name`,
35
+ `^name`), `has()`, `in` over an object, and `entries()` never see inherited
36
+ members (`has(toString)` is false). Arrays expose only their integer
37
+ indices โ€” `.length` is absent; use `size()`. Primitives have no properties.
38
+ - **Absent propagates.** Any arithmetic with an absent operand is absent,
39
+ including `"a" + missing` (was `"aundefined"`) and `null + 1` (was `1`).
40
+ `.lower()` / `.upper()` on absent are absent (were `"undefined"` /
41
+ `"NULL"`).
42
+ - **Regex is an OQX concern.** An invalid `matches()` pattern raises
43
+ `OqxError` (`eval`, `invalid regular expression`) instead of a raw
44
+ `SyntaxError`. Lookaround and backreferences are rejected (`not supported in
45
+ OQX`) so the dialect is the portable intersection.
46
+ - **Ranges never appear in results.** Projecting a range value is an error
47
+ (`a range โ€ฆ cannot appear in a result`); no range covers an absent value,
48
+ however open.
49
+ - **`&&` and `||` evaluate strictly left to right** with short-circuit; the
50
+ engine no longer reorders conjuncts by cost, so `false && bogus()` never
51
+ errors and `true && bogus()` always does.
52
+ - **`single` reports the true row count** in its error.
53
+ - **`$ordinal` path components compare as values** (numbers numerically,
54
+ before strings), not as text: `[1, 9, 10]`, not `[1, 10, 9]`.
55
+ - **Bindings out of range** (`run(query, { values })` with fewer values than
56
+ the query references) raise `OqxError` instead of reading `undefined`.
57
+
58
+ ### Changed (breaking) โ€” grammar
59
+ - **`!` has one precedence everywhere:** a prefix operator binding tighter
60
+ than comparison, applied to the operand right after it. `where !a == b` is
61
+ `(!a) == b` (it negated the whole comparison before); `!jobs exists { โ€ฆ }`
62
+ and `!(a == b)` work as written.
63
+ - **Parenthesized scalars work in `where`:** `where (a + 1) > 2` was a parse
64
+ error; a group followed by an operator is a scalar.
65
+ - **`follow distinct` requires a relation** (`follow distinct { depth 2 }` no
66
+ longer treats `distinct` as the relation name); `follow ^rel` is a clear
67
+ error.
68
+ - **Open-ended ranges stop at clause words** (`in ..5 order by a`); a range
69
+ with no bound at all is an error.
70
+ - **Duplicate projection names are an error** (`a: 1, a: 2` last-wins is
71
+ gone), including a dotted default key colliding with an alias.
72
+ - **Malformed numerals are lex errors:** `1e`, `1e+`, `1.`, `.5`, `xs.0`.
73
+ `1..5`, `1...5`, `..5`, `1..`, `1.5..2.5` are unchanged.
74
+ - **Top-level `limit ^n` / `offset ^n`** are parse errors (`no enclosing
75
+ scope`); they were always absent and failed at run time.
76
+ - **`true` / `false` / `null` are literals in every position;** as a receiver
77
+ or follow relation they error instead of resolving a root by that name.
78
+ - **Lifts (`^name:`) outside a where-position `collect`** are parse errors
79
+ instead of silently doing nothing.
80
+ - **Chained comparisons** (`a == b == c`) error clearly; **stray tokens** after
81
+ a query or in a block get a located message; clause words without their
82
+ clause (`order age`, bare `follow`, `limit x`) say what they need.
83
+
84
+ ### Changed (breaking) โ€” regular expressions
85
+ - **`matches()` compiles the OQX regex baseline, not a JavaScript `RegExp`.**
86
+ The baseline (SEMANTICS ยง11) is a fixed grammar โ€” literals and escaped
87
+ metacharacters, `\n \t \r \f \v`, `\uXXXX` / `\u{โ€ฆ}`, the classes
88
+ `\d \D \w \W \s \S` with spec-fixed sets (`\d` = `[0-9]`, `\w` =
89
+ `[A-Za-z0-9_]`, `\s` = one listed set), `\b \B` over that `\w`, bracket
90
+ classes, `.`, `^ $`, the quantifiers and their lazy forms, `|`, `(โ€ฆ)`,
91
+ `(?:โ€ฆ)`, `(?<name>โ€ฆ)` โ€” and the pattern is validated and rewritten before
92
+ `RegExp` sees it (`src/regex.ts`). **The line terminator is `\n` alone**
93
+ for `.` and for `m`-mode anchors (`\r`, U+2028, U+2029 are ordinary
94
+ characters), and `.` consumes a code point. Every construct outside the
95
+ grammar is an eval error naming it (`an inline flag (?i) is not supported in
96
+ OQX regular expressions`): lookaround and backreferences (already rejected),
97
+ now also inline flags `(?i)` / `(?i:โ€ฆ)`, `\p{โ€ฆ}`, `\x..`, `\c.`, octal
98
+ escapes, identity escapes of non-metacharacters, possessive quantifiers,
99
+ atomic and comment groups, `(?P<โ€ฆ>)`, `\A \z \Z \G \K \Q \E`, POSIX and
100
+ nested classes, class set operations, `[\b]`. Malformed patterns that
101
+ `RegExp` tolerated (`a{`, a lone `]`, `a**`, `[]`, `[^]`, `(?<n>โ€ฆ)` twice)
102
+ are `invalid regular expression`.
103
+ - **Flags are an explicit second argument**: `matches(pattern, flags)` with
104
+ `flags` a string of distinct letters from `i` (simple case folding, so `รฉ`
105
+ matches `ร‰`), `m`, `s`. Anything else is an eval error: `unknown regex
106
+ flag` (`"g"`, `"I"`, a non-string) or `duplicate regex flag`. There is no
107
+ inline flag syntax.
108
+ - **Engine-native dialects are a host opt-in, not query syntax.**
109
+ `DataContext` gains an optional `regexDialect: "oqx" | "native"` (default
110
+ `"oqx"`); `new DefaultContext(roots, { regexDialect: "native" })` hands
111
+ patterns to `RegExp` unvalidated with the `u` flag plus the given flags โ€”
112
+ implementation-defined, not portable, untested by the spec. `DefaultContext`
113
+ answers `matches` itself so it can read the dialect; `BUILTIN_METHODS.matches`
114
+ stays the baseline, and `semantics.regexMatches(recv, args, dialect)` is the
115
+ helper for custom contexts. `compileRegex(pattern, flags?, dialect?)` and the
116
+ `RegexDialect` / `RegexFlags` types are exported.
117
+ - Fixtures: the `matches` cases moved from `strings.json` to a new
118
+ `regex.json` (21 moved, 98 added), pinning each baseline construct, the
119
+ `\n`-only line terminator, ASCII `\d \w \b`, the `\s` set, code-point `.`,
120
+ flags, and one rejection per construct.
121
+
122
+ ### Documentation
123
+ - Integer-like object keys enumerate first in JavaScript before OQX sees the
124
+ object; this is a host fact the reference cannot undo, and fixtures must not
125
+ depend on the relative order of integer-like and other keys.
126
+
127
+ ## [0.11.0] - 2026-09-24
128
+
129
+ ### Changed (breaking)
130
+ - **Clause order is fixed** (omgbase ADR-020). Within one clause body โ€” the
131
+ top level or any consumer block โ€” clauses appear at most once each, in
132
+ exactly this order: `select โ€ฆ from โ€ฆ where โ€ฆ follow โ€ฆ order by โ€ฆ limit N
133
+ offset N`. Every clause is optional except a top-level `from` (the
134
+ `<receiver> <consumer> { โ€ฆ }` form supplies its own source). A clause out
135
+ of order is a parse error that names the order (`` `select` must come
136
+ before `from` โ€” OQX clause order is select, from, where, follow, order by,
137
+ limit, offset ``); a second `from` in one body is a duplicate. The
138
+ order-flexible grammar (`from ${people} select name where โ€ฆ`, `limit 1 name
139
+ from โ€ฆ`, `offset 1 limit 1`) no longer parses.
140
+ - **Only `select` may drop its keyword, and only when it is the first clause
141
+ written** (`name, age from people`). Every other clause always carries its
142
+ keyword, so a predicate is never implicit: the `looksLikePredicate` shape
143
+ heuristic is gone. **A block now needs `where`** โ€” `jobs exists { where
144
+ !end }` โ€” and a bare comparison in leading position (`exists { age > 50 }`,
145
+ `age > 50 from people`) is a parse error that points at `where`. A bare name
146
+ in leading position projects, as before (`count { active }` selects
147
+ `active`).
148
+ - **The `from docs count` footgun is closed.** A bare run after `from` is an
149
+ error; `from people count` fails at `count` with a hint โ€” write
150
+ `people count { โ€ฆ }` for the whole-query consumer, or `select count from โ€ฆ`
151
+ to project a field named `count`. (Previously it silently projected the
152
+ field.)
153
+
154
+ ### Added
155
+ - **`where` may reference the same body's `select` aliases.** Implemented as a
156
+ compile-time inline rewrite (not a second execution pass): after a body is
157
+ parsed, each bare identifier in its `where` that names an alias is replaced
158
+ by the alias's expression, so the engine and any pushdown planner see an
159
+ ordinary predicate. An alias shadows a same-named row field inside `where`;
160
+ an alias's own name inside its own expression is still the field
161
+ (`name: name.upper()` is not recursive); a chain of aliases that returns to
162
+ one being resolved (`a: b, b: a โ€ฆ where a`) is a parse error; an alias whose
163
+ value is a `collect`/`first`/`single { โ€ฆ }` block may stand alone as a
164
+ where leaf (non-empty test) but not inside an expression. Each body rewrites
165
+ only against its own `select` (nested blocks and `follow` blocks are their
166
+ own scopes); `^name` is never an alias. `order by` is unchanged โ€” it reads
167
+ row fields, not aliases.
168
+
7
169
  ## [0.10.2] - 2026-09-23
8
170
 
9
171
  Moved into the omgbase monorepo (`packages/oqx`, via `git subtree` โ€” full
package/README.md CHANGED
@@ -13,6 +13,10 @@ JavaScript tagged template.
13
13
  > is published independently under its own version line and has **zero runtime
14
14
  > dependencies** โ€” it is usable without omgbase, and omgbase code is never
15
15
  > imported here (omgbase binds it from the outside via the `DataContext` seam).
16
+ > OQX is a language with more than one implementation; the specification is
17
+ > [`spec/oqx`](https://github.com/omgbase/omgbase/tree/main/spec/oqx). The
18
+ > language version is the package version's `major.minor` (`LANGUAGE_VERSION`,
19
+ > `"0.12"`); the patch digit is this implementation's own.
16
20
  > Requirements: Node โ‰ฅ 22.13 for `@omgbase/oqx/sqlite`; Node โ‰ฅ 22.18 to run the
17
21
  > test suite (see [Requirements](#requirements)).
18
22
 
@@ -30,7 +34,7 @@ const company = "Globocorp";
30
34
  const employees = oqx`
31
35
  name, id, title
32
36
  from ${people}
33
- where jobs exists { employer == ${company} && !end_date }
37
+ where jobs exists { where employer == ${company} && !end_date }
34
38
  `;
35
39
  // โ†’ [{ name: "Bob", id: 124, title: "Engineer" }, โ€ฆ] (current Globocorp employees)
36
40
  ```
@@ -46,18 +50,30 @@ template's identity and re-runs with fresh bindings each call.
46
50
 
47
51
  ## Language tutorial
48
52
 
49
- A query has, in spirit, the shape below โ€” but at the top level the clauses are
50
- **order-flexible**, so you can lead with the projection (SQL-style) or with
51
- `from`, whichever reads better:
53
+ A query is a list of clauses in a **fixed order**. Each clause appears at most
54
+ once, and every one is optional except `from` at the top level:
52
55
 
53
56
  ```
54
- [ [select] projection ] name, id, title: label
57
+ [ [select] projection ] name, id, title: label (or: select distinct โ€ฆ, โ€ฆ values)
55
58
  from <collection> from ${people}
56
- [ where <predicate> ] where age >= 18 && jobs exists { !end }
57
- [ order by <expr> โ€ฆ ] order by age desc, name
59
+ [ where <predicate> ] where age >= 18 && jobs exists { where !end }
58
60
  [ follow <relation> โ€ฆ ] follow children { depth 4 }
61
+ [ order by <expr> โ€ฆ ] order by age desc, name
62
+ [ limit N ] limit 10
63
+ [ offset N ] offset 20
59
64
  ```
60
65
 
66
+ **`select` is the only keyword you may drop, and only when the projection comes
67
+ first** (`name, id from ${people}`). Every other clause always carries its
68
+ keyword โ€” in particular a predicate is never implicit, so a block filters with
69
+ `where` (`jobs exists { where !end }`), and `from ${people} count` is an error
70
+ rather than a projection of a field called `count`. Writing a clause out of
71
+ order is a parse error that names the order (`` `select` must come before
72
+ `from` โ€” OQX clause order is select, from, where, follow, order by, limit,
73
+ offset ``). The same body grammar applies inside every consumer block
74
+ (`collect { โ€ฆ }`, `exists { โ€ฆ }`, โ€ฆ), where `from` is optional because the
75
+ receiver supplies the rows.
76
+
61
77
  The examples below all use this dataset:
62
78
 
63
79
  ```js
@@ -113,8 +129,12 @@ An item that is not a plain navigation โ€” a call, arithmetic, a comparison โ€”
113
129
  no natural key, so it must be aliased (`n: size(jobs)`), unless the projection is
114
130
  in `values` mode (next).
115
131
 
116
- The `select` keyword is optional and works in any position โ€” `select name from โ€ฆ`
117
- is identical to `name from โ€ฆ` (and hosts `select distinct`, ยง6).
132
+ The `select` keyword may be dropped when the projection is the first clause โ€”
133
+ `select name from โ€ฆ` is identical to `name from โ€ฆ`. The keyword form hosts
134
+ `select distinct` (ยง6), and is the only way to write a projection whose first
135
+ item would otherwise be misread (there is no other keyword-less clause). The
136
+ projection always comes *before* `from`: `from ${people} select name` is an
137
+ ordering error.
118
138
 
119
139
  **`values` โ€” scalar projection.** Ordinarily every row projects to a record. Add
120
140
  `values` after a projection of exactly **one** item to get the value itself:
@@ -182,8 +202,9 @@ ask for one. As a plain value (not a source) `entries(x)` is an array of
182
202
 
183
203
  `where` filters rows. The predicate language has comparisons (`== != < <= > >=`),
184
204
  boolean operators (`&& || !`) with grouping `( )`, membership (`in`), arithmetic
185
- (`+ - * / %`), and bare truthiness. The `where` keyword is optional when the
186
- leading expression is clearly a predicate.
205
+ (`+ - * / %`), and bare truthiness. The `where` keyword is always written โ€” a
206
+ leading expression without it is a projection, and a bare comparison in that
207
+ position is an error that points you at `where`.
187
208
 
188
209
  ```js
189
210
  const min = 40;
@@ -231,6 +252,25 @@ non-range string yields an absent range, so `x in range(bad)` is just false.
231
252
  **Interpolations are always values, never syntax.** `where name == ${x}` compares
232
253
  against the value of `x`; a string in `x` can't inject operators or identifiers.
233
254
 
255
+ **`where` sees the `select` aliases.** A name defined in the same body's
256
+ projection may be used in its `where`; an alias shadows a same-named field
257
+ there. It is a compile-time rewrite โ€” the alias's expression is substituted
258
+ inline โ€” so it costs nothing at run time and a storage planner still sees an
259
+ ordinary predicate:
260
+
261
+ ```js
262
+ oqx`select name, adult: age >= 18 from ${people} where adult`; // โ‰ก where age >= 18
263
+ oqx`select name, active: age > 50 from ${people} where active`; // the ALIAS, not the field โ†’ Alice
264
+ oqx`select name, current: jobs collect { employer where !end } from ${people} where current`;
265
+ // a collect alias in predicate position means "non-empty" โ†’ Bob, Carol
266
+ ```
267
+
268
+ Inside an alias's own expression its name is still the field
269
+ (`name: name.upper()` is not recursive); a chain of aliases that comes back to
270
+ itself (`a: b, b: a โ€ฆ where a`) is a parse error. Each block rewrites only
271
+ against its *own* `select`; `^name` always reads an enclosing row's field, never
272
+ an alias. `order by` is not rewritten โ€” it reads row fields (ยง8).
273
+
234
274
  **Scoping: bare names are local, `^` reaches out.** A bare identifier resolves
235
275
  against the **current row only**. If the row lacks that property the value is
236
276
  absent โ€” it never falls through to an enclosing row. To correlate with an
@@ -242,10 +282,10 @@ const accounts = [
242
282
  { owner: "x", budget: 100, orders: [{ amount: 50 }, { amount: 150 }] },
243
283
  { owner: "y", budget: 200, orders: [{ amount: 250 }] },
244
284
  ];
245
- oqx`owner from ${accounts} where orders exists { amount > ^budget }`;
285
+ oqx`owner from ${accounts} where orders exists { where amount > ^budget }`;
246
286
  // [{ owner: "x" }, { owner: "y" }] โ€” `^budget` is the enclosing account's budget
247
287
 
248
- oqx`owner from ${accounts} where orders exists { amount > budget }`;
288
+ oqx`owner from ${accounts} where orders exists { where amount > budget }`;
249
289
  // [] โ€” a bare `budget` is the ORDER's own budget: absent, so `>` is false
250
290
  ```
251
291
 
@@ -311,13 +351,56 @@ oqx`name from ${people} where tags.contains("admin")`; // array membership
311
351
  - `.contains(v)` works on strings (substring) **and arrays** (an element equal
312
352
  to `v` under OQX's strict equality); on anything else it is false.
313
353
  - `.size()` / `size(x)` is the length of a string or array, the key count of
314
- an object, and 0 for absent; `.matches(re)` compiles its argument as a
315
- JavaScript `RegExp`.
354
+ an object, and 0 for absent.
355
+ - `.matches(re, flags?)` searches the receiver's text for a regular expression
356
+ written in the **OQX regex baseline** (below), unanchored unless the pattern
357
+ anchors. `flags` is an optional string of `i` (case-insensitive), `m` (`^`/`$`
358
+ also at newlines), `s` (`.` also matches `\n`); an absent receiver is `false`.
316
359
 
317
360
  Anything not in these tables is an **eval error** (`unknown function 'f(โ€ฆ)'` /
318
361
  `unknown method '.m(โ€ฆ)'`) โ€” see [custom functions](#custom-functions-and-methods)
319
362
  for adding your own.
320
363
 
364
+ #### The regex baseline
365
+
366
+ OQX is a language with more than one implementation, so `matches()` does not
367
+ expose the host's `RegExp`. It compiles a fixed, portable dialect whose every
368
+ construct has one spec-defined meaning (`spec/oqx/SEMANTICS.md` ยง11):
369
+
370
+ - literals; escaped metacharacters `\. \* \+ \? \( \) \[ \] \{ \} \| \^ \$ \\ \/ \-`;
371
+ `\n \t \r \f \v`; `\uXXXX` (four hex digits) and `\u{Xโ€ฆ}` (any code point);
372
+ - `\d` = `[0-9]`, `\w` = `[A-Za-z0-9_]`, `\s` = JavaScript's white space (one
373
+ listed set), `\D \W \S` their complements, `\b \B` word boundaries over that
374
+ `\w`; bracket classes `[โ€ฆ]` / `[^โ€ฆ]` with ranges;
375
+ - `.` = any code point except `\n` (the line terminator is `\n` alone โ€” `\r`
376
+ is an ordinary character); anchors `^ $`; quantifiers `* + ? {n} {n,} {n,m}`
377
+ and their lazy `?` forms; alternation `|`; groups `(โ€ฆ)`, `(?:โ€ฆ)`, `(?<name>โ€ฆ)`.
378
+
379
+ Everything else โ€” lookaround, backreferences, inline flags `(?i)`, `\p{โ€ฆ}`,
380
+ `\x..`, octal, possessive quantifiers, POSIX classes, `\A \z` โ€ฆ โ€” is an
381
+ `OqxError` (stage `eval`) naming the construct: `an inline flag (?i) is not
382
+ supported in OQX regular expressions`. A pattern that is malformed within the
383
+ baseline is `invalid regular expression โ€ฆ`; a bad flag is `unknown regex flag`
384
+ / `duplicate regex flag`. The pattern is validated and rewritten before
385
+ `RegExp` sees it (`src/regex.ts`), so JavaScript-only behavior never leaks.
386
+
387
+ ```js
388
+ oqx`name from ${people} where title.matches("^(eng|dir)", "i")`; // Bob, Alice
389
+ oqx`x: "a\nb".matches("a.b") from ${one}`; // false: . stops at \n
390
+ oqx`x: "a\nb".matches("a.b", "s") from ${one}`; // true
391
+ oqx`x: "ูฃ".matches(${"\\d"}) from ${one}`; // false: \d is ASCII
392
+ oqx`x: "รฉ".matches(${"^\\u{e9}$"}) from ${one}`; // true
393
+ ```
394
+
395
+ A pattern with backslashes is easiest to pass as a binding (`${"\\d"}`): the
396
+ template's own escaping and the OQX string literal's `\<c>` escape would each
397
+ consume one level otherwise (`"\\\\d"` in the template is `\d` to the regex).
398
+
399
+ A host that wants JavaScript's own dialect can opt in per context:
400
+ `new DefaultContext(roots, { regexDialect: "native" })` hands patterns to
401
+ `RegExp` unvalidated (with the `u` flag plus the given flags). That is
402
+ implementation-defined and not portable to the Rust implementation.
403
+
321
404
  ### 5. Consumers
322
405
 
323
406
  A consumer shapes a result set. There are six:
@@ -333,8 +416,9 @@ A consumer shapes a result set. There are six:
333
416
 
334
417
  The bare `from โ€ฆ ` form is always `collect`. To reduce the **whole** query with a
335
418
  different consumer, use the directive form `<source> <consumer> { <body> }` โ€” note
336
- this is *not* SQL: `count from people` would project a field called `count`, whereas
337
- a real reduction is a directive:
419
+ this is *not* SQL: `count from people` projects a field called `count`, and
420
+ `from people count` is a parse error that points at the directive form. A real
421
+ reduction is a directive:
338
422
 
339
423
  ```js
340
424
  oqx`${people} exists { where active }`; // true
@@ -342,8 +426,11 @@ oqx`${people} count { where active }`; // 2
342
426
  oqx`${people} first { name where age > 50 }`; // { name: "Alice" }
343
427
  ```
344
428
 
345
- (Inside a consumer block a bare identifier **projects** โ€” `count { active }` selects
346
- a field named `active`; write `count { where active }` to filter.)
429
+ (A consumer block follows the same clause grammar as the top level, with `from`
430
+ optional. A leading bare identifier **projects** โ€” `count { active }` selects a
431
+ field named `active`; write `count { where active }` to filter, and always write
432
+ `where` before a predicate: `exists { !end }` is an error, `exists { where !end }`
433
+ is the test.)
347
434
 
348
435
  ### 6. Nested collections and relations
349
436
 
@@ -354,7 +441,7 @@ In `where`, an `exists { โ€ฆ }` tests non-emptiness and `count { โ€ฆ } <op> N` c
354
441
  cardinality:
355
442
 
356
443
  ```js
357
- oqx`name from ${people} where jobs exists { !end }`; // has a current job โ†’ Bob, Carol
444
+ oqx`name from ${people} where jobs exists { where !end }`; // has a current job โ†’ Bob, Carol
358
445
  oqx`name from ${people} where jobs count {} >= 2`; // โ‰ฅ2 jobs โ†’ Bob, Alice
359
446
  oqx`name from ${people} where jobs none { where end }`; // no past job โ†’ Carol
360
447
  ```
@@ -384,7 +471,7 @@ counts and collections are over distinct projections rather than raw rows. Spell
384
471
  it after the consumer (`count distinct { โ€ฆ }`) or inside via `select distinct`:
385
472
 
386
473
  ```js
387
- oqx`from ${jobs} select distinct employer`; // distinct employers
474
+ oqx`select distinct employer from ${jobs}`; // distinct employers
388
475
  oqx`n: jobs collect distinct { select employer } from ${people}`; // per person, unique employers
389
476
  oqx`name from ${people} where jobs count distinct { select employer } == 1`; // worked at exactly one employer
390
477
  ```
@@ -437,7 +524,9 @@ fully), not a short-circuiting `exists`.
437
524
  ### 8. Ordering
438
525
 
439
526
  `order by <expr> [asc|desc]`, comma-separated for tie-breaks. Absent values sort
440
- last.
527
+ last. The sort expression reads the row (fields, `$value`, recursion
528
+ intrinsics) โ€” it is not rewritten against the `select` aliases, so
529
+ `order by decade` sorts by a field called `decade`, not by `decade: age / 10`.
441
530
 
442
531
  ```js
443
532
  oqx`name from ${people} where city == "NYC" order by age desc`;
@@ -456,7 +545,7 @@ inside `{ โ€ฆ }`; it must be a non-negative integer.
456
545
 
457
546
  ```js
458
547
  oqx`name values from ${people} order by age desc limit 2`; // ["Alice", "Bob"]
459
- oqx`name values from ${people} order by age desc offset 1 limit 1`; // ["Bob"]
548
+ oqx`name values from ${people} order by age desc limit 1 offset 1`; // ["Bob"]
460
549
  oqx`name, latest: jobs collect { employer values order by start desc limit 1 } from ${people}`;
461
550
  oqx`name from ${people} where jobs exists { offset 1 }`; // has a second job
462
551
  ```
@@ -513,9 +602,11 @@ your relation returns fresh objects rather than shared references.
513
602
  ### Cheat-sheet
514
603
 
515
604
  ```
516
- name, alias: expr, nested: rel collect { โ€ฆ } projection (select optional)
517
- from ${source} source collection
518
- where a == b && rel exists { โ€ฆ } || !c predicate tree + nested ops
605
+ select โ€ฆ from โ€ฆ where โ€ฆ follow โ€ฆ order by โ€ฆ limit N offset N the fixed clause order (each at most once)
606
+ name, alias: expr, nested: rel collect { โ€ฆ } projection (`select` may be dropped only here, in first position)
607
+ from ${source} source collection (required at the top level)
608
+ where a == b && rel exists { where โ€ฆ } || !c predicate tree + nested ops (`where` is never implicit)
609
+ where alias `where` may use this body's select aliases (inlined; an alias shadows a field)
519
610
  where rel none { โ€ฆ } zero rows (โ‰ก !rel exists { โ€ฆ }; "all" = none over the complement)
520
611
  where x in lo..hi / lo...hi / ..hi / lo.. range membership (incl. / excl. / open-ended)
521
612
  where x in range(field) coerce a string field to a range, then test coverage
@@ -699,14 +790,23 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
699
790
  - `InMemoryEngine` โ€” `new InMemoryEngine(context?)`; the reference engine over a
700
791
  `DataContext` (tier 1/2).
701
792
  - `DataContext` โ€” the tier-2 interface: `root`, `get`, `toRows`, `identity`,
702
- plus optional `callFunction` / `callMethod` (above).
793
+ plus optional `callFunction` / `callMethod` (above) and an optional
794
+ `regexDialect` (`"oqx"` default | `"native"`). The engine dispatches
795
+ `matches` through `callMethod`, so a custom context that wants the native
796
+ dialect answers `matches` itself with `semantics.regexMatches(recv, args,
797
+ "native")`; `BUILTIN_METHODS.matches` is always the baseline.
703
798
  - `CallResult` โ€” `{ handled: boolean; value?: unknown }`, returned by those hooks.
704
- - `DefaultContext` โ€” `new DefaultContext(roots?)`; plain-object access, `.id`
705
- identity, and the builtin function/method tables.
799
+ - `DefaultContext` โ€” `new DefaultContext(roots?, options?)`; plain-object
800
+ access, `.id` identity, and the builtin function/method tables. `options`:
801
+ `{ regexDialect?: RegexDialect }` (see the regex baseline, ยง4).
802
+ - `compileRegex(pattern, flags?, dialect?)` โ€” the `matches()` compiler: a
803
+ `RegExp`, or an `OqxError` for a bad pattern/flag. `RegexDialect` /
804
+ `RegexFlags` are the types.
706
805
  - `semantics` โ€” the scalar-contract module (`equals`, `relate`, `arith`,
707
806
  `membership`, `truthy`, `compareForSort`, `sizeOf`, `toList`,
708
807
  `coerceCollection`, ranges: `makeRange` / `isRange` / `rangeCovers` /
709
- `parseRangeString`, entries: `makeEntry` / `isEntry` / `entriesOf`, and the
808
+ `parseRangeString`, entries: `makeEntry` / `isEntry` / `entriesOf`, regex:
809
+ `compileRegex` / `regexMatches` / `parseRegexFlags`, and the
710
810
  `BUILTIN_FUNCTIONS` / `BUILTIN_METHODS` tables). A backend reproducing a rule
711
811
  natively must match these.
712
812
 
package/dist/context.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { RegexDialect } from "./regex.js";
1
2
  /** Result of a context-provided function/method call: `handled: false` tells the
2
3
  * engine to fall back to the builtin table (or error if none). */
3
4
  export interface CallResult {
@@ -20,13 +21,27 @@ export interface DataContext {
20
21
  callFunction?(name: string, args: unknown[]): CallResult;
21
22
  /** Optional custom method; return `{ handled: false }` to defer. */
22
23
  callMethod?(name: string, recv: unknown, args: unknown[]): CallResult;
24
+ /** The regex dialect `matches()` compiles against. `"oqx"` (the default when
25
+ * absent) is the portable baseline the spec tests; `"native"` hands the
26
+ * pattern to the host `RegExp` unvalidated โ€” implementation-defined, not
27
+ * portable. Note the engine dispatches `matches` through `callMethod`, so
28
+ * this is read by the context's own `matches` (`DefaultContext` does;
29
+ * `regexMatches(recv, args, dialect)` is the helper). `BUILTIN_METHODS.matches`
30
+ * is always the baseline. */
31
+ readonly regexDialect?: RegexDialect;
32
+ }
33
+ export interface DefaultContextOptions {
34
+ /** See `DataContext.regexDialect`. Default `"oqx"`. */
35
+ regexDialect?: RegexDialect;
23
36
  }
24
37
  /** The default context: ordinary JavaScript objects. Named roots come from a
25
- * plain `{ name: collection }` map; properties are own/inherited keys; identity
26
- * is `.id` when present, else the object itself (reference identity). */
38
+ * plain `{ name: collection }` map; properties are OWN keys only (an array
39
+ * exposes only its integer indices, a primitive has none); identity is `.id`
40
+ * when present, else the row itself (the engine keys it structurally). */
27
41
  export declare class DefaultContext implements DataContext {
28
42
  private roots;
29
- constructor(roots?: Record<string, unknown>);
43
+ readonly regexDialect: RegexDialect;
44
+ constructor(roots?: Record<string, unknown>, options?: DefaultContextOptions);
30
45
  root(name: string): unknown;
31
46
  get(row: unknown, key: string): unknown;
32
47
  toRows(value: unknown): Iterable<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAYA;kEACkE;AAClE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B;;;+DAG2D;IAC3D,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,2EAA2E;IAC3E,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACzD,oEAAoE;IACpE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;CACvE;AAED;;yEAEyE;AACzE,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,KAAK,CAA0B;gBAE3B,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IAI/C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAKvC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IAIzC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IAK/B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;IAKvD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;CAIrE"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAQ/C;kEACkE;AAClE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5B;;;+DAG2D;IAC3D,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACxC,8DAA8D;IAC9D,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,8DAA8D;IAC9D,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,2EAA2E;IAC3E,YAAY,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACzD,oEAAoE;IACpE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC;IACtE;;;;;;iCAM6B;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;CACtC;AAED,MAAM,WAAW,qBAAqB;IACpC,uDAAuD;IACvD,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;0EAG0E;AAC1E,qBAAa,cAAe,YAAW,WAAW;IAChD,OAAO,CAAC,KAAK,CAA0B;IACvC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;gBAExB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAAE,OAAO,GAAE,qBAA0B;IAKpF,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI3B,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAMvC,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IAIzC,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO;IAK/B,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;IAKvD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,UAAU;CAKrE"}
package/dist/context.js CHANGED
@@ -7,22 +7,32 @@
7
7
  //
8
8
  // (Performant execution over a real store is the tier-3 seam โ€” see planner.ts โ€”
9
9
  // which pushes work into the store instead of driving it row-by-row here.)
10
- import { coerceCollection, BUILTIN_FUNCTIONS, BUILTIN_METHODS } from "./semantics.js";
10
+ import { coerceCollection, regexMatches, BUILTIN_FUNCTIONS, BUILTIN_METHODS } from "./semantics.js";
11
+ // An array's only properties are its integer indices, spelled canonically
12
+ // (`"0"`, `"12"`; never `"01"`, `"length"`, or a method name).
13
+ function isIndexKey(key) {
14
+ return /^(0|[1-9]\d*)$/.test(key);
15
+ }
11
16
  /** The default context: ordinary JavaScript objects. Named roots come from a
12
- * plain `{ name: collection }` map; properties are own/inherited keys; identity
13
- * is `.id` when present, else the object itself (reference identity). */
17
+ * plain `{ name: collection }` map; properties are OWN keys only (an array
18
+ * exposes only its integer indices, a primitive has none); identity is `.id`
19
+ * when present, else the row itself (the engine keys it structurally). */
14
20
  export class DefaultContext {
15
21
  roots;
16
- constructor(roots = {}) {
22
+ regexDialect;
23
+ constructor(roots = {}, options = {}) {
17
24
  this.roots = roots;
25
+ this.regexDialect = options.regexDialect ?? "oqx";
18
26
  }
19
27
  root(name) {
20
- return this.roots[name];
28
+ return Object.hasOwn(this.roots, name) ? this.roots[name] : undefined;
21
29
  }
22
30
  get(row, key) {
23
- if (row == null)
31
+ if (row == null || typeof row !== "object")
24
32
  return undefined;
25
- return Object(row)[key];
33
+ if (Array.isArray(row))
34
+ return isIndexKey(key) ? row[Number(key)] : undefined;
35
+ return Object.hasOwn(row, key) ? row[key] : undefined;
26
36
  }
27
37
  toRows(value) {
28
38
  return coerceCollection(value);
@@ -33,11 +43,13 @@ export class DefaultContext {
33
43
  return row;
34
44
  }
35
45
  callFunction(name, args) {
36
- const fn = BUILTIN_FUNCTIONS[name];
46
+ const fn = Object.hasOwn(BUILTIN_FUNCTIONS, name) ? BUILTIN_FUNCTIONS[name] : undefined;
37
47
  return fn ? { handled: true, value: fn(args) } : { handled: false };
38
48
  }
39
49
  callMethod(name, recv, args) {
40
- const fn = BUILTIN_METHODS[name];
50
+ if (name === "matches")
51
+ return { handled: true, value: regexMatches(recv, args, this.regexDialect) };
52
+ const fn = Object.hasOwn(BUILTIN_METHODS, name) ? BUILTIN_METHODS[name] : undefined;
41
53
  return fn ? { handled: true, value: fn(recv, args) } : { handled: false };
42
54
  }
43
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,gFAAgF;AAChF,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAE3E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA2BtF;;yEAEyE;AACzE,MAAM,OAAO,cAAc;IACjB,KAAK,CAA0B;IAEvC,YAAY,QAAiC,EAAE;QAC7C,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,GAAY,EAAE,GAAW;QAC3B,IAAI,GAAG,IAAI,IAAI;YAAE,OAAO,SAAS,CAAC;QAClC,OAAQ,MAAM,CAAC,GAAG,CAA6B,CAAC,GAAG,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,CAAC,KAAc;QACnB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,QAAQ,CAAC,GAAY;QACnB,IAAI,GAAG,IAAI,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,IAAI,IAAI,GAAG;YAAE,OAAQ,GAA+B,CAAC,EAAE,CAAC;QACtG,OAAO,GAAG,CAAC;IACb,CAAC;IAED,YAAY,CAAC,IAAY,EAAE,IAAe;QACxC,MAAM,EAAE,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtE,CAAC;IAED,UAAU,CAAC,IAAY,EAAE,IAAa,EAAE,IAAe;QACrD,MAAM,EAAE,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5E,CAAC;CACF"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,KAAK,EACN,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOhD,yEAAyE;AACzE,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,GACxC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAC1C;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CAC5D;AA4BD,qBAAa,cAAe,YAAW,MAAM;IAC3C,OAAO,CAAC,GAAG,CAAc;gBAEb,OAAO,GAAE,WAAkC;IAIvD,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,SAAS,OAAO,EAAO,GAAG,SAAS;IAmD/D,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,KAAK;IAab,OAAO,CAAC,SAAS;IAejB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,UAAU;IA6DlB,OAAO,CAAC,KAAK;IAeb,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,WAAW;IAiCnB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAE,aAAa;IAStB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ;IAwDhB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,QAAQ;CAYjB;AAyFD,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAG9F"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,KAAK,EACN,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAOhD,yEAAyE;AACzE,MAAM,MAAM,SAAS,GACjB;IAAE,QAAQ,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,GACxC;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,GAC1C;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC;AAEhD,6EAA6E;AAC7E,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;CAC5D;AA4BD,qBAAa,cAAe,YAAW,MAAM;IAC3C,OAAO,CAAC,GAAG,CAAc;gBAEb,OAAO,GAAE,WAAkC;IAIvD,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,GAAE,SAAS,OAAO,EAAO,GAAG,SAAS;IAmD/D,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,KAAK;IASb,OAAO,CAAC,KAAK;IAab,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,iBAAiB;IA6BzB,OAAO,CAAC,UAAU;IA8DlB,OAAO,CAAC,KAAK;IAgBb,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,WAAW;IAiCnB,OAAO,CAAC,MAAM;IAQd,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAE,aAAa;IAStB,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,UAAU;IAwBlB,OAAO,CAAC,QAAQ;IA+DhB,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,QAAQ;CAYjB;AAkGD,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAG9F"}