@amazon/vinyl-xml 1.0.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.
Files changed (85) hide show
  1. package/dist/index.cjs +976 -0
  2. package/dist/index.cjs.map +7 -0
  3. package/dist/index.js +986 -0
  4. package/dist/index.js.map +7 -0
  5. package/dist/types/current/index.d.ts +14 -0
  6. package/dist/types/current/index.d.ts.map +1 -0
  7. package/dist/types/current/index.js +13 -0
  8. package/dist/types/current/tsconfig.tsbuildinfo +1 -0
  9. package/dist/types/current/xlink/actuateValidator.d.ts +3 -0
  10. package/dist/types/current/xlink/actuateValidator.d.ts.map +1 -0
  11. package/dist/types/current/xlink/actuateValidator.js +11 -0
  12. package/dist/types/current/xml/ParseXmlHandler.d.ts +56 -0
  13. package/dist/types/current/xml/ParseXmlHandler.d.ts.map +1 -0
  14. package/dist/types/current/xml/ParseXmlHandler.js +5 -0
  15. package/dist/types/current/xml/ParseXmlHandlerImpl.d.ts +49 -0
  16. package/dist/types/current/xml/ParseXmlHandlerImpl.d.ts.map +1 -0
  17. package/dist/types/current/xml/ParseXmlHandlerImpl.js +198 -0
  18. package/dist/types/current/xml/QName.d.ts +33 -0
  19. package/dist/types/current/xml/QName.d.ts.map +1 -0
  20. package/dist/types/current/xml/QName.js +19 -0
  21. package/dist/types/current/xml/XmlSchemaError.d.ts +15 -0
  22. package/dist/types/current/xml/XmlSchemaError.d.ts.map +1 -0
  23. package/dist/types/current/xml/XmlSchemaError.js +23 -0
  24. package/dist/types/current/xml/mappedXmlRules.d.ts +118 -0
  25. package/dist/types/current/xml/mappedXmlRules.d.ts.map +1 -0
  26. package/dist/types/current/xml/mappedXmlRules.js +122 -0
  27. package/dist/types/current/xml/mergeXmlRules.d.ts +62 -0
  28. package/dist/types/current/xml/mergeXmlRules.d.ts.map +1 -0
  29. package/dist/types/current/xml/mergeXmlRules.js +89 -0
  30. package/dist/types/current/xml/parseXml.d.ts +76 -0
  31. package/dist/types/current/xml/parseXml.d.ts.map +1 -0
  32. package/dist/types/current/xml/parseXml.js +305 -0
  33. package/dist/types/current/xml/stringifyXml.d.ts +67 -0
  34. package/dist/types/current/xml/stringifyXml.d.ts.map +1 -0
  35. package/dist/types/current/xml/stringifyXml.js +225 -0
  36. package/dist/types/current/xml/xmlRules.d.ts +207 -0
  37. package/dist/types/current/xml/xmlRules.d.ts.map +1 -0
  38. package/dist/types/current/xml/xmlRules.js +134 -0
  39. package/dist/types/current/xmlns/org/w3/1999/xlink.d.ts +7 -0
  40. package/dist/types/current/xmlns/org/w3/1999/xlink.d.ts.map +1 -0
  41. package/dist/types/current/xmlns/org/w3/1999/xlink.js +5 -0
  42. package/dist/types/current/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
  43. package/dist/types/current/xmlns/org/w3/2001/XmlSchema.d.ts.map +1 -0
  44. package/dist/types/current/xmlns/org/w3/2001/XmlSchema.js +5 -0
  45. package/dist/types/ts3.7/index.d.ts +14 -0
  46. package/dist/types/ts3.7/xlink/actuateValidator.d.ts +3 -0
  47. package/dist/types/ts3.7/xml/ParseXmlHandler.d.ts +56 -0
  48. package/dist/types/ts3.7/xml/ParseXmlHandlerImpl.d.ts +49 -0
  49. package/dist/types/ts3.7/xml/QName.d.ts +33 -0
  50. package/dist/types/ts3.7/xml/XmlSchemaError.d.ts +15 -0
  51. package/dist/types/ts3.7/xml/mappedXmlRules.d.ts +118 -0
  52. package/dist/types/ts3.7/xml/mergeXmlRules.d.ts +118 -0
  53. package/dist/types/ts3.7/xml/parseXml.d.ts +76 -0
  54. package/dist/types/ts3.7/xml/stringifyXml.d.ts +67 -0
  55. package/dist/types/ts3.7/xml/xmlRules.d.ts +207 -0
  56. package/dist/types/ts3.7/xmlns/org/w3/1999/xlink.d.ts +7 -0
  57. package/dist/types/ts3.7/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
  58. package/dist/types/ts4.1/index.d.ts +14 -0
  59. package/dist/types/ts4.1/xlink/actuateValidator.d.ts +3 -0
  60. package/dist/types/ts4.1/xml/ParseXmlHandler.d.ts +56 -0
  61. package/dist/types/ts4.1/xml/ParseXmlHandlerImpl.d.ts +49 -0
  62. package/dist/types/ts4.1/xml/QName.d.ts +33 -0
  63. package/dist/types/ts4.1/xml/XmlSchemaError.d.ts +15 -0
  64. package/dist/types/ts4.1/xml/mappedXmlRules.d.ts +118 -0
  65. package/dist/types/ts4.1/xml/mergeXmlRules.d.ts +118 -0
  66. package/dist/types/ts4.1/xml/parseXml.d.ts +77 -0
  67. package/dist/types/ts4.1/xml/stringifyXml.d.ts +67 -0
  68. package/dist/types/ts4.1/xml/xmlRules.d.ts +207 -0
  69. package/dist/types/ts4.1/xmlns/org/w3/1999/xlink.d.ts +7 -0
  70. package/dist/types/ts4.1/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
  71. package/dist/types/ts4.7/index.d.ts +14 -0
  72. package/dist/types/ts4.7/xlink/actuateValidator.d.ts +3 -0
  73. package/dist/types/ts4.7/xml/ParseXmlHandler.d.ts +56 -0
  74. package/dist/types/ts4.7/xml/ParseXmlHandlerImpl.d.ts +49 -0
  75. package/dist/types/ts4.7/xml/QName.d.ts +33 -0
  76. package/dist/types/ts4.7/xml/XmlSchemaError.d.ts +15 -0
  77. package/dist/types/ts4.7/xml/mappedXmlRules.d.ts +118 -0
  78. package/dist/types/ts4.7/xml/mergeXmlRules.d.ts +118 -0
  79. package/dist/types/ts4.7/xml/parseXml.d.ts +76 -0
  80. package/dist/types/ts4.7/xml/stringifyXml.d.ts +67 -0
  81. package/dist/types/ts4.7/xml/xmlRules.d.ts +207 -0
  82. package/dist/types/ts4.7/xmlns/org/w3/1999/xlink.d.ts +7 -0
  83. package/dist/types/ts4.7/xmlns/org/w3/2001/XmlSchema.d.ts +17 -0
  84. package/docs/XML_PARSING.md +210 -0
  85. package/package.json +82 -0
