@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 CHANGED
@@ -58,7 +58,7 @@ export class RuntimeRows {
58
58
  });
59
59
  }
60
60
  write(changes) {
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)');
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.codec = require('@volter/ztrack/board-document');
40
- this.preset = require('@volter/ztrack/kanban-preset');
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.93",
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": {