scribe-cms 0.0.11 → 0.0.13

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.
Files changed (56) hide show
  1. package/README.md +21 -6
  2. package/dist/cli/index.cjs +1258 -219
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.js +1259 -220
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/prompt-translate.d.ts +3 -0
  7. package/dist/cli/prompt-translate.d.ts.map +1 -1
  8. package/dist/cli/translate-progress.d.ts.map +1 -1
  9. package/dist/index.cjs +1020 -181
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.js +1021 -183
  12. package/dist/index.js.map +1 -1
  13. package/dist/runtime.cjs +149 -62
  14. package/dist/runtime.cjs.map +1 -1
  15. package/dist/runtime.js +149 -62
  16. package/dist/runtime.js.map +1 -1
  17. package/dist/src/config/resolve-config.d.ts.map +1 -1
  18. package/dist/src/core/introspect-schema.d.ts +7 -1
  19. package/dist/src/core/introspect-schema.d.ts.map +1 -1
  20. package/dist/src/core/types.d.ts +6 -0
  21. package/dist/src/core/types.d.ts.map +1 -1
  22. package/dist/src/create-project.d.ts.map +1 -1
  23. package/dist/src/i18n/resolve-document.d.ts +1 -1
  24. package/dist/src/i18n/resolve-document.d.ts.map +1 -1
  25. package/dist/src/index.d.ts +3 -3
  26. package/dist/src/index.d.ts.map +1 -1
  27. package/dist/src/storage/batch-jobs.d.ts +53 -0
  28. package/dist/src/storage/batch-jobs.d.ts.map +1 -0
  29. package/dist/src/storage/sqlite.d.ts.map +1 -1
  30. package/dist/src/translate/batch-worklist.d.ts +87 -0
  31. package/dist/src/translate/batch-worklist.d.ts.map +1 -0
  32. package/dist/src/translate/gemini-batch.d.ts +26 -0
  33. package/dist/src/translate/gemini-batch.d.ts.map +1 -0
  34. package/dist/src/translate/gemini-client.d.ts +23 -0
  35. package/dist/src/translate/gemini-client.d.ts.map +1 -1
  36. package/dist/src/translate/gemini-models.d.ts +9 -0
  37. package/dist/src/translate/gemini-models.d.ts.map +1 -1
  38. package/dist/src/translate/gemini-pricing.d.ts +2 -1
  39. package/dist/src/translate/gemini-pricing.d.ts.map +1 -1
  40. package/dist/src/translate/page-translator.d.ts +25 -52
  41. package/dist/src/translate/page-translator.d.ts.map +1 -1
  42. package/dist/src/translate/prompts/translation-prompt.d.ts +6 -0
  43. package/dist/src/translate/prompts/translation-prompt.d.ts.map +1 -1
  44. package/dist/src/translate/response-schema.d.ts +8 -1
  45. package/dist/src/translate/response-schema.d.ts.map +1 -1
  46. package/dist/src/translate/retry.d.ts +16 -0
  47. package/dist/src/translate/retry.d.ts.map +1 -0
  48. package/dist/src/translate/sanitize-mdx-jsx.d.ts.map +1 -1
  49. package/dist/src/translate/translate-core.d.ts +155 -0
  50. package/dist/src/translate/translate-core.d.ts.map +1 -0
  51. package/dist/src/validate/validate-project.d.ts.map +1 -1
  52. package/dist/studio/server.cjs +84 -47
  53. package/dist/studio/server.cjs.map +1 -1
  54. package/dist/studio/server.js +84 -47
  55. package/dist/studio/server.js.map +1 -1
  56. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sanitize-mdx-jsx.d.ts","sourceRoot":"","sources":["../../../src/translate/sanitize-mdx-jsx.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAyGA"}
