@fgv/ts-utils 5.0.1-9 → 5.0.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 (89) hide show
  1. package/dist/index.js +32 -0
  2. package/dist/packlets/base/brand.js +23 -0
  3. package/dist/packlets/base/index.js +28 -0
  4. package/dist/packlets/base/mapResults.js +217 -0
  5. package/dist/packlets/base/messageAggregator.js +110 -0
  6. package/dist/packlets/base/normalize.js +132 -0
  7. package/dist/packlets/base/result.js +611 -0
  8. package/dist/packlets/base/utils.js +185 -0
  9. package/dist/packlets/collections/collectible.js +82 -0
  10. package/dist/packlets/collections/collector.js +189 -0
  11. package/dist/packlets/collections/collectorValidator.js +94 -0
  12. package/dist/packlets/collections/common.js +23 -0
  13. package/dist/packlets/collections/convertingCollector.js +118 -0
  14. package/dist/packlets/collections/convertingCollectorValidator.js +95 -0
  15. package/dist/packlets/collections/index.js +37 -0
  16. package/dist/packlets/collections/keyValueConverters.js +100 -0
  17. package/dist/packlets/collections/readonlyResultMap.js +23 -0
  18. package/dist/packlets/collections/resultMap.js +214 -0
  19. package/dist/packlets/collections/resultMapValidator.js +122 -0
  20. package/dist/packlets/collections/utils.js +31 -0
  21. package/dist/packlets/collections/validatingCollector.js +63 -0
  22. package/dist/packlets/collections/validatingConvertingCollector.js +64 -0
  23. package/dist/packlets/collections/validatingResultMap.js +57 -0
  24. package/dist/packlets/conversion/baseConverter.js +242 -0
  25. package/dist/packlets/conversion/converter.js +2 -0
  26. package/dist/packlets/conversion/converters.js +637 -0
  27. package/dist/packlets/conversion/defaultingConverter.js +149 -0
  28. package/dist/packlets/conversion/index.js +29 -0
  29. package/dist/packlets/conversion/objectConverter.js +141 -0
  30. package/dist/packlets/conversion/stringConverter.js +93 -0
  31. package/dist/packlets/hash/crcNormalizer.js +114 -0
  32. package/dist/packlets/hash/hashingNormalizer.js +92 -0
  33. package/dist/packlets/hash/index.js +24 -0
  34. package/dist/packlets/logging/index.js +24 -0
  35. package/dist/packlets/logging/logReporter.js +114 -0
  36. package/dist/packlets/logging/logger.js +258 -0
  37. package/dist/packlets/validation/array.js +64 -0
  38. package/dist/packlets/validation/boolean.js +52 -0
  39. package/dist/packlets/validation/classes.js +31 -0
  40. package/dist/packlets/validation/common.js +23 -0
  41. package/dist/packlets/validation/field.js +67 -0
  42. package/dist/packlets/validation/genericValidator.js +160 -0
  43. package/dist/packlets/validation/index.js +29 -0
  44. package/dist/packlets/validation/number.js +52 -0
  45. package/dist/packlets/validation/object.js +134 -0
  46. package/dist/packlets/validation/oneOf.js +58 -0
  47. package/dist/packlets/validation/string.js +52 -0
  48. package/dist/packlets/validation/traits.js +56 -0
  49. package/dist/packlets/validation/typeGuard.js +59 -0
  50. package/dist/packlets/validation/validator.js +23 -0
  51. package/dist/packlets/validation/validatorBase.js +37 -0
  52. package/dist/packlets/validation/validators.js +191 -0
  53. package/dist/test/helpers/jest/helpers/fsHelpers.js +139 -0
  54. package/dist/test/helpers/jest/helpers/index.js +2 -0
  55. package/dist/test/helpers/jest/index.js +17 -0
  56. package/dist/test/helpers/jest/matchers/index.js +14 -0
  57. package/dist/test/helpers/jest/matchers/toFail/index.js +23 -0
  58. package/dist/test/helpers/jest/matchers/toFail/predicate.js +5 -0
  59. package/dist/test/helpers/jest/matchers/toFailTest/index.js +28 -0
  60. package/dist/test/helpers/jest/matchers/toFailTest/predicate.js +8 -0
  61. package/dist/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/index.js +24 -0
  62. package/dist/test/helpers/jest/matchers/toFailTestAndMatchSnapshot/predicate.js +11 -0
  63. package/dist/test/helpers/jest/matchers/toFailTestWith/index.js +29 -0
  64. package/dist/test/helpers/jest/matchers/toFailTestWith/predicate.js +26 -0
  65. package/dist/test/helpers/jest/matchers/toFailWith/index.js +27 -0
  66. package/dist/test/helpers/jest/matchers/toFailWith/predicate.js +16 -0
  67. package/dist/test/helpers/jest/matchers/toFailWithDetail/index.js +27 -0
  68. package/dist/test/helpers/jest/matchers/toFailWithDetail/predicate.js +22 -0
  69. package/dist/test/helpers/jest/matchers/toSucceed/index.js +23 -0
  70. package/dist/test/helpers/jest/matchers/toSucceed/predicate.js +5 -0
  71. package/dist/test/helpers/jest/matchers/toSucceedAndMatchInlineSnapshot/index.js +24 -0
  72. package/dist/test/helpers/jest/matchers/toSucceedAndMatchSnapshot/index.js +24 -0
  73. package/dist/test/helpers/jest/matchers/toSucceedAndSatisfy/index.js +53 -0
  74. package/dist/test/helpers/jest/matchers/toSucceedAndSatisfy/predicate.js +17 -0
  75. package/dist/test/helpers/jest/matchers/toSucceedWith/index.js +27 -0
  76. package/dist/test/helpers/jest/matchers/toSucceedWith/predicate.js +12 -0
  77. package/dist/test/helpers/jest/matchers/toSucceedWithDetail/index.js +27 -0
  78. package/dist/test/helpers/jest/matchers/toSucceedWithDetail/predicate.js +17 -0
  79. package/dist/test/helpers/jest/resolvers/cli.js +10 -0
  80. package/dist/test/helpers/jest/resolvers/ide.js +10 -0
  81. package/dist/test/helpers/jest/ts-utils.js +2 -0
  82. package/dist/test/helpers/jest/types/index.js +3 -0
  83. package/dist/test/helpers/jest/utils/matcherHelpers.js +47 -0
  84. package/dist/test/helpers/jest/utils/snapshotResolver.js +11 -0
  85. package/dist/test/unit/collections/helpers.js +106 -0
  86. package/dist/test/unit/hashTextEncodeCompat.js +23 -0
  87. package/dist/tsdoc-metadata.json +1 -1
  88. package/lib/packlets/hash/crcNormalizer.js +42 -2
  89. package/package.json +12 -2
