change our own JSON parser to untruncate-json lib, since it works fast and correctly

This commit is contained in:
Nikita 2024-12-12 00:18:20 +03:00
parent b2c3fbf927
commit 0ce98282ba
3 changed files with 15 additions and 149 deletions

12
package-lock.json generated
View file

@ -1,17 +1,18 @@
{ {
"name": "mind", "name": "mind",
"version": "0.1.2", "version": "0.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mind", "name": "mind",
"version": "0.1.2", "version": "0.2.0",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"dependencies": { "dependencies": {
"clsx": "^2.0.0", "clsx": "^2.0.0",
"marked": "^10.0.0", "marked": "^10.0.0",
"react-transition-group": "^4.4.5" "react-transition-group": "^4.4.5",
"untruncate-json": "^0.0.1"
}, },
"devDependencies": { "devDependencies": {
"@wordpress/eslint-plugin": "^17.2.0", "@wordpress/eslint-plugin": "^17.2.0",
@ -17596,6 +17597,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/untruncate-json": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/untruncate-json/-/untruncate-json-0.0.1.tgz",
"integrity": "sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA=="
},
"node_modules/update-browserslist-db": { "node_modules/update-browserslist-db": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",

View file

@ -34,6 +34,7 @@
"dependencies": { "dependencies": {
"clsx": "^2.0.0", "clsx": "^2.0.0",
"marked": "^10.0.0", "marked": "^10.0.0",
"react-transition-group": "^4.4.5" "react-transition-group": "^4.4.5",
"untruncate-json": "^0.0.1"
} }
} }

View file

@ -1,3 +1,5 @@
import untruncateJson from 'untruncate-json';
import { createBlock } from '@wordpress/blocks'; import { createBlock } from '@wordpress/blocks';
export default class BlocksStreamProcessor { export default class BlocksStreamProcessor {
@ -8,7 +10,7 @@ export default class BlocksStreamProcessor {
this.lastUpdate = Date.now(); this.lastUpdate = Date.now();
this.isJsonStarted = false; this.isJsonStarted = false;
this.jsonBuffer = ''; this.jsonBuffer = '';
this.lastDispatchedBlocks = null; this.lastDispatchedBlocks = [];
this.renderDelay = 150; this.renderDelay = 150;
// In Nginx server we have the true steaming experience and receive chunks in JS as soon as they are available. // In Nginx server we have the true steaming experience and receive chunks in JS as soon as they are available.
@ -148,150 +150,7 @@ export default class BlocksStreamProcessor {
return '[]'; return '[]';
} }
let result = ''; return untruncateJson(partial);
let inString = false;
let inEscape = false;
let openBrackets = 0;
let openBraces = 0;
let lastPropertyName = '';
let inPropertyName = false;
let currentProperty = '';
const arrayStack = [];
const braceStack = [];
// Process character by character
for (let i = 0; i < partial.length; i++) {
const char = partial[i];
const nextChar = partial[i + 1];
result += char;
// Handle escape sequences
if (char === '\\\\' && inString) {
inEscape = true;
continue;
}
if (inEscape) {
inEscape = false;
continue;
}
// Track string boundaries
if (char === '"' && !inEscape) {
if (!inString) {
inString = true;
// Check if this is a property name
if (!inPropertyName && nextChar === ':') {
inPropertyName = true;
}
} else {
inString = false;
if (inPropertyName) {
lastPropertyName = currentProperty;
currentProperty = '';
inPropertyName = false;
}
}
} else if (inString) {
if (inPropertyName) {
currentProperty += char;
}
}
// Only count structure characters outside strings
if (!inString) {
if (char === '[') {
openBrackets++;
arrayStack.push(i);
}
if (char === ']') {
openBrackets--;
if (arrayStack.length > 0) {
arrayStack.pop();
}
}
if (char === '{') {
openBraces++;
braceStack.push(i);
}
if (char === '}') {
openBraces--;
if (braceStack.length > 0) {
braceStack.pop();
}
}
}
}
// Complete the structure
let completed = result;
// Handle unclosed strings
if (inString) {
completed += '"';
}
// Complete property values if needed
if (lastPropertyName === 'content' && inString) {
completed += '"';
}
// Complete objects and arrays from inside out
while (openBraces > 0) {
completed += '}';
openBraces--;
}
while (openBrackets > 0) {
completed += ']';
openBrackets--;
}
// Validate and fix common issues
try {
JSON.parse(completed);
return completed;
} catch (e) {
// Try to fix common issues
let fixed = completed;
// Fix unclosed content property
const contentMatch = fixed.match(
/"content"\\s*:\\s*"([^"]*)(?:[^"]*)?$/
);
if (contentMatch) {
const contentEndIndex =
fixed.lastIndexOf(contentMatch[1]) + contentMatch[1].length;
fixed =
fixed.substring(0, contentEndIndex) +
'"' +
fixed.substring(contentEndIndex);
}
// Fix missing commas between blocks
fixed = fixed.replace(/}(\\s*){/g, '},\\n{');
// Fix trailing commas
fixed = fixed.replace(/,(\\s*[}\\]])/g, '$1');
try {
JSON.parse(fixed);
return fixed;
} catch (e2) {
// If still invalid, try to extract valid parts
const blockMatch = fixed.match(
/\[\s*{[^{]*"name"\s*:\s*"[^"]+"\s*,\s*"attributes"\s*:\s*{[^}]+}/
);
if (blockMatch) {
return blockMatch[0] + '}]';
}
// Return minimal valid structure as last resort
return '[{"name":"core/paragraph","attributes":{"content":""},"innerBlocks":[]}]';
}
}
} }
transformToBlock(blockData) { transformToBlock(blockData) {