yini-parser 1.5.0 → 1.6.1

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/LICENSE +1 -1
  3. package/README.md +199 -37
  4. package/dist/YINI.d.ts +22 -7
  5. package/dist/YINI.js +104 -0
  6. package/dist/core/astBuilder.d.ts +94 -15
  7. package/dist/core/astBuilder.js +396 -364
  8. package/dist/core/errorDataHandler.d.ts +29 -1
  9. package/dist/core/errorDataHandler.js +120 -63
  10. package/dist/core/internalTypes.d.ts +10 -1
  11. package/dist/core/objectBuilder.js +21 -6
  12. package/dist/core/options/defaultParserOptions.d.ts +3 -2
  13. package/dist/core/options/defaultParserOptions.js +2 -1
  14. package/dist/core/options/optionsFunctions.js +5 -1
  15. package/dist/core/pipeline/pipeline.js +31 -12
  16. package/dist/core/runtime.js +29 -34
  17. package/dist/grammar/generated/YiniLexer.d.ts +28 -35
  18. package/dist/grammar/generated/YiniLexer.js +323 -310
  19. package/dist/grammar/generated/YiniParser.d.ts +158 -80
  20. package/dist/grammar/generated/YiniParser.js +1141 -620
  21. package/dist/grammar/generated/YiniParserVisitor.d.ts +77 -14
  22. package/dist/grammar/generated/YiniParserVisitor.js +1 -1
  23. package/dist/index.d.ts +4 -1
  24. package/dist/index.js +6 -3
  25. package/dist/parsers/extractHeaderParts.d.ts +12 -19
  26. package/dist/parsers/extractHeaderParts.js +57 -46
  27. package/dist/parsers/parseNumber.d.ts +24 -6
  28. package/dist/parsers/parseNumber.js +114 -49
  29. package/dist/parsers/parseSectionHeader.d.ts +11 -3
  30. package/dist/parsers/parseSectionHeader.js +55 -43
  31. package/dist/parsers/parseString.js +42 -21
  32. package/dist/parsers/validateShebangPlacement.d.ts +4 -0
  33. package/dist/parsers/validateShebangPlacement.js +115 -0
  34. package/dist/types/index.d.ts +19 -2
  35. package/dist/utils/print.d.ts +1 -0
  36. package/dist/utils/print.js +5 -1
  37. package/dist/utils/string.d.ts +1 -0
  38. package/dist/utils/string.js +17 -1
  39. package/dist/utils/system.d.ts +1 -0
  40. package/dist/utils/system.js +6 -1
  41. package/dist/utils/yiniHelpers.d.ts +44 -2
  42. package/dist/utils/yiniHelpers.js +134 -46
  43. package/examples/basic.yini +1 -0
  44. package/examples/compare-formats.md +1 -1
  45. package/examples/nested.yini +1 -1
  46. package/examples/parse-example.ts +1 -0
  47. package/package.json +11 -3
  48. package/dist/YINI.js.map +0 -1
  49. package/dist/config/env.js.map +0 -1
  50. package/dist/core/astBuilder.js.map +0 -1
  51. package/dist/core/errorDataHandler.js.map +0 -1
  52. package/dist/core/internalTypes.js.map +0 -1
  53. package/dist/core/objectBuilder.js.map +0 -1
  54. package/dist/core/options/defaultParserOptions.js.map +0 -1
  55. package/dist/core/options/failLevel.js.map +0 -1
  56. package/dist/core/options/optionsFunctions.js.map +0 -1
  57. package/dist/core/parsingRules/modeFromRulesMatcher.js.map +0 -1
  58. package/dist/core/parsingRules/rulesConstAndGuards.js.map +0 -1
  59. package/dist/core/pipeline/errorListeners.js.map +0 -1
  60. package/dist/core/pipeline/pipeline.js.map +0 -1
  61. package/dist/core/resultMetadataBuilder.js.map +0 -1
  62. package/dist/core/runtime.js.map +0 -1
  63. package/dist/dev/main.d.ts +0 -1
  64. package/dist/dev/main.js +0 -139
  65. package/dist/dev/main.js.map +0 -1
  66. package/dist/dev/quick-test-samples/defect-inputs.d.ts +0 -37
  67. package/dist/dev/quick-test-samples/defect-inputs.js +0 -106
  68. package/dist/dev/quick-test-samples/defect-inputs.js.map +0 -1
  69. package/dist/dev/quick-test-samples/valid-inputs.d.ts +0 -21
  70. package/dist/dev/quick-test-samples/valid-inputs.js +0 -422
  71. package/dist/dev/quick-test-samples/valid-inputs.js.map +0 -1
  72. package/dist/grammar/generated/YiniLexer.js.map +0 -1
  73. package/dist/grammar/generated/YiniParser.js.map +0 -1
  74. package/dist/grammar/generated/YiniParserVisitor.js.map +0 -1
  75. package/dist/index.js.map +0 -1
  76. package/dist/parsers/extractHeaderParts.js.map +0 -1
  77. package/dist/parsers/extractSignificantYiniLine.js.map +0 -1
  78. package/dist/parsers/parseBoolean.js.map +0 -1
  79. package/dist/parsers/parseNull.js.map +0 -1
  80. package/dist/parsers/parseNumber.js.map +0 -1
  81. package/dist/parsers/parseSectionHeader.js.map +0 -1
  82. package/dist/parsers/parseString.js.map +0 -1
  83. package/dist/types/index.js.map +0 -1
  84. package/dist/utils/number.js.map +0 -1
  85. package/dist/utils/object.js.map +0 -1
  86. package/dist/utils/pathAndFileName.js.map +0 -1
  87. package/dist/utils/print.js.map +0 -1
  88. package/dist/utils/string.js.map +0 -1
  89. package/dist/utils/system.js.map +0 -1
  90. package/dist/utils/yiniHelpers.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 1.6.1 - 2026 June
