@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.
- package/CHANGELOG.md +8 -0
- package/package.json +91 -0
- package/rspack.config.mjs +65 -0
- package/src/assertions/index.ts +37 -0
- package/src/codegen/SlotPath.ts +20 -0
- package/src/codegen/blocks.ts +578 -0
- package/src/codegen/handlers/CloneHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/CompareHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/DeserializeHandlerBuilder.ts +20 -0
- package/src/codegen/handlers/EnableChangeTrackingHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/EnrichmentHandlerBuilder.ts +28 -0
- package/src/codegen/handlers/FreezeHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/HashHandlerBuilder.ts +25 -0
- package/src/codegen/handlers/HashTypeHandlerBuilder.ts +11 -0
- package/src/codegen/handlers/IdSelectorHandlerBuilder.ts +10 -0
- package/src/codegen/handlers/MergeHandlerBuilder.ts +19 -0
- package/src/codegen/handlers/PrepareHandlerBuilder.ts +23 -0
- package/src/codegen/handlers/SerializeHandlerBuilder.ts +15 -0
- package/src/codegen/handlers/StripHandlerBuilder.ts +18 -0
- package/src/codegen/handlers/clone/CloneObjectHandler.ts +29 -0
- package/src/codegen/handlers/clone/CloneValueHandler.ts +33 -0
- package/src/codegen/handlers/compare/CompareObjectHandler.ts +15 -0
- package/src/codegen/handlers/compare/CompareValueHandler.ts +27 -0
- package/src/codegen/handlers/deserialize/DeserializeComputedValueHandler.ts +16 -0
- package/src/codegen/handlers/deserialize/DeserializeDateHandler.ts +45 -0
- package/src/codegen/handlers/deserialize/DeserializeFunctionHandler.ts +16 -0
- package/src/codegen/handlers/deserialize/DeserializeObjectHandler.ts +29 -0
- package/src/codegen/handlers/deserialize/DeserializeValueHandler.ts +33 -0
- package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingObjectHandler.ts +20 -0
- package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingPrimitiveValueHandler.ts +15 -0
- package/src/codegen/handlers/enrichment/EnrichmentComputedValueHandler.ts +37 -0
- package/src/codegen/handlers/enrichment/EnrichmentDefaultFunctionHandler.ts +50 -0
- package/src/codegen/handlers/enrichment/EnrichmentDefaultValueHandler.ts +18 -0
- package/src/codegen/handlers/enrichment/EnrichmentFunctionHandler.ts +38 -0
- package/src/codegen/handlers/enrichment/EnrichmentNullableObjectHandler.ts +44 -0
- package/src/codegen/handlers/enrichment/EnrichmentObjectHandler.ts +29 -0
- package/src/codegen/handlers/enrichment/EnrichmentObjectIdentityHandler.ts +16 -0
- package/src/codegen/handlers/enrichment/EnrichmentPrimitiveHandler.ts +13 -0
- package/src/codegen/handlers/enrichment/EnrichmentPrimitiveIdentityHandler.ts +21 -0
- package/src/codegen/handlers/freeze/FreezeObjectHandler.ts +20 -0
- package/src/codegen/handlers/freeze/FreezePrimitiveValueHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashComputedValueHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashDateHandler.ts +26 -0
- package/src/codegen/handlers/hash/HashFunctionHandler.ts +16 -0
- package/src/codegen/handlers/hash/HashIdentityHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashKeyHandler.ts +26 -0
- package/src/codegen/handlers/hash/HashValueHandler.ts +26 -0
- package/src/codegen/handlers/hashType/HashTypeValueHandler.ts +20 -0
- package/src/codegen/handlers/idSelector/IdSelectorValueHandler.ts +28 -0
- package/src/codegen/handlers/index.ts +13 -0
- package/src/codegen/handlers/merge/MergeComputedValueHandler.ts +37 -0
- package/src/codegen/handlers/merge/MergeDefaultFunctionHandler.ts +40 -0
- package/src/codegen/handlers/merge/MergeDefaultValueHandler.ts +32 -0
- package/src/codegen/handlers/merge/MergeFunctionHandler.ts +16 -0
- package/src/codegen/handlers/merge/MergePrimitiveHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareComputedValueHandler.ts +16 -0
- package/src/codegen/handlers/prepare/PrepareFunctionHandler.ts +16 -0
- package/src/codegen/handlers/prepare/PrepareIdentityHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareKeyHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareObjectHandler.ts +38 -0
- package/src/codegen/handlers/prepare/PrepareValueHandler.ts +36 -0
- package/src/codegen/handlers/serialize/SerializeDateHandler.ts +45 -0
- package/src/codegen/handlers/serialize/SerializeObjectHandler.ts +28 -0
- package/src/codegen/handlers/serialize/SerializeValueHandler.ts +33 -0
- package/src/codegen/handlers/strip/StripIdentityHandler.ts +15 -0
- package/src/codegen/handlers/strip/StripKeyHandler.ts +15 -0
- package/src/codegen/handlers/strip/StripObjectHandler.ts +35 -0
- package/src/codegen/handlers/strip/StripValueHandler.ts +36 -0
- package/src/codegen/handlers/types.ts +119 -0
- package/src/codegen/index.ts +2 -0
- package/src/codegen/types.ts +2 -0
- package/src/codegen/utils.ts +74 -0
- package/src/collections/Changes.test.ts +337 -0
- package/src/collections/Changes.ts +177 -0
- package/src/collections/IdSet.ts +28 -0
- package/src/collections/MemoryDataCollection.test.ts +424 -0
- package/src/collections/MemoryDataCollection.ts +134 -0
- package/src/collections/SchemaCollection.ts +22 -0
- package/src/collections/TagCollection.test.ts +443 -0
- package/src/collections/TagCollection.ts +62 -0
- package/src/collections/index.ts +5 -0
- package/src/errors/SchemaError.ts +6 -0
- package/src/errors/index.ts +1 -0
- package/src/expressions/index.ts +3 -0
- package/src/expressions/parser.test.ts +913 -0
- package/src/expressions/parser.ts +661 -0
- package/src/expressions/types.ts +184 -0
- package/src/expressions/utils.test.ts +346 -0
- package/src/expressions/utils.ts +59 -0
- package/src/index.ts +12 -0
- package/src/performance/index.ts +26 -0
- package/src/pipeline/SyncronousQueue.test.ts +269 -0
- package/src/pipeline/SyncronousQueue.ts +30 -0
- package/src/pipeline/TrampolinePipeline.test.ts +374 -0
- package/src/pipeline/TrampolinePipeline.ts +437 -0
- package/src/pipeline/index.ts +2 -0
- package/src/plugins/EphemeralDataPlugin.ts +132 -0
- package/src/plugins/capabilities/DbPluginCapability.ts +111 -0
- package/src/plugins/capabilities/index.ts +2 -0
- package/src/plugins/capabilities/logging/DbPluginLoggingCapability.ts +261 -0
- package/src/plugins/capabilities/logging/index.ts +1 -0
- package/src/plugins/index.ts +6 -0
- package/src/plugins/query/Query.ts +67 -0
- package/src/plugins/query/QueryOptionsCollection.test.ts +86 -0
- package/src/plugins/query/QueryOptionsCollection.ts +154 -0
- package/src/plugins/query/index.ts +3 -0
- package/src/plugins/query/types.ts +46 -0
- package/src/plugins/replication/OptimisticReplicationDbPlugin.ts +202 -0
- package/src/plugins/replication/ReplicationDbPlugin.ts +137 -0
- package/src/plugins/replication/index.ts +3 -0
- package/src/plugins/replication/types.ts +5 -0
- package/src/plugins/translators/DataTranslator.ts +46 -0
- package/src/plugins/translators/JsonTranslator.test.ts +618 -0
- package/src/plugins/translators/JsonTranslator.ts +211 -0
- package/src/plugins/translators/SqlTranslator.ts +45 -0
- package/src/plugins/translators/index.ts +3 -0
- package/src/plugins/types.ts +114 -0
- package/src/results/Result.ts +91 -0
- package/src/results/index.ts +3 -0
- package/src/results/types.ts +17 -0
- package/src/results/utils.ts +15 -0
- package/src/schema/PropertyInfo.test.ts +479 -0
- package/src/schema/PropertyInfo.ts +374 -0
- package/src/schema/SchemaDefinition.ts +626 -0
- package/src/schema/builder.ts +18 -0
- package/src/schema/index.ts +7 -0
- package/src/schema/property/base/SchemaBase.ts +49 -0
- package/src/schema/property/base/index.ts +1 -0
- package/src/schema/property/modifiers/SchemaDefault.ts +29 -0
- package/src/schema/property/modifiers/SchemaDeserialize.ts +23 -0
- package/src/schema/property/modifiers/SchemaDistinct.ts +14 -0
- package/src/schema/property/modifiers/SchemaFrom.ts +54 -0
- package/src/schema/property/modifiers/SchemaIdentity.ts +13 -0
- package/src/schema/property/modifiers/SchemaIndex.ts +60 -0
- package/src/schema/property/modifiers/SchemaKey.ts +28 -0
- package/src/schema/property/modifiers/SchemaNullable.ts +18 -0
- package/src/schema/property/modifiers/SchemaOptional.ts +19 -0
- package/src/schema/property/modifiers/SchemaReadonly.ts +34 -0
- package/src/schema/property/modifiers/SchemaSerialize.ts +18 -0
- package/src/schema/property/modifiers/SchemaTracked.ts +14 -0
- package/src/schema/property/modifiers/index.ts +12 -0
- package/src/schema/property/types/SchemaArray.ts +50 -0
- package/src/schema/property/types/SchemaBoolean.ts +59 -0
- package/src/schema/property/types/SchemaDate.ts +58 -0
- package/src/schema/property/types/SchemaNumber.ts +69 -0
- package/src/schema/property/types/SchemaObject.ts +44 -0
- package/src/schema/property/types/SchemaString.ts +69 -0
- package/src/schema/property/types/index.ts +6 -0
- package/src/schema/table/SchemaComputed.ts +20 -0
- package/src/schema/table/SchemaFunction.ts +15 -0
- package/src/schema/table/index.ts +2 -0
- package/src/schema/types.ts +238 -0
- package/src/types/index.ts +5 -0
- package/src/utilities/arrays.test.ts +312 -0
- package/src/utilities/arrays.ts +12 -0
- package/src/utilities/dates.test.ts +388 -0
- package/src/utilities/dates.ts +11 -0
- package/src/utilities/dbPluginEventUtils.ts +44 -0
- package/src/utilities/index.ts +10 -0
- package/src/utilities/objects.ts +7 -0
- package/src/utilities/queryOptionsCollection.ts +16 -0
- package/src/utilities/replication.ts +23 -0
- package/src/utilities/runtime.ts +3 -0
- package/src/utilities/strings.ts +18 -0
- package/src/utilities/types.ts +1 -0
- package/src/utilities/uuid.ts +56 -0
- package/tsconfig.json +28 -0
- 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
|
+
}
|