xml-toolkit 1.0.60 → 1.1.0

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.
@@ -0,0 +1,169 @@
1
+ class XMLValidationState {
2
+
3
+ #child = null
4
+ #hadText = false
5
+
6
+ constructor (parent, parsedNode, elementDefinition) {
7
+
8
+ this.xs = (this.parent = parent).xs
9
+ this.parsedNode = parsedNode
10
+
11
+ try {
12
+
13
+ this.setTypeFrom (elementDefinition)
14
+
15
+ }
16
+ catch (cause) {
17
+
18
+ throw Error (`Validation problem with ${parsedNode.src} at position ${this.getPosition ()}: ${cause.message}`, {cause})
19
+
20
+ }
21
+
22
+ }
23
+
24
+ get getPosition () {
25
+
26
+ return this.parent.getPosition
27
+
28
+ }
29
+
30
+ setTypeFrom (element) {
31
+
32
+ const {xs} = this
33
+
34
+ const typeDefinition = xs.getTypeDefinition (element)
35
+
36
+ this.anyType = xs.getAnyType (typeDefinition)
37
+
38
+ this.validateAttributes (this.parsedNode.attributes)
39
+
40
+ const {content} = this.anyType; if (content) this.match = content.createMatch ()
41
+
42
+ }
43
+
44
+ blameNothingExpected () {
45
+
46
+ throw Error (`No nested elements allowed inside ${this.parsedNode.src}`)
47
+
48
+ }
49
+
50
+ blameUnexpectedTag (parsedNode, match) {
51
+
52
+ const expected = match.allExpected (parsedNode); if (expected == null) this.blameNothingExpected ()
53
+
54
+ const {src} = parsedNode, {length} = src
55
+
56
+ throw Error (`Unexpected ${src} at position ${BigInt (this.getPosition ()) - BigInt (length)}, expected: ${expected}`)
57
+
58
+ }
59
+
60
+ startElement (parsedNode) {
61
+
62
+ if (this.#child !== null) return this.#child.startElement (parsedNode)
63
+
64
+ const {match} = this; if (!match) throw Error (`No nested elements allowed inside ${this.anyType.debugQName}`)
65
+
66
+ const element = match.getElementDefinition (parsedNode); if (!element) this.blameUnexpectedTag (parsedNode, match)
67
+
68
+ if (element.localName === 'any') return
69
+
70
+ this.#child = new XMLValidationState (this, parsedNode, element)
71
+
72
+ }
73
+
74
+ endElement (parsedNode) {
75
+
76
+ if (this.#child === null) {
77
+
78
+ if (!this.#hadText) this.characters ('')
79
+
80
+ const {match} = this; if (match && !match.isSatisfied) this.blameUnexpectedTag (parsedNode, match)
81
+
82
+ return null
83
+
84
+ }
85
+
86
+ this.#child = this.#child.endElement (parsedNode)
87
+
88
+ return this
89
+
90
+ }
91
+
92
+ characters (text) {
93
+
94
+ if (this.#child !== null) return this.#child.characters (text)
95
+
96
+ this.#hadText = true
97
+
98
+ try {
99
+
100
+ this.anyType.asSimpleType ().validateScalar (text)
101
+
102
+ }
103
+ catch (cause) {
104
+
105
+ throw Error (`Validation problem with the contents of <${this.parsedNode.name}...> at position ${this.getPosition ()}: ${cause.message}`, {cause})
106
+
107
+ }
108
+
109
+ }
110
+
111
+ validateAttributes (attributesMap) {
112
+
113
+ const {attributes, isAnyAttributeAllowed} = this.anyType
114
+
115
+ for (const [name, value] of attributesMap) {
116
+
117
+ if (!attributes.has (name)) {
118
+
119
+ if (isAnyAttributeAllowed) continue
120
+
121
+ throw Error (`Unknown attribute: "${name}"`)
122
+
123
+ }
124
+
125
+ const def = attributes.get (name), {attributes: {fixed, type}} = def
126
+
127
+ if (typeof fixed === 'string' && value !== fixed) throw Error (`The attribute "${name}" must have the value "${fixed}", not "${value}"`)
128
+
129
+ const validateBy = typeNode => {
130
+
131
+ try {
132
+
133
+ this.xs.getSimpleType (typeNode).validateScalar (value)
134
+
135
+ }
136
+ catch (cause) {
137
+
138
+ throw Error (`attribute '${name}": ${cause.message}`, {cause})
139
+
140
+ }
141
+
142
+ }
143
+
144
+ if (type) {
145
+
146
+ validateBy (this.xs.getType (type))
147
+
148
+ }
149
+ else {
150
+
151
+ for (const node of def.children) validateBy (node)
152
+
153
+ }
154
+
155
+ }
156
+
157
+ for (const [name, def] of attributes) if (!attributesMap.has (name)) {
158
+
159
+ if (def.attributes.use === 'required') throw Error (`Missing required attribute: "${name}"`)
160
+
161
+ if ('default' in def.attributes) attributesMap.set (name, def.attributes.default)
162
+
163
+ }
164
+
165
+ }
166
+
167
+ }
168
+
169
+ module.exports = XMLValidationState
@@ -0,0 +1,38 @@
1
+ const XMLValidationState = require ('./XMLValidationState')
2
+
3
+ class XMLValidatior {
4
+
5
+ #state = null
6
+
7
+ constructor (xs, rootNode, getPosition) {
8
+
9
+ this.xs = xs
10
+ this.getPosition = getPosition
11
+
12
+ this.startElement (rootNode)
13
+
14
+ }
15
+
16
+ startElement (parsedNode) {
17
+
18
+ if (this.#state === null) return this.#state = new XMLValidationState (this, parsedNode, this.xs.getSchema (parsedNode.namespaceURI).getElement (parsedNode.localName))
19
+
20
+ this.#state.startElement (parsedNode)
21
+
22
+ }
23
+
24
+ characters (text) {
25
+
26
+ this.#state.characters (text)
27
+
28
+ }
29
+
30
+ endElement (parsedNode) {
31
+
32
+ this.#state.endElement (parsedNode)
33
+
34
+ }
35
+
36
+ }
37
+
38
+ module.exports = XMLValidatior
package/lib/xml.xsd CHANGED
@@ -6,7 +6,7 @@
6
6
  <xs:union memberTypes="xs:language">
7
7
  <xs:simpleType>
8
8
  <xs:restriction base="xs:string">
9
- <xs:enumeration value=""/>
9
+ <xs:enumeration value="en"/>
10
10
  </xs:restriction>
11
11
  </xs:simpleType>
12
12
  </xs:union>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xml-toolkit",
3
- "version": "1.0.60",
3
+ "version": "1.1.0",
4
4
  "description": "XML parser, marshaller, SOAP adapter",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -1,329 +0,0 @@
1
- const NamespacePrefixesMap = require ('./NamespacePrefixesMap.js')
2
-
3
- class XSSimpleType {
4
-
5
- constructor (xs) {
6
-
7
- this.xs = xs
8
- this.patterns = []
9
- this.fractionDigits = Infinity
10
-
11
- }
12
-
13
- blame (value) {
14
-
15
- let s; try {
16
-
17
- s = JSON.stringify (value)
18
-
19
- }
20
- catch (x) {
21
-
22
- s = String (value)
23
-
24
- }
25
-
26
- throw Error (`Cannot stringify ${typeof value} ${s}`)
27
-
28
- }
29
-
30
- stringify (value) {
31
-
32
- if (value == null) this.blame (value)
33
-
34
- for (const s of this.strings (value)) if (this.test (s)) return s
35
-
36
- this.blame (value)
37
-
38
- }
39
-
40
- * strings (value) {
41
-
42
- yield String (value)
43
-
44
- }
45
-
46
- test (s) {
47
-
48
- const {patterns} = this; if (patterns.length === 0) return true
49
-
50
- for (const pattern of patterns) if (pattern.test (s)) return true
51
-
52
- return false
53
-
54
- }
55
-
56
- restrict (entries) {
57
-
58
- return class extends this.constructor {
59
-
60
- constructor (xs) {
61
-
62
- super (xs)
63
-
64
- for (const {name, value} of entries) switch (name) {
65
-
66
- case 'pattern':
67
- this.patterns.push (new RegExp (value))
68
- break
69
-
70
- case 'fractionDigits':
71
- this.fractionDigits = parseInt (value)
72
- break
73
-
74
- }
75
-
76
- }
77
-
78
- }
79
-
80
- }
81
-
82
- }
83
-
84
- class XSSimpleTypeQName extends XSSimpleType {
85
-
86
- stringify (value) {
87
-
88
- if (typeof value !== 'object' || !('localName' in value)) this.blame (value)
89
-
90
- const {xs} = this
91
-
92
- if (!xs.ns) xs.ns = new NamespacePrefixesMap (xs)
93
-
94
- return xs.ns.QName (value.localName, value.namespaceURI)
95
-
96
- }
97
-
98
- }
99
-
100
- class XSSimpleTypeFloat extends XSSimpleType {
101
-
102
- stringify (value) {
103
-
104
- if (isNaN (value)) this.blame (value)
105
-
106
- return (
107
- value === Infinity ? 'INF' :
108
- value === -Infinity ? '-INF' :
109
- super.stringify (value)
110
- )
111
-
112
- }
113
-
114
- }
115
-
116
- class XSSimpleTypeDecimal extends XSSimpleType {
117
-
118
- isValidNumber (num) {
119
-
120
- return !Number.isNaN (num)
121
-
122
- }
123
-
124
- stringify (value) {
125
-
126
- let num = value
127
-
128
- const {fractionDigits} = this
129
-
130
- switch (typeof value) {
131
-
132
- case 'bigint':
133
- return fractionDigits === 0 || fractionDigits === Infinity ?
134
- String (value) :
135
- `${value}.${'0'.repeat (fractionDigits)}`
136
-
137
- case 'string':
138
- num = parseFloat (value)
139
-
140
- case 'number':
141
- if (!this.isValidNumber (num)) this.blame (value)
142
- return fractionDigits === Infinity ?
143
- num.toString () :
144
- num.toFixed (this.fractionDigits)
145
-
146
- default:
147
- this.blame (value)
148
-
149
- }
150
-
151
- }
152
-
153
- }
154
-
155
- class XSSimpleTypeInteger extends XSSimpleTypeDecimal {
156
-
157
- constructor (xs) {
158
-
159
- super (xs)
160
-
161
- this.fractionDigits = 0
162
-
163
- }
164
-
165
- isValidNumber (num) {
166
-
167
- return Number.isInteger (num)
168
-
169
- }
170
-
171
- }
172
-
173
- class XSSimpleTypeBoolean extends XSSimpleType {
174
-
175
- static toCanonical (value) {
176
-
177
- switch (typeof value) {
178
-
179
- case 'boolean': return value
180
-
181
- case 'number':
182
- switch (value) {
183
- case 0: return false
184
- case 1: return true
185
- default: return null
186
- }
187
-
188
- case 'string':
189
-
190
- switch (value) {
191
-
192
- case '0':
193
- case 'false':
194
- return false
195
-
196
- case '1':
197
- case 'true':
198
- return true
199
-
200
- default:
201
- return null
202
-
203
- }
204
-
205
- }
206
-
207
- }
208
-
209
- * strings (value) {
210
-
211
- const c = XSSimpleTypeBoolean.toCanonical (value); if (c !== null) {
212
-
213
- if (c) {
214
- yield 'true'
215
- yield '1'
216
- }
217
- else {
218
- yield 'false'
219
- yield '0'
220
- }
221
-
222
- }
223
-
224
- }
225
-
226
- }
227
-
228
- class XSSimpleTypeDT extends XSSimpleType {
229
-
230
- adjust (value) {
231
-
232
- return typeof value === 'string' ? value : new Date (value).toJSON ()
233
-
234
- }
235
-
236
- }
237
-
238
- class XSSimpleTypeDate extends XSSimpleTypeDT {
239
-
240
- * strings (value) {
241
-
242
- value = this.adjust (value)
243
-
244
- const ymd = value.substring (0, 10)
245
-
246
- const {length} = value; if (length > 10) {
247
-
248
- const pos = value.indexOf ('Z')
249
-
250
- if (pos !== -1 && pos !== length - 1) yield ymd + value.substring (pos)
251
-
252
- }
253
-
254
- yield ymd
255
-
256
- }
257
-
258
- }
259
-
260
- class XSSimpleTypeDateTime extends XSSimpleTypeDT {
261
-
262
- adjust (value) {
263
-
264
- value = super.adjust (value)
265
-
266
- return value.length === 10 ? value + 'T00:00:00' : value
267
-
268
- }
269
-
270
- * strings (value) {
271
-
272
- value = this.adjust (value)
273
-
274
- yield value
275
-
276
- if (value.length > 19) yield value.substring (0, 19)
277
-
278
- }
279
-
280
- }
281
-
282
- XSSimpleType.forName = name => {
283
-
284
- switch (name) {
285
-
286
- case 'QName' : return XSSimpleTypeQName
287
-
288
- case 'boolean' : return XSSimpleTypeBoolean
289
-
290
- case 'date' : return XSSimpleTypeDate
291
-
292
- case 'dateTime': return XSSimpleTypeDateTime
293
-
294
- case 'decimal' : return XSSimpleTypeDecimal
295
-
296
- case 'float' :
297
- case 'double' :
298
- return XSSimpleTypeFloat
299
-
300
- case 'byte':
301
- case 'int':
302
- case 'integer':
303
- case 'long':
304
- case 'negativeInteger':
305
- case 'nonNegativeInteger':
306
- case 'nonPositiveInteger':
307
- case 'positiveInteger':
308
- case 'short':
309
- case 'unsignedByte':
310
- case 'unsignedInt':
311
- case 'unsignedLong':
312
- case 'unsignedShort':
313
- return XSSimpleTypeInteger
314
-
315
- default : return XSSimpleType
316
- }
317
-
318
- }
319
-
320
- module.exports = {
321
- XSSimpleType,
322
- XSSimpleTypeFloat,
323
- XSSimpleTypeBoolean,
324
- XSSimpleTypeDate,
325
- XSSimpleTypeDateTime,
326
- XSSimpleTypeDecimal,
327
- XSSimpleTypeQName,
328
- XSSimpleTypeInteger,
329
- }