@routier/core 0.0.1-alpha.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 (168) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +91 -0
  3. package/rspack.config.mjs +65 -0
  4. package/src/assertions/index.ts +37 -0
  5. package/src/codegen/SlotPath.ts +20 -0
  6. package/src/codegen/blocks.ts +578 -0
  7. package/src/codegen/handlers/CloneHandlerBuilder.ts +13 -0
  8. package/src/codegen/handlers/CompareHandlerBuilder.ts +13 -0
  9. package/src/codegen/handlers/DeserializeHandlerBuilder.ts +20 -0
  10. package/src/codegen/handlers/EnableChangeTrackingHandlerBuilder.ts +13 -0
  11. package/src/codegen/handlers/EnrichmentHandlerBuilder.ts +28 -0
  12. package/src/codegen/handlers/FreezeHandlerBuilder.ts +13 -0
  13. package/src/codegen/handlers/HashHandlerBuilder.ts +25 -0
  14. package/src/codegen/handlers/HashTypeHandlerBuilder.ts +11 -0
  15. package/src/codegen/handlers/IdSelectorHandlerBuilder.ts +10 -0
  16. package/src/codegen/handlers/MergeHandlerBuilder.ts +19 -0
  17. package/src/codegen/handlers/PrepareHandlerBuilder.ts +23 -0
  18. package/src/codegen/handlers/SerializeHandlerBuilder.ts +15 -0
  19. package/src/codegen/handlers/StripHandlerBuilder.ts +18 -0
  20. package/src/codegen/handlers/clone/CloneObjectHandler.ts +29 -0
  21. package/src/codegen/handlers/clone/CloneValueHandler.ts +33 -0
  22. package/src/codegen/handlers/compare/CompareObjectHandler.ts +15 -0
  23. package/src/codegen/handlers/compare/CompareValueHandler.ts +27 -0
  24. package/src/codegen/handlers/deserialize/DeserializeComputedValueHandler.ts +16 -0
  25. package/src/codegen/handlers/deserialize/DeserializeDateHandler.ts +45 -0
  26. package/src/codegen/handlers/deserialize/DeserializeFunctionHandler.ts +16 -0
  27. package/src/codegen/handlers/deserialize/DeserializeObjectHandler.ts +29 -0
  28. package/src/codegen/handlers/deserialize/DeserializeValueHandler.ts +33 -0
  29. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingObjectHandler.ts +20 -0
  30. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingPrimitiveValueHandler.ts +15 -0
  31. package/src/codegen/handlers/enrichment/EnrichmentComputedValueHandler.ts +37 -0
  32. package/src/codegen/handlers/enrichment/EnrichmentDefaultFunctionHandler.ts +50 -0
  33. package/src/codegen/handlers/enrichment/EnrichmentDefaultValueHandler.ts +18 -0
  34. package/src/codegen/handlers/enrichment/EnrichmentFunctionHandler.ts +38 -0
  35. package/src/codegen/handlers/enrichment/EnrichmentNullableObjectHandler.ts +44 -0
  36. package/src/codegen/handlers/enrichment/EnrichmentObjectHandler.ts +29 -0
  37. package/src/codegen/handlers/enrichment/EnrichmentObjectIdentityHandler.ts +16 -0
  38. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveHandler.ts +13 -0
  39. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveIdentityHandler.ts +21 -0
  40. package/src/codegen/handlers/freeze/FreezeObjectHandler.ts +20 -0
  41. package/src/codegen/handlers/freeze/FreezePrimitiveValueHandler.ts +15 -0
  42. package/src/codegen/handlers/hash/HashComputedValueHandler.ts +15 -0
  43. package/src/codegen/handlers/hash/HashDateHandler.ts +26 -0
  44. package/src/codegen/handlers/hash/HashFunctionHandler.ts +16 -0
  45. package/src/codegen/handlers/hash/HashIdentityHandler.ts +15 -0
  46. package/src/codegen/handlers/hash/HashKeyHandler.ts +26 -0
  47. package/src/codegen/handlers/hash/HashValueHandler.ts +26 -0
  48. package/src/codegen/handlers/hashType/HashTypeValueHandler.ts +20 -0
  49. package/src/codegen/handlers/idSelector/IdSelectorValueHandler.ts +28 -0
  50. package/src/codegen/handlers/index.ts +13 -0
  51. package/src/codegen/handlers/merge/MergeComputedValueHandler.ts +37 -0
  52. package/src/codegen/handlers/merge/MergeDefaultFunctionHandler.ts +40 -0
  53. package/src/codegen/handlers/merge/MergeDefaultValueHandler.ts +32 -0
  54. package/src/codegen/handlers/merge/MergeFunctionHandler.ts +16 -0
  55. package/src/codegen/handlers/merge/MergePrimitiveHandler.ts +21 -0
  56. package/src/codegen/handlers/prepare/PrepareComputedValueHandler.ts +16 -0
  57. package/src/codegen/handlers/prepare/PrepareFunctionHandler.ts +16 -0
  58. package/src/codegen/handlers/prepare/PrepareIdentityHandler.ts +21 -0
  59. package/src/codegen/handlers/prepare/PrepareKeyHandler.ts +21 -0
  60. package/src/codegen/handlers/prepare/PrepareObjectHandler.ts +38 -0
  61. package/src/codegen/handlers/prepare/PrepareValueHandler.ts +36 -0
  62. package/src/codegen/handlers/serialize/SerializeDateHandler.ts +45 -0
  63. package/src/codegen/handlers/serialize/SerializeObjectHandler.ts +28 -0
  64. package/src/codegen/handlers/serialize/SerializeValueHandler.ts +33 -0
  65. package/src/codegen/handlers/strip/StripIdentityHandler.ts +15 -0
  66. package/src/codegen/handlers/strip/StripKeyHandler.ts +15 -0
  67. package/src/codegen/handlers/strip/StripObjectHandler.ts +35 -0
  68. package/src/codegen/handlers/strip/StripValueHandler.ts +36 -0
  69. package/src/codegen/handlers/types.ts +119 -0
  70. package/src/codegen/index.ts +2 -0
  71. package/src/codegen/types.ts +2 -0
  72. package/src/codegen/utils.ts +74 -0
  73. package/src/collections/Changes.test.ts +337 -0
  74. package/src/collections/Changes.ts +177 -0
  75. package/src/collections/IdSet.ts +28 -0
  76. package/src/collections/MemoryDataCollection.test.ts +424 -0
  77. package/src/collections/MemoryDataCollection.ts +134 -0
  78. package/src/collections/SchemaCollection.ts +22 -0
  79. package/src/collections/TagCollection.test.ts +443 -0
  80. package/src/collections/TagCollection.ts +62 -0
  81. package/src/collections/index.ts +5 -0
  82. package/src/errors/SchemaError.ts +6 -0
  83. package/src/errors/index.ts +1 -0
  84. package/src/expressions/index.ts +3 -0
  85. package/src/expressions/parser.test.ts +913 -0
  86. package/src/expressions/parser.ts +661 -0
  87. package/src/expressions/types.ts +184 -0
  88. package/src/expressions/utils.test.ts +346 -0
  89. package/src/expressions/utils.ts +59 -0
  90. package/src/index.ts +12 -0
  91. package/src/performance/index.ts +26 -0
  92. package/src/pipeline/SyncronousQueue.test.ts +269 -0
  93. package/src/pipeline/SyncronousQueue.ts +30 -0
  94. package/src/pipeline/TrampolinePipeline.test.ts +374 -0
  95. package/src/pipeline/TrampolinePipeline.ts +437 -0
  96. package/src/pipeline/index.ts +2 -0
  97. package/src/plugins/EphemeralDataPlugin.ts +132 -0
  98. package/src/plugins/capabilities/DbPluginCapability.ts +111 -0
  99. package/src/plugins/capabilities/index.ts +2 -0
  100. package/src/plugins/capabilities/logging/DbPluginLoggingCapability.ts +261 -0
  101. package/src/plugins/capabilities/logging/index.ts +1 -0
  102. package/src/plugins/index.ts +6 -0
  103. package/src/plugins/query/Query.ts +67 -0
  104. package/src/plugins/query/QueryOptionsCollection.test.ts +86 -0
  105. package/src/plugins/query/QueryOptionsCollection.ts +154 -0
  106. package/src/plugins/query/index.ts +3 -0
  107. package/src/plugins/query/types.ts +46 -0
  108. package/src/plugins/replication/OptimisticReplicationDbPlugin.ts +202 -0
  109. package/src/plugins/replication/ReplicationDbPlugin.ts +137 -0
  110. package/src/plugins/replication/index.ts +3 -0
  111. package/src/plugins/replication/types.ts +5 -0
  112. package/src/plugins/translators/DataTranslator.ts +46 -0
  113. package/src/plugins/translators/JsonTranslator.test.ts +618 -0
  114. package/src/plugins/translators/JsonTranslator.ts +211 -0
  115. package/src/plugins/translators/SqlTranslator.ts +45 -0
  116. package/src/plugins/translators/index.ts +3 -0
  117. package/src/plugins/types.ts +114 -0
  118. package/src/results/Result.ts +91 -0
  119. package/src/results/index.ts +3 -0
  120. package/src/results/types.ts +17 -0
  121. package/src/results/utils.ts +15 -0
  122. package/src/schema/PropertyInfo.test.ts +479 -0
  123. package/src/schema/PropertyInfo.ts +374 -0
  124. package/src/schema/SchemaDefinition.ts +626 -0
  125. package/src/schema/builder.ts +18 -0
  126. package/src/schema/index.ts +7 -0
  127. package/src/schema/property/base/SchemaBase.ts +49 -0
  128. package/src/schema/property/base/index.ts +1 -0
  129. package/src/schema/property/modifiers/SchemaDefault.ts +29 -0
  130. package/src/schema/property/modifiers/SchemaDeserialize.ts +23 -0
  131. package/src/schema/property/modifiers/SchemaDistinct.ts +14 -0
  132. package/src/schema/property/modifiers/SchemaFrom.ts +54 -0
  133. package/src/schema/property/modifiers/SchemaIdentity.ts +13 -0
  134. package/src/schema/property/modifiers/SchemaIndex.ts +60 -0
  135. package/src/schema/property/modifiers/SchemaKey.ts +28 -0
  136. package/src/schema/property/modifiers/SchemaNullable.ts +18 -0
  137. package/src/schema/property/modifiers/SchemaOptional.ts +19 -0
  138. package/src/schema/property/modifiers/SchemaReadonly.ts +34 -0
  139. package/src/schema/property/modifiers/SchemaSerialize.ts +18 -0
  140. package/src/schema/property/modifiers/SchemaTracked.ts +14 -0
  141. package/src/schema/property/modifiers/index.ts +12 -0
  142. package/src/schema/property/types/SchemaArray.ts +50 -0
  143. package/src/schema/property/types/SchemaBoolean.ts +59 -0
  144. package/src/schema/property/types/SchemaDate.ts +58 -0
  145. package/src/schema/property/types/SchemaNumber.ts +69 -0
  146. package/src/schema/property/types/SchemaObject.ts +44 -0
  147. package/src/schema/property/types/SchemaString.ts +69 -0
  148. package/src/schema/property/types/index.ts +6 -0
  149. package/src/schema/table/SchemaComputed.ts +20 -0
  150. package/src/schema/table/SchemaFunction.ts +15 -0
  151. package/src/schema/table/index.ts +2 -0
  152. package/src/schema/types.ts +238 -0
  153. package/src/types/index.ts +5 -0
  154. package/src/utilities/arrays.test.ts +312 -0
  155. package/src/utilities/arrays.ts +12 -0
  156. package/src/utilities/dates.test.ts +388 -0
  157. package/src/utilities/dates.ts +11 -0
  158. package/src/utilities/dbPluginEventUtils.ts +44 -0
  159. package/src/utilities/index.ts +10 -0
  160. package/src/utilities/objects.ts +7 -0
  161. package/src/utilities/queryOptionsCollection.ts +16 -0
  162. package/src/utilities/replication.ts +23 -0
  163. package/src/utilities/runtime.ts +3 -0
  164. package/src/utilities/strings.ts +18 -0
  165. package/src/utilities/types.ts +1 -0
  166. package/src/utilities/uuid.ts +56 -0
  167. package/tsconfig.json +28 -0
  168. package/vitest.config.ts +11 -0
