@fincity/kirun-js 2.15.1 → 2.16.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.
@@ -7,6 +7,34 @@ import { ExpressionEvaluationException } from '../exception/ExpressionEvaluation
7
7
  export abstract class TokenValueExtractor {
8
8
  public static readonly REGEX_SQUARE_BRACKETS: RegExp = /[\[\]]/;
9
9
  public static readonly REGEX_DOT: RegExp = /(?<!\.)\.(?!\.)/;
10
+
11
+ // Cache for parsed paths to avoid repeated regex splits
12
+ private static pathCache: Map<string, string[]> = new Map();
13
+
14
+ // Cache for parsed bracket segments to avoid repeated regex splits
15
+ private static bracketCache: Map<string, string[]> = new Map();
16
+
17
+ protected static splitPath(token: string): string[] {
18
+ let parts = TokenValueExtractor.pathCache.get(token);
19
+ if (!parts) {
20
+ parts = token.split(TokenValueExtractor.REGEX_DOT);
21
+ TokenValueExtractor.pathCache.set(token, parts);
22
+ }
23
+ return parts;
24
+ }
25
+
26
+ // Parse bracket segments with caching
27
+ private static parseBracketSegment(segment: string): string[] {
28
+ let cached = TokenValueExtractor.bracketCache.get(segment);
29
+ if (!cached) {
30
+ cached = segment
31
+ .split(TokenValueExtractor.REGEX_SQUARE_BRACKETS)
32
+ .map((e) => e.trim())
33
+ .filter((e) => !StringUtil.isNullOrBlank(e));
34
+ TokenValueExtractor.bracketCache.set(segment, cached);
35
+ }
36
+ return cached;
37
+ }
10
38
 
11
39
  public getValue(token: string): any {
12
40
  let prefix: string = this.getPrefix();
@@ -28,10 +56,12 @@ export abstract class TokenValueExtractor {
28
56
  parentPart.lastIndexOf('[') + 1,
29
57
  parentPart.length - 1,
30
58
  );
31
- const indexInt = parseInt(indexString);
59
+ const indexInt = Number.parseInt(indexString);
32
60
  if (isNaN(indexInt)) return indexString;
33
61
  return indexInt;
34
- } else return parentPart.substring(parentPart.lastIndexOf('.') + 1);
62
+ } else {
63
+ return parentPart.substring(parentPart.lastIndexOf('.') + 1);
64
+ }
35
65
  }
36
66
 
37
67
  return this.getValueInternal(token);
