@fgv/ts-utils 5.0.2 → 5.1.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 (104) hide show
  1. package/README.md +152 -84
  2. package/dist/index.js +2 -2
  3. package/dist/packlets/base/messageAggregator.js +6 -6
  4. package/dist/packlets/base/result.js +39 -39
  5. package/dist/packlets/base/utils.js +47 -0
  6. package/dist/packlets/collections/aggregatedResultMap.js +722 -0
  7. package/dist/packlets/collections/collector.js +9 -9
  8. package/dist/packlets/collections/collectorValidator.js +4 -4
  9. package/dist/packlets/collections/converters.js +39 -0
  10. package/dist/packlets/collections/convertingCollectorValidator.js +3 -3
  11. package/dist/packlets/collections/convertingResultMap.js +208 -0
  12. package/dist/packlets/collections/index.js +4 -0
  13. package/dist/packlets/collections/keyValueConverters.js +1 -4
  14. package/dist/packlets/collections/readOnlyConvertingResultMap.js +193 -0
  15. package/dist/packlets/collections/resultMapValidator.js +50 -6
  16. package/dist/packlets/collections/validatingConvertingResultMap.js +78 -0
  17. package/dist/packlets/conversion/advancedConverters.js +249 -0
  18. package/dist/packlets/conversion/baseConverter.js +23 -16
  19. package/dist/packlets/conversion/basicConverters.js +571 -0
  20. package/dist/packlets/conversion/converters.js +3 -617
  21. package/dist/packlets/conversion/defaultingConverter.js +21 -15
  22. package/dist/packlets/logging/bootLogger.js +122 -0
  23. package/dist/packlets/logging/index.js +1 -0
  24. package/dist/packlets/logging/logReporter.js +32 -1
  25. package/dist/packlets/logging/logger.js +25 -5
  26. package/dist/packlets/validation/classes.js +1 -0
  27. package/dist/packlets/validation/compositeId.js +60 -0
  28. package/dist/packlets/validation/field.js +1 -1
  29. package/dist/packlets/validation/genericValidator.js +24 -10
  30. package/dist/packlets/validation/object.js +3 -6
  31. package/dist/packlets/validation/validators.js +12 -0
  32. package/dist/ts-utils.d.ts +1633 -200
  33. package/dist/tsdoc-metadata.json +1 -1
  34. package/lib/index.d.ts +2 -2
  35. package/lib/index.js +2 -1
  36. package/lib/packlets/base/mapResults.d.ts +3 -3
  37. package/lib/packlets/base/messageAggregator.d.ts +6 -6
  38. package/lib/packlets/base/messageAggregator.js +6 -6
  39. package/lib/packlets/base/result.d.ts +74 -51
  40. package/lib/packlets/base/result.js +39 -39
  41. package/lib/packlets/base/utils.d.ts +42 -0
  42. package/lib/packlets/base/utils.js +52 -0
  43. package/lib/packlets/collections/aggregatedResultMap.d.ts +433 -0
  44. package/lib/packlets/collections/aggregatedResultMap.js +728 -0
  45. package/lib/packlets/collections/collector.d.ts +11 -10
  46. package/lib/packlets/collections/collector.js +9 -9
  47. package/lib/packlets/collections/collectorValidator.d.ts +12 -10
  48. package/lib/packlets/collections/collectorValidator.js +4 -4
  49. package/lib/packlets/collections/converters.d.ts +18 -0
  50. package/lib/packlets/collections/converters.js +42 -0
  51. package/lib/packlets/collections/convertingCollector.d.ts +4 -4
  52. package/lib/packlets/collections/convertingCollectorValidator.d.ts +10 -7
  53. package/lib/packlets/collections/convertingCollectorValidator.js +3 -3
  54. package/lib/packlets/collections/convertingResultMap.d.ts +176 -0
  55. package/lib/packlets/collections/convertingResultMap.js +213 -0
  56. package/lib/packlets/collections/index.d.ts +4 -0
  57. package/lib/packlets/collections/index.js +4 -0
  58. package/lib/packlets/collections/keyValueConverters.d.ts +1 -4
  59. package/lib/packlets/collections/keyValueConverters.js +1 -4
  60. package/lib/packlets/collections/readOnlyConvertingResultMap.d.ts +153 -0
  61. package/lib/packlets/collections/readOnlyConvertingResultMap.js +197 -0
  62. package/lib/packlets/collections/readonlyResultMap.d.ts +7 -7
  63. package/lib/packlets/collections/resultMap.d.ts +108 -2
  64. package/lib/packlets/collections/resultMapValidator.d.ts +45 -11
  65. package/lib/packlets/collections/resultMapValidator.js +52 -7
  66. package/lib/packlets/collections/validatingCollector.d.ts +5 -5
  67. package/lib/packlets/collections/validatingConvertingCollector.d.ts +3 -3
  68. package/lib/packlets/collections/validatingConvertingResultMap.d.ts +102 -0
  69. package/lib/packlets/collections/validatingConvertingResultMap.js +83 -0
  70. package/lib/packlets/collections/validatingResultMap.d.ts +1 -1
  71. package/lib/packlets/conversion/advancedConverters.d.ts +170 -0
  72. package/lib/packlets/conversion/advancedConverters.js +258 -0
  73. package/lib/packlets/conversion/baseConverter.d.ts +39 -23
  74. package/lib/packlets/conversion/baseConverter.js +23 -16
  75. package/lib/packlets/conversion/basicConverters.d.ts +470 -0
  76. package/lib/packlets/conversion/basicConverters.js +595 -0
  77. package/lib/packlets/conversion/converter.d.ts +7 -0
  78. package/lib/packlets/conversion/converters.d.ts +2 -535
  79. package/lib/packlets/conversion/converters.js +17 -639
  80. package/lib/packlets/conversion/defaultingConverter.d.ts +20 -16
  81. package/lib/packlets/conversion/defaultingConverter.js +21 -15
  82. package/lib/packlets/logging/bootLogger.d.ts +84 -0
  83. package/lib/packlets/logging/bootLogger.js +126 -0
  84. package/lib/packlets/logging/index.d.ts +1 -0
  85. package/lib/packlets/logging/index.js +1 -0
  86. package/lib/packlets/logging/logReporter.d.ts +18 -3
  87. package/lib/packlets/logging/logReporter.js +31 -0
  88. package/lib/packlets/logging/logger.d.ts +39 -6
  89. package/lib/packlets/logging/logger.js +26 -5
  90. package/lib/packlets/validation/classes.d.ts +1 -0
  91. package/lib/packlets/validation/classes.js +3 -1
  92. package/lib/packlets/validation/compositeId.d.ts +29 -0
  93. package/lib/packlets/validation/compositeId.js +64 -0
  94. package/lib/packlets/validation/field.d.ts +1 -1
  95. package/lib/packlets/validation/field.js +1 -1
  96. package/lib/packlets/validation/genericValidator.d.ts +15 -11
  97. package/lib/packlets/validation/genericValidator.js +24 -10
  98. package/lib/packlets/validation/object.d.ts +3 -6
  99. package/lib/packlets/validation/object.js +3 -6
  100. package/lib/packlets/validation/traits.d.ts +3 -3
  101. package/lib/packlets/validation/validator.d.ts +7 -0
  102. package/lib/packlets/validation/validators.d.ts +10 -0
  103. package/lib/packlets/validation/validators.js +13 -0
  104. package/package.json +27 -27
