@dxos/echo-protocol 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8
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 +721 -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 +85 -30
- 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/edge-peer.d.ts +11 -0
- package/dist/types/src/edge-peer.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 +5 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +290 -28
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/src/reference.d.ts +11 -64
- 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 +14 -15
- package/src/document-structure.ts +106 -33
- package/src/echo-feed-codec.ts +67 -0
- package/src/edge-peer.ts +27 -0
- package/src/foreign-key.ts +4 -3
- package/src/index.ts +5 -3
- package/src/query/ast.ts +353 -38
- package/src/reference.ts +13 -158
- package/dist/lib/browser/index.mjs +0 -526
- 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 -527
- 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,15 +2,17 @@
|
|
|
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
|
-
import {
|
|
8
|
+
import { EID, EntityId, URI } from '@dxos/keys';
|
|
8
9
|
|
|
9
10
|
import { ForeignKey } from '../foreign-key';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
// Type identifier URI — either a DXN (typename) or an EID (stored-schema-as-object).
|
|
13
|
+
// Matches the URI written into an object's `system.type` (see `getSchemaURI`). Null
|
|
14
|
+
// matches any type.
|
|
15
|
+
const TypenameSpecifier = Schema.Union(URI.Schema, Schema.Null);
|
|
14
16
|
|
|
15
17
|
// 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.
|
|
16
18
|
|
|
@@ -25,7 +27,7 @@ const FilterObject_ = Schema.Struct({
|
|
|
25
27
|
|
|
26
28
|
typename: TypenameSpecifier,
|
|
27
29
|
|
|
28
|
-
id: Schema.optional(Schema.Array(
|
|
30
|
+
id: Schema.optional(Schema.Array(EntityId)),
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Filter by property.
|
|
@@ -41,11 +43,25 @@ const FilterObject_ = Schema.Struct({
|
|
|
41
43
|
*/
|
|
42
44
|
foreignKeys: Schema.optional(Schema.Array(ForeignKey)),
|
|
43
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Match objects whose meta `key` equals this fully-qualified registry key (FQN format).
|
|
48
|
+
*/
|
|
49
|
+
metaKey: Schema.optional(Schema.String),
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Semver range matched against the object's meta `version`.
|
|
53
|
+
* Only consulted when {@link metaKey} is set. Objects with no `version` do not satisfy a version-constrained filter.
|
|
54
|
+
*/
|
|
55
|
+
metaVersion: Schema.optional(Schema.String),
|
|
56
|
+
|
|
44
57
|
// NOTE: Make sure to update `FilterStep.isNoop` if you change this.
|
|
45
58
|
});
|
|
46
59
|
export interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
|
|
47
60
|
export const FilterObject: Schema.Schema<FilterObject> = FilterObject_;
|
|
48
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Compare.
|
|
64
|
+
*/
|
|
49
65
|
const FilterCompare_ = Schema.Struct({
|
|
50
66
|
type: Schema.Literal('compare'),
|
|
51
67
|
operator: Schema.Literal('eq', 'neq', 'gt', 'gte', 'lt', 'lte'),
|
|
@@ -54,6 +70,9 @@ const FilterCompare_ = Schema.Struct({
|
|
|
54
70
|
export interface FilterCompare extends Schema.Schema.Type<typeof FilterCompare_> {}
|
|
55
71
|
export const FilterCompare: Schema.Schema<FilterCompare> = FilterCompare_;
|
|
56
72
|
|
|
73
|
+
/**
|
|
74
|
+
* In.
|
|
75
|
+
*/
|
|
57
76
|
const FilterIn_ = Schema.Struct({
|
|
58
77
|
type: Schema.Literal('in'),
|
|
59
78
|
values: Schema.Array(Schema.Any),
|
|
@@ -61,53 +80,137 @@ const FilterIn_ = Schema.Struct({
|
|
|
61
80
|
export interface FilterIn extends Schema.Schema.Type<typeof FilterIn_> {}
|
|
62
81
|
export const FilterIn: Schema.Schema<FilterIn> = FilterIn_;
|
|
63
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Contains.
|
|
85
|
+
*/
|
|
86
|
+
const FilterContains_ = Schema.Struct({
|
|
87
|
+
type: Schema.Literal('contains'),
|
|
88
|
+
value: Schema.Any,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Predicate for an array property to contain the provided value.
|
|
95
|
+
* Nested objects are matched using strict structural matching.
|
|
96
|
+
*/
|
|
97
|
+
export const FilterContains: Schema.Schema<FilterContains> = FilterContains_;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Filters objects that have certain tag.
|
|
101
|
+
*/
|
|
102
|
+
const FilterTag_ = Schema.Struct({
|
|
103
|
+
type: Schema.Literal('tag'),
|
|
104
|
+
tag: Schema.String, // TODO(burdon): Make OR-collection?
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
|
|
108
|
+
export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Range.
|
|
112
|
+
*/
|
|
64
113
|
const FilterRange_ = Schema.Struct({
|
|
65
114
|
type: Schema.Literal('range'),
|
|
66
115
|
from: Schema.Any,
|
|
67
116
|
to: Schema.Any,
|
|
68
117
|
});
|
|
118
|
+
|
|
69
119
|
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
70
120
|
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
71
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Filter by system timestamp (createdAt / updatedAt).
|
|
124
|
+
* Timestamps are unix milliseconds stored in the object meta index.
|
|
125
|
+
*/
|
|
126
|
+
const FilterTimestamp_ = Schema.Struct({
|
|
127
|
+
type: Schema.Literal('timestamp'),
|
|
128
|
+
field: Schema.Literal('createdAt', 'updatedAt'),
|
|
129
|
+
operator: Schema.Literal('gt', 'gte', 'lt', 'lte'),
|
|
130
|
+
value: Schema.Number,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export interface FilterTimestamp extends Schema.Schema.Type<typeof FilterTimestamp_> {}
|
|
134
|
+
export const FilterTimestamp: Schema.Schema<FilterTimestamp> = FilterTimestamp_;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Text search.
|
|
138
|
+
*/
|
|
72
139
|
const FilterTextSearch_ = Schema.Struct({
|
|
73
140
|
type: Schema.Literal('text-search'),
|
|
74
141
|
text: Schema.String,
|
|
75
142
|
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
76
143
|
});
|
|
144
|
+
|
|
77
145
|
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
78
146
|
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
79
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Not.
|
|
150
|
+
*/
|
|
80
151
|
const FilterNot_ = Schema.Struct({
|
|
81
152
|
type: Schema.Literal('not'),
|
|
82
153
|
filter: Schema.suspend(() => Filter),
|
|
83
154
|
});
|
|
155
|
+
|
|
84
156
|
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
85
157
|
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
86
158
|
|
|
159
|
+
/**
|
|
160
|
+
* And.
|
|
161
|
+
*/
|
|
87
162
|
const FilterAnd_ = Schema.Struct({
|
|
88
163
|
type: Schema.Literal('and'),
|
|
89
164
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
90
165
|
});
|
|
166
|
+
|
|
91
167
|
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
92
168
|
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
93
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Or.
|
|
172
|
+
*/
|
|
94
173
|
const FilterOr_ = Schema.Struct({
|
|
95
174
|
type: Schema.Literal('or'),
|
|
96
175
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
97
176
|
});
|
|
177
|
+
|
|
98
178
|
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
99
179
|
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
100
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Filter objects that are children of the specified parents.
|
|
183
|
+
* With transitive=true (default), matches grandchildren and beyond.
|
|
184
|
+
*/
|
|
185
|
+
const FilterChildOf_ = Schema.Struct({
|
|
186
|
+
type: Schema.Literal('child-of'),
|
|
187
|
+
/** Parent DXNs to match children of. */
|
|
188
|
+
parents: Schema.Array(EID.Schema),
|
|
189
|
+
/** Whether to match transitively (grandchildren, etc.). Defaults to true. */
|
|
190
|
+
transitive: Schema.Boolean,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
export interface FilterChildOf extends Schema.Schema.Type<typeof FilterChildOf_> {}
|
|
194
|
+
export const FilterChildOf: Schema.Schema<FilterChildOf> = FilterChildOf_;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Union of filters.
|
|
198
|
+
*/
|
|
101
199
|
export const Filter = Schema.Union(
|
|
102
200
|
FilterObject,
|
|
103
|
-
FilterTextSearch,
|
|
104
201
|
FilterCompare,
|
|
105
202
|
FilterIn,
|
|
203
|
+
FilterContains,
|
|
204
|
+
FilterTag,
|
|
106
205
|
FilterRange,
|
|
206
|
+
FilterTimestamp,
|
|
207
|
+
FilterTextSearch,
|
|
208
|
+
FilterChildOf,
|
|
107
209
|
FilterNot,
|
|
108
210
|
FilterAnd,
|
|
109
211
|
FilterOr,
|
|
110
|
-
);
|
|
212
|
+
).annotations({ identifier: 'org.dxos.schema.filter' });
|
|
213
|
+
|
|
111
214
|
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
112
215
|
|
|
113
216
|
/**
|
|
@@ -117,6 +220,7 @@ const QuerySelectClause_ = Schema.Struct({
|
|
|
117
220
|
type: Schema.Literal('select'),
|
|
118
221
|
filter: Schema.suspend(() => Filter),
|
|
119
222
|
});
|
|
223
|
+
|
|
120
224
|
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
121
225
|
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
122
226
|
|
|
@@ -128,6 +232,7 @@ const QueryFilterClause_ = Schema.Struct({
|
|
|
128
232
|
selection: Schema.suspend(() => Query),
|
|
129
233
|
filter: Schema.suspend(() => Filter),
|
|
130
234
|
});
|
|
235
|
+
|
|
131
236
|
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
132
237
|
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
133
238
|
|
|
@@ -139,6 +244,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
|
139
244
|
anchor: Schema.suspend(() => Query),
|
|
140
245
|
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
141
246
|
});
|
|
247
|
+
|
|
142
248
|
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
143
249
|
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
144
250
|
QueryReferenceTraversalClause_;
|
|
@@ -149,9 +255,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
|
|
|
149
255
|
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
150
256
|
type: Schema.Literal('incoming-references'),
|
|
151
257
|
anchor: Schema.suspend(() => Query),
|
|
152
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Property path where the reference is located.
|
|
260
|
+
* If null, matches references from any property.
|
|
261
|
+
*/
|
|
262
|
+
property: Schema.NullOr(Schema.String),
|
|
153
263
|
typename: TypenameSpecifier,
|
|
154
264
|
});
|
|
265
|
+
|
|
155
266
|
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
156
267
|
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
157
268
|
QueryIncomingReferencesClause_;
|
|
@@ -170,6 +281,7 @@ const QueryRelationClause_ = Schema.Struct({
|
|
|
170
281
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
171
282
|
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
172
283
|
});
|
|
284
|
+
|
|
173
285
|
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
174
286
|
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
175
287
|
|
|
@@ -181,9 +293,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
|
|
|
181
293
|
anchor: Schema.suspend(() => Query),
|
|
182
294
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
183
295
|
});
|
|
296
|
+
|
|
184
297
|
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
185
298
|
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
186
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Traverse parent-child hierarchy.
|
|
302
|
+
*/
|
|
303
|
+
const QueryHierarchyTraversalClause_ = Schema.Struct({
|
|
304
|
+
type: Schema.Literal('hierarchy-traversal'),
|
|
305
|
+
anchor: Schema.suspend(() => Query),
|
|
306
|
+
/**
|
|
307
|
+
* to-parent: traverse from child to parent.
|
|
308
|
+
* to-children: traverse from parent to children.
|
|
309
|
+
*/
|
|
310
|
+
direction: Schema.Literal('to-parent', 'to-children'),
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
export interface QueryHierarchyTraversalClause extends Schema.Schema.Type<typeof QueryHierarchyTraversalClause_> {}
|
|
314
|
+
export const QueryHierarchyTraversalClause: Schema.Schema<QueryHierarchyTraversalClause> =
|
|
315
|
+
QueryHierarchyTraversalClause_;
|
|
316
|
+
|
|
187
317
|
/**
|
|
188
318
|
* Union of multiple queries.
|
|
189
319
|
*/
|
|
@@ -191,6 +321,7 @@ const QueryUnionClause_ = Schema.Struct({
|
|
|
191
321
|
type: Schema.Literal('union'),
|
|
192
322
|
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
193
323
|
});
|
|
324
|
+
|
|
194
325
|
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
195
326
|
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
196
327
|
|
|
@@ -202,9 +333,53 @@ const QuerySetDifferenceClause_ = Schema.Struct({
|
|
|
202
333
|
source: Schema.suspend(() => Query),
|
|
203
334
|
exclude: Schema.suspend(() => Query),
|
|
204
335
|
});
|
|
336
|
+
|
|
205
337
|
export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
|
|
206
338
|
export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
|
|
207
339
|
|
|
340
|
+
export const OrderDirection = Schema.Literal('asc', 'desc');
|
|
341
|
+
export type OrderDirection = Schema.Schema.Type<typeof OrderDirection>;
|
|
342
|
+
|
|
343
|
+
const Order_ = Schema.Union(
|
|
344
|
+
Schema.Struct({
|
|
345
|
+
// How database wants to order them (in practice - by id).
|
|
346
|
+
kind: Schema.Literal('natural'),
|
|
347
|
+
}),
|
|
348
|
+
Schema.Struct({
|
|
349
|
+
kind: Schema.Literal('property'),
|
|
350
|
+
property: Schema.String,
|
|
351
|
+
direction: OrderDirection,
|
|
352
|
+
}),
|
|
353
|
+
Schema.Struct({
|
|
354
|
+
// Order by relevance rank (for FTS/vector search results).
|
|
355
|
+
// Default direction is 'desc' (higher rank = better match first).
|
|
356
|
+
kind: Schema.Literal('rank'),
|
|
357
|
+
direction: OrderDirection,
|
|
358
|
+
}),
|
|
359
|
+
Schema.Struct({
|
|
360
|
+
// Order by system timestamp (createdAt / updatedAt) from the object meta index.
|
|
361
|
+
kind: Schema.Literal('timestamp'),
|
|
362
|
+
field: Schema.Literal('createdAt', 'updatedAt'),
|
|
363
|
+
direction: OrderDirection,
|
|
364
|
+
}),
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
export type Order = Schema.Schema.Type<typeof Order_>;
|
|
368
|
+
export const Order: Schema.Schema<Order> = Order_;
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Order the query results.
|
|
372
|
+
* Left-to-right the orders dominate.
|
|
373
|
+
*/
|
|
374
|
+
const QueryOrderClause_ = Schema.Struct({
|
|
375
|
+
type: Schema.Literal('order'),
|
|
376
|
+
query: Schema.suspend(() => Query),
|
|
377
|
+
order: Schema.Array(Order),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
|
|
381
|
+
export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
|
|
382
|
+
|
|
208
383
|
/**
|
|
209
384
|
* Add options to a query.
|
|
210
385
|
*/
|
|
@@ -213,9 +388,37 @@ const QueryOptionsClause_ = Schema.Struct({
|
|
|
213
388
|
query: Schema.suspend(() => Query),
|
|
214
389
|
options: Schema.suspend(() => QueryOptions),
|
|
215
390
|
});
|
|
391
|
+
|
|
216
392
|
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
217
393
|
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
218
394
|
|
|
395
|
+
/**
|
|
396
|
+
* Limit the number of results.
|
|
397
|
+
*/
|
|
398
|
+
const QueryLimitClause_ = Schema.Struct({
|
|
399
|
+
type: Schema.Literal('limit'),
|
|
400
|
+
query: Schema.suspend(() => Query),
|
|
401
|
+
limit: Schema.Number,
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
export interface QueryLimitClause extends Schema.Schema.Type<typeof QueryLimitClause_> {}
|
|
405
|
+
export const QueryLimitClause: Schema.Schema<QueryLimitClause> = QueryLimitClause_;
|
|
406
|
+
|
|
407
|
+
export const QueryFromClause_ = Schema.Struct({
|
|
408
|
+
type: Schema.Literal('from'),
|
|
409
|
+
query: Schema.suspend(() => Query),
|
|
410
|
+
from: Schema.Union(
|
|
411
|
+
Schema.TaggedStruct('scope', {
|
|
412
|
+
scopes: Schema.Array(Schema.suspend(() => Scope)),
|
|
413
|
+
}),
|
|
414
|
+
Schema.TaggedStruct('query', {
|
|
415
|
+
query: Schema.suspend(() => Query),
|
|
416
|
+
}),
|
|
417
|
+
),
|
|
418
|
+
});
|
|
419
|
+
export interface QueryFromClause extends Schema.Schema.Type<typeof QueryFromClause_> {}
|
|
420
|
+
export const QueryFromClause: Schema.Schema<QueryFromClause> = QueryFromClause_;
|
|
421
|
+
|
|
219
422
|
const Query_ = Schema.Union(
|
|
220
423
|
QuerySelectClause,
|
|
221
424
|
QueryFilterClause,
|
|
@@ -223,46 +426,158 @@ const Query_ = Schema.Union(
|
|
|
223
426
|
QueryIncomingReferencesClause,
|
|
224
427
|
QueryRelationClause,
|
|
225
428
|
QueryRelationTraversalClause,
|
|
429
|
+
QueryHierarchyTraversalClause,
|
|
226
430
|
QueryUnionClause,
|
|
227
431
|
QuerySetDifferenceClause,
|
|
432
|
+
QueryOrderClause,
|
|
228
433
|
QueryOptionsClause,
|
|
229
|
-
|
|
434
|
+
QueryLimitClause,
|
|
435
|
+
QueryFromClause,
|
|
436
|
+
).annotations({ identifier: 'org.dxos.schema.query' });
|
|
230
437
|
|
|
231
438
|
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
232
439
|
export const Query: Schema.Schema<Query> = Query_;
|
|
233
440
|
|
|
234
441
|
export const QueryOptions = Schema.Struct({
|
|
235
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Nested select statements will use this option to filter deleted objects.
|
|
444
|
+
*/
|
|
236
445
|
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Diagnostics-only label for logs / tooling (not used by execution semantics).
|
|
449
|
+
*/
|
|
450
|
+
debugLabel: Schema.optional(Schema.String),
|
|
237
451
|
});
|
|
452
|
+
|
|
238
453
|
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
239
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Selects from a space (automerge documents).
|
|
457
|
+
* When `spaceId` is omitted, targets the owning space — i.e. the space of whichever
|
|
458
|
+
* database executes the query. This lets callers reference "this space" without
|
|
459
|
+
* having to look up its id.
|
|
460
|
+
* When `includeAllFeeds` is true, also selects from all feeds belonging to that space.
|
|
461
|
+
*/
|
|
462
|
+
export const SpaceScope = Schema.TaggedStruct('space', {
|
|
463
|
+
spaceId: Schema.optional(Schema.String),
|
|
464
|
+
includeAllFeeds: Schema.optional(Schema.Boolean),
|
|
465
|
+
});
|
|
466
|
+
export interface SpaceScope extends Schema.Schema.Type<typeof SpaceScope> {}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Selects from a specific feed (by its underlying queue DXN).
|
|
470
|
+
*/
|
|
471
|
+
export const FeedScope = Schema.TaggedStruct('feed', {
|
|
472
|
+
feedUri: Schema.String,
|
|
473
|
+
});
|
|
474
|
+
export interface FeedScope extends Schema.Schema.Type<typeof FeedScope> {}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Selects from a code-shipped object registry.
|
|
478
|
+
*
|
|
479
|
+
* - `'local'` — the in-process registry attached to the hypergraph.
|
|
480
|
+
* - `'remote'` — a future remote registry service (not yet implemented).
|
|
481
|
+
*
|
|
482
|
+
* To include both, add two separate `RegistryScope` entries to the `scopes` array.
|
|
483
|
+
*/
|
|
484
|
+
export const RegistryScope = Schema.TaggedStruct('registry', {
|
|
485
|
+
location: Schema.Literal('local', 'remote'),
|
|
486
|
+
});
|
|
487
|
+
export interface RegistryScope extends Schema.Schema.Type<typeof RegistryScope> {}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Specifies the scope of the data to query from.
|
|
491
|
+
* A `from` clause may carry multiple scopes; results are unioned across them.
|
|
492
|
+
*/
|
|
493
|
+
export const Scope = Schema.Union(SpaceScope, FeedScope, RegistryScope);
|
|
494
|
+
export type Scope = Schema.Schema.Type<typeof Scope>;
|
|
495
|
+
|
|
240
496
|
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
|
-
|
|
497
|
+
visitor(query);
|
|
498
|
+
|
|
499
|
+
Match.value(query).pipe(
|
|
500
|
+
Match.when({ type: 'filter' }, ({ selection }) => visit(selection, visitor)),
|
|
501
|
+
Match.when({ type: 'reference-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
502
|
+
Match.when({ type: 'incoming-references' }, ({ anchor }) => visit(anchor, visitor)),
|
|
503
|
+
Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
|
|
504
|
+
Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
|
|
505
|
+
Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
506
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
507
|
+
Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
|
|
508
|
+
Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
|
|
509
|
+
visit(source, visitor);
|
|
510
|
+
visit(exclude, visitor);
|
|
511
|
+
}),
|
|
512
|
+
Match.when({ type: 'order' }, ({ query }) => visit(query, visitor)),
|
|
513
|
+
Match.when({ type: 'limit' }, ({ query }) => visit(query, visitor)),
|
|
514
|
+
Match.when({ type: 'from' }, (node) => {
|
|
515
|
+
visit(node.query, visitor);
|
|
516
|
+
if (node.from._tag === 'query') {
|
|
517
|
+
visit(node.from.query, visitor);
|
|
518
|
+
}
|
|
519
|
+
}),
|
|
520
|
+
Match.when({ type: 'select' }, () => {}),
|
|
521
|
+
Match.exhaustive,
|
|
522
|
+
);
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Recursively transforms a query tree bottom-up.
|
|
527
|
+
* The mapper receives each node with its children already transformed.
|
|
528
|
+
*/
|
|
529
|
+
export const map = (query: Query, mapper: (node: Query) => Query): Query => {
|
|
530
|
+
const mapped: Query = Match.value(query).pipe(
|
|
531
|
+
Match.when({ type: 'filter' }, (node) => ({ ...node, selection: map(node.selection, mapper) })),
|
|
532
|
+
Match.when({ type: 'reference-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
533
|
+
Match.when({ type: 'incoming-references' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
534
|
+
Match.when({ type: 'relation' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
535
|
+
Match.when({ type: 'relation-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
536
|
+
Match.when({ type: 'hierarchy-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
537
|
+
Match.when({ type: 'options' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
538
|
+
Match.when({ type: 'order' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
539
|
+
Match.when({ type: 'limit' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
540
|
+
Match.when({ type: 'from' }, (node) => ({
|
|
541
|
+
...node,
|
|
542
|
+
query: map(node.query, mapper),
|
|
543
|
+
...(node.from._tag === 'query' ? { from: { _tag: 'query' as const, query: map(node.from.query, mapper) } } : {}),
|
|
544
|
+
})),
|
|
545
|
+
Match.when({ type: 'union' }, (node) => ({ ...node, queries: node.queries.map((q) => map(q, mapper)) })),
|
|
546
|
+
Match.when({ type: 'set-difference' }, (node) => ({
|
|
547
|
+
...node,
|
|
548
|
+
source: map(node.source, mapper),
|
|
549
|
+
exclude: map(node.exclude, mapper),
|
|
550
|
+
})),
|
|
551
|
+
Match.when({ type: 'select' }, (node) => node),
|
|
552
|
+
Match.exhaustive,
|
|
553
|
+
);
|
|
554
|
+
return mapper(mapped);
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
558
|
+
return Match.value(query).pipe(
|
|
559
|
+
Match.withReturnType<T[]>(),
|
|
560
|
+
Match.when({ type: 'filter' }, ({ selection }) => fold(selection, reducer)),
|
|
561
|
+
Match.when({ type: 'reference-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
562
|
+
Match.when({ type: 'incoming-references' }, ({ anchor }) => fold(anchor, reducer)),
|
|
563
|
+
Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
|
|
564
|
+
Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
|
|
565
|
+
Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
566
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
567
|
+
Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
|
|
568
|
+
Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
|
|
569
|
+
fold(source, reducer).concat(fold(exclude, reducer)),
|
|
570
|
+
),
|
|
571
|
+
Match.when({ type: 'order' }, ({ query }) => fold(query, reducer)),
|
|
572
|
+
Match.when({ type: 'limit' }, ({ query }) => fold(query, reducer)),
|
|
573
|
+
Match.when({ type: 'from' }, (node) => {
|
|
574
|
+
const results = fold(node.query, reducer);
|
|
575
|
+
if (node.from._tag === 'query') {
|
|
576
|
+
return results.concat(fold(node.from.query, reducer));
|
|
577
|
+
}
|
|
578
|
+
return results;
|
|
579
|
+
}),
|
|
580
|
+
Match.when({ type: 'select' }, () => []),
|
|
581
|
+
Match.exhaustive,
|
|
582
|
+
);
|
|
268
583
|
};
|