@fuzdev/mdz 0.1.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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +32 -0
  3. package/dist/Mdz.svelte +39 -0
  4. package/dist/Mdz.svelte.d.ts +17 -0
  5. package/dist/Mdz.svelte.d.ts.map +1 -0
  6. package/dist/MdzNodeView.svelte +140 -0
  7. package/dist/MdzNodeView.svelte.d.ts +9 -0
  8. package/dist/MdzNodeView.svelte.d.ts.map +1 -0
  9. package/dist/MdzPrecompiled.svelte +33 -0
  10. package/dist/MdzPrecompiled.svelte.d.ts +17 -0
  11. package/dist/MdzPrecompiled.svelte.d.ts.map +1 -0
  12. package/dist/MdzRoot.svelte +40 -0
  13. package/dist/MdzRoot.svelte.d.ts +14 -0
  14. package/dist/MdzRoot.svelte.d.ts.map +1 -0
  15. package/dist/MdzStream.svelte +37 -0
  16. package/dist/MdzStream.svelte.d.ts +18 -0
  17. package/dist/MdzStream.svelte.d.ts.map +1 -0
  18. package/dist/MdzStreamNodeView.svelte +150 -0
  19. package/dist/MdzStreamNodeView.svelte.d.ts +9 -0
  20. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -0
  21. package/dist/context_helpers.d.ts +23 -0
  22. package/dist/context_helpers.d.ts.map +1 -0
  23. package/dist/context_helpers.js +19 -0
  24. package/dist/mdz.d.ts +152 -0
  25. package/dist/mdz.d.ts.map +1 -0
  26. package/dist/mdz.js +61 -0
  27. package/dist/mdz_contexts.d.ts +106 -0
  28. package/dist/mdz_contexts.d.ts.map +1 -0
  29. package/dist/mdz_contexts.js +42 -0
  30. package/dist/mdz_helpers.d.ts +321 -0
  31. package/dist/mdz_helpers.d.ts.map +1 -0
  32. package/dist/mdz_helpers.js +668 -0
  33. package/dist/mdz_lexer.d.ts +123 -0
  34. package/dist/mdz_lexer.d.ts.map +1 -0
  35. package/dist/mdz_lexer.js +1405 -0
  36. package/dist/mdz_opcodes.d.ts +193 -0
  37. package/dist/mdz_opcodes.d.ts.map +1 -0
  38. package/dist/mdz_opcodes.js +14 -0
  39. package/dist/mdz_opcodes_to_nodes.d.ts +26 -0
  40. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -0
  41. package/dist/mdz_opcodes_to_nodes.js +410 -0
  42. package/dist/mdz_stream_parser.d.ts +109 -0
  43. package/dist/mdz_stream_parser.d.ts.map +1 -0
  44. package/dist/mdz_stream_parser.js +699 -0
  45. package/dist/mdz_stream_parser_block.d.ts +99 -0
  46. package/dist/mdz_stream_parser_block.d.ts.map +1 -0
  47. package/dist/mdz_stream_parser_block.js +437 -0
  48. package/dist/mdz_stream_parser_inline.d.ts +98 -0
  49. package/dist/mdz_stream_parser_inline.d.ts.map +1 -0
  50. package/dist/mdz_stream_parser_inline.js +481 -0
  51. package/dist/mdz_stream_parser_link.d.ts +29 -0
  52. package/dist/mdz_stream_parser_link.d.ts.map +1 -0
  53. package/dist/mdz_stream_parser_link.js +227 -0
  54. package/dist/mdz_stream_parser_list.d.ts +106 -0
  55. package/dist/mdz_stream_parser_list.d.ts.map +1 -0
  56. package/dist/mdz_stream_parser_list.js +438 -0
  57. package/dist/mdz_stream_parser_state.d.ts +476 -0
  58. package/dist/mdz_stream_parser_state.d.ts.map +1 -0
  59. package/dist/mdz_stream_parser_state.js +651 -0
  60. package/dist/mdz_stream_parser_text.d.ts +25 -0
  61. package/dist/mdz_stream_parser_text.d.ts.map +1 -0
  62. package/dist/mdz_stream_parser_text.js +67 -0
  63. package/dist/mdz_stream_parser_url.d.ts +76 -0
  64. package/dist/mdz_stream_parser_url.d.ts.map +1 -0
  65. package/dist/mdz_stream_parser_url.js +328 -0
  66. package/dist/mdz_stream_state.svelte.d.ts +53 -0
  67. package/dist/mdz_stream_state.svelte.d.ts.map +1 -0
  68. package/dist/mdz_stream_state.svelte.js +360 -0
  69. package/dist/mdz_to_svelte.d.ts +71 -0
  70. package/dist/mdz_to_svelte.d.ts.map +1 -0
  71. package/dist/mdz_to_svelte.js +127 -0
  72. package/dist/mdz_token_parser.d.ts +20 -0
  73. package/dist/mdz_token_parser.d.ts.map +1 -0
  74. package/dist/mdz_token_parser.js +449 -0
  75. package/dist/svelte_preprocess_mdz.d.ts +80 -0
  76. package/dist/svelte_preprocess_mdz.d.ts.map +1 -0
  77. package/dist/svelte_preprocess_mdz.js +543 -0
  78. package/dist/tsdoc_mdz.d.ts +45 -0
  79. package/dist/tsdoc_mdz.d.ts.map +1 -0
  80. package/dist/tsdoc_mdz.js +88 -0
  81. package/package.json +131 -0
  82. package/src/lib/context_helpers.ts +52 -0
  83. package/src/lib/mdz.ts +190 -0
  84. package/src/lib/mdz_contexts.ts +99 -0
  85. package/src/lib/mdz_helpers.ts +727 -0
  86. package/src/lib/mdz_lexer.ts +1761 -0
  87. package/src/lib/mdz_opcodes.ts +227 -0
  88. package/src/lib/mdz_opcodes_to_nodes.ts +470 -0
  89. package/src/lib/mdz_stream_parser.ts +766 -0
  90. package/src/lib/mdz_stream_parser_block.ts +490 -0
  91. package/src/lib/mdz_stream_parser_inline.ts +527 -0
  92. package/src/lib/mdz_stream_parser_link.ts +283 -0
  93. package/src/lib/mdz_stream_parser_list.ts +525 -0
  94. package/src/lib/mdz_stream_parser_state.ts +957 -0
  95. package/src/lib/mdz_stream_parser_text.ts +95 -0
  96. package/src/lib/mdz_stream_parser_url.ts +386 -0
  97. package/src/lib/mdz_stream_state.svelte.ts +383 -0
  98. package/src/lib/mdz_to_svelte.ts +213 -0
  99. package/src/lib/mdz_token_parser.ts +544 -0
  100. package/src/lib/svelte_preprocess_mdz.ts +777 -0
  101. package/src/lib/tsdoc_mdz.ts +97 -0
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Markdown link `[text](url)` handlers and HTML-style tag handlers
3
+ * (`<tag>`, `<Tag />`, `</tag>`) for the streaming mdz parser.
4
+ *
5
+ * @module
6
+ */
7
+ import { A_UPPER, LEFT_BRACKET, LEFT_PAREN, NEWLINE, RIGHT_ANGLE, RIGHT_BRACKET, RIGHT_PAREN, SLASH, SPACE, Z_UPPER, is_letter, is_tag_name_char, is_valid_path_char, mdz_is_safe_reference, mdz_is_url, } from './mdz_helpers.js';
8
+ import { accumulate_text, alloc_id, emit, ensure_paragraph, flush_text, is_run_boundary, offset, pop_stack_entry, push_stack_entry, revert_above, } from './mdz_stream_parser_state.js';
9
+ // -- Links --
10
+ /** @nodocs */
11
+ export const try_link_open = (state) => {
12
+ flush_text(state);
13
+ ensure_paragraph(state);
14
+ const id = alloc_id(state);
15
+ const link_start = offset(state);
16
+ emit(state, { type: 'open', id, node_type: 'Link', start: link_start });
17
+ push_stack_entry(state, id, 'Link', link_start, '[');
18
+ state.active_text_id = null;
19
+ state.pos++;
20
+ state.column++;
21
+ state.prev_char = LEFT_BRACKET;
22
+ return true;
23
+ };
24
+ /**
25
+ * Revert an optimistic Link container to its literal `[` and consume the
26
+ * `]` at `bracket_pos` as text. Used whenever a `]` doesn't form a valid
27
+ * `[text](url)` — missing `(`, empty url, invalid url char, etc.
28
+ *
29
+ * @nodocs
30
+ */
31
+ export const abort_link_to_text = (state, link_stack_idx, bracket_pos) => {
32
+ flush_text(state);
33
+ revert_above(state, link_stack_idx);
34
+ const entry = pop_stack_entry(state);
35
+ emit(state, { type: 'revert', id: entry.id, replacement_text: '[', start: entry.start });
36
+ state.active_text_id = null;
37
+ accumulate_text(state, ']', offset(state, bracket_pos));
38
+ state.pos = bracket_pos + 1;
39
+ state.column++;
40
+ state.prev_char = RIGHT_BRACKET;
41
+ };
42
+ /**
43
+ * Try to complete a link after seeing `]`.
44
+ * Returns false if more input is needed (leaves pos at `]`).
45
+ *
46
+ * @nodocs
47
+ */
48
+ export const try_complete_link = (state, link_stack_idx) => {
49
+ const bracket_pos = state.pos;
50
+ // need at least one char after ]
51
+ if (bracket_pos + 1 >= state.buffer.length)
52
+ return false;
53
+ const after_bracket = state.buffer.charCodeAt(bracket_pos + 1);
54
+ if (after_bracket !== LEFT_PAREN) {
55
+ abort_link_to_text(state, link_stack_idx, bracket_pos);
56
+ return true;
57
+ }
58
+ // found ]( — scan for )
59
+ let i = bracket_pos + 2; // past ](
60
+ while (i < state.buffer.length) {
61
+ const c = state.buffer.charCodeAt(i);
62
+ if (c === RIGHT_PAREN) {
63
+ const reference = state.buffer.slice(bracket_pos + 2, i);
64
+ // validate reference: non-empty and only valid path chars
65
+ if (!reference.trim()) {
66
+ abort_link_to_text(state, link_stack_idx, bracket_pos);
67
+ return true;
68
+ }
69
+ let valid = true;
70
+ for (let j = 0; j < reference.length; j++) {
71
+ if (!is_valid_path_char(reference.charCodeAt(j))) {
72
+ valid = false;
73
+ break;
74
+ }
75
+ }
76
+ if (!valid) {
77
+ abort_link_to_text(state, link_stack_idx, bracket_pos);
78
+ return true;
79
+ }
80
+ // Reject unsafe protocols (javascript:, data:, etc.) at parse time.
81
+ // `is_valid_path_char` permits `:` so we have to filter explicitly.
82
+ if (!mdz_is_safe_reference(reference)) {
83
+ abort_link_to_text(state, link_stack_idx, bracket_pos);
84
+ return true;
85
+ }
86
+ // success — close the link with reference
87
+ flush_text(state);
88
+ revert_above(state, link_stack_idx);
89
+ const entry = pop_stack_entry(state);
90
+ const link_type = mdz_is_url(reference) ? 'external' : 'internal';
91
+ emit(state, {
92
+ type: 'close',
93
+ id: entry.id,
94
+ end: offset(state, i + 1),
95
+ reference,
96
+ link_type,
97
+ });
98
+ state.active_text_id = null;
99
+ state.pos = i + 1;
100
+ state.column += i + 1 - bracket_pos;
101
+ state.prev_char = RIGHT_PAREN;
102
+ return true;
103
+ }
104
+ if (c === NEWLINE || c === SPACE) {
105
+ // invalid character in URL (simplified check)
106
+ break;
107
+ }
108
+ i++;
109
+ }
110
+ // didn't find ) — need more input or invalid
111
+ if (i >= state.buffer.length)
112
+ return false; // need more input
113
+ // found invalid char before ) — revert
114
+ abort_link_to_text(state, link_stack_idx, bracket_pos);
115
+ return true;
116
+ };
117
+ // -- Tags --
118
+ /** @nodocs */
119
+ export const try_tag_open = (state) => {
120
+ const start = state.pos;
121
+ let i = start + 1; // past <
122
+ // tag name must start with letter. A `<` at the end of the buffer can't
123
+ // classify yet — it may be a tag open, or the closing tag of an open
124
+ // element (whose `</` dispatch needs the next char); consuming it as text
125
+ // here would eat the closer and strand the element. Hold, like the
126
+ // mid-name case below.
127
+ if (i >= state.buffer.length)
128
+ return 'need_more';
129
+ if (!is_letter(state.buffer.charCodeAt(i))) {
130
+ return 'not_match';
131
+ }
132
+ // collect tag name
133
+ const name_start = i;
134
+ while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
135
+ i++;
136
+ }
137
+ if (i >= state.buffer.length)
138
+ return 'need_more';
139
+ const name = state.buffer.slice(name_start, i);
140
+ // skip whitespace
141
+ while (i < state.buffer.length && state.buffer.charCodeAt(i) === SPACE) {
142
+ i++;
143
+ }
144
+ if (i >= state.buffer.length)
145
+ return 'need_more';
146
+ // check for self-closing />
147
+ if (state.buffer.charCodeAt(i) === SLASH && i + 1 >= state.buffer.length) {
148
+ return 'need_more'; // `/` at the buffer end can't classify yet (`/>` or junk)
149
+ }
150
+ if (state.buffer.charCodeAt(i) === SLASH && state.buffer.charCodeAt(i + 1) === RIGHT_ANGLE) {
151
+ const first_char = name.charCodeAt(0);
152
+ const is_component = first_char >= A_UPPER && first_char <= Z_UPPER;
153
+ const node_type = is_component ? 'Component' : 'Element';
154
+ flush_text(state);
155
+ ensure_paragraph(state);
156
+ const id = alloc_id(state);
157
+ const tag_start = offset(state, start);
158
+ emit(state, { type: 'open', id, node_type, start: tag_start, name });
159
+ emit(state, { type: 'close', id, end: offset(state, i + 2) });
160
+ state.active_text_id = null;
161
+ state.pos = i + 2;
162
+ state.column += i + 2 - start;
163
+ state.prev_char = RIGHT_ANGLE;
164
+ return 'consumed';
165
+ }
166
+ // check for >
167
+ if (state.buffer.charCodeAt(i) !== RIGHT_ANGLE) {
168
+ return 'not_match';
169
+ }
170
+ i++; // past >
171
+ const first_char = name.charCodeAt(0);
172
+ const is_component = first_char >= A_UPPER && first_char <= Z_UPPER;
173
+ const node_type = is_component ? 'Component' : 'Element';
174
+ flush_text(state);
175
+ ensure_paragraph(state);
176
+ const id = alloc_id(state);
177
+ const tag_start = offset(state, start);
178
+ const delimiter = state.buffer.slice(start, i); // e.g., "<Alert>"
179
+ emit(state, { type: 'open', id, node_type, start: tag_start, name });
180
+ push_stack_entry(state, id, node_type, tag_start, delimiter, name);
181
+ state.active_text_id = null;
182
+ state.pos = i;
183
+ state.column += i - start;
184
+ state.prev_char = RIGHT_ANGLE;
185
+ return 'consumed';
186
+ };
187
+ /** @nodocs */
188
+ export const try_close_tag = (state) => {
189
+ // we're at </
190
+ let i = state.pos + 2; // past </
191
+ if (i >= state.buffer.length)
192
+ return 'need_more';
193
+ // collect tag name
194
+ const name_start = i;
195
+ while (i < state.buffer.length && is_tag_name_char(state.buffer.charCodeAt(i))) {
196
+ i++;
197
+ }
198
+ if (i >= state.buffer.length)
199
+ return 'need_more';
200
+ if (state.buffer.charCodeAt(i) !== RIGHT_ANGLE)
201
+ return 'not_match';
202
+ const name = state.buffer.slice(name_start, i);
203
+ i++; // past >
204
+ // find matching open tag on stack
205
+ let found_idx = -1;
206
+ for (let j = state.stack.length - 1; j >= 0; j--) {
207
+ const entry = state.stack[j];
208
+ if ((entry.node_type === 'Element' || entry.node_type === 'Component') &&
209
+ entry.tag_name === name) {
210
+ found_idx = j;
211
+ break;
212
+ }
213
+ if (is_run_boundary(entry.node_type))
214
+ break;
215
+ }
216
+ if (found_idx === -1)
217
+ return 'not_match';
218
+ flush_text(state);
219
+ revert_above(state, found_idx);
220
+ const entry = pop_stack_entry(state);
221
+ emit(state, { type: 'close', id: entry.id, end: offset(state, i) });
222
+ state.active_text_id = null;
223
+ state.column += i - state.pos;
224
+ state.pos = i;
225
+ state.prev_char = RIGHT_ANGLE;
226
+ return 'consumed';
227
+ };
@@ -0,0 +1,106 @@
1
+ /**
2
+ * List handlers for the streaming mdz parser — column-0 list starts and the
3
+ * line classifier that drives structure inside open lists.
4
+ *
5
+ * The streaming analogue of the sync lexer's `#tokenize_list`: the level
6
+ * stack lives in `state.list_levels`, and List/ListItem frames sit on the
7
+ * main stack (never reverted, like Paragraph). Every line inside an open
8
+ * list classifies from bounded input — leading whitespace plus a few marker
9
+ * chars for most lines, the whole line for empty markers and fence openers —
10
+ * and blank-run containment consumes one blank line at a time, holding only
11
+ * the structural close-vs-contain decision, never renderable content.
12
+ *
13
+ * @module
14
+ */
15
+ import { type MdzStreamParserState, type TryResult } from './mdz_stream_parser_state.js';
16
+ /**
17
+ * A matched list marker: `- ` (unordered) or `N. ` (ordered, 1-9 digits).
18
+ * `empty` markers have nothing but whitespace after the marker — valid only
19
+ * as mid-list empty items, never starting a list. Positions are local buffer
20
+ * indices.
21
+ *
22
+ * @nodocs
23
+ */
24
+ export interface MdzStreamListMarker {
25
+ ordered: boolean;
26
+ /** Parsed integer for ordered markers (`007.` → 7); `0` for unordered. */
27
+ number: number;
28
+ /** Local buffer position of the marker character. */
29
+ start: number;
30
+ /** Index just past the marker chars (`-` or `N.`), before the space. */
31
+ marker_end: number;
32
+ /** Index just past the marker space — the inline run's content. */
33
+ content_start: number;
34
+ empty: boolean;
35
+ }
36
+ /**
37
+ * Match a list marker at `i` — the streaming analogue of the sync lexer's
38
+ * `#match_list_marker`, with `'pending'` for a marker that can't resolve
39
+ * until more input arrives (digits cut off at buffer end, a possible empty
40
+ * marker whose line hasn't ended). Returns null for anything that's
41
+ * definitely not a marker (`-x`, `--`, a tab after the marker, 10+ digits).
42
+ *
43
+ * @nodocs
44
+ */
45
+ export declare const match_list_marker: (state: MdzStreamParserState, i: number, forced: boolean) => MdzStreamListMarker | "pending" | null;
46
+ /**
47
+ * A matched fence opener line (see `match_fence_opener`).
48
+ *
49
+ * @nodocs
50
+ */
51
+ export interface MdzStreamFenceOpener {
52
+ backtick_count: number;
53
+ lang: string | null;
54
+ /** Local position just past the opener's newline. */
55
+ content_start: number;
56
+ }
57
+ /**
58
+ * Match a complete fence opener line at `i`: >=3 backticks, an optional
59
+ * language word, only whitespace to a terminating newline. `'pending'` while
60
+ * the line could still become one; null when it definitely isn't — including
61
+ * an opener cut off at EOF without its newline (forced), which stays text,
62
+ * matching the sync parser's opener rule.
63
+ *
64
+ * @nodocs
65
+ */
66
+ export declare const match_fence_opener: (state: MdzStreamParserState, i: number, forced: boolean) => MdzStreamFenceOpener | "pending" | null;
67
+ /**
68
+ * Try to start a list at a column-0 marker line — `try_hr` has already
69
+ * rejected `---` when the char is `-`. Mirrors the sync lexer's
70
+ * `#tokenize_list` entry conditions: empty markers never start a list, the
71
+ * marker line must show content before the open confirms (a line-bounded
72
+ * hold, the heading has-content pattern), and a non-`1.` ordered marker
73
+ * never interrupts a current paragraph (the hard-wrap `2024.` guard).
74
+ *
75
+ * @nodocs
76
+ */
77
+ export declare const try_list_start: (state: MdzStreamParserState, forced: boolean) => TryResult;
78
+ /**
79
+ * Classify the line after a newline inside an open list and apply its
80
+ * structural effect — the streaming analogue of the sync lexer's
81
+ * `#tokenize_list` line loop plus `#list_run_end`'s continuation rules.
82
+ * Called with `pos` at the newline; returns `false` to hold until the line
83
+ * classifies (never in forced mode). Dispatch, in order:
84
+ *
85
+ * - blank line: the current run closes; the close-vs-contain decision waits
86
+ * on the next non-blank line (one blank consumes per call, so long blank
87
+ * runs make incremental progress across chunks)
88
+ * - marker lines: nest when deeper (subject to the ordered interrupt guard
89
+ * against a current run), else snap to the nearest surviving level as a
90
+ * sibling or a marker-type switch; empty markers at an exactly-matching
91
+ * level form empty items
92
+ * - deeper fence opener lines: a code block child of the item they'd
93
+ * otherwise continue (`process_codeblock_in_item` streams it)
94
+ * - deeper quote opener lines (a prefix with content after it): the levels
95
+ * pop to the attach target and `'blockquote'` returns with `pos` at the prefix —
96
+ * the main loop opens the blockquote child (the quote machinery lives
97
+ * there, beside the nested-parser driving)
98
+ * - deeper non-marker lines: continuation of a current run (soft break,
99
+ * indent stripped), else a new `Paragraph` block child
100
+ * - anything else: the whole list closes and the line re-dispatches at the
101
+ * top level
102
+ *
103
+ * @nodocs
104
+ */
105
+ export declare const process_list_line: (state: MdzStreamParserState, forced: boolean) => boolean | "blockquote";
106
+ //# sourceMappingURL=mdz_stream_parser_list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mdz_stream_parser_list.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_stream_parser_list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAiBH,OAAO,EACN,KAAK,oBAAoB,EACzB,KAAK,SAAS,EAad,MAAM,8BAA8B,CAAC;AAEtC;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CACf;AAoBD;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,GAC7B,OAAO,oBAAoB,EAC3B,GAAG,MAAM,EACT,QAAQ,OAAO,KACb,mBAAmB,GAAG,SAAS,GAAG,IAsDpC,CAAC;AAEF;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qDAAqD;IACrD,aAAa,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,OAAO,oBAAoB,EAC3B,GAAG,MAAM,EACT,QAAQ,OAAO,KACb,oBAAoB,GAAG,SAAS,GAAG,IAuBrC,CAAC;AAgGF;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,oBAAoB,EAAE,QAAQ,OAAO,KAAG,SAY7E,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,GAC7B,OAAO,oBAAoB,EAC3B,QAAQ,OAAO,KACb,OAAO,GAAG,YA2KZ,CAAC"}