@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,168 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { SimplePathCache } from '../../src/cache/simple-path-cache.js';
3
+ import { SegmentType } from '../../src/path-query/segment-type.js';
4
+ import type { Segment } from '../../src/path-query/segment-type.js';
5
+
6
+ describe(SimplePathCache.name, () => {
7
+ describe(`${SimplePathCache.name} > get`, () => {
8
+ it('returns null on cache miss', () => {
9
+ const cache = new SimplePathCache();
10
+
11
+ expect(cache.get('user.name')).toBeNull();
12
+ });
13
+
14
+ it('returns cached segments on hit', () => {
15
+ const cache = new SimplePathCache();
16
+ const segments: Segment[] = [{ type: SegmentType.Key, value: 'user' }];
17
+ cache.set('user', segments);
18
+
19
+ expect(cache.get('user')).toEqual(segments);
20
+ });
21
+
22
+ it('promotes the accessed entry to most-recently-used position', () => {
23
+ const cache = new SimplePathCache(3);
24
+
25
+ const s1: Segment[] = [{ type: SegmentType.Key, value: 'a' }];
26
+ const s2: Segment[] = [{ type: SegmentType.Key, value: 'b' }];
27
+ const s3: Segment[] = [{ type: SegmentType.Key, value: 'c' }];
28
+
29
+ cache.set('a', s1);
30
+ cache.set('b', s2);
31
+ cache.set('c', s3);
32
+
33
+ cache.get('a');
34
+
35
+ cache.set('d', [{ type: SegmentType.Key, value: 'd' }]);
36
+
37
+ expect(cache.has('a')).toBe(true);
38
+ expect(cache.has('b')).toBe(false);
39
+ expect(cache.has('c')).toBe(true);
40
+ expect(cache.has('d')).toBe(true);
41
+ });
42
+ });
43
+
44
+ describe(`${SimplePathCache.name} > set`, () => {
45
+ it('stores segments and makes them retrievable', () => {
46
+ const cache = new SimplePathCache();
47
+ const segments: Segment[] = [{ type: SegmentType.Key, value: 'name' }];
48
+
49
+ cache.set('name', segments);
50
+
51
+ expect(cache.get('name')).toEqual(segments);
52
+ });
53
+
54
+ it('evicts the least-recently-used entry when capacity is reached', () => {
55
+ const cache = new SimplePathCache(2);
56
+
57
+ cache.set('a', [{ type: SegmentType.Key, value: 'a' }]);
58
+ cache.set('b', [{ type: SegmentType.Key, value: 'b' }]);
59
+
60
+ cache.set('c', [{ type: SegmentType.Key, value: 'c' }]);
61
+
62
+ expect(cache.has('a')).toBe(false);
63
+ expect(cache.has('b')).toBe(true);
64
+ expect(cache.has('c')).toBe(true);
65
+ });
66
+
67
+ it('overwrites an existing entry without growing the cache beyond maxSize', () => {
68
+ const cache = new SimplePathCache(2);
69
+ const s1: Segment[] = [{ type: SegmentType.Key, value: 'v1' }];
70
+ const s2: Segment[] = [{ type: SegmentType.Key, value: 'v2' }];
71
+
72
+ cache.set('a', s1);
73
+ cache.set('a', s2);
74
+
75
+ expect(cache.get('a')).toEqual(s2);
76
+ });
77
+
78
+ it('handles maxSize of zero without error', () => {
79
+ const cache = new SimplePathCache(0);
80
+ const segments: Segment[] = [{ type: SegmentType.Key, value: 'a' }];
81
+
82
+ cache.set('a', segments);
83
+
84
+ expect(cache.has('a')).toBe(true);
85
+ });
86
+
87
+ it('evicts correctly with maxSize of 1', () => {
88
+ const cache = new SimplePathCache(1);
89
+
90
+ cache.set('a', [{ type: SegmentType.Key, value: 'a' }]);
91
+ cache.set('b', [{ type: SegmentType.Key, value: 'b' }]);
92
+
93
+ expect(cache.has('a')).toBe(false);
94
+ expect(cache.has('b')).toBe(true);
95
+ });
96
+ });
97
+
98
+ describe(`${SimplePathCache.name} > has`, () => {
99
+ it('returns true for a cached path', () => {
100
+ const cache = new SimplePathCache();
101
+ cache.set('foo', []);
102
+
103
+ expect(cache.has('foo')).toBe(true);
104
+ });
105
+
106
+ it('returns false for a path not in cache', () => {
107
+ const cache = new SimplePathCache();
108
+
109
+ expect(cache.has('missing')).toBe(false);
110
+ });
111
+ });
112
+
113
+ describe(`${SimplePathCache.name} > clear`, () => {
114
+ it('removes all entries from the cache', () => {
115
+ const cache = new SimplePathCache();
116
+ cache.set('a', []);
117
+ cache.set('b', []);
118
+
119
+ cache.clear();
120
+
121
+ expect(cache.has('a')).toBe(false);
122
+ expect(cache.has('b')).toBe(false);
123
+ });
124
+
125
+ it('returns the same instance for fluent chaining', () => {
126
+ const cache = new SimplePathCache();
127
+
128
+ expect(cache.clear()).toBe(cache);
129
+ });
130
+ });
131
+
132
+ describe(`${SimplePathCache.name} > mutation boundary tests`, () => {
133
+ it('does not evict when cache has remaining capacity', () => {
134
+ const cache = new SimplePathCache(3);
135
+ cache.set('a', [{ type: SegmentType.Key, value: 'a' }]);
136
+ cache.set('b', [{ type: SegmentType.Key, value: 'b' }]);
137
+
138
+ expect(cache.has('a')).toBe(true);
139
+ expect(cache.has('b')).toBe(true);
140
+ });
141
+
142
+ it('guards firstKey !== undefined when evicting from a non-empty cache', () => {
143
+ const cache = new SimplePathCache(1);
144
+ cache.set('a', [{ type: SegmentType.Key, value: 'a' }]);
145
+
146
+ expect(cache.has('a')).toBe(true);
147
+
148
+ cache.set('b', [{ type: SegmentType.Key, value: 'b' }]);
149
+
150
+ expect(cache.has('a')).toBe(false);
151
+ expect(cache.has('b')).toBe(true);
152
+ expect(cache.get('a')).toBeNull();
153
+ expect(cache.get('b')).toEqual([{ type: SegmentType.Key, value: 'b' }]);
154
+ });
155
+
156
+ it('does not crash when set is called on max-capacity cache and firstKey exists', () => {
157
+ const cache = new SimplePathCache(2);
158
+ cache.set('x', []);
159
+ cache.set('y', []);
160
+
161
+ cache.set('z', [{ type: SegmentType.Key, value: 'z' }]);
162
+
163
+ expect(cache.has('x')).toBe(false);
164
+ expect(cache.has('y')).toBe(true);
165
+ expect(cache.has('z')).toBe(true);
166
+ });
167
+ });
168
+ });
@@ -0,0 +1,157 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { AccessorFactory } from '../../src/core/accessor-factory.js';
3
+ import { DotNotationParser } from '../../src/core/dot-notation-parser.js';
4
+ import { SecurityGuard } from '../../src/security/security-guard.js';
5
+ import { SecurityParser } from '../../src/security/security-parser.js';
6
+ import { ArrayAccessor } from '../../src/accessors/formats/array-accessor.js';
7
+ import { ObjectAccessor } from '../../src/accessors/formats/object-accessor.js';
8
+ import { JsonAccessor } from '../../src/accessors/formats/json-accessor.js';
9
+ import { XmlAccessor } from '../../src/accessors/formats/xml-accessor.js';
10
+ import { YamlAccessor } from '../../src/accessors/formats/yaml-accessor.js';
11
+ import { IniAccessor } from '../../src/accessors/formats/ini-accessor.js';
12
+ import { EnvAccessor } from '../../src/accessors/formats/env-accessor.js';
13
+ import { NdjsonAccessor } from '../../src/accessors/formats/ndjson-accessor.js';
14
+ import { AnyAccessor } from '../../src/accessors/formats/any-accessor.js';
15
+ import { InvalidFormatException } from '../../src/exceptions/invalid-format-exception.js';
16
+ import { FakeParseIntegration } from '../mocks/fake-parse-integration.js';
17
+
18
+ function makeParser(): DotNotationParser {
19
+ return new DotNotationParser(new SecurityGuard(), new SecurityParser());
20
+ }
21
+
22
+ describe(AccessorFactory.name, () => {
23
+ describe('getParser', () => {
24
+ it('returns the parser passed to the constructor', () => {
25
+ const parser = makeParser();
26
+ const factory = new AccessorFactory(parser);
27
+ expect(factory.getParser()).toBe(parser);
28
+ });
29
+ });
30
+
31
+ describe('array', () => {
32
+ it('creates an ArrayAccessor from object data', () => {
33
+ const factory = new AccessorFactory(makeParser());
34
+ const accessor = factory.array({ key: 'value' });
35
+ expect(accessor).toBeInstanceOf(ArrayAccessor);
36
+ expect(accessor.get('key')).toBe('value');
37
+ });
38
+
39
+ it('creates an ArrayAccessor from array data', () => {
40
+ const factory = new AccessorFactory(makeParser());
41
+ const accessor = factory.array([1, 2, 3]);
42
+ expect(accessor).toBeInstanceOf(ArrayAccessor);
43
+ expect(accessor.get('0')).toBe(1);
44
+ });
45
+ });
46
+
47
+ describe('object', () => {
48
+ it('creates an ObjectAccessor from object data', () => {
49
+ const factory = new AccessorFactory(makeParser());
50
+ const accessor = factory.object({ name: 'Alice' });
51
+ expect(accessor).toBeInstanceOf(ObjectAccessor);
52
+ expect(accessor.get('name')).toBe('Alice');
53
+ });
54
+ });
55
+
56
+ describe('json', () => {
57
+ it('creates a JsonAccessor from JSON string', () => {
58
+ const factory = new AccessorFactory(makeParser());
59
+ const accessor = factory.json('{"key":"value"}');
60
+ expect(accessor).toBeInstanceOf(JsonAccessor);
61
+ expect(accessor.get('key')).toBe('value');
62
+ });
63
+ });
64
+
65
+ describe('xml', () => {
66
+ it('creates an XmlAccessor from XML string', () => {
67
+ const factory = new AccessorFactory(makeParser());
68
+ const accessor = factory.xml('<root><key>value</key></root>');
69
+ expect(accessor).toBeInstanceOf(XmlAccessor);
70
+ expect(accessor.get('key')).toBe('value');
71
+ });
72
+ });
73
+
74
+ describe('yaml', () => {
75
+ it('creates a YamlAccessor from YAML string', () => {
76
+ const factory = new AccessorFactory(makeParser());
77
+ const accessor = factory.yaml('key: value');
78
+ expect(accessor).toBeInstanceOf(YamlAccessor);
79
+ expect(accessor.get('key')).toBe('value');
80
+ });
81
+ });
82
+
83
+ describe('ini', () => {
84
+ it('creates an IniAccessor from INI string', () => {
85
+ const factory = new AccessorFactory(makeParser());
86
+ const accessor = factory.ini('[section]\nkey=value');
87
+ expect(accessor).toBeInstanceOf(IniAccessor);
88
+ expect(accessor.get('section.key')).toBe('value');
89
+ });
90
+ });
91
+
92
+ describe('env', () => {
93
+ it('creates an EnvAccessor from dotenv string', () => {
94
+ const factory = new AccessorFactory(makeParser());
95
+ const accessor = factory.env('APP_NAME=MyApp');
96
+ expect(accessor).toBeInstanceOf(EnvAccessor);
97
+ expect(accessor.get('APP_NAME')).toBe('MyApp');
98
+ });
99
+ });
100
+
101
+ describe('ndjson', () => {
102
+ it('creates an NdjsonAccessor from NDJSON string', () => {
103
+ const factory = new AccessorFactory(makeParser());
104
+ const accessor = factory.ndjson('{"id":1}\n{"id":2}');
105
+ expect(accessor).toBeInstanceOf(NdjsonAccessor);
106
+ expect(accessor.get('0.id')).toBe(1);
107
+ });
108
+ });
109
+
110
+ describe('any', () => {
111
+ it('creates an AnyAccessor with a provided integration', () => {
112
+ const integration = new FakeParseIntegration(true, { foo: 'bar' });
113
+ const factory = new AccessorFactory(makeParser(), integration);
114
+ const accessor = factory.any('raw');
115
+ expect(accessor).toBeInstanceOf(AnyAccessor);
116
+ expect(accessor.get('foo')).toBe('bar');
117
+ });
118
+
119
+ it('uses inline integration over default', () => {
120
+ const defaultInt = new FakeParseIntegration(true, { from: 'default' });
121
+ const overrideInt = new FakeParseIntegration(true, { from: 'override' });
122
+ const factory = new AccessorFactory(makeParser(), defaultInt);
123
+ const accessor = factory.any('raw', overrideInt);
124
+ expect(accessor.get('from')).toBe('override');
125
+ });
126
+
127
+ it('throws InvalidFormatException when no integration is available', () => {
128
+ const factory = new AccessorFactory(makeParser());
129
+ expect(() => factory.any('data')).toThrow(InvalidFormatException);
130
+ });
131
+
132
+ it('throws with guidance message when no integration is set', () => {
133
+ const factory = new AccessorFactory(makeParser());
134
+ expect(() => factory.any('data')).toThrow(
135
+ 'AnyAccessor requires a ParseIntegrationInterface',
136
+ );
137
+ });
138
+
139
+ it('includes withParserIntegration guidance in the error message', () => {
140
+ const factory = new AccessorFactory(makeParser());
141
+ expect(() => factory.any('data')).toThrow(/withParserIntegration/);
142
+ });
143
+ });
144
+
145
+ describe('strict mode', () => {
146
+ it('applies strict mode to created accessors when configured', () => {
147
+ const factory = new AccessorFactory(makeParser(), null, false);
148
+ const accessor = factory.json('{"__proto__":"ok"}');
149
+ expect(accessor.get('__proto__')).toBe('ok');
150
+ });
151
+
152
+ it('does not apply strict mode when null', () => {
153
+ const factory = new AccessorFactory(makeParser(), null, null);
154
+ expect(() => factory.json('{"__proto__":"fail"}')).toThrow();
155
+ });
156
+ });
157
+ });