@volter/supercode-orchestrator 0.4.93 → 0.4.95
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/board/rows.mjs +1 -1
- package/board/ztrack.mjs +40 -7
- package/package.json +1 -1
package/board/rows.mjs
CHANGED
|
@@ -58,7 +58,7 @@ export class RuntimeRows {
|
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
60
|
write(changes) {
|
|
61
|
-
this.db.exec(
|
|
61
|
+
this.db.exec(`CREATE TABLE IF NOT EXISTS runtime_schema(name TEXT PRIMARY KEY,sql TEXT NOT NULL); CREATE TABLE IF NOT EXISTS runtime_rows(name TEXT,key TEXT,value TEXT,revision INTEGER NOT NULL,PRIMARY KEY(name,key)); CREATE INDEX IF NOT EXISTS runtime_rows_revision ON runtime_rows(revision); CREATE INDEX IF NOT EXISTS runtime_rows_task ON runtime_rows(name,json_extract(value,'$.task_id'),json_extract(value,'$.id')); CREATE INDEX IF NOT EXISTS runtime_rows_id ON runtime_rows(name,json_extract(value,'$.id'))`);
|
|
62
62
|
const revision=Number(this.db.prepare("SELECT value FROM state WHERE key='rows_revision'").get()?.value??0)+1;
|
|
63
63
|
const statement=this.db.prepare('INSERT INTO runtime_rows VALUES(?,?,?,?) ON CONFLICT(name,key) DO UPDATE SET value=excluded.value,revision=excluded.revision');
|
|
64
64
|
for(const {name,key,value,source} of changes) {
|
package/board/ztrack.mjs
CHANGED
|
@@ -36,9 +36,8 @@ class ZtrackBoard {
|
|
|
36
36
|
this.archive = `${this.path}.archive.md`;
|
|
37
37
|
this.seed = seed;
|
|
38
38
|
this.root = root; this.slug = slug; this.init = init; this.compatPath = compatPath;
|
|
39
|
-
this.
|
|
40
|
-
this.
|
|
41
|
-
this.runtime = new DatabaseSync(`${marker}.runtime.db`);
|
|
39
|
+
this.runtimePath = `${marker}.runtime.db`;
|
|
40
|
+
this.runtime = new DatabaseSync(this.runtimePath);
|
|
42
41
|
this.readOnly(()=>{ if (!this.runtime.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='state'").get()) this.runtime.exec('CREATE TABLE IF NOT EXISTS state (key TEXT PRIMARY KEY, value TEXT NOT NULL)'); });
|
|
43
42
|
this.errors = new Map(); this.preserve = new Map();
|
|
44
43
|
this.mem = null; this.active = false; this.rows = new RuntimeRows(this.runtime);
|
|
@@ -53,6 +52,8 @@ class ZtrackBoard {
|
|
|
53
52
|
if (seed) this.tx(() => {});
|
|
54
53
|
else this.readOnly(()=>{ this.load(); this.loadedStamp = this.stamp(); });
|
|
55
54
|
}
|
|
55
|
+
get codec() { return this._codec ??= require('@volter/ztrack/board-document'); }
|
|
56
|
+
get preset() { return this._preset ??= require('@volter/ztrack/kanban-preset'); }
|
|
56
57
|
get(key, fallback = null) { return this.computing && this.pendingValues.has(key) ? this.pendingValues.get(key) : json(this.runtime.prepare('SELECT value FROM state WHERE key = ?').get(key)?.value, fallback); }
|
|
57
58
|
put(key, value) { if (this.computing) { this.pendingValues.set(key, value); return; } this.runtime.prepare('INSERT INTO state VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value').run(key, JSON.stringify(value)); }
|
|
58
59
|
stamp() {
|
|
@@ -107,6 +108,7 @@ class ZtrackBoard {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
transaction(fn) {
|
|
111
|
+
if (this.cachedReader) this.invalidate();
|
|
110
112
|
for (;;) {
|
|
111
113
|
try { this.runtime.exec('BEGIN'); }
|
|
112
114
|
catch (error) { if (!/busy|locked/i.test(error.message)) throw error; continue; }
|
|
@@ -131,7 +133,7 @@ class ZtrackBoard {
|
|
|
131
133
|
});
|
|
132
134
|
const documents = cardDirty ? this.documents() : {open:this.text, archived:this.archiveText};
|
|
133
135
|
const files = [{ path: this.path, before: hash(this.text), base: this.text, text: documents.open }, { path: this.archive, before: hash(this.archiveText), base: this.archiveText, text: documents.archived }];
|
|
134
|
-
const snapshot = this.snapshot();
|
|
136
|
+
const snapshot = this.snapshot(documents);
|
|
135
137
|
this.computing = false;
|
|
136
138
|
this.runtime.exec('BEGIN');
|
|
137
139
|
this.recover();
|
|
@@ -174,7 +176,7 @@ class ZtrackBoard {
|
|
|
174
176
|
} finally { this.active = false; this.computing = false; this.pendingValues = null; }
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
|
-
invalidate() { this.mem?.close(); this.mem = null; this.rows = new RuntimeRows(this.runtime); this.loadedStamp = null; }
|
|
179
|
+
invalidate() { this.mem?.close(); this.mem = null; this.rows = new RuntimeRows(this.runtime); this.loadedStamp = null; this.cachedReader=false; }
|
|
178
180
|
seedIntoMemory() {
|
|
179
181
|
const source = this.seed;
|
|
180
182
|
for (const table of this.mem.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'").all()) this.mem.exec(`DELETE FROM ${quote(table.name)}`);
|
|
@@ -216,8 +218,39 @@ class ZtrackBoard {
|
|
|
216
218
|
if (!keys.length) return;
|
|
217
219
|
this.mem.prepare(`INSERT OR REPLACE INTO ${quote(table)} (${keys.map(quote).join(',')}) VALUES (${keys.map(() => '?').join(',')})`).run(...keys.map((k) => row[k] ?? null));
|
|
218
220
|
}
|
|
221
|
+
openCachedReader(saved) {
|
|
222
|
+
if (!this.cachedReader) {
|
|
223
|
+
this.mem?.close(); this.mem=new DatabaseSync(':memory:'); this.init(this.mem,{fresh:true});
|
|
224
|
+
for(const row of this.runtime.prepare('SELECT name,sql FROM runtime_schema').all())this.mem.exec(row.sql.replace('CREATE TABLE ','CREATE TABLE IF NOT EXISTS '));
|
|
225
|
+
this.mem.prepare('ATTACH DATABASE ? AS source').run(this.runtimePath);
|
|
226
|
+
const tables=this.mem.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").all();
|
|
227
|
+
for(const {name} of tables) {
|
|
228
|
+
if(name==='task_links') {
|
|
229
|
+
this.mem.exec(`CREATE TEMP VIEW task_links AS SELECT json_extract(b.value,'$.id') parent_id,r.key child_id FROM source.runtime_rows r,json_each(json_extract(r.value,'$.blocked_by')) b WHERE r.name='@card_cache' AND json_extract(b.value,'$.kind')='card'`);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const columns=this.mem.prepare(`PRAGMA main.table_info(${quote(name)})`).all();
|
|
233
|
+
const recordName=name==='tasks'?'@task_cache':name==='supercode_cards'?'@card_cache':name;
|
|
234
|
+
const fields=columns.map(({name:column})=>name==='tasks'&&column==='status'?`COALESCE(json_extract(c.value,'$.stage'),json_extract(r.value,'$.status')) AS status`:`json_extract(r.value,'$.${column}') AS ${quote(column)}`);
|
|
235
|
+
const join=name==='tasks'?" LEFT JOIN source.runtime_rows c ON c.name='@card_cache' AND c.key=r.key":'';
|
|
236
|
+
this.mem.exec(`CREATE TEMP VIEW ${quote(name)} AS SELECT ${fields.join(',')} FROM source.runtime_rows r${join} WHERE r.name='${recordName}' AND r.value IS NOT NULL`);
|
|
237
|
+
}
|
|
238
|
+
this.mem.exec('PRAGMA query_only=ON');
|
|
239
|
+
this.cachedReader=true;
|
|
240
|
+
}
|
|
241
|
+
this.saved=saved; this.errors.clear();
|
|
242
|
+
for(const [id,reason] of saved.errors??[])this.errors.set(id,reason);
|
|
243
|
+
this.mem.boardErrors=this.errors;
|
|
244
|
+
}
|
|
219
245
|
load({ observe = false } = {}) {
|
|
220
246
|
const text = read(this.path), archiveText = read(this.archive);
|
|
247
|
+
const saved=this.get('snapshot', {});
|
|
248
|
+
if (!observe && saved.documentHashes?.open===hash(text) && saved.documentHashes?.archived===hash(archiveText) && this.rows.normalized()) {
|
|
249
|
+
this.openCachedReader(saved);
|
|
250
|
+
this.text=text;this.archiveText=archiveText;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (this.cachedReader)this.invalidate();
|
|
221
254
|
const fresh = !this.mem;
|
|
222
255
|
const changed = fresh || text !== this.text || archiveText !== this.archiveText;
|
|
223
256
|
this.text = text; this.archiveText = archiveText;
|
|
@@ -394,8 +427,8 @@ class ZtrackBoard {
|
|
|
394
427
|
}
|
|
395
428
|
return { open: this.codec.renderBoardDocument(this.text, open, this.path, [...(this.preserve.get(this.path)??[])]), archived: this.codec.renderBoardDocument(this.archiveText, archived, this.archive, [...(this.preserve.get(this.archive)??[])]) };
|
|
396
429
|
}
|
|
397
|
-
snapshot() {
|
|
398
|
-
return { fingerprints: this.fingerprints(), taskIds: Object.fromEntries([...this.taskKeys].map(([id, key]) => [key, id])) };
|
|
430
|
+
snapshot(documents) {
|
|
431
|
+
return { documentHashes:{open:hash(documents.open),archived:hash(documents.archived)}, errors:[...this.errors], fingerprints: this.fingerprints(), taskIds: Object.fromEntries([...this.taskKeys].map(([id, key]) => [key, id])) };
|
|
399
432
|
}
|
|
400
433
|
|
|
401
434
|
lift() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.95",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|