@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,184 @@
1
+ import { QueryOptionExecutionTarget } from "../plugins";
2
+ import { PropertyInfo } from "../schema";
3
+
4
+ export type ParsedExpression = {
5
+ expression: Expression;
6
+ // Will be memory when trying to query on an untracked computed property or on a function
7
+ executionTarget: QueryOptionExecutionTarget;
8
+ }
9
+
10
+ /**
11
+ * The base class for all expression types.
12
+ */
13
+ export abstract class Expression {
14
+ /** The type of the expression. */
15
+ abstract readonly type: ExpressionType;
16
+ /** The left-hand side of the expression (if applicable). */
17
+ left?: Expression;
18
+ /** The right-hand side of the expression (if applicable). */
19
+ right?: Expression;
20
+
21
+ constructor(left?: Expression, right?: Expression) {
22
+ this.left = left;
23
+ this.right = right;
24
+ }
25
+
26
+ static get EMPTY() {
27
+ return new EmptyExpression();
28
+ }
29
+
30
+ static get NOT_PARSABLE() {
31
+ return new NotParsableExpression();
32
+ }
33
+
34
+ static isEmpty(expression: Expression) {
35
+ return expression.type === "empty" || expression instanceof EmptyExpression;
36
+ }
37
+
38
+ static isNotParsable(expression: Expression) {
39
+ return expression.type === "not-parsable" || expression instanceof NotParsableExpression;
40
+ }
41
+ }
42
+
43
+ export class EmptyExpression extends Expression {
44
+ readonly type = "empty" as const;
45
+ }
46
+
47
+ export class NotParsableExpression extends Expression {
48
+ readonly type = "not-parsable" as const;
49
+ }
50
+
51
+ /**
52
+ * A class representing a comparison operation (e.g., equals, greater-than).
53
+ */
54
+ export class ComparatorExpression extends Expression {
55
+ /** The type of the expression (always 'comparator'). */
56
+ readonly type = "comparator" as const;
57
+ /** The comparator operation (e.g., equals, greater-than). */
58
+ comparator: Comparator;
59
+ /** Whether the comparison is negated (e.g., not equals). */
60
+ negated: boolean;
61
+ /** Whether the comparison is strict (type-sensitive). */
62
+ strict: boolean;
63
+
64
+ constructor(
65
+ options: {
66
+ comparator: Comparator,
67
+ negated: boolean,
68
+ strict: boolean,
69
+ left?: Expression,
70
+ right?: Expression
71
+ }
72
+ ) {
73
+ super(options.left, options.right);
74
+ this.comparator = options.comparator;
75
+ this.negated = options.negated;
76
+ this.strict = options.strict;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * A class representing a logical operator (e.g., &&, ||).
82
+ */
83
+ export class OperatorExpression extends Expression {
84
+ /** The type of the expression (always 'operator'). */
85
+ readonly type = "operator" as const;
86
+ /** The logical operator. */
87
+ operator: Operator;
88
+
89
+ constructor(options: { operator: Operator, left?: Expression, right?: Expression }) {
90
+ super(options.left, options.right);
91
+ this.operator = options.operator;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * A class representing a property path.
97
+ */
98
+ export class PropertyExpression extends Expression {
99
+ /** The type of the expression (always 'property'). */
100
+ readonly type = "property" as const;
101
+ /** The property info for the path. */
102
+ property: PropertyInfo<any>;
103
+ transformer: Transformer | null = null;
104
+ locale: string | null = null;
105
+
106
+ constructor(options: { property: PropertyInfo<any> }) {
107
+ super();
108
+ this.property = options.property;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * A class representing a literal value.
114
+ */
115
+ export class ValueExpression extends Expression {
116
+ /** The type of the expression (always 'value'). */
117
+ readonly type = "value" as const;
118
+ /** The literal value. */
119
+ value: unknown;
120
+
121
+ transformer: Transformer | null = null;
122
+ locale: string | null = null;
123
+
124
+ constructor(options: {
125
+ value: unknown
126
+ }) {
127
+ super();
128
+ this.value = options.value;
129
+ }
130
+ }
131
+
132
+
133
+ /**
134
+ * The set of possible expression types.
135
+ */
136
+ export type ExpressionType = "operator" | "comparator" | "property" | "value" | "empty" | "not-parsable";
137
+
138
+ /**
139
+ * Supported value transformations that can be applied to values.
140
+ */
141
+ export type Transformer = "to-lower-case" | "to-upper-case";
142
+
143
+ /**
144
+ * Supported comparator operations for expressions.
145
+ */
146
+ export type Comparator =
147
+ | "equals"
148
+ | "starts-with"
149
+ | "includes"
150
+ | "ends-with"
151
+ | "greater-than"
152
+ | "greater-than-equals"
153
+ | "less-than"
154
+ | "less-than-equals";
155
+
156
+ /**
157
+ * Supported logical operators for expressions.
158
+ */
159
+ export type Operator = "&&" | "||";
160
+
161
+ /**
162
+ * A function that filters a value of type T and returns a boolean.
163
+ */
164
+ export type Filter<T extends any> = (value: T) => boolean;
165
+
166
+ /**
167
+ * A function that filters a value of type T with additional parameters P.
168
+ */
169
+ export type ParamsFilter<T extends any, P> = (payload: [T, P]) => boolean;
170
+
171
+ /**
172
+ * A filter that can be either a simple filter or a parameterized filter.
173
+ */
174
+ export type CompositeFilter<T extends any, P = never> = Filter<T> | ParamsFilter<T, P>;
175
+
176
+ /**
177
+ * An object that can be filtered using a composite filter and optional parameters.
178
+ */
179
+ export type Filterable<T extends any, P = any> = {
180
+ /** The filter function. */
181
+ filter: CompositeFilter<T, P>;
182
+ /** Optional parameters for the filter. */
183
+ params?: P;
184
+ }
@@ -0,0 +1,346 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { getProperties } from './utils';
3
+ import { PropertyExpression, ComparatorExpression, OperatorExpression, ValueExpression, EmptyExpression } from './types';
4
+ import { PropertyInfo } from '../schema/PropertyInfo';
5
+
6
+ const createMockProperty = (name: string): PropertyInfo<any> => ({
7
+ name,
8
+ type: 'string',
9
+ isNullable: false,
10
+ isOptional: false,
11
+ isKey: false,
12
+ isIdentity: false,
13
+ isReadonly: false,
14
+ isUnmapped: false,
15
+ isDistinct: false,
16
+ indexes: [],
17
+ injected: null,
18
+ defaultValue: null,
19
+ valueSerializer: null,
20
+ valueDeserializer: null,
21
+ functionBody: null,
22
+ children: [],
23
+ schema: {} as any,
24
+ literals: [],
25
+ getPathArray: () => [name],
26
+ getParentPathArray: () => [] as any[],
27
+ getValue: () => undefined as any,
28
+ setValue: () => { },
29
+ getSelectrorPath: () => name,
30
+ getAssignmentPath: () => name,
31
+ get level() { return 0; },
32
+ get hasNullableParents() { return false; },
33
+ get hasIdentityChildren() { return false; },
34
+ get id() { return name; },
35
+ _getPropertyChain: () => [] as any[],
36
+ _needsOptionalChaining: () => false,
37
+ _resolvePathArray: () => [name]
38
+ } as unknown as PropertyInfo<any>);
39
+
40
+ describe('getProperties', () => {
41
+ it('should extract properties from a simple property expression', () => {
42
+ const property = createMockProperty('name');
43
+ const expression = new PropertyExpression({ property });
44
+
45
+ const result = getProperties(expression);
46
+
47
+ expect(result).toHaveLength(1);
48
+ expect(result[0]).toBe(property);
49
+ });
50
+
51
+ it('should extract properties from a comparator expression with property on left', () => {
52
+ const property = createMockProperty('age');
53
+ const expression = new ComparatorExpression({
54
+ comparator: 'equals',
55
+ negated: false,
56
+ strict: false,
57
+ left: new PropertyExpression({ property }),
58
+ right: new ValueExpression({ value: 25 })
59
+ });
60
+
61
+ const result = getProperties(expression);
62
+
63
+ expect(result).toHaveLength(1);
64
+ expect(result[0]).toBe(property);
65
+ });
66
+
67
+ it('should extract properties from a comparator expression with property on right', () => {
68
+ const property = createMockProperty('score');
69
+ const expression = new ComparatorExpression({
70
+ comparator: 'greater-than',
71
+ negated: false,
72
+ strict: false,
73
+ left: new ValueExpression({ value: 100 }),
74
+ right: new PropertyExpression({ property })
75
+ });
76
+
77
+ const result = getProperties(expression);
78
+
79
+ expect(result).toHaveLength(1);
80
+ expect(result[0]).toBe(property);
81
+ });
82
+
83
+ it('should extract properties from a comparator expression with properties on both sides', () => {
84
+ const leftProperty = createMockProperty('x');
85
+ const rightProperty = createMockProperty('y');
86
+ const expression = new ComparatorExpression({
87
+ comparator: 'equals',
88
+ negated: false,
89
+ strict: false,
90
+ left: new PropertyExpression({ property: leftProperty }),
91
+ right: new PropertyExpression({ property: rightProperty })
92
+ });
93
+
94
+ const result = getProperties(expression);
95
+
96
+ expect(result).toHaveLength(2);
97
+ expect(result).toContain(leftProperty);
98
+ expect(result).toContain(rightProperty);
99
+ });
100
+
101
+ it('should extract properties from an operator expression with two properties', () => {
102
+ const leftProperty = createMockProperty('isActive');
103
+ const rightProperty = createMockProperty('isVerified');
104
+ const expression = new OperatorExpression({
105
+ operator: '&&',
106
+ left: new PropertyExpression({ property: leftProperty }),
107
+ right: new PropertyExpression({ property: rightProperty })
108
+ });
109
+
110
+ const result = getProperties(expression);
111
+
112
+ expect(result).toHaveLength(2);
113
+ expect(result).toContain(leftProperty);
114
+ expect(result).toContain(rightProperty);
115
+ });
116
+
117
+ it('should extract properties from a complex nested expression', () => {
118
+ const prop1 = createMockProperty('name');
119
+ const prop2 = createMockProperty('age');
120
+ const prop3 = createMockProperty('email');
121
+
122
+ const innerExpression = new OperatorExpression({
123
+ operator: '||',
124
+ left: new PropertyExpression({ property: prop2 }),
125
+ right: new PropertyExpression({ property: prop3 })
126
+ });
127
+
128
+ const expression = new OperatorExpression({
129
+ operator: '&&',
130
+ left: new PropertyExpression({ property: prop1 }),
131
+ right: innerExpression
132
+ });
133
+
134
+ const result = getProperties(expression);
135
+
136
+ expect(result).toHaveLength(3);
137
+ expect(result).toContain(prop1);
138
+ expect(result).toContain(prop2);
139
+ expect(result).toContain(prop3);
140
+ });
141
+
142
+ it('should extract properties from a deeply nested expression', () => {
143
+ const prop1 = createMockProperty('level1');
144
+ const prop2 = createMockProperty('level2');
145
+ const prop3 = createMockProperty('level3');
146
+
147
+ const level3Expression = new PropertyExpression({ property: prop3 });
148
+
149
+ const level2Expression = new OperatorExpression({
150
+ operator: '&&',
151
+ left: new PropertyExpression({ property: prop2 }),
152
+ right: level3Expression
153
+ });
154
+
155
+ const level1Expression = new OperatorExpression({
156
+ operator: '||',
157
+ left: new PropertyExpression({ property: prop1 }),
158
+ right: level2Expression
159
+ });
160
+
161
+ const result = getProperties(level1Expression);
162
+
163
+ expect(result).toHaveLength(3);
164
+ expect(result).toContain(prop1);
165
+ expect(result).toContain(prop2);
166
+ expect(result).toContain(prop3);
167
+ });
168
+
169
+ it('should handle expressions with duplicate properties', () => {
170
+ const property = createMockProperty('status');
171
+ const expression = new OperatorExpression({
172
+ operator: '&&',
173
+ left: new PropertyExpression({ property }),
174
+ right: new PropertyExpression({ property })
175
+ });
176
+
177
+ const result = getProperties(expression);
178
+
179
+ expect(result).toHaveLength(2);
180
+ expect(result[0]).toBe(property);
181
+ expect(result[1]).toBe(property);
182
+ });
183
+
184
+ it('should handle expressions with only value expressions', () => {
185
+ const expression = new ValueExpression({ value: 'test' });
186
+
187
+ const result = getProperties(expression);
188
+
189
+ expect(result).toHaveLength(0);
190
+ });
191
+
192
+ it('should handle expressions with mixed property and value expressions', () => {
193
+ const property = createMockProperty('category');
194
+ const expression = new OperatorExpression({
195
+ operator: '&&',
196
+ left: new PropertyExpression({ property }),
197
+ right: new ValueExpression({ value: 'electronics' })
198
+ });
199
+
200
+ const result = getProperties(expression);
201
+
202
+ expect(result).toHaveLength(1);
203
+ expect(result[0]).toBe(property);
204
+ });
205
+
206
+ it('should handle expressions with null or undefined children', () => {
207
+ const property = createMockProperty('title');
208
+ const expression = new ComparatorExpression({
209
+ comparator: 'equals',
210
+ negated: false,
211
+ strict: false,
212
+ left: new PropertyExpression({ property }),
213
+ right: null as any
214
+ });
215
+
216
+ const result = getProperties(expression);
217
+
218
+ expect(result).toHaveLength(1);
219
+ expect(result[0]).toBe(property);
220
+ });
221
+
222
+ it('should handle empty expression', () => {
223
+ const expression = new EmptyExpression();
224
+
225
+ const result = getProperties(expression);
226
+
227
+ expect(result).toHaveLength(0);
228
+ });
229
+
230
+ it('should handle complex expression with multiple operators and properties', () => {
231
+ const prop1 = createMockProperty('firstName');
232
+ const prop2 = createMockProperty('lastName');
233
+ const prop3 = createMockProperty('email');
234
+ const prop4 = createMockProperty('phone');
235
+
236
+ const leftBranch = new OperatorExpression({
237
+ operator: '&&',
238
+ left: new PropertyExpression({ property: prop1 }),
239
+ right: new PropertyExpression({ property: prop2 })
240
+ });
241
+
242
+ const rightBranch = new OperatorExpression({
243
+ operator: '||',
244
+ left: new PropertyExpression({ property: prop3 }),
245
+ right: new PropertyExpression({ property: prop4 })
246
+ });
247
+
248
+ const rootExpression = new OperatorExpression({
249
+ operator: '&&',
250
+ left: leftBranch,
251
+ right: rightBranch
252
+ });
253
+
254
+ const result = getProperties(rootExpression);
255
+
256
+ expect(result).toHaveLength(4);
257
+ expect(result).toContain(prop1);
258
+ expect(result).toContain(prop2);
259
+ expect(result).toContain(prop3);
260
+ expect(result).toContain(prop4);
261
+ });
262
+
263
+ it('should handle a large expression tree with many properties', () => {
264
+ const properties = [
265
+ createMockProperty('prop1'),
266
+ createMockProperty('prop2'),
267
+ createMockProperty('prop3'),
268
+ createMockProperty('prop4'),
269
+ createMockProperty('prop5')
270
+ ];
271
+
272
+ const expression = new OperatorExpression({
273
+ operator: '&&',
274
+ left: new OperatorExpression({
275
+ operator: '||',
276
+ left: new PropertyExpression({ property: properties[0] }),
277
+ right: new PropertyExpression({ property: properties[1] })
278
+ }),
279
+ right: new OperatorExpression({
280
+ operator: '&&',
281
+ left: new PropertyExpression({ property: properties[2] }),
282
+ right: new OperatorExpression({
283
+ operator: '||',
284
+ left: new PropertyExpression({ property: properties[3] }),
285
+ right: new PropertyExpression({ property: properties[4] })
286
+ })
287
+ })
288
+ });
289
+
290
+ const result = getProperties(expression);
291
+
292
+ expect(result).toHaveLength(5);
293
+ properties.forEach(prop => {
294
+ expect(result).toContain(prop);
295
+ });
296
+ });
297
+
298
+ it('should handle expressions with only non-property expressions', () => {
299
+ const expression = new OperatorExpression({
300
+ operator: '&&',
301
+ left: new ValueExpression({ value: true }),
302
+ right: new ValueExpression({ value: false })
303
+ });
304
+
305
+ const result = getProperties(expression);
306
+
307
+ expect(result).toHaveLength(0);
308
+ });
309
+
310
+ it('should handle expressions with one property and one non-property', () => {
311
+ const property = createMockProperty('enabled');
312
+ const expression = new OperatorExpression({
313
+ operator: '&&',
314
+ left: new PropertyExpression({ property }),
315
+ right: new ValueExpression({ value: true })
316
+ });
317
+
318
+ const result = getProperties(expression);
319
+
320
+ expect(result).toHaveLength(1);
321
+ expect(result[0]).toBe(property);
322
+ });
323
+
324
+ it('should preserve order of properties as they appear in traversal', () => {
325
+ const prop1 = createMockProperty('a');
326
+ const prop2 = createMockProperty('b');
327
+ const prop3 = createMockProperty('c');
328
+
329
+ const expression = new OperatorExpression({
330
+ operator: '&&',
331
+ left: new PropertyExpression({ property: prop1 }),
332
+ right: new OperatorExpression({
333
+ operator: '||',
334
+ left: new PropertyExpression({ property: prop2 }),
335
+ right: new PropertyExpression({ property: prop3 })
336
+ })
337
+ });
338
+
339
+ const result = getProperties(expression);
340
+
341
+ expect(result).toHaveLength(3);
342
+ expect(result[0]).toBe(prop1);
343
+ expect(result[1]).toBe(prop2);
344
+ expect(result[2]).toBe(prop3);
345
+ });
346
+ });
@@ -0,0 +1,59 @@
1
+ import { PropertyInfo } from "../schema";
2
+ import { Expression, PropertyExpression } from "./types";
3
+
4
+ /**
5
+ * Extracts all properties referenced in an expression
6
+ * @param expression The expression to analyze
7
+ * @returns Array of PropertyInfo objects referenced in the expression
8
+ */
9
+ export function getProperties(expression: Expression): PropertyInfo<any>[] {
10
+ const properties: PropertyInfo<any>[] = [];
11
+
12
+ function traverse(expr: Expression) {
13
+ // If this is a property expression, add it to our collection
14
+ if (expr.type === "property") {
15
+ properties.push((expr as PropertyExpression).property);
16
+ }
17
+
18
+ // Traverse left and right expressions if they exist
19
+ if (expr.left) {
20
+ traverse(expr.left);
21
+ }
22
+ if (expr.right) {
23
+ traverse(expr.right);
24
+ }
25
+ }
26
+
27
+ traverse(expression);
28
+ return properties;
29
+ }
30
+
31
+ export function isPropertyExpression(expression: Expression): expression is PropertyExpression {
32
+ return expression.type === "property";
33
+ }
34
+
35
+ export function forEach(expression: Expression, callback: (expression: Expression) => boolean) {
36
+ function traverse(expr: Expression): boolean {
37
+ // Call the callback for this expression
38
+ // If callback returns false, stop traversing
39
+ if (!callback(expr)) {
40
+ return false;
41
+ }
42
+
43
+ // Traverse left and right expressions if they exist
44
+ if (expr.left) {
45
+ if (!traverse(expr.left)) {
46
+ return false;
47
+ }
48
+ }
49
+ if (expr.right) {
50
+ if (!traverse(expr.right)) {
51
+ return false;
52
+ }
53
+ }
54
+
55
+ return true;
56
+ }
57
+
58
+ traverse(expression);
59
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ export * from './assertions';
2
+ export * from './codegen';
3
+ export * from './collections';
4
+ export * from './errors';
5
+ export * from './expressions';
6
+ export * from './performance';
7
+ export * from './pipeline';
8
+ export * from './plugins';
9
+ export * from './results';
10
+ export * from './schema';
11
+ export * from './types';
12
+ export * from './utilities';
@@ -0,0 +1,26 @@
1
+ const measurements: Record<string, number> = {}
2
+
3
+ export const now = (): number => {
4
+ if (typeof performance !== 'undefined' && performance.now) {
5
+ // Browser or modern Node.js
6
+ return performance.now();
7
+ }
8
+
9
+ // Fallback for older environments
10
+ return Date.now();
11
+ }
12
+
13
+ export const measure = {
14
+ start: (name: string): void => {
15
+ measurements[name] = now();
16
+ },
17
+ end: (name: string): void => {
18
+ const start = measurements[name];
19
+
20
+ delete measurements[name];
21
+
22
+ const delta = now() - start;
23
+
24
+ console.log(`[ROUTIER] - Performance: ${name} took ${delta}`);
25
+ }
26
+ }