@khairold/xml-render 0.1.3 → 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 +135 -80
- 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 +1 -1
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
|
*/
|
|
@@ -25,6 +30,12 @@ function parseAttributes(attrString) {
|
|
|
25
30
|
}
|
|
26
31
|
return attrs;
|
|
27
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Escape special regex characters in a string
|
|
35
|
+
*/
|
|
36
|
+
function escapeRegex(str) {
|
|
37
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
+
}
|
|
28
39
|
/**
|
|
29
40
|
* Create a parser instance bound to a registry.
|
|
30
41
|
*
|
|
@@ -178,6 +189,105 @@ export function createParser(registry) {
|
|
|
178
189
|
tagStartIndex: 0,
|
|
179
190
|
};
|
|
180
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
|
+
}
|
|
181
291
|
/**
|
|
182
292
|
* Parse a streaming chunk with state management
|
|
183
293
|
*/
|
|
@@ -190,88 +300,23 @@ export function createParser(registry) {
|
|
|
190
300
|
let processedUpTo = 0;
|
|
191
301
|
while (remaining.length > 0) {
|
|
192
302
|
if (newState.inComponent && newState.currentTag) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (closeMatch) {
|
|
197
|
-
const closeIndex = remaining.indexOf(closeMatch[0]);
|
|
198
|
-
// We have a complete component
|
|
199
|
-
const content = remaining.slice(0, closeIndex);
|
|
200
|
-
const segment = createSegment(newState.currentTag, newState.currentAttrs, content);
|
|
201
|
-
segments.push(segment);
|
|
202
|
-
// Reset state
|
|
203
|
-
remaining = remaining.slice(closeIndex + closeMatch[0].length);
|
|
303
|
+
const result = processInsideComponent(remaining, newState, segments);
|
|
304
|
+
if (result !== null) {
|
|
305
|
+
remaining = result;
|
|
204
306
|
processedUpTo = newState.buffer.length - remaining.length;
|
|
205
|
-
newState.inComponent = false;
|
|
206
|
-
newState.currentTag = null;
|
|
207
|
-
newState.currentAttrs = "";
|
|
208
307
|
}
|
|
209
308
|
else {
|
|
210
|
-
// Still waiting for closing tag
|
|
211
309
|
break;
|
|
212
310
|
}
|
|
213
311
|
}
|
|
214
312
|
else {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const tagIndex = remaining.indexOf(openMatch[0]);
|
|
219
|
-
if (tagIndex > 0) {
|
|
220
|
-
// Text before the tag
|
|
221
|
-
textBuffer += remaining.slice(0, tagIndex);
|
|
222
|
-
remaining = remaining.slice(tagIndex);
|
|
223
|
-
continue;
|
|
224
|
-
}
|
|
225
|
-
// Check if it might be an incomplete tag at the end
|
|
226
|
-
// Only buffer if the potential tag is at the very end and looks incomplete
|
|
227
|
-
if (remaining.endsWith("<") || /^<[^>]*$/.test(remaining)) {
|
|
228
|
-
// Potentially incomplete tag, keep buffering
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
const tagName = openMatch[1].toLowerCase();
|
|
232
|
-
const attrStr = openMatch[2] || "";
|
|
233
|
-
const isSelfClosing = openMatch[3] === "/" ||
|
|
234
|
-
registry.isSelfClosing(tagName);
|
|
235
|
-
if (isSelfClosing) {
|
|
236
|
-
// Flush text buffer
|
|
237
|
-
if (textBuffer) {
|
|
238
|
-
addTextSegment(segments, textBuffer);
|
|
239
|
-
textBuffer = "";
|
|
240
|
-
}
|
|
241
|
-
// Create segment for self-closing tag
|
|
242
|
-
const segment = createSegment(tagName, attrStr, "");
|
|
243
|
-
segments.push(segment);
|
|
244
|
-
remaining = remaining.slice(openMatch[0].length);
|
|
245
|
-
processedUpTo = newState.buffer.length - remaining.length;
|
|
246
|
-
continue;
|
|
247
|
-
}
|
|
248
|
-
// Start of a component tag with content
|
|
249
|
-
if (textBuffer) {
|
|
250
|
-
addTextSegment(segments, textBuffer);
|
|
251
|
-
textBuffer = "";
|
|
252
|
-
}
|
|
253
|
-
newState.inComponent = true;
|
|
254
|
-
newState.currentTag = tagName;
|
|
255
|
-
newState.currentAttrs = attrStr;
|
|
256
|
-
newState.tagStartIndex = processedUpTo;
|
|
257
|
-
remaining = remaining.slice(openMatch[0].length);
|
|
258
|
-
processedUpTo = newState.buffer.length - remaining.length;
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
// No tag found, check for potential incomplete tag at end
|
|
262
|
-
const potentialTagStart = remaining.lastIndexOf("<");
|
|
263
|
-
if (potentialTagStart !== -1 &&
|
|
264
|
-
potentialTagStart > remaining.length - 20) {
|
|
265
|
-
// Might be start of a tag, buffer it
|
|
266
|
-
textBuffer += remaining.slice(0, potentialTagStart);
|
|
267
|
-
remaining = remaining.slice(potentialTagStart);
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
// No tags, all text
|
|
271
|
-
textBuffer += remaining;
|
|
272
|
-
remaining = "";
|
|
273
|
-
processedUpTo = newState.buffer.length;
|
|
313
|
+
const result = processOpenTag(remaining, newState, segments, textBuffer, processedUpTo, newState.buffer.length);
|
|
314
|
+
if (result === null) {
|
|
315
|
+
break;
|
|
274
316
|
}
|
|
317
|
+
remaining = result.remaining;
|
|
318
|
+
textBuffer = result.textBuffer;
|
|
319
|
+
processedUpTo = result.processedUpTo;
|
|
275
320
|
}
|
|
276
321
|
}
|
|
277
322
|
// Add remaining text to complete segments if not buffering
|
|
@@ -281,6 +326,21 @@ export function createParser(registry) {
|
|
|
281
326
|
}
|
|
282
327
|
// Update buffer to only contain unprocessed content
|
|
283
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
|
+
}
|
|
284
344
|
return {
|
|
285
345
|
segments,
|
|
286
346
|
state: newState,
|
|
@@ -288,6 +348,7 @@ export function createParser(registry) {
|
|
|
288
348
|
bufferingTag: newState.inComponent
|
|
289
349
|
? newState.currentTag
|
|
290
350
|
: null,
|
|
351
|
+
partialSegment,
|
|
291
352
|
};
|
|
292
353
|
}
|
|
293
354
|
/**
|
|
@@ -313,10 +374,4 @@ export function createParser(registry) {
|
|
|
313
374
|
registry,
|
|
314
375
|
};
|
|
315
376
|
}
|
|
316
|
-
/**
|
|
317
|
-
* Escape special regex characters in a string
|
|
318
|
-
*/
|
|
319
|
-
function escapeRegex(str) {
|
|
320
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
321
|
-
}
|
|
322
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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XmlRender.d.ts","sourceRoot":"","sources":["../../src/react/XmlRender.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAc,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"XmlRender.d.ts","sourceRoot":"","sources":["../../src/react/XmlRender.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAc,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIzC;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,cAAc;IAC1D,yCAAyC;IACzC,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACvE,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACzB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,KAAK,SAAS,CAAC;IACjE,kDAAkD;IAClD,cAAc,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC;CACxC;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,SAAS,CAAC,KAAK,SAAS,cAAc,EAAE,EACtD,QAAQ,EACR,QAAQ,EACR,OAAO,EAAE,WAAW,EACpB,aAAa,EACb,cAAc,GACf,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,YAAY,CAyCtC"}
|