@omgbase/oqx 0.14.0 → 0.15.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/README.md +162 -24
  3. package/dist/adapters/indexed.d.ts +13 -1
  4. package/dist/adapters/indexed.d.ts.map +1 -1
  5. package/dist/adapters/indexed.js +55 -24
  6. package/dist/adapters/indexed.js.map +1 -1
  7. package/dist/ast.d.ts +1 -1
  8. package/dist/ast.d.ts.map +1 -1
  9. package/dist/context.d.ts +24 -0
  10. package/dist/context.d.ts.map +1 -1
  11. package/dist/context.js.map +1 -1
  12. package/dist/engine.d.ts +55 -2
  13. package/dist/engine.d.ts.map +1 -1
  14. package/dist/engine.js +272 -39
  15. package/dist/engine.js.map +1 -1
  16. package/dist/index.d.ts +4 -2
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +2 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/optimize/analysis.d.ts +45 -0
  21. package/dist/optimize/analysis.d.ts.map +1 -0
  22. package/dist/optimize/analysis.js +194 -0
  23. package/dist/optimize/analysis.js.map +1 -0
  24. package/dist/optimize/driver.d.ts +8 -0
  25. package/dist/optimize/driver.d.ts.map +1 -0
  26. package/dist/optimize/driver.js +35 -0
  27. package/dist/optimize/driver.js.map +1 -0
  28. package/dist/optimize/hash-index.d.ts +34 -0
  29. package/dist/optimize/hash-index.d.ts.map +1 -0
  30. package/dist/optimize/hash-index.js +79 -0
  31. package/dist/optimize/hash-index.js.map +1 -0
  32. package/dist/optimize/index.d.ts +8 -0
  33. package/dist/optimize/index.d.ts.map +1 -0
  34. package/dist/optimize/index.js +11 -0
  35. package/dist/optimize/index.js.map +1 -0
  36. package/dist/optimize/ir.d.ts +84 -0
  37. package/dist/optimize/ir.d.ts.map +1 -0
  38. package/dist/optimize/ir.js +60 -0
  39. package/dist/optimize/ir.js.map +1 -0
  40. package/dist/optimize/rules.d.ts +8 -0
  41. package/dist/optimize/rules.d.ts.map +1 -0
  42. package/dist/optimize/rules.js +136 -0
  43. package/dist/optimize/rules.js.map +1 -0
  44. package/dist/plan.js +1 -1
  45. package/dist/plan.js.map +1 -1
  46. package/dist/semantics.js +1 -1
  47. package/dist/semantics.js.map +1 -1
  48. package/package.json +1 -1
  49. package/src/adapters/indexed.ts +60 -24
  50. package/src/ast.ts +2 -2
  51. package/src/context.ts +24 -0
  52. package/src/engine.ts +307 -37
  53. package/src/index.ts +7 -2
  54. package/src/optimize/analysis.ts +183 -0
  55. package/src/optimize/driver.ts +37 -0
  56. package/src/optimize/hash-index.ts +91 -0
  57. package/src/optimize/index.ts +16 -0
  58. package/src/optimize/ir.ts +128 -0
  59. package/src/optimize/rules.ts +136 -0
  60. package/src/plan.ts +1 -1
  61. package/src/semantics.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,75 @@ 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.15.0] - 2026-10-09
