@rjsf/utils 6.6.2 → 6.7.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 (88) hide show
  1. package/LICENSE.md +201 -0
  2. package/README.md +0 -4
  3. package/dist/index.cjs +246 -112
  4. package/dist/index.cjs.map +4 -4
  5. package/dist/utils.esm.js +246 -112
  6. package/dist/utils.esm.js.map +4 -4
  7. package/dist/utils.umd.js +277 -140
  8. package/lib/SelectedOptionDescription.d.ts +4 -0
  9. package/lib/SelectedOptionDescription.js +24 -0
  10. package/lib/SelectedOptionDescription.js.map +1 -0
  11. package/lib/asNumber.d.ts +6 -5
  12. package/lib/asNumber.js +14 -9
  13. package/lib/asNumber.js.map +1 -1
  14. package/lib/constants.d.ts +8 -5
  15. package/lib/constants.js +10 -7
  16. package/lib/constants.js.map +1 -1
  17. package/lib/createSchemaUtils.js +1 -1
  18. package/lib/createSchemaUtils.js.map +1 -1
  19. package/lib/enums.d.ts +4 -0
  20. package/lib/enums.js +4 -0
  21. package/lib/enums.js.map +1 -1
  22. package/lib/getDecimalSeparator.d.ts +8 -0
  23. package/lib/getDecimalSeparator.js +23 -0
  24. package/lib/getDecimalSeparator.js.map +1 -0
  25. package/lib/getSubmitButtonOptions.js +1 -1
  26. package/lib/getSubmitButtonOptions.js.map +1 -1
  27. package/lib/getWidget.js +2 -1
  28. package/lib/getWidget.js.map +1 -1
  29. package/lib/hashForSchema.d.ts +2 -0
  30. package/lib/hashForSchema.js +3 -1
  31. package/lib/hashForSchema.js.map +1 -1
  32. package/lib/index.d.ts +6 -2
  33. package/lib/index.js +5 -2
  34. package/lib/index.js.map +1 -1
  35. package/lib/isFixedItems.d.ts +2 -2
  36. package/lib/isFixedItems.js +3 -3
  37. package/lib/isFixedItems.js.map +1 -1
  38. package/lib/isRootSchema.js +2 -3
  39. package/lib/isRootSchema.js.map +1 -1
  40. package/lib/logUnsupportedDefaultForEnum.d.ts +9 -0
  41. package/lib/logUnsupportedDefaultForEnum.js +18 -0
  42. package/lib/logUnsupportedDefaultForEnum.js.map +1 -0
  43. package/lib/mergeSchemas.js +8 -1
  44. package/lib/mergeSchemas.js.map +1 -1
  45. package/lib/optionsList.js +22 -21
  46. package/lib/optionsList.js.map +1 -1
  47. package/lib/resolveUiSchema.js.map +1 -1
  48. package/lib/schema/getDefaultFormState.d.ts +1 -1
  49. package/lib/schema/getDefaultFormState.js +53 -17
  50. package/lib/schema/getDefaultFormState.js.map +1 -1
  51. package/lib/schema/getDisplayLabel.js +2 -3
  52. package/lib/schema/getDisplayLabel.js.map +1 -1
  53. package/lib/schema/omitExtraData.js +11 -0
  54. package/lib/schema/omitExtraData.js.map +1 -1
  55. package/lib/schema/retrieveSchema.d.ts +5 -1
  56. package/lib/schema/retrieveSchema.js +49 -14
  57. package/lib/schema/retrieveSchema.js.map +1 -1
  58. package/lib/schemaRequiresTrueValue.js +4 -3
  59. package/lib/schemaRequiresTrueValue.js.map +1 -1
  60. package/lib/tsconfig.tsbuildinfo +1 -1
  61. package/lib/types.d.ts +24 -0
  62. package/lib/useAltDateWidgetProps.d.ts +1 -1
  63. package/lib/useFileWidgetProps.js +2 -2
  64. package/lib/useFileWidgetProps.js.map +1 -1
  65. package/package.json +19 -19
  66. package/src/SelectedOptionDescription.tsx +48 -0
  67. package/src/asNumber.ts +18 -9
  68. package/src/constants.ts +10 -7
  69. package/src/createSchemaUtils.ts +1 -1
  70. package/src/enums.ts +4 -0
  71. package/src/getDecimalSeparator.ts +20 -0
  72. package/src/getSubmitButtonOptions.ts +1 -1
  73. package/src/getWidget.tsx +1 -1
  74. package/src/hashForSchema.ts +3 -1
  75. package/src/index.ts +8 -0
  76. package/src/isFixedItems.ts +3 -3
  77. package/src/isRootSchema.ts +2 -4
  78. package/src/logUnsupportedDefaultForEnum.ts +28 -0
  79. package/src/mergeSchemas.ts +9 -2
  80. package/src/optionsList.ts +20 -22
  81. package/src/resolveUiSchema.ts +12 -4
  82. package/src/schema/getDefaultFormState.ts +79 -22
  83. package/src/schema/getDisplayLabel.ts +3 -4
  84. package/src/schema/omitExtraData.ts +11 -0
  85. package/src/schema/retrieveSchema.ts +59 -13
  86. package/src/schemaRequiresTrueValue.ts +3 -3
  87. package/src/types.ts +31 -0
  88. package/src/useFileWidgetProps.ts +2 -2
