@jarenjs/linq 0.49.2 → 0.56.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 (77) hide show
  1. package/ARCHITECTURE.md +217 -0
  2. package/README.md +559 -17
  3. package/docs/APP-PEN.md +1143 -0
  4. package/docs/CONTRACT-PEN.md +1217 -0
  5. package/docs/DB-CLIENT.md +814 -0
  6. package/docs/FLOW-PEN.md +1026 -0
  7. package/docs/FORMS-PEN.md +940 -0
  8. package/docs/JSLT-PEN.md +955 -0
  9. package/docs/LINQ-FORMAT.md +771 -383
  10. package/docs/MIGRATION-PEN.md +781 -0
  11. package/docs/MODEL-PEN.md +1083 -0
  12. package/docs/QUERY-PEN.md +1636 -0
  13. package/docs/SCHEMA-PEN.md +1218 -0
  14. package/package.json +57 -4
  15. package/src/app/action.js +255 -0
  16. package/src/app/capture.js +63 -0
  17. package/src/app/define.js +260 -0
  18. package/src/app/index.js +20 -0
  19. package/src/app/patch.js +277 -0
  20. package/src/app/sub.js +106 -0
  21. package/src/async.js +329 -75
  22. package/src/capture-root.js +82 -0
  23. package/src/concurrency.js +9 -4
  24. package/src/contract/define.js +269 -0
  25. package/src/contract/http.js +247 -0
  26. package/src/contract/index.js +23 -0
  27. package/src/contract/operation.js +342 -0
  28. package/src/db/handle.js +86 -0
  29. package/src/db/include.js +316 -0
  30. package/src/db/index.js +19 -0
  31. package/src/db/live.js +43 -0
  32. package/src/db/membership.js +37 -0
  33. package/src/db/open.js +82 -0
  34. package/src/document.js +143 -13
  35. package/src/effect.js +65 -0
  36. package/src/errors.js +69 -6
  37. package/src/expression.js +437 -36
  38. package/src/flow/capture.js +33 -0
  39. package/src/flow/dag.js +302 -0
  40. package/src/flow/fsm.js +328 -0
  41. package/src/flow/index.js +22 -0
  42. package/src/forms/index.js +43 -0
  43. package/src/forms/rules.js +170 -0
  44. package/src/forms/submit.js +177 -0
  45. package/src/index.js +4 -2
  46. package/src/jslt/body.js +226 -0
  47. package/src/jslt/index.js +18 -0
  48. package/src/jslt/rules.js +207 -0
  49. package/src/json-boundary.js +90 -0
  50. package/src/migration/define.js +323 -0
  51. package/src/migration/index.js +15 -0
  52. package/src/migration/steps.js +248 -0
  53. package/src/model/collection.js +171 -0
  54. package/src/model/define.js +125 -0
  55. package/src/model/entity.js +307 -0
  56. package/src/model/index.js +47 -0
  57. package/src/model/relation.js +85 -0
  58. package/src/provider.js +137 -20
  59. package/src/schema/brand.js +31 -0
  60. package/src/schema/builders.js +526 -0
  61. package/src/schema/check.js +29 -0
  62. package/src/schema/emit.js +394 -0
  63. package/src/schema/factories.js +239 -0
  64. package/src/schema/index.js +37 -0
  65. package/src/schema-of.js +24 -0
  66. package/src/sequence.js +233 -103
  67. package/src/sources.js +10 -3
  68. package/types/app.d.ts +293 -0
  69. package/types/contract.d.ts +371 -0
  70. package/types/db.d.ts +188 -0
  71. package/types/flow.d.ts +285 -0
  72. package/types/forms.d.ts +253 -0
  73. package/types/index.d.ts +231 -26
  74. package/types/jslt.d.ts +193 -0
  75. package/types/migration.d.ts +201 -0
  76. package/types/model.d.ts +493 -0
  77. package/types/schema.d.ts +494 -0
package/src/document.js CHANGED
@@ -21,24 +21,142 @@
21
21
  * not tell an absent clause from a null-valued one, and silently emitted
22
22
  * the document WITHOUT the clause. Every such query then returned its
23
23
  * unfiltered, unprojected source.