4
+ - **Fixed:** Aligned parser behavior with the external `yini-test` conformance suite for shebang-like comment lines, misplaced `@yini` directives, and triple-quoted string line endings.
5
+ - **Fixed:** `#!` lines after an opening `@yini` marker are now treated as comment trivia, while `@yini` directives after document content are reported as syntax errors in both lenient and strict mode.
6
+ - **Fixed:** Triple-quoted raw and Classic strings now normalize physical `CRLF`/`CR` line endings to `LF`, producing stable output across platforms.
7
+ - **Fixed:** Diagnostic line numbers no longer shift by one after a valid shebang line; strict-mode trailing-comma errors now point to the actual physical source line.
8
+ - **Fixed:** Removed the outdated warning `Warning: Strict initialMode is not yet fully implemented.` Strict mode is now implemented against the latest YINI Specification RC 6.
9
+ - **Improved:** Parse errors now use a concise `YiniParseError`, and missing `/END` messages clearly explain that `/END` is required in strict mode but optional in the default lenient mode.
10
+
11
+ ## 1.6.0 - 2026 May
12
+ - **Improved:** Reduced the published npm package contents by excluding development-only build output, internal tool output, and duplicate `dist/src` declaration files from the package tarball.
13
+ - **Added:** Implemented a `yini-test` adapter (`tools/yini-test-adapter.ts`) for testing this parser against an external conformance corpus. The `yini-test` corpus/test runner is planned to be made public and released separately in the future.
14
+ - **Updated:** Parser behavior aligned with YINI Specification `v1.0.0-RC.6`, including:
15
+ * **Changed:** `#` now always starts a comment outside string literals. No whitespace is required before or after `#`.
16
+ * **Added:** Support for explicit hexadecimal notation using `hex:` as an alternative to `0x...`.
17
+ * **Removed:** Support for `#` as a hexadecimal number prefix. Hexadecimal numbers must now use `0x...` or `hex:...`.
18
+ * **Removed:** Internal handling of Hyper Strings (H-Strings), simplifying string parsing and reducing parser complexity.
19
+ * **Added:** Lenient mode now accepts `=` as an alternative inline object member separator. The canonical form remains `key: value`.
20
+ * **Changed:** Strict mode rejects `=` inside inline objects; inline object members must use `:`.
21
+ * **Changed:** String concatenation now uses the updated grammar model:
22
+ - `+` performs explicit string concatenation only; it does not define numeric addition.
23
+ - In strict mode, all operands must be string literals.
24
+ - In lenient mode, the first operand must be a string literal; later operands may be string, number, boolean, or null literals.
25
+ - If the expression does not begin with a string literal, it is rejected rather than treated as arithmetic.
26
+ - A line break is allowed after `+`, but not before it.
27
+ - Lists and inline objects are invalid concatenation operands.
28
+ * **Added:** Empty-document handling by mode:
29
+ - In lenient mode, empty documents now parse successfully with a warning.
30
+ - In strict mode, empty documents now produce an error.
31
+ * **Improved:** Orphan root-level members in lenient mode are mounted directly on the resulting top-level object.
32
+ * **Improved:** Parser/runtime handling of empty inline input and final newline normalization.
33
+ * **Updated:** Tests for string concatenation, empty documents, inline object separators, hash comments, and hex notation.
34
+ * **Added:** Support for YINI mode declarations: `@yini strict` and `@yini lenient`. Mode declarations validate the active parser mode; they do not switch parser mode. `@yini strict` parsed in lenient mode produces a mode-mismatch error, while `@yini lenient` parsed in strict mode remains valid but produces a mode-mismatch warning.
35
+ * **Updated:** Shebang handling. A shebang is recognized only when `#!` is the first two non-BOM characters of the document. Valid shebang lines are ignored. Misplaced shebang-like sequences are no longer treated as shebangs; they may produce warnings in lenient mode and errors in strict mode. Only the first misplaced sequence is reported.
36
+ * **Updated:** Section marker handling. Repeated section markers now support levels 1–9; level 10 and deeper must use numeric shorthand, for example `^10 Section`. Marker separators using `_` are supported inside repeated marker sequences, invalid separator placement and mixed marker characters are rejected, the obsolete `€` marker has been removed, and `>` is supported as an alternative marker.
37
+ * **Updated:** Numeric shorthand section headers. Numeric shorthand now keeps marker and depth parsing separate, requires a positive section depth, uses a single marker followed by a number, and rejects repeated markers combined with numeric shorthand, such as `^^1 Section`.
38
+ * **Improved:** Section header parsing and validation. Backticked section names are normalized, invalid simple names such as names with dots, hyphens, or leading digits are rejected, and parsing is aligned with the updated marker separator and alternative marker rules.
39
+ * **Updated:** String literal handling. Raw strings remain the default; `R`/`r` explicit raw prefixes, `C`/`c` Classic prefixes, raw triple-quoted strings, and C-triple-quoted strings are supported. C-triple-quoted strings preserve real line breaks while interpreting escape sequences. Invalid Classic string escapes are reported as errors, and invalid string members are omitted from recovered partial results in lenient `ignore-errors` mode.
40
+ * **Improved:** Escape sequence validation. Unicode escapes must represent valid Unicode scalar values, surrogate code points are rejected, and invalid hex, Unicode, UTF-32, and octal escapes are rejected.
41
+ * **Improved:** Duplicate inline object member handling. Duplicate members are no longer silently overwritten: lenient mode keeps the first member and reports duplicates; strict mode treats duplicates as errors.
42
+ * **Updated:** Comment and disabled-line handling. `;` is full-line only, `--` disables a line only when it is the first non-whitespace content, and `#` / `//` remain inline comment markers outside string literals.
43
+
3
44
  ## 1.5.0 - 2026 Apr
