Handle jsonl - #172
Conversation
Create SafeUrlValidatorService for http/https validation. Create DomainOriginVerificationService for strict iframe networking validation.
Wire bypassSecurityTrustResourceUrl inside rendered-frame.ts through the rigorous URL validator. Swap the check in preview-bridge.ts for the strict origin verification. Fix LLM concurrency in ChatCoordinator submitPrompt. Fix Monaco model.dispose() in a2ui-composer-monaco-editor. Inject takeUntilDestroyed in api-key-selector.ts and renderer-selector.ts.
Delete debug/mock-rules/ entirely. Rip out console.error patch hacks from ChatCoordinator. Delete obsolete storage key ACTIVE_DRAFT.
…hot Capture and IDB Base
…ks into decoupled targets accurately abandoning monolithic arrays globally
…mentations and un-deferred webrtc layout code gracefully
…ngestion - Implement TrackEventDirective applying declarative tracking boundaries natively. - Construct FileIngestionService strictly leveraging @angular/cdk/clipboard utilities. - Construct ShareService cleanly encapsulating clipboard interactions.
- Sweep all active standalone UI components throughout the app, converting default ChangeDetection behavior to ChangeDetectionStrategy.OnPush. - Strip FileReader native accesses from ChatPanel into the FileIngestionService. - Swap template tracking to the directive.
- Rip out the legacy shareDesign() bloated component implementation comprehensively. - Delete deprecated inner test asserts and old inline code.
When a design link is shared and loaded, cleanSharedA2uiUrl removes the 'a2ui', 'renderer', and 'rendererId' parameters from the window hash and search parameters using history.replaceState so they do not linger in the address bar. Additionally, update StartupResolution to map queryRendererUrl to its corresponding static or custom renderer configuration and persist allowed unknown URLs into LocalStorage. Update Settings view to default to the active renderer selection instead of an unselected state. Include rendererId alongside renderer in share URL hashes.
Update tryParseJsonArray in shell utils/json.ts to parse:
- Standard JSON arrays ([...])
- Single JSON objects ({...} -> [obj])
- JSON Lines ({...}\n{...} -> [obj1, obj2, ...])
Update RawFrame to use tryParseJsonArray when parsing layout JSON.
Update ChatCleaner to validate layout snapshots using isLayoutArray.
Add unit tests covering JSON Lines, single objects, and edge cases.
⚡ A2UI Composer PR PreviewYour automated preview is successfully live (commit
|
There was a problem hiding this comment.
Code Review
This pull request refactors the workspace by modularizing several services, including origin verification, payload parsing, error formatting, file ingestion, URL validation, and sharing. It also introduces base classes for selectors, adds event tracking directives, and removes the mock rules debug component. The review feedback focuses on hardening string processing utility methods (such as tryParseJsonArray, parseAndHealJsonLines, and attemptSyntaxHealing) with guard clauses to handle nullish or undefined inputs safely, as well as retaining fallback logic and tests for parsing single JSON objects to ensure backwards compatibility.
| export function tryParseJsonArray(content: string): unknown[] | null { | ||
| const trimmed = content.trim(); | ||
| if (trimmed.length === 0) { | ||
| return null; | ||
| } | ||
|
|
||
| if (trimmed.startsWith('[') && trimmed.endsWith(']')) { | ||
| try { | ||
| const parsed = JSON.parse(trimmed); | ||
| if (Array.isArray(parsed)) { | ||
| return parsed; | ||
| } | ||
| } catch (e) { | ||
| // Ignore and return null | ||
| // Ignore array parse errors | ||
| } | ||
| } else if (trimmed.startsWith('{') && trimmed.endsWith('}')) { | ||
| try { | ||
| const parsed = JSON.parse(trimmed); | ||
| if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) { | ||
| return [parsed]; | ||
| } | ||
| } catch (e) { | ||
| // Ignore object parse errors | ||
| } | ||
| } |
There was a problem hiding this comment.
When refactoring or updating parsing/sanitization logic, do not remove existing fallback blocks (such as those supporting single JSON objects instead of arrays) if doing so would alter existing functionality or risk breaking backwards compatibility. Please retain the fallback logic for single JSON objects. Additionally, we should harden this string processing utility method with a guard clause to safely handle nullish or undefined inputs by returning a safe default (null), per our general rules.
export function tryParseJsonArray(content: string | null | undefined): unknown[] | null {
if (content == null) {
return null;
}
const trimmed = content.trim();
if (trimmed.length === 0) {
return null;
}
if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
try {
const parsed = JSON.parse(trimmed);
return [parsed];
} catch (e) {
return null;
}
}
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
try {
const parsed = JSON.parse(trimmed);
if (Array.isArray(parsed)) {
return parsed;
}
} catch (e) {
// Ignore array parse errors
}
}
return null;
}References
- Harden string processing utility methods with guard clauses to safely handle nullish or undefined inputs by returning safe defaults, and ensure corresponding unit tests are added to verify resilience against nullish values.
- When refactoring or updating parsing/sanitization logic, do not remove existing fallback blocks (such as those supporting single JSON objects instead of arrays) if doing so would alter existing functionality or risk breaking backwards compatibility.
| export function parseAndHealJsonLines(content: string): ParseResult { | ||
| let wasHealed = false; | ||
|
|
||
| // Attempt full JSON parsing before line-by-line processing | ||
| const parsedArray = tryParseJsonArray(content); | ||
| if (parsedArray) { | ||
| return {blocks: parsedArray, wasHealed: false}; | ||
| } | ||
|
|
||
| try { | ||
| const parsedSingle = JSON.parse(content); | ||
| if (Array.isArray(parsedSingle)) { | ||
| return {blocks: parsedSingle, wasHealed}; | ||
| } | ||
| if (parsedSingle && typeof parsedSingle === 'object') { | ||
| return {blocks: [parsedSingle], wasHealed}; | ||
| } | ||
| } catch { | ||
| // Continue to line-by-line healing | ||
| } |
There was a problem hiding this comment.
We should harden this string processing utility method with a guard clause to safely handle nullish or undefined inputs by returning a safe default ({blocks: [], wasHealed: false}), per our general rules.
export function parseAndHealJsonLines(content: string | null | undefined): ParseResult {
if (content == null) {
return {blocks: [], wasHealed: false};
}
let wasHealed = false;
// Attempt full JSON parsing before line-by-line processing
const parsedArray = tryParseJsonArray(content);
if (parsedArray) {
return {blocks: parsedArray, wasHealed: false};
}References
- Harden string processing utility methods with guard clauses to safely handle nullish or undefined inputs by returning safe defaults, and ensure corresponding unit tests are added to verify resilience against nullish values.
| export function attemptSyntaxHealing(line: string): unknown | null { | ||
| let patched = line.trim(); |
There was a problem hiding this comment.
Harden this string processing utility method with a guard clause to safely handle nullish or undefined inputs by returning a safe default (null), per our general rules.
| export function attemptSyntaxHealing(line: string): unknown | null { | |
| let patched = line.trim(); | |
| export function attemptSyntaxHealing(line: string | null | undefined): unknown | null { | |
| if (line == null) { | |
| return null; | |
| } | |
| let patched = line.trim(); |
References
- Harden string processing utility methods with guard clauses to safely handle nullish or undefined inputs by returning safe defaults, and ensure corresponding unit tests are added to verify resilience against nullish values.
| it('parses a single JSON object as a single-element array', () => { | ||
| expect(tryParseJsonArray('{"not": "an array"}')).toEqual([{not: 'an array'}]); | ||
| expect(tryParseJsonArray(' {"foo": "bar"} ')).toEqual([{foo: 'bar'}]); | ||
| }); |
There was a problem hiding this comment.
Since we must not remove existing fallback blocks (such as those supporting single JSON objects instead of arrays) to maintain backwards compatibility, we should keep this test verifying that a single JSON object is parsed successfully instead of expecting null.
| it('parses a single JSON object as a single-element array', () => { | |
| expect(tryParseJsonArray('{"not": "an array"}')).toEqual([{not: 'an array'}]); | |
| expect(tryParseJsonArray(' {"foo": "bar"} ')).toEqual([{foo: 'bar'}]); | |
| }); | |
| it('returns a single-element array for a single JSON object', () => { | |
| expect(tryParseJsonArray('{"not": "an array"}')).toEqual([{"not": "an array"}]); | |
| expect(tryParseJsonArray(' {"foo": "bar"} ')).toEqual([{"foo": "bar"}]); | |
| }); |
References
- When refactoring or updating parsing/sanitization logic, do not remove existing fallback blocks (such as those supporting single JSON objects instead of arrays) if doing so would alter existing functionality or risk breaking backwards compatibility.
Description
Multi-Format Layout Parser (JSON, JSONL, Single-Object)
Pre-launch Checklist
If you need help, consider asking for advice on the [discussion board].