scribe-cms 0.0.7 → 0.0.8

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
@@ -1176,6 +1176,9 @@ function createProject(config) {
1176
1176
  getType: getRuntime,
1177
1177
  listTypes() {
1178
1178
  return Array.from(runtimes.values());
1179
+ },
1180
+ listRoutableTypes() {
1181
+ return Array.from(runtimes.values()).filter((runtime) => isRoutableType(runtime.config));
1179
1182
  }
1180
1183
  };
1181
1184
  }
@@ -1671,6 +1674,7 @@ function createScribe(input) {
1671
1674
  project,
1672
1675
  getType: project.getType,
1673
1676
  listTypes: project.listTypes,
1677
+ listRoutableTypes: project.listRoutableTypes,
1674
1678
  sitemap(options) {
1675
1679
  return generateSitemap(project, options);
1676
1680
  }
@@ -2358,11 +2362,12 @@ function buildGeminiResponseSchema(schema, slugStrategy) {
2358
2362
  function slugStrategyRules(slugStrategy, preserveTerms) {
2359
2363
  if (slugStrategy === "localized") {
2360
2364
  const rules = [
2361
- "Provide a URL slug in JSON field `slug` that is TRANSLATED into the target language.",
2365
+ "Provide a URL slug in JSON field `slug` that is Localized into the target language.",
2362
2366
  "Base the slug on the meaning of the translated title \u2014 do NOT reuse the English slug words.",
2363
2367
  "Slug MUST be ASCII only: a-z, 0-9, hyphens. No uppercase, accents, underscores, or spaces.",
2364
2368
  "For non-Latin languages, write the words in the target language and transliterate them into Latin script (e.g. Russian -> romanized Russian, not English).",
2365
- "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."
2369
+ "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.",
2370
+ 'In JSX attributes (e.g. FaqItem question="..."), use single quotes when the value contains double-quote characters (e.g. Hebrew \u05D3\u05D5\u05D0"\u05DC), or escape them as \\".'
2366
2371
  ];
2367
2372
  if (preserveTerms?.length) {
2368
2373
  rules.push(
@@ -2395,6 +2400,95 @@ function resolveTranslateConfig(config, type) {
2395
2400
  return mergeTranslateConfig(config.translate, type.translate, type.slugStrategy);
2396
2401
  }
2397
2402
 
2403
+ // src/translate/sanitize-mdx-jsx.ts
2404
+ function sanitizeMdxJsxAttributeQuotes(body) {
2405
+ let adjusted = false;
2406
+ let out = "";
2407
+ let i = 0;
2408
+ while (i < body.length) {
2409
+ if (body[i] !== "<" || !/[\w.]/.test(body[i + 1] ?? "")) {
2410
+ out += body[i];
2411
+ i += 1;
2412
+ continue;
2413
+ }
2414
+ const tagStart = i;
2415
+ i += 1;
2416
+ while (i < body.length && /[\w.-]/.test(body[i] ?? "")) i += 1;
2417
+ const tagName = body.slice(tagStart + 1, i);
2418
+ let tagOut = `<${tagName}`;
2419
+ let tagAdjusted = false;
2420
+ while (i < body.length && body[i] !== ">" && body[i] !== "/") {
2421
+ while (i < body.length && /\s/.test(body[i] ?? "")) {
2422
+ tagOut += body[i];
2423
+ i += 1;
2424
+ }
2425
+ if (i >= body.length || body[i] === ">" || body[i] === "/") break;
2426
+ const attrStart = i;
2427
+ while (i < body.length && /[\w:.-]/.test(body[i] ?? "")) i += 1;
2428
+ body.slice(attrStart, i);
2429
+ while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
2430
+ if (body[i] !== "=") {
2431
+ tagOut += body.slice(attrStart, i);
2432
+ continue;
2433
+ }
2434
+ tagOut += body.slice(attrStart, i);
2435
+ tagOut += "=";
2436
+ i += 1;
2437
+ while (i < body.length && /\s/.test(body[i] ?? "")) i += 1;
2438
+ const quote = body[i];
2439
+ if (quote !== '"' && quote !== "'") {
2440
+ continue;
2441
+ }
2442
+ if (quote === "'") {
2443
+ const valStart2 = i + 1;
2444
+ i += 1;
2445
+ while (i < body.length) {
2446
+ if (body[i] === "\\") {
2447
+ i += 2;
2448
+ continue;
2449
+ }
2450
+ if (body[i] === "'") break;
2451
+ i += 1;
2452
+ }
2453
+ tagOut += body.slice(valStart2 - 1, i + 1);
2454
+ i += 1;
2455
+ continue;
2456
+ }
2457
+ const valStart = i + 1;
2458
+ i += 1;
2459
+ let closeIdx = -1;
2460
+ for (let scan = valStart; scan < body.length; scan += 1) {
2461
+ if (body[scan] !== '"') continue;
2462
+ let j = scan + 1;
2463
+ while (j < body.length && /\s/.test(body[j] ?? "")) j += 1;
2464
+ if (j < body.length && (body[j] === ">" || body[j] === "/" || /[a-zA-Z]/.test(body[j] ?? ""))) {
2465
+ closeIdx = scan;
2466
+ break;
2467
+ }
2468
+ }
2469
+ if (closeIdx === -1) {
2470
+ tagOut += body.slice(valStart - 1, i);
2471
+ break;
2472
+ }
2473
+ const value = body.slice(valStart, closeIdx);
2474
+ const hasInternalQuote = value.includes('"');
2475
+ if (hasInternalQuote) {
2476
+ const escaped = value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
2477
+ tagOut += `'${escaped}'`;
2478
+ tagAdjusted = true;
2479
+ } else {
2480
+ tagOut += `"${value}"`;
2481
+ }
2482
+ i = closeIdx + 1;
2483
+ }
2484
+ if (tagAdjusted) adjusted = true;
2485
+ tagOut += body[i] ?? "";
2486
+ out += tagOut;
2487
+ i += 1;
2488
+ }
2489
+ return { body: out, adjusted };
2490
+ }
2491
+
2398
2492
  // src/translate/validate-translation.ts
2399
2493
  function formatZodIssues(error) {
2400
2494
  return error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
@@ -2528,6 +2622,9 @@ async function translatePage(config, item, options = {}) {
2528
2622
  if (!validated.ok) {
2529
2623
  throw new Error(`Translation validation failed: ${validated.error}`);
2530
2624
  }
2625
+ const { body: translatedBody, adjusted: mdxAdjusted } = sanitizeMdxJsxAttributeQuotes(
2626
+ result.parsed.body
2627
+ );
2531
2628
  const writeDb = openStore(config, "readwrite");
2532
2629
  const snapshotId = recordEnSnapshot(
2533
2630
  config,
@@ -2546,7 +2643,7 @@ async function translatePage(config, item, options = {}) {
2546
2643
  locale: item.locale,
2547
2644
  slug,
2548
2645
  frontmatter: validated.frontmatter,
2549
- body: result.parsed.body,
2646
+ body: translatedBody,
2550
2647
  enHash: currentEnHash,
2551
2648
  translatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2552
2649
  model: result.model,
@@ -2565,7 +2662,8 @@ async function translatePage(config, item, options = {}) {
2565
2662
  usage: result.usage,
2566
2663
  estimatedCostUsd,
2567
2664
  durationMs: Date.now() - startedAt,
2568
- slugAdjusted
2665
+ slugAdjusted,
2666
+ mdxAdjusted: mdxAdjusted || void 0
2569
2667
  };
2570
2668
  } catch (error) {
2571
2669
  return {
@@ -2700,6 +2798,6 @@ function writeStaticRawExports(project, options = {}) {
2700
2798
  return { exports, written: exports.length };
2701
2799
  }
2702
2800
 
2703
- export { buildAllContentRedirects, buildStaticRawExports, buildWorklist, createProject, createScribe, defineConfig, defineContentType, exportDirSegment, field, findConfigPath, generateSitemap, getFieldKind, getRedirectSourceSlugs, getRelationTarget, getStaticExportRoots, isResolvedConfig, loadConfigSync, resolveConfig, resolveLocalesFromPreset, serializeMdx, translatePage, translateWorklist, unwrapSchema, validateProject, writeStaticRawExports };
2801
+ export { buildAllContentRedirects, buildStaticRawExports, buildWorklist, createProject, createScribe, defineConfig, defineContentType, exportDirSegment, field, findConfigPath, generateSitemap, getFieldKind, getRedirectSourceSlugs, getRelationTarget, getStaticExportRoots, isResolvedConfig, isRoutableType, loadConfigSync, resolveConfig, resolveLocalesFromPreset, serializeMdx, translatePage, translateWorklist, unwrapSchema, validateProject, writeStaticRawExports };
2704
2802
  //# sourceMappingURL=index.js.map
2705
2803
  //# sourceMappingURL=index.js.map