@@ -20,6 +20,7 @@ import {
20
20
  PATTERN_PROPERTIES_KEY,
21
21
  PROPERTIES_KEY,
22
22
  REF_KEY,
23
+ RJSF_REF_CYCLE_KEY,
23
24
  RJSF_REF_KEY,
24
25
  } from '../constants';
25
26
  import deepEquals from '../deepEquals';
@@ -32,6 +33,7 @@ import type {
32
33
  Experimental_CustomMergeAllOf,
33
34
  FormContextType,
34
35
  GenericObjectType,
36
+ RJSFMarkedSchema,
35
37
  RJSFSchema,
36
38
  StrictRJSFSchema,
37
39
  ValidatorType,
@@ -75,6 +77,18 @@ export default function retrieveSchema<
75
77
  )[0];
76
78
  }
77
79
 
80
+ /** Converts boolean schemas to equivalent object schemas for APIs that operate on `StrictRJSFSchema` objects.
81
+ *
82
+ * @param schema - The schema or boolean schema to normalize
83
+ * @returns - The original schema or an equivalent object schema
84
+ */
85
+ function normalizeBooleanSchema<S extends StrictRJSFSchema = RJSFSchema>(schema: S | boolean): S {
86
+ if (typeof schema !== 'boolean') {
87
+ return schema;
88
+ }
89
+ return (schema ? {} : { not: {} }) as S;
90
+ }
91
+
78
92
  /** Resolves a conditional block (if/else/then) by removing the condition and merging the appropriate conditional branch
79
93
  * with the rest of the schema. If `expandAllBranches` is true, then the `retrieveSchemaInteral()` results for both
80
94
  * conditions will be returned.
@@ -105,10 +119,11 @@ export function resolveCondition<T = any, S extends StrictRJSFSchema = RJSFSchem
105
119
  let schemas: S[] = [];
106
120
  if (expandAllBranches) {
107
121
  if (then && typeof then !== 'boolean') {
122
+ const thenSchema = then as unknown as S;
108
123
  schemas = schemas.concat(
109
124
  retrieveSchemaInternal<T, S, F>(
110
125
  validator,
111
- then as S,
126
+ thenSchema,
112
127
  rootSchema,
113
128
  formData,
114
129
  expandAllBranches,
@@ -118,10 +133,11 @@ export function resolveCondition<T = any, S extends StrictRJSFSchema = RJSFSchem
118
133
  );
119
134
  }
120
135
  if (otherwise && typeof otherwise !== 'boolean') {
136
+ const otherwiseSchema = otherwise as unknown as S;
121
137
  schemas = schemas.concat(
122
138
  retrieveSchemaInternal<T, S, F>(
123
139
  validator,
124
- otherwise as S,
140
+ otherwiseSchema,
125
141
  rootSchema,
126
142
  formData,
127
143
  expandAllBranches,
@@ -131,12 +147,13 @@ export function resolveCondition<T = any, S extends StrictRJSFSchema = RJSFSchem
131
147
  );
132
148
  }
133
149
  } else {
134
- const conditionalSchema = conditionValue ? then : otherwise;
135
- if (conditionalSchema && typeof conditionalSchema !== 'boolean') {
150
+ const conditionalBranch = (conditionValue ? then : otherwise) as S | boolean | undefined;
151
+ if (conditionalBranch !== undefined) {
152
+ const conditionalSchema = normalizeBooleanSchema<S>(conditionalBranch);
136
153
  schemas = schemas.concat(
137
154
  retrieveSchemaInternal<T, S, F>(
138
155
  validator,
139
- conditionalSchema as S,
156
+ conditionalSchema,
140
157
  rootSchema,
141
158
  formData,
142
159
  expandAllBranches,
@@ -348,6 +365,10 @@ export function resolveReference<T = any, S extends StrictRJSFSchema = RJSFSchem
348
365
  * @param recurseList - List of $refs already resolved to prevent recursion
349
366
  * @param [baseURI] - The base URI to be used for resolving relative references
350
367
  * @param [resolveAnyOfOrOneOfRefs] - Optional flag indicating whether to resolved refs in anyOf/oneOf lists
368
+ * @param [markCycleOnDetection=false] - When true and a recursive $ref is detected, the returned schema is tagged
369
+ * with `__rjsf_ref_cycle: true` so that `SchemaField` can render a cycle indicator instead of recursing.
370
+ * Should only be `true` when called from an **object-property** context, because object properties are always
371
+ * rendered (creating an infinite loop), whereas array items and anyOf/oneOf branches are data-driven.
351
372
  * @returns - given schema will all references resolved or the original schema if no internal `$refs` were resolved
352
373
  */
