scribe-cms 0.0.5 → 0.0.6

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/index.js CHANGED
@@ -498,7 +498,7 @@ function seoFieldsFromEn(enDoc) {
498
498
  redirectTo: enDoc.redirectTo
499
499
  };
500
500
  }
501
- var SCHEMA_VERSION = 3;
501
+ var SCHEMA_VERSION = 4;
502
502
  var MIGRATIONS = [
503
503
  `CREATE TABLE IF NOT EXISTS meta (
504
504
  key TEXT PRIMARY KEY,
@@ -518,20 +518,6 @@ var MIGRATIONS = [
518
518
  )`,
519
519
  `CREATE INDEX IF NOT EXISTS idx_translations_type_locale
520
520
  ON translations(content_type, locale)`,
521
- `CREATE TABLE IF NOT EXISTS revisions (
522
- id INTEGER PRIMARY KEY AUTOINCREMENT,
523
- content_type TEXT NOT NULL,
524
- en_slug TEXT NOT NULL,
525
- locale TEXT,
526
- revision_kind TEXT NOT NULL,
527
- en_hash TEXT NOT NULL,
528
- body_hash TEXT NOT NULL,
529
- created_at TEXT NOT NULL,
530
- model TEXT,
531
- body_preview TEXT
532
- )`,
533
- `CREATE INDEX IF NOT EXISTS idx_revisions_lookup
534
- ON revisions(content_type, en_slug, locale, created_at DESC)`,
535
521
  `CREATE TABLE IF NOT EXISTS slug_aliases (
536
522
  content_type TEXT NOT NULL,
537
523
  canonical_en_slug TEXT NOT NULL,
@@ -548,7 +534,19 @@ var MIGRATIONS = [
548
534
  locale_slug TEXT NOT NULL,
549
535
  captured_at TEXT NOT NULL,
550
536
  PRIMARY KEY (content_type, alias_en_slug, locale)
551
- )`
537
+ )`,
538
+ `CREATE TABLE IF NOT EXISTS en_snapshots (
539
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
540
+ content_type TEXT NOT NULL,
541
+ en_slug TEXT NOT NULL,
542
+ en_hash TEXT NOT NULL,
543
+ frontmatter_json TEXT NOT NULL,
544
+ body TEXT NOT NULL,
545
+ created_at TEXT NOT NULL,
546
+ UNIQUE (content_type, en_slug, en_hash)
547
+ )`,
548
+ `CREATE INDEX IF NOT EXISTS idx_en_snapshots_lookup
549
+ ON en_snapshots(content_type, en_slug, created_at DESC)`
552
550
  ];
553
551
  function resolveStorePath(config) {
554
552
  return config.storePath;
@@ -570,12 +568,19 @@ function addColumnIfMissing(db, table, column, ddlType) {
570
568
  db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${ddlType}`);
571
569
  }
572
570
  }
