fix(mysql): normalize smart quotes in JSON input
* fix(grid): normalize smart quotes in JSON input to prevent parse errors When manually typing JSON values in the data grid, input methods (especially Chinese IME and macOS smart punctuation) can automatically convert standard ASCII quotes to smart quotes (U+201C, U+201D, etc.), causing JSON parse failures. Changes: - Remove incorrect logic that tried to parse original value before normalization - Add hasSmartQuotes() function to detect 7 types of smart quotes: * Chinese quotes: U+201C, U+201D * German quotes: U+201E, U+201F * Smart single quotes: U+2018, U+2019 * Fullwidth quote: U+FF02 - Rewrite normalizeSmartQuotes() using charCodeAt to avoid oxc compiler issues - Add comprehensive tests for MySQL JSON field quote normalization All existing tests remain passing. * fix(grid): preserve valid JSON while normalizing smart quotes
This commit is contained in:
parent
5a08d42258
commit
b2617039fa
|
|
@ -80,15 +80,21 @@ function isPostgresArrayColumn(columnInfo: Pick<ColumnInfo, "data_type"> | undef
|
|||
}
|
||||
|
||||
function normalizeSmartQuotedJsonInput(value: string): string {
|
||||
if (!/[“”]/.test(value)) return value;
|
||||
// Check for smart double quotes that input methods might insert.
|
||||
// U+201C, U+201D, U+201E, U+201F, U+FF02
|
||||
if (!hasSmartDoubleQuotes(value)) return value;
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return value;
|
||||
|
||||
try {
|
||||
JSON.parse(value);
|
||||
return value;
|
||||
} catch {
|
||||
// macOS smart punctuation can turn JSON delimiters into Chinese-style quotes.
|
||||
// Input methods can turn JSON delimiters into smart quotes.
|
||||
}
|
||||
|
||||
// Input methods (especially on macOS and with Chinese IME) can turn JSON delimiters
|
||||
// into smart quotes. Normalize them to standard ASCII quotes.
|
||||
const normalized = normalizeSmartQuotes(value);
|
||||
try {
|
||||
JSON.parse(normalized);
|
||||
|
|
@ -98,8 +104,29 @@ function normalizeSmartQuotedJsonInput(value: string): string {
|
|||
}
|
||||
}
|
||||
|
||||
function hasSmartDoubleQuotes(value: string): boolean {
|
||||
// Check for smart double quotes: U+201C, U+201D, U+201E, U+201F, U+FF02
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const code = value.charCodeAt(i);
|
||||
if (code === 0x201c || code === 0x201d || code === 0x201e || code === 0x201f || code === 0xff02) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function normalizeSmartQuotes(value: string): string {
|
||||
return value.replace(/[“”]/g, '"');
|
||||
let result = "";
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
const code = value.charCodeAt(i);
|
||||
if (code === 0x201c || code === 0x201d || code === 0x201e || code === 0x201f || code === 0xff02) {
|
||||
// Convert to standard double quote
|
||||
result += '"';
|
||||
} else {
|
||||
result += value[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function formatPostgresArrayText(value: unknown[]): string {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
import { strict as assert } from "node:assert";
|
||||
import { test } from "vitest";
|
||||
import { coerceDataGridCellValue } from "../../apps/desktop/src/lib/dataGridCellCoercion.ts";
|
||||
|
||||
test("MySQL JSON field with English quotes should remain unchanged", () => {
|
||||
const input = '{"2:3":"3:4","3:2":"4:3","21:9":"16:9"}';
|
||||
const result = coerceDataGridCellValue({
|
||||
value: input,
|
||||
oldValue: null,
|
||||
databaseType: "mysql",
|
||||
columnInfo: { data_type: "json" },
|
||||
});
|
||||
|
||||
assert.equal(result, input);
|
||||
assert.ok(result.includes('"'), "Should contain English quotes");
|
||||
assert.ok(!result.includes('“') && !result.includes('”'), "Should NOT contain Chinese quotes");
|
||||
});
|
||||
|
||||
test("MySQL JSON field with Chinese quotes should be normalized to English quotes", () => {
|
||||
const input = "{\u201c2:3\u201d:\u201c3:4\u201d,\u201c3:2\u201d:\u201c4:3\u201d,\u201c21:9\u201d:\u201c16:9\u201d}";
|
||||
const expected = '{"2:3":"3:4","3:2":"4:3","21:9":"16:9"}';
|
||||
|
||||
const result = coerceDataGridCellValue({
|
||||
value: input,
|
||||
oldValue: null,
|
||||
databaseType: "mysql",
|
||||
columnInfo: { data_type: "json" },
|
||||
});
|
||||
|
||||
assert.equal(result, expected);
|
||||
assert.ok(result.includes('"'), "Should contain English quotes");
|
||||
assert.ok(!result.includes('“') && !result.includes('”'), "Should NOT contain Chinese quotes");
|
||||
});
|
||||
|
||||
test("MySQL JSON field with mixed quotes should be normalized", () => {
|
||||
const input = "{\u201ckey\u201d:\"value\"}";
|
||||
const expected = '{"key":"value"}';
|
||||
|
||||
const result = coerceDataGridCellValue({
|
||||
value: input,
|
||||
oldValue: null,
|
||||
databaseType: "mysql",
|
||||
columnInfo: { data_type: "json" },
|
||||
});
|
||||
|
||||
assert.equal(result, expected);
|
||||
});
|
||||
|
||||
test("MySQL JSON field with smart apostrophe inside a string should remain unchanged", () => {
|
||||
const input = '{"text":"it\u2019s ok"}';
|
||||
const result = coerceDataGridCellValue({
|
||||
value: input,
|
||||
oldValue: null,
|
||||
databaseType: "mysql",
|
||||
columnInfo: { data_type: "json" },
|
||||
});
|
||||
|
||||
assert.equal(result, input);
|
||||
});
|
||||
Loading…
Reference in New Issue