@dxos/echo-query 0.8.4-main.ef1bc66f44 → 0.8.4-main.effb148878

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.
@@ -4,10 +4,12 @@
4
4
 
5
5
  import type * as Schema from 'effect/Schema';
6
6
 
7
- import type { Filter as Filter$, Order as Order$, Query as Query$, Ref } from '@dxos/echo';
7
+ import type { Filter as Filter$, Obj as Obj$, Order as Order$, Query as Query$, Ref, Type as Type$ } from '@dxos/echo';
8
8
  import type { ForeignKey, QueryAST } from '@dxos/echo-protocol';
9
9
  import { assertArgument } from '@dxos/invariant';
10
- import type { DXN, ObjectId } from '@dxos/keys';
10
+ // `DXN`/`EchoURI` are type-only imports to keep the `query-lite` bundle free of
11
+ // `effect/Schema` (which pulls runtime helpers QuickJS can't parse — e.g. private class fields).
12
+ import type { DXN, EchoURI, ObjectId, URI } from '@dxos/keys';
11
13
 
12
14
  //
13
15
  // Light-weight implementation of query execution.
@@ -16,7 +18,7 @@ import type { DXN, ObjectId } from '@dxos/keys';
16
18
  // TODO(wittjosiah): The `export * as ...` syntax causes tsdown to genereate multiple files which breaks the sandbox.
17
19
 
