@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.
- package/.gitattributes +1 -1
- package/CHANGELOG.md +10 -5
- package/LICENSE +1 -1
- package/README.md +56 -14
- package/dist/accessors/abstract-accessor.d.ts +22 -10
- package/dist/accessors/abstract-accessor.js +21 -8
- package/dist/accessors/abstract-integration-accessor.d.ts +22 -0
- package/dist/accessors/abstract-integration-accessor.js +23 -0
- package/dist/accessors/formats/any-accessor.d.ts +10 -8
- package/dist/accessors/formats/any-accessor.js +9 -8
- package/dist/accessors/formats/array-accessor.d.ts +2 -0
- package/dist/accessors/formats/array-accessor.js +2 -0
- package/dist/accessors/formats/env-accessor.d.ts +2 -0
- package/dist/accessors/formats/env-accessor.js +2 -0
- package/dist/accessors/formats/ini-accessor.d.ts +2 -0
- package/dist/accessors/formats/ini-accessor.js +2 -0
- package/dist/accessors/formats/json-accessor.d.ts +2 -0
- package/dist/accessors/formats/json-accessor.js +2 -0
- package/dist/accessors/formats/ndjson-accessor.d.ts +2 -0
- package/dist/accessors/formats/ndjson-accessor.js +2 -0
- package/dist/accessors/formats/object-accessor.d.ts +2 -0
- package/dist/accessors/formats/object-accessor.js +2 -0
- package/dist/accessors/formats/xml-accessor.d.ts +2 -0
- package/dist/accessors/formats/xml-accessor.js +2 -0
- package/dist/accessors/formats/yaml-accessor.d.ts +3 -1
- package/dist/accessors/formats/yaml-accessor.js +4 -2
- package/dist/cache/simple-path-cache.d.ts +51 -0
- package/dist/cache/simple-path-cache.js +72 -0
- package/dist/contracts/accessors-interface.d.ts +2 -0
- package/dist/contracts/factory-accessors-interface.d.ts +2 -0
- package/dist/contracts/filter-evaluator-interface.d.ts +28 -0
- package/dist/contracts/filter-evaluator-interface.js +1 -0
- package/dist/contracts/parse-integration-interface.d.ts +2 -0
- package/dist/contracts/parser-interface.d.ts +92 -0
- package/dist/contracts/parser-interface.js +1 -0
- package/dist/contracts/path-cache-interface.d.ts +7 -6
- package/dist/contracts/readable-accessors-interface.d.ts +11 -6
- package/dist/contracts/security-guard-interface.d.ts +2 -0
- package/dist/contracts/security-parser-interface.d.ts +2 -0
- package/dist/contracts/validatable-parser-interface.d.ts +59 -0
- package/dist/contracts/validatable-parser-interface.js +1 -0
- package/dist/contracts/writable-accessors-interface.d.ts +5 -0
- package/dist/core/accessor-factory.d.ts +124 -0
- package/dist/core/accessor-factory.js +157 -0
- package/dist/core/dot-notation-parser.d.ts +34 -5
- package/dist/core/dot-notation-parser.js +51 -10
- package/dist/core/inline-builder-accessor.d.ts +82 -0
- package/dist/core/inline-builder-accessor.js +107 -0
- package/dist/exceptions/accessor-exception.d.ts +9 -0
- package/dist/exceptions/accessor-exception.js +9 -0
- package/dist/exceptions/invalid-format-exception.d.ts +5 -0
- package/dist/exceptions/invalid-format-exception.js +5 -0
- package/dist/exceptions/parser-exception.d.ts +4 -0
- package/dist/exceptions/parser-exception.js +4 -0
- package/dist/exceptions/path-not-found-exception.d.ts +4 -0
- package/dist/exceptions/path-not-found-exception.js +4 -0
- package/dist/exceptions/readonly-violation-exception.d.ts +4 -0
- package/dist/exceptions/readonly-violation-exception.js +4 -0
- package/dist/exceptions/security-exception.d.ts +6 -0
- package/dist/exceptions/security-exception.js +6 -0
- package/dist/exceptions/unsupported-type-exception.d.ts +4 -0
- package/dist/exceptions/unsupported-type-exception.js +4 -0
- package/dist/exceptions/yaml-parse-exception.d.ts +4 -0
- package/dist/exceptions/yaml-parse-exception.js +4 -0
- package/dist/index.js +2 -1
- package/dist/inline.d.ts +22 -56
- package/dist/inline.js +39 -111
- package/dist/parser/xml-parser.js +23 -10
- package/dist/parser/yaml-parser.d.ts +54 -7
- package/dist/parser/yaml-parser.js +268 -51
- package/dist/path-query/segment-filter-parser.d.ts +142 -0
- package/dist/path-query/segment-filter-parser.js +384 -0
- package/dist/path-query/segment-parser.d.ts +98 -0
- package/dist/path-query/segment-parser.js +283 -0
- package/dist/path-query/segment-path-resolver.d.ts +149 -0
- package/dist/path-query/segment-path-resolver.js +351 -0
- package/dist/path-query/segment-type.d.ts +85 -0
- package/dist/path-query/segment-type.js +35 -0
- package/dist/security/forbidden-keys.d.ts +2 -2
- package/dist/security/forbidden-keys.js +5 -5
- package/dist/security/security-guard.d.ts +3 -1
- package/dist/security/security-guard.js +5 -2
- package/dist/security/security-parser.d.ts +10 -1
- package/dist/security/security-parser.js +10 -1
- package/dist/type-format.d.ts +2 -0
- package/dist/type-format.js +2 -0
- package/package.json +11 -3
- package/src/accessors/abstract-accessor.ts +23 -19
- package/src/accessors/abstract-integration-accessor.ts +27 -0
- package/src/accessors/formats/any-accessor.ts +11 -11
- package/src/accessors/formats/array-accessor.ts +2 -0
- package/src/accessors/formats/env-accessor.ts +2 -0
- package/src/accessors/formats/ini-accessor.ts +2 -0
- package/src/accessors/formats/json-accessor.ts +2 -0
- package/src/accessors/formats/ndjson-accessor.ts +2 -0
- package/src/accessors/formats/object-accessor.ts +2 -0
- package/src/accessors/formats/xml-accessor.ts +2 -0
- package/src/accessors/formats/yaml-accessor.ts +4 -2
- package/src/cache/simple-path-cache.ts +77 -0
- package/src/contracts/accessors-interface.ts +2 -0
- package/src/contracts/factory-accessors-interface.ts +2 -0
- package/src/contracts/filter-evaluator-interface.ts +30 -0
- package/src/contracts/parse-integration-interface.ts +2 -0
- package/src/contracts/parser-interface.ts +114 -0
- package/src/contracts/path-cache-interface.ts +8 -6
- package/src/contracts/readable-accessors-interface.ts +11 -6
- package/src/contracts/security-guard-interface.ts +2 -0
- package/src/contracts/security-parser-interface.ts +2 -0
- package/src/contracts/validatable-parser-interface.ts +64 -0
- package/src/contracts/writable-accessors-interface.ts +5 -0
- package/src/core/accessor-factory.ts +173 -0
- package/src/core/dot-notation-parser.ts +74 -11
- package/src/core/inline-builder-accessor.ts +163 -0
- package/src/exceptions/accessor-exception.ts +9 -0
- package/src/exceptions/invalid-format-exception.ts +5 -0
- package/src/exceptions/parser-exception.ts +4 -0
- package/src/exceptions/path-not-found-exception.ts +4 -0
- package/src/exceptions/readonly-violation-exception.ts +4 -0
- package/src/exceptions/security-exception.ts +6 -0
- package/src/exceptions/unsupported-type-exception.ts +4 -0
- package/src/exceptions/yaml-parse-exception.ts +4 -0
- package/src/index.ts +3 -1
- package/src/inline.ts +42 -120
- package/src/parser/xml-parser.ts +31 -10
- package/src/parser/yaml-parser.ts +310 -45
- package/src/path-query/segment-filter-parser.ts +444 -0
- package/src/path-query/segment-parser.ts +321 -0
- package/src/path-query/segment-path-resolver.ts +521 -0
- package/src/path-query/segment-type.ts +82 -0
- package/src/security/forbidden-keys.ts +5 -5
- package/src/security/security-guard.ts +7 -2
- package/src/security/security-parser.ts +18 -3
- package/src/type-format.ts +2 -0
- package/stryker.config.json +8 -10
- package/tests/accessors/abstract-accessor.test.ts +217 -0
- package/tests/accessors/abstract-integration-accessor.test.ts +37 -0
- package/tests/accessors/formats/any-accessor.test.ts +57 -0
- package/tests/accessors/formats/array-accessor.test.ts +42 -0
- package/tests/accessors/formats/env-accessor.test.ts +103 -0
- package/tests/accessors/formats/ini-accessor.test.ts +186 -0
- package/tests/accessors/{json-accessor.test.ts → formats/json-accessor.test.ts} +6 -6
- package/tests/accessors/formats/ndjson-accessor.test.ts +49 -0
- package/tests/accessors/formats/object-accessor.test.ts +172 -0
- package/tests/accessors/formats/xml-accessor.test.ts +162 -0
- package/tests/accessors/formats/yaml-accessor.test.ts +36 -0
- package/tests/cache/simple-path-cache.test.ts +168 -0
- package/tests/core/accessor-factory.test.ts +157 -0
- package/tests/core/dot-notation-parser-edge-cases.test.ts +415 -0
- package/tests/core/dot-notation-parser.test.ts +0 -288
- package/tests/core/inline-builder-accessor.test.ts +114 -0
- package/tests/exceptions/accessor-exception.test.ts +28 -0
- package/tests/exceptions/invalid-format-exception.test.ts +31 -0
- package/tests/exceptions/path-not-found-exception.test.ts +33 -0
- package/tests/exceptions/readonly-violation-exception.test.ts +35 -0
- package/tests/exceptions/security-exception.test.ts +33 -0
- package/tests/exceptions/unsupported-type-exception.test.ts +33 -0
- package/tests/exceptions/yaml-parse-exception.test.ts +38 -0
- package/tests/mocks/fake-path-cache.ts +4 -3
- package/tests/parity-from.test.ts +118 -0
- package/tests/parity.test.ts +227 -10
- package/tests/parser/xml-parser-mutations.test.ts +579 -0
- package/tests/parser/xml-parser-scanner.test.ts +332 -0
- package/tests/parser/xml-parser.test.ts +10 -334
- package/tests/parser/yaml-parser-mutations.test.ts +750 -0
- package/tests/parser/yaml-parser.test.ts +844 -18
- package/tests/path-query/segment-filter-parser-mutations.test.ts +735 -0
- package/tests/path-query/segment-filter-parser.test.ts +1091 -0
- package/tests/path-query/segment-parser-mutations.test.ts +539 -0
- package/tests/path-query/segment-parser.test.ts +606 -0
- package/tests/path-query/segment-path-resolver-mutations.test.ts +626 -0
- package/tests/path-query/segment-path-resolver.test.ts +1009 -0
- package/tests/security/security-guard-advanced.test.ts +413 -0
- package/tests/security/security-guard-forbidden-keys.test.ts +87 -0
- package/tests/security/security-guard.test.ts +3 -484
- package/tests/security/security-parser.test.ts +18 -14
- package/vitest.config.ts +3 -3
- package/benchmarks/get.bench.ts +0 -26
- package/benchmarks/parse.bench.ts +0 -41
- package/tests/accessors/accessors.test.ts +0 -1017
package/src/index.ts
CHANGED
|
@@ -19,7 +19,9 @@ export { NdjsonAccessor } from './accessors/formats/ndjson-accessor.js';
|
|
|
19
19
|
export { AnyAccessor } from './accessors/formats/any-accessor.js';
|
|
20
20
|
|
|
21
21
|
// Core
|
|
22
|
-
// NOTE: DotNotationParser is intentionally not exported
|
|
22
|
+
// NOTE: DotNotationParser is intentionally not exported - it is an internal component.
|
|
23
|
+
|
|
24
|
+
// Cache - SimplePathCache is @internal; consumers must use PathCacheInterface
|
|
23
25
|
|
|
24
26
|
// Security
|
|
25
27
|
export { SecurityGuard } from './security/security-guard.js';
|
package/src/inline.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { TypeFormat } from './type-format.js';
|
|
2
|
-
import { DotNotationParser } from './core/dot-notation-parser.js';
|
|
3
|
-
import { SecurityGuard } from './security/security-guard.js';
|
|
4
|
-
import { SecurityParser } from './security/security-parser.js';
|
|
5
2
|
import type { SecurityGuardInterface } from './contracts/security-guard-interface.js';
|
|
6
3
|
import type { SecurityParserInterface } from './contracts/security-parser-interface.js';
|
|
7
4
|
import type { AccessorsInterface } from './contracts/accessors-interface.js';
|
|
8
5
|
import type { ParseIntegrationInterface } from './contracts/parse-integration-interface.js';
|
|
9
6
|
import type { PathCacheInterface } from './contracts/path-cache-interface.js';
|
|
7
|
+
import type { ValidatableParserInterface } from './contracts/validatable-parser-interface.js';
|
|
10
8
|
import { AbstractAccessor } from './accessors/abstract-accessor.js';
|
|
11
9
|
import { ArrayAccessor } from './accessors/formats/array-accessor.js';
|
|
12
10
|
import { ObjectAccessor } from './accessors/formats/object-accessor.js';
|
|
@@ -18,7 +16,7 @@ import { EnvAccessor } from './accessors/formats/env-accessor.js';
|
|
|
18
16
|
import { NdjsonAccessor } from './accessors/formats/ndjson-accessor.js';
|
|
19
17
|
import { AnyAccessor } from './accessors/formats/any-accessor.js';
|
|
20
18
|
import { UnsupportedTypeException } from './exceptions/unsupported-type-exception.js';
|
|
21
|
-
import {
|
|
19
|
+
import { InlineBuilderAccessor } from './core/inline-builder-accessor.js';
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
22
|
* Facade for creating typed data accessors fluently.
|
|
@@ -27,6 +25,8 @@ import { InvalidFormatException } from './exceptions/invalid-format-exception.js
|
|
|
27
25
|
* Use the builder methods (`withSecurityGuard`, `withSecurityParser`) to
|
|
28
26
|
* customize the security configuration before creating an accessor.
|
|
29
27
|
*
|
|
28
|
+
* @api
|
|
29
|
+
*
|
|
30
30
|
* @example
|
|
31
31
|
* const accessor = Inline.fromJson('{"name":"Alice"}');
|
|
32
32
|
* accessor.get('name'); // 'Alice'
|
|
@@ -35,100 +35,12 @@ import { InvalidFormatException } from './exceptions/invalid-format-exception.js
|
|
|
35
35
|
* const accessor = Inline.from(TypeFormat.Yaml, 'name: Alice');
|
|
36
36
|
* accessor.get('name'); // 'Alice'
|
|
37
37
|
*/
|
|
38
|
-
export class Inline {
|
|
39
|
-
private readonly guard: SecurityGuardInterface;
|
|
40
|
-
private readonly secParser: SecurityParserInterface;
|
|
41
|
-
private readonly pathCache: PathCacheInterface | null;
|
|
42
|
-
private readonly integration: ParseIntegrationInterface | null;
|
|
43
|
-
private readonly strictMode: boolean | null;
|
|
44
|
-
|
|
45
|
-
private constructor(
|
|
46
|
-
guard: SecurityGuardInterface,
|
|
47
|
-
secParser: SecurityParserInterface,
|
|
48
|
-
pathCache: PathCacheInterface | null = null,
|
|
49
|
-
integration: ParseIntegrationInterface | null = null,
|
|
50
|
-
strictMode: boolean | null = null,
|
|
51
|
-
) {
|
|
52
|
-
this.guard = guard;
|
|
53
|
-
this.secParser = secParser;
|
|
54
|
-
this.pathCache = pathCache;
|
|
55
|
-
this.integration = integration;
|
|
56
|
-
this.strictMode = strictMode;
|
|
57
|
-
}
|
|
58
|
-
|
|
38
|
+
export class Inline extends InlineBuilderAccessor {
|
|
59
39
|
private static defaultInstance(): Inline {
|
|
60
|
-
return new Inline(
|
|
40
|
+
return new Inline();
|
|
61
41
|
}
|
|
62
42
|
|
|
63
|
-
|
|
64
|
-
return new DotNotationParser(this.guard, this.secParser, this.pathCache ?? undefined);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Apply configured strict mode to a new accessor before hydration.
|
|
69
|
-
*
|
|
70
|
-
* @param accessor - Unhydrated accessor instance.
|
|
71
|
-
* @returns Same accessor with strict mode applied if configured.
|
|
72
|
-
*/
|
|
73
|
-
private prepare<T extends AbstractAccessor>(accessor: T): T {
|
|
74
|
-
if (this.strictMode !== null) {
|
|
75
|
-
return accessor.strict(this.strictMode) as T;
|
|
76
|
-
}
|
|
77
|
-
return accessor;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Return a new Inline instance with a custom SecurityGuard, preserving other settings.
|
|
82
|
-
*
|
|
83
|
-
* @param guard - Custom security guard implementation.
|
|
84
|
-
* @returns New Inline builder instance.
|
|
85
|
-
*/
|
|
86
|
-
withSecurityGuard(guard: SecurityGuardInterface): Inline {
|
|
87
|
-
return new Inline(guard, this.secParser, this.pathCache, this.integration, this.strictMode);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Return a new Inline instance with a custom SecurityParser, preserving other settings.
|
|
92
|
-
*
|
|
93
|
-
* @param parser - Custom security parser implementation.
|
|
94
|
-
* @returns New Inline builder instance.
|
|
95
|
-
*/
|
|
96
|
-
withSecurityParser(parser: SecurityParserInterface): Inline {
|
|
97
|
-
return new Inline(this.guard, parser, this.pathCache, this.integration, this.strictMode);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Return a new Inline instance with a custom path cache, preserving other settings.
|
|
102
|
-
*
|
|
103
|
-
* @param cache - Custom path cache implementation.
|
|
104
|
-
* @returns New Inline builder instance.
|
|
105
|
-
*/
|
|
106
|
-
withPathCache(cache: PathCacheInterface): Inline {
|
|
107
|
-
return new Inline(this.guard, this.secParser, cache, this.integration, this.strictMode);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Return a new Inline instance with a custom parser integration, preserving other settings.
|
|
112
|
-
*
|
|
113
|
-
* @param integration - Custom format integration implementation.
|
|
114
|
-
* @returns New Inline builder instance.
|
|
115
|
-
*/
|
|
116
|
-
withParserIntegration(integration: ParseIntegrationInterface): Inline {
|
|
117
|
-
return new Inline(this.guard, this.secParser, this.pathCache, integration, this.strictMode);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Return a new Inline instance with the given strict mode, preserving other settings.
|
|
122
|
-
*
|
|
123
|
-
* @param strict - Whether to enable strict security validation.
|
|
124
|
-
* @returns New Inline builder instance.
|
|
125
|
-
*
|
|
126
|
-
* @security Passing `false` disables all SecurityGuard and SecurityParser
|
|
127
|
-
* validation. Only use with fully trusted, application-controlled input.
|
|
128
|
-
*/
|
|
129
|
-
withStrictMode(strict: boolean): Inline {
|
|
130
|
-
return new Inline(this.guard, this.secParser, this.pathCache, this.integration, strict);
|
|
131
|
-
}
|
|
43
|
+
// ── Instance factory methods ──────────────────────────────────────
|
|
132
44
|
|
|
133
45
|
/**
|
|
134
46
|
* Return a new Inline instance with a custom SecurityGuard.
|
|
@@ -140,7 +52,7 @@ export class Inline {
|
|
|
140
52
|
* Inline.withSecurityGuard(new SecurityGuard(10, ['extraKey'])).fromJson('{}');
|
|
141
53
|
*/
|
|
142
54
|
static withSecurityGuard(guard: SecurityGuardInterface): Inline {
|
|
143
|
-
return new Inline(guard
|
|
55
|
+
return new Inline(guard);
|
|
144
56
|
}
|
|
145
57
|
|
|
146
58
|
/**
|
|
@@ -153,7 +65,7 @@ export class Inline {
|
|
|
153
65
|
* Inline.withSecurityParser(new SecurityParser({ maxDepth: 10 })).fromJson('{}');
|
|
154
66
|
*/
|
|
155
67
|
static withSecurityParser(parser: SecurityParserInterface): Inline {
|
|
156
|
-
return new Inline(
|
|
68
|
+
return new Inline(undefined, parser);
|
|
157
69
|
}
|
|
158
70
|
|
|
159
71
|
/**
|
|
@@ -167,7 +79,7 @@ export class Inline {
|
|
|
167
79
|
* Inline.withPathCache(cache).fromJson('{"key":"value"}');
|
|
168
80
|
*/
|
|
169
81
|
static withPathCache(cache: PathCacheInterface): Inline {
|
|
170
|
-
return new Inline(
|
|
82
|
+
return new Inline(undefined, undefined, cache);
|
|
171
83
|
}
|
|
172
84
|
|
|
173
85
|
/**
|
|
@@ -180,7 +92,7 @@ export class Inline {
|
|
|
180
92
|
* Inline.withParserIntegration(new MyCsvIntegration()).fromAny(csvString);
|
|
181
93
|
*/
|
|
182
94
|
static withParserIntegration(integration: ParseIntegrationInterface): Inline {
|
|
183
|
-
return new Inline(
|
|
95
|
+
return new Inline(undefined, undefined, null, integration);
|
|
184
96
|
}
|
|
185
97
|
|
|
186
98
|
/**
|
|
@@ -196,7 +108,7 @@ export class Inline {
|
|
|
196
108
|
* Inline.withStrictMode(false).fromJson(hugePayload).get('key');
|
|
197
109
|
*/
|
|
198
110
|
static withStrictMode(strict: boolean): Inline {
|
|
199
|
-
return new Inline(
|
|
111
|
+
return new Inline(undefined, undefined, null, null, strict);
|
|
200
112
|
}
|
|
201
113
|
|
|
202
114
|
/**
|
|
@@ -210,7 +122,7 @@ export class Inline {
|
|
|
210
122
|
* inline.fromArray({ name: 'Alice' }).get('name'); // 'Alice'
|
|
211
123
|
*/
|
|
212
124
|
fromArray(data: Record<string, unknown> | unknown[]): ArrayAccessor {
|
|
213
|
-
return this.
|
|
125
|
+
return this.builder().array(data);
|
|
214
126
|
}
|
|
215
127
|
|
|
216
128
|
/**
|
|
@@ -224,7 +136,7 @@ export class Inline {
|
|
|
224
136
|
* inline.fromObject({ user: { name: 'Alice' } }).get('user.name');
|
|
225
137
|
*/
|
|
226
138
|
fromObject(data: object): ObjectAccessor {
|
|
227
|
-
return this.
|
|
139
|
+
return this.builder().object(data);
|
|
228
140
|
}
|
|
229
141
|
|
|
230
142
|
/**
|
|
@@ -239,12 +151,15 @@ export class Inline {
|
|
|
239
151
|
* inline.fromJson('{"key":"value"}').get('key'); // 'value'
|
|
240
152
|
*/
|
|
241
153
|
fromJson(data: string): JsonAccessor {
|
|
242
|
-
return this.
|
|
154
|
+
return this.builder().json(data);
|
|
243
155
|
}
|
|
244
156
|
|
|
245
157
|
/**
|
|
246
158
|
* Create an XmlAccessor from an XML string.
|
|
247
159
|
*
|
|
160
|
+
* Note: The PHP equivalent also accepts `\SimpleXMLElement`; JS only
|
|
161
|
+
* accepts raw XML strings (no pre-parsed equivalent exists in JS).
|
|
162
|
+
*
|
|
248
163
|
* @param data - Raw XML string.
|
|
249
164
|
* @returns Populated XmlAccessor instance.
|
|
250
165
|
* @throws {InvalidFormatException} When the XML is malformed.
|
|
@@ -254,7 +169,7 @@ export class Inline {
|
|
|
254
169
|
* inline.fromXml('<root><key>value</key></root>').get('key');
|
|
255
170
|
*/
|
|
256
171
|
fromXml(data: string): XmlAccessor {
|
|
257
|
-
return this.
|
|
172
|
+
return this.builder().xml(data);
|
|
258
173
|
}
|
|
259
174
|
|
|
260
175
|
/**
|
|
@@ -269,7 +184,7 @@ export class Inline {
|
|
|
269
184
|
* inline.fromYaml('name: Alice').get('name'); // 'Alice'
|
|
270
185
|
*/
|
|
271
186
|
fromYaml(data: string): YamlAccessor {
|
|
272
|
-
return this.
|
|
187
|
+
return this.builder().yaml(data);
|
|
273
188
|
}
|
|
274
189
|
|
|
275
190
|
/**
|
|
@@ -284,7 +199,7 @@ export class Inline {
|
|
|
284
199
|
* inline.fromIni('[section]\nkey=value').get('section.key'); // 'value'
|
|
285
200
|
*/
|
|
286
201
|
fromIni(data: string): IniAccessor {
|
|
287
|
-
return this.
|
|
202
|
+
return this.builder().ini(data);
|
|
288
203
|
}
|
|
289
204
|
|
|
290
205
|
/**
|
|
@@ -298,7 +213,7 @@ export class Inline {
|
|
|
298
213
|
* inline.fromEnv('APP_NAME=MyApp').get('APP_NAME'); // 'MyApp'
|
|
299
214
|
*/
|
|
300
215
|
fromEnv(data: string): EnvAccessor {
|
|
301
|
-
return this.
|
|
216
|
+
return this.builder().env(data);
|
|
302
217
|
}
|
|
303
218
|
|
|
304
219
|
/**
|
|
@@ -313,7 +228,7 @@ export class Inline {
|
|
|
313
228
|
* inline.fromNdjson('{"id":1}\n{"id":2}').get('0.id'); // 1
|
|
314
229
|
*/
|
|
315
230
|
fromNdjson(data: string): NdjsonAccessor {
|
|
316
|
-
return this.
|
|
231
|
+
return this.builder().ndjson(data);
|
|
317
232
|
}
|
|
318
233
|
|
|
319
234
|
/**
|
|
@@ -332,18 +247,15 @@ export class Inline {
|
|
|
332
247
|
* Inline.withParserIntegration(new CsvIntegration()).fromAny(csvString);
|
|
333
248
|
*/
|
|
334
249
|
fromAny(data: unknown, integration?: ParseIntegrationInterface): AnyAccessor {
|
|
335
|
-
|
|
336
|
-
if (resolved === null) {
|
|
337
|
-
throw new InvalidFormatException(
|
|
338
|
-
'AnyAccessor requires a ParseIntegrationInterface — use Inline.withParserIntegration(integration).fromAny(data).',
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
return this.prepare(new AnyAccessor(this.makeParser(), resolved)).from(data);
|
|
250
|
+
return this.builder().any(data, integration);
|
|
342
251
|
}
|
|
343
252
|
|
|
344
253
|
/**
|
|
345
254
|
* Create a typed accessor by its constructor.
|
|
346
255
|
*
|
|
256
|
+
* Note: The PHP equivalent accepts a class-string (FQCN) instead of a
|
|
257
|
+
* constructor reference, e.g. `Inline::make(JsonAccessor::class, $data)`.
|
|
258
|
+
*
|
|
347
259
|
* @param AccessorConstructor - The accessor class to instantiate.
|
|
348
260
|
* @param data - Raw data to hydrate the accessor with.
|
|
349
261
|
* @returns Populated accessor instance.
|
|
@@ -352,12 +264,13 @@ export class Inline {
|
|
|
352
264
|
* Inline.make(JsonAccessor, '{"key":"value"}').get('key'); // 'value'
|
|
353
265
|
*/
|
|
354
266
|
make<T extends AccessorsInterface>(
|
|
355
|
-
AccessorConstructor: new (parser:
|
|
267
|
+
AccessorConstructor: new (parser: ValidatableParserInterface) => T,
|
|
356
268
|
data: unknown,
|
|
357
269
|
): T {
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
270
|
+
const factory = this.builder();
|
|
271
|
+
const accessor = new AccessorConstructor(factory.getParser());
|
|
272
|
+
if (this._strictMode !== null && accessor instanceof AbstractAccessor) {
|
|
273
|
+
return (accessor.strict(this._strictMode) as T).from(data);
|
|
361
274
|
}
|
|
362
275
|
return accessor.from(data);
|
|
363
276
|
}
|
|
@@ -371,6 +284,9 @@ export class Inline {
|
|
|
371
284
|
* @throws {InvalidFormatException} When the data is malformed for the target format.
|
|
372
285
|
* @throws {SecurityException} When security constraints are violated.
|
|
373
286
|
* @throws {UnsupportedTypeException} When the TypeFormat is not supported.
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* Inline.from(TypeFormat.Json, '{"key":"value"}').get('key'); // 'value'
|
|
374
290
|
*/
|
|
375
291
|
from(typeFormat: TypeFormat, data: unknown): AccessorsInterface {
|
|
376
292
|
switch (typeFormat) {
|
|
@@ -447,6 +363,9 @@ export class Inline {
|
|
|
447
363
|
/**
|
|
448
364
|
* Create an XmlAccessor from an XML string.
|
|
449
365
|
*
|
|
366
|
+
* Note: The PHP equivalent also accepts `\SimpleXMLElement`; JS only
|
|
367
|
+
* accepts raw XML strings (no pre-parsed equivalent exists in JS).
|
|
368
|
+
*
|
|
450
369
|
* @param data - Raw XML string.
|
|
451
370
|
* @returns Populated XmlAccessor instance.
|
|
452
371
|
* @throws {InvalidFormatException} When the XML is malformed.
|
|
@@ -554,6 +473,9 @@ export class Inline {
|
|
|
554
473
|
/**
|
|
555
474
|
* Create a typed accessor by its constructor.
|
|
556
475
|
*
|
|
476
|
+
* Note: The PHP equivalent accepts a class-string (FQCN) instead of a
|
|
477
|
+
* constructor reference, e.g. `Inline::make(JsonAccessor::class, $data)`.
|
|
478
|
+
*
|
|
557
479
|
* @param AccessorConstructor - The accessor class to instantiate.
|
|
558
480
|
* @param data - Raw data to hydrate the accessor with.
|
|
559
481
|
* @returns Populated accessor instance.
|
|
@@ -562,7 +484,7 @@ export class Inline {
|
|
|
562
484
|
* Inline.make(JsonAccessor, '{"key":"value"}').get('key'); // 'value'
|
|
563
485
|
*/
|
|
564
486
|
static make<T extends AccessorsInterface>(
|
|
565
|
-
AccessorConstructor: new (parser:
|
|
487
|
+
AccessorConstructor: new (parser: ValidatableParserInterface) => T,
|
|
566
488
|
data: unknown,
|
|
567
489
|
): T {
|
|
568
490
|
return Inline.defaultInstance().make(AccessorConstructor, data);
|
package/src/parser/xml-parser.ts
CHANGED
|
@@ -23,9 +23,7 @@ export class XmlParser {
|
|
|
23
23
|
*/
|
|
24
24
|
constructor(maxDepth: number, maxElements: number = 10_000) {
|
|
25
25
|
this.maxDepth = maxDepth;
|
|
26
|
-
this.maxElements = Number.isFinite(maxElements) && maxElements >= 1
|
|
27
|
-
? maxElements
|
|
28
|
-
: 10_000;
|
|
26
|
+
this.maxElements = Number.isFinite(maxElements) && maxElements >= 1 ? maxElements : 10_000;
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
/**
|
|
@@ -114,7 +112,7 @@ export class XmlParser {
|
|
|
114
112
|
const stripped = xml.replace(/<\?xml[^?]*\?>/i, '').trim();
|
|
115
113
|
|
|
116
114
|
// Bound parser complexity before the linear inner-content scan: count opening
|
|
117
|
-
// tags as a document-complexity proxy. This is a defence-in-depth limit
|
|
115
|
+
// tags as a document-complexity proxy. This is a defence-in-depth limit -
|
|
118
116
|
// the linear scanner below is already O(n), but bounding element count also
|
|
119
117
|
// caps the total number of recursive parseXmlChildren calls.
|
|
120
118
|
// Browser environments (DOMParser) are unaffected.
|
|
@@ -168,9 +166,12 @@ export class XmlParser {
|
|
|
168
166
|
}
|
|
169
167
|
|
|
170
168
|
// Walk backward from the final '>' to locate the closing tag for this root element.
|
|
171
|
-
// This is O(tagNameLen)
|
|
169
|
+
// This is O(tagNameLen) - the tag name is typically short and always bounded.
|
|
172
170
|
let pos = doc.length - 2;
|
|
173
|
-
while (
|
|
171
|
+
while (
|
|
172
|
+
pos >= 0 &&
|
|
173
|
+
(doc[pos] === ' ' || doc[pos] === '\t' || doc[pos] === '\n' || doc[pos] === '\r')
|
|
174
|
+
) {
|
|
174
175
|
pos--;
|
|
175
176
|
}
|
|
176
177
|
|
|
@@ -258,7 +259,14 @@ export class XmlParser {
|
|
|
258
259
|
|
|
259
260
|
if (content.startsWith(closeTag, nextLt)) {
|
|
260
261
|
const c = content[nextLt + closeTag.length];
|
|
261
|
-
if (
|
|
262
|
+
if (
|
|
263
|
+
c === '>' ||
|
|
264
|
+
c === ' ' ||
|
|
265
|
+
c === '\t' ||
|
|
266
|
+
c === '\n' ||
|
|
267
|
+
c === '\r' ||
|
|
268
|
+
c === undefined
|
|
269
|
+
) {
|
|
262
270
|
nestDepth--;
|
|
263
271
|
if (nestDepth === 0) {
|
|
264
272
|
innerEnd = nextLt;
|
|
@@ -273,9 +281,22 @@ export class XmlParser {
|
|
|
273
281
|
|
|
274
282
|
if (content.startsWith(openPrefix, nextLt)) {
|
|
275
283
|
const c = content[nextLt + openPrefix.length];
|
|
276
|
-
if (
|
|
284
|
+
if (
|
|
285
|
+
c === '>' ||
|
|
286
|
+
c === ' ' ||
|
|
287
|
+
c === '\t' ||
|
|
288
|
+
c === '\n' ||
|
|
289
|
+
c === '\r' ||
|
|
290
|
+
c === '/'
|
|
291
|
+
) {
|
|
277
292
|
const ogt = content.indexOf('>', nextLt + openPrefix.length);
|
|
278
|
-
if (
|
|
293
|
+
if (
|
|
294
|
+
ogt !== -1 &&
|
|
295
|
+
!content
|
|
296
|
+
.slice(nextLt + openPrefix.length, ogt)
|
|
297
|
+
.trimEnd()
|
|
298
|
+
.endsWith('/')
|
|
299
|
+
) {
|
|
279
300
|
nestDepth++;
|
|
280
301
|
}
|
|
281
302
|
}
|
|
@@ -285,7 +306,7 @@ export class XmlParser {
|
|
|
285
306
|
}
|
|
286
307
|
|
|
287
308
|
if (innerEnd === -1) {
|
|
288
|
-
// Unclosed or malformed tag
|
|
309
|
+
// Unclosed or malformed tag - skip past the opening tag
|
|
289
310
|
i = gt + 1;
|
|
290
311
|
continue;
|
|
291
312
|
}
|