@dxos/echo-protocol 0.8.4-main.fffef41 → 0.8.4-staging.ac66bdf99f
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/dist/lib/{browser → neutral}/index.mjs +259 -34
- 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 +7 -1
- 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/index.d.ts +4 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +127 -29
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/dist/types/src/reference.d.ts +1 -0
- package/dist/types/src/reference.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -11
- package/src/document-structure.ts +11 -1
- package/src/echo-feed-codec.ts +67 -0
- package/src/index.ts +4 -3
- package/src/query/ast.ts +179 -10
- package/src/reference.ts +4 -1
- 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/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-protocol",
|
|
3
|
-
"version": "0.8.4-
|
|
3
|
+
"version": "0.8.4-staging.ac66bdf99f",
|
|
4
4
|
"description": "Core ECHO APIs.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
-
"repository":
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
8
11
|
"license": "MIT",
|
|
9
12
|
"author": "DXOS.org",
|
|
10
|
-
"sideEffects":
|
|
13
|
+
"sideEffects": false,
|
|
11
14
|
"type": "module",
|
|
12
15
|
"exports": {
|
|
13
16
|
".": {
|
|
14
17
|
"source": "./src/index.ts",
|
|
15
18
|
"types": "./dist/types/src/index.d.ts",
|
|
16
|
-
"
|
|
17
|
-
"node": "./dist/lib/node-esm/index.mjs"
|
|
19
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
18
20
|
}
|
|
19
21
|
},
|
|
20
22
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -26,12 +28,12 @@
|
|
|
26
28
|
"src"
|
|
27
29
|
],
|
|
28
30
|
"dependencies": {
|
|
29
|
-
"effect": "3.
|
|
30
|
-
"@dxos/crypto": "0.8.4-
|
|
31
|
-
"@dxos/invariant": "0.8.4-
|
|
32
|
-
"@dxos/keys": "0.8.4-
|
|
33
|
-
"@dxos/protocols": "0.8.4-
|
|
34
|
-
"@dxos/util": "0.8.4-
|
|
31
|
+
"effect": "3.20.0",
|
|
32
|
+
"@dxos/crypto": "0.8.4-staging.ac66bdf99f",
|
|
33
|
+
"@dxos/invariant": "0.8.4-staging.ac66bdf99f",
|
|
34
|
+
"@dxos/keys": "0.8.4-staging.ac66bdf99f",
|
|
35
|
+
"@dxos/protocols": "0.8.4-staging.ac66bdf99f",
|
|
36
|
+
"@dxos/util": "0.8.4-staging.ac66bdf99f"
|
|
35
37
|
},
|
|
36
38
|
"publishConfig": {
|
|
37
39
|
"access": "public"
|
|
@@ -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
|
*/
|
|
@@ -248,13 +252,19 @@ export type ObjectSystem = {
|
|
|
248
252
|
*/
|
|
249
253
|
deleted?: boolean;
|
|
250
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
|
+
|
|
251
261
|
/**
|
|
252
262
|
* Only for relations.
|
|
253
263
|
*/
|
|
254
264
|
source?: EncodedReference;
|
|
255
265
|
|
|
256
266
|
/**
|
|
257
|
-
* Only for relations.
|
|
267
|
+
* Only for relations.
|
|
258
268
|
*/
|
|
259
269
|
target?: EncodedReference;
|
|
260
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
|
+
}
|
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
|
@@ -10,7 +10,7 @@ import { DXN, ObjectId } from '@dxos/keys';
|
|
|
10
10
|
import { ForeignKey } from '../foreign-key';
|
|
11
11
|
|
|
12
12
|
const TypenameSpecifier = Schema.Union(DXN.Schema, Schema.Null).annotations({
|
|
13
|
-
description: 'DXN or null
|
|
13
|
+
description: 'DXN or null; null matches any type',
|
|
14
14
|
});
|
|
15
15
|
|
|
16
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.
|
|
@@ -75,7 +75,9 @@ const FilterContains_ = Schema.Struct({
|
|
|
75
75
|
type: Schema.Literal('contains'),
|
|
76
76
|
value: Schema.Any,
|
|
77
77
|
});
|
|
78
|
+
|
|
78
79
|
export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
|
|
80
|
+
|
|
79
81
|
/**
|
|
80
82
|
* Predicate for an array property to contain the provided value.
|
|
81
83
|
* Nested objects are matched using strict structural matching.
|
|
@@ -89,6 +91,7 @@ const FilterTag_ = Schema.Struct({
|
|
|
89
91
|
type: Schema.Literal('tag'),
|
|
90
92
|
tag: Schema.String, // TODO(burdon): Make OR-collection?
|
|
91
93
|
});
|
|
94
|
+
|
|
92
95
|
export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
|
|
93
96
|
export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
|
|
94
97
|
|
|
@@ -100,9 +103,24 @@ const FilterRange_ = Schema.Struct({
|
|
|
100
103
|
from: Schema.Any,
|
|
101
104
|
to: Schema.Any,
|
|
102
105
|
});
|
|
106
|
+
|
|
103
107
|
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
104
108
|
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
105
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Filter by system timestamp (createdAt / updatedAt).
|
|
112
|
+
* Timestamps are unix milliseconds stored in the object meta index.
|
|
113
|
+
*/
|
|
114
|
+
const FilterTimestamp_ = Schema.Struct({
|
|
115
|
+
type: Schema.Literal('timestamp'),
|
|
116
|
+
field: Schema.Literal('createdAt', 'updatedAt'),
|
|
117
|
+
operator: Schema.Literal('gt', 'gte', 'lt', 'lte'),
|
|
118
|
+
value: Schema.Number,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
export interface FilterTimestamp extends Schema.Schema.Type<typeof FilterTimestamp_> {}
|
|
122
|
+
export const FilterTimestamp: Schema.Schema<FilterTimestamp> = FilterTimestamp_;
|
|
123
|
+
|
|
106
124
|
/**
|
|
107
125
|
* Text search.
|
|
108
126
|
*/
|
|
@@ -111,6 +129,7 @@ const FilterTextSearch_ = Schema.Struct({
|
|
|
111
129
|
text: Schema.String,
|
|
112
130
|
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
113
131
|
});
|
|
132
|
+
|
|
114
133
|
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
115
134
|
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
116
135
|
|
|
@@ -121,6 +140,7 @@ const FilterNot_ = Schema.Struct({
|
|
|
121
140
|
type: Schema.Literal('not'),
|
|
122
141
|
filter: Schema.suspend(() => Filter),
|
|
123
142
|
});
|
|
143
|
+
|
|
124
144
|
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
125
145
|
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
126
146
|
|
|
@@ -131,6 +151,7 @@ const FilterAnd_ = Schema.Struct({
|
|
|
131
151
|
type: Schema.Literal('and'),
|
|
132
152
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
133
153
|
});
|
|
154
|
+
|
|
134
155
|
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
135
156
|
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
136
157
|
|
|
@@ -141,9 +162,25 @@ const FilterOr_ = Schema.Struct({
|
|
|
141
162
|
type: Schema.Literal('or'),
|
|
142
163
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
143
164
|
});
|
|
165
|
+
|
|
144
166
|
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
145
167
|
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
146
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Filter objects that are children of the specified parents.
|
|
171
|
+
* With transitive=true (default), matches grandchildren and beyond.
|
|
172
|
+
*/
|
|
173
|
+
const FilterChildOf_ = Schema.Struct({
|
|
174
|
+
type: Schema.Literal('child-of'),
|
|
175
|
+
/** Parent DXNs to match children of. */
|
|
176
|
+
parents: Schema.Array(DXN.Schema),
|
|
177
|
+
/** Whether to match transitively (grandchildren, etc.). Defaults to true. */
|
|
178
|
+
transitive: Schema.Boolean,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
export interface FilterChildOf extends Schema.Schema.Type<typeof FilterChildOf_> {}
|
|
182
|
+
export const FilterChildOf: Schema.Schema<FilterChildOf> = FilterChildOf_;
|
|
183
|
+
|
|
147
184
|
/**
|
|
148
185
|
* Union of filters.
|
|
149
186
|
*/
|
|
@@ -154,11 +191,14 @@ export const Filter = Schema.Union(
|
|
|
154
191
|
FilterContains,
|
|
155
192
|
FilterTag,
|
|
156
193
|
FilterRange,
|
|
194
|
+
FilterTimestamp,
|
|
157
195
|
FilterTextSearch,
|
|
196
|
+
FilterChildOf,
|
|
158
197
|
FilterNot,
|
|
159
198
|
FilterAnd,
|
|
160
199
|
FilterOr,
|
|
161
|
-
).annotations({ identifier: 'dxos.
|
|
200
|
+
).annotations({ identifier: 'org.dxos.schema.filter' });
|
|
201
|
+
|
|
162
202
|
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
163
203
|
|
|
164
204
|
/**
|
|
@@ -168,6 +208,7 @@ const QuerySelectClause_ = Schema.Struct({
|
|
|
168
208
|
type: Schema.Literal('select'),
|
|
169
209
|
filter: Schema.suspend(() => Filter),
|
|
170
210
|
});
|
|
211
|
+
|
|
171
212
|
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
172
213
|
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
173
214
|
|
|
@@ -179,6 +220,7 @@ const QueryFilterClause_ = Schema.Struct({
|
|
|
179
220
|
selection: Schema.suspend(() => Query),
|
|
180
221
|
filter: Schema.suspend(() => Filter),
|
|
181
222
|
});
|
|
223
|
+
|
|
182
224
|
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
183
225
|
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
184
226
|
|
|
@@ -190,6 +232,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
|
190
232
|
anchor: Schema.suspend(() => Query),
|
|
191
233
|
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
192
234
|
});
|
|
235
|
+
|
|
193
236
|
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
194
237
|
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
195
238
|
QueryReferenceTraversalClause_;
|
|
@@ -200,9 +243,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
|
|
|
200
243
|
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
201
244
|
type: Schema.Literal('incoming-references'),
|
|
202
245
|
anchor: Schema.suspend(() => Query),
|
|
203
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Property path where the reference is located.
|
|
248
|
+
* If null, matches references from any property.
|
|
249
|
+
*/
|
|
250
|
+
property: Schema.NullOr(Schema.String),
|
|
204
251
|
typename: TypenameSpecifier,
|
|
205
252
|
});
|
|
253
|
+
|
|
206
254
|
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
207
255
|
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
208
256
|
QueryIncomingReferencesClause_;
|
|
@@ -221,6 +269,7 @@ const QueryRelationClause_ = Schema.Struct({
|
|
|
221
269
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
222
270
|
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
223
271
|
});
|
|
272
|
+
|
|
224
273
|
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
225
274
|
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
226
275
|
|
|
@@ -232,9 +281,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
|
|
|
232
281
|
anchor: Schema.suspend(() => Query),
|
|
233
282
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
234
283
|
});
|
|
284
|
+
|
|
235
285
|
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
236
286
|
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
237
287
|
|
|
288
|
+
/**
|
|
289
|
+
* Traverse parent-child hierarchy.
|
|
290
|
+
*/
|
|
291
|
+
const QueryHierarchyTraversalClause_ = Schema.Struct({
|
|
292
|
+
type: Schema.Literal('hierarchy-traversal'),
|
|
293
|
+
anchor: Schema.suspend(() => Query),
|
|
294
|
+
/**
|
|
295
|
+
* to-parent: traverse from child to parent.
|
|
296
|
+
* to-children: traverse from parent to children.
|
|
297
|
+
*/
|
|
298
|
+
direction: Schema.Literal('to-parent', 'to-children'),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
export interface QueryHierarchyTraversalClause extends Schema.Schema.Type<typeof QueryHierarchyTraversalClause_> {}
|
|
302
|
+
export const QueryHierarchyTraversalClause: Schema.Schema<QueryHierarchyTraversalClause> =
|
|
303
|
+
QueryHierarchyTraversalClause_;
|
|
304
|
+
|
|
238
305
|
/**
|
|
239
306
|
* Union of multiple queries.
|
|
240
307
|
*/
|
|
@@ -242,6 +309,7 @@ const QueryUnionClause_ = Schema.Struct({
|
|
|
242
309
|
type: Schema.Literal('union'),
|
|
243
310
|
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
244
311
|
});
|
|
312
|
+
|
|
245
313
|
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
246
314
|
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
247
315
|
|
|
@@ -253,6 +321,7 @@ const QuerySetDifferenceClause_ = Schema.Struct({
|
|
|
253
321
|
source: Schema.suspend(() => Query),
|
|
254
322
|
exclude: Schema.suspend(() => Query),
|
|
255
323
|
});
|
|
324
|
+
|
|
256
325
|
export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
|
|
257
326
|
export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
|
|
258
327
|
|
|
@@ -269,7 +338,14 @@ const Order_ = Schema.Union(
|
|
|
269
338
|
property: Schema.String,
|
|
270
339
|
direction: OrderDirection,
|
|
271
340
|
}),
|
|
341
|
+
Schema.Struct({
|
|
342
|
+
// Order by relevance rank (for FTS/vector search results).
|
|
343
|
+
// Default direction is 'desc' (higher rank = better match first).
|
|
344
|
+
kind: Schema.Literal('rank'),
|
|
345
|
+
direction: OrderDirection,
|
|
346
|
+
}),
|
|
272
347
|
);
|
|
348
|
+
|
|
273
349
|
export type Order = Schema.Schema.Type<typeof Order_>;
|
|
274
350
|
export const Order: Schema.Schema<Order> = Order_;
|
|
275
351
|
|
|
@@ -282,6 +358,7 @@ const QueryOrderClause_ = Schema.Struct({
|
|
|
282
358
|
query: Schema.suspend(() => Query),
|
|
283
359
|
order: Schema.Array(Order),
|
|
284
360
|
});
|
|
361
|
+
|
|
285
362
|
export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
|
|
286
363
|
export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
|
|
287
364
|
|
|
@@ -293,9 +370,37 @@ const QueryOptionsClause_ = Schema.Struct({
|
|
|
293
370
|
query: Schema.suspend(() => Query),
|
|
294
371
|
options: Schema.suspend(() => QueryOptions),
|
|
295
372
|
});
|
|
373
|
+
|
|
296
374
|
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
297
375
|
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
298
376
|
|
|
377
|
+
/**
|
|
378
|
+
* Limit the number of results.
|
|
379
|
+
*/
|
|
380
|
+
const QueryLimitClause_ = Schema.Struct({
|
|
381
|
+
type: Schema.Literal('limit'),
|
|
382
|
+
query: Schema.suspend(() => Query),
|
|
383
|
+
limit: Schema.Number,
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
export interface QueryLimitClause extends Schema.Schema.Type<typeof QueryLimitClause_> {}
|
|
387
|
+
export const QueryLimitClause: Schema.Schema<QueryLimitClause> = QueryLimitClause_;
|
|
388
|
+
|
|
389
|
+
export const QueryFromClause_ = Schema.Struct({
|
|
390
|
+
type: Schema.Literal('from'),
|
|
391
|
+
query: Schema.suspend(() => Query),
|
|
392
|
+
from: Schema.Union(
|
|
393
|
+
Schema.TaggedStruct('scope', {
|
|
394
|
+
scope: Schema.suspend(() => Scope),
|
|
395
|
+
}),
|
|
396
|
+
Schema.TaggedStruct('query', {
|
|
397
|
+
query: Schema.suspend(() => Query),
|
|
398
|
+
}),
|
|
399
|
+
),
|
|
400
|
+
});
|
|
401
|
+
export interface QueryFromClause extends Schema.Schema.Type<typeof QueryFromClause_> {}
|
|
402
|
+
export const QueryFromClause: Schema.Schema<QueryFromClause> = QueryFromClause_;
|
|
403
|
+
|
|
299
404
|
const Query_ = Schema.Union(
|
|
300
405
|
QuerySelectClause,
|
|
301
406
|
QueryFilterClause,
|
|
@@ -303,16 +408,31 @@ const Query_ = Schema.Union(
|
|
|
303
408
|
QueryIncomingReferencesClause,
|
|
304
409
|
QueryRelationClause,
|
|
305
410
|
QueryRelationTraversalClause,
|
|
411
|
+
QueryHierarchyTraversalClause,
|
|
306
412
|
QueryUnionClause,
|
|
307
413
|
QuerySetDifferenceClause,
|
|
308
414
|
QueryOrderClause,
|
|
309
415
|
QueryOptionsClause,
|
|
310
|
-
|
|
416
|
+
QueryLimitClause,
|
|
417
|
+
QueryFromClause,
|
|
418
|
+
).annotations({ identifier: 'org.dxos.schema.query' });
|
|
311
419
|
|
|
312
420
|
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
313
421
|
export const Query: Schema.Schema<Query> = Query_;
|
|
314
422
|
|
|
315
423
|
export const QueryOptions = Schema.Struct({
|
|
424
|
+
/**
|
|
425
|
+
* Nested select statements will use this option to filter deleted objects.
|
|
426
|
+
*/
|
|
427
|
+
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Specifies the scope of the data to query from.
|
|
434
|
+
*/
|
|
435
|
+
export const Scope = Schema.Struct({
|
|
316
436
|
/**
|
|
317
437
|
* The nested select statemets will select from the given spaces.
|
|
318
438
|
*
|
|
@@ -320,19 +440,19 @@ export const QueryOptions = Schema.Struct({
|
|
|
320
440
|
*/
|
|
321
441
|
spaceIds: Schema.optional(Schema.Array(Schema.String)),
|
|
322
442
|
|
|
443
|
+
/**
|
|
444
|
+
* If true, the nested select statements will select from all queues in the spaces specified by `spaceIds`.
|
|
445
|
+
*/
|
|
446
|
+
allQueuesFromSpaces: Schema.optional(Schema.Boolean),
|
|
447
|
+
|
|
323
448
|
/**
|
|
324
449
|
* The nested select statemets will select from the given queues.
|
|
325
450
|
*
|
|
326
451
|
* NOTE: Spaces and queues are unioned together if both are specified.
|
|
327
452
|
*/
|
|
328
453
|
queues: Schema.optional(Schema.Array(DXN.Schema)),
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Nested select statements will use this option to filter deleted objects.
|
|
332
|
-
*/
|
|
333
|
-
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
334
454
|
});
|
|
335
|
-
export interface
|
|
455
|
+
export interface Scope extends Schema.Schema.Type<typeof Scope> {}
|
|
336
456
|
|
|
337
457
|
export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
338
458
|
visitor(query);
|
|
@@ -344,17 +464,57 @@ export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
|
344
464
|
Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
|
|
345
465
|
Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
|
|
346
466
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
467
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
347
468
|
Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
|
|
348
469
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
|
|
349
470
|
visit(source, visitor);
|
|
350
471
|
visit(exclude, visitor);
|
|
351
472
|
}),
|
|
352
473
|
Match.when({ type: 'order' }, ({ query }) => visit(query, visitor)),
|
|
474
|
+
Match.when({ type: 'limit' }, ({ query }) => visit(query, visitor)),
|
|
475
|
+
Match.when({ type: 'from' }, (node) => {
|
|
476
|
+
visit(node.query, visitor);
|
|
477
|
+
if (node.from._tag === 'query') {
|
|
478
|
+
visit(node.from.query, visitor);
|
|
479
|
+
}
|
|
480
|
+
}),
|
|
353
481
|
Match.when({ type: 'select' }, () => {}),
|
|
354
482
|
Match.exhaustive,
|
|
355
483
|
);
|
|
356
484
|
};
|
|
357
485
|
|
|
486
|
+
/**
|
|
487
|
+
* Recursively transforms a query tree bottom-up.
|
|
488
|
+
* The mapper receives each node with its children already transformed.
|
|
489
|
+
*/
|
|
490
|
+
export const map = (query: Query, mapper: (node: Query) => Query): Query => {
|
|
491
|
+
const mapped: Query = Match.value(query).pipe(
|
|
492
|
+
Match.when({ type: 'filter' }, (node) => ({ ...node, selection: map(node.selection, mapper) })),
|
|
493
|
+
Match.when({ type: 'reference-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
494
|
+
Match.when({ type: 'incoming-references' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
495
|
+
Match.when({ type: 'relation' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
496
|
+
Match.when({ type: 'relation-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
497
|
+
Match.when({ type: 'hierarchy-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
498
|
+
Match.when({ type: 'options' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
499
|
+
Match.when({ type: 'order' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
500
|
+
Match.when({ type: 'limit' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
501
|
+
Match.when({ type: 'from' }, (node) => ({
|
|
502
|
+
...node,
|
|
503
|
+
query: map(node.query, mapper),
|
|
504
|
+
...(node.from._tag === 'query' ? { from: { _tag: 'query' as const, query: map(node.from.query, mapper) } } : {}),
|
|
505
|
+
})),
|
|
506
|
+
Match.when({ type: 'union' }, (node) => ({ ...node, queries: node.queries.map((q) => map(q, mapper)) })),
|
|
507
|
+
Match.when({ type: 'set-difference' }, (node) => ({
|
|
508
|
+
...node,
|
|
509
|
+
source: map(node.source, mapper),
|
|
510
|
+
exclude: map(node.exclude, mapper),
|
|
511
|
+
})),
|
|
512
|
+
Match.when({ type: 'select' }, (node) => node),
|
|
513
|
+
Match.exhaustive,
|
|
514
|
+
);
|
|
515
|
+
return mapper(mapped);
|
|
516
|
+
};
|
|
517
|
+
|
|
358
518
|
export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
359
519
|
return Match.value(query).pipe(
|
|
360
520
|
Match.withReturnType<T[]>(),
|
|
@@ -364,11 +524,20 @@ export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
|
364
524
|
Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
|
|
365
525
|
Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
|
|
366
526
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
527
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
367
528
|
Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
|
|
368
529
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
|
|
369
530
|
fold(source, reducer).concat(fold(exclude, reducer)),
|
|
370
531
|
),
|
|
371
532
|
Match.when({ type: 'order' }, ({ query }) => fold(query, reducer)),
|
|
533
|
+
Match.when({ type: 'limit' }, ({ query }) => fold(query, reducer)),
|
|
534
|
+
Match.when({ type: 'from' }, (node) => {
|
|
535
|
+
const results = fold(node.query, reducer);
|
|
536
|
+
if (node.from._tag === 'query') {
|
|
537
|
+
return results.concat(fold(node.from.query, reducer));
|
|
538
|
+
}
|
|
539
|
+
return results;
|
|
540
|
+
}),
|
|
372
541
|
Match.when({ type: 'select' }, () => []),
|
|
373
542
|
Match.exhaustive,
|
|
374
543
|
);
|
package/src/reference.ts
CHANGED
|
@@ -177,6 +177,9 @@ export const EncodedReference = Object.freeze({
|
|
|
177
177
|
return DXN.parse(EncodedReference.getReferenceString(value));
|
|
178
178
|
},
|
|
179
179
|
fromDXN: (dxn: DXN): EncodedReference => {
|
|
180
|
-
return
|
|
180
|
+
return { '/': dxn.toString() };
|
|
181
|
+
},
|
|
182
|
+
fromLegacyTypename: (typename: string): EncodedReference => {
|
|
183
|
+
return { '/': DXN.fromTypename(typename).toString() };
|
|
181
184
|
},
|
|
182
185
|
});
|