Skip to content

Commit 1aa60aa

Browse files
committed
Release v0.1.7
1 parent a1082cd commit 1aa60aa

File tree

6 files changed

+91
-13
lines changed

6 files changed

+91
-13
lines changed

CHANGELOG.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.1.7] - 2025-10-27
11+
12+
### Fixed
13+
- **Windows HTML payloads**: Layer 3's binary optimization now trims HTML fragments using byte-accurate offsets, ensuring CRLF-terminated bodies are quoted without leaving stray delimiters.
14+
- **Regression coverage**: Added a direct regression test mirroring the reporter's `CRLF_html.json` sample so Windows-style newlines stay guarded.
15+
16+
### Added
17+
- **Examples**: Extended `examples/html_content_examples.exs` with a Windows newline scenario to demonstrate the repaired behaviour via `mix run`.
18+
- **Documentation**: HexDocs now surfaces grouped README, changelog, and license pages for easier navigation.
19+
1020
## [0.1.6] - 2025-10-24
1121

1222
### Added
@@ -297,7 +307,7 @@ This is a **100% rewrite** - all previous code has been replaced with the new la
297307
- Incomplete objects and arrays
298308
- Boolean variants (True/False/TRUE/FALSE)
299309
- Null variants (None/NULL/Null)
300-
- Code fence removal (```json blocks)
310+
- Code fence removal (triple-backtick `json` fences)
301311
- Comment stripping (// and /* */)
302312
- Core API functions:
303313
- `JsonRemedy.repair/2` - Parse and repair JSON to Elixir terms
@@ -318,11 +328,12 @@ This is a **100% rewrite** - all previous code has been replaced with the new la
318328
- Minimal memory overhead (< 8KB for repairs)
319329
- All operations pass performance thresholds
320330

321-
[Unreleased]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.6...HEAD
331+
[Unreleased]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.7...HEAD
332+
[0.1.7]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.6...v0.1.7
322333
[0.1.6]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.5...v0.1.6
323334
[0.1.5]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.4...v0.1.5
324335
[0.1.4]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.3...v0.1.4
325336
[0.1.3]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.2...v0.1.3
326337
[0.1.2]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.1...v0.1.2
327338
[0.1.1]: https://github.com/nshkrdotcom/json_remedy/compare/v0.1.0...v0.1.1
328-
[0.1.0]: https://github.com/nshkrdotcom/json_remedy/releases/tag/v0.1.0
339+
[0.1.0]: https://github.com/nshkrdotcom/json_remedy/releases/tag/v0.1.0

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ Add JsonRemedy to your `mix.exs`:
158158
```elixir
159159
def deps do
160160
[
161-
{:json_remedy, "~> 0.1.6"}
161+
{:json_remedy, "~> 0.1.7"}
162162
]
163163
end
164164
```

examples/html_content_examples.exs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ defmodule HtmlContentExamples do
3232
# Example 5: Complex nested HTML with JSON-like content
3333
example_5_complex_nested_html()
3434

35+
# Example 6: Windows-style CRLF HTML body
36+
example_6_api_503_error_windows()
37+
3538
IO.puts("\n=== All HTML content examples completed! ===")
3639
end
3740

@@ -240,7 +243,32 @@ defmodule HtmlContentExamples do
240243

241244
IO.puts("\n" <> String.duplicate("-", 80) <> "\n")
242245
end
246+
247+
defp example_6_api_503_error_windows do
248+
IO.puts("Example 6: API 503 Error Page with Windows Newlines")
249+
IO.puts("====================================================")
250+
251+
malformed =
252+
~s({"responses": [{"id":"33","status":503,"headers":{"Content-Type":"text/html; charset=us-ascii"},"body":<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><HTML><HEAD><TITLE>Service Unavailable</TITLE><META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD><BODY><h2>Application Request Queue Full</h2><hr><p>HTTP Error 503. The application request queue is full.</p>\r\n</BODY></HTML>}]} )
253+
254+
IO.puts("Input (API response with CRLF-terminated HTML body):")
255+
IO.puts(String.slice(malformed, 0, 200) <> "...\n")
256+
257+
case JsonRemedy.repair(malformed) do
258+
{:ok, result} ->
259+
IO.puts("✓ Successfully repaired!")
260+
[response] = result["responses"]
261+
IO.puts("\nParsed response:")
262+
IO.inspect(response, pretty: true, limit: :infinity)
263+
IO.puts("\n✓ HTML body retains CRLF sequence:")
264+
IO.puts(String.replace(response["body"], "\r\n", "\\r\\n"))
265+
266+
{:error, reason} ->
267+
IO.puts("✗ Failed to repair: #{reason}")
268+
end
269+
270+
IO.puts("\n" <> String.duplicate("-", 80) <> "\n")
271+
end
243272
end
244273

245-
# Run all examples
246274
HtmlContentExamples.run_all_examples()

lib/json_remedy/layer3/syntax_normalization.ex

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -805,13 +805,27 @@ defmodule JsonRemedy.Layer3.SyntaxNormalization do
805805
char == ?< and expecting == :value and
806806
HtmlHandlers.is_html_start?(<<char::utf8, rest::binary>>, 0) ->
807807
# Start of HTML content - quote it
808-
{html_content, chars_consumed} =
809-
HtmlHandlers.extract_html_content(<<char::utf8, rest::binary>>, 0)
808+
fragment = <<char::utf8, rest::binary>>
809+
{html_content, chars_consumed} = HtmlHandlers.extract_html_content(fragment, 0)
810810

811811
{quoted_html, html_repairs} = HtmlHandlers.quote_html_content(html_content, pos)
812812

813-
# Calculate remaining binary after consuming HTML
814-
remaining = binary_part(rest, chars_consumed - 1, byte_size(rest) - (chars_consumed - 1))
813+
consumed_fragment = String.slice(fragment, 0, chars_consumed)
814+
bytes_consumed = byte_size(consumed_fragment)
815+
bytes_for_rest = max(bytes_consumed - byte_size(<<char::utf8>>), 0)
816+
817+
remaining =
818+
if bytes_for_rest <= 0 do
819+
rest
820+
else
821+
rest_size = byte_size(rest)
822+
823+
if bytes_for_rest >= rest_size do
824+
<<>>
825+
else
826+
binary_part(rest, bytes_for_rest, rest_size - bytes_for_rest)
827+
end
828+
end
815829

816830
normalize_syntax_binary_simple(
817831
remaining,

mix.exs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule JsonRemedy.MixProject do
22
use Mix.Project
33

4-
@version "0.1.6"
4+
@version "0.1.7"
55
@source_url "https://github.com/nshkrdotcom/json_remedy"
66

77
def project do
@@ -11,6 +11,9 @@ defmodule JsonRemedy.MixProject do
1111
elixir: "~> 1.14",
1212
start_permanent: Mix.env() == :prod,
1313
deps: deps(),
14+
name: "JsonRemedy",
15+
source_url: @source_url,
16+
homepage_url: @source_url,
1417
description:
1518
"A blazingly fast Elixir library for repairing malformed JSON using binary pattern matching. Handles LLM outputs, legacy data, and broken JSON with intelligent context-aware fixes.",
1619
package: package(),
@@ -55,18 +58,29 @@ defmodule JsonRemedy.MixProject do
5558
"GitHub" => @source_url,
5659
"Documentation" => "https://hexdocs.pm/json_remedy"
5760
},
58-
files: ~w(lib .formatter.exs mix.exs README.md LICENSE assets)
61+
files: ~w(lib .formatter.exs mix.exs README.md CHANGELOG.md LICENSE assets)
5962
]
6063
end
6164

6265
defp docs do
6366
[
64-
main: "JsonRemedy",
67+
main: "readme",
68+
name: "JsonRemedy",
6569
source_ref: "v#{@version}",
6670
source_url: @source_url,
71+
homepage_url: @source_url,
6772
assets: %{"assets" => "assets"},
6873
logo: "assets/json_remedy_logo.svg",
69-
extras: ["README.md", "LICENSE"]
74+
extras: [
75+
{"README.md", [title: "Overview"]},
76+
{"CHANGELOG.md", [title: "Changelog"]},
77+
{"LICENSE", [title: "License"]}
78+
],
79+
groups_for_extras: [
80+
"Getting Started": ["README.md"],
81+
"Release Notes": ["CHANGELOG.md"],
82+
Reference: ["LICENSE"]
83+
]
7084
]
7185
end
7286
end

test/unit/layer3_html_content_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ defmodule JsonRemedy.Layer3HtmlContentTest do
9393
assert String.contains?(result["snippet"], ~s(User said: "Hello World"))
9494
end
9595

96+
test "handles HTML with Windows-style newlines" do
97+
malformed = ~s(
98+
{"responses": [{"id":"33","status":503,"headers":{"Content-Type":"text/html; charset=us-ascii"},"body":<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd"><HTML><HEAD><TITLE>Service Unavailable</TITLE><META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD><BODY><h2>Application Request Queue Full</h2><hr><p>HTTP Error 503. The application request queue is full.</p>\r\n</BODY></HTML>}]}
99+
)
100+
101+
assert {:ok, result} = JsonRemedy.repair(malformed)
102+
[response] = result["responses"]
103+
assert String.contains?(response["body"], "\r\n")
104+
assert String.contains?(response["body"], "Application Request Queue Full")
105+
end
106+
96107
test "handles HTML with special entities" do
97108
malformed = ~s({"content":<p>Read more &raquo; or &amp; continue</p>})
98109

0 commit comments

Comments
 (0)