@fgv/ts-utils 5.0.0-22 → 5.0.0-23

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 (80) hide show
  1. package/dist/ts-utils.d.ts +307 -33
  2. package/lib/index.d.ts +2 -1
  3. package/lib/index.js +3 -1
  4. package/lib/packlets/base/index.d.ts +0 -2
  5. package/lib/packlets/base/index.js +1 -25
  6. package/lib/packlets/base/result.d.ts +49 -0
  7. package/lib/packlets/base/result.js +36 -0
  8. package/lib/packlets/collections/collector.d.ts +9 -0
  9. package/lib/packlets/collections/collector.js +6 -0
  10. package/lib/packlets/collections/validatingCollector.d.ts +5 -1
  11. package/lib/packlets/hash/hashingNormalizer.js +4 -3
  12. package/lib/packlets/logging/index.d.ts +3 -0
  13. package/{src/packlets/hash/index.ts → lib/packlets/logging/index.js} +19 -3
  14. package/lib/packlets/logging/logReporter.d.ts +69 -0
  15. package/lib/packlets/logging/logReporter.js +72 -0
  16. package/lib/packlets/logging/logger.d.ts +183 -0
  17. package/lib/packlets/logging/logger.js +237 -0
  18. package/lib/test/unit/consoleLogger.test.d.ts +2 -0
  19. package/lib/test/unit/hashingNormalizerCollisions.test.d.ts +2 -0
  20. package/package.json +1 -1
  21. package/lib/packlets/base/logger.d.ts +0 -52
  22. package/lib/packlets/base/logger.js +0 -128
  23. package/src/index.ts +0 -63
  24. package/src/packlets/base/brand.ts +0 -28
  25. package/src/packlets/base/index.ts +0 -31
  26. package/src/packlets/base/logger.ts +0 -156
  27. package/src/packlets/base/mapResults.ts +0 -302
  28. package/src/packlets/base/messageAggregator.ts +0 -120
  29. package/src/packlets/base/normalize.ts +0 -144
  30. package/src/packlets/base/result.ts +0 -961
  31. package/src/packlets/base/utils.ts +0 -236
  32. package/src/packlets/collections/collectible.ts +0 -238
  33. package/src/packlets/collections/collector.ts +0 -273
  34. package/src/packlets/collections/collectorValidator.ts +0 -178
  35. package/src/packlets/collections/common.ts +0 -27
  36. package/src/packlets/collections/convertingCollector.ts +0 -223
  37. package/src/packlets/collections/convertingCollectorValidator.ts +0 -164
  38. package/src/packlets/collections/index.ts +0 -39
  39. package/src/packlets/collections/keyValueConverters.ts +0 -142
  40. package/src/packlets/collections/readonlyResultMap.ts +0 -99
  41. package/src/packlets/collections/resultMap.ts +0 -322
  42. package/src/packlets/collections/resultMapValidator.ts +0 -188
  43. package/src/packlets/collections/utils.ts +0 -33
  44. package/src/packlets/collections/validatingCollector.ts +0 -123
  45. package/src/packlets/collections/validatingConvertingCollector.ts +0 -113
  46. package/src/packlets/collections/validatingResultMap.ts +0 -94
  47. package/src/packlets/conversion/baseConverter.ts +0 -330
  48. package/src/packlets/conversion/converter.ts +0 -260
  49. package/src/packlets/conversion/converters.ts +0 -1039
  50. package/src/packlets/conversion/defaultingConverter.ts +0 -188
  51. package/src/packlets/conversion/index.ts +0 -30
  52. package/src/packlets/conversion/objectConverter.ts +0 -259
  53. package/src/packlets/conversion/stringConverter.ts +0 -165
  54. package/src/packlets/file-tree/directoryItem.ts +0 -84
  55. package/src/packlets/file-tree/fileItem.ts +0 -119
  56. package/src/packlets/file-tree/fileTree.ts +0 -152
  57. package/src/packlets/file-tree/fileTreeAccessors.ts +0 -176
  58. package/src/packlets/file-tree/fsTree.ts +0 -118
  59. package/src/packlets/file-tree/in-memory/inMemoryTree.ts +0 -171
  60. package/src/packlets/file-tree/in-memory/index.ts +0 -23
  61. package/src/packlets/file-tree/in-memory/treeBuilder.ts +0 -203
  62. package/src/packlets/file-tree/index.ts +0 -31
  63. package/src/packlets/hash/crcNormalizer.ts +0 -81
  64. package/src/packlets/hash/hashingNormalizer.ts +0 -102
  65. package/src/packlets/validation/array.ts +0 -84
  66. package/src/packlets/validation/boolean.ts +0 -63
  67. package/src/packlets/validation/classes.ts +0 -38
  68. package/src/packlets/validation/common.ts +0 -28
  69. package/src/packlets/validation/field.ts +0 -99
  70. package/src/packlets/validation/genericValidator.ts +0 -204
  71. package/src/packlets/validation/index.ts +0 -31
  72. package/src/packlets/validation/number.ts +0 -66
  73. package/src/packlets/validation/object.ts +0 -208
  74. package/src/packlets/validation/oneOf.ts +0 -78
  75. package/src/packlets/validation/string.ts +0 -66
  76. package/src/packlets/validation/traits.ts +0 -113
  77. package/src/packlets/validation/typeGuard.ts +0 -83
  78. package/src/packlets/validation/validator.ts +0 -157
  79. package/src/packlets/validation/validatorBase.ts +0 -66
  80. package/src/packlets/validation/validators.ts +0 -254
