@strictly/react-form 0.0.1 → 0.0.3

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 (173) hide show
  1. package/.out/core/mobx/field_adapter.d.ts +7 -6
  2. package/.out/core/mobx/field_adapter_builder.d.ts +12 -13
  3. package/.out/core/mobx/field_adapter_builder.js +8 -12
  4. package/.out/core/mobx/field_adapters_of_values.d.ts +4 -0
  5. package/.out/core/mobx/flattened_adapters_of_fields.d.ts +2 -2
  6. package/.out/core/mobx/flattened_list_types_of_type.d.ts +8 -0
  7. package/.out/core/mobx/form_fields_of_field_adapters.d.ts +8 -0
  8. package/.out/core/mobx/form_presenter.d.ts +21 -24
  9. package/.out/core/mobx/form_presenter.js +64 -69
  10. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.d.ts +13 -0
  11. package/.out/core/mobx/merge_field_adapters_with_two_way_converter.js +11 -0
  12. package/.out/core/mobx/merge_field_adapters_with_validators.d.ts +11 -0
  13. package/.out/core/mobx/merge_field_adapters_with_validators.js +45 -0
  14. package/.out/core/mobx/specs/fixtures.d.ts +7 -0
  15. package/.out/core/mobx/specs/fixtures.js +20 -0
  16. package/.out/core/mobx/specs/flattened_adapters_of_fields.tests.js +5 -2
  17. package/.out/core/mobx/specs/{flattened_list_type_defs_of.tests.js → flattened_list_types_of_types.tests.js} +7 -7
  18. package/.out/core/mobx/specs/form_presenter.tests.js +162 -60
  19. package/.out/core/mobx/specs/merge_field_adapters_with_two_way_converter.js +89 -0
  20. package/.out/core/mobx/specs/merge_field_adapters_with_validators.tests.js +172 -0
  21. package/.out/core/mobx/types.d.ts +2 -2
  22. package/.out/field_converters/chain_field_converter.d.ts +3 -3
  23. package/.out/field_converters/chain_field_converter.js +17 -12
  24. package/.out/field_converters/identity_converter.d.ts +3 -3
  25. package/.out/field_converters/identity_converter.js +10 -6
  26. package/.out/field_converters/integer_to_string_converter.d.ts +5 -4
  27. package/.out/field_converters/integer_to_string_converter.js +13 -6
  28. package/.out/field_converters/list_converter.d.ts +2 -2
  29. package/.out/field_converters/list_converter.js +6 -1
  30. package/.out/field_converters/maybe_identity_converter.d.ts +3 -3
  31. package/.out/field_converters/maybe_identity_converter.js +3 -1
  32. package/.out/field_converters/nullable_to_boolean_converter.d.ts +9 -8
  33. package/.out/field_converters/nullable_to_boolean_converter.js +13 -7
  34. package/.out/field_converters/select_value_type_converter.d.ts +20 -15
  35. package/.out/field_converters/select_value_type_converter.js +29 -14
  36. package/.out/field_converters/specs/chain_field_converter.tests.d.ts +1 -0
  37. package/.out/field_converters/specs/chain_field_converter.tests.js +251 -0
  38. package/.out/field_converters/trimming_string_converter.d.ts +3 -3
  39. package/.out/field_converters/trimming_string_converter.js +7 -3
  40. package/.out/field_converters/validating_converter.d.ts +3 -3
  41. package/.out/field_converters/validating_converter.js +7 -5
  42. package/.out/index.d.ts +9 -2
  43. package/.out/index.js +9 -2
  44. package/.out/mantine/create_checkbox.d.ts +2 -3
  45. package/.out/mantine/create_checkbox.js +6 -5
  46. package/.out/mantine/create_pill.js +2 -2
  47. package/.out/mantine/create_radio.js +1 -1
  48. package/.out/mantine/create_radio_group.d.ts +2 -3
  49. package/.out/mantine/create_radio_group.js +4 -3
  50. package/.out/mantine/create_text_input.d.ts +2 -3
  51. package/.out/mantine/create_text_input.js +6 -5
  52. package/.out/mantine/create_value_input.d.ts +2 -3
  53. package/.out/mantine/create_value_input.js +6 -5
  54. package/.out/mantine/error_renderer.d.ts +6 -0
  55. package/.out/mantine/error_renderer.js +5 -0
  56. package/.out/mantine/hooks.d.ts +9 -13
  57. package/.out/mantine/hooks.js +10 -15
  58. package/.out/mantine/specs/checkbox_hooks.stories.d.ts +7 -2
  59. package/.out/mantine/specs/checkbox_hooks.stories.js +33 -6
  60. package/.out/mantine/specs/list_hooks.stories.js +2 -2
  61. package/.out/mantine/specs/radio_group_hooks.stories.d.ts +7 -2
  62. package/.out/mantine/specs/radio_group_hooks.stories.js +33 -6
  63. package/.out/mantine/specs/select_hooks.stories.d.ts +8 -2
  64. package/.out/mantine/specs/select_hooks.stories.js +45 -8
  65. package/.out/mantine/specs/text_input_hooks.stories.d.ts +5 -1
  66. package/.out/mantine/specs/text_input_hooks.stories.js +23 -8
  67. package/.out/mantine/specs/value_input_hooks.stories.d.ts +7 -2
  68. package/.out/mantine/specs/value_input_hooks.stories.js +49 -15
  69. package/.out/mantine/types.d.ts +4 -1
  70. package/.out/tsconfig.tsbuildinfo +1 -1
  71. package/.out/types/error_of_field.d.ts +2 -0
  72. package/.out/types/error_of_field.js +1 -0
  73. package/.out/types/field.d.ts +1 -1
  74. package/.out/types/field_converters.d.ts +17 -10
  75. package/.out/types/field_converters.js +5 -5
  76. package/.out/types/flattened_validators_of_fields.d.ts +8 -0
  77. package/.out/types/flattened_validators_of_fields.js +1 -0
  78. package/.out/types/merge_validators.d.ts +7 -0
  79. package/.out/types/merge_validators.js +38 -0
  80. package/.out/types/specs/flattened_validators_of_fields.tests.d.ts +1 -0
  81. package/.out/types/specs/flattened_validators_of_fields.tests.js +16 -0
  82. package/.out/types/specs/merge_validators.tests.d.ts +1 -0
  83. package/.out/types/specs/merge_validators.tests.js +192 -0
  84. package/.out/util/partial.d.ts +11 -5
  85. package/.out/util/partial.js +55 -15
  86. package/.turbo/turbo-build.log +9 -9
  87. package/.turbo/turbo-check-types.log +1 -1
  88. package/.turbo/turbo-release$colon$exports.log +1 -1
  89. package/README.md +5 -1
  90. package/core/mobx/field_adapter.ts +15 -7
  91. package/core/mobx/field_adapter_builder.ts +39 -75
  92. package/core/mobx/field_adapters_of_values.ts +17 -0
  93. package/core/mobx/flattened_adapters_of_fields.ts +3 -3
  94. package/core/mobx/flattened_list_types_of_type.ts +17 -0
  95. package/core/mobx/form_fields_of_field_adapters.ts +16 -0
  96. package/core/mobx/form_presenter.ts +117 -104
  97. package/core/mobx/merge_field_adapters_with_two_way_converter.ts +68 -0
  98. package/core/mobx/merge_field_adapters_with_validators.ts +99 -0
  99. package/core/mobx/specs/fixtures.ts +73 -0
  100. package/core/mobx/specs/flattened_adapters_of_fields.tests.ts +23 -2
  101. package/core/mobx/specs/flattened_list_types_of_types.tests.ts +35 -0
  102. package/core/mobx/specs/form_presenter.tests.ts +248 -124
  103. package/core/mobx/specs/merge_field_adapters_with_two_way_converter.ts +140 -0
  104. package/core/mobx/specs/merge_field_adapters_with_validators.tests.ts +259 -0
  105. package/core/mobx/types.ts +3 -3
  106. package/dist/index.cjs +527 -10412
  107. package/dist/index.d.cts +153 -111
  108. package/dist/index.d.ts +153 -111
  109. package/dist/index.js +527 -10420
  110. package/field_converters/chain_field_converter.ts +37 -23
  111. package/field_converters/identity_converter.ts +14 -10
  112. package/field_converters/integer_to_string_converter.ts +15 -9
  113. package/field_converters/list_converter.ts +8 -3
  114. package/field_converters/maybe_identity_converter.ts +7 -4
  115. package/field_converters/nullable_to_boolean_converter.ts +23 -16
  116. package/field_converters/select_value_type_converter.ts +86 -26
  117. package/field_converters/specs/chain_field_converter.tests.ts +302 -0
  118. package/field_converters/trimming_string_converter.ts +11 -6
  119. package/field_converters/validating_converter.ts +21 -11
  120. package/index.ts +9 -2
  121. package/mantine/create_checkbox.tsx +15 -8
  122. package/mantine/create_list.tsx +1 -4
  123. package/mantine/create_pill.tsx +2 -2
  124. package/mantine/create_radio.tsx +1 -1
  125. package/mantine/create_radio_group.tsx +8 -6
  126. package/mantine/create_text_input.tsx +20 -8
  127. package/mantine/create_value_input.tsx +17 -8
  128. package/mantine/error_renderer.ts +15 -0
  129. package/mantine/hooks.tsx +25 -51
  130. package/mantine/specs/__snapshots__/checkbox_hooks.tests.tsx.snap +126 -0
  131. package/mantine/specs/__snapshots__/radio_group_hooks.tests.tsx.snap +356 -0
  132. package/mantine/specs/__snapshots__/select_hooks.tests.tsx.snap +208 -12
  133. package/mantine/specs/__snapshots__/text_input_hooks.tests.tsx.snap +45 -0
  134. package/mantine/specs/__snapshots__/value_input_hooks.tests.tsx.snap +194 -8
  135. package/mantine/specs/checkbox_hooks.stories.tsx +47 -7
  136. package/mantine/specs/list_hooks.stories.tsx +2 -2
  137. package/mantine/specs/radio_group_hooks.stories.tsx +47 -7
  138. package/mantine/specs/select_hooks.stories.tsx +55 -8
  139. package/mantine/specs/text_input_hooks.stories.tsx +32 -7
  140. package/mantine/specs/value_input_hooks.stories.tsx +57 -16
  141. package/mantine/types.ts +5 -1
  142. package/package.json +20 -7
  143. package/tsconfig.json +1 -0
  144. package/types/error_of_field.ts +3 -0
  145. package/types/field.ts +1 -1
  146. package/types/field_converters.ts +21 -10
  147. package/types/flattened_validators_of_fields.ts +34 -0
  148. package/types/merge_validators.ts +80 -0
  149. package/types/specs/error_type_of_field.tests.ts +2 -2
  150. package/types/specs/flattened_validators_of_fields.tests.ts +93 -0
  151. package/types/specs/merge_validators.tests.ts +267 -0
  152. package/util/partial.tsx +200 -16
  153. package/.out/core/mobx/flattened_list_type_defs_of.d.ts +0 -8
  154. package/.out/field_validators/minimum_string_length_field_validator.d.ts +0 -2
  155. package/.out/field_validators/minimum_string_length_field_validator.js +0 -8
  156. package/.out/types/error_type_of_field.d.ts +0 -2
  157. package/.out/types/field_validator.d.ts +0 -3
  158. package/.out/types/flattened_form_fields_of.d.ts +0 -9
  159. package/.out/types/specs/flattened_form_fields_of.tests.js +0 -13
  160. package/core/mobx/flattened_list_type_defs_of.ts +0 -17
  161. package/core/mobx/specs/flattened_list_type_defs_of.tests.ts +0 -35
  162. package/field_validators/minimum_string_length_field_validator.ts +0 -13
  163. package/mantine/specs/__snapshots__/check_box_hooks.tests.tsx.snap +0 -227
  164. package/types/error_type_of_field.ts +0 -3
  165. package/types/field_validator.ts +0 -7
  166. package/types/flattened_form_fields_of.ts +0 -16
  167. package/types/specs/flattened_form_fields_of.tests.ts +0 -43
  168. /package/.out/core/mobx/{flattened_list_type_defs_of.js → field_adapters_of_values.js} +0 -0
  169. /package/.out/core/mobx/{specs/flattened_list_type_defs_of.tests.d.ts → flattened_list_types_of_type.js} +0 -0
  170. /package/.out/{types/error_type_of_field.js → core/mobx/form_fields_of_field_adapters.js} +0 -0
  171. /package/.out/{types/field_validator.js → core/mobx/specs/flattened_list_types_of_types.tests.d.ts} +0 -0
  172. /package/.out/{types/flattened_form_fields_of.js → core/mobx/specs/merge_field_adapters_with_two_way_converter.d.ts} +0 -0
  173. /package/.out/{types/specs/flattened_form_fields_of.tests.d.ts → core/mobx/specs/merge_field_adapters_with_validators.tests.d.ts} +0 -0
@@ -2,13 +2,14 @@ import {
2
2
  UnreachableError,
3
3
  } from '@strictly/base'
4
4
  import {
5
- type FieldConversion,
6
- FieldConversionResult,
7
- type FieldConverter,
8
- type SafeFieldConverter,
5
+ type AnnotatedFieldConversion,
6
+ type AnnotatedFieldConverter,
7
+ type UnreliableFieldConversion,
8
+ UnreliableFieldConversionType,
9
+ type UnreliableFieldConverter,
9
10
  } from 'types/field_converters'
10
11
 
11
- export function chainFieldConverter<
12
+ export function chainUnreliableFieldConverter<
12
13
  From,
13
14
  Intermediate,
14
15
  To,
@@ -17,27 +18,27 @@ export function chainFieldConverter<
17
18
  ValuePath extends string,
18
19
  Context,
19
20
  >(
20
- from: FieldConverter<From, Intermediate, E1, ValuePath, Context>,
21
- to: FieldConverter<Intermediate, To, E2, ValuePath, Context>,
22
- ): FieldConverter<From, To, E1 | E2, ValuePath, Context> {
23
- return function (value: From, valuePath: ValuePath, context: Context): FieldConversion<To, E1 | E2> {
21
+ from: UnreliableFieldConverter<From, Intermediate, E1, ValuePath, Context>,
22
+ to: UnreliableFieldConverter<Intermediate, To, E2, ValuePath, Context>,
23
+ ): UnreliableFieldConverter<From, To, E1 | E2, ValuePath, Context> {
24
+ return function (value: From, valuePath: ValuePath, context: Context): UnreliableFieldConversion<To, E1 | E2> {
24
25
  const fromConversion = from(value, valuePath, context)
25
26
  switch (fromConversion.type) {
26
- case FieldConversionResult.Success:
27
+ case UnreliableFieldConversionType.Success:
27
28
  return to(fromConversion.value, valuePath, context)
28
- case FieldConversionResult.Failure:
29
+ case UnreliableFieldConversionType.Failure:
29
30
  if (fromConversion.value != null) {
30
31
  const toConversion = to(fromConversion.value[0], valuePath, context)
31
32
  switch (toConversion.type) {
32
- case FieldConversionResult.Success:
33
+ case UnreliableFieldConversionType.Success:
33
34
  return {
34
- type: FieldConversionResult.Failure,
35
+ type: UnreliableFieldConversionType.Failure,
35
36
  error: fromConversion.error,
36
37
  value: [toConversion.value],
37
38
  }
38
- case FieldConversionResult.Failure:
39
+ case UnreliableFieldConversionType.Failure:
39
40
  return {
40
- type: FieldConversionResult.Failure,
41
+ type: UnreliableFieldConversionType.Failure,
41
42
  error: fromConversion.error,
42
43
  value: toConversion.value,
43
44
  }
@@ -46,7 +47,7 @@ export function chainFieldConverter<
46
47
  }
47
48
  } else {
48
49
  return {
49
- type: FieldConversionResult.Failure,
50
+ type: UnreliableFieldConversionType.Failure,
50
51
  error: fromConversion.error,
51
52
  value: null,
52
53
  }
@@ -57,18 +58,31 @@ export function chainFieldConverter<
57
58
  }
58
59
  }
59
60
 
60
- export function chainSafeFieldConverter<
61
+ export function chainAnnotatedFieldConverter<
61
62
  From,
62
63
  Intermediate,
63
64
  To,
64
65
  ValuePath extends string,
65
66
  Context,
66
67
  >(
67
- from: SafeFieldConverter<From, Intermediate, ValuePath, Context>,
68
- to: SafeFieldConverter<Intermediate, To, ValuePath, Context>,
69
- ): SafeFieldConverter<From, To, ValuePath, Context> {
70
- return function (value: From, valuePath: ValuePath, context: Context): To {
71
- const intermediate = from(value, valuePath, context)
72
- return to(intermediate, valuePath, context)
68
+ from: AnnotatedFieldConverter<From, Intermediate, ValuePath, Context>,
69
+ to: AnnotatedFieldConverter<Intermediate, To, ValuePath, Context>,
70
+ ): AnnotatedFieldConverter<From, To, ValuePath, Context> {
71
+ return function (value: From, valuePath: ValuePath, context: Context): AnnotatedFieldConversion {
72
+ const {
73
+ required: intermediateRequired,
74
+ readonly: intermediateDisabled,
75
+ value: intermediateValue,
76
+ } = from(value, valuePath, context)
77
+ const {
78
+ required: finalRequired,
79
+ readonly: finalDisabled,
80
+ value: finalValue,
81
+ } = to(intermediateValue, valuePath, context)
82
+ return {
83
+ value: finalValue,
84
+ required: intermediateRequired || finalRequired,
85
+ readonly: intermediateDisabled || finalDisabled,
86
+ }
73
87
  }
74
88
  }
@@ -1,29 +1,33 @@
1
1
  import {
2
- FieldConversionResult,
3
- type FieldConverter,
4
- type SafeFieldConverter,
2
+ type AnnotatedFieldConverter,
3
+ UnreliableFieldConversionType,
4
+ type UnreliableFieldConverter,
5
5
  } from 'types/field_converters'
6
6
 
7
- export function safeIdentityConverter<
7
+ export function annotatedIdentityConverter<
8
8
  V,
9
9
  ValuePath extends string,
10
10
  Context,
11
- >(): SafeFieldConverter<
11
+ >(required: boolean = false): AnnotatedFieldConverter<
12
12
  V,
13
13
  V,
14
14
  ValuePath,
15
15
  Context
16
16
  > {
17
- return function (v: V) {
18
- return v
17
+ return function (value: V) {
18
+ return {
19
+ value,
20
+ required,
21
+ readonly: false,
22
+ }
19
23
  }
20
24
  }
21
25
 
22
- export function identityConverter<
26
+ export function unreliableIdentityConverter<
23
27
  V,
24
28
  ValuePath extends string,
25
29
  Context,
26
- >(): FieldConverter<
30
+ >(): UnreliableFieldConverter<
27
31
  V,
28
32
  V,
29
33
  never,
@@ -32,7 +36,7 @@ export function identityConverter<
32
36
  > {
33
37
  return function (value: V) {
34
38
  return {
35
- type: FieldConversionResult.Success,
39
+ type: UnreliableFieldConversionType.Success,
36
40
  value,
37
41
  }
38
42
  }
@@ -1,30 +1,36 @@
1
1
  import {
2
- type FieldConversion,
3
- FieldConversionResult,
2
+ type AnnotatedFieldConversion,
4
3
  type TwoWayFieldConverter,
4
+ type UnreliableFieldConversion,
5
+ UnreliableFieldConversionType,
5
6
  } from 'types/field_converters'
6
7
 
7
8
  export class IntegerToStringConverter<E, ValuePath extends string, Context>
8
9
  implements TwoWayFieldConverter<number, string, E, ValuePath, Context>
9
10
  {
10
- constructor(private readonly isNanError: E) {
11
+ constructor(private readonly isNanError: E, private readonly base = 10) {
11
12
  }
12
13
 
13
- convert(from: number): string {
14
- return Math.floor(from).toString()
14
+ convert(from: number): AnnotatedFieldConversion<string> {
15
+ const value = Math.floor(from).toString()
16
+ return {
17
+ value,
18
+ required: false,
19
+ readonly: false,
20
+ }
15
21
  }
16
22
 
17
- revert(from: string): FieldConversion<number, E> {
18
- const value = parseInt(from, 10)
23
+ revert(from: string): UnreliableFieldConversion<number, E> {
24
+ const value = parseInt(from, this.base)
19
25
  if (Number.isNaN(value)) {
20
26
  return {
21
- type: FieldConversionResult.Failure,
27
+ type: UnreliableFieldConversionType.Failure,
22
28
  error: this.isNanError,
23
29
  value: null,
24
30
  }
25
31
  } else {
26
32
  return {
27
- type: FieldConversionResult.Success,
33
+ type: UnreliableFieldConversionType.Success,
28
34
  value,
29
35
  }
30
36
  }
@@ -1,15 +1,20 @@
1
- import { type SafeFieldConverter } from 'types/field_converters'
1
+ import { type AnnotatedFieldConverter } from 'types/field_converters'
2
2
 
3
3
  export function listConverter<
4
4
  E,
5
5
  K extends string,
6
6
  ValuePath extends string,
7
7
  Context,
8
- >(): SafeFieldConverter<readonly E[], K[], ValuePath, Context> {
8
+ >(): AnnotatedFieldConverter<readonly E[], K[], ValuePath, Context> {
9
9
  return function (from: readonly E[], valuePath: ValuePath) {
10
- return from.map(function (_v, i) {
10
+ const value = from.map(function (_v, i) {
11
11
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
12
12
  return `${valuePath as string}.${i}` as K
13
13
  })
14
+ return {
15
+ value,
16
+ required: false,
17
+ readonly: false,
18
+ }
14
19
  }
15
20
  }
@@ -1,6 +1,7 @@
1
1
  import {
2
- type FieldConversion,
2
+ type AnnotatedFieldConversion,
3
3
  type TwoWayFieldConverter,
4
+ type UnreliableFieldConversion,
4
5
  } from 'types/field_converters'
5
6
 
6
7
  export class MaybeIdentityConverter<From, To, E, ValuePath extends string, Context>
@@ -12,12 +13,14 @@ export class MaybeIdentityConverter<From, To, E, ValuePath extends string, Conte
12
13
  ) {
13
14
  }
14
15
 
15
- convert(from: From, valuePath: ValuePath, context: Context): To | From {
16
+ convert(from: From, valuePath: ValuePath, context: Context): AnnotatedFieldConversion<To | From> {
16
17
  return this.converter.convert(from, valuePath, context)
17
18
  }
18
19
 
19
- revert(from: To | From, valuePath: ValuePath, context: Context): FieldConversion<From, E> {
20
- const value = this.isFrom(from) ? this.converter.convert(from, valuePath, context) : from
20
+ revert(from: To | From, valuePath: ValuePath, context: Context): UnreliableFieldConversion<From, E> {
21
+ const value = this.isFrom(from)
22
+ ? this.converter.convert(from, valuePath, context).value
23
+ : from
21
24
  return this.converter.revert(value, valuePath, context)
22
25
  }
23
26
  }
@@ -1,13 +1,14 @@
1
1
  import {
2
2
  copy,
3
- type ReadonlyTypeDefOf,
3
+ type ReadonlyTypeOfType,
4
4
  type Type,
5
- type ValueTypeOf,
5
+ type ValueOfType,
6
6
  } from '@strictly/define'
7
7
  import {
8
- type FieldConversion,
9
- FieldConversionResult,
8
+ type AnnotatedFieldConversion,
10
9
  type TwoWayFieldConverterWithValueFactory,
10
+ type UnreliableFieldConversion,
11
+ UnreliableFieldConversionType,
11
12
  } from 'types/field_converters'
12
13
 
13
14
  export class NullableToBooleanConverter<
@@ -15,42 +16,48 @@ export class NullableToBooleanConverter<
15
16
  E,
16
17
  ValuePath extends string,
17
18
  Context,
19
+ NullType extends null | undefined,
18
20
  > implements TwoWayFieldConverterWithValueFactory<
19
- ValueTypeOf<ReadonlyTypeDefOf<T>> | null,
21
+ ValueOfType<ReadonlyTypeOfType<T>> | NullType,
20
22
  boolean,
21
23
  E,
22
24
  ValuePath,
23
25
  Context
24
26
  > {
25
- readonly defaultValue: ValueTypeOf<ReadonlyTypeDefOf<T>> | null
27
+ readonly defaultValue: ValueOfType<ReadonlyTypeOfType<T>> | NullType
26
28
 
27
29
  constructor(
28
30
  private readonly typeDef: T,
29
- private readonly prototype: ValueTypeOf<ReadonlyTypeDefOf<T>>,
31
+ private readonly prototype: ValueOfType<ReadonlyTypeOfType<T>>,
32
+ private readonly nullType: NullType,
30
33
  defaultToNull = true,
31
34
  ) {
32
- this.defaultValue = defaultToNull ? null : prototype
35
+ this.defaultValue = defaultToNull ? this.nullType : prototype
33
36
  }
34
37
 
35
- convert(from: ValueTypeOf<ReadonlyTypeDefOf<T>> | null): boolean {
36
- return from != null
38
+ convert(from: ValueOfType<ReadonlyTypeOfType<T>> | NullType): AnnotatedFieldConversion<boolean> {
39
+ return {
40
+ value: from !== this.nullType,
41
+ required: false,
42
+ readonly: false,
43
+ }
37
44
  }
38
45
 
39
- revert(from: boolean): FieldConversion<ValueTypeOf<ReadonlyTypeDefOf<T>> | null, E> {
46
+ revert(from: boolean): UnreliableFieldConversion<ValueOfType<ReadonlyTypeOfType<T>> | NullType, E> {
40
47
  if (from) {
41
- const value: ValueTypeOf<T> = copy(this.typeDef, this.prototype)
48
+ const value: ValueOfType<T> = copy(this.typeDef, this.prototype)
42
49
  return {
43
- type: FieldConversionResult.Success,
50
+ type: UnreliableFieldConversionType.Success,
44
51
  value,
45
52
  }
46
53
  }
47
54
  return {
48
- type: FieldConversionResult.Success,
49
- value: null,
55
+ type: UnreliableFieldConversionType.Success,
56
+ value: this.nullType,
50
57
  }
51
58
  }
52
59
 
53
- create(): ValueTypeOf<ReadonlyTypeDefOf<T>> | null {
60
+ create(): ValueOfType<ReadonlyTypeOfType<T>> | NullType {
54
61
  return this.defaultValue
55
62
  }
56
63
  }
@@ -1,31 +1,35 @@
1
1
  import {
2
+ type ExhaustiveArrayOfUnion,
2
3
  reverse,
3
4
  type StringKeyOf,
4
5
  } from '@strictly/base'
5
6
  import {
6
7
  copy,
7
8
  type LiteralTypeDef,
9
+ type ReadonlyTypeOfType,
8
10
  type Type,
9
11
  type UnionTypeDef,
10
- type ValueTypeOf,
12
+ type ValueOfType,
11
13
  type ValueTypesOfDiscriminatedUnion,
12
14
  } from '@strictly/define'
13
15
  import {
14
- type FieldConversion,
15
- FieldConversionResult,
16
+ type AnnotatedFieldConversion,
16
17
  type TwoWayFieldConverterWithValueFactory,
18
+ type UnreliableFieldConversion,
19
+ UnreliableFieldConversionType,
17
20
  } from 'types/field_converters'
18
21
 
19
22
  export abstract class AbstractSelectValueTypeConverter<
20
23
  T extends Type,
24
+ From extends ValueOfType<T> | undefined,
21
25
  To extends string | null,
22
- Values extends Readonly<Record<NonNullable<To>, ValueTypeOf<T>>>,
26
+ Values extends Readonly<Record<NonNullable<To>, From>>,
23
27
  NoSuchValueError,
24
28
  ValuePath extends string,
25
29
  Context,
26
30
  > implements TwoWayFieldConverterWithValueFactory<
27
- ValueTypeOf<T>,
28
- keyof Values | null,
31
+ From,
32
+ To,
29
33
  NoSuchValueError,
30
34
  ValuePath,
31
35
  Context
@@ -33,50 +37,56 @@ export abstract class AbstractSelectValueTypeConverter<
33
37
  constructor(
34
38
  protected readonly typeDef: T,
35
39
  protected readonly values: Values,
36
- private readonly defaultValueKey: keyof Values | null,
40
+ private readonly defaultValueKey: keyof Values | null | undefined,
37
41
  private readonly noSuchValueError: NoSuchValueError | null,
42
+ private readonly required: boolean,
38
43
  ) {
39
44
  }
40
45
 
41
- revert(from: keyof Values | null): FieldConversion<ValueTypeOf<T>, NoSuchValueError> {
42
- const prototype: ValueTypeOf<T> | null = from == null ? null : this.values[from]
46
+ revert(from: To): UnreliableFieldConversion<From, NoSuchValueError> {
47
+ const prototype: From = from == null ? null! : this.values[from]
43
48
  if (prototype == null && this.noSuchValueError != null) {
44
49
  return {
45
- type: FieldConversionResult.Failure,
50
+ type: UnreliableFieldConversionType.Failure,
46
51
  error: this.noSuchValueError,
47
52
  value: null,
48
53
  }
49
54
  }
50
- const value = prototype == null ? null : copy(this.typeDef, prototype)
55
+ const value = prototype == null ? prototype : copy(this.typeDef, prototype)
51
56
  // TODO given we are dealing with strings, maybe we should have a check to make sure value is in the record
52
57
  // of values?
53
58
  return {
54
- type: FieldConversionResult.Success,
59
+ type: UnreliableFieldConversionType.Success,
55
60
  value: value!,
56
61
  }
57
62
  }
58
63
 
59
- convert(from: ValueTypeOf<T>): To {
60
- if (from == null) {
61
- return null!
64
+ convert(from: From): AnnotatedFieldConversion<To> {
65
+ const value = from == null ? from! : this.doConvert(from)
66
+ return {
67
+ value,
68
+ required: this.required,
69
+ readonly: false,
62
70
  }
63
- return this.doConvert(from)
64
71
  }
65
72
 
66
- protected abstract doConvert(from: ValueTypeOf<T>): To
73
+ protected abstract doConvert(from: NonNullable<ValueOfType<T>>): To
67
74
 
68
- create(): ValueTypeOf<T> {
75
+ create(): From {
69
76
  return this.defaultValueKey != null ? this.values[this.defaultValueKey] : null!
70
77
  }
71
78
  }
72
79
 
73
80
  export class SelectDiscriminatedUnionConverter<
74
81
  U extends UnionTypeDef,
82
+ From extends ValueOfType<ReadonlyTypeOfType<Type<U>>> | (Required extends true ? never : undefined),
75
83
  To extends StringKeyOf<U['unions']> | null,
76
84
  ValuePath extends string,
77
85
  Context,
86
+ Required extends boolean,
78
87
  > extends AbstractSelectValueTypeConverter<
79
88
  Type<U>,
89
+ From,
80
90
  To,
81
91
  ValueTypesOfDiscriminatedUnion<U>,
82
92
  never,
@@ -84,19 +94,21 @@ export class SelectDiscriminatedUnionConverter<
84
94
  Context
85
95
  > {
86
96
  constructor(
87
- typeDef: Type<U>,
97
+ type: Type<U>,
88
98
  values: ValueTypesOfDiscriminatedUnion<U>,
89
99
  defaultValueKey: keyof U['unions'],
100
+ required: Required,
90
101
  ) {
91
102
  super(
92
- typeDef,
103
+ type,
93
104
  values,
94
105
  defaultValueKey,
95
106
  null,
107
+ required,
96
108
  )
97
109
  }
98
110
 
99
- protected override doConvert(from: ValueTypeOf<Type<U>>) {
111
+ protected override doConvert(from: NonNullable<ValueOfType<Type<U>>>) {
100
112
  const {
101
113
  definition: {
102
114
  discriminator,
@@ -108,15 +120,18 @@ export class SelectDiscriminatedUnionConverter<
108
120
 
109
121
  export class SelectLiteralConverter<
110
122
  L extends string | number | null,
123
+ From extends L | (Required extends true ? never : undefined),
111
124
  To extends string | null,
112
- Values extends Record<NonNullable<L>, NonNullable<To>>,
125
+ Values extends Record<NonNullable<From>, NonNullable<To>>,
113
126
  NoSuchValueError,
114
127
  ValuePath extends string,
115
128
  Context,
129
+ Required extends boolean,
116
130
  > extends AbstractSelectValueTypeConverter<
117
131
  Type<LiteralTypeDef<L>>,
132
+ From,
118
133
  To,
119
- Record<NonNullable<To>, L>,
134
+ Record<NonNullable<To>, NonNullable<From>>,
120
135
  NoSuchValueError,
121
136
  ValuePath,
122
137
  Context
@@ -124,18 +139,63 @@ export class SelectLiteralConverter<
124
139
  constructor(
125
140
  typeDef: Type<LiteralTypeDef<L>>,
126
141
  private readonly valuesToStrings: Values,
127
- defaultValue: L | null,
142
+ defaultValue: From,
128
143
  noSuchValueError: NoSuchValueError | null,
144
+ required: Required,
129
145
  ) {
130
146
  super(
131
147
  typeDef,
132
148
  reverse(valuesToStrings),
133
149
  defaultValue && valuesToStrings[defaultValue],
134
150
  noSuchValueError,
151
+ required,
152
+ )
153
+ }
154
+
155
+ protected override doConvert(from: NonNullable<From>) {
156
+ return this.valuesToStrings[from]
157
+ }
158
+ }
159
+
160
+ export class SelectStringConverter<
161
+ L extends string | null,
162
+ From extends L | undefined,
163
+ A extends readonly NonNullable<From>[],
164
+ NoSuchValueError,
165
+ ValuePath extends string,
166
+ Context,
167
+ > extends AbstractSelectValueTypeConverter<
168
+ Type<LiteralTypeDef<L>>,
169
+ From,
170
+ string | null,
171
+ Record<string, From>,
172
+ NoSuchValueError,
173
+ ValuePath,
174
+ Context
175
+ > {
176
+ constructor(
177
+ typeDef: Type<LiteralTypeDef<L>>,
178
+ allowedValues: ExhaustiveArrayOfUnion<NonNullable<From>, A>,
179
+ defaultValue: L | undefined,
180
+ noSuchValueError: NoSuchValueError | null,
181
+ required = false,
182
+ ) {
183
+ super(
184
+ typeDef,
185
+ allowedValues.reduce<Record<string, From>>(
186
+ function (acc, value) {
187
+ acc[value] = value
188
+ return acc
189
+ },
190
+ {},
191
+ ),
192
+ defaultValue,
193
+ noSuchValueError,
194
+ required,
135
195
  )
136
196
  }
137
197
 
138
- protected override doConvert(from: L) {
139
- return from && this.valuesToStrings[from]
198
+ protected override doConvert(from: NonNullable<L>) {
199
+ return from
140
200
  }
141
201
  }