@safeaccess/inline 0.1.1 → 0.1.2

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 (179) hide show
  1. package/.gitattributes +1 -1
  2. package/CHANGELOG.md +10 -5
  3. package/LICENSE +1 -1
  4. package/README.md +56 -14
  5. package/dist/accessors/abstract-accessor.d.ts +22 -10
  6. package/dist/accessors/abstract-accessor.js +21 -8
  7. package/dist/accessors/abstract-integration-accessor.d.ts +22 -0
  8. package/dist/accessors/abstract-integration-accessor.js +23 -0
  9. package/dist/accessors/formats/any-accessor.d.ts +10 -8
  10. package/dist/accessors/formats/any-accessor.js +9 -8
  11. package/dist/accessors/formats/array-accessor.d.ts +2 -0
  12. package/dist/accessors/formats/array-accessor.js +2 -0
  13. package/dist/accessors/formats/env-accessor.d.ts +2 -0
  14. package/dist/accessors/formats/env-accessor.js +2 -0
  15. package/dist/accessors/formats/ini-accessor.d.ts +2 -0
  16. package/dist/accessors/formats/ini-accessor.js +2 -0
  17. package/dist/accessors/formats/json-accessor.d.ts +2 -0
  18. package/dist/accessors/formats/json-accessor.js +2 -0
  19. package/dist/accessors/formats/ndjson-accessor.d.ts +2 -0
  20. package/dist/accessors/formats/ndjson-accessor.js +2 -0
  21. package/dist/accessors/formats/object-accessor.d.ts +2 -0
  22. package/dist/accessors/formats/object-accessor.js +2 -0
  23. package/dist/accessors/formats/xml-accessor.d.ts +2 -0
  24. package/dist/accessors/formats/xml-accessor.js +2 -0
  25. package/dist/accessors/formats/yaml-accessor.d.ts +3 -1
  26. package/dist/accessors/formats/yaml-accessor.js +4 -2
  27. package/dist/cache/simple-path-cache.d.ts +51 -0
  28. package/dist/cache/simple-path-cache.js +72 -0
  29. package/dist/contracts/accessors-interface.d.ts +2 -0
  30. package/dist/contracts/factory-accessors-interface.d.ts +2 -0
  31. package/dist/contracts/filter-evaluator-interface.d.ts +28 -0
  32. package/dist/contracts/filter-evaluator-interface.js +1 -0
  33. package/dist/contracts/parse-integration-interface.d.ts +2 -0
  34. package/dist/contracts/parser-interface.d.ts +92 -0
  35. package/dist/contracts/parser-interface.js +1 -0
  36. package/dist/contracts/path-cache-interface.d.ts +7 -6
  37. package/dist/contracts/readable-accessors-interface.d.ts +11 -6
  38. package/dist/contracts/security-guard-interface.d.ts +2 -0
  39. package/dist/contracts/security-parser-interface.d.ts +2 -0
  40. package/dist/contracts/validatable-parser-interface.d.ts +59 -0
  41. package/dist/contracts/validatable-parser-interface.js +1 -0
  42. package/dist/contracts/writable-accessors-interface.d.ts +5 -0
  43. package/dist/core/accessor-factory.d.ts +124 -0
  44. package/dist/core/accessor-factory.js +157 -0
  45. package/dist/core/dot-notation-parser.d.ts +34 -5
  46. package/dist/core/dot-notation-parser.js +51 -10
  47. package/dist/core/inline-builder-accessor.d.ts +82 -0
  48. package/dist/core/inline-builder-accessor.js +107 -0
  49. package/dist/exceptions/accessor-exception.d.ts +9 -0
  50. package/dist/exceptions/accessor-exception.js +9 -0
  51. package/dist/exceptions/invalid-format-exception.d.ts +5 -0
  52. package/dist/exceptions/invalid-format-exception.js +5 -0
  53. package/dist/exceptions/parser-exception.d.ts +4 -0
  54. package/dist/exceptions/parser-exception.js +4 -0
  55. package/dist/exceptions/path-not-found-exception.d.ts +4 -0
  56. package/dist/exceptions/path-not-found-exception.js +4 -0
  57. package/dist/exceptions/readonly-violation-exception.d.ts +4 -0
  58. package/dist/exceptions/readonly-violation-exception.js +4 -0
  59. package/dist/exceptions/security-exception.d.ts +6 -0
  60. package/dist/exceptions/security-exception.js +6 -0
  61. package/dist/exceptions/unsupported-type-exception.d.ts +4 -0
  62. package/dist/exceptions/unsupported-type-exception.js +4 -0
  63. package/dist/exceptions/yaml-parse-exception.d.ts +4 -0
  64. package/dist/exceptions/yaml-parse-exception.js +4 -0
  65. package/dist/index.js +2 -1
  66. package/dist/inline.d.ts +22 -56
  67. package/dist/inline.js +39 -111
  68. package/dist/parser/xml-parser.js +23 -10
  69. package/dist/parser/yaml-parser.d.ts +54 -7
  70. package/dist/parser/yaml-parser.js +268 -51
  71. package/dist/path-query/segment-filter-parser.d.ts +142 -0
  72. package/dist/path-query/segment-filter-parser.js +384 -0
  73. package/dist/path-query/segment-parser.d.ts +98 -0
  74. package/dist/path-query/segment-parser.js +283 -0
  75. package/dist/path-query/segment-path-resolver.d.ts +149 -0
  76. package/dist/path-query/segment-path-resolver.js +351 -0
  77. package/dist/path-query/segment-type.d.ts +85 -0
  78. package/dist/path-query/segment-type.js +35 -0
  79. package/dist/security/forbidden-keys.d.ts +2 -2
  80. package/dist/security/forbidden-keys.js +5 -5
  81. package/dist/security/security-guard.d.ts +3 -1
  82. package/dist/security/security-guard.js +5 -2
  83. package/dist/security/security-parser.d.ts +10 -1
  84. package/dist/security/security-parser.js +10 -1
  85. package/dist/type-format.d.ts +2 -0
  86. package/dist/type-format.js +2 -0
  87. package/package.json +11 -3
  88. package/src/accessors/abstract-accessor.ts +23 -19
  89. package/src/accessors/abstract-integration-accessor.ts +27 -0
  90. package/src/accessors/formats/any-accessor.ts +11 -11
  91. package/src/accessors/formats/array-accessor.ts +2 -0
  92. package/src/accessors/formats/env-accessor.ts +2 -0
  93. package/src/accessors/formats/ini-accessor.ts +2 -0
  94. package/src/accessors/formats/json-accessor.ts +2 -0
  95. package/src/accessors/formats/ndjson-accessor.ts +2 -0
  96. package/src/accessors/formats/object-accessor.ts +2 -0
  97. package/src/accessors/formats/xml-accessor.ts +2 -0
  98. package/src/accessors/formats/yaml-accessor.ts +4 -2
  99. package/src/cache/simple-path-cache.ts +77 -0
  100. package/src/contracts/accessors-interface.ts +2 -0
  101. package/src/contracts/factory-accessors-interface.ts +2 -0
  102. package/src/contracts/filter-evaluator-interface.ts +30 -0
  103. package/src/contracts/parse-integration-interface.ts +2 -0
  104. package/src/contracts/parser-interface.ts +114 -0
  105. package/src/contracts/path-cache-interface.ts +8 -6
  106. package/src/contracts/readable-accessors-interface.ts +11 -6
  107. package/src/contracts/security-guard-interface.ts +2 -0
  108. package/src/contracts/security-parser-interface.ts +2 -0
  109. package/src/contracts/validatable-parser-interface.ts +64 -0
  110. package/src/contracts/writable-accessors-interface.ts +5 -0
  111. package/src/core/accessor-factory.ts +173 -0
  112. package/src/core/dot-notation-parser.ts +74 -11
  113. package/src/core/inline-builder-accessor.ts +163 -0
  114. package/src/exceptions/accessor-exception.ts +9 -0
  115. package/src/exceptions/invalid-format-exception.ts +5 -0
  116. package/src/exceptions/parser-exception.ts +4 -0
  117. package/src/exceptions/path-not-found-exception.ts +4 -0
  118. package/src/exceptions/readonly-violation-exception.ts +4 -0
  119. package/src/exceptions/security-exception.ts +6 -0
  120. package/src/exceptions/unsupported-type-exception.ts +4 -0
  121. package/src/exceptions/yaml-parse-exception.ts +4 -0
  122. package/src/index.ts +3 -1
  123. package/src/inline.ts +42 -120
  124. package/src/parser/xml-parser.ts +31 -10
  125. package/src/parser/yaml-parser.ts +310 -45
  126. package/src/path-query/segment-filter-parser.ts +444 -0
  127. package/src/path-query/segment-parser.ts +321 -0
  128. package/src/path-query/segment-path-resolver.ts +521 -0
  129. package/src/path-query/segment-type.ts +82 -0
  130. package/src/security/forbidden-keys.ts +5 -5
  131. package/src/security/security-guard.ts +7 -2
  132. package/src/security/security-parser.ts +18 -3
  133. package/src/type-format.ts +2 -0
  134. package/stryker.config.json +8 -10
  135. package/tests/accessors/abstract-accessor.test.ts +217 -0
  136. package/tests/accessors/abstract-integration-accessor.test.ts +37 -0
  137. package/tests/accessors/formats/any-accessor.test.ts +57 -0
  138. package/tests/accessors/formats/array-accessor.test.ts +42 -0
  139. package/tests/accessors/formats/env-accessor.test.ts +103 -0
  140. package/tests/accessors/formats/ini-accessor.test.ts +186 -0
  141. package/tests/accessors/{json-accessor.test.ts → formats/json-accessor.test.ts} +6 -6
  142. package/tests/accessors/formats/ndjson-accessor.test.ts +49 -0
  143. package/tests/accessors/formats/object-accessor.test.ts +172 -0
  144. package/tests/accessors/formats/xml-accessor.test.ts +162 -0
  145. package/tests/accessors/formats/yaml-accessor.test.ts +36 -0
  146. package/tests/cache/simple-path-cache.test.ts +168 -0
  147. package/tests/core/accessor-factory.test.ts +157 -0
  148. package/tests/core/dot-notation-parser-edge-cases.test.ts +415 -0
  149. package/tests/core/dot-notation-parser.test.ts +0 -288
  150. package/tests/core/inline-builder-accessor.test.ts +114 -0
  151. package/tests/exceptions/accessor-exception.test.ts +28 -0
  152. package/tests/exceptions/invalid-format-exception.test.ts +31 -0
  153. package/tests/exceptions/path-not-found-exception.test.ts +33 -0
  154. package/tests/exceptions/readonly-violation-exception.test.ts +35 -0
  155. package/tests/exceptions/security-exception.test.ts +33 -0
  156. package/tests/exceptions/unsupported-type-exception.test.ts +33 -0
  157. package/tests/exceptions/yaml-parse-exception.test.ts +38 -0
  158. package/tests/mocks/fake-path-cache.ts +4 -3
  159. package/tests/parity-from.test.ts +118 -0
  160. package/tests/parity.test.ts +227 -10
  161. package/tests/parser/xml-parser-mutations.test.ts +579 -0
  162. package/tests/parser/xml-parser-scanner.test.ts +332 -0
  163. package/tests/parser/xml-parser.test.ts +10 -334
  164. package/tests/parser/yaml-parser-mutations.test.ts +750 -0
  165. package/tests/parser/yaml-parser.test.ts +844 -18
  166. package/tests/path-query/segment-filter-parser-mutations.test.ts +735 -0
  167. package/tests/path-query/segment-filter-parser.test.ts +1091 -0
  168. package/tests/path-query/segment-parser-mutations.test.ts +539 -0
  169. package/tests/path-query/segment-parser.test.ts +606 -0
  170. package/tests/path-query/segment-path-resolver-mutations.test.ts +626 -0
  171. package/tests/path-query/segment-path-resolver.test.ts +1009 -0
  172. package/tests/security/security-guard-advanced.test.ts +413 -0
  173. package/tests/security/security-guard-forbidden-keys.test.ts +87 -0
  174. package/tests/security/security-guard.test.ts +3 -484
  175. package/tests/security/security-parser.test.ts +18 -14
  176. package/vitest.config.ts +3 -3
  177. package/benchmarks/get.bench.ts +0 -26
  178. package/benchmarks/parse.bench.ts +0 -41
  179. package/tests/accessors/accessors.test.ts +0 -1017