@@ -0,0 +1,210 @@
1
+ # XML Parsing
2
+
3
+ Amazon Vinyl provides a fast way to parse XML documents into a type-safe object
4
+ that is validated against an XSD schema.
5
+
6
+ To create a parser for a schema, first create the Type definitions for the shape
7
+ of the document.
8
+
9
+ There are many ways to do this, for the Dash 2011 schema we used
10
+ [CXSD](https://github.com/charto/cxsd) (MIT Licensed). Take the type
11
+ definitions, and discard their generated parser.
12
+
13
+ Using the type definitions we can build a ruleset following the XSD. This is a
14
+ manual process; follow the XSD closely to match min/max occurrences, defaults,
15
+ and required attributes.
16
+
17
+ MPDType example, following the
18
+ [MPDType definition](https://github.com/Dash-Industry-Forum/MPEG-Conformance-and-reference-source/blob/master/conformance/MPDValidator/schemas/DASH-MPD.xsd):
19
+
20
+ ```typescript
21
+ const mpdType: XmlRules<MPDtype> = {
22
+ ProgramInformation: elements(programInformationType),
23
+ BaseURL: elements(baseURLType),
24
+ Location: elements({ _content: charactersString }),
25
+ Period: elements(periodType, { minOccurs: 1 }),
26
+ Metrics: elements(metricsType),
27
+ EssentialProperty: elements(descriptorType),
28
+ SupplementalProperty: elements(descriptorType),
29
+ UTCTiming: elements(descriptorType),
30
+ availabilityEndTime: attrDateTime,
31
+ availabilityStartTime: attrDateTime,
32
+ id: attrString,
33
+ maxSegmentDuration: attr(parseDuration, stringifyDuration),
34
+ maxSubsegmentDuration: attr(parseDuration, stringifyDuration),
35
+ mediaPresentationDuration: attr(parseDuration, stringifyDuration),
36
+ minBufferTime: attr(parseDuration, stringifyDuration, { required: true }),
37
+ minimumUpdatePeriod: attr(parseDuration, stringifyDuration),
38
+ profiles: attr(parseProfiles, stringifyProfiles, { required: true }),
39
+ publishTime: attrDateTime,
40
+ suggestedPresentationDelay: attr(parseDuration, stringifyDuration),
41
+ timeShiftBufferDepth: attr(parseDuration, stringifyDuration),
42
+ type: attr(parsePresentationTypeType, stringify, {
43
+ default: 'static',
44
+ } as const),
45
+ } as const
46
+ ```
47
+
48
+ _Style note - abbreviations should be cased as they are in the XSD, this follows
49
+ the exception noted in the
50
+ [TS style guide](https://google.github.io/styleguide/tsguide.html#identifiers-abbreviations)
51
+ we follow._
52
+
53
+ `XmlRules` definitions are meant to be human-readable and easy for the
54
+ TypeScript compiler to validate that the rule matches the type definition.
55
+ Before this rule set can be used by the parser or stringifier, it must be mapped
56
+ to a more performant machine-friendly version.
57
+
58
+ Map the rules using `mapXmlRules`:
59
+
60
+ ```typescript
61
+ const dashDrmRulesMapped = mapXmlRules(dashDrmRules)
62
+ ```
63
+
64
+ XmlRules maps XML content to a typed object using the following four rules:
65
+
66
+ - `attr` Attributes
67
+ - `element` Elements with a max occurrence of 1.
68
+ - `elements` Elements, as an array.
69
+ - `characters` Text nodes (concatenated).
70
+
71
+ Attribute and characters handlers provide parse and stringify functions to
72
+ serialize/deserialize the value from the XML.
73
+
74
+ From the ruleset, we can create an XML handler for the SAX parser:
75
+
76
+ ```typescript
77
+ const dashManifestHandler = new ParseXmlHandlerImpl(dashDrmRulesMapped)
78
+ ```
79
+
80
+ _Concurrency note: A handler is stateful while parsing a document, so if there
81
+ are multiple workers parsing documents simultaneously, a handler per worker
82
+ thread must be created._
83
+
84
+ With the handler now we can parse XML into a validated type-safe object matching
85
+ our type definition.
86
+
87
+ ```typescript
88
+ export function parseDashManifest(manifest: string): DashDrmManifest {
89
+ return parseXml(manifest, dashManifestHandler)
90
+ }
91
+ ```
92
+
93
+ The overall process is as follows:
94
+
95
+ `parseXml` is given an XML string, and a content handler. A content handler
96
+ abstractly provides methods for opening and closing XML nodes. Based on an XSD
97
+ schema, we can build a content handler `XmlRules` that knows how each node
98
+ should be processed.
99
+
100
+ At a low level, `parseXml` uses `StringParser` to read XML content. As elements
101
+ are opened and closed, and attributes or text nodes are read, the corresponding
102
+ content handler method is invoked. Validating against the schema rules as the
103
+ document is read gives the added benefit of knowing the exact XML source
104
+ location of the invalid document.
105
+
106
+ ## Namespaces
107
+
108
+ Namespaces are not optional, if an XSD schema uses a namespace, the namespace
109
+ must be defined in the rules.
110
+
111
+ For example:
112
+
113
+ ```typescript
114
+ export const dashNamespaceUri = 'urn:mpeg:dash:schema:mpd:2011'
115
+
116
+ export const dashManifestXmlRules: XmlRules<DashManifest> = {
117
+ MPD: element(mpdType, {
118
+ required: true,
119
+ namespaceUri: dashNamespaceUri,
120
+ }),
121
+ } as const
122
+ ```
123
+
124
+ Namespaces follow correct scoping and defaulting rules.
125
+
126
+ https://www.w3.org/TR/REC-xml-names/
127
+
128
+ For XmlRules, namespaces inherit that of the parent rule unless overridden. When
129
+ using `stringifyXml`, defining namespace prefixes is supported, but optional. If
130
+ a prefix isn't specified, one will be generated if the default namespace isn't
131
+ used.
132
+
133
+ ## Merging Schemas
134
+
135
+ In many cases, an XML document has multiple XSD definitions.
136
+
137
+ These can be merged using `mergeXmlRules`.
138
+
139
+ Example:
140
+
141
+ ```typescript
142
+ const dashDrmRules = mergeXmlRules(
143
+ dashManifestXmlRules,
144
+ createDashProtectionXmlRules(cencXmlRules),
145
+ createDashProtectionXmlRules(playreadyXmlRules)
146
+ )
147
+ ```
148
+
149
+ The `mergeXmlRules` utility ensures that the combined schema definitions are
150
+ compatible. When the rules represent separate XSD schemas with different
151
+ namespace uris, there generally isn't overlap.
152
+
153
+ ## Stringify
154
+
155
+ `XmlRules` provides both deserializers and serializers, so the same mapped
156
+ ruleset can be used in both the content handler for `parseXml` and
157
+ `stringifyXml`.
158
+
159
+ ```typescript
160
+ stringifyXml(xmlString, dashDrmRulesMapped)
161
+ ```
162
+
163
+ ### Element Ordering
164
+
165
+ In an XML Schema, order matters for `xs:sequence` definitions. Amazon Vinyl
166
+ `XmlRules` maps define properties in this order. This doesn't matter for
167
+ parsing, but it does for serialization to pass schema. This means that
168
+ serialization relies on the ES6 requirement that object keys are ordered by
169
+ insertion. Many ES5 browsers also follow this rule, but this means that
170
+ `xmlStringify` will not be to spec for browsers not following this requirement.
171
+ XML serialization is not required for playback, only deserialization.
172
+
173
+ ## Limitations
174
+
175
+ The parser currently ignores processing instructions and supports only the
176
+ default standard XML entities. This isn't a necessary feature for our purposes.
177
+
178
+ ## Parent References
179
+
180
+ Parsed XML nodes have a reference to their parent elements in the assigned
181
+ attribute `parent`. These references are non-enumerable and will be correctly
182
+ reassigned when using the `clone` utility. They will not be included if
183
+ serialized using `JSON.stringify`. Type definitions may include a `parent`
184
+ definition. The type is not enforced by the rule-sets and is expected to
185
+ correspond to the type of parent to which the element may belong, or it may
186
+ simply be typed as `any`.
187
+
188
+ ## Name Conflicts
189
+
190
+ In certain edge cases, an XSD may describe element or attribute rules that
191
+ cannot be mapped 1:1 with property names. Cases include:
192
+
193
+ - If the child element or attribute name is an ES6 reserved word such as
194
+ `public`, `private`, `get`, `set`, etc.
195
+ - If the name is an internally used reserved word: `parent` or `clone`.
196
+ - If a child element local name matches an attribute name. In these cases, when
197
+ defining an XML rule, choose a different property name, and provide a
198
+ 'localPart' value to the rule configuration.
199
+
200
+ For example:
201
+
202
+ ```typescript
203
+ import { attrString } from '@amazon/vinyl-xml'
204
+
205
+ type MyType = { _parent: string }
206
+
207
+ export const myXmlRules: XmlRules<MyType> = {
208
+ _parent: attrString({ localPart: 'parent' }),
209
+ } as const
210
+ ```
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@amazon/vinyl-xml",
3
+ "version": "1.0.0",
4
+ "description": "A SAX XML parser",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/amazonmusic/vinyl.git"
8
+ },
9
+ "license": "UNLICENSED",
10
+ "module": "./dist/index.js",
11
+ "main": "./dist/index.cjs",
12
+ "types": "./dist/types/ts3.7/index.d.ts",
13
+ "type": "module",
14
+ "sideEffects": [
15
+ "test/**/*"
16
+ ],
17
+ "files": [
18
+ "dist/*",
19
+ "docs/*"
20
+ ],
21
+ "typesVersions": {
22
+ ">=5.4": {
23
+ "*": [
24
+ "./dist/types/current/index.d.ts"
25
+ ]
26
+ },
27
+ ">=4.7": {
28
+ "*": [
29
+ "./dist/types/ts4.7/index.d.ts"
30
+ ]
31
+ },
32
+ ">=4.1": {
33
+ "*": [
34
+ "./dist/types/ts4.1/index.d.ts"
35
+ ]
36
+ },
37
+ ">=3.7": {
38
+ "*": [
39
+ "./dist/types/ts3.7/index.d.ts"
40
+ ]
41
+ }
42
+ },
43
+ "exports": {
44
+ ".": {
45
+ "types@>=5.4": "./dist/types/current/index.d.ts",
46
+ "types@>=4.7": "./dist/types/ts4.7/index.d.ts",
47
+ "require": "./dist/index.cjs",
48
+ "import": "./dist/index.js"
49
+ }
50
+ },
51
+ "scripts": {
52
+ "analyzeExports": "tsx buildSrc/analyzeExports.ts",
53
+ "analyzeExports:write": "tsx buildSrc/analyzeExports.ts --write",
54
+ "clean": "rimraf --glob dist .eslintcache \"**/*.tsbuildinfo\"",
55
+ "indexFiles:check": "tsx buildSrc/writeIndexFiles.ts --check",
56
+ "indexFiles:write": "tsx buildSrc/writeIndexFiles.ts",
57
+ "build": "tsx buildSrc/build.ts",
58
+ "build:release": "tsx buildSrc/build.ts --release",
59
+ "prettier:check": "prettier . --ignore-path ../../.prettierignore --cache --check",
60
+ "prettier:write": "prettier . --ignore-path ../../.prettierignore --cache --write",
61
+ "eslint:check": "eslint . --cache --max-warnings=0",
62
+ "eslint:write": "eslint . --cache --fix",
63
+ "lint": "npm run indexFiles:check && npm run eslint:check && npm run prettier:check",
64
+ "lint:write": "npm run indexFiles:write && npm run eslint:write && npm run prettier:write",
65
+ "validate": "tsc --project test/unit && npm run lint && npm run test",
66
+ "test": "tsx --tsconfig ../../tsconfig.flat.json ../../node_modules/jasmine/bin/jasmine --config=test/unit/jasmine.config.json",
67
+ "test:coverage": "c8 npm run test",
68
+ "watch": "tsx buildSrc/build.ts --watch",
69
+ "watch:release": "tsx buildSrc/build.ts --watch --release"
70
+ },
71
+ "devDependencies": {
72
+ "@amazon/vinyl-build-utils": "^1.0.0",
73
+ "@amazon/vinyl-jasmine-wrapper": "^1.0.0"
74
+ },
75
+ "dependencies": {
76
+ "@amazon/vinyl-util": "^1.0.0",
77
+ "@amazon/vinyl-validation": "^1.0.0"
78
+ },
79
+ "browserslist": [
80
+ "defaults, chrome 52, firefox 54, edge 15, safari 8"
81
+ ]
82
+ }