@vaultgradient/pipequery-lang 1.12.2 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -3
- package/dist/builder/index.cjs +926 -0
- package/dist/builder/index.d.cts +458 -0
- package/dist/builder/index.d.ts +458 -0
- package/dist/builder/index.js +926 -0
- package/dist/chunk-KNVFXND5.js +2384 -0
- package/dist/chunk-LWM2ZXGD.cjs +2384 -0
- package/dist/engine/index.cjs +12 -2084
- package/dist/engine/index.d.cts +16 -140
- package/dist/engine/index.d.ts +16 -140
- package/dist/engine/index.js +19 -2091
- package/dist/react/index.cjs +18 -2
- package/dist/react/index.js +17 -1
- package/dist/types-CukH_v-n.d.cts +141 -0
- package/dist/types-CukH_v-n.d.ts +141 -0
- package/package.json +11 -1
package/README.md
CHANGED
|
@@ -151,9 +151,8 @@ query(data, 'where(price > 100) | sort(price desc)');
|
|
|
151
151
|
query(data, 'rollup(sum(price) as total, count() as n)');
|
|
152
152
|
// → [{ total: 1377, n: 3 }]
|
|
153
153
|
|
|
154
|
-
// Group and aggregate
|
|
155
|
-
query(data, 'groupBy(category) |
|
|
156
|
-
// → [{ category: 'Electronics', avgPrice: 514 }, { category: 'Furniture', avgPrice: 349 }]
|
|
154
|
+
// Group and aggregate (one row per category)
|
|
155
|
+
query(data, 'groupBy(category) | select(category, avg(price) as avgPrice)');
|
|
157
156
|
```
|
|
158
157
|
|
|
159
158
|
## Query Syntax
|
|
@@ -262,6 +261,18 @@ const ast = parseQuery('items | where(price > 100)');
|
|
|
262
261
|
// Inspect tokens, operations, expressions
|
|
263
262
|
```
|
|
264
263
|
|
|
264
|
+
### `printQuery(ast)`
|
|
265
|
+
|
|
266
|
+
Print a pipeline AST back to canonical pipequery text. Parentheses are added only where precedence requires them, numbers and strings are escaped, and invalid nodes throw. Use it to inspect or rewrite a parsed query, for example to cap every result.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { parseQuery, printQuery } from '@andreadito/pipequery-lang';
|
|
270
|
+
|
|
271
|
+
const ast = parseQuery('orders | where(total > 100)');
|
|
272
|
+
ast.operations.push({ kind: 'FirstOp', count: 1000 });
|
|
273
|
+
printQuery(ast); // orders | where(total > 100) | first(1000)
|
|
274
|
+
```
|
|
275
|
+
|
|
265
276
|
### `liveQuery(data, expression, options)`
|
|
266
277
|
|
|
267
278
|
Streaming query evaluator that accepts data patches and re-executes efficiently.
|
|
@@ -327,6 +338,67 @@ A TextMate grammar is included at `dist/highlighting/textmate/pipequery.tmLangua
|
|
|
327
338
|
|
|
328
339
|
**Peer dependencies:** `@codemirror/language`, `@codemirror/state`, `@codemirror/view`, `@lezer/highlight` (CodeMirror only)
|
|
329
340
|
|
|
341
|
+
### Typed query builder
|
|
342
|
+
|
|
343
|
+
`@andreadito/pipequery-lang/builder` builds queries in code instead of strings. It is a headless utility, not an ORM: it builds the same AST the parser produces, and `toString()` prints it back with `printQuery`. The builder entry imports nothing from React or MUI.
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { from, agg, col } from '@andreadito/pipequery-lang/builder';
|
|
347
|
+
import { query } from '@andreadito/pipequery-lang';
|
|
348
|
+
|
|
349
|
+
type Order = { id: number; customerId: number; total: number; status: string; region: string };
|
|
350
|
+
|
|
351
|
+
const paid = from<Order>('orders')
|
|
352
|
+
.where((o) => o.status.eq('paid'))
|
|
353
|
+
.join('customers', (o, c) => o.customerId.eq(c.id))
|
|
354
|
+
.select((o) => ({ id: o.id, name: o.name, total: o.total }));
|
|
355
|
+
|
|
356
|
+
paid.toString();
|
|
357
|
+
// orders | where(status == "paid") | join(customers, customerId == id) | select(id, name, total)
|
|
358
|
+
|
|
359
|
+
paid.run({ orders, customers }); // rows
|
|
360
|
+
query({ orders, customers }, paid.toString()); // same rows
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Terminal aggregates end the pipeline and return a `Scalar`, whose `run()` returns one value:
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
from<Order>('orders').where((o) => o.status.eq('paid')).sum((o) => o.total).run({ orders }); // 160
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Grouping works with `rollup` (group keys go inside it) or with `groupBy` followed by `select`:
|
|
370
|
+
|
|
371
|
+
```ts
|
|
372
|
+
from<Order>('orders').rollup('region', agg.sum(col('total')).as('revenue'));
|
|
373
|
+
// orders | rollup(region, sum(total) as revenue)
|
|
374
|
+
|
|
375
|
+
from<Order>('orders')
|
|
376
|
+
.groupBy('region')
|
|
377
|
+
.select((o) => ({ region: o.region, revenue: agg.sum(o.total) }));
|
|
378
|
+
// orders | groupBy(region) | select(region, sum(total) as revenue)
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Escape hatch.** `.pipe()` accepts raw pipequery operations. They are parsed with the real parser, so a malformed string throws a `ParseError`:
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
from<Order>('orders').pipe('where(total > 100) | first(10)');
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Values are escaped.** `o.status.eq(input)` prints `input` as a string literal, so quotes, backslashes and newlines in user input cannot change the query structure.
|
|
388
|
+
|
|
389
|
+
**Invalid input throws at build time.** Bad identifiers, reserved words, `NaN`/`Infinity`, `first(-1)` and misplaced aliases throw a `BuilderError`.
|
|
390
|
+
|
|
391
|
+
#### Grammar limits
|
|
392
|
+
|
|
393
|
+
- **Identifiers** must match `[A-Za-z_][A-Za-z0-9_]*`. Field and source names with spaces, hyphens or other characters cannot be written, and there is no quoted-identifier syntax yet. `from('my-orders')` throws.
|
|
394
|
+
- **Reserved words** `as asc desc true false null` cannot be names.
|
|
395
|
+
- **Aliases** (`.as(name)`) work in `select`, `map`, `rollup`, `pivot` and `reduce`. They throw in `sort`, `groupBy`, `distinct`, `flatten`, `transpose` and standalone aggregates.
|
|
396
|
+
- **`||` is not supported.** The lexer reads it as two pipes. Use `.or()`, which prints as `if(a, true, b)`.
|
|
397
|
+
- **Window functions** (`win.*`) are allowed only as top-level fields of `select`.
|
|
398
|
+
- **Group keys** go in `rollup(keys, aggs)` or `groupBy(...) | select(...)`. `groupBy(k) | rollup(agg)` returns `null` aggregates in the current engine.
|
|
399
|
+
- **Offsets** for `lag` and `lead` must be integers >= 1.
|
|
400
|
+
- **Numbers** such as `1e21` print as plain decimals. Negative literals print as unary minus (`-5`), which re-parses as `-(5)`.
|
|
401
|
+
|
|
330
402
|
### React Components
|
|
331
403
|
|
|
332
404
|
```tsx
|