xml-toolkit 1.1.5 → 1.1.6

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/README.md CHANGED
@@ -30,6 +30,8 @@ const parser = new XMLParser ({
30
30
 
31
31
  const document = parser.process (xml)
32
32
 
33
+ // console.log (parser.validationMessages)
34
+
33
35
  for (const element of document.detach ().children) {
34
36
  console.log (element.attributes)
35
37
  }
@@ -46,7 +48,9 @@ const records = new XMLReader ({
46
48
  //xs,
47
49
  filterElements : 'Record',
48
50
  map : XMLNode.toObject ({})
49
- }).process (xmlSource)
51
+ })
52
+ //.on ('validation-message', s => console.log (s))
53
+ .process (xmlSource)
50
54
 
51
55
  // ...then:
52
56
  // await someLoader.load (records)
@@ -6,6 +6,9 @@ const VOCABULARY = new Map ([
6
6
  ['XML-00002', 'Unbalanced end element'],
7
7
  ['XML-00003', 'Unmatched end element, </${%s}> expected'],
8
8
 
9
+ ['XSD-00001', 'Unknown namespace: %s'],
10
+ ['XSD-00002', 'The element %s is not found in %s'],
11
+
9
12
  ['XVC-00001', 'No nested elements allowed inside %s'],
10
13
  ['XVC-00002', 'is unexpected here; should be %s'],
11
14
  ['XVC-00003', 'Unknown attribute: %s'],
@@ -28,7 +31,7 @@ const VOCABULARY = new Map ([
28
31
  ['XVS-00014', `'%s' is not a valid decimal: '%s' can occur only at the beginning`],
29
32
  ['XVS-00015', `'%s' is not a valid decimal: 2nd period occured at position %i`],
30
33
  ['XVS-00016', `'%s' is not a valid decimal: '%s' occured at position %i`],
31
- ['XVS-00017', `'%s' is not a valid decimal: is has no digits at all`],
34
+ ['XVS-00017', `'%s' is not a valid decimal: it has no digits at all`],
32
35
  ['XVS-00018', `'%s' has %i digits, only %i allowed`],
33
36
  ['XVS-00019', `'%s' has %i digits after period, only %i allowed`],
34
37
  ['XVS-00020', `No floating point number can be empty`],
@@ -37,6 +40,22 @@ const VOCABULARY = new Map ([
37
40
  ['XVS-00023', `For a dateTime value, the time part is mandatory, missing from '%s'`],
38
41
  ['XVS-00024', `For a dateTimeStamp value, both time and timezone parts are mandatory, missing from '%s'`],
39
42
  ['XVS-00025', `The year separator is not found in '%s'`],
43
+ ['XVS-00026', `'%s': '%s' is not a valid year, must be 4 chars long`],
44
+ ['XVS-00027', `'%s': The character at %i must be '-', not '%s'`],
45
+ ['XVS-00028', `'%s': The day part must be 2 chars long, found '%s'`],
46
+ ['XVS-00029', `'%s': Invalid time part: the character at position %i must be ':', not '%s'`],
47
+ ['XVS-00030', `'%s': '%s' is not a valid year: '%s' at position %i`],
48
+ ['XVS-00031', `'%s': Invalid month '%s'`],
49
+ ['XVS-00032', `'%s': Invalid day '%s'`],
50
+ ['XVS-00033', `'%s': Non existing day`],
51
+ ['XVS-00034', `'%s': Invalid hour '%s'`],
52
+ ['XVS-00035', `'%s': Invalid minute '%s'`],
53
+ ['XVS-00036', `'%s': Invalid second '%s'`],
54
+ ['XVS-00037', `'%s': Invalid TZ hour '%s'`],
55
+ ['XVS-00038', `'%s': Invalid TZ minute '%s'`],
56
+ ['XVS-00039', `'%s': Unless 'Z', the timezone must start with either '+' or '-', not '%s'`],
57
+ ['XVS-00040', `'%s': Invalid timezone length '%i'`],
58
+ ['XVS-00041', `'%s': Invalid timezone: ':' not found at position 3`],
40
59
 
41
60
  ])
42
61
 
@@ -46,9 +65,21 @@ class XMLMessages {
46
65
 
47
66
  static format (args) {
48
67
 
49
- args [0] = VOCABULARY.get (args [0])
68
+ const code = args [0]
69
+
70
+ args [0] = VOCABULARY.get (code)
71
+
72
+ return code + ' ' + util.format.apply (util, args)
73
+
74
+ }
75
+
76
+ static raise (args) {
77
+
78
+ const error = Error (XMLMessages.format (args))
79
+
80
+ error.args = args
50
81
 
51
- return util.format.apply (util, args)
82
+ throw error
52
83
 
53
84
  }
54
85
 
package/lib/XMLParser.js CHANGED
@@ -48,6 +48,7 @@ const XMLParser = class {
48
48
  this.text = ''
49
49
  this.document = null
50
50
  this.element = null
51
+ this.validationMessages = []
51
52
 
52
53
  const {entityResolver} = this, nodes = new XMLIterator (src, {entityResolver})
53
54
 
@@ -67,9 +68,7 @@ const XMLParser = class {
67
68
 
68
69
  if (this.stripSpace) this.text = this.text.trim ()
69
70
  if (this.text.length === 0) break
70
- if (this.validator) {
71
- this.validator.characters (this.text)
72
- }
71
+ if (this.validator) {this.validator.characters (this.text)}
73
72
  (new XMLNode (this.text, null, SAXEvent.TYPES.CHARACTERS)).parent = this.element
74
73
  this.text = ''
75
74
 
@@ -84,10 +83,17 @@ const XMLParser = class {
84
83
  this.element = node
85
84
  if (this.document === null) {
86
85
  this.document = node
87
- if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, nodes.position)
86
+ if (this.xs !== null) {
87
+ try {
88
+ this.validator = new XMLValidatior (this.xs, node, nodes.position, message => this.validationMessages.push (message))
89
+ }
90
+ catch (err) {
91
+ this.validationMessages.push (nodes.position + err.message)
92
+ }
93
+ }
88
94
  }
89
95
  else {
90
- if (this.xs !== null) this.validator.startElement (node)
96
+ if (this.validator) this.validator.startElement (node)
91
97
  }
92
98
  break
93
99
 
@@ -98,7 +104,7 @@ const XMLParser = class {
98
104
  node ['_ns_map'] = this.element ['_ns_map']
99
105
  this.element.type = type
100
106
  this.element = this.element.parent
101
- if (this.xs !== null) this.validator.endElement (node)
107
+ if (this.validator) this.validator.endElement (node)
102
108
  break
103
109
 
104
110
  }
package/lib/XMLReader.js CHANGED
@@ -147,13 +147,20 @@ const XMLReader = class extends Transform {
147
147
  if (!this.validator) switch (xmlNode.type) {
148
148
 
149
149
  case SAXEvent.TYPES.START_ELEMENT:
150
- this.validator = new XMLValidatior (this.xs, xmlNode, this.lex.position)
150
+ try {
151
+ this.validator = new XMLValidatior (this.xs, xmlNode, this.lex.position, s => this.emit ('validation-message', s))
152
+ }
153
+ catch (err) {
154
+ this.emit ('validation-message', this.lex.position + err.message)
155
+ }
151
156
 
152
157
  default:
153
158
  return
154
159
 
155
160
  }
156
161
 
162
+ // if (!this.validator) return
163
+
157
164
  switch (xmlNode.type) {
158
165
 
159
166
  case SAXEvent.TYPES.START_ELEMENT : return this.validator.startElement (xmlNode)
@@ -170,17 +177,17 @@ const XMLReader = class extends Transform {
170
177
 
171
178
  if (type !== null) xmlNode.type = type
172
179
 
173
- if (this.xs) try {
180
+ if (this.xs) /*try {*/
174
181
 
175
182
  this.validate (xmlNode)
176
-
183
+ /*
177
184
  }
178
185
  catch (error) {
179
186
 
180
187
  this.destroy (error)
181
188
 
182
189
  }
183
-
190
+ */
184
191
  const {filter} = this; if (filter !== null) {
185
192
 
186
193
  if (!filter (xmlNode)) return
package/lib/XMLSchema.js CHANGED
@@ -1,3 +1,4 @@
1
+ const XMLMessages = require ('./XMLMessages.js')
1
2
  const TYPES = Symbol ('_types')
2
3
 
3
4
  const FORM_U = 'unqualified'
@@ -91,7 +92,7 @@ const XMLSchema = class extends Map {
91
92
 
92
93
  const el = this.get (localName)
93
94
 
94
- if (!el || el.localName !== 'element') throw Error (`The element ${localName} is not found in ${this.targetNamespace}`)
95
+ if (!el || el.localName !== 'element') XMLMessages.raise (['XSD-00002', localName, this.targetNamespace])
95
96
 
96
97
  return el
97
98
 
@@ -168,7 +168,7 @@ const XMLSchemata = class extends Map {
168
168
 
169
169
  getSchema (namespaceURI) {
170
170
 
171
- if (!this.has (namespaceURI)) throw new Error ('Unknown namespace: ' + namespaceURI)
171
+ if (!this.has (namespaceURI)) XMLMessages.raise (['XSD-00001', namespaceURI])
172
172
 
173
173
  return this.get (namespaceURI)
174
174
 
package/lib/simple/DT7.js CHANGED
@@ -9,44 +9,44 @@ const CH_9 = '9'.charCodeAt (0)
9
9
 
10
10
  const tzCache = new Set (['Z', '+14:00', '-14:00'])
11
11
 
12
- const die = payload => {
13
-
14
- const err = Error ('DT7 error')
15
-
16
- err.payload = payload
17
-
18
- throw err
19
-
20
- }
21
-
22
12
  class DT7 {
23
13
 
24
14
  constructor (src) {
25
15
 
26
16
  const t = typeof src; if (t !== 'string') throw Error (`The Seven-property Date Time Model can only be constructed from a string, not a(n) ${t}`)
27
17
 
28
- const {length} = src; if (length < MIN_LENGTH) die (['XVS-00005', src, length, MIN_LENGTH])
29
-
30
18
  this.src = src
31
19
 
20
+ const {length} = src; if (length < MIN_LENGTH) this.raise ('XVS-00005', length, MIN_LENGTH)
21
+
32
22
  this.parse ()
33
23
  this.validate ()
34
24
 
35
25
  }
36
26
 
27
+ raise (code, ...args) {
28
+
29
+ const err = Error ('DT7 error')
30
+
31
+ err.payload = [code, this.src, ...args]
32
+
33
+ throw err
34
+
35
+ }
36
+
37
37
  parse () {
38
38
 
39
39
  this.posMonth = this.src.indexOf ('-', 1)
40
40
 
41
- if (this.posMonth === -1) die (['XVS-00025', this.src])
42
-
43
- if (this.posMonth < 3) throw Error (`'${this.year}' is not a valid year: cannot be shorter than 4 characters`)
41
+ if (this.posMonth === -1) this.raise ('XVS-00025')
44
42
 
45
- if (this.src.charCodeAt (this.posDay) !== CH_MINUS) throw Error (`The character at ${this.posDay} must be '-', not '${this.src.charAt (this.posDay)}'`)
43
+ if (this.posMonth < 3) this.raise ('XVS-00026', this.year)
44
+
45
+ if (this.src.charCodeAt (this.posDay) !== CH_MINUS) this.raise ('XVS-00027', this.posDay, this.src.charAt (this.posDay))
46
46
 
47
47
  const {length} = this.src
48
48
 
49
- if (length < this.endOfDate) throw Error (`The day part must be 2 chars long, found '${this.day}'`)
49
+ if (length < this.endOfDate) this.raise ('XVS-00028', this.day)
50
50
 
51
51
  if (length !== (this.endOfDateTime = this.endOfDate)) this.parseAfterDate ()
52
52
 
@@ -56,12 +56,8 @@ class DT7 {
56
56
 
57
57
  if (this.src.charCodeAt (this.endOfDate) !== CH_T) return
58
58
 
59
- for (const pos of [this.endOfHour, this.endOfMinute])
60
-
61
- if (this.src.charCodeAt (pos) !== CH_COLON)
59
+ for (const pos of [this.endOfHour, this.endOfMinute]) if (this.src.charCodeAt (pos) !== CH_COLON) this.raise ('XVS-00029', pos, this.src.charAt (pos))
62
60
 
63
- throw Error (`Invalid time part: the character at position ${pos} must be ':', not '${this.src.charAt (pos)}'`)
64
-
65
61
  this.hasTime = true
66
62
 
67
63
  this.parseAfterMinute ()
@@ -174,13 +170,13 @@ class DT7 {
174
170
 
175
171
  const {year} = this, {length} = year, base = year.charCodeAt (0) === CH_MINUS ? 1 : 0
176
172
 
177
- if (year.charCodeAt (base) === CH_0 && length - base !== 4) throw Error (`Starting with 0, the year must have exactly 4 digits`)
173
+ if (year.charCodeAt (base) === CH_0 && length - base !== 4) this.raise ('XVS-00026', year)
178
174
 
179
175
  for (let i = base; i < length; i ++) {
180
176
 
181
177
  const c = year.charCodeAt (i)
182
178
 
183
- if (c > CH_9 || c < CH_0) throw Error (`Invalid year: '${year.charAt (i)} at position ${i}'`)
179
+ if (c > CH_9 || c < CH_0) this.raise ('XVS-00030', year, year.charAt (i), i)
184
180
 
185
181
  }
186
182
 
@@ -190,7 +186,13 @@ class DT7 {
190
186
 
191
187
  const {month} = this
192
188
 
193
- if (month < '01' || month > '12') throw Error (`Invalid month`)
189
+ if (month < '01' || month > '12') this.raise ('XVS-00031', month)
190
+
191
+ }
192
+
193
+ validate2digits (value, code, max = '59', min = '00') {
194
+
195
+ if (value < min || value > max) this.raise (code, value)
194
196
 
195
197
  }
196
198
 
@@ -198,11 +200,11 @@ class DT7 {
198
200
 
199
201
  const {day} = this
200
202
 
201
- if (day < '01' || day > '31') throw Error (`Invalid day`)
203
+ this.validate2digits (day, 'XVS-00032', '31', '01')
202
204
 
203
205
  if (day === '31' || (this.month === '02' && day > '28')) {
204
206
 
205
- if (new Date (this.src.substring (0, this.endOfDate)).getDate () != day) throw Error (`Non existing day`)
207
+ if (new Date (this.src.substring (0, this.endOfDate)).getDate () != day) this.raise ('XVS-00033')
206
208
 
207
209
  }
208
210
 
@@ -210,14 +212,12 @@ class DT7 {
210
212
 
211
213
  validateTime () {
212
214
 
213
- const {hour, minute, second} = this
214
-
215
- if (hour < '00' || hour > '23') throw Error (`Invalid hour`)
216
- if (minute < '00' || minute > '59') throw Error (`Invalid minute`)
215
+ this.validate2digits (this.hour, 'XVS-00034', '23')
216
+ this.validate2digits (this.minute, 'XVS-00035')
217
217
 
218
- const intSecond = second.length === 2 ? second : second.substring (0, 2)
218
+ const {second} = this, intSecond = second.length === 2 ? second : second.substring (0, 2)
219
219
 
220
- if (intSecond < '00' || intSecond > '59') throw Error (`Invalid second`)
220
+ this.validate2digits (intSecond, 'XVS-00036')
221
221
 
222
222
  }
223
223
 
@@ -230,24 +230,15 @@ class DT7 {
230
230
  case CH_MINUS:
231
231
  break
232
232
  default:
233
- throw Error (`Unless 'Z', the timezone must start with either '+' or '-', not ${tz.charAt (0)}`)
233
+ this.raise ('XVS-00039', tz.charAt (0))
234
234
  }
235
235
 
236
- if (tz.length !== 6) throw Error (`Invalid timezone length: ${tz.length}`)
236
+ if (tz.length !== 6) this.raise ('XVS-00040', tz.length)
237
237
 
238
- if (tz.charCodeAt (3) !== CH_COLON) throw Error (`Invalid timezone: ':' not found at position 3`)
238
+ if (tz.charCodeAt (3) !== CH_COLON) this.raise ('XVS-00041')
239
239
 
240
- {
241
-
242
- const hh = tz.substring (1, 3); if (hh < '00' || hh > '13') throw Error (`Invalid TZ hour: ${hh}`)
243
-
244
- }
245
-
246
- {
247
-
248
- const mm = tz.substring (4, 6); if (mm < '00' || mm > '59') throw Error (`Invalid TZ minute: ${mm}`)
249
-
250
- }
240
+ this.validate2digits (tz.substring (1, 3), 'XVS-00037', '13')
241
+ this.validate2digits (tz.substring (4, 6), 'XVS-00038')
251
242
 
252
243
  tzCache.add (tz)
253
244
 
@@ -64,9 +64,20 @@ class XSSimpleTypeDateTime extends XSSimpleTypeDT {
64
64
 
65
65
  testFormat (s) {
66
66
 
67
- const dt7 = new DT7 (s)
67
+ try {
68
+
69
+ const dt7 = new DT7 (s)
70
+
71
+ if (dt7.hour === undefined) return ['XVS-00023', s]
72
+
73
+ }
74
+ catch (err) {
75
+
76
+ if ('payload' in err) return err.payload
68
77
 
69
- if (dt7.hour === undefined) return ['XVS-00023', s]
78
+ throw err
79
+
80
+ }
70
81
 
71
82
  }
72
83
 
@@ -86,9 +97,20 @@ class XSSimpleTypeDateTimeStamp extends XSSimpleTypeDateTime {
86
97
 
87
98
  testFormat (s) {
88
99
 
89
- const dt7 = new DT7 (s)
100
+ try {
101
+
102
+ const dt7 = new DT7 (s)
103
+
104
+ if (dt7.hour === undefined || dt7.tz === undefined) return ['XVS-00024', s]
105
+
106
+ }
107
+ catch (err) {
108
+
109
+ if ('payload' in err) return err.payload
90
110
 
91
- if (dt7.hour === undefined || dt7.tz === undefined) return ['XVS-00024', s]
111
+ throw err
112
+
113
+ }
92
114
 
93
115
  }
94
116
 
@@ -8,53 +8,36 @@ class XMLValidationState {
8
8
 
9
9
  constructor (parent, parsedNode, elementDefinition) {
10
10
 
11
- this.xs = (this.parent = parent).xs
11
+ const xs = this.xs = (this.parent = parent).xs
12
12
  this.parsedNode = parsedNode
13
-
14
- try {
15
13
 
16
- this.setTypeFrom (elementDefinition)
14
+ const typeDefinition = xs.getTypeDefinition (elementDefinition)
17
15
 
18
- }
19
- catch (cause) {
16
+ this.anyType = xs.getAnyType (typeDefinition)
20
17
 
21
- this.warn (cause.message)
18
+ this.validateAttributes (this.parsedNode.attributes)
22
19
 
23
- }
20
+ const {content} = this.anyType; if (content) this.match = content.createMatch ()
24
21
 
25
22
  }
26
23
 
27
24
  warn (message, cause) {
28
25
 
29
- this.parent.warn (message, cause)
30
-
31
- }
32
-
33
- setTypeFrom (element) {
34
-
35
- const {xs} = this
36
-
37
- const typeDefinition = xs.getTypeDefinition (element)
38
-
39
- this.anyType = xs.getAnyType (typeDefinition)
40
-
41
- this.validateAttributes (this.parsedNode.attributes)
42
-
43
- const {content} = this.anyType; if (content) this.match = content.createMatch ()
26
+ return this.parent.warn (message, cause)
44
27
 
45
28
  }
46
29
 
47
30
  blameNothingExpected () {
48
31
 
49
- this.warn (['XVC-00001', this.parsedNode.src])
32
+ return this.warn (['XVC-00001', this.parsedNode.src])
50
33
 
51
34
  }
52
35
 
53
36
  blameUnexpectedTag (parsedNode, match) {
54
37
 
55
- const expected = match.allExpected (parsedNode); if (expected == null) this.blameNothingExpected ()
38
+ const expected = match.allExpected (parsedNode); if (expected == null) return this.blameNothingExpected ()
56
39
 
57
- this.warn (['XVC-00002', expected])
40
+ return this.warn (['XVC-00002', expected])
58
41
 
59
42
  }
60
43
 
@@ -62,9 +45,9 @@ class XMLValidationState {
62
45
 
63
46
  if (this.#child !== null) return this.#child.startElement (parsedNode)
64
47
 
65
- const {match} = this; if (!match) this.warn (['XVC-00001', this.anyType.debugQName])
48
+ const {match} = this; if (!match) return this.warn (['XVC-00001', this.anyType.debugQName])
66
49
 
67
- const element = match.getElementDefinition (parsedNode); if (!element) this.blameUnexpectedTag (parsedNode, match)
50
+ const element = match.getElementDefinition (parsedNode); if (!element) return this.blameUnexpectedTag (parsedNode, match)
68
51
 
69
52
  if (element.localName === 'any') return
70
53
 
@@ -78,7 +61,7 @@ class XMLValidationState {
78
61
 
79
62
  if (!this.#hadText) this.characters ('')
80
63
 
81
- const {match} = this; if (match && !match.isSatisfied) this.blameUnexpectedTag (parsedNode, match)
64
+ const {match} = this; if (match && !match.isSatisfied) return this.blameUnexpectedTag (parsedNode, match)
82
65
 
83
66
  return null
84
67
 
@@ -146,7 +129,7 @@ class XMLValidationState {
146
129
 
147
130
  if (attributesMap.getNamespaceURI (name) === XSINamespace) continue
148
131
 
149
- this.warn (['XVC-00003', name])
132
+ return this.warn (['XVC-00003', name])
150
133
 
151
134
  }
152
135
 
@@ -154,7 +137,7 @@ class XMLValidationState {
154
137
 
155
138
  const {attributes: {fixed, type}} = def
156
139
 
157
- if (typeof fixed === 'string' && value !== fixed) this.warn (['XVC-00004', name, fixed, value])
140
+ if (typeof fixed === 'string' && value !== fixed) return this.warn (['XVC-00004', name, fixed, value])
158
141
 
159
142
  if (type) {
160
143
 
@@ -173,7 +156,7 @@ class XMLValidationState {
173
156
 
174
157
  for (const [name, def] of attributes) if (!matched.has (def)) {
175
158
 
176
- if (def.attributes.use === 'required') this.warn (['XVC-00005', name])
159
+ if (def.attributes.use === 'required') return this.warn (['XVC-00005', name])
177
160
 
178
161
  if ('default' in def.attributes) attributesMap.set (name, def.attributes.default)
179
162
 
@@ -4,12 +4,13 @@ class XMLValidatior {
4
4
 
5
5
  #state = null
6
6
 
7
- constructor (xs, rootNode, position) {
7
+ constructor (xs, rootNode, position, onMessage) {
8
8
 
9
9
  this.xs = xs
10
10
  this.position = position
11
11
 
12
- this.startElement (rootNode)
12
+ this.onMessage = onMessage
13
+ this.startElement (rootNode)
13
14
 
14
15
  }
15
16
 
@@ -17,7 +18,13 @@ class XMLValidatior {
17
18
 
18
19
  const {position} = this
19
20
 
20
- throw Error (Array.isArray (message) ? position.format (message) : this.position + message)
21
+ if (Array.isArray (message)) message = position.format (message)
22
+
23
+ this.onMessage (message)
24
+
25
+ return null
26
+
27
+ // throw Error (message)
21
28
 
22
29
  }
23
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [