@statedelta-libs/expressions 2.2.0 → 3.0.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/dist/index.d.ts CHANGED
@@ -1,5 +1,3 @@
1
- import { types } from 'omni-ast';
2
-
3
1
  /**
4
2
  * @statedelta-libs/expressions - Types
5
3
  *
@@ -45,15 +43,16 @@ interface PipeExpr {
45
43
  $pipe: Expression[];
46
44
  }
47
45
  /**
48
- * Callback expression - HOC de continuidade
49
- * Executa body dentro de um context function (tryCatch, transaction, etc.)
50
- * @example { $cb: "tryCatch", body: { $fn: "query" } }
51
- * @example { $cb: "transaction", params: { isolation: "serializable" }, body: { $fn: "query" } }
46
+ * Arrow expression closure deferida
47
+ * Cria uma função sem executar. Pode receber args posicionais nomeados.
48
+ *
49
+ * @example { "$arrow": { "$fn": "query" } } // () => query()
50
+ * @example { "$arrow": { "$": "item.price" }, "args": ["item"] } // (item) => item.price
52
51
  */
53
- interface CbExpr {
54
- $cb: string;
55
- body: Expression;
56
- params?: Expression;
52
+ interface ArrowExpr {
53
+ $arrow: Expression;
54
+ args?: string[];
55
+ schema?: Record<string, unknown>;
57
56
  }
58
57
  /**
59
58
  * Literal values (primitives, arrays, plain objects)
@@ -86,7 +85,7 @@ type ConditionExpr = Condition | ConditionGroup;
86
85
  /**
87
86
  * Union of all expression types
88
87
  */
89
- type Expression = Literal | RefExpr | ConditionalExpr | FnExpr | PipeExpr | CbExpr | ConditionExpr;
88
+ type Expression = Literal | RefExpr | ConditionalExpr | FnExpr | PipeExpr | ArrowExpr | ConditionExpr;
90
89
  /**
91
90
  * Compiled expression function
92
91
  */
@@ -133,50 +132,6 @@ interface ValidationResult {
133
132
  * @example { add, subtract, multiply, filter, map, sum }
134
133
  */
135
134
  type Scope = Record<string, (...args: any[]) => any>;
136
- /**
137
- * Path getter function for context callbacks
138
- */
139
- type PathGetterFn = (path: string) => unknown;
140
- /**
141
- * Context function - HOC que controla execução do body.
142
- * Recebe o body como callback e decide quando/se executá-lo.
143
- *
144
- * @param cb - Body compilado como callback (recebe data e get)
145
- * @param data - Dados do runtime
146
- * @param get - Resolver de paths (accessor)
147
- * @param params - Parâmetros extras para o context (opcional)
148
- *
149
- * @example
150
- * ```ts
151
- * const tryCatch: ContextFn = (cb, data, get, params) => {
152
- * try {
153
- * return cb(data, get);
154
- * } catch (e) {
155
- * return params?.fallback ?? null;
156
- * }
157
- * };
158
- *
159
- * const transaction: ContextFn = (cb, data, get, params) => {
160
- * const tx = db.beginTransaction(params);
161
- * try {
162
- * const result = cb({ ...data, tx }, get);
163
- * tx.commit();
164
- * return result;
165
- * } catch (e) {
166
- * tx.rollback();
167
- * throw e;
168
- * }
169
- * };
170
- * ```
171
- */
172
- type ContextFn<T = any, R = any> = (cb: (data: T, get: PathGetterFn) => R, data: T, get: PathGetterFn, params?: unknown) => R;
173
- /**
174
- * Context registry - HOC functions available to $cb expressions.
175
- * Separate from scope because context functions have different signature.
176
- *
177
- * @example { tryCatch, transaction, withRetry }
178
- */
179
- type Context = Record<string, ContextFn>;
180
135
  /**
181
136
  * Options for compilation
182
137
  */
@@ -192,263 +147,125 @@ interface CompileOptions<T = unknown> {
192
147
  * compile(expr, { accessor: (path, ctx) => ctx.get(path) })
193
148
  */
194
149
  accessor?: AccessorFn<T>;
195
- /**
196
- * Context functions available to $cb expressions.
197
- * HOC functions that control body execution (tryCatch, transaction, etc.).
198
- *
199
- * @example
200
- * compile(expr, {
201
- * context: {
202
- * tryCatch: (cb, data, get) => { try { return cb(data, get); } catch { return null; } }
203
- * }
204
- * })
205
- */
206
- context?: Context;
150
+ /** Boundary definitions for compile-time interception */
151
+ boundaries?: BoundaryDef<T>[];
207
152
  }
208
153
  /**
209
- * Check if value is a reference expression
210
- */
211
- declare const isRef: (v: unknown) => v is RefExpr;
212
- /**
213
- * Check if value is a conditional expression
214
- */
215
- declare const isConditional: (v: unknown) => v is ConditionalExpr;
216
- /**
217
- * Check if value is a function call expression
218
- */
219
- declare const isFn: (v: unknown) => v is FnExpr;
220
- /**
221
- * Check if value is a pipe expression
222
- */
223
- declare const isPipe: (v: unknown) => v is PipeExpr;
224
- /**
225
- * Check if value is a callback expression
226
- */
227
- declare const isCb: (v: unknown) => v is CbExpr;
228
- /**
229
- * Check if value is a condition expression
230
- *
231
- * Verifies `op` is a valid condition operator to distinguish from
232
- * effect objects that also have `left` and `op` (e.g. op: "set").
233
- */
234
- declare const isCondition: (v: unknown) => v is Condition;
235
- /**
236
- * Check if value is a condition group
237
- */
238
- declare const isConditionGroup: (v: unknown) => v is ConditionGroup;
239
- /**
240
- * Check if value is any condition expression
241
- */
242
- declare const isConditionExpr: (v: unknown) => v is ConditionExpr;
243
- /**
244
- * Check if value is a literal (not an expression object)
245
- */
246
- declare const isLiteral: (v: unknown) => v is Literal;
247
-
248
- /**
249
- * @statedelta-libs/expressions - Compiler
154
+ * Boundary definition for compile-time interception.
155
+ * Allows foreign DSLs and compilers to plug into the expression walk.
156
+ * The handler is a self-sufficient closure — it captures whatever it needs
157
+ * from the consumer's scope (ExpressionCompiler, databases, other compilers).
250
158
  *
251
- * Compiles JSON DSL expressions to optimized functions.
252
- * Functions are provided via scope, not hardcoded.
159
+ * @example
160
+ * ```ts
161
+ * const rawBoundary: BoundaryDef = {
162
+ * check: (node) => "$raw" in node,
163
+ * handle: (node) => () => node.$raw,
164
+ * };
165
+ * ```
253
166
  */
167
+ interface BoundaryDef<T = unknown> {
168
+ /** Check if node belongs to this boundary */
169
+ check: (node: Record<string, unknown>) => boolean;
170
+ /** Compile the node. Returns a ready-to-call function. */
171
+ handle: (node: Record<string, unknown>) => CompiledFn<T>;
172
+ }
254
173
 
255
174
  /**
256
- * Compile expression to optimized function
175
+ * @statedelta-libs/expressions - Normalize
257
176
  *
258
- * @param expr - Expression to compile
259
- * @param options - Compile options (scope with functions, accessor)
260
- * @returns Compiled expression with fn, deps, and hash
177
+ * External normalization helper for custom expression types.
178
+ * Converts custom DSL nodes ($query, $mapper, etc.) to standard Expression
179
+ * BEFORE compilation. Keeps the core compiler pure.
261
180
  *
262
181
  * @example
263
182
  * ```ts
264
- * // Uso padrão - acesso direto por propriedade
265
- * import { add, filter, sum } from '@statedelta-libs/operators';
266
- *
267
- * const { fn } = compile(
268
- * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
269
- * { scope: { add, filter, sum } }
270
- * );
271
- *
272
- * fn({ a: 1, b: 2 }); // 3
273
- * ```
183
+ * const transforms = {
184
+ * $query: (node) => ({ $fn: "__query", args: [node.$query, node.params ?? {}] })
185
+ * };
274
186
  *
275
- * @example
276
- * ```ts
277
- * // Com accessor customizado (ex: TickContext)
278
- * const { fn } = compile(
279
- * { $: "hp:value" },
280
- * { accessor: (path, ctx) => ctx.get(path) }
187
+ * const pure = normalize(
188
+ * { $query: "isAttacked", params: { row: { $: "kingRow" } } },
189
+ * transforms
281
190
  * );
191
+ * // → { $fn: "__query", args: ["isAttacked", { row: { $: "kingRow" } }] }
282
192
  *
283
- * fn(tickContext); // usa ctx.get('hp:value')
193
+ * compile(pure, { scope });
284
194
  * ```
285
195
  */
286
- declare function compile<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions<T>): CompiledExpression<T, R>;
196
+
287
197
  /**
288
- * Compile and execute in one step
198
+ * Transform function for custom expression types.
199
+ * Receives the raw node and returns a standard Expression.
289
200
  *
290
201
  * @example
291
202
  * ```ts
292
- * // Uso padrão
293
- * evaluate(
294
- * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
295
- * { a: 1, b: 2 },
296
- * { scope: { add } }
297
- * ); // 3
298
- *
299
- * // Com accessor customizado
300
- * evaluate(
301
- * { $: "hp:value" },
302
- * tickContext,
303
- * { accessor: (path, ctx) => ctx.get(path) }
304
- * );
203
+ * const transforms: Transforms = {
204
+ * $query: (node) => ({
205
+ * $fn: "__query",
206
+ * args: [node.$query, node.params ?? {}]
207
+ * }),
208
+ * };
305
209
  * ```
306
210
  */
307
- declare function evaluate<T = unknown, R = unknown>(expr: Expression, data: T, options?: CompileOptions<T>): R;
308
-
309
- /**
310
- * @statedelta-libs/expressions - Path Compiler
311
- *
312
- * Compiles paths to optimized getters with wildcard support.
313
- * Performance: ~50M ops/sec for simple paths, ~10M ops/sec for wildcards
314
- */
315
-
316
- /**
317
- * Check if path has wildcards
318
- */
319
- declare function hasWildcard(path: string): boolean;
320
- /**
321
- * Compile path to optimized getter
322
- */
323
- declare function compilePath<T = unknown>(path: string): PathGetter<T>;
324
- /**
325
- * Get value at path directly (without caching)
326
- */
327
- declare function get<T = unknown>(data: T, path: string): unknown;
328
- /**
329
- * Normalize path for dependency tracking (remove wildcards)
330
- * @example "items[*].price" → "items"
331
- * @example "users[*].posts[*].title" → "users"
332
- */
333
- declare function normalizePath(path: string): string;
334
- /**
335
- * Clear path cache
336
- */
337
- declare function clearPathCache(): void;
338
- /**
339
- * Get cache size
340
- */
341
- declare function getPathCacheSize(): number;
342
-
343
- /**
344
- * @statedelta-libs/expressions - Dependency Extraction
345
- *
346
- * Extracts paths that an expression depends on.
347
- * Performance: ~200K ops/sec
348
- */
349
-
350
- /**
351
- * Extract all paths an expression depends on
352
- * @example { "$": "user.age" } → ["user.age"]
353
- * @example { "$if": "$isVip", "then": { "$": "price.vip" } } → ["$isVip", "price.vip"]
354
- */
355
- declare function extractDeps(expr: Expression): string[];
356
- /**
357
- * Check if expression has dependencies
358
- */
359
- declare function hasDeps(expr: Expression): boolean;
360
- /**
361
- * Check if expression is pure (no dependencies)
362
- */
363
- declare function isPure(expr: Expression): boolean;
364
-
365
- /**
366
- * @statedelta-libs/expressions - Cache
367
- *
368
- * LRU cache for compiled expressions.
369
- * Performance: cache hit ~10M ops/sec
370
- */
371
-
372
- /**
373
- * LRU Cache for compiled expressions
374
- */
375
- declare class ExpressionCache {
376
- private cache;
377
- private _maxSize;
378
- constructor(maxSize?: number);
379
- /**
380
- * Get or compile expression
381
- *
382
- * Note: The cache key is based on the expression only, not the scope.
383
- * If you use different scopes for the same expression, consider using
384
- * separate cache instances or not caching at all.
385
- */
386
- get<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions): CompiledExpression<T, R>;
387
- /**
388
- * Check if expression is cached
389
- */
390
- has(expr: Expression): boolean;
391
- /**
392
- * Delete specific expression from cache
393
- */
394
- delete(expr: Expression): boolean;
395
- /**
396
- * Clear the cache
397
- */
398
- clear(): void;
399
- /**
400
- * Current cache size
401
- */
402
- get size(): number;
403
- /**
404
- * Maximum cache size
405
- */
406
- get maxSize(): number;
407
- /**
408
- * Set maximum cache size
409
- */
410
- set maxSize(value: number);
411
- }
412
- declare const cache: ExpressionCache;
211
+ type TransformFn = (node: Record<string, unknown>) => Expression;
413
212
  /**
414
- * Compile and cache expression (convenience function)
415
- *
416
- * Note: Uses global cache. For custom scope management,
417
- * use a dedicated ExpressionCache instance.
213
+ * Map of transform functions keyed by marker (e.g. "$query", "$mapper")
418
214
  */