8
+
9
+ ### Minor
10
+ - OQX language **0.15**: the intrinsic naming the current item is **`$it`**; `$value` is no longer an intrinsic and has no synonym. `$it` is the scope's row itself — the top-level row, a nested block's inner item, each element of a scalar collection, the entry's value inside `entries(x)` — absent at the root, with `^$it` the enclosing row, `$it.field` navigation, `$it values`, `$it` as a `follow … { by $it }` identity. A row property literally named `$it` is shadowed by the intrinsic. `$value` is now an ordinary property name under SEMANTICS §2 step 5, exactly like the non-metadata `$` names since 0.13: `{ "$value": 3 }` projects `3`, a row without one reads absent, over scalars it is absent, and inside an `entries()` scope it is a property of the entry's *value*. Why: "self" implies a fixed referent while the row changes per scope; "it" is the pronoun for whatever is being handled; keeping `$value` as a synonym would spell one concept two ways. `$key` is unchanged, and the `{ key, value }` record of `entries(x)` used as a plain value is unchanged (that is a value shape, not the intrinsic). **Migration:** replace `$value` with `$it` in every query (`s/\$value/\$it/g`); nothing else moves. New `intrinsics` fixtures pin the rule; every other fixture query was rewritten with its expectation untouched. omgbase's query surface (`query`, `query_syntax`, the CLI help, `docs/query-language.md`, the tutorial, `spec/surface` fixture queries) follows: a `$value` in an omgbase query now reads a frontmatter/attrs key so named.
11
+
12
+ ## [0.14.1] - 2026-10-09
13
+
14
+ ### Patch
15
+ - Relational patterns in nested blocks stop being quadratic. The in-memory engine
16
+ now runs every nested block through an optimizer (`optimize/`: a logical
17
+ `BlockPlan` per block, a `Rule` interface, a fixpoint driver) with four rules,
18
+ each proven unobservable — same rows, lifts and errors as the scan:
19
+
20
+ - **Correlated equality → hash probe.** `^customers first { where id ==
21
+ ^customer_id }`, `^orders collect { … where customer_id == ^id }`,
22
+ `^customers exists { where id == ^customer_id }`, `${customers} first { … }`:
23
+ a top-level `&&` conjunct `local == outer` (either side) is answered from a
24
+ hash index on the receiver built once per run (per collection and path),
25
+ with the residual conjuncts evaluated over the bucket in receiver order.
26
+ Multiple equalities intersect smallest-first. The index reproduces §5
27
+ equality exactly (absent ≡ null, `-0` ≡ `0`, `NaN` matches nothing, objects
28
+ by reference). The sound rule: every conjunct left of the equality must be
29
+ raise-free (no call, no `single`, no lift, no bound, no out-of-range binding)
30
+ and the outer side must be raise-free and read nothing from the block's row;
31
+ otherwise the scan runs. A receiver is indexed when it is statically stable
32
+ (reads only the root/bindings) or when the same collection object is probed
33
+ twice; a receiver that yields a fresh value per enclosing row is scanned.
34
+ 5k × 5k zip: ~9 ms (the scan: seconds).
35
+ - **Invariant block hoisting.** A block that reads nothing from any enclosing
36
+ row and lifts nothing is evaluated once per run and its value reused.
37
+ - **Semi/anti-join short-circuit** is kept over the probe's bucket (`exists`
38
+ stops at the first bucket row that passes).
39
+ - **Cardinality.** `exists`/`none`/`count` with no residual predicate are
40
+ answered from the bucket (or receiver) size without entering a row.
41
+
42
+ `DataContext.indexFor(collection, path)` (TypeScript) / `index_for` (Rust) is
43
+ a new optional seam for pre-built indexes; `IndexedCollection` keys under §5
44
+ equality (it kept `null` and `undefined` apart and matched `NaN` to itself)
45
+ and exposes `context()` (TypeScript) so a residual's nested blocks see the
46
+ root and reuse its indexes. `InMemoryEngine` takes `{ rules, trace }`
47
+ (TypeScript) / `with_rules` (Rust); `rules: []` is the naive scan, and the
48
+ conformance suites prove optimized ≡ naive — result or error, stage and
49
+ message — over every `spec/oqx` fixture. No language change.
50
+
51
+ A `RowIndex` may also implement `lookupRows(value)` (TypeScript) /
52
+ `lookup_rows` (Rust): the matching rows themselves, in receiver order. For a
53
+ statically stable receiver the engine asks the context's index **before**
54
+ reading the collection and, when it answers rows, never materializes the
55
+ receiver (one correlation is probed — the first whose outer side is a `^`
56
+ reference rather than a literal — and the other conjuncts, remaining equalities
57
+ included, stay residual in their original order; a `TraceEvent` of kind
58
+ `lookup` reports it). A statically stable receiver is now also read once per
59
+ run when nothing is correlated, instead of once per enclosing row. Rust:
60
+ `index_for` returns `Option<Rc<dyn RowIndex>>` and `lookup` returns
61
+ `Vec<usize>`, so a context can create indexes on demand.
62
+
63
+ `DataContext::materialize(value)` (Rust) / `materialize?(value)` (TypeScript)
64
+ is a new optional seam, default identity: a context that hands out a stand-in
65
+ for a collection it has not read yet (a lazy table handle) resolves it here.
66
+ The engine calls it on every value it is about to observe as a value — an
67
+ operand, an argument, a projected item, an `order by`/`distinct` key, a
68
+ `where` scalar, a lift — and never in row position (the source, a receiver, a
69
+ `from`, a `follow` destination), where the value reaches `to_rows`/`index_for`
70
+ as handed out. The Rust engine's execution path was folded to mirror the
71
+ TypeScript one: one `access` for every consumer (the `follow` seeds included),
72
+ `scan`/`materialize`, a `memoized` helper for invariant plans, `outer_value`,
73
+ and `lookup_order`/`residual_without` in `optimize`; optimized ≡ naive over
74
+ every fixture, same perf.
75
+
7
76
  ## [0.14.0] - 2026-10-06
8
77
 
9
78
  ### Minor
package/README.md CHANGED
@@ -16,7 +16,7 @@ JavaScript tagged template.
16
16
  > OQX is a language with more than one implementation; the specification is
17
17
  > [`spec/oqx`](https://github.com/omgbase/omgbase/tree/main/spec/oqx). The
18
18
  > language version is the package version's `major.minor` (`LANGUAGE_VERSION`,
19
- > `"0.14"`); the patch digit is this implementation's own.
19
+ > `"0.15"`); the patch digit is this implementation's own.
20
20
  > Requirements: Node ≥ 22.13 for `@omgbase/oqx/sqlite`; Node ≥ 22.18 to run the
