industrial-model 0.11.2 → 0.12.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,879 @@
1
+ // src/cognite/adapter.ts
2
+ function createCogniteAdapter(client) {
3
+ return new CogniteSdkAdapter(client);
4
+ }
5
+ var CogniteSdkAdapter = class {
6
+ constructor(client) {
7
+ this.client = client;
8
+ }
9
+ client;
10
+ async retrieveDataModels(ids, options) {
11
+ const response = await this.client.dataModels.retrieve(ids, options);
12
+ return {
13
+ items: response.items.map((item) => ({
14
+ createdTime: item.createdTime,
15
+ views: item.views ?? []
16
+ }))
17
+ };
18
+ }
19
+ async retrieveViews(ids) {
20
+ const cleanIds = ids.map(({ space, externalId, version }) => ({ space, externalId, version }));
21
+ const response = await this.client.views.retrieve(cleanIds);
22
+ return { items: response.items };
23
+ }
24
+ async queryInstances(request) {
25
+ const response = await this.client.instances.query(
26
+ request
27
+ );
28
+ return {
29
+ items: response.items,
30
+ nextCursor: response.nextCursor
31
+ };
32
+ }
33
+ async searchInstances(request) {
34
+ const search = this.client.instances.search;
35
+ const response = await search(request);
36
+ return {
37
+ items: response.items
38
+ };
39
+ }
40
+ async aggregateInstances(request) {
41
+ const response = await this.client.instances.aggregate(
42
+ request
43
+ );
44
+ return {
45
+ items: response.items
46
+ };
47
+ }
48
+ async applyInstances(request) {
49
+ const apply = this.client.instances.apply;
50
+ const response = await apply(request);
51
+ return {
52
+ items: response.items
53
+ };
54
+ }
55
+ async retrieveDatapoints(options) {
56
+ const { items, ...rest } = options;
57
+ const sdkItems = items.map(({ space, externalId, ...itemRest }) => ({
58
+ ...itemRest,
59
+ instanceId: { space, externalId }
60
+ }));
61
+ const response = await this.client.datapoints.retrieve({
62
+ ...rest,
63
+ items: sdkItems
64
+ });
65
+ return { items: response.map(mapDatapointResult) };
66
+ }
67
+ async retrieveLatestDatapoints(items, options) {
68
+ const sdkItems = items.map(({ space, externalId, before }) => ({
69
+ instanceId: { space, externalId },
70
+ ...before !== void 0 ? { before } : {}
71
+ }));
72
+ const response = await this.client.datapoints.retrieveLatest(
73
+ sdkItems,
74
+ options
75
+ );
76
+ return { items: response.map(mapDatapointResult) };
77
+ }
78
+ async insertDatapoints(items) {
79
+ const sdkItems = items.map(({ space, externalId, datapoints }) => ({
80
+ instanceId: { space, externalId },
81
+ datapoints
82
+ }));
83
+ await this.client.datapoints.insert(
84
+ sdkItems
85
+ );
86
+ }
87
+ async deleteDatapoints(items) {
88
+ const sdkItems = items.map(({ space, externalId, inclusiveBegin, exclusiveEnd }) => ({
89
+ instanceId: { space, externalId },
90
+ inclusiveBegin,
91
+ ...exclusiveEnd !== void 0 ? { exclusiveEnd } : {}
92
+ }));
93
+ await this.client.datapoints.delete(
94
+ sdkItems
95
+ );
96
+ }
97
+ async uploadFile(fileInfo, content) {
98
+ const { instanceId, ...rest } = fileInfo;
99
+ const response = await this.client.files.upload(
100
+ { ...rest, instanceId },
101
+ content,
102
+ false,
103
+ content !== void 0
104
+ );
105
+ return mapFileResult(response);
106
+ }
107
+ async getFileDownloadUrls(ids) {
108
+ const response = await this.client.files.getDownloadUrls(
109
+ ids
110
+ );
111
+ return response.map((item) => ({
112
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
113
+ downloadUrl: item.downloadUrl
114
+ }));
115
+ }
116
+ };
117
+ function mapFileResult(item) {
118
+ return {
119
+ ...item.instanceId !== void 0 ? { instanceId: item.instanceId } : {},
120
+ name: item.name,
121
+ uploaded: item.uploaded,
122
+ createdTime: item.createdTime,
123
+ lastUpdatedTime: item.lastUpdatedTime,
124
+ ...item.uploadedTime !== void 0 ? { uploadedTime: item.uploadedTime } : {},
125
+ ...item.mimeType !== void 0 ? { mimeType: item.mimeType } : {},
126
+ ...item.directory !== void 0 ? { directory: item.directory } : {},
127
+ ...item.source !== void 0 ? { source: item.source } : {},
128
+ ...item.uploadUrl !== void 0 ? { uploadUrl: item.uploadUrl } : {}
129
+ };
130
+ }
131
+ function mapDatapointResult(item) {
132
+ return {
133
+ ...item.instanceId?.space !== void 0 ? { space: item.instanceId.space } : {},
134
+ ...item.instanceId?.externalId !== void 0 ? { externalId: item.instanceId.externalId } : {},
135
+ isString: item.isString ?? false,
136
+ ...item.unit !== void 0 ? { unit: item.unit } : {},
137
+ datapoints: item.datapoints ?? [],
138
+ ...item.nextCursor !== void 0 ? { nextCursor: item.nextCursor } : {}
139
+ };
140
+ }
141
+
142
+ // src/calculator/datapoints-retrieval.ts
143
+ var DatapointsRetriever = class {
144
+ constructor(cognite) {
145
+ this.cognite = cognite;
146
+ }
147
+ cognite;
148
+ async retrieveDatapoints(parameters, start, end) {
149
+ const { requests, indexMapping } = this.buildRequests(parameters);
150
+ if (requests.length === 0) {
151
+ return parameters.map(() => []);
152
+ }
153
+ const options = { items: requests, start, end };
154
+ const raw = await this.cognite.retrieveDatapoints(options);
155
+ return parameters.map((parameter, index) => {
156
+ const requestIndex = indexMapping[index];
157
+ const item = raw.items[requestIndex];
158
+ if (item === void 0) {
159
+ throw new Error(`missing datapoints response for parameter '${parameter.alias}'`);
160
+ }
161
+ return parseDatapoints(item, parameter);
162
+ });
163
+ }
164
+ buildRequests(parameters) {
165
+ const rawRequestIndex = /* @__PURE__ */ new Map();
166
+ const aggregateRequestIndex = /* @__PURE__ */ new Map();
167
+ const requests = [];
168
+ const indexMapping = new Array(parameters.length);
169
+ parameters.forEach((parameter, index) => {
170
+ const { space, externalId } = parameter.timeSeries;
171
+ const tsKey = `${space}:${externalId}`;
172
+ if (parameter.aggregateType === void 0) {
173
+ let requestIndex2 = rawRequestIndex.get(tsKey);
174
+ if (requestIndex2 === void 0) {
175
+ requestIndex2 = requests.length;
176
+ rawRequestIndex.set(tsKey, requestIndex2);
177
+ requests.push({ space, externalId });
178
+ }
179
+ indexMapping[index] = requestIndex2;
180
+ return;
181
+ }
182
+ if (parameter.granularity === void 0) {
183
+ throw new Error(
184
+ `Missing granularity for '${parameter.alias}' with aggregate '${parameter.aggregateType}'`
185
+ );
186
+ }
187
+ const aggregateKey = `${tsKey}|${parameter.granularity}`;
188
+ let requestIndex = aggregateRequestIndex.get(aggregateKey);
189
+ if (requestIndex === void 0) {
190
+ requestIndex = requests.length;
191
+ aggregateRequestIndex.set(aggregateKey, requestIndex);
192
+ requests.push({
193
+ space,
194
+ externalId,
195
+ aggregates: [parameter.aggregateType],
196
+ granularity: parameter.granularity
197
+ });
198
+ } else {
199
+ const entry = requests[requestIndex];
200
+ const aggregates = entry.aggregates;
201
+ if (!aggregates.includes(parameter.aggregateType)) {
202
+ aggregates.push(parameter.aggregateType);
203
+ }
204
+ }
205
+ indexMapping[index] = requestIndex;
206
+ });
207
+ return { requests, indexMapping };
208
+ }
209
+ };
210
+ function parseDatapoints(item, parameter) {
211
+ if (item.isString) {
212
+ throw new Error("expected numeric datapoints, got string");
213
+ }
214
+ const result = [];
215
+ for (const datapoint of item.datapoints) {
216
+ const value = readValue(datapoint, parameter.aggregateType);
217
+ if (value === void 0 || value === null) {
218
+ continue;
219
+ }
220
+ result.push({ timestamp: datapoint.timestamp, value });
221
+ }
222
+ return result;
223
+ }
224
+ function readValue(datapoint, aggregateType) {
225
+ if (aggregateType === void 0) {
226
+ return datapoint.value;
227
+ }
228
+ return datapoint[aggregateType];
229
+ }
230
+
231
+ // src/calculator/formula-expression/ast.ts
232
+ function isConditionalNode(node) {
233
+ return node.kind === "compare" || node.kind === "boolop" || node.kind === "ifexp";
234
+ }
235
+
236
+ // src/calculator/formula-expression/exceptions.ts
237
+ var FormulaError = class extends Error {
238
+ constructor(message) {
239
+ super(message);
240
+ this.name = "FormulaError";
241
+ }
242
+ };
243
+ var InvalidFormulaError = class extends FormulaError {
244
+ constructor(message) {
245
+ super(message);
246
+ this.name = "InvalidFormulaError";
247
+ }
248
+ };
249
+ var MissingParameterError = class extends FormulaError {
250
+ missing;
251
+ constructor(missing) {
252
+ super(`missing formula parameter(s): ${missing.join(", ")}`);
253
+ this.name = "MissingParameterError";
254
+ this.missing = [...missing];
255
+ }
256
+ };
257
+ var ParameterError = class extends FormulaError {
258
+ constructor(message) {
259
+ super(message);
260
+ this.name = "ParameterError";
261
+ }
262
+ };
263
+ var ParameterLengthError = class extends ParameterError {
264
+ lengths;
265
+ constructor(lengths) {
266
+ const detail = Object.entries(lengths).map(([name, length]) => `'${name}' has ${length}`).join(", ");
267
+ super(`parameter length mismatch: ${detail}`);
268
+ this.name = "ParameterLengthError";
269
+ this.lengths = { ...lengths };
270
+ }
271
+ };
272
+ var ArithmeticError = class extends Error {
273
+ constructor(message) {
274
+ super(message);
275
+ this.name = "ArithmeticError";
276
+ }
277
+ };
278
+ var ZeroDivisionError = class extends ArithmeticError {
279
+ constructor(message) {
280
+ super(message);
281
+ this.name = "ZeroDivisionError";
282
+ }
283
+ };
284
+ var OverflowError = class extends ArithmeticError {
285
+ constructor(message) {
286
+ super(message);
287
+ this.name = "OverflowError";
288
+ }
289
+ };
290
+
291
+ // src/calculator/formula-expression/compiler.ts
292
+ var PLACEHOLDER_RE = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
293
+ var UNRESOLVED_BRACE_RE = /[{}]/;
294
+ var SAFE_NAME_PREFIX = "__formula_expression_param_";
295
+ var CACHE_MAX_SIZE = 1024;
296
+ var cache = /* @__PURE__ */ new Map();
297
+ function compileFormula(formula) {
298
+ const normalized = normalizeFormulaText(formula);
299
+ const cached = cache.get(normalized);
300
+ if (cached !== void 0) {
301
+ return cached;
302
+ }
303
+ const compiled = compileNormalized(normalized);
304
+ if (cache.size >= CACHE_MAX_SIZE) {
305
+ const oldest = cache.keys().next().value;
306
+ if (oldest !== void 0) {
307
+ cache.delete(oldest);
308
+ }
309
+ }
310
+ cache.set(normalized, compiled);
311
+ return compiled;
312
+ }
313
+ function clearCache() {
314
+ cache.clear();
315
+ }
316
+ function normalizeFormulaText(formula) {
317
+ if (typeof formula !== "string") {
318
+ throw new TypeError("formula must be a string");
319
+ }
320
+ const trimmed = formula.trim();
321
+ if (trimmed === "") {
322
+ return "";
323
+ }
324
+ return trimmed.split(/\s+/).join(" ");
325
+ }
326
+ function compileNormalized(raw) {
327
+ if (raw === "") {
328
+ throw new InvalidFormulaError("formula must not be empty");
329
+ }
330
+ const variables = [];
331
+ const nameMap = /* @__PURE__ */ new Map();
332
+ const expression = replacePlaceholders(raw, variables, nameMap);
333
+ if (variables.length === 0) {
334
+ throw new InvalidFormulaError("formula must reference at least one parameter");
335
+ }
336
+ if (UNRESOLVED_BRACE_RE.test(expression)) {
337
+ throw new InvalidFormulaError("formula contains invalid placeholder syntax");
338
+ }
339
+ const tree = parse(expression);
340
+ validateTree(tree, new Set(nameMap.values()));
341
+ const hasConditional = treeHasConditional(tree);
342
+ return { raw, expression, tree, variables, nameMap, hasConditional };
343
+ }
344
+ function replacePlaceholders(formula, variables, nameMap) {
345
+ return formula.replace(PLACEHOLDER_RE, (_match, name) => {
346
+ let safe = nameMap.get(name);
347
+ if (safe === void 0) {
348
+ safe = `${SAFE_NAME_PREFIX}${nameMap.size}`;
349
+ nameMap.set(name, safe);
350
+ variables.push(name);
351
+ }
352
+ return safe;
353
+ });
354
+ }
355
+ function validateTree(tree, allowedNames) {
356
+ walk(tree, (node) => {
357
+ if (node.kind === "name" && !allowedNames.has(node.id)) {
358
+ throw new InvalidFormulaError(`unknown formula identifier: ${node.id}`);
359
+ }
360
+ });
361
+ }
362
+ function treeHasConditional(tree) {
363
+ let found = false;
364
+ walk(tree, (node) => {
365
+ if (isConditionalNode(node)) {
366
+ found = true;
367
+ }
368
+ });
369
+ return found;
370
+ }
371
+ function walk(node, visit) {
372
+ visit(node);
373
+ switch (node.kind) {
374
+ case "binop":
375
+ walk(node.left, visit);
376
+ walk(node.right, visit);
377
+ break;
378
+ case "unaryop":
379
+ walk(node.operand, visit);
380
+ break;
381
+ case "compare":
382
+ walk(node.left, visit);
383
+ for (const comparator of node.comparators) {
384
+ walk(comparator, visit);
385
+ }
386
+ break;
387
+ case "boolop":
388
+ for (const value of node.values) {
389
+ walk(value, visit);
390
+ }
391
+ break;
392
+ case "ifexp":
393
+ walk(node.test, visit);
394
+ walk(node.body, visit);
395
+ walk(node.orelse, visit);
396
+ break;
397
+ }
398
+ }
399
+ var NUMBER_RE = /(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/y;
400
+ var IDENT_RE = /[A-Za-z_][A-Za-z0-9_]*/y;
401
+ var TWO_CHAR_OPS = /* @__PURE__ */ new Set(["**", "==", "!=", "<=", ">="]);
402
+ var ONE_CHAR_OPS = /* @__PURE__ */ new Set(["+", "-", "*", "/", "%", "(", ")", "<", ">"]);
403
+ function tokenize(expression) {
404
+ const tokens = [];
405
+ let index = 0;
406
+ while (index < expression.length) {
407
+ const char = expression[index];
408
+ if (char === " ") {
409
+ index += 1;
410
+ continue;
411
+ }
412
+ if (isDigit(char) || char === "." && isDigit(expression[index + 1])) {
413
+ NUMBER_RE.lastIndex = index;
414
+ const match = NUMBER_RE.exec(expression);
415
+ if (match === null) {
416
+ throw new InvalidFormulaError("invalid formula syntax: malformed number");
417
+ }
418
+ tokens.push({ type: "number", value: Number(match[0]) });
419
+ index = NUMBER_RE.lastIndex;
420
+ continue;
421
+ }
422
+ if (isIdentStart(char)) {
423
+ IDENT_RE.lastIndex = index;
424
+ const match = IDENT_RE.exec(expression);
425
+ const value = match[0];
426
+ tokens.push({ type: "name", value });
427
+ index = IDENT_RE.lastIndex;
428
+ continue;
429
+ }
430
+ const twoChar = expression.slice(index, index + 2);
431
+ if (TWO_CHAR_OPS.has(twoChar)) {
432
+ tokens.push({ type: "op", value: twoChar });
433
+ index += 2;
434
+ continue;
435
+ }
436
+ if (char !== void 0 && ONE_CHAR_OPS.has(char)) {
437
+ tokens.push({ type: "op", value: char });
438
+ index += 1;
439
+ continue;
440
+ }
441
+ throw new InvalidFormulaError(`invalid formula syntax: unexpected character '${char}'`);
442
+ }
443
+ return tokens;
444
+ }
445
+ function isDigit(char) {
446
+ return char !== void 0 && char >= "0" && char <= "9";
447
+ }
448
+ function isIdentStart(char) {
449
+ if (char === void 0) {
450
+ return false;
451
+ }
452
+ return char === "_" || char >= "a" && char <= "z" || char >= "A" && char <= "Z";
453
+ }
454
+ var KEYWORDS = /* @__PURE__ */ new Set(["and", "or", "if", "else"]);
455
+ var CONSTANT_KEYWORDS = /* @__PURE__ */ new Set(["True", "False", "None"]);
456
+ var COMPARE_OPS = {
457
+ "==": "eq",
458
+ "!=": "ne",
459
+ "<": "lt",
460
+ "<=": "le",
461
+ ">": "gt",
462
+ ">=": "ge"
463
+ };
464
+ function parse(expression) {
465
+ const tokens = tokenize(expression);
466
+ const parser = new Parser(tokens);
467
+ const tree = parser.parseExpression();
468
+ parser.expectEnd();
469
+ return tree;
470
+ }
471
+ var Parser = class {
472
+ constructor(tokens) {
473
+ this.tokens = tokens;
474
+ }
475
+ tokens;
476
+ position = 0;
477
+ parseExpression() {
478
+ return this.parseConditional();
479
+ }
480
+ expectEnd() {
481
+ if (this.position < this.tokens.length) {
482
+ throw new InvalidFormulaError("invalid formula syntax: unexpected trailing tokens");
483
+ }
484
+ }
485
+ parseConditional() {
486
+ const body = this.parseOr();
487
+ if (this.matchKeyword("if")) {
488
+ const test = this.parseOr();
489
+ this.expectKeyword("else");
490
+ const orelse = this.parseConditional();
491
+ return { kind: "ifexp", test, body, orelse };
492
+ }
493
+ return body;
494
+ }
495
+ parseOr() {
496
+ const values = [this.parseAnd()];
497
+ while (this.matchKeyword("or")) {
498
+ values.push(this.parseAnd());
499
+ }
500
+ return values.length === 1 ? values[0] : { kind: "boolop", op: "or", values };
501
+ }
502
+ parseAnd() {
503
+ const values = [this.parseComparison()];
504
+ while (this.matchKeyword("and")) {
505
+ values.push(this.parseComparison());
506
+ }
507
+ const op = "and";
508
+ return values.length === 1 ? values[0] : { kind: "boolop", op, values };
509
+ }
510
+ parseComparison() {
511
+ const left = this.parseAdditive();
512
+ const ops = [];
513
+ const comparators = [];
514
+ while (true) {
515
+ const op = this.peekCompareOp();
516
+ if (op === void 0) {
517
+ break;
518
+ }
519
+ this.position += 1;
520
+ ops.push(op);
521
+ comparators.push(this.parseAdditive());
522
+ }
523
+ return ops.length === 0 ? left : { kind: "compare", left, ops, comparators };
524
+ }
525
+ parseAdditive() {
526
+ let node = this.parseMultiplicative();
527
+ while (true) {
528
+ const op = this.peekOp();
529
+ if (op === "+" || op === "-") {
530
+ this.position += 1;
531
+ const right = this.parseMultiplicative();
532
+ node = { kind: "binop", op: op === "+" ? "add" : "sub", left: node, right };
533
+ continue;
534
+ }
535
+ break;
536
+ }
537
+ return node;
538
+ }
539
+ parseMultiplicative() {
540
+ let node = this.parseUnary();
541
+ while (true) {
542
+ const op = this.peekOp();
543
+ if (op === "*" || op === "/" || op === "%") {
544
+ this.position += 1;
545
+ const right = this.parseUnary();
546
+ const kind = op === "*" ? "mul" : op === "/" ? "div" : "mod";
547
+ node = { kind: "binop", op: kind, left: node, right };
548
+ continue;
549
+ }
550
+ break;
551
+ }
552
+ return node;
553
+ }
554
+ parseUnary() {
555
+ const op = this.peekOp();
556
+ if (op === "+" || op === "-") {
557
+ this.position += 1;
558
+ const operand = this.parseUnary();
559
+ const kind = op === "+" ? "pos" : "neg";
560
+ return { kind: "unaryop", op: kind, operand };
561
+ }
562
+ return this.parsePower();
563
+ }
564
+ parsePower() {
565
+ const base = this.parseAtom();
566
+ if (this.peekOp() === "**") {
567
+ this.position += 1;
568
+ const exponent = this.parseUnary();
569
+ return { kind: "binop", op: "pow", left: base, right: exponent };
570
+ }
571
+ return base;
572
+ }
573
+ parseAtom() {
574
+ const token = this.tokens[this.position];
575
+ if (token === void 0) {
576
+ throw new InvalidFormulaError("invalid formula syntax: unexpected end of expression");
577
+ }
578
+ if (token.type === "number") {
579
+ this.position += 1;
580
+ return { kind: "constant", value: token.value };
581
+ }
582
+ if (token.type === "name") {
583
+ if (CONSTANT_KEYWORDS.has(token.value)) {
584
+ throw new InvalidFormulaError("only numeric constants are supported");
585
+ }
586
+ if (KEYWORDS.has(token.value)) {
587
+ throw new InvalidFormulaError(
588
+ `invalid formula syntax: unexpected keyword '${token.value}'`
589
+ );
590
+ }
591
+ this.position += 1;
592
+ return { kind: "name", id: token.value };
593
+ }
594
+ if (token.value === "(") {
595
+ this.position += 1;
596
+ const inner = this.parseExpression();
597
+ this.expectOp(")");
598
+ return inner;
599
+ }
600
+ throw new InvalidFormulaError(`invalid formula syntax: unexpected token '${token.value}'`);
601
+ }
602
+ peekOp() {
603
+ const token = this.tokens[this.position];
604
+ return token !== void 0 && token.type === "op" ? token.value : void 0;
605
+ }
606
+ peekCompareOp() {
607
+ const op = this.peekOp();
608
+ return op !== void 0 ? COMPARE_OPS[op] : void 0;
609
+ }
610
+ matchKeyword(keyword) {
611
+ const token = this.tokens[this.position];
612
+ if (token !== void 0 && token.type === "name" && token.value === keyword) {
613
+ this.position += 1;
614
+ return true;
615
+ }
616
+ return false;
617
+ }
618
+ expectKeyword(keyword) {
619
+ if (!this.matchKeyword(keyword)) {
620
+ throw new InvalidFormulaError(`invalid formula syntax: expected '${keyword}'`);
621
+ }
622
+ }
623
+ expectOp(op) {
624
+ if (this.peekOp() !== op) {
625
+ throw new InvalidFormulaError(`invalid formula syntax: expected '${op}'`);
626
+ }
627
+ this.position += 1;
628
+ }
629
+ };
630
+
631
+ // src/calculator/formula-expression/evaluator.ts
632
+ var UNARY_OPS = {
633
+ pos: (value) => value,
634
+ neg: (value) => -value
635
+ };
636
+ var BINARY_OPS = {
637
+ add: (left, right) => left + right,
638
+ sub: (left, right) => left - right,
639
+ mul: (left, right) => left * right,
640
+ div: (left, right) => {
641
+ if (right === 0) {
642
+ throw new ZeroDivisionError("float division by zero");
643
+ }
644
+ return left / right;
645
+ },
646
+ mod: pythonMod,
647
+ pow: safePow
648
+ };
649
+ var COMPARE_OPS2 = {
650
+ eq: (left, right) => left === right,
651
+ ne: (left, right) => left !== right,
652
+ lt: (left, right) => left < right,
653
+ le: (left, right) => left <= right,
654
+ gt: (left, right) => left > right,
655
+ ge: (left, right) => left >= right
656
+ };
657
+ function pythonMod(left, right) {
658
+ if (right === 0) {
659
+ throw new ZeroDivisionError("float modulo");
660
+ }
661
+ return left - Math.floor(left / right) * right;
662
+ }
663
+ function safePow(base, exponent) {
664
+ if (base === 0 && exponent < 0) {
665
+ throw new ZeroDivisionError("0.0 cannot be raised to a negative power");
666
+ }
667
+ const result = base ** exponent;
668
+ if (!Number.isFinite(result) && Number.isFinite(base) && Number.isFinite(exponent)) {
669
+ throw new OverflowError("(34, 'Numerical result out of range')");
670
+ }
671
+ return result;
672
+ }
673
+ function isTruthy(value) {
674
+ return value !== 0;
675
+ }
676
+ function evaluateTree(tree, environment, length, hasConditional) {
677
+ if (hasConditional) {
678
+ const result = new Array(length);
679
+ for (let index = 0; index < length; index += 1) {
680
+ result[index] = evaluateNodeAt(tree, environment, index);
681
+ }
682
+ return result;
683
+ }
684
+ const value = evaluateNode(tree, environment);
685
+ if (Array.isArray(value)) {
686
+ return value;
687
+ }
688
+ return new Array(length).fill(value);
689
+ }
690
+ function evaluateNode(node, environment) {
691
+ switch (node.kind) {
692
+ case "name": {
693
+ const series = environment[node.id];
694
+ return series;
695
+ }
696
+ case "constant":
697
+ return node.value;
698
+ case "binop": {
699
+ const left = evaluateNode(node.left, environment);
700
+ const right = evaluateNode(node.right, environment);
701
+ return applyBinary(BINARY_OPS[node.op], left, right);
702
+ }
703
+ case "unaryop": {
704
+ const operand = evaluateNode(node.operand, environment);
705
+ const op = UNARY_OPS[node.op];
706
+ return Array.isArray(operand) ? operand.map(op) : op(operand);
707
+ }
708
+ default:
709
+ throw new TypeError(`unsupported formula element: ${node.kind}`);
710
+ }
711
+ }
712
+ function applyBinary(op, left, right) {
713
+ if (Array.isArray(left)) {
714
+ if (Array.isArray(right)) {
715
+ const result = new Array(left.length);
716
+ for (let index = 0; index < left.length; index += 1) {
717
+ result[index] = op(left[index], right[index]);
718
+ }
719
+ return result;
720
+ }
721
+ const scalar = right;
722
+ return left.map((value) => op(value, scalar));
723
+ }
724
+ const scalarLeft = left;
725
+ if (Array.isArray(right)) {
726
+ return right.map((value) => op(scalarLeft, value));
727
+ }
728
+ return op(scalarLeft, right);
729
+ }
730
+ function evaluateNodeAt(node, environment, index) {
731
+ switch (node.kind) {
732
+ case "name": {
733
+ const series = environment[node.id];
734
+ return series[index];
735
+ }
736
+ case "constant":
737
+ return node.value;
738
+ case "binop": {
739
+ const left = evaluateNodeAt(node.left, environment, index);
740
+ const right = evaluateNodeAt(node.right, environment, index);
741
+ return BINARY_OPS[node.op](left, right);
742
+ }
743
+ case "unaryop":
744
+ return UNARY_OPS[node.op](evaluateNodeAt(node.operand, environment, index));
745
+ case "compare": {
746
+ let left = evaluateNodeAt(node.left, environment, index);
747
+ for (let i = 0; i < node.ops.length; i += 1) {
748
+ const right = evaluateNodeAt(node.comparators[i], environment, index);
749
+ if (!COMPARE_OPS2[node.ops[i]](left, right)) {
750
+ return 0;
751
+ }
752
+ left = right;
753
+ }
754
+ return 1;
755
+ }
756
+ case "boolop": {
757
+ if (node.op === "and") {
758
+ for (const value of node.values) {
759
+ if (!isTruthy(evaluateNodeAt(value, environment, index))) {
760
+ return 0;
761
+ }
762
+ }
763
+ return 1;
764
+ }
765
+ for (const value of node.values) {
766
+ if (isTruthy(evaluateNodeAt(value, environment, index))) {
767
+ return 1;
768
+ }
769
+ }
770
+ return 0;
771
+ }
772
+ case "ifexp": {
773
+ const test = evaluateNodeAt(node.test, environment, index);
774
+ const branch = isTruthy(test) ? node.body : node.orelse;
775
+ return evaluateNodeAt(branch, environment, index);
776
+ }
777
+ default:
778
+ throw new TypeError("unsupported formula element");
779
+ }
780
+ }
781
+
782
+ // src/calculator/formula-expression/runtime.ts
783
+ function evaluateCompiled(formula, parameters) {
784
+ const missing = formula.variables.filter((name) => !Object.hasOwn(parameters, name));
785
+ if (missing.length > 0) {
786
+ throw new MissingParameterError(missing);
787
+ }
788
+ const environment = {};
789
+ const lengthsByName = {};
790
+ for (const [originalName, safeName] of formula.nameMap) {
791
+ const series = normalizeParameter(originalName, parameters[originalName]);
792
+ environment[safeName] = series;
793
+ lengthsByName[originalName] = series.length;
794
+ }
795
+ const lengths = Object.values(lengthsByName);
796
+ if (new Set(lengths).size !== 1) {
797
+ throw new ParameterLengthError(lengthsByName);
798
+ }
799
+ const length = lengths[0];
800
+ if (length === 0) {
801
+ return [];
802
+ }
803
+ return evaluateTree(formula.tree, environment, length, formula.hasConditional);
804
+ }
805
+ function normalizeParameter(name, value) {
806
+ if (!Array.isArray(value)) {
807
+ throw new ParameterError(`parameter '${name}' must be a numeric sequence`);
808
+ }
809
+ const normalized = new Array(value.length);
810
+ for (let index = 0; index < value.length; index += 1) {
811
+ const item = value[index];
812
+ if (typeof item !== "number") {
813
+ throw new ParameterError(`parameter '${name}' must be a numeric sequence`);
814
+ }
815
+ normalized[index] = item;
816
+ }
817
+ return normalized;
818
+ }
819
+
820
+ // src/calculator/formula-expression/core.ts
821
+ function evaluate(formula, parameters = {}) {
822
+ return evaluateCompiled(compileFormula(formula), parameters);
823
+ }
824
+
825
+ // src/calculator/calculator.ts
826
+ var Calculator = class {
827
+ retriever;
828
+ constructor(cognite) {
829
+ const port = isCognitePort(cognite) ? cognite : createCogniteAdapter(cognite);
830
+ this.retriever = new DatapointsRetriever(port);
831
+ }
832
+ /** Evaluate a single query over the given time range. */
833
+ async calculate(query, start, end) {
834
+ const [result] = await this.calculateMultiples([query], start, end);
835
+ return result;
836
+ }
837
+ /**
838
+ * Evaluate several queries over the given time range, retrieving every
839
+ * parameter's datapoints in a single de-duplicated round trip.
840
+ */
841
+ async calculateMultiples(queries, start, end) {
842
+ const parameters = queries.flatMap((query) => query.parameters);
843
+ const datapoints = await this.retriever.retrieveDatapoints(parameters, start, end);
844
+ const results = [];
845
+ let offset = 0;
846
+ for (const query of queries) {
847
+ const slice = datapoints.slice(offset, offset + query.parameters.length);
848
+ offset += query.parameters.length;
849
+ results.push(calculate(query, slice));
850
+ }
851
+ return results;
852
+ }
853
+ };
854
+ function calculate(query, datapoints) {
855
+ const valuesMap = {};
856
+ const seriesByAlias = /* @__PURE__ */ new Map();
857
+ query.parameters.forEach((parameter, index) => {
858
+ const series = datapoints[index] ?? [];
859
+ valuesMap[parameter.alias] = series.map((point) => point.value);
860
+ seriesByAlias.set(parameter.alias, series);
861
+ });
862
+ const values = evaluate(query.formula, valuesMap);
863
+ const [firstVariable] = compileFormula(query.formula).variables;
864
+ const timestampSeries = seriesByAlias.get(firstVariable) ?? [];
865
+ return {
866
+ query,
867
+ datapoints: timestampSeries.map((point, index) => ({
868
+ timestamp: point.timestamp,
869
+ value: values[index]
870
+ }))
871
+ };
872
+ }
873
+ function isCognitePort(value) {
874
+ return typeof value.retrieveDatapoints === "function";
875
+ }
876
+
877
+ export { ArithmeticError, Calculator, FormulaError, InvalidFormulaError, MissingParameterError, OverflowError, ParameterError, ParameterLengthError, ZeroDivisionError, clearCache, compileFormula, evaluate };
878
+ //# sourceMappingURL=index.js.map
879
+ //# sourceMappingURL=index.js.map