353
374
  export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
@@ -356,6 +377,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
356
377
  recurseList: string[],
357
378
  baseURI?: string,
358
379
  resolveAnyOfOrOneOfRefs?: boolean,
380
+ markCycleOnDetection = false,
359
381
  ): S {
360
382
  if (!isObject(schema)) {
361
383
  return schema;
@@ -367,7 +389,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
367
389
  const { $ref, ...localSchema } = resolvedSchema;
368
390
  // Check for a recursive reference and stop the loop
369
391
  if (recurseList.includes($ref!)) {
370
- return resolvedSchema;
392
+ return markCycleOnDetection ? ({ ...resolvedSchema, [RJSF_REF_CYCLE_KEY]: true } as S) : resolvedSchema;
371
393
  }
372
394
  recurseList.push($ref!);
373
395
  // Retrieve the referenced schema definition.
@@ -384,7 +406,18 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
384
406
  resolvedSchema[PROPERTIES_KEY]!,
385
407
  (acc, value, key: string) => {
386
408
  const childList: string[] = [...recurseList];
387
- acc[key] = resolveAllReferences(value as S, rootSchema, childList, currentBaseURI, resolveAnyOfOrOneOfRefs);
409
+ // Mark cycles only when NOT in resolveAnyOfOrOneOfRefs mode. When resolveAnyOfOrOneOfRefs=true
410
+ // (e.g. from ObjectField), options are resolved against a shared recurseList that accumulates
411
+ // refs across branches, causing false positives. In simple (non-anyOf) resolution, a $ref cycle
412
+ // in an object property always causes an infinite render loop and must be caught.
413
+ acc[key] = resolveAllReferences(
414
+ value as S,
415
+ rootSchema,
416
+ childList,
417
+ currentBaseURI,
418
+ resolveAnyOfOrOneOfRefs,
419
+ !resolveAnyOfOrOneOfRefs,
420
+ );
388
421
  childrenLists.push(childList);
389
422
  },
390
423
  {} as RJSFSchema,
@@ -400,6 +433,7 @@ export function resolveAllReferences<S extends StrictRJSFSchema = RJSFSchema>(
400
433
  ) {
401
434
  resolvedSchema = {
402
435
  ...resolvedSchema,
436
+ // Array items are only rendered when data exists, so a $ref cycle here does NOT cause an infinite render.
403
437
  items: resolveAllReferences(
404
438
  resolvedSchema.items as S,
405
439
  rootSchema,
@@ -476,7 +510,7 @@ export function stubExistingAdditionalProperties<
476
510
  get(formData, [key]) as T,
477
511
  experimental_customMergeAllOf,
478
512
  );
479
- set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true);
513
+ (schema.properties[key] as RJSFMarkedSchema)[ADDITIONAL_PROPERTY_FLAG] = true;
480
514
  return;
481
515
  }
482
516
  }
@@ -488,7 +522,7 @@ export function stubExistingAdditionalProperties<
488
522
  validator,
489
523
  { [REF_KEY]: get(schema.additionalProperties, [REF_KEY]) } as S,
490
524
  rootSchema,
491
- formData as T,
525
+ get(formData, [key]) as T,
492
526
  experimental_customMergeAllOf,
493
527
  );
494
528
  } else if ('type' in schema.additionalProperties!) {
@@ -508,12 +542,12 @@ export function stubExistingAdditionalProperties<
508
542
  // The type of our new key should match the additionalProperties value;
509
543
  schema.properties[key] = additionalProperties;
510
544
  // Set our additional property flag so we know it was dynamically added
511
- set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true);
545
+ (schema.properties[key] as RJSFMarkedSchema)[ADDITIONAL_PROPERTY_FLAG] = true;
512
546
  } else {
513
547
  // Invalid property
514
548
  schema.properties[key] = { type: 'null' };
515
549
  // Set our additional property flag so we know it was dynamically added
516
- set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true);
550
+ (schema.properties[key] as RJSFMarkedSchema)[ADDITIONAL_PROPERTY_FLAG] = true;
517
551
  }
