@dxos/echo-db 2.29.2-dev.8c2ad8e5 → 2.29.2-dev.f6ed60b8

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.
Files changed (89) hide show
  1. package/dist/src/api/database.d.ts +11 -7
  2. package/dist/src/api/database.d.ts.map +1 -1
  3. package/dist/src/api/database.js +22 -24
  4. package/dist/src/api/database.js.map +1 -1
  5. package/dist/src/api/database.test.js +13 -13
  6. package/dist/src/api/database.test.js.map +1 -1
  7. package/dist/src/api/item.d.ts.map +1 -1
  8. package/dist/src/api/item.js +1 -1
  9. package/dist/src/api/item.js.map +1 -1
  10. package/dist/src/api/result-set.d.ts.map +1 -1
  11. package/dist/src/api/result-set.js +1 -0
  12. package/dist/src/api/result-set.js.map +1 -1
  13. package/dist/src/api/selection/index.d.ts +5 -0
  14. package/dist/src/api/selection/index.d.ts.map +1 -0
  15. package/dist/src/api/selection/index.js +20 -0
  16. package/dist/src/api/selection/index.js.map +1 -0
  17. package/dist/src/api/selection/queries.d.ts +51 -0
  18. package/dist/src/api/selection/queries.d.ts.map +1 -0
  19. package/dist/src/api/selection/queries.js +70 -0
  20. package/dist/src/api/selection/queries.js.map +1 -0
  21. package/dist/src/api/selection/result.d.ts +50 -0
  22. package/dist/src/api/selection/result.d.ts.map +1 -0
  23. package/dist/src/api/selection/result.js +91 -0
  24. package/dist/src/api/selection/result.js.map +1 -0
  25. package/dist/src/api/selection/selection.d.ts +96 -0
  26. package/dist/src/api/selection/selection.d.ts.map +1 -0
  27. package/dist/src/api/selection/selection.js +164 -0
  28. package/dist/src/api/selection/selection.js.map +1 -0
  29. package/dist/src/api/{selection.test.d.ts → selection/selection.test.d.ts} +0 -0
  30. package/dist/src/api/selection/selection.test.d.ts.map +1 -0
  31. package/dist/src/api/{selection.test.js → selection/selection.test.js} +46 -44
  32. package/dist/src/api/selection/selection.test.js.map +1 -0
  33. package/dist/src/api/selection/util.d.ts +7 -0
  34. package/dist/src/api/selection/util.d.ts.map +1 -0
  35. package/dist/src/api/selection/util.js +25 -0
  36. package/dist/src/api/selection/util.js.map +1 -0
  37. package/dist/src/echo.test.js +20 -20
  38. package/dist/src/echo.test.js.map +1 -1
  39. package/dist/src/halo/contact-manager.js +2 -2
  40. package/dist/src/halo/contact-manager.js.map +1 -1
  41. package/dist/src/halo/halo.d.ts +1 -1
  42. package/dist/src/halo/halo.d.ts.map +1 -1
  43. package/dist/src/halo/halo.js +2 -2
  44. package/dist/src/halo/halo.js.map +1 -1
  45. package/dist/src/halo/preferences.js +6 -6
  46. package/dist/src/halo/preferences.js.map +1 -1
  47. package/dist/src/parties/party-core.test.js +3 -3
  48. package/dist/src/parties/party-core.test.js.map +1 -1
  49. package/dist/src/parties/party-factory.d.ts.map +1 -1
  50. package/dist/src/parties/party-factory.js +3 -3
  51. package/dist/src/parties/party-factory.js.map +1 -1
  52. package/dist/src/parties/party-internal.js +3 -3
  53. package/dist/src/parties/party-internal.js.map +1 -1
  54. package/dist/src/parties/party-manager.js +1 -1
  55. package/dist/src/parties/party-manager.js.map +1 -1
  56. package/dist/src/parties/party-manager.test.js +8 -10
  57. package/dist/src/parties/party-manager.test.js.map +1 -1
  58. package/dist/src/testing/testing-factories.d.ts +2 -2
  59. package/dist/src/testing/testing-factories.d.ts.map +1 -1
  60. package/dist/src/testing/testing-factories.js +1 -1
  61. package/dist/src/testing/testing-factories.js.map +1 -1
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +17 -17
  64. package/src/api/database.test.ts +13 -13
  65. package/src/api/database.ts +30 -30
  66. package/src/api/item.ts +2 -2
  67. package/src/api/result-set.ts +1 -0
  68. package/src/api/selection/index.ts +8 -0
  69. package/src/api/selection/queries.ts +108 -0
  70. package/src/api/selection/result.ts +112 -0
  71. package/src/api/{selection.test.ts → selection/selection.test.ts} +50 -48
  72. package/src/api/{selection.ts → selection/selection.ts} +30 -231
  73. package/src/api/selection/util.ts +27 -0
  74. package/src/echo.test.ts +20 -20
  75. package/src/halo/contact-manager.ts +2 -2
  76. package/src/halo/halo.ts +2 -3
  77. package/src/halo/preferences.ts +6 -6
  78. package/src/parties/party-core.test.ts +3 -3
  79. package/src/parties/party-factory.ts +3 -4
  80. package/src/parties/party-internal.ts +3 -3
  81. package/src/parties/party-manager.test.ts +8 -10
  82. package/src/parties/party-manager.ts +1 -1
  83. package/src/testing/testing-factories.ts +3 -3
  84. package/dist/src/api/selection.d.ts +0 -183
  85. package/dist/src/api/selection.d.ts.map +0 -1
  86. package/dist/src/api/selection.js +0 -308
  87. package/dist/src/api/selection.js.map +0 -1
  88. package/dist/src/api/selection.test.d.ts.map +0 -1
  89. package/dist/src/api/selection.test.js.map +0 -1
