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