571
+ function readSchemaVersion(db) {
572
+ const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
573
+ return row ? Number.parseInt(row.value, 10) || 0 : 0;
574
+ }
573
575
  function migrate(db) {
576
+ const previousVersion = readSchemaVersion(db);
574
577
  for (const sql of MIGRATIONS) {
575
578
  db.exec(sql);
576
579
  }
577
- addColumnIfMissing(db, "revisions", "frontmatter_json", "TEXT");
578
- addColumnIfMissing(db, "revisions", "body", "TEXT");
580
+ if (previousVersion < 4) {
581
+ db.exec(`DROP TABLE IF EXISTS revisions`);
582
+ }
583
+ addColumnIfMissing(db, "translations", "snapshot_id", "INTEGER");
579
584
  db.prepare(
580
585
  `INSERT INTO meta(key, value) VALUES('schema_version', ?)
581
586
  ON CONFLICT(key) DO UPDATE SET value = excluded.value`
@@ -583,18 +588,38 @@ function migrate(db) {
583
588
  }
584
589
 
585
590
  // src/storage/translations.ts
591
+ function getOrCreateEnSnapshot(db, input) {
592
+ db.prepare(
593
+ `INSERT OR IGNORE INTO en_snapshots (
594
+ content_type, en_slug, en_hash, frontmatter_json, body, created_at
595
+ ) VALUES (?, ?, ?, ?, ?, ?)`
596
+ ).run(
597
+ input.contentType,
598
+ input.enSlug,
599
+ input.enHash,
600
+ JSON.stringify(input.frontmatter),
601
+ input.body,
602
+ input.createdAt
603
+ );
604
+ const row = db.prepare(
605
+ `SELECT id FROM en_snapshots
606
+ WHERE content_type = ? AND en_slug = ? AND en_hash = ?`
607
+ ).get(input.contentType, input.enSlug, input.enHash);
608
+ return row.id;
609
+ }
586
610
  function upsertTranslation(db, input) {
587
611
  db.prepare(
588
612
  `INSERT INTO translations (
589
- content_type, en_slug, locale, slug, frontmatter_json, body, en_hash, translated_at, model
590
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
613
+ content_type, en_slug, locale, slug, frontmatter_json, body, en_hash, translated_at, model, snapshot_id
614
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
591
615
  ON CONFLICT(content_type, en_slug, locale) DO UPDATE SET
592
616
  slug = excluded.slug,
593
617
  frontmatter_json = excluded.frontmatter_json,
594
618
  body = excluded.body,
595
619
  en_hash = excluded.en_hash,
596
620
  translated_at = excluded.translated_at,
597
- model = excluded.model`
621
+ model = excluded.model,
622
+ snapshot_id = excluded.snapshot_id`
598
623
  ).run(
599
624
  input.contentType,
600
625
  input.enSlug,
@@ -604,7 +629,8 @@ function upsertTranslation(db, input) {
604
629
  input.body,
605
630
  input.enHash,
606
631
  input.translatedAt,
607
- input.model
632
+ input.model,
633
+ input.snapshotId
608
634
  );
609
635
  }
610
636
  function getTranslation(db, contentType, enSlug, locale) {
@@ -623,27 +649,6 @@ function listTranslationsForEnSlug(db, contentType, enSlug) {
623
649
  function bulkLoadTranslations(db) {
624
650
  return db.prepare(`SELECT * FROM translations ORDER BY content_type, en_slug, locale`).all();
625
651
  }
626
- function appendRevision(db, input) {
627
- const result = db.prepare(
628
- `INSERT INTO revisions (
629
- content_type, en_slug, locale, revision_kind, en_hash, body_hash, created_at,
630
- model, body_preview, frontmatter_json, body
631
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
632
- ).run(
633
- input.contentType,
634
- input.enSlug,
635
- input.locale,
636
- input.revisionKind,
637
- input.enHash,
638
- input.bodyHash,
639
- input.createdAt,
640
- input.model ?? null,
641
- input.bodyPreview ?? null,
642
- input.frontmatterJson ?? null,
643
- input.body ?? null
644
- );
645
- return Number(result.lastInsertRowid);
646
- }
647
652
 
648
653
  // src/loader/normalize-en.ts
649
654
  function normalizeEnFrontmatter(data) {
@@ -2048,9 +2053,6 @@ function computePageEnHash(translatableFrontmatter, body) {
2048
2053
  const payload = JSON.stringify({ frontmatter: translatableFrontmatter, body });
2049
2054
  return sha256(payload);
2050
2055
  }
2051
- function computeBodyHash(body) {
2052
- return sha256(body);
2053
- }
2054
2056
 
2055
2057
  // src/translate/worklist.ts
2056
2058
  function listEnSlugs3(rootDir, contentDir) {
@@ -2111,23 +2113,18 @@ function resolveLocalesFromPreset(config, preset, explicitLocales) {
2111
2113
  return config.locales.filter((l) => l !== config.defaultLocale);
2112
2114
  }
2113
2115
 
2114
- // src/history/record-revision.ts
2115
- function recordRevision(config, input) {
2116
- const db = openStore(config, "readwrite");
2117
- const id = appendRevision(db, {
2116
+ // src/history/record-snapshot.ts
2117
+ function recordEnSnapshot(config, input, db) {
2118
+ const ownDb = db ?? openStore(config, "readwrite");
2119
+ const id = getOrCreateEnSnapshot(ownDb, {
2118
2120
  contentType: input.contentType,
2119
2121
  enSlug: input.enSlug,
2120
- locale: input.locale,
2121
- revisionKind: input.revisionKind,
2122
2122
  enHash: input.enHash,
2123
- bodyHash: computeBodyHash(input.body),
2124
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2125
- model: input.model,
2126
- bodyPreview: input.body.slice(0, 200),
2127
- frontmatterJson: input.frontmatter ? JSON.stringify(input.frontmatter) : null,
2128
- body: input.body
2123
+ frontmatter: input.frontmatter,
2124
+ body: input.body,
2125
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
2129
2126
  });
2130
- db.close();
2127
+ if (!db) ownDb.close();
2131
2128
  return id;
2132
2129
  }
2133
2130
 
@@ -2504,6 +2501,17 @@ async function translatePage(config, item, options = {}) {
2504
2501
  throw new Error(`Translation validation failed: ${validated.error}`);
2505
2502
  }
2506
2503
  const writeDb = openStore(config, "readwrite");
2504
+ const snapshotId = recordEnSnapshot(
2505
+ config,
2506
+ {
2507
+ contentType: type.id,
2508
+ enSlug: item.enSlug,
2509
+ enHash: currentEnHash,
2510
+ frontmatter: payload.frontmatter,
2511
+ body: payload.body
2512
+ },
2513
+ writeDb
2514
+ );
2507
2515
  upsertTranslation(writeDb, {
2508
2516
  contentType: type.id,
2509
2517
  enSlug: item.enSlug,
@@ -2513,19 +2521,10 @@ async function translatePage(config, item, options = {}) {
2513
2521
  body: result.parsed.body,
2514
2522
  enHash: currentEnHash,
2515
2523
  translatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2516
- model: result.model
2524
+ model: result.model,
2525
+ snapshotId
2517
2526
  });
2518
2527
  writeDb.close();
2519
- recordRevision(config, {
2520
- contentType: type.id,
2521
- enSlug: item.enSlug,
2522
- locale: item.locale,
2523
- revisionKind: "translation",
2524
- enHash: currentEnHash,
2525
- body: result.parsed.body,
2526
- frontmatter: validated.frontmatter,
2527
- model: result.model
2528
- });
2529
2528
  const estimatedCostUsd = estimateTranslationCostUsd(
2530
2529
  normalizeGeminiDisplayName(result.model),
2531
2530
  result.usage.inputTokens,