@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,149 @@
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 { succeed } from '../base';
23
+ /**
24
+ * Generic {@link Conversion.DefaultingConverter | DefaultingConverter}, which wraps another converter
25
+ * to substitute a supplied default value for any errors returned by the inner converter.
26
+ * @public
27
+ */
28
+ export class GenericDefaultingConverter {
29
+ /**
30
+ * {@inheritdoc Converter.isOptional}
31
+ */
32
+ get isOptional() {
33
+ return this._converter.isOptional;
34
+ }
35
+ /**
36
+ * {@inheritdoc Converter.isOptional}
37
+ */
38
+ get brand() {
39
+ return this._converter.brand;
40
+ }
41
+ /**
42
+ * Constructs a new {@link Conversion.GenericDefaultingConverter | generic defaulting converter}.
43
+ * @param converter - inner {@link Converter | Converter} used for the base conversion.
44
+ * @param defaultValue - default value to be supplied if the inner conversion fails.
45
+ */
46
+ constructor(converter, defaultValue) {
47
+ this.defaultValue = defaultValue;
48
+ this._converter = converter;
49
+ }
50
+ /**
51
+ * {@inheritdoc Converter.convert}
52
+ */
53
+ convert(from, ctx) {
54
+ return this._applyDefault(this._converter.convert(from, ctx));
55
+ }
56
+ /**
57
+ * {@inheritdoc Converter.convertOptional}
58
+ */
59
+ convertOptional(from, context, onError) {
60
+ const converted = this._converter.convertOptional(from, context, onError);
61
+ if (converted.isFailure()) {
62
+ return succeed(this.defaultValue);
63
+ }
64
+ return converted;
65
+ }
66
+ /**
67
+ * {@inheritdoc Converter.optional}
68
+ */
69
+ optional(onError) {
70
+ // need to let the inner converter do its optional thing first or our default
71
+ // steps on everything
72
+ return this._converter.optional(onError).withAction((result) => this._applyDefault(result));
73
+ }
74
+ /**
75
+ * {@inheritdoc Converter.map}
76
+ */
77
+ map(mapper) {
78
+ return this._converter.withAction((result) => this._applyDefault(result)).map(mapper);
79
+ }
80
+ /**
81
+ * {@inheritdoc Converter.mapConvert}
82
+ */
83
+ mapConvert(mapConverter) {
84
+ return this._converter.withAction((result) => this._applyDefault(result)).mapConvert(mapConverter);
85
+ }
86
+ /**
87
+ * {@inheritdoc Converter.mapItems}
88
+ */
89
+ mapItems(mapper) {
90
+ return this._converter.withAction((result) => this._applyDefault(result)).mapItems(mapper);
91
+ }
92
+ /**
93
+ * {@inheritdoc Converter.mapConvertItems}
94
+ */
95
+ mapConvertItems(mapConverter) {
96
+ return this._converter.withAction((result) => this._applyDefault(result)).mapConvertItems(mapConverter);
97
+ }
98
+ /**
99
+ * {@inheritdoc Converter.withAction}
100
+ */
101
+ withAction(action) {
102
+ return this._converter.withAction((result) => this._applyDefault(result)).withAction(action);
103
+ }
104
+ /**
105
+ * {@inheritdoc Converter.withTypeGuard}
106
+ */
107
+ withTypeGuard(guard, message) {
108
+ return this._converter.withAction((result) => this._applyDefault(result)).withTypeGuard(guard);
109
+ }
110
+ /**
111
+ * {@inheritdoc Converter.withItemTypeGuard}
112
+ */
113
+ withItemTypeGuard(guard, message) {
114
+ return this._converter.withAction((result) => this._applyDefault(result)).withItemTypeGuard(guard);
115
+ }
116
+ /**
117
+ * {@inheritdoc Converter.withConstraint}
118
+ */
119
+ withConstraint(constraint, options) {
120
+ return this._converter.withAction((result) => this._applyDefault(result)).withConstraint(constraint);
121
+ }
122
+ /**
123
+ * {@inheritdoc Converter.withBrand}
124
+ */
125
+ withBrand(brand) {
126
+ return this._converter.withAction((result) => this._applyDefault(result)).withBrand(brand);
127
+ }
128
+ /**
129
+ * {@inheritdoc Converter.withFormattedError}
130
+ */
131
+ withFormattedError(formatter) {
132
+ // formatter should never actually be invoked for a defaulting converter
133
+ return this._converter.withAction((result) => this._applyDefault(result)).withFormattedError(formatter);
134
+ }
135
+ /**
136
+ * Returns a Converter which always succeeds with the supplied default value rather
137
+ * than failing.
138
+ *
139
+ * Note that the supplied default value *overrides* the default value of this
140
+ * {@link Conversion.DefaultingConverter | DefaultingConverter}.
141
+ */
142
+ withDefault(dflt) {
143
+ return new GenericDefaultingConverter(this._converter, dflt);
144
+ }
145
+ _applyDefault(converted) {
146
+ return converted.success ? converted : succeed(this.defaultValue);
147
+ }
148
+ }
149
+ //# sourceMappingURL=defaultingConverter.js.map
@@ -0,0 +1,29 @@
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
+ import * as Converters from './converters';
23
+ export * from './baseConverter';
24
+ export * from './converter';
25
+ export * from './defaultingConverter';
26
+ export * from './objectConverter';
27
+ export * from './stringConverter';
28
+ export { Converters };
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,141 @@
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
+ import { fail, isKeyOf, succeed } from '../base';
23
+ import { BaseConverter } from './baseConverter';
24
+ import { field, optionalField } from './converters';
25
+ /**
26
+ * A {@link Converter | Converter} which converts an object of type `<T>` without changing shape, given
27
+ * a {@link Conversion.FieldConverters | FieldConverters<T>} for the fields in the object.
28
+ * @remarks
29
+ * By default, if all of the required fields exist and can be converted, returns a new object with
30
+ * the converted values under the original key names. If any required fields do not exist or cannot
31
+ * be converted, the entire conversion fails. See {@link Conversion.ObjectConverterOptions | ObjectConverterOptions}
32
+ * for other conversion options.
33
+ * @public
34
+ */
35
+ export class ObjectConverter extends BaseConverter {
36
+ /**
37
+ * Concrete implementation of {@link Converters.(ObjectConverter:constructor)}
38
+ * @internal
39
+ */
40
+ constructor(fields, opt) {
41
+ const options = Array.isArray(opt) ? { optionalFields: opt } : opt !== null && opt !== void 0 ? opt : { optionalFields: [] };
42
+ super((from, __self, context) => ObjectConverter._convert(from, context, fields, options));
43
+ this.fields = fields;
44
+ this.options = options;
45
+ }
46
+ /**
47
+ * Converts the supplied object using the {@link Conversion.ObjectConverter | ObjectConverter}
48
+ * with all fields optional.
49
+ * @param from - The object to be converted.
50
+ * @param context - An optional context object passed to the field converters.
51
+ * @returns A {@link Result} containing the converted object or an error message.
52
+ */
53
+ convertPartial(from, context) {
54
+ const options = Object.assign(Object.assign({}, this.options), { modifier: 'partial' });
55
+ return ObjectConverter._convert(from, context, this.fields, options);
56
+ }
57
+ /**
58
+ * Converts the supplied object using the {@link Conversion.ObjectConverter | ObjectConverter}
59
+ * with all fields required.
60
+ * @param from - The object to be converted.
61
+ * @param context - An optional context object passed to the field converters.
62
+ * @returns A {@link Result} containing the converted object or an error message.
63
+ */
64
+ convertRequired(from, context) {
65
+ const options = Object.assign(Object.assign({}, this.options), { modifier: 'required' });
66
+ return ObjectConverter._convert(from, context, this.fields, options);
67
+ }
68
+ /**
69
+ * Concrete implementation of
70
+ * {@link Conversion.ObjectConverter.(partial:1) | ObjectConverter.partial(ObjectConverterOptions)} and
71
+ * {@link Conversion.ObjectConverter.(partial:2) | ObjectConverter.partial((keyof T))[]}.
72
+ * @internal
73
+ */
74
+ partial(opt) {
75
+ const options = opt === undefined
76
+ ? Object.assign(Object.assign({}, this.options), { modifier: 'partial' }) : Array.isArray(opt)
77
+ ? Object.assign(Object.assign({}, this.options), { optionalFields: [...opt] }) : opt;
78
+ return new ObjectConverter(this.fields, options)._with(this._traits());
79
+ }
80
+ /**
81
+ * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
82
+ * new optional properties as specified by a supplied array of `keyof T`.
83
+ * @param addOptionalProperties - The keys to be made optional.
84
+ * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional optional source
85
+ * properties.
86
+ */
87
+ addPartial(addOptionalProperties) {
88
+ var _a;
89
+ /* c8 ignore next 1 - coverage having a bad day */
90
+ const myOptional = (_a = this.options.optionalFields) !== null && _a !== void 0 ? _a : [];
91
+ const optional = [...myOptional, ...addOptionalProperties];
92
+ return this.partial(optional)._with(this._traits());
93
+ }
94
+ /**
95
+ * Creates a new {@link Conversion.ObjectConverter | ObjectConverter} derived from this one but with
96
+ * all properties required.
97
+ * @returns A new {@link Conversion.ObjectConverter | ObjectConverter} with the additional required source properties.
98
+ */
99
+ required() {
100
+ const options = Object.assign(Object.assign({}, this.options), { modifier: 'required' });
101
+ return new ObjectConverter(this.fields, options)._with(this._traits());
102
+ }
103
+ static _convert(from, context, fields, options) {
104
+ // eslint bug thinks key is used before defined
105
+ var _a;
106
+ const converted = {};
107
+ const errors = [];
108
+ for (const key in fields) {
109
+ if (fields[key]) {
110
+ const isOptional = options.modifier === 'partial' ||
111
+ (options.modifier !== 'required' &&
112
+ (fields[key].isOptional === true || ((_a = options.optionalFields) === null || _a === void 0 ? void 0 : _a.includes(key)) === true));
113
+ const result = isOptional
114
+ ? optionalField(key, fields[key]).convert(from, context)
115
+ : field(key, fields[key]).convert(from, context);
116
+ if (result.isSuccess() && result.value !== undefined) {
117
+ converted[key] = result.value;
118
+ }
119
+ else if (result.isFailure()) {
120
+ errors.push(result.message);
121
+ }
122
+ }
123
+ }
124
+ if (options.strict === true) {
125
+ if (typeof from === 'object' && !Array.isArray(from)) {
126
+ for (const key in from) {
127
+ if (from.hasOwnProperty(key) && (!isKeyOf(key, fields) || fields[key] === undefined)) {
128
+ errors.push(`${key}: unexpected property in source object`);
129
+ }
130
+ }
131
+ }
132
+ else {
133
+ errors.push('source is not an object');
134
+ }
135
+ }
136
+ return errors.length === 0
137
+ ? succeed(converted)
138
+ : fail(options.description ? `${options.description}: ${errors.join('\n')}` : errors.join('\n'));
139
+ }
140
+ }
141
+ //# sourceMappingURL=objectConverter.js.map
@@ -0,0 +1,93 @@
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
+ import { fail, succeed } from '../base';
23
+ import { BaseConverter } from './baseConverter';
24
+ /**
25
+ * The {@link Conversion.StringConverter | StringConverter} class extends
26
+ * {@link Conversion.BaseConverter | BaseConverter} to provide string-specific
27
+ * helper methods.
28
+ * @public
29
+ */
30
+ export class StringConverter extends BaseConverter {
31
+ /**
32
+ * Construct a new {@link Conversion.StringConverter | StringConverter}.
33
+ * @param defaultContext - Optional context used by the conversion.
34
+ * @param traits - Optional traits to be applied to the conversion.
35
+ * @param converter - Optional conversion function to be used for the conversion.
36
+ */
37
+ constructor(defaultContext, traits, converter = StringConverter._convert) {
38
+ super(converter, defaultContext, traits);
39
+ }
40
+ /**
41
+ * @internal
42
+ */
43
+ static _convert(from) {
44
+ return typeof from === 'string' ? succeed(from) : fail(`Not a string: ${JSON.stringify(from)}`);
45
+ }
46
+ /**
47
+ * @internal
48
+ */
49
+ static _wrap(wrapped, converter, traits) {
50
+ return new StringConverter(undefined, undefined, (from) => {
51
+ return wrapped.convert(from).onSuccess(converter);
52
+ })._with(wrapped._traits(traits));
53
+ }
54
+ /**
55
+ * Concrete implementation of {@link Conversion.StringConverter.matching#string | StringConverter.matching(string)},
56
+ * {@link Conversion.StringConverter.matching#array | StringConverter.matching(string[])},
57
+ * {@link Conversion.StringConverter.matching#set | StringConverter.matching(Set<string>)}, and
58
+ * {@link Conversion.StringConverter.matching#regexp | StringConverter.matching(RegExp)}.
59
+ * @internal
60
+ */
61
+ matching(match, options) {
62
+ const message = options === null || options === void 0 ? void 0 : options.message;
63
+ if (typeof match === 'string') {
64
+ return StringConverter._wrap(this, (from) => {
65
+ return match === from
66
+ ? succeed(from)
67
+ : fail(message ? `"${from}": ${message}` : `"${from}": does not match "${match}"`);
68
+ });
69
+ }
70
+ else if (match instanceof RegExp) {
71
+ return StringConverter._wrap(this, (from) => {
72
+ return match.test(from)
73
+ ? succeed(from)
74
+ : fail(message ? `"${from}": ${message}` : `"${from}": does not match "${match}"`);
75
+ });
76
+ }
77
+ else if (match instanceof Set) {
78
+ return StringConverter._wrap(this, (from) => {
79
+ return match.has(from)
80
+ ? succeed(from)
81
+ : fail(message ? `"${from}": ${message}` : `"${from}": not found in set`);
82
+ });
83
+ }
84
+ else {
85
+ return StringConverter._wrap(this, (from) => {
86
+ return match.includes(from)
87
+ ? succeed(from)
88
+ : fail(message ? `"${from}": ${message}` : `"${from}": not found in [${match.join(',')}]`);
89
+ });
90
+ }
91
+ }
92
+ }
93
+ //# sourceMappingURL=stringConverter.js.map
@@ -0,0 +1,114 @@
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
+ import { HashingNormalizer } from './hashingNormalizer';
23
+ function encodeUtf8(input) {
24
+ // Prefer built-in TextEncoder when available (browsers and modern Node)
25
+ // @ts-ignore - global TextEncoder may be unavailable in some environments
26
+ const GlobalTextEncoder = typeof TextEncoder !== 'undefined' ? TextEncoder : undefined;
27
+ if (GlobalTextEncoder) {
28
+ return new GlobalTextEncoder().encode(input);
29
+ }
30
+ // Very small manual UTF-8 encoder as a last resort
31
+ /* eslint-disable no-bitwise */
32
+ const bytes = [];
33
+ for (let i = 0; i < input.length; i++) {
34
+ let codePoint = input.charCodeAt(i);
35
+ if (codePoint >= 0xd800 && codePoint <= 0xdbff && i + 1 < input.length) {
36
+ const next = input.charCodeAt(i + 1);
37
+ if (next >= 0xdc00 && next <= 0xdfff) {
38
+ codePoint = ((codePoint - 0xd800) << 10) + (next - 0xdc00) + 0x10000;
39
+ i++;
40
+ }
41
+ }
42
+ if (codePoint < 0x80) {
43
+ bytes.push(codePoint);
44
+ }
45
+ else if (codePoint < 0x800) {
46
+ bytes.push(0xc0 | (codePoint >> 6));
47
+ bytes.push(0x80 | (codePoint & 0x3f));
48
+ }
49
+ else if (codePoint < 0x10000) {
50
+ bytes.push(0xe0 | (codePoint >> 12));
51
+ bytes.push(0x80 | ((codePoint >> 6) & 0x3f));
52
+ bytes.push(0x80 | (codePoint & 0x3f));
53
+ }
54
+ else {
55
+ bytes.push(0xf0 | (codePoint >> 18));
56
+ bytes.push(0x80 | ((codePoint >> 12) & 0x3f));
57
+ bytes.push(0x80 | ((codePoint >> 6) & 0x3f));
58
+ bytes.push(0x80 | (codePoint & 0x3f));
59
+ }
60
+ }
61
+ /* eslint-enable no-bitwise */
62
+ return new Uint8Array(bytes);
63
+ }
64
+ const POLYNOMIAL = 0xedb88320;
65
+ const crc32Table = [];
66
+ function crc32(bytes, crc = 0xffffffff) {
67
+ if (crc32Table.length === 0) {
68
+ buildCrc32Table();
69
+ }
70
+ for (let i = 0; i < bytes.length; ++i) {
71
+ // eslint-disable-next-line no-bitwise
72
+ crc = crc32Table[(crc ^ bytes[i]) & 0xff] ^ (crc >>> 8);
73
+ }
74
+ return (crc ^ -1) >>> 0;
75
+ }
76
+ const toUInt32 = (n) => {
77
+ if (n >= 0) {
78
+ return n;
79
+ }
80
+ return 0xffffffff - n * -1 + 1;
81
+ };
82
+ function buildCrc32Table() {
83
+ if (crc32Table.length === 0) {
84
+ for (let n = 0; n < 256; n++) {
85
+ let r = n;
86
+ for (let i = 0; i < 8; i++) {
87
+ // eslint-disable-next-line no-bitwise
88
+ if (r & 1) {
89
+ r = (r >>> 1) ^ POLYNOMIAL;
90
+ }
91
+ else {
92
+ r = r >>> 1;
93
+ }
94
+ }
95
+ crc32Table.push(toUInt32(r));
96
+ }
97
+ }
98
+ }
99
+ /**
100
+ * A {@link Hash.HashingNormalizer | hashing normalizer} which computes object
101
+ * hash using the CRC32 algorithm.
102
+ * @public
103
+ */
104
+ export class Crc32Normalizer extends HashingNormalizer {
105
+ constructor() {
106
+ super(Crc32Normalizer.crc32Hash);
107
+ }
108
+ static crc32Hash(parts) {
109
+ return crc32(encodeUtf8(parts.join('|')))
110
+ .toString(16)
111
+ .padStart(8, '0');
112
+ }
113
+ }
114
+ //# sourceMappingURL=crcNormalizer.js.map
@@ -0,0 +1,92 @@
1
+ /*
2
+ * Copyright (c) 2021 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 { Normalizer, captureResult, fail, mapResults, succeed } from '../base';
23
+ /**
24
+ * Normalizes an arbitrary JSON object
25
+ * @public
26
+ */
27
+ export class HashingNormalizer extends Normalizer {
28
+ constructor(hash) {
29
+ super();
30
+ this._hash = hash;
31
+ }
32
+ computeHash(from) {
33
+ switch (typeof from) {
34
+ case 'string':
35
+ case 'bigint':
36
+ case 'boolean':
37
+ case 'number':
38
+ case 'symbol':
39
+ case 'undefined':
40
+ return this._normalizeLiteralToString(from).onSuccess((v) => {
41
+ return captureResult(() => this._hash([v]));
42
+ });
43
+ case 'object':
44
+ if (from === null || from instanceof Date || from instanceof RegExp) {
45
+ return this._normalizeLiteralToString(from).onSuccess((v) => {
46
+ return captureResult(() => this._hash([v]));
47
+ });
48
+ }
49
+ else if (Array.isArray(from)) {
50
+ return mapResults(from.map((e) => this.computeHash(e))).onSuccess((a) => {
51
+ return captureResult(() => this._hash(['array', ...a]));
52
+ });
53
+ }
54
+ else if (from instanceof Map || from instanceof Set) {
55
+ const type = from instanceof Map ? 'map' : 'set';
56
+ return this.computeHash([type, ...this.normalizeEntries(from.entries())]);
57
+ }
58
+ return this.computeHash(['object', ...this.normalizeEntries(Object.entries(from))]);
59
+ }
60
+ return fail(`computeHash: Unexpected type - cannot hash '${typeof from}'`);
61
+ }
62
+ /**
63
+ * Constructs a normalized string representation of some literal value.
64
+ * @param from - The literal value to be normalized.
65
+ * @returns A normalized string representation of the literal.
66
+ * @internal
67
+ */
68
+ _normalizeLiteralToString(from) {
69
+ switch (typeof from) {
70
+ case 'string':
71
+ return succeed(from);
72
+ case 'bigint':
73
+ case 'boolean':
74
+ case 'number':
75
+ case 'symbol':
76
+ case 'undefined':
77
+ return succeed(`${typeof from}:[[[${String(from)}]]]`);
78
+ }
79
+ if (from === null) {
80
+ return succeed('object:[[[null]]');
81
+ }
82
+ if (from instanceof Date) {
83
+ return succeed(`Date:[[[${String(from.valueOf())}]]]`);
84
+ }
85
+ if (from instanceof RegExp) {
86
+ return succeed(`RegExp:[[[${from.toString()}]]]`);
87
+ }
88
+ /* c8 ignore next 2 */
89
+ return fail(`cannot normalize ${JSON.stringify(from)}`);
90
+ }
91
+ }
92
+ //# sourceMappingURL=hashingNormalizer.js.map
@@ -0,0 +1,24 @@
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
+ export * from './crcNormalizer';
23
+ export * from './hashingNormalizer';
24
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,24 @@
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
+ export * from './logger';
23
+ export * from './logReporter';
24
+ //# sourceMappingURL=index.js.map