@vxrn/mdx 1.1.321

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/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@vxrn/mdx",
3
+ "version": "1.1.321",
4
+ "types": "./types/index.d.ts",
5
+ "main": "dist/index.js",
6
+ "type": "commonjs",
7
+ "files": [
8
+ "src",
9
+ "types",
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tamagui-build --bundle --bundle-modules",
14
+ "watch": "yarn build --watch",
15
+ "lint": "biome check src",
16
+ "lint:fix": "biome check --write src",
17
+ "clean": "tamagui-build clean",
18
+ "clean:build": "tamagui-build clean:build"
19
+ },
20
+ "exports": {
21
+ "./package.json": "./package.json",
22
+ ".": {
23
+ "types": "./types/index.d.ts",
24
+ "// 👋 note - import === require here, mdx-bundler is cjs only must await import() this package": "./",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.js",
27
+ "react-native": "./dist/index.native.js",
28
+ "default": "./dist/index.native.js"
29
+ }
30
+ },
31
+ "dependencies": {
32
+ "compare-versions": "^4.1.3",
33
+ "gray-matter": "^4.0.3",
34
+ "hast-util-to-html": "^8.0.3",
35
+ "hast-util-to-string": "^2.0.0",
36
+ "mdx-bundler": "^10.0.2",
37
+ "parse-numeric-range": "^1.3.0",
38
+ "reading-time": "1.3.0",
39
+ "refractor": "^4.7.0",
40
+ "rehype": "^12.0.1",
41
+ "rehype-autolink-headings": "^6.1.1",
42
+ "rehype-parse": "^8.0.4",
43
+ "rehype-slug": "^5.0.1",
44
+ "shiki": "1.3.0",
45
+ "unified": "^10.1.2",
46
+ "unist-util-visit": "^2.0.3"
47
+ },
48
+ "devDependencies": {
49
+ "@tamagui/build": "1.116.4"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
54
+ }
@@ -0,0 +1,25 @@
1
+ import glob from 'glob'
2
+ import matter from 'gray-matter'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import readingTime from 'reading-time'
6
+ import { getHeadings } from './getHeadings'
7
+ import type { Frontmatter } from './types'
8
+
9
+ // the front matter and content of all mdx files based on `docsPaths`
10
+ export const getAllFrontmatter = (fromPath: string): Frontmatter[] => {
11
+ const paths = glob.sync(`${fromPath}/**/*.mdx`)
12
+ return paths
13
+ .map((filePath) => {
14
+ const source = fs.readFileSync(path.join(filePath), 'utf8')
15
+ const { data, content } = matter(source)
16
+ const slug = filePath.replace(`${fromPath.replaceAll('\\', '/')}/`, '').replace('.mdx', '')
17
+ return {
18
+ ...data,
19
+ slug,
20
+ headings: getHeadings(source),
21
+ readingTime: readingTime(content),
22
+ } as Frontmatter
23
+ })
24
+ .sort((a, b) => Number(new Date(b.publishedAt || '')) - Number(new Date(a.publishedAt || '')))
25
+ }
@@ -0,0 +1,11 @@
1
+ const getTitle = (source: string) => source.replace(/^\#+\s+/, '').replace(/\<.*\>/, ' ')
2
+
3
+ export const getHeadings = (source: string) =>
4
+ source
5
+ .split('\n')
6
+ .filter((x) => x.startsWith('#'))
7
+ .map((x) => ({
8
+ title: getTitle(x),
9
+ priority: x.trim().split(' ')[0].length,
10
+ id: `#${getTitle(x).replace(/\s+/, '-').toLowerCase()}`,
11
+ }))
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import compareVersions from 'compare-versions'
4
+ import { bundleMDX } from 'mdx-bundler'
5
+ import readingTime from 'reading-time'
6
+ import type { Frontmatter } from './types'
7
+ import { rehypeHighlightCode } from './rehypeHighlightCode'
8
+ import rehypeMetaAttribute from './rehypeMetaAttribute'
9
+ import rehypeAutolinkHeadings from 'rehype-autolink-headings'
10
+ import rehypeSlug from 'rehype-slug'
11
+ import { getHeadings } from './getHeadings'
12
+
13
+ export const getMDXBySlug = async (
14
+ basePath: string,
15
+ slug: string
16
+ ): Promise<{ frontmatter: Frontmatter; code: string }> => {
17
+ let mdxPath = slug
18
+
19
+ // if no version given, find it
20
+ if (!slug.includes('.') && basePath.includes('components')) {
21
+ const versions = getAllVersionsFromPath(path.join(basePath, slug))
22
+ mdxPath += `/${versions[0]}`
23
+ }
24
+
25
+ const filePath = path.join(basePath, `${mdxPath}.mdx`)
26
+ const source = fs.readFileSync(filePath, 'utf8')
27
+ const { frontmatter, code } = await getMDX(source)
28
+ return {
29
+ frontmatter: {
30
+ ...frontmatter,
31
+ headings: getHeadings(source),
32
+ readingTime: readingTime(code),
33
+ } as Frontmatter,
34
+ code,
35
+ }
36
+ }
37
+
38
+ export async function getMDX(source: string) {
39
+ return await bundleMDX({
40
+ source,
41
+ mdxOptions(options) {
42
+ const plugins = [
43
+ ...(options.rehypePlugins ?? []),
44
+ rehypeMetaAttribute,
45
+ rehypeHighlightCode,
46
+ rehypeAutolinkHeadings,
47
+ rehypeSlug,
48
+ ]
49
+ options.rehypePlugins = plugins as any
50
+ return options
51
+ },
52
+ })
53
+ }
54
+
55
+ export function getAllVersionsFromPath(fromPath: string): string[] {
56
+ if (!fs.existsSync(fromPath)) return []
57
+ return fs
58
+ .readdirSync(fromPath)
59
+ .map((fileName) => fileName.replace('.mdx', ''))
60
+ .sort(compareVersions)
61
+ .reverse()
62
+ }
@@ -0,0 +1,27 @@
1
+ import { toHtml } from 'hast-util-to-html'
2
+ import rangeParser from 'parse-numeric-range'
3
+ import { refractor } from 'refractor'
4
+ import css from 'refractor/lang/css.js'
5
+ import tsx from 'refractor/lang/tsx.js'
6
+
7
+ import { rehypeHighlightLine } from './rehypeLine'
8
+ import { rehypeHighlightWord } from './rehypeWord'
9
+
10
+ let highlighter: (source: string, language: 'tsx' | 'css' | string, line?: string) => string
11
+
12
+ export function createCodeHighlighter() {
13
+ if (highlighter) return highlighter
14
+
15
+ refractor.register(tsx)
16
+ refractor.register(css)
17
+
18
+ highlighter = function codeToHtml(source, language, line = '0') {
19
+ let result: any = refractor.highlight(source, language)
20
+ result = rehypeHighlightLine(result, rangeParser(line))
21
+ result = rehypeHighlightWord(result)
22
+ result = toHtml(result)
23
+ return result as string
24
+ }
25
+
26
+ return highlighter
27
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // export { rehypeHighlightCode } from './rehypeHighlightCode'
2
+ export { getAllFrontmatter } from './getAllFrontmatter'
3
+ export { getMDXBySlug, getAllVersionsFromPath } from './getMDXBySlug'
4
+ export type { Frontmatter } from './types'
5
+ export { createCodeHighlighter } from './highlightCode'
@@ -0,0 +1,40 @@
1
+ // Inspired by https://github.com/j0lv3r4/mdx-prism
2
+
3
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
4
+ import { toString } from 'hast-util-to-string'
5
+ import rangeParser from 'parse-numeric-range'
6
+ import { refractor } from 'refractor'
7
+ import tsx from 'refractor/lang/tsx.js'
8
+ import visit from 'unist-util-visit'
9
+
10
+ import { rehypeHighlightLine } from './rehypeLine'
11
+ import { rehypeHighlightWord } from './rehypeWord'
12
+
13
+ refractor.register(tsx)
14
+
15
+ export const rehypeHighlightCode = (options = {}) => {
16
+ return (tree) => {
17
+ visit(tree, 'element', visitor)
18
+ }
19
+
20
+ function visitor(node, index, parent) {
21
+ if (
22
+ !parent ||
23
+ parent.tagName !== 'pre' ||
24
+ node.tagName !== 'code' ||
25
+ !node.properties.className
26
+ ) {
27
+ return
28
+ }
29
+
30
+ const [_, lang] = node.properties.className[0].split('-')
31
+ const codeString = toString(node)
32
+ let result = refractor.highlight(codeString, lang)
33
+
34
+ const linesToHighlight = rangeParser(node.properties.line || '0')
35
+
36
+ result = rehypeHighlightLine(result, linesToHighlight)
37
+
38
+ node.children = rehypeHighlightWord(result)
39
+ }
40
+ }
@@ -0,0 +1,112 @@
1
+ // Inspired by https://github.com/j0lv3r4/mdx-prism
2
+
3
+ import { toHtml } from 'hast-util-to-html'
4
+ import parse from 'rehype-parse'
5
+ import { unified } from 'unified'
6
+
7
+ const lineNumberify = function lineNumberify(ast, lineNum = 1) {
8
+ let lineNumber = lineNum
9
+ return ast.reduce(
10
+ (result, node) => {
11
+ if (node.type === 'text') {
12
+ if (node.value.indexOf('\n') === -1) {
13
+ node.lineNumber = lineNumber
14
+ result.nodes.push(node)
15
+ return result
16
+ }
17
+
18
+ const lines = node.value.split('\n')
19
+ for (let i = 0; i < lines.length; i++) {
20
+ if (i !== 0) ++lineNumber
21
+ if (i === lines.length - 1 && lines[i].length === 0) continue
22
+ result.nodes.push({
23
+ type: 'text',
24
+ value: i === lines.length - 1 ? lines[i] : `${lines[i]}\n`,
25
+ lineNumber: lineNumber,
26
+ })
27
+ }
28
+
29
+ result.lineNumber = lineNumber
30
+ return result
31
+ }
32
+
33
+ if (node.children) {
34
+ node.lineNumber = lineNumber
35
+ const processed = lineNumberify(node.children, lineNumber)
36
+ node.children = processed.nodes
37
+ result.lineNumber = processed.lineNumber
38
+ result.nodes.push(node)
39
+ return result
40
+ }
41
+
42
+ result.nodes.push(node)
43
+ return result
44
+ },
45
+ { nodes: [], lineNumber: lineNumber }
46
+ )
47
+ }
48
+
49
+ const wrapLines = function wrapLines(ast: any[], linesToHighlight) {
50
+ const highlightAll = linesToHighlight.length === 1 && linesToHighlight[0] === 0
51
+ const allLines: any[] = Array.from(new Set(ast.map((x) => x.lineNumber)))
52
+ let i = 0
53
+ const wrapped = allLines.reduce((nodes, marker) => {
54
+ const line = marker
55
+ const children: any[] = []
56
+ for (; i < ast.length; i++) {
57
+ if (ast[i].lineNumber < line) {
58
+ nodes.push(ast[i])
59
+ continue
60
+ }
61
+
62
+ if (ast[i].lineNumber === line) {
63
+ children.push(ast[i])
64
+ continue
65
+ }
66
+
67
+ if (ast[i].lineNumber > line) {
68
+ break
69
+ }
70
+ }
71
+
72
+ nodes.push({
73
+ type: 'element',
74
+ tagName: 'div',
75
+ properties: {
76
+ dataLine: line,
77
+ className: 'highlight-line',
78
+ dataHighlighted: linesToHighlight.includes(line) || highlightAll ? 'true' : 'false',
79
+ },
80
+ children,
81
+ lineNumber: line,
82
+ })
83
+
84
+ return nodes
85
+ }, [])
86
+
87
+ return wrapped
88
+ }
89
+
90
+ // https://github.com/gatsbyjs/gatsby/pull/26161/files
91
+ const MULTILINE_TOKEN_SPAN = /<span class="token ([^"]+)">[^<]*\n[^<]*<\/span>/g
92
+
93
+ const applyMultilineFix = (ast) => {
94
+ // AST to HTML
95
+ let html = toHtml(ast)
96
+
97
+ // Fix JSX issue
98
+ html = html.replace(MULTILINE_TOKEN_SPAN, (match, token) =>
99
+ match.replace(/\n/g, `</span>\n<span class="token ${token}">`)
100
+ )
101
+
102
+ // HTML to AST
103
+ const hast = unified().use(parse, { emitParseErrors: true, fragment: true }).parse(html)
104
+
105
+ return hast['children']
106
+ }
107
+
108
+ export function rehypeHighlightLine(ast, lines) {
109
+ const formattedAst = applyMultilineFix(ast)
110
+ const numbered = lineNumberify(formattedAst).nodes
111
+ return wrapLines(numbered, lines)
112
+ }
@@ -0,0 +1,22 @@
1
+ // https://github.com/wooorm/xdm#syntax-highlighting-with-the-meta-field
2
+
3
+ import visit from 'unist-util-visit'
4
+
5
+ const re = /\b([-\w]+)(?:=(?:"([^"]*)"|'([^']*)'|([^"'\s]+)))?/g
6
+
7
+ export default (options = {}) => {
8
+ return (tree) => {
9
+ visit(tree, 'element', onelement)
10
+ }
11
+
12
+ function onelement(node) {
13
+ let match
14
+ if (node.tagName === 'code' && node.data && node.data.meta) {
15
+ re.lastIndex = 0 // Reset regex.
16
+
17
+ while ((match = re.exec(node.data.meta))) {
18
+ node.properties[match[1]] = match[2] || match[3] || match[4] || ''
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ // const visit = require('unist-util-visit')
2
+ import { toHtml } from 'hast-util-to-html'
3
+ import parse from 'rehype-parse'
4
+ import { unified } from 'unified'
5
+
6
+ const CALLOUT = /__(.*?)__/g
7
+
8
+ export const rehypeHighlightWord = (code) => {
9
+ const html = toHtml(code)
10
+ const result = html.replace(CALLOUT, (_, text) => `<span class="highlight-word">${text}</span>`)
11
+ const hast = unified().use(parse, { emitParseErrors: true, fragment: true }).parse(result)
12
+ return hast['children']
13
+ }
package/src/types.ts ADDED
@@ -0,0 +1,20 @@
1
+ export type Frontmatter = {
2
+ title: string
3
+ headings?: { title: string; priority: number; id: string }[]
4
+ description?: string
5
+ name?: string
6
+ versions?: string[]
7
+ version?: string
8
+ by?: string
9
+ publishedAt?: string
10
+ draft?: boolean
11
+ relatedIds?: string[]
12
+ type?: 'changelog' | string
13
+ readingTime?: { text: string; minutes: number; time: number; words: number }
14
+ poster?: string
15
+ slug: string
16
+ image?: string
17
+ component?: string
18
+ package?: string
19
+ demoName?: string
20
+ }
@@ -0,0 +1,3 @@
1
+ import type { Frontmatter } from './types';
2
+ export declare const getAllFrontmatter: (fromPath: string) => Frontmatter[];
3
+ //# sourceMappingURL=getAllFrontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAllFrontmatter.d.ts","sourceRoot":"","sources":["../src/getAllFrontmatter.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG1C,eAAO,MAAM,iBAAiB,aAAc,MAAM,KAAG,WAAW,EAe/D,CAAA"}
@@ -0,0 +1,6 @@
1
+ export declare const getHeadings: (source: string) => {
2
+ title: string;
3
+ priority: number;
4
+ id: string;
5
+ }[];
6
+ //# sourceMappingURL=getHeadings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getHeadings.d.ts","sourceRoot":"","sources":["../src/getHeadings.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW,WAAY,MAAM;;;;GAQnC,CAAA"}
@@ -0,0 +1,19 @@
1
+ import type { Frontmatter } from './types';
2
+ export declare const getMDXBySlug: (basePath: string, slug: string) => Promise<{
3
+ frontmatter: Frontmatter;
4
+ code: string;
5
+ }>;
6
+ export declare function getMDX(source: string): Promise<{
7
+ code: string;
8
+ frontmatter: {
9
+ [key: string]: any;
10
+ };
11
+ errors: import("esbuild").Message[];
12
+ matter: Omit<import("gray-matter").GrayMatterFile<string>, "data"> & {
13
+ data: {
14
+ [key: string]: any;
15
+ };
16
+ };
17
+ }>;
18
+ export declare function getAllVersionsFromPath(fromPath: string): string[];
19
+ //# sourceMappingURL=getMDXBySlug.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getMDXBySlug.d.ts","sourceRoot":"","sources":["../src/getMDXBySlug.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAO1C,eAAO,MAAM,YAAY,aACb,MAAM,QACV,MAAM,KACX,OAAO,CAAC;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAoBpD,CAAA;AAED,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM;;;;;;;;;;;GAe1C;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAOjE"}
@@ -0,0 +1,2 @@
1
+ export declare function createCodeHighlighter(): (source: string, language: "tsx" | "css" | string, line?: string) => string;
2
+ //# sourceMappingURL=highlightCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"highlightCode.d.ts","sourceRoot":"","sources":["../src/highlightCode.tsx"],"names":[],"mappings":"AAWA,wBAAgB,qBAAqB,aAFX,MAAM,YAAY,KAAK,GAAG,KAAK,GAAG,MAAM,SAAS,MAAM,KAAK,MAAM,CAiB3F"}
@@ -0,0 +1,5 @@
1
+ export { getAllFrontmatter } from './getAllFrontmatter';
2
+ export { getMDXBySlug, getAllVersionsFromPath } from './getMDXBySlug';
3
+ export type { Frontmatter } from './types';
4
+ export { createCodeHighlighter } from './highlightCode';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA;AACrE,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare const rehypeHighlightCode: (options?: {}) => (tree: any) => void;
2
+ //# sourceMappingURL=rehypeHighlightCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeHighlightCode.d.ts","sourceRoot":"","sources":["../src/rehypeHighlightCode.tsx"],"names":[],"mappings":"AAcA,eAAO,MAAM,mBAAmB,uCAyB/B,CAAA"}
@@ -0,0 +1,2 @@
1
+ export declare function rehypeHighlightLine(ast: any, lines: any): any;
2
+ //# sourceMappingURL=rehypeLine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeLine.d.ts","sourceRoot":"","sources":["../src/rehypeLine.tsx"],"names":[],"mappings":"AA2GA,wBAAgB,mBAAmB,CAAC,GAAG,KAAA,EAAE,KAAK,KAAA,OAI7C"}
@@ -0,0 +1,3 @@
1
+ declare const _default: (options?: {}) => (tree: any) => void;
2
+ export default _default;
3
+ //# sourceMappingURL=rehypeMetaAttribute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeMetaAttribute.d.ts","sourceRoot":"","sources":["../src/rehypeMetaAttribute.tsx"],"names":[],"mappings":";AAMA,wBAeC"}
@@ -0,0 +1,2 @@
1
+ export declare const rehypeHighlightWord: (code: any) => import("hast").RootContent[];
2
+ //# sourceMappingURL=rehypeWord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehypeWord.d.ts","sourceRoot":"","sources":["../src/rehypeWord.tsx"],"names":[],"mappings":"AAOA,eAAO,MAAM,mBAAmB,6CAK/B,CAAA"}
@@ -0,0 +1,30 @@
1
+ export type Frontmatter = {
2
+ title: string;
3
+ headings?: {
4
+ title: string;
5
+ priority: number;
6
+ id: string;
7
+ }[];
8
+ description?: string;
9
+ name?: string;
10
+ versions?: string[];
11
+ version?: string;
12
+ by?: string;
13
+ publishedAt?: string;
14
+ draft?: boolean;
15
+ relatedIds?: string[];
16
+ type?: 'changelog' | string;
17
+ readingTime?: {
18
+ text: string;
19
+ minutes: number;
20
+ time: number;
21
+ words: number;
22
+ };
23
+ poster?: string;
24
+ slug: string;
25
+ image?: string;
26
+ component?: string;
27
+ package?: string;
28
+ demoName?: string;
29
+ };
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;IACrB,IAAI,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC3B,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB,CAAA"}