@equationalapplications/core-llm-wiki 5.2.0 → 5.2.1
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/README.md +14 -0
- package/dist/{chunk-J3N3WRK7.mjs → chunk-6HNIOKT4.mjs} +188 -77
- package/dist/chunk-6HNIOKT4.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +326 -85
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +142 -12
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DyXISRwS.d.mts → testing-CAk9oFvw.d.mts} +109 -6
- package/dist/{testing-DyXISRwS.d.ts → testing-CAk9oFvw.d.ts} +109 -6
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +240 -75
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-J3N3WRK7.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -825,6 +825,20 @@ const { chunks, truncated } = chunkText(
|
|
|
825
825
|
|
|
826
826
|
> **Note:** `ingestDocument()` clamps the resolved overlap — whether it came from `chunkOverlap`, `WikiConfig`, or `DEFAULT_CHUNK_OVERLAP` — to `maxChunkLength - 1`. The clamp is evaluated on every call and is a no-op at the shipped defaults (`400 < 12000 - 1`), but it also bites when a custom `maxChunkLength` alone leaves the resolved overlap too large (e.g. `maxChunkLength: 100` with the default overlap `400` ingests at an effective overlap of `99`). That clamp is internal: passing the unclamped pair straight to `chunkText` throws, since it requires `overlap < maxChunkLength`. Apply the same `Math.min(overlap, maxChunkLength - 1)` yourself when re-chunking under a custom config.
|
|
827
827
|
|
|
828
|
+
## Schema Migrations
|
|
829
|
+
|
|
830
|
+
`wikiMemory.setup()` runs pending schema migrations in order and records the applied version. Migrations are additive and safe to re-run.
|
|
831
|
+
|
|
832
|
+
**Migration v9** (`add_live_hash_unique_index`) adds a partial `UNIQUE` index on `(entity_id, source_hash)` scoped to live (`deleted_at IS NULL`), non-null-hash rows. This closes a TOCTOU race where two concurrent `ingestDocument()` calls for different `sourceRef`s could both pass the duplicate-hash pre-check before either write committed, leaving two live rows with the same content hash.
|
|
833
|
+
|
|
834
|
+
If your existing database already has live rows that violate this invariant (created before the app-level race fix shipped), `setup()` throws:
|
|
835
|
+
|
|
836
|
+
```text
|
|
837
|
+
Migration v9 (add_live_hash_unique_index) failed: existing live rows violate the new UNIQUE index. …
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
The error lists the offending `(entity_id, source_hash)` groups. `setup()` performs no destructive cleanup on this path — the index is not created and the schema version is not advanced, so it's safe to retry after remediation. To remediate, either soft-delete (or re-ingest with a corrected `sourceRef`) all but one live row per listed group, then re-run `setup()`.
|
|
841
|
+
|
|
828
842
|
## Adapter Interface
|
|
829
843
|
|
|
830
844
|
Implement `SQLiteAdapter` to use your platform's SQLite driver:
|
|
@@ -38,6 +38,12 @@ var SQLITE_RESULT_CODE_NAMES = {
|
|
|
38
38
|
26: "SQLITE_NOTADB",
|
|
39
39
|
27: "SQLITE_NOTICE",
|
|
40
40
|
28: "SQLITE_WARNING",
|
|
41
|
+
// Extended result codes (https://sqlite.org/rescode.html#extended_result_code_list).
|
|
42
|
+
// 2067 = SQLITE_CONSTRAINT_UNIQUE — raised when a UNIQUE index/PRIMARY KEY
|
|
43
|
+
// rejects an INSERT/UPDATE; expo-sqlite reports this as a numeric "Error code N:"
|
|
44
|
+
// prefix and we normalize it back to the symbolic name so callers can branch on
|
|
45
|
+
// the same string the rest of the ecosystem uses.
|
|
46
|
+
2067: "SQLITE_CONSTRAINT_UNIQUE",
|
|
41
47
|
100: "SQLITE_ROW",
|
|
42
48
|
101: "SQLITE_DONE"
|
|
43
49
|
};
|
|
@@ -500,6 +506,19 @@ var JobManager = class {
|
|
|
500
506
|
this.prefix = prefix;
|
|
501
507
|
this.activeMaintenanceJobs = /* @__PURE__ */ new Set();
|
|
502
508
|
this.activeIngestJobs = /* @__PURE__ */ new Map();
|
|
509
|
+
/**
|
|
510
|
+
* Per-(entityId, sourceHash) promise-chain lock. Serializes ingest races
|
|
511
|
+
* at the application level (the v9 partial UNIQUE index enforces it at
|
|
512
|
+
* the DB level as defense-in-depth). Two callers holding the same hash
|
|
513
|
+
* see FIFO order; different hashes never block one another.
|
|
514
|
+
*
|
|
515
|
+
* Keyed `${entityId}\0${sourceHash}` so distinct (entityId, sourceHash)
|
|
516
|
+
* pairs can never share a lock. A raw concatenation would let
|
|
517
|
+
* ("ab", "c…") collide with ("a", "bc…"); the NUL separator makes that
|
|
518
|
+
* impossible because neither component can contain NUL (entityIds are
|
|
519
|
+
* user-supplied identifiers; sourceHashes are hex digests).
|
|
520
|
+
*/
|
|
521
|
+
this.hashLocks = /* @__PURE__ */ new Map();
|
|
503
522
|
this.statusSubscribers = /* @__PURE__ */ new Map();
|
|
504
523
|
/**
|
|
505
524
|
* Lookup table for acquireLock/releaseLock's dynamic-dispatch branch.
|
|
@@ -517,6 +536,51 @@ var JobManager = class {
|
|
|
517
536
|
ontologyBackfill: (id) => this._ontologyBackfillKey(id)
|
|
518
537
|
};
|
|
519
538
|
}
|
|
539
|
+
_hashLockKey(entityId, sourceHash) {
|
|
540
|
+
return `${entityId}\0${sourceHash}`;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Acquire a promise-chain lock scoped to one `(entityId, sourceHash)` pair.
|
|
544
|
+
* Returns a zero-argument release closure that removes the current tail
|
|
545
|
+
* from the chain. Releasing is idempotent so a holder that double-fires
|
|
546
|
+
* (e.g. on a finally that races an explicit release) cannot unblock
|
|
547
|
+
* the next holder prematurely.
|
|
548
|
+
*
|
|
549
|
+
* Documented ordering: ingest acquires the hash lock FIRST, then the
|
|
550
|
+
* synchronous sourceRef ingest lock, and the release closure unwinds in
|
|
551
|
+
* the opposite order (sourceRef first, hash second). Hash-then-sourceRef
|
|
552
|
+
* is required because the v9 partial UNIQUE index conflicts on the
|
|
553
|
+
* (entity_id, source_hash) pair regardless of source_ref, so two callers
|
|
554
|
+
* with DIFFERENT source_refs racing the same hash are the exact case the
|
|
555
|
+
* TOCTOU race fix needs to serialize.
|
|
556
|
+
*
|
|
557
|
+
* Mechanics: each holder owns a private `released` promise that resolves
|
|
558
|
+
* only when THIS holder calls release(). The map stores the holder's
|
|
559
|
+
* `released` promise as the "tail" — the NEXT caller awaits that tail
|
|
560
|
+
* via `previous = this.hashLocks.get(key)`. The current caller gets
|
|
561
|
+
* their unique release function once `previous` (the prior holder's
|
|
562
|
+
* release signal) settles, so each holder is guaranteed a UNIQUE release
|
|
563
|
+
* closure bound to their own `released` promise.
|
|
564
|
+
*/
|
|
565
|
+
acquireHashLock(entityId, sourceHash) {
|
|
566
|
+
const key = this._hashLockKey(entityId, sourceHash);
|
|
567
|
+
const previous = this.hashLocks.get(key) ?? Promise.resolve();
|
|
568
|
+
let resolveReleased;
|
|
569
|
+
const released = new Promise((resolve) => {
|
|
570
|
+
resolveReleased = resolve;
|
|
571
|
+
});
|
|
572
|
+
let fired = false;
|
|
573
|
+
const release = () => {
|
|
574
|
+
if (fired) return;
|
|
575
|
+
fired = true;
|
|
576
|
+
if (this.hashLocks.get(key) === released) {
|
|
577
|
+
this.hashLocks.delete(key);
|
|
578
|
+
}
|
|
579
|
+
resolveReleased();
|
|
580
|
+
};
|
|
581
|
+
this.hashLocks.set(key, released);
|
|
582
|
+
return previous.then(() => release);
|
|
583
|
+
}
|
|
520
584
|
_pruneKey(entityId) {
|
|
521
585
|
return `${this.prefix}:${entityId}:prune`;
|
|
522
586
|
}
|
|
@@ -730,6 +794,38 @@ var JobManager = class {
|
|
|
730
794
|
this._notifyStatusSubscribers(entityId);
|
|
731
795
|
return true;
|
|
732
796
|
}
|
|
797
|
+
/**
|
|
798
|
+
* Centralized ingest lock acquisition. Acquires the hash lock FIRST
|
|
799
|
+
* (FIFO across callers racing the same hash), then the synchronous
|
|
800
|
+
* sourceRef ingest lock (rejects cross-entity conflicts and busy
|
|
801
|
+
* operations). Returns a single zero-argument release closure that
|
|
802
|
+
* unwinds in the opposite order — sourceRef first, hash second — so
|
|
803
|
+
* the sourceRef lock is freed the instant its work is done and only
|
|
804
|
+
* the hash lock keeps serializing the duplicate-content race window.
|
|
805
|
+
*
|
|
806
|
+
* On a sourceRef conflict (WikiBusyError thrown synchronously by the
|
|
807
|
+
* underlying acquireLock) the hash lock is released BEFORE the error
|
|
808
|
+
* propagates, so the next caller in the hash chain is not held up by
|
|
809
|
+
* a request that never reached the DB.
|
|
810
|
+
*/
|
|
811
|
+
async acquireIngestLocks(entityId, sourceRef, sourceHash) {
|
|
812
|
+
const releaseHash = await this.acquireHashLock(entityId, sourceHash);
|
|
813
|
+
let sourceLockAcquired = false;
|
|
814
|
+
try {
|
|
815
|
+
this.acquireLock("ingest", entityId, sourceRef);
|
|
816
|
+
sourceLockAcquired = true;
|
|
817
|
+
} catch (err) {
|
|
818
|
+
releaseHash();
|
|
819
|
+
throw err;
|
|
820
|
+
}
|
|
821
|
+
return () => {
|
|
822
|
+
try {
|
|
823
|
+
if (sourceLockAcquired) this.releaseLock("ingest", entityId, sourceRef);
|
|
824
|
+
} finally {
|
|
825
|
+
releaseHash();
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
}
|
|
733
829
|
/**
|
|
734
830
|
* Validates then acquires global + per-entity import locks atomically.
|
|
735
831
|
* Validates all entities before acquiring any lock (same as current importDump semantics).
|
|
@@ -1170,11 +1266,12 @@ var DEFAULT_CHUNK_OVERLAP = 400;
|
|
|
1170
1266
|
|
|
1171
1267
|
// src/services/IngestionService.ts
|
|
1172
1268
|
var IngestionService = class {
|
|
1173
|
-
constructor(db, prefix, options, entryRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1269
|
+
constructor(db, prefix, options, entryRepo, sourceRefIndexRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1174
1270
|
this.db = db;
|
|
1175
1271
|
this.prefix = prefix;
|
|
1176
1272
|
this.options = options;
|
|
1177
1273
|
this.entryRepo = entryRepo;
|
|
1274
|
+
this.sourceRefIndexRepo = sourceRefIndexRepo;
|
|
1178
1275
|
this.searchService = searchService;
|
|
1179
1276
|
this.jobManager = jobManager;
|
|
1180
1277
|
this.embeddingService = embeddingService;
|
|
@@ -1186,18 +1283,6 @@ var IngestionService = class {
|
|
|
1186
1283
|
if (!sourceRef) throw new Error("Invalid sourceRef");
|
|
1187
1284
|
const sourceHash = normalizeSourceHash(params.sourceHash);
|
|
1188
1285
|
if (!sourceHash) throw new Error("Invalid sourceHash (must be 64-char hex string)");
|
|
1189
|
-
const onDuplicateHash = opts?.onDuplicateHash ?? "ingest";
|
|
1190
|
-
if (onDuplicateHash !== "ingest") {
|
|
1191
|
-
const refs = await this.entryRepo.findSourceRefsByHash(entityId, sourceHash);
|
|
1192
|
-
const others = refs.filter((r) => r !== sourceRef);
|
|
1193
|
-
if (others.length > 0) {
|
|
1194
|
-
const canonical = others[0];
|
|
1195
|
-
if (onDuplicateHash === "throw") {
|
|
1196
|
-
throw new WikiDuplicateHashError({ canonical, sourceHash, entityId });
|
|
1197
|
-
}
|
|
1198
|
-
return { truncated: false, chunks: 0, duplicateOf: canonical };
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
1286
|
const maxChunkLength = params.maxChunkLength ?? this.options.config?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
|
|
1202
1287
|
const rawOverlap = params.chunkOverlap ?? this.options.config?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
|
|
1203
1288
|
const chunkOverlap = Math.min(
|
|
@@ -1209,8 +1294,18 @@ var IngestionService = class {
|
|
|
1209
1294
|
if (typeof params.documentChunk !== "string") {
|
|
1210
1295
|
throw new Error(`documentChunk must be a string, received ${typeof params.documentChunk}`);
|
|
1211
1296
|
}
|
|
1212
|
-
this.jobManager.
|
|
1297
|
+
const releaseIngestLocks = await this.jobManager.acquireIngestLocks(entityId, sourceRef, sourceHash);
|
|
1213
1298
|
try {
|
|
1299
|
+
const onDuplicateHash = opts?.onDuplicateHash ?? "ingest";
|
|
1300
|
+
if (onDuplicateHash !== "ingest") {
|
|
1301
|
+
const canonical = await this.sourceRefIndexRepo.findActiveByEntityAndHash(entityId, sourceHash);
|
|
1302
|
+
if (canonical !== null && canonical !== sourceRef) {
|
|
1303
|
+
if (onDuplicateHash === "throw") {
|
|
1304
|
+
throw new WikiDuplicateHashError({ canonical, sourceHash, entityId });
|
|
1305
|
+
}
|
|
1306
|
+
return { truncated: false, chunks: 0, duplicateOf: canonical };
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1214
1309
|
const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
|
|
1215
1310
|
if (chunks.length === 0) return { truncated: false, chunks: 0 };
|
|
1216
1311
|
const chunkResults = await withConcurrency(
|
|
@@ -1246,68 +1341,81 @@ var IngestionService = class {
|
|
|
1246
1341
|
const now = Date.now();
|
|
1247
1342
|
const insertedFacts = [];
|
|
1248
1343
|
const deletedSourceFactIds = [];
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
for (const { facts, ontology_updates } of orderedChunkFacts) {
|
|
1264
|
-
if (mode === "emergent" && ontology_updates && this.ontologyService) {
|
|
1265
|
-
manifest = await this.ontologyService.mergeEmergentUpdates(entityId, ontology_updates, tx);
|
|
1266
|
-
ontologyState = await this.ontologyService.getEffectiveState(entityId, tx);
|
|
1267
|
-
mode = ontologyState.mode;
|
|
1344
|
+
try {
|
|
1345
|
+
await this.db.withTransactionAsync(async (tx) => {
|
|
1346
|
+
deletedSourceFactIds.push(...await this.entryRepo.findIdsBySource(entityId, sourceRef, null, tx, false));
|
|
1347
|
+
await this.entryRepo.softDeleteBySource(entityId, tx, sourceRef, null);
|
|
1348
|
+
await this.sourceRefIndexRepo.softDeleteByEntityAndSourceRef(entityId, sourceRef, tx);
|
|
1349
|
+
await this.sourceRefIndexRepo.upsert(entityId, sourceHash, sourceRef, tx);
|
|
1350
|
+
const titleIndex = /* @__PURE__ */ new Map();
|
|
1351
|
+
const pendingEdges = [];
|
|
1352
|
+
const existingFacts = await this.entryRepo.findRecentByEntityId(entityId, 500, tx);
|
|
1353
|
+
for (const existing of existingFacts) {
|
|
1354
|
+
titleIndex.set(normalizeTitleKey(existing.title), {
|
|
1355
|
+
id: existing.id,
|
|
1356
|
+
okf_type: existing.okf_type ?? null
|
|
1357
|
+
});
|
|
1268
1358
|
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1359
|
+
let ontologyState = await this.ontologyService?.getEffectiveState(entityId, tx) ?? { mode: "off", manifest: { node_types: [], edge_types: [] } };
|
|
1360
|
+
let { mode, manifest } = ontologyState;
|
|
1361
|
+
for (const { facts, ontology_updates } of orderedChunkFacts) {
|
|
1362
|
+
if (mode === "emergent" && ontology_updates && this.ontologyService) {
|
|
1363
|
+
manifest = await this.ontologyService.mergeEmergentUpdates(entityId, ontology_updates, tx);
|
|
1364
|
+
ontologyState = await this.ontologyService.getEffectiveState(entityId, tx);
|
|
1365
|
+
mode = ontologyState.mode;
|
|
1366
|
+
}
|
|
1367
|
+
for (const fact of facts) {
|
|
1368
|
+
const ontologyFact = fact;
|
|
1369
|
+
const normalized = this.ontologyService?.validateAndNormalizeFact(ontologyFact, manifest) ?? { okf_type: null, edges: [] };
|
|
1370
|
+
const id = generateId("fact_");
|
|
1371
|
+
const wikiFact = {
|
|
1372
|
+
id,
|
|
1373
|
+
entity_id: entityId,
|
|
1374
|
+
title: fact.title,
|
|
1375
|
+
body: fact.body,
|
|
1376
|
+
tags: fact.tags,
|
|
1377
|
+
confidence: fact.confidence,
|
|
1378
|
+
source_type: "immutable_document",
|
|
1379
|
+
source_hash: sourceHash,
|
|
1380
|
+
source_ref: sourceRef,
|
|
1381
|
+
created_at: now,
|
|
1382
|
+
updated_at: now,
|
|
1383
|
+
last_accessed_at: null,
|
|
1384
|
+
access_count: 0,
|
|
1385
|
+
deleted_at: null,
|
|
1386
|
+
okf_type: normalized.okf_type
|
|
1387
|
+
};
|
|
1388
|
+
await this.entryRepo.upsert(wikiFact, tx);
|
|
1389
|
+
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1390
|
+
titleIndex.set(normalizeTitleKey(fact.title), { id, okf_type: normalized.okf_type });
|
|
1391
|
+
if (normalized.edges.length > 0) {
|
|
1392
|
+
pendingEdges.push({ sourceId: id, sourceType: normalized.okf_type, edges: normalized.edges });
|
|
1393
|
+
}
|
|
1295
1394
|
}
|
|
1296
1395
|
}
|
|
1396
|
+
for (const item of pendingEdges) {
|
|
1397
|
+
await this.ontologyService?.resolveAndPersistEdges(
|
|
1398
|
+
entityId,
|
|
1399
|
+
item.sourceId,
|
|
1400
|
+
item.sourceType,
|
|
1401
|
+
item.edges ?? [],
|
|
1402
|
+
manifest,
|
|
1403
|
+
titleIndex,
|
|
1404
|
+
tx,
|
|
1405
|
+
now
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
} catch (err) {
|
|
1410
|
+
const sqliteCode = err instanceof WikiTransactionError ? err.sqliteErrorCode : extractSqliteCode(err);
|
|
1411
|
+
if (sqliteCode !== "SQLITE_CONSTRAINT_UNIQUE") throw err;
|
|
1412
|
+
const canonical = await this.sourceRefIndexRepo.findActiveByEntityAndHash(entityId, sourceHash);
|
|
1413
|
+
if (canonical === null) throw err;
|
|
1414
|
+
if (onDuplicateHash === "throw" || onDuplicateHash === "ingest") {
|
|
1415
|
+
throw new WikiDuplicateHashError({ canonical, sourceHash, entityId });
|
|
1297
1416
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
entityId,
|
|
1301
|
-
item.sourceId,
|
|
1302
|
-
item.sourceType,
|
|
1303
|
-
item.edges ?? [],
|
|
1304
|
-
manifest,
|
|
1305
|
-
titleIndex,
|
|
1306
|
-
tx,
|
|
1307
|
-
now
|
|
1308
|
-
);
|
|
1309
|
-
}
|
|
1310
|
-
});
|
|
1417
|
+
return { truncated: false, chunks: 0, duplicateOf: canonical };
|
|
1418
|
+
}
|
|
1311
1419
|
await this.searchService.sync(entityId);
|
|
1312
1420
|
const uniqueDeletedSourceFactIds = Array.from(new Set(deletedSourceFactIds));
|
|
1313
1421
|
for (const factId of uniqueDeletedSourceFactIds) {
|
|
@@ -1323,7 +1431,7 @@ var IngestionService = class {
|
|
|
1323
1431
|
this.searchService.evictCache(entityId);
|
|
1324
1432
|
return { truncated, chunks: chunks.length };
|
|
1325
1433
|
} finally {
|
|
1326
|
-
|
|
1434
|
+
releaseIngestLocks();
|
|
1327
1435
|
}
|
|
1328
1436
|
}
|
|
1329
1437
|
};
|
|
@@ -1610,11 +1718,12 @@ var HEAL_MAX_PROMPT_CHARS = 4e4;
|
|
|
1610
1718
|
var HEAL_BATCH_SIZE = 25;
|
|
1611
1719
|
var HEAL_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1612
1720
|
var MaintenanceService = class {
|
|
1613
|
-
constructor(db, prefix, options, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1721
|
+
constructor(db, prefix, options, entryRepo, sourceRefIndexRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1614
1722
|
this.db = db;
|
|
1615
1723
|
this.prefix = prefix;
|
|
1616
1724
|
this.options = options;
|
|
1617
1725
|
this.entryRepo = entryRepo;
|
|
1726
|
+
this.sourceRefIndexRepo = sourceRefIndexRepo;
|
|
1618
1727
|
this.taskRepo = taskRepo;
|
|
1619
1728
|
this.eventRepo = eventRepo;
|
|
1620
1729
|
this.metadataRepo = metadataRepo;
|
|
@@ -1813,10 +1922,12 @@ var MaintenanceService = class {
|
|
|
1813
1922
|
const entryPromise = params.entryId ? this.entryRepo.softDelete(params.entryId, entityId, tx).then((r) => r.changes > 0) : null;
|
|
1814
1923
|
const taskDeletedPromise = params.taskId ? this.taskRepo.softDeleteById(params.taskId, entityId, tx).then((r) => r.changes > 0) : null;
|
|
1815
1924
|
const refPromise = sourceRef || sourceHash ? this.entryRepo.softDeleteBySource(entityId, tx, sourceRef, sourceHash) : null;
|
|
1925
|
+
const sourceRefIndexPromise = sourceRef ? this.sourceRefIndexRepo.softDeleteByEntityAndSourceRef(entityId, sourceRef, tx).then(() => void 0) : null;
|
|
1816
1926
|
const [entryResult, taskResult, refResult] = await Promise.all([
|
|
1817
1927
|
entryPromise ?? Promise.resolve(false),
|
|
1818
1928
|
taskDeletedPromise ?? Promise.resolve(false),
|
|
1819
|
-
refPromise ?? Promise.resolve(0)
|
|
1929
|
+
refPromise ?? Promise.resolve(0),
|
|
1930
|
+
sourceRefIndexPromise ?? Promise.resolve(void 0)
|
|
1820
1931
|
]);
|
|
1821
1932
|
if (entryResult) deletedEntries++;
|
|
1822
1933
|
if (taskResult) deletedTasks++;
|
|
@@ -3631,5 +3742,5 @@ var WriteService = class {
|
|
|
3631
3742
|
};
|
|
3632
3743
|
|
|
3633
3744
|
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, EmbeddingService, HEAL_BATCH_SIZE, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDuplicateHashError, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, validateInlineEdges, validateManifest };
|
|
3634
|
-
//# sourceMappingURL=chunk-
|
|
3635
|
-
//# sourceMappingURL=chunk-
|
|
3745
|
+
//# sourceMappingURL=chunk-6HNIOKT4.mjs.map
|
|
3746
|
+
//# sourceMappingURL=chunk-6HNIOKT4.mjs.map
|