@dxos/echo-pipeline 0.8.3 → 0.8.4-main.1068cf700f

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 (199) hide show
  1. package/dist/lib/neutral/chunk-FJPXA75J.mjs +235 -0
  2. package/dist/lib/neutral/chunk-FJPXA75J.mjs.map +7 -0
  3. package/dist/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-WIQUYO7H.mjs} +300 -213
  4. package/dist/lib/neutral/chunk-WIQUYO7H.mjs.map +7 -0
  5. package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
  6. package/dist/lib/{browser → neutral}/index.mjs +2832 -946
  7. package/dist/lib/neutral/index.mjs.map +7 -0
  8. package/dist/lib/neutral/meta.json +1 -0
  9. package/dist/lib/{browser → neutral}/testing/index.mjs +73 -224
  10. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  11. package/dist/types/src/automerge/automerge-host.d.ts +53 -42
  12. package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
  13. package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
  14. package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
  15. package/dist/types/src/automerge/collection-synchronizer.d.ts +6 -4
  16. package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
  17. package/dist/types/src/automerge/echo-network-adapter.d.ts +22 -10
  18. package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
  19. package/dist/types/src/automerge/echo-replicator.d.ts +37 -15
  20. package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
  21. package/dist/types/src/automerge/heads-store.d.ts +9 -2
  22. package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
  23. package/dist/types/src/automerge/index.d.ts +1 -1
  24. package/dist/types/src/automerge/index.d.ts.map +1 -1
  25. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +5 -5
  26. package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
  27. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +9 -8
  28. package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
  29. package/dist/types/src/automerge/mesh-echo-replicator.d.ts +5 -5
  30. package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
  31. package/dist/types/src/common/codec.d.ts +1 -1
  32. package/dist/types/src/common/codec.d.ts.map +1 -1
  33. package/dist/types/src/db-host/automerge-data-source.d.ts +34 -0
  34. package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
  35. package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
  36. package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
  37. package/dist/types/src/db-host/data-service.d.ts +5 -4
  38. package/dist/types/src/db-host/data-service.d.ts.map +1 -1
  39. package/dist/types/src/db-host/database-root.d.ts.map +1 -1
  40. package/dist/types/src/db-host/documents-synchronizer.d.ts +6 -5
  41. package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
  42. package/dist/types/src/db-host/echo-host.d.ts +44 -23
  43. package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
  44. package/dist/types/src/db-host/index.d.ts +1 -0
  45. package/dist/types/src/db-host/index.d.ts.map +1 -1
  46. package/dist/types/src/db-host/local-queue-service.d.ts +16 -0
  47. package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
  48. package/dist/types/src/db-host/query-service.d.ts +14 -9
  49. package/dist/types/src/db-host/query-service.d.ts.map +1 -1
  50. package/dist/types/src/db-host/queue-data-source.d.ts +28 -0
  51. package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
  52. package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
  53. package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
  54. package/dist/types/src/db-host/space-state-manager.d.ts +2 -1
  55. package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
  56. package/dist/types/src/db-host/stub.d.ts +10 -0
  57. package/dist/types/src/db-host/stub.d.ts.map +1 -0
  58. package/dist/types/src/edge/echo-edge-replicator.d.ts +8 -6
  59. package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
  60. package/dist/types/src/filter/filter-match.d.ts +4 -1
  61. package/dist/types/src/filter/filter-match.d.ts.map +1 -1
  62. package/dist/types/src/metadata/metadata-store.d.ts +1 -1
  63. package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
  64. package/dist/types/src/pipeline/pipeline.d.ts +3 -3
  65. package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
  66. package/dist/types/src/query/errors.d.ts +21 -9
  67. package/dist/types/src/query/errors.d.ts.map +1 -1
  68. package/dist/types/src/query/plan.d.ts +57 -5
  69. package/dist/types/src/query/plan.d.ts.map +1 -1
  70. package/dist/types/src/query/query-executor.d.ts +49 -10
  71. package/dist/types/src/query/query-executor.d.ts.map +1 -1
  72. package/dist/types/src/query/query-planner.d.ts +9 -0
  73. package/dist/types/src/query/query-planner.d.ts.map +1 -1
  74. package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
  75. package/dist/types/src/space/auth.d.ts +3 -3
  76. package/dist/types/src/space/auth.d.ts.map +1 -1
  77. package/dist/types/src/space/control-pipeline.d.ts +3 -3
  78. package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
  79. package/dist/types/src/space/space-manager.d.ts +7 -7
  80. package/dist/types/src/space/space-manager.d.ts.map +1 -1
  81. package/dist/types/src/space/space-protocol.d.ts +6 -6
  82. package/dist/types/src/space/space-protocol.d.ts.map +1 -1
  83. package/dist/types/src/space/space.d.ts +4 -4
  84. package/dist/types/src/space/space.d.ts.map +1 -1
  85. package/dist/types/src/testing/index.d.ts +0 -2
  86. package/dist/types/src/testing/index.d.ts.map +1 -1
  87. package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
  88. package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
  89. package/dist/types/src/testing/test-network-adapter.d.ts +1 -1
  90. package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
  91. package/dist/types/src/testing/test-replicator.d.ts +14 -13
  92. package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
  93. package/dist/types/src/util.d.ts +1 -1
  94. package/dist/types/src/util.d.ts.map +1 -1
  95. package/dist/types/tsconfig.tsbuildinfo +1 -1
  96. package/package.json +53 -45
  97. package/src/automerge/automerge-host.test.ts +55 -24
  98. package/src/automerge/automerge-host.ts +399 -126
  99. package/src/automerge/automerge-repo.test.ts +214 -46
  100. package/src/automerge/collection-synchronizer.test.ts +22 -15
  101. package/src/automerge/collection-synchronizer.ts +57 -22
  102. package/src/automerge/echo-data-monitor.ts +1 -1
  103. package/src/automerge/echo-network-adapter.test.ts +3 -3
  104. package/src/automerge/echo-network-adapter.ts +73 -28
  105. package/src/automerge/echo-replicator.ts +40 -15
  106. package/src/automerge/heads-store.ts +42 -3
  107. package/src/automerge/index.ts +1 -1
  108. package/src/automerge/leveldb-storage-adapter.ts +11 -11
  109. package/src/automerge/mesh-echo-replicator-connection.ts +12 -8
  110. package/src/automerge/mesh-echo-replicator.ts +15 -11
  111. package/src/automerge/storage-adapter.test.ts +1 -1
  112. package/src/common/space-id.ts +1 -1
  113. package/src/db-host/automerge-data-source.test.ts +292 -0
  114. package/src/db-host/automerge-data-source.ts +144 -0
  115. package/src/db-host/data-service.ts +19 -20
  116. package/src/db-host/database-root.ts +2 -2
  117. package/src/db-host/documents-synchronizer.test.ts +73 -13
  118. package/src/db-host/documents-synchronizer.ts +58 -44
  119. package/src/db-host/echo-host.ts +194 -84
  120. package/src/db-host/index.ts +1 -0
  121. package/src/db-host/local-queue-service.ts +147 -0
  122. package/src/db-host/query-service.ts +28 -95
  123. package/src/db-host/queue-data-source.ts +132 -0
  124. package/src/db-host/queue-service.test.ts +145 -0
  125. package/src/db-host/space-state-manager.ts +6 -2
  126. package/src/db-host/stub.ts +28 -0
  127. package/src/edge/echo-edge-replicator.test.ts +8 -6
  128. package/src/edge/echo-edge-replicator.ts +124 -42
  129. package/src/filter/filter-match.test.ts +58 -32
  130. package/src/filter/filter-match.ts +163 -15
  131. package/src/metadata/metadata-store.ts +8 -5
  132. package/src/pipeline/pipeline-stress.test.ts +4 -2
  133. package/src/pipeline/pipeline.test.ts +3 -2
  134. package/src/pipeline/pipeline.ts +10 -7
  135. package/src/query/errors.ts +3 -1
  136. package/src/query/plan.ts +75 -5
  137. package/src/query/query-executor.ts +812 -117
  138. package/src/query/query-planner.test.ts +652 -34
  139. package/src/query/query-planner.ts +276 -13
  140. package/src/space/admission-discovery-extension.ts +3 -3
  141. package/src/space/auth.ts +6 -6
  142. package/src/space/control-pipeline.test.ts +4 -3
  143. package/src/space/control-pipeline.ts +11 -8
  144. package/src/space/space-manager.browser.test.ts +1 -1
  145. package/src/space/space-manager.ts +11 -10
  146. package/src/space/space-protocol.browser.test.ts +2 -2
  147. package/src/space/space-protocol.test.ts +3 -2
  148. package/src/space/space-protocol.ts +17 -14
  149. package/src/space/space.test.ts +1 -1
  150. package/src/space/space.ts +6 -5
  151. package/src/testing/index.ts +0 -2
  152. package/src/testing/test-agent-builder.ts +4 -3
  153. package/src/testing/test-network-adapter.ts +16 -3
  154. package/src/testing/test-replicator.ts +23 -18
  155. package/src/util.ts +7 -3
  156. package/dist/lib/browser/chunk-35I6ERLG.mjs.map +0 -7
  157. package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
  158. package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
  159. package/dist/lib/browser/chunk-TQJTKNMS.mjs +0 -126
  160. package/dist/lib/browser/chunk-TQJTKNMS.mjs.map +0 -7
  161. package/dist/lib/browser/index.mjs.map +0 -7
  162. package/dist/lib/browser/meta.json +0 -1
  163. package/dist/lib/browser/testing/index.mjs.map +0 -7
  164. package/dist/lib/node/chunk-HOPOFWAL.cjs +0 -147
  165. package/dist/lib/node/chunk-HOPOFWAL.cjs.map +0 -7
  166. package/dist/lib/node/chunk-JXX6LF5U.cjs +0 -2084
  167. package/dist/lib/node/chunk-JXX6LF5U.cjs.map +0 -7
  168. package/dist/lib/node/chunk-Q7SFCCGT.cjs +0 -33
  169. package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +0 -7
  170. package/dist/lib/node/filter/index.cjs +0 -32
  171. package/dist/lib/node/filter/index.cjs.map +0 -7
  172. package/dist/lib/node/index.cjs +0 -4699
  173. package/dist/lib/node/index.cjs.map +0 -7
  174. package/dist/lib/node/meta.json +0 -1
  175. package/dist/lib/node/testing/index.cjs +0 -753
  176. package/dist/lib/node/testing/index.cjs.map +0 -7
  177. package/dist/lib/node-esm/chunk-5BHLPT24.mjs +0 -2067
  178. package/dist/lib/node-esm/chunk-5BHLPT24.mjs.map +0 -7
  179. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
  180. package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
  181. package/dist/lib/node-esm/chunk-RVK35BS7.mjs +0 -126
  182. package/dist/lib/node-esm/chunk-RVK35BS7.mjs.map +0 -7
  183. package/dist/lib/node-esm/filter/index.mjs +0 -11
  184. package/dist/lib/node-esm/filter/index.mjs.map +0 -7
  185. package/dist/lib/node-esm/index.mjs +0 -4743
  186. package/dist/lib/node-esm/index.mjs.map +0 -7
  187. package/dist/lib/node-esm/meta.json +0 -1
  188. package/dist/lib/node-esm/testing/index.mjs +0 -742
  189. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  190. package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
  191. package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
  192. package/dist/types/src/testing/test-data.d.ts +0 -18
  193. package/dist/types/src/testing/test-data.d.ts.map +0 -1
  194. package/dist/types/src/testing/test-schema.d.ts +0 -37
  195. package/dist/types/src/testing/test-schema.d.ts.map +0 -1
  196. package/src/db-host/documents-iterator.ts +0 -74
  197. package/src/testing/test-data.ts +0 -127
  198. package/src/testing/test-schema.ts +0 -53
  199. /package/dist/lib/{browser → neutral}/filter/index.mjs.map +0 -0
