scribe-cms 0.0.5 → 0.0.7

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.
@@ -333,7 +333,7 @@ function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale) {
333
333
  out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale);
334
334
  return out;
335
335
  }
336
- var SCHEMA_VERSION = 3;
336
+ var SCHEMA_VERSION = 4;
337
337
  var MIGRATIONS = [
338
338
  `CREATE TABLE IF NOT EXISTS meta (
339
339
  key TEXT PRIMARY KEY,
@@ -353,20 +353,6 @@ var MIGRATIONS = [
353
353
  )`,
354
354
  `CREATE INDEX IF NOT EXISTS idx_translations_type_locale
355
355
  ON translations(content_type, locale)`,
356
- `CREATE TABLE IF NOT EXISTS revisions (
357
- id INTEGER PRIMARY KEY AUTOINCREMENT,
358
- content_type TEXT NOT NULL,
359
- en_slug TEXT NOT NULL,
360
- locale TEXT,
361
- revision_kind TEXT NOT NULL,
362
- en_hash TEXT NOT NULL,
363
- body_hash TEXT NOT NULL,
364
- created_at TEXT NOT NULL,
365
- model TEXT,
366
- body_preview TEXT
367
- )`,
368
- `CREATE INDEX IF NOT EXISTS idx_revisions_lookup
369
- ON revisions(content_type, en_slug, locale, created_at DESC)`,
370
356
  `CREATE TABLE IF NOT EXISTS slug_aliases (
371
357
  content_type TEXT NOT NULL,
372
358
  canonical_en_slug TEXT NOT NULL,
@@ -383,7 +369,19 @@ var MIGRATIONS = [
383
369
  locale_slug TEXT NOT NULL,
384
370
  captured_at TEXT NOT NULL,
385
371
  PRIMARY KEY (content_type, alias_en_slug, locale)
386
- )`
372
+ )`,
373
+ `CREATE TABLE IF NOT EXISTS en_snapshots (
374
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
375
+ content_type TEXT NOT NULL,
376
+ en_slug TEXT NOT NULL,
377
+ en_hash TEXT NOT NULL,
378
+ frontmatter_json TEXT NOT NULL,
379
+ body TEXT NOT NULL,
380
+ created_at TEXT NOT NULL,
381
+ UNIQUE (content_type, en_slug, en_hash)
382
+ )`,
383
+ `CREATE INDEX IF NOT EXISTS idx_en_snapshots_lookup
384
+ ON en_snapshots(content_type, en_slug, created_at DESC)`
387
385
  ];
388
386
  function resolveStorePath(config) {
389
387
  return config.storePath;
@@ -405,12 +403,19 @@ function addColumnIfMissing(db, table, column, ddlType) {
405
403
  db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${ddlType}`);
406
404
  }
407
405
  }
