scribe-cms 0.0.6 → 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.
@@ -12,6 +12,7 @@ var genai = require('@google/genai');
12
12
  var dotenv = require('dotenv');
13
13
  var nodeServer = require('@hono/node-server');
14
14
  var hono = require('hono');
15
+ var core = require('@inquirer/core');
15
16
  var prompts = require('@inquirer/prompts');
16
17
  var url = require('url');
17
18
 
@@ -25,7 +26,6 @@ var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
25
26
 
26
27
  var __defProp = Object.defineProperty;
27
28
  var __getOwnPropNames = Object.getOwnPropertyNames;
28
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
29
29
  var __esm = (fn, res) => function __init() {
30
30
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
31
31
  };
@@ -33,7 +33,6 @@ var __export = (target, all) => {
33
33
  for (var name in all)
34
34
  __defProp(target, name, { get: all[name], enumerable: true });
35
35
  };
36
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
37
36
 
38
37
  // ../../node_modules/tsup/assets/cjs_shims.js
39
38
  var getImportMetaUrl, importMetaUrl;
@@ -1642,22 +1641,45 @@ function validateDocumentAssets(config, input) {
1642
1641
 
1643
1642
  // src/validate/validate-slug-suffix.ts
1644
1643
  init_cjs_shims();
1644
+
1645
+ // src/core/localized-slug.ts
1646
+ init_cjs_shims();
1647
+ function findLocaleSuffixInSlug(slug, localeCodes) {
1648
+ const sorted = [...localeCodes].sort((a, b) => b.length - a.length);
1649
+ for (const code of sorted) {
1650
+ if (slug.endsWith(`-${code}`)) {
1651
+ return code;
1652
+ }
1653
+ }
1654
+ return void 0;
1655
+ }
1656
+ function stripLocaleSuffixFromSlug(slug, localeCodes) {
1657
+ const matchedCode = findLocaleSuffixInSlug(slug, localeCodes);
1658
+ if (!matchedCode) {
1659
+ return { slug, stripped: false };
1660
+ }
1661
+ return {
1662
+ slug: slug.slice(0, -(matchedCode.length + 1)),
1663
+ stripped: true,
1664
+ matchedCode
1665
+ };
1666
+ }
1667
+
1668
+ // src/validate/validate-slug-suffix.ts
1645
1669
  function validateTranslationSlugSuffixes(config, db) {
1646
1670
  const issues = [];
1647
1671
  const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
1648
1672
  for (const row of bulkLoadTranslations(db)) {
1649
1673
  if (row.locale === config.defaultLocale) continue;
1650
- for (const code of localeCodes) {
1651
- if (row.slug.endsWith(`-${code}`)) {
1652
- issues.push({
1653
- contentTypeId: row.content_type,
1654
- enSlug: row.en_slug,
1655
- locale: row.locale,
1656
- slug: row.slug,
1657
- message: `Translation slug "${row.slug}" ends with locale code "-${code}"`
1658
- });
1659
- break;
1660
- }
1674
+ const matchedCode = findLocaleSuffixInSlug(row.slug, localeCodes);
1675
+ if (matchedCode) {
1676
+ issues.push({
1677
+ contentTypeId: row.content_type,
1678
+ enSlug: row.en_slug,
1679
+ locale: row.locale,
1680
+ slug: row.slug,
1681
+ message: `Translation slug "${row.slug}" ends with locale code "-${matchedCode}"`
1682
+ });
1661
1683
  }
1662
1684
  }
1663
1685
  return issues;
@@ -1805,7 +1827,7 @@ function validateProject(config) {
1805
1827
  try {
1806
1828
  for (const issue of validateTranslationSlugSuffixes(config, dbForSuffix)) {
1807
1829
  issues.push({
1808
- level: "error",
1830
+ level: "warning",
1809
1831
  contentType: issue.contentTypeId,
1810
1832
  enSlug: issue.enSlug,
1811
1833
  locale: issue.locale,
@@ -2056,6 +2078,9 @@ function buildPageTranslationPrompt(input) {
2056
2078
  ...input.contextLabel ? [`Document: ${input.contextLabel}`, ""] : [],
2057
2079
  "## Rules",
2058
2080
  ...input.resolved.rules.map((rule) => `- ${rule}`),
2081
+ ...input.slugStrategy === "localized" ? [
2082
+ `- The slug MUST be written in ${localeName}, derived from the ${localeName} title and its meaning \u2014 never the English slug. Transliterate non-Latin ${localeName} into ASCII Latin.`
2083
+ ] : [],
2059
2084
  "",
2060
2085
  "## Output format",
2061
2086
  "Return ONLY valid JSON with keys:",
@@ -2143,9 +2168,11 @@ init_cjs_shims();
2143
2168
  function slugStrategyRules(slugStrategy, preserveTerms) {
2144
2169
  if (slugStrategy === "localized") {
2145
2170
  const rules = [
2146
- "Provide a localized URL slug in JSON field `slug`.",
2171
+ "Provide a URL slug in JSON field `slug` that is TRANSLATED into the target language.",
2172
+ "Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
2147
2173
  "Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
2148
- "For non-Latin locales, transliterate into Latin script."
2174
+ "For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
2175
+ "Do NOT append locale codes to the slug (e.g. -fr, -he, -zh-cn). Locale routing is handled by the URL prefix, not the slug."
2149
2176
  ];
2150
2177
  if (preserveTerms?.length) {
2151
2178
  rules.push(
@@ -2304,7 +2331,10 @@ async function translatePage(config, item, options = {}) {
2304
2331
  model,
2305
2332
  responseSchema: responseSchema ?? void 0
2306
2333
  });
2307
- const slug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
2334
+ const rawSlug = type.slugStrategy === "localized" ? result.parsed.slug ?? existing?.slug ?? item.enSlug : item.enSlug;
2335
+ const localeCodes = config.locales.filter((l) => l !== config.defaultLocale);
2336
+ const { slug, stripped, matchedCode } = stripLocaleSuffixFromSlug(rawSlug, localeCodes);
2337
+ const slugAdjusted = stripped && matchedCode ? { from: rawSlug, to: slug, matchedCode } : void 0;
2308
2338
  const validated = validateTranslatedFrontmatter(enDoc, result.parsed.frontmatter, type.schema);
2309
2339
  if (!validated.ok) {
2310
2340
  throw new Error(`Translation validation failed: ${validated.error}`);
@@ -2345,7 +2375,8 @@ async function translatePage(config, item, options = {}) {
2345
2375
  model: result.model,
2346
2376
  usage: result.usage,
2347
2377
  estimatedCostUsd,
2348
- durationMs: Date.now() - startedAt
2378
+ durationMs: Date.now() - startedAt,
2379
+ slugAdjusted
2349
2380
  };
2350
2381
  } catch (error) {
2351
2382
  return {
@@ -2971,16 +3002,6 @@ async function startStudio(project, options = {}) {
2971
3002
 
2972
3003
  // cli/prompt-translate.ts
2973
3004
  init_cjs_shims();
2974
-
2975
- // ../../node_modules/@inquirer/core/dist/lib/errors.js
2976
- init_cjs_shims();
2977
- var CancelPromptError = class extends Error {
2978
- constructor() {
2979
- super(...arguments);
2980
- __publicField(this, "name", "CancelPromptError");
2981
- __publicField(this, "message", "Prompt was canceled");
2982
- }
2983
- };
2984
3005
  function isInteractive() {
2985
3006
  return process.stdin.isTTY === true && process.stdout.isTTY === true;
2986
3007
  }
@@ -2991,7 +3012,7 @@ async function runPrompt(prompt) {
2991
3012
  try {
2992
3013
  return await prompt;
2993
3014
  } catch (error) {
2994
- if (error instanceof CancelPromptError) process.exit(0);
3015
+ if (error instanceof core.CancelPromptError) process.exit(0);
2995
3016
  throw error;
2996
3017
  }
2997
3018
  }
@@ -3092,8 +3113,17 @@ function statusForResult(result, dryRun) {
3092
3113
  if (dryRun) return yellow("would translate");
3093
3114
  return green("translated");
3094
3115
  }
3116
+ function slugAdjustedMessage(result) {
3117
+ if (!result.slugAdjusted) return void 0;
3118
+ const { from, to, matchedCode } = result.slugAdjusted;
3119
+ return yellow(
3120
+ `slug adjusted: "${from}" \u2192 "${to}" (stripped -${matchedCode})`
3121
+ );
3122
+ }
3095
3123
  function detailForResult(result) {
3096
3124
  const parts = [];
3125
+ const slugMsg = slugAdjustedMessage(result);
3126
+ if (slugMsg) parts.push(slugMsg);
3097
3127
  if (result.durationMs !== void 0) parts.push(`${(result.durationMs / 1e3).toFixed(1)}s`);
3098
3128
  if (result.usage) {
3099
3129
  parts.push(`${formatTokenCount(result.usage.inputTokens)} in`);
@@ -3127,6 +3157,10 @@ function createTranslateProgressReporter(options = {}) {
3127
3157
  const status = statusForResult(event.result, dryRun);
3128
3158
  const detail = detailForResult(event.result);
3129
3159
  console.log(`${label}: ${status}${detail ? ` (${detail.replace(/\x1b\[[0-9;]*m/g, "")})` : ""}`);
3160
+ const slugMsg = slugAdjustedMessage(event.result);
3161
+ if (slugMsg) {
3162
+ console.log(`[warning] ${labelForResult(event.result)} ${slugMsg.replace(/\x1b\[[0-9;]*m/g, "")}`);
3163
+ }
3130
3164
  if (event.result.failed && event.result.error) {
3131
3165
  console.error(event.result.error);
3132
3166
  }
@@ -3230,6 +3264,14 @@ function createTranslateProgressReporter(options = {}) {
3230
3264
  for (const result of event.results.filter((entry) => entry.failed)) {
3231
3265
  process.stdout.write("\x1B[2K" + red(`${labelForResult(result)}: ${result.error ?? "failed"}`) + "\n");
3232
3266
  }
3267
+ for (const result of event.results.filter((entry) => entry.slugAdjusted)) {
3268
+ const slugMsg = slugAdjustedMessage(result);
3269
+ if (slugMsg) {
3270
+ process.stdout.write(
3271
+ "\x1B[2K" + yellow(`[warning] ${labelForResult(result)} ${slugMsg.replace(/\x1b\[[0-9;]*m/g, "")}`) + "\n"
3272
+ );
3273
+ }
3274
+ }
3233
3275
  break;
3234
3276
  }
3235
3277
  },