@knpkv/confluence-to-markdown 0.4.2 → 0.6.0
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/CHANGELOG.md +35 -0
- package/README.md +45 -10
- package/dist/ConfluenceAuth.d.ts.map +1 -1
- package/dist/ConfluenceAuth.js +12 -22
- package/dist/ConfluenceAuth.js.map +1 -1
- package/dist/ConfluenceClient.d.ts +13 -3
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +34 -70
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceError.d.ts +12 -12
- package/dist/GitError.d.ts +5 -5
- package/dist/GitService.d.ts.map +1 -1
- package/dist/GitService.js +0 -3
- package/dist/GitService.js.map +1 -1
- package/dist/SchemaConverterError.d.ts +3 -3
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +23 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +1 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js +323 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +1 -0
- package/dist/parsers/preprocessing/index.d.ts +7 -0
- package/dist/parsers/preprocessing/index.d.ts.map +1 -0
- package/dist/parsers/preprocessing/index.js +7 -0
- package/dist/parsers/preprocessing/index.js.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +29 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -1
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +3 -5
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -1
- package/package.json +35 -26
- package/src/AdfPlaceholders.ts +266 -0
- package/src/AdfSchemaValidator.ts +67 -0
- package/src/AdfWalker.ts +511 -0
- package/src/AtlaskitTransformers.ts +72 -0
- package/src/ConfluenceClient.ts +4 -4
- package/src/ConfluenceError.ts +65 -3
- package/src/MarkdownConverter.ts +106 -139
- package/src/Schemas.ts +4 -4
- package/src/SyncEngine.ts +130 -83
- package/src/atlaskit-adf-schema.d.ts +3 -0
- package/src/commands/clone.ts +8 -1
- package/src/commands/layers.ts +11 -4
- package/src/index.ts +3 -18
- package/test/AdfPlaceholders.test.ts +295 -0
- package/test/AdfSchemaValidator.test.ts +34 -0
- package/test/AdfWalker.test.ts +530 -0
- package/test/AtlaskitTransformers.test.ts +25 -0
- package/test/MarkdownConverter.test.ts +120 -105
- package/test/RoundTrip.test.ts +266 -0
- package/LICENSE +0 -21
- package/src/SchemaConverterError.ts +0 -108
- package/src/ast/BlockNode.ts +0 -425
- package/src/ast/Document.ts +0 -90
- package/src/ast/InlineNode.ts +0 -323
- package/src/ast/MacroNode.ts +0 -245
- package/src/ast/index.ts +0 -83
- package/src/parsers/ConfluenceParser.ts +0 -950
- package/src/parsers/MarkdownParser.ts +0 -1198
- package/src/parsers/index.ts +0 -8
- package/src/schemas/ConfluenceSchema.ts +0 -56
- package/src/schemas/ConversionSchema.ts +0 -318
- package/src/schemas/MarkdownSchema.ts +0 -56
- package/src/schemas/hast/HastFromHtml.ts +0 -153
- package/src/schemas/hast/HastSchema.ts +0 -274
- package/src/schemas/hast/index.ts +0 -35
- package/src/schemas/index.ts +0 -20
- package/src/schemas/mdast/MdastFromMarkdown.ts +0 -118
- package/src/schemas/mdast/MdastSchema.ts +0 -566
- package/src/schemas/mdast/index.ts +0 -59
- package/src/schemas/mdast/mdastToString.ts +0 -102
- package/src/schemas/nodes/block/BlockSchema.ts +0 -773
- package/src/schemas/nodes/block/index.ts +0 -13
- package/src/schemas/nodes/index.ts +0 -20
- package/src/schemas/nodes/inline/InlineSchema.ts +0 -523
- package/src/schemas/nodes/inline/index.ts +0 -14
- package/src/schemas/nodes/macro/MacroSchema.ts +0 -226
- package/src/schemas/nodes/macro/index.ts +0 -6
- package/src/schemas/preprocessing/ConfluencePreprocessor.ts +0 -446
- package/src/schemas/preprocessing/index.ts +0 -8
- package/src/serializers/ConfluenceSerializer.ts +0 -717
- package/src/serializers/MarkdownSerializer.ts +0 -493
- package/src/serializers/index.ts +0 -8
- package/test/ast/BlockNode.test.ts +0 -265
- package/test/ast/Document.test.ts +0 -126
- package/test/ast/InlineNode.test.ts +0 -161
- package/test/fixtures/integration-test.html.fixture +0 -103
- package/test/fixtures/integration-test.md.expected +0 -257
- package/test/parsers/ConfluenceParser.test.ts +0 -283
- package/test/schemas/ConfluencePreprocessor.test.ts +0 -180
- package/test/schemas/ConversionSchema.test.ts +0 -159
- package/test/schemas/HastSchema.test.ts +0 -138
- package/test/schemas/MdastSchema.test.ts +0 -145
- package/test/schemas/nodes/block/BlockSchema.test.ts +0 -173
- package/test/schemas/nodes/inline/InlineSchema.test.ts +0 -198
- package/test/schemas/nodes/macro/MacroSchema.test.ts +0 -142
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
<!--cf:layout-start-->
|
|
2
|
-
|
|
3
|
-
<!--cf:section:0;fixed-width;default;;1-->
|
|
4
|
-
|
|
5
|
-
<!--cf:cell:0;0-->
|
|
6
|
-
|
|
7
|
-
| | |
|
|
8
|
-
| --- | --- |
|
|
9
|
-
| Owner | <!--cf:user:557058:76c20100-e45a-495d-9cb2-4d7c809e6a9b--> |
|
|
10
|
-
| On this page | <!--cf:toc:;--> |
|
|
11
|
-
|
|
12
|
-
Normal text
|
|
13
|
-
|
|
14
|
-
## Headings
|
|
15
|
-
|
|
16
|
-
# Heading 1
|
|
17
|
-
|
|
18
|
-
## Heading 2
|
|
19
|
-
|
|
20
|
-
### Heading 3
|
|
21
|
-
|
|
22
|
-
#### Heading 4
|
|
23
|
-
|
|
24
|
-
##### Heading 5
|
|
25
|
-
|
|
26
|
-
###### Heading 6
|
|
27
|
-
|
|
28
|
-
## Text Formatting
|
|
29
|
-
|
|
30
|
-
### Basic Styles
|
|
31
|
-
|
|
32
|
-
**Bold**
|
|
33
|
-
|
|
34
|
-
*Italic*
|
|
35
|
-
|
|
36
|
-
<u>Inderline</u>
|
|
37
|
-
|
|
38
|
-
~~Strikethrough~~
|
|
39
|
-
|
|
40
|
-
`Code`
|
|
41
|
-
|
|
42
|
-
<sub>Subscript</sub>
|
|
43
|
-
|
|
44
|
-
<sup>Superscript</sup>
|
|
45
|
-
|
|
46
|
-
### Quotes
|
|
47
|
-
|
|
48
|
-
> Quote
|
|
49
|
-
|
|
50
|
-
### Alignment
|
|
51
|
-
|
|
52
|
-
<p style="text-align: center;">Align center</p>
|
|
53
|
-
|
|
54
|
-
<p style="text-align: right;">Align right</p>
|
|
55
|
-
|
|
56
|
-
Default
|
|
57
|
-
|
|
58
|
-
## Colors
|
|
59
|
-
|
|
60
|
-
### Text Colors
|
|
61
|
-
|
|
62
|
-
<span style="color: rgb(151,160,175);">Gray</span>
|
|
63
|
-
|
|
64
|
-
<span style="color: rgb(255,255,255);">White</span>
|
|
65
|
-
|
|
66
|
-
<span style="color: rgb(7,71,166);">Bold blue</span>
|
|
67
|
-
|
|
68
|
-
<span style="color: rgb(76,154,255);">Blue</span>
|
|
69
|
-
|
|
70
|
-
<span style="color: rgb(179,212,255);">Subtle blue</span>
|
|
71
|
-
|
|
72
|
-
<span style="color: rgb(0,141,166);">Bold teal</span>
|
|
73
|
-
|
|
74
|
-
<span style="color: rgb(0,184,217);">Teal</span>
|
|
75
|
-
|
|
76
|
-
<span style="color: rgb(179,245,255);">Subtle blue</span>
|
|
77
|
-
|
|
78
|
-
<span style="color: rgb(0,102,68);">Bold green</span>
|
|
79
|
-
|
|
80
|
-
<span style="color: rgb(54,179,126);">Green</span>
|
|
81
|
-
|
|
82
|
-
<span style="color: rgb(171,245,209);">Subtle green</span>
|
|
83
|
-
|
|
84
|
-
<span style="color: rgb(255,153,31);">Bold orange</span>
|
|
85
|
-
|
|
86
|
-
<span style="color: rgb(255,196,0);">Yellow</span>
|
|
87
|
-
|
|
88
|
-
<span style="color: rgb(255,240,179);">Subtle yellow</span>
|
|
89
|
-
|
|
90
|
-
<span style="color: rgb(191,38,0);">Bold red</span>
|
|
91
|
-
|
|
92
|
-
<span style="color: rgb(255,86,48);">Red</span>
|
|
93
|
-
|
|
94
|
-
<span style="color: rgb(255,189,173);">Subtle red</span>
|
|
95
|
-
|
|
96
|
-
<span style="color: rgb(64,50,148);">Bold purple</span>
|
|
97
|
-
|
|
98
|
-
<span style="color: rgb(101,84,192);">Purple</span>
|
|
99
|
-
|
|
100
|
-
<span style="color: rgb(234,230,255);">Subtle purple</span>
|
|
101
|
-
|
|
102
|
-
### Highlights
|
|
103
|
-
|
|
104
|
-
<span style="background-color: rgb(220,223,228);">Gray highlight</span>
|
|
105
|
-
|
|
106
|
-
<span style="background-color: rgb(198,237,251);">Teal highlight</span>
|
|
107
|
-
|
|
108
|
-
<span style="background-color: rgb(211,241,167);">Lime highlight</span>
|
|
109
|
-
|
|
110
|
-
<span style="background-color: rgb(254,222,200);">Yellow highlight</span>
|
|
111
|
-
|
|
112
|
-
<span style="background-color: rgb(253,208,236);">Magenta highlight</span>
|
|
113
|
-
|
|
114
|
-
<span style="background-color: rgb(223,216,253);">Purple highlight</span>
|
|
115
|
-
|
|
116
|
-
## Lists
|
|
117
|
-
|
|
118
|
-
### Bullet Lists
|
|
119
|
-
|
|
120
|
-
- Bullet list 1
|
|
121
|
-
<ul local-id="bed75a55-f8b9-45fb-9346-7531dd9ea338"><li local-id="30852041-2521-40ed-acbb-1a8469be3c30"><p local-id="5b0cccdf-3a87-4b32-b618-910a94529992">Bullet list 1.1</p><ul local-id="453de478-a2d7-4b2f-9b93-91732877c960"><li local-id="cc32bd38-0927-42f2-b2bc-8f480ea97762"><p local-id="308bc2ca-c9b5-4734-a0ff-dea8e319184d">Bullet list 1.1.1</p></li></ul></li></ul>
|
|
122
|
-
- Bullet list 2
|
|
123
|
-
|
|
124
|
-
### Numbered Lists
|
|
125
|
-
|
|
126
|
-
1. Numbered list 1
|
|
127
|
-
<ol start="1" local-id="d40d0651-9647-4012-aa44-22dea36dca0f"><li local-id="16340895-0a02-4369-a4f2-7fe3f7c56112"><p local-id="d5ad263d-afe0-4ca3-8299-929fbc1c7cd0">Numbered list a</p><ol start="1" local-id="897771c4-1ff1-4b72-832b-c8d6cc79f57b"><li local-id="25120a69-d5ff-41e0-9192-35e036d752f0"><p local-id="6bd35901-285f-4f58-872f-51e4152e296b">Numbered list i</p></li></ol></li></ol>
|
|
128
|
-
2. Numbered list 2
|
|
129
|
-
|
|
130
|
-
### Indentation
|
|
131
|
-
|
|
132
|
-
<p style="margin-left: 30px;">Indent tab 1</p>
|
|
133
|
-
|
|
134
|
-
<p style="margin-left: 60px;">Indent tab 2</p>
|
|
135
|
-
|
|
136
|
-
<p style="margin-left: 90px;">Indent tab 3</p>
|
|
137
|
-
|
|
138
|
-
### Action Items
|
|
139
|
-
|
|
140
|
-
<!--cf:tasklist:11|bce1bc39-6cba-44f9-b1b3-6adf0634475a|incomplete|Action%20Item%20unchecked;12|49c6a48e-cb41-4d98-ba45-64c1ecf96702|complete|Action%20item%20checked-->
|
|
141
|
-
|
|
142
|
-
## Links & Media
|
|
143
|
-
|
|
144
|
-
### Links
|
|
145
|
-
|
|
146
|
-
[External link](http://example.com)
|
|
147
|
-
|
|
148
|
-
<!--cf:link:Confluence%20link-->
|
|
149
|
-
|
|
150
|
-
### Images
|
|
151
|
-
|
|
152
|
-
<!--cf:image:f=Atlassian_Confluence_2017_logo.svg|a=Atlassian_Confluence_2017_logo.svg|al=center|w=238-->
|
|
153
|
-
|
|
154
|
-
### User Mentions
|
|
155
|
-
|
|
156
|
-
<!--cf:user:557058:76c20100-e45a-495d-9cb2-4d7c809e6a9b-->
|
|
157
|
-
|
|
158
|
-
### Emoticons
|
|
159
|
-
|
|
160
|
-
<!--cf:emoticon:%3Agrinning%3A|1f600|%F0%9F%98%80--> <!--cf:emoticon:%3Asmiley%3A|1f603|%F0%9F%98%83--> <!--cf:emoticon:%3Asmile%3A|1f604|%F0%9F%98%84--> <!--cf:emoticon:%3Agrin%3A|1f601|%F0%9F%98%81--> <!--cf:emoticon:%3Alaughing%3A|1f606|%F0%9F%98%86--> <!--cf:emoticon:%3Asweat_smile%3A|1f605|%F0%9F%98%85--> <!--cf:emoticon:%3Ajoy%3A|1f602|%F0%9F%98%82--> <!--cf:emoticon:%3Arofl%3A|1f923|%F0%9F%A4%A3-->
|
|
161
|
-
|
|
162
|
-
## Tables
|
|
163
|
-
|
|
164
|
-
| **Header 1** | **Header 2** | **Header 3** |
|
|
165
|
-
| --- | --- | --- |
|
|
166
|
-
| Cell 1.1 | Cell 2.1 | Cell 3.1 |
|
|
167
|
-
| Cell 1.2 | Cell 2.2 | Cell 3.2 |
|
|
168
|
-
|
|
169
|
-
<!--cf:section-end:0-->
|
|
170
|
-
|
|
171
|
-
<!--cf:section:1;two_equal;wide;760;2-->
|
|
172
|
-
|
|
173
|
-
<!--cf:cell:1;0-->
|
|
174
|
-
|
|
175
|
-
Column 1
|
|
176
|
-
|
|
177
|
-
<!--cf:cell:1;1-->
|
|
178
|
-
|
|
179
|
-
Column 2
|
|
180
|
-
|
|
181
|
-
<!--cf:section-end:1-->
|
|
182
|
-
|
|
183
|
-
<!--cf:section:2;fixed-width;default;;1-->
|
|
184
|
-
|
|
185
|
-
<!--cf:cell:2;0-->
|
|
186
|
-
|
|
187
|
-
### Code Blocks
|
|
188
|
-
|
|
189
|
-
```typescript
|
|
190
|
-
const typescript = {};
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
{
|
|
195
|
-
"json": true
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
### Decisions
|
|
200
|
-
|
|
201
|
-
<!--cf:decision:7c3012f5-3ad3-47e1-89cc-cd87267668d3;DECIDED;Decision-->
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
### Expand
|
|
206
|
-
|
|
207
|
-
<!--cf:expand:Expand%20title:Expand%20content-->
|
|
208
|
-
|
|
209
|
-
### Dates
|
|
210
|
-
|
|
211
|
-
<!--cf:date:2026-01-01-->
|
|
212
|
-
|
|
213
|
-
### Status
|
|
214
|
-
|
|
215
|
-
<!--cf:status:Gray;--> <!--cf:status:;Blue--> <!--cf:status:;Green--> <!--cf:status:;Yellow--> <!--cf:status:;Red--> <!--cf:status:;Purple-->
|
|
216
|
-
|
|
217
|
-
### Smart Links
|
|
218
|
-
|
|
219
|
-
<!--cf:smartlink:https%3A%2F%2Fknpkv-team.atlassian.net%2Fissues%2F%3Fjql%3Dproject%2520in%2520(LEARNJIRA)%2520ORDER%2520BY%2520created%2520DESC;block;%7B%22id%22%3A%22d8b75300-dfda-4519-b6cd-e49abbd50401%22%2C%22parameters%22%3A%7B%22cloudId%22%3A%2285fe2d49-d86f-4afa-be53-e7ec408a4d5e%22%2C%22jql%22%3A%22project%20in%20(LEARNJIRA)%20ORDER%20BY%20created%20DESC%22%7D%2C%22views%22%3A%5B%7B%22type%22%3A%22table%22%2C%22properties%22%3A%7B%22columns%22%3A%5B%7B%22key%22%3A%22issuetype%22%7D%2C%7B%22key%22%3A%22key%22%7D%2C%7B%22key%22%3A%22summary%22%7D%2C%7B%22key%22%3A%22assignee%22%7D%2C%7B%22key%22%3A%22priority%22%7D%2C%7B%22key%22%3A%22status%22%7D%2C%7B%22key%22%3A%22updated%22%7D%5D%7D%7D%5D%7D-->
|
|
220
|
-
|
|
221
|
-
<!--cf:smartlink:https%3A%2F%2Fknpkv-team.atlassian.net%2Fwiki%2Fsearch%3Ftext%3DGetting%2Bstarted;block;%7B%22id%22%3A%22768fc736-3af4-4a8f-b27e-203602bff8ca%22%2C%22parameters%22%3A%7B%22cloudId%22%3A%2285fe2d49-d86f-4afa-be53-e7ec408a4d5e%22%2C%22searchString%22%3A%22Getting%20started%22%7D%2C%22views%22%3A%5B%7B%22type%22%3A%22table%22%2C%22properties%22%3A%7B%22columns%22%3A%5B%7B%22key%22%3A%22type%22%7D%2C%7B%22key%22%3A%22title%22%7D%2C%7B%22key%22%3A%22space%22%2C%22width%22%3A243%7D%2C%7B%22key%22%3A%22description%22%7D%2C%7B%22key%22%3A%22ownedBy%22%7D%2C%7B%22key%22%3A%22updatedAt%22%7D%5D%7D%7D%5D%7D-->
|
|
222
|
-
|
|
223
|
-
### Panels
|
|
224
|
-
|
|
225
|
-
:::info
|
|
226
|
-
Info panel
|
|
227
|
-
:::
|
|
228
|
-
|
|
229
|
-
:::note
|
|
230
|
-
Note panel
|
|
231
|
-
:::
|
|
232
|
-
|
|
233
|
-
:::warning
|
|
234
|
-
Error panel
|
|
235
|
-
:::
|
|
236
|
-
|
|
237
|
-
:::panel
|
|
238
|
-
Custom panel
|
|
239
|
-
:::
|
|
240
|
-
|
|
241
|
-
:::tip
|
|
242
|
-
Success panel
|
|
243
|
-
:::
|
|
244
|
-
|
|
245
|
-
:::note
|
|
246
|
-
Warning panel
|
|
247
|
-
:::
|
|
248
|
-
|
|
249
|
-
### Other
|
|
250
|
-
|
|
251
|
-
Hello 3
|
|
252
|
-
|
|
253
|
-
<!--cf:section-end:2-->
|
|
254
|
-
|
|
255
|
-
<!--cf:layout-end-->
|
|
256
|
-
|
|
257
|
-
<!--cf:raw:PGFjOmxheW91dD48YWM6bGF5b3V0LXNlY3Rpb24gYWM6dHlwZT0iZml4ZWQtd2lkdGgiIGFjOmJyZWFrb3V0LW1vZGU9ImRlZmF1bHQiPjxhYzpsYXlvdXQtY2VsbD48dGFibGU+PHRoZWFkPjx0cj48dGggLz48dGggLz48L3RyPjwvdGhlYWQ+PHRib2R5Pjx0cj48dGQ+T3duZXI8L3RkPjx0ZD48YWM6bGluaz48cmk6dXNlciByaTphY2NvdW50LWlkPSI1NTcwNTg6NzZjMjAxMDAtZTQ1YS00OTVkLTljYjItNGQ3YzgwOWU2YTliIiAvPjwvYWM6bGluaz48L3RkPjwvdHI+PHRyPjx0ZD5PbiB0aGlzIHBhZ2U8L3RkPjx0ZD48YWM6c3RydWN0dXJlZC1tYWNybyBhYzpuYW1lPSJ0b2MiIGFjOnNjaGVtYS12ZXJzaW9uPSIxIiBhYzptYWNyby1pZD0iZDQ1MmJhZDQtNzAwZi00OTczLWFlMDYtNTc0Nzc2YTE0Njg3IiAvPjwvdGQ+PC90cj48L3Rib2R5PjwvdGFibGU+CjxwPk5vcm1hbCB0ZXh0PC9wPgo8aDI+SGVhZGluZ3M8L2gyPgo8aDE+SGVhZGluZyAxPC9oMT4KPGgyPkhlYWRpbmcgMjwvaDI+CjxoMz5IZWFkaW5nIDM8L2gzPgo8aDQ+SGVhZGluZyA0PC9oND4KPGg1PkhlYWRpbmcgNTwvaDU+CjxoNj5IZWFkaW5nIDY8L2g2Pgo8aDI+VGV4dCBGb3JtYXR0aW5nPC9oMj4KPGgzPkJhc2ljIFN0eWxlczwvaDM+CjxwPjxzdHJvbmc+Qm9sZDwvc3Ryb25nPjwvcD4KPHA+PGVtPkl0YWxpYzwvZW0+PC9wPgo8cD48dT5JbmRlcmxpbmU8L3U+PC9wPgo8cD48ZGVsPlN0cmlrZXRocm91Z2g8L2RlbD48L3A+CjxwPjxjb2RlPkNvZGU8L2NvZGU+PC9wPgo8cD48c3ViPlN1YnNjcmlwdDwvc3ViPjwvcD4KPHA+PHN1cD5TdXBlcnNjcmlwdDwvc3VwPjwvcD4KPGgzPlF1b3RlczwvaDM+CjxibG9ja3F1b3RlPjxwPlF1b3RlPC9wPjwvYmxvY2txdW90ZT4KPGgzPkFsaWdubWVudDwvaDM+CjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij5BbGlnbiBjZW50ZXI8L3A+CjxwIHN0eWxlPSJ0ZXh0LWFsaWduOiByaWdodDsiPkFsaWduIHJpZ2h0PC9wPgo8cD5EZWZhdWx0PC9wPgo8aDI+Q29sb3JzPC9oMj4KPGgzPlRleHQgQ29sb3JzPC9oMz4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMTUxLDE2MCwxNzUpOyI+R3JheTwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjogcmdiKDI1NSwyNTUsMjU1KTsiPldoaXRlPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoNyw3MSwxNjYpOyI+Qm9sZCBibHVlPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoNzYsMTU0LDI1NSk7Ij5CbHVlPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMTc5LDIxMiwyNTUpOyI+U3VidGxlIGJsdWU8L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iY29sb3I6IHJnYigwLDE0MSwxNjYpOyI+Qm9sZCB0ZWFsPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMCwxODQsMjE3KTsiPlRlYWw8L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iY29sb3I6IHJnYigxNzksMjQ1LDI1NSk7Ij5TdWJ0bGUgYmx1ZTwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjogcmdiKDAsMTAyLDY4KTsiPkJvbGQgZ3JlZW48L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iY29sb3I6IHJnYig1NCwxNzksMTI2KTsiPkdyZWVuPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMTcxLDI0NSwyMDkpOyI+U3VidGxlIGdyZWVuPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMjU1LDE1MywzMSk7Ij5Cb2xkIG9yYW5nZTwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjogcmdiKDI1NSwxOTYsMCk7Ij5ZZWxsb3c8L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iY29sb3I6IHJnYigyNTUsMjQwLDE3OSk7Ij5TdWJ0bGUgeWVsbG93PC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMTkxLDM4LDApOyI+Qm9sZCByZWQ8L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iY29sb3I6IHJnYigyNTUsODYsNDgpOyI+UmVkPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMjU1LDE4OSwxNzMpOyI+U3VidGxlIHJlZDwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjogcmdiKDY0LDUwLDE0OCk7Ij5Cb2xkIHB1cnBsZTwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJjb2xvcjogcmdiKDEwMSw4NCwxOTIpOyI+UHVycGxlPC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImNvbG9yOiByZ2IoMjM0LDIzMCwyNTUpOyI+U3VidGxlIHB1cnBsZTwvc3Bhbj48L3A+CjxoMz5IaWdobGlnaHRzPC9oMz4KPHA+PHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IHJnYigyMjAsMjIzLDIyOCk7Ij5HcmF5IGhpZ2hsaWdodDwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiByZ2IoMTk4LDIzNywyNTEpOyI+VGVhbCBoaWdobGlnaHQ8L3NwYW4+PC9wPgo8cD48c3BhbiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjogcmdiKDIxMSwyNDEsMTY3KTsiPkxpbWUgaGlnaGxpZ2h0PC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IHJnYigyNTQsMjIyLDIwMCk7Ij5ZZWxsb3cgaGlnaGxpZ2h0PC9zcGFuPjwvcD4KPHA+PHNwYW4gc3R5bGU9ImJhY2tncm91bmQtY29sb3I6IHJnYigyNTMsMjA4LDIzNik7Ij5NYWdlbnRhIGhpZ2hsaWdodDwvc3Bhbj48L3A+CjxwPjxzcGFuIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiByZ2IoMjIzLDIxNiwyNTMpOyI+UHVycGxlIGhpZ2hsaWdodDwvc3Bhbj48L3A+CjxoMj5MaXN0czwvaDI+CjxoMz5CdWxsZXQgTGlzdHM8L2gzPgo8dWw+PGxpPjxwPkJ1bGxldCBsaXN0IDE8L3A+PHVsIGxvY2FsLWlkPSJiZWQ3NWE1NS1mOGI5LTQ1ZmItOTM0Ni03NTMxZGQ5ZWEzMzgiPjxsaSBsb2NhbC1pZD0iMzA4NTIwNDEtMjUyMS00MGVkLWFjYmItMWE4NDY5YmUzYzMwIj48cCBsb2NhbC1pZD0iNWIwY2NjZGYtM2E4Ny00YjMyLWI2MTgtOTEwYTk0NTI5OTkyIj5CdWxsZXQgbGlzdCAxLjE8L3A+PHVsIGxvY2FsLWlkPSI0NTNkZTQ3OC1hMmQ3LTRiMmYtOWI5My05MTczMjg3N2M5NjAiPjxsaSBsb2NhbC1pZD0iY2MzMmJkMzgtMDkyNy00MmYyLWIyYmMtOGY0ODBlYTk3NzYyIj48cCBsb2NhbC1pZD0iMzA4YmMyY2EtYzliNS00NzM0LWEwZmYtZGVhOGUzMTkxODRkIj5CdWxsZXQgbGlzdCAxLjEuMTwvcD48L2xpPjwvdWw+PC9saT48L3VsPjwvbGk+PGxpPjxwPkJ1bGxldCBsaXN0IDI8L3A+PC9saT48L3VsPgo8aDM+TnVtYmVyZWQgTGlzdHM8L2gzPgo8b2w+PGxpPjxwPk51bWJlcmVkIGxpc3QgMTwvcD48b2wgc3RhcnQ9IjEiIGxvY2FsLWlkPSJkNDBkMDY1MS05NjQ3LTQwMTItYWE0NC0yMmRlYTM2ZGNhMGYiPjxsaSBsb2NhbC1pZD0iMTYzNDA4OTUtMGEwMi00MzY5LWE0ZjItN2ZlM2Y3YzU2MTEyIj48cCBsb2NhbC1pZD0iZDVhZDI2M2QtYWZlMC00Y2EzLTgyOTktOTI5ZmJjMWM3Y2QwIj5OdW1iZXJlZCBsaXN0IGE8L3A+PG9sIHN0YXJ0PSIxIiBsb2NhbC1pZD0iODk3NzcxYzQtMWZmMS00YjcyLTgzMmItYzhkNmNjNzlmNTdiIj48bGkgbG9jYWwtaWQ9IjI1MTIwYTY5LWQ1ZmYtNDFlMC05MTkyLTM1ZTAzNmQ3NTJmMCI+PHAgbG9jYWwtaWQ9IjZiZDM1OTAxLTI4NWYtNGY1OC04NzJmLTUxZTQxNTJlMjk2YiI+TnVtYmVyZWQgbGlzdCBpPC9wPjwvbGk+PC9vbD48L2xpPjwvb2w+PC9saT48bGk+PHA+TnVtYmVyZWQgbGlzdCAyPC9wPjwvbGk+PC9vbD4KPGgzPkluZGVudGF0aW9uPC9oMz4KPHAgc3R5bGU9Im1hcmdpbi1sZWZ0OiAzMC4wcHg7Ij5JbmRlbnQgdGFiIDE8L3A+CjxwIHN0eWxlPSJtYXJnaW4tbGVmdDogNjAuMHB4OyI+SW5kZW50IHRhYiAyPC9wPgo8cCBzdHlsZT0ibWFyZ2luLWxlZnQ6IDkwLjBweDsiPkluZGVudCB0YWIgMzwvcD4KPGgzPkFjdGlvbiBJdGVtczwvaDM+CjxhYzp0YXNrLWxpc3Q+CjxhYzp0YXNrPjxhYzp0YXNrLWlkPjQzPC9hYzp0YXNrLWlkPjxhYzp0YXNrLXV1aWQ+Yjk0ZTUzMzQtNzRkYy00Yjk1LWJkNmYtYTAzMGMxZGY3MDY4PC9hYzp0YXNrLXV1aWQ+PGFjOnRhc2stc3RhdHVzPmluY29tcGxldGU8L2FjOnRhc2stc3RhdHVzPjxhYzp0YXNrLWJvZHk+PHNwYW4gY2xhc3M9InBsYWNlaG9sZGVyLWlubGluZS10YXNrcyI+QWN0aW9uIEl0ZW0gdW5jaGVja2VkPC9zcGFuPjwvYWM6dGFzay1ib2R5PjwvYWM6dGFzaz4KPGFjOnRhc2s+PGFjOnRhc2staWQ+NDQ8L2FjOnRhc2staWQ+PGFjOnRhc2stdXVpZD5mN2YzNDBlMS03MmNmLTQ2NWItYTE3YS00NDA0OGY1M2I0ZjU8L2FjOnRhc2stdXVpZD48YWM6dGFzay1zdGF0dXM+Y29tcGxldGU8L2FjOnRhc2stc3RhdHVzPjxhYzp0YXNrLWJvZHk+PHNwYW4gY2xhc3M9InBsYWNlaG9sZGVyLWlubGluZS10YXNrcyI+QWN0aW9uIGl0ZW0gY2hlY2tlZDwvc3Bhbj48L2FjOnRhc2stYm9keT48L2FjOnRhc2s+CjwvYWM6dGFzay1saXN0Pgo8aDI+TGlua3MgJmFtcDsgTWVkaWE8L2gyPgo8aDM+TGlua3M8L2gzPgo8cD48YSBocmVmPSJodHRwOi8vZXhhbXBsZS5jb20iPkxpbms8L2E+PC9wPgo8aDM+SW1hZ2VzPC9oMz4KPGFjOmltYWdlIGFjOmFsaWduPSJjZW50ZXIiIGFjOndpZHRoPSIyNTAiIGFjOmFsdD0iQXRsYXNzaWFuX0NvbmZsdWVuY2VfMjAxN19sb2dvLnN2ZyI+PHJpOmF0dGFjaG1lbnQgcmk6ZmlsZW5hbWU9IkF0bGFzc2lhbl9Db25mbHVlbmNlXzIwMTdfbG9nby5zdmciIHJpOnZlcnNpb24tYXQtc2F2ZT0iMSIgLz48L2FjOmltYWdlPgo8aDM+VXNlciBNZW50aW9uczwvaDM+CjxwPjxhYzpsaW5rPjxyaTp1c2VyIHJpOmFjY291bnQtaWQ9IjU1NzA1ODo3NmMyMDEwMC1lNDVhLTQ5NWQtOWNiMi00ZDdjODA5ZTZhOWIiIC8+PC9hYzpsaW5rPjwvcD4KPGgzPkVtb3RpY29uczwvaDM+CjxwPjxhYzplbW90aWNvbiBhYzplbW9qaS1zaG9ydG5hbWU9IjpncmlubmluZzoiIGFjOmVtb2ppLWlkPSIxZjYwMCIgYWM6ZW1vamktZmFsbGJhY2s9IvCfmIAiIC8+IDxhYzplbW90aWNvbiBhYzplbW9qaS1zaG9ydG5hbWU9IjpzbWlsZXk6IiBhYzplbW9qaS1pZD0iMWY2MDMiIGFjOmVtb2ppLWZhbGxiYWNrPSLwn5iDIiAvPiA8YWM6ZW1vdGljb24gYWM6ZW1vamktc2hvcnRuYW1lPSI6c21pbGU6IiBhYzplbW9qaS1pZD0iMWY2MDQiIGFjOmVtb2ppLWZhbGxiYWNrPSLwn5iEIiAvPiA8YWM6ZW1vdGljb24gYWM6ZW1vamktc2hvcnRuYW1lPSI6Z3JpbjoiIGFjOmVtb2ppLWlkPSIxZjYwMSIgYWM6ZW1vamktZmFsbGJhY2s9IvCfmIEiIC8+IDxhYzplbW90aWNvbiBhYzplbW9qaS1zaG9ydG5hbWU9IjpsYXVnaGluZzoiIGFjOmVtb2ppLWlkPSIxZjYwNiIgYWM6ZW1vamktZmFsbGJhY2s9IvCfmIYiIC8+IDxhYzplbW90aWNvbiBhYzplbW9qaS1zaG9ydG5hbWU9Ijpzd2VhdF9zbWlsZToiIGFjOmVtb2ppLWlkPSIxZjYwNSIgYWM6ZW1vamktZmFsbGJhY2s9IvCfmIUiIC8+IDxhYzplbW90aWNvbiBhYzplbW9qaS1zaG9ydG5hbWU9Ijpqb3k6IiBhYzplbW9qaS1pZD0iMWY2MDIiIGFjOmVtb2ppLWZhbGxiYWNrPSLwn5iCIiAvPiA8YWM6ZW1vdGljb24gYWM6ZW1vamktc2hvcnRuYW1lPSI6cm9mbDoiIGFjOmVtb2ppLWlkPSIxZjkyMyIgYWM6ZW1vamktZmFsbGJhY2s9IvCfpKMiIC8+PC9wPgo8aDI+VGFibGVzPC9oMj4KPHRhYmxlPjx0aGVhZD48dHI+PHRoPjxzdHJvbmc+SGVhZGVyIDE8L3N0cm9uZz48L3RoPjx0aD48c3Ryb25nPkhlYWRlciAyPC9zdHJvbmc+PC90aD48dGg+PHN0cm9uZz5IZWFkZXIgMzwvc3Ryb25nPjwvdGg+PC90cj48L3RoZWFkPjx0Ym9keT48dHI+PHRkPkNlbGwgMS4xPC90ZD48dGQ+Q2VsbCAyLjE8L3RkPjx0ZD5DZWxsIDMuMTwvdGQ+PC90cj48dHI+PHRkPkNlbGwgMS4yPC90ZD48dGQ+Q2VsbCAyLjI8L3RkPjx0ZD5DZWxsIDMuMjwvdGQ+PC90cj48L3Rib2R5PjwvdGFibGU+PC9hYzpsYXlvdXQtY2VsbD48L2FjOmxheW91dC1zZWN0aW9uPjxhYzpsYXlvdXQtc2VjdGlvbiBhYzp0eXBlPSJ0d29fZXF1YWwiIGFjOmJyZWFrb3V0LW1vZGU9IndpZGUiIGFjOmJyZWFrb3V0LXdpZHRoPSI3NjAiPjxhYzpsYXlvdXQtY2VsbD48cD5Db2x1bW4gMTwvcD48L2FjOmxheW91dC1jZWxsPjxhYzpsYXlvdXQtY2VsbD48cD5Db2x1bW4gMjwvcD48L2FjOmxheW91dC1jZWxsPjwvYWM6bGF5b3V0LXNlY3Rpb24+PGFjOmxheW91dC1zZWN0aW9uIGFjOnR5cGU9ImZpeGVkLXdpZHRoIiBhYzpicmVha291dC1tb2RlPSJkZWZhdWx0Ij48YWM6bGF5b3V0LWNlbGw+PGgzPkNvZGUgQmxvY2tzPC9oMz4KPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0iY29kZSIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSI4NGRlN2UwMy1lM2IwLTQ4ODQtYWE3Yi0zNGNiZmNlNmMxYzEiPjxhYzpwYXJhbWV0ZXIgYWM6bmFtZT0ibGFuZ3VhZ2UiPnR5cGVzY3JpcHQ8L2FjOnBhcmFtZXRlcj48YWM6cGxhaW4tdGV4dC1ib2R5PjwhW0NEQVRBW2NvbnN0IHR5cGVzY3JpcHQgPSB7fTtdXT48L2FjOnBsYWluLXRleHQtYm9keT48L2FjOnN0cnVjdHVyZWQtbWFjcm8+CjxhYzpzdHJ1Y3R1cmVkLW1hY3JvIGFjOm5hbWU9ImNvZGUiIGFjOnNjaGVtYS12ZXJzaW9uPSIxIiBhYzptYWNyby1pZD0iMGQ2MGZlNTYtYzkzYy00ZjZiLTk3OWItMzQzMmM3OWRiOWJhIj48YWM6cGFyYW1ldGVyIGFjOm5hbWU9Imxhbmd1YWdlIj5qc29uPC9hYzpwYXJhbWV0ZXI+PGFjOnBsYWluLXRleHQtYm9keT48IVtDREFUQVt7CiAgImpzb24iOiB0cnVlCn1dXT48L2FjOnBsYWluLXRleHQtYm9keT48L2FjOnN0cnVjdHVyZWQtbWFjcm8+CjxoMz5EZWNpc2lvbnM8L2gzPgo8YWM6YWRmLWV4dGVuc2lvbj48YWM6YWRmLW5vZGUgdHlwZT0iZGVjaXNpb24tbGlzdCI+PGFjOmFkZi1ub2RlIHR5cGU9ImRlY2lzaW9uLWl0ZW0iPjxhYzphZGYtYXR0cmlidXRlIGtleT0ibG9jYWwtaWQiPjdjMzAxMmY1LTNhZDMtNDdlMS04OWNjLWNkODcyNjc2NjhkMzwvYWM6YWRmLWF0dHJpYnV0ZT48YWM6YWRmLWF0dHJpYnV0ZSBrZXk9InN0YXRlIj5ERUNJREVEPC9hYzphZGYtYXR0cmlidXRlPjxhYzphZGYtY29udGVudD5EZWNpc2lvbjwvYWM6YWRmLWNvbnRlbnQ+PC9hYzphZGYtbm9kZT48L2FjOmFkZi1ub2RlPjxhYzphZGYtZmFsbGJhY2s+PHVsIGNsYXNzPSJkZWNpc2lvbi1saXN0Ij48bGk+RGVjaXNpb248L2xpPjwvdWw+PC9hYzphZGYtZmFsbGJhY2s+PC9hYzphZGYtZXh0ZW5zaW9uPgo8aHIgLz4KPGgzPkV4cGFuZDwvaDM+CjxhYzpzdHJ1Y3R1cmVkLW1hY3JvIGFjOm5hbWU9ImV4cGFuZCIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSJhMWM1NmMyZC1kOGExLTRiODMtOGRiYy1jMTI4M2E0MDFjYjgiPjxhYzpwYXJhbWV0ZXIgYWM6bmFtZT0idGl0bGUiPkV4cGFuZCB0aXRsZTwvYWM6cGFyYW1ldGVyPjxhYzpyaWNoLXRleHQtYm9keT48cD5FeHBhbmQgY29udGVudDwvcD48L2FjOnJpY2gtdGV4dC1ib2R5PjwvYWM6c3RydWN0dXJlZC1tYWNybz4KPGgzPkRhdGVzPC9oMz4KPHA+PHRpbWUgZGF0ZXRpbWU9IjIwMjYtMDEtMDEiIC8+PC9wPgo8aDM+U3RhdHVzPC9oMz4KPHA+PGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ic3RhdHVzIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9ImJhMGE0Mjk3LTlmZjktNGQ0NC1iZjY5LTM0Njk2NjM2MzZmNyI+PGFjOnBhcmFtZXRlciBhYzpuYW1lPSJ0aXRsZSI+R3JheTwvYWM6cGFyYW1ldGVyPjwvYWM6c3RydWN0dXJlZC1tYWNybz4gPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ic3RhdHVzIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9ImYyMjBlZTBjLTczNWEtNDYxMy1iYmI2LTY0YjVmOTk1MjQ3MiI+PGFjOnBhcmFtZXRlciBhYzpuYW1lPSJjb2xvdXIiPkJsdWU8L2FjOnBhcmFtZXRlcj48L2FjOnN0cnVjdHVyZWQtbWFjcm8+IDxhYzpzdHJ1Y3R1cmVkLW1hY3JvIGFjOm5hbWU9InN0YXR1cyIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSI3MTIzNGZkNS01NDQyLTQ1NTctYTZkZS1lZDc2MjkyM2Q5OWQiPjxhYzpwYXJhbWV0ZXIgYWM6bmFtZT0iY29sb3VyIj5HcmVlbjwvYWM6cGFyYW1ldGVyPjwvYWM6c3RydWN0dXJlZC1tYWNybz4gPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ic3RhdHVzIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9IjcyYWMzMDA3LTkyNjQtNDY4ZS1iMGYyLTg1ODQ2NWU1ZTljZCI+PGFjOnBhcmFtZXRlciBhYzpuYW1lPSJjb2xvdXIiPlllbGxvdzwvYWM6cGFyYW1ldGVyPjwvYWM6c3RydWN0dXJlZC1tYWNybz4gPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ic3RhdHVzIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9IjI3ZmFmYjU5LTQwZDktNDRiOS05NzlhLTdiODIwY2NjMWJhNiI+PGFjOnBhcmFtZXRlciBhYzpuYW1lPSJjb2xvdXIiPlJlZDwvYWM6cGFyYW1ldGVyPjwvYWM6c3RydWN0dXJlZC1tYWNybz4gPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ic3RhdHVzIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9ImNjMzc2NDE5LTFhYTAtNGZjMy05Mzc4LTRhOWUwZWZjNWE0ZSI+PGFjOnBhcmFtZXRlciBhYzpuYW1lPSJjb2xvdXIiPlB1cnBsZTwvYWM6cGFyYW1ldGVyPjwvYWM6c3RydWN0dXJlZC1tYWNybz48L3A+CjxoMz5TbWFydCBMaW5rczwvaDM+CjxwPjxhIGhyZWY9Imh0dHBzOi8va25wa3YtdGVhbS5hdGxhc3NpYW4ubmV0L2lzc3Vlcy8/anFsPXByb2plY3QlMjBpbiUyMChMRUFSTkpJUkEpJTIwT1JERVIlMjBCWSUyMGNyZWF0ZWQlMjBERVNDIiBkYXRhLWNhcmQtYXBwZWFyYW5jZT0iYmxvY2siIGRhdGEtZGF0YXNvdXJjZT0ieyZxdW90O2lkJnF1b3Q7OiZxdW90O2Q4Yjc1MzAwLWRmZGEtNDUxOS1iNmNkLWU0OWFiYmQ1MDQwMSZxdW90OywmcXVvdDtwYXJhbWV0ZXJzJnF1b3Q7OnsmcXVvdDtjbG91ZElkJnF1b3Q7OiZxdW90Ozg1ZmUyZDQ5LWQ4NmYtNGFmYS1iZTUzLWU3ZWM0MDhhNGQ1ZSZxdW90OywmcXVvdDtqcWwmcXVvdDs6JnF1b3Q7cHJvamVjdCBpbiAoTEVBUk5KSVJBKSBPUkRFUiBCWSBjcmVhdGVkIERFU0MmcXVvdDt9LCZxdW90O3ZpZXdzJnF1b3Q7Olt7JnF1b3Q7dHlwZSZxdW90OzomcXVvdDt0YWJsZSZxdW90OywmcXVvdDtwcm9wZXJ0aWVzJnF1b3Q7OnsmcXVvdDtjb2x1bW5zJnF1b3Q7Olt7JnF1b3Q7a2V5JnF1b3Q7OiZxdW90O2lzc3VldHlwZSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDtrZXkmcXVvdDt9LHsmcXVvdDtrZXkmcXVvdDs6JnF1b3Q7c3VtbWFyeSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDthc3NpZ25lZSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDtwcmlvcml0eSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDtzdGF0dXMmcXVvdDt9LHsmcXVvdDtrZXkmcXVvdDs6JnF1b3Q7dXBkYXRlZCZxdW90O31dfX1dfSI+aHR0cHM6Ly9rbnBrdi10ZWFtLmF0bGFzc2lhbi5uZXQvaXNzdWVzLz9qcWw9cHJvamVjdCUyMGluJTIwKExFQVJOSklSQSklMjBPUkRFUiUyMEJZJTIwY3JlYXRlZCUyMERFU0M8L2E+PC9wPgo8cD48YSBocmVmPSJodHRwczovL2tucGt2LXRlYW0uYXRsYXNzaWFuLm5ldC93aWtpL3NlYXJjaD90ZXh0PUdldHRpbmcrc3RhcnRlZCIgZGF0YS1jYXJkLWFwcGVhcmFuY2U9ImJsb2NrIiBkYXRhLWRhdGFzb3VyY2U9InsmcXVvdDtpZCZxdW90OzomcXVvdDs3NjhmYzczNi0zYWY0LTRhOGYtYjI3ZS0yMDM2MDJiZmY4Y2EmcXVvdDssJnF1b3Q7cGFyYW1ldGVycyZxdW90Ozp7JnF1b3Q7Y2xvdWRJZCZxdW90OzomcXVvdDs4NWZlMmQ0OS1kODZmLTRhZmEtYmU1My1lN2VjNDA4YTRkNWUmcXVvdDssJnF1b3Q7c2VhcmNoU3RyaW5nJnF1b3Q7OiZxdW90O0dldHRpbmcgc3RhcnRlZCZxdW90O30sJnF1b3Q7dmlld3MmcXVvdDs6W3smcXVvdDt0eXBlJnF1b3Q7OiZxdW90O3RhYmxlJnF1b3Q7LCZxdW90O3Byb3BlcnRpZXMmcXVvdDs6eyZxdW90O2NvbHVtbnMmcXVvdDs6W3smcXVvdDtrZXkmcXVvdDs6JnF1b3Q7dHlwZSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDt0aXRsZSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDtzcGFjZSZxdW90OywmcXVvdDt3aWR0aCZxdW90OzoyNDN9LHsmcXVvdDtrZXkmcXVvdDs6JnF1b3Q7ZGVzY3JpcHRpb24mcXVvdDt9LHsmcXVvdDtrZXkmcXVvdDs6JnF1b3Q7b3duZWRCeSZxdW90O30seyZxdW90O2tleSZxdW90OzomcXVvdDt1cGRhdGVkQXQmcXVvdDt9XX19XX0iPmh0dHBzOi8va25wa3YtdGVhbS5hdGxhc3NpYW4ubmV0L3dpa2kvc2VhcmNoP3RleHQ9R2V0dGluZytzdGFydGVkPC9hPjwvcD4KPGgzPlBhbmVsczwvaDM+CjxhYzpzdHJ1Y3R1cmVkLW1hY3JvIGFjOm5hbWU9ImluZm8iIGFjOnNjaGVtYS12ZXJzaW9uPSIxIiBhYzptYWNyby1pZD0iNWRjY2M3ZjEtZWRlMi00M2U2LTkyNmMtOWMzNmZjNDE0NGNlIj48YWM6cmljaC10ZXh0LWJvZHk+PHA+SW5mbyBwYW5lbDwvcD48L2FjOnJpY2gtdGV4dC1ib2R5PjwvYWM6c3RydWN0dXJlZC1tYWNybz4KPGFjOnN0cnVjdHVyZWQtbWFjcm8gYWM6bmFtZT0ibm90ZSIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSI1ODg0ZGE5Yi1kNzM4LTQ1YjMtOTgyMS1lOGRjODMyZTY2MDEiPjxhYzpyaWNoLXRleHQtYm9keT48cD5Ob3RlIHBhbmVsPC9wPjwvYWM6cmljaC10ZXh0LWJvZHk+PC9hYzpzdHJ1Y3R1cmVkLW1hY3JvPgo8YWM6c3RydWN0dXJlZC1tYWNybyBhYzpuYW1lPSJ3YXJuaW5nIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9ImMxYzI4MzI5LTIyOGMtNDJiNy04NDU2LWYyNTMwZjU2ZjJjMCI+PGFjOnJpY2gtdGV4dC1ib2R5PjxwPkVycm9yIHBhbmVsPC9wPjwvYWM6cmljaC10ZXh0LWJvZHk+PC9hYzpzdHJ1Y3R1cmVkLW1hY3JvPgo8YWM6c3RydWN0dXJlZC1tYWNybyBhYzpuYW1lPSJwYW5lbCIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSJmZTQ4OWY2Ny1mZTY5LTRhNTItYmQyNi05Y2MyM2VlYTgxNWMiPjxhYzpyaWNoLXRleHQtYm9keT48cD5DdXN0b20gcGFuZWw8L3A+PC9hYzpyaWNoLXRleHQtYm9keT48L2FjOnN0cnVjdHVyZWQtbWFjcm8+CjxhYzpzdHJ1Y3R1cmVkLW1hY3JvIGFjOm5hbWU9InRpcCIgYWM6c2NoZW1hLXZlcnNpb249IjEiIGFjOm1hY3JvLWlkPSI3N2FkN2RjZS1kNDg5LTQyYTEtOTk1ZC0wYTFkYjNhOWVmODMiPjxhYzpyaWNoLXRleHQtYm9keT48cD5TdWNjZXNzIHBhbmVsPC9wPjwvYWM6cmljaC10ZXh0LWJvZHk+PC9hYzpzdHJ1Y3R1cmVkLW1hY3JvPgo8YWM6c3RydWN0dXJlZC1tYWNybyBhYzpuYW1lPSJub3RlIiBhYzpzY2hlbWEtdmVyc2lvbj0iMSIgYWM6bWFjcm8taWQ9ImU1YzYzNTJlLWNmNmUtNDdjYi1hNDIzLWE5MTI4MzhkY2VjNCI+PGFjOnJpY2gtdGV4dC1ib2R5PjxwPldhcm5pbmcgcGFuZWw8L3A+PC9hYzpyaWNoLXRleHQtYm9keT48L2FjOnN0cnVjdHVyZWQtbWFjcm8+CjxoMz5PdGhlcjwvaDM+CjxwPkhlbGxvIDM8L3A+PC9hYzpsYXlvdXQtY2VsbD48L2FjOmxheW91dC1zZWN0aW9uPjwvYWM6bGF5b3V0Pg==-->
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem"
|
|
2
|
-
import * as NodePath from "@effect/platform-node/NodePath"
|
|
3
|
-
import * as FileSystem from "@effect/platform/FileSystem"
|
|
4
|
-
import * as Path from "@effect/platform/Path"
|
|
5
|
-
import { describe, expect, it } from "@effect/vitest"
|
|
6
|
-
import * as Effect from "effect/Effect"
|
|
7
|
-
import * as Layer from "effect/Layer"
|
|
8
|
-
import { parseConfluenceHtml } from "../../src/parsers/ConfluenceParser.js"
|
|
9
|
-
import { parseMarkdown } from "../../src/parsers/MarkdownParser.js"
|
|
10
|
-
import { serializeToConfluence } from "../../src/serializers/ConfluenceSerializer.js"
|
|
11
|
-
import { serializeToMarkdown } from "../../src/serializers/MarkdownSerializer.js"
|
|
12
|
-
|
|
13
|
-
const PlatformLive = Layer.mergeAll(NodeFileSystem.layer, NodePath.layer)
|
|
14
|
-
|
|
15
|
-
const getFixturesDir = Effect.gen(function*() {
|
|
16
|
-
const path = yield* Path.Path
|
|
17
|
-
return path.join(import.meta.dirname, "../fixtures")
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
const readFixture = (filename: string) =>
|
|
21
|
-
Effect.gen(function*() {
|
|
22
|
-
const fs = yield* FileSystem.FileSystem
|
|
23
|
-
const path = yield* Path.Path
|
|
24
|
-
const fixturesDir = yield* getFixturesDir
|
|
25
|
-
return yield* fs.readFileString(path.join(fixturesDir, filename))
|
|
26
|
-
}).pipe(Effect.provide(PlatformLive))
|
|
27
|
-
|
|
28
|
-
describe("ConfluenceParser", () => {
|
|
29
|
-
describe("parseConfluenceHtml", () => {
|
|
30
|
-
it.effect("parses basic headings", () =>
|
|
31
|
-
Effect.gen(function*() {
|
|
32
|
-
const html = "<h1>Title</h1><h2>Subtitle</h2>"
|
|
33
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
34
|
-
expect(doc.children.length).toBe(2)
|
|
35
|
-
expect(doc.children[0]?._tag).toBe("Heading")
|
|
36
|
-
}))
|
|
37
|
-
|
|
38
|
-
it.effect("parses paragraphs", () =>
|
|
39
|
-
Effect.gen(function*() {
|
|
40
|
-
const html = "<p>Hello world</p>"
|
|
41
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
42
|
-
expect(doc.children.length).toBe(1)
|
|
43
|
-
expect(doc.children[0]?._tag).toBe("Paragraph")
|
|
44
|
-
}))
|
|
45
|
-
|
|
46
|
-
it.effect("parses task lists", () =>
|
|
47
|
-
Effect.gen(function*() {
|
|
48
|
-
const html = `<ac:task-list ac:task-list-id="test">
|
|
49
|
-
<ac:task>
|
|
50
|
-
<ac:task-id>1</ac:task-id>
|
|
51
|
-
<ac:task-uuid>uuid-1</ac:task-uuid>
|
|
52
|
-
<ac:task-status>incomplete</ac:task-status>
|
|
53
|
-
<ac:task-body><span>Task 1</span></ac:task-body>
|
|
54
|
-
</ac:task>
|
|
55
|
-
<ac:task>
|
|
56
|
-
<ac:task-id>2</ac:task-id>
|
|
57
|
-
<ac:task-uuid>uuid-2</ac:task-uuid>
|
|
58
|
-
<ac:task-status>complete</ac:task-status>
|
|
59
|
-
<ac:task-body><span>Task 2</span></ac:task-body>
|
|
60
|
-
</ac:task>
|
|
61
|
-
</ac:task-list>`
|
|
62
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
63
|
-
expect(doc.children.length).toBe(1)
|
|
64
|
-
const taskList = doc.children[0]
|
|
65
|
-
expect(taskList?._tag).toBe("TaskList")
|
|
66
|
-
if (taskList?._tag === "TaskList") {
|
|
67
|
-
expect(taskList.children.length).toBe(2)
|
|
68
|
-
expect(taskList.children[0]?.status).toBe("incomplete")
|
|
69
|
-
expect(taskList.children[0]?.body[0]?._tag).toBe("Text")
|
|
70
|
-
expect(taskList.children[1]?.status).toBe("complete")
|
|
71
|
-
}
|
|
72
|
-
}))
|
|
73
|
-
|
|
74
|
-
it.effect("parses images with attachments", () =>
|
|
75
|
-
Effect.gen(function*() {
|
|
76
|
-
const html = `<ac:image ac:align="center" ac:width="250" ac:alt="logo">
|
|
77
|
-
<ri:attachment ri:filename="logo.svg"/>
|
|
78
|
-
</ac:image>`
|
|
79
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
80
|
-
expect(doc.children.length).toBe(1)
|
|
81
|
-
const image = doc.children[0]
|
|
82
|
-
expect(image?._tag).toBe("Image")
|
|
83
|
-
if (image?._tag === "Image") {
|
|
84
|
-
expect(image.attachment?.filename).toBe("logo.svg")
|
|
85
|
-
expect(image.align).toBe("center")
|
|
86
|
-
expect(image.width).toBe(250)
|
|
87
|
-
expect(image.alt).toBe("logo")
|
|
88
|
-
}
|
|
89
|
-
}))
|
|
90
|
-
|
|
91
|
-
it.effect("parses emoticons", () =>
|
|
92
|
-
Effect.gen(function*() {
|
|
93
|
-
const html =
|
|
94
|
-
`<p><ac:emoticon ac:emoji-shortname=":grinning:" ac:emoji-id="1f600" ac:emoji-fallback="smile"/></p>`
|
|
95
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
96
|
-
expect(doc.children.length).toBe(1)
|
|
97
|
-
const para = doc.children[0]
|
|
98
|
-
expect(para?._tag).toBe("Paragraph")
|
|
99
|
-
if (para?._tag === "Paragraph") {
|
|
100
|
-
const emoticon = para.children[0]
|
|
101
|
-
expect(emoticon?._tag).toBe("Emoticon")
|
|
102
|
-
if (emoticon?._tag === "Emoticon") {
|
|
103
|
-
expect(emoticon.shortname).toBe(":grinning:")
|
|
104
|
-
expect(emoticon.emojiId).toBe("1f600")
|
|
105
|
-
expect(emoticon.fallback).toBe("smile")
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}))
|
|
109
|
-
|
|
110
|
-
it.effect("parses user mentions", () =>
|
|
111
|
-
Effect.gen(function*() {
|
|
112
|
-
const html = `<p><ac:link><ri:user ri:account-id="557058:abc123"/></ac:link></p>`
|
|
113
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
114
|
-
expect(doc.children.length).toBe(1)
|
|
115
|
-
const para = doc.children[0]
|
|
116
|
-
if (para?._tag === "Paragraph") {
|
|
117
|
-
const mention = para.children[0]
|
|
118
|
-
expect(mention?._tag).toBe("UserMention")
|
|
119
|
-
if (mention?._tag === "UserMention") {
|
|
120
|
-
expect(mention.accountId).toBe("557058:abc123")
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}))
|
|
124
|
-
|
|
125
|
-
it.effect("parses colored text", () =>
|
|
126
|
-
Effect.gen(function*() {
|
|
127
|
-
const html = `<p><span style="color: rgb(255,0,0);">Red text</span></p>`
|
|
128
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
129
|
-
const para = doc.children[0]
|
|
130
|
-
if (para?._tag === "Paragraph") {
|
|
131
|
-
const colored = para.children[0]
|
|
132
|
-
expect(colored?._tag).toBe("ColoredText")
|
|
133
|
-
if (colored?._tag === "ColoredText") {
|
|
134
|
-
expect(colored.color).toBe("rgb(255,0,0)")
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}))
|
|
138
|
-
|
|
139
|
-
it.effect("parses highlighted text", () =>
|
|
140
|
-
Effect.gen(function*() {
|
|
141
|
-
const html = `<p><span style="background-color: rgb(255,255,0);">Highlighted</span></p>`
|
|
142
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
143
|
-
const para = doc.children[0]
|
|
144
|
-
if (para?._tag === "Paragraph") {
|
|
145
|
-
const highlight = para.children[0]
|
|
146
|
-
expect(highlight?._tag).toBe("Highlight")
|
|
147
|
-
if (highlight?._tag === "Highlight") {
|
|
148
|
-
expect(highlight.backgroundColor).toBe("rgb(255,255,0)")
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}))
|
|
152
|
-
|
|
153
|
-
it.effect("parses paragraph with alignment", () =>
|
|
154
|
-
Effect.gen(function*() {
|
|
155
|
-
const html = `<p style="text-align: center;">Centered</p>`
|
|
156
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
157
|
-
const para = doc.children[0]
|
|
158
|
-
expect(para?._tag).toBe("Paragraph")
|
|
159
|
-
if (para?._tag === "Paragraph") {
|
|
160
|
-
expect(para.alignment).toBe("center")
|
|
161
|
-
}
|
|
162
|
-
}))
|
|
163
|
-
|
|
164
|
-
it.effect("parses paragraph with indent", () =>
|
|
165
|
-
Effect.gen(function*() {
|
|
166
|
-
const html = `<p style="margin-left: 30px;">Indented</p>`
|
|
167
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
168
|
-
const para = doc.children[0]
|
|
169
|
-
expect(para?._tag === "Paragraph").toBe(true)
|
|
170
|
-
if (para?._tag === "Paragraph") {
|
|
171
|
-
expect(para.indent).toBe(30)
|
|
172
|
-
}
|
|
173
|
-
}))
|
|
174
|
-
|
|
175
|
-
it.effect("parses underline, subscript, superscript", () =>
|
|
176
|
-
Effect.gen(function*() {
|
|
177
|
-
const html = `<p><u>Underline</u> <sub>Sub</sub> <sup>Sup</sup></p>`
|
|
178
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
179
|
-
const para = doc.children[0]
|
|
180
|
-
if (para?._tag === "Paragraph") {
|
|
181
|
-
const [u, , sub, , sup] = para.children
|
|
182
|
-
expect(u?._tag).toBe("Underline")
|
|
183
|
-
expect(sub?._tag).toBe("Subscript")
|
|
184
|
-
expect(sup?._tag).toBe("Superscript")
|
|
185
|
-
}
|
|
186
|
-
}))
|
|
187
|
-
|
|
188
|
-
it.effect("parses tables with proper cell content", () =>
|
|
189
|
-
Effect.gen(function*() {
|
|
190
|
-
const html = `<table>
|
|
191
|
-
<thead><tr><th><p>Header 1</p></th><th><p>Header 2</p></th></tr></thead>
|
|
192
|
-
<tbody><tr><td><p>Cell 1</p></td><td><p>Cell 2</p></td></tr></tbody>
|
|
193
|
-
</table>`
|
|
194
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
195
|
-
const table = doc.children[0]
|
|
196
|
-
expect(table?._tag).toBe("Table")
|
|
197
|
-
if (table?._tag === "Table") {
|
|
198
|
-
// Header should have 2 cells
|
|
199
|
-
expect(table.header?.cells.length).toBe(2)
|
|
200
|
-
// First header cell should have text "Header 1"
|
|
201
|
-
const headerCell = table.header?.cells[0]
|
|
202
|
-
if (headerCell) {
|
|
203
|
-
expect(headerCell.children[0]?._tag).toBe("Text")
|
|
204
|
-
if (headerCell.children[0]?._tag === "Text") {
|
|
205
|
-
expect(headerCell.children[0].value).toBe("Header 1")
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Body rows
|
|
209
|
-
expect(table.rows.length).toBe(1)
|
|
210
|
-
const bodyCell = table.rows[0]?.cells[0]
|
|
211
|
-
if (bodyCell) {
|
|
212
|
-
expect(bodyCell.children[0]?._tag).toBe("Text")
|
|
213
|
-
if (bodyCell.children[0]?._tag === "Text") {
|
|
214
|
-
expect(bodyCell.children[0].value).toBe("Cell 1")
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}))
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
describe("integration test fixture", () => {
|
|
222
|
-
it.effect("parses and serializes integration test fixture", () =>
|
|
223
|
-
Effect.gen(function*() {
|
|
224
|
-
const html = yield* readFixture("integration-test.html.fixture")
|
|
225
|
-
const doc = yield* parseConfluenceHtml(html)
|
|
226
|
-
|
|
227
|
-
// Doc should have children - layout markers plus actual content
|
|
228
|
-
expect(doc.children.length).toBeGreaterThan(0)
|
|
229
|
-
|
|
230
|
-
// Serialize to markdown - content should be readable (not URL-encoded)
|
|
231
|
-
const markdown = yield* serializeToMarkdown(doc)
|
|
232
|
-
// Layout markers should be present as comments
|
|
233
|
-
expect(markdown).toContain("cf:layout-start")
|
|
234
|
-
expect(markdown).toContain("cf:section:")
|
|
235
|
-
// Content should be readable markdown, not URL-encoded
|
|
236
|
-
expect(markdown).toContain("# Heading 1")
|
|
237
|
-
expect(markdown).toContain("## Heading 2")
|
|
238
|
-
|
|
239
|
-
// Serialize to confluence - should reconstruct layout structure
|
|
240
|
-
const confluenceHtml = yield* serializeToConfluence(doc)
|
|
241
|
-
expect(confluenceHtml).toContain("<ac:layout>")
|
|
242
|
-
expect(confluenceHtml).toContain("<ac:layout-section")
|
|
243
|
-
expect(confluenceHtml).toContain("<ac:layout-cell>")
|
|
244
|
-
// Note: ac:task-list may have attributes, so search for the tag name
|
|
245
|
-
expect(confluenceHtml).toContain("ac:task-list")
|
|
246
|
-
expect(confluenceHtml).toContain("<ac:task-status>incomplete</ac:task-status>")
|
|
247
|
-
expect(confluenceHtml).toContain("<ac:task-status>complete</ac:task-status>")
|
|
248
|
-
expect(confluenceHtml).toContain("ac:emoticon")
|
|
249
|
-
expect(confluenceHtml).toContain("ri:account-id")
|
|
250
|
-
}))
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
describe("roundtrip HTML -> MD -> HTML", () => {
|
|
254
|
-
it.effect("roundtrips integration-test.html with 1-to-1 preservation", () =>
|
|
255
|
-
Effect.gen(function*() {
|
|
256
|
-
const originalHtml = yield* readFixture("integration-test.html.fixture")
|
|
257
|
-
const doc1 = yield* parseConfluenceHtml(originalHtml)
|
|
258
|
-
const md = yield* serializeToMarkdown(doc1)
|
|
259
|
-
const doc2 = yield* parseMarkdown(md)
|
|
260
|
-
const finalHtml = yield* serializeToConfluence(doc2)
|
|
261
|
-
|
|
262
|
-
// 1-to-1 roundtrip: finalHtml should EXACTLY match originalHtml
|
|
263
|
-
expect(finalHtml).toBe(originalHtml)
|
|
264
|
-
}))
|
|
265
|
-
|
|
266
|
-
it.effect("roundtrips TOC macro", () =>
|
|
267
|
-
Effect.gen(function*() {
|
|
268
|
-
const html =
|
|
269
|
-
`<ac:structured-macro ac:name="toc"><ac:parameter ac:name="minLevel">2</ac:parameter></ac:structured-macro>`
|
|
270
|
-
const doc1 = yield* parseConfluenceHtml(html)
|
|
271
|
-
expect(doc1.children[0]?._tag).toBe("TocMacro")
|
|
272
|
-
|
|
273
|
-
const md = yield* serializeToMarkdown(doc1)
|
|
274
|
-
expect(md).toContain("[[toc]]")
|
|
275
|
-
|
|
276
|
-
const doc2 = yield* parseMarkdown(md)
|
|
277
|
-
expect(doc2.children[0]?._tag).toBe("TocMacro")
|
|
278
|
-
|
|
279
|
-
const html2 = yield* serializeToConfluence(doc2)
|
|
280
|
-
expect(html2).toContain("<ac:structured-macro ac:name=\"toc\">")
|
|
281
|
-
}))
|
|
282
|
-
})
|
|
283
|
-
})
|