From f95bd70b01aa99ba0bddc3c900aba455ccdf6d63 Mon Sep 17 00:00:00 2001 From: Capelo Date: Thu, 17 Jul 2025 16:32:19 +0100 Subject: [PATCH 01/10] chore: improve doc annotation on JsFSchema type --- src/types.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/types.ts b/src/types.ts index d97da7820..3873e8954 100644 --- a/src/types.ts +++ b/src/types.ts @@ -68,17 +68,19 @@ export type JsfSchema = JSONSchema & { // schema (like an if inside another schema), the required property won't be // present in the type 'required'?: string[] - // Defines the order of the fields in the form. + /* Defines the order of the fields in the form. */ 'x-jsf-order'?: string[] - // Defines the presentation of the field in the form. + /* Defines the presentation of the field in the form. */ 'x-jsf-presentation'?: JsfPresentation - // Defines the error message of the field in the form. + /* Defines the error message of the field in the form. */ 'x-jsf-errorMessage'?: Record 'x-jsf-logic'?: JsonLogicSchema - // Extra validations to run. References validations in the `x-jsf-logic` root property. + /* Extra validations to run. References validations in the `x-jsf-logic` root property. */ 'x-jsf-logic-validations'?: string[] - // Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. + /* Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ 'x-jsf-logic-computedAttrs'?: Record + /* validations references validations in the `x-jsf-logic` root property as if they were properties of the schema, so logic can be applied to them. */ + 'validations'?: JsfSchema['properties'] } /** From 3052ad00f163e0f36a8711773a88ec245a961835 Mon Sep 17 00:00:00 2001 From: Capelo Date: Thu, 17 Jul 2025 16:42:40 +0100 Subject: [PATCH 02/10] fix: apply json-logic computed values to conditionals as well --- src/validation/json-logic.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts index 3c5c5d303..5f1527848 100644 --- a/src/validation/json-logic.ts +++ b/src/validation/json-logic.ts @@ -165,7 +165,7 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu delete propertySchema['x-jsf-logic-computedAttrs'] } - // If this is a full property schema, we need to cycle through the properties and apply the computed values + // If the schemas has properties, we need to cycle through each one and apply the computed values // Otherwise, just process the property if (schemaCopy.properties) { for (const propertyName in schemaCopy.properties) { @@ -175,6 +175,27 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu else { processProperty(schemaCopy) } + + // If the schema has an if statement, we need to cycle through the properties and apply the computed values + if (schemaCopy.if) { + cycleThroughPropertiesAndApplyValues(schemaCopy.if as JsfObjectSchema, computedValues) + } + + /* If the schema has an allOf or anyOf property, we need to cycle through each property inside it and + * apply the computed values + */ + + if (schemaCopy.allOf && schemaCopy.allOf.length > 0) { + for (const schema of schemaCopy.allOf) { + cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) + } + } + + if (schemaCopy.anyOf && schemaCopy.anyOf.length > 0) { + for (const schema of schemaCopy.anyOf) { + cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) + } + } } /** From 9cb6fe3ad2fe47df7bf12f90fe655f09892a9551 Mon Sep 17 00:00:00 2001 From: Capelo Date: Thu, 17 Jul 2025 16:43:03 +0100 Subject: [PATCH 03/10] fix: ensure schema gets re-calculated if computed values were applied to it --- src/mutations.ts | 3 ++ test/validation/json-logic.test.ts | 72 ++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/mutations.ts b/src/mutations.ts index b0e67a619..1a0e89028 100644 --- a/src/mutations.ts +++ b/src/mutations.ts @@ -32,6 +32,9 @@ export function calculateFinalSchema({ if (jsonLogicContext?.schema.computedValues) { applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values) + // If we had computed values applied to the schema, + // we need to re-apply the schema rules to update the fields + applySchemaRules(schemaCopy, values, options, jsonLogicContext) } return schemaCopy diff --git a/test/validation/json-logic.test.ts b/test/validation/json-logic.test.ts index 69b883e45..b8b8528b5 100644 --- a/test/validation/json-logic.test.ts +++ b/test/validation/json-logic.test.ts @@ -464,4 +464,76 @@ describe('applyComputedAttrsToSchema', () => { const ageProperties = result.properties?.age as JsfObjectSchema expect(ageProperties?.minimum).toBe(21) }) + + it('allows to use computed values inside conditional statements', () => { + const schema: JsfObjectSchema = { + 'properties': { + pine_trees: { + title: 'Pine trees planted', + type: 'number', + description: 'The number of pine trees you have planted.', + }, + oak_trees: { + type: 'number', + title: 'Oak trees planted', + description: 'Enter the number of oak trees you\'ve planted. If there are more pine trees than oak trees, you\'ll need to plant spruce trees as well. But this only counts if less than 10 pines planted.', + }, + spruce_trees: { + title: 'Spruce trees planted', + type: 'number', + description: 'The number of spruce trees you have planted (only required if specific conditions are met).', + }, + }, + 'allOf': [ + { + if: { + properties: { + oak_trees: { + 'x-jsf-logic-computedAttrs': { + minimum: 'pine_value', + }, + }, + }, + }, + then: { + required: [ + 'spruce_trees', + ], + }, + else: { + properties: { + spruce_trees: false, + }, + }, + }, + ], + 'required': [ + 'pine_trees', + 'oak_trees', + ], + 'x-jsf-logic': { + computedValues: { + pine_value: { + rule: { + '+': [ + { + var: 'pine_trees', + }, + 1, + ], + }, + }, + }, + }, + }; + + // Mock the jsonLogic.apply to return 10 + (jsonLogic.apply as jest.Mock).mockReturnValue(10) + + const result = JsonLogicValidation.applyComputedAttrsToSchema(schema, schema['x-jsf-logic']?.computedValues, { pine_trees: 10, oak_trees: 2 }) + + const conditionValue = result.allOf?.[0].if?.properties?.oak_trees as JsfObjectSchema + expect(conditionValue['x-jsf-logic-computedAttrs']).toBeUndefined() + expect(conditionValue.minimum).toBe(10) + }) }) From 95def1168d208167113293fc89193c69d86773ed Mon Sep 17 00:00:00 2001 From: Capelo Date: Thu, 17 Jul 2025 16:47:58 +0100 Subject: [PATCH 04/10] chore: revert temp change --- src/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types.ts b/src/types.ts index 3873e8954..22fc75095 100644 --- a/src/types.ts +++ b/src/types.ts @@ -79,8 +79,6 @@ export type JsfSchema = JSONSchema & { 'x-jsf-logic-validations'?: string[] /* Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ 'x-jsf-logic-computedAttrs'?: Record - /* validations references validations in the `x-jsf-logic` root property as if they were properties of the schema, so logic can be applied to them. */ - 'validations'?: JsfSchema['properties'] } /** From 862970cbee53337571a800f4fecec12fded7df07 Mon Sep 17 00:00:00 2001 From: Capelo Date: Thu, 17 Jul 2025 17:02:00 +0100 Subject: [PATCH 05/10] chore: fix types --- ok.txt | 162 +++++++++++++++++++++++++++++ test/validation/json-logic.test.ts | 1 + 2 files changed, 163 insertions(+) create mode 100644 ok.txt diff --git a/ok.txt b/ok.txt new file mode 100644 index 000000000..af3b07bd5 --- /dev/null +++ b/ok.txt @@ -0,0 +1,162 @@ +diff --git a/src/mutations.ts b/src/mutations.ts +index b0e67a6..1a0e890 100644 +--- a/src/mutations.ts ++++ b/src/mutations.ts +@@ -32,6 +32,9 @@ export function calculateFinalSchema({ + + if (jsonLogicContext?.schema.computedValues) { + applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values) ++ // If we had computed values applied to the schema, ++ // we need to re-apply the schema rules to update the fields ++ applySchemaRules(schemaCopy, values, options, jsonLogicContext) + } + + return schemaCopy +diff --git a/src/types.ts b/src/types.ts +index d97da78..22fc750 100644 +--- a/src/types.ts ++++ b/src/types.ts +@@ -68,16 +68,16 @@ export type JsfSchema = JSONSchema & { + // schema (like an if inside another schema), the required property won't be + // present in the type + 'required'?: string[] +- // Defines the order of the fields in the form. ++ /* Defines the order of the fields in the form. */ + 'x-jsf-order'?: string[] +- // Defines the presentation of the field in the form. ++ /* Defines the presentation of the field in the form. */ + 'x-jsf-presentation'?: JsfPresentation +- // Defines the error message of the field in the form. ++ /* Defines the error message of the field in the form. */ + 'x-jsf-errorMessage'?: Record + 'x-jsf-logic'?: JsonLogicSchema +- // Extra validations to run. References validations in the `x-jsf-logic` root property. ++ /* Extra validations to run. References validations in the `x-jsf-logic` root property. */ + 'x-jsf-logic-validations'?: string[] +- // Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. ++ /* Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ + 'x-jsf-logic-computedAttrs'?: Record + } + +diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts +index 3c5c5d3..5f15278 100644 +--- a/src/validation/json-logic.ts ++++ b/src/validation/json-logic.ts +@@ -165,7 +165,7 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu + delete propertySchema['x-jsf-logic-computedAttrs'] + } + +- // If this is a full property schema, we need to cycle through the properties and apply the computed values ++ // If the schemas has properties, we need to cycle through each one and apply the computed values + // Otherwise, just process the property + if (schemaCopy.properties) { + for (const propertyName in schemaCopy.properties) { +@@ -175,6 +175,27 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu + else { + processProperty(schemaCopy) + } ++ ++ // If the schema has an if statement, we need to cycle through the properties and apply the computed values ++ if (schemaCopy.if) { ++ cycleThroughPropertiesAndApplyValues(schemaCopy.if as JsfObjectSchema, computedValues) ++ } ++ ++ /* If the schema has an allOf or anyOf property, we need to cycle through each property inside it and ++ * apply the computed values ++ */ ++ ++ if (schemaCopy.allOf && schemaCopy.allOf.length > 0) { ++ for (const schema of schemaCopy.allOf) { ++ cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) ++ } ++ } ++ ++ if (schemaCopy.anyOf && schemaCopy.anyOf.length > 0) { ++ for (const schema of schemaCopy.anyOf) { ++ cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) ++ } ++ } + } + + /** +diff --git a/test/validation/json-logic.test.ts b/test/validation/json-logic.test.ts +index 69b883e..b8b8528 100644 +--- a/test/validation/json-logic.test.ts ++++ b/test/validation/json-logic.test.ts +@@ -464,4 +464,76 @@ describe('applyComputedAttrsToSchema', () => { + const ageProperties = result.properties?.age as JsfObjectSchema + expect(ageProperties?.minimum).toBe(21) + }) ++ ++ it('allows to use computed values inside conditional statements', () => { ++ const schema: JsfObjectSchema = { ++ 'properties': { ++ pine_trees: { ++ title: 'Pine trees planted', ++ type: 'number', ++ description: 'The number of pine trees you have planted.', ++ }, ++ oak_trees: { ++ type: 'number', ++ title: 'Oak trees planted', ++ description: 'Enter the number of oak trees you\'ve planted. If there are more pine trees than oak trees, you\'ll need to plant spruce trees as well. But this only counts if less than 10 pines planted.', ++ }, ++ spruce_trees: { ++ title: 'Spruce trees planted', ++ type: 'number', ++ description: 'The number of spruce trees you have planted (only required if specific conditions are met).', ++ }, ++ }, ++ 'allOf': [ ++ { ++ if: { ++ properties: { ++ oak_trees: { ++ 'x-jsf-logic-computedAttrs': { ++ minimum: 'pine_value', ++ }, ++ }, ++ }, ++ }, ++ then: { ++ required: [ ++ 'spruce_trees', ++ ], ++ }, ++ else: { ++ properties: { ++ spruce_trees: false, ++ }, ++ }, ++ }, ++ ], ++ 'required': [ ++ 'pine_trees', ++ 'oak_trees', ++ ], ++ 'x-jsf-logic': { ++ computedValues: { ++ pine_value: { ++ rule: { ++ '+': [ ++ { ++ var: 'pine_trees', ++ }, ++ 1, ++ ], ++ }, ++ }, ++ }, ++ }, ++ }; ++ ++ // Mock the jsonLogic.apply to return 10 ++ (jsonLogic.apply as jest.Mock).mockReturnValue(10) ++ ++ const result = JsonLogicValidation.applyComputedAttrsToSchema(schema, schema['x-jsf-logic']?.computedValues, { pine_trees: 10, oak_trees: 2 }) ++ ++ const conditionValue = result.allOf?.[0].if?.properties?.oak_trees as JsfObjectSchema ++ expect(conditionValue['x-jsf-logic-computedAttrs']).toBeUndefined() ++ expect(conditionValue.minimum).toBe(10) ++ }) + }) diff --git a/test/validation/json-logic.test.ts b/test/validation/json-logic.test.ts index b8b8528b5..ce08dc623 100644 --- a/test/validation/json-logic.test.ts +++ b/test/validation/json-logic.test.ts @@ -467,6 +467,7 @@ describe('applyComputedAttrsToSchema', () => { it('allows to use computed values inside conditional statements', () => { const schema: JsfObjectSchema = { + 'type': 'object', 'properties': { pine_trees: { title: 'Pine trees planted', From 5eeefdbf1e3e553721bcd00d52bf059966439871 Mon Sep 17 00:00:00 2001 From: Capelo Date: Mon, 21 Jul 2025 10:10:59 +0100 Subject: [PATCH 06/10] chore: remove aux file --- ok.txt | 162 --------------------------------------------------------- 1 file changed, 162 deletions(-) delete mode 100644 ok.txt diff --git a/ok.txt b/ok.txt deleted file mode 100644 index af3b07bd5..000000000 --- a/ok.txt +++ /dev/null @@ -1,162 +0,0 @@ -diff --git a/src/mutations.ts b/src/mutations.ts -index b0e67a6..1a0e890 100644 ---- a/src/mutations.ts -+++ b/src/mutations.ts -@@ -32,6 +32,9 @@ export function calculateFinalSchema({ - - if (jsonLogicContext?.schema.computedValues) { - applyComputedAttrsToSchema(schemaCopy, jsonLogicContext.schema.computedValues, values) -+ // If we had computed values applied to the schema, -+ // we need to re-apply the schema rules to update the fields -+ applySchemaRules(schemaCopy, values, options, jsonLogicContext) - } - - return schemaCopy -diff --git a/src/types.ts b/src/types.ts -index d97da78..22fc750 100644 ---- a/src/types.ts -+++ b/src/types.ts -@@ -68,16 +68,16 @@ export type JsfSchema = JSONSchema & { - // schema (like an if inside another schema), the required property won't be - // present in the type - 'required'?: string[] -- // Defines the order of the fields in the form. -+ /* Defines the order of the fields in the form. */ - 'x-jsf-order'?: string[] -- // Defines the presentation of the field in the form. -+ /* Defines the presentation of the field in the form. */ - 'x-jsf-presentation'?: JsfPresentation -- // Defines the error message of the field in the form. -+ /* Defines the error message of the field in the form. */ - 'x-jsf-errorMessage'?: Record - 'x-jsf-logic'?: JsonLogicSchema -- // Extra validations to run. References validations in the `x-jsf-logic` root property. -+ /* Extra validations to run. References validations in the `x-jsf-logic` root property. */ - 'x-jsf-logic-validations'?: string[] -- // Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. -+ /* Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ - 'x-jsf-logic-computedAttrs'?: Record - } - -diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts -index 3c5c5d3..5f15278 100644 ---- a/src/validation/json-logic.ts -+++ b/src/validation/json-logic.ts -@@ -165,7 +165,7 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu - delete propertySchema['x-jsf-logic-computedAttrs'] - } - -- // If this is a full property schema, we need to cycle through the properties and apply the computed values -+ // If the schemas has properties, we need to cycle through each one and apply the computed values - // Otherwise, just process the property - if (schemaCopy.properties) { - for (const propertyName in schemaCopy.properties) { -@@ -175,6 +175,27 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu - else { - processProperty(schemaCopy) - } -+ -+ // If the schema has an if statement, we need to cycle through the properties and apply the computed values -+ if (schemaCopy.if) { -+ cycleThroughPropertiesAndApplyValues(schemaCopy.if as JsfObjectSchema, computedValues) -+ } -+ -+ /* If the schema has an allOf or anyOf property, we need to cycle through each property inside it and -+ * apply the computed values -+ */ -+ -+ if (schemaCopy.allOf && schemaCopy.allOf.length > 0) { -+ for (const schema of schemaCopy.allOf) { -+ cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) -+ } -+ } -+ -+ if (schemaCopy.anyOf && schemaCopy.anyOf.length > 0) { -+ for (const schema of schemaCopy.anyOf) { -+ cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) -+ } -+ } - } - - /** -diff --git a/test/validation/json-logic.test.ts b/test/validation/json-logic.test.ts -index 69b883e..b8b8528 100644 ---- a/test/validation/json-logic.test.ts -+++ b/test/validation/json-logic.test.ts -@@ -464,4 +464,76 @@ describe('applyComputedAttrsToSchema', () => { - const ageProperties = result.properties?.age as JsfObjectSchema - expect(ageProperties?.minimum).toBe(21) - }) -+ -+ it('allows to use computed values inside conditional statements', () => { -+ const schema: JsfObjectSchema = { -+ 'properties': { -+ pine_trees: { -+ title: 'Pine trees planted', -+ type: 'number', -+ description: 'The number of pine trees you have planted.', -+ }, -+ oak_trees: { -+ type: 'number', -+ title: 'Oak trees planted', -+ description: 'Enter the number of oak trees you\'ve planted. If there are more pine trees than oak trees, you\'ll need to plant spruce trees as well. But this only counts if less than 10 pines planted.', -+ }, -+ spruce_trees: { -+ title: 'Spruce trees planted', -+ type: 'number', -+ description: 'The number of spruce trees you have planted (only required if specific conditions are met).', -+ }, -+ }, -+ 'allOf': [ -+ { -+ if: { -+ properties: { -+ oak_trees: { -+ 'x-jsf-logic-computedAttrs': { -+ minimum: 'pine_value', -+ }, -+ }, -+ }, -+ }, -+ then: { -+ required: [ -+ 'spruce_trees', -+ ], -+ }, -+ else: { -+ properties: { -+ spruce_trees: false, -+ }, -+ }, -+ }, -+ ], -+ 'required': [ -+ 'pine_trees', -+ 'oak_trees', -+ ], -+ 'x-jsf-logic': { -+ computedValues: { -+ pine_value: { -+ rule: { -+ '+': [ -+ { -+ var: 'pine_trees', -+ }, -+ 1, -+ ], -+ }, -+ }, -+ }, -+ }, -+ }; -+ -+ // Mock the jsonLogic.apply to return 10 -+ (jsonLogic.apply as jest.Mock).mockReturnValue(10) -+ -+ const result = JsonLogicValidation.applyComputedAttrsToSchema(schema, schema['x-jsf-logic']?.computedValues, { pine_trees: 10, oak_trees: 2 }) -+ -+ const conditionValue = result.allOf?.[0].if?.properties?.oak_trees as JsfObjectSchema -+ expect(conditionValue['x-jsf-logic-computedAttrs']).toBeUndefined() -+ expect(conditionValue.minimum).toBe(10) -+ }) - }) From 9ee46ad58af51c785ca0d76430f4894a9c4a4e8a Mon Sep 17 00:00:00 2001 From: Capelo Date: Mon, 21 Jul 2025 10:13:48 +0100 Subject: [PATCH 07/10] chore: clean up outdated test file --- test/form.test.ts | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/test/form.test.ts b/test/form.test.ts index 4240dc3ba..dcbcd5aab 100644 --- a/test/form.test.ts +++ b/test/form.test.ts @@ -1,5 +1,5 @@ import type { JsfObjectSchema } from '../src/types' -import { describe, expect, it, jest } from '@jest/globals' +import { afterEach, describe, expect, it, jest } from '@jest/globals' import { createHeadlessForm } from '../src' describe('createHeadlessForm', () => { @@ -14,28 +14,31 @@ describe('createHeadlessForm', () => { name: { type: 'string' }, }, } + afterEach(() => { + jest.clearAllMocks() + }) - it('should throw error when customProperties option is provided', () => { + it('should log error when customProperties option is provided', () => { // spy on console.error const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) createHeadlessForm(basicSchema, { customProperties: {} } as any) expect(consoleErrorSpy).toHaveBeenCalledWith('[json-schema-form] `customProperties` is a deprecated option and it\'s not supported on json-schema-form v1') }) - it('should not throw error when modifyConfig option is not provided', () => { - expect(() => { - createHeadlessForm(basicSchema, {}) - }).not.toThrow() + it('should not log error when modifyConfig option is not provided', () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + createHeadlessForm(basicSchema, {}) + expect(consoleErrorSpy).not.toHaveBeenCalled() }) - it('should not throw error when other valid options are provided', () => { - expect(() => { - createHeadlessForm(basicSchema, { - initialValues: { name: 'test' }, - legacyOptions: {}, - strictInputType: true, - }) - }).not.toThrow() + it('should not log error when other valid options are provided', () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}) + createHeadlessForm(basicSchema, { + initialValues: { name: 'test' }, + legacyOptions: {}, + strictInputType: true, + }) + expect(consoleErrorSpy).not.toHaveBeenCalled() }) }) }) From 086b4add48b4fc86d12e78ea5de6dc62a2009040 Mon Sep 17 00:00:00 2001 From: Capelo Date: Tue, 22 Jul 2025 11:20:24 +0100 Subject: [PATCH 08/10] chore: improve tsdoc --- src/types.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/types.ts b/src/types.ts index 22fc75095..817f9a549 100644 --- a/src/types.ts +++ b/src/types.ts @@ -68,16 +68,17 @@ export type JsfSchema = JSONSchema & { // schema (like an if inside another schema), the required property won't be // present in the type 'required'?: string[] - /* Defines the order of the fields in the form. */ + /** Defines the order of the fields in the form. */ 'x-jsf-order'?: string[] - /* Defines the presentation of the field in the form. */ + /** Defines the presentation of the field in the form. */ 'x-jsf-presentation'?: JsfPresentation - /* Defines the error message of the field in the form. */ + /** Defines the error message of the field in the form. */ 'x-jsf-errorMessage'?: Record + /** Defines all JSON Logic rules for the schema (both validations and computed values). */ 'x-jsf-logic'?: JsonLogicSchema - /* Extra validations to run. References validations in the `x-jsf-logic` root property. */ + /** Extra validations to run. References validations declared in the `x-jsf-logic` root property. */ 'x-jsf-logic-validations'?: string[] - /* Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ + /** Extra attributes to add to the schema. References computedValues in the `x-jsf-logic` root property. */ 'x-jsf-logic-computedAttrs'?: Record } From f68cd25eb2ce274277650a60ef3034285de91cf8 Mon Sep 17 00:00:00 2001 From: Capelo Date: Tue, 22 Jul 2025 12:07:53 +0100 Subject: [PATCH 09/10] chore: simplify types and removes castings --- src/validation/json-logic.ts | 40 +++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts index 5f1527848..0daea0b2b 100644 --- a/src/validation/json-logic.ts +++ b/src/validation/json-logic.ts @@ -150,8 +150,13 @@ export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValu * @param schemaCopy - The schema to apply computed values to * @param computedValues - The computed values to apply */ -function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, computedValues: Record) { - function processProperty(propertySchema: JsfObjectSchema) { +function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfSchema, computedValues: Record) { + function processProperty(propertySchema: JsfSchema) { + // Checking that the schema is non-boolean and is has a type property before processing it + if (typeof propertySchema === 'boolean' || typeof propertySchema.type === 'undefined') { + return + } + const computedAttrs = propertySchema['x-jsf-logic-computedAttrs'] if (computedAttrs) { cycleThroughAttrsAndApplyValues(propertySchema, computedValues, computedAttrs) @@ -169,7 +174,7 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu // Otherwise, just process the property if (schemaCopy.properties) { for (const propertyName in schemaCopy.properties) { - processProperty(schemaCopy.properties[propertyName] as JsfObjectSchema) + processProperty(schemaCopy.properties[propertyName]) } } else { @@ -177,8 +182,8 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu } // If the schema has an if statement, we need to cycle through the properties and apply the computed values - if (schemaCopy.if) { - cycleThroughPropertiesAndApplyValues(schemaCopy.if as JsfObjectSchema, computedValues) + if (typeof schemaCopy.if === 'object') { + cycleThroughPropertiesAndApplyValues(schemaCopy.if, computedValues) } /* If the schema has an allOf or anyOf property, we need to cycle through each property inside it and @@ -187,13 +192,19 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu if (schemaCopy.allOf && schemaCopy.allOf.length > 0) { for (const schema of schemaCopy.allOf) { - cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) + cycleThroughPropertiesAndApplyValues(schema, computedValues) } } if (schemaCopy.anyOf && schemaCopy.anyOf.length > 0) { for (const schema of schemaCopy.anyOf) { - cycleThroughPropertiesAndApplyValues(schema as JsfObjectSchema, computedValues) + cycleThroughPropertiesAndApplyValues(schema, computedValues) + } + } + + if (schemaCopy.oneOf && schemaCopy.oneOf.length > 0) { + for (const schema of schemaCopy.oneOf) { + cycleThroughPropertiesAndApplyValues(schema, computedValues) } } } @@ -203,7 +214,11 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfObjectSchema, compu * @param propertySchema - The schema to apply computed values to * @param computedValues - The computed values to apply */ -function cycleThroughAttrsAndApplyValues(propertySchema: JsfObjectSchema, computedValues: Record, computedAttrs: JsfSchema['x-jsf-logic-computedAttrs']) { +function cycleThroughAttrsAndApplyValues(propertySchema: JsfSchema, computedValues: Record, computedAttrs: JsfSchema['x-jsf-logic-computedAttrs']) { + if (typeof propertySchema !== 'object' || propertySchema.type !== 'object') { + return + } + /** * Evaluates a string or a handlebars template, using the computed values mapping, and returns the computed value * @param message - The string or template to evaluate @@ -229,7 +244,12 @@ function cycleThroughAttrsAndApplyValues(propertySchema: JsfObjectSchema, comput * @param computationName - The name of the computed value to apply * @param computedValues - The computed values to apply */ - function applyNestedComputedValues(propertySchema: JsfObjectSchema, attrName: string, computationName: string | object, computedValues: Record) { + function applyNestedComputedValues(propertySchema: JsfSchema, attrName: string, computationName: string | object, computedValues: Record) { + // Checking that the schema is non-boolean and is has a type property before processing it + if (typeof propertySchema !== 'object' || typeof propertySchema.type === 'undefined') { + return + } + const attributeName = attrName as keyof NonBooleanJsfSchema if (!propertySchema[attributeName]) { // Making sure the attribute object is created if it does not exist in the original schema @@ -253,7 +273,7 @@ function cycleThroughAttrsAndApplyValues(propertySchema: JsfObjectSchema, comput if (typeof computationName === 'string') { propertySchema[attributeName] = evalStringOrTemplate(computationName) } - else { + else if (propertySchema.type === 'object' && typeof propertySchema !== 'boolean') { // Otherwise, it's a nested object, so we need to apply the computed values to the nested object applyNestedComputedValues(propertySchema, attributeName, computationName, computedValues) } From 01f2faeaad6535ddf18d278c32839bd0c6dfb120 Mon Sep 17 00:00:00 2001 From: Capelo Date: Tue, 22 Jul 2025 15:33:03 +0100 Subject: [PATCH 10/10] chore: remove unnecessary checks --- src/validation/json-logic.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/validation/json-logic.ts b/src/validation/json-logic.ts index 0daea0b2b..1530f1f8d 100644 --- a/src/validation/json-logic.ts +++ b/src/validation/json-logic.ts @@ -153,7 +153,7 @@ export function applyComputedAttrsToSchema(schema: JsfObjectSchema, computedValu function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfSchema, computedValues: Record) { function processProperty(propertySchema: JsfSchema) { // Checking that the schema is non-boolean and is has a type property before processing it - if (typeof propertySchema === 'boolean' || typeof propertySchema.type === 'undefined') { + if (typeof propertySchema !== 'object') { return } @@ -215,7 +215,7 @@ function cycleThroughPropertiesAndApplyValues(schemaCopy: JsfSchema, computedVal * @param computedValues - The computed values to apply */ function cycleThroughAttrsAndApplyValues(propertySchema: JsfSchema, computedValues: Record, computedAttrs: JsfSchema['x-jsf-logic-computedAttrs']) { - if (typeof propertySchema !== 'object' || propertySchema.type !== 'object') { + if (typeof propertySchema !== 'object') { return } @@ -246,7 +246,7 @@ function cycleThroughAttrsAndApplyValues(propertySchema: JsfSchema, computedValu */ function applyNestedComputedValues(propertySchema: JsfSchema, attrName: string, computationName: string | object, computedValues: Record) { // Checking that the schema is non-boolean and is has a type property before processing it - if (typeof propertySchema !== 'object' || typeof propertySchema.type === 'undefined') { + if (typeof propertySchema !== 'object') { return } @@ -273,7 +273,7 @@ function cycleThroughAttrsAndApplyValues(propertySchema: JsfSchema, computedValu if (typeof computationName === 'string') { propertySchema[attributeName] = evalStringOrTemplate(computationName) } - else if (propertySchema.type === 'object' && typeof propertySchema !== 'boolean') { + else if (typeof propertySchema === 'object') { // Otherwise, it's a nested object, so we need to apply the computed values to the nested object applyNestedComputedValues(propertySchema, attributeName, computationName, computedValues) }