@inerrata-corporation/errata 2.0.22-dev.4164 → 2.0.22-dev.4180
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/consolidate-worker.mjs +203 -26
- package/errata.mjs +265 -54
- package/generalize-worker.mjs +204 -27
- package/package.json +1 -1
- package/pass-worker.mjs +212 -33
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/generalize-worker.mjs
CHANGED
|
@@ -14788,20 +14788,20 @@ function openDatabase(path2, opts) {
|
|
|
14788
14788
|
} catch {
|
|
14789
14789
|
}
|
|
14790
14790
|
}
|
|
14791
|
-
const wrapStmt = (
|
|
14791
|
+
const wrapStmt = (stmt2) => ({
|
|
14792
14792
|
run: (...params) => {
|
|
14793
|
-
const r =
|
|
14793
|
+
const r = stmt2.run(...coerceParams(params));
|
|
14794
14794
|
return {
|
|
14795
14795
|
changes: Number(r.changes),
|
|
14796
14796
|
lastInsertRowid: Number(r.lastInsertRowid)
|
|
14797
14797
|
};
|
|
14798
14798
|
},
|
|
14799
14799
|
get: (...params) => {
|
|
14800
|
-
const r =
|
|
14800
|
+
const r = stmt2.get(...coerceParams(params));
|
|
14801
14801
|
return r;
|
|
14802
14802
|
},
|
|
14803
14803
|
all: (...params) => {
|
|
14804
|
-
const r =
|
|
14804
|
+
const r = stmt2.all(...coerceParams(params));
|
|
14805
14805
|
return r;
|
|
14806
14806
|
}
|
|
14807
14807
|
});
|
|
@@ -15661,6 +15661,169 @@ function textIndexReady(db) {
|
|
|
15661
15661
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
15662
15662
|
}
|
|
15663
15663
|
|
|
15664
|
+
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
15665
|
+
var statements = /* @__PURE__ */ new WeakMap();
|
|
15666
|
+
function stmt(db, sql) {
|
|
15667
|
+
let byDb = statements.get(db);
|
|
15668
|
+
if (!byDb) statements.set(db, byDb = /* @__PURE__ */ new Map());
|
|
15669
|
+
let s = byDb.get(sql);
|
|
15670
|
+
if (!s) byDb.set(sql, s = db.prepare(sql));
|
|
15671
|
+
return s;
|
|
15672
|
+
}
|
|
15673
|
+
var HISTORY_EDGE_TYPES = [
|
|
15674
|
+
"SUPERSEDED_BY",
|
|
15675
|
+
"CONTINUES",
|
|
15676
|
+
"FORKED_FROM",
|
|
15677
|
+
"REGRESSION_OF",
|
|
15678
|
+
"SPLIT_INTO",
|
|
15679
|
+
"REVEALED_BY"
|
|
15680
|
+
];
|
|
15681
|
+
var LINEAGE_INTEGRITY_KEY = "lineage-integrity-v1";
|
|
15682
|
+
var HISTORY_SQL = HISTORY_EDGE_TYPES.map((t) => `'${t}'`).join(",");
|
|
15683
|
+
var NOT_HISTORY = `type NOT IN (${HISTORY_SQL})`;
|
|
15684
|
+
var NOT_HISTORICAL_ROW = `json_type(attrs_json, '$.historical') IS NOT 'true'`;
|
|
15685
|
+
var RETAINS_EDGES = (n) => `(${n}.label = 'Problem'
|
|
15686
|
+
AND json_type(${n}.attrs_json, '$.resolvedAt') IS NOT NULL
|
|
15687
|
+
AND COALESCE(json_extract(${n}.attrs_json, '$.resolvedAs'), '') NOT IN ('duplicate', 'false_positive'))`;
|
|
15688
|
+
var PRESENT = (n) => `(${n}.valid_to IS NULL OR ${RETAINS_EDGES(n)})`;
|
|
15689
|
+
var STRANDED_EDGE_PREDICATE = `e.valid_to IS NULL AND e.${NOT_HISTORY}
|
|
15690
|
+
AND (NOT EXISTS (SELECT 1 FROM nodes f WHERE f.id = e.from_id AND ${PRESENT("f")})
|
|
15691
|
+
OR NOT EXISTS (SELECT 1 FROM nodes t WHERE t.id = e.to_id AND ${PRESENT("t")}))`;
|
|
15692
|
+
function countStrandedEdges(db) {
|
|
15693
|
+
const rows = db.prepare(`SELECT e.type AS type, count(*) AS n FROM edges e WHERE ${STRANDED_EDGE_PREDICATE} GROUP BY e.type ORDER BY n DESC`).all();
|
|
15694
|
+
const byType = {};
|
|
15695
|
+
let total = 0;
|
|
15696
|
+
for (const r of rows) {
|
|
15697
|
+
byType[r.type] = Number(r.n);
|
|
15698
|
+
total += Number(r.n);
|
|
15699
|
+
}
|
|
15700
|
+
return { total, byType };
|
|
15701
|
+
}
|
|
15702
|
+
function countLiveHeadConflicts(db) {
|
|
15703
|
+
const row = db.prepare(
|
|
15704
|
+
`SELECT count(*) AS n FROM (
|
|
15705
|
+
SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
15706
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
15707
|
+
GROUP BY lid HAVING count(*) > 1)`
|
|
15708
|
+
).get();
|
|
15709
|
+
return Number(row?.n ?? 0);
|
|
15710
|
+
}
|
|
15711
|
+
function closeIncidentEdges(db, id, t) {
|
|
15712
|
+
let closed = 0;
|
|
15713
|
+
for (const side of ["from_id", "to_id"]) {
|
|
15714
|
+
const r = stmt(db, `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE ${side} = ? AND valid_to IS NULL AND ${NOT_HISTORY}`).run(t, id);
|
|
15715
|
+
closed += Number(r.changes);
|
|
15716
|
+
}
|
|
15717
|
+
return closed;
|
|
15718
|
+
}
|
|
15719
|
+
function reopenCascadeClosedEdges(db, id, closedAt) {
|
|
15720
|
+
let reopened = 0;
|
|
15721
|
+
for (const [side, other] of [["from_id", "to_id"], ["to_id", "from_id"]]) {
|
|
15722
|
+
const r = stmt(
|
|
15723
|
+
db,
|
|
15724
|
+
`UPDATE edges SET valid_to = NULL
|
|
15725
|
+
WHERE ${side} = ? AND valid_to = ? AND ${NOT_HISTORY}
|
|
15726
|
+
AND EXISTS (SELECT 1 FROM nodes o WHERE o.id = edges.${other} AND o.valid_to IS NULL)`
|
|
15727
|
+
).run(id, closedAt);
|
|
15728
|
+
reopened += Number(r.changes);
|
|
15729
|
+
}
|
|
15730
|
+
return reopened;
|
|
15731
|
+
}
|
|
15732
|
+
function collapseLiveHeads(db, logicalIds, t) {
|
|
15733
|
+
const headsOf = db.prepare(
|
|
15734
|
+
`SELECT id, version, valid_from FROM (
|
|
15735
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE logical_id = ?1 AND valid_to IS NULL
|
|
15736
|
+
UNION
|
|
15737
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE id = ?1 AND logical_id IS NULL AND valid_to IS NULL
|
|
15738
|
+
) WHERE ${NOT_HISTORICAL_ROW}`
|
|
15739
|
+
);
|
|
15740
|
+
const continuesInto = db.prepare(
|
|
15741
|
+
`SELECT to_id FROM edges WHERE from_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
15742
|
+
);
|
|
15743
|
+
const rekeyHead = db.prepare(`UPDATE nodes SET logical_id = ?1 WHERE id = ?1`);
|
|
15744
|
+
const rekeyCopies = db.prepare(
|
|
15745
|
+
`UPDATE nodes SET logical_id = ?1 WHERE logical_id = ?2 AND substr(id, 1, length(?1) + 2) = ?1 || '@v'`
|
|
15746
|
+
);
|
|
15747
|
+
const lineageMembers = db.prepare(
|
|
15748
|
+
`SELECT id FROM nodes WHERE logical_id = ?1 UNION SELECT id FROM nodes WHERE id = ?1`
|
|
15749
|
+
);
|
|
15750
|
+
const closeContinues = db.prepare(
|
|
15751
|
+
`UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE from_id = ? AND to_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
15752
|
+
);
|
|
15753
|
+
let split = 0;
|
|
15754
|
+
const queue = [...new Set(logicalIds)];
|
|
15755
|
+
for (let i = 0; i < queue.length; i++) {
|
|
15756
|
+
const lid = queue[i];
|
|
15757
|
+
const heads = headsOf.all(lid);
|
|
15758
|
+
if (heads.length < 2) continue;
|
|
15759
|
+
const members = new Set(lineageMembers.all(lid).map((r) => r.id));
|
|
15760
|
+
const splitOff = (h) => {
|
|
15761
|
+
for (const { to_id } of continuesInto.all(h.id)) {
|
|
15762
|
+
if (members.has(to_id)) closeContinues.run(t, h.id, to_id);
|
|
15763
|
+
}
|
|
15764
|
+
rekeyHead.run(h.id);
|
|
15765
|
+
rekeyCopies.run(h.id, lid);
|
|
15766
|
+
queue.push(h.id);
|
|
15767
|
+
split++;
|
|
15768
|
+
};
|
|
15769
|
+
const kept = new Set(heads.map((h) => h.id));
|
|
15770
|
+
for (const h of heads) {
|
|
15771
|
+
if (h.id === lid) continue;
|
|
15772
|
+
const refuted = continuesInto.all(h.id).some(
|
|
15773
|
+
(e) => e.to_id !== h.id && kept.has(e.to_id)
|
|
15774
|
+
);
|
|
15775
|
+
if (refuted && kept.size > 1) {
|
|
15776
|
+
splitOff(h);
|
|
15777
|
+
kept.delete(h.id);
|
|
15778
|
+
}
|
|
15779
|
+
}
|
|
15780
|
+
let remaining = heads.filter((h) => kept.has(h.id));
|
|
15781
|
+
if (remaining.length < 2) continue;
|
|
15782
|
+
remaining = [...remaining].sort(
|
|
15783
|
+
(a, b) => Number(b.id === lid) - Number(a.id === lid) || Number(b.version) - Number(a.version) || Number(b.valid_from) - Number(a.valid_from) || (a.id < b.id ? -1 : 1)
|
|
15784
|
+
);
|
|
15785
|
+
for (const h of remaining.slice(1)) splitOff(h);
|
|
15786
|
+
}
|
|
15787
|
+
return split;
|
|
15788
|
+
}
|
|
15789
|
+
function repairLineageIntegrity(db, now) {
|
|
15790
|
+
const historicalRowsClosed = Number(
|
|
15791
|
+
db.prepare(
|
|
15792
|
+
`UPDATE nodes SET valid_to = MAX(valid_from, COALESCE(
|
|
15793
|
+
(SELECT s.valid_from FROM edges s WHERE s.id = 'edge_superseded_' || nodes.id), ?))
|
|
15794
|
+
WHERE valid_to IS NULL AND json_type(attrs_json, '$.historical') = 'true'`
|
|
15795
|
+
).run(now).changes
|
|
15796
|
+
);
|
|
15797
|
+
const conflicted = db.prepare(
|
|
15798
|
+
`SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
15799
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
15800
|
+
GROUP BY lid HAVING count(*) > 1`
|
|
15801
|
+
).all().map((r) => r.lid);
|
|
15802
|
+
const headsSplit = collapseLiveHeads(db, conflicted, now);
|
|
15803
|
+
const strandedEdgesClosed = Number(
|
|
15804
|
+
db.prepare(
|
|
15805
|
+
`UPDATE edges SET valid_to = MAX(valid_from, COALESCE(
|
|
15806
|
+
(SELECT min(v) FROM (
|
|
15807
|
+
SELECT f.valid_to AS v FROM nodes f WHERE f.id = edges.from_id AND NOT ${PRESENT("f")}
|
|
15808
|
+
UNION ALL
|
|
15809
|
+
SELECT t.valid_to FROM nodes t WHERE t.id = edges.to_id AND NOT ${PRESENT("t")})), ?))
|
|
15810
|
+
WHERE id IN (SELECT e.id FROM edges e WHERE ${STRANDED_EDGE_PREDICATE})`
|
|
15811
|
+
).run(now).changes
|
|
15812
|
+
);
|
|
15813
|
+
return { historicalRowsClosed, headsSplit, strandedEdgesClosed };
|
|
15814
|
+
}
|
|
15815
|
+
function installLineageIntegrityRepair(db, now = Date.now()) {
|
|
15816
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(LINEAGE_INTEGRITY_KEY)) return null;
|
|
15817
|
+
return db.transaction(() => {
|
|
15818
|
+
const report = repairLineageIntegrity(db, now);
|
|
15819
|
+
db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, ?)").run(
|
|
15820
|
+
LINEAGE_INTEGRITY_KEY,
|
|
15821
|
+
JSON.stringify({ at: now, ...report })
|
|
15822
|
+
);
|
|
15823
|
+
return report;
|
|
15824
|
+
});
|
|
15825
|
+
}
|
|
15826
|
+
|
|
15664
15827
|
// ../../packages/math/src/relevance.ts
|
|
15665
15828
|
function pageRankScale(ranks) {
|
|
15666
15829
|
const positive = [...ranks].filter((r) => Number.isFinite(r) && r > 0).sort((a, b) => a - b);
|
|
@@ -16107,6 +16270,7 @@ var SqliteGraphStore = class {
|
|
|
16107
16270
|
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
16108
16271
|
});
|
|
16109
16272
|
}
|
|
16273
|
+
installLineageIntegrityRepair(this.db);
|
|
16110
16274
|
this.stmts = {
|
|
16111
16275
|
insertNode: this.db.prepare(`
|
|
16112
16276
|
INSERT INTO nodes (
|
|
@@ -16317,6 +16481,12 @@ var SqliteGraphStore = class {
|
|
|
16317
16481
|
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
16318
16482
|
`).all();
|
|
16319
16483
|
}
|
|
16484
|
+
strandedEdges() {
|
|
16485
|
+
return countStrandedEdges(this.db);
|
|
16486
|
+
}
|
|
16487
|
+
liveHeadConflicts() {
|
|
16488
|
+
return countLiveHeadConflicts(this.db);
|
|
16489
|
+
}
|
|
16320
16490
|
fileIndexStates(ids) {
|
|
16321
16491
|
if (ids.length === 0) return [];
|
|
16322
16492
|
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
@@ -16485,15 +16655,15 @@ var SqliteGraphStore = class {
|
|
|
16485
16655
|
findNodeFieldsByLabel(label, attrKeys) {
|
|
16486
16656
|
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
|
|
16487
16657
|
const cacheKey = attrKeys.join(",");
|
|
16488
|
-
let
|
|
16489
|
-
if (!
|
|
16658
|
+
let stmt2 = this.fieldStmts.get(cacheKey);
|
|
16659
|
+
if (!stmt2) {
|
|
16490
16660
|
const cols = attrKeys.map((k, i) => `, ${attrValueSql(`'$.${k}'`)} AS a${i}, json_type(attrs_json, '$.${k}') AS t${i}`).join("");
|
|
16491
|
-
|
|
16661
|
+
stmt2 = this.db.prepare(
|
|
16492
16662
|
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
16493
16663
|
);
|
|
16494
|
-
this.fieldStmts.set(cacheKey,
|
|
16664
|
+
this.fieldStmts.set(cacheKey, stmt2);
|
|
16495
16665
|
}
|
|
16496
|
-
return
|
|
16666
|
+
return stmt2.all(label).map((r) => {
|
|
16497
16667
|
const attrs = {};
|
|
16498
16668
|
attrKeys.forEach((k, i) => {
|
|
16499
16669
|
const v = decodeAttr(r[`a${i}`], r[`t${i}`]);
|
|
@@ -16613,14 +16783,14 @@ var SqliteGraphStore = class {
|
|
|
16613
16783
|
}
|
|
16614
16784
|
nodeLabels(ids) {
|
|
16615
16785
|
const width = NODE_LABELS_CHUNK;
|
|
16616
|
-
const
|
|
16786
|
+
const stmt2 = this.typedEdgeStmt(
|
|
16617
16787
|
`SELECT id, label FROM nodes WHERE id IN (${Array.from({ length: width }, () => "?").join(",")})`
|
|
16618
16788
|
);
|
|
16619
16789
|
const labels = /* @__PURE__ */ new Map();
|
|
16620
16790
|
for (let i = 0; i < ids.length; i += width) {
|
|
16621
16791
|
const chunk = ids.slice(i, i + width);
|
|
16622
16792
|
while (chunk.length < width) chunk.push(null);
|
|
16623
|
-
for (const row of
|
|
16793
|
+
for (const row of stmt2.all(...chunk)) labels.set(row.id, row.label);
|
|
16624
16794
|
}
|
|
16625
16795
|
return labels;
|
|
16626
16796
|
}
|
|
@@ -16819,12 +16989,12 @@ var SqliteGraphStore = class {
|
|
|
16819
16989
|
return scale;
|
|
16820
16990
|
}
|
|
16821
16991
|
typedEdgeStmt(sql) {
|
|
16822
|
-
let
|
|
16823
|
-
if (!
|
|
16824
|
-
|
|
16825
|
-
this.typedEdgeStmts.set(sql,
|
|
16992
|
+
let stmt2 = this.typedEdgeStmts.get(sql);
|
|
16993
|
+
if (!stmt2) {
|
|
16994
|
+
stmt2 = this.db.prepare(sql);
|
|
16995
|
+
this.typedEdgeStmts.set(sql, stmt2);
|
|
16826
16996
|
}
|
|
16827
|
-
return
|
|
16997
|
+
return stmt2;
|
|
16828
16998
|
}
|
|
16829
16999
|
outEdges(nodeId, types) {
|
|
16830
17000
|
if (!types || types.length === 0) {
|
|
@@ -17025,22 +17195,28 @@ var SqliteGraphStore = class {
|
|
|
17025
17195
|
}
|
|
17026
17196
|
}
|
|
17027
17197
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
17028
|
-
|
|
17198
|
+
const revived = [];
|
|
17029
17199
|
const CHUNK = 400;
|
|
17030
17200
|
for (let i = 0; i < relPaths.length; i += CHUNK) {
|
|
17031
17201
|
const slice = relPaths.slice(i, i + CHUNK);
|
|
17032
17202
|
if (slice.length === 0) continue;
|
|
17033
|
-
const
|
|
17034
|
-
`
|
|
17203
|
+
const rows = this.db.prepare(
|
|
17204
|
+
`SELECT id, valid_to AS closedAt, COALESCE(logical_id, id) AS lid FROM nodes
|
|
17035
17205
|
WHERE valid_to IS NOT NULL
|
|
17036
17206
|
AND last_updated_at >= ?
|
|
17037
17207
|
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
17038
|
-
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
17039
|
-
|
|
17040
|
-
|
|
17041
|
-
|
|
17042
|
-
|
|
17043
|
-
return
|
|
17208
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
17209
|
+
AND json_type(attrs_json, '$.historical') IS NOT 'true'`
|
|
17210
|
+
).all(since, workspaceId, ...slice);
|
|
17211
|
+
revived.push(...rows);
|
|
17212
|
+
}
|
|
17213
|
+
if (revived.length === 0) return 0;
|
|
17214
|
+
const reopen = this.db.prepare("UPDATE nodes SET valid_to = NULL WHERE id = ? AND valid_to IS NOT NULL");
|
|
17215
|
+
for (const r of revived) reopen.run(r.id);
|
|
17216
|
+
for (const r of revived) reopenCascadeClosedEdges(this.db, r.id, Number(r.closedAt));
|
|
17217
|
+
collapseLiveHeads(this.db, revived.map((r) => r.lid), since);
|
|
17218
|
+
this.mutations++;
|
|
17219
|
+
return revived.length;
|
|
17044
17220
|
}
|
|
17045
17221
|
incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
|
|
17046
17222
|
if (edgeTypes.length === 0) return { count: 0, total: 0 };
|
|
@@ -17186,8 +17362,9 @@ var SqliteGraphStore = class {
|
|
|
17186
17362
|
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
17187
17363
|
return frozenId;
|
|
17188
17364
|
}
|
|
17189
|
-
closeNode(id, t) {
|
|
17190
|
-
this.stmts.closeNode.run({ id, t });
|
|
17365
|
+
closeNode(id, t, opts = {}) {
|
|
17366
|
+
const r = this.stmts.closeNode.run({ id, t });
|
|
17367
|
+
if (Number(r.changes) > 0 && !opts.retainEdges) closeIncidentEdges(this.db, id, t);
|
|
17191
17368
|
}
|
|
17192
17369
|
closeEdge(id, t) {
|
|
17193
17370
|
this.stmts.closeEdge.run({ id, t });
|
package/package.json
CHANGED
package/pass-worker.mjs
CHANGED
|
@@ -16402,20 +16402,20 @@ function openDatabase(path2, opts) {
|
|
|
16402
16402
|
} catch {
|
|
16403
16403
|
}
|
|
16404
16404
|
}
|
|
16405
|
-
const wrapStmt = (
|
|
16405
|
+
const wrapStmt = (stmt2) => ({
|
|
16406
16406
|
run: (...params) => {
|
|
16407
|
-
const r =
|
|
16407
|
+
const r = stmt2.run(...coerceParams(params));
|
|
16408
16408
|
return {
|
|
16409
16409
|
changes: Number(r.changes),
|
|
16410
16410
|
lastInsertRowid: Number(r.lastInsertRowid)
|
|
16411
16411
|
};
|
|
16412
16412
|
},
|
|
16413
16413
|
get: (...params) => {
|
|
16414
|
-
const r =
|
|
16414
|
+
const r = stmt2.get(...coerceParams(params));
|
|
16415
16415
|
return r;
|
|
16416
16416
|
},
|
|
16417
16417
|
all: (...params) => {
|
|
16418
|
-
const r =
|
|
16418
|
+
const r = stmt2.all(...coerceParams(params));
|
|
16419
16419
|
return r;
|
|
16420
16420
|
}
|
|
16421
16421
|
});
|
|
@@ -17609,9 +17609,11 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
17609
17609
|
signatureAfter: a.signature
|
|
17610
17610
|
});
|
|
17611
17611
|
}
|
|
17612
|
+
const repointable = /* @__PURE__ */ new Map();
|
|
17612
17613
|
store2.transaction(() => {
|
|
17613
17614
|
for (const nid of changedIds) store2.freezeNode(nid, t);
|
|
17614
17615
|
for (const nid of removedIds) {
|
|
17616
|
+
repointable.set(nid, store2.inEdges(nid, ["CALLS", "ACCESSES", "ANCHORED_AT"]));
|
|
17615
17617
|
for (const e of store2.outEdges(nid)) store2.closeEdge(e.id, t);
|
|
17616
17618
|
store2.closeNode(nid, t);
|
|
17617
17619
|
}
|
|
@@ -17700,8 +17702,8 @@ async function incrementalReindex(store2, rootPath, workspaceId, changedAbsPaths
|
|
|
17700
17702
|
version: b.version + 1
|
|
17701
17703
|
});
|
|
17702
17704
|
store2.mergeEdge(continuesEdge(m, t));
|
|
17703
|
-
for (const e of
|
|
17704
|
-
if (e.
|
|
17705
|
+
for (const e of repointable.get(m.fromNid) ?? []) {
|
|
17706
|
+
if (e.from === m.fromNid || !store2.liveNodeIds([e.from]).has(e.from)) continue;
|
|
17705
17707
|
const newId = edgeId(e.from, e.type, m.toNid);
|
|
17706
17708
|
if (!store2.getEdge(newId)) {
|
|
17707
17709
|
store2.mergeEdge({ ...e, id: newId, to: m.toNid, lastSeenAt: t });
|
|
@@ -23793,10 +23795,10 @@ function collectDocstrings(root, records, out2) {
|
|
|
23793
23795
|
}
|
|
23794
23796
|
}
|
|
23795
23797
|
}
|
|
23796
|
-
function docstringIn(
|
|
23797
|
-
if (!
|
|
23798
|
-
if (
|
|
23799
|
-
const expr =
|
|
23798
|
+
function docstringIn(stmt2) {
|
|
23799
|
+
if (!stmt2) return null;
|
|
23800
|
+
if (stmt2.type !== "expression_statement") return null;
|
|
23801
|
+
const expr = stmt2.namedChild(0);
|
|
23800
23802
|
if (!expr) return null;
|
|
23801
23803
|
if (expr.type !== "string") return null;
|
|
23802
23804
|
const text = expr.text;
|
|
@@ -26091,6 +26093,169 @@ function textIndexReady(db) {
|
|
|
26091
26093
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
26092
26094
|
}
|
|
26093
26095
|
|
|
26096
|
+
// ../../packages/local-graph/src/lineage-integrity.ts
|
|
26097
|
+
var statements = /* @__PURE__ */ new WeakMap();
|
|
26098
|
+
function stmt(db, sql) {
|
|
26099
|
+
let byDb = statements.get(db);
|
|
26100
|
+
if (!byDb) statements.set(db, byDb = /* @__PURE__ */ new Map());
|
|
26101
|
+
let s = byDb.get(sql);
|
|
26102
|
+
if (!s) byDb.set(sql, s = db.prepare(sql));
|
|
26103
|
+
return s;
|
|
26104
|
+
}
|
|
26105
|
+
var HISTORY_EDGE_TYPES = [
|
|
26106
|
+
"SUPERSEDED_BY",
|
|
26107
|
+
"CONTINUES",
|
|
26108
|
+
"FORKED_FROM",
|
|
26109
|
+
"REGRESSION_OF",
|
|
26110
|
+
"SPLIT_INTO",
|
|
26111
|
+
"REVEALED_BY"
|
|
26112
|
+
];
|
|
26113
|
+
var LINEAGE_INTEGRITY_KEY = "lineage-integrity-v1";
|
|
26114
|
+
var HISTORY_SQL = HISTORY_EDGE_TYPES.map((t) => `'${t}'`).join(",");
|
|
26115
|
+
var NOT_HISTORY = `type NOT IN (${HISTORY_SQL})`;
|
|
26116
|
+
var NOT_HISTORICAL_ROW = `json_type(attrs_json, '$.historical') IS NOT 'true'`;
|
|
26117
|
+
var RETAINS_EDGES = (n) => `(${n}.label = 'Problem'
|
|
26118
|
+
AND json_type(${n}.attrs_json, '$.resolvedAt') IS NOT NULL
|
|
26119
|
+
AND COALESCE(json_extract(${n}.attrs_json, '$.resolvedAs'), '') NOT IN ('duplicate', 'false_positive'))`;
|
|
26120
|
+
var PRESENT = (n) => `(${n}.valid_to IS NULL OR ${RETAINS_EDGES(n)})`;
|
|
26121
|
+
var STRANDED_EDGE_PREDICATE = `e.valid_to IS NULL AND e.${NOT_HISTORY}
|
|
26122
|
+
AND (NOT EXISTS (SELECT 1 FROM nodes f WHERE f.id = e.from_id AND ${PRESENT("f")})
|
|
26123
|
+
OR NOT EXISTS (SELECT 1 FROM nodes t WHERE t.id = e.to_id AND ${PRESENT("t")}))`;
|
|
26124
|
+
function countStrandedEdges(db) {
|
|
26125
|
+
const rows = db.prepare(`SELECT e.type AS type, count(*) AS n FROM edges e WHERE ${STRANDED_EDGE_PREDICATE} GROUP BY e.type ORDER BY n DESC`).all();
|
|
26126
|
+
const byType = {};
|
|
26127
|
+
let total = 0;
|
|
26128
|
+
for (const r of rows) {
|
|
26129
|
+
byType[r.type] = Number(r.n);
|
|
26130
|
+
total += Number(r.n);
|
|
26131
|
+
}
|
|
26132
|
+
return { total, byType };
|
|
26133
|
+
}
|
|
26134
|
+
function countLiveHeadConflicts(db) {
|
|
26135
|
+
const row = db.prepare(
|
|
26136
|
+
`SELECT count(*) AS n FROM (
|
|
26137
|
+
SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
26138
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
26139
|
+
GROUP BY lid HAVING count(*) > 1)`
|
|
26140
|
+
).get();
|
|
26141
|
+
return Number(row?.n ?? 0);
|
|
26142
|
+
}
|
|
26143
|
+
function closeIncidentEdges(db, id, t) {
|
|
26144
|
+
let closed = 0;
|
|
26145
|
+
for (const side of ["from_id", "to_id"]) {
|
|
26146
|
+
const r = stmt(db, `UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE ${side} = ? AND valid_to IS NULL AND ${NOT_HISTORY}`).run(t, id);
|
|
26147
|
+
closed += Number(r.changes);
|
|
26148
|
+
}
|
|
26149
|
+
return closed;
|
|
26150
|
+
}
|
|
26151
|
+
function reopenCascadeClosedEdges(db, id, closedAt) {
|
|
26152
|
+
let reopened = 0;
|
|
26153
|
+
for (const [side, other] of [["from_id", "to_id"], ["to_id", "from_id"]]) {
|
|
26154
|
+
const r = stmt(
|
|
26155
|
+
db,
|
|
26156
|
+
`UPDATE edges SET valid_to = NULL
|
|
26157
|
+
WHERE ${side} = ? AND valid_to = ? AND ${NOT_HISTORY}
|
|
26158
|
+
AND EXISTS (SELECT 1 FROM nodes o WHERE o.id = edges.${other} AND o.valid_to IS NULL)`
|
|
26159
|
+
).run(id, closedAt);
|
|
26160
|
+
reopened += Number(r.changes);
|
|
26161
|
+
}
|
|
26162
|
+
return reopened;
|
|
26163
|
+
}
|
|
26164
|
+
function collapseLiveHeads(db, logicalIds, t) {
|
|
26165
|
+
const headsOf = db.prepare(
|
|
26166
|
+
`SELECT id, version, valid_from FROM (
|
|
26167
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE logical_id = ?1 AND valid_to IS NULL
|
|
26168
|
+
UNION
|
|
26169
|
+
SELECT id, version, valid_from, attrs_json FROM nodes WHERE id = ?1 AND logical_id IS NULL AND valid_to IS NULL
|
|
26170
|
+
) WHERE ${NOT_HISTORICAL_ROW}`
|
|
26171
|
+
);
|
|
26172
|
+
const continuesInto = db.prepare(
|
|
26173
|
+
`SELECT to_id FROM edges WHERE from_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
26174
|
+
);
|
|
26175
|
+
const rekeyHead = db.prepare(`UPDATE nodes SET logical_id = ?1 WHERE id = ?1`);
|
|
26176
|
+
const rekeyCopies = db.prepare(
|
|
26177
|
+
`UPDATE nodes SET logical_id = ?1 WHERE logical_id = ?2 AND substr(id, 1, length(?1) + 2) = ?1 || '@v'`
|
|
26178
|
+
);
|
|
26179
|
+
const lineageMembers = db.prepare(
|
|
26180
|
+
`SELECT id FROM nodes WHERE logical_id = ?1 UNION SELECT id FROM nodes WHERE id = ?1`
|
|
26181
|
+
);
|
|
26182
|
+
const closeContinues = db.prepare(
|
|
26183
|
+
`UPDATE edges SET valid_to = MAX(valid_from, ?) WHERE from_id = ? AND to_id = ? AND type = 'CONTINUES' AND valid_to IS NULL`
|
|
26184
|
+
);
|
|
26185
|
+
let split = 0;
|
|
26186
|
+
const queue = [...new Set(logicalIds)];
|
|
26187
|
+
for (let i2 = 0; i2 < queue.length; i2++) {
|
|
26188
|
+
const lid = queue[i2];
|
|
26189
|
+
const heads = headsOf.all(lid);
|
|
26190
|
+
if (heads.length < 2) continue;
|
|
26191
|
+
const members = new Set(lineageMembers.all(lid).map((r) => r.id));
|
|
26192
|
+
const splitOff = (h) => {
|
|
26193
|
+
for (const { to_id } of continuesInto.all(h.id)) {
|
|
26194
|
+
if (members.has(to_id)) closeContinues.run(t, h.id, to_id);
|
|
26195
|
+
}
|
|
26196
|
+
rekeyHead.run(h.id);
|
|
26197
|
+
rekeyCopies.run(h.id, lid);
|
|
26198
|
+
queue.push(h.id);
|
|
26199
|
+
split++;
|
|
26200
|
+
};
|
|
26201
|
+
const kept = new Set(heads.map((h) => h.id));
|
|
26202
|
+
for (const h of heads) {
|
|
26203
|
+
if (h.id === lid) continue;
|
|
26204
|
+
const refuted = continuesInto.all(h.id).some(
|
|
26205
|
+
(e) => e.to_id !== h.id && kept.has(e.to_id)
|
|
26206
|
+
);
|
|
26207
|
+
if (refuted && kept.size > 1) {
|
|
26208
|
+
splitOff(h);
|
|
26209
|
+
kept.delete(h.id);
|
|
26210
|
+
}
|
|
26211
|
+
}
|
|
26212
|
+
let remaining = heads.filter((h) => kept.has(h.id));
|
|
26213
|
+
if (remaining.length < 2) continue;
|
|
26214
|
+
remaining = [...remaining].sort(
|
|
26215
|
+
(a, b) => Number(b.id === lid) - Number(a.id === lid) || Number(b.version) - Number(a.version) || Number(b.valid_from) - Number(a.valid_from) || (a.id < b.id ? -1 : 1)
|
|
26216
|
+
);
|
|
26217
|
+
for (const h of remaining.slice(1)) splitOff(h);
|
|
26218
|
+
}
|
|
26219
|
+
return split;
|
|
26220
|
+
}
|
|
26221
|
+
function repairLineageIntegrity(db, now) {
|
|
26222
|
+
const historicalRowsClosed = Number(
|
|
26223
|
+
db.prepare(
|
|
26224
|
+
`UPDATE nodes SET valid_to = MAX(valid_from, COALESCE(
|
|
26225
|
+
(SELECT s.valid_from FROM edges s WHERE s.id = 'edge_superseded_' || nodes.id), ?))
|
|
26226
|
+
WHERE valid_to IS NULL AND json_type(attrs_json, '$.historical') = 'true'`
|
|
26227
|
+
).run(now).changes
|
|
26228
|
+
);
|
|
26229
|
+
const conflicted = db.prepare(
|
|
26230
|
+
`SELECT COALESCE(logical_id, id) AS lid FROM nodes
|
|
26231
|
+
WHERE valid_to IS NULL AND ${NOT_HISTORICAL_ROW}
|
|
26232
|
+
GROUP BY lid HAVING count(*) > 1`
|
|
26233
|
+
).all().map((r) => r.lid);
|
|
26234
|
+
const headsSplit = collapseLiveHeads(db, conflicted, now);
|
|
26235
|
+
const strandedEdgesClosed = Number(
|
|
26236
|
+
db.prepare(
|
|
26237
|
+
`UPDATE edges SET valid_to = MAX(valid_from, COALESCE(
|
|
26238
|
+
(SELECT min(v) FROM (
|
|
26239
|
+
SELECT f.valid_to AS v FROM nodes f WHERE f.id = edges.from_id AND NOT ${PRESENT("f")}
|
|
26240
|
+
UNION ALL
|
|
26241
|
+
SELECT t.valid_to FROM nodes t WHERE t.id = edges.to_id AND NOT ${PRESENT("t")})), ?))
|
|
26242
|
+
WHERE id IN (SELECT e.id FROM edges e WHERE ${STRANDED_EDGE_PREDICATE})`
|
|
26243
|
+
).run(now).changes
|
|
26244
|
+
);
|
|
26245
|
+
return { historicalRowsClosed, headsSplit, strandedEdgesClosed };
|
|
26246
|
+
}
|
|
26247
|
+
function installLineageIntegrityRepair(db, now = Date.now()) {
|
|
26248
|
+
if (db.prepare("SELECT value FROM store_meta WHERE key = ?").get(LINEAGE_INTEGRITY_KEY)) return null;
|
|
26249
|
+
return db.transaction(() => {
|
|
26250
|
+
const report = repairLineageIntegrity(db, now);
|
|
26251
|
+
db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, ?)").run(
|
|
26252
|
+
LINEAGE_INTEGRITY_KEY,
|
|
26253
|
+
JSON.stringify({ at: now, ...report })
|
|
26254
|
+
);
|
|
26255
|
+
return report;
|
|
26256
|
+
});
|
|
26257
|
+
}
|
|
26258
|
+
|
|
26094
26259
|
// ../../packages/math/src/pagerank.ts
|
|
26095
26260
|
function pagerank(input) {
|
|
26096
26261
|
const damping = input.damping ?? 0.85;
|
|
@@ -26973,6 +27138,7 @@ var SqliteGraphStore = class {
|
|
|
26973
27138
|
this.db.prepare("INSERT OR REPLACE INTO store_meta (key, value) VALUES (?, 'done')").run(SESSION_REPAIR_KEY);
|
|
26974
27139
|
});
|
|
26975
27140
|
}
|
|
27141
|
+
installLineageIntegrityRepair(this.db);
|
|
26976
27142
|
this.stmts = {
|
|
26977
27143
|
insertNode: this.db.prepare(`
|
|
26978
27144
|
INSERT INTO nodes (
|
|
@@ -27183,6 +27349,12 @@ var SqliteGraphStore = class {
|
|
|
27183
27349
|
AND (t.id IS NULL OR t.valid_to IS NOT NULL)
|
|
27184
27350
|
`).all();
|
|
27185
27351
|
}
|
|
27352
|
+
strandedEdges() {
|
|
27353
|
+
return countStrandedEdges(this.db);
|
|
27354
|
+
}
|
|
27355
|
+
liveHeadConflicts() {
|
|
27356
|
+
return countLiveHeadConflicts(this.db);
|
|
27357
|
+
}
|
|
27186
27358
|
fileIndexStates(ids) {
|
|
27187
27359
|
if (ids.length === 0) return [];
|
|
27188
27360
|
if (ids.length > 500) throw new RangeError("fileIndexStates accepts at most 500 ids per read");
|
|
@@ -27351,15 +27523,15 @@ var SqliteGraphStore = class {
|
|
|
27351
27523
|
findNodeFieldsByLabel(label, attrKeys) {
|
|
27352
27524
|
for (const k of attrKeys) if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(k)) throw new Error(`findNodeFieldsByLabel: bad attr key ${k}`);
|
|
27353
27525
|
const cacheKey = attrKeys.join(",");
|
|
27354
|
-
let
|
|
27355
|
-
if (!
|
|
27526
|
+
let stmt2 = this.fieldStmts.get(cacheKey);
|
|
27527
|
+
if (!stmt2) {
|
|
27356
27528
|
const cols = attrKeys.map((k, i2) => `, ${attrValueSql(`'$.${k}'`)} AS a${i2}, json_type(attrs_json, '$.${k}') AS t${i2}`).join("");
|
|
27357
|
-
|
|
27529
|
+
stmt2 = this.db.prepare(
|
|
27358
27530
|
`SELECT id, description, community, extraction_confidence${cols} FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
27359
27531
|
);
|
|
27360
|
-
this.fieldStmts.set(cacheKey,
|
|
27532
|
+
this.fieldStmts.set(cacheKey, stmt2);
|
|
27361
27533
|
}
|
|
27362
|
-
return
|
|
27534
|
+
return stmt2.all(label).map((r) => {
|
|
27363
27535
|
const attrs = {};
|
|
27364
27536
|
attrKeys.forEach((k, i2) => {
|
|
27365
27537
|
const v = decodeAttr(r[`a${i2}`], r[`t${i2}`]);
|
|
@@ -27479,14 +27651,14 @@ var SqliteGraphStore = class {
|
|
|
27479
27651
|
}
|
|
27480
27652
|
nodeLabels(ids) {
|
|
27481
27653
|
const width = NODE_LABELS_CHUNK;
|
|
27482
|
-
const
|
|
27654
|
+
const stmt2 = this.typedEdgeStmt(
|
|
27483
27655
|
`SELECT id, label FROM nodes WHERE id IN (${Array.from({ length: width }, () => "?").join(",")})`
|
|
27484
27656
|
);
|
|
27485
27657
|
const labels = /* @__PURE__ */ new Map();
|
|
27486
27658
|
for (let i2 = 0; i2 < ids.length; i2 += width) {
|
|
27487
27659
|
const chunk = ids.slice(i2, i2 + width);
|
|
27488
27660
|
while (chunk.length < width) chunk.push(null);
|
|
27489
|
-
for (const row of
|
|
27661
|
+
for (const row of stmt2.all(...chunk)) labels.set(row.id, row.label);
|
|
27490
27662
|
}
|
|
27491
27663
|
return labels;
|
|
27492
27664
|
}
|
|
@@ -27685,12 +27857,12 @@ var SqliteGraphStore = class {
|
|
|
27685
27857
|
return scale;
|
|
27686
27858
|
}
|
|
27687
27859
|
typedEdgeStmt(sql) {
|
|
27688
|
-
let
|
|
27689
|
-
if (!
|
|
27690
|
-
|
|
27691
|
-
this.typedEdgeStmts.set(sql,
|
|
27860
|
+
let stmt2 = this.typedEdgeStmts.get(sql);
|
|
27861
|
+
if (!stmt2) {
|
|
27862
|
+
stmt2 = this.db.prepare(sql);
|
|
27863
|
+
this.typedEdgeStmts.set(sql, stmt2);
|
|
27692
27864
|
}
|
|
27693
|
-
return
|
|
27865
|
+
return stmt2;
|
|
27694
27866
|
}
|
|
27695
27867
|
outEdges(nodeId, types) {
|
|
27696
27868
|
if (!types || types.length === 0) {
|
|
@@ -27891,22 +28063,28 @@ var SqliteGraphStore = class {
|
|
|
27891
28063
|
}
|
|
27892
28064
|
}
|
|
27893
28065
|
reviveReobserved(relPaths, workspaceId, since) {
|
|
27894
|
-
|
|
28066
|
+
const revived = [];
|
|
27895
28067
|
const CHUNK = 400;
|
|
27896
28068
|
for (let i2 = 0; i2 < relPaths.length; i2 += CHUNK) {
|
|
27897
28069
|
const slice = relPaths.slice(i2, i2 + CHUNK);
|
|
27898
28070
|
if (slice.length === 0) continue;
|
|
27899
|
-
const
|
|
27900
|
-
`
|
|
28071
|
+
const rows = this.db.prepare(
|
|
28072
|
+
`SELECT id, valid_to AS closedAt, COALESCE(logical_id, id) AS lid FROM nodes
|
|
27901
28073
|
WHERE valid_to IS NOT NULL
|
|
27902
28074
|
AND last_updated_at >= ?
|
|
27903
28075
|
AND json_extract(attrs_json, '$.workspaceId') = ?
|
|
27904
|
-
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
27905
|
-
|
|
27906
|
-
|
|
27907
|
-
|
|
27908
|
-
|
|
27909
|
-
return
|
|
28076
|
+
AND json_extract(attrs_json, '$.relPath') IN (${slice.map(() => "?").join(",")})
|
|
28077
|
+
AND json_type(attrs_json, '$.historical') IS NOT 'true'`
|
|
28078
|
+
).all(since, workspaceId, ...slice);
|
|
28079
|
+
revived.push(...rows);
|
|
28080
|
+
}
|
|
28081
|
+
if (revived.length === 0) return 0;
|
|
28082
|
+
const reopen = this.db.prepare("UPDATE nodes SET valid_to = NULL WHERE id = ? AND valid_to IS NOT NULL");
|
|
28083
|
+
for (const r of revived) reopen.run(r.id);
|
|
28084
|
+
for (const r of revived) reopenCascadeClosedEdges(this.db, r.id, Number(r.closedAt));
|
|
28085
|
+
collapseLiveHeads(this.db, revived.map((r) => r.lid), since);
|
|
28086
|
+
this.mutations++;
|
|
28087
|
+
return revived.length;
|
|
27910
28088
|
}
|
|
27911
28089
|
incomingEdgeCoverage(label, edgeTypes, cloudOnly) {
|
|
27912
28090
|
if (edgeTypes.length === 0) return { count: 0, total: 0 };
|
|
@@ -28052,8 +28230,9 @@ var SqliteGraphStore = class {
|
|
|
28052
28230
|
this.stmts.advanceLive.run({ live_id: liveId, t });
|
|
28053
28231
|
return frozenId;
|
|
28054
28232
|
}
|
|
28055
|
-
closeNode(id, t) {
|
|
28056
|
-
this.stmts.closeNode.run({ id, t });
|
|
28233
|
+
closeNode(id, t, opts = {}) {
|
|
28234
|
+
const r = this.stmts.closeNode.run({ id, t });
|
|
28235
|
+
if (Number(r.changes) > 0 && !opts.retainEdges) closeIncidentEdges(this.db, id, t);
|
|
28057
28236
|
}
|
|
28058
28237
|
closeEdge(id, t) {
|
|
28059
28238
|
this.stmts.closeEdge.run({ id, t });
|