@dxos/echo-query 0.8.4-main.72ec0f3 → 0.8.4-main.74a063c4e0
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/dist/lib/{browser → neutral}/index.mjs +159 -3
- package/dist/lib/{node-esm → neutral}/index.mjs.map +4 -4
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/query-lite/index.d.ts +9932 -0
- package/dist/query-lite/index.d.ts.map +1 -0
- package/dist/query-lite/index.js +506 -375
- package/dist/query-lite/index.js.map +1 -0
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query-lite/query-lite.d.ts +4 -4
- package/dist/types/src/query-lite/query-lite.d.ts.map +1 -1
- package/dist/types/src/sandbox/index.d.ts +2 -0
- package/dist/types/src/sandbox/index.d.ts.map +1 -0
- package/dist/types/src/sandbox/query-sandbox.d.ts +1 -1
- package/dist/types/src/sandbox/quickjs.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -16
- package/src/index.ts +1 -0
- package/src/parser/query.test.ts +29 -29
- package/src/query-lite/query-lite.ts +302 -70
- package/src/sandbox/index.ts +5 -0
- package/src/sandbox/query-sandbox.test.ts +10 -10
- package/src/sandbox/query-sandbox.ts +1 -1
- package/src/sandbox/quickjs.ts +1 -2
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -563
- package/dist/lib/node-esm/meta.json +0 -1
|
@@ -4,8 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type * as Schema from 'effect/Schema';
|
|
6
6
|
|
|
7
|
-
import type { Filter
|
|
8
|
-
import type * as Echo from '@dxos/echo';
|
|
7
|
+
import type { Filter as Filter$, Order as Order$, Query as Query$, Ref } from '@dxos/echo';
|
|
9
8
|
import type { ForeignKey, QueryAST } from '@dxos/echo-protocol';
|
|
10
9
|
import { assertArgument } from '@dxos/invariant';
|
|
11
10
|
import type { DXN, ObjectId } from '@dxos/keys';
|
|
@@ -14,10 +13,12 @@ import type { DXN, ObjectId } from '@dxos/keys';
|
|
|
14
13
|
// Light-weight implementation of query execution.
|
|
15
14
|
//
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
private static variance: Echo.Order<any>['~Order'] = {} as Echo.Order<any>['~Order'];
|
|
16
|
+
// TODO(wittjosiah): The `export * as ...` syntax causes tsdown to genereate multiple files which breaks the sandbox.
|
|
19
17
|
|
|
20
|
-
|
|
18
|
+
class OrderClass implements Order$.Any {
|
|
19
|
+
private static 'variance': Order$.Any['~Order'] = {} as Order$.Any['~Order'];
|
|
20
|
+
|
|
21
|
+
static is(value: unknown): value is Order$.Any {
|
|
21
22
|
return typeof value === 'object' && value !== null && '~Order' in value;
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -27,26 +28,31 @@ class OrderClass implements Echo.Order<any> {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
namespace Order1 {
|
|
30
|
-
export const natural:
|
|
31
|
-
export const property = <T>(property: keyof T & string, direction: QueryAST.OrderDirection):
|
|
31
|
+
export const natural: Order$.Any = new OrderClass({ kind: 'natural' });
|
|
32
|
+
export const property = <T>(property: keyof T & string, direction: QueryAST.OrderDirection): Order$.Order<T> =>
|
|
32
33
|
new OrderClass({
|
|
33
34
|
kind: 'property',
|
|
34
35
|
property,
|
|
35
36
|
direction,
|
|
36
37
|
});
|
|
38
|
+
export const rank = <T>(direction: QueryAST.OrderDirection = 'desc'): Order$.Order<T> =>
|
|
39
|
+
new OrderClass({
|
|
40
|
+
kind: 'rank',
|
|
41
|
+
direction,
|
|
42
|
+
});
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
const Order2: typeof
|
|
45
|
+
const Order2: typeof Order$ = Order1;
|
|
40
46
|
export { Order2 as Order };
|
|
41
47
|
|
|
42
|
-
class FilterClass implements
|
|
43
|
-
private static variance:
|
|
48
|
+
class FilterClass implements Filter$.Any {
|
|
49
|
+
private static 'variance': Filter$.Any['~Filter'] = {} as Filter$.Any['~Filter'];
|
|
44
50
|
|
|
45
|
-
static is(value: unknown): value is
|
|
51
|
+
static is(value: unknown): value is Filter$.Any {
|
|
46
52
|
return typeof value === 'object' && value !== null && '~Filter' in value;
|
|
47
53
|
}
|
|
48
54
|
|
|
49
|
-
static fromAst(ast: QueryAST.Filter): Filter
|
|
55
|
+
static fromAst(ast: QueryAST.Filter): Filter$.Any {
|
|
50
56
|
return new FilterClass(ast);
|
|
51
57
|
}
|
|
52
58
|
|
|
@@ -77,7 +83,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
|
|
80
|
-
static
|
|
86
|
+
static id(...ids: ObjectId[]): Filter$.Any {
|
|
81
87
|
// assertArgument(
|
|
82
88
|
// ids.every((id) => ObjectId.isValid(id)),
|
|
83
89
|
// 'ids',
|
|
@@ -98,8 +104,8 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
98
104
|
|
|
99
105
|
static type<S extends Schema.Schema.All>(
|
|
100
106
|
schema: S | string,
|
|
101
|
-
props?:
|
|
102
|
-
):
|
|
107
|
+
props?: Filter$.Props<Schema.Schema.Type<S>>,
|
|
108
|
+
): Filter$.Filter<Schema.Schema.Type<S>> {
|
|
103
109
|
if (typeof schema !== 'string') {
|
|
104
110
|
throw new TypeError('expected typename as the first paramter');
|
|
105
111
|
}
|
|
@@ -110,7 +116,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
110
116
|
});
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
static typename(typename: string):
|
|
119
|
+
static typename(typename: string): Filter$.Any {
|
|
114
120
|
return new FilterClass({
|
|
115
121
|
type: 'object',
|
|
116
122
|
typename: makeTypeDxn(typename),
|
|
@@ -118,7 +124,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
118
124
|
});
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
static typeDXN(dxn: DXN):
|
|
127
|
+
static typeDXN(dxn: DXN): Filter$.Any {
|
|
122
128
|
return new FilterClass({
|
|
123
129
|
type: 'object',
|
|
124
130
|
typename: dxn.toString(),
|
|
@@ -126,14 +132,14 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
126
132
|
});
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
static tag(tag: string):
|
|
135
|
+
static tag(tag: string): Filter$.Any {
|
|
130
136
|
return new FilterClass({
|
|
131
137
|
type: 'tag',
|
|
132
138
|
tag,
|
|
133
139
|
});
|
|
134
140
|
}
|
|
135
141
|
|
|
136
|
-
static props<T>(props:
|
|
142
|
+
static props<T>(props: Filter$.Props<T>): Filter$.Filter<T> {
|
|
137
143
|
return new FilterClass({
|
|
138
144
|
type: 'object',
|
|
139
145
|
typename: null,
|
|
@@ -141,7 +147,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
141
147
|
});
|
|
142
148
|
}
|
|
143
149
|
|
|
144
|
-
static text(text: string, options?:
|
|
150
|
+
static text(text: string, options?: Filter$.TextSearchOptions): Filter$.Any {
|
|
145
151
|
return new FilterClass({
|
|
146
152
|
type: 'text-search',
|
|
147
153
|
text,
|
|
@@ -152,7 +158,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
152
158
|
static foreignKeys<S extends Schema.Schema.All>(
|
|
153
159
|
schema: S | string,
|
|
154
160
|
keys: ForeignKey[],
|
|
155
|
-
):
|
|
161
|
+
): Filter$.Filter<Schema.Schema.Type<S>> {
|
|
156
162
|
assertArgument(typeof schema === 'string', 'schema');
|
|
157
163
|
assertArgument(!schema.startsWith('dxn:'), 'schema');
|
|
158
164
|
return new FilterClass({
|
|
@@ -163,7 +169,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
163
169
|
});
|
|
164
170
|
}
|
|
165
171
|
|
|
166
|
-
static eq<T>(value: T):
|
|
172
|
+
static eq<T>(value: T): Filter$.Filter<T | undefined> {
|
|
167
173
|
if (!isRef(value) && typeof value === 'object' && value !== null) {
|
|
168
174
|
throw new TypeError('Cannot use object as a value for eq filter');
|
|
169
175
|
}
|
|
@@ -175,7 +181,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
175
181
|
});
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
static neq<T>(value: T):
|
|
184
|
+
static neq<T>(value: T): Filter$.Filter<T | undefined> {
|
|
179
185
|
return new FilterClass({
|
|
180
186
|
type: 'compare',
|
|
181
187
|
operator: 'neq',
|
|
@@ -183,7 +189,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
183
189
|
});
|
|
184
190
|
}
|
|
185
191
|
|
|
186
|
-
static gt<T>(value: T):
|
|
192
|
+
static gt<T>(value: T): Filter$.Filter<T | undefined> {
|
|
187
193
|
return new FilterClass({
|
|
188
194
|
type: 'compare',
|
|
189
195
|
operator: 'gt',
|
|
@@ -191,7 +197,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
191
197
|
});
|
|
192
198
|
}
|
|
193
199
|
|
|
194
|
-
static gte<T>(value: T):
|
|
200
|
+
static gte<T>(value: T): Filter$.Filter<T | undefined> {
|
|
195
201
|
return new FilterClass({
|
|
196
202
|
type: 'compare',
|
|
197
203
|
operator: 'gte',
|
|
@@ -199,7 +205,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
199
205
|
});
|
|
200
206
|
}
|
|
201
207
|
|
|
202
|
-
static lt<T>(value: T):
|
|
208
|
+
static lt<T>(value: T): Filter$.Filter<T | undefined> {
|
|
203
209
|
return new FilterClass({
|
|
204
210
|
type: 'compare',
|
|
205
211
|
operator: 'lt',
|
|
@@ -207,7 +213,7 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
207
213
|
});
|
|
208
214
|
}
|
|
209
215
|
|
|
210
|
-
static lte<T>(value: T):
|
|
216
|
+
static lte<T>(value: T): Filter$.Filter<T | undefined> {
|
|
211
217
|
return new FilterClass({
|
|
212
218
|
type: 'compare',
|
|
213
219
|
operator: 'lte',
|
|
@@ -215,21 +221,21 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
215
221
|
});
|
|
216
222
|
}
|
|
217
223
|
|
|
218
|
-
static in<T>(...values: T[]):
|
|
224
|
+
static in<T>(...values: T[]): Filter$.Filter<T> {
|
|
219
225
|
return new FilterClass({
|
|
220
226
|
type: 'in',
|
|
221
227
|
values,
|
|
222
228
|
});
|
|
223
229
|
}
|
|
224
230
|
|
|
225
|
-
static contains<T>(value: T):
|
|
231
|
+
static contains<T>(value: T): Filter$.Filter<readonly T[] | undefined> {
|
|
226
232
|
return new FilterClass({
|
|
227
233
|
type: 'contains',
|
|
228
234
|
value,
|
|
229
235
|
});
|
|
230
236
|
}
|
|
231
237
|
|
|
232
|
-
static between<T>(from: T, to: T):
|
|
238
|
+
static between<T>(from: T, to: T): Filter$.Filter<T> {
|
|
233
239
|
return new FilterClass({
|
|
234
240
|
type: 'range',
|
|
235
241
|
from,
|
|
@@ -237,33 +243,68 @@ class FilterClass implements Echo.Filter<any> {
|
|
|
237
243
|
});
|
|
238
244
|
}
|
|
239
245
|
|
|
240
|
-
static
|
|
246
|
+
static updated(range: { after?: Date | number; before?: Date | number }): Filter$.Any {
|
|
247
|
+
return FilterClass.#timeRangeFilter('updatedAt', range);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static created(range: { after?: Date | number; before?: Date | number }): Filter$.Any {
|
|
251
|
+
return FilterClass.#timeRangeFilter('createdAt', range);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static #timeRangeFilter(
|
|
255
|
+
field: 'updatedAt' | 'createdAt',
|
|
256
|
+
range: { after?: Date | number; before?: Date | number },
|
|
257
|
+
): Filter$.Any {
|
|
258
|
+
const toMs = (d: Date | number) => (typeof d === 'number' ? d : d.getTime());
|
|
259
|
+
const filters: Filter$.Any[] = [];
|
|
260
|
+
if (range.after != null) {
|
|
261
|
+
filters.push(new FilterClass({ type: 'timestamp', field, operator: 'gte', value: toMs(range.after) }));
|
|
262
|
+
}
|
|
263
|
+
if (range.before != null) {
|
|
264
|
+
filters.push(new FilterClass({ type: 'timestamp', field, operator: 'lte', value: toMs(range.before) }));
|
|
265
|
+
}
|
|
266
|
+
if (filters.length === 0) {
|
|
267
|
+
return FilterClass.everything();
|
|
268
|
+
}
|
|
269
|
+
return filters.length === 1 ? filters[0] : FilterClass.and(...filters);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static not<F extends Filter$.Any>(filter: F): Filter$.Filter<Filter$.Type<F>> {
|
|
241
273
|
return new FilterClass({
|
|
242
274
|
type: 'not',
|
|
243
275
|
filter: filter.ast,
|
|
244
276
|
});
|
|
245
277
|
}
|
|
246
278
|
|
|
247
|
-
static and<
|
|
279
|
+
static and<Filters extends readonly Filter$.Any[]>(
|
|
280
|
+
...filters: Filters
|
|
281
|
+
): Filter$.Filter<Filter$.Type<Filters[number]>> {
|
|
248
282
|
return new FilterClass({
|
|
249
283
|
type: 'and',
|
|
250
284
|
filters: filters.map((f) => f.ast),
|
|
251
285
|
});
|
|
252
286
|
}
|
|
253
287
|
|
|
254
|
-
static or<
|
|
288
|
+
static or<Filters extends readonly Filter$.Any[]>(
|
|
289
|
+
...filters: Filters
|
|
290
|
+
): Filter$.Filter<Filter$.Type<Filters[number]>> {
|
|
255
291
|
return new FilterClass({
|
|
256
292
|
type: 'or',
|
|
257
293
|
filters: filters.map((f) => f.ast),
|
|
258
294
|
});
|
|
259
295
|
}
|
|
260
296
|
|
|
297
|
+
/** Returns a human-readable string representation of a Filter AST. */
|
|
298
|
+
static pretty(filter: Filter$.Any): string {
|
|
299
|
+
return prettyFilter(filter.ast);
|
|
300
|
+
}
|
|
301
|
+
|
|
261
302
|
private constructor(public readonly ast: QueryAST.Filter) {}
|
|
262
303
|
|
|
263
304
|
'~Filter' = FilterClass.variance;
|
|
264
305
|
}
|
|
265
306
|
|
|
266
|
-
export const Filter1: typeof
|
|
307
|
+
export const Filter1: typeof Filter$ = FilterClass;
|
|
267
308
|
export { Filter1 as Filter };
|
|
268
309
|
|
|
269
310
|
/**
|
|
@@ -272,7 +313,7 @@ export { Filter1 as Filter };
|
|
|
272
313
|
// TODO(dmaretskyi): Filter only properties that are references (or optional references, or unions that include references).
|
|
273
314
|
type RefPropKey<T> = keyof T & string;
|
|
274
315
|
|
|
275
|
-
const propsFilterToAst = (predicates:
|
|
316
|
+
const propsFilterToAst = (predicates: Filter$.Props<any>): Pick<QueryAST.FilterObject, 'id' | 'props'> => {
|
|
276
317
|
let idFilter: readonly ObjectId[] | undefined;
|
|
277
318
|
if ('id' in predicates) {
|
|
278
319
|
assertArgument(
|
|
@@ -317,32 +358,48 @@ const processPredicate = (predicate: any): QueryAST.Filter => {
|
|
|
317
358
|
return FilterClass.eq(predicate).ast;
|
|
318
359
|
};
|
|
319
360
|
|
|
320
|
-
class QueryClass implements
|
|
321
|
-
private static variance:
|
|
361
|
+
class QueryClass implements Query$.Any {
|
|
362
|
+
private static 'variance': Query$.Any['~Query'] = {} as Query$.Any['~Query'];
|
|
322
363
|
|
|
323
|
-
static is(value: unknown): value is
|
|
364
|
+
static is(value: unknown): value is Query$.Any {
|
|
324
365
|
return typeof value === 'object' && value !== null && '~Query' in value;
|
|
325
366
|
}
|
|
326
367
|
|
|
327
|
-
static fromAst(ast: QueryAST.Query):
|
|
368
|
+
static fromAst(ast: QueryAST.Query): Query$.Any {
|
|
328
369
|
return new QueryClass(ast);
|
|
329
370
|
}
|
|
330
371
|
|
|
331
|
-
static select<F extends
|
|
372
|
+
static select<F extends Filter$.Any>(filter: F): Query$.Query<Filter$.Type<F>> {
|
|
332
373
|
return new QueryClass({
|
|
333
374
|
type: 'select',
|
|
334
375
|
filter: filter.ast,
|
|
335
376
|
});
|
|
336
377
|
}
|
|
337
378
|
|
|
338
|
-
|
|
379
|
+
select(filter: Filter$.Any | Filter$.Props<any>): Query$.Any {
|
|
380
|
+
if (FilterClass.is(filter)) {
|
|
381
|
+
return new QueryClass({
|
|
382
|
+
type: 'filter',
|
|
383
|
+
selection: this.ast,
|
|
384
|
+
filter: filter.ast,
|
|
385
|
+
});
|
|
386
|
+
} else {
|
|
387
|
+
return new QueryClass({
|
|
388
|
+
type: 'filter',
|
|
389
|
+
selection: this.ast,
|
|
390
|
+
filter: FilterClass.props(filter).ast,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
static type(schema: Schema.Schema.All | string, predicates?: Filter$.Props<unknown>): Query$.Any {
|
|
339
396
|
return new QueryClass({
|
|
340
397
|
type: 'select',
|
|
341
398
|
filter: FilterClass.type(schema, predicates).ast,
|
|
342
399
|
});
|
|
343
400
|
}
|
|
344
401
|
|
|
345
|
-
static all(...queries: Query
|
|
402
|
+
static all(...queries: Query$.Any[]): Query$.Any {
|
|
346
403
|
if (queries.length === 0) {
|
|
347
404
|
throw new TypeError(
|
|
348
405
|
'Query.all combines results of multiple queries, to query all objects use Query.select(Filter.everything())',
|
|
@@ -354,7 +411,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
354
411
|
});
|
|
355
412
|
}
|
|
356
413
|
|
|
357
|
-
static without<T>(source: Query<T>, exclude: Query<T>): Query<T> {
|
|
414
|
+
static without<T>(source: Query$.Query<T>, exclude: Query$.Query<T>): Query$.Query<T> {
|
|
358
415
|
return new QueryClass({
|
|
359
416
|
type: 'set-difference',
|
|
360
417
|
source: source.ast,
|
|
@@ -362,27 +419,50 @@ class QueryClass implements Echo.Query<any> {
|
|
|
362
419
|
});
|
|
363
420
|
}
|
|
364
421
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
422
|
+
static from(source: any, options?: { includeFeeds?: boolean }): Query$.Any {
|
|
423
|
+
const baseQuery: QueryAST.Query = {
|
|
424
|
+
type: 'select',
|
|
425
|
+
filter: FilterClass.everything().ast,
|
|
426
|
+
};
|
|
427
|
+
const wrapper = new QueryClass(baseQuery);
|
|
428
|
+
return wrapper.from(source, options);
|
|
429
|
+
}
|
|
368
430
|
|
|
369
|
-
|
|
370
|
-
if (
|
|
431
|
+
from(arg: any, options?: { includeFeeds?: boolean }): Query$.Any {
|
|
432
|
+
if (arg === 'all-accessible-spaces') {
|
|
371
433
|
return new QueryClass({
|
|
372
|
-
type: '
|
|
373
|
-
|
|
374
|
-
|
|
434
|
+
type: 'from',
|
|
435
|
+
query: this.ast,
|
|
436
|
+
from: {
|
|
437
|
+
_tag: 'scope',
|
|
438
|
+
scope: {
|
|
439
|
+
...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
|
|
440
|
+
},
|
|
441
|
+
},
|
|
375
442
|
});
|
|
376
|
-
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (_isScopeLike(arg)) {
|
|
377
446
|
return new QueryClass({
|
|
378
|
-
type: '
|
|
379
|
-
|
|
380
|
-
|
|
447
|
+
type: 'from',
|
|
448
|
+
query: this.ast,
|
|
449
|
+
from: { _tag: 'scope', scope: arg },
|
|
381
450
|
});
|
|
382
451
|
}
|
|
452
|
+
|
|
453
|
+
throw new TypeError('Database and Feed objects are not supported in query-lite sandbox');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/** Returns a human-readable string representation of a Query AST. */
|
|
457
|
+
static pretty(query: Query$.Any): string {
|
|
458
|
+
return prettyQuery(query.ast);
|
|
383
459
|
}
|
|
384
460
|
|
|
385
|
-
|
|
461
|
+
constructor(public readonly ast: QueryAST.Query) {}
|
|
462
|
+
|
|
463
|
+
'~Query' = QueryClass.variance;
|
|
464
|
+
|
|
465
|
+
reference(key: string): Query$.Any {
|
|
386
466
|
return new QueryClass({
|
|
387
467
|
type: 'reference-traversal',
|
|
388
468
|
anchor: this.ast,
|
|
@@ -390,36 +470,40 @@ class QueryClass implements Echo.Query<any> {
|
|
|
390
470
|
});
|
|
391
471
|
}
|
|
392
472
|
|
|
393
|
-
referencedBy(target
|
|
394
|
-
|
|
395
|
-
|
|
473
|
+
referencedBy(target?: Schema.Schema.All | string, key?: string): Query$.Any {
|
|
474
|
+
const typename =
|
|
475
|
+
target !== undefined
|
|
476
|
+
? (assertArgument(typeof target === 'string', 'target'),
|
|
477
|
+
assertArgument(!target.startsWith('dxn:'), 'target'),
|
|
478
|
+
target)
|
|
479
|
+
: null;
|
|
396
480
|
return new QueryClass({
|
|
397
481
|
type: 'incoming-references',
|
|
398
482
|
anchor: this.ast,
|
|
399
|
-
property: key,
|
|
400
|
-
typename
|
|
483
|
+
property: key ?? null,
|
|
484
|
+
typename,
|
|
401
485
|
});
|
|
402
486
|
}
|
|
403
487
|
|
|
404
|
-
sourceOf(relation
|
|
488
|
+
sourceOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
|
|
405
489
|
return new QueryClass({
|
|
406
490
|
type: 'relation',
|
|
407
491
|
anchor: this.ast,
|
|
408
492
|
direction: 'outgoing',
|
|
409
|
-
filter: FilterClass.type(relation, predicates).ast,
|
|
493
|
+
filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
|
|
410
494
|
});
|
|
411
495
|
}
|
|
412
496
|
|
|
413
|
-
targetOf(relation
|
|
497
|
+
targetOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
|
|
414
498
|
return new QueryClass({
|
|
415
499
|
type: 'relation',
|
|
416
500
|
anchor: this.ast,
|
|
417
501
|
direction: 'incoming',
|
|
418
|
-
filter: FilterClass.type(relation, predicates).ast,
|
|
502
|
+
filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
|
|
419
503
|
});
|
|
420
504
|
}
|
|
421
505
|
|
|
422
|
-
source(): Query
|
|
506
|
+
source(): Query$.Any {
|
|
423
507
|
return new QueryClass({
|
|
424
508
|
type: 'relation-traversal',
|
|
425
509
|
anchor: this.ast,
|
|
@@ -427,7 +511,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
427
511
|
});
|
|
428
512
|
}
|
|
429
513
|
|
|
430
|
-
target(): Query
|
|
514
|
+
target(): Query$.Any {
|
|
431
515
|
return new QueryClass({
|
|
432
516
|
type: 'relation-traversal',
|
|
433
517
|
anchor: this.ast,
|
|
@@ -435,7 +519,23 @@ class QueryClass implements Echo.Query<any> {
|
|
|
435
519
|
});
|
|
436
520
|
}
|
|
437
521
|
|
|
438
|
-
|
|
522
|
+
parent(): Query$.Any {
|
|
523
|
+
return new QueryClass({
|
|
524
|
+
type: 'hierarchy-traversal',
|
|
525
|
+
anchor: this.ast,
|
|
526
|
+
direction: 'to-parent',
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
children(): Query$.Any {
|
|
531
|
+
return new QueryClass({
|
|
532
|
+
type: 'hierarchy-traversal',
|
|
533
|
+
anchor: this.ast,
|
|
534
|
+
direction: 'to-children',
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
orderBy(...order: Order$.Any[]): Query$.Any {
|
|
439
539
|
return new QueryClass({
|
|
440
540
|
type: 'order',
|
|
441
541
|
query: this.ast,
|
|
@@ -443,7 +543,15 @@ class QueryClass implements Echo.Query<any> {
|
|
|
443
543
|
});
|
|
444
544
|
}
|
|
445
545
|
|
|
446
|
-
|
|
546
|
+
limit(limit: number): Query$.Any {
|
|
547
|
+
return new QueryClass({
|
|
548
|
+
type: 'limit',
|
|
549
|
+
query: this.ast,
|
|
550
|
+
limit,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
options(options: QueryAST.QueryOptions): Query$.Any {
|
|
447
555
|
return new QueryClass({
|
|
448
556
|
type: 'options',
|
|
449
557
|
query: this.ast,
|
|
@@ -452,7 +560,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
452
560
|
}
|
|
453
561
|
}
|
|
454
562
|
|
|
455
|
-
export const Query1: typeof
|
|
563
|
+
export const Query1: typeof Query$ = QueryClass;
|
|
456
564
|
export { Query1 as Query };
|
|
457
565
|
|
|
458
566
|
const RefTypeId: unique symbol = Symbol('@dxos/echo-query/Ref');
|
|
@@ -465,3 +573,127 @@ const makeTypeDxn = (typename: string) => {
|
|
|
465
573
|
assertArgument(!typename.startsWith('dxn:'), 'typename');
|
|
466
574
|
return `dxn:type:${typename}`;
|
|
467
575
|
};
|
|
576
|
+
|
|
577
|
+
const SCOPE_KEYS = new Set(['spaceIds', 'queues', 'allQueuesFromSpaces']);
|
|
578
|
+
|
|
579
|
+
const _isScopeLike = (value: unknown): value is QueryAST.Scope => {
|
|
580
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
return Object.keys(value).every((key) => SCOPE_KEYS.has(key));
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const prettyFilter = (filter: QueryAST.Filter): string => {
|
|
587
|
+
switch (filter.type) {
|
|
588
|
+
case 'object': {
|
|
589
|
+
const parts: string[] = [];
|
|
590
|
+
if (filter.typename !== null) {
|
|
591
|
+
parts.push(JSON.stringify(filter.typename));
|
|
592
|
+
}
|
|
593
|
+
const propEntries = Object.entries(filter.props);
|
|
594
|
+
if (propEntries.length > 0) {
|
|
595
|
+
const propsStr = propEntries.map(([k, v]) => `${k}: ${prettyFilter(v)}`).join(', ');
|
|
596
|
+
parts.push(`{ ${propsStr} }`);
|
|
597
|
+
}
|
|
598
|
+
if (filter.id !== undefined) {
|
|
599
|
+
parts.push(`id: [${filter.id.join(', ')}]`);
|
|
600
|
+
}
|
|
601
|
+
return parts.length > 0 ? `Filter.type(${parts.join(', ')})` : 'Filter.everything()';
|
|
602
|
+
}
|
|
603
|
+
case 'compare':
|
|
604
|
+
return `Filter.${filter.operator}(${JSON.stringify(filter.value)})`;
|
|
605
|
+
case 'in':
|
|
606
|
+
return `Filter.in(${filter.values.map((v) => JSON.stringify(v)).join(', ')})`;
|
|
607
|
+
case 'contains':
|
|
608
|
+
return `Filter.contains(${JSON.stringify(filter.value)})`;
|
|
609
|
+
case 'range':
|
|
610
|
+
return `Filter.between(${JSON.stringify(filter.from)}, ${JSON.stringify(filter.to)})`;
|
|
611
|
+
case 'text-search':
|
|
612
|
+
return `Filter.text(${JSON.stringify(filter.text)})`;
|
|
613
|
+
case 'tag':
|
|
614
|
+
return `Filter.tag(${JSON.stringify(filter.tag)})`;
|
|
615
|
+
case 'timestamp':
|
|
616
|
+
return `Filter.${filter.field}.${filter.operator}(${filter.value})`;
|
|
617
|
+
case 'not':
|
|
618
|
+
return `Filter.not(${prettyFilter(filter.filter)})`;
|
|
619
|
+
case 'and':
|
|
620
|
+
return `Filter.and(${filter.filters.map(prettyFilter).join(', ')})`;
|
|
621
|
+
case 'or':
|
|
622
|
+
return `Filter.or(${filter.filters.map(prettyFilter).join(', ')})`;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
const prettyQuery = (query: QueryAST.Query): string => {
|
|
627
|
+
switch (query.type) {
|
|
628
|
+
case 'select':
|
|
629
|
+
return `Query.select(${prettyFilter(query.filter)})`;
|
|
630
|
+
case 'filter':
|
|
631
|
+
return `${prettyQuery(query.selection)}.select(${prettyFilter(query.filter)})`;
|
|
632
|
+
case 'reference-traversal':
|
|
633
|
+
return `${prettyQuery(query.anchor)}.reference(${JSON.stringify(query.property)})`;
|
|
634
|
+
case 'incoming-references': {
|
|
635
|
+
const args: string[] = [];
|
|
636
|
+
if (query.typename !== null) {
|
|
637
|
+
args.push(JSON.stringify(query.typename));
|
|
638
|
+
}
|
|
639
|
+
if (query.property !== null) {
|
|
640
|
+
args.push(JSON.stringify(query.property));
|
|
641
|
+
}
|
|
642
|
+
return `${prettyQuery(query.anchor)}.referencedBy(${args.join(', ')})`;
|
|
643
|
+
}
|
|
644
|
+
case 'relation': {
|
|
645
|
+
const method =
|
|
646
|
+
query.direction === 'outgoing' ? 'sourceOf' : query.direction === 'incoming' ? 'targetOf' : 'relationOf';
|
|
647
|
+
const filterStr = query.filter !== undefined ? prettyFilter(query.filter) : '';
|
|
648
|
+
return `${prettyQuery(query.anchor)}.${method}(${filterStr})`;
|
|
649
|
+
}
|
|
650
|
+
case 'relation-traversal':
|
|
651
|
+
return `${prettyQuery(query.anchor)}.${query.direction}()`;
|
|
652
|
+
case 'hierarchy-traversal':
|
|
653
|
+
return query.direction === 'to-parent'
|
|
654
|
+
? `${prettyQuery(query.anchor)}.parent()`
|
|
655
|
+
: `${prettyQuery(query.anchor)}.children()`;
|
|
656
|
+
case 'union':
|
|
657
|
+
return `Query.all(${query.queries.map(prettyQuery).join(', ')})`;
|
|
658
|
+
case 'set-difference':
|
|
659
|
+
return `Query.without(${prettyQuery(query.source)}, ${prettyQuery(query.exclude)})`;
|
|
660
|
+
case 'order': {
|
|
661
|
+
const orders = query.order.map((o) => {
|
|
662
|
+
if (o.kind === 'natural') {
|
|
663
|
+
return 'Order.natural';
|
|
664
|
+
}
|
|
665
|
+
if (o.kind === 'rank') {
|
|
666
|
+
return `Order.rank(${JSON.stringify(o.direction)})`;
|
|
667
|
+
}
|
|
668
|
+
return `Order.property(${JSON.stringify(o.property)}, ${JSON.stringify(o.direction)})`;
|
|
669
|
+
});
|
|
670
|
+
return `${prettyQuery(query.query)}.orderBy(${orders.join(', ')})`;
|
|
671
|
+
}
|
|
672
|
+
case 'options': {
|
|
673
|
+
const parts: string[] = [];
|
|
674
|
+
if (query.options.deleted !== undefined) {
|
|
675
|
+
parts.push(`deleted: ${JSON.stringify(query.options.deleted)}`);
|
|
676
|
+
}
|
|
677
|
+
return `${prettyQuery(query.query)}.options({ ${parts.join(', ')} })`;
|
|
678
|
+
}
|
|
679
|
+
case 'from': {
|
|
680
|
+
if (query.from._tag === 'scope') {
|
|
681
|
+
const scope = query.from.scope;
|
|
682
|
+
const parts: string[] = [];
|
|
683
|
+
if (scope.spaceIds !== undefined) {
|
|
684
|
+
parts.push(`spaceIds: [${scope.spaceIds.join(', ')}]`);
|
|
685
|
+
}
|
|
686
|
+
if (scope.queues !== undefined) {
|
|
687
|
+
parts.push(`queues: [${scope.queues.join(', ')}]`);
|
|
688
|
+
}
|
|
689
|
+
if (scope.allQueuesFromSpaces !== undefined) {
|
|
690
|
+
parts.push(`allQueuesFromSpaces: ${scope.allQueuesFromSpaces}`);
|
|
691
|
+
}
|
|
692
|
+
return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
|
|
693
|
+
}
|
|
694
|
+
return `${prettyQuery(query.query)}.from(${prettyQuery(query.from.query)})`;
|
|
695
|
+
}
|
|
696
|
+
case 'limit':
|
|
697
|
+
return `${prettyQuery(query.query)}.limit(${query.limit})`;
|
|
698
|
+
}
|
|
699
|
+
};
|