@@ -2,8 +2,8 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import { decodeReference, isEncodedReference, type QueryAST, type ObjectStructure } from '@dxos/echo-protocol';
6
- import { EXPANDO_TYPENAME } from '@dxos/echo-schema';
5
+ import { ATTR_META, type ObjectJSON } from '@dxos/echo/internal';
6
+ import { EncodedReference, ObjectStructure, type QueryAST, isEncodedReference } from '@dxos/echo-protocol';
7
7
  import { DXN, type ObjectId, type SpaceId } from '@dxos/keys';
8
8
 
9
9
  export type MatchedObject = {
@@ -14,35 +14,32 @@ export type MatchedObject = {
14
14
 
15
15
  /**
16
16
  * Matches an object against a filter AST.
17
+ * @param obj object structure as stored in automerge.
17
18
  */
18
19
  export const filterMatchObject = (filter: QueryAST.Filter, obj: MatchedObject): boolean => {
19
20
  switch (filter.type) {
20
21
  case 'object': {
21
- // Check typename if specified
22
+ // Check typename if specified.
22
23
  if (filter.typename !== null) {
23
24
  // TODO(dmaretskyi): `system` is missing in some cases.
24
25
  if (!obj.doc.system?.type?.['/']) {
25
- // Objects with no type are considered to be expando objects
26
- const expectedDXN = DXN.parse(filter.typename).asTypeDXN();
27
- if (expectedDXN?.type !== EXPANDO_TYPENAME) {
28
- return false;
29
- }
26
+ // Objects with no type are deprecated.
27
+ return false;
30
28
  } else {
31
29
  const actualDXN = DXN.parse(obj.doc.system.type['/']);
32
30
  const expectedDXN = DXN.parse(filter.typename);
33
-
34
31
  if (!compareTypename(expectedDXN, actualDXN)) {
35
32
  return false;
36
33
  }
37
34
  }
38
35
  }
39
36
 
40
- // Check IDs if specified
37
+ // Check IDs if specified.
41
38
  if (filter.id && filter.id.length > 0 && !filter.id.includes(obj.id)) {
42
39
  return false;
43
40
  }
44
41
 
45
- // Check properties
42
+ // Check properties.
46
43
  if (filter.props) {
47
44
  for (const [key, valueFilter] of Object.entries(filter.props)) {
48
45
  const value = obj.doc.data[key];
@@ -52,7 +49,7 @@ export const filterMatchObject = (filter: QueryAST.Filter, obj: MatchedObject):
52
49
  }
53
50
  }
54
51
 
55
- // Check foreign keys if specified
52
+ // Check foreign keys if specified.
56
53
  if (filter.foreignKeys && filter.foreignKeys.length > 0) {
57
54
  const hasMatchingKey = filter.foreignKeys.some((filterKey) =>
58
55
  obj.doc.meta.keys.some((objKey) => objKey.source === filterKey.source && objKey.id === filterKey.id),
@@ -65,8 +62,13 @@ export const filterMatchObject = (filter: QueryAST.Filter, obj: MatchedObject):
65
62
  return true;
66
63
  }
67
64
 
65
+ case 'tag': {
66
+ const tags = ObjectStructure.getTags(obj.doc);
67
+ return tags.some((tag) => tag === filter.tag);
68
+ }
69
+
68
70
  case 'text-search': {
69
- // TODO: Implement text search
71
+ // TODO(???): Implement text search.
70
72
  return false;
71
73
  }
72
74
 
@@ -87,6 +89,120 @@ export const filterMatchObject = (filter: QueryAST.Filter, obj: MatchedObject):
87
89
  }
88
90
  };
89
91
 
92
+ // TODO(burdon): Reconcile with filterMatchObject.
93
+ export const filterMatchObjectJSON = (filter: QueryAST.Filter, obj: ObjectJSON): boolean => {
94
+ switch (filter.type) {
95
+ case 'object': {
96
+ // Check typename if specified
97
+ if (filter.typename !== null) {
98
+ // TODO(dmaretskyi): `system` is missing in some cases.
99
+ if (!obj['@type']) {
100
+ // Objects with no type are deprecated.
101
+ return false;
102
+ } else {
103
+ const actualDXN = DXN.parse(obj['@type']);
104
+ const expectedDXN = DXN.parse(filter.typename);
105
+ if (!compareTypename(expectedDXN, actualDXN)) {
106
+ return false;
107
+ }
108
+ }
109
+ }
110
+
111
+ // Check IDs if specified
112
+ if (filter.id && filter.id.length > 0 && !filter.id.includes(obj.id)) {
113
+ return false;
114
+ }
115
+
116
+ // Check properties
117
+ if (filter.props) {
118
+ for (const [key, valueFilter] of Object.entries(filter.props)) {
119
+ if (key.startsWith('@')) {
120
+ // ignore meta properties
121
+ continue;
122
+ }
123
+ const value = (obj as any)[key];
124
+ if (!filterMatchValue(valueFilter, value)) {
125
+ return false;
126
+ }
127
+ }
128
+ }
129
+
130
+ // Check foreign keys if specified
131
+ if (filter.foreignKeys && filter.foreignKeys.length > 0) {
132
+ const hasMatchingKey = filter.foreignKeys.some((filterKey) =>
133
+ obj['@meta']?.keys?.some((objKey) => objKey.source === filterKey.source && objKey.id === filterKey.id),
134
+ );
135
+ if (!hasMatchingKey) {
136
+ return false;
137
+ }
138
+ }
139
+
140
+ return true;
141
+ }
142
+
143
+ case 'tag': {
144
+ const tags = obj[ATTR_META]?.tags ?? [];
145
+ return tags.some((tag) => tag === filter.tag);
146
+ }
147
+
148
+ // TODO: Implement text search.
149
+ case 'text-search': {
150
+ return false;
151
+ }
152
+
153
+ case 'not': {
154
+ return !filterMatchObjectJSON(filter.filter, obj);
155
+ }
156
+
157
+ case 'and': {
158
+ return filter.filters.every((f) => filterMatchObjectJSON(f, obj));
159
+ }
160
+
161
+ case 'or': {
162
+ return filter.filters.some((f) => filterMatchObjectJSON(f, obj));
163
+ }
164
+
165
+ default:
166
+ return false;
167
+ }
168
+ };
169
+
170
+ /**
171
+ * Performs structural matching between a filter object and a target object.
172
+ * This handles nested object comparison for array matching scenarios.
173
+ */
174
+ // TODO(wittjosiah): Add ast support for non-strict matching.
175
+ const structuralMatch = (filterObj: any, targetObj: any, strict = true): boolean => {
176
+ if (typeof filterObj !== 'object' || filterObj === null) {
177
+ return filterObj === targetObj;
178
+ }
179
+
180
+ if (typeof targetObj !== 'object' || targetObj === null) {
181
+ return false;
182
+ }
183
+
184
+ // Prohibit extra keys in targetObj.
185
+ const filterKeys = Object.keys(filterObj);
186
+ const targetKeys = Object.keys(targetObj);
187
+ if (strict && filterKeys.length !== targetKeys.length) {
188
+ return false;
189
+ }
190
+
191
+ return filterKeys.every((key) => {
192
+ if (!(key in targetObj)) {
193
+ return false;
194
+ }
195
+ const filterValue = filterObj[key];
196
+ const targetValue = targetObj[key];
197
+
198
+ if (typeof filterValue === 'object' && filterValue !== null) {
199
+ return structuralMatch(filterValue, targetValue);
200
+ }
201
+
202
+ return filterValue === targetValue;
203
+ });
204
+ };
205
+
90
206
  export const filterMatchValue = (filter: QueryAST.Filter, value: unknown): boolean => {
91
207
  switch (filter.type) {
92
208
  case 'compare': {
@@ -97,7 +213,7 @@ export const filterMatchValue = (filter: QueryAST.Filter, value: unknown): boole
97
213
  if (!isEncodedReference(value)) {
98
214
  return false;
99
215
  }
100
- return DXN.equals(decodeReference(value).toDXN(), decodeReference(compareValue).toDXN());
216
+ return DXN.equals(EncodedReference.toDXN(value), EncodedReference.toDXN(compareValue));
101
217
  }
102
218
  return value === compareValue;
103
219
  case 'neq':
@@ -110,12 +226,44 @@ export const filterMatchValue = (filter: QueryAST.Filter, value: unknown): boole
110
226
  return (value as any) < compareValue;
111
227
  case 'lte':
112
228
  return (value as any) <= compareValue;
229
+ default:
230
+ return false;
231
+ }
232
+ }
233
+ case 'object': {
234
+ // Handle nested object filters for property matching
235
+ if (typeof value !== 'object' || value === null) {
236
+ return false;
237
+ }
238
+
239
+ // Check properties
240
+ if (filter.props) {
241
+ for (const [key, valueFilter] of Object.entries(filter.props)) {
242
+ const nestedValue = (value as any)[key];
243
+ if (!filterMatchValue(valueFilter, nestedValue)) {
244
+ return false;
245
+ }
246
+ }
113
247
  }
114
- break;
248
+
249
+ return true;
115
250
  }
116
251
  case 'in': {
117
252
  return filter.values.includes(value);
118
253
  }
254
+ case 'contains': {
255
+ if (!Array.isArray(value)) {
256
+ return false;
257
+ }
258
+
259
+ return value.some((element) => {
260
+ if (typeof filter.value === 'object' && filter.value !== null && !Array.isArray(filter.value)) {
261
+ return structuralMatch(filter.value, element);
262
+ }
263
+
264
+ return element === filter.value;
265
+ });
266
+ }
119
267
  case 'range': {
120
268
  return (value as any) >= filter.from && (value as any) <= filter.to;
121
269
  }
@@ -16,11 +16,11 @@ import { Invitation, SpaceState } from '@dxos/protocols/proto/dxos/client/servic
16
16
  import {
17
17
  type ControlPipelineSnapshot,
18
18
  type EchoMetadata,
19
- type SpaceMetadata,
19
+ type EdgeReplicationSetting,
20
20
  type IdentityRecord,
21
- type SpaceCache,
22
21
  type LargeSpaceMetadata,
23
- type EdgeReplicationSetting,
22
+ type SpaceCache,
23
+ type SpaceMetadata,
24
24
  } from '@dxos/protocols/proto/dxos/echo/metadata';
25
25
  import { type Directory, type File } from '@dxos/random-access-storage';
26
26
  import { type Timeframe } from '@dxos/timeframe';
@@ -91,14 +91,17 @@ export class MetadataStore {
91
91
  log('loaded', { size: dataSize, checksum, name: file.filename });
92
92
 
93
93
  if (fileLength < dataSize + 8) {
94
- throw new DataCorruptionError('Metadata size is smaller than expected.', { fileLength, dataSize });
94
+ throw new DataCorruptionError({
95
+ message: 'Metadata size is smaller than expected.',
96
+ context: { fileLength, dataSize },
97
+ });
95
98
  }
96
99
 
97
100
  const data = await file.read(8, dataSize);
98
101
 
99
102
  const calculatedChecksum = CRC32.buf(data);
100
103
  if (calculatedChecksum !== checksum) {
101
- throw new DataCorruptionError('Metadata checksum is invalid.');
104
+ throw new DataCorruptionError({ message: 'Metadata checksum is invalid.' });
102
105
  }
103
106
 
104
107
  return codec.decode(data);
@@ -2,8 +2,9 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import * as fc from 'fast-check';
6
5
  import { inspect } from 'node:util';
6
+
7
+ import * as fc from 'fast-check';
7
8
  import { describe, expect, test } from 'vitest';
8
9
 
9
10
  import { asyncTimeout } from '@dxos/async';
@@ -15,9 +16,10 @@ import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
15
16
  import { Timeframe } from '@dxos/timeframe';
16
17
  import { range } from '@dxos/util';
17
18
 
18
- import { Pipeline } from './pipeline';
19
19
  import { TestFeedBuilder } from '../testing';
20
20
 
21
+ import { Pipeline } from './pipeline';
22
+
21
23
  const NUM_AGENTS = 2;
22
24
  const NUM_MESSAGES = 10;
23
25
 
@@ -2,16 +2,17 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { describe, expect, test, onTestFinished } from 'vitest';
5
+ import { describe, expect, onTestFinished, test } from 'vitest';
6
6
 
7
7
  import { Event, sleep } from '@dxos/async';
8
8
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
9
9
  import { Timeframe } from '@dxos/timeframe';
10
10
  import { range } from '@dxos/util';
11
11
 
12
- import { Pipeline } from './pipeline';
13
12
  import { TestFeedBuilder } from '../testing';
14
13
 
14
+ import { Pipeline } from './pipeline';
15
+
15
16
  const TEST_MESSAGE: FeedMessage = {
16
17
  timeframe: new Timeframe(),
17
18
  payload: {},
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { Event, sleepWithContext, synchronized, Trigger } from '@dxos/async';
5
+ import { Event, Trigger, sleepWithContext, synchronized } from '@dxos/async';
6
6
  import { Context, rejectOnDispose } from '@dxos/context';
7
7
  import { failUndefined } from '@dxos/debug';
8
8
  import { FeedSetIterator, type FeedWrapper, type FeedWriter } from '@dxos/feed-store';
@@ -14,11 +14,12 @@ import type { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
14
14
  import { Timeframe } from '@dxos/timeframe';
15
15
  import { ComplexMap } from '@dxos/util';
16
16
 
17
- import { createMessageSelector } from './message-selector';
18
- import { mapFeedIndexesToTimeframe, startAfter, TimeframeClock } from './timeframe-clock';
19
17
  import { createMappedFeedWriter } from '../common';
20
18
 
21
- export type WaitUntilReachedTargetParams = {
19
+ import { createMessageSelector } from './message-selector';
20
+ import { TimeframeClock, mapFeedIndexesToTimeframe, startAfter } from './timeframe-clock';
21
+
22
+ export type WaitUntilReachedTargetProps = {
22
23
  /**
23
24
  * For cancellation.
24
25
  */
@@ -40,8 +41,10 @@ export class PipelineState {
40
41
  */
41
42
  _ctx = new Context();
42
43
 
43
- // TODO(dmaretskyi): Remove?.
44
- public readonly timeframeUpdate = this._timeframeClock.update;
44
+ // TODO(dmaretskyi): Remove?. Avoid accessing `_timeframeClock` before constructor initialization.
45
+ public get timeframeUpdate() {
46
+ return this._timeframeClock.update;
47
+ }
45
48
 
46
49
  public readonly stalled = new Event();
47
50
 
@@ -129,7 +132,7 @@ export class PipelineState {
129
132
  ctx = new Context(),
130
133
  timeout,
131
134
  breakOnStall = true,
132
- }: WaitUntilReachedTargetParams = {}): Promise<void> {
135
+ }: WaitUntilReachedTargetProps = {}): Promise<void> {
133
136
  log('waitUntilReachedTargetTimeframe', {
134
137
  timeout,
135
138
  current: this.timeframe,
@@ -4,4 +4,6 @@
4
4
 
5
5
  import { BaseError } from '@dxos/errors';
6
6
 
7
- export class QueryError extends BaseError.extend('QUERY_ERROR') {}
7
+ export class QueryError extends BaseError.extend('QueryError') {}
8
+
9
+ export class InvalidQueryError extends QueryError.extend('InvalidQueryError') {}
package/src/query/plan.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import type { QueryAST } from '@dxos/echo-protocol';
6
- import type { EscapedPropPath } from '@dxos/indexing';
6
+ import type { EscapedPropPath } from '@dxos/index-core';
7
7
  import type { DXN, ObjectId, SpaceId } from '@dxos/keys';
8
8
 
9
9
  export namespace QueryPlan {
@@ -32,7 +32,9 @@ export namespace QueryPlan {
32
32
  | FilterDeletedStep
33
33
  | TraverseStep
34
34
  | UnionStep
35
- | SetDifferenceStep;
35
+ | SetDifferenceStep
36
+ | OrderStep
37
+ | LimitStep;
36
38
 
37
39
  /**
38
40
  * Clear the current working set.
@@ -43,13 +45,36 @@ export namespace QueryPlan {
43
45
 
44
46
  /**
45
47
  * Select objects based on id, typename, or other predicates.
46
- * Specifies the spaces to select from.
48
+ * Specifies the spaces and/or queues to select from.
49
+ * The results from spaces and queues are unioned together.
47
50
  */
48
51
  export type SelectStep = {
49
52
  _tag: 'SelectStep';
50
53
 
54
+ // TODO(dmaretskyi): Extract the object container spec into a separate type in ECHO-protocol that we can share with the indexer.
55
+
56
+ /**
57
+ * Select from specific spaces.
58
+ */
51
59
  spaces: readonly SpaceId[];
60
+
61
+ /**
62
+ * If true, select from all queues in the spaces specified by `spaces`.
63
+ */
64
+ allQueuesFromSpaces: boolean;
65
+
66
+ /**
67
+ * Select from specific queues.
68
+ */
69
+ queues: readonly DXN.String[];
70
+
52
71
  selector: Selector;
72
+
73
+ /**
74
+ * Optional limit on the number of results to select.
75
+ * When set, the index scan can be optimized to stop early.
76
+ */
77
+ limit?: number;
53
78
  };
54
79
 
55
80
  /**
@@ -90,6 +115,11 @@ export namespace QueryPlan {
90
115
 
91
116
  text: string;
92
117
  searchKind: TextSearchKind;
118
+
119
+ /**
120
+ * Optionally select only objects of the given typenames.
121
+ */
122
+ typename: DXN.String[] | null;
93
123
  };
94
124
 
95
125
  /**
@@ -140,7 +170,7 @@ export namespace QueryPlan {
140
170
  /**
141
171
  * Describes a traversal of the object graph.
142
172
  */
143
- export type Traversal = ReferenceTraversal | RelationTraversal;
173
+ export type Traversal = ReferenceTraversal | RelationTraversal | HierarchyTraversal;
144
174
 
145
175
  /**
146
176
  * Traverse a reference connection.
@@ -150,8 +180,9 @@ export namespace QueryPlan {
150
180
 
151
181
  /**
152
182
  * Property path where the reference is located.
183
+ * If null, matches references from any property (only valid for incoming direction).
153
184
  */
154
- property: EscapedPropPath;
185
+ property: EscapedPropPath | null;
155
186
 
156
187
  /**
157
188
  * outgoing: the reference points from the anchor object to the target.
@@ -173,6 +204,19 @@ export namespace QueryPlan {
173
204
  direction: 'source-to-relation' | 'relation-to-source' | 'target-to-relation' | 'relation-to-target';
174
205
  };
175
206
 
207
+ /**
208
+ * Traverse parent-child hierarchy.
209
+ */
210
+ export type HierarchyTraversal = {
211
+ _tag: 'HierarchyTraversal';
212
+
213
+ /**
214
+ * to-parent: traverse from child to parent.
215
+ * to-children: traverse from parent to children.
216
+ */
217
+ direction: 'to-parent' | 'to-children';
218
+ };
219
+
176
220
  /**
177
221
  * Combine results from multiple plans.
178
222
  * Each of the plans starts with a copy of the current working set.
@@ -193,4 +237,30 @@ export namespace QueryPlan {
193
237
  source: Plan;
194
238
  exclude: Plan;
195
239
  };
240
+
241
+ /**
242
+ * Order the results of the plan.
243
+ */
244
+ export type OrderStep = {
245
+ _tag: 'OrderStep';
246
+
247
+ // Defaults to natural order if empty.
248
+ order: readonly QueryAST.Order[];
249
+
250
+ /**
251
+ * Optional limit on the number of results to return after ordering.
252
+ * When set, the sorting can be optimized to only track the top N elements.
253
+ */
254
+ // TODO(dmaretskyi): Remove. This doesn't belong here.
255
+ limit?: number;
256
+ };
257
+
258
+ /**
259
+ * Limit the number of results.
260
+ */
261
+ export type LimitStep = {
262
+ _tag: 'LimitStep';
263
+
264
+ limit: number;
265
+ };
196
266
  }