@objectstack/client 0.6.1 → 0.7.2
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 +18 -0
- package/README.md +89 -5
- package/dist/index.d.ts +78 -3
- package/dist/index.js +217 -12
- package/dist/query-builder.d.ts +124 -0
- package/dist/query-builder.js +221 -0
- package/package.json +3 -2
- package/src/index.ts +304 -13
- package/src/query-builder.ts +251 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-Safe Query Builder
|
|
3
|
+
*
|
|
4
|
+
* Provides a fluent API for building ObjectStack queries with:
|
|
5
|
+
* - Compile-time type checking
|
|
6
|
+
* - Intelligent code completion
|
|
7
|
+
* - Runtime validation
|
|
8
|
+
* - Type-safe filters and selections
|
|
9
|
+
*/
|
|
10
|
+
import { QueryAST, FilterCondition } from '@objectstack/spec/data';
|
|
11
|
+
/**
|
|
12
|
+
* Type-safe filter builder
|
|
13
|
+
*/
|
|
14
|
+
export declare class FilterBuilder<T = any> {
|
|
15
|
+
private conditions;
|
|
16
|
+
/**
|
|
17
|
+
* Equality filter: field = value
|
|
18
|
+
*/
|
|
19
|
+
equals<K extends keyof T>(field: K, value: T[K]): this;
|
|
20
|
+
/**
|
|
21
|
+
* Not equals filter: field != value
|
|
22
|
+
*/
|
|
23
|
+
notEquals<K extends keyof T>(field: K, value: T[K]): this;
|
|
24
|
+
/**
|
|
25
|
+
* Greater than filter: field > value
|
|
26
|
+
*/
|
|
27
|
+
greaterThan<K extends keyof T>(field: K, value: T[K]): this;
|
|
28
|
+
/**
|
|
29
|
+
* Greater than or equal filter: field >= value
|
|
30
|
+
*/
|
|
31
|
+
greaterThanOrEqual<K extends keyof T>(field: K, value: T[K]): this;
|
|
32
|
+
/**
|
|
33
|
+
* Less than filter: field < value
|
|
34
|
+
*/
|
|
35
|
+
lessThan<K extends keyof T>(field: K, value: T[K]): this;
|
|
36
|
+
/**
|
|
37
|
+
* Less than or equal filter: field <= value
|
|
38
|
+
*/
|
|
39
|
+
lessThanOrEqual<K extends keyof T>(field: K, value: T[K]): this;
|
|
40
|
+
/**
|
|
41
|
+
* IN filter: field IN (value1, value2, ...)
|
|
42
|
+
*/
|
|
43
|
+
in<K extends keyof T>(field: K, values: T[K][]): this;
|
|
44
|
+
/**
|
|
45
|
+
* NOT IN filter: field NOT IN (value1, value2, ...)
|
|
46
|
+
*/
|
|
47
|
+
notIn<K extends keyof T>(field: K, values: T[K][]): this;
|
|
48
|
+
/**
|
|
49
|
+
* LIKE filter: field LIKE pattern
|
|
50
|
+
*/
|
|
51
|
+
like<K extends keyof T>(field: K, pattern: string): this;
|
|
52
|
+
/**
|
|
53
|
+
* IS NULL filter: field IS NULL
|
|
54
|
+
*/
|
|
55
|
+
isNull<K extends keyof T>(field: K): this;
|
|
56
|
+
/**
|
|
57
|
+
* IS NOT NULL filter: field IS NOT NULL
|
|
58
|
+
*/
|
|
59
|
+
isNotNull<K extends keyof T>(field: K): this;
|
|
60
|
+
/**
|
|
61
|
+
* Build the filter condition
|
|
62
|
+
*/
|
|
63
|
+
build(): FilterCondition;
|
|
64
|
+
/**
|
|
65
|
+
* Get raw conditions array
|
|
66
|
+
*/
|
|
67
|
+
getConditions(): FilterCondition[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Type-safe query builder
|
|
71
|
+
*/
|
|
72
|
+
export declare class QueryBuilder<T = any> {
|
|
73
|
+
private query;
|
|
74
|
+
private _object;
|
|
75
|
+
constructor(object: string);
|
|
76
|
+
/**
|
|
77
|
+
* Select specific fields
|
|
78
|
+
*/
|
|
79
|
+
select<K extends keyof T>(...fields: K[]): this;
|
|
80
|
+
/**
|
|
81
|
+
* Add filters using a builder function
|
|
82
|
+
*/
|
|
83
|
+
where(builderFn: (builder: FilterBuilder<T>) => void): this;
|
|
84
|
+
/**
|
|
85
|
+
* Add raw filter condition
|
|
86
|
+
*/
|
|
87
|
+
filter(condition: FilterCondition): this;
|
|
88
|
+
/**
|
|
89
|
+
* Sort by fields
|
|
90
|
+
*/
|
|
91
|
+
orderBy<K extends keyof T>(field: K, order?: 'asc' | 'desc'): this;
|
|
92
|
+
/**
|
|
93
|
+
* Limit the number of results
|
|
94
|
+
*/
|
|
95
|
+
limit(count: number): this;
|
|
96
|
+
/**
|
|
97
|
+
* Skip records (for pagination)
|
|
98
|
+
*/
|
|
99
|
+
skip(count: number): this;
|
|
100
|
+
/**
|
|
101
|
+
* Paginate results
|
|
102
|
+
*/
|
|
103
|
+
paginate(page: number, pageSize: number): this;
|
|
104
|
+
/**
|
|
105
|
+
* Group by fields
|
|
106
|
+
*/
|
|
107
|
+
groupBy<K extends keyof T>(...fields: K[]): this;
|
|
108
|
+
/**
|
|
109
|
+
* Build the final query AST
|
|
110
|
+
*/
|
|
111
|
+
build(): QueryAST;
|
|
112
|
+
/**
|
|
113
|
+
* Get the current query state
|
|
114
|
+
*/
|
|
115
|
+
getQuery(): Partial<QueryAST>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Create a type-safe query builder for an object
|
|
119
|
+
*/
|
|
120
|
+
export declare function createQuery<T = any>(object: string): QueryBuilder<T>;
|
|
121
|
+
/**
|
|
122
|
+
* Create a type-safe filter builder
|
|
123
|
+
*/
|
|
124
|
+
export declare function createFilter<T = any>(): FilterBuilder<T>;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-Safe Query Builder
|
|
3
|
+
*
|
|
4
|
+
* Provides a fluent API for building ObjectStack queries with:
|
|
5
|
+
* - Compile-time type checking
|
|
6
|
+
* - Intelligent code completion
|
|
7
|
+
* - Runtime validation
|
|
8
|
+
* - Type-safe filters and selections
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Type-safe filter builder
|
|
12
|
+
*/
|
|
13
|
+
export class FilterBuilder {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.conditions = [];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Equality filter: field = value
|
|
19
|
+
*/
|
|
20
|
+
equals(field, value) {
|
|
21
|
+
this.conditions.push([field, '=', value]);
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Not equals filter: field != value
|
|
26
|
+
*/
|
|
27
|
+
notEquals(field, value) {
|
|
28
|
+
this.conditions.push([field, '!=', value]);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Greater than filter: field > value
|
|
33
|
+
*/
|
|
34
|
+
greaterThan(field, value) {
|
|
35
|
+
this.conditions.push([field, '>', value]);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Greater than or equal filter: field >= value
|
|
40
|
+
*/
|
|
41
|
+
greaterThanOrEqual(field, value) {
|
|
42
|
+
this.conditions.push([field, '>=', value]);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Less than filter: field < value
|
|
47
|
+
*/
|
|
48
|
+
lessThan(field, value) {
|
|
49
|
+
this.conditions.push([field, '<', value]);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Less than or equal filter: field <= value
|
|
54
|
+
*/
|
|
55
|
+
lessThanOrEqual(field, value) {
|
|
56
|
+
this.conditions.push([field, '<=', value]);
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* IN filter: field IN (value1, value2, ...)
|
|
61
|
+
*/
|
|
62
|
+
in(field, values) {
|
|
63
|
+
this.conditions.push([field, 'in', values]);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* NOT IN filter: field NOT IN (value1, value2, ...)
|
|
68
|
+
*/
|
|
69
|
+
notIn(field, values) {
|
|
70
|
+
this.conditions.push([field, 'not_in', values]);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* LIKE filter: field LIKE pattern
|
|
75
|
+
*/
|
|
76
|
+
like(field, pattern) {
|
|
77
|
+
this.conditions.push([field, 'like', pattern]);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* IS NULL filter: field IS NULL
|
|
82
|
+
*/
|
|
83
|
+
isNull(field) {
|
|
84
|
+
this.conditions.push([field, 'is_null', null]);
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* IS NOT NULL filter: field IS NOT NULL
|
|
89
|
+
*/
|
|
90
|
+
isNotNull(field) {
|
|
91
|
+
this.conditions.push([field, 'is_not_null', null]);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build the filter condition
|
|
96
|
+
*/
|
|
97
|
+
build() {
|
|
98
|
+
if (this.conditions.length === 0) {
|
|
99
|
+
throw new Error('Filter builder has no conditions');
|
|
100
|
+
}
|
|
101
|
+
if (this.conditions.length === 1) {
|
|
102
|
+
return this.conditions[0];
|
|
103
|
+
}
|
|
104
|
+
// Combine multiple conditions with AND
|
|
105
|
+
return ['and', ...this.conditions];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get raw conditions array
|
|
109
|
+
*/
|
|
110
|
+
getConditions() {
|
|
111
|
+
return this.conditions;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Type-safe query builder
|
|
116
|
+
*/
|
|
117
|
+
export class QueryBuilder {
|
|
118
|
+
constructor(object) {
|
|
119
|
+
this.query = {};
|
|
120
|
+
this._object = object;
|
|
121
|
+
this.query.object = object;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Select specific fields
|
|
125
|
+
*/
|
|
126
|
+
select(...fields) {
|
|
127
|
+
this.query.fields = fields;
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Add filters using a builder function
|
|
132
|
+
*/
|
|
133
|
+
where(builderFn) {
|
|
134
|
+
const builder = new FilterBuilder();
|
|
135
|
+
builderFn(builder);
|
|
136
|
+
const conditions = builder.getConditions();
|
|
137
|
+
if (conditions.length === 1) {
|
|
138
|
+
this.query.where = conditions[0];
|
|
139
|
+
}
|
|
140
|
+
else if (conditions.length > 1) {
|
|
141
|
+
this.query.where = ['and', ...conditions];
|
|
142
|
+
}
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Add raw filter condition
|
|
147
|
+
*/
|
|
148
|
+
filter(condition) {
|
|
149
|
+
this.query.where = condition;
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Sort by fields
|
|
154
|
+
*/
|
|
155
|
+
orderBy(field, order = 'asc') {
|
|
156
|
+
if (!this.query.orderBy) {
|
|
157
|
+
this.query.orderBy = [];
|
|
158
|
+
}
|
|
159
|
+
this.query.orderBy.push({
|
|
160
|
+
field: field,
|
|
161
|
+
order
|
|
162
|
+
});
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Limit the number of results
|
|
167
|
+
*/
|
|
168
|
+
limit(count) {
|
|
169
|
+
this.query.limit = count;
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Skip records (for pagination)
|
|
174
|
+
*/
|
|
175
|
+
skip(count) {
|
|
176
|
+
this.query.offset = count;
|
|
177
|
+
return this;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Paginate results
|
|
181
|
+
*/
|
|
182
|
+
paginate(page, pageSize) {
|
|
183
|
+
this.query.limit = pageSize;
|
|
184
|
+
this.query.offset = (page - 1) * pageSize;
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Group by fields
|
|
189
|
+
*/
|
|
190
|
+
groupBy(...fields) {
|
|
191
|
+
this.query.groupBy = fields;
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Build the final query AST
|
|
196
|
+
*/
|
|
197
|
+
build() {
|
|
198
|
+
return {
|
|
199
|
+
object: this._object,
|
|
200
|
+
...this.query
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get the current query state
|
|
205
|
+
*/
|
|
206
|
+
getQuery() {
|
|
207
|
+
return { ...this.query };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Create a type-safe query builder for an object
|
|
212
|
+
*/
|
|
213
|
+
export function createQuery(object) {
|
|
214
|
+
return new QueryBuilder(object);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Create a type-safe filter builder
|
|
218
|
+
*/
|
|
219
|
+
export function createFilter() {
|
|
220
|
+
return new FilterBuilder();
|
|
221
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "Official Client SDK for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@objectstack/spec": "0.
|
|
8
|
+
"@objectstack/spec": "0.7.2",
|
|
9
|
+
"@objectstack/core": "0.7.2"
|
|
9
10
|
},
|
|
10
11
|
"devDependencies": {
|
|
11
12
|
"typescript": "^5.0.0"
|