xml-toolkit 1.1.1 → 1.1.3

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.
@@ -2,6 +2,7 @@ const CH_COLON = ':'.charCodeAt (0)
2
2
 
3
3
  const XMLNamespace = 'http://www.w3.org/XML/1998/namespace'
4
4
  const XMLNamespacePrefix = 'xml'
5
+ const XSINamespace = 'http://www.w3.org/2001/XMLSchema-instance'
5
6
 
6
7
  const DEFAULTS = [
7
8
  [XMLNamespacePrefix, XMLNamespace],
@@ -75,5 +76,6 @@ const NamespacesMap = class extends Map {
75
76
 
76
77
  NamespacesMap.XMLNamespace = XMLNamespace
77
78
  NamespacesMap.XMLNamespacePrefix = XMLNamespacePrefix
79
+ NamespacesMap.XSINamespace = XSINamespace
78
80
 
79
81
  module.exports = NamespacesMap
@@ -2,6 +2,8 @@ const XMLNode = require ('./XMLNode.js')
2
2
  const SAXEvent = require ('./SAXEvent.js')
3
3
  const EntityResolver = require ('./EntityResolver.js')
4
4
 
5
+ const CH_LF = '\n'.charCodeAt (0)
6
+
5
7
  const XMLIterator = class {
6
8
 
7
9
  constructor (src, options = {}) {
@@ -30,6 +32,26 @@ const XMLIterator = class {
30
32
 
31
33
  }
32
34
 
35
+ get linePos () {
36
+
37
+ const {pos} = this
38
+
39
+ let l = 1, c = -1
40
+
41
+ for (let i = pos; i >= 0; i --) {
42
+
43
+ if (this.src.charCodeAt (i) !== CH_LF) continue
44
+
45
+ l ++; if (c !== -1) continue
46
+
47
+ c = pos - i
48
+
49
+ }
50
+
51
+ return [l, c]
52
+
53
+ }
54
+
33
55
  next () {
34
56
 
35
57
  if (this.selfEnclosed !== null) return this.autoClose ()
package/lib/XMLParser.js CHANGED
@@ -41,7 +41,7 @@ const XMLParser = class {
41
41
 
42
42
  if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
43
43
 
44
- }
44
+ }
45
45
 
46
46
  process (src) {
47
47
 
@@ -84,7 +84,7 @@ const XMLParser = class {
84
84
  this.element = node
85
85
  if (this.document === null) {
86
86
  this.document = node
87
- if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () => BigInt (nodes.pos))
87
+ if (this.xs !== null) this.validator = new XMLValidatior (this.xs, node, () => nodes.linePos)
88
88
  }
89
89
  else {
90
90
  if (this.xs !== null) this.validator.startElement (node)
@@ -93,7 +93,7 @@ const XMLParser = class {
93
93
 
94
94
  case SAXEvent.TYPES.END_ELEMENT:
95
95
 
96
- if (this.element === null) throw new Error (`Unbalanced end element tag "${node.text}" occured at position ${nodes.pos}`)
96
+ if (this.element === null) throw new Error (`Unbalanced end element tag "${node.src}" occured at position ${nodes.pos}`)
97
97
  node ['_ns_map'] = this.element ['_ns_map']
98
98
  this.element.type = type
99
99
  this.element = this.element.parent
package/lib/XMLReader.js CHANGED
@@ -10,6 +10,8 @@ const OPT_SAX = Symbol ('_sax')
10
10
 
11
11
  const NO_CHILDREN = []; NO_CHILDREN.push = () => {}
12
12
 
13
+ const CH_LF = '\n'.charCodeAt (0)
14
+
13
15
  const XMLReader = class extends Transform {
14
16
 
15
17
  constructor (options = {}) {
@@ -68,12 +70,16 @@ const XMLReader = class extends Transform {
68
70
  this.useNamespaces = options.useNamespaces
69
71
  this.filter = filter || null
70
72
  this.map = map || null
73
+
74
+ this.lineNo = 1
75
+ this.lineStart = 0
71
76
 
72
77
  if (this.useEntities) this.entityResolver = new (require ('./EntityResolver.js')) ()
73
78
 
74
79
  this.text = ''
75
80
  this.element = null
76
- this.position = 0n
81
+ this.position = 0
82
+ this.pos = 0 // TODO: cleanup
77
83
 
78
84
  this [OPT_SRC] = null; this.on ('pipe', src => this [OPT_SRC] = src)
79
85
 
@@ -111,7 +117,7 @@ const XMLReader = class extends Transform {
111
117
 
112
118
  process (src, lexerOptions = {}) {
113
119
 
114
- const lex = new XMLLexer (lexerOptions)
120
+ const lex = this.lex = new XMLLexer (lexerOptions)
115
121
 
116
122
  lex.once ('error', x => this.destroy (x))
117
123
 
@@ -148,7 +154,7 @@ const XMLReader = class extends Transform {
148
154
  if (!this.validator) switch (xmlNode.type) {
149
155
 
150
156
  case SAXEvent.TYPES.START_ELEMENT:
151
- this.validator = new XMLValidatior (this.xs, xmlNode, () => this.position)
157
+ this.validator = new XMLValidatior (this.xs, xmlNode, () => this.linePos)
152
158
 
153
159
  default:
154
160
  return
@@ -244,10 +250,28 @@ const XMLReader = class extends Transform {
244
250
 
245
251
  }
246
252
 
253
+ get linePos () {
254
+
255
+ return [this.lineNo, this.pos - this.lineStart]
256
+
257
+ }
258
+
247
259
  _transform (chunk, encoding, callback) {
248
260
 
249
261
  const {length} = chunk; if (length !== 0) {
250
262
 
263
+ for (let i = 0; i < length; i ++) {
264
+
265
+ this.pos ++
266
+
267
+ if (chunk.charCodeAt (i) !== CH_LF) continue
268
+
269
+ this.lineNo ++
270
+
271
+ this.lineStart = this.pos
272
+
273
+ }
274
+
251
275
  let e = new XMLNode (chunk, this.entityResolver), {type} = e, {element} = this
252
276
 
253
277
  switch (type) {
@@ -310,7 +334,7 @@ const XMLReader = class extends Transform {
310
334
 
311
335
  }
312
336
 
313
- this.position += BigInt (length)
337
+ this.position += length
314
338
 
315
339
  }
316
340
 
package/lib/XMLSchema.js CHANGED
@@ -15,10 +15,26 @@ const XMLSchema = class extends Map {
15
15
 
16
16
  this [TYPES] = new Map ()
17
17
 
18
- {(this.parent = parent).set (this.targetNamespace = targetNamespace, this)}
18
+ this.targetNamespace = targetNamespace
19
+ this.parent = parent
19
20
 
20
21
  this.setSource (node)
21
22
 
23
+ if (this.parent.has (targetNamespace)) {
24
+
25
+ const existing = this.parent.get (targetNamespace)
26
+
27
+ for (const [k, v] of this.entries ()) existing.set (k, v)
28
+
29
+ for (const [k, v] of this [TYPES].entries ()) existing [TYPES].set (k, v)
30
+
31
+ }
32
+ else {
33
+
34
+ this.parent.set (targetNamespace, this)
35
+
36
+ }
37
+
22
38
  }
23
39
 
24
40
  adjustNode (node) {
@@ -51,6 +67,7 @@ const XMLSchema = class extends Map {
51
67
  case 'group':
52
68
  splitNs ('ref')
53
69
  splitNs ('type')
70
+ splitNs ('substitutionGroup')
54
71
  break
55
72
 
56
73
  case 'extension':
@@ -50,8 +50,8 @@ const XMLSchemata = class extends Map {
50
50
 
51
51
  }
52
52
 
53
- getType ([localName, namespaceURI]) {
54
-
53
+ getType ([localName, namespaceURI]) {
54
+
55
55
  const schema = this.get (namespaceURI); if (schema == null) throw new Error ('Unknown namespace: ' + namespaceURI)
56
56
 
57
57
  const node = schema.getType (localName)
@@ -234,12 +234,22 @@ const XMLSchemata = class extends Map {
234
234
 
235
235
  const schema = new XMLSchema (this, targetNamespace, node)
236
236
 
237
- for (const {localName, namespaceURI, attributes: {schemaLocation, namespace}} of schema._src.children)
237
+ for (const {localName, namespaceURI, attributes: {schemaLocation, namespace}} of schema._src.children) {
238
238
 
239
- if (localName === 'import' && namespaceURI === XMLSchema.namespaceURI && namespace !== NamespacesMap.XMLNamespace)
239
+ if (localName === 'import' && namespaceURI === XMLSchema.namespaceURI && namespace !== NamespacesMap.XMLNamespace) {
240
240
 
241
241
  this.addFile (path.join (options.dirname, schemaLocation), {targetNamespace: namespace})
242
242
 
243
+ }
244
+
245
+ if (localName === 'include' && namespaceURI === XMLSchema.namespaceURI) {
246
+
247
+ this.addFile (path.join (options.dirname, schemaLocation), {targetNamespace})
248
+
249
+ }
250
+
251
+ }
252
+
243
253
  }
244
254
 
245
255
  addFile (fn, options = {}) {
@@ -6,11 +6,22 @@ class MatchElement extends Match {
6
6
 
7
7
  const {node: {attributes: {name}, targetNamespace}} = this.occurable
8
8
 
9
- if (localName !== name || namespaceURI != targetNamespace) return null
9
+ if (localName === name && namespaceURI == targetNamespace) {
10
+
11
+ this.occured ++
12
+
13
+ return this.occurable.node
14
+
15
+ }
16
+
17
+ const el = this.occurable.xs?.getSchema (namespaceURI)?.get (localName)
18
+ const sg = el?.attributes?.substitutionGroup
19
+
20
+ if (!sg || sg [0] !== name || sg [1] !== targetNamespace) return null
10
21
 
11
22
  this.occured ++
12
23
 
13
- return this.occurable.node
24
+ return el
14
25
 
15
26
  }
16
27
 
@@ -1,3 +1,5 @@
1
+ const {XSINamespace} = require ('../NamespacesMap')
2
+
1
3
  class XMLValidationState {
2
4
 
3
5
  #child = null
@@ -49,9 +51,9 @@ class XMLValidationState {
49
51
 
50
52
  formatPosition ({src}) {
51
53
 
52
- const pos = String (this.getPosition () - BigInt (src.length))
54
+ const [line, char] = this.getPosition ()
53
55
 
54
- return `${src} at position ${pos}`
56
+ return `${src} at line ${line}, position ${char}`
55
57
 
56
58
  }
57
59
 
@@ -114,21 +116,51 @@ class XMLValidationState {
114
116
 
115
117
  }
116
118
 
119
+ resolveAttribute (name, attributesMap) {
120
+
121
+ const {attributes} = this.anyType
122
+
123
+ if (attributes.has (name)) {
124
+
125
+ const def = attributes.get (name)
126
+
127
+ if (!def.targetNamespace || def.targetNamespace === attributesMap.getNamespaceURI (name)) return def
128
+
129
+ }
130
+
131
+ const localName = attributesMap.getLocalName (name); if (localName === name) return null
132
+
133
+ const def = attributes.get (localName); if (!def) return null
134
+
135
+ if (def.targetNamespace !== attributesMap.getNamespaceURI (name)) return null
136
+
137
+ return def
138
+
139
+ }
140
+
117
141
  validateAttributes (attributesMap) {
118
142
 
119
143
  const {attributes, isAnyAttributeAllowed} = this.anyType
120
144
 
145
+ const matched = new Set ()
146
+
121
147
  for (const [name, value] of attributesMap) {
122
148
 
123
- if (!attributes.has (name)) {
149
+ const def = this.resolveAttribute (name, attributesMap)
150
+
151
+ if (!def) {
124
152
 
125
153
  if (isAnyAttributeAllowed) continue
126
154
 
155
+ if (attributesMap.getNamespaceURI (name) === XSINamespace) continue
156
+
127
157
  throw Error (`Unknown attribute: "${name}"`)
128
158
 
129
159
  }
130
-
131
- const def = attributes.get (name), {attributes: {fixed, type}} = def
160
+
161
+ matched.add (def)
162
+
163
+ const {attributes: {fixed, type}} = def
132
164
 
133
165
  if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`)
134
166
 
@@ -160,7 +192,7 @@ class XMLValidationState {
160
192
 
161
193
  }
162
194
 
163
- for (const [name, def] of attributes) if (!attributesMap.has (name)) {
195
+ for (const [name, def] of attributes) if (!matched.has (def)) {
164
196
 
165
197
  if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)
166
198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [