@dxos/echo-protocol 0.8.4-main.3a94e84 → 0.8.4-main.3eb6e50203

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/package.json CHANGED
@@ -1,19 +1,22 @@
1
1
  {
2
2
  "name": "@dxos/echo-protocol",
3
- "version": "0.8.4-main.3a94e84",
3
+ "version": "0.8.4-main.3eb6e50203",
4
4
  "description": "Core ECHO APIs.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
- "repository": "github:dxos/dxos",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
8
11
  "license": "MIT",
9
12
  "author": "DXOS.org",
10
- "sideEffects": true,
13
+ "sideEffects": false,
11
14
  "type": "module",
12
15
  "exports": {
13
16
  ".": {
17
+ "source": "./src/index.ts",
14
18
  "types": "./dist/types/src/index.d.ts",
15
- "browser": "./dist/lib/browser/index.mjs",
16
- "node": "./dist/lib/node-esm/index.mjs"
19
+ "default": "./dist/lib/neutral/index.mjs"
17
20
  }
18
21
  },
19
22
  "types": "dist/types/src/index.d.ts",
@@ -25,12 +28,12 @@
25
28
  "src"
26
29
  ],
27
30
  "dependencies": {
28
- "effect": "3.17.0",
29
- "@dxos/crypto": "0.8.4-main.3a94e84",
30
- "@dxos/invariant": "0.8.4-main.3a94e84",
31
- "@dxos/keys": "0.8.4-main.3a94e84",
32
- "@dxos/protocols": "0.8.4-main.3a94e84",
33
- "@dxos/util": "0.8.4-main.3a94e84"
31
+ "effect": "3.19.16",
32
+ "@dxos/crypto": "0.8.4-main.3eb6e50203",
33
+ "@dxos/invariant": "0.8.4-main.3eb6e50203",
34
+ "@dxos/keys": "0.8.4-main.3eb6e50203",
35
+ "@dxos/protocols": "0.8.4-main.3eb6e50203",
36
+ "@dxos/util": "0.8.4-main.3eb6e50203"
34
37
  },
