@dxos/echo-protocol 0.8.1 → 0.8.2-main.10c050d
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/index.mjs +341 -1
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +338 -3
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +340 -1
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/document-structure.d.ts +69 -17
- package/dist/types/src/document-structure.d.ts.map +1 -1
- package/dist/types/src/foreign-key.d.ts +19 -0
- package/dist/types/src/foreign-key.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +194 -0
- package/dist/types/src/query/ast.d.ts.map +1 -0
- package/dist/types/src/query/index.d.ts +2 -0
- package/dist/types/src/query/index.d.ts.map +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 +7 -5
- package/src/document-structure.ts +170 -21
- package/src/foreign-key.ts +27 -0
- package/src/index.ts +2 -0
- package/src/query/ast.ts +252 -0
- package/src/query/index.ts +5 -0
- package/src/reference.ts +16 -1
package/src/query/ast.ts
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
|
+
|
|
7
|
+
import { DXN, ObjectId } from '@dxos/keys';
|
|
8
|
+
|
|
9
|
+
import { ForeignKey } from '../foreign-key';
|
|
10
|
+
|
|
11
|
+
const TypenameSpecifier = Schema.Union(DXN.Schema, Schema.Null).annotations({
|
|
12
|
+
description: 'DXN or null. Null means any type will match',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// 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.
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Filter by object type and properties.
|
|
19
|
+
*
|
|
20
|
+
* Clauses are combined using logical AND.
|
|
21
|
+
*/
|
|
22
|
+
// TODO(burdon): Filter object vs. relation.
|
|
23
|
+
const FilterObject_ = Schema.Struct({
|
|
24
|
+
type: Schema.Literal('object'),
|
|
25
|
+
|
|
26
|
+
typename: TypenameSpecifier,
|
|
27
|
+
|
|
28
|
+
id: Schema.optional(Schema.Array(ObjectId)),
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Filter by property.
|
|
32
|
+
* Must not include object ID.
|
|
33
|
+
*/
|
|
34
|
+
props: Schema.Record({
|
|
35
|
+
key: Schema.String.annotations({ description: 'Property name' }),
|
|
36
|
+
value: Schema.suspend(() => Filter),
|
|
37
|
+
}),
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Objects that have any of the given foreign keys.
|
|
41
|
+
*/
|
|
42
|
+
foreignKeys: Schema.optional(Schema.Array(ForeignKey)),
|
|
43
|
+
|
|
44
|
+
// NOTE: Make sure to update `FilterStep.isNoop` if you change this.
|
|
45
|
+
});
|
|
46
|
+
export interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
|
|
47
|
+
export const FilterObject: Schema.Schema<FilterObject> = FilterObject_;
|
|
48
|
+
|
|
49
|
+
const FilterCompare_ = Schema.Struct({
|
|
50
|
+
type: Schema.Literal('compare'),
|
|
51
|
+
operator: Schema.Literal('eq', 'neq', 'gt', 'gte', 'lt', 'lte'),
|
|
52
|
+
value: Schema.Unknown,
|
|
53
|
+
});
|
|
54
|
+
export interface FilterCompare extends Schema.Schema.Type<typeof FilterCompare_> {}
|
|
55
|
+
export const FilterCompare: Schema.Schema<FilterCompare> = FilterCompare_;
|
|
56
|
+
|
|
57
|
+
const FilterIn_ = Schema.Struct({
|
|
58
|
+
type: Schema.Literal('in'),
|
|
59
|
+
values: Schema.Array(Schema.Any),
|
|
60
|
+
});
|
|
61
|
+
export interface FilterIn extends Schema.Schema.Type<typeof FilterIn_> {}
|
|
62
|
+
export const FilterIn: Schema.Schema<FilterIn> = FilterIn_;
|
|
63
|
+
|
|
64
|
+
const FilterRange_ = Schema.Struct({
|
|
65
|
+
type: Schema.Literal('range'),
|
|
66
|
+
from: Schema.Any,
|
|
67
|
+
to: Schema.Any,
|
|
68
|
+
});
|
|
69
|
+
export interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
70
|
+
export const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
71
|
+
|
|
72
|
+
const FilterTextSearch_ = Schema.Struct({
|
|
73
|
+
type: Schema.Literal('text-search'),
|
|
74
|
+
text: Schema.String,
|
|
75
|
+
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
76
|
+
});
|
|
77
|
+
export interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
78
|
+
export const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
79
|
+
|
|
80
|
+
const FilterNot_ = Schema.Struct({
|
|
81
|
+
type: Schema.Literal('not'),
|
|
82
|
+
filter: Schema.suspend(() => Filter),
|
|
83
|
+
});
|
|
84
|
+
export interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
85
|
+
export const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
86
|
+
|
|
87
|
+
const FilterAnd_ = Schema.Struct({
|
|
88
|
+
type: Schema.Literal('and'),
|
|
89
|
+
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
90
|
+
});
|
|
91
|
+
export interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
92
|
+
export const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
93
|
+
|
|
94
|
+
const FilterOr_ = Schema.Struct({
|
|
95
|
+
type: Schema.Literal('or'),
|
|
96
|
+
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
97
|
+
});
|
|
98
|
+
export interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
99
|
+
export const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
100
|
+
|
|
101
|
+
export const Filter = Schema.Union(
|
|
102
|
+
FilterObject,
|
|
103
|
+
FilterTextSearch,
|
|
104
|
+
FilterCompare,
|
|
105
|
+
FilterIn,
|
|
106
|
+
FilterRange,
|
|
107
|
+
FilterNot,
|
|
108
|
+
FilterAnd,
|
|
109
|
+
FilterOr,
|
|
110
|
+
);
|
|
111
|
+
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Query objects by type, id, and/or predicates.
|
|
115
|
+
*/
|
|
116
|
+
const QuerySelectClause_ = Schema.Struct({
|
|
117
|
+
type: Schema.Literal('select'),
|
|
118
|
+
filter: Schema.suspend(() => Filter),
|
|
119
|
+
});
|
|
120
|
+
export interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
121
|
+
export const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Filter objects from selection.
|
|
125
|
+
*/
|
|
126
|
+
const QueryFilterClause_ = Schema.Struct({
|
|
127
|
+
type: Schema.Literal('filter'),
|
|
128
|
+
selection: Schema.suspend(() => Query),
|
|
129
|
+
filter: Schema.suspend(() => Filter),
|
|
130
|
+
});
|
|
131
|
+
export interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
132
|
+
export const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Traverse references from an anchor object.
|
|
136
|
+
*/
|
|
137
|
+
const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
138
|
+
type: Schema.Literal('reference-traversal'),
|
|
139
|
+
anchor: Schema.suspend(() => Query),
|
|
140
|
+
property: Schema.String, // TODO(dmaretskyi): Change to EscapedPropPath.
|
|
141
|
+
});
|
|
142
|
+
export interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
143
|
+
export const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> =
|
|
144
|
+
QueryReferenceTraversalClause_;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Traverse incoming references to an anchor object.
|
|
148
|
+
*/
|
|
149
|
+
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
150
|
+
type: Schema.Literal('incoming-references'),
|
|
151
|
+
anchor: Schema.suspend(() => Query),
|
|
152
|
+
property: Schema.String,
|
|
153
|
+
typename: TypenameSpecifier,
|
|
154
|
+
});
|
|
155
|
+
export interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
156
|
+
export const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> =
|
|
157
|
+
QueryIncomingReferencesClause_;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Traverse relations connecting to an anchor object.
|
|
161
|
+
*/
|
|
162
|
+
const QueryRelationClause_ = Schema.Struct({
|
|
163
|
+
type: Schema.Literal('relation'),
|
|
164
|
+
anchor: Schema.suspend(() => Query),
|
|
165
|
+
/**
|
|
166
|
+
* outgoing: anchor is the source of the relation.
|
|
167
|
+
* incoming: anchor is the target of the relation.
|
|
168
|
+
* both: anchor is either the source or target of the relation.
|
|
169
|
+
*/
|
|
170
|
+
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
171
|
+
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
172
|
+
});
|
|
173
|
+
export interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
174
|
+
export const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Traverse into the source or target of a relation.
|
|
178
|
+
*/
|
|
179
|
+
const QueryRelationTraversalClause_ = Schema.Struct({
|
|
180
|
+
type: Schema.Literal('relation-traversal'),
|
|
181
|
+
anchor: Schema.suspend(() => Query),
|
|
182
|
+
direction: Schema.Literal('source', 'target', 'both'),
|
|
183
|
+
});
|
|
184
|
+
export interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
185
|
+
export const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Union of multiple queries.
|
|
189
|
+
*/
|
|
190
|
+
const QueryUnionClause_ = Schema.Struct({
|
|
191
|
+
type: Schema.Literal('union'),
|
|
192
|
+
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
193
|
+
});
|
|
194
|
+
export interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
195
|
+
export const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Add options to a query.
|
|
199
|
+
*/
|
|
200
|
+
const QueryOptionsClause_ = Schema.Struct({
|
|
201
|
+
type: Schema.Literal('options'),
|
|
202
|
+
query: Schema.suspend(() => Query),
|
|
203
|
+
options: Schema.suspend(() => QueryOptions),
|
|
204
|
+
});
|
|
205
|
+
export interface QueryOptionsClause extends Schema.Schema.Type<typeof QueryOptionsClause_> {}
|
|
206
|
+
export const QueryOptionsClause: Schema.Schema<QueryOptionsClause> = QueryOptionsClause_;
|
|
207
|
+
|
|
208
|
+
const Query_ = Schema.Union(
|
|
209
|
+
QuerySelectClause,
|
|
210
|
+
QueryFilterClause,
|
|
211
|
+
QueryReferenceTraversalClause,
|
|
212
|
+
QueryIncomingReferencesClause,
|
|
213
|
+
QueryRelationClause,
|
|
214
|
+
QueryRelationTraversalClause,
|
|
215
|
+
QueryUnionClause,
|
|
216
|
+
QueryOptionsClause,
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
220
|
+
export const Query: Schema.Schema<Query> = Query_;
|
|
221
|
+
|
|
222
|
+
export const QueryOptions = Schema.Struct({
|
|
223
|
+
spaceIds: Schema.optional(Schema.Array(Schema.String)),
|
|
224
|
+
deleted: Schema.optional(Schema.Literal('include', 'exclude', 'only')),
|
|
225
|
+
});
|
|
226
|
+
export interface QueryOptions extends Schema.Schema.Type<typeof QueryOptions> {}
|
|
227
|
+
|
|
228
|
+
export const visit = (query: Query, visitor: (node: Query) => void) => {
|
|
229
|
+
switch (query.type) {
|
|
230
|
+
case 'filter':
|
|
231
|
+
visit(query.selection, visitor);
|
|
232
|
+
break;
|
|
233
|
+
case 'reference-traversal':
|
|
234
|
+
visit(query.anchor, visitor);
|
|
235
|
+
break;
|
|
236
|
+
case 'incoming-references':
|
|
237
|
+
visit(query.anchor, visitor);
|
|
238
|
+
break;
|
|
239
|
+
case 'relation':
|
|
240
|
+
visit(query.anchor, visitor);
|
|
241
|
+
break;
|
|
242
|
+
case 'options':
|
|
243
|
+
visit(query.query, visitor);
|
|
244
|
+
break;
|
|
245
|
+
case 'relation-traversal':
|
|
246
|
+
visit(query.anchor, visitor);
|
|
247
|
+
break;
|
|
248
|
+
case 'union':
|
|
249
|
+
query.queries.forEach((q) => visit(q, visitor));
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
};
|
package/src/reference.ts
CHANGED
|
@@ -135,7 +135,22 @@ export const encodeReference = (reference: Reference): EncodedReference => ({
|
|
|
135
135
|
'/': reference.toDXN().toString(),
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
export const decodeReference = (value: any) =>
|
|
138
|
+
export const decodeReference = (value: any) => {
|
|
139
|
+
if (typeof value !== 'object' || value === null || typeof value['/'] !== 'string') {
|
|
140
|
+
throw new Error('Invalid reference');
|
|
141
|
+
}
|
|
142
|
+
const dxnString = value['/'];
|
|
143
|
+
|
|
144
|
+
if (
|
|
145
|
+
dxnString.length % 2 === 0 &&
|
|
146
|
+
dxnString.slice(0, dxnString.length / 2) === dxnString.slice(dxnString.length / 2) &&
|
|
147
|
+
dxnString.includes('dxn:echo')
|
|
148
|
+
) {
|
|
149
|
+
throw new Error('Automerge bug detected!');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return Reference.fromDXN(DXN.parse(dxnString));
|
|
153
|
+
};
|
|
139
154
|
|
|
140
155
|
export const isEncodedReference = (value: any): value is EncodedReference =>
|
|
141
156
|
typeof value === 'object' && value !== null && Object.keys(value).length === 1 && typeof value['/'] === 'string';
|