mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 11:56:12 +00:00
Merge pull request #700 from kaoru0822-kitauji/fix/json-shape-collation
fix(comparison): break JSON shape collation ties
This commit is contained in:
3 files changed
+358
-1
No files matched your search
@@ -39,6 +39,85 @@ describe("inferJsonShape", () => {
|
||||
expect(inferJsonShape("not-json")).toBeNull();
|
||||
});
|
||||
|
||||
it.each([
|
||||
["é", "e\u0301", "e\u0341"],
|
||||
["é", "e\u0341", "e\u0301"],
|
||||
["e\u0301", "é", "e\u0341"],
|
||||
["e\u0301", "e\u0341", "é"],
|
||||
["e\u0341", "é", "e\u0301"],
|
||||
["e\u0341", "e\u0301", "é"],
|
||||
])("orders distinct collation-equal paths from %j, %j, %j", (...names) => {
|
||||
for (const name of names) expect(name.localeCompare("é")).toBe(0);
|
||||
const values: Readonly<Record<string, unknown>> = {
|
||||
é: 1,
|
||||
"e\u0301": "value",
|
||||
"e\u0341": null,
|
||||
};
|
||||
const shape = inferJsonShape(
|
||||
JSON.stringify(
|
||||
Object.fromEntries(names.map((name) => [name, values[name]])),
|
||||
),
|
||||
);
|
||||
|
||||
expect(shape).toEqual({
|
||||
root_type: "object",
|
||||
node_count: 4,
|
||||
max_depth_observed: 1,
|
||||
properties: [
|
||||
{ path: "/e\u0301", types: ["string"], observations: 1 },
|
||||
{ path: "/e\u0341", types: ["null"], observations: 1 },
|
||||
{ path: "/é", types: ["number"], observations: 1 },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it.each([false, true])(
|
||||
"preserves nested types, counts, depth and pointer names with reversed keys: %s",
|
||||
(reverse) => {
|
||||
const object = (number: number, value: unknown) => {
|
||||
const entries = [
|
||||
["é/~", number],
|
||||
["e\u0301/~", value],
|
||||
];
|
||||
return Object.fromEntries(reverse ? entries.reverse() : entries);
|
||||
};
|
||||
const shape = inferJsonShape(
|
||||
JSON.stringify({ nested: [object(1, null), object(2, [null])] }),
|
||||
);
|
||||
|
||||
expect(shape).toEqual({
|
||||
root_type: "object",
|
||||
node_count: 9,
|
||||
max_depth_observed: 4,
|
||||
properties: [
|
||||
{ path: "/nested", types: ["array"], observations: 1 },
|
||||
{
|
||||
path: "/nested/*/e\u0301~1~0",
|
||||
types: ["array", "null"],
|
||||
observations: 2,
|
||||
},
|
||||
{ path: "/nested/*/é~1~0", types: ["number"], observations: 2 },
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("preserves the existing locale order for paths that do not tie", () => {
|
||||
const names = ["z", "é", "A", "_", "a"];
|
||||
const expected = names
|
||||
.map((name) => `/${name}`)
|
||||
.sort((left, right) => left.localeCompare(right));
|
||||
expect(new Set(expected).size).toBe(names.length);
|
||||
for (const [index, left] of expected.entries())
|
||||
for (const right of expected.slice(index + 1))
|
||||
expect(left.localeCompare(right)).not.toBe(0);
|
||||
const shape = inferJsonShape(
|
||||
JSON.stringify(Object.fromEntries(names.map((name) => [name, true]))),
|
||||
);
|
||||
|
||||
expect(shape?.properties.map(({ path }) => path)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("retains every parsed property beyond the former shape-node limit", () => {
|
||||
const content = Object.fromEntries(
|
||||
Array.from({ length: 5_001 }, (_, index) => [`field_${index}`, index]),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { compareCodePoints } from "./canonicalOrdering.js";
|
||||
|
||||
const jsonValueTypeSchema = z.enum([
|
||||
"object",
|
||||
"array",
|
||||
@@ -88,7 +90,11 @@ export const inferJsonShape = (text: string): JsonShape | null => {
|
||||
types: [...value.types].sort(),
|
||||
observations: value.observations,
|
||||
}))
|
||||
.sort((left, right) => left.path.localeCompare(right.path)),
|
||||
.sort(
|
||||
(left, right) =>
|
||||
left.path.localeCompare(right.path) ||
|
||||
compareCodePoints(left.path, right.path),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user