package/.gitattributes CHANGED
@@ -1,4 +1,4 @@
1
- # Distribution archive ignore files excluded from npm/archive downloads
1
+ # Distribution archive ignore - files excluded from npm/archive downloads
2
2
  # Note: npm uses the `files` field in package.json for publish filtering.
3
3
  # These export-ignore rules apply to git archive and the split repo.
4
4
 
package/CHANGELOG.md CHANGED
@@ -2,17 +2,22 @@
2
2
 
3
3
  All notable changes to the `@safeaccess/inline` JavaScript/TypeScript package are documented in this file.
4
4
 
5
- ## [0.1.1](https://github.com/felipesauer/safeaccess-inline/compare/js-v0.1.0...js-v0.1.1) (2026-04-07)
5
+ ## [0.1.2](https://github.com/felipesauer/safeaccess-inline/compare/js-v0.1.1...js-v0.1.2) (2026-04-08)
6
6
 
7
7
 
8
- ### Features
8
+ ### Bug Fixes
9
+
10
+ * **js:** fix logo image URL in README ([16f4fc5](https://github.com/felipesauer/safeaccess-inline/commit/16f4fc5d69fa7ce86e3017bbbfc9f393925a5c37))
9
11
 
10
- * **js:** bootstrap release tracking for rebranded package ([5fc07d7](https://github.com/felipesauer/safeaccess-inline/commit/5fc07d7126870d72145bbfc80609370c9d1509c7))
12
+ ## [0.1.1](https://github.com/felipesauer/safeaccess-inline/compare/js-v0.1.0...js-v0.1.1) (2026-04-07)
13
+
14
+ ### Features
11
15
 
16
+ - **js:** bootstrap release tracking for rebranded package ([5fc07d7](https://github.com/felipesauer/safeaccess-inline/commit/5fc07d7126870d72145bbfc80609370c9d1509c7))
12
17
 
13
18
  ### Bug Fixes
14
19
 
15
- * **js:** add repository field for npm provenance validation ([b34cdef](https://github.com/felipesauer/safeaccess-inline/commit/b34cdeff01e7e7566921f04b11f33fbd391aa8d2))
20
+ - **js:** add repository field for npm provenance validation ([b34cdef](https://github.com/felipesauer/safeaccess-inline/commit/b34cdeff01e7e7566921f04b11f33fbd391aa8d2))
16
21
 
17
22
  ## 0.1.0 (2026-04-07)
18
23
 
@@ -20,7 +25,7 @@ All notable changes to the `@safeaccess/inline` JavaScript/TypeScript package ar
20
25
 
21
26
  - **ci:** achieve 100% branch coverage on Vitest 4.x and fix docs-ci workflow ([#14](https://github.com/felipesauer/safeaccess-inline/issues/14)) ([11daf5a](https://github.com/felipesauer/safeaccess-inline/commit/11daf5aaa1ff1b901c8297921533485f1584a330))
22
27
 
23
- ## [0.1.0] 2026-04-06
28
+ ## [0.1.0] - 2026-04-06
24
29
 
25
30
  ### Features
26
31
 
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Felipe Sauer
3
+ Copyright (c) 2026 Safe Access Inline
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <p align="center">
2
- <img src="../../public/logo.svg" width="80" alt="safeaccess-inline logo">
2
+ <img src="https://github.com/user-attachments/assets/28202f8b-8ef1-4b94-b6d1-ec16f16c9cf9" width="80" alt="safeaccess-inline logo">
3
3
  </p>
4
4
 
5
- <h1 align="center">Safe Access Inline TypeScript</h1>
5
+ <h1 align="center">Safe Access Inline - TypeScript</h1>
6
6
 
7
7
  <p align="center">
8
8
  <a href="https://www.npmjs.com/package/@safeaccess/inline"><img src="https://img.shields.io/npm/v/@safeaccess/inline?label=npm" alt="npm"></a>
@@ -11,7 +11,7 @@
11
11
 
12
12
  ---
13
13
 
14
- Safe nested data access with dot notation for JavaScript and TypeScript. Navigate deeply nested objects, JSON, YAML, XML, INI, ENV, and NDJSON structures with built-in security validation, immutable writes, and a fluent builder API.
14
+ Safe nested data access with dot notation for JavaScript and TypeScript. Navigate deeply nested objects, JSON, YAML, XML, INI, ENV, and NDJSON structures - with built-in security validation, immutable writes, and a fluent builder API.
15
15
 
16
16
  ## Installation
17
17
 
@@ -33,7 +33,7 @@ accessor.get('user.email', 'N/A'); // 'N/A' (default when missing)
33
33
  accessor.has('user.age'); // true
34
34
  accessor.getOrFail('user.name'); // 'Alice' (throws if missing)
35
35
 
36
- // Immutable writes original is never modified
36
+ // Immutable writes - original is never modified
37
37
  const updated = accessor.set('user.email', 'alice@example.com');
38
38
  updated.get('user.email'); // 'alice@example.com'
39
39
  accessor.has('user.email'); // false (original unchanged)
@@ -41,12 +41,14 @@ accessor.has('user.email'); // false (original unchanged)
41
41
 
42
42
  ## Dot Notation Syntax
43
43
 
44
- The TypeScript package supports dot-separated key access:
44
+ ### Basic Syntax
45
45
 
46
- | Syntax | Example | Description |
47
- | ----------- | -------------- | ------------------------- |
48
- | `key.key` | `user.name` | Nested key access |
49
- | `key.0.key` | `users.0.name` | Numeric key (array index) |
46
+ | Syntax | Example | Description |
47
+ | ----------------- | ------------------ | ------------------------------- |
48
+ | `key.key` | `user.name` | Nested key access |
49
+ | `key.0.key` | `users.0.name` | Numeric key (array index) |
50
+ | `key\.with\.dots` | `config\.db\.host` | Escaped dots in key names |
51
+ | `$` or `$.path` | `$.user.name` | Optional root prefix (stripped) |
50
52
 
51
53
  ```typescript
52
54
  const data = Inline.fromJson('{"users": [{"name": "Alice"}, {"name": "Bob"}]}');
@@ -54,7 +56,46 @@ data.get('users.0.name'); // 'Alice'
54
56
  data.get('users.1.name'); // 'Bob'
55
57
  ```
56
58
 
57
- > **Note:** Advanced PathQuery features (wildcards, filters, slices, recursive descent, projections) are available in the PHP package only. See the [PHP README](../php/README.md#advanced-pathquery) for details.
59
+ ### Advanced PathQuery
60
+
61
+ | Syntax | Example | Description |
62
+ | --------------- | ------------------- | ----------------------------------------- |
63
+ | `[0]` | `users[0]` | Bracket index access |
64
+ | `*` or `[*]` | `users.*` | Wildcard - expand all children |
65
+ | `..key` | `..name` | Recursive descent - find key at any depth |
66
+ | `..['a','b']` | `..['name','age']` | Multi-key recursive descent |
67
+ | `[0,1,2]` | `users[0,1,2]` | Multi-index selection |
68
+ | `['a','b']` | `['name','age']` | Multi-key selection |
69
+ | `[0:5]` | `items[0:5]` | Slice - indices 0 through 4 |
70
+ | `[::2]` | `items[::2]` | Slice with step |
71
+ | `[::-1]` | `items[::-1]` | Reverse slice |
72
+ | `[?expr]` | `users[?age>18]` | Filter predicate expression |
73
+ | `.{fields}` | `.{name, age}` | Projection - select fields |
74
+ | `.{alias: src}` | `.{fullName: name}` | Aliased projection |
75
+
76
+ ### Filter Expressions
77
+
78
+ ```typescript
79
+ const data = Inline.fromJson(`[
80
+ {"name": "Alice", "age": 25, "role": "admin"},
81
+ {"name": "Bob", "age": 17, "role": "user"},
82
+ {"name": "Carol", "age": 30, "role": "admin"}
83
+ ]`);
84
+
85
+ // Comparison: ==, !=, >, <, >=, <=
86
+ data.get('[?age>18]'); // Alice and Carol
87
+
88
+ // Logical: && and ||
89
+ data.get('[?age>18 && role=="admin"]'); // Alice and Carol
90
+
91
+ // Built-in functions: starts_with, contains, values
92
+ data.get('[?starts_with(@.name, "A")]'); // Alice
93
+ data.get('[?contains(@.name, "ob")]'); // Bob
94
+
95
+ // Arithmetic in predicates: +, -, *, /
96
+ const orders = Inline.fromJson('[{"price": 10, "qty": 5}, {"price": 3, "qty": 2}]');
97
+ orders.get('[?@.price * @.qty > 20]'); // first order only
98
+ ```
58
99
 
59
100
  ## Supported Formats
60
101
 
@@ -173,13 +214,13 @@ accessor.getMany({
173
214
  }); // { 'a.b': 1, 'a.x': 'fallback' }
174
215
  accessor.getRaw(); // original JSON string
175
216
 
176
- // Write (immutable every write returns a new instance)
217
+ // Write (immutable - every write returns a new instance)
177
218
  const updated = accessor.set('a.d', 3);
178
219
  const cleaned = updated.remove('a.c');
179
220
  const merged = cleaned.merge('a', { e: 4 });
180
221
  const full = merged.mergeAll({ f: 5 });
181
222
 
182
- // Readonly mode block all writes
223
+ // Readonly mode - block all writes
183
224
  const readonly = accessor.readonly();
184
225
  readonly.get('a.b'); // 1 (reads work)
185
226
  readonly.set('a.b', 99); // throws ReadonlyViolationException
@@ -243,7 +284,7 @@ const accessor = Inline.withSecurityGuard(guard).fromJson(data);
243
284
 
244
285
  | Format | Protection |
245
286
  | ------ | ------------------------------------------------ |
246
- | XML | Rejects `<!DOCTYPE` prevents XXE attacks |
287
+ | XML | Rejects `<!DOCTYPE` - prevents XXE attacks |
247
288
  | YAML | Blocks unsafe tags, anchors, aliases, merge keys |
248
289
  | All | Forbidden key validation on every parsed key |
249
290
 
@@ -291,7 +332,7 @@ try {
291
332
 
292
333
  | Exception | Extends | When |
293
334
  | ---------------------------- | ------------------------ | ----------------------------------------- |
294
- | `AccessorException` | `Error` | Root catch-all |
335
+ | `AccessorException` | `Error` | Root - catch-all |
295
336
  | `SecurityException` | `AccessorException` | Forbidden key, payload, structural limits |
296
337
  | `InvalidFormatException` | `AccessorException` | Malformed JSON, XML, INI, NDJSON |
297
338
  | `YamlParseException` | `InvalidFormatException` | Unsafe or malformed YAML |
@@ -315,6 +356,7 @@ const accessor = Inline.withStrictMode(false).fromJson(trustedPayload);
315
356
 
316
357
  ```typescript
317
358
  // Implement PathCacheInterface for repeated lookups
359
+ const cacheMap = new Map();
318
360
  const cache: PathCacheInterface = {
319
361
  get: (path) => cacheMap.get(path) ?? null,
320
362
  set: (path, segments) => {
@@ -1,13 +1,25 @@
1
1
  import type { AccessorsInterface } from '../contracts/accessors-interface.js';
2
- import { DotNotationParser } from '../core/dot-notation-parser.js';
2
+ import type { ValidatableParserInterface } from '../contracts/validatable-parser-interface.js';
3
+ /**
4
+ * Base accessor providing read, write, and lifecycle operations.
5
+ *
6
+ * Implements all AccessorsInterface methods with immutable copy
7
+ * semantics for writes, optional readonly enforcement, and strict mode
8
+ * for security validation on data ingestion.
9
+ *
10
+ * Subclasses must implement `parse()` to convert raw input into
11
+ * a normalized plain object.
12
+ *
13
+ * @api
14
+ */
3
15
  export declare abstract class AbstractAccessor implements AccessorsInterface {
4
- protected readonly parser: DotNotationParser;
16
+ protected readonly parser: ValidatableParserInterface;
5
17
  /** @internal Mutable state grouped to allow O(1) shallow clone in mutations. */
6
18
  private _state;
7
19
  /**
8
20
  * @param parser - Dot-notation parser for path operations.
9
21
  */
10
- constructor(parser: DotNotationParser);
22
+ constructor(parser: ValidatableParserInterface);
11
23
  /**
12
24
  * Convert raw input data into a normalized plain object.
13
25
  *
@@ -59,7 +71,7 @@ export declare abstract class AbstractAccessor implements AccessorsInterface {
59
71
  * Only use with fully trusted, application-controlled input.
60
72
  *
61
73
  * @example
62
- * // Trust the input skip all security checks
74
+ * // Trust the input - skip all security checks
63
75
  * const accessor = new JsonAccessor(parser).strict(false).from(trustedPayload);
64
76
  */
65
77
  strict(strict?: boolean): this;
@@ -159,19 +171,19 @@ export declare abstract class AbstractAccessor implements AccessorsInterface {
159
171
  */
160
172
  all(): Record<string, unknown>;
161
173
  /**
162
- * Count elements at a path, or the root if undefined.
174
+ * Count elements at a path, or the root if null/undefined.
163
175
  *
164
- * @param path - Dot-notation path, or undefined for root.
176
+ * @param path - Dot-notation path, or null/undefined for root.
165
177
  * @returns Number of elements.
166
178
  */
167
- count(path?: string): number;
179
+ count(path?: string | null): number;
168
180
  /**
169
- * Retrieve array keys at a path, or root keys if undefined.
181
+ * Retrieve array keys at a path, or root keys if null/undefined.
170
182
  *
171
- * @param path - Dot-notation path, or undefined for root.
183
+ * @param path - Dot-notation path, or null/undefined for root.
172
184
  * @returns List of keys.
173
185
  */
174
- keys(path?: string): string[];
186
+ keys(path?: string | null): string[];
175
187
  /**
176
188
  * Deep-merge an object into the value at a dot-notation path.
177
189
  *
@@ -1,5 +1,17 @@
1
1
  import { PathNotFoundException } from '../exceptions/path-not-found-exception.js';
2
2
  import { ReadonlyViolationException } from '../exceptions/readonly-violation-exception.js';
3
+ /**
4
+ * Base accessor providing read, write, and lifecycle operations.
5
+ *
6
+ * Implements all AccessorsInterface methods with immutable copy
7
+ * semantics for writes, optional readonly enforcement, and strict mode
8
+ * for security validation on data ingestion.
9
+ *
10
+ * Subclasses must implement `parse()` to convert raw input into
11
+ * a normalized plain object.
12
+ *
13
+ * @api
14
+ */
3
15
  export class AbstractAccessor {
4
16
  parser;
5
17
  /** @internal Mutable state grouped to allow O(1) shallow clone in mutations. */
@@ -68,7 +80,7 @@ export class AbstractAccessor {
68
80
  * Only use with fully trusted, application-controlled input.
69
81
  *
70
82
  * @example
71
- * // Trust the input skip all security checks
83
+ * // Trust the input - skip all security checks
72
84
  * const accessor = new JsonAccessor(parser).strict(false).from(trustedPayload);
73
85
  */
74
86
  strict(strict = true) {
@@ -133,7 +145,8 @@ export class AbstractAccessor {
133
145
  * @returns True if the path resolves to a value.
134
146
  */
135
147
  hasAt(segments) {
136
- return this.parser.hasAt(this._state.data, segments);
148
+ const sentinel = Object.create(null);
149
+ return this.parser.getAt(this._state.data, segments, sentinel) !== sentinel;
137
150
  }
138
151
  /**
139
152
  * Set a value at a dot-notation path.
@@ -207,26 +220,26 @@ export class AbstractAccessor {
207
220
  return this._state.data;
208
221
  }
209
222
  /**
210
- * Count elements at a path, or the root if undefined.
223
+ * Count elements at a path, or the root if null/undefined.
211
224
  *
212
- * @param path - Dot-notation path, or undefined for root.
225
+ * @param path - Dot-notation path, or null/undefined for root.
213
226
  * @returns Number of elements.
214
227
  */
215
228
  count(path) {
216
- const target = path !== undefined ? this.get(path, {}) : this._state.data;
229
+ const target = path != null ? this.get(path, {}) : this._state.data;
217
230
  if (typeof target === 'object' && target !== null) {
218
231
  return Object.keys(target).length;
219
232
  }
220
233
  return 0;
221
234
  }
222
235
  /**
223
- * Retrieve array keys at a path, or root keys if undefined.
236
+ * Retrieve array keys at a path, or root keys if null/undefined.
224
237
  *
225
- * @param path - Dot-notation path, or undefined for root.
238
+ * @param path - Dot-notation path, or null/undefined for root.
226
239
  * @returns List of keys.
227
240
  */
228
241
  keys(path) {
229
- const target = path !== undefined ? this.get(path, {}) : this._state.data;
242
+ const target = path != null ? this.get(path, {}) : this._state.data;
230
243
  /* Stryker disable next-line ConditionalExpression -- equivalent: get() always returns an object-type value here; typeof check is a type guard only */
231
244
  if (typeof target === 'object' && target !== null) {
232
245
  return Object.keys(target);
@@ -0,0 +1,22 @@
1
+ import { AbstractAccessor } from './abstract-accessor.js';
2
+ import type { ParseIntegrationInterface } from '../contracts/parse-integration-interface.js';
3
+ import type { ValidatableParserInterface } from '../contracts/validatable-parser-interface.js';
4
+ /**
5
+ * Base accessor with custom format integration support.
6
+ *
7
+ * Extends {@link AbstractAccessor} to inject a {@link ParseIntegrationInterface}
8
+ * for user-defined format detection and parsing. Used exclusively by
9
+ * {@link AnyAccessor} to handle arbitrary input formats.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare abstract class AbstractIntegrationAccessor extends AbstractAccessor {
14
+ protected readonly integration: ParseIntegrationInterface;
15
+ /**
16
+ * Create an accessor with parser and custom integration dependencies.
17
+ *
18
+ * @param parser - Dot-notation parser.
19
+ * @param integration - Custom format parser.
20
+ */
21
+ constructor(parser: ValidatableParserInterface, integration: ParseIntegrationInterface);
22
+ }
@@ -0,0 +1,23 @@
1
+ import { AbstractAccessor } from './abstract-accessor.js';
2
+ /**
3
+ * Base accessor with custom format integration support.
4
+ *
5
+ * Extends {@link AbstractAccessor} to inject a {@link ParseIntegrationInterface}
6
+ * for user-defined format detection and parsing. Used exclusively by
7
+ * {@link AnyAccessor} to handle arbitrary input formats.
8
+ *
9
+ * @internal
10
+ */
11
+ export class AbstractIntegrationAccessor extends AbstractAccessor {
12
+ integration;
13
+ /**
14
+ * Create an accessor with parser and custom integration dependencies.
15
+ *
16
+ * @param parser - Dot-notation parser.
17
+ * @param integration - Custom format parser.
18
+ */
19
+ constructor(parser, integration) {
20
+ super(parser);
21
+ this.integration = integration;
22
+ }
23
+ }
@@ -1,24 +1,25 @@
1
- import { AbstractAccessor } from '../abstract-accessor.js';
1
+ import { AbstractIntegrationAccessor } from '../abstract-integration-accessor.js';
2
2
  import type { ParseIntegrationInterface } from '../../contracts/parse-integration-interface.js';
3
- import type { DotNotationParser } from '../../core/dot-notation-parser.js';
3
+ import type { ValidatableParserInterface } from '../../contracts/validatable-parser-interface.js';
4
4
  /**
5
5
  * Accessor for arbitrary formats via a custom {@link ParseIntegrationInterface}.
6
6
  *
7
7
  * Delegates format detection and parsing to a user-provided integration.
8
8
  * Validates string payloads against security constraints before parsing.
9
9
  *
10
+ * @api
11
+ *
10
12
  * @example
11
13
  * const integration = new MyCsvIntegration();
12
14
  * const accessor = Inline.withParserIntegration(integration).fromAny(csvString);
13
15
  * accessor.get('0.name'); // first row, name column
14
16
  */
15
- export declare class AnyAccessor extends AbstractAccessor {
16
- private readonly integration;
17
+ export declare class AnyAccessor extends AbstractIntegrationAccessor {
17
18
  /**
18
19
  * @param parser - Dot-notation parser with security configuration.
19
20
  * @param integration - Custom format parser for detecting and parsing input.
20
21
  */
21
- constructor(parser: DotNotationParser, integration: ParseIntegrationInterface);
22
+ constructor(parser: ValidatableParserInterface, integration: ParseIntegrationInterface);
22
23
  /**
23
24
  * Hydrate from raw data via the custom integration.
24
25
  *
@@ -26,10 +27,11 @@ export declare class AnyAccessor extends AbstractAccessor {
26
27
  * @returns Populated accessor instance.
27
28
  * @throws {InvalidFormatException} When the integration rejects the format.
28
29
  * @throws {SecurityException} When string input violates payload-size limits.
30
+ *
31
+ * @example
32
+ * const accessor = new AnyAccessor(parser, integration).from(rawData);
29
33
  */
30
34
  from(data: unknown): this;
31
- /**
32
- * @internal
33
- */
35
+ /** {@inheritDoc} */
34
36
  protected parse(raw: unknown): Record<string, unknown>;
35
37
  }
@@ -1,4 +1,4 @@
1
- import { AbstractAccessor } from '../abstract-accessor.js';
1
+ import { AbstractIntegrationAccessor } from '../abstract-integration-accessor.js';
2
2
  import { InvalidFormatException } from '../../exceptions/invalid-format-exception.js';
3
3
  /**
4
4
  * Accessor for arbitrary formats via a custom {@link ParseIntegrationInterface}.
@@ -6,20 +6,20 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
6
6
  * Delegates format detection and parsing to a user-provided integration.
7
7
  * Validates string payloads against security constraints before parsing.
8
8
  *
9
+ * @api
10
+ *
9
11
  * @example
10
12
  * const integration = new MyCsvIntegration();
11
13
  * const accessor = Inline.withParserIntegration(integration).fromAny(csvString);
12
14
  * accessor.get('0.name'); // first row, name column
13
15
  */
14
- export class AnyAccessor extends AbstractAccessor {
15
- integration;
16
+ export class AnyAccessor extends AbstractIntegrationAccessor {
16
17
  /**
17
18
  * @param parser - Dot-notation parser with security configuration.
18
19
  * @param integration - Custom format parser for detecting and parsing input.
19
20
  */
20
21
  constructor(parser, integration) {
21
- super(parser);
22
- this.integration = integration;
22
+ super(parser, integration);
23
23
  }
24
24
  /**
25
25
  * Hydrate from raw data via the custom integration.
@@ -28,6 +28,9 @@ export class AnyAccessor extends AbstractAccessor {
28
28
  * @returns Populated accessor instance.
29
29
  * @throws {InvalidFormatException} When the integration rejects the format.
30
30
  * @throws {SecurityException} When string input violates payload-size limits.
31
+ *
32
+ * @example
33
+ * const accessor = new AnyAccessor(parser, integration).from(rawData);
31
34
  */
32
35
  from(data) {
33
36
  if (!this.integration.assertFormat(data)) {
@@ -35,9 +38,7 @@ export class AnyAccessor extends AbstractAccessor {
35
38
  }
36
39
  return this.ingest(data);
37
40
  }
38
- /**
39
- * @internal
40
- */
41
+ /** {@inheritDoc} */
41
42
  parse(raw) {
42
43
  return this.integration.parse(raw);
43
44
  }
@@ -4,6 +4,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
4
4
  *
5
5
  * Accepts a plain object or array directly. No string parsing is involved.
6
6
  *
7
+ * @api
8
+ *
7
9
  * @example
8
10
  * const accessor = new ArrayAccessor(parser).from({ key: 'value' });
9
11
  * accessor.get('key'); // 'value'
@@ -5,6 +5,8 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
5
5
  *
6
6
  * Accepts a plain object or array directly. No string parsing is involved.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const accessor = new ArrayAccessor(parser).from({ key: 'value' });
10
12
  * accessor.get('key'); // 'value'
@@ -5,6 +5,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
5
5
  * Parses KEY=VALUE lines, skipping comments (#) and blank lines.
6
6
  * Strips surrounding single and double quotes from values.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const accessor = new EnvAccessor(parser).from('DB_HOST=localhost\nDEBUG=true');
10
12
  * accessor.get('DB_HOST'); // 'localhost'
@@ -6,6 +6,8 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
6
6
  * Parses KEY=VALUE lines, skipping comments (#) and blank lines.
7
7
  * Strips surrounding single and double quotes from values.
8
8
  *
9
+ * @api
10
+ *
9
11
  * @example
10
12
  * const accessor = new EnvAccessor(parser).from('DB_HOST=localhost\nDEBUG=true');
11
13
  * accessor.get('DB_HOST'); // 'localhost'
@@ -5,6 +5,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
5
5
  * Parses sections (e.g. `[section]`) as nested keys.
6
6
  * Type inference: numeric strings become numbers, `true`/`false` become booleans.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const accessor = new IniAccessor(parser).from('[db]\nhost=localhost\nport=5432');
10
12
  * accessor.get('db.host'); // 'localhost'
@@ -6,6 +6,8 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
6
6
  * Parses sections (e.g. `[section]`) as nested keys.
7
7
  * Type inference: numeric strings become numbers, `true`/`false` become booleans.
8
8
  *
9
+ * @api
10
+ *
9
11
  * @example
10
12
  * const accessor = new IniAccessor(parser).from('[db]\nhost=localhost\nport=5432');
11
13
  * accessor.get('db.host'); // 'localhost'
@@ -4,6 +4,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
4
4
  *
5
5
  * Decodes JSON via `JSON.parse()`. Validates payload size before parsing.
6
6
  *
7
+ * @api
8
+ *
7
9
  * @example
8
10
  * const accessor = new JsonAccessor(parser).from('{"key":"value"}');
9
11
  * accessor.get('key'); // 'value'
@@ -5,6 +5,8 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
5
5
  *
6
6
  * Decodes JSON via `JSON.parse()`. Validates payload size before parsing.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const accessor = new JsonAccessor(parser).from('{"key":"value"}');
10
12
  * accessor.get('key'); // 'value'
@@ -5,6 +5,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
5
5
  * Parses each non-empty line as a standalone JSON object,
6
6
  * producing an indexed record of parsed entries.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const ndjson = '{"id":1}\n{"id":2}';
10
12
  * const accessor = new NdjsonAccessor(parser).from(ndjson);
@@ -6,6 +6,8 @@ import { InvalidFormatException } from '../../exceptions/invalid-format-exceptio
6
6
  * Parses each non-empty line as a standalone JSON object,
7
7
  * producing an indexed record of parsed entries.
8
8
  *
9
+ * @api
10
+ *
9
11
  * @example
10
12
  * const ndjson = '{"id":1}\n{"id":2}';
11
13
  * const accessor = new NdjsonAccessor(parser).from(ndjson);
@@ -5,6 +5,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
5
5
  * Handles nested objects and arrays of objects without JSON roundtrip.
6
6
  * Respects the configured max depth to prevent DoS from deeply nested structures.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const obj = { user: { name: 'Alice' } };
10
12
  * const accessor = new ObjectAccessor(parser).from(obj);
@@ -7,6 +7,8 @@ import { SecurityException } from '../../exceptions/security-exception.js';
7
7
  * Handles nested objects and arrays of objects without JSON roundtrip.
8
8
  * Respects the configured max depth to prevent DoS from deeply nested structures.
9
9
  *
10
+ * @api
11
+ *
10
12
  * @example
11
13
  * const obj = { user: { name: 'Alice' } };
12
14
  * const accessor = new ObjectAccessor(parser).from(obj);
@@ -5,6 +5,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
5
5
  * Parses XML using the DOM parser available in the current environment.
6
6
  * Blocks DOCTYPE declarations to prevent XXE attacks.
7
7
  *
8
+ * @api
9
+ *
8
10
  * @example
9
11
  * const accessor = new XmlAccessor(parser).from('<root><key>value</key></root>');
10
12
  * accessor.get('key'); // 'value'
@@ -8,6 +8,8 @@ import { XmlParser } from '../../parser/xml-parser.js';
8
8
  * Parses XML using the DOM parser available in the current environment.
9
9
  * Blocks DOCTYPE declarations to prevent XXE attacks.
10
10
  *
11
+ * @api
12
+ *
11
13
  * @example
12
14
  * const accessor = new XmlAccessor(parser).from('<root><key>value</key></root>');
13
15
  * accessor.get('key'); // 'value'
@@ -6,6 +6,8 @@ import { AbstractAccessor } from '../abstract-accessor.js';
6
6
  * depending on external YAML libraries. Tags, anchors, aliases, and
7
7
  * merge keys are blocked as unsafe constructs.
8
8
  *
9
+ * @api
10
+ *
9
11
  * @example
10
12
  * const accessor = new YamlAccessor(parser).from('key: value\nnested:\n a: 1');
11
13
  * accessor.get('nested.a'); // 1
@@ -21,7 +23,7 @@ export declare class YamlAccessor extends AbstractAccessor {
21
23
  * @throws {SecurityException} When payload size exceeds limit.
22
24
  *
23
25
  * @example
24
- * accessor.from('name: Alice\nage: 30');
26
+ * accessor.from('name: Alice\nage: 30'); // { name: 'Alice', age: 30 }
25
27
  */
26
28
  from(data: unknown): this;
27
29
  /** {@inheritDoc} */