1
+ {"version":3,"file":"sanitize-mdx-jsx.d.ts","sourceRoot":"","sources":["../../../src/translate/sanitize-mdx-jsx.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB,CAkHA"}
@@ -0,0 +1,155 @@
1
+ import type { ContentTypeConfig, ScribeConfig, ScribeDocument } from "../core/types.js";
2
+ import type { GeminiTokenUsage } from "./gemini-client.js";
3
+ import { type TranslationCostMode } from "./gemini-pricing.js";
4
+ import type { TranslationWorkItem } from "./worklist.js";
5
+ export type TranslateMode = "batch" | "direct";
6
+ export interface TranslatePageResult {
7
+ contentType: string;
8
+ enSlug: string;
9
+ locale: string;
10
+ skipped: boolean;
11
+ failed?: boolean;
12
+ reason?: "fresh" | "in-flight";
13
+ model?: string;
14
+ usage?: GeminiTokenUsage;
15
+ estimatedCostUsd?: number;
16
+ durationMs?: number;
17
+ error?: string;
18
+ slugAdjusted?: {
19
+ from: string;
20
+ to: string;
21
+ matchedCode: string;
22
+ };
23
+ mdxAdjusted?: boolean;
24
+ }
25
+ export interface TranslateWorklistTotals {
26
+ translated: number;
27
+ skipped: number;
28
+ failed: number;
29
+ inputTokens: number;
30
+ outputTokens: number;
31
+ estimatedCostUsd: number;
32
+ durationMs: number;
33
+ }
34
+ export type TranslateProgressEvent = {
35
+ type: "start";
36
+ total: number;
37
+ concurrency: number;
38
+ dryRun: boolean;
39
+ model?: string;
40
+ mode?: TranslateMode;
41
+ } | {
42
+ type: "item-start";
43
+ item: TranslationWorkItem;
44
+ active: string[];
45
+ } | {
46
+ type: "item-done";
47
+ result: TranslatePageResult;
48
+ } | {
49
+ type: "batch-submitted";
50
+ name: string;
51
+ count: number;
52
+ model?: string;
53
+ jobIndex: number;
54
+ jobCount: number;
55
+ resumed?: boolean;
56
+ /** ISO timestamp of the job's actual submission (persisted created_at). */
57
+ createdAt?: string;
58
+ } | {
59
+ type: "batch-polling";
60
+ name: string;
61
+ state: string;
62
+ /** Elapsed since the job was submitted (not since the CLI started). */
63
+ elapsedMs: number;
64
+ jobIndex: number;
65
+ jobCount: number;
66
+ } | {
67
+ type: "batch-done";
68
+ name: string;
69
+ state: string;
70
+ model?: string;
71
+ count: number;
72
+ jobIndex: number;
73
+ jobCount: number;
74
+ translated: number;
75
+ failed: number;
76
+ inputTokens: number;
77
+ outputTokens: number;
78
+ estimatedCostUsd: number;
79
+ /** Elapsed since the job was submitted. */
80
+ elapsedMs: number;
81
+ /**
82
+ * True when a concurrent scribe process won the ingestion claim: the
83
+ * job's results were saved by that process, this one has nothing to add.
84
+ */
85
+ alreadyIngested?: boolean;
86
+ } | {
87
+ type: "done";
88
+ results: TranslatePageResult[];
89
+ totals: TranslateWorklistTotals;
90
+ };
91
+ /** Stable identity for a worklist item / batch item row. */
92
+ export declare function translationItemKey(item: {
93
+ contentType?: string;
94
+ content_type?: string;
95
+ enSlug?: string;
96
+ en_slug?: string;
97
+ locale: string;
98
+ }): string;
99
+ export declare function summarizeResults(results: TranslatePageResult[], durationMs: number): TranslateWorklistTotals;
100
+ export declare function formatTranslateError(error: unknown): string;
101
+ export declare function baseForItem(item: TranslationWorkItem): Pick<TranslatePageResult, "contentType" | "enSlug" | "locale">;
102
+ /** Everything needed to submit and finalize one translation request. */
103
+ export interface PreparedTranslation {
104
+ item: TranslationWorkItem;
105
+ type: ContentTypeConfig;
106
+ enDoc: ScribeDocument;
107
+ payload: {
108
+ frontmatter: Record<string, unknown>;
109
+ body: string;
110
+ };
111
+ currentEnHash: string;
112
+ existingSlug?: string;
113
+ /** Configured model (may be undefined; env/default applies downstream). */
114
+ model?: string;
115
+ prompt: string;
116
+ responseSchema?: Record<string, unknown>;
117
+ }
118
+ export type PrepareOutcome = {
119
+ status: "ready";
120
+ prepared: PreparedTranslation;
121
+ } | {
122
+ status: "done";
123
+ result: TranslatePageResult;
124
+ };
125
+ /**
126
+ * Shared pre-flight for the direct and batch paths: read the EN doc, skip when
127
+ * the stored translation is fresh, and build the prompt + response schema.
128
+ * Throws on unrecoverable item errors (unknown type, missing EN doc).
129
+ */
130
+ export declare function prepareTranslation(config: ScribeConfig, item: TranslationWorkItem, options: {
131
+ model?: string;
132
+ force?: boolean;
133
+ }, startedAt: number): PrepareOutcome;
134
+ /**
135
+ * Shared post-processing for the direct and batch paths: strip locale suffixes
136
+ * from the slug, validate frontmatter + MDX body, snapshot the EN source and
137
+ * upsert the translation. Never throws — failures come back as a failed result.
138
+ * Pass `snapshotId` when the EN snapshot was already recorded (batch submission
139
+ * time) so ingestion does not depend on the current EN files.
140
+ */
141
+ export declare function finalizeTranslation(config: ScribeConfig, prepared: PreparedTranslation, output: {
142
+ model: string;
143
+ parsed: {
144
+ frontmatter: Record<string, unknown>;
145
+ body: string;
146
+ slug?: string;
147
+ };
148
+ usage: GeminiTokenUsage;
149
+ }, options: {
150
+ costMode: TranslationCostMode;
151
+ startedAt: number;
152
+ snapshotId?: number;
153
+ }): TranslatePageResult;
154
+ export declare function displayModelFor(prepared: PreparedTranslation): string;
155
+ //# sourceMappingURL=translate-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translate-core.d.ts","sourceRoot":"","sources":["../../../src/translate/translate-core.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate-project.d.ts","sourceRoot":"","sources":["../../../src/validate/validate-project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAcrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAWD,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAyJpE"}
1
+ {"version":3,"file":"validate-project.d.ts","sourceRoot":"","sources":["../../../src/validate/validate-project.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAcrD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB;AAkCD,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CA2JpE"}
@@ -51,20 +51,16 @@ function getRelationTarget(schema) {
51
51
  }
52
52
  return null;
53
53
  }
54
- function unwrapSchema(schema) {
54
+ function peelOptionalWrappers(schema) {
55
55
  let current = schema;
56
56
  for (let i = 0; i < 8; i++) {
57
- const anySchema = current;
58
- if (typeof anySchema.unwrap === "function") {
59
- current = anySchema.unwrap();
57
+ const type = current._def?.type;
58
+ if (type === "optional" || type === "nullable") {
59
+ current = current.unwrap();
60
60
  continue;
61
61
  }
62
- if (typeof anySchema.removeDefault === "function") {
63
- current = anySchema.removeDefault();
64
- continue;
65
- }
66
- if (anySchema._def?.innerType) {
67
- current = anySchema._def.innerType;
62
+ if (type === "default") {
63
+ current = current.removeDefault();
68
64
  continue;
69
65
  }
70
66
  break;
@@ -73,6 +69,12 @@ function unwrapSchema(schema) {
73
69
  }
74
70
 
75
71
  // src/core/introspect-schema.ts
72
+ function getArrayElement(schema) {
73
+ if (schema instanceof Object && "element" in schema && schema._def?.type === "array") {
74
+ return schema.element;
75
+ }
76
+ return null;
77
+ }
76
78
  function introspectSchema(schema, prefix = []) {
77
79
  const relation = getRelationTarget(schema);
78
80
  if (relation && prefix.length > 0) {
@@ -86,7 +88,10 @@ function introspectSchema(schema, prefix = []) {
86
88
  }
87
89
  ];
88
90
  }
89
- const base = unwrapSchema(schema);
91
+ if (prefix.length > 0 && getFieldKind(schema) === "translatable") {
92
+ return [{ path: prefix, kind: "translatable" }];
93
+ }
94
+ const base = peelOptionalWrappers(schema);
90
95
  if (base instanceof Object && "shape" in base) {
91
96
  const shape = base.shape;
92
97
  const fields = [];
@@ -95,9 +100,9 @@ function introspectSchema(schema, prefix = []) {
95
100
  }
96
101
  return fields;
97
102
  }
98
- if (base instanceof Object && "element" in base) {
99
- const element = base.element;
100
- const elementBase = unwrapSchema(element);
103
+ const element = getArrayElement(base);
104
+ if (element) {
105
+ const elementBase = peelOptionalWrappers(element);
101
106
  if (elementBase instanceof Object && "shape" in elementBase) {
102
107
  const shape = elementBase.shape;
103
108
  const fields = [];
@@ -109,15 +114,48 @@ function introspectSchema(schema, prefix = []) {
109
114
  }
110
115
  return [{ path: prefix, kind: getFieldKind(schema) }];
111
116
  }
112
- function extractByPaths(data, paths) {
113
- const out = {};
117
+ function buildPathTrie(paths) {
118
+ const root = { leaf: false, children: /* @__PURE__ */ new Map() };
114
119
  for (const path4 of paths) {
115
- const value = getAtPath(data, path4);
120
+ let node = root;
121
+ for (const segment of path4) {
122
+ let child = node.children.get(segment);
123
+ if (!child) {
124
+ child = { leaf: false, children: /* @__PURE__ */ new Map() };
125
+ node.children.set(segment, child);
126
+ }
127
+ node = child;
128
+ }
129
+ node.leaf = true;
130
+ }
131
+ return root;
132
+ }
133
+ function extractNode(data, trie) {
134
+ if (trie.leaf) return data;
135
+ const star = trie.children.get("*");
136
+ if (star) {
137
+ if (!Array.isArray(data)) return void 0;
138
+ return data.map(
139
+ (item) => typeof item === "object" && item !== null ? extractNode(item, star) ?? {} : {}
140
+ );
141
+ }
142
+ if (typeof data !== "object" || data === null || Array.isArray(data)) return void 0;
143
+ const record = data;
144
+ const out = {};
145
+ let any = false;
146
+ for (const [key, child] of trie.children) {
147
+ const value = extractNode(record[key], child);
116
148
  if (value !== void 0) {
117
- setAtPath(out, path4.filter((p) => p !== "*"), value);
149
+ out[key] = value;
150
+ any = true;
118
151
  }
119
152
  }
120
- return out;
153
+ return any ? out : void 0;
154
+ }
155
+ function extractByPaths(data, paths) {
156
+ if (paths.length === 0) return {};
157
+ const extracted = extractNode(data, buildPathTrie(paths));
158
+ return extracted && typeof extracted === "object" && !Array.isArray(extracted) ? extracted : {};
121
159
  }
122
160
  function pickTranslatable(data, schema) {
123
161
  const translatablePaths = introspectSchema(schema).filter((f) => f.kind === "translatable").map((f) => f.path);
@@ -132,32 +170,6 @@ function mergeStructuralOntoLocale(localeData, enData, schema) {
132
170
  const translatable = pickTranslatable(localeData, schema);
133
171
  return deepMerge(structural, translatable);
134
172
  }
135
- function getAtPath(obj, path4) {
136
- let current = obj;
137
- for (const segment of path4) {
138
- if (segment === "*") {
139
- if (!Array.isArray(current)) return void 0;
140
- return current.map(
141
- (item) => typeof item === "object" && item !== null ? getAtPath(item, path4.slice(path4.indexOf("*") + 1)) : void 0
142
- );
143
- }
144
- if (typeof current !== "object" || current === null || Array.isArray(current)) return void 0;
145
- current = current[segment];
146
- }
147
- return current;
148
- }
149
- function setAtPath(obj, path4, value) {
150
- if (path4.length === 0) return;
151
- if (path4.length === 1) {
152
- obj[path4[0]] = value;
153
- return;
154
- }
155
- const [head, ...rest] = path4;
156
- if (!(head in obj) || typeof obj[head] !== "object" || obj[head] === null) {
157
- obj[head] = {};
158
- }
159
- setAtPath(obj[head], rest, value);
160
- }
161
173
  function deepMerge(base, overlay) {
162
174
  const out = { ...base };
163
175
  for (const [key, value] of Object.entries(overlay)) {
@@ -377,7 +389,7 @@ function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale, loc
377
389
  out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale, localeRouting);
378
390
  return out;
379
391
  }
380
- var SCHEMA_VERSION = 4;
392
+ var SCHEMA_VERSION = 5;
381
393
  var MIGRATIONS = [
382
394
  `CREATE TABLE IF NOT EXISTS meta (
383
395
  key TEXT PRIMARY KEY,
@@ -425,7 +437,30 @@ var MIGRATIONS = [
425
437
  UNIQUE (content_type, en_slug, en_hash)
426
438
  )`,
427
439
  `CREATE INDEX IF NOT EXISTS idx_en_snapshots_lookup
428
- ON en_snapshots(content_type, en_slug, created_at DESC)`
440
+ ON en_snapshots(content_type, en_slug, created_at DESC)`,
441
+ `CREATE TABLE IF NOT EXISTS translation_batch_jobs (
442
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
443
+ job_name TEXT NOT NULL UNIQUE,
444
+ model TEXT NOT NULL,
445
+ display_model TEXT NOT NULL,
446
+ created_at TEXT NOT NULL,
447
+ state TEXT NOT NULL,
448
+ completed_at TEXT
449
+ )`,
450
+ `CREATE TABLE IF NOT EXISTS translation_batch_items (
451
+ job_id INTEGER NOT NULL,
452
+ request_index INTEGER NOT NULL,
453
+ content_type TEXT NOT NULL,
454
+ en_slug TEXT NOT NULL,
455
+ locale TEXT NOT NULL,
456
+ en_hash TEXT NOT NULL,
457
+ snapshot_id INTEGER NOT NULL,
458
+ status TEXT NOT NULL,
459
+ error TEXT,
460
+ PRIMARY KEY (job_id, request_index)
461
+ )`,
462
+ `CREATE INDEX IF NOT EXISTS idx_batch_items_status
463
+ ON translation_batch_items(status)`
429
464
  ];
430
465
  function resolveStorePath(config) {
431
466
  return config.storePath;
@@ -448,6 +483,8 @@ function addColumnIfMissing(db, table, column, ddlType) {
448
483
  }
449
484
  }
450
485
  function readSchemaVersion(db) {
486
+ const metaTable = db.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'meta'`).get();
487
+ if (!metaTable) return 0;
451
488
  const row = db.prepare(`SELECT value FROM meta WHERE key = 'schema_version'`).get();
452
489
  return row ? Number.parseInt(row.value, 10) || 0 : 0;
453
490
  }