@dxos/echo 0.8.4-main.84f28bd → 0.8.4-main.a4bbb77
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/README.md +1 -1
- package/dist/lib/browser/{chunk-SVSJEELN.mjs → chunk-I4YEWYJA.mjs} +431 -168
- package/dist/lib/browser/chunk-I4YEWYJA.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node-esm/{chunk-53HXWYR6.mjs → chunk-AWWFAF75.mjs} +431 -168
- package/dist/lib/node-esm/chunk-AWWFAF75.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +9 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +1 -1
- package/dist/types/src/Obj.d.ts +108 -13
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +10 -7
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Type.d.ts +1 -1
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +72 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/dsl.d.ts +32 -5
- package/dist/types/src/query/dsl.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -14
- package/src/Obj.ts +242 -22
- package/src/Relation.ts +22 -11
- package/src/Type.ts +4 -2
- package/src/errors.ts +18 -0
- package/src/index.ts +4 -1
- package/src/query/dsl.test.ts +40 -1
- package/src/query/dsl.ts +127 -14
- package/src/test/api.test.ts +16 -9
- package/dist/lib/browser/chunk-SVSJEELN.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-53HXWYR6.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.a4bbb77",
|
|
4
4
|
"description": "ECHO API",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -10,31 +10,37 @@
|
|
|
10
10
|
"type": "module",
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
+
"source": "./src/index.ts",
|
|
13
14
|
"types": "./dist/types/src/index.d.ts",
|
|
14
15
|
"browser": "./dist/lib/browser/index.mjs",
|
|
15
16
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
16
17
|
},
|
|
17
18
|
"./Type": {
|
|
19
|
+
"source": "./src/Type.ts",
|
|
18
20
|
"types": "./dist/types/src/Type.d.ts",
|
|
19
21
|
"browser": "./dist/lib/browser/Type.mjs",
|
|
20
22
|
"node": "./dist/lib/node-esm/Type.mjs"
|
|
21
23
|
},
|
|
22
24
|
"./Obj": {
|
|
25
|
+
"source": "./src/Obj.ts",
|
|
23
26
|
"types": "./dist/types/src/Obj.d.ts",
|
|
24
27
|
"browser": "./dist/lib/browser/Obj.mjs",
|
|
25
28
|
"node": "./dist/lib/node-esm/Obj.mjs"
|
|
26
29
|
},
|
|
27
30
|
"./Relation": {
|
|
31
|
+
"source": "./src/Relation.ts",
|
|
28
32
|
"types": "./dist/types/src/Relation.d.ts",
|
|
29
33
|
"browser": "./dist/lib/browser/Relation.mjs",
|
|
30
34
|
"node": "./dist/lib/node-esm/Relation.mjs"
|
|
31
35
|
},
|
|
32
36
|
"./Ref": {
|
|
37
|
+
"source": "./src/Ref.ts",
|
|
33
38
|
"types": "./dist/types/src/Ref.d.ts",
|
|
34
39
|
"browser": "./dist/lib/browser/Ref.mjs",
|
|
35
40
|
"node": "./dist/lib/node-esm/Ref.mjs"
|
|
36
41
|
},
|
|
37
42
|
"./testing": {
|
|
43
|
+
"source": "./src/testing/types.ts",
|
|
38
44
|
"types": "./dist/types/src/testing/types.d.ts",
|
|
39
45
|
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
40
46
|
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
@@ -65,19 +71,20 @@
|
|
|
65
71
|
"src"
|
|
66
72
|
],
|
|
67
73
|
"dependencies": {
|
|
68
|
-
"@preact/signals-core": "^1.
|
|
69
|
-
"effect": "3.
|
|
70
|
-
"@dxos/debug": "0.8.4-main.
|
|
71
|
-
"@dxos/echo-
|
|
72
|
-
"@dxos/echo-
|
|
73
|
-
"@dxos/echo-signals": "0.8.4-main.
|
|
74
|
-
"@dxos/effect": "0.8.4-main.
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/live-object": "0.8.4-main.
|
|
78
|
-
"@dxos/log": "0.8.4-main.
|
|
79
|
-
"@dxos/node-std": "0.8.4-main.
|
|
80
|
-
"@dxos/util": "0.8.4-main.
|
|
74
|
+
"@preact/signals-core": "^1.12.1",
|
|
75
|
+
"effect": "3.18.3",
|
|
76
|
+
"@dxos/debug": "0.8.4-main.a4bbb77",
|
|
77
|
+
"@dxos/echo-protocol": "0.8.4-main.a4bbb77",
|
|
78
|
+
"@dxos/echo-schema": "0.8.4-main.a4bbb77",
|
|
79
|
+
"@dxos/echo-signals": "0.8.4-main.a4bbb77",
|
|
80
|
+
"@dxos/effect": "0.8.4-main.a4bbb77",
|
|
81
|
+
"@dxos/errors": "0.8.4-main.a4bbb77",
|
|
82
|
+
"@dxos/invariant": "0.8.4-main.a4bbb77",
|
|
83
|
+
"@dxos/live-object": "0.8.4-main.a4bbb77",
|
|
84
|
+
"@dxos/log": "0.8.4-main.a4bbb77",
|
|
85
|
+
"@dxos/node-std": "0.8.4-main.a4bbb77",
|
|
86
|
+
"@dxos/util": "0.8.4-main.a4bbb77",
|
|
87
|
+
"@dxos/keys": "0.8.4-main.a4bbb77"
|
|
81
88
|
},
|
|
82
89
|
"publishConfig": {
|
|
83
90
|
"access": "public"
|
package/src/Obj.ts
CHANGED
|
@@ -3,48 +3,72 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Schema } from 'effect';
|
|
6
|
+
import { dual } from 'effect/Function';
|
|
6
7
|
|
|
7
8
|
import * as EchoSchema from '@dxos/echo-schema';
|
|
8
9
|
import { assertArgument, invariant } from '@dxos/invariant';
|
|
9
10
|
import { type DXN } from '@dxos/keys';
|
|
10
|
-
import
|
|
11
|
+
import * as LiveObject from '@dxos/live-object';
|
|
11
12
|
import { live } from '@dxos/live-object';
|
|
12
|
-
import { assumeType } from '@dxos/util';
|
|
13
|
+
import { assumeType, deepMapValues } from '@dxos/util';
|
|
13
14
|
|
|
14
15
|
import type * as Ref from './Ref';
|
|
15
16
|
import type * as Relation from './Relation';
|
|
16
|
-
import
|
|
17
|
+
import * as Type from './Type';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
19
|
+
/**
|
|
20
|
+
* NOTE: Don't export: Obj.Any and Obj.Obj form the public API.
|
|
21
|
+
*/
|
|
22
|
+
interface BaseObj extends EchoSchema.HasId, Type.OfKind<EchoSchema.EntityKind.Object> {}
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Object type with specific properties.
|
|
25
26
|
*/
|
|
26
|
-
export type Obj<Props> =
|
|
27
|
+
export type Obj<Props> = BaseObj & Props;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Base type for all ECHO objects.
|
|
30
31
|
*/
|
|
31
|
-
export interface Any extends
|
|
32
|
+
export interface Any extends BaseObj {}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
export const Any = Schema.Struct({}).pipe(
|
|
35
|
+
Type.Obj({
|
|
36
|
+
typename: 'dxos.org/types/Any',
|
|
37
|
+
version: '0.1.0',
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
type Props<T = any> = { id?: EchoSchema.ObjectId } & Type.Properties<T>;
|
|
42
|
+
|
|
43
|
+
export type MakeProps<T extends Type.Obj.Any> = NoInfer<Props<Schema.Schema.Type<T>>> & {
|
|
44
|
+
[Meta]?: Partial<EchoSchema.ObjectMeta>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const Meta: unique symbol = EchoSchema.MetaId as any;
|
|
48
|
+
|
|
49
|
+
// TODO(dmaretskyi): Expose Meta = EchoSchema.MetaId.
|
|
36
50
|
|
|
37
51
|
/**
|
|
38
52
|
* Creates new object.
|
|
53
|
+
* @param schema - Object schema.
|
|
54
|
+
* @param props - Object properties.
|
|
55
|
+
* @param meta - Object metadata (deprecated) -- pass with Obj.Meta.
|
|
56
|
+
*
|
|
57
|
+
* Meta can be passed as a symbol in `props`.
|
|
58
|
+
*
|
|
59
|
+
* Example:
|
|
60
|
+
* ```ts
|
|
61
|
+
* const obj = Obj.make(Person, { [Obj.Meta]: { keys: [...] }, name: 'John' });
|
|
62
|
+
* ```
|
|
39
63
|
*/
|
|
40
|
-
// TODO(dmaretskyi): Move meta into props.
|
|
41
64
|
export const make = <S extends Type.Obj.Any>(
|
|
42
65
|
schema: S,
|
|
43
|
-
props:
|
|
66
|
+
props: MakeProps<S>,
|
|
44
67
|
meta?: EchoSchema.ObjectMeta,
|
|
45
68
|
): LiveObject.Live<Schema.Schema.Type<S>> => {
|
|
46
69
|
assertArgument(
|
|
47
70
|
EchoSchema.getTypeAnnotation(schema)?.kind === EchoSchema.EntityKind.Object,
|
|
71
|
+
'schema',
|
|
48
72
|
'Expected an object schema',
|
|
49
73
|
);
|
|
50
74
|
|
|
@@ -90,8 +114,8 @@ export const instanceOf: {
|
|
|
90
114
|
export const getSchema = EchoSchema.getSchema;
|
|
91
115
|
|
|
92
116
|
// TODO(dmaretskyi): Allow returning undefined.
|
|
93
|
-
export const getDXN = (obj: Any): DXN => {
|
|
94
|
-
assertArgument(!Schema.isSchema(obj), 'Object should not be a schema.');
|
|
117
|
+
export const getDXN = (obj: Any | Relation.Any): DXN => {
|
|
118
|
+
assertArgument(!Schema.isSchema(obj), 'obj', 'Object should not be a schema.');
|
|
95
119
|
const dxn = EchoSchema.getObjectDXN(obj);
|
|
96
120
|
invariant(dxn != null, 'Invalid object.');
|
|
97
121
|
return dxn;
|
|
@@ -108,7 +132,7 @@ export const getTypeDXN = EchoSchema.getType;
|
|
|
108
132
|
* @returns The typename of the object's type.
|
|
109
133
|
* @example `example.com/type/Contact`
|
|
110
134
|
*/
|
|
111
|
-
export const getTypename = (obj: Any): string | undefined => {
|
|
135
|
+
export const getTypename = (obj: Any | Relation.Any): string | undefined => {
|
|
112
136
|
const schema = getSchema(obj);
|
|
113
137
|
if (schema == null) {
|
|
114
138
|
// Try to extract typename from DXN.
|
|
@@ -119,27 +143,89 @@ export const getTypename = (obj: Any): string | undefined => {
|
|
|
119
143
|
};
|
|
120
144
|
|
|
121
145
|
// TODO(dmaretskyi): Allow returning undefined.
|
|
122
|
-
export const getMeta = (obj: Any): EchoSchema.ObjectMeta => {
|
|
146
|
+
export const getMeta = (obj: Any | Relation.Any): EchoSchema.ObjectMeta => {
|
|
123
147
|
const meta = EchoSchema.getMeta(obj);
|
|
124
148
|
invariant(meta != null, 'Invalid object.');
|
|
125
149
|
return meta;
|
|
126
150
|
};
|
|
127
151
|
|
|
152
|
+
/**
|
|
153
|
+
* @returns Foreign keys for the object from the specified source.
|
|
154
|
+
*/
|
|
155
|
+
export const getKeys: {
|
|
156
|
+
(obj: Any | Relation.Any, source: string): EchoSchema.ForeignKey[];
|
|
157
|
+
(source: string): (obj: Any | Relation.Any) => EchoSchema.ForeignKey[];
|
|
158
|
+
} = dual(2, (obj: Any | Relation.Any, source?: string): EchoSchema.ForeignKey[] => {
|
|
159
|
+
const meta = EchoSchema.getMeta(obj);
|
|
160
|
+
invariant(meta != null, 'Invalid object.');
|
|
161
|
+
return meta.keys.filter((key) => key.source === source);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Delete all keys from the object for the specified source.
|
|
166
|
+
* @param obj
|
|
167
|
+
* @param source
|
|
168
|
+
*/
|
|
169
|
+
export const deleteKeys = (obj: Any | Relation.Any, source: string) => {
|
|
170
|
+
const meta = EchoSchema.getMeta(obj);
|
|
171
|
+
for (let i = 0; i < meta.keys.length; i++) {
|
|
172
|
+
if (meta.keys[i].source === source) {
|
|
173
|
+
meta.keys.splice(i, 1);
|
|
174
|
+
i--;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
128
179
|
// TODO(dmaretskyi): Default to `false`.
|
|
129
|
-
export const isDeleted = (obj: Any): boolean => {
|
|
180
|
+
export const isDeleted = (obj: Any | Relation.Any): boolean => {
|
|
130
181
|
const deleted = EchoSchema.isDeleted(obj);
|
|
131
182
|
invariant(typeof deleted === 'boolean', 'Invalid object.');
|
|
132
183
|
return deleted;
|
|
133
184
|
};
|
|
134
185
|
|
|
135
|
-
|
|
136
|
-
export const getLabel = (obj: Any): string | undefined => {
|
|
186
|
+
export const getLabel = (obj: Any | Relation.Any): string | undefined => {
|
|
137
187
|
const schema = getSchema(obj);
|
|
138
188
|
if (schema != null) {
|
|
139
189
|
return EchoSchema.getLabel(schema, obj);
|
|
140
190
|
}
|
|
141
191
|
};
|
|
142
192
|
|
|
193
|
+
export const setLabel = (obj: Any | Relation.Any, label: string) => {
|
|
194
|
+
const schema = getSchema(obj);
|
|
195
|
+
if (schema != null) {
|
|
196
|
+
EchoSchema.setLabel(schema, obj, label);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const compare = (a?: string, b?: string) => {
|
|
201
|
+
if (a == null) {
|
|
202
|
+
return b == null ? 0 : 1;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (b == null) {
|
|
206
|
+
return -1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return a.localeCompare(b);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
export type Comparator = (a: Any, b: Any) => number;
|
|
213
|
+
|
|
214
|
+
export const sortByLabel: Comparator = (a: Any, b: Any) => compare(getLabel(a), getLabel(b));
|
|
215
|
+
export const sortByTypename: Comparator = (a: Any, b: Any) => compare(getTypename(a), getTypename(b));
|
|
216
|
+
export const sort = (...comparators: Comparator[]): Comparator => {
|
|
217
|
+
return (a: Any, b: Any) => {
|
|
218
|
+
for (const comparator of comparators) {
|
|
219
|
+
const result = comparator(a, b);
|
|
220
|
+
if (result !== 0) {
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return 0;
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
|
|
143
229
|
/**
|
|
144
230
|
* JSON representation of an object.
|
|
145
231
|
*/
|
|
@@ -150,6 +236,7 @@ export type JSON = EchoSchema.ObjectJSON;
|
|
|
150
236
|
*
|
|
151
237
|
* The same algorithm is used when calling the standard `JSON.stringify(obj)` function.
|
|
152
238
|
*/
|
|
239
|
+
// TODO(burdon): Base util type for Obj/Relation?
|
|
153
240
|
export const toJSON = (obj: Any | Relation.Any): JSON => EchoSchema.objectToJSON(obj);
|
|
154
241
|
|
|
155
242
|
/**
|
|
@@ -157,6 +244,139 @@ export const toJSON = (obj: Any | Relation.Any): JSON => EchoSchema.objectToJSON
|
|
|
157
244
|
* References and schemas will be resolvable if the `refResolver` is provided.
|
|
158
245
|
*
|
|
159
246
|
* The function need to be async to support resolving the schema as well as the relation endpoints.
|
|
247
|
+
*
|
|
248
|
+
* @param options.refResolver - Resolver for references. Produces hydrated references that can be resolved.
|
|
249
|
+
* @param options.dxn - Override object DXN. Changes the result of `Obj.getDXN`.
|
|
160
250
|
*/
|
|
161
|
-
export const fromJSON: (json: unknown, options?: { refResolver?: Ref.Resolver }) => Promise<Any> =
|
|
251
|
+
export const fromJSON: (json: unknown, options?: { refResolver?: Ref.Resolver; dxn?: DXN }) => Promise<Any> =
|
|
162
252
|
EchoSchema.objectFromJSON as any;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Returns an immutable snapshot of an object.
|
|
256
|
+
*/
|
|
257
|
+
export const getSnapshot: <T extends Any>(obj: Obj<T>) => T = LiveObject.getSnapshot;
|
|
258
|
+
|
|
259
|
+
export type CloneOptions = {
|
|
260
|
+
/**
|
|
261
|
+
* Retain the original object's ID.
|
|
262
|
+
* @default false
|
|
263
|
+
*/
|
|
264
|
+
retainId?: boolean;
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clones an object or relation.
|
|
269
|
+
* This does not clone referenced objects, only the properties in the object.
|
|
270
|
+
* @returns A new object with the same schema and properties.
|
|
271
|
+
*/
|
|
272
|
+
export const clone = <T extends Any | Relation.Any>(obj: T, opts?: CloneOptions): T => {
|
|
273
|
+
const { id, ...data } = obj;
|
|
274
|
+
const schema = getSchema(obj);
|
|
275
|
+
invariant(schema != null, 'Object should have a schema');
|
|
276
|
+
const props: any = deepMapValues(data, (value, recurse) => {
|
|
277
|
+
if (EchoSchema.Ref.isRef(value)) {
|
|
278
|
+
return value;
|
|
279
|
+
}
|
|
280
|
+
return recurse(value);
|
|
281
|
+
});
|
|
282
|
+
if (opts?.retainId) {
|
|
283
|
+
props.id = id;
|
|
284
|
+
}
|
|
285
|
+
const meta = getMeta(obj);
|
|
286
|
+
props[EchoSchema.MetaId] = deepMapValues(meta, (value, recurse) => {
|
|
287
|
+
if (EchoSchema.Ref.isRef(value)) {
|
|
288
|
+
return value;
|
|
289
|
+
}
|
|
290
|
+
return recurse(value);
|
|
291
|
+
});
|
|
292
|
+
return make(schema, props);
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const VersionTypeId = EchoSchema.VersionTypeId;
|
|
296
|
+
export type VersionType = typeof VersionTypeId;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Represent object version.
|
|
300
|
+
* May be backed by Automerge.
|
|
301
|
+
* Objects with no history are not versioned.
|
|
302
|
+
*/
|
|
303
|
+
export interface Version {
|
|
304
|
+
[VersionTypeId]: {};
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Whether the object is versioned.
|
|
308
|
+
*/
|
|
309
|
+
versioned: boolean;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Automerge heads.
|
|
313
|
+
*/
|
|
314
|
+
automergeHeads?: string[];
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const unversioned: Version = {
|
|
318
|
+
[VersionTypeId]: {},
|
|
319
|
+
versioned: false,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Checks that `obj` is a version object.
|
|
324
|
+
*/
|
|
325
|
+
export const isVersion = (obj: unknown): obj is Version => {
|
|
326
|
+
return obj != null && typeof obj === 'object' && VersionTypeId in obj;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Returns the version of the object.
|
|
331
|
+
*/
|
|
332
|
+
export const version = (obj: Any | Relation.Any): Version => {
|
|
333
|
+
const version = (obj as any)[EchoSchema.ObjectVersionId];
|
|
334
|
+
if (version === undefined) {
|
|
335
|
+
return unversioned;
|
|
336
|
+
}
|
|
337
|
+
return version;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Checks that `version` is a valid version object.
|
|
342
|
+
*/
|
|
343
|
+
export const versionValid = (version: Version): boolean => {
|
|
344
|
+
assertArgument(isVersion(version), 'version', 'Invalid version object');
|
|
345
|
+
return !!version.versioned;
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
export type VersionCompareResult = 'unversioned' | 'equal' | 'different';
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Compares two versions.
|
|
352
|
+
* @param version1
|
|
353
|
+
* @param version2
|
|
354
|
+
* @returns 'unversioned' if either object is unversioned, 'equal' if the versions are equal, 'different' if the versions are different.
|
|
355
|
+
*/
|
|
356
|
+
export const compareVersions = (version1: Version, version2: Version): VersionCompareResult => {
|
|
357
|
+
assertArgument(isVersion(version1), 'version1', 'Invalid version object');
|
|
358
|
+
assertArgument(isVersion(version2), 'version2', 'Invalid version object');
|
|
359
|
+
|
|
360
|
+
if (!versionValid(version1) || !versionValid(version2)) {
|
|
361
|
+
return 'unversioned';
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (version1.automergeHeads?.length !== version2.automergeHeads?.length) {
|
|
365
|
+
return 'different';
|
|
366
|
+
}
|
|
367
|
+
if (version1.automergeHeads?.some((head) => !version2.automergeHeads?.includes(head))) {
|
|
368
|
+
return 'different';
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return 'equal';
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
export const encodeVersion = (version: Version): string => {
|
|
375
|
+
return JSON.stringify(version);
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
export const decodeVersion = (version: string): Version => {
|
|
379
|
+
const parsed = JSON.parse(version);
|
|
380
|
+
parsed[VersionTypeId] = {};
|
|
381
|
+
return parsed;
|
|
382
|
+
};
|
package/src/Relation.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
6
|
|
|
7
7
|
import { raise } from '@dxos/debug';
|
|
8
8
|
import * as EchoSchema from '@dxos/echo-schema';
|
|
@@ -11,25 +11,35 @@ import { DXN } from '@dxos/keys';
|
|
|
11
11
|
import { type Live, live } from '@dxos/live-object';
|
|
12
12
|
import { assumeType } from '@dxos/util';
|
|
13
13
|
|
|
14
|
-
import
|
|
15
|
-
import
|
|
14
|
+
import * as Obj from './Obj';
|
|
15
|
+
import * as Type from './Type';
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
/**
|
|
18
|
+
* NOTE: Don't export: Relation.Relation and Relation.Any form the public API.
|
|
19
|
+
*/
|
|
20
|
+
interface BaseRelation<Source, Target>
|
|
21
|
+
extends EchoSchema.HasId,
|
|
22
|
+
Type.Relation.Endpoints<Source, Target>,
|
|
23
|
+
Type.OfKind<EchoSchema.EntityKind.Relation> {}
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Relation type with specific properties.
|
|
26
27
|
*/
|
|
27
|
-
export type Relation<Source extends Obj.Any, Target extends Obj.Any, Props> =
|
|
28
|
+
export type Relation<Source extends Obj.Any, Target extends Obj.Any, Props> = BaseRelation<Source, Target> & Props;
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Base type for all ECHO relations.
|
|
31
32
|
*/
|
|
32
|
-
export interface Any extends
|
|
33
|
+
export interface Any extends BaseRelation<Obj.Any, Obj.Any> {}
|
|
34
|
+
|
|
35
|
+
export const Any = Schema.Struct({}).pipe(
|
|
36
|
+
Type.Relation({
|
|
37
|
+
typename: 'dxos.org/types/Any',
|
|
38
|
+
version: '0.1.0',
|
|
39
|
+
source: Obj.Any,
|
|
40
|
+
target: Obj.Any,
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
33
43
|
|
|
34
44
|
// TODO(dmaretskyi): Has to be `unique symbol`.
|
|
35
45
|
export const Source: unique symbol = EchoSchema.RelationSourceId as any;
|
|
@@ -59,6 +69,7 @@ export const make = <S extends Type.Relation.Any>(
|
|
|
59
69
|
): Live<Schema.Schema.Type<S> & Type.OfKind<EchoSchema.EntityKind.Relation>> => {
|
|
60
70
|
assertArgument(
|
|
61
71
|
EchoSchema.getTypeAnnotation(schema)?.kind === EchoSchema.EntityKind.Relation,
|
|
72
|
+
'schema',
|
|
62
73
|
'Expected a relation schema',
|
|
63
74
|
);
|
|
64
75
|
|
package/src/Type.ts
CHANGED
|
@@ -39,7 +39,7 @@ export type Schema = EchoSchema.EchoSchema;
|
|
|
39
39
|
/**
|
|
40
40
|
* Returns all properties of an object or relation except for the id and kind.
|
|
41
41
|
*/
|
|
42
|
-
export type Properties<T> = Omit<T, 'id' | KindId | RelationModule.Source | RelationModule.Target>;
|
|
42
|
+
export type Properties<T = any> = Omit<T, 'id' | KindId | RelationModule.Source | RelationModule.Target>;
|
|
43
43
|
|
|
44
44
|
//
|
|
45
45
|
// Obj
|
|
@@ -74,12 +74,13 @@ export const Obj: {
|
|
|
74
74
|
/**
|
|
75
75
|
* Object schema type definitions.
|
|
76
76
|
*/
|
|
77
|
-
export namespace Obj {
|
|
77
|
+
export declare namespace Obj {
|
|
78
78
|
/**
|
|
79
79
|
* Type that represents an arbitrary schema type of an object.
|
|
80
80
|
* NOTE: This is not an instance type.
|
|
81
81
|
*/
|
|
82
82
|
// TODO(dmaretskyi): If schema was covariant, we could specify props in here, like `id: ObjectId`.
|
|
83
|
+
// TODO(burdon): This erases the ECHO type info (e.g., id, typename).
|
|
83
84
|
export type Any = Schema.Schema.AnyNoContext;
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -87,6 +88,7 @@ export namespace Obj {
|
|
|
87
88
|
// Expando
|
|
88
89
|
//
|
|
89
90
|
|
|
91
|
+
// TODO(burdon): We're using Expando in many places as a base type.
|
|
90
92
|
export interface Expando extends OfKind<EchoSchema.EntityKind.Object> {
|
|
91
93
|
[key: string]: any;
|
|
92
94
|
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { BaseError, type BaseErrorOptions } from '@dxos/errors';
|
|
6
|
+
import { type DXN } from '@dxos/keys';
|
|
7
|
+
|
|
8
|
+
export class SchemaNotFoundError extends BaseError.extend('SCHEMA_NOT_FOUND', 'Schema not found') {
|
|
9
|
+
constructor(schema: string, options?: BaseErrorOptions) {
|
|
10
|
+
super({ context: { schema }, ...options });
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class ObjectNotFoundError extends BaseError.extend('OBJECT_NOT_FOUND', 'Object not found') {
|
|
15
|
+
constructor(dxn: DXN, options?: BaseErrorOptions) {
|
|
16
|
+
super({ context: { dxn }, ...options });
|
|
17
|
+
}
|
|
18
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
export * from './errors';
|
|
6
|
+
|
|
5
7
|
export * as Key from './Key';
|
|
6
8
|
export * as Obj from './Obj';
|
|
7
9
|
export * as Ref from './Ref';
|
|
@@ -9,5 +11,6 @@ export * as Relation from './Relation';
|
|
|
9
11
|
export * as Type from './Type';
|
|
10
12
|
|
|
11
13
|
export { DXN } from '@dxos/keys';
|
|
12
|
-
export { Filter, Query } from './query';
|
|
14
|
+
export { Filter, Order, Query } from './query';
|
|
15
|
+
export { QueryAST } from '@dxos/echo-protocol';
|
|
13
16
|
export { type Live } from '@dxos/live-object';
|
package/src/query/dsl.test.ts
CHANGED
|
@@ -9,11 +9,12 @@ import { QueryAST } from '@dxos/echo-protocol';
|
|
|
9
9
|
import { DXN } from '@dxos/keys';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
|
|
12
|
-
import { Filter, Query } from './dsl';
|
|
13
12
|
import * as Obj from '../Obj';
|
|
14
13
|
import * as Ref from '../Ref';
|
|
15
14
|
import * as Type from '../Type';
|
|
16
15
|
|
|
16
|
+
import { Filter, Order, Query } from './dsl';
|
|
17
|
+
|
|
17
18
|
//
|
|
18
19
|
// Example schema
|
|
19
20
|
//
|
|
@@ -23,6 +24,10 @@ const Person = Schema.Struct({
|
|
|
23
24
|
name: Schema.String,
|
|
24
25
|
email: Schema.optional(Schema.String),
|
|
25
26
|
age: Schema.optional(Schema.Number),
|
|
27
|
+
fields: Schema.Struct({
|
|
28
|
+
label: Schema.String,
|
|
29
|
+
value: Schema.String,
|
|
30
|
+
}).pipe(Schema.Array, Schema.optional),
|
|
26
31
|
}).pipe(
|
|
27
32
|
Type.Obj({
|
|
28
33
|
typename: 'dxos.org/type/Person',
|
|
@@ -33,6 +38,12 @@ interface Person extends Schema.Schema.Type<typeof Person> {}
|
|
|
33
38
|
|
|
34
39
|
const Organization = Schema.Struct({
|
|
35
40
|
name: Schema.String,
|
|
41
|
+
properties: Schema.optional(
|
|
42
|
+
Schema.Record({
|
|
43
|
+
key: Schema.String,
|
|
44
|
+
value: Schema.String,
|
|
45
|
+
}),
|
|
46
|
+
),
|
|
36
47
|
}).pipe(
|
|
37
48
|
Type.Obj({
|
|
38
49
|
typename: 'dxos.org/type/Organization',
|
|
@@ -73,6 +84,14 @@ describe('query api', () => {
|
|
|
73
84
|
console.log('getAllPeople', JSON.stringify(getAllPeople.ast, null, 2));
|
|
74
85
|
});
|
|
75
86
|
|
|
87
|
+
test('get all people ordered by name', () => {
|
|
88
|
+
const getAllPeopleOrderedByName = Query.type(Person).orderBy(Order.property('name', 'asc'));
|
|
89
|
+
|
|
90
|
+
log('query', { ast: getAllPeopleOrderedByName.ast });
|
|
91
|
+
Schema.validateSync(QueryAST.Query)(getAllPeopleOrderedByName.ast);
|
|
92
|
+
console.log('getAllPeopleOrderedByName', JSON.stringify(getAllPeopleOrderedByName.ast, null, 2));
|
|
93
|
+
});
|
|
94
|
+
|
|
76
95
|
test('get all people named Fred', () => {
|
|
77
96
|
const PeopleNamedFred = Query.select(Filter.type(Person, { name: 'Fred' }));
|
|
78
97
|
|
|
@@ -81,6 +100,26 @@ describe('query api', () => {
|
|
|
81
100
|
console.log('PeopleNamedFred', JSON.stringify(PeopleNamedFred.ast, null, 2));
|
|
82
101
|
});
|
|
83
102
|
|
|
103
|
+
test('get all people with field of "label" set to "Research"', () => {
|
|
104
|
+
const PeopleWithFieldLabelSetToResearch = Query.select(
|
|
105
|
+
Filter.type(Person, { fields: Filter.contains({ label: 'label', value: 'Research' }) }),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
log('query', { ast: PeopleWithFieldLabelSetToResearch.ast });
|
|
109
|
+
Schema.validateSync(QueryAST.Query)(PeopleWithFieldLabelSetToResearch.ast);
|
|
110
|
+
console.log('PeopleWithFieldLabelSetToResearch', JSON.stringify(PeopleWithFieldLabelSetToResearch.ast, null, 2));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('get all orgs with property "label" set to "Research"', () => {
|
|
114
|
+
const OrgsWithPropertyLabelSetToResearch = Query.select(
|
|
115
|
+
Filter.type(Organization, { properties: { label: 'Research' } }),
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
log('query', { ast: OrgsWithPropertyLabelSetToResearch.ast });
|
|
119
|
+
Schema.validateSync(QueryAST.Query)(OrgsWithPropertyLabelSetToResearch.ast);
|
|
120
|
+
console.log('OrgsWithPropertyLabelSetToResearch', JSON.stringify(OrgsWithPropertyLabelSetToResearch.ast, null, 2));
|
|
121
|
+
});
|
|
122
|
+
|
|
84
123
|
test('get all orgs Fred worked for since 2020', () => {
|
|
85
124
|
const fred = Obj.make(Person, { name: 'Fred' });
|
|
86
125
|
const OrganizationsFredWorkedForSince2020 = Query.select(Filter.type(Person, { id: fred.id }))
|