@blinkk/root-cms 3.0.1-alpha.1 → 3.0.1-beta.3

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
@@ -1,19 +1,28 @@
1
1
  import {
2
- ChatClient,
3
- summarizeDiff
4
- } from "./chunk-T5UK2H24.js";
5
- import {
6
- getServerVersion
7
- } from "./chunk-BBOESYH7.js";
2
+ ChatStore,
3
+ findModel,
4
+ generateAltText,
5
+ generateImage,
6
+ generatePublishMessage,
7
+ getAiConfig,
8
+ getServerVersion,
9
+ normalizeExecutionMode,
10
+ runChatStream,
11
+ runEditObjectStream,
12
+ serializeAiConfig,
13
+ summarizeDiff,
14
+ translateString
15
+ } from "./chunk-NXEXOHBD.js";
8
16
  import {
9
17
  SearchIndexService,
10
18
  runCronJobs
11
- } from "./chunk-UTSL2E2P.js";
19
+ } from "./chunk-TRM4MQHU.js";
12
20
  import {
13
21
  RootCMSClient,
14
22
  parseDocId,
15
23
  unmarshalData
16
- } from "./chunk-SNZ4S4IC.js";
24
+ } from "./chunk-F4SODS5S.js";
25
+ import "./chunk-CRK7N6RR.js";
17
26
  import "./chunk-MLKGABMK.js";
18
27
 
19
28
  // core/plugin.ts
