@dxos/echo-protocol 0.8.4-main.fffef41 → 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/{browser → neutral}/index.mjs +315 -197
- 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 +81 -34
- 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/index.d.ts +5 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +203 -41
- 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 +98 -37
- package/src/echo-feed-codec.ts +67 -0
- package/src/edge-peer.ts +27 -0
- package/src/index.ts +5 -3
- package/src/query/ast.ts +229 -21
- package/src/reference.ts +12 -157
- 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 -604
- 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
|
@@ -5,13 +5,14 @@
|
|
|
5
5
|
import * as Match from 'effect/Match';
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { EID, EntityId, URI } from '@dxos/keys';
|
|
9
9
|
|
|
10
10
|
import { ForeignKey } from '../foreign-key';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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);
|
|
15
16
|
|
|
16
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.
|
|
17
18
|
|
|
@@ -26,7 +27,7 @@ const FilterObject_ = Schema.Struct({
|
|
|
26
27
|
|
|
27
28
|
typename: TypenameSpecifier,
|
|
28
29
|
|
|
29
|
-
id: Schema.optional(Schema.Array(
|
|
30
|
+
id: Schema.optional(Schema.Array(EntityId)),
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Filter by property.
|
|
@@ -42,6 +43,17 @@ const FilterObject_ = Schema.Struct({
|
|
|
42
43
|
*/
|
|
43
44
|
foreignKeys: Schema.optional(Schema.Array(ForeignKey)),
|
|
44
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
|
+
|
|
45
57
|
// NOTE: Make sure to update `FilterStep.isNoop` if you change this.
|
|
46
58
|
});
|
|
47
59
|
export interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
|
|
@@ -75,7 +87,9 @@ const FilterContains_ = Schema.Struct({
|
|
|
75
87
|
type: Schema.Literal('contains'),
|
|
76
88
|
value: Schema.Any,
|
|
77
89
|
});
|
|
90
|
+
|
|
78
91
|
export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
|
|
92
|
+
|
|
79
93
|
/**
|
|
80
94
|
* Predicate for an array property to contain the provided value.
|
|
81
95
|
* Nested objects are matched using strict structural matching.
|
|
@@ -89,6 +103,7 @@ const FilterTag_ = Schema.Struct({
|
|
|
89
103
|
type: Schema.Literal('tag'),
|
|
90
104
|
tag: Schema.String, // TODO(burdon): Make OR-collection?
|
|
91
105
|
});
|
|
106
|
+
|
|
92
107
|
export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
|
|
93
108
|
export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
|
|
94
109
|
|
|
@@ -100,9 +115,24 @@ const FilterRange_ = Schema.Struct({
|
|
|
100
115
|
from: Schema.Any,
|
|
101
116
|
to: Schema.Any,
|
|
102
117
|
});
|
|
118
|
+
|
|
103
119
|
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
104
120
|
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
105
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
|
+
|
|
106
136
|
/**
|
|
107
137
|
* Text search.
|
|
108
138
|
*/
|
|
@@ -111,6 +141,7 @@ const FilterTextSearch_ = Schema.Struct({
|
|
|
111
141
|
text: Schema.String,
|
|
112
142
|
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
113
143
|
});
|
|
144
|
+
|
|
114
145
|
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
115
146
|
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
116
147
|
|
|
@@ -121,6 +152,7 @@ const FilterNot_ = Schema.Struct({
|
|
|
121
152
|
type: Schema.Literal('not'),
|
|
122
153
|
filter: Schema.suspend(() => Filter),
|
|
123
154
|
});
|
|
155
|
+
|
|
124
156
|
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
125
157
|
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
126
158
|
|
|
@@ -131,6 +163,7 @@ const FilterAnd_ = Schema.Struct({
|
|
|
131
163
|
type: Schema.Literal('and'),
|
|
132
164
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
133
165
|
});
|
|
166
|
+
|
|
134
167
|
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
135
168
|
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
136
169
|
|
|
@@ -141,9 +174,25 @@ const FilterOr_ = Schema.Struct({
|
|
|
141
174
|
type: Schema.Literal('or'),
|
|
142
175
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
143
176
|
});
|
|
177
|
+
|
|
144
178
|
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
145
179
|
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
146
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
|
+
|
|
147
196
|
/**
|
|
148
197
|
* Union of filters.
|
|
149
198
|
*/
|
|
@@ -154,11 +203,14 @@ export const Filter = Schema.Union(
|
|
|
154
203
|
FilterContains,
|
|
155
204
|
FilterTag,
|
|
156
205
|
FilterRange,
|
|
206
|
+
FilterTimestamp,
|
|
157
207
|
FilterTextSearch,
|
|
208
|
+
FilterChildOf,
|
|
158
209
|
FilterNot,
|
|
159
210
|
FilterAnd,
|
|
160
211
|
FilterOr,
|
|
161
|
-
).annotations({ identifier: 'dxos.
|
|
212
|
+
).annotations({ identifier: 'org.dxos.schema.filter' });
|
|
213
|
+
|
|
162
214
|
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
163
215
|
|
|
164
216
|
/**
|
|
@@ -168,6 +220,7 @@ const QuerySelectClause_ = Schema.Struct({
|
|
|
168
220
|
type: Schema.Literal('select'),
|
|
169
221
|
filter: Schema.suspend(() => Filter),
|
|
170
222
|
});
|
|
223
|
+
|
|
171
224
|
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
172
225
|
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
173
226
|
|
|
@@ -179,6 +232,7 @@ const QueryFilterClause_ = Schema.Struct({
|
|
|
179
232
|
selection: Schema.suspend(() => Query),
|
|
180
233
|
filter: Schema.suspend(() => Filter),
|
|
181
234
|
});
|
|
235
|
+
|
|
182
236
|
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
183
237
|
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
184
238
|
|
|
@@ -190,6 +244,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
|
190
244
|
anchor: Schema.suspend(() => Query),
|
|
191
245
|
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
192
246
|
});
|
|
247
|
+
|
|
193
248
|
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
194
249
|
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
195
250
|
QueryReferenceTraversalClause_;
|
|
@@ -200,9 +255,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
|
|
|
200
255
|
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
201
256
|
type: Schema.Literal('incoming-references'),
|
|
202
257
|
anchor: Schema.suspend(() => Query),
|
|
203
|
-
|
|
258
|
+
/**
|
|
259
|
+
* Property path where the reference is located.
|
|
260
|
+
* If null, matches references from any property.
|
|
261
|
+
*/
|
|
262
|
+
property: Schema.NullOr(Schema.String),
|
|
204
263
|
typename: TypenameSpecifier,
|
|
205
264
|
});
|
|
265
|
+
|
|
206
266
|
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
207
267
|
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
208
268
|
QueryIncomingReferencesClause_;
|
|
@@ -221,6 +281,7 @@ const QueryRelationClause_ = Schema.Struct({
|
|
|
221
281
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
222
282
|
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
223
283
|
});
|
|
284
|
+
|
|
224
285
|
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
225
286
|
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
226
287
|
|
|
@@ -232,9 +293,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
|
|
|
232
293
|
anchor: Schema.suspend(() => Query),
|
|
233
294
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
234
295
|
});
|
|
296
|
+
|
|
235
297
|
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
236
298
|
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
237
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
|
+
|
|
238
317
|
/**
|
|
239
318
|
* Union of multiple queries.
|
|
240
319
|
*/
|
|
@@ -242,6 +321,7 @@ const QueryUnionClause_ = Schema.Struct({
|
|
|
242
321
|
type: Schema.Literal('union'),
|
|
243
322
|
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
244
323
|
});
|
|
324
|
+
|
|
245
325
|
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
246
326
|
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
247
327
|
|
|
@@ -253,6 +333,7 @@ const QuerySetDifferenceClause_ = Schema.Struct({
|
|
|
253
333
|
source: Schema.suspend(() => Query),
|
|
254
334
|
exclude: Schema.suspend(() => Query),
|
|
255
335
|
});
|
|
336
|
+
|
|
256
337
|
export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
|
|
257
338
|
export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
|
|
258
339
|
|
|
@@ -269,7 +350,20 @@ const Order_ = Schema.Union(
|
|
|
269
350
|
property: Schema.String,
|
|
270
351
|
direction: OrderDirection,
|
|
271
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
|
+
}),
|
|
272
365
|
);
|
|
366
|
+
|
|
273
367
|
export type Order = Schema.Schema.Type<typeof Order_>;
|
|
274
368
|
export const Order: Schema.Schema<Order> = Order_;
|
|
275
369
|
|
|
@@ -282,6 +376,7 @@ const QueryOrderClause_ = Schema.Struct({
|
|
|
282
376
|
query: Schema.suspend(() => Query),
|
|
283
377
|
order: Schema.Array(Order),
|
|
284
378
|
});
|
|
379
|
+
|
|
285
380
|
export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
|
|
286
381
|
export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
|
|
287
382
|
|
|
@@ -293,9 +388,37 @@ const QueryOptionsClause_ = Schema.Struct({
|
|
|
293
388
|
query: Schema.suspend(() => Query),
|
|
294
389
|
options: Schema.suspend(() => QueryOptions),
|
|
295
390
|
});
|
|
391
|
+
|
|
296
392
|
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
297
393
|
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
298
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
|
+
|
|
299
422
|
const Query_ = Schema.Union(
|
|
300
423
|
QuerySelectClause,
|
|
301
424
|
QueryFilterClause,
|
|
@@ -303,37 +426,73 @@ const Query_ = Schema.Union(
|
|
|
303
426
|
QueryIncomingReferencesClause,
|
|
304
427
|
QueryRelationClause,
|
|
305
428
|
QueryRelationTraversalClause,
|
|
429
|
+
QueryHierarchyTraversalClause,
|
|
306
430
|
QueryUnionClause,
|
|
307
431
|
QuerySetDifferenceClause,
|
|
308
432
|
QueryOrderClause,
|
|
309
433
|
QueryOptionsClause,
|
|
310
|
-
|
|
434
|
+
QueryLimitClause,
|
|
435
|
+
QueryFromClause,
|
|
436
|
+
).annotations({ identifier: 'org.dxos.schema.query' });
|
|
311
437
|
|
|
312
438
|
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
313
439
|
export const Query: Schema.Schema<Query> = Query_;
|
|
314
440
|
|
|
315
441
|
export const QueryOptions = Schema.Struct({
|
|
316
442
|
/**
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* NOTE: Spaces and queues are unioned together if both are specified.
|
|
320
|
-
*/
|
|
321
|
-
spaceIds: Schema.optional(Schema.Array(Schema.String)),
|
|
322
|
-
|
|
323
|
-
/**
|
|
324
|
-
* The nested select statemets will select from the given queues.
|
|
325
|
-
*
|
|
326
|
-
* NOTE: Spaces and queues are unioned together if both are specified.
|
|
443
|
+
* Nested select statements will use this option to filter deleted objects.
|
|
327
444
|
*/
|
|
328
|
-
|
|
445
|
+
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
329
446
|
|
|
330
447
|
/**
|
|
331
|
-
*
|
|
448
|
+
* Diagnostics-only label for logs / tooling (not used by execution semantics).
|
|
332
449
|
*/
|
|
333
|
-
|
|
450
|
+
debugLabel: Schema.optional(Schema.String),
|
|
334
451
|
});
|
|
452
|
+
|
|
335
453
|
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
336
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
|
+
|
|
337
496
|
export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
338
497
|
visitor(query);
|
|
339
498
|
|
|
@@ -344,17 +503,57 @@ export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
|
344
503
|
Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
|
|
345
504
|
Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
|
|
346
505
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
506
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
347
507
|
Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
|
|
348
508
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
|
|
349
509
|
visit(source, visitor);
|
|
350
510
|
visit(exclude, visitor);
|
|
351
511
|
}),
|
|
352
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
|
+
}),
|
|
353
520
|
Match.when({ type: 'select' }, () => {}),
|
|
354
521
|
Match.exhaustive,
|
|
355
522
|
);
|
|
356
523
|
};
|
|
357
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
|
+
|
|
358
557
|
export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
359
558
|
return Match.value(query).pipe(
|
|
360
559
|
Match.withReturnType<T[]>(),
|
|
@@ -364,11 +563,20 @@ export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
|
364
563
|
Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
|
|
365
564
|
Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
|
|
366
565
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
566
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
367
567
|
Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
|
|
368
568
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
|
|
369
569
|
fold(source, reducer).concat(fold(exclude, reducer)),
|
|
370
570
|
),
|
|
371
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
|
+
}),
|
|
372
580
|
Match.when({ type: 'select' }, () => []),
|
|
373
581
|
Match.exhaustive,
|
|
374
582
|
);
|
package/src/reference.ts
CHANGED
|
@@ -3,126 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { assertArgument } from '@dxos/invariant';
|
|
6
|
-
import {
|
|
7
|
-
import { type ObjectId } from '@dxos/protocols';
|
|
8
|
-
import { type Reference as ReferenceProto } from '@dxos/protocols/proto/dxos/echo/model/document';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Runtime representation of an reference in ECHO.
|
|
12
|
-
* Implemented as a DXN, but we might extend it to other URIs in the future.
|
|
13
|
-
* @deprecated Use `EncodedReference` instead.
|
|
14
|
-
*/
|
|
15
|
-
export class Reference {
|
|
16
|
-
/**
|
|
17
|
-
* Protocol references to runtime registered types.
|
|
18
|
-
* @deprecated
|
|
19
|
-
*/
|
|
20
|
-
static TYPE_PROTOCOL = 'protobuf';
|
|
21
|
-
|
|
22
|
-
static fromDXN(dxn: DXN): Reference {
|
|
23
|
-
switch (dxn.kind) {
|
|
24
|
-
case DXN.kind.TYPE:
|
|
25
|
-
return new Reference(dxn.parts[0], Reference.TYPE_PROTOCOL, 'dxos.org', dxn);
|
|
26
|
-
case DXN.kind.ECHO:
|
|
27
|
-
if (dxn.parts[0] === LOCAL_SPACE_TAG) {
|
|
28
|
-
return new Reference(dxn.parts[1], undefined, undefined, dxn);
|
|
29
|
-
} else {
|
|
30
|
-
return new Reference(dxn.parts[1], undefined, dxn.parts[0], dxn);
|
|
31
|
-
}
|
|
32
|
-
default:
|
|
33
|
-
return new Reference(dxn.parts[0], undefined, dxn.parts[0], dxn);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
static fromValue(value: ReferenceProto): Reference {
|
|
38
|
-
return new Reference(value.objectId, value.protocol, value.host);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Reference an object in the local space.
|
|
43
|
-
*/
|
|
44
|
-
static localObjectReference(objectId: ObjectId): Reference {
|
|
45
|
-
return new Reference(objectId);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* @deprecated
|
|
50
|
-
*/
|
|
51
|
-
// TODO(dmaretskyi): Remove.
|
|
52
|
-
static fromLegacyTypename(type: string): Reference {
|
|
53
|
-
return new Reference(type, Reference.TYPE_PROTOCOL, 'dxos.org');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @deprecated
|
|
58
|
-
*/
|
|
59
|
-
// TODO(dmaretskyi): Remove
|
|
60
|
-
static fromObjectIdAndSpaceKey(objectId: ObjectId, spaceKey: PublicKey): Reference {
|
|
61
|
-
// TODO(dmaretskyi): FIX ME! This should be a space ID not a space key.
|
|
62
|
-
return new Reference(objectId, undefined, spaceKey.toHex());
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// prettier-ignore
|
|
66
|
-
private constructor(
|
|
67
|
-
// TODO(dmaretskyi): Remove and just leave DXN.
|
|
68
|
-
private readonly _objectId: ObjectId,
|
|
69
|
-
private readonly _protocol?: string,
|
|
70
|
-
private readonly _host?: string,
|
|
71
|
-
private readonly _dxn?: DXN,
|
|
72
|
-
) {}
|
|
73
|
-
|
|
74
|
-
get dxn(): DXN | undefined {
|
|
75
|
-
return this._dxn;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* @deprecated
|
|
80
|
-
*/
|
|
81
|
-
// TODO(dmaretskyi): Remove.
|
|
82
|
-
get objectId(): ObjectId {
|
|
83
|
-
return this._objectId;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* @deprecated
|
|
88
|
-
*/
|
|
89
|
-
// TODO(dmaretskyi): Remove.
|
|
90
|
-
get protocol(): string | undefined {
|
|
91
|
-
return this._protocol;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* @deprecated
|
|
96
|
-
*/
|
|
97
|
-
// TODO(dmaretskyi): Remove.
|
|
98
|
-
get host(): string | undefined {
|
|
99
|
-
return this._host;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
encode(): ReferenceProto {
|
|
103
|
-
return { objectId: this.objectId, host: this.host, protocol: this.protocol };
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// TODO(dmaretskyi): Remove in favor of `reference.dxn`.
|
|
107
|
-
toDXN(): DXN {
|
|
108
|
-
if (this._dxn) {
|
|
109
|
-
return this._dxn;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (this.protocol === Reference.TYPE_PROTOCOL) {
|
|
113
|
-
return new DXN(DXN.kind.TYPE, [this.objectId]);
|
|
114
|
-
} else {
|
|
115
|
-
if (this.host) {
|
|
116
|
-
// Host is assumed to be the space key.
|
|
117
|
-
// The DXN should actually have the space ID.
|
|
118
|
-
// TODO(dmaretskyi): Migrate to space id.
|
|
119
|
-
return new DXN(DXN.kind.ECHO, [this.host, this.objectId]);
|
|
120
|
-
} else {
|
|
121
|
-
return new DXN(DXN.kind.ECHO, [LOCAL_SPACE_TAG, this.objectId]);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
6
|
+
import { URI } from '@dxos/keys';
|
|
126
7
|
|
|
127
8
|
// TODO(dmaretskyi): Is this used anywhere?
|
|
128
9
|
export const REFERENCE_TYPE_TAG = 'dxos.echo.model.document.Reference';
|
|
@@ -131,52 +12,26 @@ export const REFERENCE_TYPE_TAG = 'dxos.echo.model.document.Reference';
|
|
|
131
12
|
* Reference as it is stored in Automerge document.
|
|
132
13
|
*/
|
|
133
14
|
export type EncodedReference = {
|
|
134
|
-
'/':
|
|
15
|
+
'/': URI.URI;
|
|
135
16
|
};
|
|
136
17
|
|
|
137
|
-
/**
|
|
138
|
-
* @deprecated Use `EncodedReference.fromDXN` instead.
|
|
139
|
-
*/
|
|
140
|
-
export const encodeReference = (reference: Reference): EncodedReference => ({
|
|
141
|
-
'/': reference.toDXN().toString(),
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* @deprecated Use `EncodedReference.toDXN` instead.
|
|
146
|
-
*/
|
|
147
|
-
export const decodeReference = (value: any) => {
|
|
148
|
-
if (typeof value !== 'object' || value === null || typeof value['/'] !== 'string') {
|
|
149
|
-
throw new Error('Invalid reference');
|
|
150
|
-
}
|
|
151
|
-
const dxnString = value['/'];
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
dxnString.length % 2 === 0 &&
|
|
155
|
-
dxnString.slice(0, dxnString.length / 2) === dxnString.slice(dxnString.length / 2) &&
|
|
156
|
-
dxnString.includes('dxn:echo')
|
|
157
|
-
) {
|
|
158
|
-
throw new Error('Automerge bug detected!');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return Reference.fromDXN(DXN.parse(dxnString));
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* @deprecated Use `EncodedReference.isEncodedReference` instead.
|
|
166
|
-
*/
|
|
167
18
|
export const isEncodedReference = (value: any): value is EncodedReference =>
|
|
168
19
|
typeof value === 'object' && value !== null && Object.keys(value).length === 1 && typeof value['/'] === 'string';
|
|
169
20
|
|
|
170
21
|
export const EncodedReference = Object.freeze({
|
|
171
22
|
isEncodedReference,
|
|
172
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Returns the opaque URI stored in the encoded reference (any scheme: `echo:` or `dxn:`).
|
|
25
|
+
* Consumers can narrow with `EID.isEID(uri)` / `DXN.isDXN(uri)`.
|
|
26
|
+
*/
|
|
27
|
+
toURI: (value: EncodedReference): URI.URI => {
|
|
173
28
|
assertArgument(isEncodedReference(value), 'value', 'invalid reference');
|
|
174
29
|
return value['/'];
|
|
175
30
|
},
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return
|
|
31
|
+
/**
|
|
32
|
+
* Creates an encoded reference from an opaque URI.
|
|
33
|
+
*/
|
|
34
|
+
fromURI: (uri: URI.URI): EncodedReference => {
|
|
35
|
+
return { '/': uri };
|
|
181
36
|
},
|
|
182
37
|
});
|