4
45
  - **Updated:** Parser behavior aligned with YINI Specification `v1.0.0-RC.5`.
5
46
  - **Changed:** In strict mode, YINI documents must now end with the document terminator `/END`.
package/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2024 Gothenburg, Marko K. S. (Sweden via Finland).
189
+ Copyright 2026 Marko K. Seppänen
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,12 +1,52 @@
1
1
  # yini-parser
2
- > **Readable configuration for Node.js and TypeScript/JavaScript — without YAML footguns or JSON noise.**
3
2
 
4
- The official TypeScript / Node.js parser for **YINI** (by the YINI-lang project) — a human-friendly configuration format with clear structure, nested sections, comments, and predictable parsing.
5
-
6
- YINI is designed for applications, tools, and services that need configuration that stays readable for humans without becoming vague, fragile, or hard to maintain.
3
+ The official TypeScript / Node.js parser for **YINI** (by the YINI-lang project) — a human-readable, INI-inspired, indentation-insensitive configuration format with clear nested sections, explicit structure, comments, and predictable parsing.
7
4
 
8
5
  [![npm version](https://img.shields.io/npm/v/yini-parser.svg)](https://www.npmjs.com/package/yini-parser) [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![All Test Suites](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-all-tests.yml/badge.svg)](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-all-tests.yml) [![All Regression Tests](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-regression-tests.yml/badge.svg)](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-regression-tests.yml) [![Grammar Drift Check](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-grammar-drift-check.yml/badge.svg)](https://github.com/YINI-lang/yini-parser-typescript/actions/workflows/run-grammar-drift-check.yml) [![npm downloads](https://img.shields.io/npm/dm/yini-parser)](https://www.npmjs.com/package/yini-parser)
9
6
 
7
+ YINI is intended to emphasize clarity, readability, explicit structure, and predictable parsing, while remaining simple, but not simplistic, and without relying on implicit or indentation-sensitive structure.
8
+
9
+ ## Copy-paste test
10
+
11
+ Test the package in under one minute.
12
+
13
+ Install:
14
+
15
+ ```sh
16
+ npm install yini-parser
17
+ ```
18
+
19
+ Parse a YINI string:
20
+
21
+ ```ts
22
+ import YINI from 'yini-parser'
23
+
24
+ const data = YINI.parse(`
25
+ ^ Application
26
+ name = "demo"
27
+
28
+ ^^ Server
29
+ port = 8080
30
+ `)
31
+
32
+ console.log(data)
33
+ ```
34
+
35
+ Expected output:
36
+
37
+ ```ts
38
+ {
39
+ Application: {
40
+ name: 'demo',
41
+ Server: {
42
+ port: 8080,
43
+ },
44
+ },
45
+ }
46
+ ```
47
+
48
+ ---
49
+
10
50
  ## Quick Start
11
51
 
12
52
  ```sh
@@ -18,27 +58,43 @@ import YINI from 'yini-parser'
18
58
 
19
59
  const config = YINI.parse(`
20
60
  ^ App
21
- name = 'My App'
22
- darkMode = true
61
+ name = 'My App'
62
+ list = ['web', 'api']
63
+ darkMode = true // Yes/On works too
23
64
 
24
65
  ^^ Features
25
66
  caching = on
67
+ object = { logging: true, mode: 'debug' }
26
68
  `)
27
69
 
28
70
  console.log(config.App.name) // My App
29
71
  console.log(config.App.Features.caching) // true
30
72
  ```
31
73
 
32
- ➡️ Learn more in the [YINI specification and documentation](https://yini-lang.org/refs/specification).
74
+ ### Modes and `/END`
75
+
76
+ `yini-parser` runs in lenient mode by default. In lenient mode, the document terminator `/END` is optional.
77
+
78
+ Strict mode requires `/END` unless you explicitly override `requireDocTerminator`:
79
+
80
+ ```ts
81
+ const config = YINI.parseFile('./config.yini', {
82
+ strictMode: true,
83
+ })
84
+ ```
85
+
86
+ If strict mode reports a missing `/END`, either add `/END` as the final significant line or parse with the default lenient mode.
87
+
88
+ See the [YINI specification and documentation](https://yini-lang.org/refs/specification?utm_source=github&utm_medium=referral&utm_campaign=yini_parser_ts&utm_content=readme).
33
89
 
34
90
  ---
35
91
 
36
- ## 🙋‍♀️ Why try YINI?
92
+ ## Format characteristics
37
93
 
38
- - **Readable by humans** — Less noisy than JSON, less fragile than indentation-driven formats.
39
- - **Structured for real-world configuration** — Sections, nested sections, lists, objects, booleans, and null.
40
- - **Predictable parsing** — Explicit syntax with clear rules.
41
- - **Easy to use from TypeScript/Node.js** — Parse from strings or files in a few lines.
94
+ - **Human-readable** — Uses explicit syntax and indentation-independent structure.
95
+ - **Structured configuration model** — Supports sections, nested sections, lists, objects, booleans, and null.
96
+ - **Predictable parsing** — Explicit syntax with clear rules and deterministic parsing behavior.
97
+ - **TypeScript and Node.js integration** — Supports parsing from strings and files.
42
98
 
43
99
  ---
44
100
 
@@ -47,16 +103,17 @@ console.log(config.App.Features.caching) // true
47
103
  ![YINI Config Example](./samples/basic.yini.png)
48
104
  Source: [basic.yini](./samples/basic.yini)
49
105
 
50
- - ▶️ [Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main) with complete usage examples.
106
+ - [Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main) with usage examples.
51
107
 
52
108
  ---
53
109
 
54
- ## Why YINI works well for configuration
110
+ ## Configuration-oriented design
55
111
  - **Indentation-independent structure:** Spaces and tabs never change meaning, so files can be reformatted without changing structure.
56
- - **Explicit nesting:** Hierarchy is defined with section markers like `^`, `^^`, and `^^^`, making large configurations easier to scan and refactor.
112
+ - **Explicit nesting:** Hierarchy is defined with section markers such as `^`, `^^`, and `^^^`, rather than by indentation.
57
113
  - **Multiple data types:** Supports booleans (`true` / `false`, `yes` / `no`, etc.), numbers, lists, and inline objects, with explicit string syntax.
58
- - **Comment support:** YINI supports multiple comment styles (`#`, `//`, `/* ... */`, and `;`), making it easy to document configuration directly in the file.
59
- - **Predictable parsing:** Clear rules with optional strict and lenient modes for different use cases.
114
+ - **Comment support:** YINI supports `//`, `#`, block comments (`/* ... */`), and full-line `;` comments for documenting configuration directly in the file.
115
+ - **Clear hash comments:** Outside string literals, `#` always starts a comment; hexadecimal values use `0x...` or `hex:...`.
116
+ - **Predictable parsing:** Clear rules with optional strict and lenient modes (enforced by the parser) for different use cases.
60
117
 
61
118
  ---
62
119
 
@@ -80,7 +137,7 @@ pnpm add yini-parser
80
137
  ```
81
138
 
82
139
  ### Node.js (CommonJS)
83
- **Note:** Only a default export (YINI) is provided. Named imports are not supported.
140
+ **Note:** The default export is the main API. Named exports such as `parse`, `parseFile`, and `parseForTooling` are also available from the package entry.
84
141
  ```js
85
142
  const YINI = require('yini-parser').default;
86
143
  // If your setup handles default interop differently, try:
@@ -130,7 +187,7 @@ const configFromFile = YINI.parseFile('./config.yini');
130
187
 
131
188
  ## 📂 More Examples
132
189
 
133
- - ▶️ Explore more [YINI examples](https://yini-lang.org/learn-yini/examples/?utm_source=yini-parser-ts&utm_medium=github&utm_campaign=repo-link&utm_content=readme).
190
+ - Additional [YINI examples](https://yini-lang.org/learn-yini/examples/?utm_source=yini-parser-ts&utm_medium=github&utm_campaign=repo-link&utm_content=readme).
134
191
 
135
192
  ### Example 2
136
193
  > A real-world YINI configuration example, showing sections, nesting, comments, and multiple data types:
@@ -139,36 +196,141 @@ Source: [config.yini](./samples/config.yini)
139
196
 
140
197
  ---
141
198
 
142
- ## 🧪 Testing and Stability
199
+ ## Why YINI?
200
+
201
+ YINI is intended for configuration files where human readability, explicit structure, and predictable parsing are more important than minimal syntax or maximum flexibility.
143
202
 
144
- This parser is validated through regression and smoke tests to help ensure stable, predictable parsing across default, strict, and metadata-enabled modes.
203
+ Compared with common configuration formats:
204
+ - **INI:** YINI supports clearer nested sections and typed values.
205
+ - **JSON:** YINI supports comments and is easier to edit by hand.
206
+ - **YAML:** YINI does not use indentation to define structure.
207
+ - **TOML:** YINI uses explicit section markers for hierarchy instead of dotted table names.
208
+
209
+ The same small configuration can be written in several formats:
210
+
211
+ ### YINI
212
+ ```ini
213
+ ^ Application
214
+ name = 'demo'
215
+ environment = 'dev'
216
+
217
+ ^^ Server
218
+ host = 'localhost'
219
+ ports = [8080, 8081]
220
+
221
+ ^^^ TLS
222
+ enabled = true
223
+ mode = 'optional'
224
+ ```
225
+
226
+ - `Application` contains the top-level application settings.
227
+ - `Server` is nested under `Application`.
228
+ - `TLS` is nested under `Server`.
229
+ - The section markers `^` make the nesting explicit. Indentation is optional and not required for structure.
230
+ - Strings can use either `'` or `"`.
231
+
232
+ ### JSON
233
+ ```json
234
+ {
235
+ "Application": {
236
+ "name": "demo",
237
+ "environment": "dev",
238
+ "Server": {
239
+ "host": "localhost",
240
+ "ports": [8080, 8081],
241
+ "TLS": {
242
+ "enabled": true,
243
+ "mode": "optional"
244
+ }
245
+ }
246
+ }
247
+ }
248
+ ```
249
+
250
+ ### YAML
251
+ ```yaml
252
+ Application:
253
+ name: demo
254
+ environment: dev
255
+ Server:
256
+ host: localhost
257
+ ports:
258
+ - 8080
259
+ - 8081
260
+ TLS:
261
+ enabled: true
262
+ mode: optional
263
+ ```
264
+
265
+ ### TOML
266
+ ```toml
267
+ [Application]
268
+ name = "demo"
269
+ environment = "dev"
270
+
271
+ [Application.Server]
272
+ host = "localhost"
273
+ ports = [8080, 8081]
274
+
275
+ [Application.Server.TLS]
276
+ enabled = true
277
+ mode = "optional"
278
+ ```
279
+
280
+ Note: YINI may not be the right choice when you need mature ecosystem support, existing schema tooling, or maximum compatibility with infrastructure that already expects JSON, YAML, or TOML.
145
281
 
146
282
  ---
147
283
 
148
- ## Links
149
- - ➡️ [YINI Homepage](https://yini-lang.org)
150
- *Tutorials, guides, and examples.*
284
+ ## Parser implementation
285
+
286
+ `yini-parser` uses TypeScript/JavaScript parser code generated by ANTLR.
287
+
288
+ The generated parser files are included in the published npm package. Users do **not** need Java or the ANTLR generator tool to install or use `yini-parser`.
151
289
 
152
- - ➡️ [Read the YINI Specification](https://yini-lang.org/refs/specification)
153
- *Full syntax and format reference.*
290
+ The package depends on the ANTLR JavaScript/TypeScript runtime used by the generated lexer and parser while parsing.
154
291
 
155
- - ➡️ [YINI CLI on GitHub](https://github.com/YINI-lang/yini-cli)
156
- *CLI tooling for working with YINI files.*
292
+ The ANTLR generator JAR is only needed by maintainers when regenerating parser sources from the grammar, and it is not included in the published npm package.
293
+
294
+ ---
157
295
 
158
- - ➡️ [Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main)
159
- *Complete basic usage examples.*
296
+ ## Feedback and bug reports
160
297
 
161
- - ➡️ [YINI-lang Project](https://github.com/YINI-lang)
162
- *Repositories and related ecosystem projects.*
298
+ If you find a problem, please open an issue on GitHub:
299
+
300
+ - [Report a bug or issue](https://github.com/YINI-lang/yini-parser-typescript/issues)
301
+
302
+ When reporting parser behavior, it is helpful to include:
303
+ - The YINI input that caused the issue.
304
+ - The expected result.
305
+ - The actual result or error message.
306
+ - The installed `yini-parser` version.
307
+ - The Node.js version used.
308
+
309
+ ---
310
+
311
+ ## 🧪 Testing and Stability
312
+
313
+ This parser is covered by smoke, integration, and regression tests across lenient, strict, and metadata-enabled modes.
314
+
315
+ It has also been run against `yini-test-suite` v0.3.0, the external [YINI conformance test suite](https://github.com/YINI-lang/yini-test-suite), with all TypeScript parser cases passing.
316
+
317
+ ---
318
+
319
+ ## Links
320
+ - [YINI Homepage](https://yini-lang.org)
321
+ - [YINI Specification](https://yini-lang.org/refs/specification)
322
+ - [YINI CLI on GitHub](https://github.com/YINI-lang/yini-cli)
323
+ - [YINI Demo Apps](https://github.com/YINI-lang/yini-demo-apps/tree/main)
324
+ - [YINI-lang Project](https://github.com/YINI-lang)
163
325
 
164
326
  ---
165
327
 
166
328
  ## 🤝 Contributing
167
- Feedback, bug reports, feature requests, and code contributions are welcome.
329
+ Bug reports, feature requests, discussions, and code contributions are welcome.
168
330
  - [Open an Issue](https://github.com/YINI-lang/yini-parser-typescript/issues)
169
331
  - [Start a Discussion](https://github.com/YINI-lang/yini-parser-typescript/discussions)
170
332
 
171
- If this library is useful to you, a GitHub star helps more people discover the project and supports future development.
333
+ GitHub Issues and Discussions are available for feedback and project discussion.
172
334
 
173
335
  ### Documentation
174
336
  - [Project Setup](https://github.com/YINI-lang/yini-parser-typescript/blob/main/docs/Project-Setup.md) — How to run, test, and build the project, etc.
@@ -179,13 +341,13 @@ If this library is useful to you, a GitHub star helps more people discover the p
179
341
  ## License
180
342
  This project is licensed under the Apache License 2.0 — see the [LICENSE](./LICENSE) file for details.
181
343
 
182
- In this project on GitHub, the `libs` directory contains third-party software and each is licensed under its own license, described in its own license file under the respective directory under `libs`.
344
+ In this project on GitHub, the `libs` directory contains third-party software and each is licensed under its own license which is described in its own license file under the respective directory under `libs`.
183
345
 
184
346
  ---
185
347
 
186
348
  **^YINI ≡**
187
- > Readable like INI. Structured like JSON. No indentation surprises.
349
+ > YINI is a human-readable configuration format designed for clarity, readability, explicit structure, and predictable parsing.
188
350
  >
189
- > Predictable configuration with clear rules.
351
+ > See the specification for syntax and format details.
190
352
 
191
353
  [yini-lang.org](https://yini-lang.org/?utm_source=github&utm_medium=referral&utm_campaign=yini_parser_ts&utm_content=readme_footer) · [YINI-lang on GitHub](https://github.com/YINI-lang)
package/dist/YINI.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ParsedObject, ParseOptions, PreferredFailLevel, YiniParseResult } from './types';
1
+ import { ParsedObject, ParseForToolingOptions, ParseOptions, PreferredFailLevel, YiniParseResult, YiniToolingParseResult } from './types';
2
2
  /**
3
3
  * This class is the main public API. It exposes `parse(..)` and `parseFile(..)`
4
4
  * as the primary entry points, while the implementation details remain internal.
@@ -69,11 +69,13 @@ export default class YINI {
69
69
  * Allowed values: `'warn-and-keep-first'` | `'warn-and-overwrite'` | `'keep-first'` (silent, first wins) | `'overwrite'` (silent, last wins) | `'error'`.
70
70
  * @param options.preserveUndefinedInMeta - Keep properties with value `undefined` inside
71
71
  * the returned metadata. Requires: `includeMetadata = true`. Ignored otherwise.
72
- * @param options.quiet - Show only errors, will suppress warnings and messages sent to the console/log.
73
- * Does not affect warnings included in returned metadata.
72
+ * @param options.logDiagnostics - Opt in to writing diagnostics to stderr.
73
+ * Library calls do not write diagnostics by default.
74
+ * @param options.quiet - When `logDiagnostics = true`, show only errors.
75
+ * Does not affect diagnostics included in returned metadata.
74
76
  * @param options.requireDocTerminator - Controls whether a document terminator is required.
75
77
  * Allowed values: `'optional'` | `'warn-if-missing'` | `'required'`.
76
- * @param options.silent - Suppress all console output, including errors and warnings.
78
+ * @param options.silent - Suppress diagnostic output, even when `logDiagnostics = true`.
77
79
  * @param options.strictMode - Sets the baseline ruleset (true = strict, false = lenient).
78
80
  * This is only a starting point: rule-specific options (e.g., `treatEmptyValueAsNull`,
79
81
  * `onDuplicateKey`, etc.) can override parts of that ruleset. If any overrides are given,
@@ -101,6 +103,17 @@ export default class YINI {
101
103
  * `
102
104
  */
103
105
  static parse(yiniContent: string, options?: ParseOptions): ParsedObject | YiniParseResult;
106
+ /**
107
+ * Parse inline YINI content for tools, editors, linters, and test runners.
108
+ *
109
+ * This API always returns a stable result object and structured diagnostics.
110
+ * It does not write diagnostics to stdout/stderr and does not throw for
111
+ * normal parse errors.
112
+ */
113
+ static parseForTooling(yiniContent: string, options?: ParseForToolingOptions): YiniToolingParseResult;
114
+ private static toToolingDiagnostics;
115
+ private static toToolingDiagnostic;
116
+ private static toDiagnosticCode;
104
117
  /**
105
118
  * Parse a YINI file into a JavaScript object.
106
119
  *
@@ -155,11 +168,13 @@ export default class YINI {
155
168
  * Allowed values: `'warn-and-keep-first'` | `'warn-and-overwrite'` | `'keep-first'` (silent, first wins) | `'overwrite'` (silent, last wins) | `'error'`.
156
169
  * @param options.preserveUndefinedInMeta - Keep properties with value `undefined` inside
157
170
  * the returned metadata. Requires: `includeMetadata = true`. Ignored otherwise.
158
- * @param options.quiet - Show only errors, will suppress warnings and messages sent to the console/log.
159
- * Does not affect warnings included in returned metadata.
171
+ * @param options.logDiagnostics - Opt in to writing diagnostics to stderr.
172
+ * Library calls do not write diagnostics by default.
173
+ * @param options.quiet - When `logDiagnostics = true`, show only errors.
174
+ * Does not affect diagnostics included in returned metadata.
160
175
  * @param options.requireDocTerminator - Controls whether a document terminator is required.
161
176
  * Allowed values: `'optional'` | `'warn-if-missing'` | `'required'`.
162
- * @param options.silent - Suppress all console output, including errors and warnings.
177
+ * @param options.silent - Suppress diagnostic output, even when `logDiagnostics = true`.
163
178
  * @param options.strictMode - Sets the baseline ruleset (true = strict, false = lenient).
164
179
  * This is only a starting point: rule-specific options (e.g., `treatEmptyValueAsNull`,
165
180
  * `onDuplicateKey`, etc.) can override parts of that ruleset. If any overrides are given,
package/dist/YINI.js CHANGED
@@ -53,6 +53,110 @@ class YINI {
53
53
  }
54
54
  return result;
55
55
  }
56
+ /**
57
+ * Parse inline YINI content for tools, editors, linters, and test runners.
58
+ *
59
+ * This API always returns a stable result object and structured diagnostics.
60
+ * It does not write diagnostics to stdout/stderr and does not throw for
61
+ * normal parse errors.
62
+ */
63
+ static parseForTooling(yiniContent, options = {}) {
64
+ try {
65
+ const parsed = this.parse(yiniContent, {
66
+ ...options,
67
+ failLevel: 'ignore-errors',
68
+ includeMetadata: true,
69
+ includeDiagnostics: true,
70
+ logDiagnostics: false,
71
+ silent: true,
72
+ throwOnError: false,
73
+ });
74
+ return {
75
+ ok: parsed.meta.totalErrors === 0,
76
+ result: parsed.result,
77
+ diagnostics: this.toToolingDiagnostics(parsed),
78
+ };
79
+ }
80
+ catch (error) {
81
+ return {
82
+ ok: false,
83
+ result: {},
84
+ diagnostics: [
85
+ {
86
+ severity: 'error',
87
+ code: 'parser-error',
88
+ message: error instanceof Error
89
+ ? error.message
90
+ : String(error),
91
+ },
92
+ ],
93
+ };
94
+ }
95
+ }
96
+ static toToolingDiagnostics(parsed) {
97
+ const diagnostics = parsed.meta.diagnostics;
98
+ if (!diagnostics) {
99
+ return [];
100
+ }
101
+ return [
102
+ ...diagnostics.errors.payload.map((issue) => this.toToolingDiagnostic(issue, 'error')),
103
+ ...diagnostics.warnings.payload.map((issue) => this.toToolingDiagnostic(issue, 'warning')),
104
+ ...diagnostics.notices.payload.map((issue) => this.toToolingDiagnostic(issue, 'notice')),
105
+ ...diagnostics.infos.payload.map((issue) => this.toToolingDiagnostic(issue, 'info')),
106
+ ];
107
+ }
108
+ static toToolingDiagnostic(issue, severity) {
109
+ const diagnostic = {
110
+ severity,
111
+ code: this.toDiagnosticCode(issue),
112
+ message: issue.message,
113
+ };
114
+ if (issue.line !== undefined) {
115
+ diagnostic.line = issue.line;
116
+ }
117
+ if (issue.column !== undefined) {
118
+ diagnostic.column = issue.column;
119
+ }
120
+ return diagnostic;
121
+ }
122
+ static toDiagnosticCode(issue) {
123
+ const text = [issue.message, issue.advice, issue.hint]
124
+ .filter((part) => Boolean(part))
125
+ .join(' ')
126
+ .toLowerCase();
127
+ if (text.includes('empty yini document')) {
128
+ return 'empty-document';
129
+ }
130
+ if (text.includes('duplicate key')) {
131
+ return 'duplicate-key';
132
+ }
133
+ if (text.includes('duplicate section')) {
134
+ return 'duplicate-section';
135
+ }
136
+ if (text.includes('yini mode declaration') &&
137
+ text.includes('active parser mode')) {
138
+ return 'YINI_MODE_MISMATCH';
139
+ }
140
+ if (text.includes('directive') && text.includes('wrong place')) {
141
+ return 'misplaced-directive';
142
+ }
143
+ if (text.includes('invalid escape sequence')) {
144
+ return 'invalid-escape-sequence';
145
+ }
146
+ if (text.includes('unterminated') && text.includes('string')) {
147
+ return 'unterminated-string';
148
+ }
149
+ if (text.includes('/end') && text.includes('missing')) {
150
+ return 'missing-document-terminator';
151
+ }
152
+ if (text.includes('shebang')) {
153
+ return 'misplaced-shebang';
154
+ }
155
+ if (text.includes('trailing comma')) {
156
+ return 'trailing-comma';
157
+ }
158
+ return issue.typeKey.replace(/_/g, '-');
159
+ }
56
160
  // --- Single implementation --------------------------------------------
57
161
  // Implementation method (not declared with arrow function) for both method overload signatures.
58
162
  // NOTE: Must be method declaration with NO =, arrow functions not (currently) supported for this type of method overloading.