| author | |
| committer | |
| log | 32407df9c5ffd3c5dda2e745568997ac33c15ed1 |
| tree | daf2abf5312b3c8ba744ed5f97e0ac5cd08dd7e1 |
| parent | 162e774294f9e3dda15ca2f8083db019fe398837 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>2 files changed, 17 insertions(+), 2 deletions(-)
packages/shared/src/schemaJson.test.ts+9| ... | ... | @@ -57,6 +57,15 @@ Done.`), |
| 57 | 57 | expect(() => decodeLenientJson('{ "enabled": true,, }')).toThrow(); |
| 58 | 58 | }); |
| 59 | 59 | |
| 60 | it("preserves commas before brackets inside string values", () => { | |
| 61 | // A comma inside a string value that happens to precede `}`/`]` must not | |
| 62 | // be stripped as if it were a trailing comma. | |
| 63 | expect(decodeLenientJson('{"note":"a,]"}')).toEqual({ note: "a,]" }); | |
| 64 | expect(decodeLenientJson('{"list":["x,}"]}')).toEqual({ list: ["x,}"] }); | |
| 65 | // Genuine trailing commas are still removed. | |
| 66 | expect(decodeLenientJson('{"values":[1, 2,],}')).toEqual({ values: [1, 2] }); | |
| 67 | }); | |
| 68 | ||
| 60 | 69 | it("formats schema failures with paths without exposing invalid values", () => { |
| 61 | 70 | const decodeCredential = decodeJsonResult(Schema.Struct({ token: Schema.Number })); |
| 62 | 71 | const decoded = decodeCredential('{"token":"credential=secret-value"}'); |
packages/shared/src/schemaJson.ts+8-2| ... | ... | @@ -190,8 +190,14 @@ const parseLenientJsonGetter = SchemaGetter.onSome((input: string) => { |
| 190 | 190 | (match, stringLiteral: string | undefined) => (stringLiteral ? match : ""), |
| 191 | 191 | ); |
| 192 | 192 | |
| 193 | // Strip trailing commas before `}` or `]`. | |
| 194 | stripped = stripped.replace(/,(\s*[}\]])/g, "$1"); | |
| 193 | // Strip trailing commas before `}` or `]`. The alternation preserves quoted | |
| 194 | // strings so a comma inside a string value (e.g. `{"note":"a,]"}`) is not | |
| 195 | // mistaken for a trailing comma and removed. | |
| 196 | stripped = stripped.replace( | |
| 197 | /("(?:[^"\\]|\\.)*")|,(\s*[}\]])/g, | |
| 198 | (match, stringLiteral: string | undefined, bracket: string | undefined) => | |
| 199 | stringLiteral ? match : (bracket ?? ""), | |
| 200 | ); | |
| 195 | 201 | |
| 196 | 202 | return decodeJsonString(stripped).pipe( |
| 197 | 203 | Effect.map(Option.some), |