@fgv/ts-utils 5.0.0-11 → 5.0.0-14

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 (288) hide show
  1. package/.vscode/launch.json +16 -0
  2. package/.vscode/settings.json +36 -0
  3. package/config/api-extractor.json +343 -0
  4. package/config/rig.json +16 -0
  5. package/dist/tsdoc-metadata.json +1 -1
  6. package/lib/test/helpers/jest/helpers/fsHelpers.d.ts +52 -0
  7. package/lib/test/helpers/jest/helpers/fsHelpers.js +180 -0
  8. package/lib/test/helpers/jest/helpers/index.d.ts +2 -0
  9. package/lib/test/helpers/jest/helpers/index.js +18 -0
  10. package/lib/test/helpers/jest/index.d.ts +3 -0
  11. package/lib/test/helpers/jest/index.js +27 -0
  12. package/lib/test/helpers/jest/matchers/index.d.ts +16 -0
  13. package/lib/test/helpers/jest/matchers/index.js +19 -0
  14. package/lib/test/helpers/jest/matchers/toFail/index.d.ts +16 -0
  15. package/lib/test/helpers/jest/matchers/toFail/index.js +25 -0
  16. package/lib/test/helpers/jest/matchers/toFail/predicate.d.ts +4 -0
  17. package/lib/test/helpers/jest/matchers/toFail/predicate.js +9 -0
  18. package/lib/test/helpers/jest/matchers/toFailTest/index.d.ts +16 -0
  19. package/lib/test/helpers/jest/matchers/toFailTest/index.js +30 -0
  20. package/lib/test/helpers/jest/matchers/toFailTest/predicate.d.ts +3 -0
  21. package/lib/test/helpers/jest/matchers/toFailTest/predicate.js +12 -0
  22. package/lib/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/index.d.ts +17 -0
  23. package/lib/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/index.js +26 -0
  24. package/lib/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/predicate.d.ts +4 -0
  25. package/lib/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/predicate.js +15 -0
  26. package/lib/test/helpers/jest/matchers/toFailTestWith/index.d.ts +18 -0
  27. package/lib/test/helpers/jest/matchers/toFailTestWith/index.js +31 -0
  28. package/lib/test/helpers/jest/matchers/toFailTestWith/predicate.d.ts +4 -0
  29. package/lib/test/helpers/jest/matchers/toFailTestWith/predicate.js +30 -0
  30. package/lib/test/helpers/jest/matchers/toFailWith/index.d.ts +18 -0
  31. package/lib/test/helpers/jest/matchers/toFailWith/index.js +29 -0
  32. package/lib/test/helpers/jest/matchers/toFailWith/predicate.d.ts +4 -0
  33. package/lib/test/helpers/jest/matchers/toFailWith/predicate.js +20 -0
  34. package/lib/test/helpers/jest/matchers/toFailWithDetail/index.d.ts +19 -0
  35. package/lib/test/helpers/jest/matchers/toFailWithDetail/index.js +29 -0
  36. package/lib/test/helpers/jest/matchers/toFailWithDetail/predicate.d.ts +4 -0
  37. package/lib/test/helpers/jest/matchers/toFailWithDetail/predicate.js +26 -0
  38. package/lib/test/helpers/jest/matchers/toSucceed/index.d.ts +16 -0
  39. package/lib/test/helpers/jest/matchers/toSucceed/index.js +25 -0
  40. package/lib/test/helpers/jest/matchers/toSucceed/predicate.d.ts +4 -0
  41. package/lib/test/helpers/jest/matchers/toSucceed/predicate.js +9 -0
  42. package/lib/test/helpers/jest/matchers/toSucceedAndMatchInlineSnapshot/index.d.ts +17 -0
  43. package/lib/test/helpers/jest/matchers/toSucceedAndMatchInlineSnapshot/index.js +26 -0
  44. package/lib/test/helpers/jest/matchers/toSucceedAndMatchSnapshot/index.d.ts +17 -0
  45. package/lib/test/helpers/jest/matchers/toSucceedAndMatchSnapshot/index.js +26 -0
  46. package/lib/test/helpers/jest/matchers/toSucceedAndSatisfy/index.d.ts +19 -0
  47. package/lib/test/helpers/jest/matchers/toSucceedAndSatisfy/index.js +55 -0
  48. package/lib/test/helpers/jest/matchers/toSucceedAndSatisfy/predicate.d.ts +4 -0
  49. package/lib/test/helpers/jest/matchers/toSucceedAndSatisfy/predicate.js +21 -0
  50. package/lib/test/helpers/jest/matchers/toSucceedWith/index.d.ts +18 -0
  51. package/lib/test/helpers/jest/matchers/toSucceedWith/index.js +29 -0
  52. package/lib/test/helpers/jest/matchers/toSucceedWith/predicate.d.ts +4 -0
  53. package/lib/test/helpers/jest/matchers/toSucceedWith/predicate.js +16 -0
  54. package/lib/test/helpers/jest/matchers/toSucceedWithDetail/index.d.ts +18 -0
  55. package/lib/test/helpers/jest/matchers/toSucceedWithDetail/index.js +29 -0
  56. package/lib/test/helpers/jest/matchers/toSucceedWithDetail/predicate.d.ts +4 -0
  57. package/lib/test/helpers/jest/matchers/toSucceedWithDetail/predicate.js +21 -0
  58. package/lib/test/helpers/jest/resolvers/cli.d.ts +5 -0
  59. package/lib/test/helpers/jest/resolvers/cli.js +48 -0
  60. package/lib/test/helpers/jest/resolvers/ide.d.ts +5 -0
  61. package/lib/test/helpers/jest/resolvers/ide.js +48 -0
  62. package/lib/test/helpers/jest/ts-utils.d.ts +2 -0
  63. package/lib/test/helpers/jest/ts-utils.js +18 -0
  64. package/lib/test/helpers/jest/types/index.d.ts +79 -0
  65. package/lib/test/helpers/jest/types/index.js +4 -0
  66. package/lib/test/helpers/jest/utils/matcherHelpers.d.ts +6 -0
  67. package/lib/test/helpers/jest/utils/matcherHelpers.js +53 -0
  68. package/lib/test/helpers/jest/utils/snapshotResolver.d.ts +4 -0
  69. package/lib/test/helpers/jest/utils/snapshotResolver.js +19 -0
  70. package/lib/test/unit/collections/collectible.test.d.ts +2 -0
  71. package/lib/test/unit/collections/collector.test.d.ts +2 -0
  72. package/lib/test/unit/collections/collectors.test.d.ts +2 -0
  73. package/lib/test/unit/collections/convertingCollector.test.d.ts +2 -0
  74. package/lib/test/unit/collections/helpers.d.ts +44 -0
  75. package/lib/test/unit/collections/helpers.js +115 -0
  76. package/lib/test/unit/collections/resultMap.test.d.ts +2 -0
  77. package/lib/test/unit/collections/validatingCollector.test.d.ts +2 -0
  78. package/lib/test/unit/collections/validatingConvertingCollector.test.d.ts +2 -0
  79. package/lib/test/unit/collections/validatingResultMap.test.d.ts +2 -0
  80. package/lib/test/unit/converter.test.d.ts +2 -0
  81. package/lib/test/unit/converters.test.d.ts +2 -0
  82. package/lib/test/unit/defaultingConverter.test.d.ts +2 -0
  83. package/lib/test/unit/file-tree/fileItem.test.d.ts +2 -0
  84. package/lib/test/unit/file-tree/fileTree.test.d.ts +2 -0
  85. package/lib/test/unit/file-tree/fsTree.test.d.ts +2 -0
  86. package/lib/test/unit/file-tree/inMemoryTree.test.d.ts +2 -0
  87. package/lib/test/unit/file-tree/sample/rootFile.json +1 -0
  88. package/lib/test/unit/file-tree/sample/some/other/path/file2.json +1 -0
  89. package/lib/test/unit/file-tree/sample/some/path/below/file4.json +1 -0
  90. package/lib/test/unit/file-tree/sample/some/path/file1.json +1 -0
  91. package/lib/test/unit/file-tree/sample/some/path/file3.json +1 -0
  92. package/lib/test/unit/hash.test.d.ts +2 -0
  93. package/lib/test/unit/logger.test.d.ts +2 -0
  94. package/lib/test/unit/mapResults.test.d.ts +3 -0
  95. package/lib/test/unit/messageAggregator.test.d.ts +3 -0
  96. package/lib/test/unit/normalize.test.d.ts +2 -0
  97. package/lib/test/unit/result.test.d.ts +3 -0
  98. package/lib/test/unit/utils.test.d.ts +3 -0
  99. package/lib/test/unit/validation/array.test.d.ts +2 -0
  100. package/lib/test/unit/validation/boolean.test.d.ts +2 -0
  101. package/lib/test/unit/validation/genericValidator.test.d.ts +2 -0
  102. package/lib/test/unit/validation/number.test.d.ts +2 -0
  103. package/lib/test/unit/validation/object.test.d.ts +2 -0
  104. package/lib/test/unit/validation/oneOf.test.d.ts +2 -0
  105. package/lib/test/unit/validation/recordOf.test.d.ts +2 -0
  106. package/lib/test/unit/validation/string.test.d.ts +2 -0
  107. package/lib/test/unit/validation/traits.test.d.ts +2 -0
  108. package/lib/test/unit/validation/typeGuard.test.d.ts +2 -0
  109. package/lib/test/unit/validation/validators.test.d.ts +2 -0
  110. package/package.json +9 -9
  111. package/src/index.ts +63 -0
  112. package/src/packlets/base/brand.ts +28 -0
  113. package/src/packlets/base/index.ts +31 -0
  114. package/src/packlets/base/logger.ts +156 -0
  115. package/src/packlets/base/mapResults.ts +302 -0
  116. package/src/packlets/base/messageAggregator.ts +120 -0
  117. package/src/packlets/base/normalize.ts +144 -0
  118. package/src/packlets/base/result.ts +961 -0
  119. package/src/packlets/base/utils.ts +236 -0
  120. package/src/packlets/collections/collectible.ts +238 -0
  121. package/src/packlets/collections/collector.ts +273 -0
  122. package/src/packlets/collections/collectorValidator.ts +178 -0
  123. package/src/packlets/collections/common.ts +27 -0
  124. package/src/packlets/collections/convertingCollector.ts +223 -0
  125. package/src/packlets/collections/convertingCollectorValidator.ts +164 -0
  126. package/src/packlets/collections/index.ts +39 -0
  127. package/src/packlets/collections/keyValueConverters.ts +142 -0
  128. package/src/packlets/collections/readonlyResultMap.ts +99 -0
  129. package/src/packlets/collections/resultMap.ts +322 -0
  130. package/src/packlets/collections/resultMapValidator.ts +188 -0
  131. package/src/packlets/collections/utils.ts +33 -0
  132. package/src/packlets/collections/validatingCollector.ts +123 -0
  133. package/src/packlets/collections/validatingConvertingCollector.ts +113 -0
  134. package/src/packlets/collections/validatingResultMap.ts +94 -0
  135. package/src/packlets/conversion/baseConverter.ts +330 -0
  136. package/src/packlets/conversion/converter.ts +260 -0
  137. package/src/packlets/conversion/converters.ts +1039 -0
  138. package/src/packlets/conversion/defaultingConverter.ts +188 -0
  139. package/src/packlets/conversion/index.ts +30 -0
  140. package/src/packlets/conversion/objectConverter.ts +259 -0
  141. package/src/packlets/conversion/stringConverter.ts +165 -0
  142. package/src/packlets/file-tree/directoryItem.ts +84 -0
  143. package/src/packlets/file-tree/fileItem.ts +119 -0
  144. package/src/packlets/file-tree/fileTree.ts +152 -0
  145. package/src/packlets/file-tree/fileTreeAccessors.ts +176 -0
  146. package/src/packlets/file-tree/fsTree.ts +118 -0
  147. package/src/packlets/file-tree/in-memory/inMemoryTree.ts +171 -0
  148. package/src/packlets/file-tree/in-memory/index.ts +23 -0
  149. package/src/packlets/file-tree/in-memory/treeBuilder.ts +203 -0
  150. package/src/packlets/file-tree/index.ts +31 -0
  151. package/src/packlets/hash/crcNormalizer.ts +81 -0
  152. package/src/packlets/hash/hashingNormalizer.ts +102 -0
  153. package/src/packlets/hash/index.ts +24 -0
  154. package/src/packlets/validation/array.ts +83 -0
  155. package/src/packlets/validation/boolean.ts +63 -0
  156. package/src/packlets/validation/classes.ts +38 -0
  157. package/src/packlets/validation/common.ts +38 -0
  158. package/src/packlets/validation/field.ts +99 -0
  159. package/src/packlets/validation/genericValidator.ts +205 -0
  160. package/src/packlets/validation/index.ts +31 -0
  161. package/src/packlets/validation/number.ts +66 -0
  162. package/src/packlets/validation/object.ts +208 -0
  163. package/src/packlets/validation/oneOf.ts +77 -0
  164. package/src/packlets/validation/string.ts +66 -0
  165. package/src/packlets/validation/traits.ts +113 -0
  166. package/src/packlets/validation/typeGuard.ts +82 -0
  167. package/src/packlets/validation/validator.ts +145 -0
  168. package/src/packlets/validation/validatorBase.ts +61 -0
  169. package/src/packlets/validation/validators.ts +246 -0
  170. package/CHANGELOG.md +0 -135
  171. package/lib/index.d.ts.map +0 -1
  172. package/lib/index.js.map +0 -1
  173. package/lib/packlets/base/brand.d.ts.map +0 -1
  174. package/lib/packlets/base/brand.js.map +0 -1
  175. package/lib/packlets/base/index.d.ts.map +0 -1
  176. package/lib/packlets/base/index.js.map +0 -1
  177. package/lib/packlets/base/logger.d.ts.map +0 -1
  178. package/lib/packlets/base/logger.js.map +0 -1
  179. package/lib/packlets/base/mapResults.d.ts.map +0 -1
  180. package/lib/packlets/base/mapResults.js.map +0 -1
  181. package/lib/packlets/base/messageAggregator.d.ts.map +0 -1
  182. package/lib/packlets/base/messageAggregator.js.map +0 -1
  183. package/lib/packlets/base/normalize.d.ts.map +0 -1
  184. package/lib/packlets/base/normalize.js.map +0 -1
  185. package/lib/packlets/base/result.d.ts.map +0 -1
  186. package/lib/packlets/base/result.js.map +0 -1
  187. package/lib/packlets/base/utils.d.ts.map +0 -1
  188. package/lib/packlets/base/utils.js.map +0 -1
  189. package/lib/packlets/collections/collectible.d.ts.map +0 -1
  190. package/lib/packlets/collections/collectible.js.map +0 -1
  191. package/lib/packlets/collections/collector.d.ts.map +0 -1
  192. package/lib/packlets/collections/collector.js.map +0 -1
  193. package/lib/packlets/collections/collectorValidator.d.ts.map +0 -1
  194. package/lib/packlets/collections/collectorValidator.js.map +0 -1
  195. package/lib/packlets/collections/common.d.ts.map +0 -1
  196. package/lib/packlets/collections/common.js.map +0 -1
  197. package/lib/packlets/collections/convertingCollector.d.ts.map +0 -1
  198. package/lib/packlets/collections/convertingCollector.js.map +0 -1
  199. package/lib/packlets/collections/convertingCollectorValidator.d.ts.map +0 -1
  200. package/lib/packlets/collections/convertingCollectorValidator.js.map +0 -1
  201. package/lib/packlets/collections/index.d.ts.map +0 -1
  202. package/lib/packlets/collections/index.js.map +0 -1
  203. package/lib/packlets/collections/keyValueConverters.d.ts.map +0 -1
  204. package/lib/packlets/collections/keyValueConverters.js.map +0 -1
  205. package/lib/packlets/collections/readonlyResultMap.d.ts.map +0 -1
  206. package/lib/packlets/collections/readonlyResultMap.js.map +0 -1
  207. package/lib/packlets/collections/resultMap.d.ts.map +0 -1
  208. package/lib/packlets/collections/resultMap.js.map +0 -1
  209. package/lib/packlets/collections/resultMapValidator.d.ts.map +0 -1
  210. package/lib/packlets/collections/resultMapValidator.js.map +0 -1
  211. package/lib/packlets/collections/utils.d.ts.map +0 -1
  212. package/lib/packlets/collections/utils.js.map +0 -1
  213. package/lib/packlets/collections/validatingCollector.d.ts.map +0 -1
  214. package/lib/packlets/collections/validatingCollector.js.map +0 -1
  215. package/lib/packlets/collections/validatingConvertingCollector.d.ts.map +0 -1
  216. package/lib/packlets/collections/validatingConvertingCollector.js.map +0 -1
  217. package/lib/packlets/collections/validatingResultMap.d.ts.map +0 -1
  218. package/lib/packlets/collections/validatingResultMap.js.map +0 -1
  219. package/lib/packlets/conversion/baseConverter.d.ts.map +0 -1
  220. package/lib/packlets/conversion/baseConverter.js.map +0 -1
  221. package/lib/packlets/conversion/converter.d.ts.map +0 -1
  222. package/lib/packlets/conversion/converter.js.map +0 -1
  223. package/lib/packlets/conversion/converters.d.ts.map +0 -1
  224. package/lib/packlets/conversion/converters.js.map +0 -1
  225. package/lib/packlets/conversion/defaultingConverter.d.ts.map +0 -1
  226. package/lib/packlets/conversion/defaultingConverter.js.map +0 -1
  227. package/lib/packlets/conversion/index.d.ts.map +0 -1
  228. package/lib/packlets/conversion/index.js.map +0 -1
  229. package/lib/packlets/conversion/objectConverter.d.ts.map +0 -1
  230. package/lib/packlets/conversion/objectConverter.js.map +0 -1
  231. package/lib/packlets/conversion/stringConverter.d.ts.map +0 -1
  232. package/lib/packlets/conversion/stringConverter.js.map +0 -1
  233. package/lib/packlets/file-tree/directoryItem.d.ts.map +0 -1
  234. package/lib/packlets/file-tree/directoryItem.js.map +0 -1
  235. package/lib/packlets/file-tree/fileItem.d.ts.map +0 -1
  236. package/lib/packlets/file-tree/fileItem.js.map +0 -1
  237. package/lib/packlets/file-tree/fileTree.d.ts.map +0 -1
  238. package/lib/packlets/file-tree/fileTree.js.map +0 -1
  239. package/lib/packlets/file-tree/fileTreeAccessors.d.ts.map +0 -1
  240. package/lib/packlets/file-tree/fileTreeAccessors.js.map +0 -1
  241. package/lib/packlets/file-tree/fsTree.d.ts.map +0 -1
  242. package/lib/packlets/file-tree/fsTree.js.map +0 -1
  243. package/lib/packlets/file-tree/in-memory/inMemoryTree.d.ts.map +0 -1
  244. package/lib/packlets/file-tree/in-memory/inMemoryTree.js.map +0 -1
  245. package/lib/packlets/file-tree/in-memory/index.d.ts.map +0 -1
  246. package/lib/packlets/file-tree/in-memory/index.js.map +0 -1
  247. package/lib/packlets/file-tree/in-memory/treeBuilder.d.ts.map +0 -1
  248. package/lib/packlets/file-tree/in-memory/treeBuilder.js.map +0 -1
  249. package/lib/packlets/file-tree/index.d.ts.map +0 -1
  250. package/lib/packlets/file-tree/index.js.map +0 -1
  251. package/lib/packlets/hash/crcNormalizer.d.ts.map +0 -1
  252. package/lib/packlets/hash/crcNormalizer.js.map +0 -1
  253. package/lib/packlets/hash/hashingNormalizer.d.ts.map +0 -1
  254. package/lib/packlets/hash/hashingNormalizer.js.map +0 -1
  255. package/lib/packlets/hash/index.d.ts.map +0 -1
  256. package/lib/packlets/hash/index.js.map +0 -1
  257. package/lib/packlets/validation/array.d.ts.map +0 -1
  258. package/lib/packlets/validation/array.js.map +0 -1
  259. package/lib/packlets/validation/boolean.d.ts.map +0 -1
  260. package/lib/packlets/validation/boolean.js.map +0 -1
  261. package/lib/packlets/validation/classes.d.ts.map +0 -1
  262. package/lib/packlets/validation/classes.js.map +0 -1
  263. package/lib/packlets/validation/common.d.ts.map +0 -1
  264. package/lib/packlets/validation/common.js.map +0 -1
  265. package/lib/packlets/validation/field.d.ts.map +0 -1
  266. package/lib/packlets/validation/field.js.map +0 -1
  267. package/lib/packlets/validation/genericValidator.d.ts.map +0 -1
  268. package/lib/packlets/validation/genericValidator.js.map +0 -1
  269. package/lib/packlets/validation/index.d.ts.map +0 -1
  270. package/lib/packlets/validation/index.js.map +0 -1
  271. package/lib/packlets/validation/number.d.ts.map +0 -1
  272. package/lib/packlets/validation/number.js.map +0 -1
  273. package/lib/packlets/validation/object.d.ts.map +0 -1
  274. package/lib/packlets/validation/object.js.map +0 -1
  275. package/lib/packlets/validation/oneOf.d.ts.map +0 -1
  276. package/lib/packlets/validation/oneOf.js.map +0 -1
  277. package/lib/packlets/validation/string.d.ts.map +0 -1
  278. package/lib/packlets/validation/string.js.map +0 -1
  279. package/lib/packlets/validation/traits.d.ts.map +0 -1
  280. package/lib/packlets/validation/traits.js.map +0 -1
  281. package/lib/packlets/validation/typeGuard.d.ts.map +0 -1
  282. package/lib/packlets/validation/typeGuard.js.map +0 -1
  283. package/lib/packlets/validation/validator.d.ts.map +0 -1
  284. package/lib/packlets/validation/validator.js.map +0 -1
  285. package/lib/packlets/validation/validatorBase.d.ts.map +0 -1
  286. package/lib/packlets/validation/validatorBase.js.map +0 -1
  287. package/lib/packlets/validation/validators.d.ts.map +0 -1
  288. package/lib/packlets/validation/validators.js.map +0 -1
