@khairold/xml-render 0.1.1 → 0.1.4
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/README.md +29 -5
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/parser.d.ts +2 -86
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +138 -82
- package/dist/parser.js.map +1 -1
- package/dist/react/ErrorBoundary.d.ts +2 -15
- package/dist/react/ErrorBoundary.d.ts.map +1 -1
- package/dist/react/ErrorBoundary.js +18 -43
- package/dist/react/ErrorBoundary.js.map +1 -1
- package/dist/react/XmlRender.d.ts +4 -2
- package/dist/react/XmlRender.d.ts.map +1 -1
- package/dist/react/XmlRender.js +4 -28
- package/dist/react/XmlRender.js.map +1 -1
- package/dist/react/catalog.d.ts +2 -77
- package/dist/react/catalog.d.ts.map +1 -1
- package/dist/react/catalog.js +3 -34
- package/dist/react/catalog.js.map +1 -1
- package/dist/react/context.d.ts +2 -47
- package/dist/react/context.d.ts.map +1 -1
- package/dist/react/context.js +2 -44
- package/dist/react/context.js.map +1 -1
- package/dist/react-native/ErrorBoundary.d.ts +2 -15
- package/dist/react-native/ErrorBoundary.d.ts.map +1 -1
- package/dist/react-native/ErrorBoundary.js +9 -34
- package/dist/react-native/ErrorBoundary.js.map +1 -1
- package/dist/react-native/XmlRender.d.ts +4 -2
- package/dist/react-native/XmlRender.d.ts.map +1 -1
- package/dist/react-native/XmlRender.js +4 -28
- package/dist/react-native/XmlRender.js.map +1 -1
- package/dist/react-native/catalog.d.ts +2 -77
- package/dist/react-native/catalog.d.ts.map +1 -1
- package/dist/react-native/catalog.js +3 -34
- package/dist/react-native/catalog.js.map +1 -1
- package/dist/react-native/context.d.ts +2 -47
- package/dist/react-native/context.d.ts.map +1 -1
- package/dist/react-native/context.js +2 -44
- package/dist/react-native/context.js.map +1 -1
- package/dist/registry.d.ts +2 -51
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js.map +1 -1
- package/dist/shared/ErrorBoundary.d.ts +43 -0
- package/dist/shared/ErrorBoundary.d.ts.map +1 -0
- package/dist/shared/ErrorBoundary.js +38 -0
- package/dist/shared/ErrorBoundary.js.map +1 -0
- package/dist/shared/catalog.d.ts +79 -0
- package/dist/shared/catalog.d.ts.map +1 -0
- package/dist/shared/catalog.js +32 -0
- package/dist/shared/catalog.js.map +1 -0
- package/dist/shared/context.d.ts +42 -0
- package/dist/shared/context.d.ts.map +1 -0
- package/dist/shared/context.js +39 -0
- package/dist/shared/context.js.map +1 -0
- package/dist/shared/renderSegment.d.ts +26 -0
- package/dist/shared/renderSegment.d.ts.map +1 -0
- package/dist/shared/renderSegment.js +30 -0
- package/dist/shared/renderSegment.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ const segments = parser.parse('Hello <callout type="info">World</callout>');
|
|
|
137
137
|
For real-time content streaming (e.g., LLM responses):
|
|
138
138
|
|
|
139
139
|
```ts
|
|
140
|
-
import { createParser, type ParserState } from '@khairold/xml-render';
|
|
140
|
+
import { createParser, type ParserState, type Segments } from '@khairold/xml-render';
|
|
141
141
|
|
|
142
142
|
const parser = createParser(registry);
|
|
143
143
|
|
|
@@ -155,10 +155,9 @@ function onChunk(chunk: string) {
|
|
|
155
155
|
// Append complete segments
|
|
156
156
|
allSegments = [...allSegments, ...result.segments];
|
|
157
157
|
|
|
158
|
-
//
|
|
159
|
-
if (result.
|
|
160
|
-
|
|
161
|
-
showPlaceholder(result.bufferingTag);
|
|
158
|
+
// Access partial content of in-progress tags
|
|
159
|
+
if (result.partialSegment) {
|
|
160
|
+
console.log(`Streaming <${result.partialSegment.type}>: ${result.partialSegment.content}`);
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -169,6 +168,30 @@ function onStreamEnd() {
|
|
|
169
168
|
}
|
|
170
169
|
```
|
|
171
170
|
|
|
171
|
+
#### Rendering partial segments
|
|
172
|
+
|
|
173
|
+
Pass `partialSegment` to `XmlRender` to render in-progress tags alongside completed segments. Renderer components receive a `streaming` prop so they can show streaming UI (e.g., a blinking cursor):
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
const catalog = createCatalog(registry, {
|
|
177
|
+
components: {
|
|
178
|
+
codeblock: ({ segment, streaming }) => (
|
|
179
|
+
<pre>
|
|
180
|
+
{segment.content}
|
|
181
|
+
{streaming && <span className="cursor-blink">|</span>}
|
|
182
|
+
</pre>
|
|
183
|
+
),
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// In your component
|
|
188
|
+
<XmlRender
|
|
189
|
+
segments={allSegments}
|
|
190
|
+
catalog={catalog}
|
|
191
|
+
partialSegment={result.partialSegment}
|
|
192
|
+
/>
|
|
193
|
+
```
|
|
194
|
+
|
|
172
195
|
### React Renderer
|
|
173
196
|
|
|
174
197
|
```tsx
|
|
@@ -249,6 +272,7 @@ The React Native API is identical to React. The only differences are:
|
|
|
249
272
|
```ts
|
|
250
273
|
import {
|
|
251
274
|
type ParsedSegment,
|
|
275
|
+
type PartialSegment,
|
|
252
276
|
type Segments,
|
|
253
277
|
type SegmentType,
|
|
254
278
|
type ParserState,
|
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,4 @@ export { createRegistry } from "./registry";
|
|
|
28
28
|
export { createParser } from "./parser";
|
|
29
29
|
export * from "./types";
|
|
30
30
|
export { isSegmentType } from "./types";
|
|
31
|
-
export type { TagDefinition, TagDefinitions, Registry, InferAttributes, SafeParseResult, } from "./registry";
|
|
32
|
-
export type { Parser, ParsedSegment, SegmentType, Segments, ParserState, StreamingParseResult, } from "./parser";
|
|
33
31
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOxC,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAOxC,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/parser.d.ts
CHANGED
|
@@ -4,92 +4,8 @@
|
|
|
4
4
|
* Creates a registry-aware parser that converts text containing XML-like tags
|
|
5
5
|
* into typed segments. Supports complete text parsing and streaming.
|
|
6
6
|
*/
|
|
7
|
-
import type { Registry, TagDefinitions,
|
|
8
|
-
|
|
9
|
-
* A parsed segment representing either plain text or a recognized tag
|
|
10
|
-
*/
|
|
11
|
-
export interface ParsedSegment<TDefs extends TagDefinitions = TagDefinitions, TType extends keyof TDefs | "text" = keyof TDefs | "text"> {
|
|
12
|
-
/** The segment type: 'text' or a registered tag name */
|
|
13
|
-
type: TType;
|
|
14
|
-
/** The content inside the tag, or the text content for 'text' segments */
|
|
15
|
-
content: string;
|
|
16
|
-
/** Attributes parsed from the tag (undefined for 'text' segments) */
|
|
17
|
-
attributes?: TType extends keyof TDefs ? InferAttributes<TDefs[TType]> : undefined;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Union of all segment types for a given registry
|
|
21
|
-
*/
|
|
22
|
-
export type SegmentType<TDefs extends TagDefinitions> = keyof TDefs | "text";
|
|
23
|
-
/**
|
|
24
|
-
* A segment array that can contain any valid segment for the registry
|
|
25
|
-
*/
|
|
26
|
-
export type Segments<TDefs extends TagDefinitions> = Array<ParsedSegment<TDefs, keyof TDefs | "text">>;
|
|
27
|
-
/**
|
|
28
|
-
* Parser state for handling incomplete/streaming content
|
|
29
|
-
*/
|
|
30
|
-
export interface ParserState {
|
|
31
|
-
/** Accumulated text buffer */
|
|
32
|
-
buffer: string;
|
|
33
|
-
/** Whether we're currently inside an unclosed component tag */
|
|
34
|
-
inComponent: boolean;
|
|
35
|
-
/** The tag name being processed, if any */
|
|
36
|
-
currentTag: string | null;
|
|
37
|
-
/** Attributes string for the current tag */
|
|
38
|
-
currentAttrs: string;
|
|
39
|
-
/** Index in buffer where current tag started */
|
|
40
|
-
tagStartIndex: number;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Result of parsing a streaming chunk
|
|
44
|
-
*/
|
|
45
|
-
export interface StreamingParseResult<TDefs extends TagDefinitions> {
|
|
46
|
-
/** Segments that are complete and can be rendered */
|
|
47
|
-
segments: Segments<TDefs>;
|
|
48
|
-
/** Updated parser state for next chunk */
|
|
49
|
-
state: ParserState;
|
|
50
|
-
/** Whether we're currently buffering (waiting for more data) */
|
|
51
|
-
isBuffering: boolean;
|
|
52
|
-
/** The type of tag being buffered, if any */
|
|
53
|
-
bufferingTag: keyof TDefs | null;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Parser interface returned by createParser
|
|
57
|
-
*/
|
|
58
|
-
export interface Parser<TDefs extends TagDefinitions> {
|
|
59
|
-
/**
|
|
60
|
-
* Parse a complete text string into segments.
|
|
61
|
-
* This is the main entry point for non-streaming use cases.
|
|
62
|
-
*
|
|
63
|
-
* @param text - The complete text to parse
|
|
64
|
-
* @returns Array of parsed segments in order
|
|
65
|
-
*/
|
|
66
|
-
parse(text: string): Segments<TDefs>;
|
|
67
|
-
/**
|
|
68
|
-
* Create initial parser state for streaming parsing.
|
|
69
|
-
*
|
|
70
|
-
* @returns Fresh parser state
|
|
71
|
-
*/
|
|
72
|
-
createState(): ParserState;
|
|
73
|
-
/**
|
|
74
|
-
* Parse a chunk of streaming text.
|
|
75
|
-
* Handles incomplete tags by buffering until complete.
|
|
76
|
-
*
|
|
77
|
-
* @param chunk - New text chunk to process
|
|
78
|
-
* @param state - Current parser state
|
|
79
|
-
* @returns Parse result with complete segments and updated state
|
|
80
|
-
*/
|
|
81
|
-
parseChunk(chunk: string, state: ParserState): StreamingParseResult<TDefs>;
|
|
82
|
-
/**
|
|
83
|
-
* Finalize parsing, returning any remaining buffered content as text.
|
|
84
|
-
* Call this when streaming is complete.
|
|
85
|
-
*
|
|
86
|
-
* @param state - Current parser state
|
|
87
|
-
* @returns Final segments including any buffered content
|
|
88
|
-
*/
|
|
89
|
-
finalize(state: ParserState): Segments<TDefs>;
|
|
90
|
-
/** The registry used by this parser */
|
|
91
|
-
readonly registry: Registry<TDefs>;
|
|
92
|
-
}
|
|
7
|
+
import type { Registry, TagDefinitions, Parser } from "./types";
|
|
8
|
+
export type { ParsedSegment, PartialSegment, SegmentType, Segments, ParserState, StreamingParseResult, Parser, } from "./types";
|
|
93
9
|
/**
|
|
94
10
|
* Create a parser instance bound to a registry.
|
|
95
11
|
*
|
package/dist/parser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EAQd,MAAM,EACP,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,aAAa,EACb,cAAc,EACd,WAAW,EACX,QAAQ,EACR,WAAW,EACX,oBAAoB,EACpB,MAAM,GACP,MAAM,SAAS,CAAC;AA4CjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,YAAY,CAAC,KAAK,SAAS,cAAc,EACvD,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,GACxB,MAAM,CAAC,KAAK,CAAC,CA4Yf"}
|
package/dist/parser.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maximum number of characters from the end of a buffer to check
|
|
3
|
+
* for potential incomplete tag starts
|
|
4
|
+
*/
|
|
5
|
+
const MAX_INCOMPLETE_TAG_LENGTH = 20;
|
|
1
6
|
/**
|
|
2
7
|
* Decode basic XML entities
|
|
3
8
|
*/
|
|
@@ -17,13 +22,20 @@ function parseAttributes(attrString) {
|
|
|
17
22
|
if (!attrString)
|
|
18
23
|
return attrs;
|
|
19
24
|
// Match key="value" or key='value' patterns
|
|
20
|
-
|
|
25
|
+
// Each quote type only terminates at its own matching quote
|
|
26
|
+
const pattern = /(\w+)=(?:"([^"]*)"|'([^']*)')/g;
|
|
21
27
|
let match;
|
|
22
28
|
while ((match = pattern.exec(attrString)) !== null) {
|
|
23
|
-
attrs[match[1]] = decodeXmlEntities(match[2]);
|
|
29
|
+
attrs[match[1]] = decodeXmlEntities(match[2] ?? match[3]);
|
|
24
30
|
}
|
|
25
31
|
return attrs;
|
|
26
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Escape special regex characters in a string
|
|
35
|
+
*/
|
|
36
|
+
function escapeRegex(str) {
|
|
37
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
+
}
|
|
27
39
|
/**
|
|
28
40
|
* Create a parser instance bound to a registry.
|
|
29
41
|
*
|
|
@@ -177,6 +189,105 @@ export function createParser(registry) {
|
|
|
177
189
|
tagStartIndex: 0,
|
|
178
190
|
};
|
|
179
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Process the case where we're inside an open component tag,
|
|
194
|
+
* looking for the closing tag.
|
|
195
|
+
*
|
|
196
|
+
* @returns The remaining unprocessed string, or null if still waiting for closing tag
|
|
197
|
+
*/
|
|
198
|
+
function processInsideComponent(remaining, newState, segments) {
|
|
199
|
+
const closingPattern = new RegExp(`</${escapeRegex(newState.currentTag)}>`, "i");
|
|
200
|
+
const closeMatch = remaining.match(closingPattern);
|
|
201
|
+
if (closeMatch) {
|
|
202
|
+
const closeIndex = remaining.indexOf(closeMatch[0]);
|
|
203
|
+
// We have a complete component
|
|
204
|
+
const content = remaining.slice(0, closeIndex);
|
|
205
|
+
const segment = createSegment(newState.currentTag, newState.currentAttrs, content);
|
|
206
|
+
segments.push(segment);
|
|
207
|
+
// Reset state
|
|
208
|
+
const afterClose = remaining.slice(closeIndex + closeMatch[0].length);
|
|
209
|
+
newState.inComponent = false;
|
|
210
|
+
newState.currentTag = null;
|
|
211
|
+
newState.currentAttrs = "";
|
|
212
|
+
return afterClose;
|
|
213
|
+
}
|
|
214
|
+
// Still waiting for closing tag
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Process text looking for an opening tag.
|
|
219
|
+
*
|
|
220
|
+
* @returns Object with the remaining string and updated textBuffer,
|
|
221
|
+
* or null if we should break out of the loop (buffering)
|
|
222
|
+
*/
|
|
223
|
+
function processOpenTag(remaining, newState, segments, textBuffer, processedUpTo, bufferLength) {
|
|
224
|
+
const openMatch = remaining.match(openingTagPattern);
|
|
225
|
+
if (openMatch) {
|
|
226
|
+
const tagIndex = remaining.indexOf(openMatch[0]);
|
|
227
|
+
if (tagIndex > 0) {
|
|
228
|
+
// Text before the tag
|
|
229
|
+
return {
|
|
230
|
+
remaining: remaining.slice(tagIndex),
|
|
231
|
+
textBuffer: textBuffer + remaining.slice(0, tagIndex),
|
|
232
|
+
processedUpTo,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// Check if it might be an incomplete tag at the end
|
|
236
|
+
if (remaining.endsWith("<") || /^<[^>]*$/.test(remaining)) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
const tagName = openMatch[1].toLowerCase();
|
|
240
|
+
const attrStr = openMatch[2] || "";
|
|
241
|
+
const isSelfClosing = openMatch[3] === "/" ||
|
|
242
|
+
registry.isSelfClosing(tagName);
|
|
243
|
+
if (isSelfClosing) {
|
|
244
|
+
if (textBuffer) {
|
|
245
|
+
addTextSegment(segments, textBuffer);
|
|
246
|
+
textBuffer = "";
|
|
247
|
+
}
|
|
248
|
+
const segment = createSegment(tagName, attrStr, "");
|
|
249
|
+
segments.push(segment);
|
|
250
|
+
const afterTag = remaining.slice(openMatch[0].length);
|
|
251
|
+
return {
|
|
252
|
+
remaining: afterTag,
|
|
253
|
+
textBuffer,
|
|
254
|
+
processedUpTo: bufferLength - afterTag.length,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// Start of a component tag with content
|
|
258
|
+
if (textBuffer) {
|
|
259
|
+
addTextSegment(segments, textBuffer);
|
|
260
|
+
textBuffer = "";
|
|
261
|
+
}
|
|
262
|
+
newState.inComponent = true;
|
|
263
|
+
newState.currentTag = tagName;
|
|
264
|
+
newState.currentAttrs = attrStr;
|
|
265
|
+
newState.tagStartIndex = processedUpTo;
|
|
266
|
+
const afterTag = remaining.slice(openMatch[0].length);
|
|
267
|
+
return {
|
|
268
|
+
remaining: afterTag,
|
|
269
|
+
textBuffer,
|
|
270
|
+
processedUpTo: bufferLength - afterTag.length,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
// No tag found, check for potential incomplete tag at end
|
|
274
|
+
const potentialTagStart = remaining.lastIndexOf("<");
|
|
275
|
+
if (potentialTagStart !== -1 &&
|
|
276
|
+
potentialTagStart > remaining.length - MAX_INCOMPLETE_TAG_LENGTH) {
|
|
277
|
+
// Might be start of a tag, buffer it
|
|
278
|
+
return {
|
|
279
|
+
remaining: remaining.slice(potentialTagStart),
|
|
280
|
+
textBuffer: textBuffer + remaining.slice(0, potentialTagStart),
|
|
281
|
+
processedUpTo: bufferLength - remaining.slice(potentialTagStart).length,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
// No tags, all text
|
|
285
|
+
return {
|
|
286
|
+
remaining: "",
|
|
287
|
+
textBuffer: textBuffer + remaining,
|
|
288
|
+
processedUpTo: bufferLength,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
180
291
|
/**
|
|
181
292
|
* Parse a streaming chunk with state management
|
|
182
293
|
*/
|
|
@@ -189,88 +300,23 @@ export function createParser(registry) {
|
|
|
189
300
|
let processedUpTo = 0;
|
|
190
301
|
while (remaining.length > 0) {
|
|
191
302
|
if (newState.inComponent && newState.currentTag) {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
if (closeMatch) {
|
|
196
|
-
const closeIndex = remaining.indexOf(closeMatch[0]);
|
|
197
|
-
// We have a complete component
|
|
198
|
-
const content = remaining.slice(0, closeIndex);
|
|
199
|
-
const segment = createSegment(newState.currentTag, newState.currentAttrs, content);
|
|
200
|
-
segments.push(segment);
|
|
201
|
-
// Reset state
|
|
202
|
-
remaining = remaining.slice(closeIndex + closeMatch[0].length);
|
|
303
|
+
const result = processInsideComponent(remaining, newState, segments);
|
|
304
|
+
if (result !== null) {
|
|
305
|
+
remaining = result;
|
|
203
306
|
processedUpTo = newState.buffer.length - remaining.length;
|
|
204
|
-
newState.inComponent = false;
|
|
205
|
-
newState.currentTag = null;
|
|
206
|
-
newState.currentAttrs = "";
|
|
207
307
|
}
|
|
208
308
|
else {
|
|
209
|
-
// Still waiting for closing tag
|
|
210
309
|
break;
|
|
211
310
|
}
|
|
212
311
|
}
|
|
213
312
|
else {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const tagIndex = remaining.indexOf(openMatch[0]);
|
|
218
|
-
if (tagIndex > 0) {
|
|
219
|
-
// Text before the tag
|
|
220
|
-
textBuffer += remaining.slice(0, tagIndex);
|
|
221
|
-
remaining = remaining.slice(tagIndex);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
// Check if it might be an incomplete tag at the end
|
|
225
|
-
// Only buffer if the potential tag is at the very end and looks incomplete
|
|
226
|
-
if (remaining.endsWith("<") || /^<[^>]*$/.test(remaining)) {
|
|
227
|
-
// Potentially incomplete tag, keep buffering
|
|
228
|
-
break;
|
|
229
|
-
}
|
|
230
|
-
const tagName = openMatch[1].toLowerCase();
|
|
231
|
-
const attrStr = openMatch[2] || "";
|
|
232
|
-
const isSelfClosing = openMatch[3] === "/" ||
|
|
233
|
-
registry.isSelfClosing(tagName);
|
|
234
|
-
if (isSelfClosing) {
|
|
235
|
-
// Flush text buffer
|
|
236
|
-
if (textBuffer) {
|
|
237
|
-
addTextSegment(segments, textBuffer);
|
|
238
|
-
textBuffer = "";
|
|
239
|
-
}
|
|
240
|
-
// Create segment for self-closing tag
|
|
241
|
-
const segment = createSegment(tagName, attrStr, "");
|
|
242
|
-
segments.push(segment);
|
|
243
|
-
remaining = remaining.slice(openMatch[0].length);
|
|
244
|
-
processedUpTo = newState.buffer.length - remaining.length;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
// Start of a component tag with content
|
|
248
|
-
if (textBuffer) {
|
|
249
|
-
addTextSegment(segments, textBuffer);
|
|
250
|
-
textBuffer = "";
|
|
251
|
-
}
|
|
252
|
-
newState.inComponent = true;
|
|
253
|
-
newState.currentTag = tagName;
|
|
254
|
-
newState.currentAttrs = attrStr;
|
|
255
|
-
newState.tagStartIndex = processedUpTo;
|
|
256
|
-
remaining = remaining.slice(openMatch[0].length);
|
|
257
|
-
processedUpTo = newState.buffer.length - remaining.length;
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
// No tag found, check for potential incomplete tag at end
|
|
261
|
-
const potentialTagStart = remaining.lastIndexOf("<");
|
|
262
|
-
if (potentialTagStart !== -1 &&
|
|
263
|
-
potentialTagStart > remaining.length - 20) {
|
|
264
|
-
// Might be start of a tag, buffer it
|
|
265
|
-
textBuffer += remaining.slice(0, potentialTagStart);
|
|
266
|
-
remaining = remaining.slice(potentialTagStart);
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
// No tags, all text
|
|
270
|
-
textBuffer += remaining;
|
|
271
|
-
remaining = "";
|
|
272
|
-
processedUpTo = newState.buffer.length;
|
|
313
|
+
const result = processOpenTag(remaining, newState, segments, textBuffer, processedUpTo, newState.buffer.length);
|
|
314
|
+
if (result === null) {
|
|
315
|
+
break;
|
|
273
316
|
}
|
|
317
|
+
remaining = result.remaining;
|
|
318
|
+
textBuffer = result.textBuffer;
|
|
319
|
+
processedUpTo = result.processedUpTo;
|
|
274
320
|
}
|
|
275
321
|
}
|
|
276
322
|
// Add remaining text to complete segments if not buffering
|
|
@@ -280,6 +326,21 @@ export function createParser(registry) {
|
|
|
280
326
|
}
|
|
281
327
|
// Update buffer to only contain unprocessed content
|
|
282
328
|
newState.buffer = remaining + textBuffer;
|
|
329
|
+
// Build partialSegment when inside an open tag
|
|
330
|
+
let partialSegment;
|
|
331
|
+
if (newState.inComponent && newState.currentTag) {
|
|
332
|
+
const rawAttrs = parseAttributes(newState.currentAttrs);
|
|
333
|
+
const normalizedTag = newState.currentTag.toLowerCase();
|
|
334
|
+
const validationResult = registry.validateAttributes(normalizedTag, rawAttrs);
|
|
335
|
+
partialSegment = {
|
|
336
|
+
type: normalizedTag,
|
|
337
|
+
content: decodeXmlEntities(remaining),
|
|
338
|
+
attributes: validationResult.success
|
|
339
|
+
? validationResult.data
|
|
340
|
+
: rawAttrs,
|
|
341
|
+
streaming: true,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
283
344
|
return {
|
|
284
345
|
segments,
|
|
285
346
|
state: newState,
|
|
@@ -287,6 +348,7 @@ export function createParser(registry) {
|
|
|
287
348
|
bufferingTag: newState.inComponent
|
|
288
349
|
? newState.currentTag
|
|
289
350
|
: null,
|
|
351
|
+
partialSegment,
|
|
290
352
|
};
|
|
291
353
|
}
|
|
292
354
|
/**
|
|
@@ -312,10 +374,4 @@ export function createParser(registry) {
|
|
|
312
374
|
registry,
|
|
313
375
|
};
|
|
314
376
|
}
|
|
315
|
-
/**
|
|
316
|
-
* Escape special regex characters in a string
|
|
317
|
-
*/
|
|
318
|
-
function escapeRegex(str) {
|
|
319
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
320
|
-
}
|
|
321
377
|
//# sourceMappingURL=parser.js.map
|
package/dist/parser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Catches render errors in segment components and displays a fallback UI.
|
|
5
5
|
* Prevents a single segment error from crashing the entire content area.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import React, { type ReactNode } from "react";
|
|
8
8
|
/**
|
|
9
9
|
* Props for the ErrorBoundary component
|
|
10
10
|
*/
|
|
@@ -16,13 +16,6 @@ export interface ErrorBoundaryProps {
|
|
|
16
16
|
/** Optional custom fallback renderer */
|
|
17
17
|
fallback?: (error: Error, segmentType: string) => ReactNode;
|
|
18
18
|
}
|
|
19
|
-
/**
|
|
20
|
-
* State for the ErrorBoundary component
|
|
21
|
-
*/
|
|
22
|
-
interface ErrorBoundaryState {
|
|
23
|
-
hasError: boolean;
|
|
24
|
-
error: Error | null;
|
|
25
|
-
}
|
|
26
19
|
/**
|
|
27
20
|
* Error boundary component that catches render errors in child components.
|
|
28
21
|
*
|
|
@@ -46,11 +39,5 @@ interface ErrorBoundaryState {
|
|
|
46
39
|
* </ErrorBoundary>
|
|
47
40
|
* ```
|
|
48
41
|
*/
|
|
49
|
-
export declare
|
|
50
|
-
constructor(props: ErrorBoundaryProps);
|
|
51
|
-
static getDerivedStateFromError(error: Error): ErrorBoundaryState;
|
|
52
|
-
componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
|
|
53
|
-
render(): ReactNode;
|
|
54
|
-
}
|
|
55
|
-
export {};
|
|
42
|
+
export declare function ErrorBoundary({ children, segmentType, fallback }: ErrorBoundaryProps): React.ReactElement;
|
|
56
43
|
//# sourceMappingURL=ErrorBoundary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/react/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/react/ErrorBoundary.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qCAAqC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,SAAS,CAAC;CAC7D;AAyBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAWzG"}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
import { ErrorBoundaryBase } from "../shared/ErrorBoundary";
|
|
3
|
+
function renderDevError(error, segmentType) {
|
|
4
|
+
return (_jsxs("span", { style: {
|
|
5
|
+
display: "inline-block",
|
|
6
|
+
padding: "4px 8px",
|
|
7
|
+
backgroundColor: "#fee2e2",
|
|
8
|
+
border: "1px solid #ef4444",
|
|
9
|
+
borderRadius: "4px",
|
|
10
|
+
color: "#991b1b",
|
|
11
|
+
fontSize: "12px",
|
|
12
|
+
fontFamily: "monospace",
|
|
13
|
+
}, children: ["Error in [", segmentType, "]: ", error.message] }));
|
|
14
|
+
}
|
|
15
|
+
function renderProdFallback() {
|
|
16
|
+
return _jsx("span", { style: { display: "none" } });
|
|
17
|
+
}
|
|
9
18
|
/**
|
|
10
19
|
* Error boundary component that catches render errors in child components.
|
|
11
20
|
*
|
|
@@ -29,41 +38,7 @@ import { Component } from "react";
|
|
|
29
38
|
* </ErrorBoundary>
|
|
30
39
|
* ```
|
|
31
40
|
*/
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
super(props);
|
|
35
|
-
this.state = { hasError: false, error: null };
|
|
36
|
-
}
|
|
37
|
-
static getDerivedStateFromError(error) {
|
|
38
|
-
return { hasError: true, error };
|
|
39
|
-
}
|
|
40
|
-
componentDidCatch(error, errorInfo) {
|
|
41
|
-
// Log the error for debugging
|
|
42
|
-
console.error(`XmlRender ErrorBoundary: Failed to render segment type "${this.props.segmentType}"`, error, errorInfo.componentStack);
|
|
43
|
-
}
|
|
44
|
-
render() {
|
|
45
|
-
if (this.state.hasError && this.state.error) {
|
|
46
|
-
// Use custom fallback if provided
|
|
47
|
-
if (this.props.fallback) {
|
|
48
|
-
return this.props.fallback(this.state.error, this.props.segmentType);
|
|
49
|
-
}
|
|
50
|
-
// Development: show detailed error info
|
|
51
|
-
if (process.env.NODE_ENV === "development") {
|
|
52
|
-
return (_jsxs("span", { style: {
|
|
53
|
-
display: "inline-block",
|
|
54
|
-
padding: "4px 8px",
|
|
55
|
-
backgroundColor: "#fee2e2",
|
|
56
|
-
border: "1px solid #ef4444",
|
|
57
|
-
borderRadius: "4px",
|
|
58
|
-
color: "#991b1b",
|
|
59
|
-
fontSize: "12px",
|
|
60
|
-
fontFamily: "monospace",
|
|
61
|
-
}, children: ["Error in [", this.props.segmentType, "]: ", this.state.error.message] }));
|
|
62
|
-
}
|
|
63
|
-
// Production: minimal hidden fallback
|
|
64
|
-
return _jsx("span", { style: { display: "none" } });
|
|
65
|
-
}
|
|
66
|
-
return this.props.children;
|
|
67
|
-
}
|
|
41
|
+
export function ErrorBoundary({ children, segmentType, fallback }) {
|
|
42
|
+
return (_jsx(ErrorBoundaryBase, { segmentType: segmentType, fallback: fallback, renderDevError: renderDevError, renderProdFallback: renderProdFallback, children: children }));
|
|
68
43
|
}
|
|
69
44
|
//# sourceMappingURL=ErrorBoundary.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../src/react/ErrorBoundary.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"ErrorBoundary.js","sourceRoot":"","sources":["../../src/react/ErrorBoundary.tsx"],"names":[],"mappings":";AAOA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAc5D,SAAS,cAAc,CAAC,KAAY,EAAE,WAAmB;IACvD,OAAO,CACL,gBACE,KAAK,EAAE;YACL,OAAO,EAAE,cAAc;YACvB,OAAO,EAAE,SAAS;YAClB,eAAe,EAAE,SAAS;YAC1B,MAAM,EAAE,mBAAmB;YAC3B,YAAY,EAAE,KAAK;YACnB,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,WAAW;SACxB,2BAEU,WAAW,SAAK,KAAK,CAAC,OAAO,IACnC,CACR,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB;IACzB,OAAO,eAAM,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAI,CAAC;AAC9C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,aAAa,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAsB;IACnF,OAAO,CACL,KAAC,iBAAiB,IAChB,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,kBAAkB,YAErC,QAAQ,GACS,CACrB,CAAC;AACJ,CAAC"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type ReactElement, type ReactNode } from "react";
|
|
8
8
|
import type { TagDefinitions } from "../registry";
|
|
9
|
-
import type { Segments, ParsedSegment } from "../parser";
|
|
9
|
+
import type { Segments, ParsedSegment, PartialSegment } from "../parser";
|
|
10
10
|
import type { Catalog } from "./catalog";
|
|
11
11
|
/**
|
|
12
12
|
* Props for the XmlRender component
|
|
@@ -20,6 +20,8 @@ export interface XmlRenderProps<TDefs extends TagDefinitions> {
|
|
|
20
20
|
catalog?: Catalog<TDefs>;
|
|
21
21
|
/** Optional custom error fallback renderer */
|
|
22
22
|
errorFallback?: (error: Error, segmentType: string) => ReactNode;
|
|
23
|
+
/** Optional in-progress segment being streamed */
|
|
24
|
+
partialSegment?: PartialSegment<TDefs>;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Renders an array of parsed XML segments using the component catalog.
|
|
@@ -58,5 +60,5 @@ export interface XmlRenderProps<TDefs extends TagDefinitions> {
|
|
|
58
60
|
* />
|
|
59
61
|
* ```
|
|
60
62
|
*/
|
|
61
|
-
export declare function XmlRender<TDefs extends TagDefinitions>({ segments, fallback, catalog: catalogProp, errorFallback, }: XmlRenderProps<TDefs>): ReactElement;
|
|
63
|
+
export declare function XmlRender<TDefs extends TagDefinitions>({ segments, fallback, catalog: catalogProp, errorFallback, partialSegment, }: XmlRenderProps<TDefs>): ReactElement;
|
|
62
64
|
//# sourceMappingURL=XmlRender.d.ts.map
|