@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.
@@ -0,0 +1,511 @@
1
+ import { ConditionExpr, Condition, ConditionGroup } from '@statedelta-libs/conditions';
2
+ export { Condition, ConditionExpr, ConditionGroup, Ref } from '@statedelta-libs/conditions';
3
+ import { types } from 'omni-ast';
4
+
5
+ /**
6
+ * @statedelta-libs/expressions - Types
7
+ *
8
+ * Core types for expression compilation.
9
+ */
10
+
11
+ /**
12
+ * Reference to a path in data object
13
+ * @example { "$": "user.name" }
14
+ * @example { "$": "items[*].price" }
15
+ */
16
+ interface RefExpr {
17
+ $: string;
18
+ }
19
+ /**
20
+ * Conditional expression
21
+ * @example { "$if": condition, "then": expr, "else": expr }
22
+ */
23
+ interface ConditionalExpr {
24
+ $if: Expression;
25
+ then: Expression;
26
+ else?: Expression;
27
+ }
28
+ /**
29
+ * Function call expression
30
+ * Calls a function from the provided scope with compiled arguments
31
+ * @example { "$fn": "add", "args": [{ "$": "a" }, { "$": "b" }] }
32
+ */
33
+ interface FnExpr {
34
+ $fn: string;
35
+ args?: Expression[];
36
+ }
37
+ /**
38
+ * Pipe expression (DSL syntax for composition with initial value)
39
+ * First element is the initial value, rest are transformers
40
+ * @example { "$pipe": [{ "$": "items" }, { "$fn": "filter", "args": [pred] }, { "$fn": "sum" }] }
41
+ */
42
+ interface PipeExpr {
43
+ $pipe: Expression[];
44
+ }
45
+ /**
46
+ * Literal values (primitives, arrays, plain objects)
47
+ */
48
+ type Literal = string | number | boolean | null | Literal[] | {
49
+ [key: string]: Literal;
50
+ };
51
+ /**
52
+ * Union of all expression types
53
+ */
54
+ type Expression = Literal | RefExpr | ConditionalExpr | FnExpr | PipeExpr | ConditionExpr;
55
+ /**
56
+ * Compiled expression function
57
+ */
58
+ type CompiledFn<T = unknown, R = unknown> = (data: T) => R;
59
+ /**
60
+ * Result of compiling an expression
61
+ */
62
+ interface CompiledExpression<T = unknown, R = unknown> {
63
+ /** Compiled function */
64
+ fn: CompiledFn<T, R>;
65
+ /** Paths this expression depends on */
66
+ deps: string[];
67
+ /** Unique hash for caching */
68
+ hash: string;
69
+ }
70
+ /**
71
+ * Path getter function
72
+ */
73
+ type PathGetter<T = unknown> = (data: T) => unknown;
74
+ /**
75
+ * Validation result
76
+ */
77
+ interface ValidationResult {
78
+ valid: boolean;
79
+ errors: string[];
80
+ }
81
+ /**
82
+ * Function scope - functions available to $fn expressions
83
+ * @example { add, subtract, multiply, filter, map, sum }
84
+ */
85
+ type Scope = Record<string, (...args: unknown[]) => unknown>;
86
+ /**
87
+ * Options for compilation
88
+ */
89
+ interface CompileOptions {
90
+ /** Functions available to $fn expressions */
91
+ scope?: Scope;
92
+ }
93
+ /**
94
+ * Check if value is a reference expression
95
+ */
96
+ declare const isRef: (v: unknown) => v is RefExpr;
97
+ /**
98
+ * Check if value is a conditional expression
99
+ */
100
+ declare const isConditional: (v: unknown) => v is ConditionalExpr;
101
+ /**
102
+ * Check if value is a function call expression
103
+ */
104
+ declare const isFn: (v: unknown) => v is FnExpr;
105
+ /**
106
+ * Check if value is a pipe expression
107
+ */
108
+ declare const isPipe: (v: unknown) => v is PipeExpr;
109
+ /**
110
+ * Check if value is a condition (from @statedelta-libs/conditions)
111
+ *
112
+ * IMPORTANT: Must verify that `op` is a valid condition operator,
113
+ * not just any string. Effect objects also have `path` and `op`
114
+ * properties (e.g., { resource: "x", op: "set", path: "y", value: z })
115
+ * but "set" is NOT a condition operator.
116
+ */
117
+ declare const isCondition: (v: unknown) => v is Condition;
118
+ /**
119
+ * Check if value is a condition group (from @statedelta-libs/conditions)
120
+ */
121
+ declare const isConditionGroup: (v: unknown) => v is ConditionGroup;
122
+ /**
123
+ * Check if value is any condition expression
124
+ */
125
+ declare const isConditionExpr: (v: unknown) => v is ConditionExpr;
126
+ /**
127
+ * Check if value is a literal (not an expression object)
128
+ */
129
+ declare const isLiteral: (v: unknown) => v is Literal;
130
+
131
+ /**
132
+ * @statedelta-libs/expressions - Compiler
133
+ *
134
+ * Compiles JSON DSL expressions to optimized functions.
135
+ * Functions are provided via scope, not hardcoded.
136
+ */
137
+
138
+ /**
139
+ * Compile expression to optimized function
140
+ *
141
+ * @param expr - Expression to compile
142
+ * @param options - Compile options (scope with functions)
143
+ * @returns Compiled expression with fn, deps, and hash
144
+ *
145
+ * @example
146
+ * ```ts
147
+ * import { add, filter, sum } from '@statedelta-libs/operators';
148
+ *
149
+ * const { fn } = compile(
150
+ * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
151
+ * { scope: { add, filter, sum } }
152
+ * );
153
+ *
154
+ * fn({ a: 1, b: 2 }); // 3
155
+ * ```
156
+ */
157
+ declare function compile<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions): CompiledExpression<T, R>;
158
+ /**
159
+ * Compile and execute in one step
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * evaluate(
164
+ * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
165
+ * { a: 1, b: 2 },
166
+ * { scope: { add } }
167
+ * ); // 3
168
+ * ```
169
+ */
170
+ declare function evaluate<T = unknown, R = unknown>(expr: Expression, data: T, options?: CompileOptions): R;
171
+
172
+ /**
173
+ * @statedelta-libs/expressions - Path Compiler
174
+ *
175
+ * Compiles paths to optimized getters with wildcard support.
176
+ * Performance: ~50M ops/sec for simple paths, ~10M ops/sec for wildcards
177
+ */
178
+
179
+ /**
180
+ * Check if path has wildcards
181
+ */
182
+ declare function hasWildcard(path: string): boolean;
183
+ /**
184
+ * Compile path to optimized getter
185
+ */
186
+ declare function compilePath<T = unknown>(path: string): PathGetter<T>;
187
+ /**
188
+ * Get value at path directly (without caching)
189
+ */
190
+ declare function get<T = unknown>(data: T, path: string): unknown;
191
+ /**
192
+ * Normalize path for dependency tracking (remove wildcards)
193
+ * @example "items[*].price" → "items"
194
+ * @example "users[*].posts[*].title" → "users"
195
+ */
196
+ declare function normalizePath(path: string): string;
197
+ /**
198
+ * Clear path cache
199
+ */
200
+ declare function clearPathCache(): void;
201
+ /**
202
+ * Get cache size
203
+ */
204
+ declare function getPathCacheSize(): number;
205
+
206
+ /**
207
+ * @statedelta-libs/expressions - Dependency Extraction
208
+ *
209
+ * Extracts paths that an expression depends on.
210
+ * Performance: ~200K ops/sec
211
+ */
212
+
213
+ /**
214
+ * Extract all paths an expression depends on
215
+ * @example { "$": "user.age" } → ["user.age"]
216
+ * @example { "$if": "$isVip", "then": { "$": "price.vip" } } → ["$isVip", "price.vip"]
217
+ */
218
+ declare function extractDeps(expr: Expression): string[];
219
+ /**
220
+ * Check if expression has dependencies
221
+ */
222
+ declare function hasDeps(expr: Expression): boolean;
223
+ /**
224
+ * Check if expression is pure (no dependencies)
225
+ */
226
+ declare function isPure(expr: Expression): boolean;
227
+
228
+ /**
229
+ * @statedelta-libs/expressions - Cache
230
+ *
231
+ * LRU cache for compiled expressions.
232
+ * Performance: cache hit ~10M ops/sec
233
+ */
234
+
235
+ /**
236
+ * LRU Cache for compiled expressions
237
+ */
238
+ declare class ExpressionCache {
239
+ private cache;
240
+ private _maxSize;
241
+ constructor(maxSize?: number);
242
+ /**
243
+ * Get or compile expression
244
+ *
245
+ * Note: The cache key is based on the expression only, not the scope.
246
+ * If you use different scopes for the same expression, consider using
247
+ * separate cache instances or not caching at all.
248
+ */
249
+ get<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions): CompiledExpression<T, R>;
250
+ /**
251
+ * Check if expression is cached
252
+ */
253
+ has(expr: Expression): boolean;
254
+ /**
255
+ * Delete specific expression from cache
256
+ */
257
+ delete(expr: Expression): boolean;
258
+ /**
259
+ * Clear the cache
260
+ */
261
+ clear(): void;
262
+ /**
263
+ * Current cache size
264
+ */
265
+ get size(): number;
266
+ /**
267
+ * Maximum cache size
268
+ */
269
+ get maxSize(): number;
270
+ /**
271
+ * Set maximum cache size
272
+ */
273
+ set maxSize(value: number);
274
+ }
275
+ declare const cache: ExpressionCache;
276
+ /**
277
+ * Compile and cache expression (convenience function)
278
+ *
279
+ * Note: Uses global cache. For custom scope management,
280
+ * use a dedicated ExpressionCache instance.
281
+ */
282
+ declare function cached<T = unknown, R = unknown>(expr: Expression, options?: CompileOptions): CompiledExpression<T, R>;
283
+
284
+ /**
285
+ * @statedelta-libs/expressions - Validation
286
+ *
287
+ * Validates expression structure before compilation.
288
+ */
289
+
290
+ /**
291
+ * Validation options
292
+ */
293
+ interface ValidateOptions {
294
+ /** Scope to validate function names against (optional) */
295
+ scope?: Scope;
296
+ }
297
+ /**
298
+ * Validate expression structure
299
+ *
300
+ * @param expr - Expression to validate
301
+ * @param path - Current path in expression tree (for error messages)
302
+ * @param options - Validation options
303
+ */
304
+ declare function validate(expr: Expression, path?: string, options?: ValidateOptions): ValidationResult;
305
+ /**
306
+ * Validate and throw if invalid
307
+ */
308
+ declare function assertValid(expr: Expression, options?: ValidateOptions): void;
309
+ /**
310
+ * Check if expression is valid
311
+ */
312
+ declare function isValid(expr: Expression, options?: ValidateOptions): boolean;
313
+
314
+ /**
315
+ * @statedelta-libs/expressions - DSL to AST Transformer
316
+ *
317
+ * Transforms JSON DSL expressions to ESTree AST nodes using omni-ast builders.
318
+ * Supports two modes:
319
+ * - With prefixes (default): generates `data?.user?.name`, `scope.add()`
320
+ * - Without prefixes: generates `user?.name`, `add()` (for use with destructuring)
321
+ */
322
+
323
+ type ASTNode = types.Expression;
324
+ /** Options for DSL to AST transformation */
325
+ interface TransformOptions {
326
+ /**
327
+ * Name of data parameter (default: "data")
328
+ * When noPrefixes=true, this is ignored for property access
329
+ */
330
+ dataParam?: string;
331
+ /**
332
+ * Name of scope parameter (default: "scope")
333
+ * When noPrefixes=true, this is ignored for function calls
334
+ */
335
+ scopeParam?: string;
336
+ /**
337
+ * When true, generates code without data/scope prefixes.
338
+ * Assumes variables are available via destructuring.
339
+ * - `user?.name` instead of `data?.user?.name`
340
+ * - `add(a, b)` instead of `scope.add(data?.a, data?.b)`
341
+ */
342
+ noPrefixes?: boolean;
343
+ }
344
+ /**
345
+ * Transform DSL expression to AST node
346
+ *
347
+ * @param expr - DSL expression
348
+ * @param options - Transform options
349
+ * @returns ESTree AST node
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * // With prefixes (default)
354
+ * dslToAST({ $: "user.name" })
355
+ * // → data?.user?.name
356
+ *
357
+ * // Without prefixes (for destructuring)
358
+ * dslToAST({ $: "user.name" }, { noPrefixes: true })
359
+ * // → user?.name
360
+ * ```
361
+ */
362
+ declare function dslToAST(expr: Expression, options?: TransformOptions): ASTNode;
363
+ /**
364
+ * Wrap AST expression in an arrow function
365
+ */
366
+ declare function wrapInFunction(bodyAst: ASTNode, params?: string[]): ASTNode;
367
+
368
+ /**
369
+ * @statedelta-libs/expressions - Extract Scope Functions
370
+ *
371
+ * Extracts function names used from scope in a DSL expression.
372
+ * Used to generate optimized destructuring: const { add, filter } = scope;
373
+ */
374
+
375
+ /**
376
+ * Extract all function names used from scope in an expression
377
+ *
378
+ * @param expr - DSL expression to analyze
379
+ * @returns Set of function names used
380
+ *
381
+ * @example
382
+ * ```ts
383
+ * extractScopeFns({ $fn: "add", args: [{ $: "a" }, { $: "b" }] })
384
+ * // Set { "add" }
385
+ *
386
+ * extractScopeFns({
387
+ * $pipe: [
388
+ * { $: "items" },
389
+ * { $fn: "filter", args: [pred] },
390
+ * { $fn: "sum" }
391
+ * ]
392
+ * })
393
+ * // Set { "filter", "sum" }
394
+ * ```
395
+ */
396
+ declare function extractScopeFns(expr: Expression): Set<string>;
397
+ /**
398
+ * Extract root-level data dependencies from paths
399
+ *
400
+ * @param deps - Array of dependency paths
401
+ * @returns Set of root-level property names
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * extractDataRoots(["user.name", "user.age", "items", "config.theme"])
406
+ * // Set { "user", "items", "config" }
407
+ * ```
408
+ */
409
+ declare function extractDataRoots(deps: string[]): Set<string>;
410
+
411
+ /**
412
+ * @statedelta-libs/expressions - AST Compiler
413
+ *
414
+ * Compiles DSL expressions to optimized functions using AST generation.
415
+ * Uses destructuring for better performance:
416
+ * - Variables from data are destructured into local scope
417
+ * - Functions from scope are destructured into local scope
418
+ * - Generated code is smaller and faster
419
+ */
420
+
421
+ /** Options for AST compilation */
422
+ interface CompileASTOptions {
423
+ /** Scope with functions available to the expression */
424
+ scope?: Scope;
425
+ /** Return generated code instead of function (for debugging) */
426
+ returnCode?: boolean;
427
+ }
428
+ /** Result when returnCode is true */
429
+ interface CompileASTCodeResult {
430
+ code: string;
431
+ deps: string[];
432
+ hash: string;
433
+ dataRoots: string[];
434
+ scopeFns: string[];
435
+ }
436
+ /**
437
+ * Compile DSL expression to optimized function using AST generation
438
+ *
439
+ * This is the high-performance compiler that:
440
+ * 1. Extracts dependencies and scope functions
441
+ * 2. Generates AST without prefixes
442
+ * 3. Wraps in destructuring for optimal variable access
443
+ * 4. Creates function via `new Function()` or `eval()`
444
+ *
445
+ * @example
446
+ * ```ts
447
+ * import { compileAST } from '@statedelta-libs/expressions';
448
+ * import { add, filter, sum } from '@statedelta-libs/operators';
449
+ *
450
+ * const { fn } = compileAST(
451
+ * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
452
+ * { scope: { add, filter, sum } }
453
+ * );
454
+ *
455
+ * fn({ a: 1, b: 2 }); // 3
456
+ * ```
457
+ */
458
+ declare function compileAST<T = unknown, R = unknown>(expr: Expression, options: CompileASTOptions & {
459
+ returnCode: true;
460
+ }): CompileASTCodeResult;
461
+ declare function compileAST<T = unknown, R = unknown>(expr: Expression, options?: CompileASTOptions): CompiledExpression<T, R>;
462
+ /**
463
+ * Compile and execute in one step
464
+ *
465
+ * @example
466
+ * ```ts
467
+ * evaluateAST(
468
+ * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
469
+ * { a: 1, b: 2 },
470
+ * { scope: { add } }
471
+ * ); // 3
472
+ * ```
473
+ */
474
+ declare function evaluateAST<T = unknown, R = unknown>(expr: Expression, data: T, options?: CompileASTOptions): R;
475
+
476
+ /**
477
+ * @statedelta-libs/expressions
478
+ *
479
+ * JSON DSL compiler for optimized functions.
480
+ * Functions are provided via scope, not hardcoded.
481
+ *
482
+ * @example
483
+ * ```ts
484
+ * import { compile, evaluate } from '@statedelta-libs/expressions';
485
+ * import { filter, map, sum } from '@statedelta-libs/operators';
486
+ *
487
+ * const scope = { filter, map, sum };
488
+ *
489
+ * // Using $pipe for composition with initial value
490
+ * const { fn } = compile({
491
+ * $pipe: [
492
+ * { $: "items" },
493
+ * { $fn: "filter", args: [{ path: "active", op: "eq", value: true }] },
494
+ * { $fn: "map", args: [{ $: "price" }] },
495
+ * { $fn: "sum" }
496
+ * ]
497
+ * }, { scope });
498
+ *
499
+ * fn({ items: [...] }); // sum of active item prices
500
+ *
501
+ * // Using $fn for simple function calls
502
+ * evaluate(
503
+ * { $fn: "add", args: [{ $: "a" }, { $: "b" }] },
504
+ * { a: 1, b: 2 },
505
+ * { scope: { add: (a, b) => a + b } }
506
+ * ); // 3
507
+ * ```
508
+ */
509
+ declare const VERSION = "0.0.1";
510
+
511
+ export { type CompileASTCodeResult, type CompileASTOptions, type CompileOptions, type CompiledExpression, type CompiledFn, type ConditionalExpr, type Expression, ExpressionCache, type FnExpr, type Literal, type PathGetter, type PipeExpr, type RefExpr, type Scope, type TransformOptions, VERSION, type ValidateOptions, type ValidationResult, assertValid, cache, cached, clearPathCache, compile, compileAST, compilePath, dslToAST, evaluate, evaluateAST, extractDataRoots, extractDeps, extractScopeFns, get, getPathCacheSize, hasDeps, hasWildcard, isCondition, isConditionExpr, isConditionGroup, isConditional, isFn, isLiteral, isPipe, isPure, isRef, isValid, normalizePath, validate, wrapInFunction };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import {isRef,compile,validate}from'@statedelta-libs/conditions';import {builders,generate}from'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&&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 compile(n);if(y(n))return 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=validate(n,e);r.valid||t.push(...r.errors);return}if(y(n)){let r=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 builders.literal(null);if(typeof n=="string")return builders.literal(n);if(typeof n=="number")return builders.literal(n);if(typeof n=="boolean")return builders.literal(n);if(Array.isArray(n))return 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])=>builders.property(builders.identifier(r),d(c,e,t,o)));return builders.objectExpression(s)}return 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?builders.identifier("undefined"):builders.identifier(e);let i;if(t){let s=o[0];i=builders.identifier(s.value);for(let r=1;r<o.length;r++){let c=o[r];c.type==="key"?i=builders.memberExpression(i,builders.identifier(c.value),false,true):i=builders.memberExpression(i,builders.literal(c.value),true,true);}}else {i=builders.identifier(e);for(let s of o)s.type==="key"?i=builders.memberExpression(i,builders.identifier(s.value),false,true):i=builders.memberExpression(i,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?builders.identifier("undefined"):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=builders.identifier(c);if(f){let u=F(f);for(let p of u)p.type==="key"?a=builders.memberExpression(a,builders.identifier(p.value),false,true):a=builders.memberExpression(a,builders.literal(p.value),true,true);}return builders.callExpression(builders.memberExpression(r,builders.identifier("map"),false,true),[builders.arrowFunctionExpression([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=builders.identifier(s);if(r){let a=F(r);for(let u of a)u.type==="key"&&(c=builders.memberExpression(c,builders.identifier(u.value),false,true));}if(i.includes("[*]")){let a=B(c,i);return builders.callExpression(builders.memberExpression(n,builders.identifier("flatMap"),false,true),[builders.arrowFunctionExpression([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=builders.memberExpression(c,builders.identifier(u.value),false,true));}return builders.callExpression(builders.memberExpression(n,builders.identifier("flatMap"),false,true),[builders.arrowFunctionExpression([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?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):builders.identifier("undefined");return builders.conditionalExpression(i,s,r)}function bn(n,e,t,o){let i=o?builders.identifier(n.$fn):builders.memberExpression(builders.identifier(t),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 builders.callExpression(i,s)}function Sn(n,e,t,o){if(n.length===0)return 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=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):builders.literal(null),r=yn[n.op];if(r)return builders.binaryExpression(r,i,s);switch(n.op){case "in":return builders.callExpression(builders.memberExpression(s,builders.identifier("includes")),[i]);case "notIn":return builders.unaryExpression("!",builders.callExpression(builders.memberExpression(s,builders.identifier("includes")),[i]));case "contains":return builders.callExpression(builders.memberExpression(i,builders.identifier("includes"),false,true),[s]);case "notContains":return builders.unaryExpression("!",builders.callExpression(builders.memberExpression(i,builders.identifier("includes"),false,true),[s]));case "exists":return builders.binaryExpression("!=",i,builders.literal(null));case "notExists":return builders.binaryExpression("==",i,builders.literal(null));case "matches":return builders.callExpression(builders.memberExpression(builders.newExpression(builders.identifier("RegExp"),[s]),builders.identifier("test")),[i]);case "notMatches":return builders.unaryExpression("!",builders.callExpression(builders.memberExpression(builders.newExpression(builders.identifier("RegExp"),[s]),builders.identifier("test")),[i]));case "startsWith":return builders.callExpression(builders.memberExpression(i,builders.identifier("startsWith"),false,true),[s]);case "endsWith":return builders.callExpression(builders.memberExpression(i,builders.identifier("endsWith"),false,true),[s]);default:return builders.binaryExpression("===",i,s)}}function $n(n,e,t,o){let{logic:i,conditions:s}=n,r=i==="AND"?"&&":"||";if(s.length===0)return 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=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 builders.arrowFunctionExpression(e.map(t=>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=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";export{v as ExpressionCache,ie as VERSION,mn as assertValid,_ as cache,dn as cached,en as clearPathCache,O as compile,D as compileAST,x as compilePath,N as dslToAST,pn as evaluate,Y as evaluateAST,G as extractDataRoots,T as extractDeps,j as extractScopeFns,nn as get,tn as getPathCacheSize,rn as hasDeps,V as hasWildcard,g as isCondition,H as isConditionExpr,y as isConditionGroup,h as isConditional,E as isFn,W as isLiteral,C as isPipe,sn as isPure,m as isRef,gn as isValid,k as normalizePath,z as validate,K as wrapInFunction};
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@statedelta-libs/expressions",
3
+ "version": "0.0.1",
4
+ "description": "JSON DSL compiler for optimized functions - StateDelta expression engine",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "dev": "tsup --watch",
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:coverage": "vitest run --coverage",
22
+ "typecheck": "tsc --noEmit",
23
+ "clean": "rm -rf dist",
24
+ "format": "prettier --write \"src/**/*.ts\"",
25
+ "format:check": "prettier --check \"src/**/*.ts\""
26
+ },
27
+ "dependencies": {
28
+ "@statedelta-libs/conditions": "workspace:*",
29
+ "omni-ast": "^2.0.0"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "README.md"
34
+ ],
35
+ "sideEffects": false,
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/andersondrosa/statedelta.git",
42
+ "directory": "engine/expressions"
43
+ },
44
+ "keywords": [
45
+ "statedelta",
46
+ "expressions",
47
+ "dsl",
48
+ "compiler",
49
+ "json",
50
+ "functional",
51
+ "typescript"
52
+ ],
53
+ "author": "Anderson D. Rosa <andersondrosa@outlook.com>",
54
+ "license": "MIT",
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
58
+ }