@@ -0,0 +1,223 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ import { captureResult, DetailedResult, failWithDetail, Result, succeedWithDetail } from '../base';
24
+ import { ICollectible, CollectibleFactoryCallback, CollectibleFactory, CollectibleKey } from './collectible';
25
+ import { Collector, CollectorResultDetail } from './collector';
26
+ import { KeyValueEntry } from './common';
27
+
28
+ /**
29
+ * Parameters for constructing a {@link Collections.ConvertingCollector | ConvertingCollector}.
30
+ * @public
31
+ */
32
+ export interface IConvertingCollectorConstructorParams<
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ TITEM extends ICollectible<any, any>,
35
+ TSRC = TITEM
36
+ > {
37
+ /**
38
+ * The default {@link Collections.CollectibleFactory | factory} to create items.
39
+ */
40
+ factory: CollectibleFactory<TITEM, TSRC>;
41
+ /**
42
+ * An optional array of entries to add to the collector.
43
+ */
44
+ entries?: KeyValueEntry<CollectibleKey<TITEM>, TSRC>[];
45
+ }
46
+
47
+ /**
48
+ * A {@link Collector | collector} that collects {@link Collections.ICollectible | ICollectible} items,
49
+ * optionally converting them from a source representation to the target representation using a factory
50
+ * supplied at default or at the time of collection.
51
+ * @public
52
+ */
53
+ export class ConvertingCollector<
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ TITEM extends ICollectible<any, any>,
56
+ TSRC = TITEM
57
+ > extends Collector<TITEM> {
58
+ private _factory: CollectibleFactory<TITEM, TSRC>;
59
+
60
+ /**
61
+ * Constructs a new {@link Collections.ConvertingCollector | ConvertingCollector}.
62
+ * @param params - Parameters for constructing the collector.
63
+ */
64
+ public constructor(params: IConvertingCollectorConstructorParams<TITEM, TSRC>) {
65
+ super();
66
+ this._factory = params.factory;
67
+ params.entries?.forEach((entry) => {
68
+ this.getOrAdd(entry[0], entry[1]).orThrow();
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Creates a new {@link Collections.ConvertingCollector | ConvertingCollector}.
74
+ * @param params - Required parameters for constructing the collector.
75
+ * @returns Returns {@link Success | Success} with the new collector if it is created, or {@link Failure | Failure}
76
+ * with an error if the collector cannot be created.
77
+ */
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ public static createConvertingCollector<TITEM extends ICollectible<any, any>, TSRC = TITEM>(
80
+ params: IConvertingCollectorConstructorParams<TITEM, TSRC>
81
+ ): Result<ConvertingCollector<TITEM, TSRC>> {
82
+ return captureResult(() => new ConvertingCollector(params));
83
+ }
84
+
85
+ /**
86
+ * {@inheritdoc Collections.Collector.add}
87
+ */
88
+ public add(item: TITEM): DetailedResult<TITEM, CollectorResultDetail>;
89
+
90
+ /**
91
+ * Adds an item to the collector using the default {@link Collections.CollectibleFactory | factory}
92
+ * at a specified key, failing if an item with that key already exists.
93
+ * @param key - The key of the item to add.
94
+ * @param item - The source representation of the item to be added.
95
+ * @returns Returns {@link Success | Success} with the item if it is added, or {@link Failure | Failure} with
96
+ * an error if the item cannot be created and indexed.
97
+ * @public
98
+ */
99
+ public add(key: CollectibleKey<TITEM>, item: TSRC): DetailedResult<TITEM, CollectorResultDetail>;
100
+
101
+ /**
102
+ * Adds an item to the collector using a supplied {@link Collections.CollectibleFactoryCallback | factory callback}
103
+ * at a specified key, failing if an item with that key already exists or if the created item is invalid.
104
+ * @param key - The key of the item to add.
105
+ * @param callback - The factory callback to create the item.
106
+ * @returns Returns {@link Success | Success} with the item if it is added, or {@link Failure | Failure} with
107
+ * an error if the item cannot be created and indexed.
108
+ */
109
+ public add(
110
+ key: CollectibleKey<TITEM>,
111
+ cb: CollectibleFactoryCallback<TITEM>
112
+ ): DetailedResult<TITEM, CollectorResultDetail>;
113
+
114
+ public add(
115
+ keyOrItem: CollectibleKey<TITEM> | TITEM,
116
+ itemOrCb?: TSRC | CollectibleFactoryCallback<TITEM>
117
+ ): DetailedResult<TITEM, CollectorResultDetail> {
118
+ if (this._overloadIsItem(keyOrItem, itemOrCb)) {
119
+ return super.add(keyOrItem);
120
+ }
121
+ return this._buildItem(keyOrItem, itemOrCb!)
122
+ .withFailureDetail<CollectorResultDetail>('invalid-value')
123
+ .onSuccess((item) => {
124
+ return super.add(item);
125
+ });
126
+ }
127
+
128
+ /**
129
+ * {@inheritdoc Collections.Collector.(getOrAdd:1)}
130
+ */
131
+ public getOrAdd(item: TITEM): DetailedResult<TITEM, CollectorResultDetail>;
132
+
133
+ /**
134
+ * {@inheritdoc Collections.Collector.(getOrAdd:2)}
135
+ */
136
+ public getOrAdd(
137
+ key: CollectibleKey<TITEM>,
138
+ callback: CollectibleFactoryCallback<TITEM>
139
+ ): DetailedResult<TITEM, CollectorResultDetail>;
140
+
141
+ /**
142
+ * Gets an item by key if it exists, or creates a new item and adds it using the default {@link Collections.CollectibleFactory | factory} if not.
143
+ * @param key - The key of the item to retrieve.
144
+ * @param item - The source representation of the item to be added if it does not exist.
145
+ * @returns Returns {@link Success | Success} with the item if it exists or could be created, or {@link Failure | Failure} with an error if the
146
+ * item cannot be created and indexed.
147
+ */
148
+ public getOrAdd(key: CollectibleKey<TITEM>, item: TSRC): DetailedResult<TITEM, CollectorResultDetail>;
149
+
150
+ public getOrAdd(
151
+ keyOrItem: CollectibleKey<TITEM> | TITEM,
152
+ itemOrCb?: TSRC | CollectibleFactoryCallback<TITEM>
153
+ ): DetailedResult<TITEM, CollectorResultDetail> {
154
+ if (this._overloadIsItem(keyOrItem, itemOrCb)) {
155
+ return super.getOrAdd(keyOrItem);
156
+ }
157
+
158
+ if (this.has(keyOrItem)) {
159
+ return this.get(keyOrItem);
160
+ }
161
+
162
+ return this._buildItem(keyOrItem, itemOrCb!)
163
+ .withFailureDetail<CollectorResultDetail>('invalid-value')
164
+ .onSuccess((item) => {
165
+ if (item.key !== keyOrItem) {
166
+ return failWithDetail<TITEM, CollectorResultDetail>(
167
+ `${keyOrItem}: key mismatch - item has unexpected key ${item.key}`,
168
+ 'invalid-key'
169
+ );
170
+ }
171
+ return succeedWithDetail(item, 'success');
172
+ })
173
+ .onSuccess((item) => {
174
+ return super.add(item);
175
+ });
176
+ }
177
+
178
+ /**
179
+ * Helper method for derived classes to determine if a supplied
180
+ * itemOrCb parameter is a factory callback.
181
+ * @param itemOrCb - Overloaded parameter is either `CollectibleKey<TITEM>` or
182
+ * a {@link Collections.CollectibleFactoryCallback | factory callback}.
183
+ * @returns Returns `true` if the parameter is a factory callback, `false` otherwise.
184
+ * @public
185
+ */
186
+ protected _isFactoryCB(
187
+ itemOrCb: TSRC | CollectibleFactoryCallback<TITEM>
188
+ ): itemOrCb is CollectibleFactoryCallback<TITEM> {
189
+ return typeof itemOrCb === 'function';
190
+ }
191
+
192
+ /**
193
+ * Helper method for derived classes to determine if a supplied
194
+ * keyOrItem parameter is an item.
195
+ * @param keyOrItem - Overloaded parameter is either `CollectibleKey<TITEM>` or `TITEM`.
196
+ * @param itemOrCb - Overloaded parameter is either `TSRC`, a {@link Collections.CollectibleFactoryCallback | factory callback}
197
+ * or `undefined`.
198
+ * @returns Returns `true` if the parameter is an item, `false` otherwise.
199
+ * @public
200
+ */
201
+ protected _overloadIsItem(
202
+ keyOrItem: CollectibleKey<TITEM> | TITEM,
203
+ itemOrCb?: TSRC | CollectibleFactoryCallback<TITEM>
204
+ ): keyOrItem is TITEM {
205
+ return itemOrCb === undefined;
206
+ }
207
+
208
+ /**
209
+ * Helper method for derived classes to build an item from a key and a source representation using
210
+ * a default or supplied factory.
211
+ * @param key - The key of the item to build.
212
+ * @param itemOrCb - The source representation of the item to build, or a factory callback to create it.
213
+ * @returns Returns {@link Success | Success} with the item if it is built, or {@link Failure | Failure}
214
+ * with an error if the item cannot be built.
215
+ * @public
216
+ */
217
+ protected _buildItem(
218
+ key: CollectibleKey<TITEM>,
219
+ itemOrCb: TSRC | CollectibleFactoryCallback<TITEM>
220
+ ): Result<TITEM> {
221
+ return this._isFactoryCB(itemOrCb) ? itemOrCb(key, this.size) : this._factory(key, this.size, itemOrCb);
222
+ }
223
+ }
@@ -0,0 +1,164 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ import { DetailedResult, failWithDetail } from '../base';
24
+ import { IReadOnlyResultMap, ResultMapResultDetail } from './readonlyResultMap';
25
+ import { ResultMapValueFactory } from './resultMap';
26
+ import { KeyValueConverters } from './keyValueConverters';
27
+ import { CollectibleFactoryCallback, CollectibleKey, ICollectible } from './collectible';
28
+ import { CollectorResultDetail } from './collector';
29
+ import { ConvertingCollector } from './convertingCollector';
30
+ import { IReadOnlyCollectorValidator } from './collectorValidator';
31
+
32
+ /**
33
+ * Parameters for constructing a {@link Collections.ConvertingCollectorValidator | ConvertingCollectorValidator}.
34
+ * @public
35
+ */
36
+ export interface IConvertingCollectorValidatorCreateParams<
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ TITEM extends ICollectible<any, any>,
39
+ TSRC = TITEM
40
+ > {
41
+ collector: ConvertingCollector<TITEM, TSRC>;
42
+ converters: KeyValueConverters<CollectibleKey<TITEM>, TSRC>;
43
+ }
44
+
45
+ /**
46
+ * A {@link Collections.ConvertingCollector | ConvertingCollector} wrapper which validates weakly-typed keys
47
+ * and values before calling the wrapped collector. Unlike the basic {@link Collections.CollectorValidator | CollectorValidator},
48
+ * the converting collector expects the items to be in the source type of the converting collector, not the target type.
49
+ * @public
50
+ */
51
+ export class ConvertingCollectorValidator<
52
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
+ TITEM extends ICollectible<any, any>,
54
+ TSRC = TITEM
55
+ > implements IReadOnlyCollectorValidator<TITEM>
56
+ {
57
+ public readonly converters: KeyValueConverters<CollectibleKey<TITEM>, TSRC>;
58
+
59
+ public get map(): IReadOnlyResultMap<CollectibleKey<TITEM>, TITEM> {
60
+ return this._collector.toReadOnly();
61
+ }
62
+
63
+ protected _collector: ConvertingCollector<TITEM, TSRC>;
64
+
65
+ /**
66
+ * Constructs a new {@link Collections.ConvertingCollectorValidator | ConvertingCollectorValidator}.
67
+ * @param params - Required parameters for constructing the collector validator.
68
+ */
69
+ public constructor(params: IConvertingCollectorValidatorCreateParams<TITEM, TSRC>) {
70
+ this._collector = params.collector;
71
+ this.converters = params.converters;
72
+ }
73
+
74
+ /**
75
+ * {@inheritdoc Collections.ConvertingCollector.(add:1)}
76
+ */
77
+ public add(key: string, value: unknown): DetailedResult<TITEM, CollectorResultDetail>;
78
+
79
+ /**
80
+ * {@inheritdoc Collections.ConvertingCollector.(add:2)}
81
+ */
82
+ public add(
83
+ key: string,
84
+ factory: ResultMapValueFactory<CollectibleKey<TITEM>, TITEM>
85
+ ): DetailedResult<TITEM, CollectorResultDetail>;
86
+ public add(
87
+ key: string,
88
+ valueOrFactory: unknown | ResultMapValueFactory<CollectibleKey<TITEM>, TITEM>
89
+ ): DetailedResult<TITEM, CollectorResultDetail> {
90
+ if (this.has(key)) {
91
+ return failWithDetail(`${key}: already exists`, 'exists');
92
+ }
93
+ return this.getOrAdd(key, valueOrFactory);
94
+ }
95
+
96
+ /**
97
+ * {@inheritdoc Collections.Collector.get}
98
+ */
99
+ public get(key: string): DetailedResult<TITEM, ResultMapResultDetail> {
100
+ return this.converters.convertKey(key).onSuccess((k) => {
101
+ return this._collector.get(k);
102
+ });
103
+ }
104
+
105
+ /**
106
+ * {@inheritdoc Collections.ConvertingCollector.(getOrAdd:3)}
107
+ */
108
+ public getOrAdd(key: string, value: unknown): DetailedResult<TITEM, CollectorResultDetail>;
109
+
110
+ /**
111
+ * {@inheritdoc Collections.Collector.(getOrAdd:2)}
112
+ */
113
+ public getOrAdd(
114
+ key: string,
115
+ factory: ResultMapValueFactory<CollectibleKey<TITEM>, TITEM>
116
+ ): DetailedResult<TITEM, CollectorResultDetail>;
117
+
118
+ public getOrAdd(
119
+ key: string,
120
+ valueOrFactory: unknown | CollectibleFactoryCallback<TITEM>
121
+ ): DetailedResult<TITEM, CollectorResultDetail> {
122
+ if (!this._isCollectibleFactoryCallback(valueOrFactory)) {
123
+ const converted = this.converters.convertEntry([key, valueOrFactory]);
124
+ if (converted.isFailure()) {
125
+ return failWithDetail(converted.message, converted.detail);
126
+ }
127
+ const [vk, vs] = converted.value;
128
+ return this._collector.getOrAdd(vk, vs);
129
+ } else {
130
+ const converted = this.converters.convertKey(key);
131
+ if (converted.isFailure()) {
132
+ return failWithDetail(converted.message, converted.detail);
133
+ }
134
+ return this._collector.getOrAdd(converted.value, valueOrFactory);
135
+ }
136
+ }
137
+
138
+ /**
139
+ * {@inheritdoc Collections.ResultMap.has}
140
+ */
141
+ public has(key: string): boolean {
142
+ return this._collector.has(key as CollectibleKey<TITEM>);
143
+ }
144
+
145
+ /**
146
+ * {@inheritdoc Collections.Collector.toReadOnly}
147
+ */
148
+ public toReadOnly(): IReadOnlyCollectorValidator<TITEM> {
149
+ return this;
150
+ }
151
+
152
+ /**
153
+ * Determines if a value is a {@link Collections.CollectibleFactoryCallback | CollectibleFactoryCallback}.
154
+ * @param value - The value to check.
155
+ * @returns `true` if the value is a {@link Collections.CollectibleFactoryCallback | CollectibleFactoryCallback},
156
+ * `false` otherwise.
157
+ * @public
158
+ */
159
+ protected _isCollectibleFactoryCallback(
160
+ value: unknown | CollectibleFactoryCallback<TITEM>
161
+ ): value is CollectibleFactoryCallback<TITEM> {
162
+ return typeof value === 'function';
163
+ }
164
+ }
@@ -0,0 +1,39 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ import * as Utils from './utils';
24
+
25
+ export * from './collectible';
26
+ export * from './convertingCollector';
27
+ export * from './collector';
28
+ export * from './collectorValidator';
29
+ export * from './convertingCollectorValidator';
30
+ export * from './validatingConvertingCollector';
31
+ export * from './common';
32
+ export * from './keyValueConverters';
33
+ export * from './readonlyResultMap';
34
+ export * from './resultMap';
35
+ export * from './resultMapValidator';
36
+ export * from './validatingCollector';
37
+ export * from './validatingResultMap';
38
+
39
+ export { Utils };
@@ -0,0 +1,142 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ import {
24
+ DetailedResult,
25
+ failWithDetail,
26
+ MessageAggregator,
27
+ Result,
28
+ succeed,
29
+ succeedWithDetail
30
+ } from '../base';
31
+ import { Converter, ConverterFunc, Converters } from '../conversion';
32
+ import { Validator } from '../validation';
33
+ import { KeyValueEntry } from './common';
34
+ import { ResultMapResultDetail } from './readonlyResultMap';
35
+
36
+ /**
37
+ * Parameters for constructing a {@link Collections.KeyValueConverters | KeyValueConverters} instance.
38
+ * @public
39
+ */
40
+ export interface IKeyValueConverterConstructorParams<TK extends string = string, TV = unknown> {
41
+ /**
42
+ * Required key {@link Validator | validator}, {@link Converter | converter},
43
+ * or {@link Conversion.ConverterFunc | converter function}.
44
+ */
45
+ key: Validator<TK, unknown> | Converter<TK, unknown> | ConverterFunc<TK, unknown>;
46
+
47
+ /**
48
+ * Required value {@link Validator | validator}, {@link Converter | converter},
49
+ * or {@link Conversion.ConverterFunc | converter function}.
50
+ */
51
+ value: Validator<TV, unknown> | Converter<TV, unknown> | ConverterFunc<TV, unknown>;
52
+ }
53
+
54
+ /**
55
+ * Helper class for converting strongly-typed keys, values, or entries
56
+ * from unknown values.
57
+ * @public
58
+ */
59
+ export class KeyValueConverters<TK extends string = string, TV = unknown> {
60
+ /**
61
+ * Required key {@link Validator | validator} or {@link Converter | converter}.
62
+ */
63
+ public readonly key: Validator<TK, unknown> | Converter<TK, unknown>;
64
+ /**
65
+ * Required value {@link Validator | validator} or {@link Converter | converter}.
66
+ */
67
+ public readonly value: Validator<TV, unknown> | Converter<TV, unknown>;
68
+
69
+ /**
70
+ * Constructs a new key-value validator.
71
+ * @param key - Required key {@link Validator | validator}, {@link Converter | converter},
72
+ * or {@link Conversion.ConverterFunc | converter function}.
73
+ * @param value - Required value {@link Validator | validator}, {@link Converter | converter},
74
+ * or {@link Conversion.ConverterFunc | converter function}.
75
+ */
76
+ public constructor({ key, value }: IKeyValueConverterConstructorParams<TK, TV>) {
77
+ this.key = typeof key === 'function' ? Converters.generic(key) : key;
78
+ this.value = typeof value === 'function' ? Converters.generic(value) : value;
79
+ }
80
+
81
+ /**
82
+ * Converts a supplied unknown to a valid key value of type `<TK>`.
83
+ * @param key - The unknown to be converted.
84
+ * @returns `Success` with the converted key value and 'success' detail if the key is valid,
85
+ * or `Failure` with an error message and 'invalid-key' detail if the key is invalid.
86
+ */
87
+ public convertKey(key: unknown): DetailedResult<TK, ResultMapResultDetail> {
88
+ return this.key.convert(key).withFailureDetail('invalid-key');
89
+ }
90
+
91
+ /**
92
+ * Converts a supplied unknown to a valid value of type `<TV>`.
93
+ * @param key - The unknown to be converted.
94
+ * @returns `Success` with the converted value and 'success' detail if the value is valid,
95
+ * or `Failure` with an error message and 'invalid-value' detail if the value is invalid.
96
+ */
97
+ public convertValue(key: unknown): DetailedResult<TV, ResultMapResultDetail> {
98
+ return this.value.convert(key).withFailureDetail('invalid-value');
99
+ }
100
+
101
+ /**
102
+ * Converts a supplied unknown to a valid entry of type `[<TK>, <TV>]`.
103
+ * @param entry - The unknown to be converted.
104
+ * @returns `Success` with the converted entry and 'success' detail if the entry
105
+ * is valid, or `Failure` with an error message and 'invalid-key' or 'invalid-value' detail if
106
+ * the entry is invalid
107
+ */
108
+ public convertEntry(entry: unknown): DetailedResult<KeyValueEntry<TK, TV>, ResultMapResultDetail> {
109
+ if (Array.isArray(entry) && entry.length === 2) {
110
+ const errors = new MessageAggregator();
111
+ const key = this.convertKey(entry[0]).aggregateError(errors).orDefault();
112
+ const value = this.convertValue(entry[1]).aggregateError(errors).orDefault();
113
+ if (key && value) {
114
+ return succeedWithDetail([key, value], 'success');
115
+ }
116
+ const detail = key ? 'invalid-value' : 'invalid-key';
117
+ return failWithDetail(`invalid entry: ${errors.toString()}`, detail);
118
+ }
119
+ /* c8 ignore next 2 */
120
+ return failWithDetail(`malformed entry: "${JSON.stringify(entry)}"`, 'invalid-value');
121
+ }
122
+
123
+ /**
124
+ * Converts a supplied iterable of unknowns to valid key-value pairs.
125
+ * @param entries - The iterable of unknowns to be converted.
126
+ * @returns `Success` with an array of converted key-value pairs if all entries are valid,
127
+ * or `Failure` with an error message if any entry is invalid.
128
+ */
129
+ public convertEntries(entries: Iterable<unknown>): Result<KeyValueEntry<TK, TV>[]> {
130
+ const errors = new MessageAggregator();
131
+ const converted: KeyValueEntry<TK, TV>[] = [];
132
+ for (const element of entries) {
133
+ this.convertEntry(element)
134
+ .onSuccess((e) => {
135
+ converted.push(e);
136
+ return succeedWithDetail(e, 'success');
137
+ })
138
+ .aggregateError(errors);
139
+ }
140
+ return errors.returnOrReport(succeed(converted));
141
+ }
142
+ }
@@ -0,0 +1,99 @@
1
+ /*
2
+ * Copyright (c) 2025 Erik Fortune
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ import { DetailedResult } from '../base';
24
+ import { KeyValueEntry } from './common';
25
+
26
+ /**
27
+ * Additional success or failure details for {@link Collections.ResultMap | ResultMap} calls.
28
+ * @public
29
+ */
30
+ export type ResultMapResultDetail =
31
+ | 'added'
32
+ | 'deleted'
33
+ | 'exists'
34
+ | 'failure'
35
+ | 'invalid-key'
36
+ | 'invalid-value'
37
+ | 'not-found'
38
+ | 'success'
39
+ | 'updated';
40
+
41
+ /**
42
+ * Callback for {@link Collections.ResultMap | ResultMap} `forEach` method.
43
+ * @public
44
+ */
45
+ export type ResultMapForEachCb<TK extends string = string, TE = unknown> = (
46
+ value: TE,
47
+ key: TK,
48
+ map: IReadOnlyResultMap<TK, TE>,
49
+ thisArg?: unknown
50
+ ) => void;
51
+
52
+ /**
53
+ * A readonly `ReadonlyMap<TK, TV>`-like object which reports success or failure
54
+ * with additional details using the
55
+ * {@link https://github.com/ErikFortune/fgv/tree/main/libraries/ts-utils#the-result-pattern | result pattern}.
56
+ * @public
57
+ */
58
+ export interface IReadOnlyResultMap<TK extends string = string, TV = unknown> {
59
+ /**
60
+ * {@inheritdoc Collections.ResultMap.size}
61
+ */
62
+ readonly size: number;
63
+
64
+ /**
65
+ * {@inheritdoc Collections.ResultMap.entries}
66
+ */
67
+ entries(): IterableIterator<KeyValueEntry<TK, TV>>;
68
+
69
+ /**
70
+ * {@inheritdoc Collections.ResultMap.forEach}
71
+ */
72
+ forEach(cb: ResultMapForEachCb, arg?: unknown): void;
73
+
74
+ /**
75
+ * {@inheritdoc Collections.ResultMap.get}
76
+ */
77
+ get(key: TK): DetailedResult<TV, ResultMapResultDetail>;
78
+
79
+ /**
80
+ * {@inheritdoc Collections.ResultMap.has}
81
+ */
82
+ has(key: TK): boolean;
83
+
84
+ /**
85
+ * {@inheritdoc Collections.ResultMap.keys}
86
+ */
87
+ keys(): IterableIterator<TK>;
88
+
89
+ /**
90
+ * {@inheritdoc Collections.ResultMap.values}
91
+ */
92
+ values(): IterableIterator<TV>;
93
+
94
+ /**
95
+ * Gets an iterator over the map entries.
96
+ * @returns An iterator over the map entries.
97
+ */
98
+ [Symbol.iterator](): IterableIterator<KeyValueEntry<TK, TV>>;
99
+ }