@dxos/echo-db 2.28.3-dev.e4579238 → 2.28.4-dev.424a2abb
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/{database → api}/database.d.ts +2 -4
- package/dist/src/api/database.d.ts.map +1 -0
- package/dist/src/{database → api}/database.js +6 -9
- package/dist/src/api/database.js.map +1 -0
- package/dist/src/{database → api}/database.test.d.ts +0 -0
- package/dist/src/api/database.test.d.ts.map +1 -0
- package/dist/src/{database → api}/database.test.js +17 -19
- package/dist/src/api/database.test.js.map +1 -0
- package/dist/src/{database → api}/entity.d.ts +1 -1
- package/dist/src/api/entity.d.ts.map +1 -0
- package/dist/src/{database → api}/entity.js +0 -0
- package/dist/src/api/entity.js.map +1 -0
- package/dist/src/api/index.d.ts +7 -0
- package/dist/src/api/index.d.ts.map +1 -0
- package/dist/src/api/index.js +22 -0
- package/dist/src/api/index.js.map +1 -0
- package/dist/src/{database → api}/item.d.ts +1 -1
- package/dist/src/api/item.d.ts.map +1 -0
- package/dist/src/{database → api}/item.js +0 -0
- package/dist/src/api/item.js.map +1 -0
- package/dist/src/{database → api}/link.d.ts +1 -1
- package/dist/src/api/link.d.ts.map +1 -0
- package/dist/src/{database → api}/link.js +0 -0
- package/dist/src/api/link.js.map +1 -0
- package/dist/src/{result.d.ts → api/result-set.d.ts} +1 -1
- package/dist/src/api/result-set.d.ts.map +1 -0
- package/dist/src/{result.js → api/result-set.js} +1 -1
- package/dist/src/api/result-set.js.map +1 -0
- package/dist/src/{database → api}/selection.d.ts +25 -11
- package/dist/src/api/selection.d.ts.map +1 -0
- package/dist/src/{database → api}/selection.js +64 -38
- package/dist/src/api/selection.js.map +1 -0
- package/dist/src/{database → api}/selection.test.d.ts +0 -0
- package/dist/src/api/selection.test.d.ts.map +1 -0
- package/dist/src/{database → api}/selection.test.js +18 -19
- package/dist/src/api/selection.test.js.map +1 -0
- package/dist/src/database/data-mirror.test.js.map +1 -1
- package/dist/src/database/data-service-host.d.ts.map +1 -1
- package/dist/src/database/data-service-host.js +3 -4
- package/dist/src/database/data-service-host.js.map +1 -1
- package/dist/src/database/index.d.ts +4 -6
- package/dist/src/database/index.d.ts.map +1 -1
- package/dist/src/database/index.js +4 -6
- package/dist/src/database/index.js.map +1 -1
- package/dist/src/database/item-demuxer.d.ts +1 -1
- package/dist/src/database/item-demuxer.d.ts.map +1 -1
- package/dist/src/database/item-demuxer.js +3 -3
- package/dist/src/database/item-demuxer.js.map +1 -1
- package/dist/src/database/item-demuxer.test.js +2 -2
- package/dist/src/database/item-demuxer.test.js.map +1 -1
- package/dist/src/database/item-manager.d.ts +1 -3
- package/dist/src/database/item-manager.d.ts.map +1 -1
- package/dist/src/database/item-manager.js +10 -11
- package/dist/src/database/item-manager.js.map +1 -1
- package/dist/src/database/testing.d.ts +1 -1
- package/dist/src/database/testing.d.ts.map +1 -1
- package/dist/src/database/testing.js +3 -3
- package/dist/src/database/testing.js.map +1 -1
- package/dist/src/echo.d.ts +1 -1
- package/dist/src/echo.d.ts.map +1 -1
- package/dist/src/echo.js +4 -4
- package/dist/src/echo.js.map +1 -1
- package/dist/src/echo.test.js +25 -28
- package/dist/src/echo.test.js.map +1 -1
- package/dist/src/halo/contact-manager.d.ts +1 -2
- package/dist/src/halo/contact-manager.d.ts.map +1 -1
- package/dist/src/halo/contact-manager.js +6 -6
- package/dist/src/halo/contact-manager.js.map +1 -1
- package/dist/src/halo/halo.d.ts +1 -1
- package/dist/src/halo/halo.d.ts.map +1 -1
- package/dist/src/halo/preferences.d.ts +1 -1
- package/dist/src/halo/preferences.d.ts.map +1 -1
- package/dist/src/halo/preferences.js +9 -9
- package/dist/src/halo/preferences.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/parties/party-core.d.ts +1 -1
- package/dist/src/parties/party-core.d.ts.map +1 -1
- package/dist/src/parties/party-core.js +2 -1
- package/dist/src/parties/party-core.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-internal.d.ts +2 -3
- package/dist/src/parties/party-internal.d.ts.map +1 -1
- package/dist/src/parties/party-internal.js +4 -5
- package/dist/src/parties/party-internal.js.map +1 -1
- package/dist/src/parties/party-manager.test.js +19 -18
- package/dist/src/parties/party-manager.test.js.map +1 -1
- package/dist/src/snapshots/snapshot.test.js +3 -3
- package/dist/src/snapshots/snapshot.test.js.map +1 -1
- package/dist/src/testing/index.d.ts +3 -0
- package/dist/src/testing/index.d.ts.map +1 -0
- package/dist/src/testing/index.js +18 -0
- package/dist/src/testing/index.js.map +1 -0
- package/dist/src/{util → testing}/testing-factories.d.ts +1 -1
- package/dist/src/testing/testing-factories.d.ts.map +1 -0
- package/dist/src/{util → testing}/testing-factories.js +0 -0
- package/dist/src/testing/testing-factories.js.map +1 -0
- package/dist/src/{util → testing}/testing.d.ts +1 -1
- package/dist/src/testing/testing.d.ts.map +1 -0
- package/dist/src/{util → testing}/testing.js +2 -2
- package/dist/src/testing/testing.js.map +1 -0
- package/dist/src/util/index.d.ts +0 -2
- package/dist/src/util/index.d.ts.map +1 -1
- package/dist/src/util/index.js +0 -2
- package/dist/src/util/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -22
- package/src/{database → api}/database.test.ts +15 -18
- package/src/{database → api}/database.ts +7 -14
- package/src/{database → api}/entity.ts +1 -1
- package/src/api/index.ts +10 -0
- package/src/{database → api}/item.ts +1 -1
- package/src/{database → api}/link.ts +1 -1
- package/src/{result.ts → api/result-set.ts} +0 -0
- package/src/{database → api}/selection.test.ts +24 -23
- package/src/{database → api}/selection.ts +72 -43
- package/src/database/data-mirror.test.ts +1 -1
- package/src/database/data-mirror.ts +1 -1
- package/src/database/data-service-host.ts +1 -2
- package/src/database/index.ts +4 -6
- package/src/database/item-demuxer.test.ts +1 -1
- package/src/database/item-demuxer.ts +3 -6
- package/src/database/item-manager.ts +1 -3
- package/src/database/testing.ts +1 -1
- package/src/echo.test.ts +28 -29
- package/src/echo.ts +2 -2
- package/src/halo/contact-manager.ts +5 -6
- package/src/halo/halo.ts +1 -1
- package/src/halo/preferences.ts +12 -11
- package/src/index.ts +2 -1
- package/src/parties/party-core.test.ts +3 -3
- package/src/parties/party-core.ts +2 -1
- package/src/parties/party-internal.ts +3 -4
- package/src/parties/party-manager.test.ts +19 -18
- package/src/snapshots/snapshot.test.ts +1 -1
- package/src/testing/index.ts +6 -0
- package/src/{util → testing}/testing-factories.ts +6 -2
- package/src/{util → testing}/testing.ts +2 -2
- package/src/util/index.ts +0 -2
- package/dist/src/conflict.test.d.ts +0 -2
- package/dist/src/conflict.test.d.ts.map +0 -1
- package/dist/src/conflict.test.js +0 -33
- package/dist/src/conflict.test.js.map +0 -1
- package/dist/src/database/database.d.ts.map +0 -1
- package/dist/src/database/database.js.map +0 -1
- package/dist/src/database/database.test.d.ts.map +0 -1
- package/dist/src/database/database.test.js.map +0 -1
- package/dist/src/database/entity.d.ts.map +0 -1
- package/dist/src/database/entity.js.map +0 -1
- package/dist/src/database/item.d.ts.map +0 -1
- package/dist/src/database/item.js.map +0 -1
- package/dist/src/database/link.d.ts.map +0 -1
- package/dist/src/database/link.js.map +0 -1
- package/dist/src/database/selection.d.ts.map +0 -1
- package/dist/src/database/selection.js.map +0 -1
- package/dist/src/database/selection.test.d.ts.map +0 -1
- package/dist/src/database/selection.test.js.map +0 -1
- package/dist/src/result.d.ts.map +0 -1
- package/dist/src/result.js.map +0 -1
- package/dist/src/util/testing-factories.d.ts.map +0 -1
- package/dist/src/util/testing-factories.js.map +0 -1
- package/dist/src/util/testing.d.ts.map +0 -1
- package/dist/src/util/testing.js.map +0 -1
- package/src/conflict.test.ts +0 -37
|
@@ -10,14 +10,11 @@ import { ItemID, ItemType } from '@dxos/echo-protocol';
|
|
|
10
10
|
import { Model, ModelConstructor, ModelFactory, validateModelClass } from '@dxos/model-factory';
|
|
11
11
|
import { ObjectModel } from '@dxos/object-model';
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import { DataServiceHost } from './data-service-host';
|
|
15
|
-
import { DatabaseBackend } from './database-backend';
|
|
13
|
+
import { DatabaseBackend, DataServiceHost, ItemManager } from '../database';
|
|
16
14
|
import { Entity } from './entity';
|
|
17
15
|
import { Item } from './item';
|
|
18
|
-
import { ItemManager } from './item-manager';
|
|
19
16
|
import { Link } from './link';
|
|
20
|
-
import { createSelector, RootFilter } from './selection';
|
|
17
|
+
import { Selection, createSelector, RootFilter } from './selection';
|
|
21
18
|
|
|
22
19
|
export interface ItemCreationOptions<M extends Model> {
|
|
23
20
|
model: ModelConstructor<M>
|
|
@@ -161,14 +158,10 @@ export class Database {
|
|
|
161
158
|
* Waits for item matching the filter to be present and returns it.
|
|
162
159
|
*/
|
|
163
160
|
async waitForItem<T extends Model<any>> (filter: RootFilter): Promise<Item<T>> {
|
|
164
|
-
const
|
|
165
|
-
await
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
const item = Array.isArray(query.result) ? query.result[0] : query.result;
|
|
171
|
-
assert(item, 'Race condition detected');
|
|
161
|
+
const result = this.select(filter).query();
|
|
162
|
+
await result.update.waitForCondition(() => result.entities.length > 0);
|
|
163
|
+
const item = result.expectOne();
|
|
164
|
+
assert(item, 'Possible condition detected.');
|
|
172
165
|
return item as Item<T>;
|
|
173
166
|
}
|
|
174
167
|
|
|
@@ -192,7 +185,7 @@ export class Database {
|
|
|
192
185
|
* @param filter
|
|
193
186
|
*/
|
|
194
187
|
reduce<R> (result: R, filter?: RootFilter): Selection<Item<any>, R> {
|
|
195
|
-
return createSelector(
|
|
188
|
+
return createSelector<R>(
|
|
196
189
|
() => this._itemManager.items,
|
|
197
190
|
() => this._itemManager.debouncedUpdate,
|
|
198
191
|
this,
|
|
@@ -7,7 +7,7 @@ import { ItemID, ItemType } from '@dxos/echo-protocol';
|
|
|
7
7
|
import { Model, ModelMeta, StateManager } from '@dxos/model-factory';
|
|
8
8
|
import { SubscriptionGroup } from '@dxos/util';
|
|
9
9
|
|
|
10
|
-
import { ItemManager } from '
|
|
10
|
+
import { ItemManager } from '../database';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Base class for all ECHO entitities.
|
package/src/api/index.ts
ADDED
|
@@ -7,8 +7,8 @@ import debug from 'debug';
|
|
|
7
7
|
import { EchoEnvelope, ItemID, ItemMutation, ItemType, FeedWriter } from '@dxos/echo-protocol';
|
|
8
8
|
import { Model, StateManager } from '@dxos/model-factory';
|
|
9
9
|
|
|
10
|
+
import { ItemManager } from '../database';
|
|
10
11
|
import { Entity } from './entity';
|
|
11
|
-
import { ItemManager } from './item-manager';
|
|
12
12
|
import type { Link } from './link';
|
|
13
13
|
import { Selection, createItemSelector } from './selection';
|
|
14
14
|
|
|
@@ -7,9 +7,9 @@ import assert from 'assert';
|
|
|
7
7
|
import { ItemID, ItemType } from '@dxos/echo-protocol';
|
|
8
8
|
import { Model, StateManager } from '@dxos/model-factory';
|
|
9
9
|
|
|
10
|
+
import { ItemManager } from '../database';
|
|
10
11
|
import { Entity } from './entity';
|
|
11
12
|
import { Item } from './item';
|
|
12
|
-
import { ItemManager } from './item-manager';
|
|
13
13
|
|
|
14
14
|
export interface LinkData {
|
|
15
15
|
sourceId: ItemID
|
|
File without changes
|
|
@@ -11,10 +11,10 @@ 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
|
|
14
|
+
import { Entity } from './entity';
|
|
15
15
|
import { Item } from './item';
|
|
16
16
|
import { Link } from './link';
|
|
17
|
-
import { createSelector } from './selection';
|
|
17
|
+
import { RootFilter, createSelector } from './selection';
|
|
18
18
|
|
|
19
19
|
// Use to prevent ultra-long diffs.
|
|
20
20
|
const ids = (entities: Entity[]) => entities.map(entity => entity.id);
|
|
@@ -40,9 +40,11 @@ const createLink = (id: ItemID, type: ItemType, source: Item<any>, target: Item<
|
|
|
40
40
|
return link;
|
|
41
41
|
};
|
|
42
42
|
|
|
43
|
-
const createRootSelector = (filter?: RootFilter) =>
|
|
43
|
+
const createRootSelector = (filter?: RootFilter) =>
|
|
44
|
+
createSelector<void>(() => items, () => new Event(), null as any, filter, undefined);
|
|
44
45
|
|
|
45
|
-
const createReducer = <R>(result: R) =>
|
|
46
|
+
const createReducer = <R>(result: R) =>
|
|
47
|
+
createSelector<R>(() => items, () => new Event(), null as any, undefined, result);
|
|
46
48
|
|
|
47
49
|
// TODO(burdon): Use more complex data set (org, person, project, task).
|
|
48
50
|
|
|
@@ -89,33 +91,33 @@ describe('Selection', () => {
|
|
|
89
91
|
test('all', () => {
|
|
90
92
|
expect(
|
|
91
93
|
createRootSelector()
|
|
92
|
-
.query().
|
|
94
|
+
.query().entities
|
|
93
95
|
).toHaveLength(items.length);
|
|
94
96
|
});
|
|
95
97
|
|
|
96
98
|
test('by id', () => {
|
|
97
99
|
expect(
|
|
98
100
|
createRootSelector({ id: org1.id })
|
|
99
|
-
.query().
|
|
101
|
+
.query().entities
|
|
100
102
|
).toEqual([org1]);
|
|
101
103
|
|
|
102
104
|
expect(
|
|
103
105
|
createRootSelector({ id: org2.id })
|
|
104
|
-
.query().
|
|
106
|
+
.query().entities
|
|
105
107
|
).toEqual([org2]);
|
|
106
108
|
});
|
|
107
109
|
|
|
108
110
|
test('single type', () => {
|
|
109
111
|
expect(
|
|
110
112
|
createRootSelector({ type: ITEM_PROJECT })
|
|
111
|
-
.query().
|
|
113
|
+
.query().entities
|
|
112
114
|
).toHaveLength(3);
|
|
113
115
|
});
|
|
114
116
|
|
|
115
117
|
test('multiple types', () => {
|
|
116
118
|
expect(
|
|
117
119
|
createRootSelector({ type: [ITEM_ORG, ITEM_PROJECT] })
|
|
118
|
-
.query().
|
|
120
|
+
.query().entities
|
|
119
121
|
).toHaveLength(5);
|
|
120
122
|
});
|
|
121
123
|
});
|
|
@@ -125,7 +127,7 @@ describe('Selection', () => {
|
|
|
125
127
|
expect(
|
|
126
128
|
createRootSelector()
|
|
127
129
|
.filter({ type: 'dxos:type.invalid' })
|
|
128
|
-
.query().
|
|
130
|
+
.query().entities
|
|
129
131
|
).toHaveLength(0);
|
|
130
132
|
});
|
|
131
133
|
|
|
@@ -133,7 +135,7 @@ describe('Selection', () => {
|
|
|
133
135
|
expect(
|
|
134
136
|
createRootSelector()
|
|
135
137
|
.filter({ type: ITEM_PROJECT })
|
|
136
|
-
.query().
|
|
138
|
+
.query().entities
|
|
137
139
|
).toHaveLength(3);
|
|
138
140
|
});
|
|
139
141
|
|
|
@@ -141,7 +143,7 @@ describe('Selection', () => {
|
|
|
141
143
|
expect(
|
|
142
144
|
createRootSelector()
|
|
143
145
|
.filter({ type: [ITEM_ORG, ITEM_PROJECT] })
|
|
144
|
-
.query().
|
|
146
|
+
.query().entities
|
|
145
147
|
).toHaveLength(5);
|
|
146
148
|
});
|
|
147
149
|
|
|
@@ -149,7 +151,7 @@ describe('Selection', () => {
|
|
|
149
151
|
expect(
|
|
150
152
|
createRootSelector()
|
|
151
153
|
.filter(item => item.type === ITEM_ORG)
|
|
152
|
-
.query().
|
|
154
|
+
.query().entities
|
|
153
155
|
).toHaveLength(2);
|
|
154
156
|
});
|
|
155
157
|
});
|
|
@@ -160,7 +162,7 @@ describe('Selection', () => {
|
|
|
160
162
|
createRootSelector()
|
|
161
163
|
.filter({ type: ITEM_ORG })
|
|
162
164
|
.children({ type: ITEM_PROJECT })
|
|
163
|
-
.query().
|
|
165
|
+
.query().entities
|
|
164
166
|
)).toStrictEqual(ids([
|
|
165
167
|
project1,
|
|
166
168
|
project2,
|
|
@@ -172,7 +174,7 @@ describe('Selection', () => {
|
|
|
172
174
|
expect(ids(
|
|
173
175
|
createRootSelector({ id: org1.id })
|
|
174
176
|
.children()
|
|
175
|
-
.query().
|
|
177
|
+
.query().entities
|
|
176
178
|
)).toStrictEqual(ids([
|
|
177
179
|
project1,
|
|
178
180
|
project2,
|
|
@@ -188,7 +190,7 @@ describe('Selection', () => {
|
|
|
188
190
|
createRootSelector()
|
|
189
191
|
.filter({ type: ITEM_PROJECT })
|
|
190
192
|
.parent()
|
|
191
|
-
.query().
|
|
193
|
+
.query().entities
|
|
192
194
|
)).toStrictEqual(ids([
|
|
193
195
|
org1,
|
|
194
196
|
org2
|
|
@@ -199,7 +201,7 @@ describe('Selection', () => {
|
|
|
199
201
|
expect(ids(
|
|
200
202
|
createRootSelector({ id: project1.id })
|
|
201
203
|
.parent()
|
|
202
|
-
.query().
|
|
204
|
+
.query().entities
|
|
203
205
|
)).toStrictEqual(ids([
|
|
204
206
|
org1
|
|
205
207
|
]));
|
|
@@ -209,7 +211,7 @@ describe('Selection', () => {
|
|
|
209
211
|
expect(
|
|
210
212
|
createRootSelector({ id: org1.id })
|
|
211
213
|
.parent()
|
|
212
|
-
.query().
|
|
214
|
+
.query().entities
|
|
213
215
|
).toEqual([]);
|
|
214
216
|
});
|
|
215
217
|
});
|
|
@@ -220,7 +222,7 @@ describe('Selection', () => {
|
|
|
220
222
|
createRootSelector({ id: project1.id })
|
|
221
223
|
.links()
|
|
222
224
|
.target()
|
|
223
|
-
.query().
|
|
225
|
+
.query().entities
|
|
224
226
|
)).toStrictEqual(ids([
|
|
225
227
|
person1,
|
|
226
228
|
person2
|
|
@@ -231,7 +233,7 @@ describe('Selection', () => {
|
|
|
231
233
|
expect(
|
|
232
234
|
createRootSelector({ type: ITEM_PROJECT })
|
|
233
235
|
.links()
|
|
234
|
-
.query().
|
|
236
|
+
.query().entities
|
|
235
237
|
).toHaveLength(links.length);
|
|
236
238
|
});
|
|
237
239
|
|
|
@@ -240,7 +242,7 @@ describe('Selection', () => {
|
|
|
240
242
|
createRootSelector({ type: ITEM_PERSON })
|
|
241
243
|
.refs()
|
|
242
244
|
.source()
|
|
243
|
-
.query().
|
|
245
|
+
.query().entities
|
|
244
246
|
)).toStrictEqual(ids([
|
|
245
247
|
project1,
|
|
246
248
|
project2
|
|
@@ -270,9 +272,8 @@ describe('Selection', () => {
|
|
|
270
272
|
return { ...rest, numLinks: numLinks + links.length, stage: 'c' };
|
|
271
273
|
})
|
|
272
274
|
.target()
|
|
273
|
-
.query();
|
|
275
|
+
.query();
|
|
274
276
|
|
|
275
|
-
expect(ids(query.result)).toStrictEqual(ids([person1, person2, person3]));
|
|
276
277
|
expect(query.value).toEqual({ numItems: 5, numLinks: 4, stage: 'c' });
|
|
277
278
|
});
|
|
278
279
|
});
|
|
@@ -70,7 +70,7 @@ export type SelectionRoot = Database | Entity;
|
|
|
70
70
|
/**
|
|
71
71
|
* Returned from each stage of the visitor.
|
|
72
72
|
*/
|
|
73
|
-
export type SelectionContext<T extends Entity, R> = [entities: T[], result
|
|
73
|
+
export type SelectionContext<T extends Entity, R> = [entities: T[], result?: R]
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Visitor callback.
|
|
@@ -79,19 +79,18 @@ export type SelectionContext<T extends Entity, R> = [entities: T[], result: R]
|
|
|
79
79
|
*/
|
|
80
80
|
export type Callable<T extends Entity, R> = (entities: T[], result: R) => R
|
|
81
81
|
|
|
82
|
-
const dedupe = <T>(values: T[]) => Array.from(new Set(values));
|
|
83
|
-
|
|
84
82
|
/**
|
|
85
83
|
* Factory for selector that provides a root set of items.
|
|
86
84
|
* @param itemsProvider
|
|
87
85
|
* @param updateEventProvider
|
|
88
86
|
* @param root
|
|
87
|
+
* @param filter
|
|
89
88
|
* @param value Initial reducer value.
|
|
90
89
|
*/
|
|
91
90
|
export const createSelector = <R>(
|
|
92
91
|
// Provider is called each time the query is executed.
|
|
93
92
|
itemsProvider: () => Item[],
|
|
94
|
-
// TODO(burdon):
|
|
93
|
+
// TODO(burdon): Replace with direct event handler.
|
|
95
94
|
updateEventProvider: () => Event<Entity[]>,
|
|
96
95
|
root: SelectionRoot,
|
|
97
96
|
filter: RootFilter | undefined,
|
|
@@ -106,7 +105,7 @@ export const createSelector = <R>(
|
|
|
106
105
|
return [items, value];
|
|
107
106
|
};
|
|
108
107
|
|
|
109
|
-
return new Selection(visitor, updateEventProvider(), root);
|
|
108
|
+
return new Selection(visitor, updateEventProvider(), root, value !== undefined);
|
|
110
109
|
};
|
|
111
110
|
|
|
112
111
|
/**
|
|
@@ -119,7 +118,7 @@ export const createItemSelector = <R>(
|
|
|
119
118
|
root: Item<any>,
|
|
120
119
|
update: Event<Entity[]>,
|
|
121
120
|
value: R
|
|
122
|
-
): Selection<Item<any>, R> => new Selection(() => [[root], value], update, root);
|
|
121
|
+
): Selection<Item<any>, R> => new Selection(() => [[root], value], update, root, value !== undefined);
|
|
123
122
|
|
|
124
123
|
/**
|
|
125
124
|
* Selections are used to construct database subscriptions.
|
|
@@ -135,11 +134,13 @@ export class Selection<T extends Entity<any>, R = void> {
|
|
|
135
134
|
* @param _visitor Executes the query.
|
|
136
135
|
* @param _update The unfiltered update event.
|
|
137
136
|
* @param _root The root of the selection. Must be a stable reference.
|
|
137
|
+
* @param _reducer
|
|
138
138
|
*/
|
|
139
139
|
constructor (
|
|
140
140
|
private readonly _visitor: (options: QueryOptions) => SelectionContext<T, R>,
|
|
141
141
|
private readonly _update: Event<Entity[]>,
|
|
142
|
-
private readonly _root: SelectionRoot
|
|
142
|
+
private readonly _root: SelectionRoot,
|
|
143
|
+
private readonly _reducer = false
|
|
143
144
|
) {}
|
|
144
145
|
|
|
145
146
|
/**
|
|
@@ -148,14 +149,15 @@ export class Selection<T extends Entity<any>, R = void> {
|
|
|
148
149
|
private _createSubSelection<U extends Entity> (
|
|
149
150
|
map: (context: SelectionContext<T, R>, options: QueryOptions) => SelectionContext<U, R>
|
|
150
151
|
): Selection<U, R> {
|
|
151
|
-
return new Selection(options => map(this._visitor(options), options), this._update, this._root);
|
|
152
|
+
return new Selection(options => map(this._visitor(options), options), this._update, this._root, this._reducer);
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
/**
|
|
155
156
|
* Finish the selection and return the result.
|
|
156
157
|
*/
|
|
158
|
+
// TODO(burdon): Rename exec.
|
|
157
159
|
query (options: QueryOptions = {}): SelectionResult<T, R> {
|
|
158
|
-
return new SelectionResult<T, R>(() => this._visitor(options), this._update, this._root);
|
|
160
|
+
return new SelectionResult<T, R>(() => this._visitor(options), this._update, this._root, this._reducer);
|
|
159
161
|
}
|
|
160
162
|
|
|
161
163
|
/**
|
|
@@ -262,69 +264,96 @@ export class SelectionResult<T extends Entity, R = any> {
|
|
|
262
264
|
* Fired when there are updates in the selection.
|
|
263
265
|
* Only update that are relevant to the selection cause the update.
|
|
264
266
|
*/
|
|
265
|
-
readonly update = new Event<T
|
|
267
|
+
readonly update = new Event<SelectionResult<T>>(); // TODO(burdon): Result result object.
|
|
266
268
|
|
|
267
|
-
private _lastResult: SelectionContext<T, R
|
|
269
|
+
private _lastResult: SelectionContext<T, R> = [[]];
|
|
268
270
|
|
|
269
271
|
constructor (
|
|
270
272
|
private readonly _execute: () => SelectionContext<T, R>,
|
|
271
273
|
private readonly _update: Event<Entity[]>,
|
|
272
|
-
private readonly _root: SelectionRoot
|
|
274
|
+
private readonly _root: SelectionRoot,
|
|
275
|
+
private readonly _reducer: boolean
|
|
273
276
|
) {
|
|
274
|
-
this.
|
|
275
|
-
|
|
276
|
-
// TODO(burdon): Query updates are based on
|
|
277
|
-
|
|
278
|
-
// TODO(burdon): Every update should update reducer.
|
|
277
|
+
this.refresh();
|
|
279
278
|
|
|
280
279
|
// Re-run if deps change.
|
|
281
|
-
// TODO(burdon): Explain this.
|
|
282
|
-
// TODO(burdon): Should also fire if entities have been REMOVED from the set?
|
|
283
280
|
this.update.addEffect(() => _update.on(currentEntities => {
|
|
284
|
-
const
|
|
285
|
-
const [entities] = result;
|
|
286
|
-
const set = new Set([...entities, ...this._lastResult[0]]);
|
|
287
|
-
this._lastResult = result;
|
|
281
|
+
const [previousEntities] = this._lastResult;
|
|
288
282
|
|
|
289
|
-
|
|
290
|
-
|
|
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);
|
|
291
289
|
}
|
|
292
290
|
}));
|
|
293
291
|
}
|
|
294
292
|
|
|
295
293
|
/**
|
|
296
|
-
*
|
|
294
|
+
* Re-run query.
|
|
297
295
|
*/
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return dedupe(entities);
|
|
296
|
+
refresh () {
|
|
297
|
+
const [entities, result] = this._execute();
|
|
298
|
+
this._lastResult = [dedupe(entities), result];
|
|
299
|
+
return this;
|
|
303
300
|
}
|
|
304
301
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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;
|
|
309
307
|
}
|
|
310
308
|
|
|
311
309
|
/**
|
|
312
|
-
*
|
|
310
|
+
* @deprecated
|
|
313
311
|
*/
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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;
|
|
318
327
|
}
|
|
319
328
|
|
|
320
329
|
/**
|
|
321
|
-
*
|
|
330
|
+
* Returns the selection or reducer result.
|
|
322
331
|
*/
|
|
323
|
-
get
|
|
324
|
-
|
|
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];
|
|
325
348
|
}
|
|
326
349
|
}
|
|
327
350
|
|
|
351
|
+
//
|
|
352
|
+
// Utils
|
|
353
|
+
//
|
|
354
|
+
|
|
355
|
+
const dedupe = <T>(values: T[]) => Array.from(new Set(values));
|
|
356
|
+
|
|
328
357
|
const coerceToId = (item: Item | ItemID): ItemID => {
|
|
329
358
|
if (typeof item === 'string') {
|
|
330
359
|
return item;
|
|
@@ -11,10 +11,10 @@ import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
|
|
|
11
11
|
import { ModelFactory } from '@dxos/model-factory';
|
|
12
12
|
import { ObjectModel } from '@dxos/object-model';
|
|
13
13
|
|
|
14
|
+
import { Item } from '../api';
|
|
14
15
|
import { DataMirror } from './data-mirror';
|
|
15
16
|
import { DataServiceHost } from './data-service-host';
|
|
16
17
|
import { DataServiceRouter } from './data-service-router';
|
|
17
|
-
import { Item } from './item';
|
|
18
18
|
import { ItemDemuxer } from './item-demuxer';
|
|
19
19
|
import { ItemManager } from './item-manager';
|
|
20
20
|
|
|
@@ -10,7 +10,7 @@ import { failUndefined } from '@dxos/debug';
|
|
|
10
10
|
import { DataService } from '@dxos/echo-protocol';
|
|
11
11
|
import { Model } from '@dxos/model-factory';
|
|
12
12
|
|
|
13
|
-
import { Entity } from '
|
|
13
|
+
import { Entity } from '../api';
|
|
14
14
|
import { ItemManager } from './item-manager';
|
|
15
15
|
|
|
16
16
|
const log = debug('dxos:echo-db:data-mirror');
|
|
@@ -18,11 +18,10 @@ import {
|
|
|
18
18
|
SubscribeEntityStreamResponse
|
|
19
19
|
} from '@dxos/echo-protocol';
|
|
20
20
|
|
|
21
|
+
import { Item, Link } from '../api';
|
|
21
22
|
import { EntityNotFoundError } from '../errors';
|
|
22
|
-
import { Item } from './item';
|
|
23
23
|
import { ItemDemuxer } from './item-demuxer';
|
|
24
24
|
import { ItemManager } from './item-manager';
|
|
25
|
-
import { Link } from './link';
|
|
26
25
|
|
|
27
26
|
const log = debug('dxos:echo-db:data-service-host');
|
|
28
27
|
|
package/src/database/index.ts
CHANGED
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
// Copyright 2020 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * from './
|
|
5
|
+
export * from './data-mirror';
|
|
6
|
+
export * from './data-service-host';
|
|
7
|
+
export * from './data-service-router';
|
|
6
8
|
export * from './database-backend';
|
|
7
|
-
export * from './entity';
|
|
8
|
-
export * from './item';
|
|
9
|
-
export * from './link';
|
|
10
9
|
export * from './item-demuxer';
|
|
11
10
|
export * from './item-manager';
|
|
12
|
-
export * from './timeframe-clock';
|
|
13
|
-
export * from './selection';
|
|
14
11
|
export * from './testing';
|
|
12
|
+
export * from './timeframe-clock';
|
|
@@ -14,7 +14,7 @@ import { createTransform } from '@dxos/feed-store';
|
|
|
14
14
|
import { ModelFactory, TestModel } from '@dxos/model-factory';
|
|
15
15
|
import { ObjectModel } from '@dxos/object-model';
|
|
16
16
|
|
|
17
|
-
import { Item } from '
|
|
17
|
+
import { Item } from '../api';
|
|
18
18
|
import { ItemDemuxer } from './item-demuxer';
|
|
19
19
|
import { ItemManager } from './item-manager';
|
|
20
20
|
|
|
@@ -7,15 +7,12 @@ import debug from 'debug';
|
|
|
7
7
|
|
|
8
8
|
import { Event } from '@dxos/async';
|
|
9
9
|
import { failUndefined } from '@dxos/debug';
|
|
10
|
-
import {
|
|
11
|
-
DatabaseSnapshot, IEchoStream, ItemID, ItemSnapshot
|
|
12
|
-
} from '@dxos/echo-protocol';
|
|
10
|
+
import { DatabaseSnapshot, IEchoStream, ItemID, ItemSnapshot } from '@dxos/echo-protocol';
|
|
13
11
|
import { createWritable } from '@dxos/feed-store';
|
|
14
12
|
import { Model, ModelFactory, ModelMessage } from '@dxos/model-factory';
|
|
15
13
|
import { jsonReplacer } from '@dxos/util';
|
|
16
14
|
|
|
17
|
-
import { Entity } from '
|
|
18
|
-
import { Item } from './item';
|
|
15
|
+
import { Entity, Item } from '../api';
|
|
19
16
|
import { ItemManager, ModelConstructionOptions } from './item-manager';
|
|
20
17
|
|
|
21
18
|
const log = debug('dxos:echo-db:item-demuxer');
|
|
@@ -93,7 +90,7 @@ export class ItemDemuxer {
|
|
|
93
90
|
const item = this._itemManager.getItem(itemId);
|
|
94
91
|
assert(item);
|
|
95
92
|
|
|
96
|
-
item._processMutation(itemMutation, itemId => this._itemManager.getItem(itemId));
|
|
93
|
+
item._processMutation(itemMutation, (itemId: ItemID) => this._itemManager.getItem(itemId));
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
//
|
|
@@ -11,10 +11,8 @@ import { timed } from '@dxos/debug';
|
|
|
11
11
|
import { EchoEnvelope, FeedWriter, ItemID, ItemType, mapFeedWriter, ModelSnapshot } from '@dxos/echo-protocol';
|
|
12
12
|
import { Model, ModelFactory, ModelMessage, ModelType, StateManager } from '@dxos/model-factory';
|
|
13
13
|
|
|
14
|
+
import { Entity, Item, Link } from '../api';
|
|
14
15
|
import { UnknownModelError } from '../errors';
|
|
15
|
-
import { Entity } from './entity';
|
|
16
|
-
import { Item } from './item';
|
|
17
|
-
import { Link } from './link';
|
|
18
16
|
|
|
19
17
|
const log = debug('dxos:echo-db:item-manager');
|
|
20
18
|
|
package/src/database/testing.ts
CHANGED
|
@@ -8,9 +8,9 @@ import { PublicKey } from '@dxos/crypto';
|
|
|
8
8
|
import { EchoEnvelope, MockFeedWriter, Timeframe } from '@dxos/echo-protocol';
|
|
9
9
|
import { ModelFactory } from '@dxos/model-factory';
|
|
10
10
|
|
|
11
|
+
import { Database } from '../api';
|
|
11
12
|
import { DataServiceHost } from './data-service-host';
|
|
12
13
|
import { DataServiceRouter } from './data-service-router';
|
|
13
|
-
import { Database } from './database';
|
|
14
14
|
import { FeedDatabaseBackend, RemoteDatabaseBackend } from './database-backend';
|
|
15
15
|
|
|
16
16
|
export const createInMemoryDatabase = async (modelFactory: ModelFactory) => {
|