@@ -0,0 +1,95 @@
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
+ import { failWithDetail } from '../base';
23
+ /**
24
+ * A {@link Collections.ConvertingCollector | ConvertingCollector} wrapper which validates weakly-typed keys
25
+ * and values before calling the wrapped collector. Unlike the basic {@link Collections.CollectorValidator | CollectorValidator},
26
+ * the converting collector expects the items to be in the source type of the converting collector, not the target type.
27
+ * @public
28
+ */
29
+ export class ConvertingCollectorValidator {
30
+ get map() {
31
+ return this._collector.toReadOnly();
32
+ }
33
+ /**
34
+ * Constructs a new {@link Collections.ConvertingCollectorValidator | ConvertingCollectorValidator}.
35
+ * @param params - Required parameters for constructing the collector validator.
36
+ */
37
+ constructor(params) {
38
+ this._collector = params.collector;
39
+ this.converters = params.converters;
40
+ }
41
+ add(key, valueOrFactory) {
42
+ if (this.has(key)) {
43
+ return failWithDetail(`${key}: already exists`, 'exists');
44
+ }
45
+ return this.getOrAdd(key, valueOrFactory);
46
+ }
47
+ /**
48
+ * {@inheritdoc Collections.Collector.get}
49
+ */
50
+ get(key) {
51
+ return this.converters.convertKey(key).onSuccess((k) => {
52
+ return this._collector.get(k);
53
+ });
54
+ }
55
+ getOrAdd(key, valueOrFactory) {
56
+ if (!this._isCollectibleFactoryCallback(valueOrFactory)) {
57
+ const converted = this.converters.convertEntry([key, valueOrFactory]);
58
+ if (converted.isFailure()) {
59
+ return failWithDetail(converted.message, converted.detail);
60
+ }
61
+ const [vk, vs] = converted.value;
62
+ return this._collector.getOrAdd(vk, vs);
63
+ }
64
+ else {
65
+ const converted = this.converters.convertKey(key);
66
+ if (converted.isFailure()) {
67
+ return failWithDetail(converted.message, converted.detail);
68
+ }
69
+ return this._collector.getOrAdd(converted.value, valueOrFactory);
70
+ }
71
+ }
72
+ /**
73
+ * {@inheritdoc Collections.ResultMap.has}
74
+ */
75
+ has(key) {
76
+ return this._collector.has(key);
77
+ }
78
+ /**
79
+ * {@inheritdoc Collections.Collector.toReadOnly}
80
+ */
81
+ toReadOnly() {
82
+ return this;
83
+ }
84
+ /**
85
+ * Determines if a value is a {@link Collections.CollectibleFactoryCallback | CollectibleFactoryCallback}.
86
+ * @param value - The value to check.
87
+ * @returns `true` if the value is a {@link Collections.CollectibleFactoryCallback | CollectibleFactoryCallback},
88
+ * `false` otherwise.
89
+ * @public
90
+ */
91
+ _isCollectibleFactoryCallback(value) {
92
+ return typeof value === 'function';
93
+ }
94
+ }
95
+ //# sourceMappingURL=convertingCollectorValidator.js.map
@@ -0,0 +1,37 @@
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
+ import * as Utils from './utils';
23
+ export * from './collectible';
24
+ export * from './convertingCollector';
25
+ export * from './collector';
26
+ export * from './collectorValidator';
27
+ export * from './convertingCollectorValidator';
28
+ export * from './validatingConvertingCollector';
29
+ export * from './common';
30
+ export * from './keyValueConverters';
31
+ export * from './readonlyResultMap';
32
+ export * from './resultMap';
33
+ export * from './resultMapValidator';
34
+ export * from './validatingCollector';
35
+ export * from './validatingResultMap';
36
+ export { Utils };
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,100 @@
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
+ import { failWithDetail, MessageAggregator, succeed, succeedWithDetail } from '../base';
23
+ import { Converters } from '../conversion';
24
+ /**
25
+ * Helper class for converting strongly-typed keys, values, or entries
26
+ * from unknown values.
27
+ * @public
28
+ */
29
+ export class KeyValueConverters {
30
+ /**
31
+ * Constructs a new key-value validator.
32
+ * @param key - Required key {@link Validator | validator}, {@link Converter | converter},
33
+ * or {@link Conversion.ConverterFunc | converter function}.
34
+ * @param value - Required value {@link Validator | validator}, {@link Converter | converter},
35
+ * or {@link Conversion.ConverterFunc | converter function}.
36
+ */
37
+ constructor({ key, value }) {
38
+ this.key = typeof key === 'function' ? Converters.generic(key) : key;
39
+ this.value = typeof value === 'function' ? Converters.generic(value) : value;
40
+ }
41
+ /**
42
+ * Converts a supplied unknown to a valid key value of type `<TK>`.
43
+ * @param key - The unknown to be converted.
44
+ * @returns `Success` with the converted key value and 'success' detail if the key is valid,
45
+ * or `Failure` with an error message and 'invalid-key' detail if the key is invalid.
46
+ */
47
+ convertKey(key) {
48
+ return this.key.convert(key).withFailureDetail('invalid-key');
49
+ }
50
+ /**
51
+ * Converts a supplied unknown to a valid value of type `<TV>`.
52
+ * @param key - The unknown to be converted.
53
+ * @returns `Success` with the converted value and 'success' detail if the value is valid,
54
+ * or `Failure` with an error message and 'invalid-value' detail if the value is invalid.
55
+ */
56
+ convertValue(key) {
57
+ return this.value.convert(key).withFailureDetail('invalid-value');
58
+ }
59
+ /**
60
+ * Converts a supplied unknown to a valid entry of type `[<TK>, <TV>]`.
61
+ * @param entry - The unknown to be converted.
62
+ * @returns `Success` with the converted entry and 'success' detail if the entry
63
+ * is valid, or `Failure` with an error message and 'invalid-key' or 'invalid-value' detail if
64
+ * the entry is invalid
65
+ */
66
+ convertEntry(entry) {
67
+ if (Array.isArray(entry) && entry.length === 2) {
68
+ const errors = new MessageAggregator();
69
+ const key = this.convertKey(entry[0]).aggregateError(errors).orDefault();
70
+ const value = this.convertValue(entry[1]).aggregateError(errors).orDefault();
71
+ if (key && value) {
72
+ return succeedWithDetail([key, value], 'success');
73
+ }
74
+ const detail = key ? 'invalid-value' : 'invalid-key';
75
+ return failWithDetail(`invalid entry: ${errors.toString()}`, detail);
76
+ }
77
+ /* c8 ignore next 2 */
78
+ return failWithDetail(`malformed entry: "${JSON.stringify(entry)}"`, 'invalid-value');
79
+ }
80
+ /**
81
+ * Converts a supplied iterable of unknowns to valid key-value pairs.
82
+ * @param entries - The iterable of unknowns to be converted.
83
+ * @returns `Success` with an array of converted key-value pairs if all entries are valid,
84
+ * or `Failure` with an error message if any entry is invalid.
85
+ */
86
+ convertEntries(entries) {
87
+ const errors = new MessageAggregator();
88
+ const converted = [];
89
+ for (const element of entries) {
90
+ this.convertEntry(element)
91
+ .onSuccess((e) => {
92
+ converted.push(e);
93
+ return succeedWithDetail(e, 'success');
94
+ })
95
+ .aggregateError(errors);
96
+ }
97
+ return errors.returnOrReport(succeed(converted));
98
+ }
99
+ }
100
+ //# sourceMappingURL=keyValueConverters.js.map
@@ -0,0 +1,23 @@
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
+ export {};
23
+ //# sourceMappingURL=readonlyResultMap.js.map
@@ -0,0 +1,214 @@
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
+ import { captureResult, failWithDetail, succeed, succeedWithDetail } from '../base';
23
+ import { isIterable } from './utils';
24
+ /**
25
+ * A {@link Collections.ResultMap | ResultMap} class as a `Map<TK, TV>`-like object which
26
+ * reports success or failure with additional details using the
27
+ * {@link https://github.com/ErikFortune/fgv/tree/main/libraries/ts-utils#the-result-pattern | result pattern}.
28
+ * @public
29
+ */
30
+ export class ResultMap {
31
+ /**
32
+ * Constructs a new {@link Collections.ResultMap | ResultMap}.
33
+ * @param iterableOrParams - An iterable to initialize the map, or a set of parameters
34
+ * to configure the map.
35
+ */
36
+ constructor(iterableOrParams) {
37
+ const params = isIterable(iterableOrParams) ? { entries: iterableOrParams } : iterableOrParams !== null && iterableOrParams !== void 0 ? iterableOrParams : {};
38
+ this._inner = new Map(params.entries);
39
+ }
40
+ /**
41
+ * Creates a new {@link Collections.ResultMap | ResultMap}.
42
+ * @param elementsOrParams - An optional iterable to initialize the map, or a set of parameters
43
+ * to configure the map.
44
+ * @returns `Success` with the new map, or `Failure` with error details
45
+ * if an error occurred.
46
+ * @public
47
+ */
48
+ static create(elementsOrParams) {
49
+ return captureResult(() => new ResultMap(elementsOrParams));
50
+ }
51
+ /**
52
+ * Sets a key/value pair in the map if the key does not already exist.
53
+ * @param key - The key to set.
54
+ * @param value - The value to set.
55
+ * @returns `Success` with the value and detail `added` if the key was added,
56
+ * `Failure` with detail `exists` if the key already exists. Fails with detail
57
+ * 'invalid-key' or 'invalid-value' and an error message if either is invalid.
58
+ */
59
+ add(key, value) {
60
+ if (this._inner.has(key)) {
61
+ return failWithDetail(`${key}: already exists.`, 'exists');
62
+ }
63
+ this._inner.set(key, value);
64
+ return succeedWithDetail(value, 'added');
65
+ }
66
+ /**
67
+ * Clears the map.
68
+ */
69
+ clear() {
70
+ this._inner.clear();
71
+ }
72
+ /**
73
+ * Deletes a key from the map.
74
+ * @param key - The key to delete.
75
+ * @returns `Success` with the previous value and the detail 'deleted'
76
+ * if the key was found and deleted, `Failure` with detail 'not-found'
77
+ * if the key was not found, or with detail 'invalid-key' if the key is invalid.
78
+ */
79
+ delete(key) {
80
+ const was = this._inner.get(key);
81
+ if (this._inner.delete(key)) {
82
+ return succeedWithDetail(was, 'deleted');
83
+ }
84
+ return failWithDetail(`${key}: not found.`, 'not-found');
85
+ }
86
+ /**
87
+ * Returns an iterator over the map entries.
88
+ * @returns An iterator over the map entries.
89
+ */
90
+ entries() {
91
+ return this._inner.entries();
92
+ }
93
+ /**
94
+ * Calls a function for each entry in the map.
95
+ * @param cb - The function to call for each entry.
96
+ * @param arg - An optional argument to pass to the callback.
97
+ */
98
+ forEach(cb, arg) {
99
+ for (const [key, value] of this._inner.entries()) {
100
+ cb(value, key, this, arg);
101
+ }
102
+ }
103
+ /**
104
+ * Gets a value from the map.
105
+ * @param key - The key to retrieve.
106
+ * @returns `Success` with the value and detail `exists` if the key was found,
107
+ * `Failure` with detail `not-found` if the key was not found or with detail
108
+ * `invalid-key` if the key is invalid.
109
+ */
110
+ get(key) {
111
+ if (this._inner.has(key)) {
112
+ return succeedWithDetail(this._inner.get(key), 'exists');
113
+ }
114
+ return failWithDetail(`${key}: not found.`, 'not-found');
115
+ }
116
+ getOrAdd(key, valueOrFactory) {
117
+ if (this._inner.has(key)) {
118
+ return succeedWithDetail(this._inner.get(key), 'exists');
119
+ }
120
+ const factory = this._isResultMapValueFactory(valueOrFactory)
121
+ ? valueOrFactory
122
+ : () => succeed(valueOrFactory);
123
+ return factory(key)
124
+ .onSuccess((val) => {
125
+ this._inner.set(key, val);
126
+ return succeedWithDetail(val, 'added');
127
+ })
128
+ .withDetail('invalid-value', 'added');
129
+ }
130
+ /**
131
+ * Returns `true` if the map contains a key.
132
+ * @param key - The key to check.
133
+ * @returns `true` if the key exists, `false` otherwise.
134
+ */
135
+ has(key) {
136
+ return this._inner.has(key);
137
+ }
138
+ /**
139
+ * Returns an iterator over the map keys.
140
+ * @returns An iterator over the map keys.
141
+ */
142
+ keys() {
143
+ return this._inner.keys();
144
+ }
145
+ /**
146
+ * Sets a key/value pair in the map.
147
+ * @param key - The key to set.
148
+ * @param value - The value to set.
149
+ * @returns `Success` with the new value and the detail `updated` if the
150
+ * key was found and updated, `Success` with the new value and detail
151
+ * `added` if the key was not found and added. Fails with detail
152
+ * 'invalid-key' or 'invalid-value' and an error message if either is invalid.
153
+ */
154
+ set(key, value) {
155
+ const detail = this._inner.has(key) ? 'updated' : 'added';
156
+ this._inner.set(key, value);
157
+ return succeedWithDetail(value, detail);
158
+ }
159
+ /**
160
+ * Returns the number of entries in the map.
161
+ */
162
+ get size() {
163
+ return this._inner.size;
164
+ }
165
+ /**
166
+ * Updates an existing key in the map - the map is not updated if the key does
167
+ * not exist.
168
+ * @param key - The key to update.
169
+ * @param value - The value to set.
170
+ * @returns `Success` with the value and detail 'exists' if the key was found
171
+ * and the value updated, `Failure` an error message and with detail `not-found`
172
+ * if the key was not found, or with detail 'invalid-key' or 'invalid-value'
173
+ * if either is invalid.
174
+ */
175
+ update(key, value) {
176
+ if (this._inner.has(key)) {
177
+ this._inner.set(key, value);
178
+ return succeedWithDetail(value, 'updated');
179
+ }
180
+ return failWithDetail(`${key}: not found.`, 'not-found');
181
+ }
182
+ /**
183
+ * Returns an iterator over the map values.
184
+ * @returns An iterator over the map values.
185
+ */
186
+ values() {
187
+ return this._inner.values();
188
+ }
189
+ /**
190
+ * Gets an iterator over the map entries.
191
+ * @returns An iterator over the map entries.
192
+ */
193
+ [Symbol.iterator]() {
194
+ return this._inner[Symbol.iterator]();
195
+ }
196
+ /**
197
+ * Gets a readonly version of this map.
198
+ * @returns A readonly version of this map.
199
+ */
200
+ toReadOnly() {
201
+ return this;
202
+ }
203
+ /**
204
+ * Determines if a value is a {@link Collections.ResultMapValueFactory | ResultMapValueFactory}.
205
+ * @param value - The value to check.
206
+ * @returns `true` if the value is a {@link Collections.ResultMapValueFactory | ResultMapValueFactory},
207
+ * `false` otherwise.
208
+ * @public
209
+ */
210
+ _isResultMapValueFactory(value) {
211
+ return typeof value === 'function';
212
+ }
213
+ }
214
+ //# sourceMappingURL=resultMap.js.map
@@ -0,0 +1,122 @@
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
+ import { failWithDetail, succeedWithDetail } from '../base';
23
+ /**
24
+ * A {@link Collections.ResultMap | ResultMap} wrapper which validates weakly-typed keys
25
+ * before calling the wrapped result map.
26
+ * @public
27
+ */
28
+ export class ResultMapValidator {
29
+ get map() {
30
+ return this._map;
31
+ }
32
+ /**
33
+ * Constructs a new {@link Collections.ResultMapValidator | ResultMapValidator}.
34
+ * @param params - Required parameters for constructing the result map validator.
35
+ */
36
+ constructor(params) {
37
+ this._map = params.map;
38
+ this.converters = params.converters;
39
+ }
40
+ /**
41
+ * {@inheritdoc Collections.ResultMap.add}
42
+ */
43
+ add(key, value) {
44
+ return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
45
+ return this._map.add(vk, vv);
46
+ });
47
+ }
48
+ /**
49
+ * {@inheritdoc Collections.ResultMap.delete}
50
+ */
51
+ delete(key) {
52
+ return this.converters.convertKey(key).onSuccess((k) => {
53
+ return this._map.delete(k);
54
+ });
55
+ }
56
+ /**
57
+ * {@inheritdoc Collections.ResultMap.get}
58
+ */
59
+ get(key) {
60
+ return this.converters.convertKey(key).onSuccess((k) => {
61
+ return this._map.get(k);
62
+ });
63
+ }
64
+ getOrAdd(key, valueOrFactory) {
65
+ if (!this._isResultMapValueFactory(valueOrFactory)) {
66
+ return this.converters.convertEntry([key, valueOrFactory]).onSuccess(([vk, vv]) => {
67
+ return this._map.getOrAdd(vk, vv);
68
+ });
69
+ }
70
+ else {
71
+ return this.converters.convertKey(key).onSuccess((k) => {
72
+ return this._map.get(k).onFailure(() => {
73
+ const value = valueOrFactory(k)
74
+ .onSuccess((value) => this.converters.convertEntry([k, value]))
75
+ .onSuccess(([__key, value]) => succeedWithDetail(value, 'added'));
76
+ return value.success
77
+ ? this._map.add(k, value.value)
78
+ : failWithDetail(value.message, 'invalid-value');
79
+ });
80
+ });
81
+ }
82
+ }
83
+ /**
84
+ * {@inheritdoc Collections.ResultMap.has}
85
+ */
86
+ has(key) {
87
+ return this._map.has(key);
88
+ }
89
+ /**
90
+ * {@inheritdoc Collections.ResultMap.set}
91
+ */
92
+ set(key, value) {
93
+ return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
94
+ return this._map.set(vk, vv);
95
+ });
96
+ }
97
+ /**
98
+ * {@inheritdoc Collections.ResultMap.update}
99
+ */
100
+ update(key, value) {
101
+ return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
102
+ return this._map.update(vk, vv);
103
+ });
104
+ }
105
+ /**
106
+ * Gets a read-only version of this validator.
107
+ */
108
+ toReadOnly() {
109
+ return this;
110
+ }
111
+ /**
112
+ * Determines if a value is a {@link Collections.ResultMapValueFactory | ResultMapValueFactory}.
113
+ * @param value - The value to check.
114
+ * @returns `true` if the value is a {@link Collections.ResultMapValueFactory | ResultMapValueFactory},
115
+ * `false` otherwise.
116
+ * @public
117
+ */
118
+ _isResultMapValueFactory(value) {
119
+ return typeof value === 'function';
120
+ }
121
+ }
122
+ //# sourceMappingURL=resultMapValidator.js.map
@@ -0,0 +1,31 @@
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
+ * Determines if a supplied value is an iterable object or some other type.
24
+ * @param value - The value to be tested.
25
+ * @returns `true` if the value is an iterable object, `false` otherwise.
26
+ * @public
27
+ */
28
+ export function isIterable(value) {
29
+ return (value && typeof value === 'object' && Symbol.iterator in value) === true;
30
+ }
31
+ //# sourceMappingURL=utils.js.map