21
21
  > test suite (see [Requirements](#requirements)).
22
22
 
@@ -151,45 +151,50 @@ projection and inside any `collect` / `first` / `single` block, and composes wit
151
151
  employers as strings, not `{ employer }` records). An alias, if present, is
152
152
  ignored; a lift (`^name:`) cannot be combined with it.
153
153
 
154
- **`$value` — the current item.** Every scope has a current value; `$value` is
154
+ **`$it` — the current item.** Every scope has a current value; `$it` is
155
155
  that exact value, whatever its type (an object row or a plain scalar). Bare names
156
- still navigate it (`name` ≡ `$value.name`), so `$value` matters exactly where
156
+ still navigate it (`name` ≡ `$it.name`), so `$it` matters exactly where
157
157
  there is nothing to navigate: collections of numbers or strings, or handing the
158
158
  whole row somewhere. Together with `values` this makes scalar collections
159
159
  first-class:
160
160
 
161
161
  ```js
162
162
  const scores = [10, 60, 70, 45];
163
- oqx`$value values from ${scores} where $value > 50`; // [60, 70]
164
- oqx`$value values from ${scores} order by $value desc`; // [70, 60, 45, 10]
163
+ oqx`$it values from ${scores} where $it > 50`; // [60, 70]
164
+ oqx`$it values from ${scores} order by $it desc`; // [70, 60, 45, 10]
165
165
 
166
166
  const players = [{ name: "Ann", scores: [10, 60, 70] }, { name: "Ben", scores: [45] }];
167
- oqx`name, big: scores collect { $value values where $value > 50 } from ${players}`;
167
+ oqx`name, big: scores collect { $it values where $it > 50 } from ${players}`;
168
168
  // [{ name: "Ann", big: [60, 70] }, { name: "Ben", big: [] }]
169
169
 
170
- oqx`employee: $value from ${people} where name == "Bob"`; // [{ employee: <Bob> }]
170
+ oqx`employee: $it from ${people} where name == "Bob"`; // [{ employee: <Bob> }]
171
171
  ```
172
172
 
173
- Inside a nested block `$value` is the inner item; the enclosing row is `^$value`
174
- (§3). At the root scope (before any row) it is absent.
173
+ Inside a nested block `$it` is the inner item; the enclosing row is `^$it`
174
+ (§3). At the root scope (before any row) it is absent. `$it` is the only name
175
+ for the current item (language 0.15; through 0.14 it was spelled `$value`, and
176
+ there is no synonym): `$value` is now an ordinary property read, like any other
177
+ non-metadata `$name` — a row `{ "$value": 3 }` projects `3`, a row without one
178
+ reads absent. A row that itself owns a `$it` property is shadowed by the
179
+ intrinsic; reach it with `^` from a nested scope or rename it upstream.
175
180
 
176
181
  **`entries(x)` and `$key` — records to collections, explicitly.** A plain object
177
182
  is **not** iterable: `from ${obj}` is one row (the object). `entries(obj)`
178
183
  converts it into a collection of entries, and inside such a scope the current
179
- item is the property's **value** — `$value` and bare names read it — while
184
+ item is the property's **value** — `$it` and bare names read it — while
180
185
  **`$key`** is the property's key:
181
186
 
182
187
  ```js
183
188
  const settings = { theme: "dark", fontSize: 14, autosave: true };
184
- oqx`key: $key, value: $value from entries(${settings})`;
189
+ oqx`key: $key, value: $it from entries(${settings})`;
185
190
  // [{ key: "theme", value: "dark" }, { key: "fontSize", value: 14 }, { key: "autosave", value: true }]
186
- oqx`$key values from entries(${settings}) where $value != "dark"`; // ["fontSize", "autosave"]
191
+ oqx`$key values from entries(${settings}) where $it != "dark"`; // ["fontSize", "autosave"]
187
192
 
188
193
  const flags = { beta: { on: true }, legacy: { on: false } };
189
194
  oqx`$key values from entries(${flags}) where on`; // ["beta"] (bare `on` reads the value)
190
195
 
191
- oqx`name, on: entries(prefs) collect { $key values where $value } from ${users}`; // as a nested receiver
192
- oqx`name from ${users} where entries(prefs) exists { where $key == "dark" && $value }`;
196
+ oqx`name, on: entries(prefs) collect { $key values where $it } from ${users}`; // as a nested receiver
197
+ oqx`name from ${users} where entries(prefs) exists { where $key == "dark" && $it }`;
193
198
  ```
194
199
 
195
200
  `entries(array)` yields numeric index keys, a `Map` yields its entries, and
@@ -524,7 +529,7 @@ fully), not a short-circuiting `exists`.
524
529
  ### 8. Ordering
525
530
 
526
531
  `order by <expr> [asc|desc]`, comma-separated for tie-breaks. Absent values sort