@@ -0,0 +1,661 @@
1
+ import { CompiledSchema, SchemaTypes } from "../schema";
2
+ import { Expression, OperatorExpression, ComparatorExpression, ValueExpression, PropertyExpression, Filter, ParamsFilter, Operator } from "./types";
3
+
4
+ // Pre-compiled regex patterns for better performance
5
+ const METHOD_REGEX = /([a-zA-Z0-9_.]+)\.(startsWith|endsWith|includes)\(([^)]+)\)(\s*(===|==|!==|!=)\s*(true|false))?/;
6
+ const TRANSFORM_METHOD_REGEX = /([a-zA-Z0-9_.]+)\.(toLowerCase|toUpperCase|toLocaleLowerCase|toLocaleUpperCase)\(\)\.(startsWith|endsWith|includes)\(((?:[^()]|\([^)]*\))*)\)(\s*(===|==|!==|!=)\s*(true|false))?/;
7
+ const EQUALITY_REGEX = /([^=!<>]+?)\s*(===|==|!==|!=)\s*(.+)/;
8
+ const COMPARISON_REGEX = /([^=!<>]+?)\s*(>=|<=|>|<)\s*(.+)/;
9
+ const PARAM_PATH_REGEX = /^([a-zA-Z0-9_.]+)/;
10
+
11
+ // Pre-compiled regex patterns for comment removal
12
+ const SINGLE_LINE_COMMENT_REGEX = /\/\/.*$/gm;
13
+ const MULTI_LINE_COMMENT_REGEX = /\/\*[\s\S]*?\*\//g;
14
+ const WHITESPACE_REGEX = /\s+/g;
15
+
16
+ // need to have negated + strict
17
+ const comparators: Record<string, ComparatorExpression> = {
18
+ startsWith: new ComparatorExpression({
19
+ comparator: "starts-with",
20
+ negated: false,
21
+ strict: false
22
+ }),
23
+ endsWith: new ComparatorExpression({
24
+ comparator: "ends-with",
25
+ negated: false,
26
+ strict: false
27
+ }),
28
+ includes: new ComparatorExpression({
29
+ comparator: "includes",
30
+ negated: false,
31
+ strict: false
32
+ }),
33
+ "==": new ComparatorExpression({
34
+ comparator: "equals",
35
+ negated: false,
36
+ strict: false
37
+ }),
38
+ "===": new ComparatorExpression({
39
+ comparator: "equals",
40
+ negated: false,
41
+ strict: true
42
+ }),
43
+ "!=": new ComparatorExpression({
44
+ comparator: "equals",
45
+ negated: true,
46
+ strict: false
47
+ }),
48
+ "!==": new ComparatorExpression({
49
+ comparator: "equals",
50
+ negated: true,
51
+ strict: true
52
+ }),
53
+ ">=": new ComparatorExpression({
54
+ comparator: "greater-than-equals",
55
+ negated: false,
56
+ strict: false
57
+ }),
58
+ ">==": new ComparatorExpression({
59
+ comparator: "greater-than-equals",
60
+ negated: false,
61
+ strict: true
62
+ }),
63
+ "<=": new ComparatorExpression({
64
+ comparator: "less-than-equals",
65
+ negated: false,
66
+ strict: false
67
+ }),
68
+ "<==": new ComparatorExpression({
69
+ comparator: "less-than-equals",
70
+ negated: false,
71
+ strict: true
72
+ }),
73
+ ">": new ComparatorExpression({
74
+ comparator: "greater-than",
75
+ negated: false,
76
+ strict: false
77
+ }),
78
+ "<": new ComparatorExpression({
79
+ comparator: "less-than",
80
+ negated: false,
81
+ strict: false
82
+ })
83
+ } as const;
84
+
85
+ // Optimized comparator lookup using Map
86
+ const COMPARATOR_MAP = new Map(Object.entries(comparators));
87
+
88
+ // Error message constants
89
+ const ERROR_MESSAGES = {
90
+ COMPARATOR_NOT_FOUND: (value: string) => `Cannot find comparator: ${value}`,
91
+ PROPERTY_NOT_FOUND: (path: string) => `Error parsing query, could not find PropertyInfo for path: ${path}`,
92
+ PARAM_PATH_NOT_FOUND: (value: string, params: any) => `Cannot find path in params for .where(). Make sure parameters are not used inline.\r\nPath: ${value}, Params: ${JSON.stringify(params)}`
93
+ };
94
+
95
+ export const combineExpressions = (...expressions: Expression[]): Expression => {
96
+
97
+ if (expressions.length === 0) {
98
+ throw new Error("combineExpressions requires at least 1 expression");
99
+ }
100
+
101
+
102
+ if (expressions.length === 1) {
103
+ return expressions[0];
104
+ }
105
+
106
+ // Start with the first expression
107
+ let result = expressions[0];
108
+
109
+ // Loop through remaining expressions and combine them
110
+ for (let i = 1; i < expressions.length; i++) {
111
+ result = new OperatorExpression({
112
+ operator: "&&",
113
+ left: result,
114
+ right: expressions[i]
115
+ });
116
+ }
117
+
118
+ return result;
119
+ };
120
+
121
+ export const toExpression = <T extends any, P extends any>(schema: CompiledSchema<any>, fn: Filter<T> | ParamsFilter<T, P>, params?: P) => {
122
+ try {
123
+
124
+ const stringifiedFunction = fn.toString();
125
+
126
+ // Optimized string parsing
127
+ const arrowIndex = stringifiedFunction.indexOf('=>');
128
+ if (arrowIndex === -1) {
129
+ throw new Error("Invalid Function");
130
+ }
131
+
132
+ const parameterNames = stringifiedFunction.substring(0, arrowIndex).trim();
133
+ let expression = stringifiedFunction.substring(arrowIndex + 2).trim();
134
+
135
+ // Remove JavaScript comments from the expression
136
+ expression = expression
137
+ .replace(SINGLE_LINE_COMMENT_REGEX, '') // Remove single-line comments
138
+ .replace(MULTI_LINE_COMMENT_REGEX, '') // Remove multi-line comments
139
+ .replace(WHITESPACE_REGEX, ' ') // Normalize whitespace
140
+ .trim();
141
+
142
+ let parameterData: { name: string, data: P } | undefined = undefined;
143
+
144
+ if (params != null) {
145
+ let name: string;
146
+ if (parameterNames.includes("[") && parameterNames.includes("]")) {
147
+ // Optimized parameter name extraction
148
+ const commaIndex = parameterNames.indexOf(",");
149
+ if (commaIndex !== -1) {
150
+ const bracketIndex = parameterNames.indexOf("]", commaIndex);
151
+ if (bracketIndex !== -1) {
152
+ name = parameterNames.substring(commaIndex + 1, bracketIndex).trim();
153
+ }
154
+ }
155
+ }
156
+
157
+ parameterData = {
158
+ name,
159
+ data: params
160
+ };
161
+ }
162
+
163
+ return parseExpressionToTree(schema, expression, parameterData);
164
+ } catch (e) {
165
+ console.warn("Error parsing expression", e);
166
+ return Expression.NOT_PARSABLE;
167
+ }
168
+ }
169
+
170
+ const parseExpressionToTree = <P extends any>(schema: CompiledSchema<any>, expression: string, params?: { name: string, data: P }) => {
171
+
172
+ const parse = (exp: string): Expression => {
173
+ // Remove any wrapping parentheses
174
+ exp = exp.trim();
175
+ if (exp.startsWith('(') && exp.endsWith(')')) {
176
+ let depth = 0;
177
+ let isWrapper = true;
178
+ for (let i = 0; i < exp.length; i++) {
179
+ const char = exp[i];
180
+ if (char === '(') {
181
+ depth++;
182
+ } else if (char === ')') {
183
+ depth--;
184
+ if (depth < 0) break; // Early exit for invalid parentheses
185
+ }
186
+ // Early exit if we find a closing parenthesis that's not the last character
187
+ if (depth === 0 && i !== exp.length - 1) {
188
+ isWrapper = false;
189
+ break; // Early exit
190
+ }
191
+ // Early exit if we go negative (invalid parentheses)
192
+ if (depth < 0) {
193
+ isWrapper = false;
194
+ break;
195
+ }
196
+ }
197
+ if (isWrapper) {
198
+ exp = exp.slice(1, -1).trim();
199
+ }
200
+ }
201
+
202
+ // Parse based on the operator precedence
203
+ let operator: Operator | null = null, splitIndex = -1, depth = 0;
204
+
205
+ for (let i = 0; i < exp.length - 1; i++) {
206
+ const char = exp[i];
207
+ if (char === '(') depth++;
208
+ else if (char === ')') depth--;
209
+ else if (depth === 0) {
210
+ // Optimized operator detection using character comparison
211
+ if (char === '&' && exp[i + 1] === '&') {
212
+ operator = '&&';
213
+ splitIndex = i;
214
+ break; // AND takes precedence over OR
215
+ } else if (operator === null && char === '|' && exp[i + 1] === '|') {
216
+ operator = '||';
217
+ splitIndex = i;
218
+ }
219
+ }
220
+ }
221
+
222
+ if (operator) {
223
+ const left = exp.slice(0, splitIndex).trim();
224
+ const right = exp.slice(splitIndex + 2).trim();
225
+
226
+ return new OperatorExpression({
227
+ operator,
228
+ left: parse(left),
229
+ right: parse(right)
230
+ });
231
+ }
232
+
233
+ // If no operator, try to parse as a condition
234
+ return parseCondition(schema, exp, params);
235
+ }
236
+
237
+ return parse(expression);
238
+ }
239
+
240
+ const convertAndAssignValue = (valueExpression: unknown, propertyPathExpression: unknown) => {
241
+
242
+ assertIsPropertyPathExpression(propertyPathExpression);
243
+
244
+ assertIsValueExpression(valueExpression)
245
+
246
+ const propertyType = propertyPathExpression.property.type;
247
+
248
+ valueExpression.value = converters[propertyType](valueExpression.value);
249
+ }
250
+
251
+ const converters: Record<SchemaTypes, (value: unknown) => unknown> = {
252
+ Array: v => v,
253
+ Boolean: v => v == null ? v : Boolean(v),
254
+ Computed: v => v,
255
+ Date: v => v,
256
+ Definition: v => v,
257
+ Function: v => v,
258
+ Number: v => v == null ? v : Number(v),
259
+ Object: v => v,
260
+ String: v => v == null ? v : String(v)
261
+ };
262
+
263
+ const isExpression = (value: unknown): value is Expression => {
264
+ return typeof value === "object" && value !== null && "type" in value;
265
+ }
266
+
267
+ function assertIsExpression(value: unknown): asserts value is Expression {
268
+ if (isExpression(value) === false) {
269
+ throw new Error("Assertion Failed: Value is not a Expression")
270
+ }
271
+ }
272
+
273
+ function assertIsPropertyPathExpression(value: unknown): asserts value is PropertyExpression {
274
+
275
+ assertIsExpression(value);
276
+
277
+ if (!("property" in value)) {
278
+ throw new Error("Assertion Failed: Value is not a PropertyPathExpression")
279
+ }
280
+ }
281
+
282
+ function assertIsValueExpression(value: unknown): asserts value is ValueExpression {
283
+
284
+ assertIsExpression(value);
285
+
286
+ if (!("value" in value)) {
287
+ throw new Error("Assertion Failed: Value is not a ValueExpression")
288
+ }
289
+ }
290
+
291
+ // Helper function to detect if a string is a property path
292
+ const isPropertyPath = (value: string): boolean => {
293
+ return value.includes('.') && value.match(/^[a-zA-Z0-9_.]+$/) !== null;
294
+ };
295
+
296
+ // Helper function to determine if we need to swap the operator for reversed comparisons
297
+ const getSwappedOperator = (operator: string): string => {
298
+ const swapMap: Record<string, string> = {
299
+ '>': '<',
300
+ '<': '>',
301
+ '>=': '<=',
302
+ '<=': '>='
303
+ };
304
+ return swapMap[operator] || operator;
305
+ };
306
+
307
+ const parseCondition = <P extends any>(schema: CompiledSchema<any>, expression: string, params?: { name: string, data: P }): Expression => {
308
+
309
+ // Optimized string operations - single trim operation
310
+ const trimmed = expression.trim();
311
+ const isNegation = trimmed.startsWith('!');
312
+ const finalExpression = isNegation ? trimmed.slice(1).trim() : trimmed;
313
+
314
+ // Enhanced pattern matching for complex expressions
315
+ const methodMatch = finalExpression.match(METHOD_REGEX);
316
+ const transformMethodMatch = finalExpression.match(TRANSFORM_METHOD_REGEX);
317
+ const equalityMatch = finalExpression.match(EQUALITY_REGEX);
318
+ const comparisonMatch = finalExpression.match(COMPARISON_REGEX);
319
+
320
+ if (methodMatch) {
321
+ // Handle .startsWith or .endsWith
322
+ const comparator = getComparator(methodMatch[2]);
323
+
324
+ if (isNegation) {
325
+ comparator.negated = isNegation;
326
+ }
327
+
328
+ // Check if the left side is a parameter path
329
+ const leftSide = methodMatch[1];
330
+ const rightSide = methodMatch[3];
331
+
332
+ if (params && leftSide.startsWith(params.name)) {
333
+ // This is a parameter path on the left side (e.g., params.distinctPlayers.includes(entity.playerId))
334
+ // For includes method, we need to swap left and right sides
335
+ if (methodMatch[2] === 'includes') {
336
+ comparator.left = getValue(leftSide, params);
337
+ comparator.right = getProperty(schema, rightSide);
338
+ } else {
339
+ // For other methods, return NOT_PARSABLE for now
340
+ return Expression.NOT_PARSABLE;
341
+ }
342
+ } else {
343
+ // Normal case: property on left, value on right
344
+ comparator.left = getProperty(schema, leftSide);
345
+ comparator.right = getValue(rightSide, params);
346
+ }
347
+
348
+ // If the comparison is explicitly to false, mark it as negated
349
+ if (methodMatch[6] === "false") {
350
+ comparator.negated = true;
351
+ }
352
+
353
+ return comparator;
354
+ }
355
+
356
+ // Check for transformations on the value side (right side of comparison) - check this FIRST
357
+ const valueTransformMatch = finalExpression.match(/([^=!<>]+?)\s*(===|==|!==|!=)\s*([^)]+)\.(toLowerCase|toUpperCase|toLocaleLowerCase|toLocaleUpperCase)\(\)/);
358
+ if (valueTransformMatch) {
359
+ const comparator = getComparator(valueTransformMatch[2]);
360
+
361
+ if (isNegation) {
362
+ comparator.negated = isNegation;
363
+ }
364
+
365
+ // Create the property expression for the left side (no transformer)
366
+ const propertyExpression = getProperty(schema, valueTransformMatch[1].trim());
367
+
368
+ // Create a ValueExpression for the right side with transformer
369
+ const valueExpression = getValue(valueTransformMatch[3], params);
370
+
371
+ // Set transformer and locale based on the method
372
+ const method = valueTransformMatch[4];
373
+ if (method === 'toLowerCase' || method === 'toLocaleLowerCase') {
374
+ valueExpression.transformer = 'to-lower-case';
375
+ if (method === 'toLocaleLowerCase') {
376
+ valueExpression.locale = 'en-US';
377
+ }
378
+ } else {
379
+ valueExpression.transformer = 'to-upper-case';
380
+ if (method === 'toLocaleUpperCase') {
381
+ valueExpression.locale = 'en-US';
382
+ }
383
+ }
384
+
385
+ comparator.left = propertyExpression;
386
+ comparator.right = valueExpression;
387
+
388
+ return comparator;
389
+ }
390
+
391
+ if (transformMethodMatch) {
392
+ // Handle .toLowerCase or .toUpperCase on the property side
393
+ const comparator = getComparator(transformMethodMatch[3]); // e.g., startsWith, endsWith, includes
394
+
395
+ if (isNegation) {
396
+ comparator.negated = isNegation;
397
+ }
398
+
399
+ // Create the property expression for the left side with transformer
400
+ const propertyExpression = getProperty(schema, transformMethodMatch[1]);
401
+
402
+ // Set transformer and locale based on the method
403
+ const method = transformMethodMatch[2];
404
+ if (method === 'toLowerCase' || method === 'toLocaleLowerCase') {
405
+ propertyExpression.transformer = 'to-lower-case';
406
+ if (method === 'toLocaleLowerCase') {
407
+ propertyExpression.locale = 'en-US'; // Default locale, could be extracted from method call
408
+ }
409
+ } else {
410
+ propertyExpression.transformer = 'to-upper-case';
411
+ if (method === 'toLocaleUpperCase') {
412
+ propertyExpression.locale = 'en-US'; // Default locale, could be extracted from method call
413
+ }
414
+ }
415
+
416
+ // Create a ValueExpression for the right side
417
+ let valueExpression = getValue(transformMethodMatch[4], params);
418
+
419
+ // Check if the value also has a transformation (but only if it's not already handled by value-side check)
420
+ const valueTransformMatch = transformMethodMatch[4].match(/^([^)]+)\.(toLowerCase|toUpperCase|toLocaleLowerCase|toLocaleUpperCase)\(\)$/);
421
+
422
+ if (valueTransformMatch) {
423
+ // Create a new ValueExpression with the base value and transformer
424
+ valueExpression = new ValueExpression({
425
+ value: valueTransformMatch[1].replace(/^["']|["']$/g, '') // Remove quotes
426
+ });
427
+
428
+ // Set transformer and locale based on the method
429
+ const valueMethod = valueTransformMatch[2];
430
+ if (valueMethod === 'toLowerCase' || valueMethod === 'toLocaleLowerCase') {
431
+ valueExpression.transformer = 'to-lower-case';
432
+ if (valueMethod === 'toLocaleLowerCase') {
433
+ valueExpression.locale = 'en-US'; // Default locale
434
+ }
435
+ } else {
436
+ valueExpression.transformer = 'to-upper-case';
437
+ if (valueMethod === 'toLocaleUpperCase') {
438
+ valueExpression.locale = 'en-US'; // Default locale
439
+ }
440
+ }
441
+ }
442
+
443
+ comparator.left = propertyExpression;
444
+ comparator.right = valueExpression;
445
+
446
+ // If the comparison is explicitly to false, mark it as negated
447
+ if (transformMethodMatch[7] === "false") {
448
+ comparator.negated = true;
449
+ }
450
+
451
+ return comparator;
452
+ }
453
+
454
+ if (equalityMatch) {
455
+ const left = equalityMatch[1].trim();
456
+ const operator = equalityMatch[2];
457
+ const right = equalityMatch[3].trim();
458
+
459
+ const leftIsProperty = isPropertyPath(left);
460
+ const rightIsProperty = isPropertyPath(right);
461
+
462
+ // Determine which side is the property and which is the value
463
+ let propertySide: string, valueSide: string, finalOperator: string;
464
+
465
+ if (leftIsProperty && !rightIsProperty) {
466
+ // Normal case: property === value
467
+ propertySide = left;
468
+ valueSide = right;
469
+ finalOperator = operator;
470
+ } else if (!leftIsProperty && rightIsProperty) {
471
+ // Reversed case: value === property
472
+ propertySide = right;
473
+ valueSide = left;
474
+ finalOperator = operator;
475
+ } else {
476
+ // Both sides look like properties or neither does - assume left is property
477
+ propertySide = left;
478
+ valueSide = right;
479
+ finalOperator = operator;
480
+ }
481
+
482
+ const comparator = getComparator(finalOperator);
483
+
484
+ if (isNegation) {
485
+ comparator.negated = isNegation;
486
+ }
487
+
488
+ comparator.left = getProperty(schema, propertySide);
489
+ comparator.right = getValue(valueSide, params);
490
+
491
+ convertAndAssignValue(comparator.right, comparator.left);
492
+
493
+ return comparator;
494
+ }
495
+
496
+ if (comparisonMatch) {
497
+ const left = comparisonMatch[1].trim();
498
+ const operator = comparisonMatch[2];
499
+ const right = comparisonMatch[3].trim();
500
+
501
+ const leftIsProperty = isPropertyPath(left);
502
+ const rightIsProperty = isPropertyPath(right);
503
+
504
+ // Determine which side is the property and which is the value
505
+ let propertySide: string, valueSide: string, finalOperator: string;
506
+
507
+ if (leftIsProperty && !rightIsProperty) {
508
+ // Normal case: property > value
509
+ propertySide = left;
510
+ valueSide = right;
511
+ finalOperator = operator;
512
+ } else if (!leftIsProperty && rightIsProperty) {
513
+ // Reversed case: value > property -> property < value
514
+ propertySide = right;
515
+ valueSide = left;
516
+ finalOperator = getSwappedOperator(operator);
517
+ } else {
518
+ // Both sides look like properties or neither does - assume left is property
519
+ propertySide = left;
520
+ valueSide = right;
521
+ finalOperator = operator;
522
+ }
523
+
524
+ const comparator = getComparator(finalOperator);
525
+
526
+ if (isNegation) {
527
+ comparator.negated = isNegation;
528
+ }
529
+
530
+ comparator.left = getProperty(schema, propertySide);
531
+ comparator.right = getValue(valueSide, params);
532
+
533
+ convertAndAssignValue(comparator.right, comparator.left);
534
+
535
+ return comparator;
536
+ }
537
+
538
+ // If we get here, the expression is too complex for the current parser
539
+ // This is expected for complex real-world expressions
540
+ throw new Error(`Unsupported expression format: ${finalExpression}`);
541
+ }
542
+
543
+ const getComparator = (value: string): ComparatorExpression => {
544
+
545
+ const comparator = COMPARATOR_MAP.get(value);
546
+
547
+ if (comparator == null) {
548
+ throw new Error(ERROR_MESSAGES.COMPARATOR_NOT_FOUND(value))
549
+ }
550
+
551
+ // Return a new instance to avoid modifying the cached one
552
+ return new ComparatorExpression({
553
+ comparator: comparator.comparator,
554
+ negated: comparator.negated,
555
+ strict: comparator.strict
556
+ });
557
+ }
558
+
559
+ const getValue = <P extends any>(value: string, params?: { name: string, data: P }): ValueExpression => {
560
+
561
+ // Early return for string literals
562
+ if (value.startsWith("\'") || value.startsWith("\"")) {
563
+ return new ValueExpression({
564
+ value: value.replace(/\"|\'/g, "")
565
+ });
566
+ }
567
+
568
+ // Early return for null/undefined
569
+ if (value === "null") {
570
+ return new ValueExpression({ value: null });
571
+ }
572
+
573
+ if (value === "undefined" || value === "void 0") {
574
+ return new ValueExpression({ value: undefined });
575
+ }
576
+
577
+ // Optimized number parsing with early return
578
+ const numValue = +value;
579
+ if (!isNaN(numValue) && isFinite(numValue)) {
580
+ return new ValueExpression({ value: numValue });
581
+ }
582
+
583
+ // Handle parameter paths if params are provided
584
+ if (params != null) {
585
+ // Check if this is a parameter path (starts with params.name)
586
+ if (value.startsWith(params.name)) {
587
+ return new ValueExpression({
588
+ value: getValueFromParams(value, params)
589
+ });
590
+ }
591
+
592
+ // Check if this is a parameter path without the params prefix
593
+ if (value.includes('.')) {
594
+ // Try to extract the parameter path
595
+ const paramMatch = value.match(PARAM_PATH_REGEX);
596
+ if (paramMatch) {
597
+ const potentialParamPath = paramMatch[1];
598
+ try {
599
+ const paramValue = getValueFromParams(`${params.name}.${potentialParamPath}`, params);
600
+ return new ValueExpression({ value: paramValue });
601
+ } catch (e) {
602
+ // Not a parameter path, continue with normal parsing
603
+ }
604
+ }
605
+ }
606
+ }
607
+
608
+ // Default case: return as-is
609
+ return new ValueExpression({ value });
610
+ }
611
+
612
+ const getValueFromParams = <P extends any>(value: string, params: { name: string, data: P }) => {
613
+
614
+ const split = value.split('.');
615
+
616
+ // Early exit for invalid paths
617
+ if (split.length === 1) {
618
+ throw new Error(ERROR_MESSAGES.PARAM_PATH_NOT_FOUND(value, params.data))
619
+ }
620
+
621
+ let result = params.data as any;
622
+
623
+ // For nested params - start from index 1 to skip the params prefix
624
+ for (let i = 1; i < split.length; i++) {
625
+ const name = split[i];
626
+
627
+ if (name in result) {
628
+ result = result[name];
629
+ continue;
630
+ }
631
+
632
+ throw new Error(ERROR_MESSAGES.PARAM_PATH_NOT_FOUND(value, params.data))
633
+ }
634
+
635
+ return result;
636
+ }
637
+
638
+ const getProperty = (schema: CompiledSchema<any>, value: string): PropertyExpression => {
639
+ // Optimized string splitting - only split if we have the expected pattern
640
+ if (!value.includes('.')) {
641
+ throw new Error(ERROR_MESSAGES.PROPERTY_NOT_FOUND(value));
642
+ }
643
+
644
+ const pathSplit = value.split(/[?!.]/g).slice(1);
645
+ const pathString = pathSplit.join(".");
646
+
647
+ // Early exit if no path found
648
+ if (!pathString) {
649
+ throw new Error(ERROR_MESSAGES.PROPERTY_NOT_FOUND(value));
650
+ }
651
+
652
+ const found = schema.properties.find(w => w.getAssignmentPath() == pathString);
653
+
654
+ if (found == null) {
655
+ throw new Error(ERROR_MESSAGES.PROPERTY_NOT_FOUND(value));
656
+ }
657
+
658
+ return new PropertyExpression({ property: found });
659
+ }
660
+
661
+