@safeaccess/inline 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/.gitattributes +16 -0
  2. package/.gitkeep +0 -0
  3. package/CHANGELOG.md +38 -0
  4. package/LICENSE +21 -0
  5. package/README.md +454 -0
  6. package/benchmarks/get.bench.ts +26 -0
  7. package/benchmarks/parse.bench.ts +41 -0
  8. package/dist/accessors/abstract-accessor.d.ts +213 -0
  9. package/dist/accessors/abstract-accessor.js +294 -0
  10. package/dist/accessors/formats/any-accessor.d.ts +35 -0
  11. package/dist/accessors/formats/any-accessor.js +44 -0
  12. package/dist/accessors/formats/array-accessor.d.ts +26 -0
  13. package/dist/accessors/formats/array-accessor.js +39 -0
  14. package/dist/accessors/formats/env-accessor.d.ts +27 -0
  15. package/dist/accessors/formats/env-accessor.js +64 -0
  16. package/dist/accessors/formats/ini-accessor.d.ts +41 -0
  17. package/dist/accessors/formats/ini-accessor.js +109 -0
  18. package/dist/accessors/formats/json-accessor.d.ts +26 -0
  19. package/dist/accessors/formats/json-accessor.js +56 -0
  20. package/dist/accessors/formats/ndjson-accessor.d.ts +28 -0
  21. package/dist/accessors/formats/ndjson-accessor.js +71 -0
  22. package/dist/accessors/formats/object-accessor.d.ts +48 -0
  23. package/dist/accessors/formats/object-accessor.js +90 -0
  24. package/dist/accessors/formats/xml-accessor.d.ts +27 -0
  25. package/dist/accessors/formats/xml-accessor.js +52 -0
  26. package/dist/accessors/formats/yaml-accessor.d.ts +29 -0
  27. package/dist/accessors/formats/yaml-accessor.js +46 -0
  28. package/dist/contracts/accessors-interface.d.ts +11 -0
  29. package/dist/contracts/accessors-interface.js +1 -0
  30. package/dist/contracts/factory-accessors-interface.d.ts +16 -0
  31. package/dist/contracts/factory-accessors-interface.js +1 -0
  32. package/dist/contracts/parse-integration-interface.d.ts +31 -0
  33. package/dist/contracts/parse-integration-interface.js +1 -0
  34. package/dist/contracts/path-cache-interface.d.ts +40 -0
  35. package/dist/contracts/path-cache-interface.js +1 -0
  36. package/dist/contracts/readable-accessors-interface.d.ts +79 -0
  37. package/dist/contracts/readable-accessors-interface.js +1 -0
  38. package/dist/contracts/security-guard-interface.d.ts +40 -0
  39. package/dist/contracts/security-guard-interface.js +1 -0
  40. package/dist/contracts/security-parser-interface.d.ts +67 -0
  41. package/dist/contracts/security-parser-interface.js +1 -0
  42. package/dist/contracts/writable-accessors-interface.d.ts +65 -0
  43. package/dist/contracts/writable-accessors-interface.js +1 -0
  44. package/dist/core/dot-notation-parser.d.ts +204 -0
  45. package/dist/core/dot-notation-parser.js +343 -0
  46. package/dist/exceptions/accessor-exception.d.ts +13 -0
  47. package/dist/exceptions/accessor-exception.js +16 -0
  48. package/dist/exceptions/invalid-format-exception.d.ts +14 -0
  49. package/dist/exceptions/invalid-format-exception.js +17 -0
  50. package/dist/exceptions/parser-exception.d.ts +14 -0
  51. package/dist/exceptions/parser-exception.js +17 -0
  52. package/dist/exceptions/path-not-found-exception.d.ts +14 -0
  53. package/dist/exceptions/path-not-found-exception.js +17 -0
  54. package/dist/exceptions/readonly-violation-exception.d.ts +15 -0
  55. package/dist/exceptions/readonly-violation-exception.js +18 -0
  56. package/dist/exceptions/security-exception.d.ts +18 -0
  57. package/dist/exceptions/security-exception.js +21 -0
  58. package/dist/exceptions/unsupported-type-exception.d.ts +14 -0
  59. package/dist/exceptions/unsupported-type-exception.js +17 -0
  60. package/dist/exceptions/yaml-parse-exception.d.ts +17 -0
  61. package/dist/exceptions/yaml-parse-exception.js +20 -0
  62. package/dist/index.d.ts +30 -0
  63. package/dist/index.js +30 -0
  64. package/dist/inline.d.ts +402 -0
  65. package/dist/inline.js +512 -0
  66. package/dist/parser/xml-parser.d.ts +46 -0
  67. package/dist/parser/xml-parser.js +288 -0
  68. package/dist/parser/yaml-parser.d.ts +94 -0
  69. package/dist/parser/yaml-parser.js +286 -0
  70. package/dist/security/forbidden-keys.d.ts +34 -0
  71. package/dist/security/forbidden-keys.js +80 -0
  72. package/dist/security/security-guard.d.ts +94 -0
  73. package/dist/security/security-guard.js +172 -0
  74. package/dist/security/security-parser.d.ts +130 -0
  75. package/dist/security/security-parser.js +192 -0
  76. package/dist/type-format.d.ts +28 -0
  77. package/dist/type-format.js +29 -0
  78. package/eslint.config.js +1 -0
  79. package/package.json +39 -0
  80. package/src/accessors/abstract-accessor.ts +353 -0
  81. package/src/accessors/formats/any-accessor.ts +51 -0
  82. package/src/accessors/formats/array-accessor.ts +45 -0
  83. package/src/accessors/formats/env-accessor.ts +79 -0
  84. package/src/accessors/formats/ini-accessor.ts +124 -0
  85. package/src/accessors/formats/json-accessor.ts +66 -0
  86. package/src/accessors/formats/ndjson-accessor.ts +82 -0
  87. package/src/accessors/formats/object-accessor.ts +100 -0
  88. package/src/accessors/formats/xml-accessor.ts +58 -0
  89. package/src/accessors/formats/yaml-accessor.ts +52 -0
  90. package/src/contracts/accessors-interface.ts +12 -0
  91. package/src/contracts/factory-accessors-interface.ts +16 -0
  92. package/src/contracts/parse-integration-interface.ts +32 -0
  93. package/src/contracts/path-cache-interface.ts +43 -0
  94. package/src/contracts/readable-accessors-interface.ts +88 -0
  95. package/src/contracts/security-guard-interface.ts +43 -0
  96. package/src/contracts/security-parser-interface.ts +74 -0
  97. package/src/contracts/writable-accessors-interface.ts +70 -0
  98. package/src/core/dot-notation-parser.ts +419 -0
  99. package/src/exceptions/accessor-exception.ts +16 -0
  100. package/src/exceptions/invalid-format-exception.ts +18 -0
  101. package/src/exceptions/parser-exception.ts +18 -0
  102. package/src/exceptions/path-not-found-exception.ts +18 -0
  103. package/src/exceptions/readonly-violation-exception.ts +19 -0
  104. package/src/exceptions/security-exception.ts +22 -0
  105. package/src/exceptions/unsupported-type-exception.ts +18 -0
  106. package/src/exceptions/yaml-parse-exception.ts +21 -0
  107. package/src/index.ts +46 -0
  108. package/src/inline.ts +570 -0
  109. package/src/parser/xml-parser.ts +334 -0
  110. package/src/parser/yaml-parser.ts +368 -0
  111. package/src/security/forbidden-keys.ts +81 -0
  112. package/src/security/security-guard.ts +195 -0
  113. package/src/security/security-parser.ts +233 -0
  114. package/src/type-format.ts +28 -0
  115. package/stryker.config.json +24 -0
  116. package/tests/accessors/accessors.test.ts +1017 -0
  117. package/tests/accessors/json-accessor.test.ts +171 -0
  118. package/tests/core/dot-notation-parser.test.ts +587 -0
  119. package/tests/exceptions/parser-exception.test.ts +31 -0
  120. package/tests/inline.test.ts +445 -0
  121. package/tests/mocks/fake-parse-integration.ts +24 -0
  122. package/tests/mocks/fake-path-cache.ts +31 -0
  123. package/tests/parity.test.ts +164 -0
  124. package/tests/parser/xml-parser.test.ts +618 -0
  125. package/tests/parser/yaml-parser.test.ts +463 -0
  126. package/tests/security/security-guard.test.ts +646 -0
  127. package/tests/security/security-parser.test.ts +391 -0
  128. package/tsconfig.json +16 -0
  129. package/vitest.config.ts +19 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Contract for a path-segment cache.
