@dxos/echo 0.8.2-main.5ca3450 → 0.8.2-main.600d381
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/types/src/query/api.d.ts +90 -23
- package/dist/types/src/query/api.d.ts.map +1 -1
- package/dist/types/src/query/ast.d.ts +95 -137
- package/dist/types/src/query/ast.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/query/api.ts +283 -76
- package/src/query/ast.ts +120 -90
- package/src/query/query.test.ts +37 -26
- package/src/type/Type.test.ts +37 -18
package/src/query/ast.ts
CHANGED
|
@@ -6,144 +6,174 @@ import { Schema } from 'effect';
|
|
|
6
6
|
|
|
7
7
|
import { DXN } from '@dxos/echo-schema';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Schema.
|
|
19
|
-
|
|
20
|
-
value: Schema.
|
|
21
|
-
}),
|
|
22
|
-
Schema.Struct({
|
|
23
|
-
type: Schema.Literal('gte'),
|
|
24
|
-
value: Schema.Any,
|
|
25
|
-
}),
|
|
26
|
-
Schema.Struct({
|
|
27
|
-
type: Schema.Literal('lt'),
|
|
28
|
-
value: Schema.Any,
|
|
29
|
-
}),
|
|
30
|
-
Schema.Struct({
|
|
31
|
-
type: Schema.Literal('lte'),
|
|
32
|
-
value: Schema.Any,
|
|
33
|
-
}),
|
|
34
|
-
Schema.Struct({
|
|
35
|
-
type: Schema.Literal('in'),
|
|
36
|
-
values: Schema.Array(Schema.Any),
|
|
37
|
-
}),
|
|
38
|
-
Schema.Struct({
|
|
39
|
-
type: Schema.Literal('range'),
|
|
40
|
-
from: Schema.Any,
|
|
41
|
-
to: Schema.Any,
|
|
9
|
+
const TypenameSpecifier = Schema.Union(DXN, Schema.Null).annotations({
|
|
10
|
+
description: 'DXN or null. Null means any type will match',
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// 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.
|
|
14
|
+
|
|
15
|
+
const FilterObject_ = Schema.Struct({
|
|
16
|
+
type: Schema.Literal('object'),
|
|
17
|
+
typename: TypenameSpecifier,
|
|
18
|
+
props: Schema.Record({
|
|
19
|
+
key: Schema.String.annotations({ description: 'Property name' }),
|
|
20
|
+
value: Schema.suspend(() => Filter),
|
|
42
21
|
}),
|
|
43
|
-
);
|
|
22
|
+
});
|
|
23
|
+
interface FilterObject extends Schema.Schema.Type<typeof FilterObject_> {}
|
|
24
|
+
const FilterObject: Schema.Schema<FilterObject> = FilterObject_;
|
|
44
25
|
|
|
45
|
-
|
|
26
|
+
const FilterCompare_ = Schema.Struct({
|
|
27
|
+
type: Schema.Literal('compare'),
|
|
28
|
+
operator: Schema.Literal('eq', 'neq', 'gt', 'gte', 'lt', 'lte'),
|
|
29
|
+
value: Schema.Unknown,
|
|
30
|
+
});
|
|
31
|
+
interface FilterCompare extends Schema.Schema.Type<typeof FilterCompare_> {}
|
|
32
|
+
const FilterCompare: Schema.Schema<FilterCompare> = FilterCompare_;
|
|
46
33
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
const FilterIn_ = Schema.Struct({
|
|
35
|
+
type: Schema.Literal('in'),
|
|
36
|
+
values: Schema.Array(Schema.Any),
|
|
50
37
|
});
|
|
38
|
+
interface FilterIn extends Schema.Schema.Type<typeof FilterIn_> {}
|
|
39
|
+
const FilterIn: Schema.Schema<FilterIn> = FilterIn_;
|
|
51
40
|
|
|
52
|
-
|
|
41
|
+
const FilterRange_ = Schema.Struct({
|
|
42
|
+
type: Schema.Literal('range'),
|
|
43
|
+
from: Schema.Any,
|
|
44
|
+
to: Schema.Any,
|
|
45
|
+
});
|
|
46
|
+
interface FilterRange extends Schema.Schema.Type<typeof FilterRange_> {}
|
|
47
|
+
const FilterRange: Schema.Schema<FilterRange> = FilterRange_;
|
|
53
48
|
|
|
54
|
-
const
|
|
55
|
-
|
|
49
|
+
const FilterTextSearch_ = Schema.Struct({
|
|
50
|
+
type: Schema.Literal('text-search'),
|
|
51
|
+
typename: TypenameSpecifier,
|
|
52
|
+
text: Schema.String,
|
|
53
|
+
searchKind: Schema.optional(Schema.Literal('full-text', 'vector')),
|
|
56
54
|
});
|
|
55
|
+
interface FilterTextSearch extends Schema.Schema.Type<typeof FilterTextSearch_> {}
|
|
56
|
+
const FilterTextSearch: Schema.Schema<FilterTextSearch> = FilterTextSearch_;
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
const FilterNot_ = Schema.Struct({
|
|
59
|
+
type: Schema.Literal('not'),
|
|
60
|
+
filter: Schema.suspend(() => Filter),
|
|
61
|
+
});
|
|
62
|
+
interface FilterNot extends Schema.Schema.Type<typeof FilterNot_> {}
|
|
63
|
+
const FilterNot: Schema.Schema<FilterNot> = FilterNot_;
|
|
64
|
+
|
|
65
|
+
const FilterAnd_ = Schema.Struct({
|
|
66
|
+
type: Schema.Literal('and'),
|
|
67
|
+
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
68
|
+
});
|
|
69
|
+
interface FilterAnd extends Schema.Schema.Type<typeof FilterAnd_> {}
|
|
70
|
+
const FilterAnd: Schema.Schema<FilterAnd> = FilterAnd_;
|
|
71
|
+
|
|
72
|
+
const FilterOr_ = Schema.Struct({
|
|
73
|
+
type: Schema.Literal('or'),
|
|
74
|
+
filters: Schema.Array(Schema.suspend(() => Filter)),
|
|
75
|
+
});
|
|
76
|
+
interface FilterOr extends Schema.Schema.Type<typeof FilterOr_> {}
|
|
77
|
+
const FilterOr: Schema.Schema<FilterOr> = FilterOr_;
|
|
78
|
+
|
|
79
|
+
export const Filter = Schema.Union(
|
|
80
|
+
FilterObject,
|
|
81
|
+
FilterTextSearch,
|
|
82
|
+
FilterCompare,
|
|
83
|
+
FilterIn,
|
|
84
|
+
FilterRange,
|
|
85
|
+
FilterNot,
|
|
86
|
+
FilterAnd,
|
|
87
|
+
FilterOr,
|
|
88
|
+
);
|
|
89
|
+
export type Filter = Schema.Schema.Type<typeof Filter>;
|
|
59
90
|
|
|
60
91
|
/**
|
|
61
92
|
* Query objects by type, id, and/or predicates.
|
|
62
93
|
*/
|
|
63
|
-
const
|
|
64
|
-
type: Schema.Literal('
|
|
65
|
-
|
|
66
|
-
id: Schema.optional(Schema.String),
|
|
67
|
-
predicates: Schema.optional(PredicateSet),
|
|
94
|
+
const QuerySelectClause_ = Schema.Struct({
|
|
95
|
+
type: Schema.Literal('select'),
|
|
96
|
+
filter: Schema.suspend(() => Filter),
|
|
68
97
|
});
|
|
69
|
-
interface
|
|
70
|
-
const
|
|
98
|
+
interface QuerySelectClause extends Schema.Schema.Type<typeof QuerySelectClause_> {}
|
|
99
|
+
const QuerySelectClause: Schema.Schema<QuerySelectClause> = QuerySelectClause_;
|
|
71
100
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Filter objects from selection.
|
|
103
|
+
*/
|
|
104
|
+
const QueryFilterClause_ = Schema.Struct({
|
|
105
|
+
type: Schema.Literal('filter'),
|
|
106
|
+
selection: Schema.suspend(() => Query),
|
|
107
|
+
filter: Schema.suspend(() => Filter),
|
|
77
108
|
});
|
|
78
|
-
interface
|
|
79
|
-
const
|
|
109
|
+
interface QueryFilterClause extends Schema.Schema.Type<typeof QueryFilterClause_> {}
|
|
110
|
+
const QueryFilterClause: Schema.Schema<QueryFilterClause> = QueryFilterClause_;
|
|
80
111
|
|
|
81
112
|
/**
|
|
82
113
|
* Traverse references from an anchor object.
|
|
83
114
|
*/
|
|
84
|
-
const
|
|
115
|
+
const QueryReferenceTraversalClause_ = Schema.Struct({
|
|
85
116
|
type: Schema.Literal('reference-traversal'),
|
|
86
|
-
anchor: Schema.suspend(() =>
|
|
117
|
+
anchor: Schema.suspend(() => Query),
|
|
87
118
|
property: Schema.String,
|
|
88
119
|
});
|
|
89
|
-
interface
|
|
90
|
-
const
|
|
120
|
+
interface QueryReferenceTraversalClause extends Schema.Schema.Type<typeof QueryReferenceTraversalClause_> {}
|
|
121
|
+
const QueryReferenceTraversalClause: Schema.Schema<QueryReferenceTraversalClause> = QueryReferenceTraversalClause_;
|
|
91
122
|
|
|
92
123
|
/**
|
|
93
124
|
* Traverse incoming references to an anchor object.
|
|
94
125
|
*/
|
|
95
|
-
const
|
|
126
|
+
const QueryIncomingReferencesClause_ = Schema.Struct({
|
|
96
127
|
type: Schema.Literal('incoming-references'),
|
|
97
|
-
anchor: Schema.suspend(() =>
|
|
128
|
+
anchor: Schema.suspend(() => Query),
|
|
98
129
|
property: Schema.String,
|
|
99
130
|
typename: TypenameSpecifier,
|
|
100
131
|
});
|
|
101
|
-
interface
|
|
102
|
-
const
|
|
132
|
+
interface QueryIncomingReferencesClause extends Schema.Schema.Type<typeof QueryIncomingReferencesClause_> {}
|
|
133
|
+
const QueryIncomingReferencesClause: Schema.Schema<QueryIncomingReferencesClause> = QueryIncomingReferencesClause_;
|
|
103
134
|
|
|
104
135
|
/**
|
|
105
136
|
* Traverse relations connecting to an anchor object.
|
|
106
137
|
*/
|
|
107
|
-
const
|
|
138
|
+
const QueryRelationClause_ = Schema.Struct({
|
|
108
139
|
type: Schema.Literal('relation'),
|
|
109
|
-
anchor: Schema.suspend(() =>
|
|
140
|
+
anchor: Schema.suspend(() => Query),
|
|
110
141
|
direction: Schema.Literal('outgoing', 'incoming', 'both'),
|
|
111
|
-
|
|
112
|
-
predicates: Schema.optional(PredicateSet),
|
|
142
|
+
filter: Schema.optional(Schema.suspend(() => Filter)),
|
|
113
143
|
});
|
|
114
|
-
interface
|
|
115
|
-
const
|
|
144
|
+
interface QueryRelationClause extends Schema.Schema.Type<typeof QueryRelationClause_> {}
|
|
145
|
+
const QueryRelationClause: Schema.Schema<QueryRelationClause> = QueryRelationClause_;
|
|
116
146
|
|
|
117
147
|
/**
|
|
118
148
|
* Traverse into the source or target of a relation.
|
|
119
149
|
*/
|
|
120
|
-
const
|
|
150
|
+
const QueryRelationTraversalClause_ = Schema.Struct({
|
|
121
151
|
type: Schema.Literal('relation-traversal'),
|
|
122
|
-
anchor: Schema.suspend(() =>
|
|
152
|
+
anchor: Schema.suspend(() => Query),
|
|
123
153
|
direction: Schema.Literal('source', 'target', 'both'),
|
|
124
154
|
});
|
|
125
|
-
interface
|
|
126
|
-
const
|
|
155
|
+
interface QueryRelationTraversalClause extends Schema.Schema.Type<typeof QueryRelationTraversalClause_> {}
|
|
156
|
+
const QueryRelationTraversalClause: Schema.Schema<QueryRelationTraversalClause> = QueryRelationTraversalClause_;
|
|
127
157
|
|
|
128
158
|
/**
|
|
129
159
|
* Union of multiple queries.
|
|
130
160
|
*/
|
|
131
|
-
const
|
|
161
|
+
const QueryUnionClause_ = Schema.Struct({
|
|
132
162
|
type: Schema.Literal('union'),
|
|
133
|
-
queries: Schema.Array(Schema.suspend(() =>
|
|
134
|
-
});
|
|
135
|
-
interface
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
163
|
+
queries: Schema.Array(Schema.suspend(() => Query)),
|
|
164
|
+
});
|
|
165
|
+
interface QueryUnionClause extends Schema.Schema.Type<typeof QueryUnionClause_> {}
|
|
166
|
+
const QueryUnionClause: Schema.Schema<QueryUnionClause> = QueryUnionClause_;
|
|
167
|
+
|
|
168
|
+
const Query_ = Schema.Union(
|
|
169
|
+
QuerySelectClause,
|
|
170
|
+
QueryFilterClause,
|
|
171
|
+
QueryReferenceTraversalClause,
|
|
172
|
+
QueryIncomingReferencesClause,
|
|
173
|
+
QueryRelationClause,
|
|
174
|
+
QueryRelationTraversalClause,
|
|
175
|
+
QueryUnionClause,
|
|
146
176
|
);
|
|
147
177
|
|
|
148
|
-
export type
|
|
149
|
-
export const
|
|
178
|
+
export type Query = Schema.Schema.Type<typeof Query_>;
|
|
179
|
+
export const Query: Schema.Schema<Query> = Query_;
|
package/src/query/query.test.ts
CHANGED
|
@@ -8,7 +8,8 @@ import { describe, test } from 'vitest';
|
|
|
8
8
|
import { create } from '@dxos/echo-schema';
|
|
9
9
|
import { log } from '@dxos/log';
|
|
10
10
|
|
|
11
|
-
import { Query } from './api';
|
|
11
|
+
import { Filter, Query } from './api';
|
|
12
|
+
import * as QueryAST from './ast';
|
|
12
13
|
import { Type, Relation } from '..';
|
|
13
14
|
|
|
14
15
|
//
|
|
@@ -18,6 +19,7 @@ import { Type, Relation } from '..';
|
|
|
18
19
|
const Person = Schema.Struct({
|
|
19
20
|
name: Schema.String,
|
|
20
21
|
email: Schema.optional(Schema.String),
|
|
22
|
+
age: Schema.optional(Schema.Number),
|
|
21
23
|
}).pipe(Type.def({ typename: 'dxos.org/type/Person', version: '0.1.0' }));
|
|
22
24
|
interface Person extends Schema.Schema.Type<typeof Person> {}
|
|
23
25
|
|
|
@@ -48,88 +50,97 @@ describe('query api', () => {
|
|
|
48
50
|
const getAllPeople = Query.type(Person);
|
|
49
51
|
|
|
50
52
|
log.info('query', { ast: getAllPeople.ast });
|
|
53
|
+
Schema.validateSync(QueryAST.Query)(getAllPeople.ast);
|
|
51
54
|
});
|
|
52
55
|
|
|
53
56
|
test('get all people named Fred', () => {
|
|
54
57
|
// Query<Person>
|
|
55
|
-
const getAllPeopleNamedFred = Query.type(Person, { name: 'Fred' });
|
|
58
|
+
const getAllPeopleNamedFred = Query.select(Filter.type(Person, { name: 'Fred' }));
|
|
56
59
|
|
|
57
60
|
log.info('query', { ast: getAllPeopleNamedFred.ast });
|
|
61
|
+
Schema.validateSync(QueryAST.Query)(getAllPeopleNamedFred.ast);
|
|
58
62
|
});
|
|
59
63
|
|
|
60
64
|
test('get all orgs Fred worked for since 2020', () => {
|
|
61
65
|
// Query<Org>
|
|
62
66
|
const fred = create(Person, { name: 'Fred' });
|
|
63
|
-
const getAllOrgsFredWorkedForSince2020 = Query.type(Person, { id: fred.id })
|
|
64
|
-
.sourceOf(WorksFor, { since:
|
|
67
|
+
const getAllOrgsFredWorkedForSince2020 = Query.select(Filter.type(Person, { id: fred.id }))
|
|
68
|
+
.sourceOf(WorksFor, { since: Filter.gt('2020') })
|
|
65
69
|
.target();
|
|
66
70
|
|
|
67
71
|
log.info('query', { ast: getAllOrgsFredWorkedForSince2020.ast });
|
|
72
|
+
Schema.validateSync(QueryAST.Query)(getAllOrgsFredWorkedForSince2020.ast);
|
|
68
73
|
});
|
|
69
74
|
|
|
70
75
|
test('get all tasks for Fred', () => {
|
|
71
76
|
// Query<Task>
|
|
72
77
|
const fred = create(Person, { name: 'Fred' });
|
|
73
|
-
const getAllTasksForFred = Query.type(Person, { id: fred.id }).referencedBy(Task, 'assignee');
|
|
78
|
+
const getAllTasksForFred = Query.select(Filter.type(Person, { id: fred.id })).referencedBy(Task, 'assignee');
|
|
74
79
|
|
|
75
80
|
log.info('query', { ast: getAllTasksForFred.ast });
|
|
81
|
+
Schema.validateSync(QueryAST.Query)(getAllTasksForFred.ast);
|
|
76
82
|
});
|
|
77
83
|
|
|
78
84
|
test('get all tasks for employees of Cyberdyne', () => {
|
|
79
85
|
// Query<Task>
|
|
80
|
-
const allTasksForEmployeesOfCyberdyne = Query.type(Org, { name: 'Cyberdyne' })
|
|
86
|
+
const allTasksForEmployeesOfCyberdyne = Query.select(Filter.type(Org, { name: 'Cyberdyne' }))
|
|
81
87
|
.targetOf(WorksFor)
|
|
82
88
|
.source()
|
|
83
89
|
.referencedBy(Task, 'assignee');
|
|
84
90
|
|
|
85
91
|
log.info('query', { ast: allTasksForEmployeesOfCyberdyne.ast });
|
|
92
|
+
Schema.validateSync(QueryAST.Query)(allTasksForEmployeesOfCyberdyne.ast);
|
|
86
93
|
});
|
|
87
94
|
|
|
88
95
|
test('get all people or orgs', () => {
|
|
89
96
|
// Query<Person | Org>
|
|
90
|
-
const allPeopleOrOrgs = Query.all(Query.type(Person), Query.type(Org));
|
|
97
|
+
const allPeopleOrOrgs = Query.all(Query.select(Filter.type(Person)), Query.select(Filter.type(Org)));
|
|
91
98
|
|
|
92
99
|
log.info('query', { ast: allPeopleOrOrgs.ast });
|
|
100
|
+
Schema.validateSync(QueryAST.Query)(allPeopleOrOrgs.ast);
|
|
93
101
|
});
|
|
94
102
|
|
|
95
103
|
test('get assignees of all tasks created after 2020', () => {
|
|
96
104
|
// Query<Person>
|
|
97
|
-
const assigneesOfAllTasksCreatedAfter2020 = Query.
|
|
105
|
+
const assigneesOfAllTasksCreatedAfter2020 = Query.select(
|
|
106
|
+
Filter.type(Task, { createdAt: Filter.gt('2020') }),
|
|
107
|
+
).reference('assignee');
|
|
98
108
|
|
|
99
109
|
log.info('query', { ast: assigneesOfAllTasksCreatedAfter2020.ast });
|
|
110
|
+
Schema.validateSync(QueryAST.Query)(assigneesOfAllTasksCreatedAfter2020.ast);
|
|
100
111
|
});
|
|
101
112
|
|
|
102
113
|
test('contact full-text search', () => {
|
|
103
114
|
// Query<Person>
|
|
104
|
-
const contactFullTextSearch = Query.text(Person, 'Bill');
|
|
115
|
+
const contactFullTextSearch = Query.select(Filter.text(Person, 'Bill'));
|
|
105
116
|
|
|
106
117
|
log.info('query', { ast: contactFullTextSearch.ast });
|
|
118
|
+
Schema.validateSync(QueryAST.Query)(contactFullTextSearch.ast);
|
|
107
119
|
});
|
|
108
120
|
|
|
109
|
-
// TODO(burdon): Experimental.
|
|
110
121
|
test.skip('chain', () => {
|
|
111
|
-
|
|
112
|
-
const
|
|
113
|
-
const Filter: any = null;
|
|
122
|
+
// NOTE: Can't support props without type since they can't be inferred.
|
|
123
|
+
// const f1: Filter<Person> = Filter.props({ name: 'Fred' });
|
|
114
124
|
|
|
115
|
-
const x =
|
|
116
|
-
const y =
|
|
125
|
+
// const x = Query.select(Filter.props({ id: '123' }));
|
|
126
|
+
const y = Query.select(Filter.type(Person));
|
|
127
|
+
|
|
128
|
+
const fOr = Filter.or(Filter.type(Person, { id: Filter.in('1', '2', '3') }), Filter.type(Org));
|
|
129
|
+
|
|
130
|
+
const fAnd = Filter.and(
|
|
131
|
+
Filter.type(Person, { id: Filter.in('1', '2', '3') }),
|
|
132
|
+
Filter.type(Person, { name: 'Fred' }),
|
|
133
|
+
);
|
|
117
134
|
|
|
118
135
|
const q = Query
|
|
119
136
|
//
|
|
120
|
-
.selectAll()
|
|
121
|
-
.select({ id: '123' })
|
|
122
137
|
// NOTE: Can't support functions since they can't be serialized (to server).
|
|
123
138
|
// .filter((object) => Math.random() > 0.5)
|
|
124
139
|
.select(Filter.type(Person))
|
|
125
|
-
.select(Filter.
|
|
126
|
-
.select({ age: Filter.
|
|
127
|
-
.select(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.target()
|
|
131
|
-
.select();
|
|
132
|
-
|
|
133
|
-
log.info('stuff', { x, y, q });
|
|
140
|
+
.select(Filter.type(Person, { name: 'Fred' }))
|
|
141
|
+
.select({ age: Filter.between(20, 40) })
|
|
142
|
+
.select(Filter.and(Filter.type(Person), Filter.type(Person, { name: Filter.in('bob', 'bill') })));
|
|
143
|
+
|
|
144
|
+
log.info('stuff', { fOr, fAnd, q, y });
|
|
134
145
|
});
|
|
135
146
|
});
|
package/src/type/Type.test.ts
CHANGED
|
@@ -37,14 +37,25 @@ namespace Testing {
|
|
|
37
37
|
|
|
38
38
|
export interface Person extends Schema.Schema.Type<typeof Person> {}
|
|
39
39
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
40
|
+
// export const WorksFor = S.Struct({
|
|
41
|
+
// id: Type.ObjectId,
|
|
42
|
+
// since: S.String,
|
|
43
|
+
// jobTitle: S.String,
|
|
44
|
+
// ...Range({ from, to }),
|
|
45
|
+
// ...Provenance({ source: 'duckduckgo.com', confidence: 0.9 }), // keys
|
|
46
|
+
// ...Relation.make({ source: Contact, target: Organization }),
|
|
47
|
+
// }).pipe(
|
|
48
|
+
// Relation.def({
|
|
49
|
+
// typename: 'example.com/relation/WorksFor',
|
|
50
|
+
// version: '0.1.0',
|
|
51
|
+
// }),
|
|
52
|
+
// );
|
|
53
|
+
|
|
54
|
+
// {
|
|
55
|
+
// const contact = db.add(create(Contact, { name: 'Test' }));
|
|
56
|
+
// const organization = db.add(create(Organization, { name: 'DXOS' }));
|
|
57
|
+
// db.add(create(WorksFor, { source: contact, target: organization }));
|
|
58
|
+
// }
|
|
48
59
|
|
|
49
60
|
export const WorksFor = Schema.Struct({
|
|
50
61
|
// id: Type.ObjectId,
|
|
@@ -61,13 +72,23 @@ namespace Testing {
|
|
|
61
72
|
|
|
62
73
|
export interface WorksFor extends Schema.Schema.Type<typeof WorksFor> {}
|
|
63
74
|
|
|
64
|
-
// TODO(burdon): Fix (Type.def currently removes TypeLiteral that implements the `make` function)
|
|
65
|
-
// }
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
// TODO(burdon): Fix (Type.def currently removes TypeLiteral that implements the `make` function).
|
|
76
|
+
// Property 'make' does not exist on type 'EchoObjectSchema<Struct<{ timestamp: PropertySignature<":", string, never, ":", string, true, never>; }>>'.ts(2339)
|
|
77
|
+
export const MessageStruct = Schema.Struct({
|
|
78
|
+
// TODO(burdon): Support S.Date; Custom Timestamp (with defaults).
|
|
79
|
+
// TODO(burdon): Support defaults (update create and create).
|
|
80
|
+
timestamp: Schema.String.pipe(
|
|
81
|
+
Schema.propertySignature,
|
|
82
|
+
Schema.withConstructorDefault(() => new Date().toISOString()),
|
|
83
|
+
),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const Message = MessageStruct.pipe(
|
|
87
|
+
Type.def({
|
|
88
|
+
typename: 'example.com/type/Message',
|
|
89
|
+
version: '0.1.0',
|
|
90
|
+
}),
|
|
91
|
+
);
|
|
71
92
|
|
|
72
93
|
export interface Message extends Schema.Schema.Type<typeof Message> {}
|
|
73
94
|
}
|
|
@@ -98,9 +119,7 @@ describe('Experimental API review', () => {
|
|
|
98
119
|
});
|
|
99
120
|
|
|
100
121
|
test('default props', ({ expect }) => {
|
|
101
|
-
|
|
102
|
-
// Property 'make' does not exist on type 'EchoObjectSchema<Struct<{ timestamp: PropertySignature<":", string, never, ":", string, true, never>; }>>'.ts(2339)
|
|
103
|
-
const message = Type.create(Testing.Message, Testing.Message.make({}));
|
|
122
|
+
const message = Type.create(Testing.Message, Testing.MessageStruct.make({}));
|
|
104
123
|
expect(message.timestamp).to.exist;
|
|
105
124
|
});
|
|
106
125
|
});
|