@inerrata-corporation/errata 2.0.22-dev.4669 → 2.0.22-dev.4676
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 +148 -38
- package/errata.mjs +339 -68
- package/generalize-worker.mjs +148 -38
- package/package.json +1 -1
- package/pass-worker.mjs +250 -43
- package/resources/context-plugin/.codex-plugin/plugin.json +1 -1
- package/resources/context-plugin/SHA256SUMS +1 -1
package/generalize-worker.mjs
CHANGED
|
@@ -15415,6 +15415,35 @@ var WM_CALIBRATION_KEYS = [
|
|
|
15415
15415
|
var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
|
|
15416
15416
|
var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
|
|
15417
15417
|
|
|
15418
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
15419
|
+
var EXPOSURE_ATTRS = [
|
|
15420
|
+
"shownCount",
|
|
15421
|
+
"evictedCount",
|
|
15422
|
+
"firstShownAt",
|
|
15423
|
+
"firstEvictedAt",
|
|
15424
|
+
"firstShownAtSeq",
|
|
15425
|
+
"lastShownAtSeq",
|
|
15426
|
+
"lastEvictedAtSeq",
|
|
15427
|
+
"shownSinceTouch",
|
|
15428
|
+
"shownSinceTouchAt",
|
|
15429
|
+
"exposureLog",
|
|
15430
|
+
"exposureLogStart",
|
|
15431
|
+
"exposureLogSince",
|
|
15432
|
+
"exposureFirst",
|
|
15433
|
+
"exposureTrimmed"
|
|
15434
|
+
];
|
|
15435
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
15436
|
+
var EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
|
|
15437
|
+
var isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
|
|
15438
|
+
function pickExposure(attrs) {
|
|
15439
|
+
const out = {};
|
|
15440
|
+
for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out[k] = attrs[k];
|
|
15441
|
+
return out;
|
|
15442
|
+
}
|
|
15443
|
+
function exposureDocSql(key) {
|
|
15444
|
+
return `coalesce((SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id AND json_type(x.attrs_json, '$.${key}') IS NOT NULL), attrs_json)`;
|
|
15445
|
+
}
|
|
15446
|
+
|
|
15418
15447
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
15419
15448
|
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
15420
15449
|
function installHistoryCacheFloor(db) {
|
|
@@ -15696,25 +15725,6 @@ function textIndexReady(db) {
|
|
|
15696
15725
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
15697
15726
|
}
|
|
15698
15727
|
|
|
15699
|
-
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
15700
|
-
var EXPOSURE_ATTRS = [
|
|
15701
|
-
"shownCount",
|
|
15702
|
-
"evictedCount",
|
|
15703
|
-
"firstShownAt",
|
|
15704
|
-
"firstEvictedAt",
|
|
15705
|
-
"firstShownAtSeq",
|
|
15706
|
-
"lastShownAtSeq",
|
|
15707
|
-
"lastEvictedAtSeq",
|
|
15708
|
-
"shownSinceTouch",
|
|
15709
|
-
"shownSinceTouchAt",
|
|
15710
|
-
"exposureLog",
|
|
15711
|
-
"exposureLogStart",
|
|
15712
|
-
"exposureLogSince",
|
|
15713
|
-
"exposureFirst",
|
|
15714
|
-
"exposureTrimmed"
|
|
15715
|
-
];
|
|
15716
|
-
var EXPOSURE_SCOPE = "exposure";
|
|
15717
|
-
|
|
15718
15728
|
// ../../packages/local-graph/src/change-feed.ts
|
|
15719
15729
|
var CHANGE_FEED_KEY = "change-feed";
|
|
15720
15730
|
var ANY_CHANGE = "*";
|
|
@@ -15778,6 +15788,16 @@ function readChangedNodeIds(db, sinceSeq, labels) {
|
|
|
15778
15788
|
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND label IN (${labels.map(() => "?").join(",")})`).all(sinceSeq, ...labels);
|
|
15779
15789
|
return rows.map((r) => r.id);
|
|
15780
15790
|
}
|
|
15791
|
+
function readChangedAmong(db, sinceSeq, ids) {
|
|
15792
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
15793
|
+
const out = [];
|
|
15794
|
+
for (let i = 0; i < ids.length; i += 500) {
|
|
15795
|
+
const chunk = ids.slice(i, i + 500);
|
|
15796
|
+
const rows = db.prepare(`SELECT DISTINCT id FROM change_log WHERE change_seq > ? AND kind = 'node' AND id IN (${chunk.map(() => "?").join(",")})`).all(sinceSeq, ...chunk);
|
|
15797
|
+
for (const r of rows) out.push(r.id);
|
|
15798
|
+
}
|
|
15799
|
+
return out;
|
|
15800
|
+
}
|
|
15781
15801
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
15782
15802
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
15783
15803
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -16075,8 +16095,11 @@ function compileNodeWhere(q) {
|
|
|
16075
16095
|
}
|
|
16076
16096
|
return { where: conds.join(" AND "), params };
|
|
16077
16097
|
}
|
|
16078
|
-
function attrValueSql(p) {
|
|
16079
|
-
return `CASE json_type(
|
|
16098
|
+
function attrValueSql(p, doc = "attrs_json") {
|
|
16099
|
+
return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
|
|
16100
|
+
}
|
|
16101
|
+
function attrDocSql(key) {
|
|
16102
|
+
return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
|
|
16080
16103
|
}
|
|
16081
16104
|
function nodeSelectFrom(q) {
|
|
16082
16105
|
if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
|
|
@@ -16089,7 +16112,7 @@ function nodeSelectFrom(q) {
|
|
|
16089
16112
|
function compileNodeSelect(q) {
|
|
16090
16113
|
const { where, params } = compileNodeWhere(q);
|
|
16091
16114
|
const attrs = q.attrs ?? [];
|
|
16092
|
-
const attrCols = attrs.map((k, i) => `, ${attrValueSql(path(k))} AS a${i}, json_type(
|
|
16115
|
+
const attrCols = attrs.map((k, i) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i}`).join("");
|
|
16093
16116
|
let order;
|
|
16094
16117
|
if (q.orderBy && "column" in q.orderBy) {
|
|
16095
16118
|
order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
@@ -16206,6 +16229,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
16206
16229
|
}
|
|
16207
16230
|
var SCHEMA_VERSION = 6;
|
|
16208
16231
|
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
16232
|
+
var EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
|
|
16209
16233
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
16210
16234
|
var SCHEMA_SQL = `
|
|
16211
16235
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -16269,8 +16293,13 @@ CREATE TABLE IF NOT EXISTS nodes (
|
|
|
16269
16293
|
);
|
|
16270
16294
|
|
|
16271
16295
|
CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
|
|
16272
|
-
|
|
16273
|
-
|
|
16296
|
+
-- Dropped: no query plans through them (every prepared statement across the
|
|
16297
|
+
-- local-graph, indexer, context-writer, generalizer and daemon suites was
|
|
16298
|
+
-- planned with and without them, 2026-10-21), and each write that touches
|
|
16299
|
+
-- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
|
|
16300
|
+
-- them. nodes_live_page_rank serves the live page-rank orderings.
|
|
16301
|
+
DROP INDEX IF EXISTS nodes_memory_tier;
|
|
16302
|
+
DROP INDEX IF EXISTS nodes_page_rank;
|
|
16274
16303
|
|
|
16275
16304
|
CREATE TABLE IF NOT EXISTS edges (
|
|
16276
16305
|
id TEXT PRIMARY KEY,
|
|
@@ -16292,7 +16321,10 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
16292
16321
|
FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
16293
16322
|
);
|
|
16294
16323
|
|
|
16295
|
-
|
|
16324
|
+
-- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
|
|
16325
|
+
-- from-side lookup and foreign-key check already plans through; it cost 47 MB
|
|
16326
|
+
-- and an index write on every edge write.
|
|
16327
|
+
DROP INDEX IF EXISTS edges_from;
|
|
16296
16328
|
CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
|
|
16297
16329
|
CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
|
|
16298
16330
|
CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
@@ -16314,6 +16346,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
|
|
|
16314
16346
|
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
16315
16347
|
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
16316
16348
|
CREATE INDEX IF NOT EXISTS nodes_live_fold_candidate ON nodes(label, valid_to) WHERE valid_to IS NULL AND json_type(attrs_json, '$.foldCandidate') IS NOT NULL;
|
|
16349
|
+
-- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
|
|
16350
|
+
-- ~40 of these, which in the node row rewrote each whole row and its indexes.
|
|
16351
|
+
CREATE TABLE IF NOT EXISTS node_exposure (
|
|
16352
|
+
id TEXT PRIMARY KEY,
|
|
16353
|
+
attrs_json TEXT NOT NULL DEFAULT '{}'
|
|
16354
|
+
) WITHOUT ROWID;
|
|
16317
16355
|
-- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
|
|
16318
16356
|
-- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
|
|
16319
16357
|
-- several times a run, and a run follows most writes (2026-10-20).
|
|
@@ -16477,6 +16515,7 @@ var SqliteGraphStore = class {
|
|
|
16477
16515
|
constructor(opts) {
|
|
16478
16516
|
this.db = openDatabase(opts.path);
|
|
16479
16517
|
this.db.exec("PRAGMA cache_size = -32768");
|
|
16518
|
+
this.db.exec("PRAGMA mmap_size = 2147483648");
|
|
16480
16519
|
this.db.exec(SCHEMA_SQL);
|
|
16481
16520
|
const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
|
|
16482
16521
|
if (!versionRow) {
|
|
@@ -16540,7 +16579,10 @@ var SqliteGraphStore = class {
|
|
|
16540
16579
|
last_reinforced_at_seq = excluded.last_reinforced_at_seq,
|
|
16541
16580
|
attrs_json = excluded.attrs_json
|
|
16542
16581
|
`),
|
|
16543
|
-
getNode: this.db.prepare("SELECT
|
|
16582
|
+
getNode: this.db.prepare("SELECT nodes.*, (SELECT x.attrs_json FROM node_exposure x WHERE x.id = nodes.id) AS exposure_json FROM nodes WHERE id = ?"),
|
|
16583
|
+
exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
|
|
16584
|
+
writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
|
|
16585
|
+
lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
|
|
16544
16586
|
labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
|
|
16545
16587
|
nodeContent: this.db.prepare(
|
|
16546
16588
|
"SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
|
|
@@ -16704,6 +16746,7 @@ var SqliteGraphStore = class {
|
|
|
16704
16746
|
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
16705
16747
|
)
|
|
16706
16748
|
};
|
|
16749
|
+
this.moveExposureToSideRows();
|
|
16707
16750
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
16708
16751
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
16709
16752
|
}
|
|
@@ -16764,21 +16807,35 @@ var SqliteGraphStore = class {
|
|
|
16764
16807
|
`SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i) => `${wmCounterSql(k)} AS c${i}`).join(", ")}
|
|
16765
16808
|
FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
|
|
16766
16809
|
).all();
|
|
16810
|
+
const exposed = this.db.prepare(
|
|
16811
|
+
`SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
|
|
16812
|
+
FROM node_exposure x JOIN nodes n ON n.id = x.id`
|
|
16813
|
+
).all();
|
|
16767
16814
|
const others = this.db.prepare(
|
|
16768
16815
|
`SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
|
|
16769
16816
|
SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
|
|
16770
|
-
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
16771
|
-
|
|
16817
|
+
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
16818
|
+
UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
|
|
16819
|
+
EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
16820
|
+
EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
|
|
16772
16821
|
).all();
|
|
16773
|
-
const
|
|
16774
|
-
|
|
16822
|
+
const byRid = /* @__PURE__ */ new Map();
|
|
16823
|
+
for (const r of [...counted, ...others]) {
|
|
16775
16824
|
const attrs = {};
|
|
16776
16825
|
WM_CALIBRATION_KEYS.forEach((k, i) => {
|
|
16777
16826
|
const v = r[`c${i}`];
|
|
16778
16827
|
if (typeof v === "number") attrs[k] = v;
|
|
16779
16828
|
});
|
|
16780
|
-
|
|
16781
|
-
}
|
|
16829
|
+
byRid.set(r.rid, { row: r, attrs });
|
|
16830
|
+
}
|
|
16831
|
+
for (const r of exposed) {
|
|
16832
|
+
const side = JSON.parse(r.exposure);
|
|
16833
|
+
const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
|
|
16834
|
+
for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
|
|
16835
|
+
byRid.set(r.rid, entry);
|
|
16836
|
+
}
|
|
16837
|
+
const order = new Map(labels.map((l, i) => [l, i]));
|
|
16838
|
+
return [...byRid.values()].filter(({ row }) => order.has(row.label)).sort((a, b) => order.get(a.row.label) - order.get(b.row.label) || a.row.rid - b.row.rid).map(({ row, attrs }) => ({ logicalId: row.logical_id, version: Number(row.version), validTo: row.valid_to ?? null, attrs }));
|
|
16782
16839
|
}
|
|
16783
16840
|
liveCommentsAttachedTo(name) {
|
|
16784
16841
|
return this.db.prepare(
|
|
@@ -16916,7 +16973,7 @@ var SqliteGraphStore = class {
|
|
|
16916
16973
|
logical_id: node.logicalId ?? node.id,
|
|
16917
16974
|
created_at_seq: node.createdAtSeq ?? null,
|
|
16918
16975
|
last_reinforced_at_seq: node.lastReinforcedAtSeq ?? null,
|
|
16919
|
-
attrs_json:
|
|
16976
|
+
attrs_json: this.nodeAttrsJson(node.id, node.attrs ?? {})
|
|
16920
16977
|
});
|
|
16921
16978
|
}
|
|
16922
16979
|
/** mergeNode, unless the live row already holds this content. Content is what
|
|
@@ -16928,7 +16985,7 @@ var SqliteGraphStore = class {
|
|
|
16928
16985
|
* whether it wrote. */
|
|
16929
16986
|
mergeNodeIfChanged(node) {
|
|
16930
16987
|
const row = this.stmts.nodeContent.get(node.id);
|
|
16931
|
-
if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
|
|
16988
|
+
if (row && row.valid_to === null && row.label === node.label && row.description === node.description && row.extraction_confidence === node.extractionConfidence && row.stability === node.stability && !EXPOSURE_ATTRS.some((k) => node.attrs?.[k] !== void 0) && row.attrs_json === JSON.stringify(node.attrs ?? {})) return false;
|
|
16932
16989
|
this.mergeNode(node);
|
|
16933
16990
|
return true;
|
|
16934
16991
|
}
|
|
@@ -16955,7 +17012,37 @@ var SqliteGraphStore = class {
|
|
|
16955
17012
|
logical_id: patch.logicalId ?? null,
|
|
16956
17013
|
created_at_seq: patch.createdAtSeq ?? null,
|
|
16957
17014
|
last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
|
|
16958
|
-
attrs_json: patch.attrs ?
|
|
17015
|
+
attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
|
|
17016
|
+
});
|
|
17017
|
+
}
|
|
17018
|
+
/**
|
|
17019
|
+
* The node-row attrs of a write, after handing any exposure keys to the side
|
|
17020
|
+
* row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
|
|
17021
|
+
* read-modify-write carries it; kept in the row it would re-bloat the row and
|
|
17022
|
+
* drift from the side row. Unchanged exposure costs no write.
|
|
17023
|
+
*/
|
|
17024
|
+
nodeAttrsJson(id, attrs) {
|
|
17025
|
+
if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
|
|
17026
|
+
const current = this.stmts.exposureOf.get(id);
|
|
17027
|
+
const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
|
|
17028
|
+
if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
|
|
17029
|
+
const rest = { ...attrs };
|
|
17030
|
+
for (const k of EXPOSURE_ATTRS) delete rest[k];
|
|
17031
|
+
return JSON.stringify(rest);
|
|
17032
|
+
}
|
|
17033
|
+
/** One-time: exposure keys written to node rows before the side table existed
|
|
17034
|
+
* move to it. Only rows carrying a counter can carry exposure, and those are
|
|
17035
|
+
* what nodes_wm_counted holds, so this reads the index, not the table. */
|
|
17036
|
+
moveExposureToSideRows() {
|
|
17037
|
+
if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
|
|
17038
|
+
const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
|
|
17039
|
+
const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
|
|
17040
|
+
this.db.transaction(() => {
|
|
17041
|
+
for (const r of rows) {
|
|
17042
|
+
const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
|
|
17043
|
+
if (stripped !== r.attrs_json) move.run(stripped, r.id);
|
|
17044
|
+
}
|
|
17045
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
|
|
16959
17046
|
});
|
|
16960
17047
|
}
|
|
16961
17048
|
/** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
|
|
@@ -17118,7 +17205,27 @@ var SqliteGraphStore = class {
|
|
|
17118
17205
|
}
|
|
17119
17206
|
getNode(id) {
|
|
17120
17207
|
const row = this.stmts.getNode.get(id);
|
|
17121
|
-
|
|
17208
|
+
if (!row) return null;
|
|
17209
|
+
const node = rowToNode(row);
|
|
17210
|
+
if (row.exposure_json) Object.assign(node.attrs, JSON.parse(row.exposure_json));
|
|
17211
|
+
return node;
|
|
17212
|
+
}
|
|
17213
|
+
/** A node's render exposure: the side row's keys over the node row's own
|
|
17214
|
+
* (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
|
|
17215
|
+
* touch" counter is relative to it) — without reading the node's attrs. */
|
|
17216
|
+
exposureOf(id) {
|
|
17217
|
+
const row = this.stmts.lastUpdatedOf.get(id);
|
|
17218
|
+
if (!row) return null;
|
|
17219
|
+
const side = this.stmts.exposureOf.get(id);
|
|
17220
|
+
return {
|
|
17221
|
+
attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
|
|
17222
|
+
lastUpdatedAt: row.t
|
|
17223
|
+
};
|
|
17224
|
+
}
|
|
17225
|
+
/** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
|
|
17226
|
+
* neither the node row nor its indexes, nor the change feed. */
|
|
17227
|
+
writeExposure(id, attrs) {
|
|
17228
|
+
this.stmts.writeExposure.run(id, JSON.stringify(attrs));
|
|
17122
17229
|
}
|
|
17123
17230
|
nodeLabels(ids) {
|
|
17124
17231
|
const width = NODE_LABELS_CHUNK;
|
|
@@ -17169,7 +17276,7 @@ var SqliteGraphStore = class {
|
|
|
17169
17276
|
findNodeStampsByLabel(label, attrKey) {
|
|
17170
17277
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
17171
17278
|
const rows = this.db.prepare(
|
|
17172
|
-
`SELECT id, created_at, created_at_seq, json_type(
|
|
17279
|
+
`SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
|
|
17173
17280
|
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
17174
17281
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17175
17282
|
).all(label);
|
|
@@ -17186,7 +17293,7 @@ var SqliteGraphStore = class {
|
|
|
17186
17293
|
const keys = [...new Set(attrKeys)];
|
|
17187
17294
|
const rows = this.db.prepare(
|
|
17188
17295
|
`SELECT id, created_at, created_at_seq,
|
|
17189
|
-
${keys.map((k, i) => `json_type(
|
|
17296
|
+
${keys.map((k, i) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i}`).join(", ")}${keys.length ? "," : ""}
|
|
17190
17297
|
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
17191
17298
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
17192
17299
|
).all(label);
|
|
@@ -17446,6 +17553,9 @@ var SqliteGraphStore = class {
|
|
|
17446
17553
|
changedNodeIds(sinceSeq, labels) {
|
|
17447
17554
|
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
17448
17555
|
}
|
|
17556
|
+
changedAmong(sinceSeq, ids) {
|
|
17557
|
+
return readChangedAmong(this.db, sinceSeq, ids);
|
|
17558
|
+
}
|
|
17449
17559
|
pruneChangeLog() {
|
|
17450
17560
|
return pruneChangeLog(this.db);
|
|
17451
17561
|
}
|