@graphty/graph-io 0.3.25 → 0.3.26
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 +49 -414
- package/dist/chunks/children-CL3Cy0ez.js.map +1 -1
- package/dist/chunks/choose-BbJI55dx.js +56 -0
- package/dist/chunks/choose-BbJI55dx.js.map +1 -0
- package/dist/chunks/constants-BCsFsMj4.js +231 -0
- package/dist/chunks/constants-BCsFsMj4.js.map +1 -0
- package/dist/chunks/{escape-B-tkk_Cl.js → escape-D0g6NNUy.js} +382 -92
- package/dist/chunks/escape-D0g6NNUy.js.map +1 -0
- package/dist/chunks/export-BTI4yQgy.js +4333 -0
- package/dist/chunks/export-BTI4yQgy.js.map +1 -0
- package/dist/chunks/exporter-BMqk5oxW.js +3465 -0
- package/dist/chunks/exporter-BMqk5oxW.js.map +1 -0
- package/dist/chunks/{importer-eUREhBGj.js → exporter-Cra2GYcN.js} +4969 -3201
- package/dist/chunks/exporter-Cra2GYcN.js.map +1 -0
- package/dist/chunks/{exporter-BGrsWamJ.js → exporter-Dxh231ZF.js} +38 -229
- package/dist/chunks/exporter-Dxh231ZF.js.map +1 -0
- package/dist/chunks/{exporter-DIeGJXAZ.js → exporter-tne475KK.js} +22 -20
- package/dist/chunks/exporter-tne475KK.js.map +1 -0
- package/dist/chunks/importer-90YWhJoF.js +2811 -0
- package/dist/chunks/importer-90YWhJoF.js.map +1 -0
- package/dist/chunks/{importer-D_e7e7LX.js → importer-Bcl1d4xH.js} +398 -145
- package/dist/chunks/importer-Bcl1d4xH.js.map +1 -0
- package/dist/chunks/{importer-BWY2FFc5.js → importer-BfIsp1f9.js} +255 -77
- package/dist/chunks/importer-BfIsp1f9.js.map +1 -0
- package/dist/chunks/{importer-zGLo8Dg_.js → importer-C_TEA1zJ.js} +484 -157
- package/dist/chunks/importer-C_TEA1zJ.js.map +1 -0
- package/dist/chunks/{importer-DET1tg6s.js → importer-DCjOvO7U.js} +1687 -310
- package/dist/chunks/importer-DCjOvO7U.js.map +1 -0
- package/dist/chunks/{importer-vi3bSdtw.js → importer-DfVu6j59.js} +509 -76
- package/dist/chunks/importer-DfVu6j59.js.map +1 -0
- package/dist/chunks/{importer-DIrbbnAf.js → importer-Dku_VAIY.js} +585 -226
- package/dist/chunks/importer-Dku_VAIY.js.map +1 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js +1225 -0
- package/dist/chunks/json-elements-3tnyt5uZ.js.map +1 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js +357 -0
- package/dist/chunks/ontology-export-B9Tcf37D.js.map +1 -0
- package/dist/chunks/{records-dbRkxwaq.js → records-CQXT__mY.js} +298 -114
- package/dist/chunks/records-CQXT__mY.js.map +1 -0
- package/dist/chunks/{text-Dr0Ifpag.js → text-DmcUs5_Q.js} +29 -11
- package/dist/chunks/text-DmcUs5_Q.js.map +1 -0
- package/dist/chunks/{writer-C7flM-Ih.js → writer-DxjgHwSe.js} +19 -1
- package/dist/chunks/writer-DxjgHwSe.js.map +1 -0
- package/dist/csv.js +951 -261
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +6 -3
- package/dist/cx2.js +2 -2
- package/dist/cys.js +6 -3
- package/dist/dot.js +1 -1
- package/dist/gexf.js +554 -122
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +342 -85
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +1105 -213
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +3 -2
- package/dist/graphml.js.map +1 -1
- package/dist/json.js +3 -2
- package/dist/neo4j.js +564 -157
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +6 -3
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts +18 -6
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +6 -3
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts +29 -11
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +25 -7
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/cell-budget.d.ts +9 -2
- package/dist/src/common/cell-budget.d.ts.map +1 -1
- package/dist/src/common/cell-budget.js +11 -3
- package/dist/src/common/cell-budget.js.map +1 -1
- package/dist/src/common/choose.d.ts +19 -0
- package/dist/src/common/choose.d.ts.map +1 -0
- package/dist/src/common/choose.js +72 -0
- package/dist/src/common/choose.js.map +1 -0
- package/dist/src/common/codes.d.ts +456 -66
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +458 -66
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/compare.d.ts +81 -0
- package/dist/src/common/compare.d.ts.map +1 -0
- package/dist/src/common/compare.js +394 -0
- package/dist/src/common/compare.js.map +1 -0
- package/dist/src/common/cx-export.d.ts +123 -0
- package/dist/src/common/cx-export.d.ts.map +1 -0
- package/dist/src/common/cx-export.js +334 -0
- package/dist/src/common/cx-export.js.map +1 -0
- package/dist/src/common/declared-types.d.ts +22 -2
- package/dist/src/common/declared-types.d.ts.map +1 -1
- package/dist/src/common/declared-types.js +19 -1
- package/dist/src/common/declared-types.js.map +1 -1
- package/dist/src/common/direction.d.ts +74 -50
- package/dist/src/common/direction.d.ts.map +1 -1
- package/dist/src/common/direction.js +47 -27
- package/dist/src/common/direction.js.map +1 -1
- package/dist/src/common/escape.d.ts +21 -9
- package/dist/src/common/escape.d.ts.map +1 -1
- package/dist/src/common/escape.js +33 -17
- package/dist/src/common/escape.js.map +1 -1
- package/dist/src/common/export.d.ts +144 -32
- package/dist/src/common/export.d.ts.map +1 -1
- package/dist/src/common/export.js +216 -42
- package/dist/src/common/export.js.map +1 -1
- package/dist/src/common/format.d.ts +23 -1
- package/dist/src/common/format.d.ts.map +1 -1
- package/dist/src/common/format.js +26 -1
- package/dist/src/common/format.js.map +1 -1
- package/dist/src/common/ids.d.ts +9 -2
- package/dist/src/common/ids.d.ts.map +1 -1
- package/dist/src/common/ids.js +6 -2
- package/dist/src/common/ids.js.map +1 -1
- package/dist/src/common/input.d.ts +122 -28
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +890 -124
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/json-elements.d.ts +144 -20
- package/dist/src/common/json-elements.d.ts.map +1 -1
- package/dist/src/common/json-elements.js +868 -188
- package/dist/src/common/json-elements.js.map +1 -1
- package/dist/src/common/line-format.d.ts +90 -0
- package/dist/src/common/line-format.d.ts.map +1 -0
- package/dist/src/common/line-format.js +185 -0
- package/dist/src/common/line-format.js.map +1 -0
- package/dist/src/common/lists.d.ts +6 -1
- package/dist/src/common/lists.d.ts.map +1 -1
- package/dist/src/common/lists.js +2 -0
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/ontology-export.d.ts +167 -0
- package/dist/src/common/ontology-export.d.ts.map +1 -0
- package/dist/src/common/ontology-export.js +359 -0
- package/dist/src/common/ontology-export.js.map +1 -0
- package/dist/src/common/ontology.d.ts +25 -3
- package/dist/src/common/ontology.d.ts.map +1 -1
- package/dist/src/common/ontology.js +30 -9
- package/dist/src/common/ontology.js.map +1 -1
- package/dist/src/common/options.d.ts +62 -12
- package/dist/src/common/options.d.ts.map +1 -1
- package/dist/src/common/options.js +89 -11
- package/dist/src/common/options.js.map +1 -1
- package/dist/src/common/plural.d.ts +31 -0
- package/dist/src/common/plural.d.ts.map +1 -0
- package/dist/src/common/plural.js +46 -0
- package/dist/src/common/plural.js.map +1 -0
- package/dist/src/common/report.d.ts +72 -14
- package/dist/src/common/report.d.ts.map +1 -1
- package/dist/src/common/report.js +146 -22
- package/dist/src/common/report.js.map +1 -1
- package/dist/src/common/temporal.d.ts +22 -5
- package/dist/src/common/temporal.d.ts.map +1 -1
- package/dist/src/common/temporal.js +13 -2
- package/dist/src/common/temporal.js.map +1 -1
- package/dist/src/common/text.d.ts +39 -23
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +51 -21
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/weights.d.ts +37 -4
- package/dist/src/common/weights.d.ts.map +1 -1
- package/dist/src/common/weights.js +61 -2
- package/dist/src/common/weights.js.map +1 -1
- package/dist/src/common/writer.d.ts +19 -3
- package/dist/src/common/writer.d.ts.map +1 -1
- package/dist/src/common/writer.js +30 -2
- package/dist/src/common/writer.js.map +1 -1
- package/dist/src/common/xml.d.ts +125 -9
- package/dist/src/common/xml.d.ts.map +1 -1
- package/dist/src/common/xml.js +400 -83
- package/dist/src/common/xml.js.map +1 -1
- package/dist/src/common/zip.d.ts +44 -4
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +134 -5
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts +80 -24
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +145 -32
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/header.d.ts +38 -9
- package/dist/src/formats/csv/header.d.ts.map +1 -1
- package/dist/src/formats/csv/header.js +46 -10
- package/dist/src/formats/csv/header.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts +187 -37
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +835 -204
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/index.d.ts +100 -19
- package/dist/src/formats/csv/index.d.ts.map +1 -1
- package/dist/src/formats/csv/index.js +94 -21
- package/dist/src/formats/csv/index.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts +95 -10
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +333 -122
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/csv/values.d.ts +1 -0
- package/dist/src/formats/csv/values.d.ts.map +1 -1
- package/dist/src/formats/csv/values.js +1 -0
- package/dist/src/formats/csv/values.js.map +1 -1
- package/dist/src/formats/cx/exporter.d.ts +102 -0
- package/dist/src/formats/cx/exporter.d.ts.map +1 -0
- package/dist/src/formats/cx/exporter.js +1217 -0
- package/dist/src/formats/cx/exporter.js.map +1 -0
- package/dist/src/formats/cx/importer.d.ts +79 -32
- package/dist/src/formats/cx/importer.d.ts.map +1 -1
- package/dist/src/formats/cx/importer.js +680 -285
- package/dist/src/formats/cx/importer.js.map +1 -1
- package/dist/src/formats/cx/index.d.ts +4 -3
- package/dist/src/formats/cx/index.d.ts.map +1 -1
- package/dist/src/formats/cx/index.js +4 -3
- package/dist/src/formats/cx/index.js.map +1 -1
- package/dist/src/formats/cx2/exporter.d.ts +29 -11
- package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
- package/dist/src/formats/cx2/exporter.js +39 -268
- package/dist/src/formats/cx2/exporter.js.map +1 -1
- package/dist/src/formats/cx2/importer.d.ts +78 -28
- package/dist/src/formats/cx2/importer.d.ts.map +1 -1
- package/dist/src/formats/cx2/importer.js +226 -79
- package/dist/src/formats/cx2/importer.js.map +1 -1
- package/dist/src/formats/cx2/index.d.ts +2 -1
- package/dist/src/formats/cx2/index.d.ts.map +1 -1
- package/dist/src/formats/cx2/index.js +2 -1
- package/dist/src/formats/cx2/index.js.map +1 -1
- package/dist/src/formats/cys/constants.d.ts +66 -17
- package/dist/src/formats/cys/constants.d.ts.map +1 -1
- package/dist/src/formats/cys/constants.js +59 -18
- package/dist/src/formats/cys/constants.js.map +1 -1
- package/dist/src/formats/cys/exporter.d.ts +118 -0
- package/dist/src/formats/cys/exporter.d.ts.map +1 -0
- package/dist/src/formats/cys/exporter.js +1072 -0
- package/dist/src/formats/cys/exporter.js.map +1 -0
- package/dist/src/formats/cys/importer.d.ts +22 -8
- package/dist/src/formats/cys/importer.d.ts.map +1 -1
- package/dist/src/formats/cys/importer.js +510 -149
- package/dist/src/formats/cys/importer.js.map +1 -1
- package/dist/src/formats/cys/index.d.ts +4 -2
- package/dist/src/formats/cys/index.d.ts.map +1 -1
- package/dist/src/formats/cys/index.js +4 -2
- package/dist/src/formats/cys/index.js.map +1 -1
- package/dist/src/formats/cys/session.d.ts +46 -7
- package/dist/src/formats/cys/session.d.ts.map +1 -1
- package/dist/src/formats/cys/session.js +98 -24
- package/dist/src/formats/cys/session.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts +9 -2
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +29 -7
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/exporter.d.ts +42 -14
- package/dist/src/formats/dot/exporter.d.ts.map +1 -1
- package/dist/src/formats/dot/exporter.js +56 -29
- package/dist/src/formats/dot/exporter.js.map +1 -1
- package/dist/src/formats/dot/importer.d.ts +91 -29
- package/dist/src/formats/dot/importer.d.ts.map +1 -1
- package/dist/src/formats/dot/importer.js +190 -39
- package/dist/src/formats/dot/importer.js.map +1 -1
- package/dist/src/formats/dot/index.d.ts +2 -1
- package/dist/src/formats/dot/index.d.ts.map +1 -1
- package/dist/src/formats/dot/index.js +2 -1
- package/dist/src/formats/dot/index.js.map +1 -1
- package/dist/src/formats/dot/names.d.ts +44 -11
- package/dist/src/formats/dot/names.d.ts.map +1 -1
- package/dist/src/formats/dot/names.js +44 -11
- package/dist/src/formats/dot/names.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +9 -2
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +20 -5
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts +46 -15
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +45 -15
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts +87 -23
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +471 -90
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gexf/index.d.ts +84 -21
- package/dist/src/formats/gexf/index.d.ts.map +1 -1
- package/dist/src/formats/gexf/index.js +80 -23
- package/dist/src/formats/gexf/index.js.map +1 -1
- package/dist/src/formats/gexf/schema.d.ts +67 -15
- package/dist/src/formats/gexf/schema.d.ts.map +1 -1
- package/dist/src/formats/gexf/schema.js +59 -13
- package/dist/src/formats/gexf/schema.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts +69 -21
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +74 -29
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts +116 -25
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +290 -61
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/index.d.ts +87 -24
- package/dist/src/formats/gml/index.d.ts.map +1 -1
- package/dist/src/formats/gml/index.js +79 -26
- package/dist/src/formats/gml/index.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts +67 -18
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +108 -26
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/constants.d.ts +174 -44
- package/dist/src/formats/graphml/constants.d.ts.map +1 -1
- package/dist/src/formats/graphml/constants.js +165 -44
- package/dist/src/formats/graphml/constants.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts +17 -9
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +33 -12
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts +19 -7
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +466 -39
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/index.d.ts +2 -1
- package/dist/src/formats/graphml/index.d.ts.map +1 -1
- package/dist/src/formats/graphml/index.js +2 -1
- package/dist/src/formats/graphml/index.js.map +1 -1
- package/dist/src/formats/graphml/tree.d.ts +6 -2
- package/dist/src/formats/graphml/tree.d.ts.map +1 -1
- package/dist/src/formats/graphml/tree.js +6 -2
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts +8 -3
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +36 -15
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts +88 -24
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +109 -19
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts +134 -22
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +135 -54
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +173 -52
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +899 -220
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/index.d.ts +3 -2
- package/dist/src/formats/json/index.d.ts.map +1 -1
- package/dist/src/formats/json/index.js +3 -2
- package/dist/src/formats/json/index.js.map +1 -1
- package/dist/src/formats/json/obographs-export.d.ts +68 -0
- package/dist/src/formats/json/obographs-export.d.ts.map +1 -0
- package/dist/src/formats/json/obographs-export.js +941 -0
- package/dist/src/formats/json/obographs-export.js.map +1 -0
- package/dist/src/formats/json/obographs.d.ts +26 -0
- package/dist/src/formats/json/obographs.d.ts.map +1 -1
- package/dist/src/formats/json/obographs.js +297 -80
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts +109 -21
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +238 -85
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts +17 -4
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +36 -4
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts +107 -27
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +415 -63
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/neo4j/index.d.ts +73 -23
- package/dist/src/formats/neo4j/index.d.ts.map +1 -1
- package/dist/src/formats/neo4j/index.js +65 -25
- package/dist/src/formats/neo4j/index.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts +150 -0
- package/dist/src/formats/obo/exporter.d.ts.map +1 -0
- package/dist/src/formats/obo/exporter.js +1736 -0
- package/dist/src/formats/obo/exporter.js.map +1 -0
- package/dist/src/formats/obo/importer.d.ts +95 -19
- package/dist/src/formats/obo/importer.d.ts.map +1 -1
- package/dist/src/formats/obo/importer.js +475 -104
- package/dist/src/formats/obo/importer.js.map +1 -1
- package/dist/src/formats/obo/index.d.ts +6 -3
- package/dist/src/formats/obo/index.d.ts.map +1 -1
- package/dist/src/formats/obo/index.js +6 -3
- package/dist/src/formats/obo/index.js.map +1 -1
- package/dist/src/formats/obo/syntax.d.ts +108 -4
- package/dist/src/formats/obo/syntax.d.ts.map +1 -1
- package/dist/src/formats/obo/syntax.js +211 -34
- package/dist/src/formats/obo/syntax.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts +54 -16
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +66 -34
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts +84 -22
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +303 -83
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/index.d.ts +2 -1
- package/dist/src/formats/pajek/index.d.ts.map +1 -1
- package/dist/src/formats/pajek/index.js +2 -1
- package/dist/src/formats/pajek/index.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts +77 -14
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +183 -51
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/columns.d.ts +1 -0
- package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
- package/dist/src/formats/xgmml/columns.js +4 -0
- package/dist/src/formats/xgmml/columns.js.map +1 -1
- package/dist/src/formats/xgmml/constants.d.ts +186 -56
- package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
- package/dist/src/formats/xgmml/constants.js +181 -57
- package/dist/src/formats/xgmml/constants.js.map +1 -1
- package/dist/src/formats/xgmml/document.d.ts +65 -11
- package/dist/src/formats/xgmml/document.d.ts.map +1 -1
- package/dist/src/formats/xgmml/document.js +157 -28
- package/dist/src/formats/xgmml/document.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +61 -3
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +129 -26
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts +13 -6
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +20 -21
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts +33 -12
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +35 -72
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/formats/xgmml/index.d.ts +1 -0
- package/dist/src/formats/xgmml/index.d.ts.map +1 -1
- package/dist/src/formats/xgmml/index.js +1 -0
- package/dist/src/formats/xgmml/index.js.map +1 -1
- package/dist/src/formats/xgmml/values.d.ts +8 -1
- package/dist/src/formats/xgmml/values.d.ts.map +1 -1
- package/dist/src/formats/xgmml/values.js +7 -2
- package/dist/src/formats/xgmml/values.js.map +1 -1
- package/dist/src/index.d.ts +23 -18
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +28 -16
- package/dist/src/index.js.map +1 -1
- package/dist/src/registry.d.ts +478 -92
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +807 -88
- package/dist/src/registry.js.map +1 -1
- package/dist/src/sniff.d.ts +61 -19
- package/dist/src/sniff.d.ts.map +1 -1
- package/dist/src/sniff.js +121 -29
- package/dist/src/sniff.js.map +1 -1
- package/dist/src/types.d.ts +362 -112
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +28 -7
- package/dist/src/types.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/docs/samples/README.md +25 -0
- package/docs/samples/got-edges.csv +353 -0
- package/docs/samples/got-network.graphml +1384 -0
- package/docs/samples/got-nodes.csv +108 -0
- package/docs/samples/got.cx +930 -0
- package/docs/samples/got.cx2 +465 -0
- package/docs/samples/got.gexf +471 -0
- package/docs/samples/got.gml +2650 -0
- package/docs/samples/got.json +2546 -0
- package/docs/samples/got.net +461 -0
- package/docs/samples/movies-nodes.csv +9 -0
- package/docs/samples/movies-rels.csv +8 -0
- package/docs/samples/networks.cys +0 -0
- package/docs/samples/proteins.xgmml +9 -0
- package/docs/samples/teams.gv +24 -0
- package/docs/samples/vehicles.obo +25 -0
- package/package.json +9 -4
- package/src/children.ts +18 -6
- package/src/common/attributes.ts +29 -11
- package/src/common/cell-budget.ts +11 -3
- package/src/common/choose.ts +89 -0
- package/src/common/codes.ts +490 -66
- package/src/common/compare.ts +472 -0
- package/src/common/cx-export.ts +432 -0
- package/src/common/declared-types.ts +38 -4
- package/src/common/direction.ts +78 -51
- package/src/common/escape.ts +33 -17
- package/src/common/export.ts +284 -47
- package/src/common/format.ts +30 -1
- package/src/common/ids.ts +14 -3
- package/src/common/input.ts +1057 -134
- package/src/common/json-elements.ts +1034 -198
- package/src/common/line-format.ts +249 -0
- package/src/common/lists.ts +6 -1
- package/src/common/ontology-export.ts +461 -0
- package/src/common/ontology.ts +31 -9
- package/src/common/options.ts +110 -15
- package/src/common/plural.ts +49 -0
- package/src/common/report.ts +168 -25
- package/src/common/temporal.ts +22 -5
- package/src/common/text.ts +62 -23
- package/src/common/weights.ts +74 -4
- package/src/common/writer.ts +37 -3
- package/src/common/xml.ts +477 -88
- package/src/common/zip.ts +169 -7
- package/src/formats/csv/exporter.ts +201 -46
- package/src/formats/csv/header.ts +50 -11
- package/src/formats/csv/importer.ts +1133 -245
- package/src/formats/csv/index.ts +106 -19
- package/src/formats/csv/records.ts +396 -122
- package/src/formats/csv/values.ts +1 -0
- package/src/formats/cx/exporter.ts +1483 -0
- package/src/formats/cx/importer.ts +888 -300
- package/src/formats/cx/index.ts +4 -3
- package/src/formats/cx2/exporter.ts +61 -338
- package/src/formats/cx2/importer.ts +317 -86
- package/src/formats/cx2/index.ts +2 -1
- package/src/formats/cys/constants.ts +62 -17
- package/src/formats/cys/exporter.ts +1368 -0
- package/src/formats/cys/importer.ts +659 -157
- package/src/formats/cys/index.ts +4 -2
- package/src/formats/cys/session.ts +139 -31
- package/src/formats/cys/tables.ts +47 -9
- package/src/formats/dot/exporter.ts +83 -37
- package/src/formats/dot/importer.ts +248 -54
- package/src/formats/dot/index.ts +2 -1
- package/src/formats/dot/names.ts +44 -11
- package/src/formats/dot/tokenizer.ts +25 -6
- package/src/formats/gexf/exporter.ts +57 -18
- package/src/formats/gexf/importer.ts +636 -105
- package/src/formats/gexf/index.ts +90 -21
- package/src/formats/gexf/schema.ts +67 -15
- package/src/formats/gml/exporter.ts +90 -36
- package/src/formats/gml/importer.ts +388 -67
- package/src/formats/gml/index.ts +85 -24
- package/src/formats/gml/syntax.ts +111 -26
- package/src/formats/graphml/constants.ts +178 -44
- package/src/formats/graphml/exporter.ts +52 -18
- package/src/formats/graphml/importer.ts +640 -60
- package/src/formats/graphml/index.ts +2 -1
- package/src/formats/graphml/tree.ts +6 -2
- package/src/formats/graphml/yfiles.ts +37 -15
- package/src/formats/json/dialect.ts +128 -29
- package/src/formats/json/exporter.ts +205 -68
- package/src/formats/json/importer.ts +1163 -253
- package/src/formats/json/index.ts +3 -1
- package/src/formats/json/obographs-export.ts +1197 -0
- package/src/formats/json/obographs.ts +353 -92
- package/src/formats/neo4j/exporter.ts +320 -101
- package/src/formats/neo4j/header.ts +44 -6
- package/src/formats/neo4j/importer.ts +512 -73
- package/src/formats/neo4j/index.ts +75 -23
- package/src/formats/obo/exporter.ts +2210 -0
- package/src/formats/obo/importer.ts +622 -109
- package/src/formats/obo/index.ts +6 -3
- package/src/formats/obo/syntax.ts +232 -37
- package/src/formats/pajek/exporter.ts +89 -41
- package/src/formats/pajek/importer.ts +407 -93
- package/src/formats/pajek/index.ts +2 -1
- package/src/formats/pajek/syntax.ts +213 -52
- package/src/formats/xgmml/columns.ts +10 -0
- package/src/formats/xgmml/constants.ts +183 -56
- package/src/formats/xgmml/document.ts +212 -34
- package/src/formats/xgmml/emit.ts +175 -34
- package/src/formats/xgmml/exporter.ts +35 -27
- package/src/formats/xgmml/importer.ts +64 -86
- package/src/formats/xgmml/index.ts +1 -0
- package/src/formats/xgmml/values.ts +11 -3
- package/src/index.ts +81 -11
- package/src/registry.ts +1121 -135
- package/src/sniff.ts +163 -32
- package/src/types.ts +385 -114
- package/dist/chunks/escape-B-tkk_Cl.js.map +0 -1
- package/dist/chunks/export-Bh60Dv-n.js +0 -582
- package/dist/chunks/export-Bh60Dv-n.js.map +0 -1
- package/dist/chunks/exporter-BGrsWamJ.js.map +0 -1
- package/dist/chunks/exporter-DIeGJXAZ.js.map +0 -1
- package/dist/chunks/importer-BWY2FFc5.js.map +0 -1
- package/dist/chunks/importer-DD-xv9_Y.js +0 -1471
- package/dist/chunks/importer-DD-xv9_Y.js.map +0 -1
- package/dist/chunks/importer-DET1tg6s.js.map +0 -1
- package/dist/chunks/importer-DEcKhsmQ.js +0 -1614
- package/dist/chunks/importer-DEcKhsmQ.js.map +0 -1
- package/dist/chunks/importer-DIrbbnAf.js.map +0 -1
- package/dist/chunks/importer-D_e7e7LX.js.map +0 -1
- package/dist/chunks/importer-eUREhBGj.js.map +0 -1
- package/dist/chunks/importer-vi3bSdtw.js.map +0 -1
- package/dist/chunks/importer-zGLo8Dg_.js.map +0 -1
- package/dist/chunks/json-elements-DDS8N2Dd.js +0 -779
- package/dist/chunks/json-elements-DDS8N2Dd.js.map +0 -1
- package/dist/chunks/ontology-BnrJ4I98.js +0 -113
- package/dist/chunks/ontology-BnrJ4I98.js.map +0 -1
- package/dist/chunks/records-dbRkxwaq.js.map +0 -1
- package/dist/chunks/report-B1z4WT9e.js +0 -2372
- package/dist/chunks/report-B1z4WT9e.js.map +0 -1
- package/dist/chunks/text-Dr0Ifpag.js.map +0 -1
- package/dist/chunks/weights-Dzba96G3.js +0 -176
- package/dist/chunks/weights-Dzba96G3.js.map +0 -1
- package/dist/chunks/writer-C7flM-Ih.js.map +0 -1
package/dist/src/common/xml.js
CHANGED
|
@@ -8,11 +8,13 @@
|
|
|
8
8
|
* instead of re-reading the token from its `<`, and line numbers are counted from a cached next
|
|
9
9
|
* line-break position, so a document without line breaks costs the same as one with them.
|
|
10
10
|
*
|
|
11
|
-
* What it handles: the XML declaration and processing instructions (skipped), comments (skipped
|
|
11
|
+
* What it handles: the XML declaration and processing instructions (skipped), comments (skipped;
|
|
12
|
+
* a `--` inside one, which XML 1.0 forbids, is accepted: a comment carries no data and banner
|
|
13
|
+
* comments such as `<!-- ----- -->` are common in hand-written files),
|
|
12
14
|
* CDATA sections (text), a DOCTYPE with an internal subset (skipped; entity declarations are not
|
|
13
15
|
* expanded, so an unknown entity reference is a syntax error), the five predefined entities and
|
|
14
16
|
* numeric character references (decimal and hexadecimal) in text and attribute values, attribute
|
|
15
|
-
* value whitespace
|
|
17
|
+
* value whitespace normalization, end-of-line normalization (CR LF and lone CR become LF), and
|
|
16
18
|
* well-formedness: matching end tags, one root element, no text outside it, no duplicate
|
|
17
19
|
* attributes, no unterminated markup at the end of the input. Namespaces are not resolved; element
|
|
18
20
|
* and attribute names are reported as written (with their prefix).
|
|
@@ -26,7 +28,11 @@
|
|
|
26
28
|
*/
|
|
27
29
|
import { XML_ILLEGAL_CHAR_CODE } from "./codes.js";
|
|
28
30
|
import { isNameChar } from "./export.js";
|
|
29
|
-
|
|
31
|
+
import { agree, plural } from "./plural.js";
|
|
32
|
+
/**
|
|
33
|
+
* A well-formedness or syntax error, with the line it was found on.
|
|
34
|
+
* @category Plugin helpers
|
|
35
|
+
*/
|
|
30
36
|
export class XmlSyntaxError extends Error {
|
|
31
37
|
/**
|
|
32
38
|
* Create the error.
|
|
@@ -39,6 +45,21 @@ export class XmlSyntaxError extends Error {
|
|
|
39
45
|
this.line = line;
|
|
40
46
|
}
|
|
41
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* The input holds no markup at all (empty, whitespace only, a byte order mark only): an importer
|
|
50
|
+
* reports it as E_EMPTY_INPUT rather than as a syntax error.
|
|
51
|
+
* @category Plugin helpers
|
|
52
|
+
*/
|
|
53
|
+
export class XmlEmptyInputError extends XmlSyntaxError {
|
|
54
|
+
/**
|
|
55
|
+
* Create the error.
|
|
56
|
+
* @param line - the 1-based line where the input ended
|
|
57
|
+
*/
|
|
58
|
+
constructor(line) {
|
|
59
|
+
super("the input is empty: no root element", line);
|
|
60
|
+
this.name = "XmlEmptyInputError";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
42
63
|
/** How far Cytoscape's bare-ampersand repair looks for the `;` that ends an entity reference. */
|
|
43
64
|
const BARE_AMPERSAND_LOOKAHEAD = 7;
|
|
44
65
|
/** A numeric character reference at the start of a text: `{` or ``. */
|
|
@@ -62,6 +83,7 @@ const OPEN_BRACKET = 91;
|
|
|
62
83
|
const CLOSE_BRACKET = 93;
|
|
63
84
|
const BANG = 33;
|
|
64
85
|
const DASH = 45;
|
|
86
|
+
const QUESTION = 63;
|
|
65
87
|
/** The longest entity reference held back at a chunk boundary (`` is 10 characters). */
|
|
66
88
|
const MAX_ENTITY_LENGTH = 16;
|
|
67
89
|
/** Markup shorter than this (`<![CDATA[` is 9 characters) cannot be classified yet. */
|
|
@@ -108,10 +130,20 @@ const ILLEGAL_CHAR = new RegExp(`[${String.fromCharCode(0)}-${String.fromCharCod
|
|
|
108
130
|
* written even as a character reference, so a conforming parser rejects the whole document.
|
|
109
131
|
* @param text - the text
|
|
110
132
|
* @returns true when the text cannot appear in an XML 1.0 document
|
|
133
|
+
* @category Plugin helpers
|
|
111
134
|
*/
|
|
112
135
|
export function hasIllegalXmlChar(text) {
|
|
113
136
|
return ILLEGAL_CHAR.test(text);
|
|
114
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Where the first character XML 1.0 forbids is in a text.
|
|
140
|
+
* @param text - the text
|
|
141
|
+
* @returns its index, or -1
|
|
142
|
+
*/
|
|
143
|
+
function illegalCharIndex(text) {
|
|
144
|
+
const match = ILLEGAL_CHAR.exec(text);
|
|
145
|
+
return match === null ? -1 : match.index;
|
|
146
|
+
}
|
|
115
147
|
/**
|
|
116
148
|
* The loss notes of the texts a snapshot holds that no XML 1.0 document can carry (see
|
|
117
149
|
* hasIllegalXmlChar): one E_XML_ILLEGAL_CHAR note per column (node, edge and graph string / dict /
|
|
@@ -119,6 +151,7 @@ export function hasIllegalXmlChar(text) {
|
|
|
119
151
|
* the first such text. Shared by the GEXF and GraphML exporters' check().
|
|
120
152
|
* @param snapshot - the snapshot
|
|
121
153
|
* @returns the notes, empty when every text is writable
|
|
154
|
+
* @category Plugin helpers
|
|
122
155
|
*/
|
|
123
156
|
export function xmlIllegalTextNotes(snapshot) {
|
|
124
157
|
const notes = [];
|
|
@@ -132,7 +165,7 @@ export function xmlIllegalTextNotes(snapshot) {
|
|
|
132
165
|
if (ids > 0) {
|
|
133
166
|
notes.push(Object.freeze({
|
|
134
167
|
code: XML_ILLEGAL_CHAR_CODE,
|
|
135
|
-
message: `${ids} node id(
|
|
168
|
+
message: `${ids} node id${plural(ids)} ${agree(ids, "holds", "hold")} a character XML 1.0 cannot carry; the save fails`,
|
|
136
169
|
column: null,
|
|
137
170
|
count: ids,
|
|
138
171
|
}));
|
|
@@ -147,7 +180,7 @@ export function xmlIllegalTextNotes(snapshot) {
|
|
|
147
180
|
if (bad > 0) {
|
|
148
181
|
notes.push(Object.freeze({
|
|
149
182
|
code: XML_ILLEGAL_CHAR_CODE,
|
|
150
|
-
message: `${domain} column "${column.meta.name}": ${bad} value(
|
|
183
|
+
message: `${domain} column "${column.meta.name}": ${bad} value${plural(bad)} ${agree(bad, "holds", "hold")} a character XML 1.0 cannot carry; the save fails`,
|
|
151
184
|
column: column.meta.name,
|
|
152
185
|
count: bad,
|
|
153
186
|
}));
|
|
@@ -180,22 +213,44 @@ function countIllegalRows(column) {
|
|
|
180
213
|
}
|
|
181
214
|
return bad;
|
|
182
215
|
}
|
|
183
|
-
/**
|
|
216
|
+
/**
|
|
217
|
+
* The encoding pseudo-attribute of an XML declaration at the very start of a document (one
|
|
218
|
+
* anywhere else is a syntax error of the tokenizer).
|
|
219
|
+
*/
|
|
184
220
|
const XML_DECLARED_ENCODING = /^<\?xml\s[^>]*?\bencoding\s*=\s*["']([A-Za-z][A-Za-z0-9._-]*)["']/;
|
|
185
221
|
/**
|
|
186
222
|
* The encoding an XML document declares in its prolog (`<?xml version="1.0" encoding="..."?>`),
|
|
187
223
|
* for the shared byte decoder (common/input.ts).
|
|
188
224
|
* @param head - the start of the document, decoded as windows-1252
|
|
189
225
|
* @returns the declared label, or null when there is no declaration or it names no encoding
|
|
226
|
+
* @category Plugin helpers
|
|
190
227
|
*/
|
|
191
228
|
export function xmlDeclaredEncoding(head) {
|
|
192
229
|
const match = XML_DECLARED_ENCODING.exec(head);
|
|
193
230
|
return match === null ? null : match[1];
|
|
194
231
|
}
|
|
232
|
+
/**
|
|
233
|
+
* The head of a document as text for an XML format's sniff(): UTF-16 when a byte order mark (or
|
|
234
|
+
* BOM-less UTF-16 `<?`, XML 1.0 Appendix F) says so, else UTF-8, never failing.
|
|
235
|
+
* @param head - the first bytes
|
|
236
|
+
* @returns the text, a leading byte order mark removed
|
|
237
|
+
* @category Plugin helpers
|
|
238
|
+
*/
|
|
239
|
+
export function sniffXmlText(head) {
|
|
240
|
+
let label = "utf-8";
|
|
241
|
+
if ((head[0] === 0xff && head[1] === 0xfe) || (head[0] === 0x3c && head[1] === 0x00 && head[2] === 0x3f)) {
|
|
242
|
+
label = "utf-16le";
|
|
243
|
+
}
|
|
244
|
+
else if ((head[0] === 0xfe && head[1] === 0xff) || (head[0] === 0x00 && head[1] === 0x3c && head[3] === 0x3f)) {
|
|
245
|
+
label = "utf-16be";
|
|
246
|
+
}
|
|
247
|
+
return new TextDecoder(label, { fatal: false }).decode(head);
|
|
248
|
+
}
|
|
195
249
|
/**
|
|
196
250
|
* Whether a text is an XML Name.
|
|
197
251
|
* @param text - the text
|
|
198
252
|
* @returns true for a well-formed name
|
|
253
|
+
* @category Plugin helpers
|
|
199
254
|
*/
|
|
200
255
|
export function isXmlName(text) {
|
|
201
256
|
if (text.length === 0) {
|
|
@@ -230,6 +285,7 @@ function isXmlChar(cp) {
|
|
|
230
285
|
* @param line - the line, for errors
|
|
231
286
|
* @param repairs - the opt-in repairs (XGMML only); none by default
|
|
232
287
|
* @returns the decoded text
|
|
288
|
+
* @category Plugin helpers
|
|
233
289
|
*/
|
|
234
290
|
export function decodeEntities(raw, line, repairs) {
|
|
235
291
|
let amp = raw.indexOf("&");
|
|
@@ -252,13 +308,14 @@ export function decodeEntities(raw, line, repairs) {
|
|
|
252
308
|
continue;
|
|
253
309
|
}
|
|
254
310
|
if (semi < 0) {
|
|
255
|
-
throw new XmlSyntaxError("unterminated entity reference", line);
|
|
311
|
+
throw new XmlSyntaxError("unterminated entity reference", lineAt(raw, amp, line));
|
|
256
312
|
}
|
|
257
313
|
const name = raw.slice(amp + 1, semi);
|
|
258
314
|
if (name.startsWith("#")) {
|
|
259
315
|
const hex = name.startsWith("#x") || name.startsWith("#X");
|
|
260
316
|
const digits = name.slice(hex ? 2 : 1);
|
|
261
|
-
|
|
317
|
+
// XML 1.0 puts no limit on leading zeros (A is "A")
|
|
318
|
+
const ok = hex ? /^0*[\da-fA-F]{1,6}$/.test(digits) : /^0*\d{1,7}$/.test(digits);
|
|
262
319
|
const cp = ok ? Number.parseInt(digits, hex ? 16 : 10) : -1;
|
|
263
320
|
const low = cp >= 0xd800 && cp <= 0xdbff ? lowSurrogateAfter(raw, semi + 1, repairs) : null;
|
|
264
321
|
if (low !== null) {
|
|
@@ -269,14 +326,14 @@ export function decodeEntities(raw, line, repairs) {
|
|
|
269
326
|
continue;
|
|
270
327
|
}
|
|
271
328
|
if (cp < 0 || !isXmlChar(cp)) {
|
|
272
|
-
throw new XmlSyntaxError(`invalid character reference &${name};`, line);
|
|
329
|
+
throw new XmlSyntaxError(`invalid character reference &${name};`, lineAt(raw, amp, line));
|
|
273
330
|
}
|
|
274
331
|
out += String.fromCodePoint(cp);
|
|
275
332
|
}
|
|
276
333
|
else {
|
|
277
334
|
const value = NAMED_ENTITIES[name];
|
|
278
335
|
if (value === undefined) {
|
|
279
|
-
throw new XmlSyntaxError(`unknown entity &${name};`, line);
|
|
336
|
+
throw new XmlSyntaxError(`unknown entity &${name};`, lineAt(raw, amp, line));
|
|
280
337
|
}
|
|
281
338
|
out += value;
|
|
282
339
|
}
|
|
@@ -322,6 +379,7 @@ function lowSurrogateAfter(raw, at, repairs) {
|
|
|
322
379
|
* Whether a text is whitespace only.
|
|
323
380
|
* @param text - the text
|
|
324
381
|
* @returns true when every character is XML whitespace (also for an empty text)
|
|
382
|
+
* @category Plugin helpers
|
|
325
383
|
*/
|
|
326
384
|
export function isWhitespace(text) {
|
|
327
385
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -335,6 +393,7 @@ export function isWhitespace(text) {
|
|
|
335
393
|
* The local part of a possibly prefixed XML name.
|
|
336
394
|
* @param name - the name as written
|
|
337
395
|
* @returns the text after the last colon, or the name itself
|
|
396
|
+
* @category Plugin helpers
|
|
338
397
|
*/
|
|
339
398
|
export function localName(name) {
|
|
340
399
|
const colon = name.lastIndexOf(":");
|
|
@@ -346,13 +405,39 @@ export function localName(name) {
|
|
|
346
405
|
* @param chunks - the text (already UTF-8 decoded, BOM removed)
|
|
347
406
|
* @param handler - the event sink
|
|
348
407
|
* @param repairs - the opt-in repairs (XGMML only); none by default
|
|
408
|
+
* @category Plugin helpers
|
|
349
409
|
*/
|
|
350
410
|
export async function tokenizeXml(chunks, handler, repairs) {
|
|
351
411
|
const tokenizer = new XmlTokenizer(handler, repairs);
|
|
352
|
-
|
|
353
|
-
|
|
412
|
+
// XML 1.0 section 2.8: nothing may precede the XML declaration, not even whitespace (template
|
|
413
|
+
// output often adds a line break, and the declared encoding would then go unread)
|
|
414
|
+
let skipped = 0;
|
|
415
|
+
let lines = 1;
|
|
416
|
+
let leading = true;
|
|
417
|
+
try {
|
|
418
|
+
for await (const chunk of chunks) {
|
|
419
|
+
if (leading) {
|
|
420
|
+
const start = chunk.search(/\S/);
|
|
421
|
+
const blank = start < 0 ? chunk : chunk.slice(0, start);
|
|
422
|
+
skipped += blank.length;
|
|
423
|
+
lines += blank.split("\n").length - 1;
|
|
424
|
+
if (start >= 0) {
|
|
425
|
+
leading = false;
|
|
426
|
+
if (skipped > 0 && /^<\?xml\s/.test(chunk.slice(start, start + 6))) {
|
|
427
|
+
throw new XmlSyntaxError("the XML declaration must be at the very start of the document; whitespace precedes it", lines);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
tokenizer.push(chunk);
|
|
432
|
+
}
|
|
433
|
+
tokenizer.finish();
|
|
434
|
+
}
|
|
435
|
+
catch (err) {
|
|
436
|
+
if (err instanceof XmlSyntaxError && tokenizer.declaresXml11 && !err.message.includes("XML 1.1")) {
|
|
437
|
+
throw new XmlSyntaxError(`${err.message}; the document declares XML 1.1, which graph-io does not read (it reads XML 1.0)`, err.line);
|
|
438
|
+
}
|
|
439
|
+
throw err;
|
|
354
440
|
}
|
|
355
|
-
tokenizer.finish();
|
|
356
441
|
}
|
|
357
442
|
/** The terminator of each fixed-terminator markup kind. */
|
|
358
443
|
const TERMINATORS = {
|
|
@@ -361,9 +446,14 @@ const TERMINATORS = {
|
|
|
361
446
|
pi: "?>",
|
|
362
447
|
endtag: ">",
|
|
363
448
|
};
|
|
449
|
+
/** The namespace the `xml` prefix is bound to by definition. */
|
|
450
|
+
const XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
|
|
451
|
+
/** The markup declarations a truncated `<!...` at the end of the input may have been cut from. */
|
|
452
|
+
const DECLARATION_OPENERS = ["<!--", "<![CDATA[", "<!DOCTYPE"];
|
|
364
453
|
/**
|
|
365
454
|
* The tokenizer state: the unconsumed tail of the input, the current line, the open element
|
|
366
455
|
* stack and the text run being accumulated. `push()` chunks, then `finish()`.
|
|
456
|
+
* @category Plugin helpers
|
|
367
457
|
*/
|
|
368
458
|
export class XmlTokenizer {
|
|
369
459
|
/**
|
|
@@ -393,15 +483,47 @@ export class XmlTokenizer {
|
|
|
393
483
|
this.stack = [];
|
|
394
484
|
this.rootSeen = false;
|
|
395
485
|
this.rootClosed = false;
|
|
486
|
+
/** Whether any markup was consumed (an input with none is empty, not malformed). */
|
|
487
|
+
this.markupSeen = false;
|
|
488
|
+
/** Whether nothing has been consumed yet: the XML declaration may only appear here. */
|
|
489
|
+
this.atStart = true;
|
|
490
|
+
/** Whether a DOCTYPE declaration was read (one is allowed, in the prolog). */
|
|
491
|
+
this.doctypeSeen = false;
|
|
492
|
+
/** The version of the XML declaration, or null. */
|
|
493
|
+
this.version = null;
|
|
494
|
+
/** The last two characters of the character data since the last markup, for the `]]>` check. */
|
|
495
|
+
this.textTail = "";
|
|
496
|
+
/** A high surrogate held back from the end of the previous chunk (its low half may start the next). */
|
|
497
|
+
this.pendingHigh = "";
|
|
498
|
+
/** The namespace declarations in scope, innermost last. */
|
|
499
|
+
this.bindings = [];
|
|
396
500
|
this.handler = handler;
|
|
397
501
|
this.repairs = repairs;
|
|
398
502
|
}
|
|
503
|
+
/**
|
|
504
|
+
* Whether the document's XML declaration says version 1.1 (read by the XML 1.0 rules all the
|
|
505
|
+
* same; the syntax error of a 1.1-only construct then says why).
|
|
506
|
+
* @returns true after a `<?xml version="1.1"?>` declaration
|
|
507
|
+
*/
|
|
508
|
+
get declaresXml11() {
|
|
509
|
+
return this.version === "1.1";
|
|
510
|
+
}
|
|
399
511
|
/**
|
|
400
512
|
* Feed one chunk and emit every complete token in it.
|
|
401
513
|
* @param chunk - the text
|
|
402
514
|
*/
|
|
403
515
|
push(chunk) {
|
|
404
516
|
let text = chunk;
|
|
517
|
+
if (this.pendingHigh.length > 0) {
|
|
518
|
+
text = this.pendingHigh + text;
|
|
519
|
+
this.pendingHigh = "";
|
|
520
|
+
}
|
|
521
|
+
const last = text.charCodeAt(text.length - 1); // NOSONAR(S7758): reads UTF-16 code units on purpose
|
|
522
|
+
if (last >= 0xd800 && last <= 0xdbff) {
|
|
523
|
+
// a text chunk may end between the two halves of an astral character
|
|
524
|
+
this.pendingHigh = text.slice(-1);
|
|
525
|
+
text = text.slice(0, -1);
|
|
526
|
+
}
|
|
405
527
|
if (this.pendingCr) {
|
|
406
528
|
text = `\r${text}`;
|
|
407
529
|
this.pendingCr = false;
|
|
@@ -418,12 +540,14 @@ export class XmlTokenizer {
|
|
|
418
540
|
/** Signal the end of the input: flush the last text run and check well-formedness. */
|
|
419
541
|
finish() {
|
|
420
542
|
this.final = true;
|
|
543
|
+
const held = this.pendingHigh;
|
|
544
|
+
this.pendingHigh = "";
|
|
421
545
|
if (this.pendingCr) {
|
|
422
546
|
this.pendingCr = false;
|
|
423
|
-
this.feed(
|
|
547
|
+
this.feed(`\n${held}`);
|
|
424
548
|
}
|
|
425
549
|
else {
|
|
426
|
-
this.feed(
|
|
550
|
+
this.feed(held);
|
|
427
551
|
}
|
|
428
552
|
if (this.pending !== null || this.buffer.length > 0) {
|
|
429
553
|
throw new XmlSyntaxError("unexpected end of input inside markup", this.line);
|
|
@@ -433,13 +557,16 @@ export class XmlTokenizer {
|
|
|
433
557
|
throw new XmlSyntaxError(`unclosed element <${this.stack[this.stack.length - 1]}>`, this.line);
|
|
434
558
|
}
|
|
435
559
|
if (!this.rootSeen) {
|
|
560
|
+
if (!this.markupSeen) {
|
|
561
|
+
throw new XmlEmptyInputError(this.line);
|
|
562
|
+
}
|
|
436
563
|
throw new XmlSyntaxError("no root element", this.line);
|
|
437
564
|
}
|
|
438
565
|
}
|
|
439
566
|
/**
|
|
440
|
-
* Append
|
|
567
|
+
* Append normalized text: continue a pending token's terminator scan over the new text alone,
|
|
441
568
|
* and once the token is complete (or when none is pending) scan the buffer for tokens.
|
|
442
|
-
* @param text - the text, line breaks
|
|
569
|
+
* @param text - the text, line breaks normalized
|
|
443
570
|
*/
|
|
444
571
|
feed(text) {
|
|
445
572
|
const { pending } = this;
|
|
@@ -546,9 +673,20 @@ export class XmlTokenizer {
|
|
|
546
673
|
* @returns the index one past the `>`, or -1
|
|
547
674
|
*/
|
|
548
675
|
scanDoctype(pending, text, from) {
|
|
549
|
-
let { quote, depth, comment, run } = pending;
|
|
676
|
+
let { quote, depth, comment, pi, run } = pending;
|
|
550
677
|
for (let i = from; i < text.length; i++) {
|
|
551
678
|
const c = text.charCodeAt(i);
|
|
679
|
+
if (pi) {
|
|
680
|
+
// a processing instruction in the subset ends at `?>`; quotes inside it are text
|
|
681
|
+
if (c === GT && run === 1) {
|
|
682
|
+
pi = false;
|
|
683
|
+
run = 0;
|
|
684
|
+
}
|
|
685
|
+
else {
|
|
686
|
+
run = c === QUESTION ? 1 : 0;
|
|
687
|
+
}
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
552
690
|
if (comment) {
|
|
553
691
|
if (c === DASH) {
|
|
554
692
|
run++;
|
|
@@ -582,6 +720,11 @@ export class XmlTokenizer {
|
|
|
582
720
|
run = 0;
|
|
583
721
|
continue;
|
|
584
722
|
}
|
|
723
|
+
if (run === 1 && c === QUESTION) {
|
|
724
|
+
pi = true;
|
|
725
|
+
run = 0;
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
585
728
|
run = 0;
|
|
586
729
|
if (c === LT) {
|
|
587
730
|
run = 1;
|
|
@@ -601,6 +744,7 @@ export class XmlTokenizer {
|
|
|
601
744
|
pending.quote = 0;
|
|
602
745
|
pending.depth = 0;
|
|
603
746
|
pending.comment = false;
|
|
747
|
+
pending.pi = false;
|
|
604
748
|
pending.run = 0;
|
|
605
749
|
return i + 1;
|
|
606
750
|
}
|
|
@@ -608,6 +752,7 @@ export class XmlTokenizer {
|
|
|
608
752
|
pending.quote = quote;
|
|
609
753
|
pending.depth = depth;
|
|
610
754
|
pending.comment = comment;
|
|
755
|
+
pending.pi = pi;
|
|
611
756
|
pending.run = run;
|
|
612
757
|
return -1;
|
|
613
758
|
}
|
|
@@ -628,7 +773,11 @@ export class XmlTokenizer {
|
|
|
628
773
|
if (!this.final) {
|
|
629
774
|
// an entity reference may be split across chunks: hold back from its "&"
|
|
630
775
|
const amp = buffer.lastIndexOf("&");
|
|
631
|
-
if (amp >= pos &&
|
|
776
|
+
if (amp >= pos &&
|
|
777
|
+
!buffer.includes(";", amp) &&
|
|
778
|
+
// ponytail: a zero-padded reference longer than MAX_ENTITY_LENGTH is held back
|
|
779
|
+
// whole, so an absurdly long run of zeros is re-scanned once per chunk
|
|
780
|
+
(amp >= length - MAX_ENTITY_LENGTH || /^&#[xX]?0[\da-fA-F]*$/.test(buffer.slice(amp)))) {
|
|
632
781
|
end = amp;
|
|
633
782
|
}
|
|
634
783
|
if (this.repairs?.surrogatePair !== undefined) {
|
|
@@ -675,6 +824,7 @@ export class XmlTokenizer {
|
|
|
675
824
|
quote: 0,
|
|
676
825
|
depth: 0,
|
|
677
826
|
comment: false,
|
|
827
|
+
pi: false,
|
|
678
828
|
run: 0,
|
|
679
829
|
};
|
|
680
830
|
if (piece.length < MIN_DECIDABLE_MARKUP) {
|
|
@@ -723,60 +873,31 @@ export class XmlTokenizer {
|
|
|
723
873
|
* @returns the index after the markup, or -1 when the buffer ends before the markup does
|
|
724
874
|
*/
|
|
725
875
|
consumeMarkup(pos) {
|
|
876
|
+
const end = this.markupEnd(pos);
|
|
877
|
+
if (end >= 0) {
|
|
878
|
+
this.markupSeen = true;
|
|
879
|
+
this.atStart = false;
|
|
880
|
+
// markup ends a run of character data: a `]]>` cannot straddle it
|
|
881
|
+
this.textTail = "";
|
|
882
|
+
}
|
|
883
|
+
return end;
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Read the markup starting at `pos` (a `<`) and deliver what it means.
|
|
887
|
+
* @param pos - the index of the `<`
|
|
888
|
+
* @returns the index after the markup, or -1 when the buffer ends before the markup does
|
|
889
|
+
*/
|
|
890
|
+
markupEnd(pos) {
|
|
726
891
|
const { buffer } = this;
|
|
727
892
|
if (buffer.startsWith("<!", pos)) {
|
|
728
|
-
|
|
729
|
-
// "<![CDATA[" is 9 characters; wait until the kind of declaration is decidable
|
|
730
|
-
return -1;
|
|
731
|
-
}
|
|
732
|
-
if (buffer.startsWith("<!--", pos)) {
|
|
733
|
-
const end = buffer.indexOf("-->", pos + 4);
|
|
734
|
-
if (end < 0) {
|
|
735
|
-
return -1;
|
|
736
|
-
}
|
|
737
|
-
this.advanceLine(pos, end + 3);
|
|
738
|
-
return end + 3;
|
|
739
|
-
}
|
|
740
|
-
if (buffer.startsWith("<![CDATA[", pos)) {
|
|
741
|
-
const end = buffer.indexOf("]]>", pos + 9);
|
|
742
|
-
if (end < 0) {
|
|
743
|
-
return -1;
|
|
744
|
-
}
|
|
745
|
-
if (this.text.length === 0) {
|
|
746
|
-
this.textLine = this.line;
|
|
747
|
-
}
|
|
748
|
-
const cdata = buffer.slice(pos + 9, end);
|
|
749
|
-
if (hasIllegalXmlChar(cdata)) {
|
|
750
|
-
throw new XmlSyntaxError("a character XML 1.0 forbids appears in a CDATA section", this.line);
|
|
751
|
-
}
|
|
752
|
-
this.text += cdata;
|
|
753
|
-
this.advanceLine(pos, end + 3);
|
|
754
|
-
return end + 3;
|
|
755
|
-
}
|
|
756
|
-
if (!buffer.startsWith("<!DOCTYPE", pos)) {
|
|
757
|
-
throw new XmlSyntaxError("unexpected markup declaration", this.line);
|
|
758
|
-
}
|
|
759
|
-
const state = {
|
|
760
|
-
kind: "doctype",
|
|
761
|
-
pieces: [],
|
|
762
|
-
tail: "",
|
|
763
|
-
quote: 0,
|
|
764
|
-
depth: 0,
|
|
765
|
-
comment: false,
|
|
766
|
-
run: 0,
|
|
767
|
-
};
|
|
768
|
-
const end = this.scanDoctype(state, buffer, pos + 9);
|
|
769
|
-
if (end < 0) {
|
|
770
|
-
return -1;
|
|
771
|
-
}
|
|
772
|
-
this.advanceLine(pos, end);
|
|
773
|
-
return end;
|
|
893
|
+
return this.declarationEnd(pos);
|
|
774
894
|
}
|
|
775
895
|
if (buffer.startsWith("<?", pos)) {
|
|
776
896
|
const end = buffer.indexOf("?>", pos + 2);
|
|
777
897
|
if (end < 0) {
|
|
778
898
|
return -1;
|
|
779
899
|
}
|
|
900
|
+
this.processingInstruction(pos, end);
|
|
780
901
|
this.advanceLine(pos, end + 2);
|
|
781
902
|
return end + 2;
|
|
782
903
|
}
|
|
@@ -785,7 +906,8 @@ export class XmlTokenizer {
|
|
|
785
906
|
if (end < 0) {
|
|
786
907
|
return -1;
|
|
787
908
|
}
|
|
788
|
-
|
|
909
|
+
// whitespace may follow the name, never precede it
|
|
910
|
+
const name = buffer.slice(pos + 2, end).trimEnd();
|
|
789
911
|
if (!isXmlName(name)) {
|
|
790
912
|
throw new XmlSyntaxError(`malformed end tag </${name}>`, this.line);
|
|
791
913
|
}
|
|
@@ -799,13 +921,149 @@ export class XmlTokenizer {
|
|
|
799
921
|
return -1;
|
|
800
922
|
}
|
|
801
923
|
this.flushText();
|
|
802
|
-
this.startElement(tag.name, tag.attrs);
|
|
924
|
+
this.startElement(tag.name, tag.attrs, tag.declaresNamespace);
|
|
803
925
|
if (tag.selfClosing) {
|
|
804
926
|
this.endElement(tag.name);
|
|
805
927
|
}
|
|
806
928
|
this.advanceLine(pos, tag.end);
|
|
807
929
|
return tag.end;
|
|
808
930
|
}
|
|
931
|
+
/**
|
|
932
|
+
* Read the CDATA section at `pos` into the text.
|
|
933
|
+
* @param pos - the index of the `<`
|
|
934
|
+
* @returns the index after the section, or -1 when the buffer ends before the section does
|
|
935
|
+
*/
|
|
936
|
+
cdataEnd(pos) {
|
|
937
|
+
const end = this.buffer.indexOf("]]>", pos + 9);
|
|
938
|
+
if (end < 0) {
|
|
939
|
+
return -1;
|
|
940
|
+
}
|
|
941
|
+
if (this.text.length === 0) {
|
|
942
|
+
this.textLine = this.line;
|
|
943
|
+
}
|
|
944
|
+
const cdata = this.buffer.slice(pos + 9, end);
|
|
945
|
+
const bad = illegalCharIndex(cdata);
|
|
946
|
+
if (bad >= 0) {
|
|
947
|
+
throw new XmlSyntaxError("a character XML 1.0 forbids appears in a CDATA section", this.lineIn(pos, pos + 9 + bad));
|
|
948
|
+
}
|
|
949
|
+
this.text += cdata;
|
|
950
|
+
this.advanceLine(pos, end + 3);
|
|
951
|
+
return end + 3;
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Read the `<!...>` markup at `pos`: a comment, a CDATA section or the DOCTYPE.
|
|
955
|
+
* @param pos - the index of the `<`
|
|
956
|
+
* @returns the index after the markup, or -1 when the buffer ends before the markup does
|
|
957
|
+
*/
|
|
958
|
+
declarationEnd(pos) {
|
|
959
|
+
const { buffer } = this;
|
|
960
|
+
if (buffer.length - pos < MIN_DECIDABLE_MARKUP) {
|
|
961
|
+
// "<![CDATA[" is 9 characters; wait until the kind of declaration is decidable, and at
|
|
962
|
+
// the end of the input read the start of one cut short as a truncation
|
|
963
|
+
const head = buffer.slice(pos);
|
|
964
|
+
if (!this.final || DECLARATION_OPENERS.some((opener) => opener.startsWith(head))) {
|
|
965
|
+
return -1;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (buffer.startsWith("<!--", pos)) {
|
|
969
|
+
const end = buffer.indexOf("-->", pos + 4);
|
|
970
|
+
if (end < 0) {
|
|
971
|
+
return -1;
|
|
972
|
+
}
|
|
973
|
+
// a "--" inside, which XML 1.0 forbids, is accepted: the comment carries no data
|
|
974
|
+
this.advanceLine(pos, end + 3);
|
|
975
|
+
return end + 3;
|
|
976
|
+
}
|
|
977
|
+
if (buffer.startsWith("<![CDATA[", pos)) {
|
|
978
|
+
return this.cdataEnd(pos);
|
|
979
|
+
}
|
|
980
|
+
if (!buffer.startsWith("<!DOCTYPE", pos)) {
|
|
981
|
+
if (buffer.slice(pos + 2, pos + 9).toUpperCase() === "DOCTYPE") {
|
|
982
|
+
throw new XmlSyntaxError(`<!${buffer.slice(pos + 2, pos + 9)}> is not XML (it must be written <!DOCTYPE>); the input looks like an HTML document`, this.line);
|
|
983
|
+
}
|
|
984
|
+
throw new XmlSyntaxError("unexpected markup declaration", this.line);
|
|
985
|
+
}
|
|
986
|
+
if (this.rootSeen || this.doctypeSeen) {
|
|
987
|
+
throw new XmlSyntaxError("a DOCTYPE declaration is allowed once, before the root element", this.line);
|
|
988
|
+
}
|
|
989
|
+
const state = {
|
|
990
|
+
kind: "doctype",
|
|
991
|
+
pieces: [],
|
|
992
|
+
tail: "",
|
|
993
|
+
quote: 0,
|
|
994
|
+
depth: 0,
|
|
995
|
+
comment: false,
|
|
996
|
+
pi: false,
|
|
997
|
+
run: 0,
|
|
998
|
+
};
|
|
999
|
+
const end = this.scanDoctype(state, buffer, pos + 9);
|
|
1000
|
+
if (end < 0) {
|
|
1001
|
+
return -1;
|
|
1002
|
+
}
|
|
1003
|
+
this.doctypeSeen = true;
|
|
1004
|
+
this.handler.doctype?.(buffer.slice(pos, end), this.line);
|
|
1005
|
+
this.advanceLine(pos, end);
|
|
1006
|
+
return end;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Check a processing instruction `<?target ...?>` (skipped otherwise): its target must be a
|
|
1010
|
+
* name, and the reserved target `xml` is the XML declaration, allowed only at the very start
|
|
1011
|
+
* of the document with a 1.x version.
|
|
1012
|
+
* @param pos - the index of the `<`
|
|
1013
|
+
* @param end - the index of the `?>`
|
|
1014
|
+
*/
|
|
1015
|
+
processingInstruction(pos, end) {
|
|
1016
|
+
const body = this.buffer.slice(pos + 2, end);
|
|
1017
|
+
const target = /^[^\s]*/.exec(body)?.[0] ?? "";
|
|
1018
|
+
if (!isXmlName(target)) {
|
|
1019
|
+
throw new XmlSyntaxError(`a processing instruction needs a target name, not "${target}"`, this.line);
|
|
1020
|
+
}
|
|
1021
|
+
if (target.toLowerCase() !== "xml") {
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
if (target !== "xml") {
|
|
1025
|
+
throw new XmlSyntaxError(`<?${target} is not an XML declaration (which is written <?xml in lower case); the target is reserved`, this.line);
|
|
1026
|
+
}
|
|
1027
|
+
if (!this.atStart || pos !== 0) {
|
|
1028
|
+
throw new XmlSyntaxError("an XML declaration (<?xml ...?>) may appear only at the very start of the document; the target xml is reserved", this.line);
|
|
1029
|
+
}
|
|
1030
|
+
const version = /\bversion\s*=\s*(["'])([^"']*)\1/.exec(body)?.[2];
|
|
1031
|
+
if (version === undefined) {
|
|
1032
|
+
throw new XmlSyntaxError("the XML declaration has no version", this.line);
|
|
1033
|
+
}
|
|
1034
|
+
if (!/^1\.\d+$/.test(version)) {
|
|
1035
|
+
throw new XmlSyntaxError(`XML version ${version} is not supported (only 1.x)`, this.line);
|
|
1036
|
+
}
|
|
1037
|
+
this.version = version;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* The line of a buffer position after `start`, the position the current line count refers to.
|
|
1041
|
+
* Used on the error path only.
|
|
1042
|
+
* @param start - a position on line `this.line`
|
|
1043
|
+
* @param at - the position
|
|
1044
|
+
* @returns its line
|
|
1045
|
+
*/
|
|
1046
|
+
lineIn(start, at) {
|
|
1047
|
+
return lineAt(this.buffer.slice(start, at), at - start, this.line);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
1050
|
+
* Decode the entities of a text, naming XML 1.1 when a character reference XML 1.0 forbids
|
|
1051
|
+
* appears in a document that declares version 1.1.
|
|
1052
|
+
* @param raw - the text as written
|
|
1053
|
+
* @param line - the line the text starts on
|
|
1054
|
+
* @returns the decoded text
|
|
1055
|
+
*/
|
|
1056
|
+
decode(raw, line) {
|
|
1057
|
+
try {
|
|
1058
|
+
return decodeEntities(raw, line, this.repairs);
|
|
1059
|
+
}
|
|
1060
|
+
catch (err) {
|
|
1061
|
+
if (err instanceof XmlSyntaxError && this.version !== null && this.version !== "1.0") {
|
|
1062
|
+
throw new XmlSyntaxError(`${err.message} (the document declares XML ${this.version}, whose character rules are not supported; it is read as XML 1.0)`, err.line);
|
|
1063
|
+
}
|
|
1064
|
+
throw err;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
809
1067
|
/**
|
|
810
1068
|
* Parse a start tag at `pos`.
|
|
811
1069
|
* @param pos - the index of the `<`
|
|
@@ -825,7 +1083,9 @@ export class XmlTokenizer {
|
|
|
825
1083
|
const name = buffer.slice(i, nameEnd);
|
|
826
1084
|
i = nameEnd;
|
|
827
1085
|
const attrs = new Map();
|
|
1086
|
+
let declaresNamespace = false;
|
|
828
1087
|
for (;;) {
|
|
1088
|
+
const before = i;
|
|
829
1089
|
while (i < length && isSpace(buffer.charCodeAt(i))) {
|
|
830
1090
|
i++;
|
|
831
1091
|
}
|
|
@@ -834,23 +1094,27 @@ export class XmlTokenizer {
|
|
|
834
1094
|
}
|
|
835
1095
|
const c = buffer.charCodeAt(i);
|
|
836
1096
|
if (c === GT) {
|
|
837
|
-
return { name, attrs, selfClosing: false, end: i + 1 };
|
|
1097
|
+
return { name, attrs, selfClosing: false, declaresNamespace, end: i + 1 };
|
|
838
1098
|
}
|
|
839
1099
|
if (c === SLASH) {
|
|
840
1100
|
if (i + 1 >= length) {
|
|
841
1101
|
return null;
|
|
842
1102
|
}
|
|
843
1103
|
if (buffer.charCodeAt(i + 1) !== GT) {
|
|
844
|
-
throw new XmlSyntaxError(`unexpected "/" in <${name}>`, this.
|
|
1104
|
+
throw new XmlSyntaxError(`unexpected "/" in <${name}>`, this.lineIn(pos, i));
|
|
845
1105
|
}
|
|
846
|
-
return { name, attrs, selfClosing: true, end: i + 2 };
|
|
1106
|
+
return { name, attrs, selfClosing: true, declaresNamespace, end: i + 2 };
|
|
1107
|
+
}
|
|
1108
|
+
if (i === before) {
|
|
1109
|
+
// the name, or the previous value's closing quote, runs into the next attribute
|
|
1110
|
+
throw new XmlSyntaxError(`the attributes of <${name}> must be separated by whitespace`, this.lineIn(pos, i));
|
|
847
1111
|
}
|
|
848
1112
|
const attrEnd = this.readName(i);
|
|
849
1113
|
if (attrEnd < 0) {
|
|
850
1114
|
return null;
|
|
851
1115
|
}
|
|
852
1116
|
if (attrEnd === i) {
|
|
853
|
-
throw new XmlSyntaxError(`malformed attribute in <${name}>`, this.
|
|
1117
|
+
throw new XmlSyntaxError(`malformed attribute in <${name}>`, this.lineIn(pos, i));
|
|
854
1118
|
}
|
|
855
1119
|
const attrName = buffer.slice(i, attrEnd);
|
|
856
1120
|
i = attrEnd;
|
|
@@ -861,7 +1125,7 @@ export class XmlTokenizer {
|
|
|
861
1125
|
return null;
|
|
862
1126
|
}
|
|
863
1127
|
if (buffer.charCodeAt(i) !== EQUALS) {
|
|
864
|
-
throw new XmlSyntaxError(`attribute ${attrName} of <${name}> has no value`, this.
|
|
1128
|
+
throw new XmlSyntaxError(`attribute ${attrName} of <${name}> has no value`, this.lineIn(pos, i));
|
|
865
1129
|
}
|
|
866
1130
|
i++;
|
|
867
1131
|
while (i < length && isSpace(buffer.charCodeAt(i))) {
|
|
@@ -872,20 +1136,27 @@ export class XmlTokenizer {
|
|
|
872
1136
|
}
|
|
873
1137
|
const quote = buffer.charCodeAt(i);
|
|
874
1138
|
if (quote !== QUOTE && quote !== APOS) {
|
|
875
|
-
throw new XmlSyntaxError(`attribute ${attrName} of <${name}> is not quoted`, this.
|
|
1139
|
+
throw new XmlSyntaxError(`attribute ${attrName} of <${name}> is not quoted`, this.lineIn(pos, i));
|
|
876
1140
|
}
|
|
877
1141
|
const close = buffer.indexOf(quote === QUOTE ? '"' : "'", i + 1);
|
|
878
1142
|
if (close < 0) {
|
|
879
1143
|
return null;
|
|
880
1144
|
}
|
|
1145
|
+
const line = this.lineIn(pos, i);
|
|
881
1146
|
if (attrs.has(attrName)) {
|
|
882
|
-
throw new XmlSyntaxError(`duplicate attribute ${attrName} in <${name}>`,
|
|
1147
|
+
throw new XmlSyntaxError(`duplicate attribute ${attrName} in <${name}>`, line);
|
|
883
1148
|
}
|
|
884
1149
|
const raw = buffer.slice(i + 1, close);
|
|
885
1150
|
if (hasIllegalXmlChar(raw)) {
|
|
886
|
-
throw new XmlSyntaxError(`a character XML 1.0 forbids appears in attribute ${attrName} of <${name}>`,
|
|
1151
|
+
throw new XmlSyntaxError(`a character XML 1.0 forbids appears in attribute ${attrName} of <${name}>`, line);
|
|
887
1152
|
}
|
|
888
|
-
|
|
1153
|
+
if (raw.includes("<")) {
|
|
1154
|
+
throw new XmlSyntaxError(`a "<" in the value of attribute ${attrName} of <${name}> (write <)`, line);
|
|
1155
|
+
}
|
|
1156
|
+
if (attrName.startsWith("xmlns")) {
|
|
1157
|
+
declaresNamespace = true;
|
|
1158
|
+
}
|
|
1159
|
+
attrs.set(attrName, this.decode(normalizeAttributeValue(raw), line));
|
|
889
1160
|
i = close + 1;
|
|
890
1161
|
}
|
|
891
1162
|
}
|
|
@@ -946,10 +1217,20 @@ export class XmlTokenizer {
|
|
|
946
1217
|
this.textLine = this.line;
|
|
947
1218
|
}
|
|
948
1219
|
const raw = this.buffer.slice(start, end);
|
|
949
|
-
|
|
950
|
-
|
|
1220
|
+
const bad = illegalCharIndex(raw);
|
|
1221
|
+
if (bad >= 0) {
|
|
1222
|
+
throw new XmlSyntaxError("a character XML 1.0 forbids appears in character data", lineAt(raw, bad, this.line));
|
|
951
1223
|
}
|
|
952
|
-
|
|
1224
|
+
if (raw.includes(">")) {
|
|
1225
|
+
const probe = this.textTail + raw;
|
|
1226
|
+
const close = probe.indexOf("]]>");
|
|
1227
|
+
if (close >= 0) {
|
|
1228
|
+
throw new XmlSyntaxError('"]]>" is not allowed in character data (write ]]>)', lineAt(raw, Math.max(0, close - this.textTail.length), this.line));
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
this.textTail = raw.length >= 2 ? raw.slice(-2) : (this.textTail + raw).slice(-2);
|
|
1232
|
+
this.atStart = false;
|
|
1233
|
+
this.text += this.decode(raw, this.line);
|
|
953
1234
|
this.advanceLine(start, end);
|
|
954
1235
|
}
|
|
955
1236
|
/**
|
|
@@ -992,16 +1273,49 @@ export class XmlTokenizer {
|
|
|
992
1273
|
* Open an element.
|
|
993
1274
|
* @param name - the element name
|
|
994
1275
|
* @param attrs - its attributes
|
|
1276
|
+
* @param declaresNamespace - whether an attribute name starts with `xmlns`
|
|
995
1277
|
*/
|
|
996
|
-
startElement(name, attrs) {
|
|
1278
|
+
startElement(name, attrs, declaresNamespace) {
|
|
997
1279
|
if (this.stack.length === 0) {
|
|
998
1280
|
if (this.rootClosed) {
|
|
999
1281
|
throw new XmlSyntaxError(`a second root element <${name}> follows the document element`, this.line);
|
|
1000
1282
|
}
|
|
1001
1283
|
this.rootSeen = true;
|
|
1002
1284
|
}
|
|
1285
|
+
if (declaresNamespace) {
|
|
1286
|
+
for (const [key, uri] of attrs) {
|
|
1287
|
+
if (key === "xmlns") {
|
|
1288
|
+
this.bindings.push({ prefix: "", uri, depth: this.stack.length });
|
|
1289
|
+
}
|
|
1290
|
+
else if (key.startsWith("xmlns:")) {
|
|
1291
|
+
this.bindings.push({ prefix: key.slice(6), uri, depth: this.stack.length });
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1003
1295
|
this.stack.push(name);
|
|
1004
|
-
this.handler.start(name, attrs, this.line);
|
|
1296
|
+
this.handler.start(name, attrs, this.line, this.namespaceOf(name));
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* The namespace an element name is in (Namespaces in XML 1.0 section 6).
|
|
1300
|
+
* @param name - the element name as written
|
|
1301
|
+
* @returns the URI; "" for no namespace; null for an undeclared prefix or a name that is not
|
|
1302
|
+
* namespace-well-formed
|
|
1303
|
+
*/
|
|
1304
|
+
namespaceOf(name) {
|
|
1305
|
+
const colon = name.indexOf(":");
|
|
1306
|
+
if (colon >= 0 && (colon === 0 || colon === name.length - 1 || name.includes(":", colon + 1))) {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
const prefix = colon < 0 ? "" : name.slice(0, colon);
|
|
1310
|
+
for (let i = this.bindings.length - 1; i >= 0; i--) {
|
|
1311
|
+
if (this.bindings[i].prefix === prefix) {
|
|
1312
|
+
return this.bindings[i].uri;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if (prefix === "xml") {
|
|
1316
|
+
return XML_NAMESPACE;
|
|
1317
|
+
}
|
|
1318
|
+
return prefix === "" ? "" : null;
|
|
1005
1319
|
}
|
|
1006
1320
|
/**
|
|
1007
1321
|
* Close an element, checking that it matches the innermost open one.
|
|
@@ -1016,6 +1330,9 @@ export class XmlTokenizer {
|
|
|
1016
1330
|
throw new XmlSyntaxError(`end tag </${name}> does not match <${open}>`, this.line);
|
|
1017
1331
|
}
|
|
1018
1332
|
this.stack.pop();
|
|
1333
|
+
while ((this.bindings.at(-1)?.depth ?? -1) >= this.stack.length) {
|
|
1334
|
+
this.bindings.pop();
|
|
1335
|
+
}
|
|
1019
1336
|
if (this.stack.length === 0) {
|
|
1020
1337
|
this.rootClosed = true;
|
|
1021
1338
|
}
|
|
@@ -1023,10 +1340,10 @@ export class XmlTokenizer {
|
|
|
1023
1340
|
}
|
|
1024
1341
|
}
|
|
1025
1342
|
/**
|
|
1026
|
-
* Attribute value
|
|
1343
|
+
* Attribute value normalization (XML 1.0 section 3.3.3): a literal tab or line break becomes a
|
|
1027
1344
|
* space; a character reference to one is kept, which is why this runs before entity decoding.
|
|
1028
1345
|
* @param raw - the value between the quotes
|
|
1029
|
-
* @returns the
|
|
1346
|
+
* @returns the normalized value
|
|
1030
1347
|
*/
|
|
1031
1348
|
function normalizeAttributeValue(raw) {
|
|
1032
1349
|
return raw.includes("\n") || raw.includes("\t") ? raw.replace(/[\n\t]/g, " ") : raw;
|