@@ -1,188 +0,0 @@
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
-
23
- import { Brand, Result, Success, succeed } from '../base';
24
- import { ConstraintOptions, ConversionErrorFormatter, Converter, DefaultingConverter } from './converter';
25
-
26
- /**
27
- * Generic {@link Conversion.DefaultingConverter | DefaultingConverter}, which wraps another converter
28
- * to substitute a supplied default value for any errors returned by the inner converter.
29
- * @public
30
- */
31
- export class GenericDefaultingConverter<T, TD = T, TC = unknown> implements DefaultingConverter<T, TD, TC> {
32
- private _converter: Converter<T, TC>;
33
-
34
- /**
35
- * {@inheritdoc Conversion.DefaultingConverter.defaultValue}
36
- */
37
- public defaultValue: TD;
38
-
39
- /**
40
- * {@inheritdoc Converter.isOptional}
41
- */
42
- public get isOptional(): boolean {
43
- return this._converter.isOptional;
44
- }
45
-
46
- /**
47
- * {@inheritdoc Converter.isOptional}
48
- */
49
- public get brand(): string | undefined {
50
- return this._converter.brand;
51
- }
52
-
53
- /**
54
- * Constructs a new {@link Conversion.GenericDefaultingConverter | generic defaulting converter}.
55
- * @param converter - inner {@link Converter | Converter} used for the base conversion.
56
- * @param defaultValue - default value to be supplied if the inner conversion fails.
57
- */
58
- public constructor(converter: Converter<T, TC>, defaultValue: TD) {
59
- this.defaultValue = defaultValue;
60
- this._converter = converter;
61
- }
62
-
63
- /**
64
- * {@inheritdoc Converter.convert}
65
- */
66
- public convert(from: unknown, ctx?: TC | undefined): Success<T | TD> {
67
- return this._applyDefault(this._converter.convert(from, ctx));
68
- }
69
-
70
- /**
71
- * {@inheritdoc Converter.convertOptional}
72
- */
73
- public convertOptional(
74
- from: unknown,
75
- context?: TC | undefined,
76
- onError?: ('failOnError' | 'ignoreErrors') | undefined
77
- ): Result<T | TD | undefined> {
78
- const converted = this._converter.convertOptional(from, context, onError);
79
- if (converted.isFailure()) {
80
- return succeed(this.defaultValue);
81
- }
82
- return converted;
83
- }
84
-
85
- /**
86
- * {@inheritdoc Converter.optional}
87
- */
88
- public optional(onError?: ('failOnError' | 'ignoreErrors') | undefined): Converter<T | TD | undefined, TC> {
89
- // need to let the inner converter do its optional thing first or our default
90
- // steps on everything
91
- return this._converter.optional(onError).withAction((result) => this._applyDefault(result));
92
- }
93
-
94
- /**
95
- * {@inheritdoc Converter.map}
96
- */
97
- public map<T2>(mapper: (from: T | TD) => Result<T2>): Converter<T2, TC> {
98
- return this._converter.withAction((result) => this._applyDefault(result)).map(mapper);
99
- }
100
-
101
- /**
102
- * {@inheritdoc Converter.mapConvert}
103
- */
104
- public mapConvert<T2>(mapConverter: Converter<T2, unknown>): Converter<T2, TC> {
105
- return this._converter.withAction((result) => this._applyDefault(result)).mapConvert(mapConverter);
106
- }
107
-
108
- /**
109
- * {@inheritdoc Converter.mapItems}
110
- */
111
- public mapItems<TI>(mapper: (from: unknown) => Result<TI>): Converter<TI[], TC> {
112
- return this._converter.withAction((result) => this._applyDefault(result)).mapItems(mapper);
113
- }
114
-
115
- /**
116
- * {@inheritdoc Converter.mapConvertItems}
117
- */
118
- public mapConvertItems<TI>(mapConverter: Converter<TI, unknown>): Converter<TI[], TC> {
119
- return this._converter.withAction((result) => this._applyDefault(result)).mapConvertItems(mapConverter);
120
- }
121
-
122
- /**
123
- * {@inheritdoc Converter.withAction}
124
- */
125
- public withAction<T2>(action: (result: Result<T | TD>) => Result<T2>): Converter<T2, TC> {
126
- return this._converter.withAction((result) => this._applyDefault(result)).withAction(action);
127
- }
128
-
129
- /**
130
- * {@inheritdoc Converter.withTypeGuard}
131
- */
132
- public withTypeGuard<TI>(
133
- guard: (from: unknown) => from is TI,
134
- message?: string | undefined
135
- ): Converter<TI, TC> {
136
- return this._converter.withAction((result) => this._applyDefault(result)).withTypeGuard(guard);
137
- }
138
-
139
- /**
140
- * {@inheritdoc Converter.withItemTypeGuard}
141
- */
142
- public withItemTypeGuard<TI>(
143
- guard: (from: unknown) => from is TI,
144
- message?: string | undefined
145
- ): Converter<TI[], TC> {
146
- return this._converter.withAction((result) => this._applyDefault(result)).withItemTypeGuard(guard);
147
- }
148
-
149
- /**
150
- * {@inheritdoc Converter.withConstraint}
151
- */
152
- public withConstraint(
153
- constraint: (val: T | TD) => boolean | Result<T | TD>,
154
- options?: ConstraintOptions | undefined
155
- ): Converter<T | TD, TC> {
156
- return this._converter.withAction((result) => this._applyDefault(result)).withConstraint(constraint);
157
- }
158
-
159
- /**
160
- * {@inheritdoc Converter.withBrand}
161
- */
162
- public withBrand<B extends string>(brand: B): Converter<Brand<T | TD, B>, TC> {
163
- return this._converter.withAction((result) => this._applyDefault(result)).withBrand(brand);
164
- }
165
-
166
- /**
167
- * {@inheritdoc Converter.withFormattedError}
168
- */
169
- public withFormattedError(formatter: ConversionErrorFormatter<TC>): Converter<T | TD, TC> {
170
- // formatter should never actually be invoked for a defaulting converter
171
- return this._converter.withAction((result) => this._applyDefault(result)).withFormattedError(formatter);
172
- }
173
-
174
- /**
175
- * Returns a Converter which always succeeds with the supplied default value rather
176
- * than failing.
177
- *
178
- * Note that the supplied default value *overrides* the default value of this
179
- * {@link Conversion.DefaultingConverter | DefaultingConverter}.
180
- */
181
- public withDefault<TD2 = T>(dflt: TD2): DefaultingConverter<T, TD2, TC> {
182
- return new GenericDefaultingConverter(this._converter, dflt);
183
- }
184
-
185
- private _applyDefault<T2 = T>(converted: Result<T2>): Success<T2 | TD> {
186
- return converted.success ? converted : succeed(this.defaultValue);
187
- }
188
- }
@@ -1,30 +0,0 @@
1
- /*
2
- * Copyright (c) 2023 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 Converters from './converters';
24
-
25
- export * from './baseConverter';
26
- export * from './converter';
27
- export * from './defaultingConverter';
28
- export * from './objectConverter';
29
- export * from './stringConverter';
30
- export { Converters };
@@ -1,259 +0,0 @@
1
- /*
2
- * Copyright (c) 2023 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 { fail, isKeyOf, Result, succeed } from '../base';
24
- import { Validator } from '../validation';
25
- import { BaseConverter } from './baseConverter';
26
- import { Converter } from './converter';
27
- import { field, optionalField } from './converters';
28
-
29
- /**
30
- * Options for an {@link Conversion.ObjectConverter | ObjectConverter}.
31
- * @public
32
- */
33
- // eslint-disable-next-line @typescript-eslint/naming-convention
34
- export interface ObjectConverterOptions<T> {
35
- /**
36
- * If present, lists optional fields. Missing non-optional fields cause an error.
37
- */
38
- optionalFields?: (keyof T)[];
39
- /**
40
- * If true, unrecognized fields yield an error. If false or undefined (default),
41
- * unrecognized fields are ignored.
42
- */
43
- strict?: boolean;
44
- /**
45
- * Optional description to be included in error messages.
46
- */
47
- description?: string;
48
-
49
- /**
50
- * Optional modifier to apply to the converter.
51
- */
52
- modifier?: 'partial' | 'required';
53
- }
54
-
55
- /**
56
- * Per-property converters or validators for each of the properties in type T.
57
- * @remarks
58
- * Used to construct a {@link Conversion.ObjectConverter | ObjectConverter}
59
- * @public
60
- */
61
- export type FieldConverters<T, TC = unknown> = {
62
- [key in keyof T]: Converter<T[key], TC | unknown> | Validator<T[key], TC>;
63
- };
64
-
65
- /**
66
- * A {@link Converter | Converter} which converts an object of type `<T>` without changing shape, given
67
- * a {@link Conversion.FieldConverters | FieldConverters<T>} for the fields in the object.
68
- * @remarks
69
- * By default, if all of the required fields exist and can be converted, returns a new object with
70
- * the converted values under the original key names. If any required fields do not exist or cannot
71
- * be converted, the entire conversion fails. See {@link Conversion.ObjectConverterOptions | ObjectConverterOptions}
72
- * for other conversion options.
73
- * @public
74
- */
75
- export class ObjectConverter<T, TC = unknown> extends BaseConverter<T, TC> {
76
- /**
77
- * Fields converted by this {@link Conversion.ObjectConverter | ObjectConverter}.
78
- */
79
- public readonly fields: FieldConverters<T, TC>;
80
- /**
81
- * Options used to initialize this {@link Conversion.ObjectConverter | ObjectConverter}.
82
- */
83
- public readonly options: ObjectConverterOptions<T>;
84
-
85
- /**
86
- * Constructs a new {@link Conversion.ObjectConverter | ObjectConverter<T, TC>} using options
87
- * supplied in a {@link Conversion.ObjectConverterOptions | ObjectConverterOptions<T>}.
88
- * @param fields - A {@link Conversion.FieldConverters | FieldConverters<T>} containing
89
- * a {@link Converter} for each field
90
- * @param options - An optional @see ObjectConverterOptions to configure the conversion
91
- * {@label WITH_OPTIONS}
92
- */
93
- public constructor(fields: FieldConverters<T, TC>, options?: ObjectConverterOptions<T>);
94
-
95
- /**
96
- * Constructs a new {@link Conversion.ObjectConverter | ObjectConverter<T, TC>} with optional
97
- * properties specified as an array of `keyof T`.
98
- * @param fields - A {@link Conversion.FieldConverters | FieldConverters<T>} containing
99
- * a {@link Converter} for each field.
100
- * @param optional - An array of `keyof T` listing fields that are not required.
101
- * {@label WITH_KEYS}
102
- * @deprecated Use {@link Conversion.Converter.optional | .optional()} on the individual
103
- * fields, or pass {@link Conversion.ObjectConverterOptions | ObjectConverterOptions<T>} to the constructor.
104
- */
105
- public constructor(fields: FieldConverters<T, TC>, optional?: (keyof T)[]);
106
- /**
107
- * Concrete implementation of {@link Converters.(ObjectConverter:constructor)}
108
- * @internal
109
- */
110
- public constructor(fields: FieldConverters<T, TC>, opt?: ObjectConverterOptions<T> | (keyof T)[]) {
111
- const options = Array.isArray(opt) ? { optionalFields: opt } : opt ?? { optionalFields: [] };
112
-
113
- super((from: unknown, __self, context?: TC) => ObjectConverter._convert(from, context, fields, options));
114
-
115
- this.fields = fields;
116
- this.options = options;
117
- }
118
-
119
- /**
120
- * Converts the supplied object using the {@link Conversion.ObjectConverter | ObjectConverter}
121
- * with all fields optional.
122
- * @param from - The object to be converted.
123
- * @param context - An optional context object passed to the field converters.
124
- * @returns A {@link Result} containing the converted object or an error message.
125
- */
126
- public convertPartial(from: unknown, context?: TC): Result<Partial<T>> {
127
- const options: ObjectConverterOptions<T> = { ...this.options, modifier: 'partial' };
128
- return ObjectConverter._convert(from, context, this.fields, options);
129
- }
130
-
131
- /**
132
- * Converts the supplied object using the {@link Conversion.ObjectConverter | ObjectConverter}
133
- * with all fields required.
134
- * @param from - The object to be converted.
135
- * @param context - An optional context object passed to the field converters.
136
- * @returns A {@link Result} containing the converted object or an error message.
137
- */
138
- public convertRequired(from: unknown, context?: TC): Result<Required<T>> {
139
- const options: ObjectConverterOptions<T> = { ...this.options, modifier: 'required' };
140
- return ObjectConverter._convert(from, context, this.fields, options) as Result<Required<T>>;
141
- }
142
-
143
- /**
144
- * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
145
- * all properties optional.
146
- * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional optional source properties.
147
- * {@label WITHOUT_OPTIONS}
148
- */
149
- public partial(): ObjectConverter<Partial<T>, TC>;
150
-
151
- /**
152
- * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
153
- * new optional properties as specified by a supplied {@link Conversion.ObjectConverterOptions | ObjectConverterOptions<T>}.
154
- * @param options - The {@link Conversion.ObjectConverterOptions | options} to be applied to the new
155
- * converter.
156
- * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional optional source properties.
157
- * {@label WITH_OPTIONS}
158
- * @deprecated Pass just the keys to be made optional.
159
- */
160
- public partial(options: ObjectConverterOptions<T>): ObjectConverter<Partial<T>, TC>;
161
-
162
- /**
163
- * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
164
- * new optional properties as specified by a supplied array of `keyof T`.
165
- * @param optional - The keys of the source object properties to be made optional.
166
- * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional optional source
167
- * properties.
168
- * {@label WITH_KEYS}
169
- */
170
- public partial(optional: (keyof T)[]): ObjectConverter<Partial<T>, TC>;
171
-
172
- /**
173
- * Concrete implementation of
174
- * {@link Conversion.ObjectConverter.(partial:1) | ObjectConverter.partial(ObjectConverterOptions)} and
175
- * {@link Conversion.ObjectConverter.(partial:2) | ObjectConverter.partial((keyof T))[]}.
176
- * @internal
177
- */
178
- public partial(opt?: ObjectConverterOptions<T> | (keyof T)[]): ObjectConverter<Partial<T>, TC> {
179
- const options: ObjectConverterOptions<Partial<T>> =
180
- opt === undefined
181
- ? { ...this.options, modifier: 'partial' }
182
- : Array.isArray(opt)
183
- ? { ...this.options, optionalFields: [...opt] }
184
- : (opt as ObjectConverterOptions<Partial<T>>);
185
- return new ObjectConverter<Partial<T>, TC>(this.fields as FieldConverters<Partial<T>, TC>, options)._with(
186
- this._traits()
187
- );
188
- }
189
-
190
- /**
191
- * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
192
- * new optional properties as specified by a supplied array of `keyof T`.
193
- * @param addOptionalProperties - The keys to be made optional.
194
- * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional optional source
195
- * properties.
196
- */
197
- public addPartial(addOptionalProperties: (keyof T)[]): ObjectConverter<Partial<T>, TC> {
198
- /* c8 ignore next 1 - coverage having a bad day */
199
- const myOptional = this.options.optionalFields ?? [];
200
- const optional = [...myOptional, ...addOptionalProperties];
201
- return this.partial(optional)._with(this._traits());
202
- }
203
-
204
- /**
205
- * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
206
- * all properties required.
207
- * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional required source properties.
208
- */
209
- public required(): ObjectConverter<Required<T>, TC> {
210
- const options: ObjectConverterOptions<Required<T>> = { ...this.options, modifier: 'required' };
211
- return new ObjectConverter<Required<T>, TC>(
212
- this.fields as FieldConverters<Required<T>, TC>,
213
- options
214
- )._with(this._traits());
215
- }
216
-
217
- private static _convert<T, TC>(
218
- from: unknown,
219
- context: TC | undefined,
220
- fields: FieldConverters<T, TC>,
221
- options: ObjectConverterOptions<T>
222
- ): Result<T> {
223
- // eslint bug thinks key is used before defined
224
- // eslint-disable-next-line no-use-before-define
225
- const converted = {} as { [key in keyof T]: T[key] };
226
- const errors: string[] = [];
227
- for (const key in fields) {
228
- if (fields[key]) {
229
- const isOptional =
230
- options.modifier === 'partial' ||
231
- (options.modifier !== 'required' &&
232
- (fields[key].isOptional === true || options.optionalFields?.includes(key) === true));
233
- const result = isOptional
234
- ? optionalField(key, fields[key]).convert(from, context)
235
- : field(key, fields[key]).convert(from, context);
236
- if (result.isSuccess() && result.value !== undefined) {
237
- converted[key] = result.value;
238
- } else if (result.isFailure()) {
239
- errors.push(result.message);
240
- }
241
- }
242
- }
243
-
244
- if (options.strict === true) {
245
- if (typeof from === 'object' && !Array.isArray(from)) {
246
- for (const key in from) {
247
- if (from.hasOwnProperty(key) && (!isKeyOf(key, fields) || fields[key] === undefined)) {
248
- errors.push(`${key}: unexpected property in source object`);
249
- }
250
- }
251
- } else {
252
- errors.push('source is not an object');
253
- }
254
- }
255
- return errors.length === 0
256
- ? succeed(converted)
257
- : fail(options.description ? `${options.description}: ${errors.join('\n')}` : errors.join('\n'));
258
- }
259
- }
@@ -1,165 +0,0 @@
1
- /*
2
- * Copyright (c) 2023 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 { Result, fail, succeed } from '../base';
24
- import { BaseConverter } from './baseConverter';
25
- import { Converter, ConverterTraits } from './converter';
26
-
27
- /**
28
- * Options for {@link Conversion.StringConverter | StringConverter}
29
- * matching method
30
- * @public
31
- */
32
-
33
- // eslint-disable-next-line @typescript-eslint/naming-convention
34
- export interface StringMatchOptions {
35
- /**
36
- * An optional message to be displayed if a non-matching string
37
- * is encountered.
38
- */
39
- message?: string;
40
- }
41
-
42
- /**
43
- * The {@link Conversion.StringConverter | StringConverter} class extends
44
- * {@link Conversion.BaseConverter | BaseConverter} to provide string-specific
45
- * helper methods.
46
- * @public
47
- */
48
- export class StringConverter<T extends string = string, TC = unknown> extends BaseConverter<T, TC> {
49
- /**
50
- * Construct a new {@link Conversion.StringConverter | StringConverter}.
51
- * @param defaultContext - Optional context used by the conversion.
52
- * @param traits - Optional traits to be applied to the conversion.
53
- * @param converter - Optional conversion function to be used for the conversion.
54
- */
55
- public constructor(
56
- defaultContext?: TC,
57
- traits?: ConverterTraits,
58
- converter: (from: unknown, self: Converter<T, TC>, context?: TC) => Result<T> = StringConverter._convert
59
- ) {
60
- super(converter, defaultContext, traits);
61
- }
62
-
63
- /**
64
- * @internal
65
- */
66
- protected static _convert<T extends string>(from: unknown): Result<T> {
67
- return typeof from === 'string' ? succeed(from as T) : fail(`Not a string: ${JSON.stringify(from)}`);
68
- }
69
-
70
- /**
71
- * @internal
72
- */
73
- protected static _wrap<T extends string, TC>(
74
- wrapped: StringConverter<T, TC>,
75
- converter: (from: T) => Result<T>,
76
- traits?: ConverterTraits
77
- ): StringConverter<T, TC> {
78
- return new StringConverter<T, TC>(undefined, undefined, (from: unknown) => {
79
- return wrapped.convert(from).onSuccess(converter);
80
- })._with(wrapped._traits(traits));
81
- }
82
-
83
- /**
84
- * Returns a {@link Conversion.StringConverter | StringConverter} which constrains the result to match
85
- * a supplied string.
86
- * @param match - The string to be matched
87
- * @param options - Optional {@link Conversion.StringMatchOptions} for this conversion.
88
- * @returns {@link Success} with a matching string or {@link Failure} with an informative
89
- * error if the string does not match.
90
- * {@label WITH_STRING}
91
- */
92
- public matching(match: string, options?: Partial<StringMatchOptions>): StringConverter<T, TC>;
93
-
94
- /**
95
- * Returns a {@link Conversion.StringConverter | StringConverter} which constrains the result to match
96
- * one of a supplied array of strings.
97
- * @param match - The array of allowed strings.
98
- * @param options - Optional {@link Conversion.StringMatchOptions} for this conversion.
99
- * @returns {@link Success} with a matching string or {@link Failure} with an informative
100
- * error if the string does not match.
101
- * {@label WITH_ARRAY}
102
- */
103
- public matching(match: string[], options?: Partial<StringMatchOptions>): StringConverter<T, TC>;
104
-
105
- /**
106
- * Returns a {@link Conversion.StringConverter | StringConverter} which constrains the result to match
107
- * one of a supplied `Set` of strings.
108
- * @param match - The `Set` of allowed strings.
109
- * @param options - Optional {@link Conversion.StringMatchOptions} for this conversion.
110
- * @returns {@link Success} with a matching string or {@link Failure} with an informative
111
- * error if the string does not match.
112
- * {@label WITH_SET}
113
- */
114
- public matching(match: Set<T>, options?: Partial<StringMatchOptions>): StringConverter<T, TC>;
115
-
116
- /**
117
- * Returns a {@link Conversion.StringConverter | StringConverter} which constrains the result to match
118
- * a supplied regular expression.
119
- * @param match - The regular expression to be used as a constraint.
120
- * @param options - Optional {@link Conversion.StringMatchOptions} for this conversion
121
- * @returns {@link Success} with a matching string or {@link Failure} with an informative
122
- * error if the string does not match.
123
- * {@label WITH_REGEXP}
124
- */
125
- public matching(match: RegExp, options?: Partial<StringMatchOptions>): StringConverter<T, TC>;
126
-
127
- /**
128
- * Concrete implementation of {@link Conversion.StringConverter.matching#string | StringConverter.matching(string)},
129
- * {@link Conversion.StringConverter.matching#array | StringConverter.matching(string[])},
130
- * {@link Conversion.StringConverter.matching#set | StringConverter.matching(Set<string>)}, and
131
- * {@link Conversion.StringConverter.matching#regexp | StringConverter.matching(RegExp)}.
132
- * @internal
133
- */
134
- public matching(
135
- match: string | string[] | Set<T> | RegExp,
136
- options?: Partial<StringMatchOptions>
137
- ): StringConverter<T, TC> {
138
- const message = options?.message;
139
- if (typeof match === 'string') {
140
- return StringConverter._wrap<T, TC>(this, (from: T) => {
141
- return match === from
142
- ? succeed(from as T)
143
- : fail(message ? `"${from}": ${message}` : `"${from}": does not match "${match}"`);
144
- });
145
- } else if (match instanceof RegExp) {
146
- return StringConverter._wrap<T, TC>(this, (from: T) => {
147
- return match.test(from)
148
- ? succeed(from as T)
149
- : fail(message ? `"${from}": ${message}` : `"${from}": does not match "${match}"`);
150
- });
151
- } else if (match instanceof Set) {
152
- return StringConverter._wrap<T, TC>(this, (from: T) => {
153
- return match.has(from)
154
- ? succeed(from as T)
155
- : fail(message ? `"${from}": ${message}` : `"${from}": not found in set`);
156
- });
157
- } else {
158
- return StringConverter._wrap<T, TC>(this, (from: T) => {
159
- return match.includes(from)
160
- ? succeed(from as T)
161
- : fail(message ? `"${from}": ${message}` : `"${from}": not found in [${match.join(',')}]`);
162
- });
163
- }
164
- }
165
- }