@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/consolidate-worker.mjs
CHANGED
|
@@ -16838,6 +16838,35 @@ var WM_CALIBRATION_KEYS = [
|
|
|
16838
16838
|
var WM_COUNTED_PREDICATE = WM_CALIBRATION_KEYS.map((k) => `json_type(attrs_json, '$.${k}') IS NOT NULL`).join(" OR ");
|
|
16839
16839
|
var wmCounterSql = (k) => `CASE WHEN json_type(attrs_json, '$.${k}') IN ('integer', 'real') THEN json_extract(attrs_json, '$.${k}') END`;
|
|
16840
16840
|
|
|
16841
|
+
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
16842
|
+
var EXPOSURE_ATTRS = [
|
|
16843
|
+
"shownCount",
|
|
16844
|
+
"evictedCount",
|
|
16845
|
+
"firstShownAt",
|
|
16846
|
+
"firstEvictedAt",
|
|
16847
|
+
"firstShownAtSeq",
|
|
16848
|
+
"lastShownAtSeq",
|
|
16849
|
+
"lastEvictedAtSeq",
|
|
16850
|
+
"shownSinceTouch",
|
|
16851
|
+
"shownSinceTouchAt",
|
|
16852
|
+
"exposureLog",
|
|
16853
|
+
"exposureLogStart",
|
|
16854
|
+
"exposureLogSince",
|
|
16855
|
+
"exposureFirst",
|
|
16856
|
+
"exposureTrimmed"
|
|
16857
|
+
];
|
|
16858
|
+
var EXPOSURE_SCOPE = "exposure";
|
|
16859
|
+
var EXPOSURE_KEYS = new Set(EXPOSURE_ATTRS);
|
|
16860
|
+
var isExposureAttr = (key) => EXPOSURE_KEYS.has(key);
|
|
16861
|
+
function pickExposure(attrs) {
|
|
16862
|
+
const out = {};
|
|
16863
|
+
for (const k of EXPOSURE_ATTRS) if (attrs[k] !== void 0) out[k] = attrs[k];
|
|
16864
|
+
return out;
|
|
16865
|
+
}
|
|
16866
|
+
function exposureDocSql(key) {
|
|
16867
|
+
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)`;
|
|
16868
|
+
}
|
|
16869
|
+
|
|
16841
16870
|
// ../../packages/local-graph/src/history-cache-floor.ts
|
|
16842
16871
|
var HISTORY_CACHE_FLOOR_KEY = "history-cache-withdrawal-floor-3";
|
|
16843
16872
|
function installHistoryCacheFloor(db) {
|
|
@@ -17122,25 +17151,6 @@ function textIndexReady(db) {
|
|
|
17122
17151
|
return db.prepare("SELECT value FROM store_meta WHERE key = ?").get(TEXT_INDEX_KEY)?.["value"] === "installed";
|
|
17123
17152
|
}
|
|
17124
17153
|
|
|
17125
|
-
// ../../packages/local-graph/src/exposure-attrs.ts
|
|
17126
|
-
var EXPOSURE_ATTRS = [
|
|
17127
|
-
"shownCount",
|
|
17128
|
-
"evictedCount",
|
|
17129
|
-
"firstShownAt",
|
|
17130
|
-
"firstEvictedAt",
|
|
17131
|
-
"firstShownAtSeq",
|
|
17132
|
-
"lastShownAtSeq",
|
|
17133
|
-
"lastEvictedAtSeq",
|
|
17134
|
-
"shownSinceTouch",
|
|
17135
|
-
"shownSinceTouchAt",
|
|
17136
|
-
"exposureLog",
|
|
17137
|
-
"exposureLogStart",
|
|
17138
|
-
"exposureLogSince",
|
|
17139
|
-
"exposureFirst",
|
|
17140
|
-
"exposureTrimmed"
|
|
17141
|
-
];
|
|
17142
|
-
var EXPOSURE_SCOPE = "exposure";
|
|
17143
|
-
|
|
17144
17154
|
// ../../packages/local-graph/src/change-feed.ts
|
|
17145
17155
|
var CHANGE_FEED_KEY = "change-feed";
|
|
17146
17156
|
var ANY_CHANGE = "*";
|
|
@@ -17204,6 +17214,16 @@ function readChangedNodeIds(db, sinceSeq, labels) {
|
|
|
17204
17214
|
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);
|
|
17205
17215
|
return rows.map((r) => r.id);
|
|
17206
17216
|
}
|
|
17217
|
+
function readChangedAmong(db, sinceSeq, ids) {
|
|
17218
|
+
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17219
|
+
const out = [];
|
|
17220
|
+
for (let i = 0; i < ids.length; i += 500) {
|
|
17221
|
+
const chunk = ids.slice(i, i + 500);
|
|
17222
|
+
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);
|
|
17223
|
+
for (const r of rows) out.push(r.id);
|
|
17224
|
+
}
|
|
17225
|
+
return out;
|
|
17226
|
+
}
|
|
17207
17227
|
function readLineageChangedFiles(db, sinceSeq) {
|
|
17208
17228
|
if (!changeLogCovers(db, sinceSeq)) return null;
|
|
17209
17229
|
const rows = db.prepare(LINEAGE_CHANGED_FILES_SQL).all({ s: sinceSeq });
|
|
@@ -17678,8 +17698,11 @@ function compileNodeWhere(q) {
|
|
|
17678
17698
|
}
|
|
17679
17699
|
return { where: conds.join(" AND "), params };
|
|
17680
17700
|
}
|
|
17681
|
-
function attrValueSql(p) {
|
|
17682
|
-
return `CASE json_type(
|
|
17701
|
+
function attrValueSql(p, doc = "attrs_json") {
|
|
17702
|
+
return `CASE json_type(${doc}, ${p}) WHEN 'integer' THEN CAST(json_extract(${doc}, ${p}) AS REAL) ELSE json_extract(${doc}, ${p}) END`;
|
|
17703
|
+
}
|
|
17704
|
+
function attrDocSql(key) {
|
|
17705
|
+
return isExposureAttr(key) ? exposureDocSql(key) : "attrs_json";
|
|
17683
17706
|
}
|
|
17684
17707
|
function nodeSelectFrom(q) {
|
|
17685
17708
|
if (q.indexedBy) return `nodes INDEXED BY ${selectIndex(q.indexedBy)}`;
|
|
@@ -17692,7 +17715,7 @@ function nodeSelectFrom(q) {
|
|
|
17692
17715
|
function compileNodeSelect(q) {
|
|
17693
17716
|
const { where, params } = compileNodeWhere(q);
|
|
17694
17717
|
const attrs = q.attrs ?? [];
|
|
17695
|
-
const attrCols = attrs.map((k, i) => `, ${attrValueSql(path(k))} AS a${i}, json_type(
|
|
17718
|
+
const attrCols = attrs.map((k, i) => `, ${attrValueSql(path(k), attrDocSql(k))} AS a${i}, json_type(${attrDocSql(k)}, ${path(k)}) AS t${i}`).join("");
|
|
17696
17719
|
let order;
|
|
17697
17720
|
if (q.orderBy && "column" in q.orderBy) {
|
|
17698
17721
|
order = `${column(q.orderBy.column)} ${q.orderBy.desc ? "DESC" : "ASC"}, rowid`;
|
|
@@ -17809,6 +17832,7 @@ function localEdgeViolation(fromLabel, type, toLabel) {
|
|
|
17809
17832
|
}
|
|
17810
17833
|
var SCHEMA_VERSION = 6;
|
|
17811
17834
|
var SESSION_REPAIR_KEY = "session-evidence-v1";
|
|
17835
|
+
var EXPOSURE_SIDE_KEY = "exposure-side-rows-1";
|
|
17812
17836
|
var SESSION_EVIDENCE_PREDICATE = SESSION_EVIDENCE_FIELDS.map((field) => `instr(attrs_json, '${JSON.stringify(field)}') > 0`).join(" OR ");
|
|
17813
17837
|
var SCHEMA_SQL = `
|
|
17814
17838
|
CREATE TABLE IF NOT EXISTS schema_version (
|
|
@@ -17872,8 +17896,13 @@ CREATE TABLE IF NOT EXISTS nodes (
|
|
|
17872
17896
|
);
|
|
17873
17897
|
|
|
17874
17898
|
CREATE INDEX IF NOT EXISTS nodes_label ON nodes(label);
|
|
17875
|
-
|
|
17876
|
-
|
|
17899
|
+
-- Dropped: no query plans through them (every prepared statement across the
|
|
17900
|
+
-- local-graph, indexer, context-writer, generalizer and daemon suites was
|
|
17901
|
+
-- planned with and without them, 2026-10-21), and each write that touches
|
|
17902
|
+
-- memory_tier / page_rank (every node upsert, every rescore) paid to maintain
|
|
17903
|
+
-- them. nodes_live_page_rank serves the live page-rank orderings.
|
|
17904
|
+
DROP INDEX IF EXISTS nodes_memory_tier;
|
|
17905
|
+
DROP INDEX IF EXISTS nodes_page_rank;
|
|
17877
17906
|
|
|
17878
17907
|
CREATE TABLE IF NOT EXISTS edges (
|
|
17879
17908
|
id TEXT PRIMARY KEY,
|
|
@@ -17895,7 +17924,10 @@ CREATE TABLE IF NOT EXISTS edges (
|
|
|
17895
17924
|
FOREIGN KEY (to_id) REFERENCES nodes(id) ON DELETE CASCADE
|
|
17896
17925
|
);
|
|
17897
17926
|
|
|
17898
|
-
|
|
17927
|
+
-- Dropped: a prefix of edges_triple (from_id, type, to_id), which every
|
|
17928
|
+
-- from-side lookup and foreign-key check already plans through; it cost 47 MB
|
|
17929
|
+
-- and an index write on every edge write.
|
|
17930
|
+
DROP INDEX IF EXISTS edges_from;
|
|
17899
17931
|
CREATE INDEX IF NOT EXISTS edges_to ON edges(to_id, type);
|
|
17900
17932
|
CREATE INDEX IF NOT EXISTS edges_type ON edges(type);
|
|
17901
17933
|
CREATE UNIQUE INDEX IF NOT EXISTS edges_triple ON edges(from_id, type, to_id);
|
|
@@ -17917,6 +17949,12 @@ CREATE INDEX IF NOT EXISTS nodes_live_local_community ON nodes(label, valid_to,
|
|
|
17917
17949
|
-- Problems parked for the fold judge (read every settle boundary): ~1 in 5 open
|
|
17918
17950
|
-- Problems; without it every live Problem was read to find them (56 MB).
|
|
17919
17951
|
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;
|
|
17952
|
+
-- Render impressions, one row per node id (exposure-attrs.ts): a render bumps
|
|
17953
|
+
-- ~40 of these, which in the node row rewrote each whole row and its indexes.
|
|
17954
|
+
CREATE TABLE IF NOT EXISTS node_exposure (
|
|
17955
|
+
id TEXT PRIMARY KEY,
|
|
17956
|
+
attrs_json TEXT NOT NULL DEFAULT '{}'
|
|
17957
|
+
) WITHOUT ROWID;
|
|
17920
17958
|
-- The semantic-maintenance sweeps' own filters (closed, asked, hinted): each read
|
|
17921
17959
|
-- every Problem row (~30 MB on a 1.1 GB store) for the few hundred that match,
|
|
17922
17960
|
-- several times a run, and a run follows most writes (2026-10-20).
|
|
@@ -18080,6 +18118,7 @@ var SqliteGraphStore = class {
|
|
|
18080
18118
|
constructor(opts) {
|
|
18081
18119
|
this.db = openDatabase(opts.path);
|
|
18082
18120
|
this.db.exec("PRAGMA cache_size = -32768");
|
|
18121
|
+
this.db.exec("PRAGMA mmap_size = 2147483648");
|
|
18083
18122
|
this.db.exec(SCHEMA_SQL);
|
|
18084
18123
|
const versionRow = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
|
|
18085
18124
|
if (!versionRow) {
|
|
@@ -18143,7 +18182,10 @@ var SqliteGraphStore = class {
|
|
|
18143
18182
|
last_reinforced_at_seq = excluded.last_reinforced_at_seq,
|
|
18144
18183
|
attrs_json = excluded.attrs_json
|
|
18145
18184
|
`),
|
|
18146
|
-
getNode: this.db.prepare("SELECT
|
|
18185
|
+
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 = ?"),
|
|
18186
|
+
exposureOf: this.db.prepare("SELECT attrs_json FROM node_exposure WHERE id = ?"),
|
|
18187
|
+
writeExposure: this.db.prepare("INSERT INTO node_exposure(id, attrs_json) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET attrs_json = excluded.attrs_json"),
|
|
18188
|
+
lastUpdatedOf: this.db.prepare("SELECT last_updated_at AS t, attrs_json FROM nodes WHERE id = ?"),
|
|
18147
18189
|
labelOf: this.db.prepare("SELECT label FROM nodes WHERE id = ?"),
|
|
18148
18190
|
nodeContent: this.db.prepare(
|
|
18149
18191
|
"SELECT label, description, extraction_confidence, extraction_source, stability, valid_to, attrs_json FROM nodes WHERE id = ?"
|
|
@@ -18307,6 +18349,7 @@ var SqliteGraphStore = class {
|
|
|
18307
18349
|
"INSERT INTO store_meta (key, value) VALUES ('ingest_seq', :v) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
18308
18350
|
)
|
|
18309
18351
|
};
|
|
18352
|
+
this.moveExposureToSideRows();
|
|
18310
18353
|
const seqRow = this.db.prepare("SELECT value FROM store_meta WHERE key = 'ingest_seq'").get();
|
|
18311
18354
|
this.ingestSeq = seqRow ? Number(seqRow.value) : 0;
|
|
18312
18355
|
}
|
|
@@ -18367,21 +18410,35 @@ var SqliteGraphStore = class {
|
|
|
18367
18410
|
`SELECT rowid AS rid, logical_id, version, valid_to, label, ${WM_CALIBRATION_KEYS.map((k, i) => `${wmCounterSql(k)} AS c${i}`).join(", ")}
|
|
18368
18411
|
FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`
|
|
18369
18412
|
).all();
|
|
18413
|
+
const exposed = this.db.prepare(
|
|
18414
|
+
`SELECT n.rowid AS rid, n.logical_id, n.version, n.valid_to, n.label, x.attrs_json AS exposure
|
|
18415
|
+
FROM node_exposure x JOIN nodes n ON n.id = x.id`
|
|
18416
|
+
).all();
|
|
18370
18417
|
const others = this.db.prepare(
|
|
18371
18418
|
`SELECT rowid AS rid, logical_id, version, valid_to, label FROM nodes WHERE rowid IN (
|
|
18372
18419
|
SELECT n.rowid FROM nodes AS n INDEXED BY nodes_logical
|
|
18373
|
-
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
18374
|
-
|
|
18420
|
+
WHERE n.logical_id IN (SELECT logical_id FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
18421
|
+
UNION SELECT n2.logical_id FROM node_exposure x2 JOIN nodes n2 ON n2.id = x2.id)
|
|
18422
|
+
EXCEPT SELECT rowid FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}
|
|
18423
|
+
EXCEPT SELECT n3.rowid FROM node_exposure x3 JOIN nodes n3 ON n3.id = x3.id)`
|
|
18375
18424
|
).all();
|
|
18376
|
-
const
|
|
18377
|
-
|
|
18425
|
+
const byRid = /* @__PURE__ */ new Map();
|
|
18426
|
+
for (const r of [...counted, ...others]) {
|
|
18378
18427
|
const attrs = {};
|
|
18379
18428
|
WM_CALIBRATION_KEYS.forEach((k, i) => {
|
|
18380
18429
|
const v = r[`c${i}`];
|
|
18381
18430
|
if (typeof v === "number") attrs[k] = v;
|
|
18382
18431
|
});
|
|
18383
|
-
|
|
18384
|
-
}
|
|
18432
|
+
byRid.set(r.rid, { row: r, attrs });
|
|
18433
|
+
}
|
|
18434
|
+
for (const r of exposed) {
|
|
18435
|
+
const side = JSON.parse(r.exposure);
|
|
18436
|
+
const entry = byRid.get(r.rid) ?? { row: r, attrs: {} };
|
|
18437
|
+
for (const k of WM_CALIBRATION_KEYS) if (typeof side[k] === "number" && Number.isFinite(side[k])) entry.attrs[k] = side[k];
|
|
18438
|
+
byRid.set(r.rid, entry);
|
|
18439
|
+
}
|
|
18440
|
+
const order = new Map(labels.map((l, i) => [l, i]));
|
|
18441
|
+
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 }));
|
|
18385
18442
|
}
|
|
18386
18443
|
liveCommentsAttachedTo(name) {
|
|
18387
18444
|
return this.db.prepare(
|
|
@@ -18519,7 +18576,7 @@ var SqliteGraphStore = class {
|
|
|
18519
18576
|
logical_id: node.logicalId ?? node.id,
|
|
18520
18577
|
created_at_seq: node.createdAtSeq ?? null,
|
|
18521
18578
|
last_reinforced_at_seq: node.lastReinforcedAtSeq ?? null,
|
|
18522
|
-
attrs_json:
|
|
18579
|
+
attrs_json: this.nodeAttrsJson(node.id, node.attrs ?? {})
|
|
18523
18580
|
});
|
|
18524
18581
|
}
|
|
18525
18582
|
/** mergeNode, unless the live row already holds this content. Content is what
|
|
@@ -18531,7 +18588,7 @@ var SqliteGraphStore = class {
|
|
|
18531
18588
|
* whether it wrote. */
|
|
18532
18589
|
mergeNodeIfChanged(node) {
|
|
18533
18590
|
const row = this.stmts.nodeContent.get(node.id);
|
|
18534
|
-
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;
|
|
18591
|
+
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;
|
|
18535
18592
|
this.mergeNode(node);
|
|
18536
18593
|
return true;
|
|
18537
18594
|
}
|
|
@@ -18558,7 +18615,37 @@ var SqliteGraphStore = class {
|
|
|
18558
18615
|
logical_id: patch.logicalId ?? null,
|
|
18559
18616
|
created_at_seq: patch.createdAtSeq ?? null,
|
|
18560
18617
|
last_reinforced_at_seq: patch.lastReinforcedAtSeq ?? null,
|
|
18561
|
-
attrs_json: patch.attrs ?
|
|
18618
|
+
attrs_json: patch.attrs ? this.nodeAttrsJson(id, patch.attrs) : null
|
|
18619
|
+
});
|
|
18620
|
+
}
|
|
18621
|
+
/**
|
|
18622
|
+
* The node-row attrs of a write, after handing any exposure keys to the side
|
|
18623
|
+
* row (exposure-attrs.ts). `getNode` returns exposure merged into attrs, so a
|
|
18624
|
+
* read-modify-write carries it; kept in the row it would re-bloat the row and
|
|
18625
|
+
* drift from the side row. Unchanged exposure costs no write.
|
|
18626
|
+
*/
|
|
18627
|
+
nodeAttrsJson(id, attrs) {
|
|
18628
|
+
if (!EXPOSURE_ATTRS.some((k) => attrs[k] !== void 0)) return JSON.stringify(attrs);
|
|
18629
|
+
const current = this.stmts.exposureOf.get(id);
|
|
18630
|
+
const next = JSON.stringify({ ...current ? JSON.parse(current.attrs_json) : {}, ...pickExposure(attrs) });
|
|
18631
|
+
if (current?.attrs_json !== next) this.stmts.writeExposure.run(id, next);
|
|
18632
|
+
const rest = { ...attrs };
|
|
18633
|
+
for (const k of EXPOSURE_ATTRS) delete rest[k];
|
|
18634
|
+
return JSON.stringify(rest);
|
|
18635
|
+
}
|
|
18636
|
+
/** One-time: exposure keys written to node rows before the side table existed
|
|
18637
|
+
* move to it. Only rows carrying a counter can carry exposure, and those are
|
|
18638
|
+
* what nodes_wm_counted holds, so this reads the index, not the table. */
|
|
18639
|
+
moveExposureToSideRows() {
|
|
18640
|
+
if (this.db.prepare("SELECT value FROM store_meta WHERE key = ?").get(EXPOSURE_SIDE_KEY)) return;
|
|
18641
|
+
const rows = this.db.prepare(`SELECT id, attrs_json FROM nodes INDEXED BY nodes_wm_counted WHERE ${WM_COUNTED_PREDICATE}`).all();
|
|
18642
|
+
const move = this.db.prepare("UPDATE nodes SET attrs_json = ? WHERE id = ?");
|
|
18643
|
+
this.db.transaction(() => {
|
|
18644
|
+
for (const r of rows) {
|
|
18645
|
+
const stripped = this.nodeAttrsJson(r.id, JSON.parse(r.attrs_json));
|
|
18646
|
+
if (stripped !== r.attrs_json) move.run(stripped, r.id);
|
|
18647
|
+
}
|
|
18648
|
+
this.db.prepare("INSERT OR REPLACE INTO store_meta(key, value) VALUES (?, 'done')").run(EXPOSURE_SIDE_KEY);
|
|
18562
18649
|
});
|
|
18563
18650
|
}
|
|
18564
18651
|
/** Fast single-column writes for the nightly pass (avoid the 18-column COALESCE
|
|
@@ -18721,7 +18808,27 @@ var SqliteGraphStore = class {
|
|
|
18721
18808
|
}
|
|
18722
18809
|
getNode(id) {
|
|
18723
18810
|
const row = this.stmts.getNode.get(id);
|
|
18724
|
-
|
|
18811
|
+
if (!row) return null;
|
|
18812
|
+
const node = rowToNode(row);
|
|
18813
|
+
if (row.exposure_json) Object.assign(node.attrs, JSON.parse(row.exposure_json));
|
|
18814
|
+
return node;
|
|
18815
|
+
}
|
|
18816
|
+
/** A node's render exposure: the side row's keys over the node row's own
|
|
18817
|
+
* (exposure-attrs.ts), plus the node's `lastUpdatedAt` (the "shown since
|
|
18818
|
+
* touch" counter is relative to it) — without reading the node's attrs. */
|
|
18819
|
+
exposureOf(id) {
|
|
18820
|
+
const row = this.stmts.lastUpdatedOf.get(id);
|
|
18821
|
+
if (!row) return null;
|
|
18822
|
+
const side = this.stmts.exposureOf.get(id);
|
|
18823
|
+
return {
|
|
18824
|
+
attrs: { ...pickExposure(JSON.parse(row.attrs_json)), ...side ? JSON.parse(side.attrs_json) : {} },
|
|
18825
|
+
lastUpdatedAt: row.t
|
|
18826
|
+
};
|
|
18827
|
+
}
|
|
18828
|
+
/** Replace a node's exposure (only EXPOSURE_ATTRS keys belong here). Touches
|
|
18829
|
+
* neither the node row nor its indexes, nor the change feed. */
|
|
18830
|
+
writeExposure(id, attrs) {
|
|
18831
|
+
this.stmts.writeExposure.run(id, JSON.stringify(attrs));
|
|
18725
18832
|
}
|
|
18726
18833
|
nodeLabels(ids) {
|
|
18727
18834
|
const width = NODE_LABELS_CHUNK;
|
|
@@ -18772,7 +18879,7 @@ var SqliteGraphStore = class {
|
|
|
18772
18879
|
findNodeStampsByLabel(label, attrKey) {
|
|
18773
18880
|
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(attrKey)) throw new Error(`findNodeStampsByLabel: bad attr key ${attrKey}`);
|
|
18774
18881
|
const rows = this.db.prepare(
|
|
18775
|
-
`SELECT id, created_at, created_at_seq, json_type(
|
|
18882
|
+
`SELECT id, created_at, created_at_seq, json_type(${attrDocSql(attrKey)}, '$.${attrKey}') IS NOT NULL AS has,
|
|
18776
18883
|
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
18777
18884
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
18778
18885
|
).all(label);
|
|
@@ -18789,7 +18896,7 @@ var SqliteGraphStore = class {
|
|
|
18789
18896
|
const keys = [...new Set(attrKeys)];
|
|
18790
18897
|
const rows = this.db.prepare(
|
|
18791
18898
|
`SELECT id, created_at, created_at_seq,
|
|
18792
|
-
${keys.map((k, i) => `json_type(
|
|
18899
|
+
${keys.map((k, i) => `json_type(${attrDocSql(k)}, '$.${k}') IS NOT NULL AS h${i}`).join(", ")}${keys.length ? "," : ""}
|
|
18793
18900
|
COALESCE(json_extract(attrs_json, '$.source') = 'cloud', 0) AS cloud
|
|
18794
18901
|
FROM nodes WHERE label = ? AND valid_to IS NULL`
|
|
18795
18902
|
).all(label);
|
|
@@ -19049,6 +19156,9 @@ var SqliteGraphStore = class {
|
|
|
19049
19156
|
changedNodeIds(sinceSeq, labels) {
|
|
19050
19157
|
return readChangedNodeIds(this.db, sinceSeq, labels);
|
|
19051
19158
|
}
|
|
19159
|
+
changedAmong(sinceSeq, ids) {
|
|
19160
|
+
return readChangedAmong(this.db, sinceSeq, ids);
|
|
19161
|
+
}
|
|
19052
19162
|
pruneChangeLog() {
|
|
19053
19163
|
return pruneChangeLog(this.db);
|
|
19054
19164
|
}
|