@safeaccess/inline 0.1.1 → 0.1.3

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 +23 -5
  3. package/LICENSE +1 -1
  4. package/README.md +79 -21
  5. package/dist/accessors/abstract-accessor.d.ts +24 -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 +26 -56
  67. package/dist/inline.js +43 -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 +4 -1
  82. package/dist/security/security-guard.js +7 -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 +25 -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 +46 -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 +10 -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 +379 -0
  163. package/tests/parser/xml-parser.test.ts +17 -330
  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 +8 -479
  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
@@ -0,0 +1,157 @@
1
+ import { ArrayAccessor } from '../accessors/formats/array-accessor.js';
2
+ import { ObjectAccessor } from '../accessors/formats/object-accessor.js';
3
+ import { JsonAccessor } from '../accessors/formats/json-accessor.js';
4
+ import { XmlAccessor } from '../accessors/formats/xml-accessor.js';
5
+ import { YamlAccessor } from '../accessors/formats/yaml-accessor.js';
6
+ import { IniAccessor } from '../accessors/formats/ini-accessor.js';
7
+ import { EnvAccessor } from '../accessors/formats/env-accessor.js';
8
+ import { NdjsonAccessor } from '../accessors/formats/ndjson-accessor.js';
9
+ import { AnyAccessor } from '../accessors/formats/any-accessor.js';
10
+ import { InvalidFormatException } from '../exceptions/invalid-format-exception.js';
11
+ /**
12
+ * Factory for creating typed format-specific accessors.
13
+ *
14
+ * Encapsulates the wiring between a parser and accessor construction,
15
+ * providing one method per supported format. Used internally by
16
+ * {@link Inline} to create accessors.
17
+ *
18
+ * @internal
19
+ */
20
+ export class AccessorFactory {
21
+ parser;
22
+ defaultIntegration;
23
+ strictMode;
24
+ /**
25
+ * Initialize the factory with a parser, optional integration, and optional strict mode.
26
+ *
27
+ * @param parser - Parser for dot-notation resolution.
28
+ * @param defaultIntegration - Default integration for AnyAccessor.
29
+ * @param strictMode - Override strict mode for created accessors.
30
+ */
31
+ constructor(parser, defaultIntegration = null, strictMode = null) {
32
+ this.parser = parser;
33
+ this.defaultIntegration = defaultIntegration;
34
+ this.strictMode = strictMode;
35
+ }
36
+ /**
37
+ * Return the underlying parser used by this factory.
38
+ *
39
+ * @returns The parser instance.
40
+ */
41
+ getParser() {
42
+ return this.parser;
43
+ }
44
+ /**
45
+ * Apply configured strict mode to a new accessor before hydration.
46
+ *
47
+ * @param accessor - Unhydrated accessor instance.
48
+ * @returns Same accessor with strict mode applied if configured.
49
+ */
50
+ applyOptions(accessor) {
51
+ if (this.strictMode !== null) {
52
+ return accessor.strict(this.strictMode);
53
+ }
54
+ return accessor;
55
+ }
56
+ /**
57
+ * Create an ArrayAccessor from raw array data.
58
+ *
59
+ * @param data - Source array or object.
60
+ * @returns Populated ArrayAccessor.
61
+ * @throws {SecurityException} When security constraints are violated.
62
+ */
63
+ array(data) {
64
+ return this.applyOptions(new ArrayAccessor(this.parser)).from(data);
65
+ }
66
+ /**
67
+ * Create an ObjectAccessor from a source object.
68
+ *
69
+ * @param data - Source object.
70
+ * @returns Populated ObjectAccessor.
71
+ * @throws {SecurityException} When security constraints are violated.
72
+ */
73
+ object(data) {
74
+ return this.applyOptions(new ObjectAccessor(this.parser)).from(data);
75
+ }
76
+ /**
77
+ * Create a JsonAccessor from a JSON string.
78
+ *
79
+ * @param data - Raw JSON string.
80
+ * @returns Populated JsonAccessor.
81
+ * @throws {InvalidFormatException} When the JSON is malformed.
82
+ * @throws {SecurityException} When security constraints are violated.
83
+ */
84
+ json(data) {
85
+ return this.applyOptions(new JsonAccessor(this.parser)).from(data);
86
+ }
87
+ /**
88
+ * Create an XmlAccessor from an XML string.
89
+ *
90
+ * @param data - Raw XML string.
91
+ * @returns Populated XmlAccessor.
92
+ * @throws {InvalidFormatException} When the XML is malformed.
93
+ * @throws {SecurityException} When DOCTYPE is detected.
94
+ */
95
+ xml(data) {
96
+ return this.applyOptions(new XmlAccessor(this.parser)).from(data);
97
+ }
98
+ /**
99
+ * Create a YamlAccessor from a YAML string.
100
+ *
101
+ * @param data - Raw YAML string.
102
+ * @returns Populated YamlAccessor.
103
+ * @throws {YamlParseException} When the YAML is malformed.
104
+ * @throws {SecurityException} When security constraints are violated.
105
+ */
106
+ yaml(data) {
107
+ return this.applyOptions(new YamlAccessor(this.parser)).from(data);
108
+ }
109
+ /**
110
+ * Create an IniAccessor from an INI string.
111
+ *
112
+ * @param data - Raw INI string.
113
+ * @returns Populated IniAccessor.
114
+ * @throws {InvalidFormatException} When the INI is malformed.
115
+ * @throws {SecurityException} When security constraints are violated.
116
+ */
117
+ ini(data) {
118
+ return this.applyOptions(new IniAccessor(this.parser)).from(data);
119
+ }
120
+ /**
121
+ * Create an EnvAccessor from a dotenv-formatted string.
122
+ *
123
+ * @param data - Raw dotenv string.
124
+ * @returns Populated EnvAccessor.
125
+ * @throws {SecurityException} When security constraints are violated.
126
+ */
127
+ env(data) {
128
+ return this.applyOptions(new EnvAccessor(this.parser)).from(data);
129
+ }
130
+ /**
131
+ * Create an NdjsonAccessor from a newline-delimited JSON string.
132
+ *
133
+ * @param data - Raw NDJSON string.
134
+ * @returns Populated NdjsonAccessor.
135
+ * @throws {InvalidFormatException} When any JSON line is malformed.
136
+ * @throws {SecurityException} When security constraints are violated.
137
+ */
138
+ ndjson(data) {
139
+ return this.applyOptions(new NdjsonAccessor(this.parser)).from(data);
140
+ }
141
+ /**
142
+ * Create an AnyAccessor with automatic format detection.
143
+ *
144
+ * @param data - Raw data in any supported format.
145
+ * @param integration - Override integration (falls back to default).
146
+ * @returns Populated AnyAccessor.
147
+ * @throws {InvalidFormatException} When no integration is available.
148
+ */
149
+ any(data, integration) {
150
+ const resolved = integration ?? this.defaultIntegration;
151
+ if (resolved === null) {
152
+ throw new InvalidFormatException('AnyAccessor requires a ParseIntegrationInterface. ' +
153
+ 'Pass one directly or configure a default via Inline.withParserIntegration(i).fromAny(data).');
154
+ }
155
+ return this.applyOptions(new AnyAccessor(this.parser, resolved)).from(data);
156
+ }
157
+ }
@@ -1,26 +1,36 @@
1
1
  import type { SecurityGuardInterface } from '../contracts/security-guard-interface.js';
