diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml new file mode 100644 index 0000000..71f8cbb --- /dev/null +++ b/.github/workflows/sync-docs.yml @@ -0,0 +1,19 @@ +name: Sync Docs + +on: + push: + branches: [main] + paths: + - 'docs/**' + +jobs: + notify-docs-repo: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.DOCS_TOKEN }} + repository: cortexphp/docs + event-type: docs-updated diff --git a/README.md b/README.md index f489f8a..9065a35 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ Repair invalid JSON strings by automatically fixing common syntax errors like si ## Requirements - PHP 8.3+ +- JSON PHP extension ## Installation @@ -41,93 +42,9 @@ $data = (new JsonRepairer($broken))->decode(); $data = json_repair_decode($broken); ``` -## Configuration Options +## Documentation -### Omit Empty Values - -When repairing JSON from streaming sources (e.g., LLM responses), you may want to remove keys with missing values instead of adding empty strings: - -```php -// Missing value - defaults to adding empty string -$broken = '{"name": "John", "age": }'; -$repaired = json_repair($broken); -// {"name": "John", "age": ""} - -// Remove keys with missing values -$repaired = json_repair($broken, omitEmptyValues: true); -// {"name": "John"} -``` - -### Omit Incomplete Strings - -Similarly, you can remove keys with incomplete string values instead of closing them: - -```php -// Incomplete string - defaults to closing the string -$broken = '{"name": "John", "bio": "A developer who'; -$repaired = json_repair($broken); -// {"name": "John", "bio": "A developer who"} - -// Remove keys with incomplete strings -$repaired = json_repair($broken, omitIncompleteStrings: true); -// {"name": "John"} -``` - -### Using Both Options Together - -Both options can be used together, which is especially useful for streaming JSON where deltas are concatenated: - -```php -$broken = '{"name": "John", "age": , "bio": "A developer who'; -$repaired = json_repair($broken, omitEmptyValues: true, omitIncompleteStrings: true); -// {"name": "John"} -``` - -### Using with JsonRepairer Class - -You can also pass these options to the `JsonRepairer` constructor: - -```php -$repairer = new JsonRepairer( - $broken, - ensureAscii: true, - omitEmptyValues: true, - omitIncompleteStrings: true -); -$repaired = $repairer->repair(); -``` - -Or with `json_repair_decode`: - -```php -$data = json_repair_decode( - $broken, - omitEmptyValues: true, - omitIncompleteStrings: true -); -``` - -## Logging - -The library supports PSR-3 logging for debugging repair operations. Pass any PSR-3 compatible logger to see what repairs are being made: - -```php -use Psr\Log\LoggerInterface; - -// Using the helper function -$repaired = json_repair($broken, logger: $logger); - -// Using the class (implements LoggerAwareInterface) -$repairer = new JsonRepairer($broken); -$repairer->setLogger($logger); -$repaired = $repairer->repair(); -``` - -Log messages include the position in the JSON string and a context snippet showing where the repair occurred. This is useful for: - -- Debugging why certain repairs are being made -- Understanding how malformed JSON is being interpreted -- Tracking repair operations in production environments +📚 **[View Full Documentation →](https://docs.cortexphp.com/json-repair)** ## Credits diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg new file mode 100644 index 0000000..3d52675 --- /dev/null +++ b/docs/assets/favicon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/assets/logo-dark.svg b/docs/assets/logo-dark.svg new file mode 100644 index 0000000..ff1b3a8 --- /dev/null +++ b/docs/assets/logo-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/logo-light.svg b/docs/assets/logo-light.svg new file mode 100644 index 0000000..01e533e --- /dev/null +++ b/docs/assets/logo-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/docs.json b/docs/docs.json new file mode 100644 index 0000000..b474818 --- /dev/null +++ b/docs/docs.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "aspen", + "name": "CortexPHP", + "colors": { + "primary": "#1C85FE", + "light": "#1C85FE", + "dark": "#1C85FE" + }, + "favicon": "/assets/favicon.svg", + "icons": { + "library": "lucide" + }, + "navbar": { + "links": [ + { + "label": "GitHub", + "icon": "github", + "href": "https://github.com/cortexphp" + } + ] + }, + "navigation": { + "products": [ + { + "product": "JSON Repair", + "icon": "shield-ellipsis", + "groups": [ + { + "group": "Get Started", + "pages": [ + "json-repair/introduction", + "json-repair/installation", + "json-repair/quickstart" + ] + }, + { + "group": "Usage", + "pages": [ + "json-repair/repair-examples", + "json-repair/configuration", + "json-repair/logging" + ] + } + ] + } + ] + }, + "logo": { + "light": "/assets/logo-light.svg", + "dark": "/assets/logo-dark.svg" + }, + "styling": { + "codeblocks": { + "theme": { + "light": "github-light-high-contrast", + "dark": "github-dark" + } + } + }, + "contextual": { + "options": [ + "copy", + "view", + "chatgpt", + "claude" + ] + }, + "footer": { + "socials": { + "github": "https://github.com/cortexphp", + "x": "https://twitter.com/cortexphp" + } + } +} diff --git a/docs/json-repair/configuration.mdx b/docs/json-repair/configuration.mdx new file mode 100644 index 0000000..c1fcacd --- /dev/null +++ b/docs/json-repair/configuration.mdx @@ -0,0 +1,94 @@ +--- +title: Configuration +description: 'Options for customizing JSON repair behavior' +icon: 'settings' +--- + +## ensureAscii + +When `true` (default), non-ASCII characters are escaped. When `false`, Unicode is preserved. + +```php +use function Cortex\JsonRepair\json_repair; + +// ensureAscii: true (default) - escapes Unicode +json_repair("{'test_中国人_ascii':'统一码'}", ensureAscii: true); +// {"test_中国人_ascii":"\u7edf\u4e00\u7801"} + +// ensureAscii: false - preserves Unicode +json_repair("{'test_中国人_ascii':'统一码'}", ensureAscii: false); +// {"test_中国人_ascii":"统一码"} +``` + +## omitEmptyValues + +When `true`, keys with missing values are removed instead of being set to empty strings. Useful for streaming JSON where `{"key": }` should become `{}`. + +```php +use function Cortex\JsonRepair\json_repair; + +// omitEmptyValues: false (default) +json_repair('{"key": }'); +// {"key":""} + +// omitEmptyValues: true +json_repair('{"key": }', omitEmptyValues: true); +// {} + +json_repair('{"key1": "v1", "key2": }', omitEmptyValues: true); +// {"key1": "v1"} + +json_repair('{"user": {"name": "John", "age": }}', omitEmptyValues: true); +// {"user": {"name": "John"}} +``` + +## omitIncompleteStrings + +When `true`, keys with incomplete (truncated) string values are removed. Useful for streaming LLM output where you prefer to drop partial strings rather than close them. + +```php +use function Cortex\JsonRepair\json_repair; + +// omitIncompleteStrings: false (default) +json_repair('{"name": "John", "description": "A person who'); +// {"name": "John", "description": "A person who"} + +// omitIncompleteStrings: true +json_repair('{"name": "John", "description": "A person who', omitIncompleteStrings: true); +// {"name": "John"} + +json_repair('{"key": "val', omitIncompleteStrings: true); +// {} + +json_repair('{"complete": "value", "incomplete": "partial', omitIncompleteStrings: true); +// {"complete": "value"} +``` + +## Combined Options + +Use both options together for streaming LLM output: + +```php +use function Cortex\JsonRepair\json_repair_decode; + +$data = json_repair_decode( + '{"name": "John", "age": , "bio": "A developer who', + omitEmptyValues: true, + omitIncompleteStrings: true +); +// ['name' => 'John'] +``` + +With the class: + +```php +use Cortex\JsonRepair\JsonRepairer; + +$repairer = new JsonRepairer( + '{"key1": "v1", "key2": , "key3": "partial', + omitEmptyValues: true, + omitIncompleteStrings: true +); +$repaired = $repairer->repair(); +// {"key1": "v1"} +``` diff --git a/docs/json-repair/installation.mdx b/docs/json-repair/installation.mdx new file mode 100644 index 0000000..02ee466 --- /dev/null +++ b/docs/json-repair/installation.mdx @@ -0,0 +1,47 @@ +--- +title: Installation +description: 'Install and set up the JSON Repair package' +icon: 'terminal' +--- + +## Requirements + +- PHP 8.3+ +- JSON PHP extension +- Composer for dependency management + +## Installation + +```bash +composer require cortexphp/json-repair +``` + +## Development Setup + +### For Package Development + +If you're contributing to the package or want to run tests: + + + + ```bash + git clone https://github.com/cortexphp/json-repair.git + cd json-repair + ``` + + + ```bash + composer install + ``` + + + ```bash + composer test + ``` + + + ```bash + composer check + ``` + + diff --git a/docs/json-repair/introduction.mdx b/docs/json-repair/introduction.mdx new file mode 100644 index 0000000..7454fe6 --- /dev/null +++ b/docs/json-repair/introduction.mdx @@ -0,0 +1,57 @@ +--- +title: Introduction +description: 'Repair invalid JSON strings by fixing common syntax errors like single quotes, unquoted keys, trailing commas, and missing brackets. Perfect for parsing LLM outputs and malformed API responses.' +icon: 'book-open' +--- + +## Key Features + + + + Converts single quotes to double quotes, adds quotes around unquoted keys and values, and handles mixed quote styles. + + + Adds missing closing brackets and braces, closes unclosed strings, and repairs incomplete JSON from streaming responses. + + + Handles truncated JSON from streaming LLM responses, Python-style booleans (True/False/None), and comments. + + + Extracts JSON from markdown code blocks, stripping surrounding text and backticks. + + + +## About This Package + +JSON Repair fixes malformed JSON from LLMs, APIs, and user input. It handles common syntax errors that cause `json_decode()` to fail: single quotes, unquoted keys, trailing commas, missing brackets, and more. + +## Quick Example + +```php +use function Cortex\JsonRepair\json_repair; + +$broken = "{'name': 'John', age: 30, active: true,}"; +$repaired = json_repair($broken); + +// {"name": "John", "age": 30, "active": true} +``` + + + Get started with the [Quick Start](/json-repair/quickstart) guide. + diff --git a/docs/json-repair/logging.mdx b/docs/json-repair/logging.mdx new file mode 100644 index 0000000..c09f080 --- /dev/null +++ b/docs/json-repair/logging.mdx @@ -0,0 +1,53 @@ +--- +title: Logging +description: 'Debug repair actions with a PSR-3 logger' +icon: 'terminal' +--- + +Attach a PSR-3 logger to see what repairs are applied. Logs are emitted at `debug` level. + +## With the Helper Function + +Pass a logger as the `logger` parameter: + +```php +use function Cortex\JsonRepair\json_repair; +use Psr\Log\LoggerInterface; + +$logger = new MyLogger(); // Any PSR-3 implementation +$result = json_repair('{"key": "value', logger: $logger); +``` + +## With the Class + +Use `setLogger()` before calling `repair()` or `decode()`: + +```php +use Cortex\JsonRepair\JsonRepairer; + +$repairer = new JsonRepairer("{'key': 'value'}"); +$repairer->setLogger($logger); +$result = $repairer->repair(); +``` + +## Log Messages + +Valid JSON produces a single log entry: + +- `JSON is already valid, returning as-is` + +When repairs occur, you'll see messages such as: + +- `Starting JSON repair` +- `Adding missing closing quote for unclosed string` +- `Adding missing closing bracket/brace` +- `Converting single-quoted key to double quotes` +- `Normalizing boolean/null value` (with context: `from: 'True', to: 'true'`) +- `Adding quotes around unquoted key` +- `Found unquoted string value, adding quotes` +- `Inserting missing comma` +- `Inserting missing colon after key` +- `Extracted JSON from markdown code block` +- `Removing key with missing value (omitEmptyValues enabled)` + +Log entries include `position` and `context` with `>>>` markers showing where the repair occurred. diff --git a/docs/json-repair/quickstart.mdx b/docs/json-repair/quickstart.mdx new file mode 100644 index 0000000..d87f85e --- /dev/null +++ b/docs/json-repair/quickstart.mdx @@ -0,0 +1,47 @@ +--- +title: Quick Start +description: 'Get up and running with JSON Repair in minutes' +icon: 'rocket' +--- + +## Helper Function + +Use `json_repair()` for a simple one-liner: + +```php +use function Cortex\JsonRepair\json_repair; + +$repaired = json_repair("{'key': 'value'}"); +// {"key": "value"} +``` + +To repair and decode in one step, use `json_repair_decode()`: + +```php +use function Cortex\JsonRepair\json_repair_decode; + +$data = json_repair_decode("{'key': 'value', 'number': 123}"); +// ['key' => 'value', 'number' => 123] +``` + +## JsonRepairer Class + +For more control (options, logging), use the class directly: + +```php +use Cortex\JsonRepair\JsonRepairer; + +$repairer = new JsonRepairer("{'key': 'value'}"); +$repaired = $repairer->repair(); +// {"key": "value"} + +// Or decode directly +$data = $repairer->decode(); +// ['key' => 'value'] +``` + +## Next Steps + +- See [Repair Examples](/json-repair/repair-examples) for what gets fixed +- Configure [omitEmptyValues](/json-repair/configuration#omitemptyvalues) and [omitIncompleteStrings](/json-repair/configuration#omitincompletestrings) for streaming LLM output +- Attach a [PSR-3 logger](/json-repair/logging) for debugging diff --git a/docs/json-repair/repair-examples.mdx b/docs/json-repair/repair-examples.mdx new file mode 100644 index 0000000..bed69a7 --- /dev/null +++ b/docs/json-repair/repair-examples.mdx @@ -0,0 +1,159 @@ +--- +title: Repair Examples +description: 'What JSON Repair fixes, with before/after examples from the test suite' +icon: 'wrench' +--- + +## Quotes + +Single quotes, unquoted keys, and unquoted values are converted to valid JSON. + + + ```php Input + json_repair("{'key': 'value'}"); + json_repair('{name: "John", age: 30}'); + json_repair("{'key': 'string', \"key4\": unquoted}"); + ``` + + ```json Output + {"key": "value"} + {"name": "John", "age": 30} + {"key": "string", "key4": "unquoted"} + ``` + + +Quotes inside string values are escaped: + + + ```php Input + json_repair('{"key": "v"alu"e"}'); + ``` + + ```json Output + {"key": "v\"alu\"e"} + ``` + + +## Commas and Colons + +Trailing commas are removed; missing commas and colons are inserted. + + + ```php Input + json_repair('{"key": "value",}'); + json_repair('[1, 2, 3,]'); + json_repair('{"key1": "v1" "key2": "v2"}'); + json_repair('{"key" "value"}'); + ``` + + ```json Output + {"key": "value"} + [1, 2, 3] + {"key1": "v1","key2": "v2"} + {"key": "value"} + ``` + + +## Brackets + +Missing closing brackets and braces are added. Unclosed strings are closed. + + + ```php Input + json_repair('{"key": "value"'); + json_repair('["a", "b"'); + json_repair('{"key": "val'); + json_repair('{"key1": {"key2": "value"'); + ``` + + ```json Output + {"key": "value"} + ["a", "b"] + {"key": "val"} + {"key1": {"key2": "value"}} + ``` + + +## Values + +Python-style booleans and null are normalized. Missing values become empty strings (or are omitted with [omitEmptyValues](/json-repair/configuration#omitemptyvalues)). + + + ```php Input + json_repair('{"key": True}'); + json_repair('{"key": False}'); + json_repair('{"key": None}'); + json_repair('[True, False, None]'); + json_repair('{"key": }'); + ``` + + ```json Output + {"key": true} + {"key": false} + {"key": null} + [true, false, null] + {"key":""} + ``` + + +## Comments + +Single-line (`//`) and block (`/* */`) comments are removed. + + + ```php Input + json_repair('{"key": "value", // comment'); + json_repair('{"key": "value", /* comment */'); + json_repair("{// User information\n\"name\": \"John\", /* Age in years */ \"age\": 30}"); + ``` + + ```json Output + {"key": "value"} + {"key": "value"} + {"name": "John", "age": 30} + ``` + + +## Markdown + +JSON is extracted from markdown code blocks. Surrounding text and backticks are stripped. + + + ```php Input + json_repair('lorem ```json {"key":"value"} ``` ipsum'); + json_repair("Based on the information extracted: ```json { 'a': 'b' } ```"); + json_repair('````{ "key": "value" }```'); + ``` + + ```json Output + {"key":"value"} + {"a": "b"} + {"key": "value"} + ``` + + +## LLM Streaming + +Incomplete JSON from streaming LLM responses is repaired by closing strings, numbers, and structures. + + + ```php Input + json_repair('{"name": "John", "description": "A person who'); + json_repair('{"count": 123'); + json_repair('{"user": {"name": "John", "age": 30'); + json_repair('{"items": [1, 2, 3'); + json_repair('{"message": "Hello\\'); + ``` + + ```json Output + {"name": "John", "description": "A person who"} + {"count": 123} + {"user": {"name": "John", "age": 30}} + {"items": [1, 2, 3]} + {"message": "Hello"} + ``` + + + + For streaming output, enable [omitIncompleteStrings](/json-repair/configuration#omitincompletestrings) to drop truncated string values instead of closing them. +