419
- declare function cached<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions): CompiledExpression<T, R>;
215
+ type Transforms = Record<string, TransformFn>;
420
216
 
421
217
  /**
422
- * @statedelta-libs/expressions - Validation
218
+ * @statedelta-libs/expressions - ExpressionCompiler
423
219
  *
424
- * Validates expression structure before compilation.
220
+ * Unified public API for expression compilation.
221
+ * Centralizes scope, accessor, and cache in a single reusable instance.
425
222
  */
426
223
 
427
- /**
428
- * Validation options
429
- */
430
- interface ValidateOptions {
431
- /** Scope to validate function names against (optional) */
224
+ interface ExpressionCompilerOptions<T = unknown> {
225
+ /** Functions available to $fn expressions */
432
226
  scope?: Scope;
433
- /** Context to validate $cb function names against (optional) */
434
- context?: Context;
227
+ /** Custom accessor for resolving paths */
228
+ accessor?: AccessorFn<T>;
229
+ /** Max LRU cache size per mode (default: 1000) */
230
+ cacheSize?: number;
231
+ /** Boundary definitions for compile-time interception */
232
+ boundaries?: BoundaryDef<T>[];
233
+ }
234
+ interface JITOptions {
235
+ /** Use accessor to resolve paths instead of direct access */
236
+ useAccessor?: boolean;
237
+ /** Prefix for paths that bypass accessor (direct access) */
238
+ lexicalPrefix?: string;
239
+ }
240
+ declare class ExpressionCompiler<T = unknown> {
241
+ private readonly scope;
242
+ private readonly accessor?;
243
+ private readonly boundaries?;
244
+ private readonly cacheClosures;
245
+ private readonly cacheJIT;
246
+ constructor(options?: ExpressionCompilerOptions<T>);
247
+ /** Compile via closure composition (interpretation) */
248
+ compile<R = unknown>(expr: Expression): CompiledExpression<T, R>;
249
+ /** Compile via JS code generation (JIT) */
250
+ jit<R = unknown>(expr: Expression, opts?: JITOptions): CompiledExpression<T, R>;
251
+ /** Compile (closures) and execute in one step */
252
+ evaluate<R = unknown>(expr: Expression, data: T): R;
253
+ /** Compile (JIT) and execute in one step */
254
+ evaluateJIT<R = unknown>(expr: Expression, data: T, opts?: JITOptions): R;
255
+ /** Normalize custom expression to pure DSL */
256
+ normalize(expr: Expression, transforms: Transforms): Expression;
257
+ /** Extract dependencies without compiling */
258
+ extractDeps(expr: Expression): string[];
259
+ /** Cache size per mode */
260
+ get cacheSize(): {
261
+ closures: number;
262
+ jit: number;
263
+ };
264
+ /** Clear both caches */
265
+ clearCache(): void;
266
+ /** Read-only access to scope */
267
+ getScope(): Readonly<Scope>;
435
268
  }
436
- /**
437
- * Validate expression structure
438
- *
439
- * @param expr - Expression to validate
440
- * @param path - Current path in expression tree (for error messages)
441
- * @param options - Validation options
442
- */
443
- declare function validate(expr: Expression, path?: string, options?: ValidateOptions): ValidationResult;
444
- /**
445
- * Validate and throw if invalid
446
- */
447
- declare function assertValid(expr: Expression, options?: ValidateOptions): void;
448
- /**
449
- * Check if expression is valid
450
- */
451
- declare function isValid(expr: Expression, options?: ValidateOptions): boolean;
452
269
 
453
270
  /**
454
271
  * @statedelta-libs/expressions - Builders
@@ -555,25 +372,22 @@ declare function $cond(left: Expression, op: ConditionOp, right?: Expression): C
555
372
  */
556
373
  declare const cond: typeof $cond;
557
374
  /**
558
- * Create a callback expression (HOC de continuidade).
375
+ * Create an arrow expression (closure).
559
376
  *
560
- * @param name - Context function name (from context)
561
- * @param body - Expression to execute inside the context
562
- * @param params - Optional parameters for the context function
563
- * @returns CbExpr
377
+ * @param body - Expression to defer
378
+ * @param args - Named positional arguments (optional)
379
+ * @returns ArrowExpr
564
380
  *
565
381
  * @example
566
- * $cb("tryCatch", $fn("query"))
567
- * // { $cb: "tryCatch", body: { $fn: "query" } }
568
- *
569
- * $cb("transaction", $fn("saveUser"), { isolation: "serializable" })
570
- * // { $cb: "transaction", body: {...}, params: {...} }
382
+ * $arrow($fn("query")) // () => query()
383
+ * $arrow($("item.price"), ["item"]) // (item) => item.price
384
+ * $arrow($fn("add", [$("a"), $("b")]), ["a", "b"]) // (a, b) => add(a, b)
571
385
  */
572
- declare function $cb(name: string, body: Expression, params?: Expression): CbExpr;
386
+ declare function $arrow(body: Expression, args?: string[]): ArrowExpr;
573
387
  /**
574
- * Alias for $cb (callback builder)
388
+ * Alias for $arrow
575
389
  */
576
- declare const cb: typeof $cb;
390
+ declare const arrow: typeof $arrow;
577
391
  /**
578
392
  * Create a function call expression that always calls the function.
579
393
  * Unlike $fn which returns function reference when args is empty/undefined,
@@ -594,428 +408,154 @@ declare function $call(name: string, args?: Expression[]): FnExpr;
594
408
  declare const call: typeof $call;
595
409
 
596
410
  declare const builders_$: typeof $;
411
+ declare const builders_$arrow: typeof $arrow;
597
412
  declare const builders_$call: typeof $call;
598
- declare const builders_$cb: typeof $cb;
599
413
  declare const builders_$cond: typeof $cond;
600
414
  declare const builders_$fn: typeof $fn;
601
415
  declare const builders_$if: typeof $if;
602
416
  declare const builders_$pipe: typeof $pipe;
417
+ declare const builders_arrow: typeof arrow;
603
418
  declare const builders_call: typeof call;
604
- declare const builders_cb: typeof cb;
605
419
  declare const builders_cond: typeof cond;
606
420
  declare const builders_fn: typeof fn;
607
421
  declare const builders_pipe: typeof pipe;
608
422
  declare const builders_ref: typeof ref;
609
423
  declare namespace builders {
610
- export { builders_$ as $, builders_$call as $call, builders_$cb as $cb, builders_$cond as $cond, builders_$fn as $fn, builders_$if as $if, builders_$pipe as $pipe, builders_call as call, builders_cb as cb, builders_cond as cond, builders_fn as fn, builders_pipe as pipe, builders_ref as ref };
424
+ export { builders_$ as $, builders_$arrow as $arrow, builders_$call as $call, builders_$cond as $cond, builders_$fn as $fn, builders_$if as $if, builders_$pipe as $pipe, builders_arrow as arrow, builders_call as call, builders_cond as cond, builders_fn as fn, builders_pipe as pipe, builders_ref as ref };
611
425
  }
612
426
 
613
427
  /**
614
- * @statedelta-libs/expressions - DSL to AST Transformer
428
+ * @statedelta-libs/expressions - Type Guards
615
429
  *
616
- * Transforms JSON DSL expressions to ESTree AST nodes using omni-ast builders.
617
- * Supports two modes:
618
- * - With prefixes (default): generates `data?.user?.name`, `scope.add()`
619
- * - Without prefixes: generates `user?.name`, `add()` (for use with destructuring)
430
+ * Type guards and constants for expression type checking.
620
431
  */
621
432
 
622
- type ASTNode = types.Expression;
623
- /** Options for DSL to AST transformation */
624
- interface TransformOptions {
625
- /**
626
- * Name of data parameter (default: "data")
627
- * When noPrefixes=true, this is ignored for property access
628
- */
629
- dataParam?: string;
630
- /**
631
- * Name of scope parameter (default: "scope")
632
- * When noPrefixes=true, this is ignored for function calls
633
- */
634
- scopeParam?: string;
635
- /**
636
- * When true, generates code without data/scope prefixes.
637
- * Assumes variables are available via destructuring.
638
- * - `user?.name` instead of `data?.user?.name`
639
- * - `add(a, b)` instead of `scope.add(data?.a, data?.b)`
640
- */
641
- noPrefixes?: boolean;
642
- /**
643
- * When true, generates accessor("path", data) instead of property access.
644
- * Requires `accessor` function in scope.
645
- * - `accessor("user.name", data)` instead of `user?.name`
646
- */
647
- useAccessor?: boolean;
648
- /**
649
- * Path prefix that should use direct property access instead of accessor.
650
- * Only used when useAccessor is true.
651
- * - `lexicalPrefix: "params"` → `params?.foo` instead of `accessor("params.foo", data)`
652
- */
653
- lexicalPrefix?: string;
654
- }
655
433
  /**
656
- * Transform DSL expression to AST node
657
- *
658
- * @param expr - DSL expression
659
- * @param options - Transform options
660
- * @returns ESTree AST node
661
- *
662
- * @example
663
- * ```ts
664
- * // With prefixes (default)
665
- * dslToAST({ $: "user.name" })
666
- * // → data?.user?.name
667
- *
668
- * // Without prefixes (for destructuring)
669
- * dslToAST({ $: "user.name" }, { noPrefixes: true })
670
- * // → user?.name
671
- * ```
434
+ * Check if value is a reference expression
672
435
  */
673
- declare function dslToAST(expr: Expression, options?: TransformOptions): ASTNode;
436
+ declare const isRef: (v: unknown) => v is RefExpr;
674
437
  /**
675
- * Wrap AST expression in an arrow function
438
+ * Check if value is a conditional expression
676
439
  */
677
- declare function wrapInFunction(bodyAst: ASTNode, params?: string[]): ASTNode;
678
-
440
+ declare const isConditional: (v: unknown) => v is ConditionalExpr;
679
441
  /**
680
- * @statedelta-libs/expressions - Extract Scope Functions
681
- *
682
- * Extracts function names used from scope in a DSL expression.
683
- * Used to generate optimized destructuring: const { add, filter } = scope;
442
+ * Check if value is a function call expression
684
443
  */
685
-
444
+ declare const isFn: (v: unknown) => v is FnExpr;
686
445
  /**
687
- * Extract all function names used from scope in an expression
688
- *
689
- * @param expr - DSL expression to analyze
690
- * @returns Set of function names used
691
- *
692
- * @example
693
- * ```ts
694
- * extractScopeFns({ $fn: "add", args: [{ $: "a" }, { $: "b" }] })
695
- * // Set { "add" }
696
- *
697
- * extractScopeFns({
698
- * $pipe: [
699
- * { $: "items" },
700
- * { $fn: "filter", args: [pred] },
701
- * { $fn: "sum" }
702
- * ]
703
- * })
704
- * // Set { "filter", "sum" }
705
- * ```
446
+ * Check if value is a pipe expression
706
447
  */
707
- declare function extractScopeFns(expr: Expression): Set<string>;
448
+ declare const isPipe: (v: unknown) => v is PipeExpr;
708
449
  /**
709
- * Extract all context function names used in an expression
710
- *
711
- * @param expr - DSL expression to analyze
712
- * @returns Set of context function names used
713
- *
714
- * @example
715
- * ```ts
716
- * extractContextFns({ $cb: "tryCatch", body: { $fn: "query" } })
717
- * // Set { "tryCatch" }
718
- * ```
450
+ * Check if value is an arrow expression (closure)
719
451
  */
720
- declare function extractContextFns(expr: Expression): Set<string>;
452
+ declare const isArrow: (v: unknown) => v is ArrowExpr;
721
453
  /**
722
- * Extract root-level data dependencies from paths
723
- *
724
- * @param deps - Array of dependency paths
725
- * @returns Set of root-level property names
454
+ * Check if value is a condition expression
726
455
  *
727
- * @example
728
- * ```ts
729
- * extractDataRoots(["user.name", "user.age", "items", "config.theme"])
730
- * // Set { "user", "items", "config" }
731
- * ```
456
+ * Verifies `op` is a valid condition operator to distinguish from
457
+ * effect objects that also have `left` and `op` (e.g. op: "set").
732
458
  */
733
- declare function extractDataRoots(deps: string[]): Set<string>;
734
-
459
+ declare const isCondition: (v: unknown) => v is Condition;
735
460
  /**
736
- * @statedelta-libs/expressions - AST Compiler
737
- *
738
- * Compiles DSL expressions to optimized functions using AST generation.
739
- * Uses destructuring for better performance:
740
- * - Variables from data are destructured into local scope
741
- * - Functions from scope are destructured into local scope
742
- * - Generated code is smaller and faster
461
+ * Check if value is a condition group
743
462
  */
744
-
745
- /** Options for AST compilation */
746
- interface CompileASTOptions {
747
- /** Scope with functions available to the expression */
748
- scope?: Scope;
749
- /** Context with HOC functions available to $cb expressions */
750
- context?: Context;
751
- /** Return generated code instead of function (for debugging) */
752
- returnCode?: boolean;
753
- /**
754
- * When true, generates accessor("path", data) instead of property access.
755
- * Requires `accessor` function in scope.
756
- */
757
- useAccessor?: boolean;
758
- /**
759
- * Path prefix that should use direct property access instead of accessor.
760
- * Only used when useAccessor is true.
761
- * - `lexicalPrefix: "params"` → `params?.foo` instead of `accessor("params.foo", data)`
762
- */
763
- lexicalPrefix?: string;
764
- }
765
- /** Result when returnCode is true */
766
- interface CompileASTCodeResult {
767
- code: string;
768
- deps: string[];
769
- hash: string;
770
- dataRoots: string[];
771
- scopeFns: string[];
772
- contextFns: string[];
773
- }
463
+ declare const isConditionGroup: (v: unknown) => v is ConditionGroup;
774
464
  /**
775
- * Compile DSL expression to optimized function using AST generation
776
- *
777
- * This is the high-performance compiler that:
778
- * 1. Extracts dependencies and scope functions
779
- * 2. Generates AST without prefixes
780
- * 3. Wraps in destructuring for optimal variable access
781
- * 4. Creates function via `new Function()` or `eval()`
782
- *
783
- * @example
784
- * ```ts
785
- * import { compileAST } from '@statedelta-libs/expressions';
786
- * import { add, filter, sum } from '@statedelta-libs/operators';
787
- *
788
- * const { fn } = compileAST(
789
- * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
790
- * { scope: { add, filter, sum } }
791
- * );
792
- *
793
- * fn({ a: 1, b: 2 }); // 3
794
- * ```
465
+ * Check if value is any condition expression
795
466
  */
796
- declare function compileAST<T = unknown, R = unknown>(expr: Expression, options: CompileASTOptions & {
797
- returnCode: true;
798
- }): CompileASTCodeResult;
799
- declare function compileAST<T = unknown, R = unknown>(expr: Expression, options?: CompileASTOptions): CompiledExpression<T, R>;
467
+ declare const isConditionExpr: (v: unknown) => v is ConditionExpr;
800
468
  /**
801
- * Compile and execute in one step
802
- *
803
- * @example
804
- * ```ts
805
- * evaluateAST(
806
- * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
807
- * { a: 1, b: 2 },
808
- * { scope: { add } }
809
- * ); // 3
810
- * ```
469
+ * Check if value is a literal (not an expression object)
811
470
  */
812
- declare function evaluateAST<T = unknown, R = unknown>(expr: Expression, data: T, options?: CompileASTOptions): R;
471
+ declare const isLiteral: (v: unknown) => v is Literal;
813
472
 
814
473
  /**
815
- * @statedelta-libs/expressions - Normalize
816
- *
817
- * External normalization helper for custom expression types.
818
- * Converts custom DSL nodes ($query, $mapper, etc.) to standard Expression
819
- * BEFORE compilation. Keeps the core compiler pure.
820
- *
821
- * @example
822
- * ```ts
823
- * const transforms = {
824
- * $query: (node) => ({ $fn: "__query", args: [node.$query, node.params ?? {}] })
825
- * };
826
- *
827
- * const pure = normalize(
828
- * { $query: "isAttacked", params: { row: { $: "kingRow" } } },
829
- * transforms
830
- * );
831
- * // → { $fn: "__query", args: ["isAttacked", { row: { $: "kingRow" } }] }
474
+ * @statedelta-libs/expressions - Validation
832
475
  *
833
- * compile(pure, { scope });
834
- * ```
476
+ * Validates expression structure before compilation.
835
477
  */
836
478
 
837
479
  /**
838
- * Transform function for custom expression types.
839
- * Receives the raw node and returns a standard Expression.
480
+ * Validation options
481
+ */
482
+ interface ValidateOptions {
483
+ /** Scope to validate function names against (optional) */
484
+ scope?: Scope;
485
+ /** Boundary definitions — matched nodes skip validation */
486
+ boundaries?: BoundaryDef[];
487
+ }
488
+ /**
489
+ * Validate expression structure
840
490
  *
841
- * @example
842
- * ```ts
843
- * const transforms: Transforms = {
844
- * $query: (node) => ({
845
- * $fn: "__query",
846
- * args: [node.$query, node.params ?? {}]
847
- * }),
848
- * };
849
- * ```
491
+ * @param expr - Expression to validate
492
+ * @param path - Current path in expression tree (for error messages)
493
+ * @param options - Validation options
850
494
  */
851
- type TransformFn = (node: Record<string, unknown>) => Expression;
495
+ declare function validate(expr: Expression, path?: string, options?: ValidateOptions): ValidationResult;
852
496
  /**
853
- * Map of transform functions keyed by marker (e.g. "$query", "$mapper")
497
+ * Validate and throw if invalid
854
498
  */
855
- type Transforms = Record<string, TransformFn>;
499
+ declare function assertValid(expr: Expression, options?: ValidateOptions): void;
856
500
  /**
857
- * Normalize an expression by applying transforms recursively.
858
- * Converts custom DSL nodes to standard Expression types.
859
- *
860
- * @param expr - Expression (possibly with custom nodes)
861
- * @param transforms - Map of transform functions
862
- * @returns Pure Expression with all custom nodes converted
863
- *
864
- * @example
865
- * ```ts
866
- * const transforms = {
867
- * $query: (node) => ({ $fn: "__query", args: [node.$query, node.params ?? {}] })
868
- * };
869
- *
870
- * normalize({ $query: "check", params: { x: 1 } }, transforms);
871
- * // → { $fn: "__query", args: ["check", { x: 1 }] }
872
- *
873
- * // Nested expressions in params are also normalized
874
- * normalize({ $query: "check", params: { row: { $: "myRow" } } }, transforms);
875
- * // → { $fn: "__query", args: ["check", { row: { $: "myRow" } }] }
876
- * ```
501
+ * Check if expression is valid
877
502
  */
878
- declare function normalize(expr: unknown, transforms: Transforms): Expression;
503
+ declare function isValid(expr: Expression, options?: ValidateOptions): boolean;
879
504
 
880
505
  /**
881
- * @statedelta-libs/expressions - Boundaries
882
- *
883
- * Resolve custom boundaries ($simulate, $query, etc.) before compilation.
884
- * Boundaries are "foreign bodies" that stop the expression flow and delegate
885
- * to external handlers. The handler can compile internal slots independently.
886
- *
887
- * @example
888
- * ```ts
889
- * const { expr, scope } = resolveBoundaries(
890
- * { $simulate: { effects: [...], query: {...} } },
891
- * {
892
- * scope: baseScope,
893
- * resolvers: {
894
- * $simulate: (node, { compile, genId }) => {
895
- * const id = `__simulate_${genId()}`;
896
- * const fn = buildSimulateFn(node, compile);
897
- * return {
898
- * expr: { $fn: id },
899
- * scopeEntry: [id, fn]
900
- * };
901
- * }
902
- * }
903
- * }
904
- * );
506
+ * @statedelta-libs/expressions - Path Compiler
905
507
  *
906
- * compile(expr, { scope });
907
- * ```
508
+ * Compiles paths to optimized getters with wildcard support.
509
+ * Performance: ~50M ops/sec for simple paths, ~10M ops/sec for wildcards
908
510
  */
909
511
 
910
512
  /**
911
- * Result returned by a boundary resolver
513
+ * Check if path has wildcards
912
514
  */
913
- interface ResolverResult {
914
- /** Expression to replace the boundary with */
915
- expr: Expression;
916
- /** Optional entry to add to scope [key, value] */
917
- scopeEntry?: [string, unknown];
918
- }
515
+ declare function hasWildcard(path: string): boolean;
919
516
  /**
920
- * Context provided to boundary resolvers
921
- */
922
- interface ResolverContext {
923
- /** Compile function for compiling internal slots */
924
- compile: typeof compile;
925
- /** Generate unique ID for scope entries */
926
- genId: () => string;
927
- /** Current scope (read-only reference) */
928
- scope: Readonly<Scope>;
929
- /** Compile options passed to resolveBoundaries */
930
- options: Readonly<CompileOptions>;
931
- }
517
+ * Compile path to optimized getter
518
+ */
519
+ declare function compilePath<T = unknown>(path: string): PathGetter<T>;
932
520
  /**
933
- * Boundary resolver function.
934
- * Receives the raw node and context, returns replacement expression.
521
+ * Get value at path directly (without caching)
935
522
  */
936
- type BoundaryResolver = (node: Record<string, unknown>, ctx: ResolverContext) => ResolverResult;
523
+ declare function get<T = unknown>(data: T, path: string): unknown;
937
524
  /**
938
- * Map of boundary resolvers keyed by marker (e.g. "$simulate", "$query")
525
+ * Normalize path for dependency tracking (remove wildcards)
526
+ * @example "items[*].price" → "items"
527
+ * @example "users[*].posts[*].title" → "users"
939
528
  */
940
- type BoundaryResolvers = Record<string, BoundaryResolver>;
529
+ declare function normalizePath(path: string): string;
530
+
941
531
  /**
942
- * ID generator function
532
+ * @statedelta-libs/expressions - Dependency Extraction
533
+ *
534
+ * Extracts paths that an expression depends on.
535
+ * Performance: ~200K ops/sec
943
536
  */
944
- type IdGenerator = () => string;
537
+
945
538
  /**
946
- * Options for resolveBoundaries
539
+ * Extract all paths an expression depends on
540
+ * @example { "$": "user.age" } → ["user.age"]
541
+ * @example { "$if": "$isVip", "then": { "$": "price.vip" } } → ["$isVip", "price.vip"]
947
542
  */
948
- interface ResolveBoundariesOptions extends CompileOptions {
949
- /** Map of boundary resolvers */
950
- resolvers: BoundaryResolvers;
951
- /** Custom ID generator. Default: simple counter ("0", "1", "2"...) */
952
- genId?: IdGenerator;
953
- }
543
+ declare function extractDeps(expr: Expression): string[];
954
544
  /**
955
- * Result of resolveBoundaries
545
+ * Check if expression has dependencies
956
546
  */
957
- interface ResolveBoundariesResult {
958
- /** Pure expression with boundaries replaced */
959
- expr: Expression;
960
- /** Enriched scope with boundary handlers */
961
- scope: Scope;
962
- }
547
+ declare function hasDeps(expr: Expression): boolean;
963
548
  /**
964
- * Resolve boundaries in an expression tree.
965
- *
966
- * Boundaries are custom DSL nodes ($simulate, $query, etc.) that stop the
967
- * normal expression compilation flow. Each boundary is extracted, processed
968
- * by its resolver, and replaced with a standard expression.
969
- *
970
- * The resolver receives:
971
- * - `node`: The raw boundary object (e.g. { $simulate: {...}, effects: [...] })
972
- * - `ctx.compile`: Compile function for internal slots
973
- * - `ctx.genId`: Unique ID generator
974
- * - `ctx.scope`: Current scope (read-only)
975
- *
976
- * The resolver returns:
977
- * - `expr`: Replacement expression (typically { $fn: "__id" })
978
- * - `scopeEntry`: Optional [key, fn] to add to scope
979
- *
980
- * @param expr - Expression (possibly with boundaries)
981
- * @param options - Resolvers, scope, and compile options
982
- * @returns Pure expression and enriched scope
549
+ * Check if expression is pure (no dependencies)
983
550
  */
984
- declare function resolveBoundaries(expr: unknown, options: ResolveBoundariesOptions): ResolveBoundariesResult;
551
+ declare function isPure(expr: Expression): boolean;
985
552
 
986
553
  /**
987
554
  * @statedelta-libs/expressions
988
555
  *
989
556
  * JSON DSL compiler for optimized functions.
990
557
  * Functions are provided via scope, not hardcoded.
991
- *
992
- * @example
993
- * ```ts
994
- * import { compile, evaluate } from '@statedelta-libs/expressions';
995
- * import { filter, map, sum } from '@statedelta-libs/operators';
996
- *
997
- * const scope = { filter, map, sum };
998
- *
999
- * // Using $pipe for composition with initial value
1000
- * const { fn } = compile({
1001
- * $pipe: [
1002
- * { $: "items" },
1003
- * { $fn: "filter", args: [{ path: "active", op: "eq", value: true }] },
1004
- * { $fn: "map", args: [{ $: "price" }] },
1005
- * { $fn: "sum" }
1006
- * ]
1007
- * }, { scope });
1008
- *
1009
- * fn({ items: [...] }); // sum of active item prices
1010
- *
1011
- * // Using $fn for simple function calls
1012
- * evaluate(
1013
- * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
1014
- * { a: 1, b: 2 },
1015
- * { scope: { add: (a, b) => a + b } }
1016
- * ); // 3
1017
- * ```
1018
558
  */
1019
- declare const VERSION = "2.0.0";
559
+ declare const VERSION = "3.0.0";
1020
560
 
1021
- export { type AccessorFn, type BoundaryResolver, type BoundaryResolvers, type CbExpr, type CompileASTCodeResult, type CompileASTOptions, type CompileOptions, type CompiledExpression, type CompiledFn, type Condition, type ConditionExpr, type ConditionGroup, type ConditionOp, type ConditionalExpr, type Context, type ContextFn, type Expression, ExpressionCache, type FnExpr, type IdGenerator, type Literal, type PathGetter, type PathGetterFn, type PipeExpr, type RefExpr, type ResolveBoundariesOptions, type ResolveBoundariesResult, type ResolverContext, type ResolverResult, type Scope, type TransformFn, type TransformOptions, type Transforms, VERSION, type ValidateOptions, type ValidationResult, assertValid, builders, cache, cached, clearPathCache, compile, compileAST, compilePath, dslToAST, evaluate, evaluateAST, extractContextFns, extractDataRoots, extractDeps, extractScopeFns, get, getPathCacheSize, hasDeps, hasWildcard, isCb, isCondition, isConditionExpr, isConditionGroup, isConditional, isFn, isLiteral, isPipe, isPure, isRef, isValid, normalize, normalizePath, resolveBoundaries, validate, wrapInFunction };
561
+ export { type AccessorFn, type ArrowExpr, type BoundaryDef, type CompileOptions, type CompiledExpression, type CompiledFn, type Condition, type ConditionExpr, type ConditionGroup, type ConditionOp, type ConditionalExpr, type Expression, ExpressionCompiler, type ExpressionCompilerOptions, type FnExpr, type JITOptions, type Literal, type PipeExpr, type RefExpr, type Scope, type TransformFn, type Transforms, VERSION, type ValidateOptions, type ValidationResult, assertValid, builders, compilePath, extractDeps, get, hasDeps, hasWildcard, isArrow, isCondition, isConditionExpr, isConditionGroup, isConditional, isFn, isLiteral, isPipe, isPure, isRef, isValid, normalizePath, validate };