diff --git a/.changeset/feat-markdown-textblock.md b/.changeset/feat-markdown-textblock.md
new file mode 100644
index 00000000..68b57c0a
--- /dev/null
+++ b/.changeset/feat-markdown-textblock.md
@@ -0,0 +1,6 @@
+---
+"@stackwright/core": minor
+"@stackwright/types": minor
+---
+
+feat(core,types): add format: markdown to TextBlock for CommonMark rendering via micromark
diff --git a/AGENTS.md b/AGENTS.md
index 6260ca4a..e48cdeff 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -167,8 +167,8 @@ The YAML key is the key used inside `content_items` entries. All types inherit `
| Type | Fields |
|---|---|
-| `TextBlock` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string) |
-| `ButtonContent` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `variant` (`text` | `outlined` | `contained`), `variantSize`? (`small` | `medium` | `large`), `href`? (string), `action`? (string), `icon`? (MediaItem), `alignment`? (`left` | `center` | `right`), `bgColor`? (string) |
+| `TextBlock` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `format`? (`plain` | `markdown`) |
+| `ButtonContent` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `format`? (`plain` | `markdown`), `variant` (`text` | `outlined` | `contained`), `variantSize`? (`small` | `medium` | `large`), `href`? (string), `action`? (string), `icon`? (MediaItem), `alignment`? (`left` | `center` | `right`), `bgColor`? (string) |
| `MediaItem` | Discriminated union: `type: "media"` \| `type: "icon"` \| `type: "image"` \| `type: "video"`. `type` field is required and acts as discriminator. |
| `ImageContent` | `label` (string), `color`? (string), `background`? (string), `src` (string), `alt`? (string), `height`? (number | string), `width`? (number | string), `style`? (`contained` | `overflow`), `type` ("image"), `aspect_ratio`? (number) |
| `IconContent` | `label` (string), `color`? (string), `background`? (string), `src` (string), `alt`? (string), `height`? (number | string), `width`? (number | string), `style`? (`contained` | `overflow`), `type` ("icon"), `size`? (number | TypographyVariant) |
diff --git a/examples/stackwright-docs/AGENTS.md b/examples/stackwright-docs/AGENTS.md
index 27c691bb..18e8ccfb 100644
--- a/examples/stackwright-docs/AGENTS.md
+++ b/examples/stackwright-docs/AGENTS.md
@@ -41,8 +41,8 @@ The YAML key is the key used inside `content_items` entries. All types inherit `
| Type | Fields |
|---|---|
-| `TextBlock` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string) |
-| `ButtonContent` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `variant` (`text` | `outlined` | `contained`), `variantSize`? (`small` | `medium` | `large`), `href`? (string), `action`? (string), `icon`? (MediaItem), `alignment`? (`left` | `center` | `right`), `bgColor`? (string) |
+| `TextBlock` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `format`? (`plain` | `markdown`) |
+| `ButtonContent` | `text` (string), `textSize` (TypographyVariant), `textColor`? (string), `format`? (`plain` | `markdown`), `variant` (`text` | `outlined` | `contained`), `variantSize`? (`small` | `medium` | `large`), `href`? (string), `action`? (string), `icon`? (MediaItem), `alignment`? (`left` | `center` | `right`), `bgColor`? (string) |
| `MediaItem` | Discriminated union: `type: "media"` \| `type: "icon"` \| `type: "image"` \| `type: "video"`. `type` field is required and acts as discriminator. |
| `ImageContent` | `label` (string), `color`? (string), `background`? (string), `src` (string), `alt`? (string), `height`? (number | string), `width`? (number | string), `style`? (`contained` | `overflow`), `type` ("image"), `aspect_ratio`? (number) |
| `IconContent` | `label` (string), `color`? (string), `background`? (string), `src` (string), `alt`? (string), `height`? (number | string), `width`? (number | string), `style`? (`contained` | `overflow`), `type` ("icon"), `size`? (number | TypographyVariant) |
diff --git a/packages/core/package.json b/packages/core/package.json
index 2990d514..01406717 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -63,6 +63,7 @@
"@stackwright/themes": "workspace:*",
"@stackwright/types": "workspace:*",
"js-yaml": "^4.1.0",
+ "micromark": "^4.0.1",
"prismjs": "^1.30.0",
"uuid": "^13.0.0",
"zod": "^4.4.3"
diff --git a/packages/core/src/components/base/TextGrid.tsx b/packages/core/src/components/base/TextGrid.tsx
index e818eac0..e2e57c13 100644
--- a/packages/core/src/components/base/TextGrid.tsx
+++ b/packages/core/src/components/base/TextGrid.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { micromark } from 'micromark';
import { TextBlock } from '@stackwright/types';
import { v4 as uuidv4 } from 'uuid';
import { useSafeTheme } from '../../hooks/useSafeTheme';
@@ -13,6 +14,16 @@ interface TextGridProps {
};
}
+/**
+ * Renders a TextBlock using CommonMark via micromark.
+ * micromark does NOT pass through raw HTML by default (allowDangerousHtml: false),
+ * so the output is XSS-safe by construction — no sanitization library needed.
+ */
+function renderMarkdown(text: string, color?: string): React.ReactNode {
+ const html = micromark(text);
+ return
;
+}
+
/**
* Renders a string with inline markdown: **bold**, *italic*, `code`.
* Returns an array of React nodes safe to embed in JSX.
@@ -85,52 +96,64 @@ export function TextGrid({ content, config }: TextGridProps) {
return (
<>
- {content.map((textItem) => (
-
- {textItem.text
- .split('\n')
- .filter((line) => line.trim() !== '')
- .map((line) => {
- const lineBlock: TextBlock = {
- ...textItem,
- text: line,
- };
- return (
-
- {startsWithBullet(line) && listIcon && (
-
- {listIcon}
-
- )}
+ {content.map((textItem) => {
+ // Markdown mode: pass the entire text to micromark, skip line-splitting and special chars
+ if (textItem.format === 'markdown') {
+ return (
+
+ {renderMarkdown(textItem.text, textItem.textColor ?? theme.colors.text)}
+
+ );
+ }
- {startsWithListNumber(line) && (
-
- {listNumber++}.
-
- )}
+ // Plain mode (default): existing line-splitting + bullet/list/special char logic
+ return (
+
+ {textItem.text
+ .split('\n')
+ .filter((line) => line.trim() !== '')
+ .map((line) => {
+ const lineBlock: TextBlock = {
+ ...textItem,
+ text: line,
+ };
+ return (
+
+ {startsWithBullet(line) && listIcon && (
+
+ {listIcon}
+
+ )}
+
+ {startsWithListNumber(line) && (
+
+ {listNumber++}.
+
+ )}
- {renderText(lineBlock)}
-
- );
- })}
-
- ))}
+ {renderText(lineBlock)}
+
+ );
+ })}
+
+ );
+ })}
>
);
}
diff --git a/packages/core/test/components/text-block.test.tsx b/packages/core/test/components/text-block.test.tsx
index bcb8b8eb..b061736a 100644
--- a/packages/core/test/components/text-block.test.tsx
+++ b/packages/core/test/components/text-block.test.tsx
@@ -345,3 +345,106 @@ describe('TextBlockGrid', () => {
expect(screen.getByText('After divider')).toBeInTheDocument();
});
});
+
+describe('TextGrid — markdown format', () => {
+ it('renders bold text when format is markdown', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('strong')).toBeTruthy();
+ expect(container.querySelector('strong')?.textContent).toBe('Bold text');
+ });
+
+ it('renders italic text when format is markdown', () => {
+ const { container } = render(
+
+ );
+ expect(container.querySelector('em')).toBeTruthy();
+ expect(container.querySelector('em')?.textContent).toBe('Italic text');
+ });
+
+ it('renders a link when format is markdown', () => {
+ const { container } = render(
+
+ );
+ const link = container.querySelector('a');
+ expect(link).toBeTruthy();
+ expect(link?.getAttribute('href')).toBe('https://example.com');
+ expect(link?.textContent).toBe('Visit site');
+ });
+
+ it('does NOT render raw HTML (XSS-safe by construction)', () => {
+ const { container } = render(
+ alert("xss") safe text',
+ textSize: 'body1',
+ format: 'markdown',
+ },
+ ]}
+ />
+ );
+ // micromark strips raw HTML by default — no script tag should appear
+ expect(container.querySelector('script')).toBeNull();
+ expect(container.textContent).toContain('safe text');
+ });
+
+ it('renders a markdown list when format is markdown', () => {
+ const { container } = render(
+
+ );
+ const listItems = container.querySelectorAll('li');
+ expect(listItems.length).toBe(3);
+ expect(listItems[0].textContent).toBe('Item one');
+ expect(listItems[1].textContent).toBe('Item two');
+ });
+
+ it('backward compat: plain format (no format field) unchanged behavior', () => {
+ const { container } = render(
+
+ );
+ // Plain mode uses renderInlineMarkdown — **text** is rendered as via React children
+ // (not via dangerouslySetInnerHTML). Existing behavior is preserved.
+ expect(container.querySelector('strong')).toBeTruthy();
+ expect(container.querySelector('strong')?.textContent).toBe('Not bold');
+ });
+
+ it('backward compat: explicit format: plain unchanged behavior', () => {
+ render(
+
+ );
+ expect(screen.getByText('Regular text')).toBeInTheDocument();
+ });
+});
diff --git a/packages/types/schemas/content-schema.json b/packages/types/schemas/content-schema.json
index 278bfb33..46f78400 100644
--- a/packages/types/schemas/content-schema.json
+++ b/packages/types/schemas/content-schema.json
@@ -111,7 +111,7 @@
"content_items": {
"type": "array",
"items": {
- "$ref": "#/definitions/__schema25"
+ "$ref": "#/definitions/__schema26"
}
},
"list_icon": {
@@ -504,6 +504,9 @@
"textColor": {
"$ref": "#/definitions/__schema24"
},
+ "format": {
+ "$ref": "#/definitions/__schema25"
+ },
"variant": {
"type": "string",
"enum": [
@@ -559,6 +562,13 @@
"type": "string"
},
"__schema25": {
+ "type": "string",
+ "enum": [
+ "plain",
+ "markdown"
+ ]
+ },
+ "__schema26": {
"anyOf": [
{
"type": "object",
@@ -640,12 +650,12 @@
"const": "main"
},
"heading": {
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
},
"textBlocks": {
"type": "array",
"items": {
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
},
"media": {
@@ -697,12 +707,12 @@
"const": "tabbed_content"
},
"heading": {
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
},
"tabs": {
"type": "array",
"items": {
- "$ref": "#/definitions/__schema25"
+ "$ref": "#/definitions/__schema26"
}
}
},
@@ -727,19 +737,19 @@
"$ref": "#/definitions/__schema21"
},
"src": {
- "$ref": "#/definitions/__schema27"
+ "$ref": "#/definitions/__schema28"
},
"alt": {
- "$ref": "#/definitions/__schema28"
+ "$ref": "#/definitions/__schema29"
},
"height": {
- "$ref": "#/definitions/__schema29"
+ "$ref": "#/definitions/__schema30"
},
"width": {
- "$ref": "#/definitions/__schema30"
+ "$ref": "#/definitions/__schema31"
},
"style": {
- "$ref": "#/definitions/__schema31"
+ "$ref": "#/definitions/__schema32"
},
"type": {
"type": "string",
@@ -772,7 +782,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -838,7 +848,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -857,19 +867,19 @@
"$ref": "#/definitions/__schema21"
},
"src": {
- "$ref": "#/definitions/__schema27"
+ "$ref": "#/definitions/__schema28"
},
"alt": {
- "$ref": "#/definitions/__schema28"
+ "$ref": "#/definitions/__schema29"
},
"height": {
- "$ref": "#/definitions/__schema29"
+ "$ref": "#/definitions/__schema30"
},
"width": {
- "$ref": "#/definitions/__schema30"
+ "$ref": "#/definitions/__schema31"
},
"style": {
- "$ref": "#/definitions/__schema31"
+ "$ref": "#/definitions/__schema32"
},
"type": {
"type": "string",
@@ -954,7 +964,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1014,7 +1024,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1080,7 +1090,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1130,7 +1140,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1246,7 +1256,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1295,7 +1305,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1390,14 +1400,14 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
"textBlocks": {
"type": "array",
"items": {
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
},
"buttons": {
@@ -1433,7 +1443,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1441,7 +1451,7 @@
"minItems": 1,
"type": "array",
"items": {
- "$ref": "#/definitions/__schema32"
+ "$ref": "#/definitions/__schema33"
}
},
"gap": {
@@ -1513,7 +1523,7 @@
"heading": {
"allOf": [
{
- "$ref": "#/definitions/__schema26"
+ "$ref": "#/definitions/__schema27"
}
]
},
@@ -1546,19 +1556,19 @@
"$ref": "#/definitions/__schema21"
},
"src": {
- "$ref": "#/definitions/__schema27"
+ "$ref": "#/definitions/__schema28"
},
"alt": {
- "$ref": "#/definitions/__schema28"
+ "$ref": "#/definitions/__schema29"
},
"height": {
- "$ref": "#/definitions/__schema29"
+ "$ref": "#/definitions/__schema30"
},
"width": {
- "$ref": "#/definitions/__schema30"
+ "$ref": "#/definitions/__schema31"
},
"style": {
- "$ref": "#/definitions/__schema31"
+ "$ref": "#/definitions/__schema32"
},
"type": {
"type": "string",
@@ -1752,7 +1762,7 @@
}
]
},
- "__schema26": {
+ "__schema27": {
"type": "object",
"properties": {
"text": {
@@ -1763,6 +1773,9 @@
},
"textColor": {
"$ref": "#/definitions/__schema24"
+ },
+ "format": {
+ "$ref": "#/definitions/__schema25"
}
},
"required": [
@@ -1771,13 +1784,13 @@
],
"additionalProperties": false
},
- "__schema27": {
- "type": "string"
- },
"__schema28": {
"type": "string"
},
"__schema29": {
+ "type": "string"
+ },
+ "__schema30": {
"anyOf": [
{
"type": "number"
@@ -1787,7 +1800,7 @@
}
]
},
- "__schema30": {
+ "__schema31": {
"anyOf": [
{
"type": "number"
@@ -1797,14 +1810,14 @@
}
]
},
- "__schema31": {
+ "__schema32": {
"allOf": [
{
"$ref": "#/definitions/__schema6"
}
]
},
- "__schema32": {
+ "__schema33": {
"type": "object",
"properties": {
"width": {
@@ -1813,7 +1826,7 @@
"content_items": {
"type": "array",
"items": {
- "$ref": "#/definitions/__schema25"
+ "$ref": "#/definitions/__schema26"
}
}
},
diff --git a/packages/types/schemas/site-config-schema.json b/packages/types/schemas/site-config-schema.json
index ecbaf6de..c2f8f58f 100644
--- a/packages/types/schemas/site-config-schema.json
+++ b/packages/types/schemas/site-config-schema.json
@@ -317,6 +317,13 @@
"textColor": {
"type": "string"
},
+ "format": {
+ "type": "string",
+ "enum": [
+ "plain",
+ "markdown"
+ ]
+ },
"variant": {
"type": "string",
"enum": [
diff --git a/packages/types/src/types/base.ts b/packages/types/src/types/base.ts
index 61406f23..62af9c55 100644
--- a/packages/types/src/types/base.ts
+++ b/packages/types/src/types/base.ts
@@ -12,6 +12,7 @@ export const textBlockSchema = z.object({
text: z.string(),
textSize: typographyVariantSchema,
textColor: z.string().optional(),
+ format: z.enum(['plain', 'markdown']).optional(), // default: 'plain' for backward compat
});
export const buttonContentSchema = textBlockSchema.extend({
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 31d44698..5568f4c5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -296,6 +296,9 @@ importers:
js-yaml:
specifier: ^4.1.0
version: 4.1.1
+ micromark:
+ specifier: ^4.0.1
+ version: 4.0.2
prismjs:
specifier: ^1.30.0
version: 1.30.0
@@ -2955,6 +2958,9 @@ packages:
'@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
+ '@types/debug@4.1.13':
+ resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+
'@types/deep-eql@4.0.2':
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
@@ -2982,6 +2988,9 @@ packages:
'@types/jsonfile@6.1.4':
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+ '@types/ms@2.1.0':
+ resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
+
'@types/mysql@2.15.26':
resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==}
@@ -3699,6 +3708,9 @@ packages:
resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+ character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+
chardet@2.1.1:
resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==}
@@ -3896,6 +3908,9 @@ packages:
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+ decode-named-character-reference@1.3.0:
+ resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
@@ -3931,6 +3946,9 @@ packages:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
+ devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+
devtools-protocol@0.0.1581282:
resolution: {integrity: sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==}
@@ -5119,6 +5137,66 @@ packages:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
+ micromark-core-commonmark@2.0.3:
+ resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
+
+ micromark-factory-destination@2.0.1:
+ resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==}
+
+ micromark-factory-label@2.0.1:
+ resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==}
+
+ micromark-factory-space@2.0.1:
+ resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==}
+
+ micromark-factory-title@2.0.1:
+ resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==}
+
+ micromark-factory-whitespace@2.0.1:
+ resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==}
+
+ micromark-util-character@2.1.1:
+ resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==}
+
+ micromark-util-chunked@2.0.1:
+ resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==}
+
+ micromark-util-classify-character@2.0.1:
+ resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==}
+
+ micromark-util-combine-extensions@2.0.1:
+ resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==}
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==}
+
+ micromark-util-encode@2.0.1:
+ resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==}
+
+ micromark-util-html-tag-name@2.0.1:
+ resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==}
+
+ micromark-util-normalize-identifier@2.0.1:
+ resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==}
+
+ micromark-util-resolve-all@2.0.1:
+ resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==}
+
+ micromark-util-sanitize-uri@2.0.1:
+ resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==}
+
+ micromark-util-subtokenize@2.1.0:
+ resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==}
+
+ micromark-util-symbol@2.0.1:
+ resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==}
+
+ micromark-util-types@2.0.2:
+ resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==}
+
+ micromark@4.0.2:
+ resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==}
+
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
@@ -8737,6 +8815,10 @@ snapshots:
dependencies:
'@types/node': 24.12.3
+ '@types/debug@4.1.13':
+ dependencies:
+ '@types/ms': 2.1.0
+
'@types/deep-eql@4.0.2': {}
'@types/estree@1.0.8': {}
@@ -8760,6 +8842,8 @@ snapshots:
dependencies:
'@types/node': 24.12.3
+ '@types/ms@2.1.0': {}
+
'@types/mysql@2.15.26':
dependencies:
'@types/node': 24.12.3
@@ -9554,6 +9638,8 @@ snapshots:
chalk@5.6.2: {}
+ character-entities@2.0.2: {}
+
chardet@2.1.1: {}
chokidar@4.0.3:
@@ -9737,6 +9823,10 @@ snapshots:
decimal.js@10.6.0: {}
+ decode-named-character-reference@1.3.0:
+ dependencies:
+ character-entities: 2.0.2
+
deep-is@0.1.4: {}
define-data-property@1.1.4:
@@ -9767,6 +9857,10 @@ snapshots:
detect-libc@2.1.2: {}
+ devlop@1.1.0:
+ dependencies:
+ dequal: 2.0.3
+
devtools-protocol@0.0.1581282: {}
devtools-protocol@0.0.1608973: {}
@@ -11159,6 +11253,132 @@ snapshots:
merge2@1.4.1: {}
+ micromark-core-commonmark@2.0.3:
+ dependencies:
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-factory-destination: 2.0.1
+ micromark-factory-label: 2.0.1
+ micromark-factory-space: 2.0.1
+ micromark-factory-title: 2.0.1
+ micromark-factory-whitespace: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-classify-character: 2.0.1
+ micromark-util-html-tag-name: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-destination@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-label@2.0.1:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-space@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-title@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-factory-whitespace@2.0.1:
+ dependencies:
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-character@2.1.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-chunked@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-classify-character@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-combine-extensions@2.0.1:
+ dependencies:
+ micromark-util-chunked: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-decode-numeric-character-reference@2.0.2:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-encode@2.0.1: {}
+
+ micromark-util-html-tag-name@2.0.1: {}
+
+ micromark-util-normalize-identifier@2.0.1:
+ dependencies:
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-resolve-all@2.0.1:
+ dependencies:
+ micromark-util-types: 2.0.2
+
+ micromark-util-sanitize-uri@2.0.1:
+ dependencies:
+ micromark-util-character: 2.1.1
+ micromark-util-encode: 2.0.1
+ micromark-util-symbol: 2.0.1
+
+ micromark-util-subtokenize@2.1.0:
+ dependencies:
+ devlop: 1.1.0
+ micromark-util-chunked: 2.0.1
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+
+ micromark-util-symbol@2.0.1: {}
+
+ micromark-util-types@2.0.2: {}
+
+ micromark@4.0.2:
+ dependencies:
+ '@types/debug': 4.1.13
+ debug: 4.4.3
+ decode-named-character-reference: 1.3.0
+ devlop: 1.1.0
+ micromark-core-commonmark: 2.0.3
+ micromark-factory-space: 2.0.1
+ micromark-util-character: 2.1.1
+ micromark-util-chunked: 2.0.1
+ micromark-util-combine-extensions: 2.0.1
+ micromark-util-decode-numeric-character-reference: 2.0.2
+ micromark-util-encode: 2.0.1
+ micromark-util-normalize-identifier: 2.0.1
+ micromark-util-resolve-all: 2.0.1
+ micromark-util-sanitize-uri: 2.0.1
+ micromark-util-subtokenize: 2.1.0
+ micromark-util-symbol: 2.0.1
+ micromark-util-types: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
micromatch@4.0.8:
dependencies:
braces: 3.0.3