2
2
  import type { SecurityParserInterface } from '../contracts/security-parser-interface.js';
3
3
  import type { PathCacheInterface } from '../contracts/path-cache-interface.js';
4
+ import type { ValidatableParserInterface } from '../contracts/validatable-parser-interface.js';
5
+ import { SegmentParser } from '../path-query/segment-parser.js';
6
+ import { SegmentPathResolver } from '../path-query/segment-path-resolver.js';
7
+ import type { Segment } from '../path-query/segment-type.js';
4
8
  /**
5
9
  * Core dot-notation parser for reading, writing, and removing nested values.
6
10
  *
7
11
  * Provides path-based access to plain objects using dot-separated keys.
8
12
  * Delegates security validation to SecurityGuard and SecurityParser.
9
13
  *
14
+ * @internal
15
+ *
10
16
  * @example
11
17
  * const parser = new DotNotationParser();
12
18
  * parser.get({ user: { name: 'Alice' } }, 'user.name'); // 'Alice'
13
19
  */
14
- export declare class DotNotationParser {
20
+ export declare class DotNotationParser implements ValidatableParserInterface {
15
21
  private readonly securityGuard;
16
22
  private readonly securityParser;
17
23
  private readonly pathCache;
24
+ private readonly segmentParser;
25
+ private readonly segmentPathResolver;
18
26
  /**
19
27
  * @param securityGuard - Key-safety guard. Defaults to a new SecurityGuard instance.
20
28
  * @param securityParser - Parser depth and size limits. Defaults to a new SecurityParser instance.
21
29
  * @param pathCache - Optional path segment cache for repeated lookups.
30
+ * @param segmentParser - Path-string → segment converter.
31
+ * @param segmentPathResolver - Segment → value resolver.
22
32
  */
23
- constructor(securityGuard?: SecurityGuardInterface, securityParser?: SecurityParserInterface, pathCache?: PathCacheInterface);
33
+ constructor(securityGuard?: SecurityGuardInterface, securityParser?: SecurityParserInterface, pathCache?: PathCacheInterface, segmentParser?: SegmentParser, segmentPathResolver?: SegmentPathResolver);
24
34
  /**
25
35
  * Resolve a dot-notation path against data, returning the matched value.
26
36
  *
@@ -34,6 +44,25 @@ export declare class DotNotationParser {
34
44
  * parser.get({ a: 1 }, 'a.b', 'default'); // 'default'
35
45
  */
36
46
  get(data: Record<string, unknown>, path: string, defaultValue?: unknown): unknown;
47
+ /**
48
+ * Resolve a pre-parsed segment array against data, returning the matched value.
49
+ *
50
+ * @param data - Source data object.
51
+ * @param segments - Typed segments from {@link SegmentParser}.
52
+ * @param defaultValue - Fallback returned when the path does not exist.
53
+ * @returns Resolved value or the default.
54
+ */
55
+ resolve(data: Record<string, unknown>, segments: Segment[], defaultValue?: unknown): unknown;
56
+ /**
57
+ * Retrieve a value at the given path, throwing when not found.
58
+ *
59
+ * @param data - Source data object.
60
+ * @param path - Dot-notation path string.
61
+ * @returns Resolved value.
62
+ *
63
+ * @throws {PathNotFoundException} When the path does not exist.
64
+ */
65
+ getStrict(data: Record<string, unknown>, path: string): unknown;
37
66
  /**
38
67
  * Set a value at a dot-notation path, returning a new object.
39
68
  *
@@ -161,12 +190,12 @@ export declare class DotNotationParser {
161
190
  */
162
191
  getMaxKeys(): number;
163
192
  /**
164
- * Parse a dot-notation path into segments, using cache when available.
193
+ * Retrieve parsed segments from cache or parse and cache the path.
165
194
  *
166
195
  * @param path - Dot-notation path string.
167
- * @returns Array of path segments.
196
+ * @returns Cached or freshly parsed typed segments.
168
197
  */
169
- private parsePath;
198
+ private segmentPathCache;
170
199
  /**
171
200
  * Recursively write a value at the given key path.
172
201
  *
@@ -1,11 +1,17 @@
1
1
  import { SecurityGuard } from '../security/security-guard.js';
2
2
  import { SecurityParser } from '../security/security-parser.js';
3
+ import { PathNotFoundException } from '../exceptions/path-not-found-exception.js';
4
+ import { SegmentFilterParser } from '../path-query/segment-filter-parser.js';
5
+ import { SegmentParser } from '../path-query/segment-parser.js';
6
+ import { SegmentPathResolver } from '../path-query/segment-path-resolver.js';
3
7
  /**
4
8
  * Core dot-notation parser for reading, writing, and removing nested values.
5
9
  *
6
10
  * Provides path-based access to plain objects using dot-separated keys.
7
11
  * Delegates security validation to SecurityGuard and SecurityParser.
8
12
  *
13
+ * @internal
14
+ *
9
15
  * @example
10
16
  * const parser = new DotNotationParser();
11
17
  * parser.get({ user: { name: 'Alice' } }, 'user.name'); // 'Alice'
@@ -14,15 +20,22 @@ export class DotNotationParser {
14
20
  securityGuard;
15
21
  securityParser;
16
22
  pathCache;
23
+ segmentParser;
24
+ segmentPathResolver;
17
25
  /**
18
26
  * @param securityGuard - Key-safety guard. Defaults to a new SecurityGuard instance.
19
27
  * @param securityParser - Parser depth and size limits. Defaults to a new SecurityParser instance.
20
28
  * @param pathCache - Optional path segment cache for repeated lookups.
29
+ * @param segmentParser - Path-string → segment converter.
30
+ * @param segmentPathResolver - Segment → value resolver.
21
31
  */
22
- constructor(securityGuard, securityParser, pathCache) {
32
+ constructor(securityGuard, securityParser, pathCache, segmentParser, segmentPathResolver) {
23
33
  this.securityGuard = securityGuard ?? new SecurityGuard();
24
34
  this.securityParser = securityParser ?? new SecurityParser();
25
35
  this.pathCache = pathCache ?? null;
36
+ const filterParser = new SegmentFilterParser(this.securityGuard);
37
+ this.segmentParser = segmentParser ?? new SegmentParser(filterParser);
38
+ this.segmentPathResolver = segmentPathResolver ?? new SegmentPathResolver(filterParser);
26
39
  }
27
40
  /**
28
41
  * Resolve a dot-notation path against data, returning the matched value.
@@ -41,8 +54,36 @@ export class DotNotationParser {
41
54
  if (path === '') {
42
55
  return defaultValue;
43
56
  }
44
- const segments = this.parsePath(path);
45
- return this.getAt(data, segments, defaultValue);
57
+ const segments = this.segmentPathCache(path);
58
+ return this.resolve(data, segments, defaultValue);
59
+ }
60
+ /**
61
+ * Resolve a pre-parsed segment array against data, returning the matched value.
62
+ *
63
+ * @param data - Source data object.
64
+ * @param segments - Typed segments from {@link SegmentParser}.
65
+ * @param defaultValue - Fallback returned when the path does not exist.
66
+ * @returns Resolved value or the default.
67
+ */
68
+ resolve(data, segments, defaultValue = null) {
69
+ return this.segmentPathResolver.resolve(data, segments, 0, defaultValue, this.securityParser.getMaxResolveDepth());
70
+ }
71
+ /**
72
+ * Retrieve a value at the given path, throwing when not found.
73
+ *
74
+ * @param data - Source data object.
75
+ * @param path - Dot-notation path string.
76
+ * @returns Resolved value.
77
+ *
78
+ * @throws {PathNotFoundException} When the path does not exist.
79
+ */
80
+ getStrict(data, path) {
81
+ const sentinel = Object.create(null);
82
+ const result = this.get(data, path, sentinel);
83
+ if (result === sentinel) {
84
+ throw new PathNotFoundException(`Path '${path}' not found.`);
85
+ }
86
+ return result;
46
87
  }
47
88
  /**
48
89
  * Set a value at a dot-notation path, returning a new object.
@@ -56,7 +97,7 @@ export class DotNotationParser {
56
97
  * parser.set({}, 'user.name', 'Alice'); // { user: { name: 'Alice' } }
57
98
  */
58
99
  set(data, path, value) {
59
- const segments = this.parsePath(path);
100
+ const segments = this.segmentParser.parseKeys(path);
60
101
  return this.setAt(data, segments, value);
61
102
  }
62
103
  /**
@@ -89,7 +130,7 @@ export class DotNotationParser {
89
130
  * parser.remove({ a: { b: 1 } }, 'a.b'); // { a: {} }
90
131
  */
91
132
  remove(data, path) {
92
- const segments = this.parsePath(path);
133
+ const segments = this.segmentParser.parseKeys(path);
93
134
  return this.removeAt(data, segments);
94
135
  }
95
136
  /**
@@ -229,22 +270,22 @@ export class DotNotationParser {
229
270
  return this.securityParser.getMaxKeys();
230
271
  }
231
272
  /**
232
- * Parse a dot-notation path into segments, using cache when available.
273
+ * Retrieve parsed segments from cache or parse and cache the path.
233
274
  *
234
275
  * @param path - Dot-notation path string.
235
- * @returns Array of path segments.
276
+ * @returns Cached or freshly parsed typed segments.
236
277
  */
237
- parsePath(path) {
278
+ segmentPathCache(path) {
238
279
  if (this.pathCache !== null) {
239
280
  const cached = this.pathCache.get(path);
240
281
  if (cached !== null) {
241
282
  return cached;
242
283
  }
243
- const segments = path.split('.');
284
+ const segments = this.segmentParser.parseSegments(path);
244
285
  this.pathCache.set(path, segments);
245
286
  return segments;
246
287
  }
247
- return path.split('.');
288
+ return this.segmentParser.parseSegments(path);
248
289
  }
249
290
  /**
250
291
  * Recursively write a value at the given key path.
@@ -0,0 +1,82 @@
1
+ import type { SecurityGuardInterface } from '../contracts/security-guard-interface.js';
2
+ import type { SecurityParserInterface } from '../contracts/security-parser-interface.js';
3
+ import type { ParseIntegrationInterface } from '../contracts/parse-integration-interface.js';
4
+ import type { PathCacheInterface } from '../contracts/path-cache-interface.js';
5
+ import { AccessorFactory } from './accessor-factory.js';
6
+ /**
7
+ * Builder for configuring and constructing the internal components of SafeAccess Inline.
8
+ *
9
+ * Provides an immutable builder API: each `withXxx()` method returns a new
10
+ * instance with the specified override, leaving the original unchanged.
11
+ *
12
+ * Note: PHP's equivalent uses `__callStatic`/`__call` magic to expose
13
+ * protected methods publicly. TypeScript has no equivalent mechanism, so
14
+ * builder methods are public directly and static forwarding is explicit
15
+ * in the {@link Inline} subclass.
16
+ *
17
+ * @internal
18
+ *
19
+ * @see Inline
20
+ */
21
+ export declare class InlineBuilderAccessor {
22
+ protected readonly _guard: SecurityGuardInterface;
23
+ protected readonly _secParser: SecurityParserInterface;
24
+ protected readonly _pathCache: PathCacheInterface | null;
25
+ protected readonly _integration: ParseIntegrationInterface | null;
26
+ protected readonly _strictMode: boolean | null;
27
+ /**
28
+ * Create a builder with optional component overrides.
29
+ *
30
+ * @param guard - Custom security guard, or undefined for default.
31
+ * @param secParser - Custom security parser, or undefined for default.
32
+ * @param pathCache - Custom path cache, or null/undefined for default.
33
+ * @param integration - Custom format integration, or null/undefined for none.
34
+ * @param strictMode - Strict mode override, or null/undefined for accessor default.
35
+ */
36
+ constructor(guard?: SecurityGuardInterface, secParser?: SecurityParserInterface, pathCache?: PathCacheInterface | null, integration?: ParseIntegrationInterface | null, strictMode?: boolean | null);
37
+ /**
38
+ * Initialize the builder with default or provided components.
39
+ *
40
+ * @returns Configured factory ready to create typed accessors.
41
+ */
42
+ builder(): AccessorFactory;
43
+ /**
44
+ * Set a custom parser integration implementation.
45
+ *
46
+ * @param integration - Custom format integration to use.
47
+ * @returns New builder instance with the integration configured.
48
+ */
49
+ withParserIntegration(integration: ParseIntegrationInterface): this;
50
+ /**
51
+ * Set a custom security guard implementation.
52
+ *
53
+ * @param guard - Custom guard implementation to use.
54
+ * @returns New builder instance with the guard configured.
55
+ */
56
+ withSecurityGuard(guard: SecurityGuardInterface): this;
57
+ /**
58
+ * Set a custom security parser implementation.
59
+ *
60
+ * @param parser - Custom parser implementation to use.
61
+ * @returns New builder instance with the parser configured.
62
+ */
63
+ withSecurityParser(parser: SecurityParserInterface): this;
64
+ /**
65
+ * Set a custom path cache implementation.
66
+ *
67
+ * @param cache - Custom cache implementation to use.
68
+ * @returns New builder instance with the cache configured.
69
+ */
70
+ withPathCache(cache: PathCacheInterface): this;
71
+ /**
72
+ * Set the strict mode for all accessors created by this builder.
73
+ *
74
+ * @param strict - Whether to enable strict security validation.
75
+ * @returns New builder instance with the strict mode configured.
76
+ *
77
+ * @security Passing `false` disables all SecurityGuard and SecurityParser
78
+ * validation (key safety, payload size, depth and key-count limits).
79
+ * Only use with fully trusted, application-controlled input.
80
+ */
81
+ withStrictMode(strict: boolean): this;
82
+ }
@@ -0,0 +1,107 @@
1
+ import { SecurityGuard } from '../security/security-guard.js';
2
+ import { SecurityParser } from '../security/security-parser.js';
3
+ import { SimplePathCache } from '../cache/simple-path-cache.js';
4
+ import { DotNotationParser } from './dot-notation-parser.js';
5
+ import { AccessorFactory } from './accessor-factory.js';
6
+ import { SegmentFilterParser } from '../path-query/segment-filter-parser.js';
7
+ import { SegmentParser } from '../path-query/segment-parser.js';
8
+ import { SegmentPathResolver } from '../path-query/segment-path-resolver.js';
9
+ /**
10
+ * Builder for configuring and constructing the internal components of SafeAccess Inline.
11
+ *
12
+ * Provides an immutable builder API: each `withXxx()` method returns a new
13
+ * instance with the specified override, leaving the original unchanged.
14
+ *
15
+ * Note: PHP's equivalent uses `__callStatic`/`__call` magic to expose
16
+ * protected methods publicly. TypeScript has no equivalent mechanism, so
17
+ * builder methods are public directly and static forwarding is explicit
18
+ * in the {@link Inline} subclass.
19
+ *
20
+ * @internal
21
+ *
22
+ * @see Inline
23
+ */
24
+ export class InlineBuilderAccessor {
25
+ _guard;
26
+ _secParser;
27
+ _pathCache;
28
+ _integration;
29
+ _strictMode;
30
+ /**
31
+ * Create a builder with optional component overrides.
32
+ *
33
+ * @param guard - Custom security guard, or undefined for default.
34
+ * @param secParser - Custom security parser, or undefined for default.
35
+ * @param pathCache - Custom path cache, or null/undefined for default.
36
+ * @param integration - Custom format integration, or null/undefined for none.
37
+ * @param strictMode - Strict mode override, or null/undefined for accessor default.
38
+ */
39
+ constructor(guard, secParser, pathCache, integration, strictMode) {
40
+ this._guard = guard ?? new SecurityGuard();
41
+ this._secParser = secParser ?? new SecurityParser();
42
+ this._pathCache = pathCache ?? null;
43
+ this._integration = integration ?? null;
44
+ this._strictMode = strictMode ?? null;
45
+ }
46
+ /**
47
+ * Initialize the builder with default or provided components.
48
+ *
49
+ * @returns Configured factory ready to create typed accessors.
50
+ */
51
+ builder() {
52
+ const filterParser = new SegmentFilterParser(this._guard);
53
+ const segmentParser = new SegmentParser(filterParser);
54
+ const segmentPathResolver = new SegmentPathResolver(filterParser);
55
+ const dotNotationParser = new DotNotationParser(this._guard, this._secParser, this._pathCache ?? new SimplePathCache(), segmentParser, segmentPathResolver);
56
+ return new AccessorFactory(dotNotationParser, this._integration, this._strictMode);
57
+ }
58
+ /**
59
+ * Set a custom parser integration implementation.
60
+ *
61
+ * @param integration - Custom format integration to use.
62
+ * @returns New builder instance with the integration configured.
63
+ */
64
+ withParserIntegration(integration) {
65
+ return new this.constructor(this._guard, this._secParser, this._pathCache, integration, this._strictMode);
66
+ }
67
+ /**
68
+ * Set a custom security guard implementation.
69
+ *
70
+ * @param guard - Custom guard implementation to use.
71
+ * @returns New builder instance with the guard configured.
72
+ */
73
+ withSecurityGuard(guard) {
74
+ return new this.constructor(guard, this._secParser, this._pathCache, this._integration, this._strictMode);
75
+ }
76
+ /**
77
+ * Set a custom security parser implementation.
78
+ *
79
+ * @param parser - Custom parser implementation to use.
80
+ * @returns New builder instance with the parser configured.
81
+ */
82
+ withSecurityParser(parser) {
83
+ return new this.constructor(this._guard, parser, this._pathCache, this._integration, this._strictMode);
84
+ }
85
+ /**
86
+ * Set a custom path cache implementation.
87
+ *
88
+ * @param cache - Custom cache implementation to use.
89
+ * @returns New builder instance with the cache configured.
90
+ */
91
+ withPathCache(cache) {
92
+ return new this.constructor(this._guard, this._secParser, cache, this._integration, this._strictMode);
93
+ }
94
+ /**
95
+ * Set the strict mode for all accessors created by this builder.
96
+ *
97
+ * @param strict - Whether to enable strict security validation.
98
+ * @returns New builder instance with the strict mode configured.
99
+ *
100
+ * @security Passing `false` disables all SecurityGuard and SecurityParser
101
+ * validation (key safety, payload size, depth and key-count limits).
102
+ * Only use with fully trusted, application-controlled input.
103
+ */
104
+ withStrictMode(strict) {
105
+ return new this.constructor(this._guard, this._secParser, this._pathCache, this._integration, strict);
106
+ }
107
+ }
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * Base exception for all accessor-layer errors.
3
3
  *
4
+ * @api
5
+ *
6
+ * @see InvalidFormatException Thrown on malformed input data.
7
+ * @see ParserException Thrown on parser-level operational errors.
8
+ * @see PathNotFoundException Thrown when a requested path does not exist.
9
+ * @see SecurityException Thrown on security constraint violations.
10
+ * @see ReadonlyViolationException Thrown on write attempts to a readonly accessor.
11
+ * @see UnsupportedTypeException Thrown when an unsupported format is requested.
12
+ *
4
13
  * @example
5
14
  * throw new AccessorException('Something went wrong.');
6
15
  */
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * Base exception for all accessor-layer errors.
3
3
  *
4
+ * @api
5
+ *
6
+ * @see InvalidFormatException Thrown on malformed input data.
7
+ * @see ParserException Thrown on parser-level operational errors.
8
+ * @see PathNotFoundException Thrown when a requested path does not exist.
9
+ * @see SecurityException Thrown on security constraint violations.
10
+ * @see ReadonlyViolationException Thrown on write attempts to a readonly accessor.
11
+ * @see UnsupportedTypeException Thrown when an unsupported format is requested.
12
+ *
4
13
  * @example
5
14
  * throw new AccessorException('Something went wrong.');
6
15
  */
@@ -2,6 +2,11 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when the input data cannot be parsed as the expected format.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ * @see YamlParseException Specialized subclass for YAML parsing errors.
9
+ *
5
10
  * @example
6
11
  * throw new InvalidFormatException('Expected JSON string, got number.');
7
12
  */
@@ -2,6 +2,11 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when the input data cannot be parsed as the expected format.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ * @see YamlParseException Specialized subclass for YAML parsing errors.
9
+ *
5
10
  * @example
6
11
  * throw new InvalidFormatException('Expected JSON string, got number.');
7
12
  */
@@ -2,6 +2,10 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when an underlying parser encounters a structural error.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ *
5
9
  * @example
6
10
  * throw new ParserException('Parser failed to process input.');
7
11
  */
@@ -2,6 +2,10 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when an underlying parser encounters a structural error.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ *
5
9
  * @example
6
10
  * throw new ParserException('Parser failed to process input.');
7
11
  */
@@ -2,6 +2,10 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when a dot-notation path does not exist in the data.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ *
5
9
  * @example
6
10
  * throw new PathNotFoundException("Path 'user.address.zip' not found.");
7
11
  */
@@ -2,6 +2,10 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when a dot-notation path does not exist in the data.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ *
5
9
  * @example
6
10
  * throw new PathNotFoundException("Path 'user.address.zip' not found.");
7
11
  */
@@ -2,6 +2,10 @@ import { AccessorException } from './accessor-exception.js';
2
2
  /**
3
3
  * Thrown when a write operation is attempted on a readonly accessor.
4
4
  *
5
+ * @api
6
+ *
7
+ * @see AccessorException Parent exception class.
8
+ *
5
9
  * @example
6
10
  * const accessor = Inline.fromJson('{}').readonly(true);
7
11
  * accessor.set('key', 'value'); // throws ReadonlyViolationException