@@ -20,6 +20,50 @@
20
20
  * SOFTWARE.
21
21
  */
22
22
  import { failWithDetail, succeedWithDetail } from '../base';
23
+ /**
24
+ * A read-only validator for any {@link Collections.IReadOnlyResultMap | IReadOnlyResultMap}
25
+ * that validates weakly-typed keys before accessing values.
26
+ * @public
27
+ */
28
+ export class ReadOnlyResultMapValidator {
29
+ /**
30
+ * The underlying map.
31
+ */
32
+ get map() {
33
+ return this._map;
34
+ }
35
+ /**
36
+ * Constructs a new {@link Collections.ReadOnlyResultMapValidator | ReadOnlyResultMapValidator}.
37
+ * @param map - The map to validate access to.
38
+ * @param converters - The key-value converters for validation.
39
+ */
40
+ constructor(map, converters) {
41
+ this._map = map;
42
+ this.converters = converters;
43
+ }
44
+ /**
45
+ * Gets a value from the map by key, validating the key first.
46
+ * @param key - The key to retrieve (will be validated).
47
+ * @returns `Success` with the value if found, `Failure` otherwise.
48
+ */
49
+ get(key) {
50
+ return this.converters.convertKey(key).onSuccess((validKey) => {
51
+ return this._map.get(validKey);
52
+ });
53
+ }
54
+ /**
55
+ * Checks if the map contains a key, validating the key first.
56
+ * @param key - The key to check (will be validated).
57
+ * @returns `true` if the key exists and is valid, `false` otherwise.
58
+ */
59
+ has(key) {
60
+ const result = this.converters.convertKey(key);
61
+ if (result.isFailure()) {
62
+ return false;
63
+ }
64
+ return this._map.has(result.value);
65
+ }
66
+ }
23
67
  /**
24
68
  * A {@link Collections.ResultMap | ResultMap} wrapper which validates weakly-typed keys
25
69
  * before calling the wrapped result map.
@@ -38,7 +82,7 @@ export class ResultMapValidator {
38
82
  this.converters = params.converters;
39
83
  }
40
84
  /**
41
- * {@inheritdoc Collections.ResultMap.add}
85
+ * {@inheritDoc Collections.ResultMap.add}
42
86
  */
