@adtrackify/at-service-common 3.19.2 → 3.19.4

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.
Files changed (31) hide show
  1. package/dist/cjs/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.d.ts +1 -0
  2. package/dist/cjs/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.js +143 -0
  3. package/dist/cjs/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.js.map +1 -0
  4. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-types.d.ts +47 -0
  5. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-types.js +26 -0
  6. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-types.js.map +1 -0
  7. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-utils.d.ts +7 -0
  8. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-utils.js +200 -0
  9. package/dist/cjs/libs/custom-measure-formula/custom-measure-formula-utils.js.map +1 -0
  10. package/dist/cjs/libs/custom-measure-formula/index.d.ts +2 -0
  11. package/dist/cjs/libs/custom-measure-formula/index.js +19 -0
  12. package/dist/cjs/libs/custom-measure-formula/index.js.map +1 -0
  13. package/dist/cjs/libs/index.d.ts +1 -0
  14. package/dist/cjs/libs/index.js +1 -0
  15. package/dist/cjs/libs/index.js.map +1 -1
  16. package/dist/esm/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.d.ts +1 -0
  17. package/dist/esm/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.js +141 -0
  18. package/dist/esm/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.js.map +1 -0
  19. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-types.d.ts +47 -0
  20. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-types.js +23 -0
  21. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-types.js.map +1 -0
  22. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-utils.d.ts +7 -0
  23. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-utils.js +191 -0
  24. package/dist/esm/libs/custom-measure-formula/custom-measure-formula-utils.js.map +1 -0
  25. package/dist/esm/libs/custom-measure-formula/index.d.ts +2 -0
  26. package/dist/esm/libs/custom-measure-formula/index.js +3 -0
  27. package/dist/esm/libs/custom-measure-formula/index.js.map +1 -0
  28. package/dist/esm/libs/index.d.ts +1 -0
  29. package/dist/esm/libs/index.js +1 -0
  30. package/dist/esm/libs/index.js.map +1 -1
  31. package/package.json +2 -2
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_js_1 = require("../../../libs/custom-measure-formula/index.js");
4
+ const SAMPLE_ULID = '01ARZ3NDEKTSV4RRFFQ69G5FAV';
5
+ describe('custom measure formula utils', () => {
6
+ describe('isCustomMeasureId', () => {
7
+ it('should detect bare-ULID custom measure ids and reject native dotted ids', () => {
8
+ expect((0, index_js_1.isCustomMeasureId)(SAMPLE_ULID)).toBe(true);
9
+ expect((0, index_js_1.isCustomMeasureId)('orders.net_revenue')).toBe(false);
10
+ expect((0, index_js_1.isCustomMeasureId)('channel.spend')).toBe(false);
11
+ expect((0, index_js_1.isCustomMeasureId)('custom.1234')).toBe(false);
12
+ expect((0, index_js_1.isCustomMeasureId)(SAMPLE_ULID.toLowerCase())).toBe(false);
13
+ expect((0, index_js_1.isCustomMeasureId)(undefined)).toBe(false);
14
+ expect((0, index_js_1.isCustomMeasureId)('')).toBe(false);
15
+ });
16
+ });
17
+ describe('tokenizeFormula', () => {
18
+ it('should tokenize measures, numbers, operators and parens', () => {
19
+ const tokens = (0, index_js_1.tokenizeFormula)('(orders.net_revenue - channel.spend) / channel.spend * 100');
20
+ expect(tokens.map((t) => t.type)).toEqual([
21
+ 'lparen',
22
+ 'measure',
23
+ 'operator',
24
+ 'measure',
25
+ 'rparen',
26
+ 'operator',
27
+ 'measure',
28
+ 'operator',
29
+ 'number',
30
+ ]);
31
+ expect(tokens[1].value).toBe('orders.net_revenue');
32
+ expect(tokens[8].value).toBe('100');
33
+ });
34
+ it('should tokenize decimal numbers and dotted measures ending in digits', () => {
35
+ const tokens = (0, index_js_1.tokenizeFormula)('orders.cm3 * 0.3');
36
+ expect(tokens.map((t) => t.value)).toEqual(['orders.cm3', '*', '0.3']);
37
+ });
38
+ it('should reject invalid characters', () => {
39
+ expect(() => (0, index_js_1.tokenizeFormula)('orders.revenue & 2')).toThrow(/Invalid character/);
40
+ });
41
+ it('should reject non-dotted identifiers', () => {
42
+ expect(() => (0, index_js_1.tokenizeFormula)('revenue + 1')).toThrow(/Invalid measure reference/);
43
+ });
44
+ it('should reject malformed numbers', () => {
45
+ expect(() => (0, index_js_1.tokenizeFormula)('1.2.3 + orders.count')).toThrow(/Invalid number/);
46
+ });
47
+ it('should reject an empty formula', () => {
48
+ expect(() => (0, index_js_1.tokenizeFormula)(' ')).toThrow(/empty/);
49
+ });
50
+ });
51
+ describe('parseFormula', () => {
52
+ it('should honour operator precedence', () => {
53
+ const ast = (0, index_js_1.parseFormula)('orders.a + orders.b * orders.c');
54
+ expect(ast).toEqual({
55
+ type: 'binary',
56
+ operator: '+',
57
+ left: { type: 'measure', measure: 'orders.a' },
58
+ right: {
59
+ type: 'binary',
60
+ operator: '*',
61
+ left: { type: 'measure', measure: 'orders.b' },
62
+ right: { type: 'measure', measure: 'orders.c' },
63
+ },
64
+ });
65
+ });
66
+ it('should honour parentheses and unary minus', () => {
67
+ const ast = (0, index_js_1.parseFormula)('-(orders.a - orders.b)');
68
+ expect(ast).toEqual({
69
+ type: 'unary',
70
+ operator: '-',
71
+ operand: {
72
+ type: 'binary',
73
+ operator: '-',
74
+ left: { type: 'measure', measure: 'orders.a' },
75
+ right: { type: 'measure', measure: 'orders.b' },
76
+ },
77
+ });
78
+ });
79
+ it('should reject unbalanced parentheses', () => {
80
+ expect(() => (0, index_js_1.parseFormula)('(orders.a + orders.b')).toThrow(/Unbalanced parentheses/);
81
+ });
82
+ it('should reject trailing operators', () => {
83
+ expect(() => (0, index_js_1.parseFormula)('orders.a +')).toThrow(/Unexpected end of formula/);
84
+ });
85
+ it('should reject dangling tokens', () => {
86
+ expect(() => (0, index_js_1.parseFormula)('orders.a orders.b')).toThrow(/Unexpected token/);
87
+ });
88
+ });
89
+ describe('collectFormulaMeasures', () => {
90
+ it('should return unique measures in order', () => {
91
+ const ast = (0, index_js_1.parseFormula)('(orders.net_revenue - channel.spend) / channel.spend');
92
+ expect((0, index_js_1.collectFormulaMeasures)(ast)).toEqual(['orders.net_revenue', 'channel.spend']);
93
+ });
94
+ });
95
+ describe('evaluateFormulaAst', () => {
96
+ const values = new Map([
97
+ ['orders.net_revenue', 1000],
98
+ ['channel.spend', 250],
99
+ ['orders.count', 0],
100
+ ]);
101
+ it('should evaluate a ratio with percentage scaling', () => {
102
+ const ast = (0, index_js_1.parseFormula)('(orders.net_revenue - channel.spend) / channel.spend * 100');
103
+ expect((0, index_js_1.evaluateFormulaAst)(ast, values)).toBe(300);
104
+ });
105
+ it('should return 0 on division by zero (matches SQL nullif semantics)', () => {
106
+ const ast = (0, index_js_1.parseFormula)('orders.net_revenue / orders.count');
107
+ expect((0, index_js_1.evaluateFormulaAst)(ast, values)).toBe(0);
108
+ });
109
+ it('should treat a missing operand as 0 (matches SQL COALESCE semantics)', () => {
110
+ const ast = (0, index_js_1.parseFormula)('orders.net_revenue - orders.unknown');
111
+ expect((0, index_js_1.evaluateFormulaAst)(ast, values)).toBe(1000);
112
+ });
113
+ });
114
+ describe('validateFormula', () => {
115
+ const knownOperands = new Set(['orders.net_revenue', 'channel.spend', 'orders.count']);
116
+ it('should accept a valid formula', () => {
117
+ const result = (0, index_js_1.validateFormula)('(orders.net_revenue - channel.spend) / channel.spend * 100', { knownOperands });
118
+ expect(result.valid).toBe(true);
119
+ expect(result.errors).toEqual([]);
120
+ expect(result.ast).toBeDefined();
121
+ });
122
+ it('should reject an unknown operand', () => {
123
+ const result = (0, index_js_1.validateFormula)('orders.net_revenue / orders.mystery', { knownOperands });
124
+ expect(result.valid).toBe(false);
125
+ expect(result.errors[0]).toMatch(/Unknown or unsupported measure/);
126
+ });
127
+ it('should reject a pure-constant formula', () => {
128
+ const result = (0, index_js_1.validateFormula)('1 + 2', { knownOperands });
129
+ expect(result.valid).toBe(false);
130
+ expect(result.errors[0]).toMatch(/at least one measure/);
131
+ });
132
+ it('should surface syntax errors', () => {
133
+ const result = (0, index_js_1.validateFormula)('(orders.count + ', { knownOperands });
134
+ expect(result.valid).toBe(false);
135
+ expect(result.errors.length).toBe(1);
136
+ });
137
+ it('should run syntactic-only when no operand predicate is supplied', () => {
138
+ const result = (0, index_js_1.validateFormula)('orders.net_revenue / channel.spend');
139
+ expect(result.valid).toBe(true);
140
+ });
141
+ });
142
+ });
143
+ //# sourceMappingURL=custom-measure-formula-utils.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-utils.spec.js","sourceRoot":"","sources":["../../../../../src/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export declare enum FORMULA_OPERATOR {
2
+ ADD = "+",
3
+ SUBTRACT = "-",
4
+ MULTIPLY = "*",
5
+ DIVIDE = "/"
6
+ }
7
+ export declare enum FORMULA_TOKEN_TYPE {
8
+ NUMBER = "number",
9
+ MEASURE = "measure",
10
+ OPERATOR = "operator",
11
+ LPAREN = "lparen",
12
+ RPAREN = "rparen"
13
+ }
14
+ export declare enum FORMULA_NODE_TYPE {
15
+ NUMBER = "number",
16
+ MEASURE = "measure",
17
+ UNARY = "unary",
18
+ BINARY = "binary"
19
+ }
20
+ export interface FormulaToken {
21
+ type: FORMULA_TOKEN_TYPE;
22
+ value: string;
23
+ }
24
+ export type FormulaAst = {
25
+ type: FORMULA_NODE_TYPE.NUMBER;
26
+ value: number;
27
+ } | {
28
+ type: FORMULA_NODE_TYPE.MEASURE;
29
+ measure: string;
30
+ } | {
31
+ type: FORMULA_NODE_TYPE.UNARY;
32
+ operator: FORMULA_OPERATOR.SUBTRACT;
33
+ operand: FormulaAst;
34
+ } | {
35
+ type: FORMULA_NODE_TYPE.BINARY;
36
+ operator: FORMULA_OPERATOR;
37
+ left: FormulaAst;
38
+ right: FormulaAst;
39
+ };
40
+ export interface FormulaValidationOptions {
41
+ knownOperands?: ReadonlySet<string>;
42
+ }
43
+ export interface FormulaValidationResult {
44
+ valid: boolean;
45
+ errors: string[];
46
+ ast?: FormulaAst;
47
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FORMULA_NODE_TYPE = exports.FORMULA_TOKEN_TYPE = exports.FORMULA_OPERATOR = void 0;
4
+ var FORMULA_OPERATOR;
5
+ (function (FORMULA_OPERATOR) {
6
+ FORMULA_OPERATOR["ADD"] = "+";
7
+ FORMULA_OPERATOR["SUBTRACT"] = "-";
8
+ FORMULA_OPERATOR["MULTIPLY"] = "*";
9
+ FORMULA_OPERATOR["DIVIDE"] = "/";
10
+ })(FORMULA_OPERATOR = exports.FORMULA_OPERATOR || (exports.FORMULA_OPERATOR = {}));
11
+ var FORMULA_TOKEN_TYPE;
12
+ (function (FORMULA_TOKEN_TYPE) {
13
+ FORMULA_TOKEN_TYPE["NUMBER"] = "number";
14
+ FORMULA_TOKEN_TYPE["MEASURE"] = "measure";
15
+ FORMULA_TOKEN_TYPE["OPERATOR"] = "operator";
16
+ FORMULA_TOKEN_TYPE["LPAREN"] = "lparen";
17
+ FORMULA_TOKEN_TYPE["RPAREN"] = "rparen";
18
+ })(FORMULA_TOKEN_TYPE = exports.FORMULA_TOKEN_TYPE || (exports.FORMULA_TOKEN_TYPE = {}));
19
+ var FORMULA_NODE_TYPE;
20
+ (function (FORMULA_NODE_TYPE) {
21
+ FORMULA_NODE_TYPE["NUMBER"] = "number";
22
+ FORMULA_NODE_TYPE["MEASURE"] = "measure";
23
+ FORMULA_NODE_TYPE["UNARY"] = "unary";
24
+ FORMULA_NODE_TYPE["BINARY"] = "binary";
25
+ })(FORMULA_NODE_TYPE = exports.FORMULA_NODE_TYPE || (exports.FORMULA_NODE_TYPE = {}));
26
+ //# sourceMappingURL=custom-measure-formula-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-types.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/custom-measure-formula-types.ts"],"names":[],"mappings":";;;AAGA,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,6BAAS,CAAA;IACT,kCAAc,CAAA;IACd,kCAAc,CAAA;IACd,gCAAY,CAAA;AACd,CAAC,EALW,gBAAgB,GAAhB,wBAAgB,KAAhB,wBAAgB,QAK3B;AAED,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC5B,uCAAiB,CAAA;IACjB,yCAAmB,CAAA;IACnB,2CAAqB,CAAA;IACrB,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;AACnB,CAAC,EANW,kBAAkB,GAAlB,0BAAkB,KAAlB,0BAAkB,QAM7B;AAED,IAAY,iBAKX;AALD,WAAY,iBAAiB;IAC3B,sCAAiB,CAAA;IACjB,wCAAmB,CAAA;IACnB,oCAAe,CAAA;IACf,sCAAiB,CAAA;AACnB,CAAC,EALW,iBAAiB,GAAjB,yBAAiB,KAAjB,yBAAiB,QAK5B"}
@@ -0,0 +1,7 @@
1
+ import { FormulaAst, FormulaToken, FormulaValidationOptions, FormulaValidationResult } from './custom-measure-formula-types.js';
2
+ export declare const isCustomMeasureId: (id?: string) => boolean;
3
+ export declare const tokenizeFormula: (formula: string) => FormulaToken[];
4
+ export declare const parseFormula: (formula: string) => FormulaAst;
5
+ export declare const collectFormulaMeasures: (ast: FormulaAst) => string[];
6
+ export declare const evaluateFormulaAst: (ast: FormulaAst, operandValues: ReadonlyMap<string, number | null | undefined>) => number;
7
+ export declare const validateFormula: (formula: string, options?: FormulaValidationOptions) => FormulaValidationResult;
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateFormula = exports.evaluateFormulaAst = exports.collectFormulaMeasures = exports.parseFormula = exports.tokenizeFormula = exports.isCustomMeasureId = void 0;
4
+ const custom_measure_formula_types_js_1 = require("./custom-measure-formula-types.js");
5
+ const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
6
+ const NUMBER_PATTERN = /^[0-9]+(\.[0-9]+)?$/;
7
+ const MEASURE_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/;
8
+ const ALLOWED_CHARS_PATTERN = /[^a-z0-9_.+\-*/()\s]/;
9
+ const isCustomMeasureId = (id) => {
10
+ return !!id && ULID_PATTERN.test(id);
11
+ };
12
+ exports.isCustomMeasureId = isCustomMeasureId;
13
+ const tokenizeFormula = (formula) => {
14
+ if (!formula || formula.trim().length === 0) {
15
+ throw new Error('Formula is empty');
16
+ }
17
+ const invalidChar = formula.match(ALLOWED_CHARS_PATTERN);
18
+ if (invalidChar) {
19
+ throw new Error(`Invalid character in formula: '${invalidChar[0]}'`);
20
+ }
21
+ const tokens = [];
22
+ let i = 0;
23
+ while (i < formula.length) {
24
+ const char = formula[i];
25
+ if (/\s/.test(char)) {
26
+ i += 1;
27
+ continue;
28
+ }
29
+ if (char === '(' || char === ')') {
30
+ tokens.push({ type: char === '(' ? custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.LPAREN : custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.RPAREN, value: char });
31
+ i += 1;
32
+ continue;
33
+ }
34
+ if (char === '+' || char === '-' || char === '*' || char === '/') {
35
+ tokens.push({ type: custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.OPERATOR, value: char });
36
+ i += 1;
37
+ continue;
38
+ }
39
+ if (char >= '0' && char <= '9') {
40
+ let value = '';
41
+ while (i < formula.length && /[0-9.]/.test(formula[i])) {
42
+ value += formula[i];
43
+ i += 1;
44
+ }
45
+ if (!NUMBER_PATTERN.test(value)) {
46
+ throw new Error(`Invalid number in formula: '${value}'`);
47
+ }
48
+ tokens.push({ type: custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.NUMBER, value });
49
+ continue;
50
+ }
51
+ if (char >= 'a' && char <= 'z') {
52
+ let value = '';
53
+ while (i < formula.length && /[a-z0-9_.]/.test(formula[i])) {
54
+ value += formula[i];
55
+ i += 1;
56
+ }
57
+ if (!MEASURE_PATTERN.test(value)) {
58
+ throw new Error(`Invalid measure reference in formula: '${value}'`);
59
+ }
60
+ tokens.push({ type: custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.MEASURE, value });
61
+ continue;
62
+ }
63
+ throw new Error(`Invalid character in formula: '${char}'`);
64
+ }
65
+ return tokens;
66
+ };
67
+ exports.tokenizeFormula = tokenizeFormula;
68
+ const parseFormula = (formula) => {
69
+ const tokens = (0, exports.tokenizeFormula)(formula);
70
+ let pos = 0;
71
+ const peek = () => tokens[pos];
72
+ const parseExpression = () => {
73
+ let left = parseTerm();
74
+ while (peek()?.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.OPERATOR && (peek()?.value === '+' || peek()?.value === '-')) {
75
+ const operator = tokens[pos].value;
76
+ pos += 1;
77
+ const right = parseTerm();
78
+ left = { type: custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.BINARY, operator, left, right };
79
+ }
80
+ return left;
81
+ };
82
+ const parseTerm = () => {
83
+ let left = parseFactor();
84
+ while (peek()?.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.OPERATOR && (peek()?.value === '*' || peek()?.value === '/')) {
85
+ const operator = tokens[pos].value;
86
+ pos += 1;
87
+ const right = parseFactor();
88
+ left = { type: custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.BINARY, operator, left, right };
89
+ }
90
+ return left;
91
+ };
92
+ const parseFactor = () => {
93
+ const token = peek();
94
+ if (!token) {
95
+ throw new Error('Unexpected end of formula');
96
+ }
97
+ if (token.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.OPERATOR && token.value === '-') {
98
+ pos += 1;
99
+ return { type: custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.UNARY, operator: custom_measure_formula_types_js_1.FORMULA_OPERATOR.SUBTRACT, operand: parseFactor() };
100
+ }
101
+ if (token.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.NUMBER) {
102
+ pos += 1;
103
+ return { type: custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.NUMBER, value: Number(token.value) };
104
+ }
105
+ if (token.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.MEASURE) {
106
+ pos += 1;
107
+ return { type: custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.MEASURE, measure: token.value };
108
+ }
109
+ if (token.type === custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.LPAREN) {
110
+ pos += 1;
111
+ const expression = parseExpression();
112
+ if (peek()?.type !== custom_measure_formula_types_js_1.FORMULA_TOKEN_TYPE.RPAREN) {
113
+ throw new Error('Unbalanced parentheses in formula');
114
+ }
115
+ pos += 1;
116
+ return expression;
117
+ }
118
+ throw new Error(`Unexpected token in formula: '${token.value}'`);
119
+ };
120
+ const ast = parseExpression();
121
+ if (pos !== tokens.length) {
122
+ throw new Error(`Unexpected token in formula: '${tokens[pos].value}'`);
123
+ }
124
+ return ast;
125
+ };
126
+ exports.parseFormula = parseFormula;
127
+ const collectFormulaMeasures = (ast) => {
128
+ const measures = [];
129
+ const walk = (node) => {
130
+ switch (node.type) {
131
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.MEASURE:
132
+ if (!measures.includes(node.measure)) {
133
+ measures.push(node.measure);
134
+ }
135
+ break;
136
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.UNARY:
137
+ walk(node.operand);
138
+ break;
139
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.BINARY:
140
+ walk(node.left);
141
+ walk(node.right);
142
+ break;
143
+ }
144
+ };
145
+ walk(ast);
146
+ return measures;
147
+ };
148
+ exports.collectFormulaMeasures = collectFormulaMeasures;
149
+ const evaluateFormulaAst = (ast, operandValues) => {
150
+ switch (ast.type) {
151
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.NUMBER:
152
+ return ast.value;
153
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.MEASURE: {
154
+ const value = Number(operandValues.get(ast.measure));
155
+ return Number.isFinite(value) ? value : 0;
156
+ }
157
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.UNARY:
158
+ return -(0, exports.evaluateFormulaAst)(ast.operand, operandValues);
159
+ case custom_measure_formula_types_js_1.FORMULA_NODE_TYPE.BINARY: {
160
+ const left = (0, exports.evaluateFormulaAst)(ast.left, operandValues);
161
+ const right = (0, exports.evaluateFormulaAst)(ast.right, operandValues);
162
+ switch (ast.operator) {
163
+ case custom_measure_formula_types_js_1.FORMULA_OPERATOR.ADD:
164
+ return left + right;
165
+ case custom_measure_formula_types_js_1.FORMULA_OPERATOR.SUBTRACT:
166
+ return left - right;
167
+ case custom_measure_formula_types_js_1.FORMULA_OPERATOR.MULTIPLY:
168
+ return left * right;
169
+ case custom_measure_formula_types_js_1.FORMULA_OPERATOR.DIVIDE:
170
+ return right === 0 ? 0 : left / right;
171
+ }
172
+ }
173
+ }
174
+ };
175
+ exports.evaluateFormulaAst = evaluateFormulaAst;
176
+ const validateFormula = (formula, options) => {
177
+ let ast;
178
+ try {
179
+ ast = (0, exports.parseFormula)(formula);
180
+ }
181
+ catch (error) {
182
+ return { valid: false, errors: [error.message] };
183
+ }
184
+ const errors = [];
185
+ const measures = (0, exports.collectFormulaMeasures)(ast);
186
+ if (measures.length === 0) {
187
+ errors.push('Formula must reference at least one measure');
188
+ }
189
+ measures.forEach((measure) => {
190
+ if ((0, exports.isCustomMeasureId)(measure)) {
191
+ errors.push(`Custom measures cannot reference another custom measure: '${measure}'`);
192
+ }
193
+ else if (options?.knownOperands && !options.knownOperands.has(measure)) {
194
+ errors.push(`Unknown or unsupported measure: '${measure}'`);
195
+ }
196
+ });
197
+ return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], ast };
198
+ };
199
+ exports.validateFormula = validateFormula;
200
+ //# sourceMappingURL=custom-measure-formula-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-utils.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/custom-measure-formula-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './custom-measure-formula-types.js';
2
+ export * from './custom-measure-formula-utils.js';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./custom-measure-formula-types.js"), exports);
18
+ __exportStar(require("./custom-measure-formula-utils.js"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oEAAkD;AAClD,oEAAkD"}
@@ -17,3 +17,4 @@ export * from './domain.js';
17
17
  export * from './emails.js';
18
18
  export * from './contacts.js';
19
19
  export * from './api-router/index.js';
20
+ export * from './custom-measure-formula/index.js';
@@ -33,4 +33,5 @@ __exportStar(require("./domain.js"), exports);
33
33
  __exportStar(require("./emails.js"), exports);
34
34
  __exportStar(require("./contacts.js"), exports);
35
35
  __exportStar(require("./api-router/index.js"), exports);
36
+ __exportStar(require("./custom-measure-formula/index.js"), exports);
36
37
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/libs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+DAA6C;AAC7C,8CAA4B;AAC5B,6CAA2B;AAC3B,kDAAgC;AAChC,yDAAuC;AACvC,2CAAyB;AACzB,6DAA2C;AAC3C,uDAAqC;AACrC,mDAAiC;AACjC,2CAAyB;AACzB,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B;AAC7B,+CAA6B;AAC7B,8CAA4B;AAC5B,8CAA4B;AAC5B,8CAA4B;AAC5B,gDAA8B;AAC9B,wDAAsC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/libs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+DAA6C;AAC7C,8CAA4B;AAC5B,6CAA2B;AAC3B,kDAAgC;AAChC,yDAAuC;AACvC,2CAAyB;AACzB,6DAA2C;AAC3C,uDAAqC;AACrC,mDAAiC;AACjC,2CAAyB;AACzB,8CAA4B;AAC5B,gDAA8B;AAC9B,+CAA6B;AAC7B,+CAA6B;AAC7B,8CAA4B;AAC5B,8CAA4B;AAC5B,8CAA4B;AAC5B,gDAA8B;AAC9B,wDAAsC;AACtC,oEAAkD"}
@@ -0,0 +1,141 @@
1
+ import { isCustomMeasureId, tokenizeFormula, parseFormula, collectFormulaMeasures, evaluateFormulaAst, validateFormula, } from '../../../libs/custom-measure-formula/index.js';
2
+ const SAMPLE_ULID = '01ARZ3NDEKTSV4RRFFQ69G5FAV';
3
+ describe('custom measure formula utils', () => {
4
+ describe('isCustomMeasureId', () => {
5
+ it('should detect bare-ULID custom measure ids and reject native dotted ids', () => {
6
+ expect(isCustomMeasureId(SAMPLE_ULID)).toBe(true);
7
+ expect(isCustomMeasureId('orders.net_revenue')).toBe(false);
8
+ expect(isCustomMeasureId('channel.spend')).toBe(false);
9
+ expect(isCustomMeasureId('custom.1234')).toBe(false);
10
+ expect(isCustomMeasureId(SAMPLE_ULID.toLowerCase())).toBe(false);
11
+ expect(isCustomMeasureId(undefined)).toBe(false);
12
+ expect(isCustomMeasureId('')).toBe(false);
13
+ });
14
+ });
15
+ describe('tokenizeFormula', () => {
16
+ it('should tokenize measures, numbers, operators and parens', () => {
17
+ const tokens = tokenizeFormula('(orders.net_revenue - channel.spend) / channel.spend * 100');
18
+ expect(tokens.map((t) => t.type)).toEqual([
19
+ 'lparen',
20
+ 'measure',
21
+ 'operator',
22
+ 'measure',
23
+ 'rparen',
24
+ 'operator',
25
+ 'measure',
26
+ 'operator',
27
+ 'number',
28
+ ]);
29
+ expect(tokens[1].value).toBe('orders.net_revenue');
30
+ expect(tokens[8].value).toBe('100');
31
+ });
32
+ it('should tokenize decimal numbers and dotted measures ending in digits', () => {
33
+ const tokens = tokenizeFormula('orders.cm3 * 0.3');
34
+ expect(tokens.map((t) => t.value)).toEqual(['orders.cm3', '*', '0.3']);
35
+ });
36
+ it('should reject invalid characters', () => {
37
+ expect(() => tokenizeFormula('orders.revenue & 2')).toThrow(/Invalid character/);
38
+ });
39
+ it('should reject non-dotted identifiers', () => {
40
+ expect(() => tokenizeFormula('revenue + 1')).toThrow(/Invalid measure reference/);
41
+ });
42
+ it('should reject malformed numbers', () => {
43
+ expect(() => tokenizeFormula('1.2.3 + orders.count')).toThrow(/Invalid number/);
44
+ });
45
+ it('should reject an empty formula', () => {
46
+ expect(() => tokenizeFormula(' ')).toThrow(/empty/);
47
+ });
48
+ });
49
+ describe('parseFormula', () => {
50
+ it('should honour operator precedence', () => {
51
+ const ast = parseFormula('orders.a + orders.b * orders.c');
52
+ expect(ast).toEqual({
53
+ type: 'binary',
54
+ operator: '+',
55
+ left: { type: 'measure', measure: 'orders.a' },
56
+ right: {
57
+ type: 'binary',
58
+ operator: '*',
59
+ left: { type: 'measure', measure: 'orders.b' },
60
+ right: { type: 'measure', measure: 'orders.c' },
61
+ },
62
+ });
63
+ });
64
+ it('should honour parentheses and unary minus', () => {
65
+ const ast = parseFormula('-(orders.a - orders.b)');
66
+ expect(ast).toEqual({
67
+ type: 'unary',
68
+ operator: '-',
69
+ operand: {
70
+ type: 'binary',
71
+ operator: '-',
72
+ left: { type: 'measure', measure: 'orders.a' },
73
+ right: { type: 'measure', measure: 'orders.b' },
74
+ },
75
+ });
76
+ });
77
+ it('should reject unbalanced parentheses', () => {
78
+ expect(() => parseFormula('(orders.a + orders.b')).toThrow(/Unbalanced parentheses/);
79
+ });
80
+ it('should reject trailing operators', () => {
81
+ expect(() => parseFormula('orders.a +')).toThrow(/Unexpected end of formula/);
82
+ });
83
+ it('should reject dangling tokens', () => {
84
+ expect(() => parseFormula('orders.a orders.b')).toThrow(/Unexpected token/);
85
+ });
86
+ });
87
+ describe('collectFormulaMeasures', () => {
88
+ it('should return unique measures in order', () => {
89
+ const ast = parseFormula('(orders.net_revenue - channel.spend) / channel.spend');
90
+ expect(collectFormulaMeasures(ast)).toEqual(['orders.net_revenue', 'channel.spend']);
91
+ });
92
+ });
93
+ describe('evaluateFormulaAst', () => {
94
+ const values = new Map([
95
+ ['orders.net_revenue', 1000],
96
+ ['channel.spend', 250],
97
+ ['orders.count', 0],
98
+ ]);
99
+ it('should evaluate a ratio with percentage scaling', () => {
100
+ const ast = parseFormula('(orders.net_revenue - channel.spend) / channel.spend * 100');
101
+ expect(evaluateFormulaAst(ast, values)).toBe(300);
102
+ });
103
+ it('should return 0 on division by zero (matches SQL nullif semantics)', () => {
104
+ const ast = parseFormula('orders.net_revenue / orders.count');
105
+ expect(evaluateFormulaAst(ast, values)).toBe(0);
106
+ });
107
+ it('should treat a missing operand as 0 (matches SQL COALESCE semantics)', () => {
108
+ const ast = parseFormula('orders.net_revenue - orders.unknown');
109
+ expect(evaluateFormulaAst(ast, values)).toBe(1000);
110
+ });
111
+ });
112
+ describe('validateFormula', () => {
113
+ const knownOperands = new Set(['orders.net_revenue', 'channel.spend', 'orders.count']);
114
+ it('should accept a valid formula', () => {
115
+ const result = validateFormula('(orders.net_revenue - channel.spend) / channel.spend * 100', { knownOperands });
116
+ expect(result.valid).toBe(true);
117
+ expect(result.errors).toEqual([]);
118
+ expect(result.ast).toBeDefined();
119
+ });
120
+ it('should reject an unknown operand', () => {
121
+ const result = validateFormula('orders.net_revenue / orders.mystery', { knownOperands });
122
+ expect(result.valid).toBe(false);
123
+ expect(result.errors[0]).toMatch(/Unknown or unsupported measure/);
124
+ });
125
+ it('should reject a pure-constant formula', () => {
126
+ const result = validateFormula('1 + 2', { knownOperands });
127
+ expect(result.valid).toBe(false);
128
+ expect(result.errors[0]).toMatch(/at least one measure/);
129
+ });
130
+ it('should surface syntax errors', () => {
131
+ const result = validateFormula('(orders.count + ', { knownOperands });
132
+ expect(result.valid).toBe(false);
133
+ expect(result.errors.length).toBe(1);
134
+ });
135
+ it('should run syntactic-only when no operand predicate is supplied', () => {
136
+ const result = validateFormula('orders.net_revenue / channel.spend');
137
+ expect(result.valid).toBe(true);
138
+ });
139
+ });
140
+ });
141
+ //# sourceMappingURL=custom-measure-formula-utils.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-utils.spec.js","sourceRoot":"","sources":["../../../../../src/__tests__/libs/custom-measure-formula/custom-measure-formula-utils.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ export declare enum FORMULA_OPERATOR {
2
+ ADD = "+",
3
+ SUBTRACT = "-",
4
+ MULTIPLY = "*",
5
+ DIVIDE = "/"
6
+ }
7
+ export declare enum FORMULA_TOKEN_TYPE {
8
+ NUMBER = "number",
9
+ MEASURE = "measure",
10
+ OPERATOR = "operator",
11
+ LPAREN = "lparen",
12
+ RPAREN = "rparen"
13
+ }
14
+ export declare enum FORMULA_NODE_TYPE {
15
+ NUMBER = "number",
16
+ MEASURE = "measure",
17
+ UNARY = "unary",
18
+ BINARY = "binary"
19
+ }
20
+ export interface FormulaToken {
21
+ type: FORMULA_TOKEN_TYPE;
22
+ value: string;
23
+ }
24
+ export type FormulaAst = {
25
+ type: FORMULA_NODE_TYPE.NUMBER;
26
+ value: number;
27
+ } | {
28
+ type: FORMULA_NODE_TYPE.MEASURE;
29
+ measure: string;
30
+ } | {
31
+ type: FORMULA_NODE_TYPE.UNARY;
32
+ operator: FORMULA_OPERATOR.SUBTRACT;
33
+ operand: FormulaAst;
34
+ } | {
35
+ type: FORMULA_NODE_TYPE.BINARY;
36
+ operator: FORMULA_OPERATOR;
37
+ left: FormulaAst;
38
+ right: FormulaAst;
39
+ };
40
+ export interface FormulaValidationOptions {
41
+ knownOperands?: ReadonlySet<string>;
42
+ }
43
+ export interface FormulaValidationResult {
44
+ valid: boolean;
45
+ errors: string[];
46
+ ast?: FormulaAst;
47
+ }
@@ -0,0 +1,23 @@
1
+ export var FORMULA_OPERATOR;
2
+ (function (FORMULA_OPERATOR) {
3
+ FORMULA_OPERATOR["ADD"] = "+";
4
+ FORMULA_OPERATOR["SUBTRACT"] = "-";
5
+ FORMULA_OPERATOR["MULTIPLY"] = "*";
6
+ FORMULA_OPERATOR["DIVIDE"] = "/";
7
+ })(FORMULA_OPERATOR || (FORMULA_OPERATOR = {}));
8
+ export var FORMULA_TOKEN_TYPE;
9
+ (function (FORMULA_TOKEN_TYPE) {
10
+ FORMULA_TOKEN_TYPE["NUMBER"] = "number";
11
+ FORMULA_TOKEN_TYPE["MEASURE"] = "measure";
12
+ FORMULA_TOKEN_TYPE["OPERATOR"] = "operator";
13
+ FORMULA_TOKEN_TYPE["LPAREN"] = "lparen";
14
+ FORMULA_TOKEN_TYPE["RPAREN"] = "rparen";
15
+ })(FORMULA_TOKEN_TYPE || (FORMULA_TOKEN_TYPE = {}));
16
+ export var FORMULA_NODE_TYPE;
17
+ (function (FORMULA_NODE_TYPE) {
18
+ FORMULA_NODE_TYPE["NUMBER"] = "number";
19
+ FORMULA_NODE_TYPE["MEASURE"] = "measure";
20
+ FORMULA_NODE_TYPE["UNARY"] = "unary";
21
+ FORMULA_NODE_TYPE["BINARY"] = "binary";
22
+ })(FORMULA_NODE_TYPE || (FORMULA_NODE_TYPE = {}));
23
+ //# sourceMappingURL=custom-measure-formula-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-types.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/custom-measure-formula-types.ts"],"names":[],"mappings":"AAGA,MAAM,CAAN,IAAY,gBAKX;AALD,WAAY,gBAAgB;IAC1B,6BAAS,CAAA;IACT,kCAAc,CAAA;IACd,kCAAc,CAAA;IACd,gCAAY,CAAA;AACd,CAAC,EALW,gBAAgB,KAAhB,gBAAgB,QAK3B;AAED,MAAM,CAAN,IAAY,kBAMX;AAND,WAAY,kBAAkB;IAC5B,uCAAiB,CAAA;IACjB,yCAAmB,CAAA;IACnB,2CAAqB,CAAA;IACrB,uCAAiB,CAAA;IACjB,uCAAiB,CAAA;AACnB,CAAC,EANW,kBAAkB,KAAlB,kBAAkB,QAM7B;AAED,MAAM,CAAN,IAAY,iBAKX;AALD,WAAY,iBAAiB;IAC3B,sCAAiB,CAAA;IACjB,wCAAmB,CAAA;IACnB,oCAAe,CAAA;IACf,sCAAiB,CAAA;AACnB,CAAC,EALW,iBAAiB,KAAjB,iBAAiB,QAK5B"}
@@ -0,0 +1,7 @@
1
+ import { FormulaAst, FormulaToken, FormulaValidationOptions, FormulaValidationResult } from './custom-measure-formula-types.js';
2
+ export declare const isCustomMeasureId: (id?: string) => boolean;
3
+ export declare const tokenizeFormula: (formula: string) => FormulaToken[];
4
+ export declare const parseFormula: (formula: string) => FormulaAst;
5
+ export declare const collectFormulaMeasures: (ast: FormulaAst) => string[];
6
+ export declare const evaluateFormulaAst: (ast: FormulaAst, operandValues: ReadonlyMap<string, number | null | undefined>) => number;
7
+ export declare const validateFormula: (formula: string, options?: FormulaValidationOptions) => FormulaValidationResult;
@@ -0,0 +1,191 @@
1
+ import { FORMULA_NODE_TYPE, FORMULA_OPERATOR, FORMULA_TOKEN_TYPE, } from './custom-measure-formula-types.js';
2
+ const ULID_PATTERN = /^[0-9A-HJKMNP-TV-Z]{26}$/;
3
+ const NUMBER_PATTERN = /^[0-9]+(\.[0-9]+)?$/;
4
+ const MEASURE_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/;
5
+ const ALLOWED_CHARS_PATTERN = /[^a-z0-9_.+\-*/()\s]/;
6
+ export const isCustomMeasureId = (id) => {
7
+ return !!id && ULID_PATTERN.test(id);
8
+ };
9
+ export const tokenizeFormula = (formula) => {
10
+ if (!formula || formula.trim().length === 0) {
11
+ throw new Error('Formula is empty');
12
+ }
13
+ const invalidChar = formula.match(ALLOWED_CHARS_PATTERN);
14
+ if (invalidChar) {
15
+ throw new Error(`Invalid character in formula: '${invalidChar[0]}'`);
16
+ }
17
+ const tokens = [];
18
+ let i = 0;
19
+ while (i < formula.length) {
20
+ const char = formula[i];
21
+ if (/\s/.test(char)) {
22
+ i += 1;
23
+ continue;
24
+ }
25
+ if (char === '(' || char === ')') {
26
+ tokens.push({ type: char === '(' ? FORMULA_TOKEN_TYPE.LPAREN : FORMULA_TOKEN_TYPE.RPAREN, value: char });
27
+ i += 1;
28
+ continue;
29
+ }
30
+ if (char === '+' || char === '-' || char === '*' || char === '/') {
31
+ tokens.push({ type: FORMULA_TOKEN_TYPE.OPERATOR, value: char });
32
+ i += 1;
33
+ continue;
34
+ }
35
+ if (char >= '0' && char <= '9') {
36
+ let value = '';
37
+ while (i < formula.length && /[0-9.]/.test(formula[i])) {
38
+ value += formula[i];
39
+ i += 1;
40
+ }
41
+ if (!NUMBER_PATTERN.test(value)) {
42
+ throw new Error(`Invalid number in formula: '${value}'`);
43
+ }
44
+ tokens.push({ type: FORMULA_TOKEN_TYPE.NUMBER, value });
45
+ continue;
46
+ }
47
+ if (char >= 'a' && char <= 'z') {
48
+ let value = '';
49
+ while (i < formula.length && /[a-z0-9_.]/.test(formula[i])) {
50
+ value += formula[i];
51
+ i += 1;
52
+ }
53
+ if (!MEASURE_PATTERN.test(value)) {
54
+ throw new Error(`Invalid measure reference in formula: '${value}'`);
55
+ }
56
+ tokens.push({ type: FORMULA_TOKEN_TYPE.MEASURE, value });
57
+ continue;
58
+ }
59
+ throw new Error(`Invalid character in formula: '${char}'`);
60
+ }
61
+ return tokens;
62
+ };
63
+ export const parseFormula = (formula) => {
64
+ const tokens = tokenizeFormula(formula);
65
+ let pos = 0;
66
+ const peek = () => tokens[pos];
67
+ const parseExpression = () => {
68
+ let left = parseTerm();
69
+ while (peek()?.type === FORMULA_TOKEN_TYPE.OPERATOR && (peek()?.value === '+' || peek()?.value === '-')) {
70
+ const operator = tokens[pos].value;
71
+ pos += 1;
72
+ const right = parseTerm();
73
+ left = { type: FORMULA_NODE_TYPE.BINARY, operator, left, right };
74
+ }
75
+ return left;
76
+ };
77
+ const parseTerm = () => {
78
+ let left = parseFactor();
79
+ while (peek()?.type === FORMULA_TOKEN_TYPE.OPERATOR && (peek()?.value === '*' || peek()?.value === '/')) {
80
+ const operator = tokens[pos].value;
81
+ pos += 1;
82
+ const right = parseFactor();
83
+ left = { type: FORMULA_NODE_TYPE.BINARY, operator, left, right };
84
+ }
85
+ return left;
86
+ };
87
+ const parseFactor = () => {
88
+ const token = peek();
89
+ if (!token) {
90
+ throw new Error('Unexpected end of formula');
91
+ }
92
+ if (token.type === FORMULA_TOKEN_TYPE.OPERATOR && token.value === '-') {
93
+ pos += 1;
94
+ return { type: FORMULA_NODE_TYPE.UNARY, operator: FORMULA_OPERATOR.SUBTRACT, operand: parseFactor() };
95
+ }
96
+ if (token.type === FORMULA_TOKEN_TYPE.NUMBER) {
97
+ pos += 1;
98
+ return { type: FORMULA_NODE_TYPE.NUMBER, value: Number(token.value) };
99
+ }
100
+ if (token.type === FORMULA_TOKEN_TYPE.MEASURE) {
101
+ pos += 1;
102
+ return { type: FORMULA_NODE_TYPE.MEASURE, measure: token.value };
103
+ }
104
+ if (token.type === FORMULA_TOKEN_TYPE.LPAREN) {
105
+ pos += 1;
106
+ const expression = parseExpression();
107
+ if (peek()?.type !== FORMULA_TOKEN_TYPE.RPAREN) {
108
+ throw new Error('Unbalanced parentheses in formula');
109
+ }
110
+ pos += 1;
111
+ return expression;
112
+ }
113
+ throw new Error(`Unexpected token in formula: '${token.value}'`);
114
+ };
115
+ const ast = parseExpression();
116
+ if (pos !== tokens.length) {
117
+ throw new Error(`Unexpected token in formula: '${tokens[pos].value}'`);
118
+ }
119
+ return ast;
120
+ };
121
+ export const collectFormulaMeasures = (ast) => {
122
+ const measures = [];
123
+ const walk = (node) => {
124
+ switch (node.type) {
125
+ case FORMULA_NODE_TYPE.MEASURE:
126
+ if (!measures.includes(node.measure)) {
127
+ measures.push(node.measure);
128
+ }
129
+ break;
130
+ case FORMULA_NODE_TYPE.UNARY:
131
+ walk(node.operand);
132
+ break;
133
+ case FORMULA_NODE_TYPE.BINARY:
134
+ walk(node.left);
135
+ walk(node.right);
136
+ break;
137
+ }
138
+ };
139
+ walk(ast);
140
+ return measures;
141
+ };
142
+ export const evaluateFormulaAst = (ast, operandValues) => {
143
+ switch (ast.type) {
144
+ case FORMULA_NODE_TYPE.NUMBER:
145
+ return ast.value;
146
+ case FORMULA_NODE_TYPE.MEASURE: {
147
+ const value = Number(operandValues.get(ast.measure));
148
+ return Number.isFinite(value) ? value : 0;
149
+ }
150
+ case FORMULA_NODE_TYPE.UNARY:
151
+ return -evaluateFormulaAst(ast.operand, operandValues);
152
+ case FORMULA_NODE_TYPE.BINARY: {
153
+ const left = evaluateFormulaAst(ast.left, operandValues);
154
+ const right = evaluateFormulaAst(ast.right, operandValues);
155
+ switch (ast.operator) {
156
+ case FORMULA_OPERATOR.ADD:
157
+ return left + right;
158
+ case FORMULA_OPERATOR.SUBTRACT:
159
+ return left - right;
160
+ case FORMULA_OPERATOR.MULTIPLY:
161
+ return left * right;
162
+ case FORMULA_OPERATOR.DIVIDE:
163
+ return right === 0 ? 0 : left / right;
164
+ }
165
+ }
166
+ }
167
+ };
168
+ export const validateFormula = (formula, options) => {
169
+ let ast;
170
+ try {
171
+ ast = parseFormula(formula);
172
+ }
173
+ catch (error) {
174
+ return { valid: false, errors: [error.message] };
175
+ }
176
+ const errors = [];
177
+ const measures = collectFormulaMeasures(ast);
178
+ if (measures.length === 0) {
179
+ errors.push('Formula must reference at least one measure');
180
+ }
181
+ measures.forEach((measure) => {
182
+ if (isCustomMeasureId(measure)) {
183
+ errors.push(`Custom measures cannot reference another custom measure: '${measure}'`);
184
+ }
185
+ else if (options?.knownOperands && !options.knownOperands.has(measure)) {
186
+ errors.push(`Unknown or unsupported measure: '${measure}'`);
187
+ }
188
+ });
189
+ return errors.length > 0 ? { valid: false, errors } : { valid: true, errors: [], ast };
190
+ };
191
+ //# sourceMappingURL=custom-measure-formula-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-measure-formula-utils.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/custom-measure-formula-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './custom-measure-formula-types.js';
2
+ export * from './custom-measure-formula-utils.js';
@@ -0,0 +1,3 @@
1
+ export * from './custom-measure-formula-types.js';
2
+ export * from './custom-measure-formula-utils.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/libs/custom-measure-formula/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC"}
@@ -17,3 +17,4 @@ export * from './domain.js';
17
17
  export * from './emails.js';
18
18
  export * from './contacts.js';
19
19
  export * from './api-router/index.js';
20
+ export * from './custom-measure-formula/index.js';
@@ -17,4 +17,5 @@ export * from './domain.js';
17
17
  export * from './emails.js';
18
18
  export * from './contacts.js';
19
19
  export * from './api-router/index.js';
20
+ export * from './custom-measure-formula/index.js';
20
21
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/libs/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/libs/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC;AACtC,cAAc,mCAAmC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adtrackify/at-service-common",
3
- "version": "3.19.2",
3
+ "version": "3.19.4",
4
4
  "description": "",
5
5
  "files": [
6
6
  "dist/*"
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@adtrackify/at-shared-utils": "^3.2.23",
40
- "@adtrackify/at-tracking-event-types": "^4.71.4",
40
+ "@adtrackify/at-tracking-event-types": "^4.71.7",
41
41
  "@aws-sdk/util-utf8": "^3.374.0",
42
42
  "@shopify/admin-api-client": "^1.0.4",
43
43
  "@shopify/shopify-api": "^11.6.1",