@dxos/echo-query 0.8.4-main.d05539e30a → 0.8.4-main.d9fc60f731

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-query",
3
- "version": "0.8.4-main.d05539e30a",
3
+ "version": "0.8.4-main.d9fc60f731",
4
4
  "description": "ECHO queries.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -29,25 +29,22 @@
29
29
  "src"
30
30
  ],
31
31
  "dependencies": {
32
- "@lezer/common": "^1.2.2",
33
- "@lezer/lezer": "^1.1.2",
34
- "@lezer/lr": "^1.4.2",
35
- "@dxos/context": "0.8.4-main.d05539e30a",
36
- "@dxos/debug": "0.8.4-main.d05539e30a",
37
- "@dxos/echo-protocol": "0.8.4-main.d05539e30a",
38
- "@dxos/errors": "0.8.4-main.d05539e30a",
39
- "@dxos/invariant": "0.8.4-main.d05539e30a",
40
- "@dxos/echo": "0.8.4-main.d05539e30a",
41
- "@dxos/node-std": "0.8.4-main.d05539e30a",
42
- "@dxos/util": "0.8.4-main.d05539e30a",
43
- "@dxos/vendor-quickjs": "0.8.4-main.d05539e30a"
32
+ "@lezer/common": "^1.5.2",
33
+ "@lezer/lr": "^1.4.10",
34
+ "@dxos/context": "0.8.4-main.d9fc60f731",
35
+ "@dxos/echo": "0.8.4-main.d9fc60f731",
36
+ "@dxos/echo-protocol": "0.8.4-main.d9fc60f731",
37
+ "@dxos/invariant": "0.8.4-main.d9fc60f731",
38
+ "@dxos/node-std": "0.8.4-main.d9fc60f731",
39
+ "@dxos/util": "0.8.4-main.d9fc60f731",
40
+ "@dxos/vendor-quickjs": "0.8.4-main.d9fc60f731"
44
41
  },
45
42
  "devDependencies": {
46
- "@lezer/generator": "^1.7.1",
43
+ "@lezer/generator": "^1.8.0",
47
44
  "tsdown": "^0.16.7",
48
45
  "typescript": "^6.0.3",
49
- "@dxos/echo-generator": "0.8.4-main.d05539e30a",
50
- "@dxos/random": "0.8.4-main.d05539e30a"
46
+ "@dxos/random": "0.8.4-main.d9fc60f731",
47
+ "@dxos/echo-generator": "0.8.4-main.d9fc60f731"
51
48
  },
52
49
  "publishConfig": {
53
50
  "access": "public"
@@ -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`/`EID` 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, EID, EntityId, URI } from '@dxos/keys';
11
13
 
12
14
  //
13
15
  // Light-weight implementation of query execution.
@@ -83,9 +85,9 @@ class FilterClass implements Filter$.Any {
83
85
  });
84
86
  }
85
87
 
86
- static id(...ids: ObjectId[]): Filter$.Any {
88
+ static id(...ids: EntityId[]): Filter$.Any {
87
89
  // assertArgument(
88
- // ids.every((id) => ObjectId.isValid(id)),
90
+ // ids.every((id) => EntityId.isValid(id)),
89
91
  // 'ids',
90
92
  // 'ids must be valid',
91
93
  // );
@@ -102,16 +104,18 @@ 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
  }
112
116
  return new FilterClass({
113
117
  type: 'object',
114
- typename: makeTypeDXN(schema),
118
+ typename: makeTypeDxn(schema),
115
119
  ...propsFilterToAst(props ?? {}),
116
120
  });
117
121
  }
@@ -119,15 +123,15 @@ class FilterClass implements Filter$.Any {
119
123
  static typename(typename: string): Filter$.Any {
120
124
  return new FilterClass({
121
125
  type: 'object',
122
- typename: makeTypeDXN(typename),
126
+ typename: makeTypeDxn(typename),
123
127
  props: {},
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
  }
@@ -165,15 +169,15 @@ class FilterClass implements Filter$.Any {
165
169
  });
166
170
  }
167
171
 
168
- static foreignKeys<S extends Schema.Schema.All>(
169
- schema: S | string,
172
+ static foreignKeys<S extends Type$.AnyEntity | string>(
173
+ schema: S,
170
174
  keys: ForeignKey[],
171
- ): Filter$.Filter<Schema.Schema.Type<S>> {
175
+ ): Filter$.Filter<S extends Type$.AnyEntity ? Type$.InstanceType<S> : unknown> {
172
176
  assertArgument(typeof schema === 'string', 'schema');
173
177
  assertArgument(!schema.startsWith('dxn:'), 'schema');
174
178
  return new FilterClass({
175
179
  type: 'object',
176
- typename: `dxn:type:${schema}`,
180
+ typename: makeTypeDxn(schema),
177
181
  props: {},
178
182
  foreignKeys: keys,
179
183
  });
@@ -261,13 +265,13 @@ class FilterClass implements Filter$.Any {
261
265
  return FilterClass._timeRangeFilter('createdAt', range);
262
266
  }
263
267
 
264
- static childOf(parents: unknown | DXN | (unknown | DXN)[], options?: { transitive?: boolean }): Filter$.Any {
268
+ static childOf(parents: unknown | unknown[], options?: { transitive?: boolean }): Filter$.Any {
265
269
  const items = Array.isArray(parents) ? parents : [parents];
266
270
  const dxns = items.map((item) => {
267
- if (isDxnLike(item)) {
268
- return item.toString();
271
+ if (isEchoUriLike(item)) {
272
+ return item.toString() as EID.EID;
269
273
  }
270
- throw new TypeError('childOf requires DXN values in query-lite');
274
+ throw new TypeError('childOf requires EID values in query-lite');
271
275
  });
272
276
  return new FilterClass({
273
277
  type: 'child-of',
@@ -339,7 +343,7 @@ export { Filter1 as Filter };
339
343
  type RefPropKey<T> = keyof T & string;
340
344
 
341
345
  const propsFilterToAst = (predicates: Filter$.Props<any>): Pick<QueryAST.FilterObject, 'id' | 'props'> => {
342
- let idFilter: readonly ObjectId[] | undefined;
346
+ let idFilter: readonly EntityId[] | undefined;
343
347
  if ('id' in predicates) {
344
348
  assertArgument(
345
349
  typeof predicates.id === 'string' || Array.isArray(predicates.id),
@@ -417,7 +421,16 @@ class QueryClass implements Query$.Any {
417
421
  }
418
422
  }
419
423
 
420
- static type(schema: Schema.Schema.All | string, predicates?: Filter$.Props<unknown>): Query$.Any {
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
+ }
421
434
  return new QueryClass({
422
435
  type: 'select',
423
436
  filter: FilterClass.type(schema, predicates).ast,
@@ -444,26 +457,31 @@ class QueryClass implements Query$.Any {
444
457
  });
445
458
  }
446
459
 
447
- static from(source: any, options?: { includeFeeds?: boolean }): Query$.Any {
460
+ static from(...args: any[]): Query$.Any {
448
461
  const baseQuery: QueryAST.Query = {
449
462
  type: 'select',
450
463
  filter: FilterClass.everything().ast,
451
464
  };
452
465
  const wrapper = new QueryClass(baseQuery);
453
- return wrapper.from(source, options);
466
+ return (wrapper.from as (...args: any[]) => Query$.Any)(...args);
454
467
  }
455
468
 
456
- from(arg: any, options?: { includeFeeds?: boolean }): Query$.Any {
469
+ from(...args: any[]): Query$.Any {
470
+ // Variadic raw scopes: `.from(Scope.space(), Scope.registry())`.
471
+ if (args.length > 1 && args.every((arg) => _isScopeLike(arg))) {
472
+ return new QueryClass({
473
+ type: 'from',
474
+ query: this.ast,
475
+ from: { _tag: 'scope', scopes: args as QueryAST.Scope[] },
476
+ });
477
+ }
478
+
479
+ const [arg] = args;
457
480
  if (arg === 'all-accessible-spaces') {
458
481
  return new QueryClass({
459
482
  type: 'from',
460
483
  query: this.ast,
461
- from: {
462
- _tag: 'scope',
463
- scope: {
464
- ...(options?.includeFeeds ? { allFeedsFromSpaces: true } : {}),
465
- },
466
- },
484
+ from: { _tag: 'scope', scopes: [] },
467
485
  });
468
486
  }
469
487
 
@@ -471,7 +489,7 @@ class QueryClass implements Query$.Any {
471
489
  return new QueryClass({
472
490
  type: 'from',
473
491
  query: this.ast,
474
- from: { _tag: 'scope', scope: arg },
492
+ from: { _tag: 'scope', scopes: Array.isArray(arg) ? arg : [arg] },
475
493
  });
476
494
  }
477
495
 
@@ -495,13 +513,11 @@ class QueryClass implements Query$.Any {
495
513
  });
496
514
  }
497
515
 
498
- referencedBy(target?: Schema.Schema.All | string, key?: string): Query$.Any {
499
- const typename =
500
- target !== undefined
501
- ? (assertArgument(typeof target === 'string', 'target'),
502
- assertArgument(!target.startsWith('dxn:'), 'target'),
503
- target)
504
- : null;
516
+ referencedBy(target?: Type$.AnyEntity | string, key?: string): Query$.Any {
517
+ if (target !== undefined && typeof target !== 'string') {
518
+ throw new TypeError('referencedBy requires a typename string in query-lite');
519
+ }
520
+ const typename = target !== undefined ? makeTypeDxn(target) : null;
505
521
  return new QueryClass({
506
522
  type: 'incoming-references',
507
523
  anchor: this.ast,
@@ -510,21 +526,37 @@ class QueryClass implements Query$.Any {
510
526
  });
511
527
  }
512
528
 
513
- sourceOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
529
+ sourceOf(
530
+ relation?: Type$.Relation<any, any, any, any> | string,
531
+ predicates?: Filter$.Props<unknown> | undefined,
532
+ ): Query$.Any {
514
533
  return new QueryClass({
515
534
  type: 'relation',
516
535
  anchor: this.ast,
517
536
  direction: 'outgoing',
518
- filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
537
+ filter:
538
+ relation === undefined
539
+ ? undefined
540
+ : typeof relation === 'string'
541
+ ? FilterClass.type(relation, predicates).ast
542
+ : FilterClass.type(relation, predicates).ast,
519
543
  });
520
544
  }
521
545
 
522
- targetOf(relation?: Schema.Schema.All | string, predicates?: Filter$.Props<unknown> | undefined): Query$.Any {
546
+ targetOf(
547
+ relation?: Type$.Relation<any, any, any, any> | string,
548
+ predicates?: Filter$.Props<unknown> | undefined,
549
+ ): Query$.Any {
523
550
  return new QueryClass({
524
551
  type: 'relation',
525
552
  anchor: this.ast,
526
553
  direction: 'incoming',
527
- filter: relation !== undefined ? FilterClass.type(relation, predicates).ast : undefined,
554
+ filter:
555
+ relation === undefined
556
+ ? undefined
557
+ : typeof relation === 'string'
558
+ ? FilterClass.type(relation, predicates).ast
559
+ : FilterClass.type(relation, predicates).ast,
528
560
  });
529
561
  }
530
562
 
@@ -608,13 +640,17 @@ const isRef = (obj: any): obj is Ref.Ref<any> => {
608
640
  return obj && typeof obj === 'object' && RefTypeId in obj;
609
641
  };
610
642
 
611
- const makeTypeDXN = (typename: string) => {
643
+ const makeTypeDxn = (typename: string): DXN.DXN => {
612
644
  assertArgument(typeof typename === 'string', 'typename');
613
645
  assertArgument(!typename.startsWith('dxn:'), 'typename');
614
- return `dxn:type:${typename}`;
646
+ // Inline template (rather than `DXN.make`) to keep the value-side `@dxos/keys` import out of this bundle.
647
+ return `dxn:${typename}` as DXN.DXN;
615
648
  };
616
649
 
617
- const isDxnLike = (value: unknown): value is DXN => {
650
+ const isDxnLike = (value: unknown): value is DXN.DXN => {
651
+ if (typeof value === 'string') {
652
+ return value.startsWith('dxn:');
653
+ }
618
654
  return (
619
655
  typeof value === 'object' &&
620
656
  value !== null &&
@@ -624,13 +660,37 @@ const isDxnLike = (value: unknown): value is DXN => {
624
660
  );
625
661
  };
626
662
 
627
- const SCOPE_KEYS = new Set(['spaceIds', 'feeds', 'allFeedsFromSpaces']);
663
+ const isEchoUriLike = (value: unknown): value is EID.EID => {
664
+ if (typeof value === 'string') {
665
+ return value.startsWith('echo:') || value.startsWith('dxn:echo:') || value.startsWith('dxn:queue:');
666
+ }
667
+ return (
668
+ typeof value === 'object' &&
669
+ value !== null &&
670
+ 'toString' in value &&
671
+ typeof value.toString === 'function' &&
672
+ isEchoUriLike(value.toString())
673
+ );
674
+ };
675
+
676
+ const SCOPE_TAGS = new Set(['space', 'feed', 'registry']);
628
677
 
629
- const _isScopeLike = (value: unknown): value is QueryAST.Scope => {
630
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
631
- return false;
678
+ const _isScopeLike = (value: unknown): value is QueryAST.Scope | QueryAST.Scope[] => {
679
+ if (Array.isArray(value)) {
680
+ return value.every((item) => _isSingleScopeLike(item));
632
681
  }
633
- return Object.keys(value).every((key) => SCOPE_KEYS.has(key));
682
+ return _isSingleScopeLike(value);
683
+ };
684
+
685
+ const _isSingleScopeLike = (value: unknown): value is QueryAST.Scope => {
686
+ return (
687
+ typeof value === 'object' &&
688
+ value !== null &&
689
+ !Array.isArray(value) &&
690
+ '_tag' in value &&
691
+ typeof value._tag === 'string' &&
692
+ SCOPE_TAGS.has(value._tag)
693
+ );
634
694
  };
635
695
 
636
696
  const prettyFilter = (filter: QueryAST.Filter): string => {
@@ -733,18 +793,21 @@ const prettyQuery = (query: QueryAST.Query): string => {
733
793
  }
734
794
  case 'from': {
735
795
  if (query.from._tag === 'scope') {
736
- const scope = query.from.scope;
737
- const parts: string[] = [];
738
- if (scope.spaceIds !== undefined) {
739
- parts.push(`spaceIds: [${scope.spaceIds.join(', ')}]`);
740
- }
741
- if (scope.feeds !== undefined) {
742
- parts.push(`feeds: [${scope.feeds.join(', ')}]`);
743
- }
744
- if (scope.allFeedsFromSpaces !== undefined) {
745
- parts.push(`allFeedsFromSpaces: ${scope.allFeedsFromSpaces}`);
796
+ if (query.from.scopes.length === 0) {
797
+ return `${prettyQuery(query.query)}.from('all-accessible-spaces')`;
746
798
  }
747
- return `${prettyQuery(query.query)}.from({ ${parts.join(', ')} })`;
799
+ const scopeStrs = query.from.scopes.map((scope) => {
800
+ if (scope._tag === 'space') {
801
+ return scope.includeAllFeeds
802
+ ? `{ space: ${JSON.stringify(scope.spaceId)}, includeAllFeeds: true }`
803
+ : `{ space: ${JSON.stringify(scope.spaceId)} }`;
804
+ }
805
+ if (scope._tag === 'feed') {
806
+ return `{ feed: ${String(scope.feedUri)} }`;
807
+ }
808
+ return `{ registry: ${JSON.stringify(scope.location)} }`;
809
+ });
810
+ return `${prettyQuery(query.query)}.from([${scopeStrs.join(', ')}])`;
748
811
  }
749
812
  return `${prettyQuery(query.query)}.from(${prettyQuery(query.from.query)})`;
750
813
  }