@dxos/echo-protocol 0.8.3 → 0.8.4-main.05e74ebcff
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/LICENSE +102 -5
- package/README.md +1 -1
- package/dist/lib/neutral/index.mjs +825 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/document-structure.d.ts +26 -2
- package/dist/types/src/document-structure.d.ts.map +1 -1
- package/dist/types/src/echo-feed-codec.d.ts +17 -0
- package/dist/types/src/echo-feed-codec.d.ts.map +1 -0
- package/dist/types/src/foreign-key.d.ts +1 -1
- package/dist/types/src/foreign-key.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +238 -25
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/src/reference.d.ts +17 -0
- package/dist/types/src/reference.d.ts.map +1 -1
- package/dist/types/src/space-id.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -15
- package/src/document-structure.ts +36 -2
- package/src/echo-feed-codec.ts +67 -0
- package/src/foreign-key.ts +4 -3
- package/src/index.ts +4 -3
- package/src/query/ast.ts +326 -34
- package/src/reference.ts +29 -0
- package/src/space-id.ts +1 -1
- package/dist/lib/browser/index.mjs +0 -509
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node/index.cjs +0 -527
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -510
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/src/query/ast.ts
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as Match from 'effect/Match';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import { DXN, ObjectId } from '@dxos/keys';
|
|
8
9
|
|
|
9
10
|
import { ForeignKey } from '../foreign-key';
|
|
10
11
|
|
|
11
12
|
const TypenameSpecifier = Schema.Union(DXN.Schema, Schema.Null).annotations({
|
|
12
|
-
description: 'DXN or null
|
|
13
|
+
description: 'DXN or null; null matches any type',
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
// NOTE: This pattern with 3 definitions per schema is need to make the types opaque, and circular references in AST to not cause compiler errors.
|
|
@@ -41,11 +42,25 @@ const FilterObject_ = Schema.Struct({
|
|
|
41
42
|
*/
|
|
42
43
|
foreignKeys: Schema.optional(Schema.Array(ForeignKey)),
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Match objects whose meta `key` equals this fully-qualified registry key (FQN format).
|
|
47
|
+
*/
|
|
48
|
+
metaKey: Schema.optional(Schema.String),
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Semver range matched against the object's meta `version`.
|
|
52
|
+
* Only consulted when {@link metaKey} is set. Objects with no `version` do not satisfy a version-constrained filter.
|
|
53
|
+
*/
|
|
54
|
+
metaVersion: Schema.optional(Schema.String),
|
|
55
|
+
|
|
44
56
|
// NOTE: Make sure to update `FilterStep.isNoop` if you change this.
|
|
45
57
|
});
|
|
46
58
|
export interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
|
|
47
59
|
export const FilterObject: Schema.Schema<FilterObject> = FilterObject_;
|
|
48
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Compare.
|
|
63
|
+
*/
|
|
49
64
|
const FilterCompare_ = Schema.Struct({
|
|
50
65
|
type: Schema.Literal('compare'),
|
|
51
66
|
operator: Schema.Literal('eq', 'neq', 'gt', 'gte', 'lt', 'lte'),
|
|
@@ -54,6 +69,9 @@ const FilterCompare_ = Schema.Struct({
|
|
|
54
69
|
export interface FilterCompare extends Schema.Schema.Type<typeof FilterCompare_> {}
|
|
55
70
|
export const FilterCompare: Schema.Schema<FilterCompare> = FilterCompare_;
|
|
56
71
|
|
|
72
|
+
/**
|
|
73
|
+
* In.
|
|
74
|
+
*/
|
|
57
75
|
const FilterIn_ = Schema.Struct({
|
|
58
76
|
type: Schema.Literal('in'),
|
|
59
77
|
values: Schema.Array(Schema.Any),
|
|
@@ -61,53 +79,137 @@ const FilterIn_ = Schema.Struct({
|
|
|
61
79
|
export interface FilterIn extends Schema.Schema.Type<typeof FilterIn_> {}
|
|
62
80
|
export const FilterIn: Schema.Schema<FilterIn> = FilterIn_;
|
|
63
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Contains.
|
|
84
|
+
*/
|
|
85
|
+
const FilterContains_ = Schema.Struct({
|
|
86
|
+
type: Schema.Literal('contains'),
|
|
87
|
+
value: Schema.Any,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Predicate for an array property to contain the provided value.
|
|
94
|
+
* Nested objects are matched using strict structural matching.
|
|
95
|
+
*/
|
|
96
|
+
export const FilterContains: Schema.Schema<FilterContains> = FilterContains_;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Filters objects that have certain tag.
|
|
100
|
+
*/
|
|
101
|
+
const FilterTag_ = Schema.Struct({
|
|
102
|
+
type: Schema.Literal('tag'),
|
|
103
|
+
tag: Schema.String, // TODO(burdon): Make OR-collection?
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
|
|
107
|
+
export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Range.
|
|
111
|
+
*/
|
|
64
112
|
const FilterRange_ = Schema.Struct({
|
|
65
113
|
type: Schema.Literal('range'),
|
|
66
114
|
from: Schema.Any,
|
|
67
115
|
to: Schema.Any,
|
|
68
116
|
});
|
|
117
|
+
|
|
69
118
|
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
70
119
|
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
71
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Filter by system timestamp (createdAt / updatedAt).
|
|
123
|
+
* Timestamps are unix milliseconds stored in the object meta index.
|
|
124
|
+
*/
|
|
125
|
+
const FilterTimestamp_ = Schema.Struct({
|
|
126
|
+
type: Schema.Literal('timestamp'),
|
|
127
|
+
field: Schema.Literal('createdAt', 'updatedAt'),
|
|
128
|
+
operator: Schema.Literal('gt', 'gte', 'lt', 'lte'),
|
|
129
|
+
value: Schema.Number,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
export interface FilterTimestamp extends Schema.Schema.Type<typeof FilterTimestamp_> {}
|
|
133
|
+
export const FilterTimestamp: Schema.Schema<FilterTimestamp> = FilterTimestamp_;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Text search.
|
|
137
|
+
*/
|
|
72
138
|
const FilterTextSearch_ = Schema.Struct({
|
|
73
139
|
type: Schema.Literal('text-search'),
|
|
74
140
|
text: Schema.String,
|
|
75
141
|
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
76
142
|
});
|
|
143
|
+
|
|
77
144
|
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
78
145
|
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
79
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Not.
|
|
149
|
+
*/
|
|
80
150
|
const FilterNot_ = Schema.Struct({
|
|
81
151
|
type: Schema.Literal('not'),
|
|
82
152
|
filter: Schema.suspend(() => Filter),
|
|
83
153
|
});
|
|
154
|
+
|
|
84
155
|
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
85
156
|
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
86
157
|
|
|
158
|
+
/**
|
|
159
|
+
* And.
|
|
160
|
+
*/
|
|
87
161
|
const FilterAnd_ = Schema.Struct({
|
|
88
162
|
type: Schema.Literal('and'),
|
|
89
163
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
90
164
|
});
|
|
165
|
+
|
|
91
166
|
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
92
167
|
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
93
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Or.
|
|
171
|
+
*/
|
|
94
172
|
const FilterOr_ = Schema.Struct({
|
|
95
173
|
type: Schema.Literal('or'),
|
|
96
174
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
97
175
|
});
|
|
176
|
+
|
|
98
177
|
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
99
178
|
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
100
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Filter objects that are children of the specified parents.
|
|
182
|
+
* With transitive=true (default), matches grandchildren and beyond.
|
|
183
|
+
*/
|
|
184
|
+
const FilterChildOf_ = Schema.Struct({
|
|
185
|
+
type: Schema.Literal('child-of'),
|
|
186
|
+
/** Parent DXNs to match children of. */
|
|
187
|
+
parents: Schema.Array(DXN.Schema),
|
|
188
|
+
/** Whether to match transitively (grandchildren, etc.). Defaults to true. */
|
|
189
|
+
transitive: Schema.Boolean,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
export interface FilterChildOf extends Schema.Schema.Type<typeof FilterChildOf_> {}
|
|
193
|
+
export const FilterChildOf: Schema.Schema<FilterChildOf> = FilterChildOf_;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Union of filters.
|
|
197
|
+
*/
|
|
101
198
|
export const Filter = Schema.Union(
|
|
102
199
|
FilterObject,
|
|
103
|
-
FilterTextSearch,
|
|
104
200
|
FilterCompare,
|
|
105
201
|
FilterIn,
|
|
202
|
+
FilterContains,
|
|
203
|
+
FilterTag,
|
|
106
204
|
FilterRange,
|
|
205
|
+
FilterTimestamp,
|
|
206
|
+
FilterTextSearch,
|
|
207
|
+
FilterChildOf,
|
|
107
208
|
FilterNot,
|
|
108
209
|
FilterAnd,
|
|
109
210
|
FilterOr,
|
|
110
|
-
);
|
|
211
|
+
).annotations({ identifier: 'org.dxos.schema.filter' });
|
|
212
|
+
|
|
111
213
|
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
112
214
|
|
|
113
215
|
/**
|
|
@@ -117,6 +219,7 @@ const QuerySelectClause_ = Schema.Struct({
|
|
|
117
219
|
type: Schema.Literal('select'),
|
|
118
220
|
filter: Schema.suspend(() => Filter),
|
|
119
221
|
});
|
|
222
|
+
|
|
120
223
|
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
121
224
|
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
122
225
|
|
|
@@ -128,6 +231,7 @@ const QueryFilterClause_ = Schema.Struct({
|
|
|
128
231
|
selection: Schema.suspend(() => Query),
|
|
129
232
|
filter: Schema.suspend(() => Filter),
|
|
130
233
|
});
|
|
234
|
+
|
|
131
235
|
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
132
236
|
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
133
237
|
|
|
@@ -139,6 +243,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
|
139
243
|
anchor: Schema.suspend(() => Query),
|
|
140
244
|
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
141
245
|
});
|
|
246
|
+
|
|
142
247
|
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
143
248
|
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
144
249
|
QueryReferenceTraversalClause_;
|
|
@@ -149,9 +254,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
|
|
|
149
254
|
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
150
255
|
type: Schema.Literal('incoming-references'),
|
|
151
256
|
anchor: Schema.suspend(() => Query),
|
|
152
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Property path where the reference is located.
|
|
259
|
+
* If null, matches references from any property.
|
|
260
|
+
*/
|
|
261
|
+
property: Schema.NullOr(Schema.String),
|
|
153
262
|
typename: TypenameSpecifier,
|
|
154
263
|
});
|
|
264
|
+
|
|
155
265
|
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
156
266
|
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
157
267
|
QueryIncomingReferencesClause_;
|
|
@@ -170,6 +280,7 @@ const QueryRelationClause_ = Schema.Struct({
|
|
|
170
280
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
171
281
|
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
172
282
|
});
|
|
283
|
+
|
|
173
284
|
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
174
285
|
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
175
286
|
|
|
@@ -181,9 +292,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
|
|
|
181
292
|
anchor: Schema.suspend(() => Query),
|
|
182
293
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
183
294
|
});
|
|
295
|
+
|
|
184
296
|
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
185
297
|
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
186
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Traverse parent-child hierarchy.
|
|
301
|
+
*/
|
|
302
|
+
const QueryHierarchyTraversalClause_ = Schema.Struct({
|
|
303
|
+
type: Schema.Literal('hierarchy-traversal'),
|
|
304
|
+
anchor: Schema.suspend(() => Query),
|
|
305
|
+
/**
|
|
306
|
+
* to-parent: traverse from child to parent.
|
|
307
|
+
* to-children: traverse from parent to children.
|
|
308
|
+
*/
|
|
309
|
+
direction: Schema.Literal('to-parent', 'to-children'),
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
export interface QueryHierarchyTraversalClause extends Schema.Schema.Type<typeof QueryHierarchyTraversalClause_> {}
|
|
313
|
+
export const QueryHierarchyTraversalClause: Schema.Schema<QueryHierarchyTraversalClause> =
|
|
314
|
+
QueryHierarchyTraversalClause_;
|
|
315
|
+
|
|
187
316
|
/**
|
|
188
317
|
* Union of multiple queries.
|
|
189
318
|
*/
|
|
@@ -191,6 +320,7 @@ const QueryUnionClause_ = Schema.Struct({
|
|
|
191
320
|
type: Schema.Literal('union'),
|
|
192
321
|
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
193
322
|
});
|
|
323
|
+
|
|
194
324
|
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
195
325
|
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
196
326
|
|
|
@@ -202,9 +332,47 @@ const QuerySetDifferenceClause_ = Schema.Struct({
|
|
|
202
332
|
source: Schema.suspend(() => Query),
|
|
203
333
|
exclude: Schema.suspend(() => Query),
|
|
204
334
|
});
|
|
335
|
+
|
|
205
336
|
export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
|
|
206
337
|
export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
|
|
207
338
|
|
|
339
|
+
export const OrderDirection = Schema.Literal('asc', 'desc');
|
|
340
|
+
export type OrderDirection = Schema.Schema.Type<typeof OrderDirection>;
|
|
341
|
+
|
|
342
|
+
const Order_ = Schema.Union(
|
|
343
|
+
Schema.Struct({
|
|
344
|
+
// How database wants to order them (in practice - by id).
|
|
345
|
+
kind: Schema.Literal('natural'),
|
|
346
|
+
}),
|
|
347
|
+
Schema.Struct({
|
|
348
|
+
kind: Schema.Literal('property'),
|
|
349
|
+
property: Schema.String,
|
|
350
|
+
direction: OrderDirection,
|
|
351
|
+
}),
|
|
352
|
+
Schema.Struct({
|
|
353
|
+
// Order by relevance rank (for FTS/vector search results).
|
|
354
|
+
// Default direction is 'desc' (higher rank = better match first).
|
|
355
|
+
kind: Schema.Literal('rank'),
|
|
356
|
+
direction: OrderDirection,
|
|
357
|
+
}),
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
export type Order = Schema.Schema.Type<typeof Order_>;
|
|
361
|
+
export const Order: Schema.Schema<Order> = Order_;
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Order the query results.
|
|
365
|
+
* Left-to-right the orders dominate.
|
|
366
|
+
*/
|
|
367
|
+
const QueryOrderClause_ = Schema.Struct({
|
|
368
|
+
type: Schema.Literal('order'),
|
|
369
|
+
query: Schema.suspend(() => Query),
|
|
370
|
+
order: Schema.Array(Order),
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
|
|
374
|
+
export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
|
|
375
|
+
|
|
208
376
|
/**
|
|
209
377
|
* Add options to a query.
|
|
210
378
|
*/
|
|
@@ -213,9 +381,37 @@ const QueryOptionsClause_ = Schema.Struct({
|
|
|
213
381
|
query: Schema.suspend(() => Query),
|
|
214
382
|
options: Schema.suspend(() => QueryOptions),
|
|
215
383
|
});
|
|
384
|
+
|
|
216
385
|
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
217
386
|
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
218
387
|
|
|
388
|
+
/**
|
|
389
|
+
* Limit the number of results.
|
|
390
|
+
*/
|
|
391
|
+
const QueryLimitClause_ = Schema.Struct({
|
|
392
|
+
type: Schema.Literal('limit'),
|
|
393
|
+
query: Schema.suspend(() => Query),
|
|
394
|
+
limit: Schema.Number,
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
export interface QueryLimitClause extends Schema.Schema.Type<typeof QueryLimitClause_> {}
|
|
398
|
+
export const QueryLimitClause: Schema.Schema<QueryLimitClause> = QueryLimitClause_;
|
|
399
|
+
|
|
400
|
+
export const QueryFromClause_ = Schema.Struct({
|
|
401
|
+
type: Schema.Literal('from'),
|
|
402
|
+
query: Schema.suspend(() => Query),
|
|
403
|
+
from: Schema.Union(
|
|
404
|
+
Schema.TaggedStruct('scope', {
|
|
405
|
+
scope: Schema.suspend(() => Scope),
|
|
406
|
+
}),
|
|
407
|
+
Schema.TaggedStruct('query', {
|
|
408
|
+
query: Schema.suspend(() => Query),
|
|
409
|
+
}),
|
|
410
|
+
),
|
|
411
|
+
});
|
|
412
|
+
export interface QueryFromClause extends Schema.Schema.Type<typeof QueryFromClause_> {}
|
|
413
|
+
export const QueryFromClause: Schema.Schema<QueryFromClause> = QueryFromClause_;
|
|
414
|
+
|
|
219
415
|
const Query_ = Schema.Union(
|
|
220
416
|
QuerySelectClause,
|
|
221
417
|
QueryFilterClause,
|
|
@@ -223,46 +419,142 @@ const Query_ = Schema.Union(
|
|
|
223
419
|
QueryIncomingReferencesClause,
|
|
224
420
|
QueryRelationClause,
|
|
225
421
|
QueryRelationTraversalClause,
|
|
422
|
+
QueryHierarchyTraversalClause,
|
|
226
423
|
QueryUnionClause,
|
|
227
424
|
QuerySetDifferenceClause,
|
|
425
|
+
QueryOrderClause,
|
|
228
426
|
QueryOptionsClause,
|
|
229
|
-
|
|
427
|
+
QueryLimitClause,
|
|
428
|
+
QueryFromClause,
|
|
429
|
+
).annotations({ identifier: 'org.dxos.schema.query' });
|
|
230
430
|
|
|
231
431
|
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
232
432
|
export const Query: Schema.Schema<Query> = Query_;
|
|
233
433
|
|
|
234
434
|
export const QueryOptions = Schema.Struct({
|
|
235
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Nested select statements will use this option to filter deleted objects.
|
|
437
|
+
*/
|
|
236
438
|
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Diagnostics-only label for logs / tooling (not used by execution semantics).
|
|
442
|
+
*/
|
|
443
|
+
debugLabel: Schema.optional(Schema.String),
|
|
237
444
|
});
|
|
445
|
+
|
|
238
446
|
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
239
447
|
|
|
448
|
+
/**
|
|
449
|
+
* Specifies the scope of the data to query from.
|
|
450
|
+
*/
|
|
451
|
+
export const Scope = Schema.Struct({
|
|
452
|
+
/**
|
|
453
|
+
* The nested select statemets will select from the given spaces.
|
|
454
|
+
*
|
|
455
|
+
* NOTE: Spaces and feeds are unioned together if both are specified.
|
|
456
|
+
*/
|
|
457
|
+
spaceIds: Schema.optional(Schema.Array(Schema.String)),
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* If true, the nested select statements will select from all feeds in the spaces specified by `spaceIds`.
|
|
461
|
+
*/
|
|
462
|
+
allFeedsFromSpaces: Schema.optional(Schema.Boolean),
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* The nested select statemets will select from the given feeds (by underlying queue DXN).
|
|
466
|
+
*
|
|
467
|
+
* NOTE: Spaces and feeds are unioned together if both are specified.
|
|
468
|
+
*/
|
|
469
|
+
feeds: Schema.optional(Schema.Array(DXN.Schema)),
|
|
470
|
+
});
|
|
471
|
+
export interface Scope extends Schema.Schema.Type<typeof Scope> {}
|
|
472
|
+
|
|
240
473
|
export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
474
|
+
visitor(query);
|
|
475
|
+
|
|
476
|
+
Match.value(query).pipe(
|
|
477
|
+
Match.when({ type: 'filter' }, ({ selection }) => visit(selection, visitor)),
|
|
478
|
+
Match.when({ type: 'reference-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
479
|
+
Match.when({ type: 'incoming-references' }, ({ anchor }) => visit(anchor, visitor)),
|
|
480
|
+
Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
|
|
481
|
+
Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
|
|
482
|
+
Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
483
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
484
|
+
Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
|
|
485
|
+
Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
|
|
486
|
+
visit(source, visitor);
|
|
487
|
+
visit(exclude, visitor);
|
|
488
|
+
}),
|
|
489
|
+
Match.when({ type: 'order' }, ({ query }) => visit(query, visitor)),
|
|
490
|
+
Match.when({ type: 'limit' }, ({ query }) => visit(query, visitor)),
|
|
491
|
+
Match.when({ type: 'from' }, (node) => {
|
|
492
|
+
visit(node.query, visitor);
|
|
493
|
+
if (node.from._tag === 'query') {
|
|
494
|
+
visit(node.from.query, visitor);
|
|
495
|
+
}
|
|
496
|
+
}),
|
|
497
|
+
Match.when({ type: 'select' }, () => {}),
|
|
498
|
+
Match.exhaustive,
|
|
499
|
+
);
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Recursively transforms a query tree bottom-up.
|
|
504
|
+
* The mapper receives each node with its children already transformed.
|
|
505
|
+
*/
|
|
506
|
+
export const map = (query: Query, mapper: (node: Query) => Query): Query => {
|
|
507
|
+
const mapped: Query = Match.value(query).pipe(
|
|
508
|
+
Match.when({ type: 'filter' }, (node) => ({ ...node, selection: map(node.selection, mapper) })),
|
|
509
|
+
Match.when({ type: 'reference-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
510
|
+
Match.when({ type: 'incoming-references' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
511
|
+
Match.when({ type: 'relation' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
512
|
+
Match.when({ type: 'relation-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
513
|
+
Match.when({ type: 'hierarchy-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
514
|
+
Match.when({ type: 'options' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
515
|
+
Match.when({ type: 'order' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
516
|
+
Match.when({ type: 'limit' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
517
|
+
Match.when({ type: 'from' }, (node) => ({
|
|
518
|
+
...node,
|
|
519
|
+
query: map(node.query, mapper),
|
|
520
|
+
...(node.from._tag === 'query' ? { from: { _tag: 'query' as const, query: map(node.from.query, mapper) } } : {}),
|
|
521
|
+
})),
|
|
522
|
+
Match.when({ type: 'union' }, (node) => ({ ...node, queries: node.queries.map((q) => map(q, mapper)) })),
|
|
523
|
+
Match.when({ type: 'set-difference' }, (node) => ({
|
|
524
|
+
...node,
|
|
525
|
+
source: map(node.source, mapper),
|
|
526
|
+
exclude: map(node.exclude, mapper),
|
|
527
|
+
})),
|
|
528
|
+
Match.when({ type: 'select' }, (node) => node),
|
|
529
|
+
Match.exhaustive,
|
|
530
|
+
);
|
|
531
|
+
return mapper(mapped);
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
535
|
+
return Match.value(query).pipe(
|
|
536
|
+
Match.withReturnType<T[]>(),
|
|
537
|
+
Match.when({ type: 'filter' }, ({ selection }) => fold(selection, reducer)),
|
|
538
|
+
Match.when({ type: 'reference-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
539
|
+
Match.when({ type: 'incoming-references' }, ({ anchor }) => fold(anchor, reducer)),
|
|
540
|
+
Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
|
|
541
|
+
Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
|
|
542
|
+
Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
543
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
544
|
+
Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
|
|
545
|
+
Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
|
|
546
|
+
fold(source, reducer).concat(fold(exclude, reducer)),
|
|
547
|
+
),
|
|
548
|
+
Match.when({ type: 'order' }, ({ query }) => fold(query, reducer)),
|
|
549
|
+
Match.when({ type: 'limit' }, ({ query }) => fold(query, reducer)),
|
|
550
|
+
Match.when({ type: 'from' }, (node) => {
|
|
551
|
+
const results = fold(node.query, reducer);
|
|
552
|
+
if (node.from._tag === 'query') {
|
|
553
|
+
return results.concat(fold(node.from.query, reducer));
|
|
554
|
+
}
|
|
555
|
+
return results;
|
|
556
|
+
}),
|
|
557
|
+
Match.when({ type: 'select' }, () => []),
|
|
558
|
+
Match.exhaustive,
|
|
559
|
+
);
|
|
268
560
|
};
|
package/src/reference.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { assertArgument } from '@dxos/invariant';
|
|
5
6
|
import { DXN, LOCAL_SPACE_TAG, type PublicKey } from '@dxos/keys';
|
|
6
7
|
import { type ObjectId } from '@dxos/protocols';
|
|
7
8
|
import { type Reference as ReferenceProto } from '@dxos/protocols/proto/dxos/echo/model/document';
|
|
@@ -9,6 +10,7 @@ import { type Reference as ReferenceProto } from '@dxos/protocols/proto/dxos/ech
|
|
|
9
10
|
/**
|
|
10
11
|
* Runtime representation of an reference in ECHO.
|
|
11
12
|
* Implemented as a DXN, but we might extend it to other URIs in the future.
|
|
13
|
+
* @deprecated Use `EncodedReference` instead.
|
|
12
14
|
*/
|
|
13
15
|
export class Reference {
|
|
14
16
|
/**
|
|
@@ -122,6 +124,7 @@ export class Reference {
|
|
|
122
124
|
}
|
|
123
125
|
}
|
|
124
126
|
|
|
127
|
+
// TODO(dmaretskyi): Is this used anywhere?
|
|
125
128
|
export const REFERENCE_TYPE_TAG = 'dxos.echo.model.document.Reference';
|
|
126
129
|
|
|
127
130
|
/**
|
|
@@ -131,10 +134,16 @@ export type EncodedReference = {
|
|
|
131
134
|
'/': string;
|
|
132
135
|
};
|
|
133
136
|
|
|
137
|
+
/**
|
|
138
|
+
* @deprecated Use `EncodedReference.fromDXN` instead.
|
|
139
|
+
*/
|
|
134
140
|
export const encodeReference = (reference: Reference): EncodedReference => ({
|
|
135
141
|
'/': reference.toDXN().toString(),
|
|
136
142
|
});
|
|
137
143
|
|
|
144
|
+
/**
|
|
145
|
+
* @deprecated Use `EncodedReference.toDXN` instead.
|
|
146
|
+
*/
|
|
138
147
|
export const decodeReference = (value: any) => {
|
|
139
148
|
if (typeof value !== 'object' || value === null || typeof value['/'] !== 'string') {
|
|
140
149
|
throw new Error('Invalid reference');
|
|
@@ -152,5 +161,25 @@ export const decodeReference = (value: any) => {
|
|
|
152
161
|
return Reference.fromDXN(DXN.parse(dxnString));
|
|
153
162
|
};
|
|
154
163
|
|
|
164
|
+
/**
|
|
165
|
+
* @deprecated Use `EncodedReference.isEncodedReference` instead.
|
|
166
|
+
*/
|
|
155
167
|
export const isEncodedReference = (value: any): value is EncodedReference =>
|
|
156
168
|
typeof value === 'object' && value !== null && Object.keys(value).length === 1 && typeof value['/'] === 'string';
|
|
169
|
+
|
|
170
|
+
export const EncodedReference = Object.freeze({
|
|
171
|
+
isEncodedReference,
|
|
172
|
+
getReferenceString: (value: EncodedReference): string => {
|
|
173
|
+
assertArgument(isEncodedReference(value), 'value', 'invalid reference');
|
|
174
|
+
return value['/'];
|
|
175
|
+
},
|
|
176
|
+
toDXN: (value: EncodedReference): DXN => {
|
|
177
|
+
return DXN.parse(EncodedReference.getReferenceString(value));
|
|
178
|
+
},
|
|
179
|
+
fromDXN: (dxn: DXN): EncodedReference => {
|
|
180
|
+
return { '/': dxn.toString() };
|
|
181
|
+
},
|
|
182
|
+
fromLegacyTypename: (typename: string): EncodedReference => {
|
|
183
|
+
return { '/': DXN.fromTypename(typename).toString() };
|
|
184
|
+
},
|
|
185
|
+
});
|
package/src/space-id.ts
CHANGED
|
@@ -18,7 +18,7 @@ export const createIdFromSpaceKey = async (spaceKey: PublicKey): Promise<SpaceId
|
|
|
18
18
|
return cachedValue;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const digest = await subtleCrypto.digest('SHA-256', spaceKey.asUint8Array());
|
|
21
|
+
const digest = await subtleCrypto.digest('SHA-256', spaceKey.asUint8Array() as Uint8Array<ArrayBuffer>);
|
|
22
22
|
|
|
23
23
|
const bytes = new Uint8Array(digest).slice(0, SpaceId.byteLength);
|
|
24
24
|
const spaceId = SpaceId.encode(bytes);
|