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