@dxos/index-core 0.8.4-main.59c2e9b → 0.8.4-main.937b3ca
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/browser/index.mjs +74 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +74 -3
- package/dist/lib/node-esm/index.mjs.map +3 -3
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/index-engine.d.ts +13 -0
- package/dist/types/src/index-engine.d.ts.map +1 -1
- package/dist/types/src/indexes/object-meta-index.d.ts +13 -0
- package/dist/types/src/indexes/object-meta-index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/index-engine.ts +25 -0
- package/src/indexes/object-meta-index.test.ts +179 -0
- package/src/indexes/object-meta-index.ts +108 -7
|
@@ -181,6 +181,11 @@ import * as SqlClient5 from "@effect/sql/SqlClient";
|
|
|
181
181
|
import * as Effect3 from "effect/Effect";
|
|
182
182
|
import * as Schema2 from "effect/Schema";
|
|
183
183
|
import { ATTR_DELETED, ATTR_RELATION_SOURCE, ATTR_RELATION_TARGET, ATTR_TYPE } from "@dxos/echo/internal";
|
|
184
|
+
import { DXN } from "@dxos/keys";
|
|
185
|
+
var _escapeLikePrefix = (prefix) => {
|
|
186
|
+
const escaped = prefix.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
187
|
+
return `${escaped}:%`;
|
|
188
|
+
};
|
|
184
189
|
var ObjectMeta = Schema2.Struct({
|
|
185
190
|
recordId: Schema2.Number,
|
|
186
191
|
objectId: Schema2.String,
|
|
@@ -188,6 +193,7 @@ var ObjectMeta = Schema2.Struct({
|
|
|
188
193
|
spaceId: Schema2.String,
|
|
189
194
|
documentId: Schema2.String,
|
|
190
195
|
entityKind: Schema2.String,
|
|
196
|
+
/** The versioned DXN of the type of the object. */
|
|
191
197
|
typeDxn: Schema2.String,
|
|
192
198
|
deleted: Schema2.Boolean,
|
|
193
199
|
source: Schema2.NullOr(Schema2.String),
|
|
@@ -217,7 +223,61 @@ var ObjectMetaIndex = class {
|
|
|
217
223
|
});
|
|
218
224
|
query = Effect3.fn("ObjectMetaIndex.queryType")((query) => Effect3.gen(function* () {
|
|
219
225
|
const sql = yield* SqlClient5.SqlClient;
|
|
220
|
-
const
|
|
226
|
+
const parsedType = DXN.tryParse(query.typeDxn)?.asTypeDXN();
|
|
227
|
+
const rows = parsedType && parsedType.version === void 0 ? yield* sql`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND (typeDxn = ${query.typeDxn} OR typeDxn LIKE ${_escapeLikePrefix(query.typeDxn)} ESCAPE '\\')` : yield* sql`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND typeDxn = ${query.typeDxn}`;
|
|
228
|
+
return rows.map((row) => ({
|
|
229
|
+
...row,
|
|
230
|
+
deleted: !!row.deleted
|
|
231
|
+
}));
|
|
232
|
+
}));
|
|
233
|
+
queryAll = Effect3.fn("ObjectMetaIndex.queryAll")((query) => Effect3.gen(function* () {
|
|
234
|
+
if (query.spaceIds.length === 0) {
|
|
235
|
+
return [];
|
|
236
|
+
}
|
|
237
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
238
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE ${sql.in("spaceId", query.spaceIds)}`;
|
|
239
|
+
return rows.map((row) => ({
|
|
240
|
+
...row,
|
|
241
|
+
deleted: !!row.deleted
|
|
242
|
+
}));
|
|
243
|
+
}));
|
|
244
|
+
queryTypes = Effect3.fn("ObjectMetaIndex.queryTypes")(({ spaceIds, typeDxns, inverted = false }) => Effect3.gen(function* () {
|
|
245
|
+
if (spaceIds.length === 0) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
if (typeDxns.length === 0) {
|
|
249
|
+
if (!inverted) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
const sql2 = yield* SqlClient5.SqlClient;
|
|
253
|
+
const rows2 = yield* sql2`SELECT * FROM objectMeta WHERE ${sql2.in("spaceId", spaceIds)}`;
|
|
254
|
+
return rows2.map((row) => ({
|
|
255
|
+
...row,
|
|
256
|
+
deleted: !!row.deleted
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
260
|
+
const spaceWhere = sql.in("spaceId", spaceIds);
|
|
261
|
+
const typeWhere = sql.or(typeDxns.map((typeDxn) => {
|
|
262
|
+
const parsedType = DXN.tryParse(typeDxn)?.asTypeDXN();
|
|
263
|
+
return parsedType && parsedType.version === void 0 ? sql.or([
|
|
264
|
+
sql`typeDxn = ${typeDxn}`,
|
|
265
|
+
sql`typeDxn LIKE ${_escapeLikePrefix(typeDxn)} ESCAPE '\\'`
|
|
266
|
+
]) : sql`typeDxn = ${typeDxn}`;
|
|
267
|
+
}));
|
|
268
|
+
const rows = inverted ? yield* sql`SELECT * FROM objectMeta WHERE ${spaceWhere} AND NOT ${typeWhere}` : yield* sql`SELECT * FROM objectMeta WHERE ${spaceWhere} AND ${typeWhere}`;
|
|
269
|
+
return rows.map((row) => ({
|
|
270
|
+
...row,
|
|
271
|
+
deleted: !!row.deleted
|
|
272
|
+
}));
|
|
273
|
+
}));
|
|
274
|
+
queryRelations = Effect3.fn("ObjectMetaIndex.queryRelations")(({ endpoint, anchorDxns }) => Effect3.gen(function* () {
|
|
275
|
+
if (anchorDxns.length === 0) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
279
|
+
const column = endpoint === "source" ? "source" : "target";
|
|
280
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE entityKind = 'relation' AND ${sql.in(column, anchorDxns)}`;
|
|
221
281
|
return rows.map((row) => ({
|
|
222
282
|
...row,
|
|
223
283
|
deleted: !!row.deleted
|
|
@@ -304,8 +364,7 @@ var ObjectMetaIndex = class {
|
|
|
304
364
|
return [];
|
|
305
365
|
}
|
|
306
366
|
const sql = yield* SqlClient5.SqlClient;
|
|
307
|
-
const
|
|
308
|
-
const rows = yield* sql.unsafe(`SELECT * FROM objectMeta WHERE recordId IN (${placeholders})`, recordIds);
|
|
367
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE ${sql.in("recordId", recordIds)}`;
|
|
309
368
|
return rows.map((row) => ({
|
|
310
369
|
...row,
|
|
311
370
|
deleted: !!row.deleted
|
|
@@ -472,6 +531,9 @@ var IndexEngine = class {
|
|
|
472
531
|
queryReverseRef(query) {
|
|
473
532
|
return this.#reverseRefIndex.query(query);
|
|
474
533
|
}
|
|
534
|
+
queryAll(query) {
|
|
535
|
+
return this.#objectMetaIndex.queryAll(query);
|
|
536
|
+
}
|
|
475
537
|
/**
|
|
476
538
|
* Query snapshots by recordIds.
|
|
477
539
|
* Used to load queue objects from indexed snapshots.
|
|
@@ -482,6 +544,15 @@ var IndexEngine = class {
|
|
|
482
544
|
queryType(query) {
|
|
483
545
|
return this.#objectMetaIndex.query(query);
|
|
484
546
|
}
|
|
547
|
+
queryTypes(query) {
|
|
548
|
+
return this.#objectMetaIndex.queryTypes(query);
|
|
549
|
+
}
|
|
550
|
+
queryRelations(query) {
|
|
551
|
+
return this.#objectMetaIndex.queryRelations(query);
|
|
552
|
+
}
|
|
553
|
+
lookupByRecordIds(recordIds) {
|
|
554
|
+
return this.#objectMetaIndex.lookupByRecordIds(recordIds);
|
|
555
|
+
}
|
|
485
556
|
update(dataSource, opts) {
|
|
486
557
|
return Effect5.gen(this, function* () {
|
|
487
558
|
let updated = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/index-engine.ts", "../../../src/index-tracker.ts", "../../../src/indexes/fts-index.ts", "../../../src/indexes/object-meta-index.ts", "../../../src/indexes/reverse-ref-index.ts", "../../../src/utils.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\n\nimport type { SpaceId } from '@dxos/keys';\nimport * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';\n\nimport { type IndexCursor, IndexTracker } from './index-tracker';\nimport {\n FtsIndex,\n type FtsQuery,\n type FtsQueryResult,\n type Index,\n type IndexerObject,\n type ObjectMeta,\n ObjectMetaIndex,\n ReverseRefIndex,\n type ReverseRefQuery,\n} from './indexes';\n\n/**\n * Cursor into indexable data-source.\n */\nexport interface DataSourceCursor {\n spaceId: SpaceId | null;\n\n /**\n * documentId or queueId.\n */\n resourceId: string | null;\n\n /**\n * heads or queue position.\n */\n cursor: number | string;\n}\n\nexport interface IndexDataSource {\n readonly sourceName: string; // e.g. queue, automerge, etc.\n\n getChangedObjects(\n cursors: DataSourceCursor[],\n opts?: { limit?: number },\n ): Effect.Effect<{ objects: IndexerObject[]; cursors: DataSourceCursor[] }>;\n}\n\nexport interface IndexEngineParams {\n tracker: IndexTracker;\n objectMetaIndex: ObjectMetaIndex;\n ftsIndex: FtsIndex;\n reverseRefIndex: ReverseRefIndex;\n}\n\nexport class IndexEngine {\n readonly #tracker: IndexTracker;\n readonly #objectMetaIndex: ObjectMetaIndex;\n readonly #ftsIndex: FtsIndex;\n readonly #reverseRefIndex: ReverseRefIndex;\n\n constructor(params?: IndexEngineParams) {\n this.#tracker = params?.tracker ?? new IndexTracker();\n this.#objectMetaIndex = params?.objectMetaIndex ?? new ObjectMetaIndex();\n this.#ftsIndex = params?.ftsIndex ?? new FtsIndex();\n this.#reverseRefIndex = params?.reverseRefIndex ?? new ReverseRefIndex();\n }\n\n migrate() {\n return Effect.gen(this, function* () {\n yield* this.#tracker.migrate();\n yield* this.#objectMetaIndex.migrate();\n yield* this.#ftsIndex.migrate();\n yield* this.#reverseRefIndex.migrate();\n });\n }\n\n /**\n * Query text index and return full object metadata with rank.\n */\n queryText(query: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(this, function* () {\n return yield* this.#ftsIndex.query(query);\n });\n }\n\n queryReverseRef(query: ReverseRefQuery) {\n // TODO(mykola): Join with metadata table here.\n return this.#reverseRefIndex.query(query);\n }\n\n /**\n * Query snapshots by recordIds.\n * Used to load queue objects from indexed snapshots.\n */\n querySnapshotsJSON(recordIds: number[]) {\n return this.#ftsIndex.querySnapshotsJSON(recordIds);\n }\n\n queryType(\n query: Pick<ObjectMeta, 'spaceId' | 'typeDxn'>,\n ): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.query(query);\n }\n\n update(\n dataSource: IndexDataSource,\n opts: { spaceId: SpaceId | null; limit?: number },\n ): Effect.Effect<\n { updated: number; done: boolean },\n SqlError.SqlError,\n SqlTransaction.SqlTransaction | SqlClient.SqlClient\n > {\n return Effect.gen(this, function* () {\n let updated = 0;\n\n const { updated: updatedFtsIndex, done: doneFtsIndex } = yield* this.#update(this.#ftsIndex, dataSource, {\n indexName: 'fts',\n spaceId: opts.spaceId,\n limit: opts.limit,\n });\n updated += updatedFtsIndex;\n\n const { updated: updatedReverseRefIndex, done: doneReverseRefIndex } = yield* this.#update(\n this.#reverseRefIndex,\n dataSource,\n {\n indexName: 'reverseRef',\n spaceId: opts.spaceId,\n limit: opts.limit,\n },\n );\n updated += updatedReverseRefIndex;\n\n return { updated, done: doneFtsIndex && doneReverseRefIndex };\n }).pipe(Effect.withSpan('IndexEngine.update'));\n }\n\n /**\n * Update a dependent index that requires recordId enrichment.\n * This method:\n * 1. Gets changed objects from the source.\n * 2. Ensures those objects exist in ObjectMetaIndex.\n * 3. Looks up recordIds for those objects.\n * 4. Enriches objects with recordIds.\n * 5. Updates the dependent index.\n */\n #update(\n index: Index,\n source: IndexDataSource,\n opts: { indexName: string; spaceId: SpaceId | null; limit?: number },\n ): Effect.Effect<\n { updated: number; done: boolean },\n SqlError.SqlError,\n SqlTransaction.SqlTransaction | SqlClient.SqlClient\n > {\n return Effect.gen(this, function* () {\n const sqlTransaction = yield* SqlTransaction.SqlTransaction;\n\n return yield* sqlTransaction.withTransaction(\n Effect.gen(this, function* () {\n const cursors = yield* this.#tracker.queryCursors({\n indexName: opts.indexName,\n sourceName: source.sourceName,\n // Pass undefined to get all cursors when spaceId is null.\n spaceId: opts.spaceId ?? undefined,\n });\n const { objects, cursors: updatedCursors } = yield* source.getChangedObjects(cursors, { limit: opts.limit });\n if (objects.length === 0) {\n return { updated: 0, done: true };\n }\n\n // Ensure objects exist in ObjectMetaIndex.\n yield* this.#objectMetaIndex.update(objects);\n\n // Look up recordIds for the objects.\n yield* this.#objectMetaIndex.lookupRecordIds(objects);\n\n yield* index.update(objects);\n yield* this.#tracker.updateCursors(\n updatedCursors.map(\n (_): IndexCursor => ({\n indexName: opts.indexName,\n spaceId: _.spaceId,\n sourceName: source.sourceName,\n resourceId: _.resourceId,\n cursor: _.cursor,\n }),\n ),\n );\n return { updated: objects.length, done: false };\n }),\n );\n }).pipe(Effect.withSpan('IndexEngine.#updateDependentIndex'));\n }\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { SpaceId } from '@dxos/keys';\n\nexport const IndexCursor = Schema.Struct({\n /**\n * Name of the index owning this cursor.\n */\n indexName: Schema.String,\n /**\n * Space id.\n */\n spaceId: Schema.NullOr(SpaceId),\n /**\n * Source name.\n * 'automerge' / 'queue' / 'index' (for secondary indexes)\n */\n sourceName: Schema.String,\n /**\n * Document id or queue id.\n * doc_id, queue_id, '' <empty string> (if indexing entire namespace)\n */\n resourceId: Schema.NullOr(Schema.String),\n /**\n * Heads, queue position, version.\n */\n cursor: Schema.Union(Schema.Number, Schema.String),\n});\nexport interface IndexCursor extends Schema.Schema.Type<typeof IndexCursor> {}\n\nexport class IndexTracker {\n migrate = Effect.fn('IndexTracker.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n // For automerge: last-indexed heads of the document\n // For queue: the position of the item that was indexed last\n yield* sql`CREATE TABLE IF NOT EXISTS indexCursor (\n indexName TEXT NOT NULL,\n spaceId TEXT NOT NULL DEFAULT '',\n sourceName TEXT NOT NULL,\n resourceId TEXT NOT NULL DEFAULT '',\n cursor,\n PRIMARY KEY (indexName, spaceId, sourceName, resourceId)\n )`;\n });\n\n queryCursors = Effect.fn('IndexTracker.queryCursors')(\n (\n query: Pick<IndexCursor, 'indexName'> & Partial<Pick<IndexCursor, 'sourceName' | 'resourceId' | 'spaceId'>>,\n ): Effect.Effect<IndexCursor[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n const spaceIdParam = query.spaceId === undefined ? null : (query.spaceId ?? '');\n const sourceNameParam = query.sourceName === undefined ? null : query.sourceName;\n const resourceIdParam = query.resourceId === undefined ? null : (query.resourceId ?? '');\n\n const rows = yield* sql<IndexCursor>`\n SELECT * FROM indexCursor \n WHERE indexName = ${query.indexName}\n AND (${spaceIdParam} IS NULL OR spaceId = ${spaceIdParam})\n AND (${sourceNameParam} IS NULL OR sourceName = ${sourceNameParam})\n AND (${resourceIdParam} IS NULL OR resourceId = ${resourceIdParam})\n `;\n\n return rows.map(\n (row): IndexCursor => ({\n indexName: row.indexName,\n spaceId: row.spaceId === '' ? null : Schema.decodeSync(SpaceId)(row.spaceId!),\n sourceName: row.sourceName,\n resourceId: row.resourceId === '' ? null : row.resourceId,\n cursor: row.cursor,\n }),\n );\n }),\n );\n\n updateCursors = Effect.fn('IndexTracker.updateCursors')(\n (cursors: IndexCursor[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n yield* Effect.forEach(\n cursors,\n (cursor) => {\n const spaceId = cursor.spaceId ?? '';\n const resourceId = cursor.resourceId ?? '';\n return sql`\n INSERT INTO indexCursor (indexName, spaceId, sourceName, resourceId, cursor)\n VALUES (${cursor.indexName}, ${spaceId}, ${cursor.sourceName}, ${resourceId}, ${cursor.cursor})\n ON CONFLICT(indexName, spaceId, sourceName, resourceId) DO UPDATE SET cursor = excluded.cursor\n `;\n },\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport type * as Statement from '@effect/sql/Statement';\nimport * as Effect from 'effect/Effect';\n\nimport type { Obj } from '@dxos/echo';\nimport type { ObjectId, SpaceId } from '@dxos/keys';\n\nimport type { Index, IndexerObject } from './interface';\nimport type { ObjectMeta } from './object-meta-index';\n\n/**\n * The space and queue constrains are combined together using a logical OR.\n */\nexport interface FtsQuery {\n /**\n * Text to search.\n */\n query: string;\n\n /**\n * Space ID to search within.\n */\n spaceId: readonly SpaceId[] | null;\n\n /**\n * If true, include all queues in the spaces specified by `spaceId`.\n */\n includeAllQueues: boolean;\n\n /**\n * Queue IDs to search within.\n */\n queueIds: readonly ObjectId[] | null;\n}\n\n/**\n * Result of FTS query including the indexed snapshot data.\n */\nexport interface FtsResult extends ObjectMeta {\n /**\n * The indexed snapshot data (JSON string).\n * Used to load queue objects without going through document loading.\n */\n snapshot: string;\n}\n\n/**\n * Result of FTS query with rank.\n */\nexport interface FtsQueryResult extends ObjectMeta {\n /**\n * Relevance rank from FTS5.\n * Higher values indicate better matches.\n * Uses BM25 algorithm when available, falls back to 1 for non-BM25 queries.\n */\n rank: number;\n}\n\n/**\n * Escapes user input for safe FTS5 queries.\n *\n * FTS5 has special syntax characters that can cause errors or unexpected behavior:\n * - `*` suffix for prefix matching (e.g., `prog*` matches \"program\", \"programming\")\n * - `\"...\"` for phrase queries\n * - `.` for column specification\n * - `AND`, `OR`, `NOT` boolean operators\n * - `+`, `-` for required/excluded terms\n *\n * This function wraps each whitespace-separated term in double quotes, treating all\n * characters as literals. Double quotes within terms are escaped by doubling (`\"\"`).\n *\n * Example: `prog* AND test.` becomes `\"prog*\" \"AND\" \"test.\"`.\n */\nconst escapeFts5Query = (text: string): string => {\n return text\n .split(/\\s+/)\n .filter(Boolean)\n .map((term) => `\"${term.replace(/\"/g, '\"\"')}\"`)\n .join(' ');\n};\n\nexport class FtsIndex implements Index {\n migrate = Effect.fn('FtsIndex.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n // https://sqlite.org/fts5.html#the_trigram_tokenizer\n // FTS5 tables are created as virtual tables; they implicitly have a `rowid`.\n // Trigram tokenizer enables substring matching (e.g., \"rog\" matches \"programming\").\n //\n // Data structure: inverted index mapping trigrams to document IDs.\n // \"hello\" → trigrams [\"hel\", \"ell\", \"llo\"] → B-tree entries: \"hel\"→[1], \"ell\"→[1], \"llo\"→[1].\n // Query \"ell\" → O(log n) B-tree lookup → returns [1].\n // Posting lists are compressed, so index size scales well with document count.\n yield* sql`CREATE VIRTUAL TABLE IF NOT EXISTS ftsIndex USING fts5(snapshot, tokenize='trigram')`;\n });\n\n query({\n query,\n spaceId,\n includeAllQueues,\n queueIds,\n }: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(function* () {\n const trimmed = query.trim();\n if (trimmed.length === 0) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n\n // Trigram tokenizer requires at least 3 characters per term.\n // Check if ALL terms are at least 3 chars; otherwise use LIKE fallback.\n const terms = trimmed.split(/\\s+/).filter(Boolean);\n const minTermLength = Math.min(...terms.map((t) => t.length));\n\n // Use BM25 ranking for FTS5 MATCH queries, fall back to rank 1 for LIKE queries.\n // BM25 returns negative values where lower (more negative) means better match,\n // so we negate it to get higher = better.\n const useBm25 = minTermLength >= 3;\n\n const conditions =\n minTermLength < 3\n ? // LIKE fallback - scan the entire table, AND all terms.\n terms.map((term) => sql`f.snapshot LIKE ${'%' + term + '%'}`)\n : // MATCH - fast index lookup.\n [sql`f.snapshot MATCH ${escapeFts5Query(trimmed)}`];\n\n // Space and queue constraints are combined with OR.\n const sourceConditions: Statement.Statement<{}>[] = [];\n\n if (spaceId && spaceId.length > 0) {\n if (includeAllQueues) {\n // All items from these spaces (both space objects and queue objects).\n sourceConditions.push(sql`m.spaceId IN ${sql.in(spaceId)}`);\n } else {\n // Only space objects (not queue objects) from these spaces.\n sourceConditions.push(sql`(m.spaceId IN ${sql.in(spaceId)} AND m.queueId = '')`);\n }\n }\n\n if (queueIds && queueIds.length > 0) {\n // Items from specific queues.\n sourceConditions.push(sql`m.queueId IN ${sql.in(queueIds)}`);\n }\n\n if (sourceConditions.length > 0) {\n conditions.push(sql`(${sql.or(sourceConditions)})`);\n }\n\n if (useBm25) {\n // Use BM25 ranking for FTS5 MATCH queries.\n // BM25 returns negative values, negate to get higher = better match.\n // Order by rank descending so best matches come first.\n // Note: bm25() requires the actual table name, not an alias.\n const rows = yield* sql<ObjectMeta & { rank: number }>`\n SELECT m.*, -bm25(ftsIndex) AS rank \n FROM ftsIndex AS f \n JOIN objectMeta AS m ON f.rowid = m.recordId \n WHERE ${sql.and(conditions)}\n ORDER BY rank DESC\n `;\n return rows;\n } else {\n // LIKE fallback - no ranking available, default to 1.\n const rows = yield* sql<ObjectMeta>`\n SELECT m.* \n FROM ftsIndex AS f \n JOIN objectMeta AS m ON f.rowid = m.recordId \n WHERE ${sql.and(conditions)}\n `;\n return rows.map((row) => ({ ...row, rank: 1 }));\n }\n });\n }\n\n /**\n * Query snapshots by recordIds.\n * Returns the parsed JSON snapshots for queue objects.\n */\n querySnapshotsJSON(\n recordIds: number[],\n ): Effect.Effect<readonly { recordId: number; snapshot: Obj.JSON }[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(function* () {\n if (recordIds.length === 0) {\n return [];\n }\n const sql = yield* SqlClient.SqlClient;\n const results = yield* sql<{\n rowid: number;\n snapshot: string;\n }>`SELECT rowid, snapshot FROM ftsIndex WHERE rowid IN ${sql.in(recordIds)}`;\n return results.map((r) => ({\n recordId: r.rowid,\n snapshot: JSON.parse(r.snapshot),\n }));\n });\n }\n\n update = Effect.fn('FtsIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { recordId, data } = object;\n if (recordId === null) {\n return yield* Effect.die(new Error('FtsIndex.update requires recordId to be set'));\n }\n\n const snapshot = JSON.stringify(data);\n\n // FTS5 doesn't support UPDATE, need DELETE + INSERT for upsert.\n const existing = yield* sql<{ rowid: number }>`SELECT rowid FROM ftsIndex WHERE rowid = ${recordId}`;\n if (existing.length > 0) {\n yield* sql`DELETE FROM ftsIndex WHERE rowid = ${recordId}`;\n }\n\n yield* sql`INSERT INTO ftsIndex (rowid, snapshot) VALUES (${recordId}, ${snapshot})`;\n }),\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { ATTR_DELETED, ATTR_RELATION_SOURCE, ATTR_RELATION_TARGET, ATTR_TYPE } from '@dxos/echo/internal';\n\nimport type { IndexerObject } from './interface';\nimport type { Index } from './interface';\n\nexport const ObjectMeta = Schema.Struct({\n recordId: Schema.Number,\n objectId: Schema.String,\n queueId: Schema.String,\n spaceId: Schema.String,\n documentId: Schema.String,\n entityKind: Schema.String,\n typeDxn: Schema.String,\n deleted: Schema.Boolean,\n source: Schema.NullOr(Schema.String),\n target: Schema.NullOr(Schema.String),\n /** Monotonically increasing sequence number assigned on insert/update for tracking indexing order. */\n version: Schema.Number,\n});\nexport interface ObjectMeta extends Schema.Schema.Type<typeof ObjectMeta> {}\n\nexport class ObjectMetaIndex implements Index {\n migrate = Effect.fn('ObjectMetaIndex.runMigrations')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* sql`CREATE TABLE IF NOT EXISTS objectMeta (\n recordId INTEGER PRIMARY KEY AUTOINCREMENT,\n objectId TEXT NOT NULL,\n queueId TEXT NOT NULL DEFAULT '',\n spaceId TEXT NOT NULL,\n documentId TEXT NOT NULL DEFAULT '',\n entityKind TEXT NOT NULL,\n typeDxn TEXT NOT NULL,\n deleted INTEGER NOT NULL,\n source TEXT,\n target TEXT,\n version INTEGER NOT NULL\n )`;\n\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_objectId ON objectMeta(spaceId, objectId)`;\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_typeDxn ON objectMeta(spaceId, typeDxn)`;\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_version ON objectMeta(version)`;\n });\n\n query = Effect.fn('ObjectMetaIndex.queryType')(\n (\n query: Pick<ObjectMeta, 'spaceId' | 'typeDxn'>,\n ): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n // SQLite stores booleans as integers, so we need to specify the raw row type.\n const rows =\n yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND typeDxn = ${query.typeDxn}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n\n // TODO(dmaretskyi): Update recordId on objects so that we don't need to look it up separately.\n update = Effect.fn('ObjectMetaIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { spaceId, queueId, documentId, data } = object;\n\n // Extract metadata (Logic emulating Echo APIs as strict imports are unavailable).\n // TODO(agent): Verify property access matches Obj.JSON structure.\n const castData = data;\n const objectId = castData.id;\n\n // Check for existing record by (spaceId, queueId) or (spaceId, documentId).\n let existing: readonly { recordId: number }[];\n if (documentId) {\n existing = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND documentId = ${documentId} AND objectId = ${objectId} LIMIT 1`;\n } else if (queueId) {\n existing = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND queueId = ${queueId} AND objectId = ${objectId} LIMIT 1`;\n } else {\n // Should not happen based on IndexerObject definition (one must be present ideally), but handle gracefully.\n existing = [];\n }\n\n // Get max version + 1.\n const result = yield* sql<{ v: number | null }>`SELECT MAX(version) as v FROM objectMeta`;\n const [{ v }] = result;\n const version = (v ?? 0) + 1;\n\n // Extract metadata.\n const entityKind = castData[ATTR_RELATION_SOURCE] ? 'relation' : 'object';\n const typeDxn = castData[ATTR_TYPE] ? String(castData[ATTR_TYPE]) : 'type';\n const deleted = castData[ATTR_DELETED] ? 1 : 0;\n // Relations.\n const source = entityKind === 'relation' ? (castData[ATTR_RELATION_SOURCE] ?? null) : null;\n const target = entityKind === 'relation' ? (castData[ATTR_RELATION_TARGET] ?? null) : null;\n\n if (existing.length > 0) {\n yield* sql`\n UPDATE objectMeta SET\n version = ${version},\n entityKind = ${entityKind},\n typeDxn = ${typeDxn},\n deleted = ${deleted},\n source = ${source},\n target = ${target}\n WHERE recordId = ${existing[0].recordId}\n `;\n } else {\n yield* sql`\n INSERT INTO objectMeta (\n objectId, queueId, spaceId, documentId, \n entityKind, typeDxn, deleted, source, target, version\n ) VALUES (\n ${objectId}, ${queueId ?? ''}, ${spaceId}, ${documentId ?? ''}, \n ${entityKind}, ${typeDxn}, ${deleted}, \n ${source}, ${target}, ${version}\n )\n `;\n }\n }),\n { discard: true },\n );\n }),\n );\n\n /**\n * Look up `recordIds` for objects that are already stored in the ObjectMetaIndex.\n * Mutates the objects in place.\n */\n lookupRecordIds = Effect.fn('ObjectMetaIndex.lookupRecordIds')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n for (const object of objects) {\n const { spaceId, queueId, documentId, data } = object;\n const objectId = data.id;\n\n let result: readonly { recordId: number }[];\n if (documentId) {\n result = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND documentId = ${documentId} AND objectId = ${objectId} LIMIT 1`;\n } else if (queueId) {\n result = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND queueId = ${queueId} AND objectId = ${objectId} LIMIT 1`;\n } else {\n result = [];\n }\n\n if (result.length === 0) {\n // TODO(mykola): Handle this case gracefully.\n yield* Effect.die(\n new Error(`Object not found in ObjectMetaIndex: ${spaceId}/${documentId ?? queueId}/${objectId}`),\n );\n }\n object.recordId = result[0].recordId;\n }\n }),\n );\n\n /**\n * Look up object metadata by recordIds.\n */\n lookupByRecordIds = Effect.fn('ObjectMetaIndex.lookupByRecordIds')(\n (recordIds: number[]): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n if (recordIds.length === 0) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n const placeholders = recordIds.map(() => '?').join(', ');\n const rows = yield* sql.unsafe<ObjectMeta>(\n `SELECT * FROM objectMeta WHERE recordId IN (${placeholders})`,\n recordIds,\n );\n\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { EncodedReference, isEncodedReference } from '@dxos/echo-protocol';\n\nimport { EscapedPropPath } from '../utils';\n\nimport type { Index, IndexerObject } from './interface';\n\n/**\n * Extracts all outgoing references from an object's data.\n */\nconst extractReferences = (data: Record<string, unknown>): { path: string[]; targetDxn: string }[] => {\n const refs: { path: string[]; targetDxn: string }[] = [];\n const visit = (path: string[], value: unknown) => {\n if (isEncodedReference(value)) {\n const dxn = EncodedReference.toDXN(value);\n const echoId = dxn.asEchoDXN()?.echoId;\n if (!echoId) {\n return; // Skip non-echo references.\n }\n refs.push({ path, targetDxn: dxn.toString() });\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n for (const [key, v] of Object.entries(value)) {\n visit([...path, key], v);\n }\n } else if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n visit([...path, String(i)], value[i]);\n }\n }\n };\n visit([], data);\n return refs;\n};\n\nexport const ReverseRef = Schema.Struct({\n recordId: Schema.Number,\n targetDxn: Schema.String,\n /**\n * Escaped property path within an object.\n *\n * Escaping rules:\n *\n * - '.' -> '\\.'\n * - '\\' -> '\\\\'\n * - contact with .\n */\n propPath: Schema.String,\n});\nexport interface ReverseRef extends Schema.Schema.Type<typeof ReverseRef> {}\n\nexport interface ReverseRefQuery {\n targetDxn: string;\n // TODO: Add prop filter\n}\n\n/**\n * Indexes reverse references - tracks which objects reference which targets.\n * Only indexes references, not relations.\n */\nexport class ReverseRefIndex implements Index {\n migrate = Effect.fn('ReverseRefIndex.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* sql`CREATE TABLE IF NOT EXISTS reverseRef (\n recordId INTEGER NOT NULL,\n targetDxn TEXT NOT NULL,\n propPath TEXT NOT NULL,\n PRIMARY KEY (recordId, targetDxn, propPath)\n )`;\n\n yield* sql`CREATE INDEX IF NOT EXISTS idx_reverse_ref_target ON reverseRef(targetDxn)`;\n });\n\n /**\n * Query all references pointing to a target DXN.\n */\n query = Effect.fn('ReverseRefIndex.query')(\n ({ targetDxn }: ReverseRefQuery): Effect.Effect<readonly ReverseRef[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n // TODO(mykola): Join objectMeta table here.\n const rows = yield* sql`SELECT * FROM reverseRef WHERE targetDxn = ${targetDxn}`;\n return rows as ReverseRef[];\n }),\n );\n\n update = Effect.fn('ReverseRefIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { recordId, data } = object;\n if (recordId === null) {\n yield* Effect.die(new Error('ReverseRefIndex.update requires recordId to be set'));\n }\n\n // Delete existing references for this record.\n yield* sql`DELETE FROM reverseRef WHERE recordId = ${recordId}`;\n\n // Extract references from data.\n const refs = extractReferences(data as unknown as Record<string, unknown>);\n\n // Insert new references.\n yield* Effect.forEach(\n refs,\n (ref) =>\n sql`INSERT INTO reverseRef (recordId, targetDxn, propPath) VALUES (${recordId}, ${ref.targetDxn}, ${EscapedPropPath.escape(ref.path)})`,\n { discard: true },\n );\n }),\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { invariant } from '@dxos/invariant';\n\nexport type ObjectPropPath = (string | number)[];\n\n/**\n * Escaped property path within an object.\n *\n * Escaping rules:\n *\n * - '.' -> '\\.'\n * - '\\' -> '\\\\'\n * - contact with .\n */\nexport const EscapedPropPath: Schema.SchemaClass<string, string> & {\n escape: (path: ObjectPropPath) => EscapedPropPath;\n unescape: (path: EscapedPropPath) => ObjectPropPath;\n} = class extends Schema.String.annotations({ title: 'EscapedPropPath' }) {\n static escape(path: ObjectPropPath): EscapedPropPath {\n return path.map((p) => p.toString().replaceAll('\\\\', '\\\\\\\\').replaceAll('.', '\\\\.')).join('.');\n }\n\n static unescape(path: EscapedPropPath): ObjectPropPath {\n const parts: string[] = [];\n let current = '';\n\n for (let i = 0; i < path.length; i++) {\n if (path[i] === '\\\\') {\n invariant(i + 1 < path.length && (path[i + 1] === '.' || path[i + 1] === '\\\\'), 'Malformed escaping.');\n current = current + path[i + 1];\n i++;\n } else if (path[i] === '.') {\n parts.push(current);\n current = '';\n } else {\n current += path[i];\n }\n }\n parts.push(current);\n\n return parts;\n }\n};\nexport type EscapedPropPath = Schema.Schema.Type<typeof EscapedPropPath>;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["Effect", "SqlTransaction", "SqlClient", "Effect", "Schema", "SpaceId", "IndexCursor", "Struct", "indexName", "String", "spaceId", "NullOr", "SpaceId", "sourceName", "resourceId", "cursor", "Union", "Number", "IndexTracker", "migrate", "fn", "sql", "SqlClient", "queryCursors", "query", "gen", "spaceIdParam", "undefined", "sourceNameParam", "resourceIdParam", "rows", "map", "row", "decodeSync", "updateCursors", "cursors", "forEach", "discard", "SqlClient", "Effect", "escapeFts5Query", "text", "split", "filter", "Boolean", "map", "term", "replace", "join", "FtsIndex", "migrate", "fn", "sql", "SqlClient", "query", "spaceId", "includeAllQueues", "queueIds", "gen", "trimmed", "trim", "length", "terms", "minTermLength", "Math", "min", "t", "useBm25", "conditions", "sourceConditions", "push", "in", "or", "rows", "and", "row", "rank", "querySnapshotsJSON", "recordIds", "results", "r", "recordId", "rowid", "snapshot", "JSON", "parse", "update", "objects", "forEach", "object", "data", "die", "Error", "stringify", "existing", "discard", "SqlClient", "Effect", "Schema", "ATTR_DELETED", "ATTR_RELATION_SOURCE", "ATTR_RELATION_TARGET", "ATTR_TYPE", "ObjectMeta", "Struct", "recordId", "Number", "objectId", "String", "queueId", "spaceId", "documentId", "entityKind", "typeDxn", "deleted", "Boolean", "source", "NullOr", "target", "version", "ObjectMetaIndex", "migrate", "fn", "sql", "SqlClient", "query", "gen", "rows", "map", "row", "update", "objects", "forEach", "object", "data", "castData", "id", "existing", "result", "v", "ATTR_RELATION_SOURCE", "ATTR_TYPE", "ATTR_DELETED", "ATTR_RELATION_TARGET", "
|
|
4
|
+
"sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport type * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\n\nimport type { SpaceId } from '@dxos/keys';\nimport * as SqlTransaction from '@dxos/sql-sqlite/SqlTransaction';\n\nimport { type IndexCursor, IndexTracker } from './index-tracker';\nimport {\n FtsIndex,\n type FtsQuery,\n type FtsQueryResult,\n type Index,\n type IndexerObject,\n type ObjectMeta,\n ObjectMetaIndex,\n ReverseRefIndex,\n type ReverseRefQuery,\n} from './indexes';\n\n/**\n * Cursor into indexable data-source.\n */\nexport interface DataSourceCursor {\n spaceId: SpaceId | null;\n\n /**\n * documentId or queueId.\n */\n resourceId: string | null;\n\n /**\n * heads or queue position.\n */\n cursor: number | string;\n}\n\nexport interface IndexDataSource {\n readonly sourceName: string; // e.g. queue, automerge, etc.\n\n getChangedObjects(\n cursors: DataSourceCursor[],\n opts?: { limit?: number },\n ): Effect.Effect<{ objects: IndexerObject[]; cursors: DataSourceCursor[] }>;\n}\n\nexport interface IndexEngineParams {\n tracker: IndexTracker;\n objectMetaIndex: ObjectMetaIndex;\n ftsIndex: FtsIndex;\n reverseRefIndex: ReverseRefIndex;\n}\n\nexport class IndexEngine {\n readonly #tracker: IndexTracker;\n readonly #objectMetaIndex: ObjectMetaIndex;\n readonly #ftsIndex: FtsIndex;\n readonly #reverseRefIndex: ReverseRefIndex;\n\n constructor(params?: IndexEngineParams) {\n this.#tracker = params?.tracker ?? new IndexTracker();\n this.#objectMetaIndex = params?.objectMetaIndex ?? new ObjectMetaIndex();\n this.#ftsIndex = params?.ftsIndex ?? new FtsIndex();\n this.#reverseRefIndex = params?.reverseRefIndex ?? new ReverseRefIndex();\n }\n\n migrate() {\n return Effect.gen(this, function* () {\n yield* this.#tracker.migrate();\n yield* this.#objectMetaIndex.migrate();\n yield* this.#ftsIndex.migrate();\n yield* this.#reverseRefIndex.migrate();\n });\n }\n\n /**\n * Query text index and return full object metadata with rank.\n */\n queryText(query: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(this, function* () {\n return yield* this.#ftsIndex.query(query);\n });\n }\n\n queryReverseRef(query: ReverseRefQuery) {\n // TODO(mykola): Join with metadata table here.\n return this.#reverseRefIndex.query(query);\n }\n\n queryAll(query: {\n spaceIds: readonly SpaceId[];\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.queryAll(query);\n }\n\n /**\n * Query snapshots by recordIds.\n * Used to load queue objects from indexed snapshots.\n */\n querySnapshotsJSON(recordIds: number[]) {\n return this.#ftsIndex.querySnapshotsJSON(recordIds);\n }\n\n queryType(\n query: Pick<ObjectMeta, 'spaceId' | 'typeDxn'>,\n ): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.query(query);\n }\n\n queryTypes(query: {\n spaceIds: readonly SpaceId[];\n typeDxns: readonly ObjectMeta['typeDxn'][];\n inverted?: boolean;\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.queryTypes(query);\n }\n\n queryRelations(query: {\n endpoint: 'source' | 'target';\n anchorDxns: readonly string[];\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.queryRelations(query);\n }\n\n lookupByRecordIds(recordIds: number[]): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> {\n return this.#objectMetaIndex.lookupByRecordIds(recordIds);\n }\n\n update(\n dataSource: IndexDataSource,\n opts: { spaceId: SpaceId | null; limit?: number },\n ): Effect.Effect<\n { updated: number; done: boolean },\n SqlError.SqlError,\n SqlTransaction.SqlTransaction | SqlClient.SqlClient\n > {\n return Effect.gen(this, function* () {\n let updated = 0;\n\n const { updated: updatedFtsIndex, done: doneFtsIndex } = yield* this.#update(this.#ftsIndex, dataSource, {\n indexName: 'fts',\n spaceId: opts.spaceId,\n limit: opts.limit,\n });\n updated += updatedFtsIndex;\n\n const { updated: updatedReverseRefIndex, done: doneReverseRefIndex } = yield* this.#update(\n this.#reverseRefIndex,\n dataSource,\n {\n indexName: 'reverseRef',\n spaceId: opts.spaceId,\n limit: opts.limit,\n },\n );\n updated += updatedReverseRefIndex;\n\n return { updated, done: doneFtsIndex && doneReverseRefIndex };\n }).pipe(Effect.withSpan('IndexEngine.update'));\n }\n\n /**\n * Update a dependent index that requires recordId enrichment.\n * This method:\n * 1. Gets changed objects from the source.\n * 2. Ensures those objects exist in ObjectMetaIndex.\n * 3. Looks up recordIds for those objects.\n * 4. Enriches objects with recordIds.\n * 5. Updates the dependent index.\n */\n #update(\n index: Index,\n source: IndexDataSource,\n opts: { indexName: string; spaceId: SpaceId | null; limit?: number },\n ): Effect.Effect<\n { updated: number; done: boolean },\n SqlError.SqlError,\n SqlTransaction.SqlTransaction | SqlClient.SqlClient\n > {\n return Effect.gen(this, function* () {\n const sqlTransaction = yield* SqlTransaction.SqlTransaction;\n\n return yield* sqlTransaction.withTransaction(\n Effect.gen(this, function* () {\n const cursors = yield* this.#tracker.queryCursors({\n indexName: opts.indexName,\n sourceName: source.sourceName,\n // Pass undefined to get all cursors when spaceId is null.\n spaceId: opts.spaceId ?? undefined,\n });\n const { objects, cursors: updatedCursors } = yield* source.getChangedObjects(cursors, { limit: opts.limit });\n if (objects.length === 0) {\n return { updated: 0, done: true };\n }\n\n // Ensure objects exist in ObjectMetaIndex.\n yield* this.#objectMetaIndex.update(objects);\n\n // Look up recordIds for the objects.\n yield* this.#objectMetaIndex.lookupRecordIds(objects);\n\n yield* index.update(objects);\n yield* this.#tracker.updateCursors(\n updatedCursors.map(\n (_): IndexCursor => ({\n indexName: opts.indexName,\n spaceId: _.spaceId,\n sourceName: source.sourceName,\n resourceId: _.resourceId,\n cursor: _.cursor,\n }),\n ),\n );\n return { updated: objects.length, done: false };\n }),\n );\n }).pipe(Effect.withSpan('IndexEngine.#updateDependentIndex'));\n }\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { SpaceId } from '@dxos/keys';\n\nexport const IndexCursor = Schema.Struct({\n /**\n * Name of the index owning this cursor.\n */\n indexName: Schema.String,\n /**\n * Space id.\n */\n spaceId: Schema.NullOr(SpaceId),\n /**\n * Source name.\n * 'automerge' / 'queue' / 'index' (for secondary indexes)\n */\n sourceName: Schema.String,\n /**\n * Document id or queue id.\n * doc_id, queue_id, '' <empty string> (if indexing entire namespace)\n */\n resourceId: Schema.NullOr(Schema.String),\n /**\n * Heads, queue position, version.\n */\n cursor: Schema.Union(Schema.Number, Schema.String),\n});\nexport interface IndexCursor extends Schema.Schema.Type<typeof IndexCursor> {}\n\nexport class IndexTracker {\n migrate = Effect.fn('IndexTracker.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n // For automerge: last-indexed heads of the document\n // For queue: the position of the item that was indexed last\n yield* sql`CREATE TABLE IF NOT EXISTS indexCursor (\n indexName TEXT NOT NULL,\n spaceId TEXT NOT NULL DEFAULT '',\n sourceName TEXT NOT NULL,\n resourceId TEXT NOT NULL DEFAULT '',\n cursor,\n PRIMARY KEY (indexName, spaceId, sourceName, resourceId)\n )`;\n });\n\n queryCursors = Effect.fn('IndexTracker.queryCursors')(\n (\n query: Pick<IndexCursor, 'indexName'> & Partial<Pick<IndexCursor, 'sourceName' | 'resourceId' | 'spaceId'>>,\n ): Effect.Effect<IndexCursor[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n const spaceIdParam = query.spaceId === undefined ? null : (query.spaceId ?? '');\n const sourceNameParam = query.sourceName === undefined ? null : query.sourceName;\n const resourceIdParam = query.resourceId === undefined ? null : (query.resourceId ?? '');\n\n const rows = yield* sql<IndexCursor>`\n SELECT * FROM indexCursor \n WHERE indexName = ${query.indexName}\n AND (${spaceIdParam} IS NULL OR spaceId = ${spaceIdParam})\n AND (${sourceNameParam} IS NULL OR sourceName = ${sourceNameParam})\n AND (${resourceIdParam} IS NULL OR resourceId = ${resourceIdParam})\n `;\n\n return rows.map(\n (row): IndexCursor => ({\n indexName: row.indexName,\n spaceId: row.spaceId === '' ? null : Schema.decodeSync(SpaceId)(row.spaceId!),\n sourceName: row.sourceName,\n resourceId: row.resourceId === '' ? null : row.resourceId,\n cursor: row.cursor,\n }),\n );\n }),\n );\n\n updateCursors = Effect.fn('IndexTracker.updateCursors')(\n (cursors: IndexCursor[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n yield* Effect.forEach(\n cursors,\n (cursor) => {\n const spaceId = cursor.spaceId ?? '';\n const resourceId = cursor.resourceId ?? '';\n return sql`\n INSERT INTO indexCursor (indexName, spaceId, sourceName, resourceId, cursor)\n VALUES (${cursor.indexName}, ${spaceId}, ${cursor.sourceName}, ${resourceId}, ${cursor.cursor})\n ON CONFLICT(indexName, spaceId, sourceName, resourceId) DO UPDATE SET cursor = excluded.cursor\n `;\n },\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport type * as Statement from '@effect/sql/Statement';\nimport * as Effect from 'effect/Effect';\n\nimport type { Obj } from '@dxos/echo';\nimport type { ObjectId, SpaceId } from '@dxos/keys';\n\nimport type { Index, IndexerObject } from './interface';\nimport type { ObjectMeta } from './object-meta-index';\n\n/**\n * The space and queue constrains are combined together using a logical OR.\n */\nexport interface FtsQuery {\n /**\n * Text to search.\n */\n query: string;\n\n /**\n * Space ID to search within.\n */\n spaceId: readonly SpaceId[] | null;\n\n /**\n * If true, include all queues in the spaces specified by `spaceId`.\n */\n includeAllQueues: boolean;\n\n /**\n * Queue IDs to search within.\n */\n queueIds: readonly ObjectId[] | null;\n}\n\n/**\n * Result of FTS query including the indexed snapshot data.\n */\nexport interface FtsResult extends ObjectMeta {\n /**\n * The indexed snapshot data (JSON string).\n * Used to load queue objects without going through document loading.\n */\n snapshot: string;\n}\n\n/**\n * Result of FTS query with rank.\n */\nexport interface FtsQueryResult extends ObjectMeta {\n /**\n * Relevance rank from FTS5.\n * Higher values indicate better matches.\n * Uses BM25 algorithm when available, falls back to 1 for non-BM25 queries.\n */\n rank: number;\n}\n\n/**\n * Escapes user input for safe FTS5 queries.\n *\n * FTS5 has special syntax characters that can cause errors or unexpected behavior:\n * - `*` suffix for prefix matching (e.g., `prog*` matches \"program\", \"programming\")\n * - `\"...\"` for phrase queries\n * - `.` for column specification\n * - `AND`, `OR`, `NOT` boolean operators\n * - `+`, `-` for required/excluded terms\n *\n * This function wraps each whitespace-separated term in double quotes, treating all\n * characters as literals. Double quotes within terms are escaped by doubling (`\"\"`).\n *\n * Example: `prog* AND test.` becomes `\"prog*\" \"AND\" \"test.\"`.\n */\nconst escapeFts5Query = (text: string): string => {\n return text\n .split(/\\s+/)\n .filter(Boolean)\n .map((term) => `\"${term.replace(/\"/g, '\"\"')}\"`)\n .join(' ');\n};\n\nexport class FtsIndex implements Index {\n migrate = Effect.fn('FtsIndex.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n // https://sqlite.org/fts5.html#the_trigram_tokenizer\n // FTS5 tables are created as virtual tables; they implicitly have a `rowid`.\n // Trigram tokenizer enables substring matching (e.g., \"rog\" matches \"programming\").\n //\n // Data structure: inverted index mapping trigrams to document IDs.\n // \"hello\" → trigrams [\"hel\", \"ell\", \"llo\"] → B-tree entries: \"hel\"→[1], \"ell\"→[1], \"llo\"→[1].\n // Query \"ell\" → O(log n) B-tree lookup → returns [1].\n // Posting lists are compressed, so index size scales well with document count.\n yield* sql`CREATE VIRTUAL TABLE IF NOT EXISTS ftsIndex USING fts5(snapshot, tokenize='trigram')`;\n });\n\n query({\n query,\n spaceId,\n includeAllQueues,\n queueIds,\n }: FtsQuery): Effect.Effect<readonly FtsQueryResult[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(function* () {\n const trimmed = query.trim();\n if (trimmed.length === 0) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n\n // Trigram tokenizer requires at least 3 characters per term.\n // Check if ALL terms are at least 3 chars; otherwise use LIKE fallback.\n const terms = trimmed.split(/\\s+/).filter(Boolean);\n const minTermLength = Math.min(...terms.map((t) => t.length));\n\n // Use BM25 ranking for FTS5 MATCH queries, fall back to rank 1 for LIKE queries.\n // BM25 returns negative values where lower (more negative) means better match,\n // so we negate it to get higher = better.\n const useBm25 = minTermLength >= 3;\n\n const conditions =\n minTermLength < 3\n ? // LIKE fallback - scan the entire table, AND all terms.\n terms.map((term) => sql`f.snapshot LIKE ${'%' + term + '%'}`)\n : // MATCH - fast index lookup.\n [sql`f.snapshot MATCH ${escapeFts5Query(trimmed)}`];\n\n // Space and queue constraints are combined with OR.\n const sourceConditions: Statement.Statement<{}>[] = [];\n\n if (spaceId && spaceId.length > 0) {\n if (includeAllQueues) {\n // All items from these spaces (both space objects and queue objects).\n sourceConditions.push(sql`m.spaceId IN ${sql.in(spaceId)}`);\n } else {\n // Only space objects (not queue objects) from these spaces.\n sourceConditions.push(sql`(m.spaceId IN ${sql.in(spaceId)} AND m.queueId = '')`);\n }\n }\n\n if (queueIds && queueIds.length > 0) {\n // Items from specific queues.\n sourceConditions.push(sql`m.queueId IN ${sql.in(queueIds)}`);\n }\n\n if (sourceConditions.length > 0) {\n conditions.push(sql`(${sql.or(sourceConditions)})`);\n }\n\n if (useBm25) {\n // Use BM25 ranking for FTS5 MATCH queries.\n // BM25 returns negative values, negate to get higher = better match.\n // Order by rank descending so best matches come first.\n // Note: bm25() requires the actual table name, not an alias.\n const rows = yield* sql<ObjectMeta & { rank: number }>`\n SELECT m.*, -bm25(ftsIndex) AS rank \n FROM ftsIndex AS f \n JOIN objectMeta AS m ON f.rowid = m.recordId \n WHERE ${sql.and(conditions)}\n ORDER BY rank DESC\n `;\n return rows;\n } else {\n // LIKE fallback - no ranking available, default to 1.\n const rows = yield* sql<ObjectMeta>`\n SELECT m.* \n FROM ftsIndex AS f \n JOIN objectMeta AS m ON f.rowid = m.recordId \n WHERE ${sql.and(conditions)}\n `;\n return rows.map((row) => ({ ...row, rank: 1 }));\n }\n });\n }\n\n /**\n * Query snapshots by recordIds.\n * Returns the parsed JSON snapshots for queue objects.\n */\n querySnapshotsJSON(\n recordIds: number[],\n ): Effect.Effect<readonly { recordId: number; snapshot: Obj.JSON }[], SqlError.SqlError, SqlClient.SqlClient> {\n return Effect.gen(function* () {\n if (recordIds.length === 0) {\n return [];\n }\n const sql = yield* SqlClient.SqlClient;\n const results = yield* sql<{\n rowid: number;\n snapshot: string;\n }>`SELECT rowid, snapshot FROM ftsIndex WHERE rowid IN ${sql.in(recordIds)}`;\n return results.map((r) => ({\n recordId: r.rowid,\n snapshot: JSON.parse(r.snapshot),\n }));\n });\n }\n\n update = Effect.fn('FtsIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { recordId, data } = object;\n if (recordId === null) {\n return yield* Effect.die(new Error('FtsIndex.update requires recordId to be set'));\n }\n\n const snapshot = JSON.stringify(data);\n\n // FTS5 doesn't support UPDATE, need DELETE + INSERT for upsert.\n const existing = yield* sql<{ rowid: number }>`SELECT rowid FROM ftsIndex WHERE rowid = ${recordId}`;\n if (existing.length > 0) {\n yield* sql`DELETE FROM ftsIndex WHERE rowid = ${recordId}`;\n }\n\n yield* sql`INSERT INTO ftsIndex (rowid, snapshot) VALUES (${recordId}, ${snapshot})`;\n }),\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { ATTR_DELETED, ATTR_RELATION_SOURCE, ATTR_RELATION_TARGET, ATTR_TYPE } from '@dxos/echo/internal';\nimport { DXN } from '@dxos/keys';\n\nimport type { IndexerObject } from './interface';\nimport type { Index } from './interface';\n\nconst _escapeLikePrefix = (prefix: string) => {\n // Escape LIKE metacharacters in the *literal* prefix (we still append a wildcard for the version suffix).\n // Backslash is used as the ESCAPE character.\n // See: https://www.sqlite.org/lang_expr.html#like\n const escaped = prefix.replaceAll('\\\\', '\\\\\\\\').replaceAll('%', '\\\\%').replaceAll('_', '\\\\_');\n return `${escaped}:%`;\n};\n\nexport const ObjectMeta = Schema.Struct({\n recordId: Schema.Number,\n objectId: Schema.String,\n queueId: Schema.String,\n spaceId: Schema.String,\n documentId: Schema.String,\n entityKind: Schema.String,\n /** The versioned DXN of the type of the object. */\n typeDxn: Schema.String,\n deleted: Schema.Boolean,\n source: Schema.NullOr(Schema.String),\n target: Schema.NullOr(Schema.String),\n /** Monotonically increasing sequence number assigned on insert/update for tracking indexing order. */\n version: Schema.Number,\n});\nexport interface ObjectMeta extends Schema.Schema.Type<typeof ObjectMeta> {}\n\nexport class ObjectMetaIndex implements Index {\n migrate = Effect.fn('ObjectMetaIndex.runMigrations')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* sql`CREATE TABLE IF NOT EXISTS objectMeta (\n recordId INTEGER PRIMARY KEY AUTOINCREMENT,\n objectId TEXT NOT NULL,\n queueId TEXT NOT NULL DEFAULT '',\n spaceId TEXT NOT NULL,\n documentId TEXT NOT NULL DEFAULT '',\n entityKind TEXT NOT NULL,\n typeDxn TEXT NOT NULL,\n deleted INTEGER NOT NULL,\n source TEXT,\n target TEXT,\n version INTEGER NOT NULL\n )`;\n\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_objectId ON objectMeta(spaceId, objectId)`;\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_typeDxn ON objectMeta(spaceId, typeDxn)`;\n yield* sql`CREATE INDEX IF NOT EXISTS idx_object_index_version ON objectMeta(version)`;\n });\n\n query = Effect.fn('ObjectMetaIndex.queryType')(\n (\n query: Pick<ObjectMeta, 'spaceId' | 'typeDxn'>,\n ): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n const parsedType = DXN.tryParse(query.typeDxn)?.asTypeDXN();\n\n // SQLite stores booleans as integers, so we need to specify the raw row type.\n const rows =\n parsedType && parsedType.version === undefined\n ? yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND (typeDxn = ${\n query.typeDxn\n } OR typeDxn LIKE ${_escapeLikePrefix(query.typeDxn)} ESCAPE '\\\\')`\n : yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND typeDxn = ${query.typeDxn}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n\n queryAll = Effect.fn('ObjectMetaIndex.queryAll')(\n (query: {\n spaceIds: readonly ObjectMeta['spaceId'][];\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n if (query.spaceIds.length === 0) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n const rows = yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE ${sql.in('spaceId', query.spaceIds)}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n\n queryTypes = Effect.fn('ObjectMetaIndex.queryTypes')(\n ({\n spaceIds,\n typeDxns,\n inverted = false,\n }: {\n spaceIds: readonly ObjectMeta['spaceId'][];\n typeDxns: readonly ObjectMeta['typeDxn'][];\n inverted?: boolean;\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n if (spaceIds.length === 0) {\n return [];\n }\n\n if (typeDxns.length === 0) {\n if (!inverted) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n const rows = yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE ${sql.in('spaceId', spaceIds)}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }\n const sql = yield* SqlClient.SqlClient;\n const spaceWhere = sql.in('spaceId', spaceIds);\n const typeWhere = sql.or(\n typeDxns.map((typeDxn) => {\n const parsedType = DXN.tryParse(typeDxn)?.asTypeDXN();\n return parsedType && parsedType.version === undefined\n ? sql.or([sql`typeDxn = ${typeDxn}`, sql`typeDxn LIKE ${_escapeLikePrefix(typeDxn)} ESCAPE '\\\\'`])\n : sql`typeDxn = ${typeDxn}`;\n }),\n );\n const rows = inverted\n ? yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE ${spaceWhere} AND NOT ${typeWhere}`\n : yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE ${spaceWhere} AND ${typeWhere}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n\n queryRelations = Effect.fn('ObjectMetaIndex.queryRelations')(\n ({\n endpoint,\n anchorDxns,\n }: {\n endpoint: 'source' | 'target';\n anchorDxns: readonly string[];\n }): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n if (anchorDxns.length === 0) {\n return [];\n }\n const sql = yield* SqlClient.SqlClient;\n const column = endpoint === 'source' ? 'source' : 'target';\n const rows = yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE entityKind = 'relation' AND ${sql.in(\n column,\n anchorDxns,\n )}`;\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n\n // TODO(dmaretskyi): Update recordId on objects so that we don't need to look it up separately.\n update = Effect.fn('ObjectMetaIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { spaceId, queueId, documentId, data } = object;\n\n // Extract metadata (Logic emulating Echo APIs as strict imports are unavailable).\n const castData = data;\n const objectId = castData.id;\n\n // Check for existing record by (spaceId, queueId) or (spaceId, documentId).\n let existing: readonly { recordId: number }[];\n if (documentId) {\n existing = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND documentId = ${documentId} AND objectId = ${objectId} LIMIT 1`;\n } else if (queueId) {\n existing = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND queueId = ${queueId} AND objectId = ${objectId} LIMIT 1`;\n } else {\n // Should not happen based on IndexerObject definition (one must be present ideally), but handle gracefully.\n existing = [];\n }\n\n // Get max version + 1.\n const result = yield* sql<{ v: number | null }>`SELECT MAX(version) as v FROM objectMeta`;\n const [{ v }] = result;\n const version = (v ?? 0) + 1;\n\n // Extract metadata.\n const entityKind = castData[ATTR_RELATION_SOURCE] ? 'relation' : 'object';\n const typeDxn = castData[ATTR_TYPE] ? String(castData[ATTR_TYPE]) : 'type';\n const deleted = castData[ATTR_DELETED] ? 1 : 0;\n // Relations.\n const source = entityKind === 'relation' ? (castData[ATTR_RELATION_SOURCE] ?? null) : null;\n const target = entityKind === 'relation' ? (castData[ATTR_RELATION_TARGET] ?? null) : null;\n\n if (existing.length > 0) {\n yield* sql`\n UPDATE objectMeta SET\n version = ${version},\n entityKind = ${entityKind},\n typeDxn = ${typeDxn},\n deleted = ${deleted},\n source = ${source},\n target = ${target}\n WHERE recordId = ${existing[0].recordId}\n `;\n } else {\n yield* sql`\n INSERT INTO objectMeta (\n objectId, queueId, spaceId, documentId, \n entityKind, typeDxn, deleted, source, target, version\n ) VALUES (\n ${objectId}, ${queueId ?? ''}, ${spaceId}, ${documentId ?? ''}, \n ${entityKind}, ${typeDxn}, ${deleted}, \n ${source}, ${target}, ${version}\n )\n `;\n }\n }),\n { discard: true },\n );\n }),\n );\n\n /**\n * Look up `recordIds` for objects that are already stored in the ObjectMetaIndex.\n * Mutates the objects in place.\n */\n lookupRecordIds = Effect.fn('ObjectMetaIndex.lookupRecordIds')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n for (const object of objects) {\n const { spaceId, queueId, documentId, data } = object;\n const objectId = data.id;\n\n let result: readonly { recordId: number }[];\n if (documentId) {\n result = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND documentId = ${documentId} AND objectId = ${objectId} LIMIT 1`;\n } else if (queueId) {\n result = yield* sql<{\n recordId: number;\n }>`SELECT recordId FROM objectMeta WHERE spaceId = ${spaceId} AND queueId = ${queueId} AND objectId = ${objectId} LIMIT 1`;\n } else {\n result = [];\n }\n\n if (result.length === 0) {\n // TODO(mykola): Handle this case gracefully.\n yield* Effect.die(\n new Error(`Object not found in ObjectMetaIndex: ${spaceId}/${documentId ?? queueId}/${objectId}`),\n );\n }\n object.recordId = result[0].recordId;\n }\n }),\n );\n\n /**\n * Look up object metadata by recordIds.\n */\n lookupByRecordIds = Effect.fn('ObjectMetaIndex.lookupByRecordIds')(\n (recordIds: number[]): Effect.Effect<readonly ObjectMeta[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n if (recordIds.length === 0) {\n return [];\n }\n\n const sql = yield* SqlClient.SqlClient;\n const rows = yield* sql<ObjectMeta>`SELECT * FROM objectMeta WHERE ${sql.in('recordId', recordIds)}`;\n\n return rows.map((row) => ({\n ...row,\n deleted: !!row.deleted,\n }));\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { EncodedReference, isEncodedReference } from '@dxos/echo-protocol';\n\nimport { EscapedPropPath } from '../utils';\n\nimport type { Index, IndexerObject } from './interface';\n\n/**\n * Extracts all outgoing references from an object's data.\n */\nconst extractReferences = (data: Record<string, unknown>): { path: string[]; targetDxn: string }[] => {\n const refs: { path: string[]; targetDxn: string }[] = [];\n const visit = (path: string[], value: unknown) => {\n if (isEncodedReference(value)) {\n const dxn = EncodedReference.toDXN(value);\n const echoId = dxn.asEchoDXN()?.echoId;\n if (!echoId) {\n return; // Skip non-echo references.\n }\n refs.push({ path, targetDxn: dxn.toString() });\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n for (const [key, v] of Object.entries(value)) {\n visit([...path, key], v);\n }\n } else if (Array.isArray(value)) {\n for (let i = 0; i < value.length; i++) {\n visit([...path, String(i)], value[i]);\n }\n }\n };\n visit([], data);\n return refs;\n};\n\nexport const ReverseRef = Schema.Struct({\n recordId: Schema.Number,\n targetDxn: Schema.String,\n /**\n * Escaped property path within an object.\n *\n * Escaping rules:\n *\n * - '.' -> '\\.'\n * - '\\' -> '\\\\'\n * - contact with .\n */\n propPath: Schema.String,\n});\nexport interface ReverseRef extends Schema.Schema.Type<typeof ReverseRef> {}\n\nexport interface ReverseRefQuery {\n targetDxn: string;\n // TODO: Add prop filter\n}\n\n/**\n * Indexes reverse references - tracks which objects reference which targets.\n * Only indexes references, not relations.\n */\nexport class ReverseRefIndex implements Index {\n migrate = Effect.fn('ReverseRefIndex.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* sql`CREATE TABLE IF NOT EXISTS reverseRef (\n recordId INTEGER NOT NULL,\n targetDxn TEXT NOT NULL,\n propPath TEXT NOT NULL,\n PRIMARY KEY (recordId, targetDxn, propPath)\n )`;\n\n yield* sql`CREATE INDEX IF NOT EXISTS idx_reverse_ref_target ON reverseRef(targetDxn)`;\n });\n\n /**\n * Query all references pointing to a target DXN.\n */\n query = Effect.fn('ReverseRefIndex.query')(\n ({ targetDxn }: ReverseRefQuery): Effect.Effect<readonly ReverseRef[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n // TODO(mykola): Join objectMeta table here.\n const rows = yield* sql`SELECT * FROM reverseRef WHERE targetDxn = ${targetDxn}`;\n return rows as ReverseRef[];\n }),\n );\n\n update = Effect.fn('ReverseRefIndex.update')(\n (objects: IndexerObject[]): Effect.Effect<void, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n yield* Effect.forEach(\n objects,\n (object) =>\n Effect.gen(function* () {\n const { recordId, data } = object;\n if (recordId === null) {\n yield* Effect.die(new Error('ReverseRefIndex.update requires recordId to be set'));\n }\n\n // Delete existing references for this record.\n yield* sql`DELETE FROM reverseRef WHERE recordId = ${recordId}`;\n\n // Extract references from data.\n const refs = extractReferences(data as unknown as Record<string, unknown>);\n\n // Insert new references.\n yield* Effect.forEach(\n refs,\n (ref) =>\n sql`INSERT INTO reverseRef (recordId, targetDxn, propPath) VALUES (${recordId}, ${ref.targetDxn}, ${EscapedPropPath.escape(ref.path)})`,\n { discard: true },\n );\n }),\n { discard: true },\n );\n }),\n );\n}\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { invariant } from '@dxos/invariant';\n\nexport type ObjectPropPath = (string | number)[];\n\n/**\n * Escaped property path within an object.\n *\n * Escaping rules:\n *\n * - '.' -> '\\.'\n * - '\\' -> '\\\\'\n * - contact with .\n */\nexport const EscapedPropPath: Schema.SchemaClass<string, string> & {\n escape: (path: ObjectPropPath) => EscapedPropPath;\n unescape: (path: EscapedPropPath) => ObjectPropPath;\n} = class extends Schema.String.annotations({ title: 'EscapedPropPath' }) {\n static escape(path: ObjectPropPath): EscapedPropPath {\n return path.map((p) => p.toString().replaceAll('\\\\', '\\\\\\\\').replaceAll('.', '\\\\.')).join('.');\n }\n\n static unescape(path: EscapedPropPath): ObjectPropPath {\n const parts: string[] = [];\n let current = '';\n\n for (let i = 0; i < path.length; i++) {\n if (path[i] === '\\\\') {\n invariant(i + 1 < path.length && (path[i + 1] === '.' || path[i + 1] === '\\\\'), 'Malformed escaping.');\n current = current + path[i + 1];\n i++;\n } else if (path[i] === '.') {\n parts.push(current);\n current = '';\n } else {\n current += path[i];\n }\n }\n parts.push(current);\n\n return parts;\n }\n};\nexport type EscapedPropPath = Schema.Schema.Type<typeof EscapedPropPath>;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["Effect", "SqlTransaction", "SqlClient", "Effect", "Schema", "SpaceId", "IndexCursor", "Struct", "indexName", "String", "spaceId", "NullOr", "SpaceId", "sourceName", "resourceId", "cursor", "Union", "Number", "IndexTracker", "migrate", "fn", "sql", "SqlClient", "queryCursors", "query", "gen", "spaceIdParam", "undefined", "sourceNameParam", "resourceIdParam", "rows", "map", "row", "decodeSync", "updateCursors", "cursors", "forEach", "discard", "SqlClient", "Effect", "escapeFts5Query", "text", "split", "filter", "Boolean", "map", "term", "replace", "join", "FtsIndex", "migrate", "fn", "sql", "SqlClient", "query", "spaceId", "includeAllQueues", "queueIds", "gen", "trimmed", "trim", "length", "terms", "minTermLength", "Math", "min", "t", "useBm25", "conditions", "sourceConditions", "push", "in", "or", "rows", "and", "row", "rank", "querySnapshotsJSON", "recordIds", "results", "r", "recordId", "rowid", "snapshot", "JSON", "parse", "update", "objects", "forEach", "object", "data", "die", "Error", "stringify", "existing", "discard", "SqlClient", "Effect", "Schema", "ATTR_DELETED", "ATTR_RELATION_SOURCE", "ATTR_RELATION_TARGET", "ATTR_TYPE", "DXN", "_escapeLikePrefix", "prefix", "escaped", "replaceAll", "ObjectMeta", "Struct", "recordId", "Number", "objectId", "String", "queueId", "spaceId", "documentId", "entityKind", "typeDxn", "deleted", "Boolean", "source", "NullOr", "target", "version", "ObjectMetaIndex", "migrate", "fn", "sql", "SqlClient", "query", "gen", "parsedType", "DXN", "tryParse", "asTypeDXN", "rows", "undefined", "map", "row", "queryAll", "spaceIds", "length", "in", "queryTypes", "typeDxns", "inverted", "spaceWhere", "typeWhere", "or", "queryRelations", "endpoint", "anchorDxns", "column", "update", "objects", "forEach", "object", "data", "castData", "id", "existing", "result", "v", "ATTR_RELATION_SOURCE", "ATTR_TYPE", "ATTR_DELETED", "ATTR_RELATION_TARGET", "discard", "lookupRecordIds", "die", "Error", "lookupByRecordIds", "recordIds", "SqlClient", "Effect", "Schema", "EncodedReference", "isEncodedReference", "Schema", "invariant", "EscapedPropPath", "String", "annotations", "title", "escape", "path", "map", "p", "toString", "replaceAll", "join", "unescape", "parts", "current", "i", "length", "push", "extractReferences", "data", "refs", "visit", "path", "value", "isEncodedReference", "dxn", "EncodedReference", "toDXN", "echoId", "asEchoDXN", "push", "targetDxn", "toString", "Array", "isArray", "key", "v", "Object", "entries", "i", "length", "String", "ReverseRef", "Struct", "recordId", "Number", "propPath", "ReverseRefIndex", "migrate", "fn", "sql", "SqlClient", "query", "gen", "rows", "update", "objects", "forEach", "object", "die", "Error", "ref", "EscapedPropPath", "escape", "discard", "IndexEngine", "params", "tracker", "IndexTracker", "objectMetaIndex", "ObjectMetaIndex", "ftsIndex", "FtsIndex", "reverseRefIndex", "ReverseRefIndex", "migrate", "gen", "queryText", "query", "queryReverseRef", "queryAll", "querySnapshotsJSON", "recordIds", "queryType", "queryTypes", "queryRelations", "lookupByRecordIds", "update", "dataSource", "opts", "updated", "updatedFtsIndex", "done", "doneFtsIndex", "indexName", "spaceId", "limit", "updatedReverseRefIndex", "doneReverseRefIndex", "pipe", "withSpan", "index", "source", "sqlTransaction", "SqlTransaction", "withTransaction", "cursors", "queryCursors", "sourceName", "undefined", "objects", "updatedCursors", "getChangedObjects", "length", "lookupRecordIds", "updateCursors", "map", "_", "resourceId", "cursor"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"inputs":{"src/index-tracker.ts":{"bytes":11349,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true}],"format":"esm"},"src/indexes/fts-index.ts":{"bytes":22141,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true}],"format":"esm"},"src/indexes/object-meta-index.ts":{"bytes":
|
|
1
|
+
{"inputs":{"src/index-tracker.ts":{"bytes":11349,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true}],"format":"esm"},"src/indexes/fts-index.ts":{"bytes":22141,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true}],"format":"esm"},"src/indexes/object-meta-index.ts":{"bytes":36097,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo/internal","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true}],"format":"esm"},"src/utils.ts":{"bytes":4862,"imports":[{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true}],"format":"esm"},"src/indexes/reverse-ref-index.ts":{"bytes":12926,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo-protocol","kind":"import-statement","external":true},{"path":"src/utils.ts","kind":"import-statement","original":"../utils"}],"format":"esm"},"src/indexes/interface.ts":{"bytes":2404,"imports":[],"format":"esm"},"src/indexes/index.ts":{"bytes":780,"imports":[{"path":"src/indexes/fts-index.ts","kind":"import-statement","original":"./fts-index"},{"path":"src/indexes/object-meta-index.ts","kind":"import-statement","original":"./object-meta-index"},{"path":"src/indexes/reverse-ref-index.ts","kind":"import-statement","original":"./reverse-ref-index"},{"path":"src/indexes/interface.ts","kind":"import-statement","original":"./interface"}],"format":"esm"},"src/index-engine.ts":{"bytes":19645,"imports":[{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/sql-sqlite/SqlTransaction","kind":"import-statement","external":true},{"path":"src/index-tracker.ts","kind":"import-statement","original":"./index-tracker"},{"path":"src/indexes/index.ts","kind":"import-statement","original":"./indexes"}],"format":"esm"},"src/index.ts":{"bytes":1608,"imports":[{"path":"src/index-engine.ts","kind":"import-statement","original":"./index-engine"},{"path":"src/index-tracker.ts","kind":"import-statement","original":"./index-tracker"},{"path":"src/indexes/fts-index.ts","kind":"import-statement","original":"./indexes/fts-index"},{"path":"src/indexes/object-meta-index.ts","kind":"import-statement","original":"./indexes/object-meta-index"},{"path":"src/indexes/reverse-ref-index.ts","kind":"import-statement","original":"./indexes/reverse-ref-index"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":52834},"dist/lib/browser/index.mjs":{"imports":[{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/sql-sqlite/SqlTransaction","kind":"import-statement","external":true},{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo/internal","kind":"import-statement","external":true},{"path":"@dxos/keys","kind":"import-statement","external":true},{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/echo-protocol","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true}],"exports":["FtsIndex","IndexEngine","IndexTracker","ObjectMetaIndex","ReverseRefIndex"],"entryPoint":"src/index.ts","inputs":{"src/index-engine.ts":{"bytesInOutput":4028},"src/index-tracker.ts":{"bytesInOutput":2850},"src/indexes/fts-index.ts":{"bytesInOutput":3468},"src/indexes/object-meta-index.ts":{"bytesInOutput":8435},"src/indexes/reverse-ref-index.ts":{"bytesInOutput":2763},"src/utils.ts":{"bytesInOutput":1121},"src/index.ts":{"bytesInOutput":0}},"bytes":23021}}}
|
|
@@ -183,6 +183,11 @@ import * as SqlClient5 from "@effect/sql/SqlClient";
|
|
|
183
183
|
import * as Effect3 from "effect/Effect";
|
|
184
184
|
import * as Schema2 from "effect/Schema";
|
|
185
185
|
import { ATTR_DELETED, ATTR_RELATION_SOURCE, ATTR_RELATION_TARGET, ATTR_TYPE } from "@dxos/echo/internal";
|
|
186
|
+
import { DXN } from "@dxos/keys";
|
|
187
|
+
var _escapeLikePrefix = (prefix) => {
|
|
188
|
+
const escaped = prefix.replaceAll("\\", "\\\\").replaceAll("%", "\\%").replaceAll("_", "\\_");
|
|
189
|
+
return `${escaped}:%`;
|
|
190
|
+
};
|
|
186
191
|
var ObjectMeta = Schema2.Struct({
|
|
187
192
|
recordId: Schema2.Number,
|
|
188
193
|
objectId: Schema2.String,
|
|
@@ -190,6 +195,7 @@ var ObjectMeta = Schema2.Struct({
|
|
|
190
195
|
spaceId: Schema2.String,
|
|
191
196
|
documentId: Schema2.String,
|
|
192
197
|
entityKind: Schema2.String,
|
|
198
|
+
/** The versioned DXN of the type of the object. */
|
|
193
199
|
typeDxn: Schema2.String,
|
|
194
200
|
deleted: Schema2.Boolean,
|
|
195
201
|
source: Schema2.NullOr(Schema2.String),
|
|
@@ -219,7 +225,61 @@ var ObjectMetaIndex = class {
|
|
|
219
225
|
});
|
|
220
226
|
query = Effect3.fn("ObjectMetaIndex.queryType")((query) => Effect3.gen(function* () {
|
|
221
227
|
const sql = yield* SqlClient5.SqlClient;
|
|
222
|
-
const
|
|
228
|
+
const parsedType = DXN.tryParse(query.typeDxn)?.asTypeDXN();
|
|
229
|
+
const rows = parsedType && parsedType.version === void 0 ? yield* sql`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND (typeDxn = ${query.typeDxn} OR typeDxn LIKE ${_escapeLikePrefix(query.typeDxn)} ESCAPE '\\')` : yield* sql`SELECT * FROM objectMeta WHERE spaceId = ${query.spaceId} AND typeDxn = ${query.typeDxn}`;
|
|
230
|
+
return rows.map((row) => ({
|
|
231
|
+
...row,
|
|
232
|
+
deleted: !!row.deleted
|
|
233
|
+
}));
|
|
234
|
+
}));
|
|
235
|
+
queryAll = Effect3.fn("ObjectMetaIndex.queryAll")((query) => Effect3.gen(function* () {
|
|
236
|
+
if (query.spaceIds.length === 0) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
240
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE ${sql.in("spaceId", query.spaceIds)}`;
|
|
241
|
+
return rows.map((row) => ({
|
|
242
|
+
...row,
|
|
243
|
+
deleted: !!row.deleted
|
|
244
|
+
}));
|
|
245
|
+
}));
|
|
246
|
+
queryTypes = Effect3.fn("ObjectMetaIndex.queryTypes")(({ spaceIds, typeDxns, inverted = false }) => Effect3.gen(function* () {
|
|
247
|
+
if (spaceIds.length === 0) {
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
if (typeDxns.length === 0) {
|
|
251
|
+
if (!inverted) {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
const sql2 = yield* SqlClient5.SqlClient;
|
|
255
|
+
const rows2 = yield* sql2`SELECT * FROM objectMeta WHERE ${sql2.in("spaceId", spaceIds)}`;
|
|
256
|
+
return rows2.map((row) => ({
|
|
257
|
+
...row,
|
|
258
|
+
deleted: !!row.deleted
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
262
|
+
const spaceWhere = sql.in("spaceId", spaceIds);
|
|
263
|
+
const typeWhere = sql.or(typeDxns.map((typeDxn) => {
|
|
264
|
+
const parsedType = DXN.tryParse(typeDxn)?.asTypeDXN();
|
|
265
|
+
return parsedType && parsedType.version === void 0 ? sql.or([
|
|
266
|
+
sql`typeDxn = ${typeDxn}`,
|
|
267
|
+
sql`typeDxn LIKE ${_escapeLikePrefix(typeDxn)} ESCAPE '\\'`
|
|
268
|
+
]) : sql`typeDxn = ${typeDxn}`;
|
|
269
|
+
}));
|
|
270
|
+
const rows = inverted ? yield* sql`SELECT * FROM objectMeta WHERE ${spaceWhere} AND NOT ${typeWhere}` : yield* sql`SELECT * FROM objectMeta WHERE ${spaceWhere} AND ${typeWhere}`;
|
|
271
|
+
return rows.map((row) => ({
|
|
272
|
+
...row,
|
|
273
|
+
deleted: !!row.deleted
|
|
274
|
+
}));
|
|
275
|
+
}));
|
|
276
|
+
queryRelations = Effect3.fn("ObjectMetaIndex.queryRelations")(({ endpoint, anchorDxns }) => Effect3.gen(function* () {
|
|
277
|
+
if (anchorDxns.length === 0) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
const sql = yield* SqlClient5.SqlClient;
|
|
281
|
+
const column = endpoint === "source" ? "source" : "target";
|
|
282
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE entityKind = 'relation' AND ${sql.in(column, anchorDxns)}`;
|
|
223
283
|
return rows.map((row) => ({
|
|
224
284
|
...row,
|
|
225
285
|
deleted: !!row.deleted
|
|
@@ -306,8 +366,7 @@ var ObjectMetaIndex = class {
|
|
|
306
366
|
return [];
|
|
307
367
|
}
|
|
308
368
|
const sql = yield* SqlClient5.SqlClient;
|
|
309
|
-
const
|
|
310
|
-
const rows = yield* sql.unsafe(`SELECT * FROM objectMeta WHERE recordId IN (${placeholders})`, recordIds);
|
|
369
|
+
const rows = yield* sql`SELECT * FROM objectMeta WHERE ${sql.in("recordId", recordIds)}`;
|
|
311
370
|
return rows.map((row) => ({
|
|
312
371
|
...row,
|
|
313
372
|
deleted: !!row.deleted
|
|
@@ -474,6 +533,9 @@ var IndexEngine = class {
|
|
|
474
533
|
queryReverseRef(query) {
|
|
475
534
|
return this.#reverseRefIndex.query(query);
|
|
476
535
|
}
|
|
536
|
+
queryAll(query) {
|
|
537
|
+
return this.#objectMetaIndex.queryAll(query);
|
|
538
|
+
}
|
|
477
539
|
/**
|
|
478
540
|
* Query snapshots by recordIds.
|
|
479
541
|
* Used to load queue objects from indexed snapshots.
|
|
@@ -484,6 +546,15 @@ var IndexEngine = class {
|
|
|
484
546
|
queryType(query) {
|
|
485
547
|
return this.#objectMetaIndex.query(query);
|
|
486
548
|
}
|
|
549
|
+
queryTypes(query) {
|
|
550
|
+
return this.#objectMetaIndex.queryTypes(query);
|
|
551
|
+
}
|
|
552
|
+
queryRelations(query) {
|
|
553
|
+
return this.#objectMetaIndex.queryRelations(query);
|
|
554
|
+
}
|
|
555
|
+
lookupByRecordIds(recordIds) {
|
|
556
|
+
return this.#objectMetaIndex.lookupByRecordIds(recordIds);
|
|
557
|
+
}
|
|
487
558
|
update(dataSource, opts) {
|
|
488
559
|
return Effect5.gen(this, function* () {
|
|
489
560
|
let updated = 0;
|