@dxos/echo-query 0.8.4-main.406dc2a → 0.8.4-main.422d1c7879
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/neutral/index.mjs +912 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/query-lite/index.d.ts +9965 -0
- package/dist/query-lite/index.d.ts.map +1 -0
- package/dist/query-lite/index.js +520 -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/parser/gen/query.terms.d.ts +1 -1
- package/dist/types/src/parser/gen/query.terms.d.ts.map +1 -1
- package/dist/types/src/parser/query-builder.d.ts +18 -3
- package/dist/types/src/parser/query-builder.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 -19
- package/src/index.ts +1 -0
- package/src/parser/gen/query.terms.ts +24 -23
- package/src/parser/gen/query.ts +8 -8
- package/src/parser/query-builder.ts +318 -18
- package/src/parser/query.grammar +8 -2
- package/src/parser/query.test.ts +207 -41
- package/src/query-lite/query-lite.ts +329 -70
- package/src/sandbox/index.ts +5 -0
- package/src/sandbox/query-sandbox.test.ts +15 -14
- package/src/sandbox/query-sandbox.ts +1 -1
- package/src/sandbox/quickjs.ts +1 -2
- package/dist/lib/browser/index.mjs +0 -530
- 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 -530
- package/dist/lib/node-esm/index.mjs.map +0 -7
- 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,83 @@ 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 childOf(parents: unknown | DXN | (unknown | DXN)[], options?: { transitive?: boolean }): Filter$.Any {
|
|
255
|
+
const items = Array.isArray(parents) ? parents : [parents];
|
|
256
|
+
const dxns = items.map((item) => {
|
|
257
|
+
if (isDxnLike(item)) {
|
|
258
|
+
return item.toString();
|
|
259
|
+
}
|
|
260
|
+
throw new TypeError('childOf requires DXN values in query-lite');
|
|
261
|
+
});
|
|
262
|
+
return new FilterClass({
|
|
263
|
+
type: 'child-of',
|
|
264
|
+
parents: dxns,
|
|
265
|
+
transitive: options?.transitive ?? true,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private static _timeRangeFilter(
|
|
270
|
+
field: 'updatedAt' | 'createdAt',
|
|
271
|
+
range: { after?: Date | number; before?: Date | number },
|
|
272
|
+
): Filter$.Any {
|
|
273
|
+
const toMs = (d: Date | number) => (typeof d === 'number' ? d : d.getTime());
|
|
274
|
+
const filters: Filter$.Any[] = [];
|
|
275
|
+
if (range.after != null) {
|
|
276
|
+
filters.push(new FilterClass({ type: 'timestamp', field, operator: 'gte', value: toMs(range.after) }));
|
|
277
|
+
}
|
|
278
|
+
if (range.before != null) {
|
|
279
|
+
filters.push(new FilterClass({ type: 'timestamp', field, operator: 'lte', value: toMs(range.before) }));
|
|
280
|
+
}
|
|
281
|
+
if (filters.length === 0) {
|
|
282
|
+
return FilterClass.everything();
|
|
283
|
+
}
|
|
284
|
+
return filters.length === 1 ? filters[0] : FilterClass.and(...filters);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
static not<F extends Filter$.Any>(filter: F): Filter$.Filter<Filter$.Type<F>> {
|
|
241
288
|
return new FilterClass({
|
|
242
289
|
type: 'not',
|
|
243
290
|
filter: filter.ast,
|
|
244
291
|
});
|
|
245
292
|
}
|
|
246
293
|
|
|
247
|
-
static and<
|
|
294
|
+
static and<Filters extends readonly Filter$.Any[]>(
|
|
295
|
+
...filters: Filters
|
|
296
|
+
): Filter$.Filter<Filter$.Type<Filters[number]>> {
|
|
248
297
|
return new FilterClass({
|
|
249
298
|
type: 'and',
|
|
250
299
|
filters: filters.map((f) => f.ast),
|
|
251
300
|
});
|
|
252
301
|
}
|
|
253
302
|
|
|
254
|
-
static or<
|
|
303
|
+
static or<Filters extends readonly Filter$.Any[]>(
|
|
304
|
+
...filters: Filters
|
|
305
|
+
): Filter$.Filter<Filter$.Type<Filters[number]>> {
|
|
255
306
|
return new FilterClass({
|
|
256
307
|
type: 'or',
|
|
257
308
|
filters: filters.map((f) => f.ast),
|
|
258
309
|
});
|
|
259
310
|
}
|
|
260
311
|
|
|
312
|
+
/** Returns a human-readable string representation of a Filter AST. */
|
|
313
|
+
static pretty(filter: Filter$.Any): string {
|
|
314
|
+
return prettyFilter(filter.ast);
|
|
315
|
+
}
|
|
316
|
+
|
|
261
317
|
private constructor(public readonly ast: QueryAST.Filter) {}
|
|
262
318
|
|
|
263
319
|
'~Filter' = FilterClass.variance;
|
|
264
320
|
}
|
|
265
321
|
|
|
266
|
-
export const Filter1: typeof
|
|
322
|
+
export const Filter1: typeof Filter$ = FilterClass;
|
|
267
323
|
export { Filter1 as Filter };
|
|
268
324
|
|
|
269
325
|
/**
|
|
@@ -272,7 +328,7 @@ export { Filter1 as Filter };
|
|
|
272
328
|
// TODO(dmaretskyi): Filter only properties that are references (or optional references, or unions that include references).
|
|
273
329
|
type RefPropKey<T> = keyof T & string;
|
|
274
330
|
|
|
275
|
-
const propsFilterToAst = (predicates:
|
|
331
|
+
const propsFilterToAst = (predicates: Filter$.Props<any>): Pick<QueryAST.FilterObject, 'id' | 'props'> => {
|
|
276
332
|
let idFilter: readonly ObjectId[] | undefined;
|
|
277
333
|
if ('id' in predicates) {
|
|
278
334
|
assertArgument(
|
|
@@ -317,32 +373,48 @@ const processPredicate = (predicate: any): QueryAST.Filter => {
|
|
|
317
373
|
return FilterClass.eq(predicate).ast;
|
|
318
374
|
};
|
|
319
375
|
|
|
320
|
-
class QueryClass implements
|
|
321
|
-
private static variance:
|
|
376
|
+
class QueryClass implements Query$.Any {
|
|
377
|
+
private static 'variance': Query$.Any['~Query'] = {} as Query$.Any['~Query'];
|
|
322
378
|
|
|
323
|
-
static is(value: unknown): value is
|
|
379
|
+
static is(value: unknown): value is Query$.Any {
|
|
324
380
|
return typeof value === 'object' && value !== null && '~Query' in value;
|
|
325
381
|
}
|
|
326
382
|
|
|
327
|
-
static fromAst(ast: QueryAST.Query):
|
|
383
|
+
static fromAst(ast: QueryAST.Query): Query$.Any {
|
|
328
384
|
return new QueryClass(ast);
|
|
329
385
|
}
|
|
330
386
|
|
|
331
|
-
static select<F extends
|
|
387
|
+
static select<F extends Filter$.Any>(filter: F): Query$.Query<Filter$.Type<F>> {
|
|
332
388
|
return new QueryClass({
|
|
333
389
|
type: 'select',
|
|
334
390
|
filter: filter.ast,
|
|
335
391
|
});
|
|
336
392
|
}
|
|
337
393
|
|
|
338
|
-
|
|
394
|
+
select(filter: Filter$.Any | Filter$.Props<any>): Query$.Any {
|
|
395
|
+
if (FilterClass.is(filter)) {
|
|
396
|
+
return new QueryClass({
|
|
397
|
+
type: 'filter',
|
|
398
|
+
selection: this.ast,
|
|
399
|
+
filter: filter.ast,
|
|
400
|
+
});
|
|
401
|
+
} else {
|
|
402
|
+
return new QueryClass({
|
|
403
|
+
type: 'filter',
|
|
404
|
+
selection: this.ast,
|
|
405
|
+
filter: FilterClass.props(filter).ast,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
static type(schema: Schema.Schema.All | string, predicates?: Filter$.Props<unknown>): Query$.Any {
|
|
339
411
|
return new QueryClass({
|
|
340
412
|
type: 'select',
|
|
341
413
|
filter: FilterClass.type(schema, predicates).ast,
|
|
342
414
|
});
|
|
343
415
|
}
|
|
344
416
|
|
|
345
|
-
static all(...queries: Query
|
|
417
|
+
static all(...queries: Query$.Any[]): Query$.Any {
|
|
346
418
|
if (queries.length === 0) {
|
|
347
419
|
throw new TypeError(
|
|
348
420
|
'Query.all combines results of multiple queries, to query all objects use Query.select(Filter.everything())',
|
|
@@ -354,7 +426,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
354
426
|
});
|
|
355
427
|
}
|
|
356
428
|
|
|
357
|
-
static without<T>(source: Query<T>, exclude: Query<T>): Query<T> {
|
|
429
|
+
static without<T>(source: Query$.Query<T>, exclude: Query$.Query<T>): Query$.Query<T> {
|
|
358
430
|
return new QueryClass({
|
|
359
431
|
type: 'set-difference',
|
|
360
432
|
source: source.ast,
|
|
@@ -362,27 +434,50 @@ class QueryClass implements Echo.Query<any> {
|
|
|
362
434
|
});
|
|
363
435
|
}
|
|
364
436
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
437
|
+
static from(source: any, options?: { includeFeeds?: boolean }): Query$.Any {
|
|
438
|
+
const baseQuery: QueryAST.Query = {
|
|
439
|
+
type: 'select',
|
|
440
|
+
filter: FilterClass.everything().ast,
|
|
441
|
+
};
|
|
442
|
+
const wrapper = new QueryClass(baseQuery);
|
|
443
|
+
return wrapper.from(source, options);
|
|
444
|
+
}
|
|
368
445
|
|
|
369
|
-
|
|
370
|
-
if (
|
|
446
|
+
from(arg: any, options?: { includeFeeds?: boolean }): Query$.Any {
|
|
447
|
+
if (arg === 'all-accessible-spaces') {
|
|
371
448
|
return new QueryClass({
|
|
372
|
-
type: '
|
|
373
|
-
|
|
374
|
-
|
|
449
|
+
type: 'from',
|
|
450
|
+
query: this.ast,
|
|
451
|
+
from: {
|
|
452
|
+
_tag: 'scope',
|
|
453
|
+
scope: {
|
|
454
|
+
...(options?.includeFeeds ? { allQueuesFromSpaces: true } : {}),
|
|
455
|
+
},
|
|
456
|
+
},
|
|
375
457
|
});
|
|
376
|
-
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (_isScopeLike(arg)) {
|
|
377
461
|
return new QueryClass({
|
|
378
|
-
type: '
|
|
379
|
-
|
|
380
|
-
|
|
462
|
+
type: 'from',
|
|
463
|
+
query: this.ast,
|
|
464
|
+
from: { _tag: 'scope', scope: arg },
|
|
381
465
|
});
|
|
382
466
|
}
|
|
467
|
+
|
|
468
|
+
throw new TypeError('Database and Feed objects are not supported in query-lite sandbox');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** Returns a human-readable string representation of a Query AST. */
|
|
472
|
+
static pretty(query: Query$.Any): string {
|
|
473
|
+
return prettyQuery(query.ast);
|
|
383
474
|
}
|
|
384
475
|
|
|
385
|
-
|
|
476
|
+
constructor(public readonly ast: QueryAST.Query) {}
|
|
477
|
+
|
|
478
|
+
'~Query' = QueryClass.variance;
|
|
479
|
+
|
|
480
|
+
reference(key: string): Query$.Any {
|
|
386
481
|
return new QueryClass({
|
|
387
482
|
type: 'reference-traversal',
|
|
388
483
|
anchor: this.ast,
|
|
@@ -390,36 +485,40 @@ class QueryClass implements Echo.Query<any> {
|
|
|
390
485
|
});
|
|
391
486
|
}
|
|
392
487
|
|
|
393
|
-
referencedBy(target
|
|
394
|
-
|
|
395
|
-
|
|
488
|
+
referencedBy(target?: Schema.Schema.All | string, key?: string): Query$.Any {
|
|
489
|
+
const typename =
|
|
490
|
+
target !== undefined
|
|
491
|
+
? (assertArgument(typeof target === 'string', 'target'),
|
|
492
|
+
assertArgument(!target.startsWith('dxn:'), 'target'),
|
|
493
|
+
target)
|
|
494
|
+
: null;
|
|
396
495
|
return new QueryClass({
|
|
397
496
|
type: 'incoming-references',
|
|
398
497
|
anchor: this.ast,
|
|
399
|
-
property: key,
|
|
400
|
-
typename
|
|
498
|
+
property: key ?? null,
|
|
499
|
+
typename,
|
|
401
500
|
});
|
|
402
501
|
}
|
|
403
502
|
|
|
404
|
-
sourceOf(relation
|
|
503
|
+
sourceOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
|
|
405
504
|
return new QueryClass({
|
|
406
505
|
type: 'relation',
|
|
407
506
|
anchor: this.ast,
|
|
408
507
|
direction: 'outgoing',
|
|
409
|
-
filter: FilterClass.type(relation, predicates).ast,
|
|
508
|
+
filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
|
|
410
509
|
});
|
|
411
510
|
}
|
|
412
511
|
|
|
413
|
-
targetOf(relation
|
|
512
|
+
targetOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
|
|
414
513
|
return new QueryClass({
|
|
415
514
|
type: 'relation',
|
|
416
515
|
anchor: this.ast,
|
|
417
516
|
direction: 'incoming',
|
|
418
|
-
filter: FilterClass.type(relation, predicates).ast,
|
|
517
|
+
filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
|
|
419
518
|
});
|
|
420
519
|
}
|
|
421
520
|
|
|
422
|
-
source(): Query
|
|
521
|
+
source(): Query$.Any {
|
|
423
522
|
return new QueryClass({
|
|
424
523
|
type: 'relation-traversal',
|
|
425
524
|
anchor: this.ast,
|
|
@@ -427,7 +526,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
427
526
|
});
|
|
428
527
|
}
|
|
429
528
|
|
|
430
|
-
target(): Query
|
|
529
|
+
target(): Query$.Any {
|
|
431
530
|
return new QueryClass({
|
|
432
531
|
type: 'relation-traversal',
|
|
433
532
|
anchor: this.ast,
|
|
@@ -435,7 +534,23 @@ class QueryClass implements Echo.Query<any> {
|
|
|
435
534
|
});
|
|
436
535
|
}
|
|
437
536
|
|
|
438
|
-
|
|
537
|
+
parent(): Query$.Any {
|
|
538
|
+
return new QueryClass({
|
|
539
|
+
type: 'hierarchy-traversal',
|
|
540
|
+
anchor: this.ast,
|
|
541
|
+
direction: 'to-parent',
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
children(): Query$.Any {
|
|
546
|
+
return new QueryClass({
|
|
547
|
+
type: 'hierarchy-traversal',
|
|
548
|
+
anchor: this.ast,
|
|
549
|
+
direction: 'to-children',
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
orderBy(...order: Order$.Any[]): Query$.Any {
|
|
439
554
|
return new QueryClass({
|
|
440
555
|
type: 'order',
|
|
441
556
|
query: this.ast,
|
|
@@ -443,7 +558,15 @@ class QueryClass implements Echo.Query<any> {
|
|
|
443
558
|
});
|
|
444
559
|
}
|
|
445
560
|
|
|
446
|
-
|
|
561
|
+
limit(limit: number): Query$.Any {
|
|
562
|
+
return new QueryClass({
|
|
563
|
+
type: 'limit',
|
|
564
|
+
query: this.ast,
|
|
565
|
+
limit,
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
options(options: QueryAST.QueryOptions): Query$.Any {
|
|
447
570
|
return new QueryClass({
|
|
448
571
|
type: 'options',
|
|
449
572
|
query: this.ast,
|
|
@@ -452,7 +575,7 @@ class QueryClass implements Echo.Query<any> {
|
|
|
452
575
|
}
|
|
453
576
|
}
|
|
454
577
|
|
|
455
|
-
export const Query1: typeof
|
|
578
|
+
export const Query1: typeof Query$ = QueryClass;
|
|
456
579
|
export { Query1 as Query };
|
|
457
580
|
|
|
458
581
|
const RefTypeId: unique symbol = Symbol('@dxos/echo-query/Ref');
|
|
@@ -465,3 +588,139 @@ const makeTypeDxn = (typename: string) => {
|
|
|
465
588
|
assertArgument(!typename.startsWith('dxn:'), 'typename');
|
|
466
589
|
return `dxn:type:${typename}`;
|
|
467
590
|
};
|
|
591
|
+
|
|
592
|
+
const isDxnLike = (value: unknown): value is DXN => {
|
|
593
|
+
return (
|
|
594
|
+
typeof value === 'object' &&
|
|
595
|
+
value !== null &&
|
|
596
|
+
'toString' in value &&
|
|
597
|
+
typeof value.toString === 'function' &&
|
|
598
|
+
value.toString().startsWith('dxn:')
|
|
599
|
+
);
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
const SCOPE_KEYS = new Set(['spaceIds', 'queues', 'allQueuesFromSpaces']);
|
|
603
|
+
|
|
604
|
+
const _isScopeLike = (value: unknown): value is QueryAST.Scope => {
|
|
605
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return Object.keys(value).every((key) => SCOPE_KEYS.has(key));
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
const prettyFilter = (filter: QueryAST.Filter): string => {
|
|
612
|
+
switch (filter.type) {
|
|
613
|
+
case 'object': {
|
|
614
|
+
const parts: string[] = [];
|
|
615
|
+
if (filter.typename !== null) {
|
|
616
|
+
parts.push(JSON.stringify(filter.typename));
|
|
617
|
+
}
|
|
618
|
+
const propEntries = Object.entries(filter.props);
|
|
619
|
+
if (propEntries.length > 0) {
|
|
620
|
+
const propsStr = propEntries.map(([k, v]) => `${k}: ${prettyFilter(v)}`).join(', ');
|
|
621
|
+
parts.push(`{ ${propsStr} }`);
|
|
622
|
+
}
|
|
623
|
+
if (filter.id !== undefined) {
|
|
624
|
+
parts.push(`id: [${filter.id.join(', ')}]`);
|
|
625
|
+
}
|
|
626
|
+
return parts.length > 0 ? `Filter.type(${parts.join(', ')})` : 'Filter.everything()';
|
|
627
|
+
}
|
|
628
|
+
case 'compare':
|
|
629
|
+
return `Filter.${filter.operator}(${JSON.stringify(filter.value)})`;
|
|
630
|
+
case 'in':
|
|
631
|
+
return `Filter.in(${filter.values.map((v) => JSON.stringify(v)).join(', ')})`;
|
|
632
|
+
case 'contains':
|
|
633
|
+
return `Filter.contains(${JSON.stringify(filter.value)})`;
|
|
634
|
+
case 'range':
|
|
635
|
+
return `Filter.between(${JSON.stringify(filter.from)}, ${JSON.stringify(filter.to)})`;
|
|
636
|
+
case 'text-search':
|
|
637
|
+
return `Filter.text(${JSON.stringify(filter.text)})`;
|
|
638
|
+
case 'tag':
|
|
639
|
+
return `Filter.tag(${JSON.stringify(filter.tag)})`;
|
|
640
|
+
case 'child-of':
|
|
641
|
+
return `Filter.childOf([${filter.parents.map((parent) => JSON.stringify(parent)).join(', ')}], { transitive: ${filter.transitive} })`;
|
|
642
|
+
case 'timestamp':
|
|
643
|
+
return `Filter.${filter.field}.${filter.operator}(${filter.value})`;
|
|
644
|
+
case 'not':
|
|
645
|
+
return `Filter.not(${prettyFilter(filter.filter)})`;
|
|
646
|
+
case 'and':
|
|
647
|
+
return `Filter.and(${filter.filters.map(prettyFilter).join(', ')})`;
|
|
648
|
+
case 'or':
|
|
649
|
+
return `Filter.or(${filter.filters.map(prettyFilter).join(', ')})`;
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
const prettyQuery = (query: QueryAST.Query): string => {
|
|
654
|
+
switch (query.type) {
|
|
655
|
+
case 'select':
|
|
656
|
+
return `Query.select(${prettyFilter(query.filter)})`;
|
|
657
|
+
case 'filter':
|
|
658
|
+
return `${prettyQuery(query.selection)}.select(${prettyFilter(query.filter)})`;
|
|
659
|
+
case 'reference-traversal':
|
|
660
|
+
return `${prettyQuery(query.anchor)}.reference(${JSON.stringify(query.property)})`;
|
|
661
|
+
case 'incoming-references': {
|
|
662
|
+
const args: string[] = [];
|
|
663
|
+
if (query.typename !== null) {
|
|
664
|
+
args.push(JSON.stringify(query.typename));
|
|
665
|
+
}
|
|
666
|
+
if (query.property !== null) {
|
|
667
|
+
args.push(JSON.stringify(query.property));
|
|
668
|
+
}
|
|
669
|
+
return `${prettyQuery(query.anchor)}.referencedBy(${args.join(', ')})`;
|
|
670
|
+
}
|
|
671
|
+
case 'relation': {
|
|
672
|
+
const method =
|
|
673
|
+
query.direction === 'outgoing' ? 'sourceOf' : query.direction === 'incoming' ? 'targetOf' : 'relationOf';
|
|
674
|
+
const filterStr = query.filter !== undefined ? prettyFilter(query.filter) : '';
|
|
675
|
+
return `${prettyQuery(query.anchor)}.${method}(${filterStr})`;
|
|
676
|
+
}
|
|
677
|
+
case 'relation-traversal':
|
|
678
|
+
return `${prettyQuery(query.anchor)}.${query.direction}()`;
|
|
679
|
+
case 'hierarchy-traversal':
|
|
680
|
+
return query.direction === 'to-parent'
|
|
681
|
+
? `${prettyQuery(query.anchor)}.parent()`
|
|
682
|
+
: `${prettyQuery(query.anchor)}.children()`;
|
|
683
|
+
case 'union':
|
|
684
|
+
return `Query.all(${query.queries.map(prettyQuery).join(', ')})`;
|
|
685
|
+
case 'set-difference':
|
|
686
|
+
return `Query.without(${prettyQuery(query.source)}, ${prettyQuery(query.exclude)})`;
|
|
687
|
+
case 'order': {
|
|
688
|
+
const orders = query.order.map((o) => {
|
|
689
|
+
if (o.kind === 'natural') {
|
|
690
|
+
return 'Order.natural';
|
|
691
|
+
}
|
|
692
|
+
if (o.kind === 'rank') {
|
|
693
|
+
return `Order.rank(${JSON.stringify(o.direction)})`;
|
|
694
|
+
}
|
|
695
|
+
return `Order.property(${JSON.stringify(o.property)}, ${JSON.stringify(o.direction)})`;
|
|
696
|
+
});
|
|
697
|
+
return `${prettyQuery(query.query)}.orderBy(${orders.join(', ')})`;
|
|
698
|
+
}
|
|
699
|
+
case 'options': {
|
|
700
|
+
const parts: string[] = [];
|
|
701
|
+
if (query.options.deleted !== undefined) {
|
|
702
|
+
parts.push(`deleted: ${JSON.stringify(query.options.deleted)}`);
|
|
703
|
+
}
|
|
704
|
+
return `${prettyQuery(query.query)}.options({ ${parts.join(', ')} })`;
|
|
705
|
+
}
|
|
706
|
+
case 'from': {
|
|
707
|
+
if (query.from._tag === 'scope') {
|
|
708
|
+
const scope = query.from.scope;
|
|
709
|
+
const parts: string[] = [];
|
|
710
|
+
if (scope.spaceIds !== undefined) {
|
|
711
|
+
parts.push(`spaceIds: [${scope.spaceIds.join(', ')}]`);
|
|
712
|
+
}
|
|
713
|
+
if (scope.queues !== undefined) {
|
|
714
|
+
parts.push(`queues: [${scope.queues.join(', ')}]`);
|
|
715
|
+
}
|
|
716
|
+
if (scope.allQueuesFromSpaces !== undefined) {
|
|
717
|
+
parts.push(`allQueuesFromSpaces: ${scope.allQueuesFromSpaces}`);
|
|
718
|
+
}
|
|
719
|
+
return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
|
|
720
|
+
}
|
|
721
|
+
return `${prettyQuery(query.query)}.from(${prettyQuery(query.from.query)})`;
|
|
722
|
+
}
|
|
723
|
+
case 'limit':
|
|
724
|
+
return `${prettyQuery(query.query)}.limit(${query.limit})`;
|
|
725
|
+
}
|
|
726
|
+
};
|