518
552
  });
519
553
 
@@ -595,11 +629,19 @@ export function retrieveSchemaInternal<
595
629
  try {
596
630
  const withContainsSchemas = [] as S[];
597
631
  const withoutContainsSchemas = [] as S[];
632
+ // Collect Symbol-keyed properties from allOf subschemas before merging; shallowAllOfMerge
633
+ // (external library) only operates on string keys and will drop them.
634
+ const allOfSymbols: Record<symbol, unknown> = {};
598
635
  resolvedSchema.allOf?.forEach((allOfItem) => {
599
636
  if (typeof allOfItem === 'object' && allOfItem.contains) {
600
637
  withContainsSchemas.push(allOfItem as S);
601
638
  } else {
602
639
  withoutContainsSchemas.push(allOfItem as S);
640
+ for (const sym of Object.getOwnPropertySymbols(allOfItem)) {
641
+ if (!(sym in allOfSymbols)) {
642
+ allOfSymbols[sym] = (allOfItem as any)[sym];
643
+ }
644
+ }
603
645
  }
604
646
  });
605
647
  if (withContainsSchemas.length) {
@@ -608,6 +650,10 @@ export function retrieveSchemaInternal<
608
650
  resolvedSchema = experimental_customMergeAllOf
609
651
  ? experimental_customMergeAllOf(resolvedSchema)
610
652
  : mergeAllOf(resolvedSchema);
653
+ // Re-apply collected Symbol properties that the merge dropped.
654
+ for (const sym of Object.getOwnPropertySymbols(allOfSymbols)) {
655
+ (resolvedSchema as any)[sym] = allOfSymbols[sym];
656
+ }
611
657
  if (withContainsSchemas.length) {
612
658
  resolvedSchema.allOf = withContainsSchemas;
613
659
  }
@@ -724,7 +770,7 @@ export function relaxOptionsForScoring<S extends StrictRJSFSchema = RJSFSchema>(
724
770
  ): S[] {
725
771
  return options.map((d) => {
726
772
  if (!isObject(d)) {
727
- return (d ? {} : { not: {} }) as S;
773
+ return normalizeBooleanSchema<S>(d);
728
774
  }
729
775
  const schema = resolveRefs && rootSchema ? resolveAllReferences<S>(d, rootSchema, []) : d;
730
776
  return schema.additionalProperties === false ? { ...schema, additionalProperties: true } : schema;
@@ -960,7 +1006,7 @@ export function withExactlyOneSubschema<
960
1006
  experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
961
1007
  ): S[] {
962
1008
  const validSubschemas = oneOf!.filter((subschema) => {
963
- if (typeof subschema === 'boolean' || !subschema || !subschema.properties) {
1009
+ if (typeof subschema === 'boolean' || !subschema?.properties) {
964
1010
  return false;
965
1011
  }
966
1012
  const { [dependencyKey]: conditionPropertySchema } = subschema.properties;
@@ -16,17 +16,17 @@ export default function schemaRequiresTrueValue<S extends StrictRJSFSchema = RJS
16
16
  }
17
17
 
18
18
  // Check if an enum has a single value of true
19
- if (schema.enum && schema.enum.length === 1 && schema.enum[0] === true) {
19
+ if (schema.enum?.length === 1 && schema.enum[0] === true) {
20
20
  return true;
21
21
  }
22
22
 
23
23
  // If anyOf has a single value, evaluate the subschema
24
- if (schema.anyOf && schema.anyOf.length === 1) {
24
+ if (schema.anyOf?.length === 1) {
25
25
  return schemaRequiresTrueValue(schema.anyOf[0] as S);
26
26
  }
27
27
 
28
28
  // If oneOf has a single value, evaluate the subschema
29
- if (schema.oneOf && schema.oneOf.length === 1) {
29
+ if (schema.oneOf?.length === 1) {
30
30
  return schemaRequiresTrueValue(schema.oneOf[0] as S);
31
31
  }
32
32
 
package/src/types.ts CHANGED
@@ -18,6 +18,11 @@ import './jsonSchemaAugmentation';
18
18
  */
19
19
  export type GenericObjectType = Record<string, any>;
20
20
 
21
+ /** The representation of any generic object type, usually used as an intersection on other types to make them more
22
+ * flexible in the properties they support (i.e. anything else) AND symbol markers with a value of string or boolean
23
+ */
24
+ export type GenericSymbolObjectType = GenericObjectType & Record<symbol, boolean | string>;
25
+
21
26
  /** Map the JSONSchema7 to our own type so that we can easily bump to a more recent version at some future date and only
22
27
  * have to update this one type.
23
28
  */
@@ -27,6 +32,11 @@ export type StrictRJSFSchema = JSONSchema7;
27
32
  */
28
33
  export type RJSFSchema = StrictRJSFSchema & GenericObjectType;
29
34
 
35
+ /** Allow for more flexible schemas (i.e. draft-2019) than the strict JSONSchema7 with special marking added by
36
+ * `retrieveSchema()`
37
+ */
38
+ export type RJSFMarkedSchema = StrictRJSFSchema & GenericSymbolObjectType;
39
+
30
40
  /** Alias GenericObjectType as FormContextType to allow us to remap this at some future date
31
41
  */
32
42
  export type FormContextType = GenericObjectType;
@@ -123,6 +133,12 @@ export interface Experimental_DefaultFormStateBehavior {
123
133
  *
124
134
  */
125
135
  constAsDefaults?: 'always' | 'skipOneOf' | 'never';
136
+ /** Optional enumerated flag controlling how defaults defined on multiple levels are merged together for overlapping
137
+ * properties, defaulting to `descendantWins`.
138
+ * - `descendantWins`: The innermost (descendant) default value definition takes precedence over its ancestor's defaults.
139
+ * - `ancestorWins`: The outermost (ancestor) default value definition takes precedence over any descendant's defaults.
140
+ */
141
+ nestedDefaultsPrecedence?: 'descendantWins' | 'ancestorWins';
126
142
  }
127
143
 
128
144
  /** Optional function that allows for custom merging of `allOf` schemas
@@ -281,6 +297,19 @@ export interface RJSFBaseProps<T = any, S extends StrictRJSFSchema = RJSFSchema,
281
297
  registry: Registry<T, S, F>;
282
298
  }
283
299
 
300
+ export type CyclicSchemaExpandProps<
301
+ T = any,
302
+ S extends StrictRJSFSchema = RJSFSchema,
303
+ F extends FormContextType = any,
304
+ > = RJSFBaseProps<T, S, F> & {
305
+ /** The FieldPathId of the field in the hierarchy */
306
+ fieldPathId: FieldPathId;
307
+ /** The unique name of the field, usually derived from the name of the property in the JSONSchema */
308
+ name: string;
309
+ /** Callback used to mark a cyclic scheme element as expanded */
310
+ onExpand: (id: string) => void;
311
+ };
312
+
284
313
  /** The properties that are passed to an `ErrorListTemplate` implementation */
285
314
  export type ErrorListProps<
286
315
  T = any,
@@ -359,6 +388,8 @@ export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F ex
359
388
  ArrayFieldTitleTemplate: ComponentType<ArrayFieldTitleProps<T, S, F>>;
360
389
  /** The template to use while rendering the standard html input */
361
390
  BaseInputTemplate: ComponentType<BaseInputTemplateProps<T, S, F>>;
391
+ /** The template to use while rendering the cyclic schema expand controls */
392
+ CyclicSchemaExpandTemplate: ComponentType<CyclicSchemaExpandProps<T, S, F>>;
362
393
  /** The template to use for rendering the description of a field */
363
394
  DescriptionFieldTemplate: ComponentType<DescriptionFieldProps<T, S, F>>;
364
395
  /** The template to use while rendering the errors for the whole form */
@@ -40,7 +40,7 @@ function addNameToDataURL(dataURL: string, name: string) {
40
40
  * @param file - The `File` information to read
41
41
  * @returns - A promise that resolves to the read file.
42
42
  */
43
- function processFile(file: File): Promise<FileInfoType> {
43
+ async function processFile(file: File): Promise<FileInfoType> {
44
44
  const { name, size, type } = file;
45
45
  return new Promise((resolve, reject) => {
46
46
  const reader = new window.FileReader();
@@ -71,7 +71,7 @@ function processFile(file: File): Promise<FileInfoType> {
71
71
  * @param files - The list of files to read
72
72
  * @returns - The list of read files
73
73
  */
74
- function processFiles(files: FileList) {
74
+ async function processFiles(files: FileList) {
75
75
  return Promise.all(Array.from(files).map(processFile));
76
76
  }
77
77