@@ -40,24 +70,76 @@ export abstract class TokenValueExtractor {
40
70
  protected retrieveElementFrom(
41
71
  token: string,
42
72
  parts: string[],
43
- partNumber: number,
73
+ startPart: number,
44
74
  jsonElement: any,
45
75
  ): any {
46
- if (isNullValue(jsonElement)) return undefined;
47
-
48
- if (parts.length == partNumber) return jsonElement;
49
-
50
- let bElement: any = parts[partNumber]
51
- .split(TokenValueExtractor.REGEX_SQUARE_BRACKETS)
52
- .map((e) => e.trim())
53
- .filter((e) => !StringUtil.isNullOrBlank(e))
54
- .reduce(
55
- (a, c) =>
56
- this.resolveForEachPartOfTokenWithBrackets(token, parts, partNumber, c, a),
57
- jsonElement,
58
- );
59
-
60
- return this.retrieveElementFrom(token, parts, partNumber + 1, bElement);
76
+ // Iterative version - avoids recursive call overhead
77
+ let current = jsonElement;
78
+
79
+ for (let partNumber = startPart; partNumber < parts.length; partNumber++) {
80
+ if (isNullValue(current)) return undefined;
81
+
82
+ // Use cached bracket segment parsing
83
+ const segments = TokenValueExtractor.parseBracketSegment(parts[partNumber]);
84
+
85
+ for (const segment of segments) {
86
+ current = this.resolveSegmentFast(token, parts, partNumber, segment, current);
87
+ if (current === undefined) return undefined;
88
+ }
89
+ }
90
+
91
+ return current;
92
+ }
93
+
94
+ // Fast path for common cases - inline to avoid function call overhead
95
+ private resolveSegmentFast(
96
+ token: string,
97
+ parts: string[],
98
+ partNumber: number,
99
+ segment: string,
100
+ element: any,
101
+ ): any {
102
+ if (element === null || element === undefined) return undefined;
103
+
104
+ // Fast path: simple property access on object (most common case)
105
+ if (typeof element === 'object' && !Array.isArray(element)) {
106
+ if (segment in element) {
107
+ return element[segment];
108
+ }
109
+ // Check for 'length' on object
110
+ if (segment === 'length') {
111
+ return Object.keys(element).length;
112
+ }
113
+ return element[segment];
114
+ }
115
+
116
+ // Fast path: array index access
117
+ if (Array.isArray(element)) {
118
+ // Check for 'length' first
119
+ if (segment === 'length') return element.length;
120
+
121
+ // Only use fast path for pure integer strings (no range operators like '..')
122
+ // Note: parseInt('2..4', 10) incorrectly returns 2, so we need to validate first
123
+ if (/^-?\d+$/.test(segment)) {
124
+ const idx = Number.parseInt(segment, 10);
125
+ const actualIdx = idx < 0 ? element.length + idx : idx;
126
+ return actualIdx >= 0 && actualIdx < element.length ? element[actualIdx] : undefined;
127
+ }
128
+ }
129
+
130
+ // Fast path: string access
131
+ if (typeof element === 'string') {
132
+ if (segment === 'length') return element.length;
133
+ // Only use fast path for pure integer strings
134
+ if (/^-?\d+$/.test(segment)) {
135
+ const idx = Number.parseInt(segment, 10);
136
+ const actualIdx = idx < 0 ? element.length + idx : idx;
137
+ return actualIdx >= 0 && actualIdx < element.length ? element[actualIdx] : undefined;
138
+ }
139
+ }
140
+
141
+ // Fall back to full handling for edge cases (range operator, etc.)
142
+ return this.resolveForEachPartOfTokenWithBrackets(token, parts, partNumber, segment, element);
61
143
  }
62
144
 
63
145
  protected resolveForEachPartOfTokenWithBrackets(
@@ -5,11 +5,20 @@ import { GraphVertexType } from './GraphVertexType';
5
5
  export class ExecutionGraph<K, T extends GraphVertexType<K>> {
6
6
  private nodeMap: Map<K, GraphVertex<K, T>> = new Map();
7
7
  private isSubGrph: boolean;
8
+ private edgesBuilt: boolean = false;
8
9
 
9
10
  public constructor(isSubGrph: boolean = false) {
10
11
  this.isSubGrph = isSubGrph;
11
12
  }
12
13
 
14
+ public areEdgesBuilt(): boolean {
15
+ return this.edgesBuilt;
16
+ }
17
+
18
+ public setEdgesBuilt(built: boolean): void {
19
+ this.edgesBuilt = built;
20
+ }
21
+
13
22
  public getVerticesData(): T[] {
14
23
  return Array.from(this.nodeMap.values()).map((e) => e.getData());
15
24
  }
@@ -8,6 +8,7 @@ export class GraphVertex<K, T extends GraphVertexType<K>> {
8
8
  private outVertices: Map<string, Set<GraphVertex<K, T>>> = new Map();
9
9
  private inVertices: Set<Tuple2<GraphVertex<K, T>, string>> = new Set();
10
10
  private graph: ExecutionGraph<K, T>;
11
+ private subGraphCache: Map<string, ExecutionGraph<K, T>> = new Map();
11
12
 
12
13
  public constructor(graph: ExecutionGraph<K, T>, data: T) {
13
14
  this.data = data;
@@ -78,6 +79,12 @@ export class GraphVertex<K, T extends GraphVertexType<K>> {
78
79
  }
79
80
 
80
81
  public getSubGraphOfType(type: string): ExecutionGraph<K, T> {
82
+ // Check cache first
83
+ const cached = this.subGraphCache.get(type);
84
+ if (cached) {
85
+ return cached;
86
+ }
87
+
81
88
  let subGraph: ExecutionGraph<K, T> = new ExecutionGraph(true);
82
89
 
83
90
  var typeVertices = new LinkedList(Array.from(this.outVertices.get(type) ?? []));
@@ -94,6 +101,8 @@ export class GraphVertex<K, T extends GraphVertexType<K>> {
94
101
  });
95
102
  }
96
103
 
104
+ // Cache for reuse
105
+ this.subGraphCache.set(type, subGraph);
97
106
  return subGraph;
98
107
  }
99
108
 
@@ -12,13 +12,14 @@ export class ArgumentsTokenValueExtractor extends TokenValueExtractor {
12
12
  }
13
13
 
14
14
  protected getValueInternal(token: string): any {
15
- let parts: string[] = token.split(TokenValueExtractor.REGEX_DOT);
15
+ let parts: string[] = TokenValueExtractor.splitPath(token);
16
16
 
17
17
  let key: string = parts[1];
18
18
  let bIndex: number = key.indexOf('[');
19
19
  let fromIndex = 2;
20
20
  if (bIndex != -1) {
21
21
  key = parts[1].substring(0, bIndex);
22
+ parts = [...parts]; // Copy since we're modifying
22
23
  parts[1] = parts[1].substring(bIndex);
23
24
  fromIndex = 1;
24
25
  }
@@ -13,13 +13,14 @@ export class ContextTokenValueExtractor extends TokenValueExtractor {
13
13
  }
14
14
 
15
15
  protected getValueInternal(token: string): any {
16
- let parts: string[] = token.split(TokenValueExtractor.REGEX_DOT);
16
+ let parts: string[] = TokenValueExtractor.splitPath(token);
17
17
 
18
18
  let key: string = parts[1];
19
19
  let bIndex: number = key.indexOf('[');
20
20
  let fromIndex = 2;
21
21
  if (bIndex != -1) {
22
22
  key = parts[1].substring(0, bIndex);
23
+ parts = [...parts]; // Copy since we're modifying
23
24
  parts[1] = parts[1].substring(bIndex);
24
25
  fromIndex = 1;
25
26
  }
@@ -11,7 +11,7 @@ export class OutputMapTokenValueExtractor extends TokenValueExtractor {
11
11
  }
12
12
 
13
13
  protected getValueInternal(token: string): any {
14
- let parts: string[] = token.split(TokenValueExtractor.REGEX_DOT);
14
+ let parts: string[] = TokenValueExtractor.splitPath(token);
15
15
 
16
16
  let ind: number = 1;
17
17