@affino/datagrid-formula-engine 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/dist/analysis/index.d.ts +2 -0
  4. package/dist/analysis/index.d.ts.map +1 -0
  5. package/dist/analysis/index.js +1 -0
  6. package/dist/analysis/types.d.ts +44 -0
  7. package/dist/analysis/types.d.ts.map +1 -0
  8. package/dist/analysis/types.js +1 -0
  9. package/dist/contracts.d.ts +177 -0
  10. package/dist/contracts.d.ts.map +1 -0
  11. package/dist/contracts.js +140 -0
  12. package/dist/coreTypes.d.ts +2 -0
  13. package/dist/coreTypes.d.ts.map +1 -0
  14. package/dist/coreTypes.js +1 -0
  15. package/dist/dependency/index.d.ts +2 -0
  16. package/dist/dependency/index.d.ts.map +1 -0
  17. package/dist/dependency/index.js +1 -0
  18. package/dist/evaluators/columnar.d.ts +6 -0
  19. package/dist/evaluators/columnar.d.ts.map +1 -0
  20. package/dist/evaluators/columnar.js +39 -0
  21. package/dist/evaluators/index.d.ts +5 -0
  22. package/dist/evaluators/index.d.ts.map +1 -0
  23. package/dist/evaluators/index.js +4 -0
  24. package/dist/evaluators/interpreter.d.ts +6 -0
  25. package/dist/evaluators/interpreter.d.ts.map +1 -0
  26. package/dist/evaluators/interpreter.js +156 -0
  27. package/dist/evaluators/jit.d.ts +7 -0
  28. package/dist/evaluators/jit.d.ts.map +1 -0
  29. package/dist/evaluators/jit.js +158 -0
  30. package/dist/evaluators/shared.d.ts +36 -0
  31. package/dist/evaluators/shared.d.ts.map +1 -0
  32. package/dist/evaluators/shared.js +242 -0
  33. package/dist/evaluators/vector.d.ts +6 -0
  34. package/dist/evaluators/vector.d.ts.map +1 -0
  35. package/dist/evaluators/vector.js +228 -0
  36. package/dist/formulaEngine/compile.d.ts +2 -0
  37. package/dist/formulaEngine/compile.d.ts.map +1 -0
  38. package/dist/formulaEngine/compile.js +1 -0
  39. package/dist/formulaEngine/core.d.ts +2 -0
  40. package/dist/formulaEngine/core.d.ts.map +1 -0
  41. package/dist/formulaEngine/core.js +1 -0
  42. package/dist/formulaEngine/evaluators.d.ts +2 -0
  43. package/dist/formulaEngine/evaluators.d.ts.map +1 -0
  44. package/dist/formulaEngine/evaluators.js +1 -0
  45. package/dist/formulaEngine/index.d.ts +5 -0
  46. package/dist/formulaEngine/index.d.ts.map +1 -0
  47. package/dist/formulaEngine/index.js +2 -0
  48. package/dist/formulaExecutionPlan.d.ts +2 -0
  49. package/dist/formulaExecutionPlan.d.ts.map +1 -0
  50. package/dist/formulaExecutionPlan.js +1 -0
  51. package/dist/graph/executionPlan.d.ts +66 -0
  52. package/dist/graph/executionPlan.d.ts.map +1 -0
  53. package/dist/graph/executionPlan.js +534 -0
  54. package/dist/graph/index.d.ts +2 -0
  55. package/dist/graph/index.d.ts.map +1 -0
  56. package/dist/graph/index.js +3 -0
  57. package/dist/index.d.ts +8 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +4 -0
  60. package/dist/runtime/compile.d.ts +7 -0
  61. package/dist/runtime/compile.d.ts.map +1 -0
  62. package/dist/runtime/compile.js +453 -0
  63. package/dist/runtime/evaluators.d.ts +5 -0
  64. package/dist/runtime/evaluators.d.ts.map +1 -0
  65. package/dist/runtime/evaluators.js +6 -0
  66. package/dist/runtime/index.d.ts +3 -0
  67. package/dist/runtime/index.d.ts.map +1 -0
  68. package/dist/runtime/index.js +2 -0
  69. package/dist/runtime/types.d.ts +63 -0
  70. package/dist/runtime/types.d.ts.map +1 -0
  71. package/dist/runtime/types.js +1 -0
  72. package/dist/syntax/analysis.d.ts +14 -0
  73. package/dist/syntax/analysis.d.ts.map +1 -0
  74. package/dist/syntax/analysis.js +159 -0
  75. package/dist/syntax/ast.d.ts +18 -0
  76. package/dist/syntax/ast.d.ts.map +1 -0
  77. package/dist/syntax/ast.js +54 -0
  78. package/dist/syntax/core.d.ts +4 -0
  79. package/dist/syntax/core.d.ts.map +1 -0
  80. package/dist/syntax/core.js +1 -0
  81. package/dist/syntax/functionGroups/advancedFunctions.d.ts +2 -0
  82. package/dist/syntax/functionGroups/advancedFunctions.d.ts.map +1 -0
  83. package/dist/syntax/functionGroups/advancedFunctions.js +252 -0
  84. package/dist/syntax/functionGroups/dateFunctions.d.ts +2 -0
  85. package/dist/syntax/functionGroups/dateFunctions.d.ts.map +1 -0
  86. package/dist/syntax/functionGroups/dateFunctions.js +144 -0
  87. package/dist/syntax/functionGroups/logicFunctions.d.ts +2 -0
  88. package/dist/syntax/functionGroups/logicFunctions.d.ts.map +1 -0
  89. package/dist/syntax/functionGroups/logicFunctions.js +140 -0
  90. package/dist/syntax/functionGroups/numericFunctions.d.ts +2 -0
  91. package/dist/syntax/functionGroups/numericFunctions.d.ts.map +1 -0
  92. package/dist/syntax/functionGroups/numericFunctions.js +268 -0
  93. package/dist/syntax/functionGroups/textFunctions.d.ts +2 -0
  94. package/dist/syntax/functionGroups/textFunctions.d.ts.map +1 -0
  95. package/dist/syntax/functionGroups/textFunctions.js +118 -0
  96. package/dist/syntax/functionHelpers.d.ts +45 -0
  97. package/dist/syntax/functionHelpers.d.ts.map +1 -0
  98. package/dist/syntax/functionHelpers.js +553 -0
  99. package/dist/syntax/functions.d.ts +9 -0
  100. package/dist/syntax/functions.d.ts.map +1 -0
  101. package/dist/syntax/functions.js +139 -0
  102. package/dist/syntax/index.d.ts +10 -0
  103. package/dist/syntax/index.d.ts.map +1 -0
  104. package/dist/syntax/index.js +7 -0
  105. package/dist/syntax/legacy.d.ts +29 -0
  106. package/dist/syntax/legacy.d.ts.map +1 -0
  107. package/dist/syntax/legacy.js +188 -0
  108. package/dist/syntax/optimizer.d.ts +6 -0
  109. package/dist/syntax/optimizer.d.ts.map +1 -0
  110. package/dist/syntax/optimizer.js +362 -0
  111. package/dist/syntax/parser.d.ts +7 -0
  112. package/dist/syntax/parser.d.ts.map +1 -0
  113. package/dist/syntax/parser.js +239 -0
  114. package/dist/syntax/tokenizer.d.ts +14 -0
  115. package/dist/syntax/tokenizer.d.ts.map +1 -0
  116. package/dist/syntax/tokenizer.js +852 -0
  117. package/dist/syntax/types.d.ts +120 -0
  118. package/dist/syntax/types.d.ts.map +1 -0
  119. package/dist/syntax/types.js +1 -0
  120. package/dist/syntax/values.d.ts +25 -0
  121. package/dist/syntax/values.d.ts.map +1 -0
  122. package/dist/syntax/values.js +270 -0
  123. package/dist/tsconfig.tsbuildinfo +1 -0
  124. package/package.json +42 -0
