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 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] === 'object') {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-json-stringify",
3
- "version": "7.1.4",
3
+ "version": "7.1.5",
4
4
  "description": "Stringify your JSON at max speed",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -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
+ })