@dxos/index-core 0.0.0 → 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 (42) hide show
  1. package/dist/lib/neutral/index.mjs +658 -0
  2. package/dist/lib/neutral/index.mjs.map +7 -0
  3. package/dist/lib/neutral/meta.json +1 -0
  4. package/dist/types/src/index-engine.d.ts +83 -0
  5. package/dist/types/src/index-engine.d.ts.map +1 -0
  6. package/dist/types/src/index-engine.test.d.ts +2 -0
  7. package/dist/types/src/index-engine.test.d.ts.map +1 -0
  8. package/dist/types/src/index-tracker.d.ts +44 -0
  9. package/dist/types/src/index-tracker.d.ts.map +1 -0
  10. package/dist/types/src/index-tracker.test.d.ts +2 -0
  11. package/dist/types/src/index-tracker.test.d.ts.map +1 -0
  12. package/dist/types/src/index.d.ts +8 -0
  13. package/dist/types/src/index.d.ts.map +1 -0
  14. package/dist/types/src/indexes/fts-index.d.ts +63 -0
  15. package/dist/types/src/indexes/fts-index.d.ts.map +1 -0
  16. package/dist/types/src/indexes/fts-index.test.d.ts +2 -0
  17. package/dist/types/src/indexes/fts-index.test.d.ts.map +1 -0
  18. package/dist/types/src/indexes/fts5.test.d.ts +2 -0
  19. package/dist/types/src/indexes/fts5.test.d.ts.map +1 -0
  20. package/dist/types/src/indexes/index.d.ts +5 -0
  21. package/dist/types/src/indexes/index.d.ts.map +1 -0
  22. package/dist/types/src/indexes/interface.d.ts +47 -0
  23. package/dist/types/src/indexes/interface.d.ts.map +1 -0
  24. package/dist/types/src/indexes/object-meta-index.d.ts +60 -0
  25. package/dist/types/src/indexes/object-meta-index.d.ts.map +1 -0
  26. package/dist/types/src/indexes/object-meta-index.test.d.ts +2 -0
  27. package/dist/types/src/indexes/object-meta-index.test.d.ts.map +1 -0
  28. package/dist/types/src/indexes/reverse-ref-index.d.ts +37 -0
  29. package/dist/types/src/indexes/reverse-ref-index.d.ts.map +1 -0
  30. package/dist/types/src/indexes/reverse-ref-index.test.d.ts +2 -0
  31. package/dist/types/src/indexes/reverse-ref-index.test.d.ts.map +1 -0
  32. package/dist/types/src/utils.d.ts +17 -0
  33. package/dist/types/src/utils.d.ts.map +1 -0
  34. package/dist/types/tsconfig.tsbuildinfo +1 -0
  35. package/package.json +22 -18
  36. package/src/index-engine.test.ts +8 -5
  37. package/src/index-engine.ts +53 -9
  38. package/src/index.ts +3 -2
  39. package/src/indexes/fts-index.ts +41 -2
  40. package/src/indexes/object-meta-index.test.ts +179 -0
  41. package/src/indexes/object-meta-index.ts +148 -13
  42. package/src/utils.ts +1 -1
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "@dxos/index-core",
3
- "version": "0.0.0",
3
+ "version": "0.8.4-main.1068cf700f",
4
4
  "description": "Indexing core.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "info@dxos.org",
9
13
  "sideEffects": false,
@@ -12,8 +16,7 @@
12
16
  ".": {
13
17
  "source": "./src/index.ts",
14
18
  "types": "./dist/types/src/index.d.ts",
15
- "browser": "./dist/lib/browser/index.mjs",
16
- "node": "./dist/lib/node-esm/index.mjs"
19
+ "default": "./dist/lib/neutral/index.mjs"
17
20
  }
18
21
  },
19
22
  "files": [
@@ -21,23 +24,24 @@
21
24
  "src"
22
25
  ],
23
26
  "dependencies": {
24
- "@dxos/async": "",
25
- "@dxos/context": "",
26
- "@dxos/debug": "",
27
- "@dxos/echo": "",
28
- "@dxos/echo-protocol": "",
29
- "@dxos/effect": "",
30
- "@dxos/invariant": "",
31
- "@dxos/keys": "",
32
- "@dxos/log": "",
33
- "@effect/cli": "0.72.1",
34
- "@effect/experimental": "0.57.11",
35
- "@effect/platform": "0.93.6",
36
- "@effect/sql": "0.48.6",
37
- "effect": "3.19.11"
27
+ "@effect/cli": "0.73.2",
28
+ "@effect/experimental": "0.58.0",
29
+ "@effect/platform": "0.94.4",
30
+ "@effect/sql": "0.49.0",
31
+ "effect": "3.19.16",
32
+ "@dxos/context": "0.8.4-main.1068cf700f",
33
+ "@dxos/async": "0.8.4-main.1068cf700f",
34
+ "@dxos/echo": "0.8.4-main.1068cf700f",
35
+ "@dxos/debug": "0.8.4-main.1068cf700f",
36
+ "@dxos/effect": "0.8.4-main.1068cf700f",
37
+ "@dxos/echo-protocol": "0.8.4-main.1068cf700f",
38
+ "@dxos/invariant": "0.8.4-main.1068cf700f",
39
+ "@dxos/log": "0.8.4-main.1068cf700f",
40
+ "@dxos/sql-sqlite": "0.8.4-main.1068cf700f",
41
+ "@dxos/keys": "0.8.4-main.1068cf700f"
38
42
  },
39
43
  "devDependencies": {
40
- "@effect/sql-sqlite-node": "0.49.1"
44
+ "@effect/sql-sqlite-node": "0.50.1"
41
45
  },
