@dxos/echo 0.8.3-main.672df60 → 0.8.3-staging.0fa589b
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 +25 -23
- package/dist/lib/browser/chunk-UYPR62ZB.mjs +624 -0
- package/dist/lib/browser/chunk-UYPR62ZB.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +11 -280
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +70 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-4HQE2F3L.cjs +644 -0
- package/dist/lib/node/chunk-4HQE2F3L.cjs.map +7 -0
- package/dist/lib/node/index.cjs +11 -282
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +89 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-BYBICDIO.mjs +624 -0
- package/dist/lib/node-esm/chunk-BYBICDIO.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +11 -280
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +70 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/Key.d.ts +2 -0
- package/dist/types/src/Key.d.ts.map +1 -0
- package/dist/types/src/Obj.d.ts +37 -18
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Ref.d.ts +9 -3
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +36 -10
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Type.d.ts +82 -17
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +5 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/dsl.d.ts +218 -0
- package/dist/types/src/query/dsl.d.ts.map +1 -0
- package/dist/types/src/query/dsl.test.d.ts +2 -0
- package/dist/types/src/query/dsl.test.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/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/types.d.ts +113 -0
- package/dist/types/src/testing/types.d.ts.map +1 -0
- package/package.json +56 -12
- package/src/Key.ts +5 -0
- package/src/Obj.ts +66 -25
- package/src/Ref.ts +9 -6
- package/src/Relation.ts +75 -13
- package/src/Type.ts +122 -18
- package/src/index.ts +5 -3
- package/src/query/dsl.test.ts +323 -0
- package/src/query/dsl.ts +646 -0
- package/src/query/index.ts +5 -0
- package/src/test/api.test.ts +54 -9
- package/src/testing/index.ts +5 -0
- package/src/testing/types.ts +91 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { Type } from '..';
|
|
3
|
+
export declare namespace Testing {
|
|
4
|
+
const _Contact: Type.obj<Schema.SchemaClass<{
|
|
5
|
+
readonly name?: string | undefined;
|
|
6
|
+
readonly email?: string | undefined;
|
|
7
|
+
readonly username?: string | undefined;
|
|
8
|
+
readonly tasks?: import("@dxos/echo-schema").Ref<Task>[] | undefined;
|
|
9
|
+
readonly address?: {
|
|
10
|
+
readonly city?: string | undefined;
|
|
11
|
+
readonly state?: string | undefined;
|
|
12
|
+
readonly zip?: string | undefined;
|
|
13
|
+
readonly coordinates: {
|
|
14
|
+
readonly lat?: number | undefined;
|
|
15
|
+
readonly lng?: number | undefined;
|
|
16
|
+
};
|
|
17
|
+
} | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
readonly name?: string | undefined;
|
|
20
|
+
readonly email?: string | undefined;
|
|
21
|
+
readonly username?: string | undefined;
|
|
22
|
+
readonly tasks?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
23
|
+
readonly address?: {
|
|
24
|
+
readonly coordinates: {
|
|
25
|
+
readonly lat?: number | undefined;
|
|
26
|
+
readonly lng?: number | undefined;
|
|
27
|
+
};
|
|
28
|
+
readonly city?: string | undefined;
|
|
29
|
+
readonly state?: string | undefined;
|
|
30
|
+
readonly zip?: string | undefined;
|
|
31
|
+
} | undefined;
|
|
32
|
+
}, never>>;
|
|
33
|
+
export interface Contact extends Schema.Schema.Type<typeof _Contact> {
|
|
34
|
+
}
|
|
35
|
+
export const Contact: Schema.Schema<Contact, Schema.Schema.Encoded<typeof _Contact>, never>;
|
|
36
|
+
const _Task: Type.obj<Schema.SchemaClass<{
|
|
37
|
+
readonly description?: string | undefined;
|
|
38
|
+
readonly title?: string | undefined;
|
|
39
|
+
readonly assignee?: import("@dxos/echo-schema").Ref<Contact> | undefined;
|
|
40
|
+
readonly completed?: boolean | undefined;
|
|
41
|
+
readonly previous?: import("@dxos/echo-schema").Ref<Task> | undefined;
|
|
42
|
+
readonly subTasks?: import("@dxos/echo-schema").Ref<Task>[] | undefined;
|
|
43
|
+
}, {
|
|
44
|
+
readonly description?: string | undefined;
|
|
45
|
+
readonly title?: string | undefined;
|
|
46
|
+
readonly assignee?: import("@dxos/echo-protocol").EncodedReference | undefined;
|
|
47
|
+
readonly completed?: boolean | undefined;
|
|
48
|
+
readonly previous?: import("@dxos/echo-protocol").EncodedReference | undefined;
|
|
49
|
+
readonly subTasks?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
50
|
+
}, never>>;
|
|
51
|
+
export interface Task extends Schema.Schema.Type<typeof _Task> {
|
|
52
|
+
}
|
|
53
|
+
export const Task: Schema.Schema<Task, Schema.Schema.Encoded<typeof _Task>, never>;
|
|
54
|
+
export enum RecordType {
|
|
55
|
+
UNDEFINED = 0,
|
|
56
|
+
PERSONAL = 1,
|
|
57
|
+
WORK = 2
|
|
58
|
+
}
|
|
59
|
+
export const Container: Type.obj<Schema.SchemaClass<{
|
|
60
|
+
readonly records?: {
|
|
61
|
+
readonly type?: RecordType | undefined;
|
|
62
|
+
readonly description?: string | undefined;
|
|
63
|
+
readonly title?: string | undefined;
|
|
64
|
+
readonly contacts?: import("@dxos/echo-schema").Ref<Contact>[] | undefined;
|
|
65
|
+
}[] | undefined;
|
|
66
|
+
readonly objects?: import("@dxos/echo-schema").Ref<Type.Expando>[] | undefined;
|
|
67
|
+
}, {
|
|
68
|
+
readonly records?: {
|
|
69
|
+
readonly type?: RecordType | undefined;
|
|
70
|
+
readonly description?: string | undefined;
|
|
71
|
+
readonly title?: string | undefined;
|
|
72
|
+
readonly contacts?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
73
|
+
}[] | undefined;
|
|
74
|
+
readonly objects?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
75
|
+
}, never>>;
|
|
76
|
+
export const WorksFor: Type.relation<Schema.Struct<{
|
|
77
|
+
since: Schema.optional<typeof Schema.String>;
|
|
78
|
+
}>, Schema.Schema<Contact, {
|
|
79
|
+
id: string;
|
|
80
|
+
name?: string | undefined;
|
|
81
|
+
email?: string | undefined;
|
|
82
|
+
username?: string | undefined;
|
|
83
|
+
tasks?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
84
|
+
address?: {
|
|
85
|
+
readonly coordinates: {
|
|
86
|
+
readonly lat?: number | undefined;
|
|
87
|
+
readonly lng?: number | undefined;
|
|
88
|
+
};
|
|
89
|
+
readonly city?: string | undefined;
|
|
90
|
+
readonly state?: string | undefined;
|
|
91
|
+
readonly zip?: string | undefined;
|
|
92
|
+
} | undefined;
|
|
93
|
+
}, never>, Schema.Schema<Contact, {
|
|
94
|
+
id: string;
|
|
95
|
+
name?: string | undefined;
|
|
96
|
+
email?: string | undefined;
|
|
97
|
+
username?: string | undefined;
|
|
98
|
+
tasks?: import("@dxos/echo-protocol").EncodedReference[] | undefined;
|
|
99
|
+
address?: {
|
|
100
|
+
readonly coordinates: {
|
|
101
|
+
readonly lat?: number | undefined;
|
|
102
|
+
readonly lng?: number | undefined;
|
|
103
|
+
};
|
|
104
|
+
readonly city?: string | undefined;
|
|
105
|
+
readonly state?: string | undefined;
|
|
106
|
+
readonly zip?: string | undefined;
|
|
107
|
+
} | undefined;
|
|
108
|
+
}, never>>;
|
|
109
|
+
export interface WorksFor extends Schema.Schema.Type<typeof WorksFor> {
|
|
110
|
+
}
|
|
111
|
+
export {};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/testing/types.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAG1B,yBAAiB,OAAO,CAAC;IACvB,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAoBb,CAAC;IACF,MAAM,WAAW,OAAQ,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC;KAAG;IACvE,MAAM,CAAC,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAY,CAAC;IAEvG,MAAM,KAAK;;;;;;;;;;;;;;cAaV,CAAC;IACF,MAAM,WAAW,IAAK,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC;KAAG;IACjE,MAAM,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,KAAK,CAAC,EAAE,KAAK,CAAS,CAAC;IAE3F,MAAM,MAAM,UAAU;QACpB,SAAS,IAAI;QACb,QAAQ,IAAI;QACZ,IAAI,IAAI;KACT;IAED,MAAM,CAAC,MAAM,SAAS;;;;;;;;;;;;;;;;cAoBrB,CAAC;IAEF,MAAM,CAAC,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cASpB,CAAC;IACF,MAAM,WAAW,QAAS,SAAQ,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,QAAQ,CAAC;KAAG;;CACzE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo",
|
|
3
|
-
"version": "0.8.3-
|
|
3
|
+
"version": "0.8.3-staging.0fa589b",
|
|
4
4
|
"description": "ECHO API",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -13,9 +13,53 @@
|
|
|
13
13
|
"types": "./dist/types/src/index.d.ts",
|
|
14
14
|
"browser": "./dist/lib/browser/index.mjs",
|
|
15
15
|
"node": "./dist/lib/node-esm/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"./Type": {
|
|
18
|
+
"types": "./dist/types/src/Type.d.ts",
|
|
19
|
+
"browser": "./dist/lib/browser/Type.mjs",
|
|
20
|
+
"node": "./dist/lib/node-esm/Type.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./Obj": {
|
|
23
|
+
"types": "./dist/types/src/Obj.d.ts",
|
|
24
|
+
"browser": "./dist/lib/browser/Obj.mjs",
|
|
25
|
+
"node": "./dist/lib/node-esm/Obj.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./Relation": {
|
|
28
|
+
"types": "./dist/types/src/Relation.d.ts",
|
|
29
|
+
"browser": "./dist/lib/browser/Relation.mjs",
|
|
30
|
+
"node": "./dist/lib/node-esm/Relation.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./Ref": {
|
|
33
|
+
"types": "./dist/types/src/Ref.d.ts",
|
|
34
|
+
"browser": "./dist/lib/browser/Ref.mjs",
|
|
35
|
+
"node": "./dist/lib/node-esm/Ref.mjs"
|
|
36
|
+
},
|
|
37
|
+
"./testing": {
|
|
38
|
+
"types": "./dist/types/src/testing/types.d.ts",
|
|
39
|
+
"browser": "./dist/lib/browser/testing/index.mjs",
|
|
40
|
+
"node": "./dist/lib/node-esm/testing/index.mjs"
|
|
16
41
|
}
|
|
17
42
|
},
|
|
18
43
|
"types": "dist/types/src/index.d.ts",
|
|
44
|
+
"typesVersions": {
|
|
45
|
+
"*": {
|
|
46
|
+
"Type": [
|
|
47
|
+
"dist/types/src/Type.d.ts"
|
|
48
|
+
],
|
|
49
|
+
"Obj": [
|
|
50
|
+
"dist/types/src/Obj.d.ts"
|
|
51
|
+
],
|
|
52
|
+
"Relation": [
|
|
53
|
+
"dist/types/src/Relation.d.ts"
|
|
54
|
+
],
|
|
55
|
+
"Ref": [
|
|
56
|
+
"dist/types/src/Ref.d.ts"
|
|
57
|
+
],
|
|
58
|
+
"testing": [
|
|
59
|
+
"dist/types/src/testing/index.d.ts"
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
19
63
|
"files": [
|
|
20
64
|
"dist",
|
|
21
65
|
"src"
|
|
@@ -23,17 +67,17 @@
|
|
|
23
67
|
"dependencies": {
|
|
24
68
|
"@preact/signals-core": "^1.9.0",
|
|
25
69
|
"effect": "3.14.21",
|
|
26
|
-
"@dxos/debug": "0.8.3-
|
|
27
|
-
"@dxos/echo-protocol": "0.8.3-
|
|
28
|
-
"@dxos/echo-schema": "0.8.3-
|
|
29
|
-
"@dxos/
|
|
30
|
-
"@dxos/
|
|
31
|
-
"@dxos/invariant": "0.8.3-
|
|
32
|
-
"@dxos/keys": "0.8.3-
|
|
33
|
-
"@dxos/live-object": "0.8.3-
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/util": "0.8.3-
|
|
70
|
+
"@dxos/debug": "0.8.3-staging.0fa589b",
|
|
71
|
+
"@dxos/echo-protocol": "0.8.3-staging.0fa589b",
|
|
72
|
+
"@dxos/echo-schema": "0.8.3-staging.0fa589b",
|
|
73
|
+
"@dxos/effect": "0.8.3-staging.0fa589b",
|
|
74
|
+
"@dxos/echo-signals": "0.8.3-staging.0fa589b",
|
|
75
|
+
"@dxos/invariant": "0.8.3-staging.0fa589b",
|
|
76
|
+
"@dxos/keys": "0.8.3-staging.0fa589b",
|
|
77
|
+
"@dxos/live-object": "0.8.3-staging.0fa589b",
|
|
78
|
+
"@dxos/log": "0.8.3-staging.0fa589b",
|
|
79
|
+
"@dxos/node-std": "0.8.3-staging.0fa589b",
|
|
80
|
+
"@dxos/util": "0.8.3-staging.0fa589b"
|
|
37
81
|
},
|
|
38
82
|
"publishConfig": {
|
|
39
83
|
"access": "public"
|
package/src/Key.ts
ADDED
package/src/Obj.ts
CHANGED
|
@@ -6,34 +6,80 @@ import { Schema } from 'effect';
|
|
|
6
6
|
|
|
7
7
|
import * as EchoSchema from '@dxos/echo-schema';
|
|
8
8
|
import { assertArgument, invariant } from '@dxos/invariant';
|
|
9
|
-
import type
|
|
10
|
-
import * as LiveObject from '@dxos/live-object';
|
|
9
|
+
import { type DXN } from '@dxos/keys';
|
|
10
|
+
import type * as LiveObject from '@dxos/live-object';
|
|
11
|
+
import { live } from '@dxos/live-object';
|
|
12
|
+
import { assumeType } from '@dxos/util';
|
|
11
13
|
|
|
12
14
|
import type * as Ref from './Ref';
|
|
15
|
+
import type * as Relation from './Relation';
|
|
13
16
|
import type * as Type from './Type';
|
|
14
17
|
|
|
15
|
-
export
|
|
18
|
+
// NOTE: Don't export: Obj.Any and Obj.Obj form the public API.
|
|
19
|
+
interface ObjBase extends Type.OfKind<EchoSchema.EntityKind.Object> {
|
|
20
|
+
readonly id: EchoSchema.ObjectId;
|
|
21
|
+
}
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Object type with specific properties.
|
|
25
|
+
*/
|
|
26
|
+
export type Obj<Props> = ObjBase & Props;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Base type for all ECHO objects.
|
|
30
|
+
*/
|
|
31
|
+
export interface Any extends ObjBase {}
|
|
32
|
+
|
|
33
|
+
type MakeProps<T> = {
|
|
34
|
+
id?: EchoSchema.ObjectId;
|
|
35
|
+
} & Type.Properties<T>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates new object.
|
|
39
|
+
*/
|
|
40
|
+
// TODO(dmaretskyi): Move meta into props.
|
|
41
|
+
export const make = <S extends Type.Obj.Any>(
|
|
42
|
+
schema: S,
|
|
43
|
+
props: NoInfer<MakeProps<Schema.Schema.Type<S>>>,
|
|
44
|
+
meta?: EchoSchema.ObjectMeta,
|
|
45
|
+
): LiveObject.Live<Schema.Schema.Type<S>> => {
|
|
46
|
+
assertArgument(
|
|
47
|
+
EchoSchema.getTypeAnnotation(schema)?.kind === EchoSchema.EntityKind.Object,
|
|
48
|
+
'Expected an object schema',
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (props[EchoSchema.MetaId] != null) {
|
|
52
|
+
meta = props[EchoSchema.MetaId] as any;
|
|
53
|
+
delete props[EchoSchema.MetaId];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return live<Schema.Schema.Type<S>>(schema, props as any, meta);
|
|
57
|
+
};
|
|
18
58
|
|
|
19
|
-
// TODO(dmaretskyi): Currently broken
|
|
20
59
|
export const isObject = (obj: unknown): obj is Any => {
|
|
21
|
-
|
|
60
|
+
assumeType<EchoSchema.InternalObjectProps>(obj);
|
|
61
|
+
return typeof obj === 'object' && obj !== null && obj[EchoSchema.EntityKindId] === EchoSchema.EntityKind.Object;
|
|
22
62
|
};
|
|
23
63
|
|
|
24
64
|
/**
|
|
25
|
-
*
|
|
65
|
+
* Test if object or relation is an instance of a schema.
|
|
26
66
|
* @example
|
|
27
67
|
* ```ts
|
|
28
|
-
* const
|
|
68
|
+
* const john = Obj.make(Person, { name: 'John' });
|
|
69
|
+
* const johnIsPerson = Obj.instanceOf(Person)(john);
|
|
70
|
+
*
|
|
29
71
|
* const isPerson = Obj.instanceOf(Person);
|
|
30
|
-
* isPerson(
|
|
72
|
+
* if(isPerson(john)) {
|
|
73
|
+
* // john is Person
|
|
74
|
+
* }
|
|
31
75
|
* ```
|
|
32
76
|
*/
|
|
33
77
|
export const instanceOf: {
|
|
34
|
-
<S extends Type.Relation.Any | Type.Obj.Any>(schema: S): (value: unknown) => value is S
|
|
35
|
-
<S extends Type.Relation.Any | Type.Obj.Any>(schema: S, value: unknown): value is S
|
|
36
|
-
} = ((
|
|
78
|
+
<S extends Type.Relation.Any | Type.Obj.Any>(schema: S): (value: unknown) => value is Schema.Schema.Type<S>;
|
|
79
|
+
<S extends Type.Relation.Any | Type.Obj.Any>(schema: S, value: unknown): value is Schema.Schema.Type<S>;
|
|
80
|
+
} = ((
|
|
81
|
+
...args: [schema: Type.Relation.Any | Type.Obj.Any, value: unknown] | [schema: Type.Relation.Any | Type.Obj.Any]
|
|
82
|
+
) => {
|
|
37
83
|
if (args.length === 1) {
|
|
38
84
|
return (obj: unknown) => EchoSchema.isInstanceOf(args[0], obj);
|
|
39
85
|
}
|
|
@@ -55,12 +101,8 @@ export const getDXN = (obj: Any): DXN => {
|
|
|
55
101
|
* @returns The DXN of the object's type.
|
|
56
102
|
* @example dxn:example.com/type/Contact:1.0.0
|
|
57
103
|
*/
|
|
58
|
-
// TODO(
|
|
59
|
-
export const
|
|
60
|
-
const type = EchoSchema.getType(obj);
|
|
61
|
-
invariant(type != null, 'Invalid object.');
|
|
62
|
-
return type;
|
|
63
|
-
};
|
|
104
|
+
// TODO(burdon): Expando does not have a type.
|
|
105
|
+
export const getTypeDXN = EchoSchema.getType;
|
|
64
106
|
|
|
65
107
|
/**
|
|
66
108
|
* @returns The typename of the object's type.
|
|
@@ -70,7 +112,7 @@ export const getTypename = (obj: Any): string | undefined => {
|
|
|
70
112
|
const schema = getSchema(obj);
|
|
71
113
|
if (schema == null) {
|
|
72
114
|
// Try to extract typename from DXN.
|
|
73
|
-
return
|
|
115
|
+
return EchoSchema.getType(obj)?.asTypeDXN()?.type;
|
|
74
116
|
}
|
|
75
117
|
|
|
76
118
|
return EchoSchema.getSchemaTypename(schema);
|
|
@@ -103,18 +145,17 @@ export const getLabel = (obj: Any): string | undefined => {
|
|
|
103
145
|
export type JSON = EchoSchema.ObjectJSON;
|
|
104
146
|
|
|
105
147
|
/**
|
|
106
|
-
* Converts object to
|
|
148
|
+
* Converts object to its JSON representation.
|
|
107
149
|
*
|
|
108
150
|
* The same algorithm is used when calling the standard `JSON.stringify(obj)` function.
|
|
109
151
|
*/
|
|
110
|
-
export const toJSON = (obj: Any): JSON => EchoSchema.objectToJSON(obj);
|
|
152
|
+
export const toJSON = (obj: Any | Relation.Any): JSON => EchoSchema.objectToJSON(obj);
|
|
111
153
|
|
|
112
154
|
/**
|
|
113
|
-
* Creates an object from
|
|
114
|
-
*
|
|
115
|
-
* References and schema will be resolvable if the `refResolver` is provided.
|
|
155
|
+
* Creates an object from its json representation, performing schema validation.
|
|
156
|
+
* References and schemas will be resolvable if the `refResolver` is provided.
|
|
116
157
|
*
|
|
117
158
|
* The function need to be async to support resolving the schema as well as the relation endpoints.
|
|
118
159
|
*/
|
|
119
160
|
export const fromJSON: (json: unknown, options?: { refResolver?: Ref.Resolver }) => Promise<Any> =
|
|
120
|
-
EchoSchema.objectFromJSON;
|
|
161
|
+
EchoSchema.objectFromJSON as any;
|
package/src/Ref.ts
CHANGED
|
@@ -6,14 +6,10 @@ import * as EchoSchema from '@dxos/echo-schema';
|
|
|
6
6
|
|
|
7
7
|
import type * as Obj from './Obj';
|
|
8
8
|
|
|
9
|
+
export type Ref<T> = EchoSchema.Ref<T>;
|
|
9
10
|
export type Any = EchoSchema.Ref<Obj.Any>;
|
|
10
11
|
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
export const isRef: (value: unknown) => value is Any = EchoSchema.Ref.isRef;
|
|
14
|
-
|
|
15
|
-
// TODO(dmaretskyi): Consider just allowing `make` to accept DXN.
|
|
16
|
-
export const fromDXN = EchoSchema.Ref.fromDXN;
|
|
12
|
+
export const Array = EchoSchema.RefArray;
|
|
17
13
|
|
|
18
14
|
/**
|
|
19
15
|
* Extract reference target.
|
|
@@ -24,3 +20,10 @@ export type Target<R extends Any> = R extends EchoSchema.Ref<infer T> ? T : neve
|
|
|
24
20
|
* Reference resolver.
|
|
25
21
|
*/
|
|
26
22
|
export type Resolver = EchoSchema.RefResolver;
|
|
23
|
+
|
|
24
|
+
export const isRef: (value: unknown) => value is Any = EchoSchema.Ref.isRef;
|
|
25
|
+
|
|
26
|
+
export const make = EchoSchema.Ref.make;
|
|
27
|
+
|
|
28
|
+
// TODO(dmaretskyi): Consider just allowing `make` to accept DXN.
|
|
29
|
+
export const fromDXN = EchoSchema.Ref.fromDXN;
|
package/src/Relation.ts
CHANGED
|
@@ -2,18 +2,78 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { type Schema } from 'effect';
|
|
6
|
+
|
|
7
|
+
import { raise } from '@dxos/debug';
|
|
5
8
|
import * as EchoSchema from '@dxos/echo-schema';
|
|
6
9
|
import { assertArgument, invariant } from '@dxos/invariant';
|
|
7
10
|
import { DXN } from '@dxos/keys';
|
|
8
|
-
import
|
|
11
|
+
import { type Live, live } from '@dxos/live-object';
|
|
9
12
|
import { assumeType } from '@dxos/util';
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
import type * as Obj from './Obj';
|
|
15
|
+
import type * as Type from './Type';
|
|
16
|
+
|
|
17
|
+
// NOTE: Don't export: Relation.Relation and Relation.Any form the public API.
|
|
18
|
+
interface RelationBase<Source, Target>
|
|
19
|
+
extends Type.Relation.Endpoints<Source, Target>,
|
|
20
|
+
Type.OfKind<EchoSchema.EntityKind.Relation> {
|
|
21
|
+
readonly id: EchoSchema.ObjectId;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Relation type with specific properties.
|
|
26
|
+
*/
|
|
27
|
+
export type Relation<Source extends Obj.Any, Target extends Obj.Any, Props> = RelationBase<Source, Target> & Props;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Base type for all ECHO relations.
|
|
31
|
+
*/
|
|
32
|
+
export interface Any extends RelationBase<Obj.Any, Obj.Any> {}
|
|
33
|
+
|
|
34
|
+
// TODO(dmaretskyi): Has to be `unique symbol`.
|
|
35
|
+
export const Source: unique symbol = EchoSchema.RelationSourceId as any;
|
|
36
|
+
export type Source = typeof Source;
|
|
37
|
+
export const Target: unique symbol = EchoSchema.RelationTargetId as any;
|
|
38
|
+
export type Target = typeof Target;
|
|
39
|
+
|
|
40
|
+
type MakeProps<T extends Any> = {
|
|
41
|
+
id?: EchoSchema.ObjectId;
|
|
42
|
+
[Source]: T[Source];
|
|
43
|
+
[Target]: T[Target];
|
|
44
|
+
} & Type.Properties<T>;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates new relation.
|
|
48
|
+
* @param schema - Relation schema.
|
|
49
|
+
* @param props - Relation properties. Endpoints are passed as [Relation.Source] and [Relation.Target] keys.
|
|
50
|
+
* @param meta - Relation metadata.
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
// NOTE: Writing the definition this way (with generic over schema) makes typescript perfer to infer the type from the first param (this schema) rather than the second param (the props).
|
|
54
|
+
// TODO(dmaretskyi): Move meta into props.
|
|
55
|
+
export const make = <S extends Type.Relation.Any>(
|
|
56
|
+
schema: S,
|
|
57
|
+
props: NoInfer<MakeProps<Schema.Schema.Type<S>>>,
|
|
58
|
+
meta?: EchoSchema.ObjectMeta,
|
|
59
|
+
): Live<Schema.Schema.Type<S> & Type.OfKind<EchoSchema.EntityKind.Relation>> => {
|
|
60
|
+
assertArgument(
|
|
61
|
+
EchoSchema.getTypeAnnotation(schema)?.kind === EchoSchema.EntityKind.Relation,
|
|
62
|
+
'Expected a relation schema',
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (props[EchoSchema.MetaId] != null) {
|
|
66
|
+
meta = props[EchoSchema.MetaId] as any;
|
|
67
|
+
delete props[EchoSchema.MetaId];
|
|
68
|
+
}
|
|
12
69
|
|
|
13
|
-
|
|
14
|
-
|
|
70
|
+
const sourceDXN = EchoSchema.getObjectDXN(props[Source]) ?? raise(new Error('Unresolved relation source'));
|
|
71
|
+
const targetDXN = EchoSchema.getObjectDXN(props[Target]) ?? raise(new Error('Unresolved relation target'));
|
|
72
|
+
(props as any)[EchoSchema.RelationSourceDXNId] = sourceDXN;
|
|
73
|
+
(props as any)[EchoSchema.RelationTargetDXNId] = targetDXN;
|
|
15
74
|
|
|
16
|
-
|
|
75
|
+
return live<Schema.Schema.Type<S>>(schema, props as any, meta);
|
|
76
|
+
};
|
|
17
77
|
|
|
18
78
|
export const isRelation = (value: unknown): value is Any => {
|
|
19
79
|
if (typeof value !== 'object' || value === null) {
|
|
@@ -34,7 +94,7 @@ export const isRelation = (value: unknown): value is Any => {
|
|
|
34
94
|
export const getSourceDXN = (value: Any): DXN => {
|
|
35
95
|
assertArgument(isRelation(value), 'Expected a relation');
|
|
36
96
|
assumeType<EchoSchema.InternalObjectProps>(value);
|
|
37
|
-
const dxn = value[EchoSchema.RelationSourceDXNId];
|
|
97
|
+
const dxn = (value as EchoSchema.InternalObjectProps)[EchoSchema.RelationSourceDXNId];
|
|
38
98
|
invariant(dxn instanceof DXN);
|
|
39
99
|
return dxn;
|
|
40
100
|
};
|
|
@@ -46,7 +106,7 @@ export const getSourceDXN = (value: Any): DXN => {
|
|
|
46
106
|
export const getTargetDXN = (value: Any): DXN => {
|
|
47
107
|
assertArgument(isRelation(value), 'Expected a relation');
|
|
48
108
|
assumeType<EchoSchema.InternalObjectProps>(value);
|
|
49
|
-
const dxn = value[EchoSchema.RelationTargetDXNId];
|
|
109
|
+
const dxn = (value as EchoSchema.InternalObjectProps)[EchoSchema.RelationTargetDXNId];
|
|
50
110
|
invariant(dxn instanceof DXN);
|
|
51
111
|
return dxn;
|
|
52
112
|
};
|
|
@@ -55,20 +115,22 @@ export const getTargetDXN = (value: Any): DXN => {
|
|
|
55
115
|
* @returns Relation source.
|
|
56
116
|
* @throws If the object is not a relation.
|
|
57
117
|
*/
|
|
58
|
-
export const getSource = <T extends Any>(relation: T):
|
|
118
|
+
export const getSource = <T extends Any>(relation: T): Type.Relation.Source<T> => {
|
|
59
119
|
assertArgument(isRelation(relation), 'Expected a relation');
|
|
60
|
-
|
|
120
|
+
assumeType<EchoSchema.InternalObjectProps>(relation);
|
|
121
|
+
const obj = (relation as EchoSchema.InternalObjectProps)[EchoSchema.RelationSourceId];
|
|
61
122
|
invariant(obj !== undefined, `Invalid source: ${relation.id}`);
|
|
62
|
-
return obj
|
|
123
|
+
return obj as Type.Relation.Source<T>;
|
|
63
124
|
};
|
|
64
125
|
|
|
65
126
|
/**
|
|
66
127
|
* @returns Relation target.
|
|
67
128
|
* @throws If the object is not a relation.
|
|
68
129
|
*/
|
|
69
|
-
export const getTarget = <T extends Any>(relation: T):
|
|
130
|
+
export const getTarget = <T extends Any>(relation: T): Type.Relation.Target<T> => {
|
|
70
131
|
assertArgument(isRelation(relation), 'Expected a relation');
|
|
71
|
-
|
|
132
|
+
assumeType<EchoSchema.InternalObjectProps>(relation);
|
|
133
|
+
const obj = (relation as EchoSchema.InternalObjectProps)[EchoSchema.RelationTargetId];
|
|
72
134
|
invariant(obj !== undefined, `Invalid target: ${relation.id}`);
|
|
73
|
-
return obj
|
|
135
|
+
return obj as Type.Relation.Target<T>;
|
|
74
136
|
};
|