24
+ *
25
+ * An item is an item. The engine's `$for` unpacks an item that is an
26
+ * array into its members, one level (QUERY-FORMAT §6.2, D4) — the
27
+ * ergonomic default for a path like `$.tags`, and the wrong default for
28
+ * a chain, where an array-valued ROW (a CSV record, a pair) is one item
29
+ * the C# contract never splits. So every source a phrase iterates is
30
+ * bound through an ARRAY CONSTRUCTOR — `{ "$for": { "it": ["$[*]"] } }`
31
+ * — whose one array item is unpacked exactly once, into the rows as
32
+ * they are; a reseated phrase is packed the same way, because a `$for`
33
+ * over an inner phrase's result would unpack again. The one source left
34
+ * bare is a PROVIDER's own root (`'$.Post[*]'`): a stored document is an
35
+ * object, so D4 never applies there, and the bare root is the shape the
36
+ * provider's planner pushes. The streaming async surface keeps items by
37
+ * construction, so the two surfaces agree on every row shape.
24
38
  */
25
39
 
26
40
  import { LinqBuildError } from './errors.js';
27
41
 
42
+ /** The binding names the emitted documents own (QUERY-PEN §7): the
43
+ * item bindings, the accumulator and the group — and the relation-hop
44
+ * bindings `r1`, `r2`, … a capture allocates (expression.js). A
45
+ * parameter may shadow none of them. */
46
+ export const RESERVED_BINDINGS = Object.freeze(['it', 'it2', 'acc', 'g']);
47
+ const HOP_BINDING_RE = /^r[1-9][0-9]*$/;
48
+
49
+ /**
50
+ * Whether a parameter name collides with a binding the document owns.
51
+ * @param {string} name
52
+ * @returns {boolean}
53
+ */
54
+ export function isReservedBinding(name) {
55
+ return RESERVED_BINDINGS.includes(name) || HOP_BINDING_RE.test(name);
56
+ }
57
+
58
+ /** The reserved names, for a refusal's message. */
59
+ export const RESERVED_BINDINGS_TEXT = 'it, it2, acc, g, and r1, r2, … for relation hops';
60
+
61
+ /** The member a `groupBy`'s items carry the group's rows in. Spelled
62
+ * once: the emitter writes it here and `expression.js` gives that member
63
+ * its fan, so `g.items.count()` counts the ROWS the way a group-join's
64
+ * `g.count()` already does. */
65
+ export const GROUP_ITEMS = 'items';
66
+
67
+ /** The stage kinds after which the items are no longer the source's
68
+ * rows: a relation name on them is an ordinary member (QUERY-PEN §3).
69
+ * The async surface adds its host boundary, `mapAsync`. */
70
+ export const PROJECTING_STAGES = new Set(['select', 'groupBy', 'join', 'groupJoin', 'aggregate']);
71
+
28
72
  /** The fixed clause order a phrase may fill left-to-right. */
29
- const SLOT_ORDER = ['where', 'groupby', 'orderby', 'return'];
73
+ const SLOT_ORDER = ['bindings', 'where', 'groupby', 'orderby', 'return'];
30
74
 
31
75
  /** The "this clause slot is unfilled" sentinel: a fresh object, so no
32
76
  * value a caller can express is ever mistaken for it. */
33
77
  const EMPTY = Symbol('linq.emptySlot');
34
78
 
