@statedelta-libs/expressions 3.0.0 → 3.2.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
@@ -91,29 +91,85 @@ type Expression = Literal | RefExpr | ConditionalExpr | FnExpr | PipeExpr | Arro
91
91
  */
92
92
  type CompiledFn<T = unknown, R = unknown> = (data: T) => R;
93
93
  /**
94
- * Result of compiling an expression
94
+ * Callable function bound to a context, with metadata as properties.
95
+ * Call directly — no `.fn` needed.
96
+ *
97
+ * @example
98
+ * const fn = artifact.bind({ scope, accessor });
99
+ * fn(data); // callable direct
100
+ * fn.deps; // ["hp", "armor"]
101
+ * fn.hash; // "a1b2c3"
102
+ * items.map(fn); // works as first-class function
103
+ */
104
+ interface BoundFn<T = unknown, R = unknown> {
105
+ /** Execute the expression with the given data */
106
+ (data: T): R;
107
+ /** Paths this expression depends on */
108
+ readonly deps: string[];
109
+ /** Unique hash for caching */
110
+ readonly hash: string;
111
+ }
112
+ /**
113
+ * Create a BoundFn from a compiled function + metadata.
114
+ * Attaches deps and hash as readonly props.
95
115
  */
96
- interface CompiledExpression<T = unknown, R = unknown> {
97
- /** Compiled function */
98
- fn: CompiledFn<T, R>;
116
+ declare function createBoundFn<T = unknown, R = unknown>(fn: CompiledFn<T, R>, deps: string[], hash: string): BoundFn<T, R>;
117
+ /**
118
+ * Context-free compile artifact — result of compilation without pre-prepared fn.
119
+ * Stored in shared compile caches. Use bind() to attach context and get fn.
120
+ */
121
+ interface CompileArtifact<T = unknown, R = unknown> {
99
122
  /** Paths this expression depends on */
100
123
  deps: string[];
101
124
  /** Unique hash for caching */
102
125
  hash: string;
126
+ /**
127
+ * Bind to a context and get a callable BoundFn.
128
+ * @param ctx - Runtime context (scope, accessor, handlers). Uses defaults if omitted.
129
+ */
130
+ bind(ctx?: BindContext): BoundFn<T, R>;
103
131
  }
132
+ /**
133
+ * Context for bind() — runtime dependencies injected after compilation.
134
+ */
135
+ interface BindContext {
136
+ scope?: Scope;
137
+ accessor?: AccessorFn;
138
+ handlers?: WrappedHandlers;
139
+ }
140
+ /**
141
+ * Alias for CompileArtifact — the public name going forward.
142
+ */
143
+ type Artifact<T = unknown, R = unknown> = CompileArtifact<T, R>;
144
+ /**
145
+ * Describes what each flat parameter of the compiled factory expects.
146
+ * Order matches the factory's parameter order.
147
+ */
148
+ type ParamSlot = {
149
+ kind: "scope";
150
+ name: string;
151
+ } | {
152
+ kind: "accessor";
153
+ } | {
154
+ kind: "handler";
155
+ ns: string;
156
+ m: string;
157
+ } | {
158
+ kind: "boundary";
159
+ index: number;
160
+ };
104
161
  /**
105
162
  * Path getter function
106
163
  */
107
164
  type PathGetter<T = unknown> = (data: T) => unknown;
108
165
  /**
109
- * Accessor customizado para resolver paths
110
- * Permite usar ctx.get(path) em vez de acesso direto por propriedade
166
+ * Accessor customizado para resolver paths.
167
+ * Closure auto-suficiente sabe de onde ler.
111
168
  *
112
169
  * @example
113
- * // Com TickContext
114
- * const accessor: AccessorFn<TickContext> = (path, ctx) => ctx.get(path);
170
+ * const accessor: AccessorFn = (path) => tickContext.get(path);
115
171
  */
116
- type AccessorFn<T = unknown> = (path: string, data: T) => unknown;
172
+ type AccessorFn = (path: string) => unknown;
117
173
  /**
118
174
  * Validation result
119
175
  */
@@ -143,13 +199,46 @@ interface CompileOptions<T = unknown> {
143
199
  * Quando fornecido, substitui o acesso direto por propriedade.
144
200
  *
145
201
  * @example
146
- * // Com TickContext
147
- * compile(expr, { accessor: (path, ctx) => ctx.get(path) })
202
+ * compile(expr, { accessor: (path) => ctx.get(path) })
148
203
  */
149
- accessor?: AccessorFn<T>;
204
+ accessor?: AccessorFn;
150
205
  /** Boundary definitions for compile-time interception */
151
206
  boundaries?: BoundaryDef<T>[];
207
+ /** Wrapped handlers (ctx already bound) for $fn "namespace:method" calls */
208
+ handlers?: WrappedHandlers;
152
209
  }
210
+ /**
211
+ * Handler function — receives HandlerContext via `this` (bound at compile time).
212
+ * Must be a regular function or method shorthand (NOT arrow function).
213
+ * HandlerContext is defined in compiler.ts.
214
+ *
215
+ * @example
216
+ * {
217
+ * query: {
218
+ * find(key: string) { return this.scope.add(key, 1); }
219
+ * }
220
+ * }
221
+ */
222
+ type HandlerFn = (...args: any[]) => any;
223
+ /**
224
+ * Handlers map — namespaces of handler methods.
225
+ * Provided by consumer in ExpressionCompiler constructor.
226
+ * Handlers access context via `this` (bound automatically via .bind()).
227
+ *
228
+ * @example
229
+ * {
230
+ * query: {
231
+ * find(filter: string) { return this.scope.transform(filter); },
232
+ * findAll() { return this.handlers.other.list(); },
233
+ * },
234
+ * }
235
+ */
236
+ type HandlersMap = Record<string, Record<string, HandlerFn>>;
237
+ /**
238
+ * Wrapped handlers (ctx already bound via .bind()).
239
+ * Used internally by both pipelines.
240
+ */
241
+ type WrappedHandlers = Record<string, Record<string, (...args: any[]) => any>>;
153
242
  /**
154
243
  * Boundary definition for compile-time interception.
155
244
  * Allows foreign DSLs and compilers to plug into the expression walk.
@@ -214,22 +303,77 @@ type TransformFn = (node: Record<string, unknown>) => Expression;
214
303
  */
215
304
  type Transforms = Record<string, TransformFn>;
216
305
 
306
+ /**
307
+ * @statedelta-libs/expressions - JIT Compiler
308
+ *
309
+ * IR-based pipeline: receives IR from analyze(), emits optimized JS via AST.
310
+ * Factory with flat parameters — zero destructuring, zero property chains.
311
+ * No DSL knowledge — no guards, no type detection.
312
+ */
313
+
314
+ /** Result when returnCode is true */
315
+ interface JITCodeResult {
316
+ code: string;
317
+ deps: string[];
318
+ hash: string;
319
+ dataRoots: string[];
320
+ scopeFns: string[];
321
+ paramNames: string[];
322
+ }
323
+
324
+ /**
325
+ * @statedelta-libs/expressions - Cache
326
+ *
327
+ * LRU cache for compiled expressions and compile artifacts.
328
+ * Performance: cache hit ~10M ops/sec
329
+ */
330
+
331
+ /**
332
+ * Shared compile cache — stores context-free CompileArtifacts.
333
+ * Can be shared between ExpressionCompiler instances for multi-tenant scenarios.
334
+ * Dual mode: separate closures and JIT caches.
335
+ */
336
+ declare class CompileCache {
337
+ private closures;
338
+ private jit;
339
+ constructor(maxSize?: number);
340
+ /**
341
+ * Get or compile artifact by key and mode.
342
+ */
343
+ getOrCompile<V>(key: string, mode: "closures" | "jit", factory: () => V): V;
344
+ /**
345
+ * Cache size per mode
346
+ */
347
+ get size(): {
348
+ closures: number;
349
+ jit: number;
350
+ };
351
+ /**
352
+ * Clear both caches
353
+ */
354
+ clear(): void;
355
+ }
356
+
217
357
  /**
218
358
  * @statedelta-libs/expressions - ExpressionCompiler
219
359
  *
220
360
  * Unified public API for expression compilation.
221
- * Centralizes scope, accessor, and cache in a single reusable instance.
361
+ * Phase 4: two-level cache (instance + shared compile), DI-first pattern.
222
362
  */
223
363
 
224
364
  interface ExpressionCompilerOptions<T = unknown> {
225
365
  /** Functions available to $fn expressions */
226
366
  scope?: Scope;
227
367
  /** Custom accessor for resolving paths */
228
- accessor?: AccessorFn<T>;
368
+ accessor?: AccessorFn;
229
369
  /** Max LRU cache size per mode (default: 1000) */
230
370
  cacheSize?: number;
231
371
  /** Boundary definitions for compile-time interception */
232
372
  boundaries?: BoundaryDef<T>[];
373
+ /** Handler namespaces — services with injected context */
374
+ handlers?: HandlersMap;
375
+ /** Shared compile cache — enables cross-instance cache sharing */
376
+ compileCache?: CompileCache;
233
377
  }
234
378
  interface JITOptions {
235
379
  /** Use accessor to resolve paths instead of direct access */
@@ -237,34 +381,84 @@ interface JITOptions {
237
381
  /** Prefix for paths that bypass accessor (direct access) */
238
382
  lexicalPrefix?: string;
239
383
  }
384
+ interface CompileConfig {
385
+ /** Use accessor to resolve paths instead of direct access */
386
+ useAccessor?: boolean;
387
+ }
388
+ interface EvalOptions {
389
+ /** Pipeline: "closures" (default) or "jit" */
390
+ mode?: "closures" | "jit";
391
+ /** Use accessor to resolve paths instead of direct access */
392
+ useAccessor?: boolean;
393
+ }
394
+ /**
395
+ * Context available via `this` inside every handler (bound via .bind()).
396
+ * Created once per ExpressionCompiler instance — zero per-call overhead.
397
+ */
398
+ interface HandlerContext<T = unknown> {
399
+ /** Custom accessor for resolving paths */
400
+ accessor?: AccessorFn;
401
+ /** All wrapped handlers — handlers can call other handlers */
402
+ handlers: WrappedHandlers;
403
+ /** Compiler instance — can compile sub-expressions */
404
+ compiler: ExpressionCompiler<T>;
405
+ /** Pure scope functions */
406
+ scope: Scope;
407
+ }
240
408
  declare class ExpressionCompiler<T = unknown> {
241
409
  private readonly scope;
242
410
  private readonly accessor?;
243
411
  private readonly boundaries?;
412
+ private readonly wrappedHandlers?;
413
+ private readonly compileCache?;
244
414
  private readonly cacheClosures;
245
415
  private readonly cacheJIT;
246
416
  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;
417
+ /** Compile via closure composition DSL → IR → closures */
418
+ compile<R = unknown>(expr: Expression, opts?: CompileConfig): CompileArtifact<T, R>;
419
+ /** Compile via JS code generation (JIT) — DSL → IR → AST → JS */
420
+ jit<R = unknown>(expr: Expression, opts?: JITOptions): CompileArtifact<T, R>;
421
+ /** Return generated JS code without creating function — for inspection/testing */
422
+ jitCode(expr: Expression, opts?: JITOptions): JITCodeResult;
423
+ /** Compile, bind with default context, and execute in one step */
424
+ eval<R = unknown>(expr: Expression, data: T, opts?: EvalOptions): R;
425
+ /**
426
+ * Wrap raw handlers with ctx via .bind().
427
+ * Creates ctx once, binds each handler method once.
428
+ * Resolves circular reference: ctx.handlers = wrappedHandlers.
429
+ * Handlers access ctx via `this`.
430
+ */
431
+ private wrapHandlers;
255
432
  /** Normalize custom expression to pure DSL */
256
433
  normalize(expr: Expression, transforms: Transforms): Expression;
257
434
  /** Extract dependencies without compiling */
258
435
  extractDeps(expr: Expression): string[];
259
- /** Cache size per mode */
436
+ /** Instance cache size per mode */
260
437
  get cacheSize(): {
261
438
  closures: number;
262
439
  jit: number;
263
440
  };
264
- /** Clear both caches */
441
+ /** Clear instance caches */
265
442
  clearCache(): void;
266
443
  /** Read-only access to scope */
267
444
  getScope(): Readonly<Scope>;
445
+ /**
446
+ * Get or create compile artifact.
447
+ * Two-level: compile cache (shared, optional) → fresh compile.
448
+ */
449
+ private getArtifact;
450
+ /**
451
+ * Full compile: analyze → backend → CompileArtifact.
452
+ */
453
+ private doCompile;
454
+ /**
455
+ * Default BindContext from constructor options.
456
+ */
457
+ private defaultCtx;
458
+ /**
459
+ * Build compile cache key including compile-time flags.
460
+ */
461
+ private compileCacheKey;
268
462
  }
269
463
 
270
464
  /**
@@ -465,6 +659,10 @@ declare const isConditionGroup: (v: unknown) => v is ConditionGroup;
465
659
  * Check if value is any condition expression
466
660
  */
467
661
  declare const isConditionExpr: (v: unknown) => v is ConditionExpr;
662
+ /**
663
+ * Check if value is any Expression DSL node
664
+ */
665
+ declare const isExpression: (v: unknown) => boolean;
468
666
  /**
469
667
  * Check if value is a literal (not an expression object)
470
668
  */
@@ -484,6 +682,8 @@ interface ValidateOptions {
484
682
  scope?: Scope;
485
683
  /** Boundary definitions — matched nodes skip validation */
486
684
  boundaries?: BoundaryDef[];
685
+ /** Handlers map to validate handler names against (optional) */
686
+ handlers?: HandlersMap;
487
687
  }
488
688
  /**
489
689
  * Validate expression structure
@@ -550,6 +750,93 @@ declare function hasDeps(expr: Expression): boolean;
550
750
  */
551
751
  declare function isPure(expr: Expression): boolean;
552
752
 
753
+ /**
754
+ * Template Compiler — Types
755
+ *
756
+ * Declarative template system for compiling arbitrary JSON structures
757
+ * using ExpressionCompiler as the engine.
758
+ */
759
+
760
+ type SchemaType = "string" | "number" | "boolean" | "string[]" | "number[]" | "enum" | "schema" | "object" | "any";
761
+ interface SchemaFieldDef {
762
+ type: SchemaType;
763
+ required?: boolean;
764
+ default?: unknown;
765
+ values?: string[];
766
+ }
767
+ interface FieldHandlerOptions {
768
+ allow?: string[];
769
+ deny?: string[];
770
+ transforms?: Transforms;
771
+ mode?: "closures" | "jit";
772
+ }
773
+ type FieldHandler = (value: unknown, compiler: ExpressionCompiler, options: FieldHandlerOptions) => unknown;
774
+ type CompileFieldDef = {
775
+ compile: "condition";
776
+ } | {
777
+ compile: "expression";
778
+ } | {
779
+ compile: string;
780
+ allow?: string[];
781
+ deny?: string[];
782
+ } | {
783
+ compile: FieldHandler;
784
+ allow?: string[];
785
+ deny?: string[];
786
+ [key: string]: unknown;
787
+ };
788
+ type FieldDef = CompileFieldDef | SchemaFieldDef;
789
+ type DefinitionTemplate = Record<string, FieldDef>;
790
+ interface CompileDefinitionOptions {
791
+ transforms?: Transforms;
792
+ mode?: "closures" | "jit";
793
+ validate?: boolean;
794
+ handlers?: Record<string, FieldHandler>;
795
+ }
796
+ interface CompiledDefinition<T = unknown> {
797
+ [field: string]: unknown;
798
+ /** Bind to a context — re-binds all compiled fields */
799
+ bind(ctx: BindContext): CompiledDefinition<T>;
800
+ }
801
+ type DefinitionValidationErrorType = "unknown_field" | "missing_required" | "type_mismatch" | "invalid_enum";
802
+ interface DefinitionValidationError {
803
+ field: string;
804
+ message: string;
805
+ type: DefinitionValidationErrorType;
806
+ }
807
+ interface DefinitionValidationResult {
808
+ valid: boolean;
809
+ errors: DefinitionValidationError[];
810
+ }
811
+ declare function isCompileField(def: FieldDef): def is CompileFieldDef;
812
+ declare function isSchemaField(def: FieldDef): def is SchemaFieldDef;
813
+ declare function isNativeCompile(def: CompileFieldDef): def is {
814
+ compile: "condition";
815
+ } | {
816
+ compile: "expression";
817
+ };
818
+ declare function isHandlerCompile(def: CompileFieldDef): def is {
819
+ compile: FieldHandler;
820
+ [key: string]: unknown;
821
+ };
822
+
823
+ /**
824
+ * Template Compiler — Core Compilation
825
+ *
826
+ * compileDefinition(): validates, normalizes, compiles, and assembles
827
+ * a JSON structure using a template + ExpressionCompiler.
828
+ */
829
+
830
+ declare function compileDefinition<T = unknown>(dsl: Record<string, unknown>, template: DefinitionTemplate, compiler: ExpressionCompiler, options?: CompileDefinitionOptions): CompiledDefinition<T>;
831
+
832
+ /**
833
+ * Template Compiler — Validation
834
+ *
835
+ * Validates a DSL object against a DefinitionTemplate.
836
+ */
837
+
838
+ declare function validateDefinition(dsl: Record<string, unknown>, template: DefinitionTemplate): DefinitionValidationResult;
839
+
553
840
  /**
554
841
  * @statedelta-libs/expressions
555
842
  *
@@ -558,4 +845,4 @@ declare function isPure(expr: Expression): boolean;
558
845
  */
559
846
  declare const VERSION = "3.0.0";
560
847
 
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 };
848
+ export { type AccessorFn, type ArrowExpr, type Artifact, type BindContext, type BoundFn, type BoundaryDef, type CompileArtifact, CompileCache, type CompileConfig, type CompileDefinitionOptions, type CompileFieldDef, type CompileOptions, type CompiledDefinition, type CompiledFn, type Condition, type ConditionExpr, type ConditionGroup, type ConditionOp, type ConditionalExpr, type DefinitionTemplate, type DefinitionValidationError, type DefinitionValidationErrorType, type DefinitionValidationResult, type EvalOptions, type Expression, ExpressionCompiler, type ExpressionCompilerOptions, type FieldDef, type FieldHandler, type FieldHandlerOptions, type FnExpr, type HandlerContext, type HandlerFn, type HandlersMap, type JITCodeResult, type JITOptions, type Literal, type ParamSlot, type PipeExpr, type RefExpr, type SchemaFieldDef, type SchemaType, type Scope, type TransformFn, type Transforms, VERSION, type ValidateOptions, type ValidationResult, type WrappedHandlers, assertValid, builders, compileDefinition, compilePath, createBoundFn, extractDeps, get, hasDeps, hasWildcard, isArrow, isCompileField, isCondition, isConditionExpr, isConditionGroup, isConditional, isExpression, isFn, isHandlerCompile, isLiteral, isNativeCompile, isPipe, isPure, isRef, isSchemaField, isValid, normalizePath, validate, validateDefinition };