@effing/satori 0.10.2 → 0.10.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/{chunk-SGDNREDY.js → chunk-W5PCQTH3.js} +12 -1
- package/dist/chunk-W5PCQTH3.js.map +1 -0
- package/dist/pool/index.js +8 -3
- package/dist/pool/index.js.map +1 -1
- package/dist/serde/index.d.ts +6 -1
- package/dist/serde/index.js +3 -1
- package/dist/worker/index.js +16 -2
- package/package.json +1 -1
- package/dist/chunk-SGDNREDY.js.map +0 -1
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
// src/serde/index.ts
|
|
2
2
|
import React from "react";
|
|
3
|
+
function ensureSingleElement(node) {
|
|
4
|
+
if (Array.isArray(node)) {
|
|
5
|
+
return React.createElement(
|
|
6
|
+
"div",
|
|
7
|
+
{ style: { display: "contents" } },
|
|
8
|
+
...node
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
return node;
|
|
12
|
+
}
|
|
3
13
|
var ELEMENT_MARKER = "__react_element__";
|
|
4
14
|
function expandElement(node) {
|
|
5
15
|
if (node == null || typeof node === "boolean") return node;
|
|
@@ -92,8 +102,9 @@ function deserializeElement(data) {
|
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
export {
|
|
105
|
+
ensureSingleElement,
|
|
95
106
|
expandElement,
|
|
96
107
|
serializeElement,
|
|
97
108
|
deserializeElement
|
|
98
109
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
110
|
+
//# sourceMappingURL=chunk-W5PCQTH3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/serde/index.ts"],"sourcesContent":["import React, { type ReactElement, type ReactNode } from \"react\";\n\n/**\n * Wrap a root-level array (e.g. from a top-level Fragment) in a\n * layout-transparent div so satori always receives a single element.\n */\nexport function ensureSingleElement(node: ReactNode): ReactNode {\n if (Array.isArray(node)) {\n return React.createElement(\n \"div\",\n { style: { display: \"contents\" } },\n ...node,\n );\n }\n return node;\n}\n\nconst ELEMENT_MARKER = \"__react_element__\";\n\ntype SerializedElement = {\n [ELEMENT_MARKER]: true;\n type: string;\n key: string | null;\n props: Record<string, unknown>;\n};\n\n/**\n * Recursively expand all function components in a React element tree\n * until only intrinsic elements (div, svg, path, img, etc.) remain.\n */\nexport function expandElement(node: ReactNode): ReactNode {\n if (node == null || typeof node === \"boolean\") return node;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) {\n return node.flatMap((child) => {\n const result = expandElement(child);\n return Array.isArray(result) ? result : [result];\n });\n }\n\n if (!React.isValidElement(node)) return node;\n\n const element = node as ReactElement;\n\n if (element.type === React.Fragment) {\n const children = (element.props as Record<string, unknown>)\n .children as ReactNode;\n if (children == null) return null;\n return expandElement(children);\n }\n\n if (typeof element.type === \"function\") {\n const result = (element.type as (props: unknown) => ReactNode)(\n element.props,\n );\n return expandElement(result);\n }\n\n // Intrinsic element — recursively expand children\n const props = Object.assign({}, element.props) as Record<string, unknown>;\n if (props.children != null) {\n props.children = expandElement(props.children as ReactNode);\n }\n return React.createElement(element.type as string, {\n ...props,\n key: element.key,\n });\n}\n\n/**\n * Serialize an expanded React element tree to a structured-clone-safe format.\n * All function components must already be expanded to intrinsic elements.\n */\nexport function serializeElement(node: ReactNode): unknown {\n if (node == null || typeof node === \"boolean\") return node;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) return node.map(serializeElement);\n\n if (!React.isValidElement(node)) return node;\n\n const element = node as ReactElement;\n\n if (typeof element.type === \"function\") {\n throw new Error(\n `Cannot serialize function component \"${element.type.name || \"anonymous\"}\". ` +\n \"Call expandElement first.\",\n );\n }\n\n if (typeof element.type !== \"string\") {\n throw new Error(\n `Cannot serialize element with type \"${String(element.type)}\". Call expandElement first.`,\n );\n }\n\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(\n element.props as Record<string, unknown>,\n )) {\n if (key === \"children\") {\n props.children = serializeElement(value as ReactNode);\n } else if (value instanceof Buffer) {\n props[key] = { __buffer__: true, data: Array.from(value) };\n } else {\n props[key] = value;\n }\n }\n\n return {\n [ELEMENT_MARKER]: true,\n type: element.type as string,\n key: element.key,\n props,\n } satisfies SerializedElement;\n}\n\n/**\n * Deserialize a serialized element tree back into React elements.\n */\nexport function deserializeElement(data: unknown): ReactNode {\n if (data == null || typeof data === \"boolean\") return data as ReactNode;\n if (typeof data === \"string\" || typeof data === \"number\") return data;\n if (Array.isArray(data)) return data.map(deserializeElement);\n\n if (typeof data === \"object\" && data !== null && ELEMENT_MARKER in data) {\n const serialized = data as SerializedElement;\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(serialized.props)) {\n if (key === \"children\") {\n props.children = deserializeElement(value);\n } else if (\n typeof value === \"object\" &&\n value !== null &&\n \"__buffer__\" in value\n ) {\n props[key] = Buffer.from((value as unknown as { data: number[] }).data);\n } else {\n props[key] = value;\n }\n }\n return React.createElement(serialized.type, {\n ...props,\n key: serialized.key,\n });\n }\n\n return data as ReactNode;\n}\n"],"mappings":"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","names":[]}
|
package/dist/pool/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ensureSingleElement,
|
|
2
3
|
expandElement,
|
|
3
4
|
serializeElement
|
|
4
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-W5PCQTH3.js";
|
|
5
6
|
|
|
6
7
|
// src/pool/index.ts
|
|
7
8
|
import os from "os";
|
|
@@ -20,7 +21,9 @@ function createSatoriPool(options) {
|
|
|
20
21
|
});
|
|
21
22
|
return {
|
|
22
23
|
async renderToPng(element, opts) {
|
|
23
|
-
const serialized = serializeElement(
|
|
24
|
+
const serialized = serializeElement(
|
|
25
|
+
ensureSingleElement(expandElement(element))
|
|
26
|
+
);
|
|
24
27
|
const result = await pool.run(
|
|
25
28
|
{
|
|
26
29
|
element: serialized,
|
|
@@ -34,7 +37,9 @@ function createSatoriPool(options) {
|
|
|
34
37
|
return Buffer.from(result);
|
|
35
38
|
},
|
|
36
39
|
async renderToSvg(element, opts) {
|
|
37
|
-
const serialized = serializeElement(
|
|
40
|
+
const serialized = serializeElement(
|
|
41
|
+
ensureSingleElement(expandElement(element))
|
|
42
|
+
);
|
|
38
43
|
return pool.run(
|
|
39
44
|
{
|
|
40
45
|
element: serialized,
|
package/dist/pool/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/pool/index.ts"],"sourcesContent":["import os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\n\nimport type { ReactNode } from \"react\";\nimport type satori from \"satori\";\nimport Tinypool from \"tinypool\";\n\nimport type { EmojiStyle } from \"../emoji.ts\";\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/pool/index.ts"],"sourcesContent":["import os from \"os\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\n\nimport type { ReactNode } from \"react\";\nimport type satori from \"satori\";\nimport Tinypool from \"tinypool\";\n\nimport type { EmojiStyle } from \"../emoji.ts\";\nimport {\n ensureSingleElement,\n expandElement,\n serializeElement,\n} from \"../serde/index.ts\";\n\nexport type SatoriPoolOptions = {\n /** Minimum number of worker threads (default: 1) */\n minThreads?: number;\n /** Maximum number of worker threads (default: os.cpus().length) */\n maxThreads?: number;\n};\n\nexport type SatoriPool = {\n /** Render a serialized React element to PNG via the worker pool */\n renderToPng(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<Buffer>;\n\n /** Render a serialized React element to SVG via the worker pool */\n renderToSvg(\n element: Parameters<typeof satori>[0],\n options: {\n width: number;\n height: number;\n fonts: Array<{\n name: string;\n data: Buffer;\n weight: number;\n style: string;\n }>;\n emoji?: EmojiStyle;\n },\n ): Promise<string>;\n\n /** Rasterize an SVG string to PNG via the worker pool */\n rasterizeSvgToPng(\n svg: string,\n options?: {\n fitTo?:\n | { mode: \"original\" }\n | { mode: \"width\"; value: number }\n | { mode: \"height\"; value: number }\n | { mode: \"zoom\"; value: number };\n crop?: {\n left: number;\n top: number;\n right?: number;\n bottom?: number;\n };\n },\n ): Promise<Buffer>;\n\n /** Shut down the worker pool */\n destroy(): Promise<void>;\n};\n\n/**\n * Create a worker pool for parallelized satori rendering.\n *\n * @param options Pool configuration\n * @returns A `SatoriPool` with `renderToPng`, `renderToSvg`, `rasterizeSvgToPng`, and `destroy`\n */\nexport function createSatoriPool(options?: SatoriPoolOptions): SatoriPool {\n const workerFile = path.resolve(\n path.dirname(fileURLToPath(import.meta.url)),\n \"../worker/index.js\",\n );\n\n const pool = new Tinypool({\n filename: workerFile,\n minThreads: options?.minThreads ?? 1,\n maxThreads: options?.maxThreads ?? os.cpus().length,\n });\n\n return {\n async renderToPng(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n const result = await pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToPng\" },\n );\n return Buffer.from(result);\n },\n\n async renderToSvg(element, opts) {\n const serialized = serializeElement(\n ensureSingleElement(expandElement(element as ReactNode)),\n );\n return pool.run(\n {\n element: serialized,\n width: opts.width,\n height: opts.height,\n fonts: opts.fonts,\n emoji: opts.emoji,\n },\n { name: \"renderToSvg\" },\n );\n },\n\n async rasterizeSvgToPng(svg, opts) {\n const result = await pool.run(\n { svg, options: opts },\n { name: \"rasterizeSvgToPng\" },\n );\n return Buffer.from(result);\n },\n\n async destroy() {\n await pool.destroy();\n },\n };\n}\n"],"mappings":";;;;;;;AAAA,OAAO,QAAQ;AACf,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AAIjB,OAAO,cAAc;AA6Ed,SAAS,iBAAiB,SAAyC;AACxE,QAAM,aAAa,KAAK;AAAA,IACtB,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,OAAO,IAAI,SAAS;AAAA,IACxB,UAAU;AAAA,IACV,YAAY,SAAS,cAAc;AAAA,IACnC,YAAY,SAAS,cAAc,GAAG,KAAK,EAAE;AAAA,EAC/C,CAAC;AAED,SAAO;AAAA,IACL,MAAM,YAAY,SAAS,MAAM;AAC/B,YAAM,aAAa;AAAA,QACjB,oBAAoB,cAAc,OAAoB,CAAC;AAAA,MACzD;AACA,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB;AAAA,UACE,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,QACd;AAAA,QACA,EAAE,MAAM,cAAc;AAAA,MACxB;AACA,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAAA,IAEA,MAAM,YAAY,SAAS,MAAM;AAC/B,YAAM,aAAa;AAAA,QACjB,oBAAoB,cAAc,OAAoB,CAAC;AAAA,MACzD;AACA,aAAO,KAAK;AAAA,QACV;AAAA,UACE,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,UACb,OAAO,KAAK;AAAA,UACZ,OAAO,KAAK;AAAA,QACd;AAAA,QACA,EAAE,MAAM,cAAc;AAAA,MACxB;AAAA,IACF;AAAA,IAEA,MAAM,kBAAkB,KAAK,MAAM;AACjC,YAAM,SAAS,MAAM,KAAK;AAAA,QACxB,EAAE,KAAK,SAAS,KAAK;AAAA,QACrB,EAAE,MAAM,oBAAoB;AAAA,MAC9B;AACA,aAAO,OAAO,KAAK,MAAM;AAAA,IAC3B;AAAA,IAEA,MAAM,UAAU;AACd,YAAM,KAAK,QAAQ;AAAA,IACrB;AAAA,EACF;AACF;","names":[]}
|
package/dist/serde/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Wrap a root-level array (e.g. from a top-level Fragment) in a
|
|
5
|
+
* layout-transparent div so satori always receives a single element.
|
|
6
|
+
*/
|
|
7
|
+
declare function ensureSingleElement(node: ReactNode): ReactNode;
|
|
3
8
|
/**
|
|
4
9
|
* Recursively expand all function components in a React element tree
|
|
5
10
|
* until only intrinsic elements (div, svg, path, img, etc.) remain.
|
|
@@ -15,4 +20,4 @@ declare function serializeElement(node: ReactNode): unknown;
|
|
|
15
20
|
*/
|
|
16
21
|
declare function deserializeElement(data: unknown): ReactNode;
|
|
17
22
|
|
|
18
|
-
export { deserializeElement, expandElement, serializeElement };
|
|
23
|
+
export { deserializeElement, ensureSingleElement, expandElement, serializeElement };
|
package/dist/serde/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
deserializeElement,
|
|
3
|
+
ensureSingleElement,
|
|
3
4
|
expandElement,
|
|
4
5
|
serializeElement
|
|
5
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-W5PCQTH3.js";
|
|
6
7
|
export {
|
|
7
8
|
deserializeElement,
|
|
9
|
+
ensureSingleElement,
|
|
8
10
|
expandElement,
|
|
9
11
|
serializeElement
|
|
10
12
|
};
|
package/dist/worker/index.js
CHANGED
|
@@ -19904,6 +19904,16 @@ function makeLoadAdditionalAsset(emoji) {
|
|
|
19904
19904
|
|
|
19905
19905
|
// src/serde/index.ts
|
|
19906
19906
|
import React from "react";
|
|
19907
|
+
function ensureSingleElement(node) {
|
|
19908
|
+
if (Array.isArray(node)) {
|
|
19909
|
+
return React.createElement(
|
|
19910
|
+
"div",
|
|
19911
|
+
{ style: { display: "contents" } },
|
|
19912
|
+
...node
|
|
19913
|
+
);
|
|
19914
|
+
}
|
|
19915
|
+
return node;
|
|
19916
|
+
}
|
|
19907
19917
|
var ELEMENT_MARKER = "__react_element__";
|
|
19908
19918
|
function deserializeElement(data) {
|
|
19909
19919
|
if (data == null || typeof data === "boolean") return data;
|
|
@@ -19937,7 +19947,9 @@ async function renderToPng({
|
|
|
19937
19947
|
fonts,
|
|
19938
19948
|
emoji = "twemoji"
|
|
19939
19949
|
}) {
|
|
19940
|
-
const reactElement =
|
|
19950
|
+
const reactElement = ensureSingleElement(
|
|
19951
|
+
deserializeElement(element)
|
|
19952
|
+
);
|
|
19941
19953
|
const svg = await iI(reactElement, {
|
|
19942
19954
|
width,
|
|
19943
19955
|
height,
|
|
@@ -19954,7 +19966,9 @@ async function renderToSvg({
|
|
|
19954
19966
|
fonts,
|
|
19955
19967
|
emoji = "twemoji"
|
|
19956
19968
|
}) {
|
|
19957
|
-
const reactElement =
|
|
19969
|
+
const reactElement = ensureSingleElement(
|
|
19970
|
+
deserializeElement(element)
|
|
19971
|
+
);
|
|
19958
19972
|
return iI(reactElement, {
|
|
19959
19973
|
width,
|
|
19960
19974
|
height,
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/serde/index.ts"],"sourcesContent":["import React, { type ReactElement, type ReactNode } from \"react\";\n\nconst ELEMENT_MARKER = \"__react_element__\";\n\ntype SerializedElement = {\n [ELEMENT_MARKER]: true;\n type: string;\n key: string | null;\n props: Record<string, unknown>;\n};\n\n/**\n * Recursively expand all function components in a React element tree\n * until only intrinsic elements (div, svg, path, img, etc.) remain.\n */\nexport function expandElement(node: ReactNode): ReactNode {\n if (node == null || typeof node === \"boolean\") return node;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) {\n return node.flatMap((child) => {\n const result = expandElement(child);\n return Array.isArray(result) ? result : [result];\n });\n }\n\n if (!React.isValidElement(node)) return node;\n\n const element = node as ReactElement;\n\n if (element.type === React.Fragment) {\n const children = (element.props as Record<string, unknown>)\n .children as ReactNode;\n if (children == null) return null;\n return expandElement(children);\n }\n\n if (typeof element.type === \"function\") {\n const result = (element.type as (props: unknown) => ReactNode)(\n element.props,\n );\n return expandElement(result);\n }\n\n // Intrinsic element — recursively expand children\n const props = Object.assign({}, element.props) as Record<string, unknown>;\n if (props.children != null) {\n props.children = expandElement(props.children as ReactNode);\n }\n return React.createElement(element.type as string, {\n ...props,\n key: element.key,\n });\n}\n\n/**\n * Serialize an expanded React element tree to a structured-clone-safe format.\n * All function components must already be expanded to intrinsic elements.\n */\nexport function serializeElement(node: ReactNode): unknown {\n if (node == null || typeof node === \"boolean\") return node;\n if (typeof node === \"string\" || typeof node === \"number\") return node;\n if (Array.isArray(node)) return node.map(serializeElement);\n\n if (!React.isValidElement(node)) return node;\n\n const element = node as ReactElement;\n\n if (typeof element.type === \"function\") {\n throw new Error(\n `Cannot serialize function component \"${element.type.name || \"anonymous\"}\". ` +\n \"Call expandElement first.\",\n );\n }\n\n if (typeof element.type !== \"string\") {\n throw new Error(\n `Cannot serialize element with type \"${String(element.type)}\". Call expandElement first.`,\n );\n }\n\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(\n element.props as Record<string, unknown>,\n )) {\n if (key === \"children\") {\n props.children = serializeElement(value as ReactNode);\n } else if (value instanceof Buffer) {\n props[key] = { __buffer__: true, data: Array.from(value) };\n } else {\n props[key] = value;\n }\n }\n\n return {\n [ELEMENT_MARKER]: true,\n type: element.type as string,\n key: element.key,\n props,\n } satisfies SerializedElement;\n}\n\n/**\n * Deserialize a serialized element tree back into React elements.\n */\nexport function deserializeElement(data: unknown): ReactNode {\n if (data == null || typeof data === \"boolean\") return data as ReactNode;\n if (typeof data === \"string\" || typeof data === \"number\") return data;\n if (Array.isArray(data)) return data.map(deserializeElement);\n\n if (typeof data === \"object\" && data !== null && ELEMENT_MARKER in data) {\n const serialized = data as SerializedElement;\n const props: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(serialized.props)) {\n if (key === \"children\") {\n props.children = deserializeElement(value);\n } else if (\n typeof value === \"object\" &&\n value !== null &&\n \"__buffer__\" in value\n ) {\n props[key] = Buffer.from((value as unknown as { data: number[] }).data);\n } else {\n props[key] = value;\n }\n }\n return React.createElement(serialized.type, {\n ...props,\n key: serialized.key,\n });\n }\n\n return data as ReactNode;\n}\n"],"mappings":";AAAA,OAAO,WAAkD;AAEzD,IAAM,iBAAiB;AAahB,SAAS,cAAc,MAA4B;AACxD,MAAI,QAAQ,QAAQ,OAAO,SAAS,UAAW,QAAO;AACtD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,SAAU,QAAO;AACjE,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,QAAQ,CAAC,UAAU;AAC7B,YAAM,SAAS,cAAc,KAAK;AAClC,aAAO,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAAA,IACjD,CAAC;AAAA,EACH;AAEA,MAAI,CAAC,MAAM,eAAe,IAAI,EAAG,QAAO;AAExC,QAAM,UAAU;AAEhB,MAAI,QAAQ,SAAS,MAAM,UAAU;AACnC,UAAM,WAAY,QAAQ,MACvB;AACH,QAAI,YAAY,KAAM,QAAO;AAC7B,WAAO,cAAc,QAAQ;AAAA,EAC/B;AAEA,MAAI,OAAO,QAAQ,SAAS,YAAY;AACtC,UAAM,SAAU,QAAQ;AAAA,MACtB,QAAQ;AAAA,IACV;AACA,WAAO,cAAc,MAAM;AAAA,EAC7B;AAGA,QAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,QAAQ,KAAK;AAC7C,MAAI,MAAM,YAAY,MAAM;AAC1B,UAAM,WAAW,cAAc,MAAM,QAAqB;AAAA,EAC5D;AACA,SAAO,MAAM,cAAc,QAAQ,MAAgB;AAAA,IACjD,GAAG;AAAA,IACH,KAAK,QAAQ;AAAA,EACf,CAAC;AACH;AAMO,SAAS,iBAAiB,MAA0B;AACzD,MAAI,QAAQ,QAAQ,OAAO,SAAS,UAAW,QAAO;AACtD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,SAAU,QAAO;AACjE,MAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,KAAK,IAAI,gBAAgB;AAEzD,MAAI,CAAC,MAAM,eAAe,IAAI,EAAG,QAAO;AAExC,QAAM,UAAU;AAEhB,MAAI,OAAO,QAAQ,SAAS,YAAY;AACtC,UAAM,IAAI;AAAA,MACR,wCAAwC,QAAQ,KAAK,QAAQ,WAAW;AAAA,IAE1E;AAAA,EACF;AAEA,MAAI,OAAO,QAAQ,SAAS,UAAU;AACpC,UAAM,IAAI;AAAA,MACR,uCAAuC,OAAO,QAAQ,IAAI,CAAC;AAAA,IAC7D;AAAA,EACF;AAEA,QAAM,QAAiC,CAAC;AACxC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO;AAAA,IAChC,QAAQ;AAAA,EACV,GAAG;AACD,QAAI,QAAQ,YAAY;AACtB,YAAM,WAAW,iBAAiB,KAAkB;AAAA,IACtD,WAAW,iBAAiB,QAAQ;AAClC,YAAM,GAAG,IAAI,EAAE,YAAY,MAAM,MAAM,MAAM,KAAK,KAAK,EAAE;AAAA,IAC3D,OAAO;AACL,YAAM,GAAG,IAAI;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AAAA,IACL,CAAC,cAAc,GAAG;AAAA,IAClB,MAAM,QAAQ;AAAA,IACd,KAAK,QAAQ;AAAA,IACb;AAAA,EACF;AACF;AAKO,SAAS,mBAAmB,MAA0B;AAC3D,MAAI,QAAQ,QAAQ,OAAO,SAAS,UAAW,QAAO;AACtD,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,SAAU,QAAO;AACjE,MAAI,MAAM,QAAQ,IAAI,EAAG,QAAO,KAAK,IAAI,kBAAkB;AAE3D,MAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,kBAAkB,MAAM;AACvE,UAAM,aAAa;AACnB,UAAM,QAAiC,CAAC;AACxC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,KAAK,GAAG;AAC3D,UAAI,QAAQ,YAAY;AACtB,cAAM,WAAW,mBAAmB,KAAK;AAAA,MAC3C,WACE,OAAO,UAAU,YACjB,UAAU,QACV,gBAAgB,OAChB;AACA,cAAM,GAAG,IAAI,OAAO,KAAM,MAAwC,IAAI;AAAA,MACxE,OAAO;AACL,cAAM,GAAG,IAAI;AAAA,MACf;AAAA,IACF;AACA,WAAO,MAAM,cAAc,WAAW,MAAM;AAAA,MAC1C,GAAG;AAAA,MACH,KAAK,WAAW;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":[]}
|