35
- /** An open FLWOR phrase under construction. */
36
- function openPhrase(source) {
79
+ /**
80
+ * An open FLWOR phrase under construction. `bare` marks a source the
81
+ * phrase iterates WITHOUT packing (a provider's root); every other
82
+ * source is packed so an array item stays one item.
83
+ * @param {any} source
84
+ * @param {boolean} [bare]
85
+ */
86
+ function openPhrase(source, bare = false) {
37
87
  return {
38
- source, fold: EMPTY, where: EMPTY, groupby: EMPTY, orderby: EMPTY, ret: EMPTY,
88
+ source, bare,
89
+ fold: EMPTY, bindings: EMPTY, where: EMPTY, groupby: EMPTY, orderby: EMPTY, ret: EMPTY,
39
90
  };
40
91
  }
41
92
 
93
+ /** Whether no clause of the phrase has been filled. */
94
+ function untouched(phrase) {
95
+ return phrase.fold === EMPTY && phrase.bindings === EMPTY && phrase.where === EMPTY
96
+ && phrase.groupby === EMPTY && phrase.orderby === EMPTY && phrase.ret === EMPTY;
97
+ }
98
+
99
+ /**
100
+ * The expression a phrase's items come from, as a `$for` source: packed
101
+ * (D4 unpacks the one array item back into the rows) unless the source
102
+ * is a bare provider root.
103
+ * @param {any} phrase
104
+ */
105
+ function iterated(phrase) {
106
+ return phrase.bare ? phrase.source : [phrase.source];
107
+ }
108
+
109
+ /**
110
+ * A closed phrase as the SOURCE of another `$for` (a join side): a bare
111
+ * untouched root stays bare; anything else is packed.
112
+ * @param {any} phrase
113
+ */
114
+ function packed(phrase) {
115
+ return phrase.bare && untouched(phrase) ? phrase.source : [closePhrase(phrase)];
116
+ }
117
+
118
+ /**
119
+ * A `selectMany` projection: the projected value iterated one level —
120
+ * an array member's elements, a constructed array's members, a scalar
121
+ * as itself — so `Seq<R[]>` really flattens to `Seq<R>`. The nested
122
+ * phrase rebinds `it` legally: its source is evaluated in the enclosing
123
+ * scope (the row), its body sees the element.
124
+ * @param {any} projection - a captured expression
125
+ * @returns {any}
126
+ */
127
+ export function fanProjection(projection) {
128
+ return { $for: { it: projection }, $return: '$it' };
129
+ }
130
+
131
+ /**
132
+ * A deep, independent copy of an emitted query document. Plain data
133
+ * only, which is exactly what a document is — every captured expression
134
+ * has already passed the JSON-domain boundary in `expression.js`, so
135
+ * there is nothing here a structural copy would lose.
136
+ * @param {any} node
137
+ * @returns {any}
138
+ */
139
+ export function snapshot(node) {
140
+ if (node === null || typeof node !== 'object') return node;
141
+ if (Array.isArray(node)) return node.map(snapshot);
142
+ /** @type {Record<string, any>} */
143
+ const out = {};
144
+ for (const key of Object.keys(node)) defineOwn(out, key, snapshot(node[key]));
145
+ return out;
146
+ }
147
+
148
+ /**
149
+ * Assign an OWN property, so a `__proto__` member stays a member instead
150
+ * of silently replacing the object's prototype and vanishing.
151
+ * @param {Record<string, any>} target
152
+ * @param {string} key
153
+ * @param {any} value
154
+ */
155
+ function defineOwn(target, key, value) {
156
+ Object.defineProperty(target, key,
157
+ { value, writable: true, enumerable: true, configurable: true });
158
+ }
159
+
42
160
  /** Whether every slot AFTER `slot` is still empty — chain order must
43
161
  * agree with the phrase's fixed semantic order. */
44
162
  function laterSlotsFree(phrase, slot) {
@@ -57,12 +175,11 @@ function slotFree(phrase, slot) {
57
175
 
58
176
  /** Close a phrase into a query expression. */
59
177
  function closePhrase(phrase) {
60
- const untouched = phrase.fold === EMPTY && phrase.where === EMPTY
61
- && phrase.groupby === EMPTY && phrase.orderby === EMPTY && phrase.ret === EMPTY;
62
- if (untouched) return phrase.source;
178
+ if (untouched(phrase)) return phrase.source;
63
179
  const doc = {};
64
180
  if (phrase.fold !== EMPTY) doc.$fold = { acc: phrase.fold };
65
- doc.$for = { it: phrase.source };
181
+ doc.$for = { it: iterated(phrase) };
182
+ if (phrase.bindings !== EMPTY) doc.$let = phrase.bindings;
66
183
  if (phrase.where !== EMPTY) doc.$where = phrase.where;
67
184
  if (phrase.groupby !== EMPTY) doc.$groupby = { g: phrase.groupby };
68
185
  if (phrase.orderby !== EMPTY) {
@@ -72,7 +189,7 @@ function closePhrase(phrase) {
72
189
  // so the member sequence packs into an array constructor and an
73
190
  // empty grouping key reads as null
74
191
  doc.$return = phrase.ret !== EMPTY ? phrase.ret : (phrase.groupby !== EMPTY
75
- ? { key: { $default: ['$g', null] }, items: ['$it'] }
192
+ ? { key: { $default: ['$g', null] }, [GROUP_ITEMS]: ['$it'] }
76
193
  : '$it');
77
194
  return doc;
78
195
  }
@@ -86,10 +203,12 @@ const andJoin = (a, b) => (a === EMPTY ? b : { $and: [a, b] });
86
203
  * (`'$[*]'` for a plain source; a stripped document for
87
204
  * `fromDocument`)
88
205
  * @param {readonly any[]} stages
206
+ * @param {{ bareRoot?: boolean }} [options] - `bareRoot` iterates the
207
+ * root without packing (a provider's own root; see the header)
89
208
  * @returns {any} the emitted query document (plain JSON)
90
209
  */
91
- export function emitDocument(root, stages) {
92
- let phrase = openPhrase(root);
210
+ export function emitDocument(root, stages, options = undefined) {
211
+ let phrase = openPhrase(root, options?.bareRoot === true);
93
212
  /** Close the open phrase and reopen over its result. */
94
213
  const reseat = () => { phrase = openPhrase(closePhrase(phrase)); };
95
214
 
@@ -125,13 +244,24 @@ export function emitDocument(root, stages) {
125
244
  break;
126
245
  case 'join':
127
246
  // the hash-join shape: nested bindings + equality (the engine's
128
- // compile-time rewrite turns exactly this into a hash probe)
247
+ // compile-time rewrite turns exactly this into a hash probe;
248
+ // packed sources keep it — the probe is keyed on the bindings,
249
+ // not on the sources' spelling)
129
250
  phrase = openPhrase({
130
- $for: { it: closePhrase(phrase), it2: stage.inner },
251
+ $for: { it: packed(phrase), it2: stage.innerBare ? stage.inner : [stage.inner] },
131
252
  $where: stage.on,
132
253
  $return: stage.result,
133
254
  });
134
255
  break;
256
+ case 'groupJoin':
257
+ // the matching group bound as an ARRAY value (`$let`, one item)
258
+ // so the projection can index it, fan it and place it in a
259
+ // member; its aggregates fan over the members (expression.js)
260
+ if (!slotFree(phrase, 'bindings')) reseat();
261
+ phrase.bindings = { g: [stage.group] };
262
+ phrase.ret = stage.projection;
263
+ reseat();
264
+ break;
135
265
  case 'aggregate': {
136
266
  // the seeded fold: its own phrase, closed immediately — the
137
267
  // result is one accumulated value, not a tuple stream
package/src/effect.js ADDED
@@ -0,0 +1,65 @@
1
+ //@ts-check
2
+ /**
3
+ * @file The effect descriptor two formats spell identically —
4
+ * `{ run, with? }` — under a machine's `entry`/`exit`/`effects`
5
+ * (FLOW-FORMAT §2) and under an app transition's `effects`
6
+ * (APP-FORMAT §5.1). One shape, one brand, one reader; what differs is
7
+ * only where the props come from, which each pen passes in: the flow
8
+ * pen captures them over the step scope, the app pen leaves them to the
9
+ * action capture already in progress.
10
+ *
11
+ * `run` is a name the pen writes and never resolves — the host's
12
+ * registry owns the handler. What an unregistered name costs differs by
13
+ * engine and neither cost is the pen's: an app document's action loop
14
+ * refuses it (`JA2006`), while `compileFsm` never looks one up at all,
15
+ * because FLOW-FORMAT §1.1 makes effect EXECUTION a non-goal — the
16
+ * descriptor comes back as data and the host decides what to do with it.
17
+ */
18
+
19
+ import { LinqBuildError } from './errors.js';
20
+ import { describeValue } from './json-boundary.js';
21
+
22
+ /** The descriptor brand: how a pen tells `effect()`'s result apart. */
23
+ export const EFFECT = Symbol.for('@jarenjs/linq/effect-descriptor');
24
+
25
+ /**
26
+ * One `{ run, with? }` descriptor, branded and frozen.
27
+ * @param {string} run - the host-registered handler name
28
+ * @param {any} props - the `with` member, or `undefined`
29
+ * @param {(props: any) => any} readProps - how this pen spells the props
30
+ * @returns {any} the effect declaration
31
+ */
32
+ export function effectDescriptor(run, props, readProps) {
33
+ if (typeof run !== 'string' || run === '') {
34
+ throw new LinqBuildError('JL0101',
35
+ `effect() takes the handler name as a non-empty string, got ${describeValue(run)}`, '/run');
36
+ }
37
+ const out = { run };
38
+ if (props !== undefined) out.with = readProps(props);
39
+ Object.defineProperty(out, EFFECT, { value: true, enumerable: false });
40
+ return Object.freeze(out);
41
+ }
42
+
43
+ /**
44
+ * An effects list, as a document carries it: plain `{ run, with? }`
45
+ * objects, the brand dropped.
46
+ * @param {any} list
47
+ * @param {string} what - the member, for the message
48
+ * @returns {any[]}
49
+ */
50
+ export function readEffects(list, what) {
51
+ if (!Array.isArray(list)) {
52
+ throw new LinqBuildError('JL0101',
53
+ `${what} is an array of effect() descriptors, got ${describeValue(list)}`);
54
+ }
55
+ return list.map((declared, i) => {
56
+ if (declared === null || typeof declared !== 'object' || Array.isArray(declared)
57
+ || declared[EFFECT] !== true) {
58
+ throw new LinqBuildError('JL0101',
59
+ `${what}[${i}] is effect(run, with?), got ${describeValue(declared)}`);
60
+ }
61
+ const out = { run: declared.run };
62
+ if (declared.with !== undefined) out.with = declared.with;
63
+ return out;
64
+ });
65
+ }
package/src/errors.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * contract: every failure carries a stable `code` (JL0xxx build, JL2xxx
5
5
  * runtime), a bare `reason`, a composed `message`, and — where a
6
6
  * document position exists — a `docPath`. The normative table lives in
7
- * docs/LINQ-FORMAT.md §9, proven in sync with `LINQ_CODES` below by a
7
+ * docs/QUERY-PEN.md §9, proven in sync with `LINQ_CODES` below by a
8
8
  * test.
9
9
  */
10
10
 
@@ -12,7 +12,7 @@ import { CodedError } from '@jarenjs/core/errors';
12
12
 
13
13
  /**
14
14
  * The runtime code table (the `CSV_CODES` shape): one entry per code
15
- * this package can raise, proven in sync with LINQ-FORMAT.md §9's
15
+ * this package can raise, proven in sync with QUERY-PEN.md §9's
16
16
  * normative table by a test.
17
17
  */
18
18
  export const LINQ_CODES = Object.freeze({
@@ -22,10 +22,20 @@ export const LINQ_CODES = Object.freeze({
22
22
  JL0004: 'an undeclared or reserved parameter name was used',
23
23
  JL0005: 'an operator was used invalidly at build time',
24
24
  JL0006: 'an unsupported operator was invoked',
25
+ JL0007: 'a provider serves several entity roots and has no root of its own',
26
+ JL0101: 'a pen received a value it cannot spell: not JSON, or not what the keyword takes',
27
+ JL0102: 'a pen was asked for a construct the format cannot carry',
28
+ JL0103: 'a $defs name collision, a dangling ref, or an unnamed recursion',
29
+ JL0104: 'a pen-owned keyword through meta(), or an external a captured rule did not declare',
30
+ JL0105: 'a relation hop cannot lower: a many-to-many member, a composite or undeclared key, or a malformed relation entry',
31
+ JL0106: 'a migration step names a table the target model does not declare, or a draft it cannot match',
32
+ JL0107: 'a client operation named a member that is not the relation kind it needs',
25
33
  JL2001: 'first/single found no element',
26
34
  JL2002: 'single found more than one element',
27
35
  JL2003: 'elementAt is out of range',
28
36
  JL2004: 'an asynchronous provider cannot back the synchronous surface',
37
+ JL2005: 'a push queue was fed after it ended',
38
+ JL2006: 'a provider answered an element terminal with something other than one array',
29
39
  });
30
40
 
31
41
  /**
@@ -42,13 +52,60 @@ export const LINQ_CODES = Object.freeze({
42
52
  * compileTypeTest })`, e.g. `createTypeTestCompiler()` from
43
53
  * `@jarenjs/validate/query`)
44
54
  * - `JL0004` — a parameter was referenced without being declared via
45
- * `.params({...})`, or a declared name is reserved (`it`, `acc`,
46
- * `g` — the document's own binding names)
55
+ * `.params({...})`, a declared name is reserved (`it`, `it2`, `acc`,
56
+ * `g`, and the relation-hop bindings `r1`, `r2`, … — the document's
57
+ * own binding names), a binding is not query data, or the two sides
58
+ * of a `join`/`groupJoin`/`concat` bind one name to different values
47
59
  * - `JL0005` — an operator was used invalidly at build time (`thenBy`
48
- * without `orderBy`, `all()` off a plain path, a negative
49
- * `skip`/`take`, a value that cannot embed in a document)
60
+ * without `orderBy`, `all()` off an operator result rather than a
61
+ * path, a member read off a to-many relation before `all()`, a
62
+ * negative `skip`/`take`, a value that cannot embed in a document)
50
63
  * - `JL0006` — an operator the mapping table records as
51
64
  * `unsupported` was invoked (`zip`); the table names the reason
65
+ * - `JL0007` — `from()`/`fromAsync()` received a provider that serves
66
+ * several entity roots (`roots`) and has no `root` of its own — a
67
+ * store with entities; chain over one of them (`store.entity(name)`)
68
+ *
69
+ * The pens (`@jarenjs/linq/schema`, `/model`, `/jslt`, `/migration`,
70
+ * `/contract`, `/flow`) and the client (`/db`; LINQ-FORMAT.md §1.3)
71
+ * refuse with the `JL01xx` codes:
72
+ *
73
+ * - `JL0101` — a pen received a value it cannot spell: a function,
74
+ * symbol, bigint, `NaN`, `±Infinity`, `-0`, a class instance or a
75
+ * cycle where JSON was needed, or a value that is not what the
76
+ * keyword takes (`min('x')`, a non-builder member)
77
+ * - `JL0102` — a construct the format cannot carry: a function
78
+ * `refine`/`transform`, a coercion the normalizer would never run,
79
+ * closed objects under `allOf`, an annotation on `never()`, an
80
+ * `apply()` as a bare object member, a `match` of `{}`, a path
81
+ * template form CONTRACT-FORMAT §4.2 reserves, an operation `kind`
82
+ * outside the three, a flow guard given as a plain string
83
+ * (FLOW-FORMAT §3 makes a non-`$` literal vacuously true), a state
84
+ * or node id no declaration carries
85
+ * - `JL0103` — two distinct builders under one `$defs` name, a
86
+ * `ref()` no definition answers, or a `lazy()` that is not named
87
+ * - `JL0104` — a pen-owned keyword written through `meta()`, or a
88
+ * captured rule naming an external it did not declare (a `check()`
89
+ * or `body()` external other than `root`/`path` and, for a body,
90
+ * its declared parameters; a `compute()` external at all, or a flow
91
+ * guard/`with`/node-query/`select` external at all — both flow
92
+ * engines evaluate with one `$` and nothing else)
93
+ * - `JL0105` — a relation hop on the chain (the query pen) cannot
94
+ * lower: the member is a many-to-many relation, whose join table is
95
+ * not a queryable root in this version (`load({ include })` reads the
96
+ * memberships); or the relation's key column or the key it references
97
+ * is composite or undeclared; or the provider's relation table holds
98
+ * something that is not a relation record
99
+ * - `JL0106` — a migration step names an entity or collection the
100
+ * target model does not declare (`transform`, `assert`, `derive`),
101
+ * or a `transform` over a planned document finds no draft to
102
+ * replace, or two drafts for one name
103
+ * - `JL0107` — the client was handed a member that is not the relation
104
+ * kind the operation needs: `include()` picks a declared relation
105
+ * member (a scalar member, or a name the model does not declare, is
106
+ * refused naming the declared ones); `link()`/`unlink()` attach
107
+ * many-to-many memberships only (a to-one or to-many relation is
108
+ * refused naming its kind)
52
109
  */
53
110
  export class LinqBuildError extends CodedError {
54
111
  /**
@@ -77,6 +134,12 @@ export class LinqBuildError extends CodedError {
77
134
  * `Sequence` terminal is a value (`toArray(): T[]`), so a promise
78
135
  * cannot be returned under that type; emit `toDocument()` and await
79
136
  * the provider directly instead.
137
+ * - `JL2005` — `feed()` was called on a push queue after `end()`
138
+ * closed it (a condition of the running stream, not of the build)
139
+ * - `JL2006` — a provider answered an element terminal (`toArray`,
140
+ * `first`, …) with something other than exactly one array; the
141
+ * emitted document is an array constructor, so a conforming
142
+ * `execute()` never answers `undefined` there
80
143
  */
81
144
  export class LinqRuntimeError extends CodedError {
82
145
  /**