@rjsf/utils 5.11.2 → 5.12.0

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 (303) hide show
  1. package/dist/index.js +2544 -5
  2. package/dist/index.js.map +7 -0
  3. package/dist/utils.esm.js +1228 -2113
  4. package/dist/utils.esm.js.map +7 -1
  5. package/dist/utils.umd.js +2414 -0
  6. package/lib/ErrorSchemaBuilder.d.ts +60 -0
  7. package/lib/ErrorSchemaBuilder.js +103 -0
  8. package/lib/ErrorSchemaBuilder.js.map +1 -0
  9. package/lib/allowAdditionalItems.d.ts +8 -0
  10. package/lib/allowAdditionalItems.js +14 -0
  11. package/lib/allowAdditionalItems.js.map +1 -0
  12. package/lib/asNumber.d.ts +10 -0
  13. package/lib/asNumber.js +36 -0
  14. package/lib/asNumber.js.map +1 -0
  15. package/lib/canExpand.d.ts +11 -0
  16. package/lib/canExpand.js +26 -0
  17. package/lib/canExpand.js.map +1 -0
  18. package/lib/constants.d.ts +31 -0
  19. package/lib/constants.js +32 -0
  20. package/lib/constants.js.map +1 -0
  21. package/lib/createErrorHandler.d.ts +7 -0
  22. package/lib/createErrorHandler.js +31 -0
  23. package/lib/createErrorHandler.js.map +1 -0
  24. package/lib/createSchemaUtils.d.ts +10 -0
  25. package/lib/createSchemaUtils.js +207 -0
  26. package/lib/createSchemaUtils.js.map +1 -0
  27. package/lib/dataURItoBlob.d.ts +16 -0
  28. package/lib/dataURItoBlob.js +43 -0
  29. package/lib/dataURItoBlob.js.map +1 -0
  30. package/lib/deepEquals.d.ts +8 -0
  31. package/lib/deepEquals.js +19 -0
  32. package/lib/deepEquals.js.map +1 -0
  33. package/lib/englishStringTranslator.d.ts +10 -0
  34. package/lib/englishStringTranslator.js +13 -0
  35. package/lib/englishStringTranslator.js.map +1 -0
  36. package/lib/enumOptionsDeselectValue.d.ts +14 -0
  37. package/lib/enumOptionsDeselectValue.js +22 -0
  38. package/lib/enumOptionsDeselectValue.js.map +1 -0
  39. package/lib/enumOptionsIndexForValue.d.ts +13 -0
  40. package/lib/enumOptionsIndexForValue.js +22 -0
  41. package/lib/enumOptionsIndexForValue.js.map +1 -0
  42. package/lib/enumOptionsIsSelected.d.ts +8 -0
  43. package/lib/enumOptionsIsSelected.js +14 -0
  44. package/lib/enumOptionsIsSelected.js.map +1 -0
  45. package/lib/enumOptionsSelectValue.d.ts +10 -0
  46. package/lib/enumOptionsSelectValue.js +23 -0
  47. package/lib/enumOptionsSelectValue.js.map +1 -0
  48. package/lib/enumOptionsValueForIndex.d.ts +13 -0
  49. package/lib/enumOptionsValueForIndex.js +21 -0
  50. package/lib/enumOptionsValueForIndex.js.map +1 -0
  51. package/lib/enums.d.ts +72 -0
  52. package/lib/enums.js +76 -0
  53. package/lib/enums.js.map +1 -0
  54. package/lib/findSchemaDefinition.d.ts +20 -0
  55. package/lib/findSchemaDefinition.js +49 -0
  56. package/lib/findSchemaDefinition.js.map +1 -0
  57. package/lib/getDiscriminatorFieldFromSchema.d.ts +8 -0
  58. package/lib/getDiscriminatorFieldFromSchema.js +20 -0
  59. package/lib/getDiscriminatorFieldFromSchema.js.map +1 -0
  60. package/lib/getInputProps.d.ts +10 -0
  61. package/lib/getInputProps.js +41 -0
  62. package/lib/getInputProps.js.map +1 -0
  63. package/lib/getSchemaType.d.ts +13 -0
  64. package/lib/getSchemaType.js +29 -0
  65. package/lib/getSchemaType.js.map +1 -0
  66. package/lib/getSubmitButtonOptions.d.ts +10 -0
  67. package/lib/getSubmitButtonOptions.js +25 -0
  68. package/lib/getSubmitButtonOptions.js.map +1 -0
  69. package/lib/getTemplate.d.ts +10 -0
  70. package/lib/getTemplate.js +19 -0
  71. package/lib/getTemplate.js.map +1 -0
  72. package/lib/getUiOptions.d.ts +9 -0
  73. package/lib/getUiOptions.js +25 -0
  74. package/lib/getUiOptions.js.map +1 -0
  75. package/lib/getWidget.d.ts +13 -0
  76. package/lib/getWidget.js +118 -0
  77. package/lib/getWidget.js.map +1 -0
  78. package/lib/guessType.d.ts +7 -0
  79. package/lib/guessType.js +29 -0
  80. package/lib/guessType.js.map +1 -0
  81. package/lib/hasWidget.d.ts +10 -0
  82. package/lib/hasWidget.js +23 -0
  83. package/lib/hasWidget.js.map +1 -0
  84. package/lib/hashForSchema.d.ts +8 -0
  85. package/lib/hashForSchema.js +29 -0
  86. package/lib/hashForSchema.js.map +1 -0
  87. package/lib/idGenerators.d.ts +47 -0
  88. package/lib/idGenerators.js +73 -0
  89. package/lib/idGenerators.js.map +1 -0
  90. package/lib/index.d.ts +57 -0
  91. package/lib/index.js +58 -0
  92. package/lib/index.js.map +1 -0
  93. package/lib/isConstant.d.ts +8 -0
  94. package/lib/isConstant.js +11 -0
  95. package/lib/isConstant.js.map +1 -0
  96. package/lib/isCustomWidget.d.ts +7 -0
  97. package/lib/isCustomWidget.js +13 -0
  98. package/lib/isCustomWidget.js.map +1 -0
  99. package/lib/isFixedItems.d.ts +8 -0
  100. package/lib/isFixedItems.js +11 -0
  101. package/lib/isFixedItems.js.map +1 -0
  102. package/lib/isObject.d.ts +7 -0
  103. package/lib/isObject.js +16 -0
  104. package/lib/isObject.js.map +1 -0
  105. package/lib/labelValue.d.ts +13 -0
  106. package/lib/labelValue.js +4 -0
  107. package/lib/labelValue.js.map +1 -0
  108. package/lib/localToUTC.d.ts +6 -0
  109. package/lib/localToUTC.js +9 -0
  110. package/lib/localToUTC.js.map +1 -0
  111. package/lib/mergeDefaultsWithFormData.d.ts +17 -0
  112. package/lib/mergeDefaultsWithFormData.js +43 -0
  113. package/lib/mergeDefaultsWithFormData.js.map +1 -0
  114. package/lib/mergeObjects.d.ts +11 -0
  115. package/lib/mergeObjects.js +35 -0
  116. package/lib/mergeObjects.js.map +1 -0
  117. package/lib/mergeSchemas.d.ts +10 -0
  118. package/lib/mergeSchemas.js +35 -0
  119. package/lib/mergeSchemas.js.map +1 -0
  120. package/lib/optionsList.d.ts +10 -0
  121. package/lib/optionsList.js +36 -0
  122. package/lib/optionsList.js.map +1 -0
  123. package/lib/orderProperties.d.ts +11 -0
  124. package/lib/orderProperties.js +38 -0
  125. package/lib/orderProperties.js.map +1 -0
  126. package/lib/pad.d.ts +7 -0
  127. package/lib/pad.js +14 -0
  128. package/lib/pad.js.map +1 -0
  129. package/lib/parseDateString.d.ts +9 -0
  130. package/lib/parseDateString.js +32 -0
  131. package/lib/parseDateString.js.map +1 -0
  132. package/lib/parser/ParserValidator.d.ts +70 -0
  133. package/lib/parser/ParserValidator.js +93 -0
  134. package/lib/parser/ParserValidator.js.map +1 -0
  135. package/lib/parser/index.d.ts +4 -0
  136. package/lib/parser/index.js +3 -0
  137. package/lib/parser/index.js.map +1 -0
  138. package/lib/parser/schemaParser.d.ts +9 -0
  139. package/lib/parser/schemaParser.js +48 -0
  140. package/lib/parser/schemaParser.js.map +1 -0
  141. package/lib/rangeSpec.d.ts +9 -0
  142. package/lib/rangeSpec.js +20 -0
  143. package/lib/rangeSpec.js.map +1 -0
  144. package/lib/replaceStringParameters.d.ts +9 -0
  145. package/lib/replaceStringParameters.js +23 -0
  146. package/lib/replaceStringParameters.js.map +1 -0
  147. package/lib/schema/getClosestMatchingOption.d.ts +49 -0
  148. package/lib/schema/getClosestMatchingOption.js +154 -0
  149. package/lib/schema/getClosestMatchingOption.js.map +1 -0
  150. package/lib/schema/getDefaultFormState.d.ts +66 -0
  151. package/lib/schema/getDefaultFormState.js +351 -0
  152. package/lib/schema/getDefaultFormState.js.map +1 -0
  153. package/lib/schema/getDisplayLabel.d.ts +12 -0
  154. package/lib/schema/getDisplayLabel.js +39 -0
  155. package/lib/schema/getDisplayLabel.js.map +1 -0
  156. package/lib/schema/getFirstMatchingOption.d.ts +13 -0
  157. package/lib/schema/getFirstMatchingOption.js +16 -0
  158. package/lib/schema/getFirstMatchingOption.js.map +1 -0
  159. package/lib/schema/getMatchingOption.d.ts +14 -0
  160. package/lib/schema/getMatchingOption.js +80 -0
  161. package/lib/schema/getMatchingOption.js.map +1 -0
  162. package/lib/schema/index.d.ts +14 -0
  163. package/lib/schema/index.js +15 -0
  164. package/lib/schema/index.js.map +1 -0
  165. package/lib/schema/isFilesArray.d.ts +10 -0
  166. package/lib/schema/isFilesArray.js +21 -0
  167. package/lib/schema/isFilesArray.js.map +1 -0
  168. package/lib/schema/isMultiSelect.d.ts +9 -0
  169. package/lib/schema/isMultiSelect.js +15 -0
  170. package/lib/schema/isMultiSelect.js.map +1 -0
  171. package/lib/schema/isSelect.d.ts +9 -0
  172. package/lib/schema/isSelect.js +21 -0
  173. package/lib/schema/isSelect.js.map +1 -0
  174. package/lib/schema/mergeValidationData.d.ts +14 -0
  175. package/lib/schema/mergeValidationData.js +28 -0
  176. package/lib/schema/mergeValidationData.js.map +1 -0
  177. package/lib/schema/retrieveSchema.d.ts +170 -0
  178. package/lib/schema/retrieveSchema.js +437 -0
  179. package/lib/schema/retrieveSchema.js.map +1 -0
  180. package/lib/schema/sanitizeDataForNewSchema.d.ts +49 -0
  181. package/lib/schema/sanitizeDataForNewSchema.js +173 -0
  182. package/lib/schema/sanitizeDataForNewSchema.js.map +1 -0
  183. package/lib/schema/toIdSchema.d.ts +13 -0
  184. package/lib/schema/toIdSchema.js +59 -0
  185. package/lib/schema/toIdSchema.js.map +1 -0
  186. package/lib/schema/toPathSchema.d.ts +11 -0
  187. package/lib/schema/toPathSchema.js +68 -0
  188. package/lib/schema/toPathSchema.js.map +1 -0
  189. package/lib/schemaRequiresTrueValue.d.ts +11 -0
  190. package/lib/schemaRequiresTrueValue.js +34 -0
  191. package/lib/schemaRequiresTrueValue.js.map +1 -0
  192. package/lib/shouldRender.d.ts +10 -0
  193. package/lib/shouldRender.js +14 -0
  194. package/lib/shouldRender.js.map +1 -0
  195. package/lib/toConstant.d.ts +9 -0
  196. package/lib/toConstant.js +18 -0
  197. package/lib/toConstant.js.map +1 -0
  198. package/lib/toDateString.d.ts +9 -0
  199. package/lib/toDateString.js +14 -0
  200. package/lib/toDateString.js.map +1 -0
  201. package/lib/toErrorList.d.ts +8 -0
  202. package/lib/toErrorList.js +34 -0
  203. package/lib/toErrorList.js.map +1 -0
  204. package/lib/toErrorSchema.d.ts +21 -0
  205. package/lib/toErrorSchema.js +41 -0
  206. package/lib/toErrorSchema.js.map +1 -0
  207. package/lib/types.d.ts +982 -0
  208. package/lib/types.js +2 -0
  209. package/lib/types.js.map +1 -0
  210. package/lib/unwrapErrorHandler.d.ts +7 -0
  211. package/lib/unwrapErrorHandler.js +21 -0
  212. package/lib/unwrapErrorHandler.js.map +1 -0
  213. package/lib/utcToLocal.d.ts +6 -0
  214. package/lib/utcToLocal.js +26 -0
  215. package/lib/utcToLocal.js.map +1 -0
  216. package/lib/validationDataMerge.d.ts +11 -0
  217. package/lib/validationDataMerge.js +26 -0
  218. package/lib/validationDataMerge.js.map +1 -0
  219. package/lib/withIdRefPrefix.d.ts +8 -0
  220. package/lib/withIdRefPrefix.js +47 -0
  221. package/lib/withIdRefPrefix.js.map +1 -0
  222. package/package.json +20 -13
  223. package/src/ErrorSchemaBuilder.ts +112 -0
  224. package/src/allowAdditionalItems.ts +15 -0
  225. package/src/asNumber.ts +38 -0
  226. package/src/canExpand.ts +31 -0
  227. package/src/constants.ts +31 -0
  228. package/src/createErrorHandler.ts +33 -0
  229. package/src/createSchemaUtils.ts +298 -0
  230. package/src/dataURItoBlob.ts +42 -0
  231. package/src/deepEquals.ts +19 -0
  232. package/src/englishStringTranslator.ts +14 -0
  233. package/src/enumOptionsDeselectValue.ts +28 -0
  234. package/src/enumOptionsIndexForValue.ts +27 -0
  235. package/src/enumOptionsIsSelected.ts +19 -0
  236. package/src/enumOptionsSelectValue.ts +28 -0
  237. package/src/enumOptionsValueForIndex.ts +26 -0
  238. package/src/enums.ts +74 -0
  239. package/src/findSchemaDefinition.ts +54 -0
  240. package/src/getDiscriminatorFieldFromSchema.ts +21 -0
  241. package/src/getInputProps.ts +55 -0
  242. package/src/getSchemaType.ts +37 -0
  243. package/src/getSubmitButtonOptions.ts +32 -0
  244. package/src/getTemplate.ts +26 -0
  245. package/src/getUiOptions.ts +32 -0
  246. package/src/getWidget.tsx +133 -0
  247. package/src/guessType.ts +28 -0
  248. package/src/hasWidget.ts +27 -0
  249. package/src/hashForSchema.ts +31 -0
  250. package/src/idGenerators.ts +81 -0
  251. package/src/index.ts +118 -0
  252. package/src/isConstant.ts +12 -0
  253. package/src/isCustomWidget.ts +19 -0
  254. package/src/isFixedItems.ts +12 -0
  255. package/src/isObject.ts +15 -0
  256. package/src/labelValue.ts +16 -0
  257. package/src/localToUTC.ts +8 -0
  258. package/src/mergeDefaultsWithFormData.ts +53 -0
  259. package/src/mergeObjects.ts +39 -0
  260. package/src/mergeSchemas.ts +38 -0
  261. package/src/optionsList.ts +41 -0
  262. package/src/orderProperties.ts +44 -0
  263. package/src/pad.ts +13 -0
  264. package/src/parseDateString.ts +33 -0
  265. package/src/parser/ParserValidator.ts +132 -0
  266. package/src/parser/index.ts +6 -0
  267. package/src/parser/schemaParser.ts +60 -0
  268. package/src/rangeSpec.ts +22 -0
  269. package/src/replaceStringParameters.ts +22 -0
  270. package/src/schema/getClosestMatchingOption.ts +191 -0
  271. package/src/schema/getDefaultFormState.ts +447 -0
  272. package/src/schema/getDisplayLabel.ts +59 -0
  273. package/src/schema/getFirstMatchingOption.ts +27 -0
  274. package/src/schema/getMatchingOption.ts +95 -0
  275. package/src/schema/index.ts +29 -0
  276. package/src/schema/isFilesArray.ts +27 -0
  277. package/src/schema/isMultiSelect.ts +21 -0
  278. package/src/schema/isSelect.ts +26 -0
  279. package/src/schema/mergeValidationData.ts +38 -0
  280. package/src/schema/retrieveSchema.ts +614 -0
  281. package/src/schema/sanitizeDataForNewSchema.ts +197 -0
  282. package/src/schema/toIdSchema.ts +105 -0
  283. package/src/schema/toPathSchema.ts +121 -0
  284. package/src/schemaRequiresTrueValue.ts +40 -0
  285. package/src/shouldRender.ts +16 -0
  286. package/src/toConstant.ts +19 -0
  287. package/src/toDateString.ts +15 -0
  288. package/src/toErrorList.ts +41 -0
  289. package/src/toErrorSchema.ts +43 -0
  290. package/src/types.ts +1139 -0
  291. package/src/unwrapErrorHandler.ts +25 -0
  292. package/src/utcToLocal.ts +30 -0
  293. package/src/validationDataMerge.ts +31 -0
  294. package/src/withIdRefPrefix.ts +49 -0
  295. package/dist/index.d.ts +0 -1911
  296. package/dist/utils.cjs.development.js +0 -3522
  297. package/dist/utils.cjs.development.js.map +0 -1
  298. package/dist/utils.cjs.production.min.js +0 -2
  299. package/dist/utils.cjs.production.min.js.map +0 -1
  300. package/dist/utils.umd.development.js +0 -3504
  301. package/dist/utils.umd.development.js.map +0 -1
  302. package/dist/utils.umd.production.min.js +0 -2
  303. package/dist/utils.umd.production.min.js.map +0 -1
