fast-json-stringify 7.1.4 → 7.1.5
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.
- package/lib/validator.js +22 -1
- package/package.json +1 -1
- package/test/anyof.test.js +102 -0
package/lib/validator.js
CHANGED
|
@@ -6,6 +6,18 @@ const fastUri = require('fast-uri')
|
|
|
6
6
|
const ajvFormats = require('ajv-formats')
|
|
7
7
|
const clone = require('rfdc')({ proto: true })
|
|
8
8
|
|
|
9
|
+
// Keywords whose value is a map of <name> -> schema rather than a schema.
|
|
10
|
+
// Their keys are user-defined, so an entry named "propertyNames" is a
|
|
11
|
+
// regular schema and must still be converted.
|
|
12
|
+
const SCHEMA_MAP_KEYWORDS = new Set([
|
|
13
|
+
'properties',
|
|
14
|
+
'patternProperties',
|
|
15
|
+
'definitions',
|
|
16
|
+
'$defs',
|
|
17
|
+
'dependentSchemas',
|
|
18
|
+
'dependencies'
|
|
19
|
+
])
|
|
20
|
+
|
|
9
21
|
class Validator {
|
|
10
22
|
constructor (ajvOptions, inlineValidators) {
|
|
11
23
|
const codeOptions = inlineValidators
|
|
@@ -90,7 +102,16 @@ class Validator {
|
|
|
90
102
|
schema.type.push('object')
|
|
91
103
|
}
|
|
92
104
|
for (const property in schema) {
|
|
93
|
-
if (typeof schema[property]
|
|
105
|
+
if (typeof schema[property] !== 'object') continue
|
|
106
|
+
// propertyNames validates object keys, which are always strings:
|
|
107
|
+
// adding "object" + fjs_type there only triggers Ajv strictTypes warnings
|
|
108
|
+
if (property === 'propertyNames') continue
|
|
109
|
+
|
|
110
|
+
if (SCHEMA_MAP_KEYWORDS.has(property)) {
|
|
111
|
+
for (const key in schema[property]) {
|
|
112
|
+
this.convertSchemaToAjvFormat(schema[property][key])
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
94
115
|
this.convertSchemaToAjvFormat(schema[property])
|
|
95
116
|
}
|
|
96
117
|
}
|
package/package.json
CHANGED
package/test/anyof.test.js
CHANGED
|
@@ -852,3 +852,105 @@ test('invalid anyOf schema', (t) => {
|
|
|
852
852
|
t.assert.ok(err.message.includes('schema is invalid'))
|
|
853
853
|
}
|
|
854
854
|
})
|
|
855
|
+
|
|
856
|
+
test('anyOf branch with propertyNames does not emit Ajv strictTypes warnings', (t) => {
|
|
857
|
+
t.plan(7)
|
|
858
|
+
|
|
859
|
+
const warnMock = t.mock.method(console, 'warn')
|
|
860
|
+
|
|
861
|
+
const stringify = build({
|
|
862
|
+
type: 'object',
|
|
863
|
+
definitions: {
|
|
864
|
+
KeySchema: { type: 'string', minLength: 1 }
|
|
865
|
+
},
|
|
866
|
+
properties: {
|
|
867
|
+
fields: {
|
|
868
|
+
anyOf: [
|
|
869
|
+
{ type: 'object', propertyNames: { type: 'string' }, additionalProperties: { type: 'string' } },
|
|
870
|
+
{ type: 'null' }
|
|
871
|
+
]
|
|
872
|
+
},
|
|
873
|
+
nestedCombinators: {
|
|
874
|
+
anyOf: [
|
|
875
|
+
{
|
|
876
|
+
type: 'object',
|
|
877
|
+
propertyNames: {
|
|
878
|
+
allOf: [{ type: 'string', minLength: 1 }, { $ref: '#/definitions/KeySchema' }],
|
|
879
|
+
anyOf: [{ type: 'string', pattern: '^[a-z]+$' }],
|
|
880
|
+
oneOf: [{ type: 'string' }],
|
|
881
|
+
not: { type: 'string', enum: ['forbidden'] }
|
|
882
|
+
},
|
|
883
|
+
additionalProperties: { type: 'string' }
|
|
884
|
+
},
|
|
885
|
+
{ type: 'null' }
|
|
886
|
+
]
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
})
|
|
890
|
+
|
|
891
|
+
t.assert.equal(stringify({ fields: { a: 'b' }, nestedCombinators: { abc: 'x' } }), '{"fields":{"a":"b"},"nestedCombinators":{"abc":"x"}}')
|
|
892
|
+
t.assert.equal(stringify({ fields: { a: new Date('2026-01-01T00:00:00.000Z') }, nestedCombinators: null }), '{"fields":{"a":"2026-01-01T00:00:00.000Z"},"nestedCombinators":null}')
|
|
893
|
+
t.assert.equal(stringify({ fields: null, nestedCombinators: null }), '{"fields":null,"nestedCombinators":null}')
|
|
894
|
+
t.assert.throws(() => stringify({ fields: { a: 'b' }, nestedCombinators: { forbidden: 'x' } }))
|
|
895
|
+
t.assert.throws(() => stringify({ fields: { a: 'b' }, nestedCombinators: { 123: 'x' } }))
|
|
896
|
+
t.assert.throws(() => stringify({ fields: 123, nestedCombinators: null }))
|
|
897
|
+
t.assert.equal(warnMock.mock.callCount(), 0)
|
|
898
|
+
})
|
|
899
|
+
|
|
900
|
+
test('anyOf branch with user-defined property named propertyNames preserves Date serialization', (t) => {
|
|
901
|
+
t.plan(3)
|
|
902
|
+
|
|
903
|
+
const warnMock = t.mock.method(console, 'warn')
|
|
904
|
+
|
|
905
|
+
const stringify = build({
|
|
906
|
+
type: 'object',
|
|
907
|
+
properties: {
|
|
908
|
+
data: {
|
|
909
|
+
anyOf: [
|
|
910
|
+
{
|
|
911
|
+
type: 'object',
|
|
912
|
+
properties: {
|
|
913
|
+
propertyNames: { type: 'string', format: 'date-time' }
|
|
914
|
+
},
|
|
915
|
+
required: ['propertyNames']
|
|
916
|
+
},
|
|
917
|
+
{ type: 'null' }
|
|
918
|
+
]
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
})
|
|
922
|
+
|
|
923
|
+
t.assert.equal(
|
|
924
|
+
stringify({ data: { propertyNames: new Date('2026-01-01T00:00:00.000Z') } }),
|
|
925
|
+
'{"data":{"propertyNames":"2026-01-01T00:00:00.000Z"}}'
|
|
926
|
+
)
|
|
927
|
+
t.assert.equal(stringify({ data: null }), '{"data":null}')
|
|
928
|
+
t.assert.equal(warnMock.mock.callCount(), 0)
|
|
929
|
+
})
|
|
930
|
+
|
|
931
|
+
test('$defs entry named propertyNames is still converted (Date serialization)', (t) => {
|
|
932
|
+
t.plan(2)
|
|
933
|
+
|
|
934
|
+
const warnMock = t.mock.method(console, 'warn')
|
|
935
|
+
|
|
936
|
+
const stringify = build({
|
|
937
|
+
type: 'object',
|
|
938
|
+
$defs: {
|
|
939
|
+
propertyNames: { type: 'string', format: 'date-time' }
|
|
940
|
+
},
|
|
941
|
+
properties: {
|
|
942
|
+
data: {
|
|
943
|
+
anyOf: [
|
|
944
|
+
{ $ref: '#/$defs/propertyNames' },
|
|
945
|
+
{ type: 'null' }
|
|
946
|
+
]
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
})
|
|
950
|
+
|
|
951
|
+
t.assert.equal(
|
|
952
|
+
stringify({ data: new Date('2026-01-01T00:00:00.000Z') }),
|
|
953
|
+
'{"data":"2026-01-01T00:00:00.000Z"}'
|
|
954
|
+
)
|
|
955
|
+
t.assert.equal(warnMock.mock.callCount(), 0)
|
|
956
|
+
})
|