@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/dist/query-lite/index.d.ts +1168 -848
- package/dist/query-lite/index.d.ts.map +1 -1
- package/dist/query-lite/index.js +47 -31
- package/dist/query-lite/index.js.map +1 -1
- package/dist/types/src/query-lite/query-lite.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -16
- package/src/query-lite/query-lite.ts +125 -62
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-query",
|
|
3
|
-
"version": "0.8.4-main.
|
|
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.
|
|
33
|
-
"@lezer/
|
|
34
|
-
"@
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
40
|
-
"@dxos/
|
|
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.
|
|
43
|
+
"@lezer/generator": "^1.8.0",
|
|
47
44
|
"tsdown": "^0.16.7",
|
|
48
45
|
"typescript": "^6.0.3",
|
|
49
|
-
"@dxos/
|
|
50
|
-
"@dxos/
|
|
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
|
-
|
|
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:
|
|
88
|
+
static id(...ids: EntityId[]): Filter$.Any {
|
|
87
89
|
// assertArgument(
|
|
88
|
-
// ids.every((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<
|
|
106
|
-
|
|
107
|
-
props?: Filter$.Props<
|
|
108
|
-
): Filter$.Filter<
|
|
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:
|
|
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:
|
|
126
|
+
typename: makeTypeDxn(typename),
|
|
123
127
|
props: {},
|
|
124
128
|
});
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
static
|
|
131
|
+
static typeURI(uri: URI.URI): Filter$.Any {
|
|
128
132
|
return new FilterClass({
|
|
129
133
|
type: 'object',
|
|
130
|
-
typename:
|
|
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
|
|
169
|
-
schema: S
|
|
172
|
+
static foreignKeys<S extends Type$.AnyEntity | string>(
|
|
173
|
+
schema: S,
|
|
170
174
|
keys: ForeignKey[],
|
|
171
|
-
): Filter$.Filter<
|
|
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:
|
|
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 |
|
|
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 (
|
|
268
|
-
return item.toString();
|
|
271
|
+
if (isEchoUriLike(item)) {
|
|
272
|
+
return item.toString() as EID.EID;
|
|
269
273
|
}
|
|
270
|
-
throw new TypeError('childOf requires
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
466
|
+
return (wrapper.from as (...args: any[]) => Query$.Any)(...args);
|
|
454
467
|
}
|
|
455
468
|
|
|
456
|
-
from(
|
|
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',
|
|
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?:
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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
|
|
643
|
+
const makeTypeDxn = (typename: string): DXN.DXN => {
|
|
612
644
|
assertArgument(typeof typename === 'string', 'typename');
|
|
613
645
|
assertArgument(!typename.startsWith('dxn:'), 'typename');
|
|
614
|
-
|
|
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
|
|
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 (
|
|
631
|
-
return
|
|
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
|
|
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
|
-
|
|
737
|
-
|
|
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
|
-
|
|
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
|
}
|