@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.
- package/dist/lib/neutral/chunk-FJPXA75J.mjs +235 -0
- package/dist/lib/neutral/chunk-FJPXA75J.mjs.map +7 -0
- package/dist/lib/{browser/chunk-35I6ERLG.mjs → neutral/chunk-WIQUYO7H.mjs} +300 -213
- package/dist/lib/neutral/chunk-WIQUYO7H.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/filter/index.mjs +3 -2
- package/dist/lib/{browser → neutral}/index.mjs +2832 -946
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +73 -224
- package/dist/lib/neutral/testing/index.mjs.map +7 -0
- package/dist/types/src/automerge/automerge-host.d.ts +53 -42
- package/dist/types/src/automerge/automerge-host.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts +1 -1
- package/dist/types/src/automerge/automerge-repo.test.d.ts.map +1 -1
- package/dist/types/src/automerge/collection-synchronizer.d.ts +6 -4
- package/dist/types/src/automerge/collection-synchronizer.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-network-adapter.d.ts +22 -10
- package/dist/types/src/automerge/echo-network-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/echo-replicator.d.ts +37 -15
- package/dist/types/src/automerge/echo-replicator.d.ts.map +1 -1
- package/dist/types/src/automerge/heads-store.d.ts +9 -2
- package/dist/types/src/automerge/heads-store.d.ts.map +1 -1
- package/dist/types/src/automerge/index.d.ts +1 -1
- package/dist/types/src/automerge/index.d.ts.map +1 -1
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts +5 -5
- package/dist/types/src/automerge/leveldb-storage-adapter.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts +9 -8
- package/dist/types/src/automerge/mesh-echo-replicator-connection.d.ts.map +1 -1
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts +5 -5
- package/dist/types/src/automerge/mesh-echo-replicator.d.ts.map +1 -1
- package/dist/types/src/common/codec.d.ts +1 -1
- package/dist/types/src/common/codec.d.ts.map +1 -1
- package/dist/types/src/db-host/automerge-data-source.d.ts +34 -0
- package/dist/types/src/db-host/automerge-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts +2 -0
- package/dist/types/src/db-host/automerge-data-source.test.d.ts.map +1 -0
- package/dist/types/src/db-host/data-service.d.ts +5 -4
- package/dist/types/src/db-host/data-service.d.ts.map +1 -1
- package/dist/types/src/db-host/database-root.d.ts.map +1 -1
- package/dist/types/src/db-host/documents-synchronizer.d.ts +6 -5
- package/dist/types/src/db-host/documents-synchronizer.d.ts.map +1 -1
- package/dist/types/src/db-host/echo-host.d.ts +44 -23
- package/dist/types/src/db-host/echo-host.d.ts.map +1 -1
- package/dist/types/src/db-host/index.d.ts +1 -0
- package/dist/types/src/db-host/index.d.ts.map +1 -1
- package/dist/types/src/db-host/local-queue-service.d.ts +16 -0
- package/dist/types/src/db-host/local-queue-service.d.ts.map +1 -0
- package/dist/types/src/db-host/query-service.d.ts +14 -9
- package/dist/types/src/db-host/query-service.d.ts.map +1 -1
- package/dist/types/src/db-host/queue-data-source.d.ts +28 -0
- package/dist/types/src/db-host/queue-data-source.d.ts.map +1 -0
- package/dist/types/src/db-host/queue-service.test.d.ts +2 -0
- package/dist/types/src/db-host/queue-service.test.d.ts.map +1 -0
- package/dist/types/src/db-host/space-state-manager.d.ts +2 -1
- package/dist/types/src/db-host/space-state-manager.d.ts.map +1 -1
- package/dist/types/src/db-host/stub.d.ts +10 -0
- package/dist/types/src/db-host/stub.d.ts.map +1 -0
- package/dist/types/src/edge/echo-edge-replicator.d.ts +8 -6
- package/dist/types/src/edge/echo-edge-replicator.d.ts.map +1 -1
- package/dist/types/src/filter/filter-match.d.ts +4 -1
- package/dist/types/src/filter/filter-match.d.ts.map +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts +1 -1
- package/dist/types/src/metadata/metadata-store.d.ts.map +1 -1
- package/dist/types/src/pipeline/pipeline.d.ts +3 -3
- package/dist/types/src/pipeline/pipeline.d.ts.map +1 -1
- package/dist/types/src/query/errors.d.ts +21 -9
- package/dist/types/src/query/errors.d.ts.map +1 -1
- package/dist/types/src/query/plan.d.ts +57 -5
- package/dist/types/src/query/plan.d.ts.map +1 -1
- package/dist/types/src/query/query-executor.d.ts +49 -10
- package/dist/types/src/query/query-executor.d.ts.map +1 -1
- package/dist/types/src/query/query-planner.d.ts +9 -0
- package/dist/types/src/query/query-planner.d.ts.map +1 -1
- package/dist/types/src/space/admission-discovery-extension.d.ts.map +1 -1
- package/dist/types/src/space/auth.d.ts +3 -3
- package/dist/types/src/space/auth.d.ts.map +1 -1
- package/dist/types/src/space/control-pipeline.d.ts +3 -3
- package/dist/types/src/space/control-pipeline.d.ts.map +1 -1
- package/dist/types/src/space/space-manager.d.ts +7 -7
- package/dist/types/src/space/space-manager.d.ts.map +1 -1
- package/dist/types/src/space/space-protocol.d.ts +6 -6
- package/dist/types/src/space/space-protocol.d.ts.map +1 -1
- package/dist/types/src/space/space.d.ts +4 -4
- package/dist/types/src/space/space.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +0 -2
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-agent-builder.d.ts +2 -2
- package/dist/types/src/testing/test-agent-builder.d.ts.map +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts +1 -1
- package/dist/types/src/testing/test-network-adapter.d.ts.map +1 -1
- package/dist/types/src/testing/test-replicator.d.ts +14 -13
- package/dist/types/src/testing/test-replicator.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +1 -1
- package/dist/types/src/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +53 -45
- package/src/automerge/automerge-host.test.ts +55 -24
- package/src/automerge/automerge-host.ts +399 -126
- package/src/automerge/automerge-repo.test.ts +214 -46
- package/src/automerge/collection-synchronizer.test.ts +22 -15
- package/src/automerge/collection-synchronizer.ts +57 -22
- package/src/automerge/echo-data-monitor.ts +1 -1
- package/src/automerge/echo-network-adapter.test.ts +3 -3
- package/src/automerge/echo-network-adapter.ts +73 -28
- package/src/automerge/echo-replicator.ts +40 -15
- package/src/automerge/heads-store.ts +42 -3
- package/src/automerge/index.ts +1 -1
- package/src/automerge/leveldb-storage-adapter.ts +11 -11
- package/src/automerge/mesh-echo-replicator-connection.ts +12 -8
- package/src/automerge/mesh-echo-replicator.ts +15 -11
- package/src/automerge/storage-adapter.test.ts +1 -1
- package/src/common/space-id.ts +1 -1
- package/src/db-host/automerge-data-source.test.ts +292 -0
- package/src/db-host/automerge-data-source.ts +144 -0
- package/src/db-host/data-service.ts +19 -20
- package/src/db-host/database-root.ts +2 -2
- package/src/db-host/documents-synchronizer.test.ts +73 -13
- package/src/db-host/documents-synchronizer.ts +58 -44
- package/src/db-host/echo-host.ts +194 -84
- package/src/db-host/index.ts +1 -0
- package/src/db-host/local-queue-service.ts +147 -0
- package/src/db-host/query-service.ts +28 -95
- package/src/db-host/queue-data-source.ts +132 -0
- package/src/db-host/queue-service.test.ts +145 -0
- package/src/db-host/space-state-manager.ts +6 -2
- package/src/db-host/stub.ts +28 -0
- package/src/edge/echo-edge-replicator.test.ts +8 -6
- package/src/edge/echo-edge-replicator.ts +124 -42
- package/src/filter/filter-match.test.ts +58 -32
- package/src/filter/filter-match.ts +163 -15
- package/src/metadata/metadata-store.ts +8 -5
- package/src/pipeline/pipeline-stress.test.ts +4 -2
- package/src/pipeline/pipeline.test.ts +3 -2
- package/src/pipeline/pipeline.ts +10 -7
- package/src/query/errors.ts +3 -1
- package/src/query/plan.ts +75 -5
- package/src/query/query-executor.ts +812 -117
- package/src/query/query-planner.test.ts +652 -34
- package/src/query/query-planner.ts +276 -13
- package/src/space/admission-discovery-extension.ts +3 -3
- package/src/space/auth.ts +6 -6
- package/src/space/control-pipeline.test.ts +4 -3
- package/src/space/control-pipeline.ts +11 -8
- package/src/space/space-manager.browser.test.ts +1 -1
- package/src/space/space-manager.ts +11 -10
- package/src/space/space-protocol.browser.test.ts +2 -2
- package/src/space/space-protocol.test.ts +3 -2
- package/src/space/space-protocol.ts +17 -14
- package/src/space/space.test.ts +1 -1
- package/src/space/space.ts +6 -5
- package/src/testing/index.ts +0 -2
- package/src/testing/test-agent-builder.ts +4 -3
- package/src/testing/test-network-adapter.ts +16 -3
- package/src/testing/test-replicator.ts +23 -18
- package/src/util.ts +7 -3
- package/dist/lib/browser/chunk-35I6ERLG.mjs.map +0 -7
- package/dist/lib/browser/chunk-CGS2ULMK.mjs +0 -11
- package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +0 -7
- package/dist/lib/browser/chunk-TQJTKNMS.mjs +0 -126
- package/dist/lib/browser/chunk-TQJTKNMS.mjs.map +0 -7
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node/chunk-HOPOFWAL.cjs +0 -147
- package/dist/lib/node/chunk-HOPOFWAL.cjs.map +0 -7
- package/dist/lib/node/chunk-JXX6LF5U.cjs +0 -2084
- package/dist/lib/node/chunk-JXX6LF5U.cjs.map +0 -7
- package/dist/lib/node/chunk-Q7SFCCGT.cjs +0 -33
- package/dist/lib/node/chunk-Q7SFCCGT.cjs.map +0 -7
- package/dist/lib/node/filter/index.cjs +0 -32
- package/dist/lib/node/filter/index.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -4699
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/testing/index.cjs +0 -753
- package/dist/lib/node/testing/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs +0 -2067
- package/dist/lib/node-esm/chunk-5BHLPT24.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs +0 -126
- package/dist/lib/node-esm/chunk-RVK35BS7.mjs.map +0 -7
- package/dist/lib/node-esm/filter/index.mjs +0 -11
- package/dist/lib/node-esm/filter/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -4743
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -742
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts +0 -7
- package/dist/types/src/db-host/documents-iterator.d.ts.map +0 -1
- package/dist/types/src/testing/test-data.d.ts +0 -18
- package/dist/types/src/testing/test-data.d.ts.map +0 -1
- package/dist/types/src/testing/test-schema.d.ts +0 -37
- package/dist/types/src/testing/test-schema.d.ts.map +0 -1
- package/src/db-host/documents-iterator.ts +0 -74
- package/src/testing/test-data.ts +0 -127
- package/src/testing/test-schema.ts +0 -53
- /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 {
|
|
6
|
-
import {
|
|
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
|
|
26
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
19
|
+
type EdgeReplicationSetting,
|
|
20
20
|
type IdentityRecord,
|
|
21
|
-
type SpaceCache,
|
|
22
21
|
type LargeSpaceMetadata,
|
|
23
|
-
type
|
|
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(
|
|
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,
|
|
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: {},
|
package/src/pipeline/pipeline.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { Event, sleepWithContext, synchronized
|
|
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
|
-
|
|
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
|
|
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
|
-
}:
|
|
135
|
+
}: WaitUntilReachedTargetProps = {}): Promise<void> {
|
|
133
136
|
log('waitUntilReachedTargetTimeframe', {
|
|
134
137
|
timeout,
|
|
135
138
|
current: this.timeframe,
|
package/src/query/errors.ts
CHANGED
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/
|
|
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
|
}
|