@agentshouse/mdruntime 0.1.0-c5fe3b2cddda7 → 0.1.0-c6197367d5fad
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 +21 -19
- package/dist/acceptance.d.ts +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +299 -0
- package/dist/act-lanes.d.ts +30 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +225 -0
- package/dist/actions.d.ts +26 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +302 -0
- package/dist/adapter.d.ts +156 -89
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -11
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +20 -60
- package/dist/authored.d.ts +88 -0
- package/dist/authored.d.ts.map +1 -0
- package/dist/authored.js +345 -0
- package/dist/bootstrap.d.ts +9 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +204 -85
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts +5 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +7 -2
- package/dist/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +11 -0
- package/dist/client.d.ts +33 -23
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +43 -56
- package/dist/collection.d.ts +26 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/column.d.ts +5 -0
- package/dist/column.d.ts.map +1 -0
- package/dist/column.js +9 -0
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +286 -336
- package/dist/connection.d.ts +5 -142
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +111 -4993
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-live.d.ts +150 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +259 -0
- package/dist/conversation-row.d.ts +40 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +66 -0
- package/dist/conversation.d.ts +72 -51
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +1001 -926
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts +11 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +157 -0
- package/dist/download.d.ts +6 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +42 -0
- package/dist/durability.d.ts +20 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +7 -0
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -8
- package/dist/expectation.d.ts +26 -13
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +72 -19
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +199 -0
- package/dist/file.d.ts +1 -1
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +3 -2
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +21 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +83 -41
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +37 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -5
- package/dist/install.d.ts +60 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +537 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +282 -0
- package/dist/listing.d.ts +52 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +152 -0
- package/dist/live.d.ts +9 -6
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +118 -36
- package/dist/loaded.d.ts +5 -0
- package/dist/loaded.d.ts.map +1 -0
- package/dist/loaded.js +40 -0
- package/dist/locks.d.ts +7 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +32 -0
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +49 -0
- package/dist/media.d.ts +19 -2
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +105 -27
- package/dist/memory.d.ts +18 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +214 -91
- package/dist/messages.d.ts +35 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +109 -39
- package/dist/model.d.ts +2 -10
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -14
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +153 -0
- package/dist/observation.d.ts +28 -13
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +185 -81
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +5 -5
- package/dist/opening.d.ts +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +53 -0
- package/dist/outcome.d.ts +17 -2
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +35 -2
- package/dist/overlay.d.ts +7 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +48 -24
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +4 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +13 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +22 -0
- package/dist/preparation.d.ts +41 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +314 -0
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +231 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +308 -0
- package/dist/query.d.ts +14 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/questions.d.ts +30 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +116 -0
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +480 -0
- package/dist/reach.d.ts +16 -15
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +257 -148
- package/dist/react.d.ts +19 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +9 -6
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +39 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +178 -0
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +38 -8
- package/dist/refresh.d.ts +2 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +31 -3
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +55 -0
- package/dist/replica.d.ts +11 -16
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +66 -32
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +34 -10
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +331 -111
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +220 -0
- package/dist/room-list.d.ts +10 -0
- package/dist/room-list.d.ts.map +1 -0
- package/dist/room-list.js +45 -0
- package/dist/rows.d.ts +16 -22
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -186
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +42 -86
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +332 -228
- package/dist/saves.d.ts +34 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +437 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -1
- package/dist/shared.d.ts +1 -6
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -95
- package/dist/shown.d.ts +35 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +313 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +28 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +103 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +67 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +164 -0
- package/dist/source.d.ts +59 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +93 -0
- package/dist/status.d.ts +0 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -6
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +40 -0
- package/dist/store/readiness.d.ts +11 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +68 -0
- package/dist/store/records.d.ts +224 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +466 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- package/dist/store/rooms-shown.d.ts +8 -0
- package/dist/store/rooms-shown.d.ts.map +1 -0
- package/dist/store/rooms-shown.js +13 -0
- package/dist/store/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +115 -143
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +171 -364
- package/dist/stored.d.ts +72 -35
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +337 -197
- package/dist/surface.d.ts +92 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +251 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +538 -0
- package/dist/tables.d.ts +118 -67
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +199 -278
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +128 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +9 -12
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +72 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +26 -5
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -370
- package/dist/log.d.ts +0 -5
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -156
package/dist/stored.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
|
+
import { instantKey, sql } from '@agentshouse/mdmodel';
|
|
1
2
|
import { composedOf } from './collection.js';
|
|
2
|
-
import {
|
|
3
|
-
import { described, derived } from './rows.js';
|
|
3
|
+
import { described, derived, heldOf } from './rows.js';
|
|
4
4
|
export const REPLICA_TABLES = [
|
|
5
|
-
'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL, fingerprint TEXT NOT NULL, rederiving INTEGER
|
|
6
|
-
'CREATE
|
|
7
|
-
'CREATE TABLE
|
|
5
|
+
'CREATE TABLE replica_keys (key INTEGER PRIMARY KEY AUTOINCREMENT, locator TEXT NOT NULL, transfer TEXT NOT NULL, fingerprint TEXT NOT NULL, rederiving INTEGER)',
|
|
6
|
+
'CREATE INDEX replica_keys_locator ON replica_keys (locator)',
|
|
7
|
+
'CREATE TABLE replica_rooms (locator TEXT PRIMARY KEY, key INTEGER NOT NULL, record TEXT NOT NULL, documents INTEGER NOT NULL DEFAULT 0) WITHOUT ROWID',
|
|
8
|
+
'CREATE INDEX replica_rooms_key ON replica_rooms (key)',
|
|
9
|
+
"CREATE INDEX replica_rooms_model ON replica_rooms (json_extract(record, '$.model.snapshot.identity'), json_extract(record, '$.model.snapshot.version'))",
|
|
10
|
+
'CREATE TABLE replica_models (identity TEXT NOT NULL, version TEXT NOT NULL, schema TEXT NOT NULL, PRIMARY KEY (identity, version)) WITHOUT ROWID',
|
|
11
|
+
"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, binary TEXT, revision TEXT, file_id TEXT, change TEXT, written TEXT, type TEXT, declared TEXT, folder TEXT NOT NULL, fields TEXT, links TEXT, part TEXT, created TEXT, created_instant TEXT, changed_instant TEXT, bytes INTEGER GENERATED ALWAYS AS (CASE WHEN binary IS NULL THEN length(CAST(content AS BLOB)) ELSE binary ->> '$.byteLength' END) STORED, content TEXT)",
|
|
8
12
|
'CREATE UNIQUE INDEX replica_entries_path ON replica_entries (key, layer, path)',
|
|
9
13
|
'CREATE INDEX replica_entries_visible ON replica_entries (key, visible, present, path, binary)',
|
|
10
14
|
'CREATE INDEX replica_entries_folder ON replica_entries (key, folder, path)',
|
|
11
|
-
'CREATE INDEX replica_entries_type ON replica_entries (key, type, visible, present,
|
|
15
|
+
'CREATE INDEX replica_entries_type ON replica_entries (key, type, visible, present, folder)',
|
|
12
16
|
'CREATE INDEX replica_entries_key ON replica_entries (key)',
|
|
13
|
-
|
|
17
|
+
sql.VALUE_TABLE,
|
|
14
18
|
'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
19
|
'CREATE INDEX replica_links_target ON replica_links (key, target, kind, keys)',
|
|
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',
|
|
20
|
+
'CREATE TABLE replica_folders (key INTEGER NOT NULL, layer INTEGER NOT NULL, path TEXT NOT NULL, present INTEGER NOT NULL, part TEXT, PRIMARY KEY (key, layer, path)) WITHOUT ROWID',
|
|
17
21
|
'CREATE TABLE replica_erasing (key INTEGER PRIMARY KEY, locator TEXT NOT NULL)',
|
|
18
22
|
'CREATE TABLE replica_facts (name TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
|
|
19
23
|
'CREATE TABLE replica_presentation (app TEXT PRIMARY KEY, value TEXT NOT NULL) WITHOUT ROWID',
|
|
@@ -26,11 +30,9 @@ const ENTRY_WEIGHT = 256;
|
|
|
26
30
|
const ABSENT = {
|
|
27
31
|
type: null,
|
|
28
32
|
declared: null,
|
|
29
|
-
queried: false,
|
|
30
33
|
folder: '',
|
|
31
34
|
fields: null,
|
|
32
|
-
|
|
33
|
-
links: [],
|
|
35
|
+
links: null,
|
|
34
36
|
};
|
|
35
37
|
function text(value) {
|
|
36
38
|
return typeof value === 'string' ? value : null;
|
|
@@ -51,17 +53,11 @@ function dropDerived(connection, doc) {
|
|
|
51
53
|
connection.run('DELETE FROM replica_values WHERE doc = ?', [doc]);
|
|
52
54
|
connection.run('DELETE FROM replica_links WHERE doc = ?', [doc]);
|
|
53
55
|
}
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
return 'Infinity';
|
|
57
|
-
if (value === -Infinity)
|
|
58
|
-
return '-Infinity';
|
|
59
|
-
return JSON.stringify(value);
|
|
60
|
-
}
|
|
61
|
-
function writeDerived(connection, doc, key, made) {
|
|
56
|
+
function writeDerived(connection, doc, key, path, held, composition) {
|
|
57
|
+
const made = derived(path, held, composition);
|
|
62
58
|
if (made.values.length > 0) {
|
|
63
|
-
const
|
|
64
|
-
connection.run(
|
|
59
|
+
const inserted = sql.insertedValues(doc, made.values);
|
|
60
|
+
connection.run(inserted.sql, inserted.bind);
|
|
65
61
|
}
|
|
66
62
|
if (made.links.length > 0) {
|
|
67
63
|
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(?)', [
|
|
@@ -75,63 +71,52 @@ function entryId(connection, key, layer, path) {
|
|
|
75
71
|
const [row] = connection.rows('SELECT id FROM replica_entries WHERE key = ? AND layer = ? AND path = ?', [key, layer, path]);
|
|
76
72
|
return row === undefined ? null : number(row.id);
|
|
77
73
|
}
|
|
78
|
-
function
|
|
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) {
|
|
74
|
+
function removeEntry(connection, key, layer, path) {
|
|
87
75
|
const id = entryId(connection, key, layer, path);
|
|
88
76
|
if (id === null)
|
|
89
77
|
return;
|
|
90
78
|
dropDerived(connection, id);
|
|
91
|
-
owed.dropped.push(id);
|
|
92
79
|
connection.run('DELETE FROM replica_entries WHERE id = ?', [id]);
|
|
93
80
|
}
|
|
94
|
-
function
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
function insertEntry(connection, key, layer, entry, present, made) {
|
|
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
81
|
+
function insertEntry(connection, key, layer, entry, present, made, part = null) {
|
|
82
|
+
connection.run('INSERT INTO replica_entries (key, layer, path, visible, present, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
99
83
|
key,
|
|
100
84
|
layer,
|
|
101
85
|
entry.path,
|
|
102
86
|
flag(present && layer !== REFRESHED),
|
|
103
87
|
flag(present),
|
|
104
|
-
flag(made.queried),
|
|
105
88
|
entry.binary === null ? entry.content : null,
|
|
106
89
|
entry.binary === null ? null : JSON.stringify(entry.binary),
|
|
107
90
|
entry.revision,
|
|
108
91
|
entry.fileId,
|
|
92
|
+
entry.created,
|
|
93
|
+
instantKey(entry.created),
|
|
109
94
|
entry.change === null ? null : JSON.stringify(entry.change),
|
|
95
|
+
instantKey(entry.change?.time),
|
|
110
96
|
entry.written,
|
|
111
97
|
made.type,
|
|
112
98
|
made.declared,
|
|
113
99
|
made.folder,
|
|
114
100
|
made.fields,
|
|
101
|
+
made.links,
|
|
102
|
+
part,
|
|
115
103
|
]);
|
|
116
104
|
}
|
|
117
105
|
function textOf(entry) {
|
|
118
106
|
return entry.binary === null ? entry.content : null;
|
|
119
107
|
}
|
|
120
|
-
function putEntry(connection, key, layer, entry, present, composition
|
|
121
|
-
removeEntry(connection, key, layer, entry.path
|
|
108
|
+
function putEntry(connection, key, layer, entry, present, composition) {
|
|
109
|
+
removeEntry(connection, key, layer, entry.path);
|
|
122
110
|
const content = textOf(entry);
|
|
123
|
-
|
|
124
|
-
insertEntry(connection, key, layer, entry, present, made);
|
|
111
|
+
insertEntry(connection, key, layer, entry, present, present ? described(entry.path, content, entry.facts) : ABSENT);
|
|
125
112
|
if (!present)
|
|
126
113
|
return;
|
|
127
114
|
const id = entryId(connection, key, layer, entry.path);
|
|
128
|
-
writeDerived(connection, id, key,
|
|
129
|
-
if (searched && content !== null && made.queried)
|
|
130
|
-
owed.made.push({ id, path: entry.path, content });
|
|
115
|
+
writeDerived(connection, id, key, entry.path, heldOf(entry.path, content, entry.facts), composition);
|
|
131
116
|
}
|
|
132
|
-
function placeEntry(connection, key, entry,
|
|
133
|
-
removeEntry(connection, key, CONFIRMED, entry.path
|
|
134
|
-
insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry)));
|
|
117
|
+
function placeEntry(connection, key, entry, part) {
|
|
118
|
+
removeEntry(connection, key, CONFIRMED, entry.path);
|
|
119
|
+
insertEntry(connection, key, CONFIRMED, entry, true, described(entry.path, textOf(entry), entry.facts), part);
|
|
135
120
|
}
|
|
136
121
|
function revealed(connection, key, touched) {
|
|
137
122
|
if (touched.length === 0)
|
|
@@ -140,7 +125,7 @@ function revealed(connection, key, touched) {
|
|
|
140
125
|
}
|
|
141
126
|
function newKey(connection, locator, transfer, fingerprint, built) {
|
|
142
127
|
const owed = built ? null : 0;
|
|
143
|
-
connection.run('INSERT INTO replica_keys (locator, transfer, fingerprint, rederiving
|
|
128
|
+
connection.run('INSERT INTO replica_keys (locator, transfer, fingerprint, rederiving) VALUES (?, ?, ?, ?)', [locator, transfer, fingerprint, owed]);
|
|
144
129
|
const [row] = connection.rows('SELECT last_insert_rowid() AS key');
|
|
145
130
|
return number(row.key);
|
|
146
131
|
}
|
|
@@ -153,36 +138,96 @@ function recomposed(connection, key, fingerprint) {
|
|
|
153
138
|
}
|
|
154
139
|
export function entered(connection, locator, composition, rows) {
|
|
155
140
|
const key = newKey(connection, locator, '', composedOf(composition).fingerprint, true);
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
putEntry(connection, key, CONFIRMED, entry, true, composition, owed, false);
|
|
159
|
-
}
|
|
160
|
-
wordsWritten(connection, owed);
|
|
141
|
+
for (const entry of rows)
|
|
142
|
+
putEntry(connection, key, CONFIRMED, entry, true, composition);
|
|
161
143
|
return key;
|
|
162
144
|
}
|
|
163
145
|
export function stage(connection, request) {
|
|
146
|
+
if (request.key === null) {
|
|
147
|
+
for (const row of connection.rows(STAGED_KEYS, [request.locator])) {
|
|
148
|
+
erasing(connection, number(row.key), request.locator);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
164
151
|
const key = request.key ??
|
|
165
152
|
newKey(connection, request.locator, request.transfer, request.fingerprint, false);
|
|
166
153
|
recomposed(connection, key, request.fingerprint);
|
|
167
|
-
connection
|
|
168
|
-
const owed = words();
|
|
154
|
+
owing(connection, key);
|
|
169
155
|
for (const entry of request.entries)
|
|
170
|
-
placeEntry(connection, key, entry,
|
|
156
|
+
placeEntry(connection, key, entry, request.part);
|
|
171
157
|
for (const path of request.folders) {
|
|
172
|
-
connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, 1)', [key, CONFIRMED, path]);
|
|
158
|
+
connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present, part) VALUES (?, ?, ?, 1, ?)', [key, CONFIRMED, path, request.part]);
|
|
173
159
|
}
|
|
174
|
-
wordsWritten(connection, owed);
|
|
175
160
|
return key;
|
|
176
161
|
}
|
|
162
|
+
function owing(connection, key) {
|
|
163
|
+
connection.run('UPDATE replica_keys SET rederiving = coalesce(rederiving, last) FROM (SELECT coalesce(max(id), 0) AS last FROM replica_entries) WHERE key = ?', [key]);
|
|
164
|
+
}
|
|
165
|
+
const STAGED_KEYS = `SELECT k.key, k.transfer FROM replica_keys k WHERE k.locator = ?
|
|
166
|
+
AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
167
|
+
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
|
|
168
|
+
ORDER BY k.key DESC`;
|
|
169
|
+
const UNPARTED = `EXISTS (SELECT 1 FROM replica_entries e WHERE e.key = k.key AND e.part IS NULL)
|
|
170
|
+
OR EXISTS (SELECT 1 FROM replica_folders f WHERE f.key = k.key AND f.part IS NULL)`;
|
|
171
|
+
function partsOf(connection, key, layer) {
|
|
172
|
+
const scoped = layer === null ? '' : ` AND layer = ${layer}`;
|
|
173
|
+
return new Set(connection
|
|
174
|
+
.rows(`SELECT part FROM replica_entries WHERE key = ?${scoped} AND part IS NOT NULL UNION SELECT part FROM replica_folders WHERE key = ?${scoped} AND part IS NOT NULL`, [key, key])
|
|
175
|
+
.map((row) => text(row.part)));
|
|
176
|
+
}
|
|
177
|
+
function resumable(connection, locator) {
|
|
178
|
+
let kept = null;
|
|
179
|
+
for (const row of connection.rows(STAGED_KEYS, [locator])) {
|
|
180
|
+
const key = number(row.key);
|
|
181
|
+
const parted = kept === null &&
|
|
182
|
+
connection.rows(`SELECT 1 FROM replica_keys k WHERE k.key = ? AND NOT (${UNPARTED})`, [key])
|
|
183
|
+
.length > 0;
|
|
184
|
+
if (parted)
|
|
185
|
+
kept = { key, transfer: text(row.transfer) };
|
|
186
|
+
else
|
|
187
|
+
erasing(connection, key, locator);
|
|
188
|
+
}
|
|
189
|
+
return kept;
|
|
190
|
+
}
|
|
191
|
+
export function resume(connection, request) {
|
|
192
|
+
const wanted = new Set(request.parts);
|
|
193
|
+
const held = resumable(connection, request.locator);
|
|
194
|
+
const staged = held === null ? new Set() : partsOf(connection, held.key, null);
|
|
195
|
+
const dropped = [...staged].filter((part) => !wanted.has(part));
|
|
196
|
+
if (held !== null && dropped.length > 0) {
|
|
197
|
+
const named = JSON.stringify(dropped);
|
|
198
|
+
const rows = `SELECT id FROM replica_entries WHERE key = ? AND part IN (SELECT value FROM json_each(?))`;
|
|
199
|
+
for (const table of ['replica_values', 'replica_links']) {
|
|
200
|
+
connection.run(`DELETE FROM ${table} WHERE doc IN (${rows})`, [held.key, named]);
|
|
201
|
+
}
|
|
202
|
+
connection.run(`DELETE FROM replica_entries WHERE id IN (${rows})`, [held.key, named]);
|
|
203
|
+
connection.run('DELETE FROM replica_folders WHERE key = ? AND part IN (SELECT value FROM json_each(?))', [held.key, named]);
|
|
204
|
+
}
|
|
205
|
+
const kept = [...staged].filter((part) => wanted.has(part));
|
|
206
|
+
const publishedParts = request.published === null
|
|
207
|
+
? new Set()
|
|
208
|
+
: partsOf(connection, request.published, CONFIRMED);
|
|
209
|
+
const copied = request.parts.filter((part) => !staged.has(part) && publishedParts.has(part));
|
|
210
|
+
if (held === null && copied.length === 0) {
|
|
211
|
+
return { key: null, transfer: request.transfer, staged: [] };
|
|
212
|
+
}
|
|
213
|
+
const key = held?.key ?? newKey(connection, request.locator, request.transfer, request.fingerprint, false);
|
|
214
|
+
recomposed(connection, key, request.fingerprint);
|
|
215
|
+
if (copied.length > 0) {
|
|
216
|
+
owing(connection, key);
|
|
217
|
+
const named = JSON.stringify(copied);
|
|
218
|
+
connection.run(`INSERT INTO replica_entries (key, layer, path, visible, present, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part) SELECT ?, ${CONFIRMED}, path, 1, 1, content, binary, revision, file_id, created, created_instant, change, changed_instant, written, type, declared, folder, fields, links, part FROM replica_entries WHERE key = ? AND layer = ${CONFIRMED} AND present = 1 AND part IN (SELECT value FROM json_each(?)) ORDER BY path`, [key, request.published, named]);
|
|
219
|
+
connection.run(`INSERT OR REPLACE INTO replica_folders (key, layer, path, present, part) SELECT ?, ${CONFIRMED}, path, 1, part FROM replica_folders WHERE key = ? AND layer = ${CONFIRMED} AND present = 1 AND part IN (SELECT value FROM json_each(?))`, [key, request.published, named]);
|
|
220
|
+
}
|
|
221
|
+
return { key, transfer: held?.transfer ?? request.transfer, staged: [...kept, ...copied] };
|
|
222
|
+
}
|
|
177
223
|
export function refreshing(connection, request) {
|
|
178
224
|
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]);
|
|
179
225
|
if (held.length === 0)
|
|
180
226
|
return null;
|
|
181
227
|
recomposed(connection, request.key, request.composed.fingerprint);
|
|
182
228
|
const composition = request.composed.composition;
|
|
183
|
-
const owed = words();
|
|
184
229
|
for (const entry of request.entries) {
|
|
185
|
-
putEntry(connection, request.key, REFRESHED, entry, true, composition
|
|
230
|
+
putEntry(connection, request.key, REFRESHED, entry, true, composition);
|
|
186
231
|
}
|
|
187
232
|
for (const path of request.removals) {
|
|
188
233
|
putEntry(connection, request.key, REFRESHED, {
|
|
@@ -191,9 +236,11 @@ export function refreshing(connection, request) {
|
|
|
191
236
|
binary: null,
|
|
192
237
|
revision: null,
|
|
193
238
|
fileId: null,
|
|
239
|
+
created: null,
|
|
194
240
|
change: null,
|
|
195
241
|
written: null,
|
|
196
|
-
|
|
242
|
+
facts: null,
|
|
243
|
+
}, false, composition);
|
|
197
244
|
}
|
|
198
245
|
for (const [list, present] of [
|
|
199
246
|
[request.folders, 1],
|
|
@@ -203,11 +250,9 @@ export function refreshing(connection, request) {
|
|
|
203
250
|
connection.run('INSERT OR REPLACE INTO replica_folders (key, layer, path, present) VALUES (?, ?, ?, ?)', [request.key, REFRESHED, path, present]);
|
|
204
251
|
}
|
|
205
252
|
}
|
|
206
|
-
wordsWritten(connection, owed);
|
|
207
253
|
return null;
|
|
208
254
|
}
|
|
209
255
|
function dropLayer(connection, key, layer) {
|
|
210
|
-
unindexDocuments(connection, heldIds(connection, 'SELECT id FROM replica_entries WHERE key = ? AND layer = ?', [key, layer]));
|
|
211
256
|
for (const table of ['replica_values', 'replica_links']) {
|
|
212
257
|
connection.run(`DELETE FROM ${table} WHERE doc IN (SELECT id FROM replica_entries WHERE key = ? AND layer = ?)`, [key, layer]);
|
|
213
258
|
}
|
|
@@ -226,7 +271,6 @@ function refreshedPaths(connection, key) {
|
|
|
226
271
|
function applyRefresh(connection, key) {
|
|
227
272
|
const touched = refreshedPaths(connection, key);
|
|
228
273
|
const replaced = `SELECT c.id FROM replica_entries c JOIN replica_entries r ON r.key = c.key AND r.layer = ${REFRESHED} AND r.path = c.path WHERE c.key = ? AND c.layer = ${CONFIRMED}`;
|
|
229
|
-
unindexDocuments(connection, heldIds(connection, replaced, [key]));
|
|
230
274
|
for (const table of ['replica_values', 'replica_links']) {
|
|
231
275
|
connection.run(`DELETE FROM ${table} WHERE doc IN (${replaced})`, [key]);
|
|
232
276
|
}
|
|
@@ -252,15 +296,18 @@ function counted(connection, key, roots) {
|
|
|
252
296
|
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)]);
|
|
253
297
|
return number(row.held);
|
|
254
298
|
}
|
|
255
|
-
export function counts(connection, rooms) {
|
|
256
|
-
return rooms.map(({ key, roots }) => counted(connection, key, roots));
|
|
257
|
-
}
|
|
258
299
|
function erasing(connection, key, locator) {
|
|
259
300
|
connection.run('INSERT OR REPLACE INTO replica_erasing (key, locator) VALUES (?, ?)', [
|
|
260
301
|
key,
|
|
261
302
|
locator,
|
|
262
303
|
]);
|
|
263
304
|
}
|
|
305
|
+
function saved(connection, key, entry, at) {
|
|
306
|
+
if (!entry.present || entry.created !== null)
|
|
307
|
+
return entry;
|
|
308
|
+
const [held] = connection.rows(`SELECT created FROM replica_entries WHERE key = ? AND layer = ${OVERLAY} AND path = ? AND present = 1`, [key, entry.path]);
|
|
309
|
+
return { ...entry, created: text(held?.created) ?? at };
|
|
310
|
+
}
|
|
264
311
|
function commitRoom(connection, room) {
|
|
265
312
|
const touched = new Set();
|
|
266
313
|
recomposed(connection, room.key, room.composed.fingerprint);
|
|
@@ -274,63 +321,88 @@ function commitRoom(connection, room) {
|
|
|
274
321
|
for (const path of applyRefresh(connection, room.key))
|
|
275
322
|
touched.add(path);
|
|
276
323
|
}
|
|
277
|
-
const owed = words();
|
|
278
324
|
for (const entry of room.confirmed?.set ?? []) {
|
|
279
|
-
putEntry(connection, room.key, CONFIRMED, entry, true, composition
|
|
325
|
+
putEntry(connection, room.key, CONFIRMED, entry, true, composition);
|
|
280
326
|
touched.add(entry.path);
|
|
281
327
|
}
|
|
282
328
|
for (const path of room.confirmed?.removed ?? []) {
|
|
283
|
-
removeEntry(connection, room.key, CONFIRMED, path
|
|
329
|
+
removeEntry(connection, room.key, CONFIRMED, path);
|
|
284
330
|
touched.add(path);
|
|
285
331
|
}
|
|
332
|
+
const savedAt = new Date().toISOString();
|
|
286
333
|
for (const entry of room.overlay?.set ?? []) {
|
|
287
|
-
putEntry(connection, room.key, OVERLAY, entry, entry.present, composition
|
|
334
|
+
putEntry(connection, room.key, OVERLAY, saved(connection, room.key, entry, savedAt), entry.present, composition);
|
|
288
335
|
touched.add(entry.path);
|
|
289
336
|
}
|
|
290
337
|
for (const path of room.overlay?.cleared ?? []) {
|
|
291
|
-
removeEntry(connection, room.key, OVERLAY, path
|
|
338
|
+
removeEntry(connection, room.key, OVERLAY, path);
|
|
292
339
|
touched.add(path);
|
|
293
340
|
}
|
|
294
341
|
const changed = [...touched];
|
|
295
342
|
revealed(connection, room.key, changed);
|
|
296
343
|
if (room.record !== undefined) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
]);
|
|
344
|
+
const replaced = heldModel(connection, room.locator);
|
|
345
|
+
connection.run(KEEP_MODEL, [room.record, room.record, room.record, room.record]);
|
|
346
|
+
connection.run(`INSERT OR REPLACE INTO replica_rooms (locator, key, record) VALUES (?, ?, ${MODEL_LEFT})`, [room.locator, room.key, room.record, room.record, room.record]);
|
|
347
|
+
letGo(connection, replaced);
|
|
302
348
|
}
|
|
303
|
-
wordsWritten(connection, owed);
|
|
304
349
|
return room.published === undefined ? changed : null;
|
|
305
350
|
}
|
|
351
|
+
const SCHEMA = '$.model.snapshot.schema';
|
|
352
|
+
const KEEP_MODEL = `INSERT OR IGNORE INTO replica_models (identity, version, schema)
|
|
353
|
+
SELECT json_extract(?, '$.model.snapshot.identity'), json_extract(?, '$.model.snapshot.version'),
|
|
354
|
+
json_extract(?, '${SCHEMA}')
|
|
355
|
+
WHERE json_type(?, '${SCHEMA}') = 'text'`;
|
|
356
|
+
const MODEL_LEFT = `CASE WHEN json_type(?, '${SCHEMA}') = 'text' THEN json_set(?, '${SCHEMA}', json('null')) ELSE ? END`;
|
|
357
|
+
const HELD_MODEL = `SELECT json_extract(record, '$.model.snapshot.identity') AS identity,
|
|
358
|
+
json_extract(record, '$.model.snapshot.version') AS version
|
|
359
|
+
FROM replica_rooms WHERE locator = ?`;
|
|
360
|
+
const UNHELD_MODEL = `DELETE FROM replica_models WHERE identity = ? AND version = ? AND NOT EXISTS (
|
|
361
|
+
SELECT 1 FROM replica_rooms r
|
|
362
|
+
WHERE json_extract(r.record, '$.model.snapshot.identity') = replica_models.identity
|
|
363
|
+
AND json_extract(r.record, '$.model.snapshot.version') = replica_models.version
|
|
364
|
+
)`;
|
|
365
|
+
function heldModel(connection, locator) {
|
|
366
|
+
return connection.rows(HELD_MODEL, [locator])[0];
|
|
367
|
+
}
|
|
368
|
+
function letGo(connection, model) {
|
|
369
|
+
if (model !== undefined)
|
|
370
|
+
connection.run(UNHELD_MODEL, [model.identity, model.version]);
|
|
371
|
+
}
|
|
372
|
+
const HELD_RECORDS = `SELECT r.locator, r.key, CASE WHEN m.schema IS NULL THEN r.record
|
|
373
|
+
ELSE json_set(r.record, '${SCHEMA}', m.schema) END AS record
|
|
374
|
+
FROM replica_rooms r
|
|
375
|
+
LEFT JOIN replica_models m
|
|
376
|
+
ON m.identity = json_extract(r.record, '$.model.snapshot.identity')
|
|
377
|
+
AND m.version = json_extract(r.record, '$.model.snapshot.version')`;
|
|
306
378
|
export function commit(connection, rooms) {
|
|
307
|
-
const counted = {};
|
|
308
379
|
const changed = {};
|
|
309
380
|
const indexed = {};
|
|
310
381
|
for (const room of rooms) {
|
|
311
382
|
changed[room.locator] = commitRoom(connection, room);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const [held] = connection.rows('SELECT rederiving, searching FROM replica_keys WHERE key = ?', [
|
|
383
|
+
connection.run('UPDATE replica_rooms SET documents = ? WHERE locator = ? AND key = ?', [
|
|
384
|
+
counted(connection, room.key, room.roots ?? rootsOf(connection, room.locator)),
|
|
385
|
+
room.locator,
|
|
316
386
|
room.key,
|
|
317
387
|
]);
|
|
318
|
-
|
|
319
|
-
|
|
388
|
+
const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [room.key]);
|
|
389
|
+
indexed[room.locator] = held !== undefined && held.rederiving === null;
|
|
320
390
|
}
|
|
321
|
-
return {
|
|
391
|
+
return { changed, indexed };
|
|
322
392
|
}
|
|
323
393
|
function rootsOf(connection, locator) {
|
|
324
394
|
const [row] = connection.rows('SELECT record FROM replica_rooms WHERE locator = ?', [locator]);
|
|
325
395
|
const record = json(row?.record);
|
|
326
|
-
return record?.typedSelection?.roots ?? [];
|
|
396
|
+
return record?.declaration.typedSelection?.roots ?? [];
|
|
327
397
|
}
|
|
328
398
|
export function fenceRooms(connection, locators) {
|
|
329
399
|
for (const locator of locators) {
|
|
330
400
|
for (const row of connection.rows('SELECT key FROM replica_keys WHERE locator = ? UNION SELECT key FROM replica_rooms WHERE locator = ?', [locator, locator])) {
|
|
331
401
|
erasing(connection, number(row.key), locator);
|
|
332
402
|
}
|
|
403
|
+
const replaced = heldModel(connection, locator);
|
|
333
404
|
connection.run('DELETE FROM replica_rooms WHERE locator = ?', [locator]);
|
|
405
|
+
letGo(connection, replaced);
|
|
334
406
|
}
|
|
335
407
|
return null;
|
|
336
408
|
}
|
|
@@ -355,7 +427,6 @@ export function erase(connection, locator, limit) {
|
|
|
355
427
|
return { remaining: false };
|
|
356
428
|
const key = number(held.key);
|
|
357
429
|
const batch = 'SELECT id FROM replica_entries WHERE key = ? ORDER BY id LIMIT ?';
|
|
358
|
-
unindexDocuments(connection, heldIds(connection, batch, [key, limit]));
|
|
359
430
|
for (const table of ['replica_values', 'replica_links']) {
|
|
360
431
|
connection.run(`DELETE FROM ${table} WHERE doc IN (${batch})`, [key, limit]);
|
|
361
432
|
}
|
|
@@ -375,40 +446,31 @@ function cursor(value) {
|
|
|
375
446
|
export function rederive(connection, key, composed, bytes, ms, floor) {
|
|
376
447
|
const started = performance.now();
|
|
377
448
|
recomposed(connection, key, composed.fingerprint);
|
|
378
|
-
const [held] = connection.rows('SELECT rederiving
|
|
379
|
-
key,
|
|
380
|
-
]);
|
|
449
|
+
const [held] = connection.rows('SELECT rederiving FROM replica_keys WHERE key = ?', [key]);
|
|
381
450
|
const deriving = cursor(held?.rederiving);
|
|
382
|
-
|
|
383
|
-
if (deriving === null && searching === null)
|
|
451
|
+
if (deriving === null)
|
|
384
452
|
return { done: true, text: 0 };
|
|
385
453
|
const composition = composed.composition;
|
|
386
|
-
let last =
|
|
454
|
+
let last = deriving;
|
|
387
455
|
let spent = 0;
|
|
388
456
|
let built = 0;
|
|
389
457
|
let read = false;
|
|
390
|
-
const late = () => spent >= floor && performance.now() - started >= ms;
|
|
391
|
-
const owed = [];
|
|
392
458
|
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]);
|
|
459
|
+
const rows = connection.rows('SELECT id, path, type, fields, links, octet_length(content) AS size FROM replica_entries WHERE key = ? AND present = 1 AND id > ? ORDER BY id LIMIT ?', [key, last, DERIVED_ROWS]);
|
|
394
460
|
for (const row of rows) {
|
|
395
461
|
const id = number(row.id);
|
|
396
462
|
const path = text(row.path);
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (owedWords && described(path, content).queried)
|
|
406
|
-
owed.push({ id, path, content });
|
|
407
|
-
else if (owedRows)
|
|
408
|
-
built += content?.length ?? 0;
|
|
463
|
+
const size = row.size === null ? 0 : number(row.size);
|
|
464
|
+
dropDerived(connection, id);
|
|
465
|
+
writeDerived(connection, id, key, path, {
|
|
466
|
+
type: text(row.type),
|
|
467
|
+
fields: json(row.fields),
|
|
468
|
+
links: json(row.links) ?? [],
|
|
469
|
+
}, composition);
|
|
470
|
+
built += size;
|
|
409
471
|
last = id;
|
|
410
|
-
spent += ENTRY_WEIGHT +
|
|
411
|
-
if (spent >= bytes ||
|
|
472
|
+
spent += ENTRY_WEIGHT + size;
|
|
473
|
+
if (spent >= bytes || (spent >= floor && performance.now() - started >= ms))
|
|
412
474
|
break reading;
|
|
413
475
|
}
|
|
414
476
|
if (rows.length < DERIVED_ROWS) {
|
|
@@ -416,23 +478,8 @@ export function rederive(connection, key, composed, bytes, ms, floor) {
|
|
|
416
478
|
break;
|
|
417
479
|
}
|
|
418
480
|
}
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
}
|
|
428
|
-
}
|
|
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),
|
|
433
|
-
key,
|
|
434
|
-
]);
|
|
435
|
-
return { done, text: built };
|
|
481
|
+
connection.run('UPDATE replica_keys SET rederiving = ? WHERE key = ?', [read ? null : last, key]);
|
|
482
|
+
return { done: read, text: built };
|
|
436
483
|
}
|
|
437
484
|
function heldEntry(row) {
|
|
438
485
|
return {
|
|
@@ -441,12 +488,24 @@ function heldEntry(row) {
|
|
|
441
488
|
binary: json(row.binary),
|
|
442
489
|
revision: text(row.revision),
|
|
443
490
|
fileId: text(row.file_id),
|
|
491
|
+
created: text(row.created),
|
|
444
492
|
change: json(row.change),
|
|
445
493
|
written: text(row.written),
|
|
446
494
|
type: text(row.type),
|
|
495
|
+
facts: factsHeld(row),
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function factsHeld(row) {
|
|
499
|
+
const type = text(row.declared);
|
|
500
|
+
if (type === null)
|
|
501
|
+
return null;
|
|
502
|
+
return {
|
|
503
|
+
type,
|
|
504
|
+
fields: json(row.fields) ?? {},
|
|
505
|
+
links: json(row.links) ?? [],
|
|
447
506
|
};
|
|
448
507
|
}
|
|
449
|
-
const ENTRY_COLUMNS = 'e.path, e.content, e.binary, e.revision, e.file_id, e.change, e.written, e.type';
|
|
508
|
+
const ENTRY_COLUMNS = 'e.path, e.content, e.binary, e.revision, e.file_id, e.created, e.change, e.written, e.type, e.declared, e.fields, e.links';
|
|
450
509
|
function unrefreshed(alias) {
|
|
451
510
|
return `NOT EXISTS (SELECT 1 FROM replica_entries r WHERE r.key = ${alias}.key AND r.layer = ${REFRESHED} AND r.path = ${alias}.path)`;
|
|
452
511
|
}
|
|
@@ -457,22 +516,6 @@ function viewed(view, alias) {
|
|
|
457
516
|
return `${alias}.layer = ${CONFIRMED}`;
|
|
458
517
|
return `${alias}.present = 1 AND (${alias}.layer = ${REFRESHED} OR (${alias}.layer = ${CONFIRMED} AND ${unrefreshed(alias)}))`;
|
|
459
518
|
}
|
|
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
|
-
]);
|
|
475
|
-
}
|
|
476
519
|
export function entries(connection, key, view, list) {
|
|
477
520
|
if (list.length === 0)
|
|
478
521
|
return [];
|
|
@@ -491,70 +534,167 @@ export function typed(connection, key, view, list) {
|
|
|
491
534
|
binary: number(row.binary) === 1,
|
|
492
535
|
}));
|
|
493
536
|
}
|
|
494
|
-
export function referrers(connection, key, view, targets
|
|
537
|
+
export function referrers(connection, key, view, targets) {
|
|
495
538
|
if (targets.length === 0)
|
|
496
539
|
return [];
|
|
497
540
|
return connection
|
|
498
|
-
.rows(`SELECT DISTINCT e.path, l.target
|
|
499
|
-
.map((row) => ({
|
|
500
|
-
path: text(row.path),
|
|
501
|
-
target: text(row.target),
|
|
502
|
-
kind: text(row.kind),
|
|
503
|
-
}));
|
|
504
|
-
}
|
|
505
|
-
export function files(connection, key, view, after, limit) {
|
|
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) }));
|
|
541
|
+
.rows(`SELECT DISTINCT e.path, l.target FROM replica_links l JOIN replica_entries e ON e.id = l.doc WHERE l.key = ? AND l.target IN (SELECT value FROM json_each(?)) AND ${viewed(view, 'e')}`, [key, paths(targets)])
|
|
542
|
+
.map((row) => ({ path: text(row.path), target: text(row.target) }));
|
|
507
543
|
}
|
|
508
|
-
export function folders(connection, key,
|
|
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(
|
|
544
|
+
export function folders(connection, key, list) {
|
|
545
|
+
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('visible', 'e')} AND e.path >= ? AND e.path < ? LIMIT 1`, [key, path, key, `${path}/`, `${path}0`]).length > 0);
|
|
510
546
|
}
|
|
511
547
|
export function typedWithin(connection, key, view, roots, within) {
|
|
512
548
|
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);
|
|
513
549
|
}
|
|
550
|
+
const ORPHANS = `SELECT k.key FROM replica_keys k
|
|
551
|
+
WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
552
|
+
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
|
|
553
|
+
AND (${UNPARTED} OR NOT EXISTS (
|
|
554
|
+
SELECT 1 FROM replica_entries e WHERE e.key = k.key
|
|
555
|
+
UNION ALL SELECT 1 FROM replica_folders f WHERE f.key = k.key
|
|
556
|
+
))`;
|
|
557
|
+
const STAGED = `SELECT DISTINCT k.locator FROM replica_keys k
|
|
558
|
+
WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
559
|
+
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)
|
|
560
|
+
AND k.key NOT IN (${ORPHANS})`;
|
|
514
561
|
export function opened(connection) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
562
|
+
return {
|
|
563
|
+
orphans: connection.rows(ORPHANS).map((row) => number(row.key)),
|
|
564
|
+
erasing: connection.rows('SELECT 1 FROM replica_erasing LIMIT 1').length > 0,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
const ROOM_ROWS = `SELECT h.locator, h.key, h.record, r.documents, k.rederiving IS NULL AS indexed
|
|
568
|
+
FROM (${HELD_RECORDS}) h
|
|
569
|
+
JOIN replica_rooms r ON r.locator = h.locator
|
|
570
|
+
LEFT JOIN replica_keys k ON k.key = h.key`;
|
|
571
|
+
function roomRow(row) {
|
|
572
|
+
return {
|
|
573
|
+
locator: text(row.locator),
|
|
574
|
+
key: number(row.key),
|
|
575
|
+
record: text(row.record),
|
|
576
|
+
indexed: number(row.indexed) === 1,
|
|
577
|
+
documents: number(row.documents),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
export function heldRooms(connection, locators) {
|
|
581
|
+
return (locators === null
|
|
582
|
+
? connection.rows(`${ROOM_ROWS} ORDER BY h.locator`)
|
|
583
|
+
: connection.rows(`${ROOM_ROWS} WHERE h.locator IN (SELECT value FROM json_each(?)) ORDER BY h.locator`, [JSON.stringify(locators)])).map(roomRow);
|
|
584
|
+
}
|
|
585
|
+
export function heldRoom(connection, locator) {
|
|
586
|
+
return heldRooms(connection, [locator])[0] ?? null;
|
|
587
|
+
}
|
|
588
|
+
export function overlaid(connection, key) {
|
|
589
|
+
return connection
|
|
590
|
+
.rows(`SELECT path FROM replica_entries WHERE key = ? AND layer = ${OVERLAY}`, [key])
|
|
591
|
+
.map((row) => text(row.path));
|
|
592
|
+
}
|
|
593
|
+
const COVER_ROWS = `SELECT r.locator, r.key, r.documents, json_extract(r.record, '$.transfer') AS transfer,
|
|
594
|
+
json_extract(r.record, '$.roomVersion') AS room_version,
|
|
595
|
+
coalesce(json_extract(r.record, '$.inventory.bytes'), 0) AS bytes,
|
|
596
|
+
json_extract(r.record, '$.declaration.replica.offline') AS offline,
|
|
597
|
+
k.rederiving IS NULL AS indexed
|
|
598
|
+
FROM replica_rooms r LEFT JOIN replica_keys k ON k.key = r.key`;
|
|
599
|
+
const COVER_MARK = `UPDATE cover_clock SET at = at + 1;
|
|
600
|
+
INSERT OR REPLACE INTO cover_changes (locator, at) SELECT`;
|
|
601
|
+
const COVER_LOG = [
|
|
602
|
+
'CREATE TEMP TABLE cover_clock (token TEXT NOT NULL, at INTEGER NOT NULL)',
|
|
603
|
+
'CREATE TEMP TABLE cover_changes (locator TEXT PRIMARY KEY, at INTEGER NOT NULL) WITHOUT ROWID',
|
|
604
|
+
'CREATE INDEX temp.cover_changes_at ON cover_changes (at)',
|
|
605
|
+
'INSERT INTO temp.cover_clock (token, at) VALUES (lower(hex(randomblob(8))), 0)',
|
|
606
|
+
`CREATE TEMP TRIGGER cover_room_added AFTER INSERT ON replica_rooms BEGIN
|
|
607
|
+
${COVER_MARK} NEW.locator, at FROM cover_clock; END`,
|
|
608
|
+
`CREATE TEMP TRIGGER cover_room_changed AFTER UPDATE ON replica_rooms BEGIN
|
|
609
|
+
${COVER_MARK} OLD.locator, at FROM cover_clock;
|
|
610
|
+
INSERT OR REPLACE INTO cover_changes (locator, at) SELECT NEW.locator, at FROM cover_clock; END`,
|
|
611
|
+
`CREATE TEMP TRIGGER cover_room_removed AFTER DELETE ON replica_rooms BEGIN
|
|
612
|
+
${COVER_MARK} OLD.locator, at FROM cover_clock; END`,
|
|
613
|
+
`CREATE TEMP TRIGGER cover_key_changed AFTER UPDATE OF rederiving ON replica_keys BEGIN
|
|
614
|
+
${COVER_MARK} r.locator, c.at FROM replica_rooms r, cover_clock c WHERE r.key = NEW.key; END`,
|
|
615
|
+
`CREATE TEMP TRIGGER cover_key_removed AFTER DELETE ON replica_keys BEGIN
|
|
616
|
+
${COVER_MARK} r.locator, c.at FROM replica_rooms r, cover_clock c WHERE r.key = OLD.key; END`,
|
|
617
|
+
];
|
|
618
|
+
function coveredRow(row) {
|
|
619
|
+
return {
|
|
620
|
+
locator: text(row.locator),
|
|
621
|
+
key: number(row.key),
|
|
622
|
+
transfer: text(row.transfer),
|
|
623
|
+
roomVersion: text(row.room_version),
|
|
624
|
+
indexed: number(row.indexed) === 1,
|
|
625
|
+
documents: number(row.documents),
|
|
626
|
+
bytes: number(row.bytes),
|
|
627
|
+
offline: text(row.offline),
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function coverLog(connection) {
|
|
631
|
+
const logged = connection.rows("SELECT 1 FROM sqlite_temp_schema WHERE type = 'table' AND name = 'cover_clock'");
|
|
632
|
+
if (logged.length === 0)
|
|
633
|
+
return null;
|
|
634
|
+
const [clock] = connection.rows('SELECT token, at FROM temp.cover_clock');
|
|
635
|
+
return { token: text(clock.token), at: number(clock.at) };
|
|
636
|
+
}
|
|
637
|
+
export function covering(connection, since) {
|
|
638
|
+
let log = coverLog(connection);
|
|
639
|
+
if (log === null) {
|
|
640
|
+
for (const statement of COVER_LOG)
|
|
641
|
+
connection.run(statement);
|
|
642
|
+
log = coverLog(connection);
|
|
643
|
+
}
|
|
644
|
+
if (since === null || since.token !== log.token) {
|
|
645
|
+
return {
|
|
646
|
+
log,
|
|
647
|
+
whole: true,
|
|
648
|
+
rooms: connection.rows(`${COVER_ROWS} ORDER BY r.locator`).map(coveredRow),
|
|
649
|
+
removed: [],
|
|
650
|
+
};
|
|
518
651
|
}
|
|
519
652
|
return {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
.rows(
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
locator: text(row.locator),
|
|
530
|
-
transfer: text(row.transfer),
|
|
531
|
-
rederiving: cursor(row.rederiving),
|
|
532
|
-
searching: cursor(row.searching),
|
|
533
|
-
})),
|
|
534
|
-
facts,
|
|
535
|
-
presentation: connection.rows('SELECT app, value FROM replica_presentation').map((row) => ({
|
|
536
|
-
app: text(row.app),
|
|
537
|
-
value: text(row.value),
|
|
538
|
-
})),
|
|
539
|
-
overlaid: connection
|
|
540
|
-
.rows(`SELECT key, path FROM replica_entries WHERE layer = ${OVERLAY}`)
|
|
541
|
-
.map((row) => ({ key: number(row.key), path: text(row.path) })),
|
|
542
|
-
orphans: connection
|
|
543
|
-
.rows(`SELECT k.key FROM replica_keys k
|
|
544
|
-
WHERE NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.key = k.key)
|
|
545
|
-
AND NOT EXISTS (SELECT 1 FROM replica_erasing e WHERE e.key = k.key)`)
|
|
546
|
-
.map((row) => number(row.key)),
|
|
547
|
-
erasing: connection.rows('SELECT key, locator FROM replica_erasing').map((row) => ({
|
|
548
|
-
key: number(row.key),
|
|
549
|
-
locator: text(row.locator),
|
|
550
|
-
})),
|
|
653
|
+
log,
|
|
654
|
+
whole: false,
|
|
655
|
+
rooms: connection
|
|
656
|
+
.rows(`${COVER_ROWS} WHERE r.locator IN (SELECT locator FROM temp.cover_changes WHERE at > ?) ORDER BY r.locator`, [since.at])
|
|
657
|
+
.map(coveredRow),
|
|
658
|
+
removed: connection
|
|
659
|
+
.rows(`SELECT c.locator FROM temp.cover_changes c WHERE c.at > ?
|
|
660
|
+
AND NOT EXISTS (SELECT 1 FROM replica_rooms r WHERE r.locator = c.locator)`, [since.at])
|
|
661
|
+
.map((row) => text(row.locator)),
|
|
551
662
|
};
|
|
552
663
|
}
|
|
553
|
-
export function
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
664
|
+
export function offlineMode(connection, locator) {
|
|
665
|
+
const [held] = connection.rows("SELECT json_extract(record, '$.declaration.replica.offline') AS offline FROM replica_rooms WHERE locator = ?", [locator]);
|
|
666
|
+
return text(held?.offline);
|
|
667
|
+
}
|
|
668
|
+
export function stagedLocators(connection) {
|
|
669
|
+
return connection.rows(STAGED).map((row) => text(row.locator));
|
|
670
|
+
}
|
|
671
|
+
export function heldLocators(connection) {
|
|
672
|
+
return connection
|
|
673
|
+
.rows(`SELECT locator FROM replica_rooms UNION ${STAGED}`)
|
|
674
|
+
.map((row) => text(row.locator));
|
|
675
|
+
}
|
|
676
|
+
export function demoted(connection) {
|
|
677
|
+
connection.run(`UPDATE replica_rooms SET record = json_set(record, '$.model.status', 'cached')
|
|
678
|
+
WHERE json_extract(record, '$.model.status') = 'current'`);
|
|
679
|
+
return null;
|
|
680
|
+
}
|
|
681
|
+
export function facts(connection, names) {
|
|
682
|
+
const held = {};
|
|
683
|
+
for (const row of names === null
|
|
684
|
+
? connection.rows('SELECT name, value FROM replica_facts')
|
|
685
|
+
: connection.rows('SELECT name, value FROM replica_facts WHERE name IN (SELECT value FROM json_each(?))', [JSON.stringify(names)])) {
|
|
686
|
+
held[text(row.name)] = text(row.value);
|
|
558
687
|
}
|
|
559
|
-
|
|
688
|
+
return held;
|
|
689
|
+
}
|
|
690
|
+
export function presentation(connection, app) {
|
|
691
|
+
const [held] = connection.rows('SELECT value FROM replica_presentation WHERE app = ?', [app]);
|
|
692
|
+
return text(held?.value);
|
|
693
|
+
}
|
|
694
|
+
export function tablesOf(statements) {
|
|
695
|
+
return statements.flatMap((statement) => /^CREATE TABLE (\w+)/u.exec(statement)?.[1] ?? []);
|
|
696
|
+
}
|
|
697
|
+
export function ended(connection) {
|
|
698
|
+
for (const table of tablesOf(REPLICA_TABLES))
|
|
699
|
+
connection.run(`DELETE FROM ${table}`);
|
|
560
700
|
}
|