@@ -0,0 +1,197 @@
1
+ import get from 'lodash/get';
2
+ import has from 'lodash/has';
3
+
4
+ import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
5
+ import { PROPERTIES_KEY, REF_KEY } from '../constants';
6
+ import retrieveSchema from './retrieveSchema';
7
+
8
+ const NO_VALUE = Symbol('no Value');
9
+
10
+ /** Sanitize the `data` associated with the `oldSchema` so it is considered appropriate for the `newSchema`. If the new
11
+ * schema does not contain any properties, then `undefined` is returned to clear all the form data. Due to the nature
12
+ * of schemas, this sanitization happens recursively for nested objects of data. Also, any properties in the old schema
13
+ * that are non-existent in the new schema are set to `undefined`. The data sanitization process has the following flow:
14
+ *
15
+ * - If the new schema is an object that contains a `properties` object then:
16
+ * - Create a `removeOldSchemaData` object, setting each key in the `oldSchema.properties` having `data` to undefined
17
+ * - Create an empty `nestedData` object for use in the key filtering below:
18
+ * - Iterate over each key in the `newSchema.properties` as follows:
19
+ * - Get the `formValue` of the key from the `data`
20
+ * - Get the `oldKeySchema` and `newKeyedSchema` for the key, defaulting to `{}` when it doesn't exist
21
+ * - Retrieve the schema for any refs within each `oldKeySchema` and/or `newKeySchema`
22
+ * - Get the types of the old and new keyed schemas and if the old doesn't exist or the old & new are the same then:
23
+ * - If `removeOldSchemaData` has an entry for the key, delete it since the new schema has the same property
24
+ * - If type of the key in the new schema is `object`:
25
+ * - Store the value from the recursive `sanitizeDataForNewSchema` call in `nestedData[key]`
26
+ * - Otherwise, check for default or const values:
27
+ * - Get the old and new `default` values from the schema and check:
28
+ * - If the new `default` value does not match the form value:
29
+ * - If the old `default` value DOES match the form value, then:
30
+ * - Replace `removeOldSchemaData[key]` with the new `default`
31
+ * - Otherwise, if the new schema is `readOnly` then replace `removeOldSchemaData[key]` with undefined
32
+ * - Get the old and new `const` values from the schema and check:
33
+ * - If the new `const` value does not match the form value:
34
+ * - If the old `const` value DOES match the form value, then:
35
+ * - Replace `removeOldSchemaData[key]` with the new `const`
36
+ * - Otherwise, replace `removeOldSchemaData[key]` with undefined
37
+ * - Once all keys have been processed, return an object built as follows:
38
+ * - `{ ...removeOldSchemaData, ...nestedData, ...pick(data, keysToKeep) }`
39
+ * - If the new and old schema types are array and the `data` is an array then:
40
+ * - If the type of the old and new schema `items` are a non-array objects:
41
+ * - Retrieve the schema for any refs within each `oldKeySchema.items` and/or `newKeySchema.items`
42
+ * - If the `type`s of both items are the same (or the old does not have a type):
43
+ * - If the type is "object", then:
44
+ * - For each element in the `data` recursively sanitize the data, stopping at `maxItems` if specified
45
+ * - Otherwise, just return the `data` removing any values after `maxItems` if it is set
46
+ * - If the type of the old and new schema `items` are booleans of the same value, return `data` as is
47
+ * - Otherwise return `undefined`
48
+ *
49
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
50
+ * @param rootSchema - The root JSON schema of the entire form
51
+ * @param [newSchema] - The new schema for which the data is being sanitized
52
+ * @param [oldSchema] - The old schema from which the data originated
53
+ * @param [data={}] - The form data associated with the schema, defaulting to an empty object when undefined
54
+ * @returns - The new form data, with all the fields uniquely associated with the old schema set
55
+ * to `undefined`. Will return `undefined` if the new schema is not an object containing properties.
56
+ */
57
+ export default function sanitizeDataForNewSchema<
58
+ T = any,
59
+ S extends StrictRJSFSchema = RJSFSchema,
60
+ F extends FormContextType = any
61
+ >(validator: ValidatorType<T, S, F>, rootSchema: S, newSchema?: S, oldSchema?: S, data: any = {}): T {
62
+ // By default, we will clear the form data
63
+ let newFormData;
64
+ // If the new schema is of type object and that object contains a list of properties
65
+ if (has(newSchema, PROPERTIES_KEY)) {
66
+ // Create an object containing root-level keys in the old schema, setting each key to undefined to remove the data
67
+ const removeOldSchemaData: GenericObjectType = {};
68
+ if (has(oldSchema, PROPERTIES_KEY)) {
69
+ const properties = get(oldSchema, PROPERTIES_KEY, {});
70
+ Object.keys(properties).forEach((key) => {
71
+ if (has(data, key)) {
72
+ removeOldSchemaData[key] = undefined;
73
+ }
74
+ });
75
+ }
76
+ const keys: string[] = Object.keys(get(newSchema, PROPERTIES_KEY, {}));
77
+ // Create a place to store nested data that will be a side-effect of the filter
78
+ const nestedData: GenericObjectType = {};
79
+ keys.forEach((key) => {
80
+ const formValue = get(data, key);
81
+ let oldKeyedSchema: S = get(oldSchema, [PROPERTIES_KEY, key], {});
82
+ let newKeyedSchema: S = get(newSchema, [PROPERTIES_KEY, key], {});
83
+ // Resolve the refs if they exist
84
+ if (has(oldKeyedSchema, REF_KEY)) {
85
+ oldKeyedSchema = retrieveSchema<T, S, F>(validator, oldKeyedSchema, rootSchema, formValue);
86
+ }
87
+ if (has(newKeyedSchema, REF_KEY)) {
88
+ newKeyedSchema = retrieveSchema<T, S, F>(validator, newKeyedSchema, rootSchema, formValue);
89
+ }
90
+ // Now get types and see if they are the same
91
+ const oldSchemaTypeForKey = get(oldKeyedSchema, 'type');
92
+ const newSchemaTypeForKey = get(newKeyedSchema, 'type');
93
+ // Check if the old option has the same key with the same type
94
+ if (!oldSchemaTypeForKey || oldSchemaTypeForKey === newSchemaTypeForKey) {
95
+ if (has(removeOldSchemaData, key)) {
96
+ // SIDE-EFFECT: remove the undefined value for a key that has the same type between the old and new schemas
97
+ delete removeOldSchemaData[key];
98
+ }
99
+ // If it is an object, we'll recurse and store the resulting sanitized data for the key
100
+ if (newSchemaTypeForKey === 'object' || (newSchemaTypeForKey === 'array' && Array.isArray(formValue))) {
101
+ // SIDE-EFFECT: process the new schema type of object recursively to save iterations
102
+ const itemData = sanitizeDataForNewSchema<T, S, F>(
103
+ validator,
104
+ rootSchema,
105
+ newKeyedSchema,
106
+ oldKeyedSchema,
107
+ formValue
108
+ );
109
+ if (itemData !== undefined || newSchemaTypeForKey === 'array') {
110
+ // only put undefined values for the array type and not the object type
111
+ nestedData[key] = itemData;
112
+ }
113
+ } else {
114
+ // Ok, the non-object types match, let's make sure that a default or a const of a different value is replaced
115
+ // with the new default or const. This allows the case where two schemas differ that only by the default/const
116
+ // value to be properly selected
117
+ const newOptionDefault = get(newKeyedSchema, 'default', NO_VALUE);
118
+ const oldOptionDefault = get(oldKeyedSchema, 'default', NO_VALUE);
119
+ if (newOptionDefault !== NO_VALUE && newOptionDefault !== formValue) {
120
+ if (oldOptionDefault === formValue) {
121
+ // If the old default matches the formValue, we'll update the new value to match the new default
122
+ removeOldSchemaData[key] = newOptionDefault;
123
+ } else if (get(newKeyedSchema, 'readOnly') === true) {
124
+ // If the new schema has the default set to read-only, treat it like a const and remove the value
125
+ removeOldSchemaData[key] = undefined;
126
+ }
127
+ }
128
+
129
+ const newOptionConst = get(newKeyedSchema, 'const', NO_VALUE);
130
+ const oldOptionConst = get(oldKeyedSchema, 'const', NO_VALUE);
131
+ if (newOptionConst !== NO_VALUE && newOptionConst !== formValue) {
132
+ // Since this is a const, if the old value matches, replace the value with the new const otherwise clear it
133
+ removeOldSchemaData[key] = oldOptionConst === formValue ? newOptionConst : undefined;
134
+ }
135
+ }
136
+ }
137
+ });
138
+
139
+ newFormData = {
140
+ ...data,
141
+ ...removeOldSchemaData,
142
+ ...nestedData,
143
+ };
144
+ // First apply removing the old schema data, then apply the nested data, then apply the old data keys to keep
145
+ } else if (get(oldSchema, 'type') === 'array' && get(newSchema, 'type') === 'array' && Array.isArray(data)) {
146
+ let oldSchemaItems = get(oldSchema, 'items');
147
+ let newSchemaItems = get(newSchema, 'items');
148
+ // If any of the array types `items` are arrays (remember arrays are objects) then we'll just drop the data
149
+ // Eventually, we may want to deal with when either of the `items` are arrays since those tuple validations
150
+ if (
151
+ typeof oldSchemaItems === 'object' &&
152
+ typeof newSchemaItems === 'object' &&
153
+ !Array.isArray(oldSchemaItems) &&
154
+ !Array.isArray(newSchemaItems)
155
+ ) {
156
+ if (has(oldSchemaItems, REF_KEY)) {
157
+ oldSchemaItems = retrieveSchema<T, S, F>(validator, oldSchemaItems as S, rootSchema, data as T);
158
+ }
159
+ if (has(newSchemaItems, REF_KEY)) {
160
+ newSchemaItems = retrieveSchema<T, S, F>(validator, newSchemaItems as S, rootSchema, data as T);
161
+ }
162
+ // Now get types and see if they are the same
163
+ const oldSchemaType = get(oldSchemaItems, 'type');
164
+ const newSchemaType = get(newSchemaItems, 'type');
165
+ // Check if the old option has the same key with the same type
166
+ if (!oldSchemaType || oldSchemaType === newSchemaType) {
167
+ const maxItems = get(newSchema, 'maxItems', -1);
168
+ if (newSchemaType === 'object') {
169
+ newFormData = data.reduce((newValue, aValue) => {
170
+ const itemValue = sanitizeDataForNewSchema<T, S, F>(
171
+ validator,
172
+ rootSchema,
173
+ newSchemaItems as S,
174
+ oldSchemaItems as S,
175
+ aValue
176
+ );
177
+ if (itemValue !== undefined && (maxItems < 0 || newValue.length < maxItems)) {
178
+ newValue.push(itemValue);
179
+ }
180
+ return newValue;
181
+ }, []);
182
+ } else {
183
+ newFormData = maxItems > 0 && data.length > maxItems ? data.slice(0, maxItems) : data;
184
+ }
185
+ }
186
+ } else if (
187
+ typeof oldSchemaItems === 'boolean' &&
188
+ typeof newSchemaItems === 'boolean' &&
189
+ oldSchemaItems === newSchemaItems
190
+ ) {
191
+ // If they are both booleans and have the same value just return the data as is otherwise fall-thru to undefined
192
+ newFormData = data;
193
+ }
194
+ // Also probably want to deal with `prefixItems` as tuples with the latest 2020 draft
195
+ }
196
+ return newFormData as T;
197
+ }
@@ -0,0 +1,105 @@
1
+ import get from 'lodash/get';
2
+ import isEqual from 'lodash/isEqual';
3
+
4
+ import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants';
5
+ import isObject from '../isObject';
6
+ import { FormContextType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
7
+ import retrieveSchema from './retrieveSchema';
8
+ import getSchemaType from '../getSchemaType';
9
+
10
+ /** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against
11
+ * infinite recursion
12
+ *
13
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
14
+ * @param schema - The schema for which the `IdSchema` is desired
15
+ * @param idPrefix - The prefix to use for the id
16
+ * @param idSeparator - The separator to use for the path segments in the id
17
+ * @param [id] - The base id for the schema
18
+ * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
19
+ * @param [formData] - The current formData, if any, to assist retrieving a schema
20
+ * @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion
21
+ * @returns - The `IdSchema` object for the `schema`
22
+ */
23
+ function toIdSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
24
+ validator: ValidatorType<T, S, F>,
25
+ schema: S,
26
+ idPrefix: string,
27
+ idSeparator: string,
28
+ id?: string | null,
29
+ rootSchema?: S,
30
+ formData?: T,
31
+ _recurseList: S[] = []
32
+ ): IdSchema<T> {
33
+ if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
34
+ const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
35
+ const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
36
+ if (sameSchemaIndex === -1) {
37
+ return toIdSchemaInternal<T, S, F>(
38
+ validator,
39
+ _schema,
40
+ idPrefix,
41
+ idSeparator,
42
+ id,
43
+ rootSchema,
44
+ formData,
45
+ _recurseList.concat(_schema)
46
+ );
47
+ }
48
+ }
49
+ if (ITEMS_KEY in schema && !get(schema, [ITEMS_KEY, REF_KEY])) {
50
+ return toIdSchemaInternal<T, S, F>(
51
+ validator,
52
+ get(schema, ITEMS_KEY) as S,
53
+ idPrefix,
54
+ idSeparator,
55
+ id,
56
+ rootSchema,
57
+ formData,
58
+ _recurseList
59
+ );
60
+ }
61
+ const $id = id || idPrefix;
62
+ const idSchema: IdSchema = { $id } as IdSchema<T>;
63
+ if (getSchemaType<S>(schema) === 'object' && PROPERTIES_KEY in schema) {
64
+ for (const name in schema.properties) {
65
+ const field = get(schema, [PROPERTIES_KEY, name]);
66
+ const fieldId = idSchema[ID_KEY] + idSeparator + name;
67
+ idSchema[name] = toIdSchemaInternal<T, S, F>(
68
+ validator,
69
+ isObject(field) ? field : {},
70
+ idPrefix,
71
+ idSeparator,
72
+ fieldId,
73
+ rootSchema,
74
+ // It's possible that formData is not an object -- this can happen if an
75
+ // array item has just been added, but not populated with data yet
76
+ get(formData, [name]),
77
+ _recurseList
78
+ );
79
+ }
80
+ }
81
+ return idSchema as IdSchema<T>;
82
+ }
83
+
84
+ /** Generates an `IdSchema` object for the `schema`, recursively
85
+ *
86
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
87
+ * @param schema - The schema for which the `IdSchema` is desired
88
+ * @param [id] - The base id for the schema
89
+ * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
90
+ * @param [formData] - The current formData, if any, to assist retrieving a schema
91
+ * @param [idPrefix='root'] - The prefix to use for the id
92
+ * @param [idSeparator='_'] - The separator to use for the path segments in the id
93
+ * @returns - The `IdSchema` object for the `schema`
94
+ */
95
+ export default function toIdSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
96
+ validator: ValidatorType<T, S, F>,
97
+ schema: S,
98
+ id?: string | null,
99
+ rootSchema?: S,
100
+ formData?: T,
101
+ idPrefix = 'root',
102
+ idSeparator = '_'
103
+ ): IdSchema<T> {
104
+ return toIdSchemaInternal<T, S, F>(validator, schema, idPrefix, idSeparator, id, rootSchema, formData);
105
+ }
@@ -0,0 +1,121 @@
1
+ import get from 'lodash/get';
2
+ import isEqual from 'lodash/isEqual';
3
+ import set from 'lodash/set';
4
+
5
+ import {
6
+ ALL_OF_KEY,
7
+ ANY_OF_KEY,
8
+ ADDITIONAL_PROPERTIES_KEY,
9
+ DEPENDENCIES_KEY,
10
+ ITEMS_KEY,
11
+ NAME_KEY,
12
+ ONE_OF_KEY,
13
+ PROPERTIES_KEY,
14
+ REF_KEY,
15
+ RJSF_ADDITONAL_PROPERTIES_FLAG,
16
+ } from '../constants';
17
+ import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
18
+ import { FormContextType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
19
+ import getClosestMatchingOption from './getClosestMatchingOption';
20
+ import retrieveSchema from './retrieveSchema';
21
+
22
+ /** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
23
+ * infinite recursion
24
+ *
25
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
26
+ * @param schema - The schema for which the `PathSchema` is desired
27
+ * @param [name=''] - The base name for the schema
28
+ * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
29
+ * @param [formData] - The current formData, if any, to assist retrieving a schema
30
+ * @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion
31
+ * @returns - The `PathSchema` object for the `schema`
32
+ */
33
+ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
34
+ validator: ValidatorType<T, S, F>,
35
+ schema: S,
36
+ name: string,
37
+ rootSchema?: S,
38
+ formData?: T,
39
+ _recurseList: S[] = []
40
+ ): PathSchema<T> {
41
+ if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) {
42
+ const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData);
43
+ const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema));
44
+ if (sameSchemaIndex === -1) {
45
+ return toPathSchemaInternal<T, S, F>(
46
+ validator,
47
+ _schema,
48
+ name,
49
+ rootSchema,
50
+ formData,
51
+ _recurseList.concat(_schema)
52
+ );
53
+ }
54
+ }
55
+
56
+ let pathSchema: PathSchema = {
57
+ [NAME_KEY]: name.replace(/^\./, ''),
58
+ } as PathSchema;
59
+
60
+ if (ONE_OF_KEY in schema || ANY_OF_KEY in schema) {
61
+ const xxxOf: S[] = ONE_OF_KEY in schema ? (schema.oneOf as S[]) : (schema.anyOf as S[]);
62
+ const discriminator = getDiscriminatorFieldFromSchema<S>(schema);
63
+ const index = getClosestMatchingOption<T, S, F>(validator, rootSchema!, formData, xxxOf, 0, discriminator);
64
+ const _schema: S = xxxOf![index] as S;
65
+ pathSchema = {
66
+ ...pathSchema,
67
+ ...toPathSchemaInternal<T, S, F>(validator, _schema, name, rootSchema, formData, _recurseList),
68
+ };
69
+ }
70
+
71
+ if (ADDITIONAL_PROPERTIES_KEY in schema && schema[ADDITIONAL_PROPERTIES_KEY] !== false) {
72
+ set(pathSchema, RJSF_ADDITONAL_PROPERTIES_FLAG, true);
73
+ }
74
+
75
+ if (ITEMS_KEY in schema && Array.isArray(formData)) {
76
+ formData.forEach((element, i: number) => {
77
+ pathSchema[i] = toPathSchemaInternal<T, S, F>(
78
+ validator,
79
+ schema.items as S,
80
+ `${name}.${i}`,
81
+ rootSchema,
82
+ element,
83
+ _recurseList
84
+ );
85
+ });
86
+ } else if (PROPERTIES_KEY in schema) {
87
+ for (const property in schema.properties) {
88
+ const field = get(schema, [PROPERTIES_KEY, property]);
89
+ pathSchema[property] = toPathSchemaInternal<T, S, F>(
90
+ validator,
91
+ field,
92
+ `${name}.${property}`,
93
+ rootSchema,
94
+ // It's possible that formData is not an object -- this can happen if an
95
+ // array item has just been added, but not populated with data yet
96
+ get(formData, [property]),
97
+ _recurseList
98
+ );
99
+ }
100
+ }
101
+ return pathSchema as PathSchema<T>;
102
+ }
103
+
104
+ /** Generates an `PathSchema` object for the `schema`, recursively
105
+ *
106
+ * @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
107
+ * @param schema - The schema for which the `PathSchema` is desired
108
+ * @param [name=''] - The base name for the schema
109
+ * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
110
+ * @param [formData] - The current formData, if any, to assist retrieving a schema
111
+ * @returns - The `PathSchema` object for the `schema`
112
+ */
113
+ export default function toPathSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
114
+ validator: ValidatorType<T, S, F>,
115
+ schema: S,
116
+ name = '',
117
+ rootSchema?: S,
118
+ formData?: T
119
+ ): PathSchema<T> {
120
+ return toPathSchemaInternal(validator, schema, name, rootSchema, formData);
121
+ }
@@ -0,0 +1,40 @@
1
+ import { RJSFSchema, StrictRJSFSchema } from './types';
2
+
3
+ /** Check to see if a `schema` specifies that a value must be true. This happens when:
4
+ * - `schema.const` is truthy
5
+ * - `schema.enum` == `[true]`
6
+ * - `schema.anyOf` or `schema.oneOf` has a single value which recursively returns true
7
+ * - `schema.allOf` has at least one value which recursively returns true
8
+ *
9
+ * @param schema - The schema to check
10
+ * @returns - True if the schema specifies a value that must be true, false otherwise
11
+ */
12
+ export default function schemaRequiresTrueValue<S extends StrictRJSFSchema = RJSFSchema>(schema: S): boolean {
13
+ // Check if const is a truthy value
14
+ if (schema.const) {
15
+ return true;
16
+ }
17
+
18
+ // Check if an enum has a single value of true
19
+ if (schema.enum && schema.enum.length === 1 && schema.enum[0] === true) {
20
+ return true;
21
+ }
22
+
23
+ // If anyOf has a single value, evaluate the subschema
24
+ if (schema.anyOf && schema.anyOf.length === 1) {
25
+ return schemaRequiresTrueValue(schema.anyOf[0] as S);
26
+ }
27
+
28
+ // If oneOf has a single value, evaluate the subschema
29
+ if (schema.oneOf && schema.oneOf.length === 1) {
30
+ return schemaRequiresTrueValue(schema.oneOf[0] as S);
31
+ }
32
+
33
+ // Evaluate each subschema in allOf, to see if one of them requires a true value
34
+ if (schema.allOf) {
35
+ const schemaSome = (subSchema: S['additionalProperties']) => schemaRequiresTrueValue(subSchema as S);
36
+ return schema.allOf.some(schemaSome);
37
+ }
38
+
39
+ return false;
40
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+
3
+ import deepEquals from './deepEquals';
4
+
5
+ /** Determines whether the given `component` should be rerendered by comparing its current set of props and state
6
+ * against the next set. If either of those two sets are not the same, then the component should be rerendered.
7
+ *
8
+ * @param component - A React component being checked
9
+ * @param nextProps - The next set of props against which to check
10
+ * @param nextState - The next set of state against which to check
11
+ * @returns - True if the component should be re-rendered, false otherwise
12
+ */
13
+ export default function shouldRender(component: React.Component, nextProps: any, nextState: any) {
14
+ const { props, state } = component;
15
+ return !deepEquals(props, nextProps) || !deepEquals(state, nextState);
16
+ }
@@ -0,0 +1,19 @@
1
+ import { CONST_KEY, ENUM_KEY } from './constants';
2
+ import { RJSFSchema, StrictRJSFSchema } from './types';
3
+
4
+ /** Returns the constant value from the schema when it is either a single value enum or has a const key. Otherwise
5
+ * throws an error.
6
+ *
7
+ * @param schema - The schema from which to obtain the constant value
8
+ * @returns - The constant value for the schema
9
+ * @throws - Error when the schema does not have a constant value
10
+ */
11
+ export default function toConstant<S extends StrictRJSFSchema = RJSFSchema>(schema: S) {
12
+ if (ENUM_KEY in schema && Array.isArray(schema.enum) && schema.enum.length === 1) {
13
+ return schema.enum[0];
14
+ }
15
+ if (CONST_KEY in schema) {
16
+ return schema.const;
17
+ }
18
+ throw new Error('schema cannot be inferred as a constant');
19
+ }
@@ -0,0 +1,15 @@
1
+ import { DateObject } from './types';
2
+
3
+ /** Returns a UTC date string for the given `dateObject`. If `time` is false, then the time portion of the string is
4
+ * removed.
5
+ *
6
+ * @param dateObject - The `DateObject` to convert to a date string
7
+ * @param [time=true] - Optional flag used to remove the time portion of the date string if false
8
+ * @returns - The UTC date string
9
+ */
10
+ export default function toDateString(dateObject: DateObject, time = true) {
11
+ const { year, month, day, hour = 0, minute = 0, second = 0 } = dateObject;
12
+ const utcTime = Date.UTC(year, month - 1, day, hour, minute, second);
13
+ const datetime = new Date(utcTime).toJSON();
14
+ return time ? datetime : datetime.slice(0, 10);
15
+ }
@@ -0,0 +1,41 @@
1
+ import isPlainObject from 'lodash/isPlainObject';
2
+
3
+ import { ERRORS_KEY } from './constants';
4
+ import { ErrorSchema, GenericObjectType, RJSFValidationError } from './types';
5
+
6
+ /** Converts an `errorSchema` into a list of `RJSFValidationErrors`
7
+ *
8
+ * @param errorSchema - The `ErrorSchema` instance to convert
9
+ * @param [fieldPath=[]] - The current field path, defaults to [] if not specified
10
+ * @returns - The list of `RJSFValidationErrors` extracted from the `errorSchema`
11
+ */
12
+ export default function toErrorList<T = any>(
13
+ errorSchema?: ErrorSchema<T>,
14
+ fieldPath: string[] = []
15
+ ): RJSFValidationError[] {
16
+ if (!errorSchema) {
17
+ return [];
18
+ }
19
+ let errorList: RJSFValidationError[] = [];
20
+ if (ERRORS_KEY in errorSchema) {
21
+ errorList = errorList.concat(
22
+ errorSchema[ERRORS_KEY]!.map((message: string) => {
23
+ const property = `.${fieldPath.join('.')}`;
24
+ return {
25
+ property,
26
+ message,
27
+ stack: `${property} ${message}`,
28
+ };
29
+ })
30
+ );
31
+ }
32
+ return Object.keys(errorSchema).reduce((acc, key) => {
33
+ if (key !== ERRORS_KEY) {
34
+ const childSchema = (errorSchema as GenericObjectType)[key];
35
+ if (isPlainObject(childSchema)) {
36
+ acc = acc.concat(toErrorList(childSchema, [...fieldPath, key]));
37
+ }
38
+ }
39
+ return acc;
40
+ }, errorList);
41
+ }
@@ -0,0 +1,43 @@
1
+ import toPath from 'lodash/toPath';
2
+
3
+ import { ErrorSchema, RJSFValidationError } from './types';
4
+ import ErrorSchemaBuilder from './ErrorSchemaBuilder';
5
+
6
+ /** Transforms a rjsf validation errors list:
7
+ * [
8
+ * {property: '.level1.level2[2].level3', message: 'err a'},
9
+ * {property: '.level1.level2[2].level3', message: 'err b'},
10
+ * {property: '.level1.level2[4].level3', message: 'err b'},
11
+ * ]
12
+ * Into an error tree:
13
+ * {
14
+ * level1: {
15
+ * level2: {
16
+ * 2: {level3: {errors: ['err a', 'err b']}},
17
+ * 4: {level3: {errors: ['err b']}},
18
+ * }
19
+ * }
20
+ * };
21
+ *
22
+ * @param errors - The list of RJSFValidationError objects
23
+ * @returns - The `ErrorSchema` built from the list of `RJSFValidationErrors`
24
+ */
25
+ export default function toErrorSchema<T = any>(errors: RJSFValidationError[]): ErrorSchema<T> {
26
+ const builder = new ErrorSchemaBuilder<T>();
27
+ if (errors.length) {
28
+ errors.forEach((error) => {
29
+ const { property, message } = error;
30
+ // When the property is the root element, just use an empty array for the path
31
+ const path = property === '.' ? [] : toPath(property);
32
+ // If the property is at the root (.level1) then toPath creates
33
+ // an empty array element at the first index. Remove it.
34
+ if (path.length > 0 && path[0] === '') {
35
+ path.splice(0, 1);
36
+ }
37
+ if (message) {
38
+ builder.addErrors(message, path);
39
+ }
40
+ });
41
+ }
42
+ return builder.ErrorSchema;
43
+ }