@@ -14,16 +14,16 @@ import { DatabaseBackend, DataServiceHost, ItemManager } from '../database';
14
14
  import { Entity } from './entity';
15
15
  import { Item } from './item';
16
16
  import { Link } from './link';
17
- import { RootFilter, Selection, createSelector } from './selection';
17
+ import { RootFilter, Selection, createSelection } from './selection';
18
18
 
19
- export interface ItemCreationOptions<M extends Model> {
19
+ export interface CreateItemOption<M extends Model> {
20
20
  model?: ModelConstructor<M>
21
21
  type?: ItemType
22
22
  parent?: ItemID
23
23
  props?: any // TODO(marik-d): Type this better. Rename properties?
24
24
  }
25
25
 
26
- export interface LinkCreationOptions<M extends Model, L extends Model, R extends Model> {
26
+ export interface CreateLinkOptions<M extends Model, L extends Model, R extends Model> {
27
27
  model?: ModelConstructor<M>
28
28
  type?: ItemType
29
29
  source: Item<L>
@@ -31,20 +31,19 @@ export interface LinkCreationOptions<M extends Model, L extends Model, R extends
31
31
  props?: any // TODO(marik-d): Type this better.
32
32
  }
33
33
 
34
- enum State {
35
- INITIAL = 'INITIAL',
36
- OPEN = 'OPEN',
34
+ export enum State {
35
+ NULL = 'NULL',
36
+ INITIALIZED = 'INITIALIZED',
37
37
  DESTROYED = 'DESTROYED',
38
38
  }
39
39
 
40
40
  /**
41
- * Represents a shared dataset containing queryable Items that are constructed from an ordered stream
42
- * of mutations.
41
+ * Represents a shared dataset containing queryable Items that are constructed from an ordered stream of mutations.
43
42
  */
44
43
  export class Database {
45
44
  private readonly _itemManager: ItemManager;
46
45
 
47
- private _state = State.INITIAL;
46
+ private _state = State.NULL;
48
47
 
49
48
  /**
50
49
  * Creates a new database instance. `database.initialize()` must be called afterwards to complete the initialization.
@@ -57,6 +56,10 @@ export class Database {
57
56
  this._itemManager = new ItemManager(this._modelFactory, memberKey, this._backend.getWriteStream());
58
57
  }
59
58
 
59
+ get state () {
60
+ return this._state;
61
+ }
62
+
60
63
  get isReadOnly () {
61
64
  return this._backend.isReadOnly;
62
65
  }
@@ -71,26 +74,26 @@ export class Database {
71
74
 
72
75
  /**
73
76
  * Fired immediately after any update in the entities.
74
- *
75
77
  * If the information about which entity got updated is not required prefer using `update`.
76
78
  */
79
+ // TODO(burdon): Unused?
77
80
  get entityUpdate (): Event<Entity<any>> {
78
81
  return this._itemManager.update;
79
82
  }
80
83
 
81
84
  @synchronized
82
85
  async initialize () {
83
- if (this._state !== State.INITIAL) {
86
+ if (this._state !== State.NULL) {
84
87
  throw new Error('Invalid state: database was already initialized.');
85
88
  }
86
89
 
87
90
  await this._backend.open(this._itemManager, this._modelFactory);
88
- this._state = State.OPEN;
91
+ this._state = State.INITIALIZED;
89
92
  }
90
93
 
91
94
  @synchronized
92
95
  async destroy () {
93
- if (this._state === State.DESTROYED || this._state === State.INITIAL) {
96
+ if (this._state === State.DESTROYED || this._state === State.NULL) {
94
97
  return;
95
98
  }
96
99
 
@@ -101,7 +104,7 @@ export class Database {
101
104
  /**
102
105
  * Creates a new item with the given queryable type and model.
103
106
  */
104
- async createItem <M extends Model<any>> (options: ItemCreationOptions<M>): Promise<Item<M>> {
107
+ async createItem <M extends Model<any>> (options: CreateItemOption<M> = {}): Promise<Item<M>> {
105
108
  this._assertInitialized();
106
109
  if (!options.model) {
107
110
  options.model = ObjectModel as any as ModelConstructor<M>;
@@ -109,22 +112,21 @@ export class Database {
109
112
 
110
113
  validateModelClass(options.model);
111
114
 
112
- if (options.type && typeof options.type !== 'string') {
115
+ if (options.type && typeof options.type !== 'string' as ItemType) {
113
116
  throw new TypeError('Invalid type.');
114
117
  }
115
118
 
116
- if (options.parent && typeof options.parent !== 'string') {
119
+ if (options.parent && typeof options.parent !== 'string' as ItemID) {
117
120
  throw new TypeError('Optional parent item id must be a string id of an existing item.');
118
121
  }
119
122
 
120
123
  // TODO(burdon): Get modelType from somewhere other than `ObjectModel.meta.type`.
121
- const item = await this._itemManager.createItem(
124
+ return await this._itemManager.createItem(
122
125
  options.model.meta.type, options.type, options.parent, options.props) as any;
123
- return item;
124
126
  }
125
127
 
126
128
  async createLink<M extends Model<any>, S extends Model<any>, T extends Model<any>> (
127
- options: LinkCreationOptions<M, S, T>
129
+ options: CreateLinkOptions<M, S, T>
128
130
  ): Promise<Link<M, S, T>> {
129
131
  this._assertInitialized();
130
132
 
@@ -135,15 +137,12 @@ export class Database {
135
137
 
136
138
  validateModelClass(model);
137
139
 
138
- if (options.type && typeof options.type !== 'string') {
140
+ if (options.type && typeof options.type !== 'string' as ItemType) {
139
141
  throw new TypeError('Invalid type.');
140
142
  }
141
143
 
142
- assert(options.source instanceof Item);
143
- assert(options.target instanceof Item);
144
-
145
- return this._itemManager
146
- .createLink(model.meta.type, options.type, options.source.id, options.target.id, options.props);
144
+ return this._itemManager.createLink(
145
+ model.meta.type, options.type, options.source.id, options.target.id, options.props);
147
146
  }
148
147
 
149
148
  /**
@@ -158,11 +157,12 @@ export class Database {
158
157
  /**
159
158
  * Waits for item matching the filter to be present and returns it.
160
159
  */
160
+ // TODO(burdon): Generalize waitForCondition.
161
161
  async waitForItem<T extends Model<any>> (filter: RootFilter): Promise<Item<T>> {
162
- const result = this.select(filter).query();
162
+ const result = this.select(filter).exec();
163
163
  await result.update.waitForCondition(() => result.entities.length > 0);
164
164
  const item = result.expectOne();
165
- assert(item, 'Possible condition detected.');
165
+ assert(item, 'Possible race condition detected.');
166
166
  return item as Item<T>;
167
167
  }
168
168
 
@@ -171,7 +171,7 @@ export class Database {
171
171
  * @param filter
172
172
  */
173
173
  select (filter?: RootFilter): Selection<Item<any>> {
174
- return createSelector<void>(
174
+ return createSelection<void>(
175
175
  () => this._itemManager.items,
176
176
  () => this._itemManager.debouncedUpdate,
177
177
  this,
@@ -186,7 +186,7 @@ export class Database {
186
186
  * @param filter
187
187
  */
188
188
  reduce<R> (result: R, filter?: RootFilter): Selection<Item<any>, R> {
189
- return createSelector<R>(
189
+ return createSelection<R>(
190
190
  () => this._itemManager.items,
191
191
  () => this._itemManager.debouncedUpdate,
192
192
  this,
@@ -205,7 +205,7 @@ export class Database {
205
205
  }
206
206
 
207
207
  private _assertInitialized () {
208
- if (this._state !== State.OPEN) {
208
+ if (this._state !== State.INITIALIZED) {
209
209
  throw new Error('Database not initialized.');
210
210
  }
211
211
  }
package/src/api/item.ts CHANGED
@@ -10,7 +10,7 @@ import { Model, StateManager } from '@dxos/model-factory';
10
10
  import { ItemManager } from '../database';
11
11
  import { Entity } from './entity';
12
12
  import type { Link } from './link';
13
- import { Selection, createItemSelector } from './selection';
13
+ import { Selection, createItemSelection } from './selection';
14
14
 
15
15
  const log = debug('dxos:echo-db:item');
16
16
 
@@ -102,7 +102,7 @@ export class Item<M extends Model | null = Model> extends Entity<M> {
102
102
  * Returns a selection context, which can be used to traverse the object graph starting from this item.
103
103
  */
104
104
  select (): Selection<Item<any>> {
105
- return createItemSelector(this as Item, this._itemManager.debouncedUpdate, undefined);
105
+ return createItemSelection(this as Item, this._itemManager.debouncedUpdate, undefined);
106
106
  }
107
107
 
108
108
  /**
@@ -9,6 +9,7 @@ import { Event, ReadOnlyEvent } from '@dxos/async';
9
9
  /**
10
10
  * Reactive query results.
11
11
  */
12
+ // TODO(burdon): Replace with Selection?
12
13
  export class ResultSet<T> {
13
14
  private readonly _resultsUpdate = new Event<T[]>();
14
15
  private readonly _itemUpdate: ReadOnlyEvent;
@@ -0,0 +1,8 @@
1
+ //
2
+ // Copyright 2022 DXOS.org
3
+ //
4
+
5
+ export * from './queries';
6
+ export * from './result';
7
+ export * from './selection';
8
+ export * from './util';
@@ -0,0 +1,108 @@
1
+ //
2
+ // Copyright 2020 DXOS.org
3
+ //
4
+
5
+ import { ItemID } from '@dxos/echo-protocol';
6
+
7
+ import { Entity } from '../entity';
8
+ import { Item } from '../item';
9
+ import { Link } from '../link';
10
+ import { coerceToId, OneOrMultiple, testOneOrMultiple } from './util';
11
+
12
+ //
13
+ // Types
14
+ //
15
+
16
+ export type ItemIdFilter = {
17
+ id: ItemID
18
+ }
19
+
20
+ export type ItemFilter = {
21
+ type?: OneOrMultiple<string>
22
+ parent?: ItemID | Item
23
+ }
24
+
25
+ export type LinkFilter = {
26
+ type?: OneOrMultiple<string>;
27
+ }
28
+
29
+ export type Predicate<T extends Entity> = (entity: T) => boolean;
30
+
31
+ export type RootFilter = ItemIdFilter | ItemFilter | Predicate<Item>
32
+
33
+ /**
34
+ * Visitor callback.
35
+ * The visitor is passed the current entities and result (accumulator),
36
+ * which may be modified and returned.
37
+ */
38
+ export type Callable<T extends Entity, R> = (entities: T[], result: R) => 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
+ // Filters
67
+ //
68
+
69
+ export const filterToPredicate = (filter: ItemFilter | ItemIdFilter | Predicate<any>): Predicate<any> => {
70
+ if (typeof filter === 'function') {
71
+ return filter;
72
+ }
73
+
74
+ return itemFilterToPredicate(filter);
75
+ };
76
+
77
+ export const itemFilterToPredicate = (filter: ItemFilter | ItemIdFilter): Predicate<Item> => {
78
+ if ('id' in filter) {
79
+ return item => item.id === filter.id;
80
+ } else {
81
+ return item =>
82
+ (!filter.type || testOneOrMultiple(filter.type, item.type)) &&
83
+ (!filter.parent || item.parent?.id === coerceToId(filter.parent));
84
+ }
85
+ };
86
+
87
+ export const linkFilterToPredicate = (filter: LinkFilter): Predicate<Link> => {
88
+ return link => (!filter.type || testOneOrMultiple(filter.type, link.type));
89
+ };
90
+
91
+ export const createQueryOptionsFilter = ({
92
+ deleted = ItemFilterDeleted.HIDE_DELETED
93
+ }: QueryOptions): Predicate<Entity> => {
94
+ return entity => {
95
+ if (entity.model === null) {
96
+ return false;
97
+ }
98
+
99
+ switch (deleted) {
100
+ case ItemFilterDeleted.HIDE_DELETED:
101
+ return !(entity instanceof Item) || !entity.deleted;
102
+ case ItemFilterDeleted.SHOW_DELETED:
103
+ return true;
104
+ case ItemFilterDeleted.SHOW_DELETED_ONLY:
105
+ return entity instanceof Item && entity.deleted;
106
+ }
107
+ };
108
+ };
@@ -0,0 +1,112 @@
1
+ //
2
+ // Copyright 2020 DXOS.org
3
+ //
4
+
5
+ import assert from 'assert';
6
+
7
+ import { Event } from '@dxos/async';
8
+
9
+ import { Database } from '../database';
10
+ import { Entity } from '../entity';
11
+ import { dedupe } from './util';
12
+
13
+ /**
14
+ * Represents where the selection has started.
15
+ */
16
+ export type SelectionRoot = Database | Entity
17
+
18
+ /**
19
+ * Returned from each stage of the visitor.
20
+ */
21
+ export type SelectionContext<T extends Entity, R> = [entities: T[], result?: R]
22
+
23
+ /**
24
+ * Query subscription.
25
+ * Represents a live-query (subscription) that can notify about future updates to the relevant subset of items.
26
+ */
27
+ export class SelectionResult<T extends Entity, R = any> {
28
+ /**
29
+ * Fired when there are updates in the selection.
30
+ * Only update that are relevant to the selection cause the update.
31
+ */
32
+ readonly update = new Event<SelectionResult<T>>(); // TODO(burdon): Result result object.
33
+
34
+ private _lastResult: SelectionContext<T, R> = [[]];
35
+
36
+ constructor (
37
+ private readonly _execute: () => SelectionContext<T, R>,
38
+ private readonly _update: Event<Entity[]>,
39
+ private readonly _root: SelectionRoot,
40
+ private readonly _reducer: boolean
41
+ ) {
42
+ this.refresh();
43
+
44
+ // Re-run if deps change.
45
+ this.update.addEffect(() => _update.on(currentEntities => {
46
+ const [previousEntities] = this._lastResult;
47
+ this.refresh();
48
+
49
+ // Filters mutation events only if selection (since we can't reason about deps of call methods).
50
+ const set = new Set([...previousEntities, ...this._lastResult![0]]);
51
+ if (this._reducer || currentEntities.some(entity => set.has(entity as any))) {
52
+ this.update.emit(this);
53
+ }
54
+ }));
55
+ }
56
+
57
+ toString () {
58
+ const [entities] = this._lastResult;
59
+ return `SelectionResult<${JSON.stringify({
60
+ entities: entities.length
61
+ })}>`;
62
+ }
63
+
64
+ /**
65
+ * Re-run query.
66
+ */
67
+ refresh () {
68
+ const [entities, result] = this._execute();
69
+ this._lastResult = [dedupe(entities), result];
70
+ return this;
71
+ }
72
+
73
+ /**
74
+ * The root of the selection. Either a database or an item. Must be a stable reference.
75
+ */
76
+ get root (): SelectionRoot {
77
+ return this._root;
78
+ }
79
+
80
+ /**
81
+ * Get the result of this selection.
82
+ */
83
+ get entities (): T[] {
84
+ if (!this._lastResult) {
85
+ this.refresh();
86
+ }
87
+
88
+ const [entities] = this._lastResult!;
89
+ return entities;
90
+ }
91
+
92
+ /**
93
+ * Returns the selection or reducer result.
94
+ */
95
+ get value (): R extends void ? T[] : R {
96
+ if (!this._lastResult) {
97
+ this.refresh();
98
+ }
99
+
100
+ const [entities, value] = this._lastResult!;
101
+ return (this._reducer ? value : entities) as any;
102
+ }
103
+
104
+ /**
105
+ * Return the first element if the set has exactly one element.
106
+ */
107
+ expectOne (): T {
108
+ const entities = this.entities;
109
+ assert(entities.length === 1, `Expected one result; got ${entities.length}`);
110
+ return entities[0];
111
+ }
112
+ }
@@ -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 './entity';
15
- import { Item } from './item';
16
- import { Link } from './link';
17
- import { RootFilter, createSelector } from './selection';
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 createRootSelector = (filter?: RootFilter) =>
44
- createSelector<void>(() => items, () => new Event(), null as any, filter, undefined);
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
- createSelector<R>(() => items, () => new Event(), null as any, undefined, result);
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
- createRootSelector()
94
- .query().entities
95
+ createRootSelection()
96
+ .exec().entities
95
97
  ).toHaveLength(items.length);
96
98
  });
97
99
 
98
100
  test('by id', () => {
99
101
  expect(
100
- createRootSelector({ id: org1.id })
101
- .query().entities
102
+ createRootSelection({ id: org1.id })
103
+ .exec().entities
102
104
  ).toEqual([org1]);
103
105
 
104
106
  expect(
105
- createRootSelector({ id: org2.id })
106
- .query().entities
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
- createRootSelector({ type: ITEM_PROJECT })
113
- .query().entities
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
- createRootSelector({ type: [ITEM_ORG, ITEM_PROJECT] })
120
- .query().entities
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
- createRootSelector()
129
- .filter({ type: 'dxos:type.invalid' })
130
- .query().entities
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
- createRootSelector()
138
+ createRootSelection()
137
139
  .filter({ type: ITEM_PROJECT })
138
- .query().entities
140
+ .exec().entities
139
141
  ).toHaveLength(3);
140
142
  });
141
143
 
142
144
  test('multiple types', () => {
143
145
  expect(
144
- createRootSelector()
146
+ createRootSelection()
145
147
  .filter({ type: [ITEM_ORG, ITEM_PROJECT] })
146
- .query().entities
148
+ .exec().entities
147
149
  ).toHaveLength(5);
148
150
  });
149
151
 
150
152
  test('by function', () => {
151
153
  expect(
152
- createRootSelector()
154
+ createRootSelection()
153
155
  .filter(item => item.type === ITEM_ORG)
154
- .query().entities
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
- createRootSelector()
164
+ createRootSelection()
163
165
  .filter({ type: ITEM_ORG })
164
166
  .children({ type: ITEM_PROJECT })
165
- .query().entities
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
- createRootSelector({ id: org1.id })
177
+ createRootSelection({ id: org1.id })
176
178
  .children()
177
- .query().entities
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
- createRootSelector()
192
+ createRootSelection()
191
193
  .filter({ type: ITEM_PROJECT })
192
194
  .parent()
193
- .query().entities
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
- createRootSelector({ id: project1.id })
204
+ createRootSelection({ id: project1.id })
203
205
  .parent()
204
- .query().entities
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
- createRootSelector({ id: org1.id })
214
+ createRootSelection({ id: org1.id })
213
215
  .parent()
214
- .query().entities
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
- createRootSelector({ id: project1.id })
224
+ createRootSelection({ id: project1.id })
223
225
  .links()
224
226
  .target()
225
- .query().entities
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
- createRootSelector({ type: ITEM_PROJECT })
236
+ createRootSelection({ type: ITEM_PROJECT })
235
237
  .links()
236
- .query().entities
238
+ .exec().entities
237
239
  ).toHaveLength(links.length);
238
240
  });
239
241
 
240
242
  test('sources', () => {
241
243
  expect(ids(
242
- createRootSelector({ type: ITEM_PERSON })
244
+ createRootSelection({ type: ITEM_PERSON })
243
245
  .refs()
244
246
  .source()
245
- .query().entities
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).query();
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
- .query();
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 = createSelector<void>(() => items, () => update, null as any, { type: ITEM_ORG }, undefined)
287
+ const query = createSelection<void>(() => items, () => update, null as any, { type: ITEM_ORG }, undefined)
286
288
  .children()
287
- .query();
289
+ .exec();
288
290
 
289
291
  {
290
292
  const promise = query.update.waitForCount(1);