@dxos/echo-protocol 0.8.4-main.a4bbb77 → 0.8.4-main.abd8ff62ef
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 +306 -97
- 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 +16 -2
- 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/foreign-key.d.ts +1 -1
- package/dist/types/src/foreign-key.d.ts.map +1 -1
- 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 +137 -33
- 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/src/space-id.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +13 -14
- package/src/document-structure.ts +23 -1
- package/src/echo-feed-codec.ts +67 -0
- package/src/foreign-key.ts +2 -1
- package/src/index.ts +4 -3
- package/src/query/ast.ts +187 -12
- 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 -608
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/package.json
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-protocol",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.abd8ff62ef",
|
|
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",
|
|
21
|
-
"typesVersions": {
|
|
22
|
-
"*": {}
|
|
23
|
-
},
|
|
24
23
|
"files": [
|
|
25
24
|
"dist",
|
|
26
25
|
"src"
|
|
27
26
|
],
|
|
28
27
|
"dependencies": {
|
|
29
|
-
"effect": "3.
|
|
30
|
-
"@dxos/crypto": "0.8.4-main.
|
|
31
|
-
"@dxos/
|
|
32
|
-
"@dxos/
|
|
33
|
-
"@dxos/protocols": "0.8.4-main.
|
|
34
|
-
"@dxos/util": "0.8.4-main.
|
|
28
|
+
"effect": "3.20.0",
|
|
29
|
+
"@dxos/crypto": "0.8.4-main.abd8ff62ef",
|
|
30
|
+
"@dxos/keys": "0.8.4-main.abd8ff62ef",
|
|
31
|
+
"@dxos/invariant": "0.8.4-main.abd8ff62ef",
|
|
32
|
+
"@dxos/protocols": "0.8.4-main.abd8ff62ef",
|
|
33
|
+
"@dxos/util": "0.8.4-main.abd8ff62ef"
|
|
35
34
|
},
|
|
36
35
|
"publishConfig": {
|
|
37
36
|
"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
|
*/
|
|
@@ -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.
|
|
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
|
+
}
|
package/src/foreign-key.ts
CHANGED
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
|
|
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
|
|
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.
|
|
@@ -74,7 +75,9 @@ const FilterContains_ = Schema.Struct({
|
|
|
74
75
|
type: Schema.Literal('contains'),
|
|
75
76
|
value: Schema.Any,
|
|
76
77
|
});
|
|
78
|
+
|
|
77
79
|
export interface FilterContains extends Schema.Schema.Type<typeof FilterContains_> {}
|
|
80
|
+
|
|
78
81
|
/**
|
|
79
82
|
* Predicate for an array property to contain the provided value.
|
|
80
83
|
* Nested objects are matched using strict structural matching.
|
|
@@ -82,12 +85,13 @@ export interface FilterContains extends Schema.Schema.Type<typeof FilterContains
|
|
|
82
85
|
export const FilterContains: Schema.Schema<FilterContains> = FilterContains_;
|
|
83
86
|
|
|
84
87
|
/**
|
|
85
|
-
*
|
|
88
|
+
* Filters objects that have certain tag.
|
|
86
89
|
*/
|
|
87
90
|
const FilterTag_ = Schema.Struct({
|
|
88
91
|
type: Schema.Literal('tag'),
|
|
89
92
|
tag: Schema.String, // TODO(burdon): Make OR-collection?
|
|
90
93
|
});
|
|
94
|
+
|
|
91
95
|
export interface FilterTag extends Schema.Schema.Type<typeof FilterTag_> {}
|
|
92
96
|
export const FilterTag: Schema.Schema<FilterTag> = FilterTag_;
|
|
93
97
|
|
|
@@ -99,9 +103,24 @@ const FilterRange_ = Schema.Struct({
|
|
|
99
103
|
from: Schema.Any,
|
|
100
104
|
to: Schema.Any,
|
|
101
105
|
});
|
|
106
|
+
|
|
102
107
|
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
103
108
|
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
104
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
|
+
|
|
105
124
|
/**
|
|
106
125
|
* Text search.
|
|
107
126
|
*/
|
|
@@ -110,6 +129,7 @@ const FilterTextSearch_ = Schema.Struct({
|
|
|
110
129
|
text: Schema.String,
|
|
111
130
|
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
112
131
|
});
|
|
132
|
+
|
|
113
133
|
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
114
134
|
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
115
135
|
|
|
@@ -120,6 +140,7 @@ const FilterNot_ = Schema.Struct({
|
|
|
120
140
|
type: Schema.Literal('not'),
|
|
121
141
|
filter: Schema.suspend(() => Filter),
|
|
122
142
|
});
|
|
143
|
+
|
|
123
144
|
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
124
145
|
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
125
146
|
|
|
@@ -130,6 +151,7 @@ const FilterAnd_ = Schema.Struct({
|
|
|
130
151
|
type: Schema.Literal('and'),
|
|
131
152
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
132
153
|
});
|
|
154
|
+
|
|
133
155
|
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
134
156
|
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
135
157
|
|
|
@@ -140,9 +162,25 @@ const FilterOr_ = Schema.Struct({
|
|
|
140
162
|
type: Schema.Literal('or'),
|
|
141
163
|
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
142
164
|
});
|
|
165
|
+
|
|
143
166
|
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
144
167
|
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
145
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
|
+
|
|
146
184
|
/**
|
|
147
185
|
* Union of filters.
|
|
148
186
|
*/
|
|
@@ -153,11 +191,14 @@ export const Filter = Schema.Union(
|
|
|
153
191
|
FilterContains,
|
|
154
192
|
FilterTag,
|
|
155
193
|
FilterRange,
|
|
194
|
+
FilterTimestamp,
|
|
156
195
|
FilterTextSearch,
|
|
196
|
+
FilterChildOf,
|
|
157
197
|
FilterNot,
|
|
158
198
|
FilterAnd,
|
|
159
199
|
FilterOr,
|
|
160
|
-
).annotations({ identifier: 'dxos.
|
|
200
|
+
).annotations({ identifier: 'org.dxos.schema.filter' });
|
|
201
|
+
|
|
161
202
|
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
162
203
|
|
|
163
204
|
/**
|
|
@@ -167,6 +208,7 @@ const QuerySelectClause_ = Schema.Struct({
|
|
|
167
208
|
type: Schema.Literal('select'),
|
|
168
209
|
filter: Schema.suspend(() => Filter),
|
|
169
210
|
});
|
|
211
|
+
|
|
170
212
|
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
171
213
|
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
172
214
|
|
|
@@ -178,6 +220,7 @@ const QueryFilterClause_ = Schema.Struct({
|
|
|
178
220
|
selection: Schema.suspend(() => Query),
|
|
179
221
|
filter: Schema.suspend(() => Filter),
|
|
180
222
|
});
|
|
223
|
+
|
|
181
224
|
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
182
225
|
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
183
226
|
|
|
@@ -189,6 +232,7 @@ const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
|
189
232
|
anchor: Schema.suspend(() => Query),
|
|
190
233
|
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
191
234
|
});
|
|
235
|
+
|
|
192
236
|
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
193
237
|
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
194
238
|
QueryReferenceTraversalClause_;
|
|
@@ -199,9 +243,14 @@ export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversa
|
|
|
199
243
|
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
200
244
|
type: Schema.Literal('incoming-references'),
|
|
201
245
|
anchor: Schema.suspend(() => Query),
|
|
202
|
-
|
|
246
|
+
/**
|
|
247
|
+
* Property path where the reference is located.
|
|
248
|
+
* If null, matches references from any property.
|
|
249
|
+
*/
|
|
250
|
+
property: Schema.NullOr(Schema.String),
|
|
203
251
|
typename: TypenameSpecifier,
|
|
204
252
|
});
|
|
253
|
+
|
|
205
254
|
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
206
255
|
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
207
256
|
QueryIncomingReferencesClause_;
|
|
@@ -220,6 +269,7 @@ const QueryRelationClause_ = Schema.Struct({
|
|
|
220
269
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
221
270
|
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
222
271
|
});
|
|
272
|
+
|
|
223
273
|
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
224
274
|
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
225
275
|
|
|
@@ -231,9 +281,27 @@ const QueryRelationTraversalClause_ = Schema.Struct({
|
|
|
231
281
|
anchor: Schema.suspend(() => Query),
|
|
232
282
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
233
283
|
});
|
|
284
|
+
|
|
234
285
|
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
235
286
|
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
236
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
|
+
|
|
237
305
|
/**
|
|
238
306
|
* Union of multiple queries.
|
|
239
307
|
*/
|
|
@@ -241,6 +309,7 @@ const QueryUnionClause_ = Schema.Struct({
|
|
|
241
309
|
type: Schema.Literal('union'),
|
|
242
310
|
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
243
311
|
});
|
|
312
|
+
|
|
244
313
|
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
245
314
|
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
246
315
|
|
|
@@ -252,6 +321,7 @@ const QuerySetDifferenceClause_ = Schema.Struct({
|
|
|
252
321
|
source: Schema.suspend(() => Query),
|
|
253
322
|
exclude: Schema.suspend(() => Query),
|
|
254
323
|
});
|
|
324
|
+
|
|
255
325
|
export interface QuerySetDifferenceClause extends Schema.Schema.Type<typeof QuerySetDifferenceClause_> {}
|
|
256
326
|
export const QuerySetDifferenceClause: Schema.Schema<QuerySetDifferenceClause> = QuerySetDifferenceClause_;
|
|
257
327
|
|
|
@@ -268,7 +338,14 @@ const Order_ = Schema.Union(
|
|
|
268
338
|
property: Schema.String,
|
|
269
339
|
direction: OrderDirection,
|
|
270
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
|
+
}),
|
|
271
347
|
);
|
|
348
|
+
|
|
272
349
|
export type Order = Schema.Schema.Type<typeof Order_>;
|
|
273
350
|
export const Order: Schema.Schema<Order> = Order_;
|
|
274
351
|
|
|
@@ -281,6 +358,7 @@ const QueryOrderClause_ = Schema.Struct({
|
|
|
281
358
|
query: Schema.suspend(() => Query),
|
|
282
359
|
order: Schema.Array(Order),
|
|
283
360
|
});
|
|
361
|
+
|
|
284
362
|
export interface QueryOrderClause extends Schema.Schema.Type<typeof QueryOrderClause_> {}
|
|
285
363
|
export const QueryOrderClause: Schema.Schema<QueryOrderClause> = QueryOrderClause_;
|
|
286
364
|
|
|
@@ -292,9 +370,37 @@ const QueryOptionsClause_ = Schema.Struct({
|
|
|
292
370
|
query: Schema.suspend(() => Query),
|
|
293
371
|
options: Schema.suspend(() => QueryOptions),
|
|
294
372
|
});
|
|
373
|
+
|
|
295
374
|
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
296
375
|
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
297
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
|
+
|
|
298
404
|
const Query_ = Schema.Union(
|
|
299
405
|
QuerySelectClause,
|
|
300
406
|
QueryFilterClause,
|
|
@@ -302,16 +408,36 @@ const Query_ = Schema.Union(
|
|
|
302
408
|
QueryIncomingReferencesClause,
|
|
303
409
|
QueryRelationClause,
|
|
304
410
|
QueryRelationTraversalClause,
|
|
411
|
+
QueryHierarchyTraversalClause,
|
|
305
412
|
QueryUnionClause,
|
|
306
413
|
QuerySetDifferenceClause,
|
|
307
414
|
QueryOrderClause,
|
|
308
415
|
QueryOptionsClause,
|
|
309
|
-
|
|
416
|
+
QueryLimitClause,
|
|
417
|
+
QueryFromClause,
|
|
418
|
+
).annotations({ identifier: 'org.dxos.schema.query' });
|
|
310
419
|
|
|
311
420
|
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
312
421
|
export const Query: Schema.Schema<Query> = Query_;
|
|
313
422
|
|
|
314
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
|
+
* Diagnostics-only label for logs / tooling (not used by execution semantics).
|
|
431
|
+
*/
|
|
432
|
+
debugLabel: Schema.optional(Schema.String),
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Specifies the scope of the data to query from.
|
|
439
|
+
*/
|
|
440
|
+
export const Scope = Schema.Struct({
|
|
315
441
|
/**
|
|
316
442
|
* The nested select statemets will select from the given spaces.
|
|
317
443
|
*
|
|
@@ -319,19 +445,19 @@ export const QueryOptions = Schema.Struct({
|
|
|
319
445
|
*/
|
|
320
446
|
spaceIds: Schema.optional(Schema.Array(Schema.String)),
|
|
321
447
|
|
|
448
|
+
/**
|
|
449
|
+
* If true, the nested select statements will select from all queues in the spaces specified by `spaceIds`.
|
|
450
|
+
*/
|
|
451
|
+
allQueuesFromSpaces: Schema.optional(Schema.Boolean),
|
|
452
|
+
|
|
322
453
|
/**
|
|
323
454
|
* The nested select statemets will select from the given queues.
|
|
324
455
|
*
|
|
325
456
|
* NOTE: Spaces and queues are unioned together if both are specified.
|
|
326
457
|
*/
|
|
327
458
|
queues: Schema.optional(Schema.Array(DXN.Schema)),
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Nested select statements will use this option to filter deleted objects.
|
|
331
|
-
*/
|
|
332
|
-
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
333
459
|
});
|
|
334
|
-
export interface
|
|
460
|
+
export interface Scope extends Schema.Schema.Type<typeof Scope> {}
|
|
335
461
|
|
|
336
462
|
export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
337
463
|
visitor(query);
|
|
@@ -343,17 +469,57 @@ export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
|
343
469
|
Match.when({ type: 'relation' }, ({ anchor }) => visit(anchor, visitor)),
|
|
344
470
|
Match.when({ type: 'options' }, ({ query }) => visit(query, visitor)),
|
|
345
471
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
472
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => visit(anchor, visitor)),
|
|
346
473
|
Match.when({ type: 'union' }, ({ queries }) => queries.forEach((q) => visit(q, visitor))),
|
|
347
474
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) => {
|
|
348
475
|
visit(source, visitor);
|
|
349
476
|
visit(exclude, visitor);
|
|
350
477
|
}),
|
|
351
478
|
Match.when({ type: 'order' }, ({ query }) => visit(query, visitor)),
|
|
479
|
+
Match.when({ type: 'limit' }, ({ query }) => visit(query, visitor)),
|
|
480
|
+
Match.when({ type: 'from' }, (node) => {
|
|
481
|
+
visit(node.query, visitor);
|
|
482
|
+
if (node.from._tag === 'query') {
|
|
483
|
+
visit(node.from.query, visitor);
|
|
484
|
+
}
|
|
485
|
+
}),
|
|
352
486
|
Match.when({ type: 'select' }, () => {}),
|
|
353
487
|
Match.exhaustive,
|
|
354
488
|
);
|
|
355
489
|
};
|
|
356
490
|
|
|
491
|
+
/**
|
|
492
|
+
* Recursively transforms a query tree bottom-up.
|
|
493
|
+
* The mapper receives each node with its children already transformed.
|
|
494
|
+
*/
|
|
495
|
+
export const map = (query: Query, mapper: (node: Query) => Query): Query => {
|
|
496
|
+
const mapped: Query = Match.value(query).pipe(
|
|
497
|
+
Match.when({ type: 'filter' }, (node) => ({ ...node, selection: map(node.selection, mapper) })),
|
|
498
|
+
Match.when({ type: 'reference-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
499
|
+
Match.when({ type: 'incoming-references' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
500
|
+
Match.when({ type: 'relation' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
501
|
+
Match.when({ type: 'relation-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
502
|
+
Match.when({ type: 'hierarchy-traversal' }, (node) => ({ ...node, anchor: map(node.anchor, mapper) })),
|
|
503
|
+
Match.when({ type: 'options' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
504
|
+
Match.when({ type: 'order' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
505
|
+
Match.when({ type: 'limit' }, (node) => ({ ...node, query: map(node.query, mapper) })),
|
|
506
|
+
Match.when({ type: 'from' }, (node) => ({
|
|
507
|
+
...node,
|
|
508
|
+
query: map(node.query, mapper),
|
|
509
|
+
...(node.from._tag === 'query' ? { from: { _tag: 'query' as const, query: map(node.from.query, mapper) } } : {}),
|
|
510
|
+
})),
|
|
511
|
+
Match.when({ type: 'union' }, (node) => ({ ...node, queries: node.queries.map((q) => map(q, mapper)) })),
|
|
512
|
+
Match.when({ type: 'set-difference' }, (node) => ({
|
|
513
|
+
...node,
|
|
514
|
+
source: map(node.source, mapper),
|
|
515
|
+
exclude: map(node.exclude, mapper),
|
|
516
|
+
})),
|
|
517
|
+
Match.when({ type: 'select' }, (node) => node),
|
|
518
|
+
Match.exhaustive,
|
|
519
|
+
);
|
|
520
|
+
return mapper(mapped);
|
|
521
|
+
};
|
|
522
|
+
|
|
357
523
|
export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
358
524
|
return Match.value(query).pipe(
|
|
359
525
|
Match.withReturnType<T[]>(),
|
|
@@ -363,11 +529,20 @@ export const fold = <T>(query: Query, reducer: (node: Query) => T): T[] => {
|
|
|
363
529
|
Match.when({ type: 'relation' }, ({ anchor }) => fold(anchor, reducer)),
|
|
364
530
|
Match.when({ type: 'options' }, ({ query }) => fold(query, reducer)),
|
|
365
531
|
Match.when({ type: 'relation-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
532
|
+
Match.when({ type: 'hierarchy-traversal' }, ({ anchor }) => fold(anchor, reducer)),
|
|
366
533
|
Match.when({ type: 'union' }, ({ queries }) => queries.flatMap((q) => fold(q, reducer))),
|
|
367
534
|
Match.when({ type: 'set-difference' }, ({ source, exclude }) =>
|
|
368
535
|
fold(source, reducer).concat(fold(exclude, reducer)),
|
|
369
536
|
),
|
|
370
537
|
Match.when({ type: 'order' }, ({ query }) => fold(query, reducer)),
|
|
538
|
+
Match.when({ type: 'limit' }, ({ query }) => fold(query, reducer)),
|
|
539
|
+
Match.when({ type: 'from' }, (node) => {
|
|
540
|
+
const results = fold(node.query, reducer);
|
|
541
|
+
if (node.from._tag === 'query') {
|
|
542
|
+
return results.concat(fold(node.from.query, reducer));
|
|
543
|
+
}
|
|
544
|
+
return results;
|
|
545
|
+
}),
|
|
371
546
|
Match.when({ type: 'select' }, () => []),
|
|
372
547
|
Match.exhaustive,
|
|
373
548
|
);
|
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
|
});
|