@dxos/echo-db 2.29.1 → 2.29.2-dev.f64f2a6f
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/src/api/database.d.ts +11 -7
- package/dist/src/api/database.d.ts.map +1 -1
- package/dist/src/api/database.js +20 -23
- package/dist/src/api/database.js.map +1 -1
- package/dist/src/api/database.test.js +13 -13
- package/dist/src/api/database.test.js.map +1 -1
- package/dist/src/api/item.d.ts.map +1 -1
- package/dist/src/api/item.js +1 -1
- package/dist/src/api/item.js.map +1 -1
- package/dist/src/api/result-set.d.ts.map +1 -1
- package/dist/src/api/result-set.js +1 -0
- package/dist/src/api/result-set.js.map +1 -1
- package/dist/src/api/selection/index.d.ts +5 -0
- package/dist/src/api/selection/index.d.ts.map +1 -0
- package/dist/src/api/selection/index.js +20 -0
- package/dist/src/api/selection/index.js.map +1 -0
- package/dist/src/api/selection/queries.d.ts +51 -0
- package/dist/src/api/selection/queries.d.ts.map +1 -0
- package/dist/src/api/selection/queries.js +70 -0
- package/dist/src/api/selection/queries.js.map +1 -0
- package/dist/src/api/selection/result.d.ts +50 -0
- package/dist/src/api/selection/result.d.ts.map +1 -0
- package/dist/src/api/selection/result.js +91 -0
- package/dist/src/api/selection/result.js.map +1 -0
- package/dist/src/api/selection/selection.d.ts +96 -0
- package/dist/src/api/selection/selection.d.ts.map +1 -0
- package/dist/src/api/selection/selection.js +164 -0
- package/dist/src/api/selection/selection.js.map +1 -0
- package/dist/src/api/{selection.test.d.ts → selection/selection.test.d.ts} +0 -0
- package/dist/src/api/selection/selection.test.d.ts.map +1 -0
- package/dist/src/api/{selection.test.js → selection/selection.test.js} +46 -44
- package/dist/src/api/selection/selection.test.js.map +1 -0
- package/dist/src/api/selection/util.d.ts +7 -0
- package/dist/src/api/selection/util.d.ts.map +1 -0
- package/dist/src/api/selection/util.js +25 -0
- package/dist/src/api/selection/util.js.map +1 -0
- package/dist/src/echo.test.js +20 -20
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/contact-manager.js +2 -2
- package/dist/src/halo/contact-manager.js.map +1 -1
- package/dist/src/halo/preferences.js +6 -6
- package/dist/src/halo/preferences.js.map +1 -1
- package/dist/src/parties/party-core.test.js +3 -3
- package/dist/src/parties/party-core.test.js.map +1 -1
- package/dist/src/parties/party-factory.d.ts.map +1 -1
- package/dist/src/parties/party-factory.js +3 -3
- package/dist/src/parties/party-factory.js.map +1 -1
- package/dist/src/parties/party-internal.js +3 -3
- package/dist/src/parties/party-internal.js.map +1 -1
- package/dist/src/parties/party-manager.js +1 -1
- package/dist/src/parties/party-manager.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +8 -10
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/testing/testing-factories.d.ts +2 -2
- package/dist/src/testing/testing-factories.d.ts.map +1 -1
- package/dist/src/testing/testing-factories.js +1 -1
- package/dist/src/testing/testing-factories.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +27 -27
- package/src/api/database.test.ts +13 -13
- package/src/api/database.ts +28 -29
- package/src/api/item.ts +2 -2
- package/src/api/result-set.ts +1 -0
- package/src/api/selection/index.ts +8 -0
- package/src/api/selection/queries.ts +108 -0
- package/src/api/selection/result.ts +112 -0
- package/src/api/{selection.test.ts → selection/selection.test.ts} +50 -48
- package/src/api/{selection.ts → selection/selection.ts} +30 -231
- package/src/api/selection/util.ts +27 -0
- package/src/echo.test.ts +20 -20
- package/src/halo/contact-manager.ts +2 -2
- package/src/halo/preferences.ts +6 -6
- package/src/parties/party-core.test.ts +3 -3
- package/src/parties/party-factory.ts +3 -4
- package/src/parties/party-internal.ts +3 -3
- package/src/parties/party-manager.test.ts +8 -10
- package/src/parties/party-manager.ts +1 -1
- package/src/testing/testing-factories.ts +3 -3
- package/dist/src/api/selection.d.ts +0 -183
- package/dist/src/api/selection.d.ts.map +0 -1
- package/dist/src/api/selection.js +0 -308
- package/dist/src/api/selection.js.map +0 -1
- package/dist/src/api/selection.test.d.ts.map +0 -1
- package/dist/src/api/selection.test.js.map +0 -1
|
@@ -11,10 +11,11 @@ import { ItemID, ItemType } from '@dxos/echo-protocol';
|
|
|
11
11
|
import { ModelFactory } from '@dxos/model-factory';
|
|
12
12
|
import { ObjectModel } from '@dxos/object-model';
|
|
13
13
|
|
|
14
|
-
import { Entity } from '
|
|
15
|
-
import { Item } from '
|
|
16
|
-
import { Link } from '
|
|
17
|
-
import { RootFilter
|
|
14
|
+
import { Entity } from '../entity';
|
|
15
|
+
import { Item } from '../item';
|
|
16
|
+
import { Link } from '../link';
|
|
17
|
+
import { RootFilter } from './queries';
|
|
18
|
+
import { createSelection } from './selection';
|
|
18
19
|
|
|
19
20
|
// Use to prevent ultra-long diffs.
|
|
20
21
|
const ids = (entities: Entity[]) => entities.map(entity => entity.id);
|
|
@@ -23,8 +24,9 @@ const modelFactory = new ModelFactory().registerModel(ObjectModel);
|
|
|
23
24
|
|
|
24
25
|
const createModel = (id: ItemID) => modelFactory.createModel(ObjectModel.meta.type, id, {}, PublicKey.random());
|
|
25
26
|
|
|
26
|
-
const createItem = (id: ItemID, type: ItemType, parent?: Item<any>) =>
|
|
27
|
-
new Item(null as any, id, type, createModel(id), undefined, parent);
|
|
27
|
+
const createItem = (id: ItemID, type: ItemType, parent?: Item<any>) => {
|
|
28
|
+
return new Item(null as any, id, type, createModel(id), undefined, parent);
|
|
29
|
+
};
|
|
28
30
|
|
|
29
31
|
const createLink = (id: ItemID, type: ItemType, source: Item<any>, target: Item<any>) => {
|
|
30
32
|
const link = new Link(null as any, id, type, createModel(id), {
|
|
@@ -40,11 +42,11 @@ const createLink = (id: ItemID, type: ItemType, source: Item<any>, target: Item<
|
|
|
40
42
|
return link;
|
|
41
43
|
};
|
|
42
44
|
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
+
const createRootSelection = (filter?: RootFilter) =>
|
|
46
|
+
createSelection<void>(() => items, () => new Event(), null as any, filter, undefined);
|
|
45
47
|
|
|
46
48
|
const createReducer = <R>(result: R) =>
|
|
47
|
-
|
|
49
|
+
createSelection<R>(() => items, () => new Event(), null as any, undefined, result);
|
|
48
50
|
|
|
49
51
|
// TODO(burdon): Use more complex data set (org, person, project, task).
|
|
50
52
|
|
|
@@ -90,34 +92,34 @@ describe('Selection', () => {
|
|
|
90
92
|
describe('root', () => {
|
|
91
93
|
test('all', () => {
|
|
92
94
|
expect(
|
|
93
|
-
|
|
94
|
-
.
|
|
95
|
+
createRootSelection()
|
|
96
|
+
.exec().entities
|
|
95
97
|
).toHaveLength(items.length);
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
test('by id', () => {
|
|
99
101
|
expect(
|
|
100
|
-
|
|
101
|
-
.
|
|
102
|
+
createRootSelection({ id: org1.id })
|
|
103
|
+
.exec().entities
|
|
102
104
|
).toEqual([org1]);
|
|
103
105
|
|
|
104
106
|
expect(
|
|
105
|
-
|
|
106
|
-
.
|
|
107
|
+
createRootSelection({ id: org2.id })
|
|
108
|
+
.exec().entities
|
|
107
109
|
).toEqual([org2]);
|
|
108
110
|
});
|
|
109
111
|
|
|
110
112
|
test('single type', () => {
|
|
111
113
|
expect(
|
|
112
|
-
|
|
113
|
-
.
|
|
114
|
+
createRootSelection({ type: ITEM_PROJECT })
|
|
115
|
+
.exec().entities
|
|
114
116
|
).toHaveLength(3);
|
|
115
117
|
});
|
|
116
118
|
|
|
117
119
|
test('multiple types', () => {
|
|
118
120
|
expect(
|
|
119
|
-
|
|
120
|
-
.
|
|
121
|
+
createRootSelection({ type: [ITEM_ORG, ITEM_PROJECT] })
|
|
122
|
+
.exec().entities
|
|
121
123
|
).toHaveLength(5);
|
|
122
124
|
});
|
|
123
125
|
});
|
|
@@ -125,33 +127,33 @@ describe('Selection', () => {
|
|
|
125
127
|
describe('filter', () => {
|
|
126
128
|
test('invalid', () => {
|
|
127
129
|
expect(
|
|
128
|
-
|
|
129
|
-
.filter({ type: 'dxos:type
|
|
130
|
-
.
|
|
130
|
+
createRootSelection()
|
|
131
|
+
.filter({ type: 'dxos:type/invalid' })
|
|
132
|
+
.exec().entities
|
|
131
133
|
).toHaveLength(0);
|
|
132
134
|
});
|
|
133
135
|
|
|
134
136
|
test('single type', () => {
|
|
135
137
|
expect(
|
|
136
|
-
|
|
138
|
+
createRootSelection()
|
|
137
139
|
.filter({ type: ITEM_PROJECT })
|
|
138
|
-
.
|
|
140
|
+
.exec().entities
|
|
139
141
|
).toHaveLength(3);
|
|
140
142
|
});
|
|
141
143
|
|
|
142
144
|
test('multiple types', () => {
|
|
143
145
|
expect(
|
|
144
|
-
|
|
146
|
+
createRootSelection()
|
|
145
147
|
.filter({ type: [ITEM_ORG, ITEM_PROJECT] })
|
|
146
|
-
.
|
|
148
|
+
.exec().entities
|
|
147
149
|
).toHaveLength(5);
|
|
148
150
|
});
|
|
149
151
|
|
|
150
152
|
test('by function', () => {
|
|
151
153
|
expect(
|
|
152
|
-
|
|
154
|
+
createRootSelection()
|
|
153
155
|
.filter(item => item.type === ITEM_ORG)
|
|
154
|
-
.
|
|
156
|
+
.exec().entities
|
|
155
157
|
).toHaveLength(2);
|
|
156
158
|
});
|
|
157
159
|
});
|
|
@@ -159,10 +161,10 @@ describe('Selection', () => {
|
|
|
159
161
|
describe('children', () => {
|
|
160
162
|
test('from multiple items', () => {
|
|
161
163
|
expect(ids(
|
|
162
|
-
|
|
164
|
+
createRootSelection()
|
|
163
165
|
.filter({ type: ITEM_ORG })
|
|
164
166
|
.children({ type: ITEM_PROJECT })
|
|
165
|
-
.
|
|
167
|
+
.exec().entities
|
|
166
168
|
)).toStrictEqual(ids([
|
|
167
169
|
project1,
|
|
168
170
|
project2,
|
|
@@ -172,9 +174,9 @@ describe('Selection', () => {
|
|
|
172
174
|
|
|
173
175
|
test('from single item', () => {
|
|
174
176
|
expect(ids(
|
|
175
|
-
|
|
177
|
+
createRootSelection({ id: org1.id })
|
|
176
178
|
.children()
|
|
177
|
-
.
|
|
179
|
+
.exec().entities
|
|
178
180
|
)).toStrictEqual(ids([
|
|
179
181
|
project1,
|
|
180
182
|
project2,
|
|
@@ -187,10 +189,10 @@ describe('Selection', () => {
|
|
|
187
189
|
describe('parent', () => {
|
|
188
190
|
test('from multiple items', () => {
|
|
189
191
|
expect(ids(
|
|
190
|
-
|
|
192
|
+
createRootSelection()
|
|
191
193
|
.filter({ type: ITEM_PROJECT })
|
|
192
194
|
.parent()
|
|
193
|
-
.
|
|
195
|
+
.exec().entities
|
|
194
196
|
)).toStrictEqual(ids([
|
|
195
197
|
org1,
|
|
196
198
|
org2
|
|
@@ -199,9 +201,9 @@ describe('Selection', () => {
|
|
|
199
201
|
|
|
200
202
|
test('from single item', () => {
|
|
201
203
|
expect(ids(
|
|
202
|
-
|
|
204
|
+
createRootSelection({ id: project1.id })
|
|
203
205
|
.parent()
|
|
204
|
-
.
|
|
206
|
+
.exec().entities
|
|
205
207
|
)).toStrictEqual(ids([
|
|
206
208
|
org1
|
|
207
209
|
]));
|
|
@@ -209,9 +211,9 @@ describe('Selection', () => {
|
|
|
209
211
|
|
|
210
212
|
test('is empty', () => {
|
|
211
213
|
expect(
|
|
212
|
-
|
|
214
|
+
createRootSelection({ id: org1.id })
|
|
213
215
|
.parent()
|
|
214
|
-
.
|
|
216
|
+
.exec().entities
|
|
215
217
|
).toEqual([]);
|
|
216
218
|
});
|
|
217
219
|
});
|
|
@@ -219,10 +221,10 @@ describe('Selection', () => {
|
|
|
219
221
|
describe('links', () => {
|
|
220
222
|
test('links from single item', () => {
|
|
221
223
|
expect(ids(
|
|
222
|
-
|
|
224
|
+
createRootSelection({ id: project1.id })
|
|
223
225
|
.links()
|
|
224
226
|
.target()
|
|
225
|
-
.
|
|
227
|
+
.exec().entities
|
|
226
228
|
)).toStrictEqual(ids([
|
|
227
229
|
person1,
|
|
228
230
|
person2
|
|
@@ -231,18 +233,18 @@ describe('Selection', () => {
|
|
|
231
233
|
|
|
232
234
|
test('links from multiple items', () => {
|
|
233
235
|
expect(
|
|
234
|
-
|
|
236
|
+
createRootSelection({ type: ITEM_PROJECT })
|
|
235
237
|
.links()
|
|
236
|
-
.
|
|
238
|
+
.exec().entities
|
|
237
239
|
).toHaveLength(links.length);
|
|
238
240
|
});
|
|
239
241
|
|
|
240
242
|
test('sources', () => {
|
|
241
243
|
expect(ids(
|
|
242
|
-
|
|
244
|
+
createRootSelection({ type: ITEM_PERSON })
|
|
243
245
|
.refs()
|
|
244
246
|
.source()
|
|
245
|
-
.
|
|
247
|
+
.exec().entities
|
|
246
248
|
)).toStrictEqual(ids([
|
|
247
249
|
project1,
|
|
248
250
|
project2
|
|
@@ -252,7 +254,7 @@ describe('Selection', () => {
|
|
|
252
254
|
|
|
253
255
|
describe('reducer', () => {
|
|
254
256
|
test('simple reducer', () => {
|
|
255
|
-
const query = createReducer(0).call((items, count) => count + items.length).
|
|
257
|
+
const query = createReducer(0).call((items, count) => count + items.length).exec();
|
|
256
258
|
expect(query.value).toEqual(items.length);
|
|
257
259
|
});
|
|
258
260
|
|
|
@@ -272,7 +274,7 @@ describe('Selection', () => {
|
|
|
272
274
|
return { ...rest, numLinks: numLinks + links.length, stage: 'c' };
|
|
273
275
|
})
|
|
274
276
|
.target()
|
|
275
|
-
.
|
|
277
|
+
.exec();
|
|
276
278
|
|
|
277
279
|
expect(query.value).toEqual({ numItems: 5, numLinks: 4, stage: 'c' });
|
|
278
280
|
});
|
|
@@ -282,9 +284,9 @@ describe('Selection', () => {
|
|
|
282
284
|
test('events get filtered correctly', async () => {
|
|
283
285
|
const update = new Event<Entity[]>();
|
|
284
286
|
|
|
285
|
-
const query =
|
|
287
|
+
const query = createSelection<void>(() => items, () => update, null as any, { type: ITEM_ORG }, undefined)
|
|
286
288
|
.children()
|
|
287
|
-
.
|
|
289
|
+
.exec();
|
|
288
290
|
|
|
289
291
|
{
|
|
290
292
|
const promise = query.update.waitForCount(1);
|
|
@@ -2,82 +2,23 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import assert from 'assert';
|
|
6
|
-
|
|
7
5
|
import { Event } from '@dxos/async';
|
|
8
|
-
import { ItemID } from '@dxos/echo-protocol';
|
|
9
|
-
|
|
10
|
-
import { Database } from './database';
|
|
11
|
-
import { Entity } from './entity';
|
|
12
|
-
import { Item } from './item';
|
|
13
|
-
import { Link } from './link';
|
|
14
|
-
|
|
15
|
-
export type OneOrMultiple<T> = T | T[]
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
// Filters
|
|
19
|
-
//
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
export type RootFilter = ItemIdFilter | ItemFilter | Predicate<Item>
|
|
37
|
-
|
|
38
|
-
export type RootSelector<R = void> = (filter?: RootFilter) => Selection<Item<any>, R>
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Controls how deleted items are filtered.
|
|
42
|
-
*/
|
|
43
|
-
export enum ItemFilterDeleted {
|
|
44
|
-
/**
|
|
45
|
-
* Do not return deleted items. Default behaviour.
|
|
46
|
-
*/
|
|
47
|
-
HIDE_DELETED = 0,
|
|
48
|
-
/**
|
|
49
|
-
* Return deleted and regular items.
|
|
50
|
-
*/
|
|
51
|
-
SHOW_DELETED = 1,
|
|
52
|
-
/**
|
|
53
|
-
* Return only deleted items.
|
|
54
|
-
*/
|
|
55
|
-
SHOW_DELETED_ONLY = 2
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type QueryOptions = {
|
|
59
|
-
/**
|
|
60
|
-
* Controls how deleted items are filtered.
|
|
61
|
-
*/
|
|
62
|
-
deleted?: ItemFilterDeleted
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Represents where the selection has started.
|
|
67
|
-
*/
|
|
68
|
-
export type SelectionRoot = Database | Entity;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Returned from each stage of the visitor.
|
|
72
|
-
*/
|
|
73
|
-
export type SelectionContext<T extends Entity, R> = [entities: T[], result?: R]
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Visitor callback.
|
|
77
|
-
* The visitor is passed the current entities and result (accumulator),
|
|
78
|
-
* which may be modified and returned.
|
|
79
|
-
*/
|
|
80
|
-
export type Callable<T extends Entity, R> = (entities: T[], result: R) => R
|
|
7
|
+
import { Entity } from '../entity';
|
|
8
|
+
import { Item } from '../item';
|
|
9
|
+
import { Link } from '../link';
|
|
10
|
+
import {
|
|
11
|
+
createQueryOptionsFilter,
|
|
12
|
+
filterToPredicate,
|
|
13
|
+
linkFilterToPredicate,
|
|
14
|
+
Callable,
|
|
15
|
+
ItemFilter,
|
|
16
|
+
LinkFilter,
|
|
17
|
+
Predicate,
|
|
18
|
+
QueryOptions,
|
|
19
|
+
RootFilter
|
|
20
|
+
} from './queries';
|
|
21
|
+
import { SelectionContext, SelectionResult, SelectionRoot } from './result';
|
|
81
22
|
|
|
82
23
|
/**
|
|
83
24
|
* Factory for selector that provides a root set of items.
|
|
@@ -87,7 +28,7 @@ export type Callable<T extends Entity, R> = (entities: T[], result: R) => R
|
|
|
87
28
|
* @param filter
|
|
88
29
|
* @param value Initial reducer value.
|
|
89
30
|
*/
|
|
90
|
-
export const
|
|
31
|
+
export const createSelection = <R>(
|
|
91
32
|
// Provider is called each time the query is executed.
|
|
92
33
|
itemsProvider: () => Item[],
|
|
93
34
|
// TODO(burdon): Replace with direct event handler.
|
|
@@ -97,6 +38,8 @@ export const createSelector = <R>(
|
|
|
97
38
|
value: R
|
|
98
39
|
): Selection<Item<any>, R> => {
|
|
99
40
|
const predicate = filter ? filterToPredicate(filter) : () => true;
|
|
41
|
+
|
|
42
|
+
// TODO(burdon): Option to filter out system types.
|
|
100
43
|
const visitor = (options: QueryOptions): SelectionContext<any, any> => {
|
|
101
44
|
const items = itemsProvider()
|
|
102
45
|
.filter(createQueryOptionsFilter(options))
|
|
@@ -114,11 +57,13 @@ export const createSelector = <R>(
|
|
|
114
57
|
* @param update
|
|
115
58
|
* @param value Initial reducer value.
|
|
116
59
|
*/
|
|
117
|
-
export const
|
|
60
|
+
export const createItemSelection = <R>(
|
|
118
61
|
root: Item<any>,
|
|
119
62
|
update: Event<Entity[]>,
|
|
120
63
|
value: R
|
|
121
|
-
): Selection<Item<any>, R> =>
|
|
64
|
+
): Selection<Item<any>, R> => {
|
|
65
|
+
return new Selection(() => [[root], value], update, root, value !== undefined);
|
|
66
|
+
};
|
|
122
67
|
|
|
123
68
|
/**
|
|
124
69
|
* Selections are used to construct database subscriptions.
|
|
@@ -155,7 +100,14 @@ export class Selection<T extends Entity<any>, R = void> {
|
|
|
155
100
|
/**
|
|
156
101
|
* Finish the selection and return the result.
|
|
157
102
|
*/
|
|
158
|
-
|
|
103
|
+
exec (options: QueryOptions = {}): SelectionResult<T, R> {
|
|
104
|
+
return this.query(options);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @deprecated
|
|
109
|
+
*/
|
|
110
|
+
// TODO(burdon): Remove.
|
|
159
111
|
query (options: QueryOptions = {}): SelectionResult<T, R> {
|
|
160
112
|
return new SelectionResult<T, R>(() => this._visitor(options), this._update, this._root, this._reducer);
|
|
161
113
|
}
|
|
@@ -254,156 +206,3 @@ export class Selection<T extends Entity<any>, R = void> {
|
|
|
254
206
|
]);
|
|
255
207
|
}
|
|
256
208
|
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Query subscription.
|
|
260
|
-
* Represents a live-query (subscription) that can notify about future updates to the relevant subset of items.
|
|
261
|
-
*/
|
|
262
|
-
export class SelectionResult<T extends Entity, R = any> {
|
|
263
|
-
/**
|
|
264
|
-
* Fired when there are updates in the selection.
|
|
265
|
-
* Only update that are relevant to the selection cause the update.
|
|
266
|
-
*/
|
|
267
|
-
readonly update = new Event<SelectionResult<T>>(); // TODO(burdon): Result result object.
|
|
268
|
-
|
|
269
|
-
private _lastResult: SelectionContext<T, R> = [[]];
|
|
270
|
-
|
|
271
|
-
constructor (
|
|
272
|
-
private readonly _execute: () => SelectionContext<T, R>,
|
|
273
|
-
private readonly _update: Event<Entity[]>,
|
|
274
|
-
private readonly _root: SelectionRoot,
|
|
275
|
-
private readonly _reducer: boolean
|
|
276
|
-
) {
|
|
277
|
-
this.refresh();
|
|
278
|
-
|
|
279
|
-
// Re-run if deps change.
|
|
280
|
-
this.update.addEffect(() => _update.on(currentEntities => {
|
|
281
|
-
const [previousEntities] = this._lastResult;
|
|
282
|
-
|
|
283
|
-
this.refresh();
|
|
284
|
-
|
|
285
|
-
// Filters mutation events only if selection (since we can't reason about deps of call methods).
|
|
286
|
-
const set = new Set([...previousEntities, ...this._lastResult![0]]);
|
|
287
|
-
if (this._reducer || currentEntities.some(entity => set.has(entity as any))) {
|
|
288
|
-
this.update.emit(this);
|
|
289
|
-
}
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Re-run query.
|
|
295
|
-
*/
|
|
296
|
-
refresh () {
|
|
297
|
-
const [entities, result] = this._execute();
|
|
298
|
-
this._lastResult = [dedupe(entities), result];
|
|
299
|
-
return this;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* The root of the selection. Either a database or an item. Must be a stable reference.
|
|
304
|
-
*/
|
|
305
|
-
get root (): SelectionRoot {
|
|
306
|
-
return this._root;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* @deprecated
|
|
311
|
-
*/
|
|
312
|
-
// TODO(burdon): Remove.
|
|
313
|
-
// get result () {
|
|
314
|
-
// return this.entities;
|
|
315
|
-
// }
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
* Get the result of this selection.
|
|
319
|
-
*/
|
|
320
|
-
get entities (): T[] {
|
|
321
|
-
if (!this._lastResult) {
|
|
322
|
-
this.refresh();
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const [entities] = this._lastResult!;
|
|
326
|
-
return entities;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Returns the selection or reducer result.
|
|
331
|
-
*/
|
|
332
|
-
get value (): R extends void ? T[] : R {
|
|
333
|
-
if (!this._lastResult) {
|
|
334
|
-
this.refresh();
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const [entities, value] = this._lastResult!;
|
|
338
|
-
return (this._reducer ? value : entities) as any;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Return the first element if the set has exactly one element.
|
|
343
|
-
*/
|
|
344
|
-
expectOne (): T {
|
|
345
|
-
const entities = this.entities;
|
|
346
|
-
assert(entities.length === 1, `Expected one result; got ${entities.length}`);
|
|
347
|
-
return entities[0];
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
//
|
|
352
|
-
// Utils
|
|
353
|
-
//
|
|
354
|
-
|
|
355
|
-
const dedupe = <T>(values: T[]) => Array.from(new Set(values));
|
|
356
|
-
|
|
357
|
-
const coerceToId = (item: Item | ItemID): ItemID => {
|
|
358
|
-
if (typeof item === 'string') {
|
|
359
|
-
return item;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return item.id;
|
|
363
|
-
};
|
|
364
|
-
|
|
365
|
-
const testOneOrMultiple = <T>(expected: OneOrMultiple<T>, value: T) => {
|
|
366
|
-
if (Array.isArray(expected)) {
|
|
367
|
-
return expected.includes(value);
|
|
368
|
-
} else {
|
|
369
|
-
return expected === value;
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
|
|
373
|
-
const filterToPredicate = (filter: ItemFilter | ItemIdFilter | Predicate<any>): Predicate<any> => {
|
|
374
|
-
if (typeof filter === 'function') {
|
|
375
|
-
return filter;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
return itemFilterToPredicate(filter);
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
const itemFilterToPredicate = (filter: ItemFilter | ItemIdFilter): Predicate<Item> => {
|
|
382
|
-
if ('id' in filter) {
|
|
383
|
-
return item => item.id === filter.id;
|
|
384
|
-
} else {
|
|
385
|
-
return item =>
|
|
386
|
-
(!filter.type || testOneOrMultiple(filter.type, item.type)) &&
|
|
387
|
-
(!filter.parent || item.parent?.id === coerceToId(filter.parent));
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
|
|
391
|
-
const linkFilterToPredicate = (filter: LinkFilter): Predicate<Link> =>
|
|
392
|
-
link => (!filter.type || testOneOrMultiple(filter.type, link.type));
|
|
393
|
-
|
|
394
|
-
const createQueryOptionsFilter = ({ deleted = ItemFilterDeleted.HIDE_DELETED }: QueryOptions): Predicate<Entity> => {
|
|
395
|
-
return entity => {
|
|
396
|
-
if (entity.model === null) {
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
switch (deleted) {
|
|
401
|
-
case ItemFilterDeleted.HIDE_DELETED:
|
|
402
|
-
return !(entity instanceof Item) || !entity.deleted;
|
|
403
|
-
case ItemFilterDeleted.SHOW_DELETED:
|
|
404
|
-
return true;
|
|
405
|
-
case ItemFilterDeleted.SHOW_DELETED_ONLY:
|
|
406
|
-
return entity instanceof Item && entity.deleted;
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2020 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { ItemID } from '@dxos/echo-protocol';
|
|
6
|
+
|
|
7
|
+
import { Item } from '../item';
|
|
8
|
+
|
|
9
|
+
export type OneOrMultiple<T> = T | T[]
|
|
10
|
+
|
|
11
|
+
export const dedupe = <T>(values: T[]) => Array.from(new Set(values));
|
|
12
|
+
|
|
13
|
+
export const coerceToId = (item: Item | ItemID): ItemID => {
|
|
14
|
+
if (typeof item === 'string') {
|
|
15
|
+
return item;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return item.id;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const testOneOrMultiple = <T>(expected: OneOrMultiple<T>, value: T) => {
|
|
22
|
+
if (Array.isArray(expected)) {
|
|
23
|
+
return expected.includes(value);
|
|
24
|
+
} else {
|
|
25
|
+
return expected === value;
|
|
26
|
+
}
|
|
27
|
+
};
|