527
- last. The sort expression reads the row (fields, `$value`, recursion
532
+ last. The sort expression reads the row (fields, `$it`, recursion
528
533
  intrinsics) — it is not rewritten against the `select` aliases, so
529
534
  `order by decade` sorts by a field called `decade`, not by `decade: age / 10`.
530
535
 
@@ -649,10 +654,10 @@ where rel none { … } zero rows (≡ !rel exists {
649
654
  where x in lo..hi / lo...hi / ..hi / lo.. range membership (incl. / excl. / open-ended)
650
655
  where x in range(field) coerce a string field to a range, then test coverage
651
656
  name the CURRENT row's field only (never climbs)
652
- $value the current item itself (a scalar row, or the whole object)
653
- from entries(obj) … $key / $value a record's properties as a collection (key + value; bare names read the value)
657
+ $it the current item itself (a scalar row, or the whole object)
658
+ from entries(obj) … $key / $it a record's properties as a collection (key + value; bare names read the value)
654
659
  <expr> values scalar projection: the value, not a { name: value } record
655
- ^name / ^^name read an enclosing row's field (exactly N scopes out); ^$value = the enclosing row
660
+ ^name / ^^name read an enclosing row's field (exactly N scopes out); ^$it = the enclosing row
656
661
  ^name: expr / ^^name: expr lift/export a value N scopes out (flatten-append)
657
662
  ^rel collect { … } / ^^root exists { … } nested consumer over an enclosing row's relation / a named root
658
663
  entries(rel) exists { … } a free-function call may be a receiver
@@ -781,6 +786,117 @@ blocks/nodes, the relations table, `$` intrinsics, `WITH RECURSIVE` for `follow`
781
786
  becomes a `QueryPlanner`, while oqx contributes the parser, IR, semantics
782
787
  contract, and residual executor.
783
788
 
789
+ ## Performance: relational patterns
790
+
791
+ A nested block runs once per enclosing row, so the natural way to join two
792
+ collections —
793
+
794
+ ```js
795
+ oqx`select id, total, customer: ${customers} first { where id == ^customer_id } from ${orders}`;
796
+ oqx`select name, orders: ${orders} collect { id, total where customer_id == ^id } from ${customers}`;
797
+ oqx`select id from ${orders} where ${customers} exists { where id == ^customer_id }`;
798
+ ```
799
+
800
+ — would be quadratic if the block scanned its receiver each time. It does not:
801
+ the in-memory engine runs every nested block through an **optimizer**
802
+ (`src/optimize/`), a small logical plan per block plus rules that rewrite it,
803
+ each with a proof that the rewrite is unobservable — same rows in the same
804
+ order, same lifts, same error (same message) at the same point. The planned
805
+ engine and the naive engine (`new InMemoryEngine(ctx, { rules: [] })`) are run
806
+ over every `spec/oqx` fixture by `test/conformance.test.ts` and must agree on
807
+ results and on errors.
808
+
809
+ **What is optimized**
810
+
811
+ - **Correlated equality → hash probe.** A top-level `&&` conjunct of the block's
812
+ `where` of the form `local == outer` (either side) — `local` a bare identifier
813
+ or member chain on the block's row (`customer_id`, `meta.id`, `$it`),
814
+ `outer` anything that reads nothing from that row (`^customer_id`, `^^x.id`,
815
+ a `${…}` binding, a literal, arithmetic over them) — is answered from a hash
816
+ index on the receiver built **once per run** per collection and path. The
817
+ remaining conjuncts run over the bucket only, in receiver order; several
818
+ equalities intersect their buckets smallest first. The index keys are exactly
819
+ `==` (SEMANTICS §5): absent ≡ `null`, `-0` ≡ `0`, `NaN` matches nothing,
820
+ objects and arrays by reference. 5 000 orders × 5 000 customers zip in a few
821
+ milliseconds; the scan takes seconds.
822
+ - **Invariant blocks run once.** A block that reads nothing from any enclosing
823
+ row (`^customers collect { name values }` from a top-level row) and lifts
824
+ nothing is evaluated the first time it is reached and its value reused for
825
+ every other row (the same array/record by reference).
826
+ - **Semi/anti-join short-circuit.** `exists` and `none` stop at the first bucket
827
+ row that passes the residual, as they stop at the first matching row of a scan.
828
+ - **Cardinality only.** `exists` / `none` / `count` with nothing left to check
829
+ per row (no `where`, or every conjunct correlated; no `distinct`, no `order
830
+ by`) are answered from the bucket's size after the bound without entering a
831
+ row.
832
+
833
+ **The sound rule.** `&&` is strictly left to right and short-circuiting (§13),
834
+ so a probe may skip a row only if everything the scan would have evaluated for
835
+ it could neither raise nor bind: an equality is hoisted only when **every
836
+ conjunct to its left is raise-free** — no function or method call, no `single`,
837
+ no lift, no `limit`/`offset`, no out-of-range `${…}`, only `exists`/`none`/
838
+ `count` blocks that are themselves raise-free — and its outer side is raise-free
839
+ too. A conjunct that could raise *right* of the equality is simply evaluated
840
+ over the bucket, where the scan would have evaluated it on the same rows. A
841
+ block whose `where` lifts anywhere is never probed (a lift could change what
842
+ the outer side reads between rows). Otherwise the block scans as before, so
843
+ `where nope(x) && id == ^id` still raises `unknown function`.
844
+
845
+ Property reads are treated as total (§23: evaluation is otherwise total). A
846
+ `DataContext.get` that throws is caught where the engine relies on reads it
847
+ would not otherwise perform — building an index, evaluating the probe value —
848
+ and the block falls back to the scan, which raises or not in its own order.
849
+
850
+ **Which receivers are indexed.** A receiver that reads only the root scope and
851
+ bindings (`^customers`, `${customers}`, `^^config.items`) is evaluated once per
852
+ run — its rows are read once and reused across the enclosing rows, with or
853
+ without a correlation — and indexed once per path even if the context hands out
854
+ a fresh array per read. Any other receiver is indexed the second time the
855
+ **same object** is probed, so a per-row relation (`lines collect { … where cid
856
+ == ^id }` with a different `lines` array per row) is scanned, not indexed. A
857
+ scalar receiver is one row.
858
+
859
+ **Pre-built indexes.** `DataContext.indexFor(collection, path)` is an optional
860
+ seam: return a `RowIndex` (`lookup(value) → ascending positions into
861
+ toRows(collection)`) for a collection you have already indexed, or `undefined`
862
+ to let the engine build one. A `RowIndex` may also implement
863
+ `lookupRows(value) → the matching rows, in receiver order`: for a stable
864
+ receiver the engine then probes it **before** reading the collection, so a
865
+ context backed by a store (a lazy handle for a table, an index on a column)
866
+ answers the probe with one indexed lookup and the table is never materialized.
867
+ One correlation is probed this way — the first whose outer side is a `^`
868
+ reference rather than a literal — and the other conjuncts, remaining
869
+ equalities included, are evaluated per selected row in their original order.
870
+
871
+ **Lazy handles.** A context that hands out a stand-in for a collection it has
872
+ not read yet may also implement `materialize(value)`: the engine calls it on
873
+ every value it is about to observe *as a value* (an operand of `==`/`in`/
874
+ arithmetic, a function or method argument, a projected item, an `order by` or
875
+ `distinct` key, a `where` scalar, a lift) and never on a value in *row
876
+ position* (the source, a block receiver, a body-level `from`, a `follow`
877
+ destination), which reaches `toRows` / `indexFor` as handed out. So the probe
878
+ above never reads the table, and the language never sees the stand-in. A
879
+ handle that already behaves as the collection (a `Proxy` over an array, as
880
+ omgbase's does) needs no `materialize`; the Rust crate's `DataContext::
881
+ materialize` is the same seam for a `Value` that cannot be lazy on its own.
882
+ `IndexedCollection` does the positional form for its fields:
883
+
884
+ ```js
885
+ import { parse, run, IndexedCollection } from "@omgbase/oqx";
886
+ const orders = new IndexedCollection("orders", orderRows, ["customer_id"]);
887
+ run(parse("select name, o: ^orders collect { id values where customer_id == ^id } from customers"),
888
+ { context: orders.context({ customers }) }); // probes the pre-built index
889
+ ```
890
+
891
+ **Still a scan.** A block with a `from` re-projection or a `follow`; a `where`
892
+ whose equality sits right of a conjunct that could raise; inequalities,
893
+ `in`, `contains()` and other non-equality correlations; `order by … limit N`
894
+ (sorted in full, then bounded); `distinct` (hashes projections, not an index);
895
+ top-level queries (a `QueryPlanner` is the seam for those). `trace` in
896
+ `InMemoryEngine`'s options receives one event per index built, probe, direct
897
+ lookup, memo hit, cardinality answer or fallback, if you want to see what a
898
+ query did.
899
+
784
900
  ## Exports
785
901
 
786
902
  Everything below is exported from `@omgbase/oqx` (`src/index.ts`); the SQLite
@@ -827,10 +943,18 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
827
943
  **Engines and contexts**
828
944
 
829
945
  - `Engine` — `{ run(query, bindings): OqxResult }`; what `run({ engine })` accepts.
830
- - `InMemoryEngine` — `new InMemoryEngine(context?)`; the reference engine over a
831
- `DataContext` (tier 1/2).
946
+ - `InMemoryEngine` — `new InMemoryEngine(context?, options?)`; the reference
947
+ engine over a `DataContext` (tier 1/2). `InMemoryEngineOptions`: `rules`
948
+ (the optimizer rules for nested blocks, default `DEFAULT_RULES`; `[]` is the
949
+ naive scan) and `trace` (a `(event: TraceEvent) => void` sink for index /
950
+ probe / memo / cardinality / fallback events). See [Performance](#performance-relational-patterns).
832
951
  - `DataContext` — the tier-2 interface: `root`, `get`, `toRows`, `identity`,
833
- plus optional `callFunction` / `callMethod` (above) and an optional
952
+ plus optional `callFunction` / `callMethod` (above), an optional
953
+ `indexFor(collection, path)` returning a `RowIndex` for a pre-built equality
954
+ index (`lookup` → positions; optionally `lookupRows` → rows, probed without
955
+ materializing the collection), an optional `materialize(value)` resolving a
956
+ lazy handle the engine is about to observe as a value (see
957
+ [Performance](#performance-relational-patterns)), and an optional
834
958
  `regexDialect` (`"oqx"` default | `"native"`). The engine dispatches
835
959
  `matches` through `callMethod`, so a custom context that wants the native
836
960
  dialect answers `matches` itself with `semantics.regexMatches(recv, args,
@@ -873,17 +997,31 @@ adapter lives on the `@omgbase/oqx/sqlite` subpath.
873
997
  its native query using `constValue` for parameters, then returns
874
998
  `{ rows, residual: residualQuery(query, residual) }`.
875
999
  - `IndexedCollection` — `new IndexedCollection(rootName, rows, indexFields)`; a
876
- `QueryPlanner` that hash-indexes `rows` on `indexFields` and answers
877
- `field == value` predicates on them from the index, leaving the rest residual:
1000
+ `QueryPlanner` that hash-indexes `rows` on `indexFields` (under `==`: absent ≡
1001
+ null, `NaN` matches nothing) and answers `field == value` predicates on them
1002
+ from the index, leaving the rest residual. `indexFor(collection, path)` serves
1003
+ the same indexes to the engine's correlated probes and `context(extraRoots?)`
1004
+ is a `DataContext` that resolves the root and wires `indexFor` up; the plans
1005
+ it returns carry that context, so a residual's nested blocks see the root.
878
1006
 
879
1007
  ```js
880
1008
  import { parse, run, IndexedCollection, PlannedEngine } from "@omgbase/oqx";
881
1009
  const planner = new IndexedCollection("people", people, ["city", "title"]);
882
- const engine = new PlannedEngine(planner);
1010
+ const engine = new PlannedEngine(planner, planner.context());
883
1011
  run(parse('name from people where city == "NYC" && age > 30'), { engine });
884
1012
  // city probe from the index; `age > 30` finished in-memory over the candidates
885
1013
  ```
886
1014
 
1015
+ **Optimizer (nested blocks)**
1016
+
1017
+ - `DEFAULT_RULES`, `correlatedEqualityProbe`, `stableReceiver`, `invariantBlock`,
1018
+ `cardinalityOnly` — the shipped `Rule`s (`(plan: BlockPlan, ctx: RuleContext)
1019
+ => BlockPlan | null`); `logicalBlock(op, depth)` is the unoptimized plan of a
1020
+ block and `optimizeBlock(op, depth, ctx, rules)` runs rules to a fixpoint.
1021
+ `BlockPlan` / `Correlation` / `RuleContext` are the types; `HashIndex` is the
1022
+ engine's `RowIndex` implementation (`add(value, position)`, `lookup(value)`),
1023
+ reusable by a context implementing `indexFor`.
1024
+
887
1025
  - `SqliteTable` (from `@omgbase/oqx/sqlite`) —
888
1026
  `new SqliteTable(db, tableName, options)` over a `node:sqlite` `DatabaseSync`.
889
1027
  `SqliteTableOptions`:
@@ -1,11 +1,23 @@
1
1
  import type { Query } from "../ast.js";
2
+ import type { DataContext } from "../context.js";
2
3
  import type { Plan, QueryPlanner } from "../planner.js";
4
+ import type { RowIndex } from "../optimize/hash-index.js";
3
5
  export declare class IndexedCollection implements QueryPlanner {
4
6
  private name;
5
7
  private rows;
6
8
  private indexes;
7
- /** Index `rows` (exposed as root `name`) on each field in `indexFields`. */
9
+ /** Index `rows` (exposed as root `name`) on each field in `indexFields`. A
10
+ * field is read as the engine reads a bare identifier (own property; array
11
+ * element for an integer-spelled name); a row without it keys as absent. */
8
12
  constructor(name: string, rows: readonly unknown[], indexFields: readonly string[]);
13
+ /** The pre-built index for `collection` on `path` when `collection` is this
14
+ * collection's rows and `path` is one indexed field — the `DataContext.indexFor`
15
+ * seam, which `context()` wires up. */
16
+ indexFor(collection: unknown, path: readonly string[]): RowIndex | undefined;
17
+ /** A `DataContext` over plain objects that serves this collection as the
18
+ * root `name` (plus `extraRoots`) and exposes the indexes through
19
+ * `indexFor`, so the engine's correlated probes reuse them. */
20
+ context(extraRoots?: Record<string, unknown>): DataContext;
9
21
  plan(query: Query, params: readonly unknown[]): Plan | null;
10
22
  }
11
23
  //# sourceMappingURL=indexed.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexed.d.ts","sourceRoot":"","sources":["../../src/adapters/indexed.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGxD,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,OAAO,CAA8C;IAE7D,4EAA4E;gBAChE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE;IAelF,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI;CAoB5D"}
1
+ {"version":3,"file":"indexed.d.ts","sourceRoot":"","sources":["../../src/adapters/indexed.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAG1D,qBAAa,iBAAkB,YAAW,YAAY;IACpD,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAqB;IACjC,OAAO,CAAC,OAAO,CAAgC;IAE/C;;gFAE4E;gBAChE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,OAAO,EAAE,EAAE,WAAW,EAAE,SAAS,MAAM,EAAE;IAWlF;;2CAEuC;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG,SAAS;IAK5E;;mEAE+D;IAC/D,OAAO,CAAC,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,WAAW;IAI9D,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI;CAoB5D"}
@@ -1,33 +1,57 @@
1
1
  // An in-memory optimizing planner: it hash-indexes a named root collection on
2
- // chosen fields and, for a query whose where-clause contains equality predicates
3
- // on those fields, answers from the index (candidate intersection) instead of a
4
- // full scan. Everything it can't turn into an index probe is left as a residual
5
- // query the in-memory engine finishes over the candidate rows.
2
+ // chosen fields and, for a query whose where-clause contains equality
3
+ // predicates on those fields, answers from the index (candidate intersection)
4
+ // instead of a full scan. Everything it can't turn into an index probe is left
5
+ // as a residual query the in-memory engine finishes over the candidate rows.
6
6
  //
7
7
  // This is the smallest honest demonstration of plan optimization: same answers
8
8
  // as a naive scan, far fewer rows examined, and partial-pushdown correctness via
9
9
  // the residual.
10
- import { partitionPushable, residualQuery, asEquality, constValue } from "../plan.js";
10
+ //
11
+ // The same indexes serve the engine's correlated probes: `context()` is a
12
+ // `DataContext` that resolves the root and answers `indexFor(collection, path)`
13
+ // from them, so a nested `^emp first { where id == ^manager_id }` probes the
14
+ // pre-built index instead of building one per run. The planner's own plans
15
+ // carry that context too, so a residual's nested blocks still see every root.
16
+ //
17
+ // Buckets are keyed under OQX equality (`HashIndex`): absent ≡ null, `-0` ≡ `0`,
18
+ // `NaN` matches nothing, objects by reference — exactly what `==` compares, so
19
+ // a probe returns the rows the scan would keep (a plain `Map` would keep `null`
20
+ // and `undefined` apart and match `NaN` to itself).
21
+ import { DefaultContext } from "../context.js";
22
+ import { partitionPushable, residualQuery, asEquality, constValue, ROWS_ROOT } from "../plan.js";
23
+ import { HashIndex, intersectPositions } from "../optimize/hash-index.js";
11
24
  export class IndexedCollection {
12
25
  name;
13
26
  rows;
14
27
  indexes = new Map();
15
- /** Index `rows` (exposed as root `name`) on each field in `indexFields`. */
28
+ /** Index `rows` (exposed as root `name`) on each field in `indexFields`. A
29
+ * field is read as the engine reads a bare identifier (own property; array
30
+ * element for an integer-spelled name); a row without it keys as absent. */
16
31
  constructor(name, rows, indexFields) {
17
32
  this.name = name;
18
33
  this.rows = rows;
34
+ const read = new DefaultContext();
19
35
  for (const field of indexFields) {
20
- const idx = new Map();
21
- for (const row of rows) {
22
- const key = row?.[field];
23
- let bucket = idx.get(key);
24
- if (!bucket)
25
- idx.set(key, (bucket = []));
26
- bucket.push(row);
27
- }
36
+ const idx = new HashIndex();
37
+ rows.forEach((row, i) => idx.add(read.get(row, field), i));
28
38
  this.indexes.set(field, idx);
29
39
  }
30
40
  }
41
+ /** The pre-built index for `collection` on `path` when `collection` is this
42
+ * collection's rows and `path` is one indexed field — the `DataContext.indexFor`
43
+ * seam, which `context()` wires up. */
44
+ indexFor(collection, path) {
45
+ if (collection !== this.rows || path.length !== 1)
46
+ return undefined;
47
+ return this.indexes.get(path[0]);
48
+ }
49
+ /** A `DataContext` over plain objects that serves this collection as the
50
+ * root `name` (plus `extraRoots`) and exposes the indexes through
51
+ * `indexFor`, so the engine's correlated probes reuse them. */
52
+ context(extraRoots = {}) {
53
+ return new IndexedContext(this, { ...extraRoots, [this.name]: this.rows });
54
+ }
31
55
  plan(query, params) {
32
56
  if (query.source.kind !== "ident" || query.source.name !== this.name)
33
57
  return null;
@@ -40,18 +64,25 @@ export class IndexedCollection {
40
64
  if (pushed.length === 0)
41
65
  return null; // no index probe available — let the scan handle it
42
66
  // Intersect the candidate sets from each indexed equality (smallest first).
43
- let candidate = null;
44
- for (const e of pushed) {
67
+ const buckets = pushed.map((e) => {
45
68
  const eq = asEquality(e);
46
- const bucket = this.indexes.get(eq.field).get(constValue(eq.value, params)) ?? [];
47
- candidate = candidate === null ? bucket.slice() : intersect(candidate, bucket);
48
- }
49
- const rows = candidate ?? [];
50
- return { rows: () => rows, residual: residualQuery(query, residual) };
69
+ return this.indexes.get(eq.field).lookup(constValue(eq.value, params));
70
+ }).sort((a, b) => a.length - b.length);
71
+ let candidate = buckets[0];
72
+ for (let i = 1; i < buckets.length && candidate.length > 0; i++)
73
+ candidate = intersectPositions(candidate, buckets[i]);
74
+ const rows = candidate.map((i) => this.rows[i]);
75
+ return { rows: () => rows, residual: residualQuery(query, residual), context: this.context({ [ROWS_ROOT]: rows }) };
51
76
  }
52
77
  }
53
- function intersect(a, b) {
54
- const set = new Set(b);
55
- return a.filter((x) => set.has(x));
78
+ class IndexedContext extends DefaultContext {
79
+ owner;
80
+ constructor(owner, roots) {
81
+ super(roots);
82
+ this.owner = owner;
83
+ }
84
+ indexFor(collection, path) {
85
+ return this.owner.indexFor(collection, path);
86
+ }
56
87
  }
57
88
  //# sourceMappingURL=indexed.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexed.js","sourceRoot":"","sources":["../../src/adapters/indexed.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indexed.js","sourceRoot":"","sources":["../../src/adapters/indexed.ts"],"names":[],"mappings":"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"}
package/dist/ast.d.ts CHANGED
@@ -109,7 +109,7 @@ export interface Subquery {
109
109
  follow: Follow | null;
110
110
  /** `values` — scalar projection mode: the (single) projected expression is the
111
111
  * row's result itself rather than being wrapped in a `{ name: value }` record,
112
- * so `name values` yields `["Bob", …]` and `$value values` yields the rows. */
112
+ * so `name values` yields `["Bob", …]` and `$it values` yields the rows. */
113
113
  values?: boolean;
114
114
  /** `limit N` / `offset N` — bound the row set AFTER where/order/distinct and
115
115
  * BEFORE the consumer reduces it, so `count { … limit 5 }` is at most 5 and
package/dist/ast.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"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"}
package/dist/context.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { RegexDialect } from "./regex.js";
2
+ import type { RowIndex } from "./optimize/hash-index.js";
2
3
  /** Result of a context-provided function/method call: `handled: false` tells the
3
4
  * engine to fall back to the builtin table (or error if none). */
4
5
  export interface CallResult {
@@ -17,6 +18,18 @@ export interface DataContext {
17
18
  get(row: unknown, key: string): unknown;
18
19
  /** Coerce a relation/source value into rows (may be lazy). */
19
20
  toRows(value: unknown): Iterable<unknown>;
21
+ /** Optional: a value this context handed out as a stand-in for a collection
22
+ * it has not read yet (a lazy table handle), resolved to what it stands for.
23
+ * The engine calls it on every value it is about to observe AS A VALUE — an
24
+ * operand of `==`/`in`/arithmetic, a function or method argument, a projected
25
+ * item, an `order by` or `distinct` key, a `where` scalar, a lift — and never
26
+ * on a value it reads in ROW POSITION (the query source, a block receiver, a
27
+ * body-level `from`, a `follow` destination), which reaches `toRows` and
28
+ * `indexFor` as handed out, so a store-backed context can answer a probe on
29
+ * the handle without reading the table and still never lets the stand-in be
30
+ * seen by the language. Absent = identity; a context whose handle already
31
+ * behaves as the collection (a `Proxy` over an array) needs none. */
32
+ materialize?(value: unknown): unknown;
20
33
  /** Identity of a row for `follow` cycle detection / dedup. */
21
34
  identity(row: unknown): unknown;
22
35
  /** Optional custom free function; return `{ handled: false }` to defer. */
@@ -31,6 +44,17 @@ export interface DataContext {
31
44
  * `regexMatches(recv, args, dialect)` is the helper). `BUILTIN_METHODS.matches`
32
45
  * is always the baseline. */
33
46
  readonly regexDialect?: RegexDialect;
47
+ /** Optional: a pre-built equality index over `collection` (a value this
48
+ * context served as a root or relation) on the property path `path`
49
+ * (`["customer_id"]`, `["meta", "id"]`; `[]` keys by the row itself). The
50
+ * engine asks before building its own hash index for a correlated equality
51
+ * in a nested block (`where id == ^customer_id`); return `undefined` to let
52
+ * it build one. `lookup(value)` must return the ascending positions, into
53
+ * `toRows(collection)` in order, of the rows whose value at `path` equals
54
+ * `value` under OQX equality (SEMANTICS §5: absent ≡ null, `-0` ≡ `0`, `NaN`
55
+ * matches nothing, objects by reference). `IndexedCollection.context()`
56
+ * implements it over its indexes. */
57
+ indexFor?(collection: unknown, path: readonly string[]): RowIndex | undefined;
34
58
  }
35
59
  export interface DefaultContextOptions {
36
60
  /** See `DataContext.regexDialect`. Default `"oqx"`. */
@@ -1 +1 @@
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;;;;;kFAK8E;IAC9E,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"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAQzD;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;;;;;kFAK8E;IAC9E,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;;;;;;;;;;yEAUqE;IACrE,WAAW,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;IACtC,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;IACrC;;;;;;;;;yCASqC;IACrC,QAAQ,CAAC,CAAC,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,GAAG,SAAS,CAAC;CAC/E;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"}
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}