406
+ function readSchemaVersion(db) {
407
+ const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
408
+ return row ? Number.parseInt(row.value, 10) || 0 : 0;
409
+ }
408
410
  function migrate(db) {
411
+ const previousVersion = readSchemaVersion(db);
409
412
  for (const sql of MIGRATIONS) {
410
413
  db.exec(sql);
411
414
  }
412
- addColumnIfMissing(db, "revisions", "frontmatter_json", "TEXT");
413
- addColumnIfMissing(db, "revisions", "body", "TEXT");
415
+ if (previousVersion < 4) {
416
+ db.exec(`DROP TABLE IF EXISTS revisions`);
417
+ }
418
+ addColumnIfMissing(db, "translations", "snapshot_id", "INTEGER");
414
419
  db.prepare(
415
420
  `INSERT INTO meta(key, value) VALUES('schema_version', ?)
416
421
  ON CONFLICT(key) DO UPDATE SET value = excluded.value`
@@ -418,6 +423,9 @@ function migrate(db) {
418
423
  }
419
424
 
420
425
  // src/storage/translations.ts
426
+ function getEnSnapshot(db, snapshotId) {
427
+ return db.prepare(`SELECT * FROM en_snapshots WHERE id = ?`).get(snapshotId);
428
+ }
421
429
  function getTranslation(db, contentType, enSlug, locale) {
422
430
  return db.prepare(
423
431
  `SELECT * FROM translations WHERE content_type = ? AND en_slug = ? AND locale = ?`
@@ -426,20 +434,6 @@ function getTranslation(db, contentType, enSlug, locale) {
426
434
  function listTranslationsForLocale(db, contentType, locale) {
427
435
  return db.prepare(`SELECT * FROM translations WHERE content_type = ? AND locale = ? ORDER BY en_slug`).all(contentType, locale);
428
436
  }
429
- function listRevisions(db, contentType, enSlug, locale) {
430
- if (locale) {
431
- return db.prepare(
432
- `SELECT * FROM revisions
433
- WHERE content_type = ? AND en_slug = ? AND locale = ?
434
- ORDER BY created_at DESC, id DESC`
435
- ).all(contentType, enSlug, locale);
436
- }
437
- return db.prepare(
438
- `SELECT * FROM revisions
439
- WHERE content_type = ? AND en_slug = ?
440
- ORDER BY created_at DESC, id DESC`
441
- ).all(contentType, enSlug);
442
- }
443
437
 
444
438
  // src/loader/normalize-en.ts
445
439
  function normalizeEnFrontmatter(data) {
@@ -672,38 +666,31 @@ function renderFrontmatterTable(frontmatter, schema) {
672
666
  <tbody>${rows || `<tr><td colspan="2" class="dim">\u2014</td></tr>`}</tbody>
673
667
  </table>`;
674
668
  }
675
- function renderRevisionSnapshot(revision) {
676
- if (revision.frontmatter_json && revision.body) {
677
- let frontmatter = {};
678
- try {
679
- frontmatter = JSON.parse(revision.frontmatter_json);
680
- } catch {
681
- frontmatter = {};
682
- }
683
- const fmRows = flattenFrontmatter(frontmatter).map(
684
- (row) => `<tr><td class="k">${escapeHtml(row.key)}</td><td class="v">${escapeHtml(row.value)}</td></tr>`
685
- ).join("");
686
- return `<table class="kv"><tbody>${fmRows}</tbody></table>
687
- <pre class="code">${escapeHtml(revision.body)}</pre>`;
669
+ function renderEnSnapshotPreview(snapshot) {
670
+ let frontmatter = {};
671
+ try {
672
+ frontmatter = JSON.parse(snapshot.frontmatter_json);
673
+ } catch {
674
+ frontmatter = {};
688
675
  }
689
- return `<p class="dim">${escapeHtml(revision.body_preview ?? "(no snapshot)")}</p>`;
676
+ const fmRows = flattenFrontmatter(frontmatter).map(
677
+ (row) => `<tr><td class="k">${escapeHtml(row.key)}</td><td class="v">${escapeHtml(row.value)}</td></tr>`
678
+ ).join("");
679
+ return `<table class="kv"><tbody>${fmRows}</tbody></table>
680
+ <pre class="code">${escapeHtml(snapshot.body)}</pre>`;
690
681
  }
691
- function renderRevisionTimeline(revisions) {
692
- if (revisions.length === 0) {
693
- return `<p class="dim">No history.</p>`;
682
+ function renderTranslationSnapshotPanel(snapshot, currentEnHash) {
683
+ if (!snapshot) {
684
+ return `<p class="dim">No EN snapshot linked to this translation.</p>`;
694
685
  }
695
- const items = revisions.map(
696
- (row) => `<tr>
697
- <td class="dim">${escapeHtml(row.created_at.slice(0, 19))}</td>
698
- <td>${escapeHtml(row.revision_kind)}</td>
699
- <td class="dim">${row.model ? escapeHtml(row.model) : "\u2014"}</td>
700
- <td class="dim mono">${escapeHtml(row.en_hash.slice(0, 8))}</td>
701
- <td><details><summary>view</summary>${renderRevisionSnapshot(row)}</details></td>
702
- </tr>`
703
- ).join("");
704
- return `<table class="data"><thead><tr>
705
- <th>When</th><th>Kind</th><th>Model</th><th>Hash</th><th></th>
706
- </tr></thead><tbody>${items}</tbody></table>`;
686
+ const staleNote = currentEnHash && currentEnHash !== snapshot.en_hash ? `<p class="dim">Current EN hash differs from snapshot (${escapeHtml(currentEnHash.slice(0, 12))} vs ${escapeHtml(snapshot.en_hash.slice(0, 12))}).</p>` : "";
687
+ return `<dl class="meta">
688
+ <dt>snapshot</dt><dd>#${snapshot.id}</dd>
689
+ <dt>captured</dt><dd>${escapeHtml(snapshot.created_at.slice(0, 19))}</dd>
690
+ <dt>en_hash</dt><dd class="mono">${escapeHtml(snapshot.en_hash.slice(0, 12))}</dd>
691
+ </dl>
692
+ ${staleNote}
693
+ <details><summary>EN source at translation time</summary>${renderEnSnapshotPreview(snapshot)}</details>`;
707
694
  }
708
695
  function renderLayout(title, body, project, options = {}) {
709
696
  const typeLinks = project.listTypes().map((type) => {
@@ -1014,10 +1001,10 @@ async function startStudio(project, options = {}) {
1014
1001
  <dt>slug</dt><dd>${escapeHtml(translation.slug)}</dd>
1015
1002
  <dt>en_hash</dt><dd>${escapeHtml(currentEnHash?.slice(0, 12) ?? "\u2014")} / ${escapeHtml(storedEnHash?.slice(0, 12) ?? "\u2014")}</dd>
1016
1003
  </dl>`;
1017
- const revisions = listRevisions(db, typeId, enSlug, locale);
1004
+ const snapshot = translation.snapshot_id != null ? getEnSnapshot(db, translation.snapshot_id) : void 0;
1018
1005
  historyPanel = `<div class="section">
1019
- <div class="section-head">History</div>
1020
- <div class="section-body">${renderRevisionTimeline(revisions)}</div>
1006
+ <div class="section-head">EN snapshot</div>
1007
+ <div class="section-body">${renderTranslationSnapshotPanel(snapshot, currentEnHash)}</div>
1021
1008
  </div>`;
1022
1009
  } else {
1023
1010
  contentPanel = `<p class="dim" style="padding:12px">No translation for ${escapeHtml(locale)}.</p>`;