3
+ *
4
+ * Provides O(1) lookup for previously parsed dot-notation path strings,
5
+ * avoiding repeated segment parsing on hot paths.
6
+ *
7
+ * Note: JS segments are flat `string[]` (from `path.split('.')`), whereas
8
+ * PHP segments are structured `array<int, array<string, mixed>>` containing
9
+ * SegmentType metadata. This architectural difference reflects each
10
+ * language's parser implementation.
11
+ */
12
+ export interface PathCacheInterface {
13
+ /**
14
+ * Retrieve cached segments for a path string.
15
+ *
16
+ * @param path - Dot-notation path string.
17
+ * @returns Cached segment array, or null if not cached.
18
+ */
19
+ get(path: string): string[] | null;
20
+
21
+ /**
22
+ * Store parsed segments for a path string.
23
+ *
24
+ * @param path - Dot-notation path string.
25
+ * @param segments - Parsed segment array to cache.
26
+ */
27
+ set(path: string, segments: string[]): void;
28
+
29
+ /**
30
+ * Check whether a path exists in the cache.
31
+ *
32
+ * @param path - Dot-notation path string.
33
+ * @returns `true` if segments are cached for this path.
34
+ */
35
+ has(path: string): boolean;
36
+
37
+ /**
38
+ * Clear all cached entries.
39
+ *
40
+ * @returns Same instance for fluent chaining.
41
+ */
42
+ clear(): this;
43
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Contract for read-only data access operations.
3
+ *
4
+ * Defines methods for retrieving, checking existence, counting,
5
+ * and inspecting keys within the accessor's internal data store.
6
+ */
7
+ export interface ReadableAccessorsInterface {
8
+ /**
9
+ * Retrieve the original raw input data before parsing.
10
+ *
11
+ * @returns Original input passed to {@link FactoryAccessorsInterface.from}.
12
+ */
13
+ getRaw(): unknown;
14
+
15
+ /**
16
+ * Retrieve a value at a dot-notation path.
17
+ *
18
+ * @param path - Dot-notation path (e.g. "user.name").
19
+ * @param defaultValue - Fallback when the path does not exist.
20
+ * @returns Resolved value or the default.
21
+ */
22
+ get(path: string, defaultValue?: unknown): unknown;
23
+
24
+ /**
25
+ * Retrieve a value or throw when the path does not exist.
26
+ *
27
+ * @param path - Dot-notation path.
28
+ * @returns Resolved value.
29
+ * @throws {PathNotFoundException} When the path is missing.
30
+ */
31
+ getOrFail(path: string): unknown;
32
+
33
+ /**
34
+ * Retrieve a value using pre-parsed key segments.
35
+ *
36
+ * @param segments - Ordered list of keys.
37
+ * @param defaultValue - Fallback when the path does not exist.
38
+ * @returns Resolved value or the default.
39
+ */
40
+ getAt(segments: Array<string | number>, defaultValue?: unknown): unknown;
41
+
42
+ /**
43
+ * Check whether a dot-notation path exists.
44
+ *
45
+ * @param path - Dot-notation path.
46
+ * @returns True if the path resolves to a value.
47
+ */
48
+ has(path: string): boolean;
49
+
50
+ /**
51
+ * Check whether a path exists using pre-parsed key segments.
52
+ *
53
+ * @param segments - Ordered list of keys.
54
+ * @returns True if the path resolves to a value.
55
+ */
56
+ hasAt(segments: Array<string | number>): boolean;
57
+
58
+ /**
59
+ * Retrieve multiple values by their paths with individual defaults.
60
+ *
61
+ * @param paths - Map of path to default value.
62
+ * @returns Map of path to resolved value.
63
+ */
64
+ getMany(paths: Record<string, unknown>): Record<string, unknown>;
65
+
66
+ /**
67
+ * Return all parsed data as a plain object.
68
+ *
69
+ * @returns Complete internal data.
70
+ */
71
+ all(): Record<string, unknown>;
72
+
73
+ /**
74
+ * Count elements at a path, or the root if undefined.
75
+ *
76
+ * @param path - Dot-notation path, or undefined for root.
77
+ * @returns Number of elements.
78
+ */
79
+ count(path?: string): number;
80
+
81
+ /**
82
+ * Retrieve array keys at a path, or root keys if undefined.
83
+ *
84
+ * @param path - Dot-notation path, or undefined for root.
85
+ * @returns List of keys.
86
+ */
87
+ keys(path?: string): string[];
88
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Contract for validating keys against a forbidden-key security list.
3
+ *
4
+ * Prevents injection attacks by rejecting prototype pollution vectors,
5
+ * legacy prototype manipulation methods, stream wrapper / protocol URI schemes,
6
+ * and Node.js globals during data access and mutation operations.
7
+ */
8
+ export interface SecurityGuardInterface {
9
+ /**
10
+ * Check whether a key is in the forbidden list.
11
+ *
12
+ * @param key - Key name to check.
13
+ * @returns True if the key is forbidden.
14
+ */
15
+ isForbiddenKey(key: string): boolean;
16
+
17
+ /**
18
+ * Assert that a single key is safe, throwing on violation.
19
+ *
20
+ * @param key - Key name to validate.
21
+ * @throws {SecurityException} When the key is forbidden.
22
+ */
23
+ assertSafeKey(key: string): void;
24
+
25
+ /**
26
+ * Recursively assert that all keys in a data structure are safe.
27
+ *
28
+ * @param data - Data to scan for forbidden keys.
29
+ * @param depth - Current recursion depth (internal use).
30
+ * @throws {SecurityException} When a forbidden key is found or depth is exceeded.
31
+ */
32
+ assertSafeKeys(data: unknown, depth?: number): void;
33
+
34
+ /**
35
+ * Remove all forbidden keys from a data structure recursively.
36
+ *
37
+ * @param data - Data to sanitize.
38
+ * @param depth - Current recursion depth.
39
+ * @returns Sanitized data without forbidden keys.
40
+ * @throws {SecurityException} When recursion depth exceeds the limit.
41
+ */
42
+ sanitize(data: Record<string, unknown>, depth?: number): Record<string, unknown>;
43
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Contract for security validation during parsing and path resolution.
3
+ *
4
+ * Defines methods for asserting payload size, maximum key counts,
5
+ * and recursion depth limits to prevent resource exhaustion and
6
+ * injection attacks during data access operations.
7
+ */
8
+ export interface SecurityParserInterface {
9
+ /**
10
+ * Assert that a raw string payload does not exceed the byte limit.
11
+ *
12
+ * @param input - Raw input string to measure.
13
+ * @param maxBytes - Override limit, or undefined to use configured default.
14
+ * @throws {SecurityException} When the payload exceeds the limit.
15
+ */
16
+ assertPayloadSize(input: string, maxBytes?: number): void;
17
+
18
+ /**
19
+ * Assert that resolve depth does not exceed the configured limit.
20
+ *
21
+ * @param depth - Current depth counter.
22
+ * @throws {SecurityException} When depth exceeds the maximum.
23
+ */
24
+ assertMaxResolveDepth(depth: number): void;
25
+
26
+ /**
27
+ * Assert that total key count does not exceed the limit.
28
+ *
29
+ * @param data - Data to count keys in.
30
+ * @param maxKeys - Override limit, or undefined to use configured default.
31
+ * @param maxCountDepth - Override recursion depth limit, or undefined for default.
32
+ * @throws {SecurityException} When key count exceeds the limit.
33
+ */
34
+ assertMaxKeys(data: Record<string, unknown>, maxKeys?: number, maxCountDepth?: number): void;
35
+
36
+ /**
37
+ * Assert that current recursion depth does not exceed the limit.
38
+ *
39
+ * @param currentDepth - Current depth counter.
40
+ * @param maxDepth - Override limit, or undefined to use configured default.
41
+ * @throws {SecurityException} When the depth exceeds the limit.
42
+ */
43
+ assertMaxDepth(currentDepth: number, maxDepth?: number): void;
44
+
45
+ /**
46
+ * Assert that structural nesting depth does not exceed the policy limit.
47
+ *
48
+ * @param data - Data to measure structural depth of.
49
+ * @param maxDepth - Maximum allowed structural depth.
50
+ * @throws {SecurityException} When structural depth exceeds the limit.
51
+ */
52
+ assertMaxStructuralDepth(data: unknown, maxDepth: number): void;
53
+
54
+ /**
55
+ * Return the configured maximum structural nesting depth.
56
+ *
57
+ * @returns Maximum allowed depth.
58
+ */
59
+ getMaxDepth(): number;
60
+
61
+ /**
62
+ * Return the configured maximum path-resolve recursion depth.
63
+ *
64
+ * @returns Maximum allowed resolve depth.
65
+ */
66
+ getMaxResolveDepth(): number;
67
+
68
+ /**
69
+ * Return the configured maximum total key count.
70
+ *
71
+ * @returns Maximum allowed key count.
72
+ */
73
+ getMaxKeys(): number;
74
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Contract for immutable write operations on accessor data.
3
+ *
4
+ * All mutations return a new instance with the modification applied,
5
+ * preserving the original accessor instance.
6
+ */
7
+ export interface WritableAccessorsInterface {
8
+ /**
9
+ * Set a value at a dot-notation path.
10
+ *
11
+ * @param path - Dot-notation path.
12
+ * @param value - Value to assign.
13
+ * @returns New accessor instance with the value set.
14
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
15
+ * @throws {SecurityException} When the path contains forbidden keys.
16
+ */
17
+ set(path: string, value: unknown): this;
18
+
19
+ /**
20
+ * Set a value using pre-parsed key segments.
21
+ *
22
+ * @param segments - Ordered list of keys.
23
+ * @param value - Value to assign.
24
+ * @returns New accessor instance with the value set.
25
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
26
+ * @throws {SecurityException} When segments contain forbidden keys.
27
+ */
28
+ setAt(segments: Array<string | number>, value: unknown): this;
29
+
30
+ /**
31
+ * Remove a value at a dot-notation path.
32
+ *
33
+ * @param path - Dot-notation path to remove.
34
+ * @returns New accessor instance without the specified path.
35
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
36
+ * @throws {SecurityException} When the path contains forbidden keys.
37
+ */
38
+ remove(path: string): this;
39
+
40
+ /**
41
+ * Remove a value using pre-parsed key segments.
42
+ *
43
+ * @param segments - Ordered list of keys.
44
+ * @returns New accessor instance without the specified path.
45
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
46
+ * @throws {SecurityException} When segments contain forbidden keys.
47
+ */
48
+ removeAt(segments: Array<string | number>): this;
49
+
50
+ /**
51
+ * Deep-merge an object into the value at a dot-notation path.
52
+ *
53
+ * @param path - Dot-notation path to the merge target.
54
+ * @param value - Object to merge into the existing value.
55
+ * @returns New accessor instance with merged data.
56
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
57
+ * @throws {SecurityException} When the path or values contain forbidden keys.
58
+ */
59
+ merge(path: string, value: Record<string, unknown>): this;
60
+
61
+ /**
62
+ * Deep-merge an object into the root data.
63
+ *
64
+ * @param value - Object to merge into the root.
65
+ * @returns New accessor instance with merged data.
66
+ * @throws {ReadonlyViolationException} When the accessor is readonly.
67
+ * @throws {SecurityException} When values contain forbidden keys.
68
+ */
69
+ mergeAll(value: Record<string, unknown>): this;
70
+ }