@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/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 { DXN, ObjectId } from '@dxos/keys';
8
+ import { EID, EntityId, URI } from '@dxos/keys';
9
9
 
10
10
  import { ForeignKey } from '../foreign-key';
11
11
 
12
- const TypenameSpecifier = Schema.Union(DXN.Schema, Schema.Null).annotations({
13
- description: 'DXN or null. Null means any type will match',
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(ObjectId)),
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.org/schema/Filter' });
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
- property: Schema.String,
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
- ).annotations({ identifier: 'dxos.org/schema/Query' });
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
- * The nested select statemets will select from the given spaces.
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
- queues: Schema.optional(Schema.Array(DXN.Schema)),
445
+ deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
329
446
 
330
447
  /**
331
- * Nested select statements will use this option to filter deleted objects.
448
+ * Diagnostics-only label for logs / tooling (not used by execution semantics).
332
449
  */
333
- deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
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 { DXN, LOCAL_SPACE_TAG, type PublicKey } from '@dxos/keys';
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
- '/': string;
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
- getReferenceString: (value: EncodedReference): string => {
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
- toDXN: (value: EncodedReference): DXN => {
177
- return DXN.parse(EncodedReference.getReferenceString(value));
178
- },
179
- fromDXN: (dxn: DXN): EncodedReference => {
180
- return encodeReference(Reference.fromDXN(dxn));
31
+ /**
32
+ * Creates an encoded reference from an opaque URI.
33
+ */
34
+ fromURI: (uri: URI.URI): EncodedReference => {
35
+ return { '/': uri };
181
36
  },
182
37
  });