18
20
  class OrderClass implements Order$.Any {
19
- private static variance: Order$.Any['~Order'] = {} as Order$.Any['~Order'];
21
+ private static 'variance': Order$.Any['~Order'] = {} as Order$.Any['~Order'];
20
22
 
21
23
  static is(value: unknown): value is Order$.Any {
22
24
  return typeof value === 'object' && value !== null && '~Order' in value;
@@ -46,7 +48,7 @@ const Order2: typeof Order$ = Order1;
46
48
  export { Order2 as Order };
47
49
 
48
50
  class FilterClass implements Filter$.Any {
49
- private static variance: Filter$.Any['~Filter'] = {} as Filter$.Any['~Filter'];
51
+ private static 'variance': Filter$.Any['~Filter'] = {} as Filter$.Any['~Filter'];
50
52
 
51
53
  static is(value: unknown): value is Filter$.Any {
52
54
  return typeof value === 'object' && value !== null && '~Filter' in value;
@@ -102,10 +104,12 @@ class FilterClass implements Filter$.Any {
102
104
  });
103
105
  }
104
106
 
105
- static type<S extends Schema.Schema.All>(
106
- schema: S | string,
107
- props?: Filter$.Props<Schema.Schema.Type<S>>,
108
- ): Filter$.Filter<Schema.Schema.Type<S>> {
107
+ static type<T extends Type$.AnyEntity>(
108
+ type: T,
109
+ props?: Filter$.Props<Type$.InstanceType<T>>,
110
+ ): Filter$.Filter<Type$.InstanceType<T>>;
111
+ static type(schema: string, props?: Filter$.Props<unknown>): Filter$.Filter<any>;
112
+ static type(schema: Type$.AnyEntity | string, props?: Filter$.Props<unknown>): Filter$.Filter<unknown> {
109
113
  if (typeof schema !== 'string') {
110
114
  throw new TypeError('expected typename as the first paramter');
111
115
  }
@@ -124,10 +128,10 @@ class FilterClass implements Filter$.Any {
124
128
  });
125
129
  }
126
130
 
127
- static typeDXN(dxn: DXN): Filter$.Any {
131
+ static typeURI(uri: URI.URI): Filter$.Any {
128
132
  return new FilterClass({
129
133
  type: 'object',
130
- typename: dxn.toString(),
134
+ typename: uri,
131
135
  props: {},
132
136
  });
133
137
  }
@@ -139,6 +143,16 @@ class FilterClass implements Filter$.Any {
139
143
  });
140
144
  }
141
145
 
146
+ static key(key: string, options?: Filter$.KeyFilterOptions): Filter$.Any {
147
+ return new FilterClass({
148
+ type: 'object',
149
+ typename: null,
150
+ props: {},
151
+ metaKey: key,
152
+ metaVersion: options?.version,
153
+ });
154
+ }
155
+
142
156
  static props<T>(props: Filter$.Props<T>): Filter$.Filter<T> {
143
157
  return new FilterClass({
144
158
  type: 'object',
@@ -155,15 +169,15 @@ class FilterClass implements Filter$.Any {
155
169
  });
156
170
  }
157
171
 
158
- static foreignKeys<S extends Schema.Schema.All>(
159
- schema: S | string,
172
+ static foreignKeys<S extends Type$.AnyEntity | string>(
173
+ schema: S,
160
174
  keys: ForeignKey[],
161
- ): Filter$.Filter<Schema.Schema.Type<S>> {
175
+ ): Filter$.Filter<S extends Type$.AnyEntity ? Type$.InstanceType<S> : unknown> {
162
176
  assertArgument(typeof schema === 'string', 'schema');
163
177
  assertArgument(!schema.startsWith('dxn:'), 'schema');
164
178
  return new FilterClass({
165
179
  type: 'object',
166
- typename: `dxn:type:${schema}`,
180
+ typename: makeTypeDxn(schema),
167
181
  props: {},
168
182
  foreignKeys: keys,
169
183
  });
@@ -221,7 +235,7 @@ class FilterClass implements Filter$.Any {
221
235
  });
222
236
  }
223
237
 
224
- static in<T>(...values: T[]): Filter$.Filter<T | undefined> {
238
+ static in<T>(...values: T[]): Filter$.Filter<T> {
225
239
  return new FilterClass({
226
240
  type: 'in',
227
241
  values,
@@ -235,7 +249,7 @@ class FilterClass implements Filter$.Any {
235
249
  });
236
250
  }
237
251
 
238
- static between<T>(from: T, to: T): Filter$.Filter<unknown> {
252
+ static between<T>(from: T, to: T): Filter$.Filter<T> {
239
253
  return new FilterClass({
240
254
  type: 'range',
241
255
  from,
@@ -243,6 +257,47 @@ class FilterClass implements Filter$.Any {
243
257
  });
244
258
  }
245
259
 
260
+ static updated(range: { after?: Date | number; before?: Date | number }): Filter$.Any {
261
+ return FilterClass._timeRangeFilter('updatedAt', range);
262
+ }
263
+
264
+ static created(range: { after?: Date | number; before?: Date | number }): Filter$.Any {
265
+ return FilterClass._timeRangeFilter('createdAt', range);
266
+ }
267
+
268
+ static childOf(parents: unknown | unknown[], options?: { transitive?: boolean }): Filter$.Any {
269
+ const items = Array.isArray(parents) ? parents : [parents];
270
+ const dxns = items.map((item) => {
271
+ if (isEchoUriLike(item)) {
272
+ return item.toString() as EchoURI.EchoURI;
273
+ }
274
+ throw new TypeError('childOf requires EchoURI values in query-lite');
275
+ });
276
+ return new FilterClass({
277
+ type: 'child-of',
278
+ parents: dxns,
279
+ transitive: options?.transitive ?? true,
280
+ });
281
+ }
282
+
283
+ private static _timeRangeFilter(
284
+ field: 'updatedAt' | 'createdAt',
285
+ range: { after?: Date | number; before?: Date | number },
286
+ ): Filter$.Any {
287
+ const toMs = (d: Date | number) => (typeof d === 'number' ? d : d.getTime());
288
+ const filters: Filter$.Any[] = [];
289
+ if (range.after != null) {
290
+ filters.push(new FilterClass({ type: 'timestamp', field, operator: 'gte', value: toMs(range.after) }));
291
+ }
292
+ if (range.before != null) {
293
+ filters.push(new FilterClass({ type: 'timestamp', field, operator: 'lte', value: toMs(range.before) }));
294
+ }
295
+ if (filters.length === 0) {
296
+ return FilterClass.everything();
297
+ }
298
+ return filters.length === 1 ? filters[0] : FilterClass.and(...filters);
299
+ }
300
+
246
301
  static not<F extends Filter$.Any>(filter: F): Filter$.Filter<Filter$.Type<F>> {
247
302
  return new FilterClass({
248
303
  type: 'not',
@@ -268,6 +323,11 @@ class FilterClass implements Filter$.Any {
268
323
  });
269
324
  }
270
325
 
326
+ /** Returns a human-readable string representation of a Filter AST. */
327
+ static pretty(filter: Filter$.Any): string {
328
+ return prettyFilter(filter.ast);
329
+ }
330
+
271
331
  private constructor(public readonly ast: QueryAST.Filter) {}
272
332
 
273
333
  '~Filter' = FilterClass.variance;
@@ -328,7 +388,7 @@ const processPredicate = (predicate: any): QueryAST.Filter => {
328
388
  };
329
389
 
330
390
  class QueryClass implements Query$.Any {
331
- private static variance: Query$.Any['~Query'] = {} as Query$.Any['~Query'];
391
+ private static 'variance': Query$.Any['~Query'] = {} as Query$.Any['~Query'];
332
392
 
333
393
  static is(value: unknown): value is Query$.Any {
334
394
  return typeof value === 'object' && value !== null && '~Query' in value;
@@ -345,7 +405,32 @@ class QueryClass implements Query$.Any {
345
405
  });
346
406
  }
347
407
 
348
- static type(schema: Schema.Schema.All | string, predicates?: Filter$.Props<unknown>): Query$.Any {
408
+ select(filter: Filter$.Any | Filter$.Props<any>): Query$.Any {
409
+ if (FilterClass.is(filter)) {
410
+ return new QueryClass({
411
+ type: 'filter',
412
+ selection: this.ast,
413
+ filter: filter.ast,
414
+ });
415
+ } else {
416
+ return new QueryClass({
417
+ type: 'filter',
418
+ selection: this.ast,
419
+ filter: FilterClass.props(filter).ast,
420
+ });
421
+ }
422
+ }
423
+
424
+ static type<S extends Schema.Schema.All>(
425
+ schema: S,
426
+ predicates?: Filter$.Props<Schema.Schema.Type<S>>,
427
+ ): Query$.Query<Schema.Schema.Type<S>>;
428
+ static type(type: Type$.Type, predicates?: Filter$.Props<Obj$.Unknown>): Query$.Query<Obj$.Unknown>;
429
+ static type(schema: string, predicates?: Filter$.Props<unknown>): Query$.Query<any>;
430
+ static type(schema: Schema.Schema.All | Type$.Type | string, predicates?: Filter$.Props<unknown>): Query$.Any {
431
+ if (typeof schema !== 'string') {
432
+ throw new TypeError('expected typename as the first paramter');
433
+ }
349
434
  return new QueryClass({
350
435
  type: 'select',
351
436
  filter: FilterClass.type(schema, predicates).ast,
@@ -372,26 +457,49 @@ class QueryClass implements Query$.Any {
372
457
  });
373
458
  }
374
459
 
375
- constructor(public readonly ast: QueryAST.Query) {}
376
-
377
- '~Query' = QueryClass.variance;
460
+ static from(source: any, options?: { includeFeeds?: boolean }): Query$.Any {
461
+ const baseQuery: QueryAST.Query = {
462
+ type: 'select',
463
+ filter: FilterClass.everything().ast,
464
+ };
465
+ const wrapper = new QueryClass(baseQuery);
466
+ return wrapper.from(source, options);
467
+ }
378
468
 
379
- select(filter: Filter$.Any | Filter$.Props<any>): Query$.Any {
380
- if (FilterClass.is(filter)) {
469
+ from(arg: any, options?: { includeFeeds?: boolean }): Query$.Any {
470
+ if (arg === 'all-accessible-spaces') {
381
471
  return new QueryClass({
382
- type: 'filter',
383
- selection: this.ast,
384
- filter: filter.ast,
472
+ type: 'from',
473
+ query: this.ast,
474
+ from: {
475
+ _tag: 'scope',
476
+ scope: {
477
+ ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
478
+ },
479
+ },
385
480
  });
386
- } else {
481
+ }
482
+
483
+ if (_isScopeLike(arg)) {
387
484
  return new QueryClass({
388
- type: 'filter',
389
- selection: this.ast,
390
- filter: FilterClass.props(filter).ast,
485
+ type: 'from',
486
+ query: this.ast,
487
+ from: { _tag: 'scope', scope: arg },
391
488
  });
392
489
  }
490
+
491
+ throw new TypeError('Database and Feed objects are not supported in query-lite sandbox');
393
492
  }
394
493
 
494
+ /** Returns a human-readable string representation of a Query AST. */
495
+ static pretty(query: Query$.Any): string {
496
+ return prettyQuery(query.ast);
497
+ }
498
+
499
+ constructor(public readonly ast: QueryAST.Query) {}
500
+
501
+ '~Query' = QueryClass.variance;
502
+
395
503
  reference(key: string): Query$.Any {
396
504
  return new QueryClass({
397
505
  type: 'reference-traversal',
@@ -400,13 +508,11 @@ class QueryClass implements Query$.Any {
400
508
  });
401
509
  }
402
510
 
403
- referencedBy(target?: Schema.Schema.All | string, key?: string): Query$.Any {
404
- const typename =
405
- target !== undefined
406
- ? (assertArgument(typeof target === 'string', 'target'),
407
- assertArgument(!target.startsWith('dxn:'), 'target'),
408
- target)
409
- : null;
511
+ referencedBy(target?: Type$.AnyEntity | string, key?: string): Query$.Any {
512
+ if (target !== undefined && typeof target !== 'string') {
513
+ throw new TypeError('referencedBy requires a typename string in query-lite');
514
+ }
515
+ const typename = target !== undefined ? makeTypeDxn(target) : null;
410
516
  return new QueryClass({
411
517
  type: 'incoming-references',
412
518
  anchor: this.ast,
@@ -415,21 +521,37 @@ class QueryClass implements Query$.Any {
415
521
  });
416
522
  }
417
523
 
418
- sourceOf(relation: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
524
+ sourceOf(
525
+ relation?: Type$.Relation<any, any, any, any> | string,
526
+ predicates?: Filter$.Props<unknown> | undefined,
527
+ ): Query$.Any {
419
528
  return new QueryClass({
420
529
  type: 'relation',
421
530
  anchor: this.ast,
422
531
  direction: 'outgoing',
423
- filter: FilterClass.type(relation, predicates).ast,
532
+ filter:
533
+ relation === undefined
534
+ ? undefined
535
+ : typeof relation === 'string'
536
+ ? FilterClass.type(relation, predicates).ast
537
+ : FilterClass.type(relation, predicates).ast,
424
538
  });
425
539
  }
426
540
 
427
- targetOf(relation: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
541
+ targetOf(
542
+ relation?: Type$.Relation<any, any, any, any> | string,
543
+ predicates?: Filter$.Props<unknown> | undefined,
544
+ ): Query$.Any {
428
545
  return new QueryClass({
429
546
  type: 'relation',
430
547
  anchor: this.ast,
431
548
  direction: 'incoming',
432
- filter: FilterClass.type(relation, predicates).ast,
549
+ filter:
550
+ relation === undefined
551
+ ? undefined
552
+ : typeof relation === 'string'
553
+ ? FilterClass.type(relation, predicates).ast
554
+ : FilterClass.type(relation, predicates).ast,
433
555
  });
434
556
  }
435
557
 
@@ -488,6 +610,21 @@ class QueryClass implements Query$.Any {
488
610
  options,
489
611
  });
490
612
  }
613
+
614
+ debugLabel(label: string): Query$.Any {
615
+ if (this.ast.type === 'options') {
616
+ return new QueryClass({
617
+ type: 'options',
618
+ query: this.ast.query,
619
+ options: { ...this.ast.options, debugLabel: label },
620
+ });
621
+ }
622
+ return new QueryClass({
623
+ type: 'options',
624
+ query: this.ast,
625
+ options: { debugLabel: label },
626
+ });
627
+ }
491
628
  }
492
629
 
493
630
  export const Query1: typeof Query$ = QueryClass;
@@ -498,8 +635,164 @@ const isRef = (obj: any): obj is Ref.Ref<any> => {
498
635
  return obj && typeof obj === 'object' && RefTypeId in obj;
499
636
  };
500
637
 
501
- const makeTypeDxn = (typename: string) => {
638
+ const makeTypeDxn = (typename: string): DXN.DXN => {
502
639
  assertArgument(typeof typename === 'string', 'typename');
503
640
  assertArgument(!typename.startsWith('dxn:'), 'typename');
504
- return `dxn:type:${typename}`;
641
+ // Inline template (rather than `DXN.make`) to keep the value-side `@dxos/keys` import out of this bundle.
642
+ return `dxn:${typename}` as DXN.DXN;
643
+ };
644
+
645
+ const isDxnLike = (value: unknown): value is DXN.DXN => {
646
+ if (typeof value === 'string') {
647
+ return value.startsWith('dxn:');
648
+ }
649
+ return (
650
+ typeof value === 'object' &&
651
+ value !== null &&
652
+ 'toString' in value &&
653
+ typeof value.toString === 'function' &&
654
+ value.toString().startsWith('dxn:')
655
+ );
656
+ };
657
+
658
+ const isEchoUriLike = (value: unknown): value is EchoURI.EchoURI => {
659
+ if (typeof value === 'string') {
660
+ return value.startsWith('echo:') || value.startsWith('dxn:echo:') || value.startsWith('dxn:queue:');
661
+ }
662
+ return (
663
+ typeof value === 'object' &&
664
+ value !== null &&
665
+ 'toString' in value &&
666
+ typeof value.toString === 'function' &&
667
+ isEchoUriLike(value.toString())
668
+ );
669
+ };
670
+
671
+ const SCOPE_KEYS = new Set(['spaceIds', 'feeds', 'allFeedsFromSpaces']);
672
+
673
+ const _isScopeLike = (value: unknown): value is QueryAST.Scope => {
674
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
675
+ return false;
676
+ }
677
+ return Object.keys(value).every((key) => SCOPE_KEYS.has(key));
678
+ };
679
+
680
+ const prettyFilter = (filter: QueryAST.Filter): string => {
681
+ switch (filter.type) {
682
+ case 'object': {
683
+ const parts: string[] = [];
684
+ if (filter.typename !== null) {
685
+ parts.push(JSON.stringify(filter.typename));
686
+ }
687
+ const propEntries = Object.entries(filter.props);
688
+ if (propEntries.length > 0) {
689
+ const propsStr = propEntries.map(([k, v]) => `${k}: ${prettyFilter(v)}`).join(', ');
690
+ parts.push(`{ ${propsStr} }`);
691
+ }
692
+ if (filter.id !== undefined) {
693
+ parts.push(`id: [${filter.id.join(', ')}]`);
694
+ }
695
+ return parts.length > 0 ? `Filter.type(${parts.join(', ')})` : 'Filter.everything()';
696
+ }
697
+ case 'compare':
698
+ return `Filter.${filter.operator}(${JSON.stringify(filter.value)})`;
699
+ case 'in':
700
+ return `Filter.in(${filter.values.map((v) => JSON.stringify(v)).join(', ')})`;
701
+ case 'contains':
702
+ return `Filter.contains(${JSON.stringify(filter.value)})`;
703
+ case 'range':
704
+ return `Filter.between(${JSON.stringify(filter.from)}, ${JSON.stringify(filter.to)})`;
705
+ case 'text-search':
706
+ return `Filter.text(${JSON.stringify(filter.text)})`;
707
+ case 'tag':
708
+ return `Filter.tag(${JSON.stringify(filter.tag)})`;
709
+ case 'child-of':
710
+ return `Filter.childOf([${filter.parents.map((parent) => JSON.stringify(parent)).join(', ')}], { transitive: ${filter.transitive} })`;
711
+ case 'timestamp':
712
+ return `Filter.${filter.field}.${filter.operator}(${filter.value})`;
713
+ case 'not':
714
+ return `Filter.not(${prettyFilter(filter.filter)})`;
715
+ case 'and':
716
+ return `Filter.and(${filter.filters.map(prettyFilter).join(', ')})`;
717
+ case 'or':
718
+ return `Filter.or(${filter.filters.map(prettyFilter).join(', ')})`;
719
+ }
720
+ };
721
+
722
+ const prettyQuery = (query: QueryAST.Query): string => {
723
+ switch (query.type) {
724
+ case 'select':
725
+ return `Query.select(${prettyFilter(query.filter)})`;
726
+ case 'filter':
727
+ return `${prettyQuery(query.selection)}.select(${prettyFilter(query.filter)})`;
728
+ case 'reference-traversal':
729
+ return `${prettyQuery(query.anchor)}.reference(${JSON.stringify(query.property)})`;
730
+ case 'incoming-references': {
731
+ const args: string[] = [];
732
+ if (query.typename !== null) {
733
+ args.push(JSON.stringify(query.typename));
734
+ }
735
+ if (query.property !== null) {
736
+ args.push(JSON.stringify(query.property));
737
+ }
738
+ return `${prettyQuery(query.anchor)}.referencedBy(${args.join(', ')})`;
739
+ }
740
+ case 'relation': {
741
+ const method =
742
+ query.direction === 'outgoing' ? 'sourceOf' : query.direction === 'incoming' ? 'targetOf' : 'relationOf';
743
+ const filterStr = query.filter !== undefined ? prettyFilter(query.filter) : '';
744
+ return `${prettyQuery(query.anchor)}.${method}(${filterStr})`;
745
+ }
746
+ case 'relation-traversal':
747
+ return `${prettyQuery(query.anchor)}.${query.direction}()`;
748
+ case 'hierarchy-traversal':
749
+ return query.direction === 'to-parent'
750
+ ? `${prettyQuery(query.anchor)}.parent()`
751
+ : `${prettyQuery(query.anchor)}.children()`;
752
+ case 'union':
753
+ return `Query.all(${query.queries.map(prettyQuery).join(', ')})`;
754
+ case 'set-difference':
755
+ return `Query.without(${prettyQuery(query.source)}, ${prettyQuery(query.exclude)})`;
756
+ case 'order': {
757
+ const orders = query.order.map((o) => {
758
+ if (o.kind === 'natural') {
759
+ return 'Order.natural';
760
+ }
761
+ if (o.kind === 'rank') {
762
+ return `Order.rank(${JSON.stringify(o.direction)})`;
763
+ }
764
+ return `Order.property(${JSON.stringify(o.property)}, ${JSON.stringify(o.direction)})`;
765
+ });
766
+ return `${prettyQuery(query.query)}.orderBy(${orders.join(', ')})`;
767
+ }
768
+ case 'options': {
769
+ const parts: string[] = [];
770
+ if (query.options.deleted !== undefined) {
771
+ parts.push(`deleted: ${JSON.stringify(query.options.deleted)}`);
772
+ }
773
+ if (query.options.debugLabel !== undefined) {
774
+ parts.push(`debugLabel: ${JSON.stringify(query.options.debugLabel)}`);
775
+ }
776
+ return `${prettyQuery(query.query)}.options({ ${parts.join(', ')} })`;
777
+ }
778
+ case 'from': {
779
+ if (query.from._tag === 'scope') {
780
+ const scope = query.from.scope;
781
+ const parts: string[] = [];
782
+ if (scope.spaceIds !== undefined) {
783
+ parts.push(`spaceIds: [${scope.spaceIds.join(', ')}]`);
784
+ }
785
+ if (scope.feeds !== undefined) {
786
+ parts.push(`feeds: [${scope.feeds.join(', ')}]`);
787
+ }
788
+ if (scope.allFeedsFromSpaces !== undefined) {
789
+ parts.push(`allFeedsFromSpaces: ${scope.allFeedsFromSpaces}`);
790
+ }
791
+ return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
792
+ }
793
+ return `${prettyQuery(query.query)}.from(${prettyQuery(query.from.query)})`;
794
+ }
795
+ case 'limit':
796
+ return `${prettyQuery(query.query)}.limit(${query.limit})`;
797
+ }
505
798
  };
@@ -16,37 +16,37 @@ describe('QuerySandbox', () => {
16
16
 
17
17
  test('works', { timeout: 10_000 }, async () => {
18
18
  const ast = sandbox.eval(trim`
19
- Query.select(Filter.typename('dxos.org/type/Person'))
19
+ Query.select(Filter.typename('org.dxos.type.person'))
20
20
  `);
21
- expect(ast).toEqual(Query.select(Filter.typename('dxos.org/type/Person')).ast);
21
+ expect(ast).toEqual(Query.select(Filter.typename('org.dxos.type.person')).ast);
22
22
  });
23
23
 
24
24
  test('works with just Filter passed in', () => {
25
25
  const ast = sandbox.eval(trim`
26
- Filter.typename('dxos.org/type/Person')
26
+ Filter.typename('org.dxos.type.person')
27
27
  `);
28
- expect(ast).toEqual(Query.select(Filter.typename('dxos.org/type/Person')).ast);
28
+ expect(ast).toEqual(Query.select(Filter.typename('org.dxos.type.person')).ast);
29
29
  });
30
30
 
31
31
  test('Order', () => {
32
32
  const ast = sandbox.eval(trim`
33
- Query.type('dxos.org/type/Person').orderBy(Order.property('name', 'desc'))
33
+ Query.type('org.dxos.type.person').orderBy(Order.property('name', 'desc'))
34
34
  `);
35
- expect(ast).toEqual(Query.type('dxos.org/type/Person').orderBy(Order.property('name', 'desc')).ast);
35
+ expect(ast).toEqual(Query.type('org.dxos.type.person').orderBy(Order.property('name', 'desc')).ast);
36
36
  });
37
37
 
38
38
  test('traversal', () => {
39
39
  const ast = sandbox.eval(trim`
40
- Query.select(Filter.type('dxos.org/type/Person', { jobTitle: 'investor' }))
40
+ Query.select(Filter.type('org.dxos.type.person', { jobTitle: 'investor' }))
41
41
  .reference('organization')
42
- .targetOf('dxos.org/relation/HasSubject')
42
+ .targetOf('org.dxos.relation.hasSubject')
43
43
  .source()
44
44
  `);
45
45
 
46
46
  expect(ast).toEqual(
47
- Query.select(Filter.type('dxos.org/type/Person', { jobTitle: 'investor' }))
47
+ Query.select(Filter.type('org.dxos.type.person', { jobTitle: 'investor' }))
48
48
  .reference('organization')
49
- .targetOf('dxos.org/relation/HasSubject')
49
+ .targetOf('org.dxos.relation.hasSubject')
50
50
  .source().ast,
51
51
  );
52
52
  });
@@ -49,7 +49,7 @@ export class QuerySandbox extends Resource {
49
49
 
50
50
  /**
51
51
  * Evaluates the query code.
52
- * @param queryCode Example: `Query.select(Filter.typename('dxos.org/type/Person'))`
52
+ * @param queryCode Example: `Query.select(Filter.typename('org.dxos.type.person'))`
53
53
  */
54
54
  eval(queryCode: string): QueryAST.Query {
55
55
  using context = this.#runtime.newContext();