@@ -0,0 +1,553 @@
1
+ import { areFormulaValuesEqual, compareFormulaValues, coerceFormulaValueToNumber, isFormulaErrorValue, isFormulaValuePresent, } from "./values.js";
2
+ import { parseFormulaReferenceSegments } from "./tokenizer.js";
3
+ const formulaTableLookupIndexCache = new WeakMap();
4
+ function isFormulaTableRowsSource(value) {
5
+ return typeof value === "object"
6
+ && value !== null
7
+ && Array.isArray(value.rows);
8
+ }
9
+ function resolveFormulaTableRowsInput(rowsInput) {
10
+ if (Array.isArray(rowsInput)) {
11
+ return {
12
+ cacheKey: rowsInput,
13
+ rows: rowsInput,
14
+ };
15
+ }
16
+ if (isFormulaTableRowsSource(rowsInput)) {
17
+ return {
18
+ cacheKey: rowsInput,
19
+ rows: rowsInput.rows,
20
+ resolveRow: typeof rowsInput.resolveRow === "function"
21
+ ? rowsInput.resolveRow
22
+ : undefined,
23
+ };
24
+ }
25
+ return null;
26
+ }
27
+ export function normalizeFormulaTableName(value) {
28
+ var _a;
29
+ if (Array.isArray(value)) {
30
+ return normalizeFormulaTableName((_a = value[0]) !== null && _a !== void 0 ? _a : null);
31
+ }
32
+ return String(value !== null && value !== void 0 ? value : "").trim().toLowerCase();
33
+ }
34
+ export function coerceFormulaValueToText(value) {
35
+ const scalar = coerceFormulaValueToScalar(value);
36
+ if (scalar === null || isFormulaErrorValue(scalar)) {
37
+ return "";
38
+ }
39
+ if (scalar instanceof Date) {
40
+ return Number.isNaN(scalar.getTime()) ? "" : scalar.toISOString();
41
+ }
42
+ return String(scalar);
43
+ }
44
+ export function createFormulaTableContextKey(tableName) {
45
+ const normalized = tableName.trim().toLowerCase();
46
+ return normalized.length === 0 ? "tables" : `table:${normalized}`;
47
+ }
48
+ export function resolveFormulaLiteralText(node) {
49
+ if (!node) {
50
+ return null;
51
+ }
52
+ if (node.kind === "literal" && typeof node.value === "string") {
53
+ return node.value;
54
+ }
55
+ if (node.kind === "number") {
56
+ return String(node.value);
57
+ }
58
+ return null;
59
+ }
60
+ export function defineFormulaFunctions(entries) {
61
+ return entries;
62
+ }
63
+ function coerceFormulaValueToScalar(value) {
64
+ var _a;
65
+ if (Array.isArray(value)) {
66
+ return (_a = value[0]) !== null && _a !== void 0 ? _a : null;
67
+ }
68
+ return value;
69
+ }
70
+ function coerceUnknownToFormulaScalar(value) {
71
+ if (value === null || typeof value === "undefined") {
72
+ return null;
73
+ }
74
+ if (isFormulaErrorValue(value)) {
75
+ return value;
76
+ }
77
+ if (typeof value === "number") {
78
+ return Number.isFinite(value) ? value : 0;
79
+ }
80
+ if (typeof value === "string" || typeof value === "boolean") {
81
+ return value;
82
+ }
83
+ if (value instanceof Date) {
84
+ return Number.isNaN(value.getTime()) ? null : value;
85
+ }
86
+ return null;
87
+ }
88
+ function normalizeFormulaLookupKey(value) {
89
+ const scalar = coerceUnknownToFormulaScalar(value);
90
+ if (scalar === null || isFormulaErrorValue(scalar)) {
91
+ return null;
92
+ }
93
+ if (scalar instanceof Date) {
94
+ return Number.isNaN(scalar.getTime()) ? null : `n:${scalar.getTime()}`;
95
+ }
96
+ if (typeof scalar === "number") {
97
+ return `n:${Number.isFinite(scalar) ? scalar : 0}`;
98
+ }
99
+ if (typeof scalar === "boolean") {
100
+ return `n:${scalar ? 1 : 0}`;
101
+ }
102
+ const numeric = scalar.trim().length > 0 ? Number(scalar) : Number.NaN;
103
+ if (Number.isFinite(numeric)) {
104
+ return `n:${numeric}`;
105
+ }
106
+ return `s:${scalar}`;
107
+ }
108
+ function readNestedFormulaTableValue(value, segments) {
109
+ let current = value;
110
+ for (const segment of segments) {
111
+ if (current === null || typeof current === "undefined") {
112
+ return null;
113
+ }
114
+ if (Array.isArray(current) && typeof segment === "number") {
115
+ current = current[segment];
116
+ continue;
117
+ }
118
+ if (typeof current === "object" && current !== null) {
119
+ current = current[segment];
120
+ continue;
121
+ }
122
+ return null;
123
+ }
124
+ return current;
125
+ }
126
+ function resolveFormulaTableFieldSegments(fieldValue) {
127
+ const field = typeof fieldValue === "undefined"
128
+ ? ""
129
+ : coerceFormulaValueToText(fieldValue).trim();
130
+ return field.length > 0 ? parseFormulaReferenceSegments(field) : [];
131
+ }
132
+ function resolveFormulaTableRowValue(input, row, index) {
133
+ return input.resolveRow
134
+ ? input.resolveRow(row, index)
135
+ : row;
136
+ }
137
+ function resolveFormulaTableFieldValue(rowValue, path) {
138
+ return path.length > 0
139
+ ? readNestedFormulaTableValue(rowValue, path)
140
+ : rowValue;
141
+ }
142
+ export function collectFormulaTableValues(rowsInput, fieldValue) {
143
+ const input = resolveFormulaTableRowsInput(rowsInput);
144
+ if (!input) {
145
+ return Object.freeze([]);
146
+ }
147
+ const path = resolveFormulaTableFieldSegments(fieldValue);
148
+ const values = [];
149
+ for (let index = 0; index < input.rows.length; index += 1) {
150
+ const row = input.rows[index];
151
+ const rowValue = resolveFormulaTableRowValue(input, row, index);
152
+ const resolved = resolveFormulaTableFieldValue(rowValue, path);
153
+ if (Array.isArray(resolved)) {
154
+ for (const entry of resolved) {
155
+ values.push(coerceUnknownToFormulaScalar(entry));
156
+ }
157
+ continue;
158
+ }
159
+ values.push(coerceUnknownToFormulaScalar(resolved));
160
+ }
161
+ return Object.freeze(values);
162
+ }
163
+ export function findFormulaTableRelatedRows(rowsInput, keyFieldValue, lookupValue) {
164
+ var _a, _b;
165
+ const input = resolveFormulaTableRowsInput(rowsInput);
166
+ if (!input) {
167
+ return Object.freeze([]);
168
+ }
169
+ const lookupKey = normalizeFormulaLookupKey(Array.isArray(lookupValue) ? (_a = lookupValue[0]) !== null && _a !== void 0 ? _a : null : lookupValue);
170
+ if (lookupKey === null) {
171
+ return Object.freeze([]);
172
+ }
173
+ const keyPath = resolveFormulaTableFieldSegments(keyFieldValue);
174
+ const cacheFieldKey = keyPath.length === 0
175
+ ? ""
176
+ : keyPath.map(segment => String(segment)).join(".");
177
+ let indexesByField = formulaTableLookupIndexCache.get(input.cacheKey);
178
+ if (!indexesByField) {
179
+ indexesByField = new Map();
180
+ formulaTableLookupIndexCache.set(input.cacheKey, indexesByField);
181
+ }
182
+ let lookupIndex = indexesByField.get(cacheFieldKey);
183
+ if (!lookupIndex) {
184
+ const nextLookupIndex = new Map();
185
+ for (let index = 0; index < input.rows.length; index += 1) {
186
+ const row = input.rows[index];
187
+ const rowValue = resolveFormulaTableRowValue(input, row, index);
188
+ const keyValue = resolveFormulaTableFieldValue(rowValue, keyPath);
189
+ const normalizedKey = normalizeFormulaLookupKey(keyValue);
190
+ if (normalizedKey === null) {
191
+ continue;
192
+ }
193
+ const bucket = nextLookupIndex.get(normalizedKey);
194
+ if (bucket) {
195
+ bucket.push(rowValue);
196
+ continue;
197
+ }
198
+ nextLookupIndex.set(normalizedKey, [rowValue]);
199
+ }
200
+ lookupIndex = new Map(nextLookupIndex);
201
+ indexesByField.set(cacheFieldKey, lookupIndex);
202
+ }
203
+ return (_b = lookupIndex.get(lookupKey)) !== null && _b !== void 0 ? _b : Object.freeze([]);
204
+ }
205
+ export function collectFormulaTableRelatedValues(rowsInput, keyFieldValue, lookupValue, returnFieldValue) {
206
+ const relatedRows = findFormulaTableRelatedRows(rowsInput, keyFieldValue, lookupValue);
207
+ if (relatedRows.length === 0) {
208
+ return Object.freeze([]);
209
+ }
210
+ const returnPath = resolveFormulaTableFieldSegments(returnFieldValue);
211
+ const values = [];
212
+ for (const relatedRow of relatedRows) {
213
+ const resolved = resolveFormulaTableFieldValue(relatedRow, returnPath);
214
+ if (Array.isArray(resolved)) {
215
+ for (const entry of resolved) {
216
+ values.push(coerceUnknownToFormulaScalar(entry));
217
+ }
218
+ continue;
219
+ }
220
+ values.push(coerceUnknownToFormulaScalar(resolved));
221
+ }
222
+ return Object.freeze(values);
223
+ }
224
+ export function expandFormulaValue(value) {
225
+ if (Array.isArray(value)) {
226
+ return value;
227
+ }
228
+ return [value];
229
+ }
230
+ export function expandFormulaArgs(args) {
231
+ const expanded = [];
232
+ for (const value of args) {
233
+ expanded.push(...expandFormulaValue(value));
234
+ }
235
+ return expanded;
236
+ }
237
+ export function coerceFormulaValueToDate(value) {
238
+ const scalarValue = coerceFormulaValueToScalar(value);
239
+ if (scalarValue === null) {
240
+ return null;
241
+ }
242
+ if (isFormulaErrorValue(scalarValue)) {
243
+ return null;
244
+ }
245
+ if (scalarValue instanceof Date) {
246
+ return Number.isNaN(scalarValue.getTime()) ? null : scalarValue;
247
+ }
248
+ if (typeof scalarValue === "number") {
249
+ if (!Number.isFinite(scalarValue)) {
250
+ return null;
251
+ }
252
+ const date = new Date(scalarValue);
253
+ return Number.isNaN(date.getTime()) ? null : date;
254
+ }
255
+ if (typeof scalarValue === "string") {
256
+ const text = scalarValue.trim();
257
+ if (text.length === 0) {
258
+ return null;
259
+ }
260
+ const timestamp = Date.parse(text);
261
+ if (!Number.isFinite(timestamp)) {
262
+ return null;
263
+ }
264
+ const date = new Date(timestamp);
265
+ return Number.isNaN(date.getTime()) ? null : date;
266
+ }
267
+ return null;
268
+ }
269
+ export function stringifyFormulaScalarValue(value) {
270
+ if (value === null) {
271
+ return "";
272
+ }
273
+ if (value instanceof Date) {
274
+ return Number.isNaN(value.getTime()) ? "" : value.toISOString();
275
+ }
276
+ if (isFormulaErrorValue(value)) {
277
+ return value.message;
278
+ }
279
+ return String(value);
280
+ }
281
+ export function flattenFormulaValuesToStrings(values) {
282
+ return expandFormulaArgs(values).map(value => stringifyFormulaScalarValue(value));
283
+ }
284
+ function tryCoerceComparableNumber(value) {
285
+ const scalarValue = coerceFormulaValueToScalar(value);
286
+ if (scalarValue === null) {
287
+ return null;
288
+ }
289
+ if (isFormulaErrorValue(scalarValue)) {
290
+ return null;
291
+ }
292
+ if (typeof scalarValue === "number") {
293
+ return Number.isFinite(scalarValue) ? scalarValue : 0;
294
+ }
295
+ if (typeof scalarValue === "boolean") {
296
+ return scalarValue ? 1 : 0;
297
+ }
298
+ if (scalarValue instanceof Date) {
299
+ return Number.isNaN(scalarValue.getTime()) ? null : scalarValue.getTime();
300
+ }
301
+ const text = scalarValue.trim();
302
+ if (text.length === 0) {
303
+ return null;
304
+ }
305
+ const parsed = Number(text);
306
+ return Number.isFinite(parsed) ? parsed : null;
307
+ }
308
+ function normalizeCriterionOperand(text) {
309
+ const trimmed = text.trim();
310
+ if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
311
+ return trimmed.slice(1, -1);
312
+ }
313
+ return trimmed;
314
+ }
315
+ export function formulaValueMatchesCriterion(candidate, criterion) {
316
+ var _a, _b;
317
+ if (typeof criterion !== "string") {
318
+ return areFormulaValuesEqual(candidate, criterion);
319
+ }
320
+ const trimmedCriterion = criterion.trim();
321
+ const match = /^(<=|>=|<>|!=|=|<|>)(.*)$/.exec(trimmedCriterion);
322
+ const operator = (_a = match === null || match === void 0 ? void 0 : match[1]) !== null && _a !== void 0 ? _a : "=";
323
+ const rawOperand = normalizeCriterionOperand((_b = match === null || match === void 0 ? void 0 : match[2]) !== null && _b !== void 0 ? _b : trimmedCriterion);
324
+ const operandValue = rawOperand;
325
+ if (operator === "=" && !match) {
326
+ return areFormulaValuesEqual(candidate, operandValue);
327
+ }
328
+ if (operator === "=" || operator === "<>" || operator === "!=") {
329
+ const equals = areFormulaValuesEqual(candidate, operandValue);
330
+ return operator === "=" ? equals : !equals;
331
+ }
332
+ const compared = (() => {
333
+ const left = tryCoerceComparableNumber(candidate);
334
+ const right = tryCoerceComparableNumber(operandValue);
335
+ if (left !== null && right !== null) {
336
+ return left - right;
337
+ }
338
+ const candidateDate = coerceFormulaValueToDate(candidate);
339
+ const operandDate = coerceFormulaValueToDate(operandValue);
340
+ if (candidateDate && operandDate) {
341
+ return candidateDate.getTime() - operandDate.getTime();
342
+ }
343
+ return compareFormulaValues(candidate, operandValue);
344
+ })();
345
+ switch (operator) {
346
+ case ">":
347
+ return compared > 0;
348
+ case ">=":
349
+ return compared >= 0;
350
+ case "<":
351
+ return compared < 0;
352
+ case "<=":
353
+ return compared <= 0;
354
+ default:
355
+ return false;
356
+ }
357
+ }
358
+ export function collectFilteredValues(values, criteria) {
359
+ var _a;
360
+ const filtered = [];
361
+ for (let index = 0; index < values.length; index += 1) {
362
+ const matches = criteria.every(({ range, criterion }) => { var _a; return formulaValueMatchesCriterion((_a = range[index]) !== null && _a !== void 0 ? _a : null, criterion); });
363
+ if (matches) {
364
+ filtered.push((_a = values[index]) !== null && _a !== void 0 ? _a : null);
365
+ }
366
+ }
367
+ return filtered;
368
+ }
369
+ export function toDistinctFormulaValues(values) {
370
+ const distinct = [];
371
+ for (const value of values) {
372
+ if (distinct.some(entry => areFormulaValuesEqual(entry, value))) {
373
+ continue;
374
+ }
375
+ distinct.push(value);
376
+ }
377
+ return Object.freeze(distinct);
378
+ }
379
+ export function toNumericFormulaValues(values) {
380
+ return values.map(value => coerceFormulaValueToNumber(value !== null && value !== void 0 ? value : null));
381
+ }
382
+ export function toStrictNumericFormulaValues(values) {
383
+ const numeric = [];
384
+ for (const value of values) {
385
+ if (typeof value === "number" && Number.isFinite(value)) {
386
+ numeric.push(value);
387
+ continue;
388
+ }
389
+ if (typeof value === "boolean") {
390
+ numeric.push(value ? 1 : 0);
391
+ continue;
392
+ }
393
+ if (value instanceof Date && !Number.isNaN(value.getTime())) {
394
+ numeric.push(value.getTime());
395
+ }
396
+ }
397
+ return numeric;
398
+ }
399
+ export function computeAverage(values) {
400
+ if (values.length === 0) {
401
+ return 0;
402
+ }
403
+ const numeric = toNumericFormulaValues(values);
404
+ const total = numeric.reduce((sum, value) => sum + value, 0);
405
+ return total / numeric.length;
406
+ }
407
+ export function computeMedian(values) {
408
+ var _a, _b, _c;
409
+ const numeric = [...toNumericFormulaValues(values)].sort((left, right) => left - right);
410
+ if (numeric.length === 0) {
411
+ return 0;
412
+ }
413
+ const middle = Math.floor(numeric.length / 2);
414
+ if (numeric.length % 2 === 0) {
415
+ const left = (_a = numeric[middle - 1]) !== null && _a !== void 0 ? _a : 0;
416
+ const right = (_b = numeric[middle]) !== null && _b !== void 0 ? _b : left;
417
+ return (left + right) / 2;
418
+ }
419
+ return (_c = numeric[middle]) !== null && _c !== void 0 ? _c : 0;
420
+ }
421
+ export function computePercentile(values, percentile) {
422
+ var _a, _b, _c;
423
+ const numeric = [...toNumericFormulaValues(values)].sort((left, right) => left - right);
424
+ if (numeric.length === 0) {
425
+ return 0;
426
+ }
427
+ const clampedPercentile = Math.max(0, Math.min(1, percentile));
428
+ const position = (numeric.length - 1) * clampedPercentile;
429
+ const lowerIndex = Math.floor(position);
430
+ const upperIndex = Math.ceil(position);
431
+ if (lowerIndex === upperIndex) {
432
+ return (_a = numeric[lowerIndex]) !== null && _a !== void 0 ? _a : 0;
433
+ }
434
+ const lower = (_b = numeric[lowerIndex]) !== null && _b !== void 0 ? _b : 0;
435
+ const upper = (_c = numeric[upperIndex]) !== null && _c !== void 0 ? _c : lower;
436
+ const weight = position - lowerIndex;
437
+ return lower + (upper - lower) * weight;
438
+ }
439
+ function computeVariance(values, mode) {
440
+ if (values.length === 0 || (mode === "sample" && values.length < 2)) {
441
+ return 0;
442
+ }
443
+ const mean = values.reduce((sum, value) => sum + value, 0) / values.length;
444
+ const squared = values.reduce((sum, value) => sum + ((value - mean) ** 2), 0);
445
+ return squared / (mode === "sample" ? values.length - 1 : values.length);
446
+ }
447
+ export function computeStdDev(values, mode) {
448
+ const numeric = toNumericFormulaValues(values);
449
+ return Math.sqrt(computeVariance(numeric, mode));
450
+ }
451
+ export function isWeekday(date) {
452
+ const day = date.getUTCDay();
453
+ return day !== 0 && day !== 6;
454
+ }
455
+ export function normalizeHolidayTimes(values) {
456
+ const holidayTimes = new Set();
457
+ for (const value of values) {
458
+ for (const entry of expandFormulaValue(value)) {
459
+ const date = coerceFormulaValueToDate(entry);
460
+ if (!date) {
461
+ continue;
462
+ }
463
+ holidayTimes.add(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
464
+ }
465
+ }
466
+ return holidayTimes;
467
+ }
468
+ function stripUtcTime(date) {
469
+ return new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
470
+ }
471
+ export function addCalendarDays(date, days) {
472
+ const next = stripUtcTime(date);
473
+ next.setUTCDate(next.getUTCDate() + days);
474
+ return next;
475
+ }
476
+ export function countDaysBetween(start, end, inclusive) {
477
+ const left = stripUtcTime(start);
478
+ const right = stripUtcTime(end);
479
+ const diff = Math.round((right.getTime() - left.getTime()) / 86400000);
480
+ return inclusive ? diff + 1 : diff;
481
+ }
482
+ export function countWorkingDays(start, end, holidays) {
483
+ const direction = start.getTime() <= end.getTime() ? 1 : -1;
484
+ let cursor = stripUtcTime(start);
485
+ const target = stripUtcTime(end);
486
+ let count = 0;
487
+ while ((direction > 0 && cursor.getTime() <= target.getTime()) || (direction < 0 && cursor.getTime() >= target.getTime())) {
488
+ const time = cursor.getTime();
489
+ if (isWeekday(cursor) && !holidays.has(time)) {
490
+ count += direction;
491
+ }
492
+ cursor = addCalendarDays(cursor, direction);
493
+ }
494
+ return count;
495
+ }
496
+ export function addWorkingDays(start, days, holidays) {
497
+ let remaining = Math.trunc(days);
498
+ let cursor = stripUtcTime(start);
499
+ const direction = remaining >= 0 ? 1 : -1;
500
+ while (remaining !== 0) {
501
+ cursor = addCalendarDays(cursor, direction);
502
+ const time = cursor.getTime();
503
+ if (isWeekday(cursor) && !holidays.has(time)) {
504
+ remaining -= direction;
505
+ }
506
+ }
507
+ return cursor;
508
+ }
509
+ export function countPresentValues(values) {
510
+ return values.reduce((count, value) => (isFormulaValuePresent(value !== null && value !== void 0 ? value : null) ? count + 1 : count), 0);
511
+ }
512
+ export function booleanResult(value) {
513
+ return value;
514
+ }
515
+ export function normalizeTextSearch(value) {
516
+ const scalar = coerceFormulaValueToScalar(value);
517
+ if (scalar === null || isFormulaErrorValue(scalar)) {
518
+ return "";
519
+ }
520
+ return String(scalar).toLowerCase();
521
+ }
522
+ export function collectValuesFromArgs(args, startIndex = 0) {
523
+ return expandFormulaArgs(args.slice(startIndex));
524
+ }
525
+ export function defaultIfEmpty(value, fallback) {
526
+ return typeof value === "undefined" ? fallback : value;
527
+ }
528
+ export function parseTimeLikeValue(value) {
529
+ var _a;
530
+ const scalar = coerceFormulaValueToScalar(value);
531
+ if (scalar === null || isFormulaErrorValue(scalar)) {
532
+ return null;
533
+ }
534
+ if (scalar instanceof Date) {
535
+ return Number.isNaN(scalar.getTime()) ? null : scalar;
536
+ }
537
+ if (typeof scalar === "number") {
538
+ const date = new Date(scalar);
539
+ return Number.isNaN(date.getTime()) ? null : date;
540
+ }
541
+ const text = String(scalar).trim();
542
+ const match = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/.exec(text);
543
+ if (!match) {
544
+ return coerceFormulaValueToDate(text);
545
+ }
546
+ const hours = Number(match[1]);
547
+ const minutes = Number(match[2]);
548
+ const seconds = Number((_a = match[3]) !== null && _a !== void 0 ? _a : 0);
549
+ if (hours > 23 || minutes > 59 || seconds > 59) {
550
+ return null;
551
+ }
552
+ return new Date(Date.UTC(1970, 0, 1, hours, minutes, seconds));
553
+ }
@@ -0,0 +1,9 @@
1
+ import type { DataGridFormulaAstNode, DataGridFormulaCompileOptions, DataGridFormulaFunctionDefinition, DataGridFormulaFunctionRegistry, DataGridFormulaFunctionRuntime as LegacyDataGridFormulaFunctionRuntime } from "./analysis.js";
2
+ export type { DataGridFormulaFunctionArity, DataGridFormulaFunctionDefinition, DataGridFormulaFunctionRegistry, DataGridFormulaFunctionRuntime, } from "./analysis.js";
3
+ export declare function normalizeFormulaText(value: unknown): string;
4
+ export declare function normalizeFormulaFieldName(value: unknown, label: string): string;
5
+ export declare function normalizeFormulaFunctionName(value: string): string;
6
+ export declare function normalizeFormulaFunctionRegistry(input: DataGridFormulaFunctionRegistry | undefined, options?: Pick<DataGridFormulaCompileOptions, "onFunctionOverride">): ReadonlyMap<string, LegacyDataGridFormulaFunctionRuntime>;
7
+ export declare function collectFormulaContextKeys(root: DataGridFormulaAstNode, functionRegistry: ReadonlyMap<string, LegacyDataGridFormulaFunctionRuntime>, output: string[]): void;
8
+ export declare const DATAGRID_DEFAULT_FORMULA_FUNCTIONS: Readonly<Record<string, DataGridFormulaFunctionDefinition>>;
9
+ //# sourceMappingURL=functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../../src/syntax/functions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,sBAAsB,EACtB,6BAA6B,EAE7B,iCAAiC,EACjC,+BAA+B,EAC/B,8BAA8B,IAAI,oCAAoC,EACvE,MAAM,eAAe,CAAA;AAUtB,YAAY,EACV,4BAA4B,EAC5B,iCAAiC,EACjC,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,eAAe,CAAA;AAEtB,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAS3D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/E;AAED,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElE;AAiDD,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,+BAA+B,GAAG,SAAS,EAClD,OAAO,GAAE,IAAI,CAAC,6BAA6B,EAAE,oBAAoB,CAAM,GACtE,WAAW,CAAC,MAAM,EAAE,oCAAoC,CAAC,CA6C3D;AAED,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,sBAAsB,EAC5B,gBAAgB,EAAE,WAAW,CAAC,MAAM,EAAE,oCAAoC,CAAC,EAC3E,MAAM,EAAE,MAAM,EAAE,GACf,IAAI,CAuBN;AAED,eAAO,MAAM,kCAAkC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iCAAiC,CAAC,CAM1G,CAAA"}