@blinkk/root-cms 2.5.8 → 2.5.10

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/plugin.js CHANGED
@@ -4,15 +4,15 @@ import {
4
4
  } from "./chunk-T5UK2H24.js";
5
5
  import {
6
6
  getServerVersion
7
- } from "./chunk-4YTAYZTP.js";
7
+ } from "./chunk-7QY6372S.js";
8
8
  import {
9
9
  runCronJobs
10
- } from "./chunk-AFL5KJ4E.js";
10
+ } from "./chunk-PKD2TDSV.js";
11
11
  import {
12
12
  RootCMSClient,
13
13
  parseDocId,
14
14
  unmarshalData
15
- } from "./chunk-5N2KLMQH.js";
15
+ } from "./chunk-YMUZ5H5C.js";
16
16
  import "./chunk-MLKGABMK.js";
17
17
 
18
18
  // core/plugin.ts
@@ -514,6 +514,96 @@ function api(server, options) {
514
514
  res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
515
515
  }
516
516
  });
517
+ server.use(
518
+ "/cms/api/translations.import",
519
+ async (req, res) => {
520
+ await handleTranslationServiceRequest(req, res, "import");
521
+ }
522
+ );
523
+ server.use(
524
+ "/cms/api/translations.export",
525
+ async (req, res) => {
526
+ await handleTranslationServiceRequest(req, res, "export");
527
+ }
528
+ );
529
+ async function handleTranslationServiceRequest(req, res, action) {
530
+ if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
531
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
532
+ return;
533
+ }
534
+ if (!req.user?.email) {
535
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
536
+ return;
537
+ }
538
+ const reqBody = req.body || {};
539
+ const serviceId = String(reqBody.serviceId || "").trim();
540
+ const docId = String(reqBody.docId || "").trim();
541
+ const data = Array.isArray(reqBody.data) ? reqBody.data : [];
542
+ for (const row of data) {
543
+ if (typeof row?.source !== "string" || typeof row?.translations !== "object" || row.translations === null || Array.isArray(row.translations)) {
544
+ res.status(400).json({ success: false, error: "INVALID_DATA_FORMAT" });
545
+ return;
546
+ }
547
+ for (const value of Object.values(row.translations)) {
548
+ if (typeof value !== "string") {
549
+ res.status(400).json({ success: false, error: "INVALID_DATA_FORMAT" });
550
+ return;
551
+ }
552
+ }
553
+ if (row.description !== void 0 && typeof row.description !== "string") {
554
+ res.status(400).json({ success: false, error: "INVALID_DATA_FORMAT" });
555
+ return;
556
+ }
557
+ }
558
+ if (!serviceId || !docId) {
559
+ res.status(400).json({ success: false, error: "MISSING_REQUIRED_FIELD" });
560
+ return;
561
+ }
562
+ const services = options.translations || [];
563
+ const service = services.find((s) => s.id === serviceId);
564
+ if (!service) {
565
+ res.status(404).json({ success: false, error: "SERVICE_NOT_FOUND" });
566
+ return;
567
+ }
568
+ const handler = action === "import" ? service.onImport : service.onExport;
569
+ if (!handler) {
570
+ res.status(400).json({
571
+ success: false,
572
+ error: `SERVICE_DOES_NOT_SUPPORT_${action.toUpperCase()}`
573
+ });
574
+ return;
575
+ }
576
+ let collectionId;
577
+ let slug;
578
+ try {
579
+ const parsed = parseDocId(docId);
580
+ collectionId = parsed.collection;
581
+ slug = parsed.slug;
582
+ } catch (err) {
583
+ console.error(err.stack || err);
584
+ res.status(400).json({ success: false, error: "INVALID_DOC_ID" });
585
+ return;
586
+ }
587
+ try {
588
+ const cmsClient = new RootCMSClient(req.rootConfig);
589
+ const locales = req.rootConfig?.i18n?.locales || [];
590
+ const result = await handler(
591
+ {
592
+ rootConfig: req.rootConfig,
593
+ cmsClient,
594
+ docId,
595
+ collectionId,
596
+ slug,
597
+ locales
598
+ },
599
+ data
600
+ );
601
+ res.status(200).json({ success: true, data: result });
602
+ } catch (err) {
603
+ console.error(err.stack || err);
604
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
605
+ }
606
+ }
517
607
  }
518
608
 
519
609
  // core/sse.ts
@@ -701,11 +791,12 @@ function extractRichTextNodeText(node) {
701
791
  }
702
792
  return "";
703
793
  }
704
- function translationsCheck() {
794
+ function translationsCheck(options) {
705
795
  return {
706
796
  id: "root-cms/translations",
707
797
  label: "Missing Translations",
708
798
  description: "Checks that all translatable strings have translations for each enabled locale.",
799
+ collections: options?.collections,
709
800
  run: async (ctx) => {
710
801
  const { cmsClient, collectionId, slug, collectionSchema } = ctx;
711
802
  const rawDoc = await cmsClient.getRawDoc(collectionId, slug, {
@@ -1160,7 +1251,11 @@ function cmsPlugin(options) {
1160
1251
  if (sseData.sseBroadcast) {
1161
1252
  sseBroadcast = sseData.sseBroadcast;
1162
1253
  }
1163
- api(server, { getRenderer, checks: options.checks });
1254
+ api(server, {
1255
+ getRenderer,
1256
+ checks: options.checks,
1257
+ translations: options.translations
1258
+ });
1164
1259
  server.use("/cms", async (req, res) => {
1165
1260
  try {
1166
1261
  if (!req.user) {
package/dist/project.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Schema, C as Collection } from './schema-Bht24XmU.js';
1
+ import { S as Schema, C as Collection } from './schema-D7MOj-YC.js';
2
2
 
3
3
  /**
4
4
  * Loads various files or configurations from the project.
@@ -21,7 +21,14 @@ type StringField = CommonFieldProps & {
21
21
  type: 'string';
22
22
  default?: string;
23
23
  translate?: boolean;
24
- variant?: 'input' | 'textarea';
24
+ /**
25
+ * The UI variant to use for the string field.
26
+ * - `input`: A single-line text input (default).
27
+ * - `textarea`: A multi-line textarea.
28
+ * - `json`: A multi-line textarea that validates and auto-formats JSON on
29
+ * blur. The value is still stored as a string.
30
+ */
31
+ variant?: 'input' | 'textarea' | 'json';
25
32
  /** For textarea variant, the maximum number of rows of text to show. */
26
33
  maxRows?: number;
27
34
  /**