@nexkit/json-repair 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Public type contracts for `@nexkit/json-repair`.
3
+ *
4
+ * These shapes are frozen for the 1.x line: fields may be added, never removed
5
+ * or narrowed. New {@link RepairType} members may appear in minor releases, so
6
+ * consumers should not write exhaustive `switch` statements over them.
7
+ */
8
+ /**
9
+ * How much interpretation the repair engine is allowed to apply.
10
+ *
11
+ * - `safe` (default) applies only repairs with a single plausible reading and
12
+ * never invents data. Anything ambiguous fails with `AMBIGUOUS_REPAIR`.
13
+ * - `aggressive` additionally applies heuristics that guess intent, such as
14
+ * quoting bare values or dropping trailing garbage. Aggressive mode always
15
+ * tries `safe` first, so whenever `safe` succeeds both modes agree byte for
16
+ * byte.
17
+ */
18
+ type RepairMode = 'safe' | 'aggressive';
19
+ /**
20
+ * The kind of change the repair engine made. Every operation reported in
21
+ * {@link RepairResult.repairs} carries one of these.
22
+ *
23
+ * Repairs that discard input have deliberately distinct types
24
+ * (`removed-*`, `terminated-string-at-newline`) so that callers can audit
25
+ * whether any content was lost.
26
+ */
27
+ type RepairType =
28
+ /** A leading U+FEFF byte order mark was removed. */
29
+ 'removed-byte-order-mark'
30
+ /** JSON was isolated from Markdown fences or surrounding prose. */
31
+ | 'extracted-json'
32
+ /** A `//`, block or `#` comment was removed. */
33
+ | 'removed-comment'
34
+ /** Whitespace JSON does not allow between tokens, such as a non-breaking space, was dropped. */
35
+ | 'normalized-whitespace'
36
+ /** A comma directly before `}` or `]` was removed. */
37
+ | 'removed-trailing-comma'
38
+ /** A repeated or leading comma inside an object was removed. */
39
+ | 'removed-extra-comma'
40
+ /** A closing bracket that matched no open container was discarded. */
41
+ | 'removed-stray-token'
42
+ /** Content after the top-level value was discarded. */
43
+ | 'removed-trailing-content'
44
+ /** A truncated final key or member was discarded (streamed output). */
45
+ | 'removed-incomplete-member'
46
+ /** A missing separator between members or elements was inserted. */
47
+ | 'added-missing-comma'
48
+ /** A missing `:` between a key and its value was inserted. */
49
+ | 'added-missing-colon'
50
+ /** An absent or elided value was replaced by `null`. */
51
+ | 'added-missing-value'
52
+ /** An unclosed object was closed. */
53
+ | 'added-closing-brace'
54
+ /** An unclosed array was closed. */
55
+ | 'added-closing-bracket'
56
+ /** Single, backtick or typographic quotes were converted to `"`. */
57
+ | 'normalized-quotes'
58
+ /** A non-JSON literal was mapped to JSON (`True`, `None`, `NaN`). */
59
+ | 'normalized-literal'
60
+ /** A number literal was rewritten to valid JSON grammar (`+1`, `.5`, `0x1F`). */
61
+ | 'normalized-number'
62
+ /** A bare object key was quoted. */
63
+ | 'quoted-key'
64
+ /** A bare value was quoted as a string. */
65
+ | 'quoted-value'
66
+ /** A raw control character, inner quote or lone surrogate was escaped. */
67
+ | 'escaped-character'
68
+ /** An invalid escape sequence was corrected. */
69
+ | 'fixed-escape'
70
+ /** A string left open at end of input was closed. Lossless. */
71
+ | 'terminated-string-at-eof'
72
+ /** A string left open was closed at a line break. **Discards** the text after that line break. */
73
+ | 'terminated-string-at-newline';
74
+ /** Every {@link RepairType}, in a stable order. Useful for exhaustive UIs and tests. */
75
+ declare const REPAIR_TYPES: readonly ["removed-byte-order-mark", "extracted-json", "removed-comment", "normalized-whitespace", "removed-trailing-comma", "removed-extra-comma", "removed-stray-token", "removed-trailing-content", "removed-incomplete-member", "added-missing-comma", "added-missing-colon", "added-missing-value", "added-closing-brace", "added-closing-bracket", "normalized-quotes", "normalized-literal", "normalized-number", "quoted-key", "quoted-value", "escaped-character", "fixed-escape", "terminated-string-at-eof", "terminated-string-at-newline"];
76
+ /**
77
+ * A single change applied to the input.
78
+ *
79
+ * Positions locate the change in the **original** input string — they stay
80
+ * absolute even when the JSON was extracted from a larger document, and they
81
+ * include a leading byte order mark if one was present.
82
+ */
83
+ interface RepairOperation {
84
+ /** What kind of change was made. */
85
+ type: RepairType;
86
+ /** Zero-based offset into the original input, in UTF-16 code units. */
87
+ position?: number;
88
+ /** One-based line number in the original input. */
89
+ line?: number;
90
+ /** One-based column number in the original input, in UTF-16 code units. */
91
+ column?: number;
92
+ /** Human-readable description, used by the CLI's `--explain` output. */
93
+ message?: string;
94
+ }
95
+ /** Result of {@link repairJson} when `returnMetadata: true` is set. */
96
+ interface RepairResult {
97
+ /** Repaired JSON text. Guaranteed to be accepted by `JSON.parse`. */
98
+ json: string;
99
+ /** `true` when {@link RepairResult.json} differs from the input. */
100
+ changed: boolean;
101
+ /** Ordered list of the changes that were applied, ascending by position. */
102
+ repairs: RepairOperation[];
103
+ }
104
+ /**
105
+ * Options accepted by {@link repairJson} and {@link parseJson}.
106
+ *
107
+ * The boolean flags are tri-state: leaving one `undefined` uses the default for
108
+ * the selected {@link RepairMode}, while an explicit `true` or `false` always
109
+ * wins. Passing `false` therefore *disables* a repair — it does not mean
110
+ * "use the default".
111
+ */
112
+ interface RepairOptions {
113
+ /**
114
+ * Repair strategy. Defaults to `'safe'`.
115
+ * @see RepairMode
116
+ */
117
+ mode?: RepairMode;
118
+ /**
119
+ * Isolate JSON from Markdown code fences and surrounding prose before
120
+ * repairing. Defaults to `true`.
121
+ */
122
+ extract?: boolean;
123
+ /**
124
+ * Accept and strip line comments, block comments and `#` comments.
125
+ * Defaults to `true`.
126
+ * When `false`, a comment is a syntax error rather than trivia.
127
+ */
128
+ allowComments?: boolean;
129
+ /** Accept single-quoted strings. Defaults to `true`. */
130
+ allowSingleQuotes?: boolean;
131
+ /** Accept and quote bare object keys. Defaults to `true`. */
132
+ allowUnquotedKeys?: boolean;
133
+ /** Remove commas that directly precede `}` or `]`. Defaults to `true`. */
134
+ fixTrailingCommas?: boolean;
135
+ /** Close containers that were never closed. Defaults to `true`. */
136
+ fixMissingBrackets?: boolean;
137
+ /**
138
+ * Maximum accepted input length, in UTF-16 code units (not bytes).
139
+ * Defaults to `10_000_000`. Exceeding it throws `MAX_LENGTH_EXCEEDED`.
140
+ */
141
+ maxLength?: number;
142
+ /**
143
+ * Maximum accepted nesting depth. Defaults to `512`, and may not exceed
144
+ * {@link MAX_SUPPORTED_DEPTH}. Exceeding it throws `MAX_DEPTH_EXCEEDED`,
145
+ * which also keeps the parser clear of host stack limits.
146
+ */
147
+ maxDepth?: number;
148
+ /**
149
+ * Maximum number of repair operations per attempt. Defaults to `Infinity`;
150
+ * the engine is single pass, so it terminates regardless of this value.
151
+ */
152
+ maxRepairs?: number;
153
+ /** Return a {@link RepairResult} instead of a plain string. Defaults to `false`. */
154
+ returnMetadata?: boolean;
155
+ }
156
+ /** Options accepted by {@link extractJson} and {@link extractAllJson}. */
157
+ interface ExtractOptions extends Pick<RepairOptions, 'mode' | 'allowComments' | 'allowSingleQuotes' | 'allowUnquotedKeys' | 'fixTrailingCommas' | 'fixMissingBrackets' | 'maxLength' | 'maxDepth' | 'maxRepairs'> {
158
+ /**
159
+ * Which candidate {@link extractJson} returns when the document contains
160
+ * several JSON blocks. Defaults to `'best'`, matching what
161
+ * {@link repairJson} picks.
162
+ *
163
+ * - `best` — fewest repairs, then longest, then earliest. Prose decoys such
164
+ * as the `[1]` in "see [1] for details" lose to a real payload beside them.
165
+ * - `first` — the earliest repairable candidate, in document order.
166
+ * - `last` — the last repairable candidate, in document order.
167
+ * - `largest` — the longest repairable candidate; ties resolve to the earliest.
168
+ */
169
+ select?: 'best' | 'first' | 'last' | 'largest';
170
+ }
171
+ /** Machine-readable reason a repair failed. */
172
+ type JsonRepairErrorCode =
173
+ /** Input was not a string, was empty/whitespace-only, or an option was invalid. */
174
+ 'INVALID_INPUT'
175
+ /** No repairable JSON value could be located in the input. */
176
+ | 'NO_JSON_FOUND'
177
+ /** No safe repair sequence exists for this input. */
178
+ | 'UNREPAIRABLE_JSON'
179
+ /** Input exceeded `maxLength`. */
180
+ | 'MAX_LENGTH_EXCEEDED'
181
+ /** Nesting exceeded `maxDepth`. */
182
+ | 'MAX_DEPTH_EXCEEDED'
183
+ /** More repairs were required than `maxRepairs` allows. */
184
+ | 'MAX_REPAIRS_EXCEEDED'
185
+ /** Several readings are possible and `safe` mode refuses to choose. */
186
+ | 'AMBIGUOUS_REPAIR';
187
+ /**
188
+ * Hard ceiling for {@link RepairOptions.maxDepth}. Requesting more throws
189
+ * `INVALID_INPUT`. The cap keeps the recursive parser well clear of host stack
190
+ * limits on every supported runtime, so deep input always produces a
191
+ * `MAX_DEPTH_EXCEEDED` error rather than a stack overflow.
192
+ */
193
+ declare const MAX_SUPPORTED_DEPTH = 1024;
194
+
195
+ /** Default maximum input length, in UTF-16 code units. */
196
+ declare const DEFAULT_MAX_LENGTH = 10000000;
197
+ /** Default maximum nesting depth. */
198
+ declare const DEFAULT_MAX_DEPTH = 512;
199
+
200
+ /**
201
+ * Repairs malformed JSON and returns valid JSON text.
202
+ *
203
+ * Input that is already valid JSON is returned byte for byte, so this is safe to
204
+ * call unconditionally on data that is usually fine.
205
+ *
206
+ * @throws {JsonRepairError} `INVALID_INPUT`, `NO_JSON_FOUND`, `UNREPAIRABLE_JSON`,
207
+ * `AMBIGUOUS_REPAIR`, `MAX_LENGTH_EXCEEDED`, `MAX_DEPTH_EXCEEDED` or
208
+ * `MAX_REPAIRS_EXCEEDED`.
209
+ */
210
+ declare function repairJson(input: string, options?: RepairOptions & {
211
+ returnMetadata?: false;
212
+ }): string;
213
+ /**
214
+ * Repairs malformed JSON and reports what was changed.
215
+ *
216
+ * @throws {JsonRepairError} See the string-returning overload.
217
+ */
218
+ declare function repairJson(input: string, options: RepairOptions & {
219
+ returnMetadata: true;
220
+ }): RepairResult;
221
+ declare function repairJson(input: string, options?: RepairOptions): string | RepairResult;
222
+
223
+ /**
224
+ * Repairs malformed JSON and returns the parsed value.
225
+ *
226
+ * Equivalent to `JSON.parse(repairJson(input, options))`, but the value comes
227
+ * from the validation stage that {@link repairJson} already runs, so the text is
228
+ * never parsed twice.
229
+ *
230
+ * `T` is an unchecked assertion, exactly as with `JSON.parse`. Nothing here
231
+ * validates the shape of the result against it — pair this with a schema
232
+ * validator when the input is untrusted.
233
+ *
234
+ * @throws {JsonRepairError} `INVALID_INPUT`, `NO_JSON_FOUND`, `UNREPAIRABLE_JSON`,
235
+ * `AMBIGUOUS_REPAIR`, `MAX_LENGTH_EXCEEDED`, `MAX_DEPTH_EXCEEDED` or
236
+ * `MAX_REPAIRS_EXCEEDED`.
237
+ */
238
+ declare function parseJson<T = unknown>(input: string, options?: RepairOptions): T;
239
+
240
+ /**
241
+ * Extracts the first — by default the best — JSON value embedded in a larger
242
+ * document, and returns it **exactly as it appears in the input**.
243
+ *
244
+ * The text is returned verbatim rather than repaired, which keeps extraction
245
+ * lossless: whitespace, key order and number formatting all survive. A
246
+ * candidate still has to be repairable to be selected, and the default `best`
247
+ * strategy prefers the longest one, so a decoy such as the `[1]` in "see [1]
248
+ * for details" loses to a real payload beside it even though the decoy is
249
+ * already valid and the payload needs work. The returned string may itself need
250
+ * repairing before `JSON.parse` will take it.
251
+ *
252
+ * @throws {JsonRepairError} `INVALID_INPUT`, `MAX_LENGTH_EXCEEDED` or
253
+ * `NO_JSON_FOUND` when no candidate could be repaired.
254
+ */
255
+ declare function extractJson(input: string, options?: ExtractOptions): string;
256
+ /**
257
+ * Extracts every JSON value embedded in a document, in document order, each
258
+ * returned verbatim as it appears in the input.
259
+ *
260
+ * Values nested inside an already-extracted value are not reported separately,
261
+ * so a document containing `[{"a":1}]` yields the array, not the array and then
262
+ * its element.
263
+ *
264
+ * Returns an empty array when the document holds no repairable JSON. Unlike
265
+ * {@link extractJson} this never throws `NO_JSON_FOUND`, because "nothing here"
266
+ * is an ordinary answer when the caller asked for everything.
267
+ *
268
+ * @throws {JsonRepairError} `INVALID_INPUT` or `MAX_LENGTH_EXCEEDED`.
269
+ */
270
+ declare function extractAllJson(input: string, options?: ExtractOptions): string[];
271
+
272
+ /**
273
+ * Brand used to identify errors from this package across module realms.
274
+ *
275
+ * A dual ESM/CJS package can be loaded twice in the same process (once through
276
+ * `import`, once through `require`), which gives two distinct class objects and
277
+ * makes `instanceof` unreliable. `Symbol.for` uses the cross-realm global
278
+ * registry, so the brand stays identical for every copy.
279
+ */
280
+ declare const ERROR_BRAND: unique symbol;
281
+ /** Extra fields accepted when constructing a {@link JsonRepairError}. */
282
+ interface JsonRepairErrorDetails {
283
+ /** Zero-based offset into the original input, in UTF-16 code units. */
284
+ position?: number;
285
+ /** One-based line number in the original input. */
286
+ line?: number;
287
+ /** One-based column number in the original input. */
288
+ column?: number;
289
+ /** A short excerpt of the input around {@link JsonRepairErrorDetails.position}. */
290
+ snippet?: string;
291
+ }
292
+ /**
293
+ * The only error type thrown by this package.
294
+ *
295
+ * Every failure carries a stable {@link JsonRepairError.code} so callers can
296
+ * branch on the reason without matching on message text, which is not part of
297
+ * the public contract and may be reworded in any release.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * import { repairJson, isJsonRepairError } from '@nexkit/json-repair';
302
+ *
303
+ * try {
304
+ * repairJson(input);
305
+ * } catch (error) {
306
+ * if (isJsonRepairError(error) && error.code === 'AMBIGUOUS_REPAIR') {
307
+ * return repairJson(input, { mode: 'aggressive' });
308
+ * }
309
+ * throw error;
310
+ * }
311
+ * ```
312
+ */
313
+ declare class JsonRepairError extends Error {
314
+ /** Always `'JsonRepairError'`, so the name survives minification. */
315
+ readonly name = "JsonRepairError";
316
+ /** Stable, machine-readable failure reason. */
317
+ readonly code: JsonRepairErrorCode;
318
+ /** Zero-based offset into the original input, in UTF-16 code units. */
319
+ readonly position: number | undefined;
320
+ /** One-based line number in the original input. */
321
+ readonly line: number | undefined;
322
+ /** One-based column number in the original input. */
323
+ readonly column: number | undefined;
324
+ /** A short excerpt of the input around {@link JsonRepairError.position}. */
325
+ readonly snippet: string | undefined;
326
+ /** Cross-realm brand; see {@link isJsonRepairError}. */
327
+ readonly [ERROR_BRAND] = true;
328
+ constructor(code: JsonRepairErrorCode, message: string, details?: JsonRepairErrorDetails);
329
+ /**
330
+ * Reports whether a value is a {@link JsonRepairError}, including instances
331
+ * created by a different copy of this package.
332
+ *
333
+ * Prefer this over `instanceof` — see {@link ERROR_BRAND}.
334
+ */
335
+ static isJsonRepairError(value: unknown): value is JsonRepairError;
336
+ }
337
+ /**
338
+ * Reports whether a value is a {@link JsonRepairError}, including instances
339
+ * created by a different copy of this package (dual ESM/CJS loading).
340
+ */
341
+ declare function isJsonRepairError(value: unknown): value is JsonRepairError;
342
+
343
+ export { DEFAULT_MAX_DEPTH, DEFAULT_MAX_LENGTH, type ExtractOptions, JsonRepairError, type JsonRepairErrorCode, MAX_SUPPORTED_DEPTH, REPAIR_TYPES, type RepairMode, type RepairOperation, type RepairOptions, type RepairResult, type RepairType, extractAllJson, extractJson, isJsonRepairError, parseJson, repairJson };
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Public type contracts for `@nexkit/json-repair`.
3
+ *
4
+ * These shapes are frozen for the 1.x line: fields may be added, never removed
5
+ * or narrowed. New {@link RepairType} members may appear in minor releases, so
6
+ * consumers should not write exhaustive `switch` statements over them.
7
+ */
8
+ /**
9
+ * How much interpretation the repair engine is allowed to apply.
10
+ *
11
+ * - `safe` (default) applies only repairs with a single plausible reading and
12
+ * never invents data. Anything ambiguous fails with `AMBIGUOUS_REPAIR`.
13
+ * - `aggressive` additionally applies heuristics that guess intent, such as
14
+ * quoting bare values or dropping trailing garbage. Aggressive mode always
15
+ * tries `safe` first, so whenever `safe` succeeds both modes agree byte for
16
+ * byte.
17
+ */
18
+ type RepairMode = 'safe' | 'aggressive';
19
+ /**
20
+ * The kind of change the repair engine made. Every operation reported in
21
+ * {@link RepairResult.repairs} carries one of these.
22
+ *
23
+ * Repairs that discard input have deliberately distinct types
24
+ * (`removed-*`, `terminated-string-at-newline`) so that callers can audit
25
+ * whether any content was lost.
26
+ */
27
+ type RepairType =
28
+ /** A leading U+FEFF byte order mark was removed. */
29
+ 'removed-byte-order-mark'
30
+ /** JSON was isolated from Markdown fences or surrounding prose. */
31
+ | 'extracted-json'
32
+ /** A `//`, block or `#` comment was removed. */
33
+ | 'removed-comment'
34
+ /** Whitespace JSON does not allow between tokens, such as a non-breaking space, was dropped. */
35
+ | 'normalized-whitespace'
36
+ /** A comma directly before `}` or `]` was removed. */
37
+ | 'removed-trailing-comma'
38
+ /** A repeated or leading comma inside an object was removed. */
39
+ | 'removed-extra-comma'
40
+ /** A closing bracket that matched no open container was discarded. */
41
+ | 'removed-stray-token'
42
+ /** Content after the top-level value was discarded. */
43
+ | 'removed-trailing-content'
44
+ /** A truncated final key or member was discarded (streamed output). */
45
+ | 'removed-incomplete-member'
46
+ /** A missing separator between members or elements was inserted. */
47
+ | 'added-missing-comma'
48
+ /** A missing `:` between a key and its value was inserted. */
49
+ | 'added-missing-colon'
50
+ /** An absent or elided value was replaced by `null`. */
51
+ | 'added-missing-value'
52
+ /** An unclosed object was closed. */
53
+ | 'added-closing-brace'
54
+ /** An unclosed array was closed. */
55
+ | 'added-closing-bracket'
56
+ /** Single, backtick or typographic quotes were converted to `"`. */
57
+ | 'normalized-quotes'
58
+ /** A non-JSON literal was mapped to JSON (`True`, `None`, `NaN`). */
59
+ | 'normalized-literal'
60
+ /** A number literal was rewritten to valid JSON grammar (`+1`, `.5`, `0x1F`). */
61
+ | 'normalized-number'
62
+ /** A bare object key was quoted. */
63
+ | 'quoted-key'
64
+ /** A bare value was quoted as a string. */
65
+ | 'quoted-value'
66
+ /** A raw control character, inner quote or lone surrogate was escaped. */
67
+ | 'escaped-character'
68
+ /** An invalid escape sequence was corrected. */
69
+ | 'fixed-escape'
70
+ /** A string left open at end of input was closed. Lossless. */
71
+ | 'terminated-string-at-eof'
72
+ /** A string left open was closed at a line break. **Discards** the text after that line break. */
73
+ | 'terminated-string-at-newline';
74
+ /** Every {@link RepairType}, in a stable order. Useful for exhaustive UIs and tests. */
75
+ declare const REPAIR_TYPES: readonly ["removed-byte-order-mark", "extracted-json", "removed-comment", "normalized-whitespace", "removed-trailing-comma", "removed-extra-comma", "removed-stray-token", "removed-trailing-content", "removed-incomplete-member", "added-missing-comma", "added-missing-colon", "added-missing-value", "added-closing-brace", "added-closing-bracket", "normalized-quotes", "normalized-literal", "normalized-number", "quoted-key", "quoted-value", "escaped-character", "fixed-escape", "terminated-string-at-eof", "terminated-string-at-newline"];
76
+ /**
77
+ * A single change applied to the input.
78
+ *
79
+ * Positions locate the change in the **original** input string — they stay
80
+ * absolute even when the JSON was extracted from a larger document, and they
81
+ * include a leading byte order mark if one was present.
82
+ */
83
+ interface RepairOperation {
84
+ /** What kind of change was made. */
85
+ type: RepairType;
86
+ /** Zero-based offset into the original input, in UTF-16 code units. */
87
+ position?: number;
88
+ /** One-based line number in the original input. */
89
+ line?: number;
90
+ /** One-based column number in the original input, in UTF-16 code units. */
91
+ column?: number;
92
+ /** Human-readable description, used by the CLI's `--explain` output. */
93
+ message?: string;
94
+ }
95
+ /** Result of {@link repairJson} when `returnMetadata: true` is set. */
96
+ interface RepairResult {
97
+ /** Repaired JSON text. Guaranteed to be accepted by `JSON.parse`. */
98
+ json: string;
99
+ /** `true` when {@link RepairResult.json} differs from the input. */
100
+ changed: boolean;
101
+ /** Ordered list of the changes that were applied, ascending by position. */
102
+ repairs: RepairOperation[];
103
+ }
104
+ /**
105
+ * Options accepted by {@link repairJson} and {@link parseJson}.
106
+ *
107
+ * The boolean flags are tri-state: leaving one `undefined` uses the default for
108
+ * the selected {@link RepairMode}, while an explicit `true` or `false` always
109
+ * wins. Passing `false` therefore *disables* a repair — it does not mean
110
+ * "use the default".
111
+ */
112
+ interface RepairOptions {
113
+ /**
114
+ * Repair strategy. Defaults to `'safe'`.
115
+ * @see RepairMode
116
+ */
117
+ mode?: RepairMode;
118
+ /**
119
+ * Isolate JSON from Markdown code fences and surrounding prose before
120
+ * repairing. Defaults to `true`.
121
+ */
122
+ extract?: boolean;
123
+ /**
124
+ * Accept and strip line comments, block comments and `#` comments.
125
+ * Defaults to `true`.
126
+ * When `false`, a comment is a syntax error rather than trivia.
127
+ */
128
+ allowComments?: boolean;
129
+ /** Accept single-quoted strings. Defaults to `true`. */
130
+ allowSingleQuotes?: boolean;
131
+ /** Accept and quote bare object keys. Defaults to `true`. */
132
+ allowUnquotedKeys?: boolean;
133
+ /** Remove commas that directly precede `}` or `]`. Defaults to `true`. */
134
+ fixTrailingCommas?: boolean;
135
+ /** Close containers that were never closed. Defaults to `true`. */
136
+ fixMissingBrackets?: boolean;
137
+ /**
138
+ * Maximum accepted input length, in UTF-16 code units (not bytes).
139
+ * Defaults to `10_000_000`. Exceeding it throws `MAX_LENGTH_EXCEEDED`.
140
+ */
141
+ maxLength?: number;
142
+ /**
143
+ * Maximum accepted nesting depth. Defaults to `512`, and may not exceed
144
+ * {@link MAX_SUPPORTED_DEPTH}. Exceeding it throws `MAX_DEPTH_EXCEEDED`,
145
+ * which also keeps the parser clear of host stack limits.
146
+ */
147
+ maxDepth?: number;
148
+ /**
149
+ * Maximum number of repair operations per attempt. Defaults to `Infinity`;
150
+ * the engine is single pass, so it terminates regardless of this value.
151
+ */
152
+ maxRepairs?: number;
153
+ /** Return a {@link RepairResult} instead of a plain string. Defaults to `false`. */
154
+ returnMetadata?: boolean;
155
+ }
156
+ /** Options accepted by {@link extractJson} and {@link extractAllJson}. */
157
+ interface ExtractOptions extends Pick<RepairOptions, 'mode' | 'allowComments' | 'allowSingleQuotes' | 'allowUnquotedKeys' | 'fixTrailingCommas' | 'fixMissingBrackets' | 'maxLength' | 'maxDepth' | 'maxRepairs'> {
158
+ /**
159
+ * Which candidate {@link extractJson} returns when the document contains
160
+ * several JSON blocks. Defaults to `'best'`, matching what
161
+ * {@link repairJson} picks.
162
+ *
163
+ * - `best` — fewest repairs, then longest, then earliest. Prose decoys such
164
+ * as the `[1]` in "see [1] for details" lose to a real payload beside them.
165
+ * - `first` — the earliest repairable candidate, in document order.
166
+ * - `last` — the last repairable candidate, in document order.
167
+ * - `largest` — the longest repairable candidate; ties resolve to the earliest.
168
+ */
169
+ select?: 'best' | 'first' | 'last' | 'largest';
170
+ }
171
+ /** Machine-readable reason a repair failed. */
172
+ type JsonRepairErrorCode =
173
+ /** Input was not a string, was empty/whitespace-only, or an option was invalid. */
174
+ 'INVALID_INPUT'
175
+ /** No repairable JSON value could be located in the input. */
176
+ | 'NO_JSON_FOUND'
177
+ /** No safe repair sequence exists for this input. */
178
+ | 'UNREPAIRABLE_JSON'
179
+ /** Input exceeded `maxLength`. */
180
+ | 'MAX_LENGTH_EXCEEDED'
181
+ /** Nesting exceeded `maxDepth`. */
182
+ | 'MAX_DEPTH_EXCEEDED'
183
+ /** More repairs were required than `maxRepairs` allows. */
184
+ | 'MAX_REPAIRS_EXCEEDED'
185
+ /** Several readings are possible and `safe` mode refuses to choose. */
186
+ | 'AMBIGUOUS_REPAIR';
187
+ /**
188
+ * Hard ceiling for {@link RepairOptions.maxDepth}. Requesting more throws
189
+ * `INVALID_INPUT`. The cap keeps the recursive parser well clear of host stack
190
+ * limits on every supported runtime, so deep input always produces a
191
+ * `MAX_DEPTH_EXCEEDED` error rather than a stack overflow.
192
+ */
193
+ declare const MAX_SUPPORTED_DEPTH = 1024;
194
+
195
+ /** Default maximum input length, in UTF-16 code units. */
196
+ declare const DEFAULT_MAX_LENGTH = 10000000;
197
+ /** Default maximum nesting depth. */
198
+ declare const DEFAULT_MAX_DEPTH = 512;
199
+
200
+ /**
201
+ * Repairs malformed JSON and returns valid JSON text.
202
+ *
203
+ * Input that is already valid JSON is returned byte for byte, so this is safe to
204
+ * call unconditionally on data that is usually fine.
205
+ *
206
+ * @throws {JsonRepairError} `INVALID_INPUT`, `NO_JSON_FOUND`, `UNREPAIRABLE_JSON`,
207
+ * `AMBIGUOUS_REPAIR`, `MAX_LENGTH_EXCEEDED`, `MAX_DEPTH_EXCEEDED` or
208
+ * `MAX_REPAIRS_EXCEEDED`.
209
+ */
210
+ declare function repairJson(input: string, options?: RepairOptions & {
211
+ returnMetadata?: false;
212
+ }): string;
213
+ /**
214
+ * Repairs malformed JSON and reports what was changed.
215
+ *
216
+ * @throws {JsonRepairError} See the string-returning overload.
217
+ */
218
+ declare function repairJson(input: string, options: RepairOptions & {
219
+ returnMetadata: true;
220
+ }): RepairResult;
221
+ declare function repairJson(input: string, options?: RepairOptions): string | RepairResult;
222
+
223
+ /**
224
+ * Repairs malformed JSON and returns the parsed value.
225
+ *
226
+ * Equivalent to `JSON.parse(repairJson(input, options))`, but the value comes
227
+ * from the validation stage that {@link repairJson} already runs, so the text is
228
+ * never parsed twice.
229
+ *
230
+ * `T` is an unchecked assertion, exactly as with `JSON.parse`. Nothing here
231
+ * validates the shape of the result against it — pair this with a schema
232
+ * validator when the input is untrusted.
233
+ *
234
+ * @throws {JsonRepairError} `INVALID_INPUT`, `NO_JSON_FOUND`, `UNREPAIRABLE_JSON`,
235
+ * `AMBIGUOUS_REPAIR`, `MAX_LENGTH_EXCEEDED`, `MAX_DEPTH_EXCEEDED` or
236
+ * `MAX_REPAIRS_EXCEEDED`.
237
+ */
238
+ declare function parseJson<T = unknown>(input: string, options?: RepairOptions): T;
239
+
240
+ /**
241
+ * Extracts the first — by default the best — JSON value embedded in a larger
242
+ * document, and returns it **exactly as it appears in the input**.
243
+ *
244
+ * The text is returned verbatim rather than repaired, which keeps extraction
245
+ * lossless: whitespace, key order and number formatting all survive. A
246
+ * candidate still has to be repairable to be selected, and the default `best`
247
+ * strategy prefers the longest one, so a decoy such as the `[1]` in "see [1]
248
+ * for details" loses to a real payload beside it even though the decoy is
249
+ * already valid and the payload needs work. The returned string may itself need
250
+ * repairing before `JSON.parse` will take it.
251
+ *
252
+ * @throws {JsonRepairError} `INVALID_INPUT`, `MAX_LENGTH_EXCEEDED` or
253
+ * `NO_JSON_FOUND` when no candidate could be repaired.
254
+ */
255
+ declare function extractJson(input: string, options?: ExtractOptions): string;
256
+ /**
257
+ * Extracts every JSON value embedded in a document, in document order, each
258
+ * returned verbatim as it appears in the input.
259
+ *
260
+ * Values nested inside an already-extracted value are not reported separately,
261
+ * so a document containing `[{"a":1}]` yields the array, not the array and then
262
+ * its element.
263
+ *
264
+ * Returns an empty array when the document holds no repairable JSON. Unlike
265
+ * {@link extractJson} this never throws `NO_JSON_FOUND`, because "nothing here"
266
+ * is an ordinary answer when the caller asked for everything.
267
+ *
268
+ * @throws {JsonRepairError} `INVALID_INPUT` or `MAX_LENGTH_EXCEEDED`.
269
+ */
270
+ declare function extractAllJson(input: string, options?: ExtractOptions): string[];
271
+
272
+ /**
273
+ * Brand used to identify errors from this package across module realms.
274
+ *
275
+ * A dual ESM/CJS package can be loaded twice in the same process (once through
276
+ * `import`, once through `require`), which gives two distinct class objects and
277
+ * makes `instanceof` unreliable. `Symbol.for` uses the cross-realm global
278
+ * registry, so the brand stays identical for every copy.
279
+ */
280
+ declare const ERROR_BRAND: unique symbol;
281
+ /** Extra fields accepted when constructing a {@link JsonRepairError}. */
282
+ interface JsonRepairErrorDetails {
283
+ /** Zero-based offset into the original input, in UTF-16 code units. */
284
+ position?: number;
285
+ /** One-based line number in the original input. */
286
+ line?: number;
287
+ /** One-based column number in the original input. */
288
+ column?: number;
289
+ /** A short excerpt of the input around {@link JsonRepairErrorDetails.position}. */
290
+ snippet?: string;
291
+ }
292
+ /**
293
+ * The only error type thrown by this package.
294
+ *
295
+ * Every failure carries a stable {@link JsonRepairError.code} so callers can
296
+ * branch on the reason without matching on message text, which is not part of
297
+ * the public contract and may be reworded in any release.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * import { repairJson, isJsonRepairError } from '@nexkit/json-repair';
302
+ *
303
+ * try {
304
+ * repairJson(input);
305
+ * } catch (error) {
306
+ * if (isJsonRepairError(error) && error.code === 'AMBIGUOUS_REPAIR') {
307
+ * return repairJson(input, { mode: 'aggressive' });
308
+ * }
309
+ * throw error;
310
+ * }
311
+ * ```
312
+ */
313
+ declare class JsonRepairError extends Error {
314
+ /** Always `'JsonRepairError'`, so the name survives minification. */
315
+ readonly name = "JsonRepairError";
316
+ /** Stable, machine-readable failure reason. */
317
+ readonly code: JsonRepairErrorCode;
318
+ /** Zero-based offset into the original input, in UTF-16 code units. */
319
+ readonly position: number | undefined;
320
+ /** One-based line number in the original input. */
321
+ readonly line: number | undefined;
322
+ /** One-based column number in the original input. */
323
+ readonly column: number | undefined;
324
+ /** A short excerpt of the input around {@link JsonRepairError.position}. */
325
+ readonly snippet: string | undefined;
326
+ /** Cross-realm brand; see {@link isJsonRepairError}. */
327
+ readonly [ERROR_BRAND] = true;
328
+ constructor(code: JsonRepairErrorCode, message: string, details?: JsonRepairErrorDetails);
329
+ /**
330
+ * Reports whether a value is a {@link JsonRepairError}, including instances
331
+ * created by a different copy of this package.
332
+ *
333
+ * Prefer this over `instanceof` — see {@link ERROR_BRAND}.
334
+ */
335
+ static isJsonRepairError(value: unknown): value is JsonRepairError;
336
+ }
337
+ /**
338
+ * Reports whether a value is a {@link JsonRepairError}, including instances
339
+ * created by a different copy of this package (dual ESM/CJS loading).
340
+ */
341
+ declare function isJsonRepairError(value: unknown): value is JsonRepairError;
342
+
343
+ export { DEFAULT_MAX_DEPTH, DEFAULT_MAX_LENGTH, type ExtractOptions, JsonRepairError, type JsonRepairErrorCode, MAX_SUPPORTED_DEPTH, REPAIR_TYPES, type RepairMode, type RepairOperation, type RepairOptions, type RepairResult, type RepairType, extractAllJson, extractJson, isJsonRepairError, parseJson, repairJson };