@agentshouse/mdruntime 0.1.0-cee9c8aa4cd86 → 0.1.0-cefbc8bb8a1c0
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 +10 -8
- package/dist/adapter.d.ts +31 -18
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +2 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +28 -4
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +15 -4
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +140 -90
- package/dist/client.d.ts +6 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +54 -7
- package/dist/collection.d.ts +5 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +23 -2
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +85 -30
- package/dist/connection.d.ts +9 -7
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +961 -476
- package/dist/conversation.d.ts +7 -5
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +166 -41
- package/dist/durability.d.ts +2 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +1 -0
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +1 -1
- package/dist/file.d.ts +2 -11
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +6 -4
- package/dist/fulltext.d.ts.map +1 -1
- package/dist/fulltext.js +8 -6
- package/dist/host.d.ts +4 -3
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +43 -15
- package/dist/index.d.ts +9 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +2 -1
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +2 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +46 -19
- package/dist/model.d.ts +6 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -1
- package/dist/observation.d.ts +1 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +1 -1
- package/dist/outcome.d.ts +13 -3
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +16 -5
- package/dist/overlay.js +1 -1
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +9 -0
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +12 -1
- package/dist/reach.d.ts +8 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +68 -45
- package/dist/readiness.d.ts +17 -7
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +6 -6
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +10 -17
- package/dist/recovery.d.ts +1 -0
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +26 -0
- package/dist/refresh.d.ts +6 -2
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +61 -27
- package/dist/replica.d.ts +5 -4
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +4 -3
- package/dist/room-draft.d.ts +18 -2
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +73 -6
- package/dist/rows.d.ts +15 -6
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +102 -74
- package/dist/save.d.ts +2 -0
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +13 -7
- package/dist/status.d.ts +5 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +18 -12
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +25 -20
- package/dist/stored.d.ts +10 -13
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +210 -174
- package/dist/tables.d.ts +26 -6
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +57 -6
- package/dist/wire.d.ts +5 -5
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +2 -2
- package/dist/words.d.ts.map +1 -1
- package/dist/words.js +8 -2
- package/package.json +11 -18
- package/AGENTS.md +0 -320
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/context.js +0 -10
- package/dist/chat/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- package/dist/chat/house.d.ts +0 -25
- package/dist/chat/house.d.ts.map +0 -1
- package/dist/chat/house.js +0 -38
- package/dist/chat/input.d.ts +0 -11
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -92
- package/dist/chat/kit.d.ts +0 -61
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -28
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -103
- package/dist/chat/markdown.d.ts +0 -21
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -246
- package/dist/chat/message.d.ts +0 -7
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -254
- package/dist/chat/messages.d.ts +0 -35
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -210
- package/dist/chat/name.d.ts +0 -7
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -12
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -19
- package/dist/chat/overlay.d.ts +0 -32
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -103
- package/dist/chat/parts.d.ts +0 -58
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -120
- package/dist/chat/pieces.d.ts +0 -62
- package/dist/chat/pieces.d.ts.map +0 -1
- package/dist/chat/pieces.js +0 -1110
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -70
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -635
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -37
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -93
- package/dist/chat/room-file.d.ts +0 -8
- package/dist/chat/room-file.d.ts.map +0 -1
- package/dist/chat/room-file.js +0 -72
- package/dist/chat/steady.d.ts +0 -3
- package/dist/chat/steady.d.ts.map +0 -1
- package/dist/chat/steady.js +0 -26
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -307
- package/dist/chat.d.ts +0 -15
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
package/dist/stored.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
+
import { composedOf } from './collection.js';
|
|
1
2
|
import { indexDocument, unindexAll, unindexDocuments } from './fulltext.js';
|
|
2
|
-
import { derived } from './rows.js';
|
|
3
|
+
import { described, derived } from './rows.js';
|
|
3
4
|
export const REPLICA_TABLES = [
|
|
4
|
-
'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL,
|
|
5
|
+
'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL, fingerprint TEXT NOT NULL, rederiving INTEGER, searching INTEGER)',
|
|
5
6
|
'CREATE TABLE replica_rooms (locator TEXT PRIMARY KEY, key INTEGER NOT NULL, record TEXT NOT NULL) WITHOUT ROWID',
|
|
6
|
-
'CREATE TABLE replica_transfers (locator TEXT PRIMARY KEY, key INTEGER NOT NULL, record TEXT NOT NULL) WITHOUT ROWID',
|
|
7
7
|
'CREATE TABLE replica_entries (id INTEGER PRIMARY KEY, key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, visible INTEGER NOT NULL, present INTEGER NOT NULL, queried INTEGER NOT NULL, binary TEXT, revision TEXT, file_id TEXT, change TEXT, written TEXT, type TEXT, declared TEXT, folder TEXT NOT NULL, fields TEXT, content TEXT)',
|
|
8
8
|
'CREATE UNIQUE INDEX replica_entries_path ON replica_entries (key, layer, path)',
|
|
9
|
-
'CREATE INDEX replica_entries_visible ON replica_entries (key, visible, path)',
|
|
9
|
+
'CREATE INDEX replica_entries_visible ON replica_entries (key, visible, present, path, binary)',
|
|
10
10
|
'CREATE INDEX replica_entries_folder ON replica_entries (key, folder, path)',
|
|
11
|
-
'CREATE
|
|
12
|
-
'CREATE INDEX
|
|
13
|
-
'CREATE TABLE
|
|
14
|
-
'CREATE
|
|
11
|
+
'CREATE INDEX replica_entries_type ON replica_entries (key, type, visible, present, queried, folder)',
|
|
12
|
+
'CREATE INDEX replica_entries_key ON replica_entries (key)',
|
|
13
|
+
'CREATE TABLE replica_values (doc INTEGER NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, item INTEGER NOT NULL, kind TEXT NOT NULL, value, size INTEGER, PRIMARY KEY (doc, keys, places, item)) WITHOUT ROWID',
|
|
14
|
+
'CREATE TABLE replica_links (doc INTEGER NOT NULL, key INTEGER NOT NULL, kind TEXT NOT NULL, keys TEXT NOT NULL, places TEXT NOT NULL, target TEXT NOT NULL, field TEXT NOT NULL, PRIMARY KEY (doc, kind, keys, places, target)) WITHOUT ROWID',
|
|
15
|
+
'CREATE INDEX replica_links_target ON replica_links (key, target, kind, keys)',
|
|
15
16
|
'CREATE TABLE replica_folders (key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, present INTEGER NOT NULL, PRIMARY KEY (key, layer, path)) WITHOUT ROWID',
|
|
16
17
|
'CREATE TABLE replica_erasing (key INTEGER PRIMARY KEY, locator TEXT NOT NULL)',
|
|
17
18
|
'CREATE TABLE replica_received (key TEXT PRIMARY KEY, head TEXT NOT NULL) WITHOUT ROWID',
|
|
@@ -22,7 +23,17 @@ export const REPLICA_TABLES = [
|
|
|
22
23
|
const CONFIRMED = 0;
|
|
23
24
|
const OVERLAY = 1;
|
|
24
25
|
const REFRESHED = 2;
|
|
25
|
-
const
|
|
26
|
+
const DERIVED_ROWS = 64;
|
|
27
|
+
const ENTRY_WEIGHT = 256;
|
|
28
|
+
const ABSENT = {
|
|
29
|
+
type: null,
|
|
30
|
+
declared: null,
|
|
31
|
+
queried: false,
|
|
32
|
+
folder: '',
|
|
33
|
+
fields: null,
|
|
34
|
+
values: [],
|
|
35
|
+
links: [],
|
|
36
|
+
};
|
|
26
37
|
function text(value) {
|
|
27
38
|
return typeof value === 'string' ? value : null;
|
|
28
39
|
}
|
|
@@ -38,84 +49,54 @@ function flag(value) {
|
|
|
38
49
|
function paths(list) {
|
|
39
50
|
return JSON.stringify(list);
|
|
40
51
|
}
|
|
41
|
-
function compositionOf(connection, key) {
|
|
42
|
-
let held = compositions.get(connection);
|
|
43
|
-
if (held === undefined) {
|
|
44
|
-
held = new Map();
|
|
45
|
-
compositions.set(connection, held);
|
|
46
|
-
}
|
|
47
|
-
const [row] = connection.rows('SELECT composition FROM replica_keys WHERE key = ?', [key]);
|
|
48
|
-
const textual = text(row?.composition) ?? 'null';
|
|
49
|
-
const known = held.get(textual);
|
|
50
|
-
if (known !== undefined)
|
|
51
|
-
return known;
|
|
52
|
-
const parsed = JSON.parse(textual) ?? {
|
|
53
|
-
model: { status: 'unavailable', reason: 'not-acquired' },
|
|
54
|
-
selection: null,
|
|
55
|
-
};
|
|
56
|
-
held.clear();
|
|
57
|
-
held.set(textual, parsed);
|
|
58
|
-
return parsed;
|
|
59
|
-
}
|
|
60
|
-
export function fingerprint(composition) {
|
|
61
|
-
const shape = (model) => model.status === 'unavailable' ? null : model.types;
|
|
62
|
-
return JSON.stringify({
|
|
63
|
-
model: shape(composition.model),
|
|
64
|
-
selection: composition.selection === null
|
|
65
|
-
? null
|
|
66
|
-
: { roots: composition.selection.roots, model: shape(composition.selection.model) },
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
52
|
function dropDerived(connection, doc) {
|
|
70
53
|
connection.run('DELETE FROM replica_values WHERE doc = ?', [doc]);
|
|
71
54
|
connection.run('DELETE FROM replica_links WHERE doc = ?', [doc]);
|
|
72
55
|
}
|
|
56
|
+
function scalarText(value) {
|
|
57
|
+
if (value === Infinity)
|
|
58
|
+
return 'Infinity';
|
|
59
|
+
if (value === -Infinity)
|
|
60
|
+
return '-Infinity';
|
|
61
|
+
return JSON.stringify(value);
|
|
62
|
+
}
|
|
73
63
|
function writeDerived(connection, doc, key, made) {
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
if (made.values.length > 0) {
|
|
65
|
+
const rows = made.values.map((value) => `[${JSON.stringify(value.keys)},${JSON.stringify(value.places)},${value.item},${JSON.stringify(value.kind)},${scalarText(value.value)},${JSON.stringify(value.size)}]`);
|
|
66
|
+
connection.run('INSERT INTO replica_values (doc, keys, places, item, kind, value, size) SELECT ?, value ->> 0, value ->> 1, value ->> 2, value ->> 3, value ->> 4, value ->> 5 FROM json_each(?)', [doc, `[${rows.join(',')}]`]);
|
|
67
|
+
}
|
|
68
|
+
if (made.links.length > 0) {
|
|
69
|
+
connection.run('INSERT OR IGNORE INTO replica_links (doc, key, kind, keys, places, target, field) SELECT ?, ?, value ->> 0, value ->> 1, value ->> 2, value ->> 3, value ->> 4 FROM json_each(?)', [
|
|
76
70
|
doc,
|
|
77
71
|
key,
|
|
78
|
-
|
|
79
|
-
value.held,
|
|
80
|
-
value.item,
|
|
81
|
-
value.kind,
|
|
82
|
-
value.value,
|
|
83
|
-
value.size,
|
|
84
|
-
flag(value.single),
|
|
72
|
+
JSON.stringify(made.links.map((link) => [link.kind, link.keys, link.places, link.target, link.field])),
|
|
85
73
|
]);
|
|
86
74
|
}
|
|
87
|
-
for (const link of made.links) {
|
|
88
|
-
connection.run('INSERT OR IGNORE INTO replica_links (doc, key, kind, form, target, field) VALUES (?, ?, ?, ?, ?, ?)', [doc, key, link.kind, link.form, link.target, link.field]);
|
|
89
|
-
}
|
|
90
75
|
}
|
|
91
76
|
function entryId(connection, key, layer, path) {
|
|
92
77
|
const [row] = connection.rows('SELECT id FROM replica_entries WHERE key = ? AND layer = ? AND path = ?', [key, layer, path]);
|
|
93
78
|
return row === undefined ? null : number(row.id);
|
|
94
79
|
}
|
|
95
|
-
function
|
|
80
|
+
function words() {
|
|
81
|
+
return { dropped: [], made: [] };
|
|
82
|
+
}
|
|
83
|
+
function wordsWritten(connection, owed) {
|
|
84
|
+
unindexDocuments(connection, owed.dropped);
|
|
85
|
+
for (const { id, path, content } of owed.made)
|
|
86
|
+
indexDocument(connection, id, path, content);
|
|
87
|
+
}
|
|
88
|
+
function removeEntry(connection, key, layer, path, owed) {
|
|
96
89
|
const id = entryId(connection, key, layer, path);
|
|
97
90
|
if (id === null)
|
|
98
91
|
return;
|
|
99
92
|
dropDerived(connection, id);
|
|
100
|
-
|
|
93
|
+
owed.dropped.push(id);
|
|
101
94
|
connection.run('DELETE FROM replica_entries WHERE id = ?', [id]);
|
|
102
95
|
}
|
|
103
96
|
function heldIds(connection, sql, bind) {
|
|
104
97
|
return connection.rows(sql, bind).map((row) => number(row.id));
|
|
105
98
|
}
|
|
106
|
-
function
|
|
107
|
-
removeEntry(connection, key, layer, entry.path);
|
|
108
|
-
const made = present
|
|
109
|
-
? derived(entry.path, entry.binary === null ? entry.content : null, composition)
|
|
110
|
-
: {
|
|
111
|
-
type: null,
|
|
112
|
-
declared: null,
|
|
113
|
-
queried: false,
|
|
114
|
-
folder: '',
|
|
115
|
-
fields: null,
|
|
116
|
-
values: [],
|
|
117
|
-
links: [],
|
|
118
|
-
};
|
|
99
|
+
function insertEntry(connection, key, layer, entry, present, made) {
|
|
119
100
|
connection.run('INSERT INTO replica_entries (key, layer, path, visible, present, queried, content, binary, revision, file_id, change, written, type, declared, folder, fields) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
120
101
|
key,
|
|
121
102
|
layer,
|
|
@@ -134,73 +115,76 @@ function putEntry(connection, key, layer, entry, present, composition, searched
|
|
|
134
115
|
made.folder,
|
|
135
116
|
made.fields,
|
|
136
117
|
]);
|
|
118
|
+
}
|
|
119
|
+
function textOf(entry) {
|
|
120
|
+
return entry.binary === null ? entry.content : null;
|
|
121
|
+
}
|
|
122
|
+
function putEntry(connection, key, layer, entry, present, composition, owed, searched = true) {
|
|
123
|
+
removeEntry(connection, key, layer, entry.path, owed);
|
|
124
|
+
const content = textOf(entry);
|
|
125
|
+
const made = present ? derived(entry.path, content, composition) : ABSENT;
|
|
126
|
+
insertEntry(connection, key, layer, entry, present, made);
|
|
137
127
|
if (!present)
|
|
138
128
|
return;
|
|
139
129
|
const id = entryId(connection, key, layer, entry.path);
|
|
140
130
|
writeDerived(connection, id, key, made);
|
|
141
|
-
if (searched &&
|
|
142
|
-
|
|
143
|
-
|
|
131
|
+
if (searched && content !== null && made.queried)
|
|
132
|
+
owed.made.push({ id, path: entry.path, content });
|
|
133
|
+
}
|
|
134
|
+
function placeEntry(connection, key, entry, owed) {
|
|
135
|
+
removeEntry(connection, key, CONFIRMED, entry.path, owed);
|
|
136
|
+
insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry)));
|
|
144
137
|
}
|
|
145
138
|
function revealed(connection, key, touched) {
|
|
146
139
|
if (touched.length === 0)
|
|
147
140
|
return;
|
|
148
141
|
connection.run(`UPDATE replica_entries SET visible = CASE WHEN layer = ${OVERLAY} THEN present ELSE NOT EXISTS (SELECT 1 FROM replica_entries o WHERE o.key = replica_entries.key AND o.layer = ${OVERLAY} AND o.path = replica_entries.path) END WHERE key = ? AND layer IN (${CONFIRMED}, ${OVERLAY}) AND path IN (SELECT value FROM json_each(?))`, [key, paths(touched)]);
|
|
149
142
|
}
|
|
150
|
-
function newKey(connection, locator, transfer,
|
|
151
|
-
|
|
143
|
+
function newKey(connection, locator, transfer, fingerprint, built) {
|
|
144
|
+
const owed = built ? null : 0;
|
|
145
|
+
connection.run('INSERT INTO replica_keys (locator, transfer, fingerprint, rederiving, searching) VALUES (?, ?, ?, ?, ?)', [locator, transfer, fingerprint, owed, owed]);
|
|
152
146
|
const [row] = connection.rows('SELECT last_insert_rowid() AS key');
|
|
153
147
|
return number(row.key);
|
|
154
148
|
}
|
|
155
|
-
function recomposed(connection, key,
|
|
156
|
-
const [row] = connection.rows('SELECT
|
|
157
|
-
|
|
158
|
-
if (before !== null && fingerprint(before) === fingerprint(composition)) {
|
|
159
|
-
connection.run('UPDATE replica_keys SET composition = ? WHERE key = ?', [
|
|
160
|
-
JSON.stringify(composition),
|
|
161
|
-
key,
|
|
162
|
-
]);
|
|
149
|
+
function recomposed(connection, key, fingerprint) {
|
|
150
|
+
const [row] = connection.rows('SELECT fingerprint FROM replica_keys WHERE key = ?', [key]);
|
|
151
|
+
if (row === undefined || text(row.fingerprint) === fingerprint)
|
|
163
152
|
return;
|
|
164
|
-
|
|
165
|
-
connection.run(
|
|
166
|
-
JSON.stringify(composition),
|
|
167
|
-
key,
|
|
168
|
-
]);
|
|
153
|
+
const held = connection.rows('SELECT 1 FROM replica_entries WHERE key = ? LIMIT 1', [key]);
|
|
154
|
+
connection.run(`UPDATE replica_keys SET fingerprint = ?${held.length === 0 ? '' : ', rederiving = 0'} WHERE key = ?`, [fingerprint, key]);
|
|
169
155
|
}
|
|
170
156
|
export function entered(connection, locator, composition, rows) {
|
|
171
|
-
const key = newKey(connection, locator, '', composition);
|
|
172
|
-
|
|
173
|
-
|
|
157
|
+
const key = newKey(connection, locator, '', composedOf(composition).fingerprint, true);
|
|
158
|
+
const owed = words();
|
|
159
|
+
for (const entry of rows) {
|
|
160
|
+
putEntry(connection, key, CONFIRMED, entry, true, composition, owed, false);
|
|
161
|
+
}
|
|
162
|
+
wordsWritten(connection, owed);
|
|
174
163
|
return key;
|
|
175
164
|
}
|
|
176
165
|
export function stage(connection, request) {
|
|
177
|
-
const [held] = connection.rows('SELECT key FROM replica_transfers WHERE locator = ?', [
|
|
178
|
-
request.locator,
|
|
179
|
-
]);
|
|
180
166
|
const key = request.key ??
|
|
181
|
-
(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
connection
|
|
187
|
-
}
|
|
188
|
-
const composition = compositionOf(connection, key);
|
|
189
|
-
for (const entry of request.entries) {
|
|
190
|
-
putEntry(connection, key, CONFIRMED, entry, true, composition);
|
|
191
|
-
}
|
|
167
|
+
newKey(connection, request.locator, request.transfer, request.fingerprint, false);
|
|
168
|
+
recomposed(connection, key, request.fingerprint);
|
|
169
|
+
connection.run('UPDATE replica_keys SET rederiving = coalesce(rederiving, last), searching = coalesce(searching, last) FROM (SELECT coalesce(max(id), 0) AS last FROM replica_entries) WHERE key = ?', [key]);
|
|
170
|
+
const owed = words();
|
|
171
|
+
for (const entry of request.entries)
|
|
172
|
+
placeEntry(connection, key, entry, owed);
|
|
192
173
|
for (const path of request.folders) {
|
|
193
174
|
connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, 1)', [key, CONFIRMED, path]);
|
|
194
175
|
}
|
|
176
|
+
wordsWritten(connection, owed);
|
|
195
177
|
return key;
|
|
196
178
|
}
|
|
197
179
|
export function refreshing(connection, request) {
|
|
198
|
-
const held = connection.rows('SELECT 1 FROM
|
|
180
|
+
const held = connection.rows('SELECT 1 FROM replica_keys k WHERE k.key = ? AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)', [request.key]);
|
|
199
181
|
if (held.length === 0)
|
|
200
182
|
return null;
|
|
201
|
-
|
|
183
|
+
recomposed(connection, request.key, request.composed.fingerprint);
|
|
184
|
+
const composition = request.composed.composition;
|
|
185
|
+
const owed = words();
|
|
202
186
|
for (const entry of request.entries) {
|
|
203
|
-
putEntry(connection, request.key, REFRESHED, entry, true, composition);
|
|
187
|
+
putEntry(connection, request.key, REFRESHED, entry, true, composition, owed);
|
|
204
188
|
}
|
|
205
189
|
for (const path of request.removals) {
|
|
206
190
|
putEntry(connection, request.key, REFRESHED, {
|
|
@@ -211,7 +195,7 @@ export function refreshing(connection, request) {
|
|
|
211
195
|
fileId: null,
|
|
212
196
|
change: null,
|
|
213
197
|
written: null,
|
|
214
|
-
}, false, composition);
|
|
198
|
+
}, false, composition, owed);
|
|
215
199
|
}
|
|
216
200
|
for (const [list, present] of [
|
|
217
201
|
[request.folders, 1],
|
|
@@ -221,6 +205,7 @@ export function refreshing(connection, request) {
|
|
|
221
205
|
connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, ?)', [request.key, REFRESHED, path, present]);
|
|
222
206
|
}
|
|
223
207
|
}
|
|
208
|
+
wordsWritten(connection, owed);
|
|
224
209
|
return null;
|
|
225
210
|
}
|
|
226
211
|
function dropLayer(connection, key, layer) {
|
|
@@ -262,12 +247,11 @@ function applyRefresh(connection, key) {
|
|
|
262
247
|
connection.run('DELETE FROM replica_folders WHERE key = ? AND layer = ?', [key, REFRESHED]);
|
|
263
248
|
return touched;
|
|
264
249
|
}
|
|
250
|
+
function beneathRoots(path) {
|
|
251
|
+
return `EXISTS (SELECT 1 FROM json_each(?) r WHERE ${path} = r.value OR (${path} >= r.value || '/' AND ${path} < r.value || '0'))`;
|
|
252
|
+
}
|
|
265
253
|
function counted(connection, key, roots) {
|
|
266
|
-
const
|
|
267
|
-
const bind = [key];
|
|
268
|
-
for (const root of roots)
|
|
269
|
-
bind.push(root, `${root}/`, `${root}0`);
|
|
270
|
-
const [row] = connection.rows(`SELECT count(*) AS held FROM replica_entries WHERE key = ? AND visible = 1 AND present = 1 AND (binary IS NOT NULL OR substr(path, -3) = '.md')${roots.length === 0 ? '' : ` AND NOT (${outside})`}`, bind);
|
|
254
|
+
const [row] = connection.rows(`SELECT count(*) AS held FROM replica_entries WHERE key = ? AND visible = 1 AND present = 1 AND (binary IS NOT NULL OR substr(path, -3) = '.md') AND NOT ${beneathRoots('replica_entries.path')}`, [key, JSON.stringify(roots)]);
|
|
271
255
|
return number(row.held);
|
|
272
256
|
}
|
|
273
257
|
export function counts(connection, rooms) {
|
|
@@ -281,33 +265,32 @@ function erasing(connection, key, locator) {
|
|
|
281
265
|
}
|
|
282
266
|
function commitRoom(connection, room) {
|
|
283
267
|
const touched = new Set();
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
connection.run('DELETE FROM replica_transfers WHERE locator = ?', [room.locator]);
|
|
268
|
+
recomposed(connection, room.key, room.composed.fingerprint);
|
|
269
|
+
const composition = room.composed.composition;
|
|
270
|
+
if (room.published !== undefined &&
|
|
271
|
+
room.published.from !== null &&
|
|
272
|
+
room.published.from !== room.key) {
|
|
273
|
+
erasing(connection, room.published.from, room.locator);
|
|
292
274
|
}
|
|
293
275
|
if (room.refreshed === true) {
|
|
294
276
|
for (const path of applyRefresh(connection, room.key))
|
|
295
277
|
touched.add(path);
|
|
296
278
|
}
|
|
279
|
+
const owed = words();
|
|
297
280
|
for (const entry of room.confirmed?.set ?? []) {
|
|
298
|
-
putEntry(connection, room.key, CONFIRMED, entry, true, composition);
|
|
281
|
+
putEntry(connection, room.key, CONFIRMED, entry, true, composition, owed);
|
|
299
282
|
touched.add(entry.path);
|
|
300
283
|
}
|
|
301
284
|
for (const path of room.confirmed?.removed ?? []) {
|
|
302
|
-
removeEntry(connection, room.key, CONFIRMED, path);
|
|
285
|
+
removeEntry(connection, room.key, CONFIRMED, path, owed);
|
|
303
286
|
touched.add(path);
|
|
304
287
|
}
|
|
305
288
|
for (const entry of room.overlay?.set ?? []) {
|
|
306
|
-
putEntry(connection, room.key, OVERLAY, entry, entry.present, composition);
|
|
289
|
+
putEntry(connection, room.key, OVERLAY, entry, entry.present, composition, owed);
|
|
307
290
|
touched.add(entry.path);
|
|
308
291
|
}
|
|
309
292
|
for (const path of room.overlay?.cleared ?? []) {
|
|
310
|
-
removeEntry(connection, room.key, OVERLAY, path);
|
|
293
|
+
removeEntry(connection, room.key, OVERLAY, path, owed);
|
|
311
294
|
touched.add(path);
|
|
312
295
|
}
|
|
313
296
|
const changed = [...touched];
|
|
@@ -319,21 +302,25 @@ function commitRoom(connection, room) {
|
|
|
319
302
|
room.record,
|
|
320
303
|
]);
|
|
321
304
|
}
|
|
305
|
+
wordsWritten(connection, owed);
|
|
322
306
|
return room.published === undefined ? changed : null;
|
|
323
307
|
}
|
|
324
308
|
export function commit(connection, rooms) {
|
|
325
309
|
const counted = {};
|
|
326
310
|
const changed = {};
|
|
327
|
-
const
|
|
311
|
+
const indexed = {};
|
|
328
312
|
for (const room of rooms) {
|
|
329
313
|
changed[room.locator] = commitRoom(connection, room);
|
|
330
314
|
counted[room.locator] = counts(connection, [
|
|
331
315
|
{ key: room.key, roots: room.roots ?? rootsOf(connection, room.locator) },
|
|
332
316
|
])[0];
|
|
333
|
-
const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [
|
|
334
|
-
|
|
317
|
+
const [held] = connection.rows('SELECT rederiving, searching FROM replica_keys WHERE key = ?', [
|
|
318
|
+
room.key,
|
|
319
|
+
]);
|
|
320
|
+
indexed[room.locator] =
|
|
321
|
+
held !== undefined && held.rederiving === null && held.searching === null;
|
|
335
322
|
}
|
|
336
|
-
return { counts: counted, changed,
|
|
323
|
+
return { counts: counted, changed, indexed };
|
|
337
324
|
}
|
|
338
325
|
function rootsOf(connection, locator) {
|
|
339
326
|
const [row] = connection.rows('SELECT record FROM replica_rooms WHERE locator = ?', [locator]);
|
|
@@ -342,27 +329,24 @@ function rootsOf(connection, locator) {
|
|
|
342
329
|
}
|
|
343
330
|
export function fenceRooms(connection, locators) {
|
|
344
331
|
for (const locator of locators) {
|
|
345
|
-
for (const row of connection.rows('SELECT key FROM replica_keys WHERE locator = ? UNION SELECT key FROM replica_rooms WHERE locator = ?
|
|
332
|
+
for (const row of connection.rows('SELECT key FROM replica_keys WHERE locator = ? UNION SELECT key FROM replica_rooms WHERE locator = ?', [locator, locator])) {
|
|
346
333
|
erasing(connection, number(row.key), locator);
|
|
347
334
|
}
|
|
348
335
|
connection.run('DELETE FROM replica_rooms WHERE locator = ?', [locator]);
|
|
349
|
-
connection.run('DELETE FROM replica_transfers WHERE locator = ?', [locator]);
|
|
350
336
|
}
|
|
351
337
|
return null;
|
|
352
338
|
}
|
|
353
|
-
export function abandon(connection, locator
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return number(held.key);
|
|
339
|
+
export function abandon(connection, locator) {
|
|
340
|
+
for (const row of connection.rows(`SELECT k.key FROM replica_keys k WHERE k.locator = ?
|
|
341
|
+
AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
342
|
+
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`, [locator])) {
|
|
343
|
+
erasing(connection, number(row.key), locator);
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
361
346
|
}
|
|
362
347
|
export function release(connection, key) {
|
|
363
348
|
const [held] = connection.rows(`SELECT k.locator FROM replica_keys k WHERE k.key = ?
|
|
364
|
-
AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
365
|
-
AND NOT EXISTS (SELECT 1 FROM replica_transfers t WHERE t.key = k.key)`, [key]);
|
|
349
|
+
AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)`, [key]);
|
|
366
350
|
if (held !== undefined)
|
|
367
351
|
erasing(connection, key, text(held.locator));
|
|
368
352
|
return null;
|
|
@@ -387,28 +371,70 @@ export function erase(connection, locator, limit) {
|
|
|
387
371
|
const [more] = connection.rows(`SELECT 1 FROM replica_erasing${locator === null ? '' : ' WHERE locator = ?'} LIMIT 1`, locator === null ? [] : [locator]);
|
|
388
372
|
return { remaining: more !== undefined };
|
|
389
373
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
374
|
+
function cursor(value) {
|
|
375
|
+
return value === null || value === undefined ? null : number(value);
|
|
376
|
+
}
|
|
377
|
+
export function rederive(connection, key, composed, bytes, ms, floor) {
|
|
378
|
+
const started = performance.now();
|
|
379
|
+
recomposed(connection, key, composed.fingerprint);
|
|
380
|
+
const [held] = connection.rows('SELECT rederiving, searching FROM replica_keys WHERE key = ?', [
|
|
381
|
+
key,
|
|
382
|
+
]);
|
|
383
|
+
const deriving = cursor(held?.rederiving);
|
|
384
|
+
const searching = cursor(held?.searching);
|
|
385
|
+
if (deriving === null && searching === null)
|
|
386
|
+
return { done: true, text: 0 };
|
|
387
|
+
const composition = composed.composition;
|
|
388
|
+
let last = Math.min(deriving ?? Infinity, searching ?? Infinity);
|
|
389
|
+
let spent = 0;
|
|
390
|
+
let built = 0;
|
|
391
|
+
let read = false;
|
|
392
|
+
const late = () => spent >= floor && performance.now() - started >= ms;
|
|
393
|
+
const owed = [];
|
|
394
|
+
reading: for (;;) {
|
|
395
|
+
const rows = connection.rows('SELECT id, path, content FROM replica_entries WHERE key = ? AND present = 1 AND id > ? ORDER BY id LIMIT ?', [key, last, DERIVED_ROWS]);
|
|
396
|
+
for (const row of rows) {
|
|
397
|
+
const id = number(row.id);
|
|
398
|
+
const path = text(row.path);
|
|
399
|
+
const content = text(row.content);
|
|
400
|
+
const owedRows = deriving !== null && id > deriving;
|
|
401
|
+
const owedWords = searching !== null && id > searching && content !== null;
|
|
402
|
+
if (owedRows) {
|
|
403
|
+
const made = derived(path, content, composition);
|
|
404
|
+
dropDerived(connection, id);
|
|
405
|
+
writeDerived(connection, id, key, made);
|
|
406
|
+
}
|
|
407
|
+
if (owedWords && described(path, content).queried)
|
|
408
|
+
owed.push({ id, path, content });
|
|
409
|
+
else if (owedRows)
|
|
410
|
+
built += content?.length ?? 0;
|
|
411
|
+
last = id;
|
|
412
|
+
spent += ENTRY_WEIGHT + (content?.length ?? 0);
|
|
413
|
+
if (spent >= bytes || late())
|
|
414
|
+
break reading;
|
|
415
|
+
}
|
|
416
|
+
if (rows.length < DERIVED_ROWS) {
|
|
417
|
+
read = true;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
let searched = last;
|
|
422
|
+
for (const [at, { id, path, content }] of owed.entries()) {
|
|
423
|
+
indexDocument(connection, id, path, content);
|
|
424
|
+
built += content.length;
|
|
425
|
+
const next = owed[at + 1];
|
|
426
|
+
if (next !== undefined && late()) {
|
|
427
|
+
searched = next.id - 1;
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
404
430
|
}
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
done ? null :
|
|
431
|
+
const done = read && searched === last;
|
|
432
|
+
connection.run('UPDATE replica_keys SET rederiving = ?, searching = ? WHERE key = ?', [
|
|
433
|
+
deriving === null || read ? null : Math.max(deriving, last),
|
|
434
|
+
searching === null || done ? null : Math.max(searching, searched),
|
|
409
435
|
key,
|
|
410
436
|
]);
|
|
411
|
-
return { done };
|
|
437
|
+
return { done, text: built };
|
|
412
438
|
}
|
|
413
439
|
function heldEntry(row) {
|
|
414
440
|
return {
|
|
@@ -423,12 +449,31 @@ function heldEntry(row) {
|
|
|
423
449
|
};
|
|
424
450
|
}
|
|
425
451
|
const ENTRY_COLUMNS = 'e.path, e.content, e.binary, e.revision, e.file_id, e.change, e.written, e.type';
|
|
452
|
+
function unrefreshed(alias) {
|
|
453
|
+
return `NOT EXISTS (SELECT 1 FROM replica_entries r WHERE r.key = ${alias}.key AND r.layer = ${REFRESHED} AND r.path = ${alias}.path)`;
|
|
454
|
+
}
|
|
426
455
|
function viewed(view, alias) {
|
|
427
456
|
if (view === 'visible')
|
|
428
457
|
return `${alias}.visible = 1 AND ${alias}.present = 1`;
|
|
429
458
|
if (view === 'confirmed')
|
|
430
459
|
return `${alias}.layer = ${CONFIRMED}`;
|
|
431
|
-
return `${alias}.present = 1 AND (${alias}.layer = ${REFRESHED} OR (${alias}.layer = ${CONFIRMED} AND
|
|
460
|
+
return `${alias}.present = 1 AND (${alias}.layer = ${REFRESHED} OR (${alias}.layer = ${CONFIRMED} AND ${unrefreshed(alias)}))`;
|
|
461
|
+
}
|
|
462
|
+
function layers(view) {
|
|
463
|
+
if (view !== 'refreshed')
|
|
464
|
+
return [viewed(view, 'e')];
|
|
465
|
+
return [
|
|
466
|
+
`e.layer = ${REFRESHED} AND e.present = 1`,
|
|
467
|
+
`e.layer = ${CONFIRMED} AND e.present = 1 AND ${unrefreshed('e')}`,
|
|
468
|
+
];
|
|
469
|
+
}
|
|
470
|
+
function pathOrdered(connection, columns, key, view, condition, after, limit) {
|
|
471
|
+
const beyond = after === null ? '' : ' AND e.path > ?';
|
|
472
|
+
const parts = layers(view).map((layer) => `SELECT ${columns} FROM replica_entries e WHERE e.key = ? AND ${layer}${condition}${beyond}`);
|
|
473
|
+
return connection.rows(`${parts.join(' UNION ALL ')} ORDER BY path LIMIT ?`, [
|
|
474
|
+
...parts.flatMap(() => (after === null ? [key] : [key, after])),
|
|
475
|
+
limit,
|
|
476
|
+
]);
|
|
432
477
|
}
|
|
433
478
|
export function entries(connection, key, view, list) {
|
|
434
479
|
if (list.length === 0)
|
|
@@ -464,7 +509,7 @@ export function walk(connection, key, view, after, bytes) {
|
|
|
464
509
|
let spent = 0;
|
|
465
510
|
let from = after;
|
|
466
511
|
for (;;) {
|
|
467
|
-
const rows = connection
|
|
512
|
+
const rows = pathOrdered(connection, ENTRY_COLUMNS, key, view, '', from, 64);
|
|
468
513
|
for (const row of rows) {
|
|
469
514
|
const entry = heldEntry(row);
|
|
470
515
|
found.push(entry);
|
|
@@ -478,17 +523,13 @@ export function walk(connection, key, view, after, bytes) {
|
|
|
478
523
|
}
|
|
479
524
|
}
|
|
480
525
|
export function files(connection, key, view, after, limit) {
|
|
481
|
-
return connection
|
|
482
|
-
|
|
483
|
-
|
|
526
|
+
return pathOrdered(connection, 'e.path, e.revision', key, view, ' AND e.content IS NOT NULL AND e.revision IS NOT NULL', after, limit).map((row) => ({ path: text(row.path), revision: text(row.revision) }));
|
|
527
|
+
}
|
|
528
|
+
export function folders(connection, key, view, list) {
|
|
529
|
+
return list.filter((path) => connection.rows(`SELECT 1 FROM replica_folders f WHERE f.key = ? AND f.layer = ${CONFIRMED} AND f.present = 1 AND f.path = ? UNION ALL SELECT 1 FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.path >= ? AND e.path < ? LIMIT 1`, [key, path, key, `${path}/`, `${path}0`]).length > 0);
|
|
484
530
|
}
|
|
485
531
|
export function typedWithin(connection, key, view, roots, within) {
|
|
486
|
-
|
|
487
|
-
const bind = [key];
|
|
488
|
-
for (const root of roots)
|
|
489
|
-
bind.push(root, `${root}/`, `${root}0`);
|
|
490
|
-
const placed = roots.length === 0 ? (within ? ' AND 0' : '') : ` AND ${within ? '' : 'NOT '}(${inside})`;
|
|
491
|
-
return (connection.rows(`SELECT 1 FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.declared IS NOT NULL AND e.declared <> 'any'${placed} LIMIT 1`, bind).length > 0);
|
|
532
|
+
return (connection.rows(`SELECT 1 FROM replica_entries e WHERE e.key = ? AND ${viewed(view, 'e')} AND e.declared IS NOT NULL AND e.declared <> 'any' AND ${within ? '' : 'NOT '}${beneathRoots('e.path')} LIMIT 1`, [key, JSON.stringify(roots)]).length > 0);
|
|
492
533
|
}
|
|
493
534
|
export function opened(connection) {
|
|
494
535
|
const facts = {};
|
|
@@ -501,18 +542,14 @@ export function opened(connection) {
|
|
|
501
542
|
key: number(row.key),
|
|
502
543
|
record: text(row.record),
|
|
503
544
|
})),
|
|
504
|
-
transfers: connection.rows('SELECT locator, key, record FROM replica_transfers').map((row) => ({
|
|
505
|
-
locator: text(row.locator),
|
|
506
|
-
key: number(row.key),
|
|
507
|
-
record: text(row.record),
|
|
508
|
-
})),
|
|
509
545
|
keys: connection
|
|
510
|
-
.rows('SELECT key, locator, transfer, rederiving FROM replica_keys')
|
|
546
|
+
.rows('SELECT key, locator, transfer, rederiving, searching FROM replica_keys')
|
|
511
547
|
.map((row) => ({
|
|
512
548
|
key: number(row.key),
|
|
513
549
|
locator: text(row.locator),
|
|
514
550
|
transfer: text(row.transfer),
|
|
515
|
-
rederiving:
|
|
551
|
+
rederiving: cursor(row.rederiving),
|
|
552
|
+
searching: cursor(row.searching),
|
|
516
553
|
})),
|
|
517
554
|
facts,
|
|
518
555
|
presentation: connection.rows('SELECT app, value FROM replica_presentation').map((row) => ({
|
|
@@ -525,7 +562,6 @@ export function opened(connection) {
|
|
|
525
562
|
orphans: connection
|
|
526
563
|
.rows(`SELECT k.key FROM replica_keys k
|
|
527
564
|
WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
528
|
-
AND NOT EXISTS (SELECT 1 FROM replica_transfers t WHERE t.key = k.key)
|
|
529
565
|
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`)
|
|
530
566
|
.map((row) => number(row.key)),
|
|
531
567
|
erasing: connection.rows('SELECT key, locator FROM replica_erasing').map((row) => ({
|