@statedelta-libs/expressions 0.0.1
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 +382 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +511 -0
- package/dist/index.d.ts +511 -0
- package/dist/index.js +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# @statedelta-libs/expressions
|
|
2
|
+
|
|
3
|
+
> Compilador de JSON DSL para funções otimizadas.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@statedelta-libs/expressions)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Características
|
|
9
|
+
|
|
10
|
+
- **Compilação** - Compila uma vez, executa milhões de vezes
|
|
11
|
+
- **Alta performance** - ~3M ops/sec após compilação
|
|
12
|
+
- **Scope externo** - Funções vêm via scope, não hardcoded
|
|
13
|
+
- **$pipe** - Composição com valor inicial (sintaxe DSL)
|
|
14
|
+
- **Path syntax** - Suporte a wildcards (`items[*].price`)
|
|
15
|
+
- **Dependency extraction** - Para dirty tracking/reatividade
|
|
16
|
+
- **Integração** - Usa `@statedelta-libs/conditions` para condicionais
|
|
17
|
+
- **Type-safe** - TypeScript nativo com inferência
|
|
18
|
+
|
|
19
|
+
## Instalação
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install @statedelta-libs/expressions
|
|
23
|
+
# ou
|
|
24
|
+
pnpm add @statedelta-libs/expressions
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { compile, evaluate } from '@statedelta-libs/expressions';
|
|
31
|
+
import { filter, map, sum } from '@statedelta-libs/operators';
|
|
32
|
+
|
|
33
|
+
// Define o scope com funções disponíveis
|
|
34
|
+
const scope = { filter, map, sum };
|
|
35
|
+
|
|
36
|
+
// Compilar expressão com $pipe
|
|
37
|
+
const expr = compile({
|
|
38
|
+
$pipe: [
|
|
39
|
+
{ $: "items" },
|
|
40
|
+
{ $fn: "filter", args: [(item) => item.active] },
|
|
41
|
+
{ $fn: "map", args: [(item) => item.price] },
|
|
42
|
+
{ $fn: "sum" }
|
|
43
|
+
]
|
|
44
|
+
}, { scope });
|
|
45
|
+
|
|
46
|
+
// Executar (muito rápido!)
|
|
47
|
+
const data = {
|
|
48
|
+
items: [
|
|
49
|
+
{ name: "A", price: 10, active: true },
|
|
50
|
+
{ name: "B", price: 20, active: false },
|
|
51
|
+
{ name: "C", price: 30, active: true }
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
expr.fn(data); // 40
|
|
56
|
+
expr.deps; // ["items"]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## API
|
|
60
|
+
|
|
61
|
+
### compile()
|
|
62
|
+
|
|
63
|
+
Compila expressão JSON DSL para função otimizada usando closures.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
const { fn, deps, hash } = compile(expression, { scope });
|
|
67
|
+
|
|
68
|
+
fn(data); // Executa
|
|
69
|
+
deps; // Paths que a expressão depende
|
|
70
|
+
hash; // Hash único (para cache)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### compileAST()
|
|
74
|
+
|
|
75
|
+
Compila usando AST + `new Function()`. **Mais rápido na execução**, ideal para expressões executadas muitas vezes.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { compileAST } from '@statedelta-libs/expressions';
|
|
79
|
+
|
|
80
|
+
const { fn, deps, hash } = compileAST(expression, { scope });
|
|
81
|
+
|
|
82
|
+
fn(data); // Executa (mais rápido que compile())
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Quando usar cada um:**
|
|
86
|
+
|
|
87
|
+
| Cenário | Recomendação | Por quê |
|
|
88
|
+
|---------|--------------|---------|
|
|
89
|
+
| Execução única | `compile()` | Compilação mais rápida |
|
|
90
|
+
| Poucas execuções (<8x) | `compile()` | Overhead de AST não compensa |
|
|
91
|
+
| Muitas execuções (>8x) | `compileAST()` | Execução ~25-170% mais rápida |
|
|
92
|
+
| Hot path crítico | `compileAST()` | V8 JIT otimiza melhor |
|
|
93
|
+
| Expressões complexas | `compileAST()` | Ganho maior em nested calls |
|
|
94
|
+
|
|
95
|
+
### evaluate() / evaluateAST()
|
|
96
|
+
|
|
97
|
+
Compila e executa em um passo.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const result = evaluate(
|
|
101
|
+
{ $fn: "add", args: [{ $: "a" }, { $: "b" }] },
|
|
102
|
+
{ a: 1, b: 2 },
|
|
103
|
+
{ scope: { add: (a, b) => a + b } }
|
|
104
|
+
);
|
|
105
|
+
// 3
|
|
106
|
+
|
|
107
|
+
// Versão AST
|
|
108
|
+
const result = evaluateAST(expression, data, { scope });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### extractDeps()
|
|
112
|
+
|
|
113
|
+
Extrai dependências sem compilar.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const deps = extractDeps({
|
|
117
|
+
$if: "$isVip",
|
|
118
|
+
then: { $: "price.vip" },
|
|
119
|
+
else: { $: "price.regular" }
|
|
120
|
+
});
|
|
121
|
+
// ["$isVip", "price.vip", "price.regular"]
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Tipos de Expressão
|
|
125
|
+
|
|
126
|
+
### Literal
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
42
|
|
130
|
+
"hello"
|
|
131
|
+
true
|
|
132
|
+
[1, 2, 3]
|
|
133
|
+
{ a: 1 }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Reference ($)
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
{ $: "user.name" } // Acessa user.name
|
|
140
|
+
{ $: "items[0].price" } // Acessa índice
|
|
141
|
+
{ $: "items[*].price" } // Wildcard → [10, 20, 30]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Conditional ($if)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
{
|
|
148
|
+
$if: { path: "age", op: "gte", value: 18 },
|
|
149
|
+
then: "adult",
|
|
150
|
+
else: "minor"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Shorthand
|
|
154
|
+
{ $if: "$isVip", then: 0.2, else: 0 }
|
|
155
|
+
{ $if: "!$isBlocked", then: "allowed" }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Pipe ($pipe)
|
|
159
|
+
|
|
160
|
+
Composição com valor inicial - o valor passa por cada função em sequência.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
{
|
|
164
|
+
$pipe: [
|
|
165
|
+
{ $: "items" }, // Valor inicial
|
|
166
|
+
{ $fn: "filter", args: [predicateFn] }, // Retorna função curried
|
|
167
|
+
{ $fn: "map", args: [mapperFn] }, // Retorna função curried
|
|
168
|
+
{ $fn: "sum" } // Referência à função
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Function Call ($fn)
|
|
174
|
+
|
|
175
|
+
Chama função do scope com argumentos compilados.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
// Com args: chama a função
|
|
179
|
+
{ $fn: "add", args: [{ $: "a" }, { $: "b" }] }
|
|
180
|
+
// → scope.add(data.a, data.b)
|
|
181
|
+
|
|
182
|
+
// Sem args: retorna referência à função (útil em $pipe)
|
|
183
|
+
{ $fn: "sum" }
|
|
184
|
+
// → scope.sum
|
|
185
|
+
|
|
186
|
+
// Com args vazio: chama função sem argumentos
|
|
187
|
+
{ $fn: "getTimestamp", args: [] }
|
|
188
|
+
// → scope.getTimestamp()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Condition (delegado)
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Detectado automaticamente e delegado para @statedelta-libs/conditions
|
|
195
|
+
{ path: "user.age", op: "gte", value: 18 }
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
> **Nota:** Apenas objetos com operadores de condition válidos (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `notIn`, `contains`, `notContains`, `exists`, `notExists`, `matches`, `notMatches`, `startsWith`, `endsWith`) são detectados como conditions. Objetos com `op: "set"` ou outros operadores não são conditions e são tratados como literals.
|
|
199
|
+
|
|
200
|
+
## Scope
|
|
201
|
+
|
|
202
|
+
O scope define quais funções estão disponíveis para `$fn`:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import * as R from '@statedelta-libs/operators';
|
|
206
|
+
|
|
207
|
+
// Todas as funções do operators
|
|
208
|
+
const scope = R;
|
|
209
|
+
|
|
210
|
+
// Ou seleção específica
|
|
211
|
+
const scope = {
|
|
212
|
+
add: R.add,
|
|
213
|
+
filter: R.filter,
|
|
214
|
+
map: R.map,
|
|
215
|
+
sum: R.sum,
|
|
216
|
+
// Funções custom
|
|
217
|
+
double: (n) => n * 2,
|
|
218
|
+
isActive: (item) => item.active
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const { fn } = compile(expression, { scope });
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## Path Syntax
|
|
225
|
+
|
|
226
|
+
| Path | Resultado |
|
|
227
|
+
|------|-----------|
|
|
228
|
+
| `user` | `data.user` |
|
|
229
|
+
| `user.name` | `data.user.name` |
|
|
230
|
+
| `items[0]` | `data.items[0]` |
|
|
231
|
+
| `items[*].price` | `data.items.map(i => i.price)` |
|
|
232
|
+
|
|
233
|
+
## Cache
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
import { cache, cached } from '@statedelta-libs/expressions';
|
|
237
|
+
|
|
238
|
+
// Com scope
|
|
239
|
+
const compiled = cache.get(expression, { scope });
|
|
240
|
+
|
|
241
|
+
// Ou função helper
|
|
242
|
+
const compiled = cached(expression, { scope });
|
|
243
|
+
|
|
244
|
+
// Gerenciar
|
|
245
|
+
cache.size; // Tamanho atual
|
|
246
|
+
cache.clear(); // Limpar
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Performance
|
|
250
|
+
|
|
251
|
+
### compile() - Closures
|
|
252
|
+
|
|
253
|
+
| Operação | Velocidade |
|
|
254
|
+
|----------|------------|
|
|
255
|
+
| Compile (literal) | ~6.5M ops/s |
|
|
256
|
+
| Compile (ref) | ~2.5M ops/s |
|
|
257
|
+
| Compile (fn nested) | ~550K ops/s |
|
|
258
|
+
| Compile (complex) | ~300K ops/s |
|
|
259
|
+
| Execute (ref) | ~28M ops/s |
|
|
260
|
+
| Execute (fn nested) | ~9M ops/s |
|
|
261
|
+
|
|
262
|
+
### compileAST() - AST + new Function
|
|
263
|
+
|
|
264
|
+
| Operação | Velocidade |
|
|
265
|
+
|----------|------------|
|
|
266
|
+
| Compile (literal) | ~1.2M ops/s |
|
|
267
|
+
| Compile (ref) | ~800K ops/s |
|
|
268
|
+
| Compile (fn nested) | ~230K ops/s |
|
|
269
|
+
| Compile (complex) | ~130K ops/s |
|
|
270
|
+
| Execute (ref) | ~27M ops/s |
|
|
271
|
+
| Execute (fn nested) | ~25M ops/s ⚡ |
|
|
272
|
+
|
|
273
|
+
### Comparação Execução
|
|
274
|
+
|
|
275
|
+
| Cenário | Closures | AST | Ganho AST |
|
|
276
|
+
|---------|----------|-----|-----------|
|
|
277
|
+
| fn nested | 9M ops/s | 25M ops/s | **+169%** |
|
|
278
|
+
| ref 4 níveis | 16M ops/s | 27M ops/s | **+69%** |
|
|
279
|
+
| conditional nested | 20M ops/s | 25M ops/s | **+25%** |
|
|
280
|
+
|
|
281
|
+
**Break-even:** ~8 execuções - após isso, `compileAST()` compensa o tempo extra de compilação.
|
|
282
|
+
|
|
283
|
+
## Segurança
|
|
284
|
+
|
|
285
|
+
Ambas as abordagens são **seguras contra injeção de código**:
|
|
286
|
+
|
|
287
|
+
| Método | Proteção |
|
|
288
|
+
|--------|----------|
|
|
289
|
+
| `compile()` | Não gera código string - apenas compõe funções |
|
|
290
|
+
| `compileAST()` | Usa destructuring que valida identificadores automaticamente |
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// Tentativa de injeção - FALHA em ambos
|
|
294
|
+
{ $fn: "add; console.log('hacked'); //" }
|
|
295
|
+
|
|
296
|
+
// compile(): tenta acessar scope["add; console.log..."] → undefined
|
|
297
|
+
// compileAST(): destructuring inválido → SyntaxError
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Funções só são acessíveis se existirem no `scope` fornecido pelo desenvolvedor.
|
|
301
|
+
|
|
302
|
+
## Type Detection
|
|
303
|
+
|
|
304
|
+
O compilador detecta automaticamente o tipo de expressão baseado na estrutura do objeto:
|
|
305
|
+
|
|
306
|
+
| Tipo | Detecção | Exemplo |
|
|
307
|
+
|------|----------|---------|
|
|
308
|
+
| Reference | `{ $ }` presente | `{ $: "user.name" }` |
|
|
309
|
+
| Conditional | `{ $if, then }` presentes | `{ $if: cond, then: x, else: y }` |
|
|
310
|
+
| Function | `{ $fn }` presente | `{ $fn: "add", args: [...] }` |
|
|
311
|
+
| Pipe | `{ $pipe }` presente | `{ $pipe: [...] }` |
|
|
312
|
+
| Condition | `{ path, op }` com operador válido | `{ path: "age", op: "gte", value: 18 }` |
|
|
313
|
+
| Literal | Nenhum dos acima | `{ foo: "bar" }`, `42`, `"hello"` |
|
|
314
|
+
|
|
315
|
+
### Distinção entre Conditions e Effect Objects
|
|
316
|
+
|
|
317
|
+
Objetos com `path` e `op` só são tratados como conditions se `op` for um operador válido:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// ✓ Condition (op: "eq" é operador válido)
|
|
321
|
+
{ path: "user.age", op: "eq", value: 18 }
|
|
322
|
+
|
|
323
|
+
// ✓ Literal object (op: "set" NÃO é operador de condition)
|
|
324
|
+
{ resource: "state", op: "set", path: "currentPlayer", value: "X" }
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Isso permite que effect objects de handlers (que usam `{ resource, op: "set", path, value }`) sejam processados corretamente sem serem confundidos com conditions.
|
|
328
|
+
|
|
329
|
+
## TypeScript
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import type {
|
|
333
|
+
Expression,
|
|
334
|
+
CompiledExpression,
|
|
335
|
+
RefExpr,
|
|
336
|
+
ConditionalExpr,
|
|
337
|
+
FnExpr,
|
|
338
|
+
PipeExpr,
|
|
339
|
+
Scope,
|
|
340
|
+
CompileOptions,
|
|
341
|
+
} from '@statedelta-libs/expressions';
|
|
342
|
+
|
|
343
|
+
// Type guards
|
|
344
|
+
import {
|
|
345
|
+
isRef,
|
|
346
|
+
isConditional,
|
|
347
|
+
isFn,
|
|
348
|
+
isPipe,
|
|
349
|
+
isCondition,
|
|
350
|
+
isLiteral,
|
|
351
|
+
} from '@statedelta-libs/expressions';
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Migração de v0.1.x
|
|
355
|
+
|
|
356
|
+
### Breaking Changes
|
|
357
|
+
|
|
358
|
+
1. **Builtins removidos**: Funções não são mais hardcoded. Passe via `scope`:
|
|
359
|
+
```typescript
|
|
360
|
+
// Antes
|
|
361
|
+
compile({ $fn: "add", args: [1, 2] })
|
|
362
|
+
|
|
363
|
+
// Depois
|
|
364
|
+
compile({ $fn: "add", args: [1, 2] }, { scope: { add } })
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
2. **$fn sem args retorna função**: Para chamar sem argumentos, use `args: []`:
|
|
368
|
+
```typescript
|
|
369
|
+
// Retorna a função
|
|
370
|
+
{ $fn: "sum" }
|
|
371
|
+
|
|
372
|
+
// Chama a função
|
|
373
|
+
{ $fn: "getTime", args: [] }
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
3. **Removidos**: `registerFunction`, `hasFunction`, `getFunctionNames`
|
|
377
|
+
|
|
378
|
+
4. **Novo**: `$pipe` como sintaxe DSL para composição
|
|
379
|
+
|
|
380
|
+
## Licença
|
|
381
|
+
|
|
382
|
+
MIT © Anderson D. Rosa
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
'use strict';var conditions=require('@statedelta-libs/conditions'),omniAst=require('omni-ast');var m=n=>n!==null&&typeof n=="object"&&"$"in n&&typeof n.$=="string"&&Object.keys(n).length===1,h=n=>n!==null&&typeof n=="object"&&"$if"in n&&"then"in n,E=n=>n!==null&&typeof n=="object"&&"$fn"in n&&typeof n.$fn=="string",C=n=>n!==null&&typeof n=="object"&&"$pipe"in n&&Array.isArray(n.$pipe),M=new Set(["eq","neq","gt","gte","lt","lte","in","notIn","contains","notContains","exists","notExists","matches","notMatches","startsWith","endsWith"]),g=n=>n!==null&&typeof n=="object"&&"path"in n&&"op"in n&&M.has(n.op)&&!("$"in n)&&!("$if"in n)&&!("$fn"in n),y=n=>n!==null&&typeof n=="object"&&"logic"in n&&"conditions"in n,H=n=>g(n)||y(n),W=n=>{if(n===null)return true;let e=typeof n;if(e==="string"||e==="number"||e==="boolean"||Array.isArray(n))return true;if(e==="object"&&n!==null){let t=n,o="path"in t&&"op"in t&&M.has(t.op);return !("$"in t)&&!("$if"in t)&&!("$fn"in t)&&!("$pipe"in t)&&!o&&!("logic"in t)}return false};var R=new Map;function I(n){let e=[],t=n.length,o=0,i="";for(;o<t;){let s=n[o];if(s===".")i&&(e.push({type:"key",value:i}),i=""),o++;else if(s==="["){i&&(e.push({type:"key",value:i}),i=""),o++;let r=o;for(;o<t&&n[o]!=="]";)o++;let c=n.slice(r,o);if(o++,c==="*")e.push({type:"wildcard",value:"*"});else {let f=parseInt(c,10);e.push({type:"index",value:isNaN(f)?c:f});}}else i+=s,o++;}return i&&e.push({type:"key",value:i}),e}function V(n){return n.includes("[*]")}function x(n){let e=R.get(n);return e||(e=V(n)?U(n):Q(n),R.set(n,e),e)}function Q(n){if(!n.includes(".")&&!n.includes("["))return i=>i?.[n];let e=I(n),t=e.length;if(t===2){let[i,s]=e,r=i.value,c=s.value;return f=>f?.[r]?.[c]}if(t===3){let[i,s,r]=e,c=i.value,f=s.value,a=r.value;return u=>u?.[c]?.[f]?.[a]}let o=e.map(i=>i.value);return i=>{let s=i;for(let r=0;r<t&&s!=null;r++)s=s[o[r]];return s}}function U(n){let e=I(n),t=[];for(let o=0;o<e.length;o++)e[o].type==="wildcard"&&t.push(o);return t.length===1?X(e,t[0]):Z(e,t)}function X(n,e){let t=n.slice(0,e).map(r=>r.value),o=n.slice(e+1).map(r=>r.value),i=t.length,s=o.length;if(s===0){if(i===1){let r=t[0];return c=>c?.[r]}return r=>{let c=r;for(let f=0;f<i&&c!=null;f++)c=c[t[f]];return c}}if(s===1){let r=o[0];if(i===1){let c=t[0];return f=>{let a=f?.[c];if(Array.isArray(a))return a.map(u=>u?.[r])}}return c=>{let f=c;for(let a=0;a<i&&f!=null;a++)f=f[t[a]];if(Array.isArray(f))return f.map(a=>a?.[r])}}return r=>{let c=r;for(let f=0;f<i&&c!=null;f++)c=c[t[f]];if(Array.isArray(c))return c.map(f=>{let a=f;for(let u=0;u<s&&a!=null;u++)a=a[o[u]];return a})}}function Z(n,e){let t=[],o=0;for(let s=0;s<e.length;s++){let r=e[s],c=s===e.length-1,f=n.slice(o,r).map(a=>a.value);f.length>0&&t.push({type:"access",keys:f}),t.push({type:c?"map":"flatMap",keys:[]}),o=r+1;}let i=n.slice(o).map(s=>s.value);return s=>{let r=s;for(let c of t){if(r==null)return;if(c.type==="access")for(let f of c.keys){if(r==null)return;r=r[f];}else if(c.type==="flatMap"){if(!Array.isArray(r))return;r=r.flatMap(f=>{let a=f;return Array.isArray(a)?a:[a]});}else if(c.type==="map"){if(!Array.isArray(r))return;i.length>0&&(r=r.map(f=>{let a=f;for(let u of i){if(a==null)return;a=a[u];}return a}));}}return r}}function nn(n,e){return x(e)(n)}function k(n){let e=n.indexOf("[*]");return e===-1?n:n.slice(0,e)}function en(){R.clear();}function tn(){return R.size}function T(n){let e=new Set;return A(n,e),Array.from(e)}function A(n,e){if(n===null||typeof n!="object")return;if(Array.isArray(n)){for(let i=0;i<n.length;i++)A(n[i],e);return}if(m(n)){e.add(k(n.$));return}if(h(n)){if(typeof n.$if=="string"){let i=n.$if.startsWith("!")?n.$if.slice(1):n.$if;e.add(k(i));}else A(n.$if,e);A(n.then,e),n.else!==void 0&&A(n.else,e);return}if(C(n)){for(let i=0;i<n.$pipe.length;i++)A(n.$pipe[i],e);return}if(E(n)){if(n.args)for(let i=0;i<n.args.length;i++)A(n.args[i],e);return}if(g(n)){e.add(k(n.path)),n.value!==void 0&&conditions.isRef(n.value)&&e.add(k(n.value.$));return}if(y(n)){for(let i=0;i<n.conditions.length;i++)A(n.conditions[i],e);return}let t=n,o=Object.keys(t);for(let i=0;i<o.length;i++)A(t[o[i]],e);}function rn(n){return T(n).length>0}function sn(n){return T(n).length===0}function ln(n){return JSON.stringify(n)}function O(n,e={}){let t=e.scope??{},o=b(n,t),i=T(n),s=ln(n);return {fn:o,deps:i,hash:s}}function b(n,e){if(n===null)return ()=>null;if(typeof n!="object")return ()=>n;if(Array.isArray(n)){let t=n.map(o=>b(o,e));return o=>t.map(i=>i(o))}if(m(n))return cn(n);if(h(n))return fn(n,e);if(C(n))return an(n,e);if(E(n))return un(n,e);if(g(n))return conditions.compile(n);if(y(n))return conditions.compile(n);if(W(n)){let t=n,o=Object.keys(t),i=o.map(s=>b(t[s],e));return s=>{let r={};for(let c=0;c<o.length;c++)r[o[c]]=i[c](s);return r}}return ()=>n}function cn(n){return x(n.$)}function fn(n,e){let t;if(typeof n.$if=="string"){let s=n.$if.startsWith("!")?n.$if.slice(1):n.$if,r=x(s);t=n.$if.startsWith("!")?f=>!r(f):f=>!!r(f);}else {let s=b(n.$if,e);t=r=>!!s(r);}let o=b(n.then,e),i=n.else!==void 0?b(n.else,e):()=>{};return s=>t(s)?o(s):i(s)}function an(n,e){let t=n.$pipe;if(t.length===0)return ()=>{};if(t.length===1)return b(t[0],e);let o=b(t[0],e),i=t.slice(1).map(r=>b(r,e)),s=i.length;if(s===1){let[r]=i;return c=>{let f=o(c),a=r(c);return typeof a=="function"?a(f):a}}if(s===2){let[r,c]=i;return f=>{let a=o(f),u=r(f);return a=typeof u=="function"?u(a):u,u=c(f),typeof u=="function"?u(a):u}}if(s===3){let[r,c,f]=i;return a=>{let u=o(a),p=r(a);return u=typeof p=="function"?p(u):p,p=c(a),u=typeof p=="function"?p(u):p,p=f(a),typeof p=="function"?p(u):p}}return r=>{let c=o(r);for(let f=0;f<s;f++){let a=i[f](r);c=typeof a=="function"?a(c):a;}return c}}function un(n,e){let t=n.$fn,o=n.args;if(o===void 0)return ()=>{let r=e[t];if(!r)throw new Error(`Function not found in scope: ${t}`);return r};let i=o.map(r=>b(r,e)),s=i.length;if(s===0)return r=>{let c=e[t];if(!c)throw new Error(`Function not found in scope: ${t}`);return c()};if(s===1){let[r]=i;return c=>{let f=e[t];if(!f)throw new Error(`Function not found in scope: ${t}`);return f(r(c))}}if(s===2){let[r,c]=i;return f=>{let a=e[t];if(!a)throw new Error(`Function not found in scope: ${t}`);return a(r(f),c(f))}}if(s===3){let[r,c,f]=i;return a=>{let u=e[t];if(!u)throw new Error(`Function not found in scope: ${t}`);return u(r(a),c(a),f(a))}}return r=>{let c=e[t];if(!c)throw new Error(`Function not found in scope: ${t}`);return c(...i.map(f=>f(r)))}}function pn(n,e,t={}){return O(n,t).fn(e)}var v=class{constructor(e=1e3){this.cache=new Map,this._maxSize=e;}get(e,t={}){let o=JSON.stringify(e),i=this.cache.get(o);if(i)return this.cache.delete(o),this.cache.set(o,i),i;let s=O(e,t);if(this.cache.size>=this._maxSize){let r=this.cache.keys().next().value;r&&this.cache.delete(r);}return this.cache.set(o,s),s}has(e){return this.cache.has(JSON.stringify(e))}delete(e){return this.cache.delete(JSON.stringify(e))}clear(){this.cache.clear();}get size(){return this.cache.size}get maxSize(){return this._maxSize}set maxSize(e){for(this._maxSize=e;this.cache.size>this._maxSize;){let t=this.cache.keys().next().value;t&&this.cache.delete(t);}}},_=new v;function dn(n,e={}){return _.get(n,e)}function z(n,e="root",t={}){let o=[];return $(n,e,o,t),{valid:o.length===0,errors:o}}function $(n,e,t,o){if(n===null||typeof n!="object")return;if(Array.isArray(n)){for(let r=0;r<n.length;r++)$(n[r],`${e}[${r}]`,t,o);return}if(m(n)){(!n.$||typeof n.$!="string")&&t.push(`${e}: invalid reference, $ must be non-empty string`);return}if(h(n)){typeof n.$if=="string"?(n.$if.startsWith("!")?n.$if.slice(1):n.$if)||t.push(`${e}.$if: empty path in string shorthand`):$(n.$if,`${e}.$if`,t,o),$(n.then,`${e}.then`,t,o),n.else!==void 0&&$(n.else,`${e}.else`,t,o);return}if(C(n)){if(!Array.isArray(n.$pipe)){t.push(`${e}.$pipe: must be an array`);return}if(n.$pipe.length===0){t.push(`${e}.$pipe: must have at least one element`);return}for(let r=0;r<n.$pipe.length;r++)$(n.$pipe[r],`${e}.$pipe[${r}]`,t,o);return}if(E(n)){if(!n.$fn||typeof n.$fn!="string"){t.push(`${e}: invalid function, $fn must be non-empty string`);return}if(o.scope&&!(n.$fn in o.scope)&&t.push(`${e}: function "${n.$fn}" not found in scope`),n.args!==void 0)if(!Array.isArray(n.args))t.push(`${e}.args: must be an array`);else for(let r=0;r<n.args.length;r++)$(n.args[r],`${e}.args[${r}]`,t,o);return}if(g(n)){let r=conditions.validate(n,e);r.valid||t.push(...r.errors);return}if(y(n)){let r=conditions.validate(n,e);r.valid||t.push(...r.errors);return}let i=n,s=Object.keys(i);for(let r=0;r<s.length;r++){let c=s[r];$(i[c],`${e}.${c}`,t,o);}}function mn(n,e={}){let t=z(n,"root",e);if(!t.valid)throw new Error(`Invalid expression: ${t.errors.join("; ")}`)}function gn(n,e={}){return z(n,"root",e).valid}var P="data",J="scope",yn={eq:"===",neq:"!==",gt:">",gte:">=",lt:"<",lte:"<="};function N(n,e={}){let{dataParam:t=P,scopeParam:o=J,noPrefixes:i=false}=e;return d(n,t,o,i)}function d(n,e,t,o){if(n===null)return omniAst.builders.literal(null);if(typeof n=="string")return omniAst.builders.literal(n);if(typeof n=="number")return omniAst.builders.literal(n);if(typeof n=="boolean")return omniAst.builders.literal(n);if(Array.isArray(n))return omniAst.builders.arrayExpression(n.map(i=>d(i,e,t,o)));if(m(n))return hn(n.$,e,o);if(h(n))return Cn(n,e,t,o);if(C(n))return Sn(n.$pipe,e,t,o);if(E(n))return bn(n,e,t,o);if(g(n))return An(n,e,t,o);if(y(n))return $n(n,e,t,o);if(typeof n=="object"){let s=Object.entries(n).map(([r,c])=>omniAst.builders.property(omniAst.builders.identifier(r),d(c,e,t,o)));return omniAst.builders.objectExpression(s)}return omniAst.builders.literal(null)}function hn(n,e,t){return n.includes("[*]")?En(n,e,t):w(n,e,t)}function w(n,e,t){let o=F(n);if(o.length===0)return t?omniAst.builders.identifier("undefined"):omniAst.builders.identifier(e);let i;if(t){let s=o[0];i=omniAst.builders.identifier(s.value);for(let r=1;r<o.length;r++){let c=o[r];c.type==="key"?i=omniAst.builders.memberExpression(i,omniAst.builders.identifier(c.value),false,true):i=omniAst.builders.memberExpression(i,omniAst.builders.literal(c.value),true,true);}}else {i=omniAst.builders.identifier(e);for(let s of o)s.type==="key"?i=omniAst.builders.memberExpression(i,omniAst.builders.identifier(s.value),false,true):i=omniAst.builders.memberExpression(i,omniAst.builders.literal(s.value),true,true);}return i}function En(n,e,t){let o=n.indexOf("[*]"),i=n.slice(0,o),s=n.slice(o+3),r;if(i?r=w(i,e,t):r=t?omniAst.builders.identifier("undefined"):omniAst.builders.identifier(e),!s||s==="")return r;if(s.includes("[*]"))return B(r,s);let c="_i",f=s.startsWith(".")?s.slice(1):s,a=omniAst.builders.identifier(c);if(f){let u=F(f);for(let p of u)p.type==="key"?a=omniAst.builders.memberExpression(a,omniAst.builders.identifier(p.value),false,true):a=omniAst.builders.memberExpression(a,omniAst.builders.literal(p.value),true,true);}return omniAst.builders.callExpression(omniAst.builders.memberExpression(r,omniAst.builders.identifier("map"),false,true),[omniAst.builders.arrowFunctionExpression([omniAst.builders.identifier(c)],a)])}function B(n,e){let t=e.indexOf("[*]"),o=e.slice(0,t),i=e.slice(t+3),s="_i",r=o.startsWith(".")?o.slice(1):o,c=omniAst.builders.identifier(s);if(r){let a=F(r);for(let u of a)u.type==="key"&&(c=omniAst.builders.memberExpression(c,omniAst.builders.identifier(u.value),false,true));}if(i.includes("[*]")){let a=B(c,i);return omniAst.builders.callExpression(omniAst.builders.memberExpression(n,omniAst.builders.identifier("flatMap"),false,true),[omniAst.builders.arrowFunctionExpression([omniAst.builders.identifier(s)],a)])}let f=i.startsWith(".")?i.slice(1):i;if(f){let a=F(f);for(let u of a)u.type==="key"&&(c=omniAst.builders.memberExpression(c,omniAst.builders.identifier(u.value),false,true));}return omniAst.builders.callExpression(omniAst.builders.memberExpression(n,omniAst.builders.identifier("flatMap"),false,true),[omniAst.builders.arrowFunctionExpression([omniAst.builders.identifier(s)],c)])}function Cn(n,e,t,o){let i;if(typeof n.$if=="string"){let c=n.$if.startsWith("!"),f=c?n.$if.slice(1):n.$if,a=w(f,e,o);i=c?omniAst.builders.unaryExpression("!",a):a;}else i=d(n.$if,e,t,o);let s=d(n.then,e,t,o),r=n.else!==void 0?d(n.else,e,t,o):omniAst.builders.identifier("undefined");return omniAst.builders.conditionalExpression(i,s,r)}function bn(n,e,t,o){let i=o?omniAst.builders.identifier(n.$fn):omniAst.builders.memberExpression(omniAst.builders.identifier(t),omniAst.builders.identifier(n.$fn),false,false);if(n.args===void 0)return i;let s=n.args.map(r=>d(r,e,t,o));return omniAst.builders.callExpression(i,s)}function Sn(n,e,t,o){if(n.length===0)return omniAst.builders.identifier("undefined");if(n.length===1)return d(n[0],e,t,o);let i=d(n[0],e,t,o);for(let s=1;s<n.length;s++){let r=d(n[s],e,t,o);i=omniAst.builders.callExpression(r,[i]);}return i}function An(n,e,t,o){let i=w(n.path,e,o),s=n.value!==void 0?m(n.value)?w(n.value.$,e,o):d(n.value,e,t,o):omniAst.builders.literal(null),r=yn[n.op];if(r)return omniAst.builders.binaryExpression(r,i,s);switch(n.op){case "in":return omniAst.builders.callExpression(omniAst.builders.memberExpression(s,omniAst.builders.identifier("includes")),[i]);case "notIn":return omniAst.builders.unaryExpression("!",omniAst.builders.callExpression(omniAst.builders.memberExpression(s,omniAst.builders.identifier("includes")),[i]));case "contains":return omniAst.builders.callExpression(omniAst.builders.memberExpression(i,omniAst.builders.identifier("includes"),false,true),[s]);case "notContains":return omniAst.builders.unaryExpression("!",omniAst.builders.callExpression(omniAst.builders.memberExpression(i,omniAst.builders.identifier("includes"),false,true),[s]));case "exists":return omniAst.builders.binaryExpression("!=",i,omniAst.builders.literal(null));case "notExists":return omniAst.builders.binaryExpression("==",i,omniAst.builders.literal(null));case "matches":return omniAst.builders.callExpression(omniAst.builders.memberExpression(omniAst.builders.newExpression(omniAst.builders.identifier("RegExp"),[s]),omniAst.builders.identifier("test")),[i]);case "notMatches":return omniAst.builders.unaryExpression("!",omniAst.builders.callExpression(omniAst.builders.memberExpression(omniAst.builders.newExpression(omniAst.builders.identifier("RegExp"),[s]),omniAst.builders.identifier("test")),[i]));case "startsWith":return omniAst.builders.callExpression(omniAst.builders.memberExpression(i,omniAst.builders.identifier("startsWith"),false,true),[s]);case "endsWith":return omniAst.builders.callExpression(omniAst.builders.memberExpression(i,omniAst.builders.identifier("endsWith"),false,true),[s]);default:return omniAst.builders.binaryExpression("===",i,s)}}function $n(n,e,t,o){let{logic:i,conditions:s}=n,r=i==="AND"?"&&":"||";if(s.length===0)return omniAst.builders.literal(i==="AND");if(s.length===1)return d(s[0],e,t,o);let c=d(s[0],e,t,o);for(let f=1;f<s.length;f++){let a=d(s[f],e,t,o);c=omniAst.builders.logicalExpression(r,c,a);}return c}function F(n){let e=[],t=n.length,o=0,i="";for(;o<t;){let s=n[o];if(s===".")i&&(e.push({type:"key",value:i}),i=""),o++;else if(s==="["){i&&(e.push({type:"key",value:i}),i=""),o++;let r=o;for(;o<t&&n[o]!=="]";)o++;let c=n.slice(r,o);if(o++,c!=="*"){let f=parseInt(c,10);e.push({type:"index",value:isNaN(f)?c:f});}}else i+=s,o++;}return i&&e.push({type:"key",value:i}),e}function K(n,e=[P]){return omniAst.builders.arrowFunctionExpression(e.map(t=>omniAst.builders.identifier(t)),n)}function j(n){let e=new Set;return S(n,e),e}function S(n,e){if(n===null||typeof n!="object")return;if(Array.isArray(n)){for(let o of n)S(o,e);return}if(m(n))return;if(h(n)){S(n.$if,e),S(n.then,e),n.else!==void 0&&S(n.else,e);return}if(C(n)){for(let o of n.$pipe)S(o,e);return}if(E(n)){if(e.add(n.$fn),n.args)for(let o of n.args)S(o,e);return}if(g(n)){n.value!==void 0&&typeof n.value=="object"&&S(n.value,e);return}if(y(n)){for(let o of n.conditions)S(o,e);return}let t=n;for(let o of Object.keys(t))S(t[o],e);}function G(n){let e=new Set;for(let t of n){let o=t.indexOf("."),i=t.indexOf("["),s=t.length;o!==-1&&(s=Math.min(s,o)),i!==-1&&(s=Math.min(s,i));let r=t.slice(0,s);r&&e.add(r);}return e}function kn(n){return JSON.stringify(n)}function xn(n,e,t){let o=N(n,{noPrefixes:true}),i=omniAst.generate(o),s=e.size>0?`const{${[...e].join(",")}}=data??{};`:"",r=t.size>0?`const{${[...t].join(",")}}=scope;`:"";return r?`(function(scope){${r}return function(data){${s}return ${i}}})`:`(function(){return function(data){${s}return ${i}}})`}function D(n,e={}){let{scope:t={},returnCode:o=false}=e,i=T(n),s=G(i),r=j(n),c=kn(n),f=xn(n,s,r);if(o)return {code:f,deps:i,hash:c,dataRoots:[...s],scopeFns:[...r]};let a;try{a=new Function(`return ${f}`)()(t);}catch(u){throw new Error(`AST compilation failed. If this is due to CSP, use the standard compile() function instead. Error: ${u instanceof Error?u.message:String(u)}`)}return {fn:a,deps:i,hash:c}}function Y(n,e,t={}){let{fn:o}=D(n,t);return o(e)}var ie="0.0.1";exports.ExpressionCache=v;exports.VERSION=ie;exports.assertValid=mn;exports.cache=_;exports.cached=dn;exports.clearPathCache=en;exports.compile=O;exports.compileAST=D;exports.compilePath=x;exports.dslToAST=N;exports.evaluate=pn;exports.evaluateAST=Y;exports.extractDataRoots=G;exports.extractDeps=T;exports.extractScopeFns=j;exports.get=nn;exports.getPathCacheSize=tn;exports.hasDeps=rn;exports.hasWildcard=V;exports.isCondition=g;exports.isConditionExpr=H;exports.isConditionGroup=y;exports.isConditional=h;exports.isFn=E;exports.isLiteral=W;exports.isPipe=C;exports.isPure=sn;exports.isRef=m;exports.isValid=gn;exports.normalizePath=k;exports.validate=z;exports.wrapInFunction=K;
|