42
46
  "publishConfig": {
43
47
  "access": "public"
@@ -11,6 +11,7 @@ import * as Layer from 'effect/Layer';
11
11
  import { ATTR_TYPE } from '@dxos/echo/internal';
12
12
  import { invariant } from '@dxos/invariant';
13
13
  import { DXN, ObjectId, SpaceId } from '@dxos/keys';
14
+ import * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
14
15
 
15
16
  import { type DataSourceCursor, type IndexDataSource, IndexEngine } from './index-engine';
16
17
  import { type IndexCursor, IndexTracker } from './index-tracker';
@@ -20,11 +21,13 @@ const TYPE_DEFAULT = DXN.parse('dxn:type:test.com/type/Type:0.1.0').toString();
20
21
  const TYPE_A = DXN.parse('dxn:type:test.com/type/TypeA:0.1.0').toString();
21
22
  const TYPE_B = DXN.parse('dxn:type:test.com/type/TypeB:0.1.0').toString();
22
23
 
23
- const TestLayer = Layer.merge(
24
- SqliteClient.layer({
25
- filename: ':memory:',
26
- }),
27
- Reactivity.layer,
24
+ const TestLayer = SqlTransaction.layer.pipe(
25
+ Layer.provideMerge(
26
+ SqliteClient.layer({
27
+ filename: ':memory:',
28
+ }),
29
+ ),
30
+ Layer.provideMerge(Reactivity.layer),
28
31
  );
29
32
 
30
33
  class MockIndexDataSource implements IndexDataSource {
@@ -2,16 +2,18 @@
2
2
  // Copyright 2026 DXOS.org
3
3
  //
4
4
 
5
- import * as SqlClient from '@effect/sql/SqlClient';
5
+ import type * as SqlClient from '@effect/sql/SqlClient';
6
6
  import type * as SqlError from '@effect/sql/SqlError';
7
7
  import * as Effect from 'effect/Effect';
8
8
 
9
- import type { SpaceId } from '@dxos/keys';
9
+ import type { ObjectId, SpaceId } from '@dxos/keys';
10
+ import * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';
10
11
 
11
12
  import { type IndexCursor, IndexTracker } from './index-tracker';
12
13
  import {
13
14
  FtsIndex,
14
15
  type FtsQuery,
16
+ type FtsQueryResult,
15
17
  type Index,
16
18
  type IndexerObject,
17
19
  type ObjectMeta,
@@ -76,9 +78,9 @@ export class IndexEngine {
76
78
  }
77
79
 
78
80
  /**
79
- * Query text index and return full object metadata.
81
+ * Query text index and return full object metadata with rank.
80
82
  */
81
- queryText(query: FtsQuery): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
83
+ queryText(query: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {
82
84
  return Effect.gen(this, function* () {
83
85
  return yield* this.#ftsIndex.query(query);
84
86
  });
@@ -89,6 +91,12 @@ export class IndexEngine {
89
91
  return this.#reverseRefIndex.query(query);
90
92
  }
91
93
 
94
+ queryAll(query: {
95
+ spaceIds: readonly SpaceId[];
96
+ }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
97
+ return this.#objectMetaIndex.queryAll(query);
98
+ }
99
+
92
100
  /**
93
101
  * Query snapshots by recordIds.
94
102
  * Used to load queue objects from indexed snapshots.
@@ -103,10 +111,41 @@ export class IndexEngine {
103
111
  return this.#objectMetaIndex.query(query);
104
112
  }
105
113
 
114
+ /**
115
+ * Query children by parent object ids.
116
+ */
117
+ queryChildren(query: {
118
+ spaceId: SpaceId[];
119
+ parentIds: ObjectId[];
120
+ }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
121
+ return this.#objectMetaIndex.queryChildren(query);
122
+ }
123
+
124
+ queryTypes(query: {
125
+ spaceIds: readonly SpaceId[];
126
+ typeDxns: readonly ObjectMeta['typeDxn'][];
127
+ inverted?: boolean;
128
+ }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
129
+ return this.#objectMetaIndex.queryTypes(query);
130
+ }
131
+ queryRelations(query: {
132
+ endpoint: 'source' | 'target';
133
+ anchorDxns: readonly string[];
134
+ }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
135
+ return this.#objectMetaIndex.queryRelations(query);
136
+ }
137
+ lookupByRecordIds(recordIds: number[]): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
138
+ return this.#objectMetaIndex.lookupByRecordIds(recordIds);
139
+ }
140
+
106
141
  update(
107
142
  dataSource: IndexDataSource,
108
143
  opts: { spaceId: SpaceId | null; limit?: number },
109
- ): Effect.Effect<{ updated: number; done: boolean }, SqlError.SqlError, SqlClient.SqlClient> {
144
+ ): Effect.Effect<
145
+ { updated: number; done: boolean },
146
+ SqlError.SqlError,
147
+ SqlTransaction.SqlTransaction | SqlClient.SqlClient
148
+ > {
110
149
  return Effect.gen(this, function* () {
111
150
  let updated = 0;
112
151
 
@@ -145,10 +184,15 @@ export class IndexEngine {
145
184
  index: Index,
146
185
  source: IndexDataSource,
147
186
  opts: { indexName: string; spaceId: SpaceId | null; limit?: number },
148
- ): Effect.Effect<{ updated: number; done: boolean }, SqlError.SqlError, SqlClient.SqlClient> {
187
+ ): Effect.Effect<
188
+ { updated: number; done: boolean },
189
+ SqlError.SqlError,
190
+ SqlTransaction.SqlTransaction | SqlClient.SqlClient
191
+ > {
149
192
  return Effect.gen(this, function* () {
150
- const sql = yield* SqlClient.SqlClient;
151
- return yield* sql.withTransaction(
193
+ const sqlTransaction = yield* SqlTransaction.SqlTransaction;
194
+
195
+ return yield* sqlTransaction.withTransaction(
152
196
  Effect.gen(this, function* () {
153
197
  const cursors = yield* this.#tracker.queryCursors({
154
198
  indexName: opts.indexName,
@@ -182,6 +226,6 @@ export class IndexEngine {
182
226
  return { updated: objects.length, done: false };
183
227
  }),
184
228
  );
185
- }).pipe(Effect.withSpan('IndexEngine.#updateDependentIndex'));
229
+ }).pipe(Effect.withSpan('IndexEngine.#update'));
186
230
  }
187
231
  }
package/src/index.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  export { IndexEngine, type IndexDataSource, type DataSourceCursor, type IndexEngineParams } from './index-engine';
6
6
  export { IndexTracker, type IndexCursor } from './index-tracker';
7
7
  export { type IndexerObject, type Index } from './indexes/interface';
8
- export { FtsIndex } from './indexes/fts-index';
8
+ export { FtsIndex, type FtsQuery } from './indexes/fts-index';
9
9
  export { ObjectMetaIndex, type ObjectMeta } from './indexes/object-meta-index';
10
- export { ReverseRefIndex, type ReverseRef } from './indexes/reverse-ref-index';
10
+ export { ReverseRefIndex, type ReverseRef, type ReverseRefQuery } from './indexes/reverse-ref-index';
11
+ export { EscapedPropPath, type ObjectPropPath } from './utils';
@@ -49,6 +49,18 @@ export interface FtsResult extends ObjectMeta {
49
49
  snapshot: string;
50
50
  }
51
51
 
52
+ /**
53
+ * Result of FTS query with rank.
54
+ */
55
+ export interface FtsQueryResult extends ObjectMeta {
56
+ /**
57
+ * Relevance rank from FTS5.
58
+ * Higher values indicate better matches.
59
+ * Uses BM25 algorithm when available, falls back to 1 for non-BM25 queries.
60
+ */
61
+ rank: number;
62
+ }
63
+
52
64
  /**
53
65
  * Escapes user input for safe FTS5 queries.
54
66
  *
@@ -92,7 +104,7 @@ export class FtsIndex implements Index {
92
104
  spaceId,
93
105
  includeAllQueues,
94
106
  queueIds,
95
- }: FtsQuery): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {
107
+ }: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {
96
108
  return Effect.gen(function* () {
97
109
  const trimmed = query.trim();
98
110
  if (trimmed.length === 0) {
@@ -106,6 +118,11 @@ export class FtsIndex implements Index {
106
118
  const terms = trimmed.split(/\s+/).filter(Boolean);
107
119
  const minTermLength = Math.min(...terms.map((t) => t.length));
108
120
 
121
+ // Use BM25 ranking for FTS5 MATCH queries, fall back to rank 1 for LIKE queries.
122
+ // BM25 returns negative values where lower (more negative) means better match,
123
+ // so we negate it to get higher = better.
124
+ const useBm25 = minTermLength >= 3;
125
+
109
126
  const conditions =
110
127
  minTermLength < 3
111
128
  ? // LIKE fallback - scan the entire table, AND all terms.
@@ -135,7 +152,29 @@ export class FtsIndex implements Index {
135
152
  conditions.push(sql`(${sql.or(sourceConditions)})`);
136
153
  }
137
154
 
138
- return yield* sql<ObjectMeta>`SELECT m.* FROM ftsIndex AS f JOIN objectMeta AS m ON f.rowid = m.recordId WHERE ${sql.and(conditions)}`;
155
+ if (useBm25) {
156
+ // Use BM25 ranking for FTS5 MATCH queries.
157
+ // BM25 returns negative values, negate to get higher = better match.
158
+ // Order by rank descending so best matches come first.
159
+ // Note: bm25() requires the actual table name, not an alias.
160
+ const rows = yield* sql<ObjectMeta & { rank: number }>`
161
+ SELECT m.*, -bm25(ftsIndex) AS rank
162
+ FROM ftsIndex AS f
163
+ JOIN objectMeta AS m ON f.rowid = m.recordId
164
+ WHERE ${sql.and(conditions)}
165
+ ORDER BY rank DESC
166
+ `;
167
+ return rows;
168
+ } else {
169
+ // LIKE fallback - no ranking available, default to 1.
170
+ const rows = yield* sql<ObjectMeta>`
171
+ SELECT m.*
172
+ FROM ftsIndex AS f
173
+ JOIN objectMeta AS m ON f.rowid = m.recordId
174
+ WHERE ${sql.and(conditions)}
175
+ `;
176
+ return rows.map((row) => ({ ...row, rank: 1 }));
177
+ }
139
178
  });
140
179
  }
141
180
 
@@ -15,8 +15,12 @@ import type { IndexerObject } from './interface';
15
15
  import { ObjectMetaIndex } from './object-meta-index';
16
16
 
17
17
  const TYPE_PERSON = DXN.parse('dxn:type:example.com/type/Person:0.1.0').toString();
18
+ const TYPE_PERSON_VERSIONLESS = DXN.parse('dxn:type:example.com/type/Person').toString();
18
19
  const TYPE_RELATION = DXN.parse('dxn:type:example.com/type/Relation:0.1.0').toString();
19
20
  const TYPE_RELATION_UPDATED = DXN.parse('dxn:type:example.com/type/RelationUpdated:0.1.0').toString();
21
+ const TYPE_WITH_UNDERSCORE = DXN.parse('dxn:type:example.com/type/Person_Extra:0.1.0').toString();
22
+ const TYPE_WITH_UNDERSCORE_VERSIONLESS = DXN.parse('dxn:type:example.com/type/Person_Extra').toString();
23
+ const TYPE_UNDERSCORE_FALSE_POSITIVE = DXN.parse('dxn:type:example.com/type/PersonAExtra:0.1.0').toString();
20
24
 
21
25
  const TestLayer = Layer.merge(
22
26
  SqliteClient.layer({
@@ -26,6 +30,86 @@ const TestLayer = Layer.merge(
26
30
  );
27
31
 
28
32
  describe('ObjectMetaIndex', () => {
33
+ it.effect('should match versioned types when queried by versionless type', () =>
34
+ Effect.gen(function* () {
35
+ const index = new ObjectMetaIndex();
36
+ yield* index.migrate();
37
+
38
+ const spaceId = SpaceId.random();
39
+ const objectId = ObjectId.random();
40
+
41
+ const item: IndexerObject = {
42
+ spaceId,
43
+ queueId: ObjectId.random(),
44
+ documentId: null,
45
+ recordId: null,
46
+ data: {
47
+ id: objectId,
48
+ [ATTR_TYPE]: TYPE_PERSON,
49
+ [ATTR_DELETED]: false,
50
+ },
51
+ };
52
+
53
+ yield* index.update([item]);
54
+
55
+ const results = yield* index.query({ spaceId, typeDxn: TYPE_PERSON_VERSIONLESS });
56
+ expect(results.map((_) => _.objectId)).toEqual([objectId]);
57
+
58
+ const otherTypeResults = yield* index.query({
59
+ spaceId,
60
+ typeDxn: DXN.parse('dxn:type:example.com/type/Other').toString(),
61
+ });
62
+ expect(otherTypeResults).toEqual([]);
63
+ }).pipe(Effect.provide(TestLayer)),
64
+ );
65
+
66
+ it.effect('should not treat LIKE wildcards in versionless type queries', () =>
67
+ Effect.gen(function* () {
68
+ const index = new ObjectMetaIndex();
69
+ yield* index.migrate();
70
+
71
+ const spaceId = SpaceId.random();
72
+ const objectIdMatch = ObjectId.random();
73
+ const objectIdFalsePositive = ObjectId.random();
74
+
75
+ const match: IndexerObject = {
76
+ spaceId,
77
+ queueId: ObjectId.random(),
78
+ documentId: null,
79
+ recordId: null,
80
+ data: {
81
+ id: objectIdMatch,
82
+ [ATTR_TYPE]: TYPE_WITH_UNDERSCORE,
83
+ [ATTR_DELETED]: false,
84
+ },
85
+ };
86
+
87
+ // Would match prior implementation because '_' is a LIKE wildcard.
88
+ const falsePositive: IndexerObject = {
89
+ spaceId,
90
+ queueId: ObjectId.random(),
91
+ documentId: null,
92
+ recordId: null,
93
+ data: {
94
+ id: objectIdFalsePositive,
95
+ [ATTR_TYPE]: TYPE_UNDERSCORE_FALSE_POSITIVE,
96
+ [ATTR_DELETED]: false,
97
+ },
98
+ };
99
+
100
+ yield* index.update([match, falsePositive]);
101
+
102
+ const queryResults = yield* index.query({ spaceId, typeDxn: TYPE_WITH_UNDERSCORE_VERSIONLESS });
103
+ expect(queryResults.map((_) => _.objectId)).toEqual([objectIdMatch]);
104
+
105
+ const queryTypesResults = yield* index.queryTypes({
106
+ spaceIds: [spaceId],
107
+ typeDxns: [TYPE_WITH_UNDERSCORE_VERSIONLESS],
108
+ });
109
+ expect(queryTypesResults.map((_) => _.objectId)).toEqual([objectIdMatch]);
110
+ }).pipe(Effect.provide(TestLayer)),
111
+ );
112
+
29
113
  it.effect('should store and update object metadata', () =>
30
114
  Effect.gen(function* () {
31
115
  const index = new ObjectMetaIndex();
@@ -116,4 +200,99 @@ describe('ObjectMetaIndex', () => {
116
200
  expect(oldTypeResults.length).toBe(0);
117
201
  }).pipe(Effect.provide(TestLayer)),
118
202
  );
203
+
204
+ it.effect('should support queryAll/queryTypes/queryRelations', () =>
205
+ Effect.gen(function* () {
206
+ const index = new ObjectMetaIndex();
207
+ yield* index.migrate();
208
+
209
+ const spaceId = SpaceId.random();
210
+ const objectId1 = ObjectId.random();
211
+ const objectId2 = ObjectId.random();
212
+ const relationId = ObjectId.random();
213
+
214
+ const item1: IndexerObject = {
215
+ spaceId,
216
+ queueId: ObjectId.random(),
217
+ documentId: null,
218
+ recordId: null,
219
+ data: {
220
+ id: objectId1,
221
+ [ATTR_TYPE]: TYPE_PERSON,
222
+ [ATTR_DELETED]: false,
223
+ },
224
+ };
225
+
226
+ const item2: IndexerObject = {
227
+ spaceId,
228
+ queueId: ObjectId.random(),
229
+ documentId: null,
230
+ recordId: null,
231
+ data: {
232
+ id: objectId2,
233
+ [ATTR_TYPE]: TYPE_RELATION,
234
+ [ATTR_DELETED]: false,
235
+ },
236
+ };
237
+
238
+ const relation: IndexerObject = {
239
+ spaceId,
240
+ queueId: ObjectId.random(),
241
+ documentId: null,
242
+ recordId: null,
243
+ data: {
244
+ id: relationId,
245
+ [ATTR_TYPE]: TYPE_RELATION,
246
+ [ATTR_RELATION_SOURCE]: DXN.fromLocalObjectId(objectId1).toString(),
247
+ [ATTR_RELATION_TARGET]: DXN.fromLocalObjectId(objectId2).toString(),
248
+ [ATTR_DELETED]: false,
249
+ },
250
+ };
251
+
252
+ yield* index.update([item1, item2, relation]);
253
+
254
+ const all = yield* index.queryAll({ spaceIds: [spaceId] });
255
+ expect(all.map((_) => _.objectId).sort()).toEqual([objectId1, objectId2, relationId].sort());
256
+
257
+ const types = yield* index.queryTypes({ spaceIds: [spaceId], typeDxns: [TYPE_PERSON, TYPE_RELATION] });
258
+ expect(types.map((_) => _.objectId).sort()).toEqual([objectId1, objectId2, relationId].sort());
259
+
260
+ const onlyPerson = yield* index.queryTypes({ spaceIds: [spaceId], typeDxns: [TYPE_PERSON] });
261
+ expect(onlyPerson.map((_) => _.objectId)).toEqual([objectId1]);
262
+
263
+ const onlyPersonVersionless = yield* index.queryTypes({
264
+ spaceIds: [spaceId],
265
+ typeDxns: [TYPE_PERSON_VERSIONLESS],
266
+ });
267
+ expect(onlyPersonVersionless.map((_) => _.objectId)).toEqual([objectId1]);
268
+
269
+ const notPerson = yield* index.queryTypes({ spaceIds: [spaceId], typeDxns: [TYPE_PERSON], inverted: true });
270
+ expect(notPerson.map((_) => _.objectId).sort()).toEqual([objectId2, relationId].sort());
271
+
272
+ const notPersonVersionless = yield* index.queryTypes({
273
+ spaceIds: [spaceId],
274
+ typeDxns: [TYPE_PERSON_VERSIONLESS],
275
+ inverted: true,
276
+ });
277
+ expect(notPersonVersionless.map((_) => _.objectId).sort()).toEqual([objectId2, relationId].sort());
278
+
279
+ const emptyTypes = yield* index.queryTypes({ spaceIds: [spaceId], typeDxns: [] });
280
+ expect(emptyTypes).toEqual([]);
281
+
282
+ const notEmptyTypes = yield* index.queryTypes({ spaceIds: [spaceId], typeDxns: [], inverted: true });
283
+ expect(notEmptyTypes.map((_) => _.objectId).sort()).toEqual([objectId1, objectId2, relationId].sort());
284
+
285
+ const bySource = yield* index.queryRelations({
286
+ endpoint: 'source',
287
+ anchorDxns: [DXN.fromLocalObjectId(objectId1).toString()],
288
+ });
289
+ expect(bySource.map((_) => _.objectId)).toEqual([relationId]);
290
+
291
+ const byTarget = yield* index.queryRelations({
292
+ endpoint: 'target',
293
+ anchorDxns: [DXN.fromLocalObjectId(objectId2).toString()],
294
+ });
295
+ expect(byTarget.map((_) => _.objectId)).toEqual([relationId]);
296
+ }).pipe(Effect.provide(TestLayer)),
297
+ );
119
298
  });