35
38
  "publishConfig": {
36
39
  "access": "public"
@@ -8,7 +8,7 @@ import { visitValues } from '@dxos/util';
8
8
 
9
9
  import { type RawString } from './automerge';
10
10
  import type { ForeignKey } from './foreign-key';
11
- import { isEncodedReference, type EncodedReference } from './reference';
11
+ import { type EncodedReference, isEncodedReference } from './reference';
12
12
  import { type SpaceDocVersion } from './space-doc-version';
13
13
 
14
14
  export type SpaceState = {
@@ -137,6 +137,10 @@ export const ObjectStructure = Object.freeze({
137
137
  return object.system?.target;
138
138
  },
139
139
 
140
+ getParent: (object: ObjectStructure): EncodedReference | undefined => {
141
+ return object.system?.parent;
142
+ },
143
+
140
144
  /**
141
145
  * @returns All references in the data section of the object.
142
146
  */
@@ -153,6 +157,10 @@ export const ObjectStructure = Object.freeze({
153
157
  return references;
154
158
  },
155
159
 
160
+ getTags: (object: ObjectStructure): string[] => {
161
+ return object.meta.tags ?? [];
162
+ },
163
+
156
164
  makeObject: ({
157
165
  type,
158
166
  data,
@@ -214,6 +222,14 @@ export type ObjectMeta = {
214
222
  * Foreign keys.
215
223
  */
216
224
  keys: ForeignKey[];
225
+
226
+ /**
227
+ * Tags.
228
+ * An array of DXNs of Tag objects within the space.
229
+ *
230
+ * NOTE: Optional for backwards compatibilty.
231
+ */
232
+ tags?: string[];
217
233
  };
218
234
 
219
235
  /**
@@ -236,13 +252,19 @@ export type ObjectSystem = {
236
252
  */
237
253
  deleted?: boolean;
238
254
 
255
+ /**
256
+ * Object parent.
257
+ * Objects with no parent are at the top level of the object hierarchy in the space.
258
+ */
259
+ parent?: EncodedReference;
260
+
239
261
  /**
240
262
  * Only for relations.
241
263
  */
242
264
  source?: EncodedReference;
243
265
 
244
266
  /**
245
- * Only for relations.w
267
+ * Only for relations.
246
268
  */
247
269
  target?: EncodedReference;
248
270
  };
@@ -0,0 +1,67 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { FeedProtocol } from '@dxos/protocols';
6
+
7
+ import type { ForeignKey } from './foreign-key';
8
+
9
+ /** Property name for meta when object is serialized to JSON. Matches @dxos/echo/internal ATTR_META. */
10
+ const ATTR_META = '@meta';
11
+
12
+ /**
13
+ * Codec for ECHO objects in feed block payload: JSON object ↔ UTF-8 bytes.
14
+ * Encodes with queue position stripped; decodes with optional position injection.
15
+ */
16
+ export class EchoFeedCodec {
17
+ static readonly #encoder = new TextEncoder();
18
+ static readonly #decoder = new TextDecoder();
19
+
20
+ /**
21
+ * Prepares a value for feed storage (strips queue position from metadata) and encodes to bytes.
22
+ */
23
+ static encode(value: Record<string, unknown>): Uint8Array {
24
+ const prepared = EchoFeedCodec.#stripQueuePosition(value);
25
+ return EchoFeedCodec.#encoder.encode(JSON.stringify(prepared));
26
+ }
27
+
28
+ /**
29
+ * Decodes feed block bytes to a JSON value.
30
+ * If position is provided, injects queue position into the decoded object's metadata.
31
+ */
32
+ static decode(data: Uint8Array, position?: number): Record<string, unknown> {
33
+ const decoded = JSON.parse(EchoFeedCodec.#decoder.decode(data));
34
+ if (position !== undefined && typeof decoded === 'object' && decoded !== null) {
35
+ EchoFeedCodec.#setQueuePosition(decoded, position);
36
+ }
37
+ return decoded;
38
+ }
39
+
40
+ static #stripQueuePosition(value: Record<string, unknown>): Record<string, unknown> {
41
+ if (typeof value !== 'object' || value === null) {
42
+ return value;
43
+ }
44
+ const obj = structuredClone(value);
45
+ const meta = obj[ATTR_META] as { keys?: ForeignKey[] } | undefined;
46
+ if (meta?.keys?.some((key: ForeignKey) => key.source === FeedProtocol.KEY_QUEUE_POSITION)) {
47
+ meta.keys = meta.keys.filter((key: ForeignKey) => key.source !== FeedProtocol.KEY_QUEUE_POSITION);
48
+ }
49
+ return obj;
50
+ }
51
+
52
+ static #setQueuePosition(obj: Record<string, any>, position: number): void {
53
+ obj[ATTR_META] ??= { keys: [] };
54
+ obj[ATTR_META]!.keys ??= [];
55
+ const keys = obj[ATTR_META]!.keys!;
56
+ for (let i = 0; i < keys.length; i++) {
57
+ if (keys[i].source === FeedProtocol.KEY_QUEUE_POSITION) {
58
+ keys.splice(i, 1);
59
+ i--;
60
+ }
61
+ }
62
+ keys.push({
63
+ source: FeedProtocol.KEY_QUEUE_POSITION,
64
+ id: position.toString(),
65
+ });
66
+ }
67
+ }
@@ -2,7 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema, SchemaAST } from 'effect';
5
+ import * as Schema from 'effect/Schema';
6
+ import * as SchemaAST from 'effect/SchemaAST';
6
7
 
7
8
  const ForeignKey_ = Schema.Struct({
8
9
  /**
@@ -15,8 +16,8 @@ const ForeignKey_ = Schema.Struct({
15
16
  * Id within the foreign database.
16
17
  */
17
18
  // TODO(wittjosiah): This annotation is currently used to ensure id field shows up in forms.
18
- // TODO(dmaretskyi): `false` is not a valid value for the annotation.
19
- id: Schema.String.annotations({ [SchemaAST.IdentifierAnnotationId]: false }),
19
+ // TODO(dmaretskyi): `false` is not a valid value for the annotation. Use a different annotation.
20
+ id: Schema.String.annotations({ [SchemaAST.IdentifierAnnotationId]: 'false' }),
20
21
  });
21
22
 
22
23
  export type ForeignKey = Schema.Schema.Type<typeof ForeignKey_>;
package/src/index.ts CHANGED
@@ -2,10 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ export type * from './collection-sync';
5
6
  export * from './document-structure';
7
+ export * from './echo-feed-codec';
8
+ export * from './foreign-key';
9
+ export * from './query';
6
10
  export * from './reference';
7
11
  export * from './space-doc-version';
8
- export type * from './collection-sync';
9
12
  export * from './space-id';
10
- export * from './foreign-key';
11
- export * from './query';
package/src/query/ast.ts CHANGED
@@ -2,14 +2,15 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { Schema } from 'effect';
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. Null means any type will match',
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.
@@ -46,6 +47,9 @@ const FilterObject_ = Schema.Struct({
46
47
  export interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
47
48
  export const FilterObject: Schema.Schema<FilterObject> = FilterObject_;
48
49
 
50
+ /**
51
+ * Compare.
52
+ */
49
53
  const FilterCompare_ = Schema.Struct({
50
54
  type: Schema.Literal('compare'),
51
55
  operator: Schema.Literal('eq', 'neq', 'gt', 'gte', 'lt', 'lte'),
@@ -54,6 +58,9 @@ const FilterCompare_ = Schema.Struct({
54
58
  export interface FilterCompare extends Schema.Schema.Type<typeof FilterCompare_> {}
55
59
  export const FilterCompare: Schema.Schema<FilterCompare> = FilterCompare_;
56
60
 
61
+ /**
62
+ * In.
63
+ */
57
64
  const FilterIn_ = Schema.Struct({
58
65
  type: Schema.Literal('in'),
59
66
  values: Schema.Array(Schema.Any),
@@ -61,53 +68,106 @@ const FilterIn_ = Schema.Struct({
61
68
  export interface FilterIn extends Schema.Schema.Type<typeof FilterIn_> {}
62
69
  export const FilterIn: Schema.Schema<FilterIn> = FilterIn_;
63
70
 
71
+ /**
72
+ * Contains.
73
+ */
74
+ const FilterContains_ = Schema.Struct({
75
+ type: Schema.Literal('contains'),
76
+ value: Schema.Any,
77
+ });
78
+
79
+ export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
80
+
81
+ /**
82
+ * Predicate for an array property to contain the provided value.
83
+ * Nested objects are matched using strict structural matching.
84
+ */
85
+ export const FilterContains: Schema.Schema<FilterContains> = FilterContains_;
86
+
87
+ /**
88
+ * Filters objects that have certain tag.
89
+ */
90
+ const FilterTag_ = Schema.Struct({
91
+ type: Schema.Literal('tag'),
92
+ tag: Schema.String, // TODO(burdon): Make OR-collection?
93
+ });
94
+
95
+ export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
96
+ export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
97
+
98
+ /**
99
+ * Range.
100
+ */
64
101
  const FilterRange_ = Schema.Struct({
65
102
  type: Schema.Literal('range'),
66
103
  from: Schema.Any,
67
104
  to: Schema.Any,
68
105
  });
106
+
69
107
  export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
70
108
  export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
71
109
 
110
+ /**
111
+ * Text search.
112
+ */
72
113
  const FilterTextSearch_ = Schema.Struct({
73
114
  type: Schema.Literal('text-search'),
74
115
  text: Schema.String,
75
116
  searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
76
117
  });
118
+
77
119
  export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
78
120
  export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
79
121
 
122
+ /**
123
+ * Not.
124
+ */
80
125
  const FilterNot_ = Schema.Struct({
81
126
  type: Schema.Literal('not'),
82
127
  filter: Schema.suspend(() => Filter),
83
128
  });
129
+
84
130
  export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
85
131
  export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
86
132
 
133
+ /**
134
+ * And.
135
+ */
87
136
  const FilterAnd_ = Schema.Struct({
88
137
  type: Schema.Literal('and'),
89
138
  filters: Schema.Array(Schema.suspend(() => Filter)),
90
139
  });
140
+
91
141
  export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
92
142
  export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
93
143
 
144
+ /**
145
+ * Or.
146
+ */
94
147
  const FilterOr_ = Schema.Struct({
95
148
  type: Schema.Literal('or'),
96
149
  filters: Schema.Array(Schema.suspend(() => Filter)),
97
150
  });
151
+
98
152
  export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
99
153
  export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
100
154
 
155
+ /**
156
+ * Union of filters.
157
+ */
101
158
  export const Filter = Schema.Union(
102
159
  FilterObject,
103
- FilterTextSearch,
104
160
  FilterCompare,
105
161
  FilterIn,
162
+ FilterContains,
163
+ FilterTag,
106
164
  FilterRange,
165
+ FilterTextSearch,
107
166
  FilterNot,
108
167
  FilterAnd,
109
168
  FilterOr,
110
- );
169
+ ).annotations({ identifier: 'dxos.org/schema/Filter' });
170
+
111
171
  export type Filter = Schema.Schema.Type<typeof Filter>;
112
172
 
113
173
  /**
@@ -117,6 +177,7 @@ const QuerySelectClause_ = Schema.Struct({
117
177
  type: Schema.Literal('select'),
118
178
  filter: Schema.suspend(() => Filter),
119
179
  });
180
+
120
181
  export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
121
182
  export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
122
183
 
@@ -128,6 +189,7 @@ const QueryFilterClause_ = Schema.Struct({
128
189
  selection: Schema.suspend(() => Query),
129
190
  filter: Schema.suspend(() => Filter),
130
191
  });
192
+
131
193
  export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
132
194
  export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
133
195
 
@@ -139,6 +201,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
139
201
  anchor: Schema.suspend(() => Query),
140
202
  property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
141
203
  });
204
+
142
205
  export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
143
206
  export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
144
207
  QueryReferenceTraversalClause_;
@@ -149,9 +212,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
149
212
  const QueryIncomingReferencesClause_ = Schema.Struct({
150
213
  type: Schema.Literal('incoming-references'),
151
214
  anchor: Schema.suspend(() => Query),
152
- property: Schema.String,
215
+ /**
216
+ * Property path where the reference is located.
217
+ * If null, matches references from any property.
218
+ */
219
+ property: Schema.NullOr(Schema.String),
153
220
  typename: TypenameSpecifier,
154
221
  });
222
+
155
223
  export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
156
224
  export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
157
225
  QueryIncomingReferencesClause_;
@@ -170,6 +238,7 @@ const QueryRelationClause_ = Schema.Struct({
170
238
  direction: Schema.Literal('outgoing', 'incoming', 'both'),
171
239
  filter: Schema.optional(Schema.suspend(() => Filter)),
172
240
  });
241
+
173
242
  export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
174
243
  export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
175
244
 
@@ -181,9 +250,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
181
250
  anchor: Schema.suspend(() => Query),
182
251
  direction: Schema.Literal('source', 'target', 'both'),
183
252
  });
253
+
184
254
  export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
185
255
  export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
186
256
 
257
+ /**
258
+ * Traverse parent-child hierarchy.
259
+ */
260
+ const QueryHierarchyTraversalClause_ = Schema.Struct({
261
+ type: Schema.Literal('hierarchy-traversal'),
262
+ anchor: Schema.suspend(() => Query),
263
+ /**
264
+ * to-parent: traverse from child to parent.
265
+ * to-children: traverse from parent to children.
266
+ */
267
+ direction: Schema.Literal('to-parent', 'to-children'),
268
+ });
269
+
270
+ export interface QueryHierarchyTraversalClause extends Schema.Schema.Type<typeof QueryHierarchyTraversalClause_> {}
271
+ export const QueryHierarchyTraversalClause: Schema.Schema<QueryHierarchyTraversalClause> =
272
+ QueryHierarchyTraversalClause_;
273
+
187
274
  /**
188
275
  * Union of multiple queries.
189
276
  */
@@ -191,6 +278,7 @@ const QueryUnionClause_ = Schema.Struct({
191
278
  type: Schema.Literal('union'),
192
279
  queries: Schema.Array(Schema.suspend(() => Query)),
193
280
  });
281
+
194
282
  export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
195
283
  export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
196
284
 
@@ -202,9 +290,47 @@ const QuerySetDifferenceClause_ = Schema.Struct({
202
290
  source: Schema.suspend(() => Query),
203
291
  exclude: Schema.suspend(() => Query),
204
292
  });
293
+
205
294
  export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
206
295
  export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
207
296
 
297
+ export const OrderDirection = Schema.Literal('asc', 'desc');
298
+ export type OrderDirection = Schema.Schema.Type<typeof OrderDirection>;
299
+
300
+ const Order_ = Schema.Union(
301
+ Schema.Struct({
302
+ // How database wants to order them (in practice - by id).
303
+ kind: Schema.Literal('natural'),
304
+ }),
305
+ Schema.Struct({
306
+ kind: Schema.Literal('property'),
307
+ property: Schema.String,
308
+ direction: OrderDirection,
309
+ }),
310
+ Schema.Struct({
311
+ // Order by relevance rank (for FTS/vector search results).
312
+ // Default direction is 'desc' (higher rank = better match first).
313
+ kind: Schema.Literal('rank'),
314
+ direction: OrderDirection,
315
+ }),
316
+ );
317
+
318
+ export type Order = Schema.Schema.Type<typeof Order_>;
319
+ export const Order: Schema.Schema<Order> = Order_;
320
+
321
+ /**
322
+ * Order the query results.
323
+ * Left-to-right the orders dominate.
324
+ */
325
+ const QueryOrderClause_ = Schema.Struct({
326
+ type: Schema.Literal('order'),
327
+ query: Schema.suspend(() => Query),
328
+ order: Schema.Array(Order),
329
+ });
330
+
331
+ export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
332
+ export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
333
+
208
334
  /**
209
335
  * Add options to a query.
210
336
  */
@@ -213,9 +339,22 @@ const QueryOptionsClause_ = Schema.Struct({
213
339
  query: Schema.suspend(() => Query),
214
340
  options: Schema.suspend(() => QueryOptions),
215
341
  });
342
+
216
343
  export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
217
344
  export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
218
345
 
346
+ /**
347
+ * Limit the number of results.
348
+ */
349
+ const QueryLimitClause_ = Schema.Struct({
350
+ type: Schema.Literal('limit'),
351
+ query: Schema.suspend(() => Query),
352
+ limit: Schema.Number,
353
+ });
354
+
355
+ export interface QueryLimitClause extends Schema.Schema.Type<typeof QueryLimitClause_> {}
356
+ export const QueryLimitClause: Schema.Schema<QueryLimitClause> = QueryLimitClause_;
357
+
219
358
  const Query_ = Schema.Union(
220
359
  QuerySelectClause,
221
360
  QueryFilterClause,
@@ -223,46 +362,85 @@ const Query_ = Schema.Union(
223
362
  QueryIncomingReferencesClause,
224
363
  QueryRelationClause,
225
364
  QueryRelationTraversalClause,
365
+ QueryHierarchyTraversalClause,
226
366
  QueryUnionClause,
227
367
  QuerySetDifferenceClause,
368
+ QueryOrderClause,
228
369
  QueryOptionsClause,
229
- );
370
+ QueryLimitClause,
371
+ ).annotations({ identifier: 'dxos.org/schema/Query' });
230
372
 
231
373
  export type Query = Schema.Schema.Type<typeof Query_>;
232
374
  export const Query: Schema.Schema<Query> = Query_;
233
375
 
234
376
  export const QueryOptions = Schema.Struct({
377
+ /**
378
+ * The nested select statemets will select from the given spaces.
379
+ *
380
+ * NOTE: Spaces and queues are unioned together if both are specified.
381
+ */
235
382
  spaceIds: Schema.optional(Schema.Array(Schema.String)),
383
+
384
+ /**
385
+ * If true, the nested select statements will select from all queues in the spaces specified by `spaceIds`.
386
+ */
387
+ allQueuesFromSpaces: Schema.optional(Schema.Boolean),
388
+
389
+ /**
390
+ * The nested select statemets will select from the given queues.
391
+ *
392
+ * NOTE: Spaces and queues are unioned together if both are specified.
393
+ */
394
+ queues: Schema.optional(Schema.Array(DXN.Schema)),
395
+
396
+ /**
397
+ * Nested select statements will use this option to filter deleted objects.
398
+ */
236
399
  deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
237
400
  });
401
+
238
402
  export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
239
403
 
240
404
  export const visit = (query: Query, visitor: (node: Query) => void) => {
241
- switch (query.type) {
242
- case 'filter':
243
- visit(query.selection, visitor);
244
- break;
245
- case 'reference-traversal':
246
- visit(query.anchor, visitor);
247
- break;
248
- case 'incoming-references':
249
- visit(query.anchor, visitor);
250
- break;
251
- case 'relation':
252
- visit(query.anchor, visitor);
253
- break;
254
- case 'options':
255
- visit(query.query, visitor);
256
- break;
257
- case 'relation-traversal':
258
- visit(query.anchor, visitor);
259
- break;
260
- case 'union':
261
- query.queries.forEach((q) => visit(q, visitor));
262
- break;
263
- case 'set-difference':
264
- visit(query.source, visitor);
265
- visit(query.exclude, visitor);
266
- break;
267
- }
405
+ visitor(query);
406
+
407
+ Match.value(query).pipe(
408
+ Match.when({ type: 'filter' }, ({ selection }) => visit(selection, visitor)),
409
+ Match.when({ type: 'reference-traversal' }, ({ anchor }) => visit(anchor, visitor)),
410
+ Match.when({ type: 'incoming-references' }, ({ anchor }) => visit(anchor, visitor)),
411
+ Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
412
+ Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
413
+ Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
414
+ Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
415
+ Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
416
+ Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
417
+ visit(source, visitor);
418
+ visit(exclude, visitor);
419
+ }),
420
+ Match.when({ type: 'order' }, ({ query }) => visit(query, visitor)),
421
+ Match.when({ type: 'limit' }, ({ query }) => visit(query, visitor)),
422
+ Match.when({ type: 'select' }, () => {}),
423
+ Match.exhaustive,
424
+ );
425
+ };
426
+
427
+ export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
428
+ return Match.value(query).pipe(
429
+ Match.withReturnType<T[]>(),
430
+ Match.when({ type: 'filter' }, ({ selection }) => fold(selection, reducer)),
431
+ Match.when({ type: 'reference-traversal' }, ({ anchor }) => fold(anchor, reducer)),
432
+ Match.when({ type: 'incoming-references' }, ({ anchor }) => fold(anchor, reducer)),
433
+ Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
434
+ Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
435
+ Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
436
+ Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
437
+ Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
438
+ Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
439
+ fold(source, reducer).concat(fold(exclude, reducer)),
440
+ ),
441
+ Match.when({ type: 'order' }, ({ query }) => fold(query, reducer)),
442
+ Match.when({ type: 'limit' }, ({ query }) => fold(query, reducer)),
443
+ Match.when({ type: 'select' }, () => []),
444
+ Match.exhaustive,
445
+ );
268
446
  };
package/src/reference.ts CHANGED
@@ -170,13 +170,16 @@ export const isEncodedReference = (value: any): value is EncodedReference =>
170
170
  export const EncodedReference = Object.freeze({
171
171
  isEncodedReference,
172
172
  getReferenceString: (value: EncodedReference): string => {
173
- assertArgument(isEncodedReference(value), 'invalid reference');
173
+ assertArgument(isEncodedReference(value), 'value', 'invalid reference');
174
174
  return value['/'];
175
175
  },
176
176
  toDXN: (value: EncodedReference): DXN => {
177
177
  return DXN.parse(EncodedReference.getReferenceString(value));
178
178
  },
179
179
  fromDXN: (dxn: DXN): EncodedReference => {
180
- return encodeReference(Reference.fromDXN(dxn));
180
+ return { '/': dxn.toString() };
181
+ },
182
+ fromLegacyTypename: (typename: string): EncodedReference => {
183
+ return { '/': DXN.fromTypename(typename).toString() };
181
184
  },
182
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);