43
87
  add(key, value) {
44
88
  return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
@@ -46,7 +90,7 @@ export class ResultMapValidator {
46
90
  });
47
91
  }
48
92
  /**
49
- * {@inheritdoc Collections.ResultMap.delete}
93
+ * {@inheritDoc Collections.ResultMap.delete}
50
94
  */
51
95
  delete(key) {
52
96
  return this.converters.convertKey(key).onSuccess((k) => {
@@ -54,7 +98,7 @@ export class ResultMapValidator {
54
98
  });
55
99
  }
56
100
  /**
57
- * {@inheritdoc Collections.ResultMap.get}
101
+ * {@inheritDoc Collections.ResultMap.get}
58
102
  */
59
103
  get(key) {
60
104
  return this.converters.convertKey(key).onSuccess((k) => {
@@ -81,13 +125,13 @@ export class ResultMapValidator {
81
125
  }
82
126
  }
83
127
  /**
84
- * {@inheritdoc Collections.ResultMap.has}
128
+ * {@inheritDoc Collections.ResultMap.has}
85
129
  */
86
130
  has(key) {
87
131
  return this._map.has(key);
88
132
  }
89
133
  /**
90
- * {@inheritdoc Collections.ResultMap.set}
134
+ * {@inheritDoc Collections.ResultMap.set}
91
135
  */
92
136
  set(key, value) {
93
137
  return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
@@ -95,7 +139,7 @@ export class ResultMapValidator {
95
139
  });
96
140
  }
97
141
  /**
98
- * {@inheritdoc Collections.ResultMap.update}
142
+ * {@inheritDoc Collections.ResultMap.update}
99
143
  */
100
144
  update(key, value) {
101
145
  return this.converters.convertEntry([key, value]).onSuccess(([vk, vv]) => {
@@ -0,0 +1,78 @@
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 } from '../base';
23
+ import { ReadOnlyConvertingResultMap } from './readOnlyConvertingResultMap';
24
+ import { ConvertingResultMap } from './convertingResultMap';
25
+ import { ReadOnlyResultMapValidator } from './resultMapValidator';
26
+ /**
27
+ * A read-only result map that wraps an inner {@link Collections.IReadOnlyResultMap | IReadOnlyResultMap}
28
+ * of source type and returns lazily-converted, cached values of a target type, with a
29
+ * {@link Collections.ReadOnlyResultMapValidator | validating} property for weakly-typed access.
30
+ * @public
31
+ */
32
+ export class ValidatingReadOnlyConvertingResultMap extends ReadOnlyConvertingResultMap {
33
+ /**
34
+ * Constructs a new {@link Collections.ValidatingReadOnlyConvertingResultMap | ValidatingReadOnlyConvertingResultMap}.
35
+ * @param params - Parameters for constructing the map.
36
+ */
37
+ constructor(params) {
38
+ super({ inner: params.inner, converter: params.converter });
39
+ this._converters = params.converters;
40
+ this.validating = new ReadOnlyResultMapValidator(this, params.converters);
41
+ }
42
+ /**
43
+ * Creates a new {@link Collections.ValidatingReadOnlyConvertingResultMap | ValidatingReadOnlyConvertingResultMap}.
44
+ * @param params - Parameters for constructing the map.
45
+ * @returns `Success` with the new map, or `Failure` with error details if an error occurred.
46
+ */
47
+ static create(params) {
48
+ return captureResult(() => new ValidatingReadOnlyConvertingResultMap(params));
49
+ }
50
+ }
51
+ /**
52
+ * A result map that wraps an inner {@link Collections.IResultMap | IResultMap} of source type
53
+ * and returns lazily-converted, cached values of a target type, with a
54
+ * {@link Collections.ReadOnlyResultMapValidator | validating} property for weakly-typed access
55
+ * and a {@link Collections.CacheInvalidatingResultMapWrapper | source} property for mutable access
56
+ * to the underlying source map.
57
+ * @public
58
+ */
59
+ export class ValidatingConvertingResultMap extends ConvertingResultMap {
60
+ /**
61
+ * Constructs a new {@link Collections.ValidatingConvertingResultMap | ValidatingConvertingResultMap}.
62
+ * @param params - Parameters for constructing the map.
63
+ */
64
+ constructor(params) {
65
+ super({ inner: params.inner, converter: params.converter });
66
+ this._converters = params.converters;
67
+ this.validating = new ReadOnlyResultMapValidator(this, params.converters);
68
+ }
69
+ /**
70
+ * Creates a new {@link Collections.ValidatingConvertingResultMap | ValidatingConvertingResultMap}.
71
+ * @param params - Parameters for constructing the map.
72
+ * @returns `Success` with the new map, or `Failure` with error details if an error occurred.
73
+ */
74
+ static create(params) {
75
+ return captureResult(() => new ValidatingConvertingResultMap(params));
76
+ }
77
+ }
78
+ //# sourceMappingURL=validatingConvertingResultMap.js.map
@@ -0,0 +1,249 @@
1
+ /*
2
+ * Copyright (c) 2020 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 { fail, isKeyOf, mapResults, succeed, Success } from '../base';
23
+ import { BaseConverter } from './baseConverter';
24
+ import { literal } from './basicConverters';
25
+ import { ObjectConverter } from './objectConverter';
26
+ /**
27
+ * Creates an {@link ObjectConverter | ObjectConverter} for a strongly-typed {@link Converters.ICompositeId | CompositeId}.
28
+ * @param collectionIdValidator - {@link Converter | Converter} or {@link Validator | Validator} for the collection ID portion.
29
+ * @param separator - The separator string.
30
+ * @param itemIdValidator - {@link Converter | Converter} or {@link Validator | Validator} for the item ID portion.
31
+ * @returns An {@link ObjectConverter | ObjectConverter} for the strongly-typed composite ICompositeId.
32
+ * @public
33
+ */
34
+ export function compositeIdFromObject(collectionIdValidator, separator, itemIdValidator) {
35
+ return new ObjectConverter({
36
+ collectionId: collectionIdValidator,
37
+ separator: literal(separator).optional(),
38
+ itemId: itemIdValidator
39
+ }).map((obj) => {
40
+ var _a;
41
+ return Success.with({
42
+ collectionId: obj.collectionId,
43
+ separator: (_a = obj.separator) !== null && _a !== void 0 ? _a : separator,
44
+ itemId: obj.itemId
45
+ });
46
+ });
47
+ }
48
+ /**
49
+ * Converts a composite ID string into a strongly-typed {@link Converters.ICompositeId | CompositeId}.
50
+ * @param collectionIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the collection ID portion.
51
+ * @param separator - The separator string.
52
+ * @param itemIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the item ID portion.
53
+ * @returns A {@link Converter | Converter} for the strongly-typed {@link Converters.ICompositeId | CompositeId}.
54
+ * @public
55
+ */
56
+ export function compositeIdFromString(collectionIdConverter, separator, itemIdConverter) {
57
+ return new BaseConverter((from, __self, context) => {
58
+ if (typeof from !== 'string') {
59
+ return fail(`${from}: invalid non-string composite ID.`);
60
+ }
61
+ const parts = from.split(separator);
62
+ if (parts.length < 2) {
63
+ return fail(`${from}: invalid composite ID - separator '${separator}' not found.`);
64
+ }
65
+ else if (parts.length > 2) {
66
+ return fail(`${from}: invalid composite ID - multiple separators '${separator}.' found.`);
67
+ }
68
+ return collectionIdConverter.convert(parts[0], context).onSuccess((collectionId) => {
69
+ return itemIdConverter.convert(parts[1], context).onSuccess((itemId) => {
70
+ return succeed({
71
+ collectionId,
72
+ separator,
73
+ itemId
74
+ });
75
+ });
76
+ });
77
+ });
78
+ }
79
+ /**
80
+ * Creates a {@link Converter | Converter} for a strongly-typed {@link Converters.ICompositeId | CompositeId} from
81
+ * either a string or an object representation.
82
+ * @param collectionIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the collection ID portion.
83
+ * @param separator - The separator string.
84
+ * @param itemIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the item ID portion.
85
+ * @returns A {@link Converter | Converter} for the strongly-typed {@link Converters.ICompositeId | CompositeId}.
86
+ * @public
87
+ */
88
+ export function compositeId(collectionIdConverter, separator, itemIdConverter) {
89
+ return compositeIdFromObject(collectionIdConverter, separator, itemIdConverter).or(compositeIdFromString(collectionIdConverter, separator, itemIdConverter));
90
+ }
91
+ /**
92
+ * Converts a strongly-typed {@link Converters.ICompositeId | CompositeId} into a string.
93
+ * @param compositeIdConverter - {@link Converter | Converter} or {@link Validator | Validator} that validates/converts the composite ID string.
94
+ * @param collectionIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the collection ID portion.
95
+ * @param separator - The separator string.
96
+ * @param itemIdConverter - {@link Converter | Converter} or {@link Validator | Validator} for the item ID portion.
97
+ * @returns A {@link Converter | Converter} which produces a composite ID string.
98
+ * @public
99
+ */
100
+ export function compositeIdString(compositeIdConverter, collectionIdConverter, separator, itemIdConverter) {
101
+ const objectConverter = compositeIdFromObject(collectionIdConverter, separator, itemIdConverter);
102
+ return new BaseConverter((from, __self, context) => {
103
+ if (typeof from === 'string') {
104
+ return compositeIdConverter.convert(from, context);
105
+ }
106
+ return objectConverter.convert(from, context).onSuccess((compositeId) => {
107
+ return compositeIdConverter.convert(`${compositeId.collectionId}${separator}${compositeId.itemId}`);
108
+ });
109
+ });
110
+ }
111
+ /**
112
+ * Helper to create a {@link Converter | Converter} which converts a source object to a new object with a
113
+ * different shape.
114
+ *
115
+ * @remarks
116
+ * On successful conversion, the resulting {@link Converter | Converter} returns {@link Success | Success} with a new
117
+ * object, which contains the converted values under the key names specified at initialization time.
118
+ * It returns {@link Failure | Failure} with an error message if any fields to be extracted do not exist
119
+ * or cannot be converted.
120
+ *
121
+ * Fields that succeed but convert to undefined are omitted from the result object but do not
122
+ * fail the conversion.
123
+ *
124
+ * @param properties - An object with key names that correspond to the target object and an
125
+ * appropriate {@link Conversion.FieldConverters | FieldConverter} which extracts and converts
126
+ * a single filed from the source object.
127
+ * @returns A {@link Converter | Converter} with the specified conversion behavior.
128
+ * @public
129
+ */
130
+ export function transform(properties) {
131
+ return new BaseConverter((from, __self, context) => {
132
+ // eslint bug thinks key is used before defined
133
+ const converted = {};
134
+ const errors = [];
135
+ for (const key in properties) {
136
+ if (properties[key]) {
137
+ const result = properties[key].convert(from, context);
138
+ if (result.isSuccess() && result.value !== undefined) {
139
+ converted[key] = result.value;
140
+ }
141
+ else if (result.isFailure()) {
142
+ errors.push(result.message);
143
+ }
144
+ }
145
+ }
146
+ return errors.length === 0 ? succeed(converted) : fail(errors.join('\n'));
147
+ });
148
+ }
149
+ /**
150
+ * Helper to create a strongly-typed {@link Converter | Converter} which converts a source object to a
151
+ * new object with a different shape.
152
+ *
153
+ * @remarks
154
+ * On successful conversion, the resulting {@link Converter | Converter} returns {@link Success | Success} with a new
155
+ * object, which contains the converted values under the key names specified at initialization time.
156
+ *
157
+ * It returns {@link Failure | Failure} with an error message if any fields to be extracted do not exist
158
+ * or cannot be converted.
159
+ *
160
+ * @param destinationFields - An object with key names that correspond to the target object and an
161
+ * appropriate {@link Converters.FieldTransformers | FieldTransformers} which specifies the name
162
+ * of the corresponding property in the source object, the converter or validator used for each source
163
+ * property and any other configuration to guide the conversion.
164
+ * @param options - Options which affect the transformation.
165
+ *
166
+ * @returns A {@link Converter | Converter} with the specified conversion behavior.
167
+ * @public
168
+ */
169
+ export function transformObject(destinationFields, options) {
170
+ return new BaseConverter((from, __self, context) => {
171
+ // eslint bug thinks key is used before defined
172
+ const converted = {};
173
+ const errors = [];
174
+ const used = new Set(options === null || options === void 0 ? void 0 : options.ignore);
175
+ if (typeof from === 'object' && !Array.isArray(from) && from !== null) {
176
+ for (const destinationKey in destinationFields) {
177
+ if (destinationFields[destinationKey]) {
178
+ const srcKey = destinationFields[destinationKey].from;
179
+ const converter = destinationFields[destinationKey].converter;
180
+ if (isKeyOf(srcKey, from)) {
181
+ const result = converter.convert(from[srcKey], context);
182
+ if (result.isSuccess() && result.value !== undefined) {
183
+ converted[destinationKey] = result.value;
184
+ }
185
+ else if (result.isFailure()) {
186
+ errors.push(`${srcKey}->${destinationKey}: ${result.message}`);
187
+ }
188
+ used.add(srcKey);
189
+ }
190
+ else if (destinationFields[destinationKey].optional !== true) {
191
+ errors.push(`${String(srcKey)}: required property missing in source object.`);
192
+ }
193
+ }
194
+ }
195
+ if ((options === null || options === void 0 ? void 0 : options.strict) === true) {
196
+ for (const key in from) {
197
+ if (isKeyOf(key, from) && !used.has(key)) {
198
+ errors.push(`${key}: unexpected property in source object`);
199
+ }
200
+ }
201
+ }
202
+ }
203
+ else {
204
+ errors.push('source is not an object');
205
+ }
206
+ return errors.length === 0
207
+ ? succeed(converted)
208
+ : fail((options === null || options === void 0 ? void 0 : options.description) ? `${options.description}:\n ${errors.join('\n ')}` : errors.join('\n'));
209
+ });
210
+ }
211
+ /**
212
+ * Creates a {@link Converter | Converter} that converts an array to a strongly-typed tuple,
213
+ * using the supplied tuple of {@link Converter | Converters} to convert each element.
214
+ *
215
+ * @remarks
216
+ * The resulting {@link Converter | Converter} returns {@link Success | Success} with a tuple
217
+ * containing the converted values if all conversions succeed. Returns {@link Failure | Failure}
218
+ * with an error message if the source is not an array, has the wrong number of elements, or
219
+ * any element conversion fails.
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const converter = tuple([Converters.string, Converters.number, Converters.boolean]);
224
+ * // Type is Converter<[string, number, boolean]>
225
+ *
226
+ * converter.convert(['hello', 42, true]);
227
+ * // Returns Success with ['hello', 42, true]
228
+ * ```
229
+ *
230
+ * @param converters - A tuple of {@link Converter | Converters} defining the expected types
231
+ * for each element of the tuple.
232
+ * @returns A {@link Converter | Converter} that returns a strongly-typed tuple.
233
+ * @public
234
+ */
235
+ export function tuple(converters) {
236
+ return new BaseConverter((from, __self, context) => {
237
+ if (!Array.isArray(from)) {
238
+ return fail(`Not an array: ${JSON.stringify(from)}`);
239
+ }
240
+ if (from.length !== converters.length) {
241
+ return fail(`Expected array of length ${converters.length}, got ${from.length}: ${JSON.stringify(from)}`);
242
+ }
243
+ const results = converters.map((converter, index) => {
244
+ return converter.convert(from[index], context);
245
+ });
246
+ return mapResults(results);
247
+ });
248
+ }
249
+ //# sourceMappingURL=advancedConverters.js.map
@@ -44,25 +44,25 @@ export class BaseConverter {
44
44
  this._brand = traits === null || traits === void 0 ? void 0 : traits.brand;
45
45
  }
46
46
  /**
47
- * {@inheritdoc Converter.isOptional}
47
+ * {@inheritDoc Converter.isOptional}
48
48
  */
49
49
  get isOptional() {
50
50
  return this._isOptional;
51
51
  }
52
52
  /**
53
- * {@inheritdoc Converter.brand}
53
+ * {@inheritDoc Converter.brand}
54
54
  */
55
55
  get brand() {
56
56
  return this._brand;
57
57
  }
58
58
  /**
59
- * {@inheritdoc Converter.convert}
59
+ * {@inheritDoc Converter.convert}
60
60
  */
61
61
  convert(from, context) {
62
62
  return this._converter(from, this, context !== null && context !== void 0 ? context : this._defaultContext);
63
63
  }
64
64
  /**
65
- * {@inheritdoc Converter.convertOptional}
65
+ * {@inheritDoc Converter.convertOptional}
66
66
  */
67
67
  convertOptional(from, context, onError) {
68
68
  const result = this._converter(from, this, this._context(context));
@@ -73,7 +73,7 @@ export class BaseConverter {
73
73
  return result;
74
74
  }
75
75
  /**
76
- * {@inheritdoc Converter.optional}
76
+ * {@inheritDoc Converter.optional}
77
77
  */
78
78
  optional(onError) {
79
79
  return new BaseConverter((from, __self, context) => {
@@ -82,7 +82,7 @@ export class BaseConverter {
82
82
  })._with(this._traits({ isOptional: true }));
83
83
  }
84
84
  /**
85
- * {@inheritdoc Converter.map}
85
+ * {@inheritDoc Converter.map}
86
86
  */
87
87
  map(mapper) {
88
88
  return new BaseConverter((from, __self, context) => {
@@ -94,7 +94,7 @@ export class BaseConverter {
94
94
  })._with(this._traits());
95
95
  }
96
96
  /**
97
- * {@inheritdoc Converter.mapConvert}
97
+ * {@inheritDoc Converter.mapConvert}
98
98
  */
99
99
  mapConvert(mapConverter) {
100
100
  return new BaseConverter((from, __self, context) => {
@@ -106,7 +106,7 @@ export class BaseConverter {
106
106
  })._with(this._traits());
107
107
  }
108
108
  /**
109
- * {@inheritdoc Converter.mapItems}
109
+ * {@inheritDoc Converter.mapItems}
110
110
  */
111
111
  mapItems(mapper) {
112
112
  return new BaseConverter((from, __self, context) => {
@@ -119,7 +119,7 @@ export class BaseConverter {
119
119
  });
120
120
  }
121
121
  /**
122
- * {@inheritdoc Converter.mapConvertItems}
122
+ * {@inheritDoc Converter.mapConvertItems}
123
123
  */
124
124
  mapConvertItems(mapConverter) {
125
125
  return new BaseConverter((from, __self, context) => {
@@ -132,7 +132,7 @@ export class BaseConverter {
132
132
  });
133
133
  }
134
134
  /**
135
- * {@inheritdoc Converter.withAction}
135
+ * {@inheritDoc Converter.withAction}
136
136
  */
137
137
  withAction(action) {
138
138
  return new BaseConverter((from, __self, context) => {
@@ -140,7 +140,7 @@ export class BaseConverter {
140
140
  })._with(this._traits());
141
141
  }
142
142
  /**
143
- * {@inheritdoc Converter.withTypeGuard}
143
+ * {@inheritDoc Converter.withTypeGuard}
144
144
  */
145
145
  withTypeGuard(guard, message) {
146
146
  return new BaseConverter((from, __self, context) => {
@@ -153,7 +153,7 @@ export class BaseConverter {
153
153
  })._with(this._traits());
154
154
  }
155
155
  /**
156
- * {@inheritdoc Converter.withItemTypeGuard}
156
+ * {@inheritDoc Converter.withItemTypeGuard}
157
157
  */
158
158
  withItemTypeGuard(guard, message) {
159
159
  return new BaseConverter((from, __self, context) => {
@@ -171,7 +171,7 @@ export class BaseConverter {
171
171
  })._with(this._traits());
172
172
  }
173
173
  /**
174
- * {@inheritdoc Converter.withConstraint}
174
+ * {@inheritDoc Converter.withConstraint}
175
175
  */
176
176
  withConstraint(constraint, options) {
177
177
  return new BaseConverter((from, __self, context) => {
@@ -190,7 +190,7 @@ export class BaseConverter {
190
190
  })._with(this._traits());
191
191
  }
192
192
  /**
193
- * {@inheritdoc Converter.withBrand}
193
+ * {@inheritDoc Converter.withBrand}
194
194
  */
195
195
  withBrand(brand) {
196
196
  if (this._brand) {
@@ -203,11 +203,18 @@ export class BaseConverter {
203
203
  })._with(this._traits({ brand }));
204
204
  }
205
205
  /**
206
- * {@inheritdoc Converter.withDefault}
206
+ * {@inheritDoc Converter.withDefault}
207
207
  */
208
208
  withDefault(defaultValue) {
209
209
  return new GenericDefaultingConverter(this, defaultValue);
210
210
  }
211
+ or(other) {
212
+ return new BaseConverter((from, __self, context) => {
213
+ return this._converter(from, this, this._context(context)).onFailure(() => {
214
+ return other.convert(from, this._context(context));
215
+ });
216
+ });
217
+ }
211
218
  /**
212
219
  * @internal
213
220
  */
@@ -215,7 +222,7 @@ export class BaseConverter {
215
222
  return supplied !== null && supplied !== void 0 ? supplied : this._defaultContext;
216
223
  }
217
224
  /**
218
- * {@inheritdoc Converter.withFormattedError}
225
+ * {@inheritDoc Converter.withFormattedError}
219
226
  */
220
227
  withFormattedError(formatter) {
221
228
  return new BaseConverter((from, __self, context) => {