@@ -110,6 +119,31 @@ function isRecord(value) {
110
119
  function isDocVersion(value) {
111
120
  return value === "draft" || value === "published";
112
121
  }
122
+ async function pipeWebResponse(webResponse, res) {
123
+ res.status(webResponse.status);
124
+ webResponse.headers.forEach((value, key) => {
125
+ res.setHeader(key, value);
126
+ });
127
+ res.setHeader("X-Accel-Buffering", "no");
128
+ if (!webResponse.body) {
129
+ res.end();
130
+ return;
131
+ }
132
+ const reader = webResponse.body.getReader();
133
+ try {
134
+ let done = false;
135
+ while (!done) {
136
+ const next = await reader.read();
137
+ done = next.done;
138
+ if (next.value) {
139
+ res.write(next.value);
140
+ }
141
+ }
142
+ } finally {
143
+ reader.releaseLock();
144
+ res.end();
145
+ }
146
+ }
113
147
  function api(server, options) {
114
148
  async function getCollectionSchema(req, collectionId) {
115
149
  if (req.viteServer) {
@@ -342,39 +376,6 @@ function api(server, options) {
342
376
  res.status(500).json({ success: false, error: "UNKNOWN" });
343
377
  }
344
378
  });
345
- server.use("/cms/api/ai.chat", async (req, res) => {
346
- if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
347
- res.status(400).json({ success: false, error: "BAD_REQUEST" });
348
- return;
349
- }
350
- if (!req.user?.email) {
351
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
352
- return;
353
- }
354
- const reqBody = req.body || {};
355
- if (!reqBody.prompt) {
356
- res.status(400).json({ success: false, error: "MISSING_PROMPT" });
357
- return;
358
- }
359
- const prompt = reqBody.prompt;
360
- try {
361
- const cmsClient = new RootCMSClient(req.rootConfig);
362
- const chatClient = new ChatClient(cmsClient, req.user.email);
363
- const chat = await chatClient.getOrCreateChat(reqBody.chatId);
364
- const apiResponse = {
365
- success: true,
366
- chatId: chat.id,
367
- response: await chat.sendPrompt(prompt, {
368
- mode: reqBody.options?.mode || "chat",
369
- editData: reqBody.options?.editData
370
- })
371
- };
372
- res.status(200).json(apiResponse);
373
- } catch (err) {
374
- console.error(err.stack || err);
375
- res.status(500).json({ success: false, error: "UNKNOWN" });
376
- }
377
- });
378
379
  server.use("/cms/api/ai.diff", async (req, res) => {
379
380
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
380
381
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
@@ -406,35 +407,14 @@ function api(server, options) {
406
407
  res.status(200).json({ success: true, summary: "" });
407
408
  return;
408
409
  }
409
- const summary = await summarizeDiff(cmsClient, {
410
+ const summary = await summarizeDiff(req.rootConfig, {
410
411
  before: diffPayload.before,
411
412
  after: diffPayload.after
412
413
  });
413
414
  res.status(200).json({ success: true, summary });
414
415
  } catch (err) {
415
416
  console.error(err.stack || err);
416
- res.status(500).json({ success: false, error: "UNKNOWN" });
417
- }
418
- });
419
- server.use("/cms/api/ai.list_chats", async (req, res) => {
420
- if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
421
- res.status(400).json({ success: false, error: "BAD_REQUEST" });
422
- return;
423
- }
424
- if (!req.user?.email) {
425
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
426
- return;
427
- }
428
- const reqBody = req.body || {};
429
- const limit = reqBody.limit;
430
- try {
431
- const cmsClient = new RootCMSClient(req.rootConfig);
432
- const chatClient = new ChatClient(cmsClient, req.user.email);
433
- const chats = await chatClient.listChats({ limit });
434
- res.status(200).json({ success: true, chats });
435
- } catch (err) {
436
- console.error(err.stack || err);
437
- res.status(500).json({ success: false, error: "UNKNOWN" });
417
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
438
418
  }
439
419
  });
440
420
  server.use(
@@ -456,13 +436,11 @@ function api(server, options) {
456
436
  return;
457
437
  }
458
438
  try {
459
- const cmsClient = new RootCMSClient(req.rootConfig);
460
- const { generateImage } = await import("./ai-OZY3JXDH.js");
461
- const imageUrl = await generateImage(cmsClient, {
439
+ const result = await generateImage(req.rootConfig, {
462
440
  prompt,
463
441
  aspectRatio
464
442
  });
465
- res.status(200).json({ success: true, image: imageUrl });
443
+ res.status(200).json({ success: true, image: result.imageUrl });
466
444
  } catch (err) {
467
445
  console.error(err.stack || err);
468
446
  res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
@@ -492,28 +470,261 @@ function api(server, options) {
492
470
  }
493
471
  try {
494
472
  const cmsClient = new RootCMSClient(req.rootConfig);
495
- const beforeVersion = "published";
496
- const afterVersion = "draft";
497
473
  const diffPayload = await buildDocDiffPayload(cmsClient, docId, {
498
- beforeVersion,
499
- afterVersion
474
+ beforeVersion: "published",
475
+ afterVersion: "draft"
500
476
  });
501
477
  if (!diffPayload.before && !diffPayload.after) {
502
478
  res.status(200).json({ success: true, message: "Initial version" });
503
479
  return;
504
480
  }
505
- const { generatePublishMessage } = await import("./ai-OZY3JXDH.js");
506
- const message = await generatePublishMessage(cmsClient, {
481
+ const message = await generatePublishMessage(req.rootConfig, {
507
482
  before: diffPayload.before,
508
483
  after: diffPayload.after
509
484
  });
510
485
  res.status(200).json({ success: true, message });
486
+ } catch (err) {
487
+ console.error(err.stack || err);
488
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
489
+ }
490
+ }
491
+ );
492
+ server.use(
493
+ "/cms/api/ai.generate_alt_text",
494
+ async (req, res) => {
495
+ if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
496
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
497
+ return;
498
+ }
499
+ if (!req.user?.email) {
500
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
501
+ return;
502
+ }
503
+ const reqBody = req.body || {};
504
+ const imageUrl = typeof reqBody.imageUrl === "string" ? reqBody.imageUrl.trim() : "";
505
+ if (!imageUrl) {
506
+ res.status(400).json({
507
+ success: false,
508
+ error: "MISSING_REQUIRED_FIELD",
509
+ field: "imageUrl"
510
+ });
511
+ return;
512
+ }
513
+ try {
514
+ const altText = await generateAltText(req.rootConfig, { imageUrl });
515
+ res.status(200).json({ success: true, altText });
516
+ } catch (err) {
517
+ console.error(err.stack || err);
518
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
519
+ }
520
+ }
521
+ );
522
+ server.use("/cms/api/ai.config", async (req, res) => {
523
+ if (!req.user?.email) {
524
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
525
+ return;
526
+ }
527
+ const aiConfig = getAiConfig(req.rootConfig);
528
+ if (!aiConfig) {
529
+ res.status(200).json({ success: true, enabled: false });
530
+ return;
531
+ }
532
+ res.status(200).json({ success: true, enabled: true, ...serializeAiConfig(aiConfig) });
533
+ });
534
+ server.use("/cms/api/ai.chat", async (req, res) => {
535
+ if (req.method !== "POST") {
536
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
537
+ return;
538
+ }
539
+ if (!req.user?.email) {
540
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
541
+ return;
542
+ }
543
+ const aiConfig = getAiConfig(req.rootConfig);
544
+ if (!aiConfig) {
545
+ res.status(404).json({ success: false, error: "AI_NOT_CONFIGURED" });
546
+ return;
547
+ }
548
+ const body = req.body || {};
549
+ const messages = body.messages || [];
550
+ if (!Array.isArray(messages) || messages.length === 0) {
551
+ res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
552
+ return;
553
+ }
554
+ const model = findModel(aiConfig, body.modelId);
555
+ if (!model) {
556
+ res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
557
+ return;
558
+ }
559
+ const executionMode = normalizeExecutionMode(body.executionMode);
560
+ const cmsClient = new RootCMSClient(req.rootConfig);
561
+ const store = new ChatStore(cmsClient, req.user.email);
562
+ const requestedChatId = typeof body.chatId === "string" ? body.chatId.trim() : "";
563
+ let chatId = "";
564
+ if (requestedChatId) {
565
+ const existing = await store.getChat(requestedChatId);
566
+ if (existing) {
567
+ chatId = existing.id;
568
+ } else {
569
+ const created = await store.createChat({
570
+ id: requestedChatId,
571
+ modelId: model.id
572
+ });
573
+ chatId = created.id;
574
+ }
575
+ } else {
576
+ const created = await store.createChat({ modelId: model.id });
577
+ chatId = created.id;
578
+ }
579
+ try {
580
+ const streamResponse = await runChatStream({
581
+ rootConfig: req.rootConfig,
582
+ cmsClient,
583
+ config: aiConfig,
584
+ model,
585
+ messages,
586
+ chatId,
587
+ user: req.user.email,
588
+ executionMode
589
+ });
590
+ streamResponse.headers.set("x-root-cms-chat-id", chatId);
591
+ await pipeWebResponse(streamResponse, res);
592
+ } catch (err) {
593
+ console.error(err.stack || err);
594
+ if (!res.headersSent) {
595
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
596
+ } else {
597
+ res.end();
598
+ }
599
+ }
600
+ });
601
+ server.use(
602
+ "/cms/api/ai.edit_object",
603
+ async (req, res) => {
604
+ if (req.method !== "POST") {
605
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
606
+ return;
607
+ }
608
+ if (!req.user?.email) {
609
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
610
+ return;
611
+ }
612
+ const aiConfig = getAiConfig(req.rootConfig);
613
+ if (!aiConfig) {
614
+ res.status(404).json({ success: false, error: "AI_NOT_CONFIGURED" });
615
+ return;
616
+ }
617
+ const body = req.body || {};
618
+ const messages = body.messages || [];
619
+ if (!Array.isArray(messages) || messages.length === 0) {
620
+ res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
621
+ return;
622
+ }
623
+ const model = findModel(aiConfig, body.modelId);
624
+ if (!model) {
625
+ res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
626
+ return;
627
+ }
628
+ try {
629
+ const streamResponse = await runEditObjectStream({
630
+ rootConfig: req.rootConfig,
631
+ config: aiConfig,
632
+ model,
633
+ messages,
634
+ editData: body.editData
635
+ });
636
+ await pipeWebResponse(streamResponse, res);
637
+ } catch (err) {
638
+ console.error(err.stack || err);
639
+ if (!res.headersSent) {
640
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
641
+ } else {
642
+ res.end();
643
+ }
644
+ }
645
+ }
646
+ );
647
+ server.use(
648
+ "/cms/api/ai.chats.list",
649
+ async (req, res) => {
650
+ if (!req.user?.email) {
651
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
652
+ return;
653
+ }
654
+ const cmsClient = new RootCMSClient(req.rootConfig);
655
+ const store = new ChatStore(cmsClient, req.user.email);
656
+ try {
657
+ const chats = await store.listChats({ limit: req.body?.limit });
658
+ res.status(200).json({
659
+ success: true,
660
+ chats: chats.map((c) => ({
661
+ id: c.id,
662
+ title: c.title,
663
+ modelId: c.modelId,
664
+ createdAt: c.createdAt.toMillis(),
665
+ modifiedAt: c.modifiedAt.toMillis()
666
+ }))
667
+ });
511
668
  } catch (err) {
512
669
  console.error(err.stack || err);
513
670
  res.status(500).json({ success: false, error: "UNKNOWN" });
514
671
  }
515
672
  }
516
673
  );
674
+ server.use(
675
+ "/cms/api/ai.chats.get",
676
+ async (req, res) => {
677
+ if (!req.user?.email) {
678
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
679
+ return;
680
+ }
681
+ const id = String(req.body?.id || "").trim();
682
+ if (!id) {
683
+ res.status(400).json({ success: false, error: "MISSING_ID" });
684
+ return;
685
+ }
686
+ const cmsClient = new RootCMSClient(req.rootConfig);
687
+ const store = new ChatStore(cmsClient, req.user.email);
688
+ const chat = await store.getChat(id);
689
+ if (!chat) {
690
+ res.status(404).json({ success: false, error: "NOT_FOUND" });
691
+ return;
692
+ }
693
+ res.status(200).json({
694
+ success: true,
695
+ chat: {
696
+ id: chat.id,
697
+ title: chat.title,
698
+ modelId: chat.modelId,
699
+ messages: chat.messages,
700
+ createdAt: chat.createdAt.toMillis(),
701
+ modifiedAt: chat.modifiedAt.toMillis()
702
+ }
703
+ });
704
+ }
705
+ );
706
+ server.use(
707
+ "/cms/api/ai.chats.delete",
708
+ async (req, res) => {
709
+ if (req.method !== "POST") {
710
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
711
+ return;
712
+ }
713
+ if (!req.user?.email) {
714
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
715
+ return;
716
+ }
717
+ const id = String(req.body?.id || "").trim();
718
+ if (!id) {
719
+ res.status(400).json({ success: false, error: "MISSING_ID" });
720
+ return;
721
+ }
722
+ const cmsClient = new RootCMSClient(req.rootConfig);
723
+ const store = new ChatStore(cmsClient, req.user.email);
724
+ await store.deleteChat(id);
725
+ res.status(200).json({ success: true });
726
+ }
727
+ );
517
728
  server.use("/cms/api/ai.translate", async (req, res) => {
518
729
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
519
730
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
@@ -533,9 +744,7 @@ function api(server, options) {
533
744
  return;
534
745
  }
535
746
  try {
536
- const cmsClient = new RootCMSClient(req.rootConfig);
537
- const { translateString } = await import("./ai-OZY3JXDH.js");
538
- const translations = await translateString(cmsClient, {
747
+ const translations = await translateString(req.rootConfig, {
539
748
  sourceText,
540
749
  targetLocales,
541
750
  description,
@@ -1378,7 +1587,7 @@ function cmsPlugin(options) {
1378
1587
  if (process.env.NODE_ENV === "development" && options.watch !== false) {
1379
1588
  plugin.onFileChange = (eventName, filepath) => {
1380
1589
  if (filepath.endsWith(".schema.ts")) {
1381
- import("./generate-types-MHWSSOWV.js").then((generateTypesModule) => {
1590
+ import("./generate-types-SPV7I3A5.js").then((generateTypesModule) => {
1382
1591
  const generateTypes = generateTypesModule.generateTypes;
1383
1592
  generateTypes();
1384
1593
  });
package/dist/project.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Schema, C as Collection } from './schema-rjBOZk-j.js';
1
+ import { S as Schema, C as Collection } from './schema-Db_xODoi.js';
2
2
 
3
3
  /**
4
4
  * Loads various files or configurations from the project.
@@ -27,5 +27,18 @@ declare function resolveOneOfPatterns(schemaObj: Schema): Schema;
27
27
  * `/collections/<id>.schema.ts`.
28
28
  */
29
29
  declare function getCollectionSchema(collectionId: string): Collection | null;
30
+ /**
31
+ * Converts all `oneof` field type definitions into a map keyed by the type
32
+ * name. The field definitions are replaced with an array of type names.
33
+ *
34
+ * String references (used for self-referencing schemas) are resolved from the
35
+ * project's schema modules. SchemaPatterns are resolved by matching file paths.
36
+ *
37
+ * Schemas pulled in via SchemaPattern or string-name reference are always
38
+ * deep-cloned before being walked. The walk rewrites `oneof` fields in place,
39
+ * and skipping the clone would mutate the shared entries in `SCHEMA_MODULES`
40
+ * and corrupt subsequent calls (e.g. when building multiple collections).
41
+ */
42
+ declare function convertOneOfTypes(collection: Collection, schemaModules?: Record<string, SchemaModule>): Collection;
30
43
 
31
- export { SCHEMA_MODULES, type SchemaModule, getCollectionSchema, getProjectSchemas, resolveOneOfPatterns };
44
+ export { SCHEMA_MODULES, type SchemaModule, convertOneOfTypes, getCollectionSchema, getProjectSchemas, resolveOneOfPatterns };
package/dist/project.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  SCHEMA_MODULES,
3
+ convertOneOfTypes,
3
4
  getCollectionSchema,
4
5
  getProjectSchemas,
5
6
  resolveOneOfPatterns
6
- } from "./chunk-R4LKO3EZ.js";
7
+ } from "./chunk-MAZA5B27.js";
7
8
  import "./chunk-MLKGABMK.js";
8
9
  export {
9
10
  SCHEMA_MODULES,
11
+ convertOneOfTypes,
10
12
  getCollectionSchema,
11
13
  getProjectSchemas,
12
14
  resolveOneOfPatterns
@@ -7,25 +7,36 @@ interface RichTextBlock {
7
7
  }
8
8
  interface RichTextData {
9
9
  [key: string]: any;
10
- blocks: any[];
10
+ blocks: RichTextBlock[];
11
11
  }
12
- type RichTextBlockComponent = FunctionalComponent<any>;
12
+ type RichTextComponent = FunctionalComponent<any>;
13
+ type RichTextBlockComponent = RichTextComponent;
14
+ type RichTextInlineComponent = RichTextComponent;
15
+ type RichTextComponentMap = Record<string, RichTextComponent>;
13
16
  interface RichTextContextProps {
14
- components?: Record<string, RichTextBlockComponent>;
17
+ /**
18
+ * Rich text components override for both inline and block level components.
19
+ */
20
+ components?: RichTextComponentMap;
21
+ /**
22
+ * Translator function override.
23
+ */
24
+ t?: (msg: string, params?: Record<string, string | number>) => string;
15
25
  }
16
26
  declare const RichTextContext: preact.Context<RichTextContextProps>;
17
27
  declare function useRichTextContext(): RichTextContextProps;
18
28
  interface RichTextProps {
19
- data: RichTextData;
29
+ data: RichTextData | undefined;
20
30
  components?: Record<string, RichTextBlockComponent>;
31
+ /** @deprecated */
32
+ translate?: boolean;
21
33
  }
22
34
  /** Renders data from the "richtext" field. */
23
- declare function RichText(props: RichTextProps): preact.JSX.Element;
35
+ declare function RichText(props: RichTextProps): preact.JSX.Element | null;
24
36
  declare namespace RichText {
37
+ var Block: (props: RichTextBlock) => preact.JSX.Element | null;
25
38
  var ParagraphBlock: (props: RichTextParagraphBlockProps) => preact.JSX.Element | null;
26
- var DelimiterBlock: () => preact.JSX.Element;
27
39
  var HeadingBlock: (props: RichTextHeadingBlockProps) => preact.JSX.Element | null;
28
- var QuoteBlock: (props: RichTextQuoteBlockProps) => preact.JSX.Element | null;
29
40
  var ListBlock: (props: RichTextListBlockProps) => preact.JSX.Element | null;
30
41
  var ImageBlock: (props: RichTextImageBlockProps) => preact.JSX.Element | null;
31
42
  var HtmlBlock: (props: RichTextHtmlBlockProps) => preact.JSX.Element | null;
@@ -35,12 +46,9 @@ interface RichTextParagraphBlockProps {
35
46
  type: 'paragraph';
36
47
  data?: {
37
48
  text?: string;
49
+ components?: Record<string, any>;
38
50
  };
39
51
  }
40
- interface RichTextDelimiterBlockProps {
41
- type: 'delimiter';
42
- data?: {};
43
- }
44
52
  interface RichTextHeadingBlockProps {
45
53
  type: 'heading';
46
54
  data?: {
@@ -48,15 +56,10 @@ interface RichTextHeadingBlockProps {
48
56
  text?: string;
49
57
  };
50
58
  }
51
- interface RichTextQuoteBlockProps {
52
- type: 'quote';
53
- data?: {
54
- text?: string;
55
- };
56
- }
57
59
  interface ListItem {
58
60
  content?: string;
59
61
  items?: ListItem[];
62
+ components?: Record<string, any>;
60
63
  }
61
64
  interface RichTextListBlockProps {
62
65
  type: 'orderedList' | 'unorderedList';
@@ -68,6 +71,8 @@ interface RichTextListBlockProps {
68
71
  interface RichTextImageBlockProps {
69
72
  type: 'image';
70
73
  data?: {
74
+ /** The caption entered into the CMS Image field. */
75
+ caption?: string;
71
76
  file?: {
72
77
  url: string;
73
78
  width: string | number;
@@ -93,5 +98,7 @@ interface RichTextTableBlockProps {
93
98
  }>;
94
99
  };
95
100
  }
101
+ /** Returns whether the rich text value is truthy. */
102
+ declare function testContent(data: RichTextData): boolean;
96
103
 
97
- export { RichText, type RichTextBlock, type RichTextBlockComponent, RichTextContext, type RichTextContextProps, type RichTextData, type RichTextDelimiterBlockProps, type RichTextHeadingBlockProps, type RichTextHtmlBlockProps, type RichTextImageBlockProps, type RichTextListBlockProps, type RichTextParagraphBlockProps, type RichTextProps, type RichTextQuoteBlockProps, type RichTextTableBlockProps, useRichTextContext };
104
+ export { RichText, type RichTextBlock, type RichTextBlockComponent, type RichTextComponent, type RichTextComponentMap, RichTextContext, type RichTextContextProps, type RichTextData, type RichTextHeadingBlockProps, type RichTextHtmlBlockProps, type RichTextImageBlockProps, type RichTextInlineComponent, type RichTextListBlockProps, type RichTextParagraphBlockProps, type RichTextProps, type RichTextTableBlockProps, testContent, useRichTextContext };