@revisium/formula 0.3.0 → 0.5.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.
@@ -0,0 +1,1453 @@
1
+ 'use strict';
2
+
3
+ var ohm = require('ohm-js');
4
+
5
+ function _interopNamespace(e) {
6
+ if (e && e.__esModule) return e;
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var ohm__namespace = /*#__PURE__*/_interopNamespace(ohm);
24
+
25
+ // src/ohm/grammar/index.ts
26
+ var grammarText = String.raw`Formula {
27
+ Expression = Ternary
28
+
29
+ // Ternary: condition ? then : else
30
+ Ternary
31
+ = LogicalOr "?" Ternary ":" Ternary -- ternary
32
+ | LogicalOr
33
+
34
+ // Logical OR: a || b
35
+ LogicalOr
36
+ = LogicalOr "||" LogicalAnd -- or
37
+ | LogicalAnd
38
+
39
+ // Logical AND: a && b
40
+ LogicalAnd
41
+ = LogicalAnd "&&" Equality -- and
42
+ | Equality
43
+
44
+ // Equality: a == b, a != b
45
+ Equality
46
+ = Equality "==" Comparison -- eq
47
+ | Equality "!=" Comparison -- neq
48
+ | Comparison
49
+
50
+ // Comparison: a > b, a < b, a >= b, a <= b
51
+ Comparison
52
+ = Comparison ">=" Additive -- gte
53
+ | Comparison "<=" Additive -- lte
54
+ | Comparison ">" Additive -- gt
55
+ | Comparison "<" Additive -- lt
56
+ | Additive
57
+
58
+ // Additive: a + b, a - b
59
+ Additive
60
+ = Additive "+" Multiplicative -- plus
61
+ | Additive "-" Multiplicative -- minus
62
+ | Multiplicative
63
+
64
+ // Multiplicative: a * b, a / b, a % b
65
+ Multiplicative
66
+ = Multiplicative "*" Unary -- times
67
+ | Multiplicative "/" Unary -- div
68
+ | Multiplicative "%" Unary -- mod
69
+ | Unary
70
+
71
+ // Unary: -a, !a
72
+ Unary
73
+ = "-" Unary -- neg
74
+ | "!" Unary -- not
75
+ | Postfix
76
+
77
+ // Postfix: function calls, property access, array access
78
+ Postfix
79
+ = Postfix "(" Arguments? ")" -- call
80
+ | Postfix "." identifier -- property
81
+ | Postfix "[" Expression "]" -- index
82
+ | Postfix "[" "*" "]" -- wildcard
83
+ | Primary
84
+
85
+ // Arguments for function calls
86
+ Arguments
87
+ = Expression ("," Expression)*
88
+
89
+ // Primary expressions
90
+ Primary
91
+ = "(" Expression ")" -- paren
92
+ | number
93
+ | string
94
+ | boolean
95
+ | null
96
+ | rootPath
97
+ | relativePath
98
+ | contextToken
99
+ | identifier
100
+
101
+ // Literals
102
+ number
103
+ = "-"? digit+ "." digit+ -- float
104
+ | "-"? digit+ -- int
105
+
106
+ string
107
+ = "\"" doubleStringChar* "\""
108
+ | "'" singleStringChar* "'"
109
+
110
+ doubleStringChar
111
+ = ~("\"" | "\\") any -- regular
112
+ | "\\" any -- escape
113
+
114
+ singleStringChar
115
+ = ~("'" | "\\") any -- regular
116
+ | "\\" any -- escape
117
+
118
+ boolean
119
+ = "true" ~identifierPart -- true
120
+ | "false" ~identifierPart -- false
121
+
122
+ null = "null" ~identifierPart
123
+
124
+ // Identifiers and paths
125
+ identifier = ~reserved identifierStart identifierPart*
126
+
127
+ identifierStart = letter | "_"
128
+ identifierPart = letter | digit | "_"
129
+
130
+ // Special paths
131
+ rootPath = "/" identifierPart+
132
+
133
+ relativePathPrefix = ".." "/"
134
+ relativePath = relativePathPrefix+ identifierPart+
135
+
136
+ contextToken
137
+ = "@" identifierPart+ -- at
138
+ | "#" identifierPart+ -- hash
139
+
140
+ // Reserved words (cannot be used as identifiers)
141
+ reserved
142
+ = ("true" | "false" | "null") ~identifierPart
143
+
144
+ // Whitespace (implicit, Ohm handles automatically)
145
+ }`;
146
+ var grammar2 = ohm__namespace.grammar(grammarText);
147
+
148
+ // src/ohm/semantics/index.ts
149
+ function childrenToAST(children) {
150
+ return children.filter((c) => "toAST" in c).map((c) => c.toAST());
151
+ }
152
+ function childrenDependencies(children) {
153
+ return children.filter((c) => "dependencies" in c).flatMap((c) => c.dependencies());
154
+ }
155
+ function childrenFeatures(children) {
156
+ return children.filter((c) => "features" in c).flatMap((c) => c.features());
157
+ }
158
+ var semantics = grammar2.createSemantics();
159
+ semantics.addOperation("toAST", {
160
+ Expression(e) {
161
+ return e.toAST();
162
+ },
163
+ Ternary_ternary(cond, _q, cons, _c, alt) {
164
+ return {
165
+ type: "TernaryOp",
166
+ condition: cond.toAST(),
167
+ consequent: cons.toAST(),
168
+ alternate: alt.toAST()
169
+ };
170
+ },
171
+ Ternary(e) {
172
+ return e.toAST();
173
+ },
174
+ LogicalOr_or(left, _op, right) {
175
+ return {
176
+ type: "BinaryOp",
177
+ op: "||",
178
+ left: left.toAST(),
179
+ right: right.toAST()
180
+ };
181
+ },
182
+ LogicalOr(e) {
183
+ return e.toAST();
184
+ },
185
+ LogicalAnd_and(left, _op, right) {
186
+ return {
187
+ type: "BinaryOp",
188
+ op: "&&",
189
+ left: left.toAST(),
190
+ right: right.toAST()
191
+ };
192
+ },
193
+ LogicalAnd(e) {
194
+ return e.toAST();
195
+ },
196
+ Equality_eq(left, _op, right) {
197
+ return {
198
+ type: "BinaryOp",
199
+ op: "==",
200
+ left: left.toAST(),
201
+ right: right.toAST()
202
+ };
203
+ },
204
+ Equality_neq(left, _op, right) {
205
+ return {
206
+ type: "BinaryOp",
207
+ op: "!=",
208
+ left: left.toAST(),
209
+ right: right.toAST()
210
+ };
211
+ },
212
+ Equality(e) {
213
+ return e.toAST();
214
+ },
215
+ Comparison_gte(left, _op, right) {
216
+ return {
217
+ type: "BinaryOp",
218
+ op: ">=",
219
+ left: left.toAST(),
220
+ right: right.toAST()
221
+ };
222
+ },
223
+ Comparison_lte(left, _op, right) {
224
+ return {
225
+ type: "BinaryOp",
226
+ op: "<=",
227
+ left: left.toAST(),
228
+ right: right.toAST()
229
+ };
230
+ },
231
+ Comparison_gt(left, _op, right) {
232
+ return {
233
+ type: "BinaryOp",
234
+ op: ">",
235
+ left: left.toAST(),
236
+ right: right.toAST()
237
+ };
238
+ },
239
+ Comparison_lt(left, _op, right) {
240
+ return {
241
+ type: "BinaryOp",
242
+ op: "<",
243
+ left: left.toAST(),
244
+ right: right.toAST()
245
+ };
246
+ },
247
+ Comparison(e) {
248
+ return e.toAST();
249
+ },
250
+ Additive_plus(left, _op, right) {
251
+ return {
252
+ type: "BinaryOp",
253
+ op: "+",
254
+ left: left.toAST(),
255
+ right: right.toAST()
256
+ };
257
+ },
258
+ Additive_minus(left, _op, right) {
259
+ return {
260
+ type: "BinaryOp",
261
+ op: "-",
262
+ left: left.toAST(),
263
+ right: right.toAST()
264
+ };
265
+ },
266
+ Additive(e) {
267
+ return e.toAST();
268
+ },
269
+ Multiplicative_times(left, _op, right) {
270
+ return {
271
+ type: "BinaryOp",
272
+ op: "*",
273
+ left: left.toAST(),
274
+ right: right.toAST()
275
+ };
276
+ },
277
+ Multiplicative_div(left, _op, right) {
278
+ return {
279
+ type: "BinaryOp",
280
+ op: "/",
281
+ left: left.toAST(),
282
+ right: right.toAST()
283
+ };
284
+ },
285
+ Multiplicative_mod(left, _op, right) {
286
+ return {
287
+ type: "BinaryOp",
288
+ op: "%",
289
+ left: left.toAST(),
290
+ right: right.toAST()
291
+ };
292
+ },
293
+ Multiplicative(e) {
294
+ return e.toAST();
295
+ },
296
+ Unary_neg(_op, expr) {
297
+ return { type: "UnaryOp", op: "-", argument: expr.toAST() };
298
+ },
299
+ Unary_not(_op, expr) {
300
+ return { type: "UnaryOp", op: "!", argument: expr.toAST() };
301
+ },
302
+ Unary(e) {
303
+ return e.toAST();
304
+ },
305
+ Postfix_call(callee, _lp, args, _rp) {
306
+ const firstArg = args.children[0];
307
+ const argList = firstArg ? firstArg.toAST() : [];
308
+ return {
309
+ type: "CallExpression",
310
+ callee: callee.toAST(),
311
+ arguments: Array.isArray(argList) ? argList : [argList]
312
+ };
313
+ },
314
+ Postfix_property(obj, _dot, prop) {
315
+ return {
316
+ type: "MemberExpression",
317
+ object: obj.toAST(),
318
+ property: prop.sourceString
319
+ };
320
+ },
321
+ Postfix_index(obj, _lb, index, _rb) {
322
+ return {
323
+ type: "IndexExpression",
324
+ object: obj.toAST(),
325
+ index: index.toAST()
326
+ };
327
+ },
328
+ Postfix_wildcard(obj, _lb, _star, _rb) {
329
+ return {
330
+ type: "WildcardExpression",
331
+ object: obj.toAST()
332
+ };
333
+ },
334
+ Postfix(e) {
335
+ return e.toAST();
336
+ },
337
+ // Returns an array of arguments, not a single ASTNode
338
+ // @ts-expect-error - intentionally returns array for function arguments
339
+ Arguments(first, _comma, rest) {
340
+ return [first.toAST(), ...rest.children.map((c) => c.toAST())];
341
+ },
342
+ Primary_paren(_lp, expr, _rp) {
343
+ return expr.toAST();
344
+ },
345
+ Primary(e) {
346
+ return e.toAST();
347
+ },
348
+ number_float(_neg, _int, _dot, _frac) {
349
+ return {
350
+ type: "NumberLiteral",
351
+ value: Number.parseFloat(this.sourceString)
352
+ };
353
+ },
354
+ number_int(_neg, _digits) {
355
+ return {
356
+ type: "NumberLiteral",
357
+ value: Number.parseInt(this.sourceString, 10)
358
+ };
359
+ },
360
+ string(_open, chars, _close) {
361
+ const raw = chars.sourceString;
362
+ return { type: "StringLiteral", value: raw.replaceAll(/\\(.)/g, "$1") };
363
+ },
364
+ boolean_true(_) {
365
+ return { type: "BooleanLiteral", value: true };
366
+ },
367
+ boolean_false(_) {
368
+ return { type: "BooleanLiteral", value: false };
369
+ },
370
+ null(_) {
371
+ return { type: "NullLiteral" };
372
+ },
373
+ identifier(_start, _rest) {
374
+ return { type: "Identifier", name: this.sourceString };
375
+ },
376
+ rootPath(_slash, _path) {
377
+ return { type: "RootPath", path: this.sourceString };
378
+ },
379
+ relativePath(_dotSlashes, _parts) {
380
+ return { type: "RelativePath", path: this.sourceString };
381
+ },
382
+ contextToken_at(_at, _name) {
383
+ return { type: "ContextToken", name: this.sourceString };
384
+ },
385
+ contextToken_hash(_hash, _name) {
386
+ return { type: "ContextToken", name: this.sourceString };
387
+ },
388
+ // @ts-expect-error - _iter returns array for iteration nodes
389
+ _iter(...children) {
390
+ return childrenToAST(children);
391
+ },
392
+ _terminal() {
393
+ return { type: "Identifier", name: this.sourceString };
394
+ }
395
+ });
396
+ semantics.addOperation("dependencies", {
397
+ identifier(_start, _rest) {
398
+ return [this.sourceString];
399
+ },
400
+ rootPath(_slash, _path) {
401
+ return [this.sourceString];
402
+ },
403
+ relativePath(_dotSlashes, _parts) {
404
+ return [this.sourceString];
405
+ },
406
+ contextToken_at(_at, _name) {
407
+ return [];
408
+ },
409
+ contextToken_hash(_hash, _name) {
410
+ return [];
411
+ },
412
+ Postfix_property(obj, _dot, prop) {
413
+ const objDeps = obj.dependencies();
414
+ if (objDeps.length === 1) {
415
+ return [`${objDeps[0]}.${prop.sourceString}`];
416
+ }
417
+ return objDeps;
418
+ },
419
+ Postfix_index(obj, _lb, index, _rb) {
420
+ const objDeps = obj.dependencies();
421
+ const indexNode = index.toAST();
422
+ const getNumericIndex = (node) => {
423
+ if (node.type === "NumberLiteral") {
424
+ return node.value;
425
+ }
426
+ if (node.type === "UnaryOp" && node.op === "-" && node.argument.type === "NumberLiteral") {
427
+ return -node.argument.value;
428
+ }
429
+ return null;
430
+ };
431
+ const numericIndex = getNumericIndex(indexNode);
432
+ if (objDeps.length === 1 && numericIndex !== null) {
433
+ return [`${objDeps[0]}[${numericIndex}]`];
434
+ }
435
+ return [...objDeps, ...index.dependencies()];
436
+ },
437
+ Postfix_wildcard(obj, _lb, _star, _rb) {
438
+ const objDeps = obj.dependencies();
439
+ if (objDeps.length === 1) {
440
+ return [`${objDeps[0]}[*]`];
441
+ }
442
+ return objDeps;
443
+ },
444
+ Postfix_call(callee, _lp, args, _rp) {
445
+ const calleeDeps = callee.dependencies();
446
+ const calleeAST = callee.toAST();
447
+ const argDeps = childrenDependencies(args.children);
448
+ if (calleeAST.type === "Identifier") {
449
+ return argDeps;
450
+ }
451
+ return [...calleeDeps, ...argDeps];
452
+ },
453
+ number_float(_neg, _int, _dot, _frac) {
454
+ return [];
455
+ },
456
+ number_int(_neg, _digits) {
457
+ return [];
458
+ },
459
+ string(_open, _chars, _close) {
460
+ return [];
461
+ },
462
+ boolean_true(_) {
463
+ return [];
464
+ },
465
+ boolean_false(_) {
466
+ return [];
467
+ },
468
+ null(_) {
469
+ return [];
470
+ },
471
+ _nonterminal(...children) {
472
+ return childrenDependencies(children);
473
+ },
474
+ _iter(...children) {
475
+ return childrenDependencies(children);
476
+ },
477
+ _terminal() {
478
+ return [];
479
+ }
480
+ });
481
+ var ARRAY_FUNCTIONS = /* @__PURE__ */ new Set([
482
+ "sum",
483
+ "avg",
484
+ "count",
485
+ "first",
486
+ "last",
487
+ "join",
488
+ "includes"
489
+ ]);
490
+ semantics.addOperation("features", {
491
+ rootPath(_slash, _path) {
492
+ const path = this.sourceString;
493
+ const features = ["root_path"];
494
+ if (path.includes(".")) {
495
+ features.push("nested_path");
496
+ }
497
+ return features;
498
+ },
499
+ relativePath(_dotSlashes, _parts) {
500
+ const path = this.sourceString;
501
+ const features = ["relative_path"];
502
+ const withoutPrefix = path.replace(/^(\.\.\/)+/, "");
503
+ if (withoutPrefix.includes(".")) {
504
+ features.push("nested_path");
505
+ }
506
+ return features;
507
+ },
508
+ contextToken_at(_at, _name) {
509
+ return ["context_token"];
510
+ },
511
+ contextToken_hash(_hash, _name) {
512
+ return ["context_token"];
513
+ },
514
+ Postfix_property(obj, _dot, _prop) {
515
+ const objFeatures = obj.features();
516
+ return [...objFeatures, "nested_path"];
517
+ },
518
+ Postfix_index(obj, _lb, index, _rb) {
519
+ const objFeatures = obj.features();
520
+ const indexFeatures = index.features();
521
+ return [...objFeatures, ...indexFeatures, "array_index"];
522
+ },
523
+ Postfix_wildcard(obj, _lb, _star, _rb) {
524
+ const objFeatures = obj.features();
525
+ return [...objFeatures, "array_wildcard"];
526
+ },
527
+ Postfix_call(callee, _lp, args, _rp) {
528
+ const calleeAST = callee.toAST();
529
+ const argFeatures = childrenFeatures(args.children);
530
+ if (calleeAST.type === "Identifier" && ARRAY_FUNCTIONS.has(calleeAST.name.toLowerCase())) {
531
+ return [...argFeatures, "array_function"];
532
+ }
533
+ return argFeatures;
534
+ },
535
+ _nonterminal(...children) {
536
+ return childrenFeatures(children);
537
+ },
538
+ _iter(...children) {
539
+ return childrenFeatures(children);
540
+ },
541
+ _terminal() {
542
+ return [];
543
+ }
544
+ });
545
+ var BUILTINS = {
546
+ // Logical
547
+ and: (a, b) => Boolean(a) && Boolean(b),
548
+ or: (a, b) => Boolean(a) || Boolean(b),
549
+ not: (a) => !a,
550
+ // String
551
+ concat: (...args) => args.map(String).join(""),
552
+ upper: (s) => String(s).toUpperCase(),
553
+ lower: (s) => String(s).toLowerCase(),
554
+ trim: (s) => String(s).trim(),
555
+ left: (s, n) => String(s).slice(0, Math.max(0, Math.floor(Number(n)))),
556
+ right: (s, n) => {
557
+ const str = String(s);
558
+ const count = Math.max(0, Math.floor(Number(n)));
559
+ return count === 0 ? "" : str.slice(-count);
560
+ },
561
+ replace: (s, search, repl) => String(s).replace(String(search), String(repl)),
562
+ tostring: String,
563
+ length: (s) => {
564
+ if (Array.isArray(s)) return s.length;
565
+ if (typeof s === "string") return s.length;
566
+ if (s !== null && typeof s === "object") return Object.keys(s).length;
567
+ return String(s).length;
568
+ },
569
+ contains: (s, search) => String(s).includes(String(search)),
570
+ startswith: (s, search) => String(s).startsWith(String(search)),
571
+ endswith: (s, search) => String(s).endsWith(String(search)),
572
+ join: (arr, sep) => {
573
+ if (!Array.isArray(arr)) return "";
574
+ if (sep === void 0 || sep === null) return arr.join(",");
575
+ if (typeof sep !== "string" && typeof sep !== "number") {
576
+ return arr.join(",");
577
+ }
578
+ return arr.join(String(sep));
579
+ },
580
+ // Numeric
581
+ tonumber: Number,
582
+ toboolean: Boolean,
583
+ isnull: (v) => v === null || v === void 0,
584
+ coalesce: (...args) => args.find((v) => v !== null && v !== void 0) ?? null,
585
+ round: (n, dec) => {
586
+ const factor = 10 ** (dec === void 0 ? 0 : Number(dec));
587
+ return Math.round(Number(n) * factor) / factor;
588
+ },
589
+ floor: (n) => Math.floor(Number(n)),
590
+ ceil: (n) => Math.ceil(Number(n)),
591
+ abs: (n) => Math.abs(Number(n)),
592
+ sqrt: (n) => Math.sqrt(Number(n)),
593
+ pow: (base, exp) => Math.pow(Number(base), Number(exp)),
594
+ min: (...args) => args.length === 0 ? Number.NaN : Math.min(...args.map(Number)),
595
+ max: (...args) => args.length === 0 ? Number.NaN : Math.max(...args.map(Number)),
596
+ log: (n) => Math.log(Number(n)),
597
+ log10: (n) => Math.log10(Number(n)),
598
+ exp: (n) => Math.exp(Number(n)),
599
+ sign: (n) => Math.sign(Number(n)),
600
+ // Array
601
+ sum: (arr) => Array.isArray(arr) ? arr.reduce((a, b) => a + Number(b), 0) : 0,
602
+ avg: (arr) => Array.isArray(arr) && arr.length > 0 ? arr.reduce((a, b) => a + Number(b), 0) / arr.length : 0,
603
+ count: (arr) => Array.isArray(arr) ? arr.length : 0,
604
+ first: (arr) => Array.isArray(arr) ? arr[0] : void 0,
605
+ last: (arr) => Array.isArray(arr) ? arr.at(-1) : void 0,
606
+ includes: (arr, val) => Array.isArray(arr) ? arr.includes(val) : false,
607
+ // Conditional
608
+ if: (cond, ifTrue, ifFalse) => cond ? ifTrue : ifFalse
609
+ };
610
+ function getByPath(obj, path) {
611
+ const parts = path.split(".");
612
+ let current = obj;
613
+ for (const part of parts) {
614
+ if (current === null || current === void 0) return void 0;
615
+ current = current[part];
616
+ }
617
+ return current;
618
+ }
619
+ semantics.addOperation("eval(ctx)", {
620
+ Expression(e) {
621
+ return e.eval(this.args.ctx);
622
+ },
623
+ Ternary_ternary(cond, _q, cons, _c, alt) {
624
+ return cond.eval(this.args.ctx) ? cons.eval(this.args.ctx) : alt.eval(this.args.ctx);
625
+ },
626
+ Ternary(e) {
627
+ return e.eval(this.args.ctx);
628
+ },
629
+ LogicalOr_or(left, _op, right) {
630
+ return left.eval(this.args.ctx) || right.eval(this.args.ctx);
631
+ },
632
+ LogicalOr(e) {
633
+ return e.eval(this.args.ctx);
634
+ },
635
+ LogicalAnd_and(left, _op, right) {
636
+ return left.eval(this.args.ctx) && right.eval(this.args.ctx);
637
+ },
638
+ LogicalAnd(e) {
639
+ return e.eval(this.args.ctx);
640
+ },
641
+ Equality_eq(left, _op, right) {
642
+ return left.eval(this.args.ctx) == right.eval(this.args.ctx);
643
+ },
644
+ Equality_neq(left, _op, right) {
645
+ return left.eval(this.args.ctx) != right.eval(this.args.ctx);
646
+ },
647
+ Equality(e) {
648
+ return e.eval(this.args.ctx);
649
+ },
650
+ Comparison_gte(left, _op, right) {
651
+ return left.eval(this.args.ctx) >= right.eval(this.args.ctx);
652
+ },
653
+ Comparison_lte(left, _op, right) {
654
+ return left.eval(this.args.ctx) <= right.eval(this.args.ctx);
655
+ },
656
+ Comparison_gt(left, _op, right) {
657
+ return left.eval(this.args.ctx) > right.eval(this.args.ctx);
658
+ },
659
+ Comparison_lt(left, _op, right) {
660
+ return left.eval(this.args.ctx) < right.eval(this.args.ctx);
661
+ },
662
+ Comparison(e) {
663
+ return e.eval(this.args.ctx);
664
+ },
665
+ Additive_plus(left, _op, right) {
666
+ const l = left.eval(this.args.ctx);
667
+ const r = right.eval(this.args.ctx);
668
+ if (typeof l === "string" || typeof r === "string") {
669
+ return String(l) + String(r);
670
+ }
671
+ return l + r;
672
+ },
673
+ Additive_minus(left, _op, right) {
674
+ return left.eval(this.args.ctx) - right.eval(this.args.ctx);
675
+ },
676
+ Additive(e) {
677
+ return e.eval(this.args.ctx);
678
+ },
679
+ Multiplicative_times(left, _op, right) {
680
+ return left.eval(this.args.ctx) * right.eval(this.args.ctx);
681
+ },
682
+ Multiplicative_div(left, _op, right) {
683
+ return left.eval(this.args.ctx) / right.eval(this.args.ctx);
684
+ },
685
+ Multiplicative_mod(left, _op, right) {
686
+ return left.eval(this.args.ctx) % right.eval(this.args.ctx);
687
+ },
688
+ Multiplicative(e) {
689
+ return e.eval(this.args.ctx);
690
+ },
691
+ Unary_neg(_op, expr) {
692
+ return -expr.eval(this.args.ctx);
693
+ },
694
+ Unary_not(_op, expr) {
695
+ return !expr.eval(this.args.ctx);
696
+ },
697
+ Unary(e) {
698
+ return e.eval(this.args.ctx);
699
+ },
700
+ Postfix_call(callee, _lp, args, _rp) {
701
+ const calleeAST = callee.toAST();
702
+ const getArgValues = () => {
703
+ const argsNode = args.children[0];
704
+ if (!argsNode) {
705
+ return [];
706
+ }
707
+ return argsNode.eval(this.args.ctx);
708
+ };
709
+ if (calleeAST.type === "Identifier") {
710
+ const fnName = calleeAST.name.toLowerCase();
711
+ const builtinFn = BUILTINS[fnName];
712
+ if (builtinFn) {
713
+ return builtinFn(...getArgValues());
714
+ }
715
+ }
716
+ const fn = callee.eval(this.args.ctx);
717
+ if (typeof fn === "function") {
718
+ return fn(...getArgValues());
719
+ }
720
+ const calleeName = calleeAST.type === "Identifier" ? calleeAST.name : "expression";
721
+ throw new Error(`'${calleeName}' is not a function`);
722
+ },
723
+ Postfix_property(obj, _dot, prop) {
724
+ const objVal = obj.eval(this.args.ctx);
725
+ return objVal?.[prop.sourceString];
726
+ },
727
+ Postfix_index(obj, _lb, index, _rb) {
728
+ const objVal = obj.eval(this.args.ctx);
729
+ const idx = index.eval(this.args.ctx);
730
+ if (idx < 0) {
731
+ return objVal?.[objVal.length + idx];
732
+ }
733
+ return objVal?.[idx];
734
+ },
735
+ Postfix_wildcard(obj, _lb, _star, _rb) {
736
+ return obj.eval(this.args.ctx);
737
+ },
738
+ Postfix(e) {
739
+ return e.eval(this.args.ctx);
740
+ },
741
+ Arguments(first, _comma, rest) {
742
+ return [
743
+ first.eval(this.args.ctx),
744
+ ...rest.children.map((c) => c.eval(this.args.ctx))
745
+ ];
746
+ },
747
+ Primary_paren(_lp, expr, _rp) {
748
+ return expr.eval(this.args.ctx);
749
+ },
750
+ Primary(e) {
751
+ return e.eval(this.args.ctx);
752
+ },
753
+ number_float(_neg, _int, _dot, _frac) {
754
+ return Number.parseFloat(this.sourceString);
755
+ },
756
+ number_int(_neg, _digits) {
757
+ return Number.parseInt(this.sourceString, 10);
758
+ },
759
+ string(_open, chars, _close) {
760
+ return chars.sourceString.replaceAll(/\\(.)/g, "$1");
761
+ },
762
+ boolean_true(_) {
763
+ return true;
764
+ },
765
+ boolean_false(_) {
766
+ return false;
767
+ },
768
+ null(_) {
769
+ return null;
770
+ },
771
+ identifier(_start, _rest) {
772
+ const name = this.sourceString;
773
+ return this.args.ctx[name];
774
+ },
775
+ rootPath(_slash, _path) {
776
+ const fullPath = this.sourceString;
777
+ if (fullPath in this.args.ctx) {
778
+ return this.args.ctx[fullPath];
779
+ }
780
+ const parts = fullPath.slice(1).split(".");
781
+ const firstPart = parts[0];
782
+ if (!firstPart) return void 0;
783
+ const rootKey = "/" + firstPart;
784
+ let value = this.args.ctx[rootKey];
785
+ for (let i = 1; i < parts.length; i++) {
786
+ if (value === null || value === void 0) return void 0;
787
+ const part = parts[i];
788
+ if (!part) continue;
789
+ value = value[part];
790
+ }
791
+ return value;
792
+ },
793
+ relativePath(_dotSlashes, _parts) {
794
+ const fullPath = this.sourceString;
795
+ if (fullPath in this.args.ctx) {
796
+ return this.args.ctx[fullPath];
797
+ }
798
+ const path = fullPath.replace(/^(\.\.\/)+/, "");
799
+ return getByPath(this.args.ctx, path);
800
+ },
801
+ contextToken_at(_at, _name) {
802
+ return this.args.ctx[this.sourceString];
803
+ },
804
+ contextToken_hash(_hash, _name) {
805
+ return this.args.ctx[this.sourceString];
806
+ },
807
+ _nonterminal(...children) {
808
+ const ctx = this.args.ctx;
809
+ for (const child of children) {
810
+ if ("eval" in child) {
811
+ return child.eval(ctx);
812
+ }
813
+ }
814
+ return void 0;
815
+ },
816
+ _iter(...children) {
817
+ return children.map((c) => c.eval(this.args.ctx));
818
+ },
819
+ _terminal() {
820
+ return void 0;
821
+ }
822
+ });
823
+
824
+ // src/ohm/core/parser.ts
825
+ function parseFormula(expression) {
826
+ const trimmed = expression.trim();
827
+ if (!trimmed) {
828
+ throw new Error("Empty expression");
829
+ }
830
+ const matchResult = grammar2.match(trimmed);
831
+ if (matchResult.failed()) {
832
+ throw new Error(matchResult.message ?? "Parse error");
833
+ }
834
+ const adapter = semantics(matchResult);
835
+ const ast = adapter.toAST();
836
+ const dependencies = [...new Set(adapter.dependencies())];
837
+ const allFeatures = adapter.features();
838
+ const features = [...new Set(allFeatures)];
839
+ const minVersion = features.length > 0 ? "1.1" : "1.0";
840
+ return { ast, dependencies, features, minVersion };
841
+ }
842
+ function validateSyntax(expression) {
843
+ const trimmed = expression.trim();
844
+ if (!trimmed) {
845
+ return { isValid: false, error: "Empty expression" };
846
+ }
847
+ const matchResult = grammar2.match(trimmed);
848
+ if (matchResult.failed()) {
849
+ const pos = matchResult.getRightmostFailurePosition?.();
850
+ return {
851
+ isValid: false,
852
+ error: matchResult.message ?? "Parse error",
853
+ position: pos
854
+ };
855
+ }
856
+ return { isValid: true };
857
+ }
858
+ function evaluate(expression, context) {
859
+ const trimmed = expression.trim();
860
+ if (!trimmed) {
861
+ throw new Error("Empty expression");
862
+ }
863
+ const matchResult = grammar2.match(trimmed);
864
+ if (matchResult.failed()) {
865
+ throw new Error(matchResult.message ?? "Parse error");
866
+ }
867
+ const safeContext = {};
868
+ for (const [key, value] of Object.entries(context)) {
869
+ if (typeof value !== "function") {
870
+ safeContext[key] = value;
871
+ }
872
+ }
873
+ return semantics(matchResult).eval(safeContext);
874
+ }
875
+ function getValueByPath(data, path) {
876
+ const segments = path.split(".");
877
+ let current = data;
878
+ for (const segment of segments) {
879
+ if (current === null || current === void 0) {
880
+ return void 0;
881
+ }
882
+ if (typeof current !== "object") {
883
+ return void 0;
884
+ }
885
+ current = current[segment];
886
+ }
887
+ return current;
888
+ }
889
+ function extractRootField(fieldPath) {
890
+ const dotIndex = fieldPath.indexOf(".");
891
+ const bracketIndex = fieldPath.indexOf("[");
892
+ if (dotIndex === -1 && bracketIndex === -1) {
893
+ return fieldPath;
894
+ }
895
+ if (dotIndex === -1) {
896
+ return fieldPath.slice(0, bracketIndex);
897
+ }
898
+ if (bracketIndex === -1) {
899
+ return fieldPath.slice(0, dotIndex);
900
+ }
901
+ return fieldPath.slice(0, Math.min(dotIndex, bracketIndex));
902
+ }
903
+ function buildPathReferences(rootData, dependencies) {
904
+ const refs = {};
905
+ for (const dep of dependencies) {
906
+ if (dep.startsWith("/")) {
907
+ const fieldPath = dep.slice(1);
908
+ const rootField = extractRootField(fieldPath);
909
+ const contextKey = "/" + rootField;
910
+ if (!(contextKey in refs)) {
911
+ refs[contextKey] = getValueByPath(rootData, rootField);
912
+ }
913
+ } else if (dep.startsWith("../")) {
914
+ const fieldPath = dep.slice(3);
915
+ refs[dep] = getValueByPath(rootData, fieldPath);
916
+ }
917
+ }
918
+ return refs;
919
+ }
920
+ function evaluateWithContext(expression, options) {
921
+ const { rootData, itemData } = options;
922
+ const trimmed = expression.trim();
923
+ if (!trimmed) {
924
+ throw new Error("Empty expression");
925
+ }
926
+ const parsed = parseFormula(trimmed);
927
+ const pathRefs = buildPathReferences(rootData, parsed.dependencies);
928
+ const context = {
929
+ ...rootData,
930
+ ...itemData,
931
+ ...pathRefs
932
+ };
933
+ return evaluate(trimmed, context);
934
+ }
935
+ var ARITHMETIC_OPS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%"]);
936
+ var COMPARISON_OPS = /* @__PURE__ */ new Set(["<", ">", "<=", ">=", "==", "!="]);
937
+ var LOGICAL_OPS = /* @__PURE__ */ new Set(["&&", "||", "!"]);
938
+ var NUMERIC_FUNCTIONS = /* @__PURE__ */ new Set([
939
+ "round",
940
+ "floor",
941
+ "ceil",
942
+ "abs",
943
+ "sqrt",
944
+ "pow",
945
+ "min",
946
+ "max",
947
+ "log",
948
+ "log10",
949
+ "exp",
950
+ "sign",
951
+ "sum",
952
+ "avg",
953
+ "count",
954
+ "tonumber",
955
+ "length"
956
+ ]);
957
+ var STRING_FUNCTIONS = /* @__PURE__ */ new Set([
958
+ "concat",
959
+ "upper",
960
+ "lower",
961
+ "trim",
962
+ "left",
963
+ "right",
964
+ "replace",
965
+ "tostring",
966
+ "join"
967
+ ]);
968
+ var BOOLEAN_FUNCTIONS = /* @__PURE__ */ new Set([
969
+ "and",
970
+ "or",
971
+ "not",
972
+ "contains",
973
+ "startswith",
974
+ "endswith",
975
+ "isnull",
976
+ "toboolean",
977
+ "includes"
978
+ ]);
979
+ function getFieldType(path, fieldTypes) {
980
+ const rootField = path.split(".")[0]?.split("[")[0] || path;
981
+ const schemaType = fieldTypes[rootField];
982
+ if (schemaType === "number") return "number";
983
+ if (schemaType === "string") return "string";
984
+ if (schemaType === "boolean") return "boolean";
985
+ return "unknown";
986
+ }
987
+ function inferLiteralType(node) {
988
+ switch (node.type) {
989
+ case "NumberLiteral":
990
+ return "number";
991
+ case "BooleanLiteral":
992
+ return "boolean";
993
+ case "StringLiteral":
994
+ return "string";
995
+ case "NullLiteral":
996
+ return "unknown";
997
+ default:
998
+ return null;
999
+ }
1000
+ }
1001
+ function inferBinaryOpType(node, fieldTypes) {
1002
+ const { op } = node;
1003
+ if (op === "+") {
1004
+ const leftType = inferTypeFromAST(node.left, fieldTypes);
1005
+ const rightType = inferTypeFromAST(node.right, fieldTypes);
1006
+ if (leftType === "string" || rightType === "string") return "string";
1007
+ if (leftType === "unknown" || rightType === "unknown") return "unknown";
1008
+ return "number";
1009
+ }
1010
+ if (ARITHMETIC_OPS.has(op)) return "number";
1011
+ if (COMPARISON_OPS.has(op)) return "boolean";
1012
+ if (LOGICAL_OPS.has(op)) return "boolean";
1013
+ return "unknown";
1014
+ }
1015
+ function inferCallExpressionType(node, fieldTypes) {
1016
+ const funcName = node.callee.type === "Identifier" ? node.callee.name.toLowerCase() : "";
1017
+ if (NUMERIC_FUNCTIONS.has(funcName)) return "number";
1018
+ if (STRING_FUNCTIONS.has(funcName)) return "string";
1019
+ if (BOOLEAN_FUNCTIONS.has(funcName)) return "boolean";
1020
+ if (funcName === "if" && node.arguments.length >= 3) {
1021
+ const thenArg = node.arguments[1];
1022
+ const elseArg = node.arguments[2];
1023
+ if (thenArg && elseArg) {
1024
+ const thenType = inferTypeFromAST(thenArg, fieldTypes);
1025
+ const elseType = inferTypeFromAST(elseArg, fieldTypes);
1026
+ if (thenType === elseType) return thenType;
1027
+ }
1028
+ }
1029
+ return "unknown";
1030
+ }
1031
+ function inferTypeFromAST(node, fieldTypes) {
1032
+ const literalType = inferLiteralType(node);
1033
+ if (literalType !== null) return literalType;
1034
+ switch (node.type) {
1035
+ case "Identifier":
1036
+ return getFieldType(node.name, fieldTypes);
1037
+ case "RootPath":
1038
+ case "RelativePath":
1039
+ case "ContextToken":
1040
+ case "IndexExpression":
1041
+ case "WildcardExpression":
1042
+ return "unknown";
1043
+ case "MemberExpression": {
1044
+ const objectType = inferTypeFromAST(node.object, fieldTypes);
1045
+ if (objectType !== "unknown") return objectType;
1046
+ if (node.object.type === "Identifier") {
1047
+ return getFieldType(`${node.object.name}.${node.property}`, fieldTypes);
1048
+ }
1049
+ return "unknown";
1050
+ }
1051
+ case "BinaryOp":
1052
+ return inferBinaryOpType(node, fieldTypes);
1053
+ case "UnaryOp": {
1054
+ if (node.op === "-") return "number";
1055
+ if (node.op === "!") return "boolean";
1056
+ return "unknown";
1057
+ }
1058
+ case "TernaryOp": {
1059
+ const thenType = inferTypeFromAST(node.consequent, fieldTypes);
1060
+ const elseType = inferTypeFromAST(node.alternate, fieldTypes);
1061
+ return thenType === elseType ? thenType : "unknown";
1062
+ }
1063
+ case "CallExpression":
1064
+ return inferCallExpressionType(node, fieldTypes);
1065
+ default:
1066
+ return "unknown";
1067
+ }
1068
+ }
1069
+ function inferFormulaType(expression, fieldTypes = {}) {
1070
+ const trimmed = expression.trim();
1071
+ if (!trimmed) {
1072
+ return "unknown";
1073
+ }
1074
+ try {
1075
+ const { ast } = parseFormula(trimmed);
1076
+ return inferTypeFromAST(ast, fieldTypes);
1077
+ } catch {
1078
+ return "unknown";
1079
+ }
1080
+ }
1081
+
1082
+ // src/parse-formula.ts
1083
+ function parseExpression(expression) {
1084
+ const result = parseFormula(expression);
1085
+ return {
1086
+ expression,
1087
+ dependencies: result.dependencies,
1088
+ minVersion: result.minVersion,
1089
+ features: result.features
1090
+ };
1091
+ }
1092
+
1093
+ // src/validate-syntax.ts
1094
+ function validateFormulaSyntax(expression) {
1095
+ const result = validateSyntax(expression);
1096
+ if (result.isValid) {
1097
+ return { isValid: true };
1098
+ }
1099
+ return {
1100
+ isValid: false,
1101
+ error: result.error,
1102
+ position: result.position
1103
+ };
1104
+ }
1105
+
1106
+ // src/dependency-graph.ts
1107
+ function buildDependencyGraph(dependencies) {
1108
+ const nodes = /* @__PURE__ */ new Set();
1109
+ const edges = /* @__PURE__ */ new Map();
1110
+ for (const [node, deps] of Object.entries(dependencies)) {
1111
+ nodes.add(node);
1112
+ edges.set(node, new Set(deps));
1113
+ for (const dep of deps) {
1114
+ nodes.add(dep);
1115
+ }
1116
+ }
1117
+ return { nodes, edges };
1118
+ }
1119
+ function detectCircularDependencies(graph) {
1120
+ const visited = /* @__PURE__ */ new Set();
1121
+ const recursionStack = /* @__PURE__ */ new Set();
1122
+ const path = [];
1123
+ for (const node of graph.nodes) {
1124
+ if (!visited.has(node)) {
1125
+ const cycle = dfsVisit(node, graph, visited, recursionStack, path);
1126
+ if (cycle) {
1127
+ return { hasCircular: true, cycle };
1128
+ }
1129
+ }
1130
+ }
1131
+ return { hasCircular: false, cycle: null };
1132
+ }
1133
+ function dfsVisit(node, graph, visited, recursionStack, path) {
1134
+ visited.add(node);
1135
+ recursionStack.add(node);
1136
+ path.push(node);
1137
+ const deps = graph.edges.get(node);
1138
+ if (deps) {
1139
+ for (const dep of deps) {
1140
+ if (!visited.has(dep)) {
1141
+ const cycle = dfsVisit(dep, graph, visited, recursionStack, path);
1142
+ if (cycle) {
1143
+ return cycle;
1144
+ }
1145
+ } else if (recursionStack.has(dep)) {
1146
+ const cycleStart = path.indexOf(dep);
1147
+ return [...path.slice(cycleStart), dep];
1148
+ }
1149
+ }
1150
+ }
1151
+ path.pop();
1152
+ recursionStack.delete(node);
1153
+ return null;
1154
+ }
1155
+ function getTopologicalOrder(graph) {
1156
+ const circularCheck = detectCircularDependencies(graph);
1157
+ if (circularCheck.hasCircular && circularCheck.cycle) {
1158
+ return {
1159
+ success: false,
1160
+ order: [],
1161
+ error: `Circular dependency detected: ${circularCheck.cycle.join(" -> ")}`
1162
+ };
1163
+ }
1164
+ const inDegree = initializeInDegree(graph);
1165
+ const queue = findZeroInDegreeNodes(inDegree);
1166
+ const order = processQueue(queue, graph, inDegree);
1167
+ order.reverse();
1168
+ return { success: true, order };
1169
+ }
1170
+ function initializeInDegree(graph) {
1171
+ const inDegree = /* @__PURE__ */ new Map();
1172
+ for (const node of graph.nodes) {
1173
+ inDegree.set(node, 0);
1174
+ }
1175
+ for (const deps of graph.edges.values()) {
1176
+ for (const dep of deps) {
1177
+ inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1);
1178
+ }
1179
+ }
1180
+ return inDegree;
1181
+ }
1182
+ function findZeroInDegreeNodes(inDegree) {
1183
+ const result = [];
1184
+ for (const [node, degree] of inDegree) {
1185
+ if (degree === 0) {
1186
+ result.push(node);
1187
+ }
1188
+ }
1189
+ return result;
1190
+ }
1191
+ function processQueue(queue, graph, inDegree) {
1192
+ const order = [];
1193
+ let head = 0;
1194
+ while (head < queue.length) {
1195
+ const node = queue[head];
1196
+ head++;
1197
+ order.push(node);
1198
+ const deps = graph.edges.get(node);
1199
+ if (deps) {
1200
+ for (const dep of deps) {
1201
+ const newDegree = (inDegree.get(dep) ?? 0) - 1;
1202
+ inDegree.set(dep, newDegree);
1203
+ if (newDegree === 0) {
1204
+ queue.push(dep);
1205
+ }
1206
+ }
1207
+ }
1208
+ }
1209
+ return order;
1210
+ }
1211
+
1212
+ // src/extract-schema.ts
1213
+ function extractSchemaFormulas(schema) {
1214
+ const formulas = [];
1215
+ extractFormulasRecursive(schema, "", formulas);
1216
+ return formulas;
1217
+ }
1218
+ function extractFormulasRecursive(schema, pathPrefix, formulas) {
1219
+ if (schema.type === "array" && schema.items) {
1220
+ extractFormulasRecursive(schema.items, `${pathPrefix}[]`, formulas);
1221
+ return;
1222
+ }
1223
+ const properties = schema.properties ?? {};
1224
+ for (const [fieldName, fieldSchema] of Object.entries(properties)) {
1225
+ const fullPath = pathPrefix ? `${pathPrefix}.${fieldName}` : fieldName;
1226
+ const xFormula = fieldSchema["x-formula"];
1227
+ if (xFormula) {
1228
+ formulas.push({
1229
+ fieldName: fullPath,
1230
+ expression: xFormula.expression,
1231
+ fieldType: fieldSchema.type ?? "string"
1232
+ });
1233
+ }
1234
+ if (fieldSchema.type === "object" && fieldSchema.properties) {
1235
+ extractFormulasRecursive(fieldSchema, fullPath, formulas);
1236
+ }
1237
+ if (fieldSchema.type === "array" && fieldSchema.items) {
1238
+ extractFormulasRecursive(fieldSchema.items, `${fullPath}[]`, formulas);
1239
+ }
1240
+ }
1241
+ }
1242
+
1243
+ // src/validate-schema.ts
1244
+ function resolveSubSchema(schema, fieldPath) {
1245
+ if (!fieldPath) {
1246
+ return schema;
1247
+ }
1248
+ const segments = parsePathSegments(fieldPath);
1249
+ let current = schema;
1250
+ for (const segment of segments) {
1251
+ if (segment === "[]") {
1252
+ if (current.type === "array" && current.items) {
1253
+ current = current.items;
1254
+ } else {
1255
+ return null;
1256
+ }
1257
+ } else {
1258
+ if (current.properties?.[segment]) {
1259
+ current = current.properties[segment];
1260
+ } else {
1261
+ return null;
1262
+ }
1263
+ }
1264
+ }
1265
+ return current;
1266
+ }
1267
+ function parsePathSegments(path) {
1268
+ const segments = [];
1269
+ let current = "";
1270
+ let inBracket = false;
1271
+ for (const char of path) {
1272
+ if (char === "[") {
1273
+ if (current) {
1274
+ segments.push(current);
1275
+ current = "";
1276
+ }
1277
+ inBracket = true;
1278
+ } else if (char === "]") {
1279
+ inBracket = false;
1280
+ segments.push("[]");
1281
+ } else if (char === "." && !inBracket) {
1282
+ if (current) {
1283
+ segments.push(current);
1284
+ current = "";
1285
+ }
1286
+ } else if (!inBracket) {
1287
+ current += char;
1288
+ }
1289
+ }
1290
+ if (current) {
1291
+ segments.push(current);
1292
+ }
1293
+ return segments;
1294
+ }
1295
+ function getParentPath(fieldPath) {
1296
+ const lastDotIndex = fieldPath.lastIndexOf(".");
1297
+ const lastBracketIndex = fieldPath.lastIndexOf("[");
1298
+ const splitIndex = Math.max(lastDotIndex, lastBracketIndex);
1299
+ if (splitIndex <= 0) {
1300
+ return "";
1301
+ }
1302
+ return fieldPath.substring(0, splitIndex);
1303
+ }
1304
+ function getFieldName(fieldPath) {
1305
+ const lastDotIndex = fieldPath.lastIndexOf(".");
1306
+ const lastBracketIndex = fieldPath.lastIndexOf("]");
1307
+ const splitIndex = Math.max(lastDotIndex, lastBracketIndex);
1308
+ if (splitIndex === -1) {
1309
+ return fieldPath;
1310
+ }
1311
+ return fieldPath.substring(splitIndex + 1);
1312
+ }
1313
+ function getSchemaFields(schema) {
1314
+ const fields = /* @__PURE__ */ new Set();
1315
+ const properties = schema.properties ?? {};
1316
+ for (const fieldName of Object.keys(properties)) {
1317
+ fields.add(fieldName);
1318
+ }
1319
+ return fields;
1320
+ }
1321
+ function getSchemaFieldTypes(schema) {
1322
+ const fieldTypes = {};
1323
+ const properties = schema.properties ?? {};
1324
+ for (const [fieldName, fieldSchema] of Object.entries(properties)) {
1325
+ const schemaType = fieldSchema.type;
1326
+ if (schemaType === "number" || schemaType === "string" || schemaType === "boolean" || schemaType === "object" || schemaType === "array") {
1327
+ fieldTypes[fieldName] = schemaType;
1328
+ }
1329
+ }
1330
+ return fieldTypes;
1331
+ }
1332
+ function schemaTypeToInferred(schemaType) {
1333
+ if (schemaType === "number") return "number";
1334
+ if (schemaType === "string") return "string";
1335
+ if (schemaType === "boolean") return "boolean";
1336
+ return null;
1337
+ }
1338
+ function isTypeCompatible(inferredType, expectedType) {
1339
+ if (expectedType === null) return true;
1340
+ if (inferredType === "unknown") return true;
1341
+ return inferredType === expectedType;
1342
+ }
1343
+ function extractFieldRoot(dependency) {
1344
+ const root = dependency.split(".")[0]?.split("[")[0];
1345
+ return root || dependency;
1346
+ }
1347
+ function validateFormulaInContext(expression, fieldPath, rootSchema) {
1348
+ const syntaxResult = validateFormulaSyntax(expression);
1349
+ if (!syntaxResult.isValid) {
1350
+ return {
1351
+ field: fieldPath,
1352
+ error: syntaxResult.error,
1353
+ position: syntaxResult.position
1354
+ };
1355
+ }
1356
+ const parentPath = getParentPath(fieldPath);
1357
+ const localFieldName = getFieldName(fieldPath);
1358
+ const contextSchema = resolveSubSchema(rootSchema, parentPath);
1359
+ if (!contextSchema) {
1360
+ return {
1361
+ field: fieldPath,
1362
+ error: `Cannot resolve schema context for path '${parentPath}'`
1363
+ };
1364
+ }
1365
+ const parseResult = parseExpression(expression);
1366
+ const schemaFields = getSchemaFields(contextSchema);
1367
+ for (const dep of parseResult.dependencies) {
1368
+ const rootField = extractFieldRoot(dep);
1369
+ if (!schemaFields.has(rootField)) {
1370
+ return {
1371
+ field: fieldPath,
1372
+ error: `Unknown field '${rootField}' in formula`
1373
+ };
1374
+ }
1375
+ }
1376
+ if (parseResult.dependencies.some((d) => extractFieldRoot(d) === localFieldName)) {
1377
+ return {
1378
+ field: fieldPath,
1379
+ error: `Formula cannot reference itself`
1380
+ };
1381
+ }
1382
+ const fieldSchema = contextSchema.properties?.[localFieldName];
1383
+ const expectedType = schemaTypeToInferred(fieldSchema?.type);
1384
+ const fieldTypes = getSchemaFieldTypes(contextSchema);
1385
+ const inferredType = inferFormulaType(expression, fieldTypes);
1386
+ if (!isTypeCompatible(inferredType, expectedType)) {
1387
+ return {
1388
+ field: fieldPath,
1389
+ error: `Type mismatch: formula returns '${inferredType}' but field expects '${expectedType}'`
1390
+ };
1391
+ }
1392
+ return null;
1393
+ }
1394
+ function validateFormulaAgainstSchema(expression, fieldName, schema) {
1395
+ return validateFormulaInContext(expression, fieldName, schema);
1396
+ }
1397
+ function validateSchemaFormulas(schema) {
1398
+ const errors = [];
1399
+ const formulas = extractSchemaFormulas(schema);
1400
+ for (const formula of formulas) {
1401
+ const error = validateFormulaAgainstSchema(
1402
+ formula.expression,
1403
+ formula.fieldName,
1404
+ schema
1405
+ );
1406
+ if (error) {
1407
+ errors.push(error);
1408
+ }
1409
+ }
1410
+ if (errors.length > 0) {
1411
+ return { isValid: false, errors };
1412
+ }
1413
+ const dependencies = {};
1414
+ for (const formula of formulas) {
1415
+ const parseResult = parseExpression(formula.expression);
1416
+ const parentPath = getParentPath(formula.fieldName);
1417
+ const prefix = parentPath ? `${parentPath}.` : "";
1418
+ dependencies[formula.fieldName] = parseResult.dependencies.map((dep) => {
1419
+ const rootField = extractFieldRoot(dep);
1420
+ return `${prefix}${rootField}`;
1421
+ });
1422
+ }
1423
+ const graph = buildDependencyGraph(dependencies);
1424
+ const circularCheck = detectCircularDependencies(graph);
1425
+ const cycle = circularCheck.cycle;
1426
+ if (circularCheck.hasCircular && cycle && cycle.length > 0) {
1427
+ const firstField = cycle[0];
1428
+ if (firstField) {
1429
+ errors.push({
1430
+ field: firstField,
1431
+ error: `Circular dependency: ${cycle.join(" \u2192 ")}`
1432
+ });
1433
+ return { isValid: false, errors };
1434
+ }
1435
+ }
1436
+ return { isValid: true, errors: [] };
1437
+ }
1438
+
1439
+ exports.buildDependencyGraph = buildDependencyGraph;
1440
+ exports.detectCircularDependencies = detectCircularDependencies;
1441
+ exports.evaluate = evaluate;
1442
+ exports.evaluateWithContext = evaluateWithContext;
1443
+ exports.extractSchemaFormulas = extractSchemaFormulas;
1444
+ exports.getTopologicalOrder = getTopologicalOrder;
1445
+ exports.inferFormulaType = inferFormulaType;
1446
+ exports.parseExpression = parseExpression;
1447
+ exports.parseFormula = parseFormula;
1448
+ exports.validateFormulaAgainstSchema = validateFormulaAgainstSchema;
1449
+ exports.validateFormulaSyntax = validateFormulaSyntax;
1450
+ exports.validateSchemaFormulas = validateSchemaFormulas;
1451
+ exports.validateSyntax = validateSyntax;
1452
+ //# sourceMappingURL=chunk-KYBZYJ7E.cjs.map
1453
+ //# sourceMappingURL=chunk-KYBZYJ7E.cjs.map