diff --git a/corpus/round-trip/combinations/escaped-json-keys.json b/corpus/round-trip/combinations/escaped-json-keys.json new file mode 100644 index 0000000..81468f7 --- /dev/null +++ b/corpus/round-trip/combinations/escaped-json-keys.json @@ -0,0 +1,53 @@ +{ + "content": [ + { + "attrs": { + "extensionKey": "toc", + "extensionType": "com.atlassian.confluence.macro.core", + "parameters": { + "back\\slash": 1, + "nested\\": { + "in\\\"ner": true + }, + "nul\u0000": 2, + "say \"hi\"": 3, + "tab\tand\u001fus": 4 + } + }, + "type": "extension" + }, + { + "attrs": { + "back\\slash": 1, + "nul\u0000": 2, + "say \"hi\"": 3, + "tab\tand\u001fus": 4 + }, + "type": "unknownBlock" + }, + { + "content": [ + { + "text": "See ", + "type": "text" + }, + { + "attrs": { + "back\\slash": 1, + "nul\u0000": 2, + "say \"hi\"": 3, + "tab\tand\u001fus": 4 + }, + "type": "unknownInline" + }, + { + "text": " now.", + "type": "text" + } + ], + "type": "paragraph" + } + ], + "type": "doc", + "version": 1 +} diff --git a/corpus/round-trip/combinations/escaped-json-keys.md b/corpus/round-trip/combinations/escaped-json-keys.md new file mode 100644 index 0000000..99438b4 --- /dev/null +++ b/corpus/round-trip/combinations/escaped-json-keys.md @@ -0,0 +1,14 @@ +!adf:extension {extensionKey=toc extensionType="com.atlassian.confluence.macro.core" parameters="{\"back\\\\slash\":1,\"nested\\\\\":{\"in\\\\\\\"ner\":true},\"nul\\u0000\":2,\"say \\\"hi\\\"\":3,\"tab\\tand\\u001fus\":4}"} + +```adf:unknownBlock +{ + "attrs": { + "back\\slash": 1, + "nul\u0000": 2, + "say \"hi\"": 3, + "tab\tand\u001fus": 4 + } +} +``` + +See !adf:carry{json="{\"attrs\":{\"back\\\\slash\":1,\"nul\\u0000\":2,\"say \\\"hi\\\"\":3,\"tab\\tand\\u001fus\":4},\"type\":\"unknownInline\"}"} now. diff --git a/src/conformance/property-harness.ts b/src/conformance/property-harness.ts index 8e9efe6..e64d540 100644 --- a/src/conformance/property-harness.ts +++ b/src/conformance/property-harness.ts @@ -43,10 +43,7 @@ const numberValue = fc.oneof( { arbitrary: fc.constant(-0), weight: 1 }, ) -// V8's JSON.parse returns a wrong key after parsing a key holding an escaped backslash (https://issues.chromium.org/issues/521080746); Bun is unaffected. -const keyPiece = fc - .oneof({ arbitrary: markdownPieces, weight: 4 }, { arbitrary: fc.string({ maxLength: 1, minLength: 1, unit: 'grapheme' }), weight: 1 }) - .filter((piece) => !/[\\"\x00-\x1f]/.test(piece)) +const keyPiece = fc.oneof({ arbitrary: markdownPieces, weight: 4 }, { arbitrary: fc.string({ maxLength: 1, minLength: 1, unit: 'grapheme' }), weight: 1 }) export const jsonKey = fc.string({ maxLength: 8, unit: keyPiece }) export const { jsonValue } = fc.letrec<{ jsonValue: JsonValue }>((tie) => ({ diff --git a/todo.md b/todo.md index 4b021ae..f202678 100644 --- a/todo.md +++ b/todo.md @@ -24,7 +24,6 @@ | ID | Release | Exempt | Item | R | S | A | G | Goals | Score | |---|---|---|---|---|---|---|---|---|---| -| 64 | 0.2.0 | defect | **Read back unchanged on V8 every JSON key the emitter writes, with a fixture whose key holds `\`, `"` or a control character.** | 4 | 4 | 6 | 8 | 1, 8 | 23.0 | | 43 | 0.2.0 | decision | **Give each markdown input its own reader, strict to its own standard.** | 6 | 7 | 8 | 9 | 4, 5 | 20.3 | | 65 | 0.2.0 | defect | **Read an image not alone in its paragraph as its alt text linked to its URL, and a titled image alone in its paragraph as the image captioned with its title.** | 5 | 5 | 7 | 7 | 4, 5, 7 | 17.1 | | 49 | 0.2.0 | | **Read a list whose bullet or ordered delimiter changes as two lists, in both readers, and retire `!adf:listBreak`.** | 4 | 5 | 5 | 8 | 4, 5 | 15.5 | @@ -64,15 +63,6 @@ ## Details -### 64. Read back unchanged on V8 every JSON key the emitter writes, with a fixture whose key holds `\`, `"` or a control character. - -`property-harness.ts` strips `\`, `"` and control characters from generated JSON keys, citing V8's -`JSON.parse` returning a wrong key for an escaped backslash. The library reads `json` attributes -(`directive-syntax.ts`) and both carries (`opaque-carry.ts`) through that `JSON.parse`, so on Node, -Deno and Chrome the round-trip would refuse its own output or read back a different key; the fixture -tells which. Confirm with a fixture first; then read JSON with our own parser or record the gap with -an ending item. Found by the README-goals audit, 2026-10-03. - ### 43. Give each markdown input its own reader, strict to its own standard. Today `markdownToAdf` reads CommonMark and the lossless flavour as one input: text shaped like a