@graphty/graph-io 0.3.24 → 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
|
@@ -34,8 +34,10 @@ import {
|
|
|
34
34
|
DANGLING_REFERENCE_CODE,
|
|
35
35
|
DUPLICATE_ATTRIBUTE_CODE,
|
|
36
36
|
DUPLICATE_NODE_CODE,
|
|
37
|
+
ELEMENT_ISSUE,
|
|
37
38
|
EMPTY_INPUT_CODE,
|
|
38
39
|
ENCODING_FALLBACK_CODE,
|
|
40
|
+
INPUT_ISSUE,
|
|
39
41
|
INVALID_ENCODING_CODE,
|
|
40
42
|
INVALID_UTF8_CODE,
|
|
41
43
|
MISSING_ID_CODE,
|
|
@@ -54,6 +56,7 @@ import {
|
|
|
54
56
|
resolveImportOptions,
|
|
55
57
|
SINK_OPTION_CODE,
|
|
56
58
|
} from "../../common/options.js";
|
|
59
|
+
import { agree, plural } from "../../common/plural.js";
|
|
57
60
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
58
61
|
import { type CommonImportOptions, type GraphImporter, type ImportInput, type ImportReport } from "../../types.js";
|
|
59
62
|
import {
|
|
@@ -62,30 +65,51 @@ import {
|
|
|
62
65
|
parseXref,
|
|
63
66
|
parseXrefList,
|
|
64
67
|
type Qualifiers,
|
|
68
|
+
splitFormFeeds,
|
|
65
69
|
splitQualifiers,
|
|
66
70
|
splitTagValue,
|
|
67
71
|
stripComment,
|
|
68
72
|
type Token,
|
|
69
73
|
tokenize,
|
|
74
|
+
unclosedBlock,
|
|
70
75
|
unescapeObo,
|
|
71
76
|
type Xref,
|
|
72
77
|
} from "./syntax.js";
|
|
73
78
|
|
|
74
|
-
/**
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
/**
|
|
80
|
+
* The format-specific options of the OBO importer.
|
|
81
|
+
* @category Built-in formats
|
|
82
|
+
*/
|
|
83
|
+
export interface OboImportOptions extends CommonImportOptions {
|
|
84
|
+
/**
|
|
85
|
+
* "keep" reads obsolete terms as nodes with `is_obsolete` set to true; "drop" leaves them and
|
|
86
|
+
* their edges out.
|
|
87
|
+
* @defaultValue "keep"
|
|
88
|
+
*/
|
|
77
89
|
obsolete?: "keep" | "drop" | undefined;
|
|
78
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* "metadata" keeps the `[Typedef]` frames (relation definitions) in
|
|
92
|
+
* `snapshot.meta.extra.obo.typedefs`; "nodes" makes them nodes too, with their `is_a` edges.
|
|
93
|
+
* @defaultValue "metadata"
|
|
94
|
+
*/
|
|
79
95
|
typedefs?: "metadata" | "nodes" | undefined;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Issue codes of the OBO importer.
|
|
100
|
+
* @category Built-in formats
|
|
101
|
+
*/
|
|
83
102
|
export const OBO_ISSUE = Object.freeze({
|
|
84
|
-
|
|
103
|
+
...INPUT_ISSUE,
|
|
104
|
+
...ELEMENT_ISSUE,
|
|
105
|
+
/** The input is empty or whitespace. The import stops. */
|
|
85
106
|
EMPTY_INPUT: EMPTY_INPUT_CODE,
|
|
86
|
-
/** The input
|
|
107
|
+
/** The input is not valid UTF-8. The import stops. */
|
|
87
108
|
INVALID_UTF8: INVALID_UTF8_CODE,
|
|
88
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Some bytes are not valid in the encoding that was chosen (by a byte order mark or the `encoding` option). The
|
|
111
|
+
* import stops.
|
|
112
|
+
*/
|
|
89
113
|
INVALID_ENCODING: INVALID_ENCODING_CODE,
|
|
90
114
|
/** Bytes that are not UTF-8 were read as windows-1252. */
|
|
91
115
|
ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
|
|
@@ -95,9 +119,9 @@ export const OBO_ISSUE = Object.freeze({
|
|
|
95
119
|
MISSING_ID: MISSING_ID_CODE,
|
|
96
120
|
/** A clause whose value cannot be read (a boolean other than true / false, a relationship with one or three values); the clause is skipped. */
|
|
97
121
|
BAD_VALUE: BAD_VALUE_CODE,
|
|
98
|
-
/** Two frames
|
|
122
|
+
/** Two frames have the same id, so they were merged into one node. */
|
|
99
123
|
DUPLICATE_NODE: DUPLICATE_NODE_CODE,
|
|
100
|
-
/** A
|
|
124
|
+
/** A tag that may appear once per frame appears twice; the first value is kept. */
|
|
101
125
|
DUPLICATE_ATTRIBUTE: DUPLICATE_ATTRIBUTE_CODE,
|
|
102
126
|
/** An unknown tag (kept in `obo.unrecognized`) or frame type (skipped), once per name. */
|
|
103
127
|
UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
|
|
@@ -105,6 +129,16 @@ export const OBO_ISSUE = Object.freeze({
|
|
|
105
129
|
DANGLING_REFERENCE: DANGLING_REFERENCE_CODE,
|
|
106
130
|
/** A line without a colon, an unterminated quote, a def without its xref list, a qualifier block that does not parse, an unescaped brace. */
|
|
107
131
|
SYNTAX: "W_OBO_SYNTAX",
|
|
132
|
+
/**
|
|
133
|
+
* Nothing in the input looks like OBO: there is no frame header and no header tag. It may be an HTML page, a JSON
|
|
134
|
+
* or GML file, or UTF-16 text without a byte order mark. The import stops.
|
|
135
|
+
*/
|
|
136
|
+
NOT_OBO: "E_OBO_NOT_OBO",
|
|
137
|
+
/**
|
|
138
|
+
* The header has no `format-version` (OBO 1.2 and 1.4 require one), or names a version other than 1.0, 1.2 or 1.4.
|
|
139
|
+
* The file is read accepting the tags of every version.
|
|
140
|
+
*/
|
|
141
|
+
FORMAT_VERSION: "W_OBO_FORMAT_VERSION",
|
|
108
142
|
/** The frame's `id` is not its first clause; it is used anyway. */
|
|
109
143
|
ID_NOT_FIRST: "W_OBO_ID_NOT_FIRST",
|
|
110
144
|
/** A synonym without a scope in a file that does not say 1.2, or with a scope that is not one of the four. */
|
|
@@ -113,7 +147,7 @@ export const OBO_ISSUE = Object.freeze({
|
|
|
113
147
|
UNDECLARED: "W_OBO_UNDECLARED",
|
|
114
148
|
/** One id for a Term and a Typedef (or an Instance); the Term (the first node frame) is the node. */
|
|
115
149
|
ID_KIND_CLASH: "W_OBO_ID_KIND_CLASH",
|
|
116
|
-
/**
|
|
150
|
+
/** A frame has only one `intersection_of` or `union_of` clause, where OBO needs at least two. */
|
|
117
151
|
CARDINALITY: "W_OBO_CARDINALITY",
|
|
118
152
|
/** An obsolete term with is_a / relationship, or replaced_by / consider on a term that is not obsolete. */
|
|
119
153
|
OBSOLETION: "W_OBO_OBSOLETION",
|
|
@@ -121,19 +155,31 @@ export const OBO_ISSUE = Object.freeze({
|
|
|
121
155
|
DEPRECATED_TAG: "W_OBO_DEPRECATED_TAG",
|
|
122
156
|
/** A backslash line continuation (deprecated in 1.4). */
|
|
123
157
|
DEPRECATED_SYNTAX: "W_OBO_DEPRECATED_SYNTAX",
|
|
124
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* A header clause whose meaning graph-io does not apply (`import`, `id-mapping`, the `treat-xrefs` macros,
|
|
160
|
+
* `owl-axioms`); it is kept in `snapshot.meta.extra.obo.header`.
|
|
161
|
+
*/
|
|
125
162
|
HEADER_NOT_APPLIED: "W_OBO_HEADER_NOT_APPLIED",
|
|
126
163
|
/** Obsolete terms and their edges left out under obsolete: "drop". */
|
|
127
164
|
OBSOLETE_DROPPED: "W_OBO_OBSOLETE_DROPPED",
|
|
128
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* Two different id texts became the same number because `ids` is "number" (for example, "042" and "42"), so their nodes
|
|
167
|
+
* were merged.
|
|
168
|
+
*/
|
|
129
169
|
ID_MERGED: ID_MERGED_CODE,
|
|
130
|
-
/**
|
|
170
|
+
/** An OBO attribute was renamed `<name>#obo` because another attribute already has its name. */
|
|
131
171
|
COLUMN_RENAMED: RENAMED_CODE,
|
|
132
|
-
/**
|
|
172
|
+
/**
|
|
173
|
+
* You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
|
|
174
|
+
* a plain attribute.
|
|
175
|
+
*/
|
|
133
176
|
ROLE_TAKEN: ROLE_TAKEN_CODE,
|
|
134
|
-
/**
|
|
177
|
+
/** You set an option this format does not use; it had no effect. The message names the option. */
|
|
135
178
|
OPTION_IGNORED: OPTION_IGNORED_CODE,
|
|
136
|
-
/**
|
|
179
|
+
/**
|
|
180
|
+
* You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
|
|
181
|
+
* `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
|
|
182
|
+
*/
|
|
137
183
|
SINK_OPTION: SINK_OPTION_CODE,
|
|
138
184
|
});
|
|
139
185
|
|
|
@@ -149,8 +195,12 @@ const USED_OPTIONS: ReadonlySet<keyof CommonImportOptions> = new Set<keyof Commo
|
|
|
149
195
|
"signal",
|
|
150
196
|
"onProgress",
|
|
151
197
|
"encoding",
|
|
198
|
+
"restoreMangledIds",
|
|
152
199
|
]);
|
|
153
200
|
|
|
201
|
+
/** The property_value relation under which the exporter keeps an id it rewrote (`sanitizeIds: "mangle"`). */
|
|
202
|
+
const ORIGINAL_ID = "graphty:originalId";
|
|
203
|
+
|
|
154
204
|
const DEFAULTS: ImportFormatDefaults = { ids: "keep", defaultDirected: true, weightFrom: null };
|
|
155
205
|
|
|
156
206
|
/** Frames between two checks of the cancellation signal. */
|
|
@@ -170,8 +220,11 @@ const DEPRECATED_HEADER: ReadonlyMap<string, string> = new Map([
|
|
|
170
220
|
["version", "data-version"],
|
|
171
221
|
]);
|
|
172
222
|
|
|
173
|
-
/**
|
|
174
|
-
|
|
223
|
+
/**
|
|
224
|
+
* The 1.0 / 1.2 frame tags read as their 1.4 meaning: tag to [1.4 tag, synonym scope].
|
|
225
|
+
* @category Plugin helpers
|
|
226
|
+
*/
|
|
227
|
+
export const DEPRECATED_TAGS: ReadonlyMap<string, readonly [string, string | null]> = new Map([
|
|
175
228
|
["exact_synonym", ["synonym", "EXACT"]],
|
|
176
229
|
["narrow_synonym", ["synonym", "NARROW"]],
|
|
177
230
|
["broad_synonym", ["synonym", "BROAD"]],
|
|
@@ -192,8 +245,11 @@ const BUILTIN_RELATIONS: ReadonlySet<string> = new Set([
|
|
|
192
245
|
"intersection_of",
|
|
193
246
|
]);
|
|
194
247
|
|
|
195
|
-
/**
|
|
196
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Single-valued text tags (first value kept).
|
|
250
|
+
* @category Plugin helpers
|
|
251
|
+
*/
|
|
252
|
+
export const TEXT_TAGS: ReadonlySet<string> = new Set([
|
|
197
253
|
"name",
|
|
198
254
|
"namespace",
|
|
199
255
|
"comment",
|
|
@@ -204,13 +260,19 @@ const TEXT_TAGS: ReadonlySet<string> = new Set([
|
|
|
204
260
|
"inverse_of",
|
|
205
261
|
]);
|
|
206
262
|
|
|
207
|
-
/**
|
|
208
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Single-valued boolean tags.
|
|
265
|
+
* @category Plugin helpers
|
|
266
|
+
*/
|
|
267
|
+
export const BOOL_TAGS: ReadonlySet<string> = new Set(
|
|
209
268
|
Object.keys(OBO_NODE_COLUMNS).filter((name) => OBO_NODE_COLUMNS[name].dtype === "bool"),
|
|
210
269
|
);
|
|
211
270
|
|
|
212
|
-
/**
|
|
213
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Tags whose value is one id, collected in a list column.
|
|
273
|
+
* @category Plugin helpers
|
|
274
|
+
*/
|
|
275
|
+
export const ID_LIST_TAGS: ReadonlySet<string> = new Set([
|
|
214
276
|
"alt_id",
|
|
215
277
|
"subset",
|
|
216
278
|
"replaced_by",
|
|
@@ -222,8 +284,87 @@ const ID_LIST_TAGS: ReadonlySet<string> = new Set([
|
|
|
222
284
|
"disjoint_over",
|
|
223
285
|
]);
|
|
224
286
|
|
|
225
|
-
/**
|
|
226
|
-
const
|
|
287
|
+
/** Header tags the specification allows once (the first is kept). */
|
|
288
|
+
const SINGLE_HEADER_TAGS: ReadonlySet<string> = new Set([
|
|
289
|
+
"format-version",
|
|
290
|
+
"data-version",
|
|
291
|
+
"version",
|
|
292
|
+
"date",
|
|
293
|
+
"saved-by",
|
|
294
|
+
"auto-generated-by",
|
|
295
|
+
"default-namespace",
|
|
296
|
+
"ontology",
|
|
297
|
+
"default-relationship-id-prefix",
|
|
298
|
+
]);
|
|
299
|
+
|
|
300
|
+
/** The format versions the importer knows (it reads their union). */
|
|
301
|
+
const KNOWN_VERSION = /^(GO_)?1\.[024]$/;
|
|
302
|
+
|
|
303
|
+
/** Tags whose value is an id, where a qualifier block cut short by a truncated file is dropped. */
|
|
304
|
+
const ID_VALUE_TAGS: ReadonlySet<string> = new Set([
|
|
305
|
+
...ID_LIST_TAGS,
|
|
306
|
+
"is_a",
|
|
307
|
+
"instance_of",
|
|
308
|
+
"relationship",
|
|
309
|
+
"intersection_of",
|
|
310
|
+
]);
|
|
311
|
+
|
|
312
|
+
/** Warnings listed per code before the rest are summed up in one (a file of 50k unknown tags). */
|
|
313
|
+
const MAX_TALLIES_PER_CODE = 100;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Whether a text holds a control character other than tab, line feed, form feed (a line end in
|
|
317
|
+
* the 1.4 grammar, text inside quotes) and carriage return.
|
|
318
|
+
* @param text - the text
|
|
319
|
+
* @returns true when it does
|
|
320
|
+
*/
|
|
321
|
+
function hasControl(text: string): boolean {
|
|
322
|
+
for (const ch of text) {
|
|
323
|
+
if ((ch < " " && ch !== "\t" && ch !== "\n" && ch !== "\f" && ch !== "\r") || ch === "\x7f") {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Whether a tag could name an OBO tag: no whitespace, control characters or the punctuation of
|
|
332
|
+
* other formats (`{`, `"`, `<`, brackets, parentheses).
|
|
333
|
+
* @param tag - the tag
|
|
334
|
+
* @returns true for a plausible tag
|
|
335
|
+
*/
|
|
336
|
+
function isTagName(tag: string): boolean {
|
|
337
|
+
return tag.length > 0 && !hasControl(tag) && !/[\s{}"<>[\]()]/.test(tag);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* A record with no prototype, so a key named after an Object.prototype member (`__proto__`,
|
|
342
|
+
* `constructor`, `toString`) is an own key like any other.
|
|
343
|
+
* @returns the empty record
|
|
344
|
+
*/
|
|
345
|
+
function ownRecord<T>(): Record<string, T> {
|
|
346
|
+
return Object.create(null) as Record<string, T>;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Append a value to the list of a key in a null-prototype record.
|
|
351
|
+
* @param map - the record
|
|
352
|
+
* @param key - the key
|
|
353
|
+
* @param value - the value
|
|
354
|
+
*/
|
|
355
|
+
function append<T>(map: Record<string, T[]>, key: string, value: T): void {
|
|
356
|
+
if (Object.hasOwn(map, key)) {
|
|
357
|
+
map[key].push(value);
|
|
358
|
+
} else {
|
|
359
|
+
map[key] = [value];
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Tags that only a Typedef frame may carry (elsewhere they are unrecognized).
|
|
365
|
+
* @category Plugin helpers
|
|
366
|
+
*/
|
|
367
|
+
export const TYPEDEF_TAGS: ReadonlySet<string> = new Set([
|
|
227
368
|
"domain",
|
|
228
369
|
"range",
|
|
229
370
|
"inverse_of",
|
|
@@ -286,7 +427,7 @@ interface Where {
|
|
|
286
427
|
|
|
287
428
|
/** A warning counted while reading and recorded once at the end. */
|
|
288
429
|
interface Tally {
|
|
289
|
-
readonly category: "parse-error" | "validation-error" | "coercion" | "unsupported";
|
|
430
|
+
readonly category: "parse-error" | "validation-error" | "coercion" | "unsupported" | "merged";
|
|
290
431
|
readonly code: string;
|
|
291
432
|
readonly element: string;
|
|
292
433
|
readonly what: string;
|
|
@@ -343,7 +484,19 @@ class OboReader {
|
|
|
343
484
|
private readonly signal: AbortSignal | null;
|
|
344
485
|
|
|
345
486
|
/** Header clauses by tag, raw, in order. */
|
|
346
|
-
private readonly header: Record<string, string[]> =
|
|
487
|
+
private readonly header: Record<string, string[]> = ownRecord<string[]>();
|
|
488
|
+
|
|
489
|
+
/** Whether `[Typedef]` frames are nodes (typedefs "nodes"). */
|
|
490
|
+
private readonly typedefsAsNodes: boolean;
|
|
491
|
+
|
|
492
|
+
/** Whether any frame header was read. */
|
|
493
|
+
private frameSeen = false;
|
|
494
|
+
|
|
495
|
+
/** Header clauses whose tag could be an OBO tag. */
|
|
496
|
+
private tagClauses = 0;
|
|
497
|
+
|
|
498
|
+
/** The first line that is not a frame header, a comment or a tag-value line with a plausible tag. */
|
|
499
|
+
private firstBadLine: number | null = null;
|
|
347
500
|
|
|
348
501
|
private version: string | null = null;
|
|
349
502
|
|
|
@@ -377,10 +530,12 @@ class OboReader {
|
|
|
377
530
|
* Create a reader.
|
|
378
531
|
* @param report - the report
|
|
379
532
|
* @param signal - the cancellation signal
|
|
533
|
+
* @param typedefsAsNodes - whether `[Typedef]` frames are nodes
|
|
380
534
|
*/
|
|
381
|
-
constructor(report: ImportReportBuilder, signal: AbortSignal | null) {
|
|
535
|
+
constructor(report: ImportReportBuilder, signal: AbortSignal | null, typedefsAsNodes: boolean) {
|
|
382
536
|
this.report = report;
|
|
383
537
|
this.signal = signal;
|
|
538
|
+
this.typedefsAsNodes = typedefsAsNodes;
|
|
384
539
|
}
|
|
385
540
|
|
|
386
541
|
/**
|
|
@@ -397,32 +552,72 @@ class OboReader {
|
|
|
397
552
|
if (t.startsWith("!")) {
|
|
398
553
|
return;
|
|
399
554
|
}
|
|
400
|
-
|
|
401
|
-
if (header !== null) {
|
|
402
|
-
this.startFrame(header[1].trim(), line);
|
|
555
|
+
if (t.startsWith("[") && this.frameHeader(t, line)) {
|
|
403
556
|
return;
|
|
404
557
|
}
|
|
405
558
|
// the comment goes first, so a colon inside it never makes `foo ! a:b` a tag-value line
|
|
406
559
|
const tv = splitTagValue(stripComment(t));
|
|
407
|
-
if (tv === null) {
|
|
408
|
-
this.
|
|
560
|
+
if (tv === null || tv.tag.length === 0) {
|
|
561
|
+
this.firstBadLine ??= line;
|
|
562
|
+
const what = tv === null ? "a line without a colon was skipped" : "a line without a tag was skipped";
|
|
563
|
+
this.tally("parse-error", OBO_ISSUE.SYNTAX, what, "line", line);
|
|
409
564
|
return;
|
|
410
565
|
}
|
|
411
566
|
const clause: Clause = { tag: tv.tag, value: stripComment(tv.rest), line };
|
|
412
567
|
if (this.frame === null) {
|
|
568
|
+
if (isTagName(tv.tag)) {
|
|
569
|
+
this.tagClauses++;
|
|
570
|
+
} else {
|
|
571
|
+
this.firstBadLine ??= line;
|
|
572
|
+
}
|
|
413
573
|
this.headerClause(clause);
|
|
414
574
|
} else {
|
|
415
575
|
this.frame.clauses.push(clause);
|
|
416
576
|
}
|
|
417
577
|
}
|
|
418
578
|
|
|
579
|
+
/**
|
|
580
|
+
* Start a frame at a line opening with `[`: a well-formed header, or one damaged so that only a
|
|
581
|
+
* frame header can be meant (a known frame name, or no colon: `[Term`, `[Term] extra`), with a
|
|
582
|
+
* warning, so the clauses after it never merge into the frame before.
|
|
583
|
+
* @param t - the trimmed line
|
|
584
|
+
* @param line - its number
|
|
585
|
+
* @returns false when the line is not a frame header
|
|
586
|
+
*/
|
|
587
|
+
private frameHeader(t: string, line: number): boolean {
|
|
588
|
+
const close = t.indexOf("]");
|
|
589
|
+
const name = (close < 0 ? t.slice(1) : t.slice(1, close)).trim();
|
|
590
|
+
const after = close < 0 ? "" : t.slice(close + 1).trim();
|
|
591
|
+
const clean = close >= 0 && (after.length === 0 || after.startsWith("!"));
|
|
592
|
+
if (!clean && !FRAME_KINDS.has(name) && t.includes(":")) {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
if (!clean) {
|
|
596
|
+
const what =
|
|
597
|
+
close < 0
|
|
598
|
+
? "a frame header without its closing ] starts the frame"
|
|
599
|
+
: "text after a frame header was ignored";
|
|
600
|
+
this.tally("parse-error", OBO_ISSUE.SYNTAX, what, `[${name}]`, line);
|
|
601
|
+
}
|
|
602
|
+
// a known frame name, or a clean header with a name: anything else (`[`, `[{"id": 1}]`, a JSON
|
|
603
|
+
// array) is read as a damaged header but is no proof the input is OBO
|
|
604
|
+
const proof = FRAME_KINDS.has(name) || (clean && /^[A-Za-z]/.test(name) && isTagName(name));
|
|
605
|
+
if (!proof) {
|
|
606
|
+
this.firstBadLine ??= line;
|
|
607
|
+
}
|
|
608
|
+
this.startFrame(name, line, proof);
|
|
609
|
+
return true;
|
|
610
|
+
}
|
|
611
|
+
|
|
419
612
|
/**
|
|
420
613
|
* Start a frame, finishing the one before.
|
|
421
614
|
* @param name - the frame type
|
|
422
615
|
* @param line - the header's line
|
|
616
|
+
* @param proof - whether the header shows the input is OBO
|
|
423
617
|
*/
|
|
424
|
-
private startFrame(name: string, line: number): void {
|
|
618
|
+
private startFrame(name: string, line: number, proof: boolean): void {
|
|
425
619
|
this.finishFrame();
|
|
620
|
+
this.frameSeen ||= proof;
|
|
426
621
|
const kind = FRAME_KINDS.has(name) ? (name as FrameKind) : null;
|
|
427
622
|
if (kind === null) {
|
|
428
623
|
this.tally(
|
|
@@ -441,14 +636,31 @@ class OboReader {
|
|
|
441
636
|
* @param clause - the clause
|
|
442
637
|
*/
|
|
443
638
|
private headerClause(clause: Clause): void {
|
|
444
|
-
(this.header
|
|
639
|
+
append(this.header, clause.tag, clause.value);
|
|
445
640
|
const { tag } = clause;
|
|
641
|
+
if (SINGLE_HEADER_TAGS.has(tag) && this.header[tag].length > 1) {
|
|
642
|
+
this.tally(
|
|
643
|
+
"validation-error",
|
|
644
|
+
OBO_ISSUE.DUPLICATE_ATTRIBUTE,
|
|
645
|
+
"a single-valued header tag given twice; the first is kept",
|
|
646
|
+
tag,
|
|
647
|
+
clause.line,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
446
650
|
const tokens = tokenize(splitQualifiers(clause.value).value);
|
|
447
651
|
const first = tokens.length > 0 && tokens[0].kind === "word" ? tokens[0].text : null;
|
|
448
652
|
if (tag === "format-version") {
|
|
449
653
|
this.version ??= unescapeObo(clause.value).trim();
|
|
450
654
|
} else if (tag === "default-namespace") {
|
|
451
655
|
this.defaultNamespace ??= first;
|
|
656
|
+
} else if ((tag === "subsetdef" || tag === "synonymtypedef") && first === null) {
|
|
657
|
+
this.tally(
|
|
658
|
+
"parse-error",
|
|
659
|
+
OBO_ISSUE.SYNTAX,
|
|
660
|
+
"a declaration whose name is not a word declares nothing",
|
|
661
|
+
tag,
|
|
662
|
+
clause.line,
|
|
663
|
+
);
|
|
452
664
|
} else if (tag === "subsetdef" && first !== null) {
|
|
453
665
|
this.subsets.add(first);
|
|
454
666
|
} else if (tag === "synonymtypedef" && first !== null) {
|
|
@@ -479,9 +691,9 @@ class OboReader {
|
|
|
479
691
|
}
|
|
480
692
|
if (frame.kind === null) {
|
|
481
693
|
// the guides: an unrecognized frame must survive, so its clauses are kept, raw
|
|
482
|
-
const clauses
|
|
694
|
+
const clauses = ownRecord<string[]>();
|
|
483
695
|
for (const clause of frame.clauses) {
|
|
484
|
-
(clauses
|
|
696
|
+
append(clauses, clause.tag, clause.value);
|
|
485
697
|
}
|
|
486
698
|
this.unknownFrames.push({ type: frame.name, clauses });
|
|
487
699
|
return;
|
|
@@ -491,7 +703,11 @@ class OboReader {
|
|
|
491
703
|
throwIfAborted(this.signal);
|
|
492
704
|
}
|
|
493
705
|
const idAt = frame.clauses.findIndex((c) => c.tag === "id");
|
|
494
|
-
const
|
|
706
|
+
const idParts = idAt < 0 ? null : splitQualifiers(frame.clauses[idAt].value);
|
|
707
|
+
const rawId = idParts === null ? "" : idParts.value.trim();
|
|
708
|
+
const id = unescapeObo(rawId).trim();
|
|
709
|
+
// a Typedef is metadata unless typedefs "nodes": it would never have been a node
|
|
710
|
+
const wouldBeNode = frame.kind !== "Typedef" || this.typedefsAsNodes;
|
|
495
711
|
if (id.length === 0) {
|
|
496
712
|
this.report.error(
|
|
497
713
|
"missing-value",
|
|
@@ -501,7 +717,23 @@ class OboReader {
|
|
|
501
717
|
line: frame.line,
|
|
502
718
|
},
|
|
503
719
|
);
|
|
504
|
-
|
|
720
|
+
if (wouldBeNode) {
|
|
721
|
+
this.report.counts.skippedNodes++;
|
|
722
|
+
}
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (tokenize(rawId).length > 1 || hasControl(id)) {
|
|
726
|
+
// a reference cannot name such an id (is_a needs one word), so the frame is refused
|
|
727
|
+
// rather than made a node no edge can reach
|
|
728
|
+
this.report.error(
|
|
729
|
+
"validation-error",
|
|
730
|
+
OBO_ISSUE.BAD_VALUE,
|
|
731
|
+
`a [${frame.kind}] frame's id ${JSON.stringify(id)} holds unescaped whitespace or a control character; the frame is skipped`,
|
|
732
|
+
{ line: frame.clauses[idAt].line, element: id },
|
|
733
|
+
);
|
|
734
|
+
if (wouldBeNode) {
|
|
735
|
+
this.report.counts.skippedNodes++;
|
|
736
|
+
}
|
|
505
737
|
return;
|
|
506
738
|
}
|
|
507
739
|
if (idAt > 0) {
|
|
@@ -513,7 +745,8 @@ class OboReader {
|
|
|
513
745
|
frame.clauses[idAt].line,
|
|
514
746
|
);
|
|
515
747
|
}
|
|
516
|
-
const
|
|
748
|
+
const target = this.recordFor(frame.kind, id, frame.line);
|
|
749
|
+
this.extraQualifiers(target, "id", id, idParts?.qualifiers ?? null);
|
|
517
750
|
for (let i = 0; i < frame.clauses.length; i++) {
|
|
518
751
|
const clause = frame.clauses[i];
|
|
519
752
|
if (clause.tag === "id") {
|
|
@@ -528,10 +761,10 @@ class OboReader {
|
|
|
528
761
|
}
|
|
529
762
|
continue;
|
|
530
763
|
}
|
|
531
|
-
this.applyClause(
|
|
764
|
+
this.applyClause(target, clause);
|
|
532
765
|
}
|
|
533
|
-
if (
|
|
534
|
-
|
|
766
|
+
if (target.kind === "Typedef" && !Object.hasOwn(target.raw, "id")) {
|
|
767
|
+
target.raw.id = [id];
|
|
535
768
|
}
|
|
536
769
|
}
|
|
537
770
|
|
|
@@ -555,19 +788,17 @@ class OboReader {
|
|
|
555
788
|
line,
|
|
556
789
|
);
|
|
557
790
|
} else {
|
|
558
|
-
this.
|
|
791
|
+
this.tally(
|
|
559
792
|
"merged",
|
|
560
793
|
OBO_ISSUE.DUPLICATE_NODE,
|
|
561
|
-
`
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
element: id,
|
|
565
|
-
},
|
|
794
|
+
`[${kind}] frames with this id are merged (spec 4.1.1)`,
|
|
795
|
+
id,
|
|
796
|
+
line,
|
|
566
797
|
);
|
|
567
798
|
}
|
|
568
799
|
return existing;
|
|
569
800
|
}
|
|
570
|
-
const
|
|
801
|
+
const created: NodeRecord = {
|
|
571
802
|
id,
|
|
572
803
|
kind,
|
|
573
804
|
line,
|
|
@@ -575,10 +806,10 @@ class OboReader {
|
|
|
575
806
|
lists: new Map(),
|
|
576
807
|
edges: [],
|
|
577
808
|
seen: new Set(),
|
|
578
|
-
raw:
|
|
809
|
+
raw: ownRecord<string[]>(),
|
|
579
810
|
};
|
|
580
|
-
map.set(id,
|
|
581
|
-
return
|
|
811
|
+
map.set(id, created);
|
|
812
|
+
return created;
|
|
582
813
|
}
|
|
583
814
|
|
|
584
815
|
/**
|
|
@@ -606,7 +837,18 @@ class OboReader {
|
|
|
606
837
|
);
|
|
607
838
|
[tag, scope] = deprecated;
|
|
608
839
|
}
|
|
609
|
-
|
|
840
|
+
let split = splitQualifiers(clause.value);
|
|
841
|
+
const open = ID_VALUE_TAGS.has(tag) ? unclosedBlock(split.value) : -1;
|
|
842
|
+
if (open >= 0) {
|
|
843
|
+
this.tally(
|
|
844
|
+
"parse-error",
|
|
845
|
+
OBO_ISSUE.SYNTAX,
|
|
846
|
+
"a qualifier block without its closing brace was dropped",
|
|
847
|
+
tag,
|
|
848
|
+
clause.line,
|
|
849
|
+
);
|
|
850
|
+
split = splitQualifiers(split.value.slice(0, open).trimEnd());
|
|
851
|
+
}
|
|
610
852
|
const { qualifiers } = split;
|
|
611
853
|
if (split.badBlock !== null) {
|
|
612
854
|
this.tally(
|
|
@@ -673,17 +915,17 @@ class OboReader {
|
|
|
673
915
|
}
|
|
674
916
|
record.seen.add(key);
|
|
675
917
|
if (record.kind === "Typedef") {
|
|
676
|
-
(record.raw
|
|
918
|
+
append(record.raw, clause.tag, clause.value);
|
|
677
919
|
}
|
|
678
920
|
const { tag, scope, qualifiers, value } = this.clauseParts(clause);
|
|
921
|
+
// a control character kept in the value is the shared W_CONTROL_CHARACTER of the text check
|
|
679
922
|
const where = { line: clause.line, element: record.id };
|
|
680
923
|
if (TYPEDEF_TAGS.has(tag) && record.kind !== "Typedef") {
|
|
681
924
|
this.unrecognized(record, clause);
|
|
682
925
|
return;
|
|
683
926
|
}
|
|
684
927
|
if (TEXT_TAGS.has(tag)) {
|
|
685
|
-
this.
|
|
686
|
-
this.extraQualifiers(record, tag, unescapeObo(value).trim(), qualifiers);
|
|
928
|
+
this.text(record, tag, value, qualifiers, clause.line);
|
|
687
929
|
return;
|
|
688
930
|
}
|
|
689
931
|
if (BOOL_TAGS.has(tag)) {
|
|
@@ -797,7 +1039,7 @@ class OboReader {
|
|
|
797
1039
|
return;
|
|
798
1040
|
}
|
|
799
1041
|
this.push(record, "xref", xref.id);
|
|
800
|
-
this.describe(record, [xref]);
|
|
1042
|
+
this.describe(record, [xref], tag, clause.line);
|
|
801
1043
|
this.extraQualifiers(record, tag, xref.id, qualifiers ?? xref.qualifiers);
|
|
802
1044
|
return;
|
|
803
1045
|
}
|
|
@@ -809,6 +1051,30 @@ class OboReader {
|
|
|
809
1051
|
}
|
|
810
1052
|
}
|
|
811
1053
|
|
|
1054
|
+
/**
|
|
1055
|
+
* A single-valued text clause (name, comment, ...), warning about a quote that never closes
|
|
1056
|
+
* (stripComment() then keeps a `!` comment after it as text).
|
|
1057
|
+
* @param record - the record
|
|
1058
|
+
* @param tag - the tag
|
|
1059
|
+
* @param value - the raw value
|
|
1060
|
+
* @param qualifiers - its qualifier block
|
|
1061
|
+
* @param line - its line
|
|
1062
|
+
*/
|
|
1063
|
+
private text(record: NodeRecord, tag: string, value: string, qualifiers: Qualifiers | null, line: number): void {
|
|
1064
|
+
if (tokenize(value).some((t) => t.kind === "quoted" && t.unterminated)) {
|
|
1065
|
+
this.tally(
|
|
1066
|
+
"parse-error",
|
|
1067
|
+
OBO_ISSUE.SYNTAX,
|
|
1068
|
+
"an unterminated quote; the text after it, a comment included, is kept",
|
|
1069
|
+
tag,
|
|
1070
|
+
line,
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
const text = unescapeObo(value).trim();
|
|
1074
|
+
this.single(record, tag, text, line);
|
|
1075
|
+
this.extraQualifiers(record, tag, text, qualifiers);
|
|
1076
|
+
}
|
|
1077
|
+
|
|
812
1078
|
/**
|
|
813
1079
|
* Read a value that must be one word.
|
|
814
1080
|
* @param tokens - the value's tokens
|
|
@@ -865,8 +1131,14 @@ class OboReader {
|
|
|
865
1131
|
this.tally("parse-error", OBO_ISSUE.SYNTAX, "the xref list is missing", tag, line);
|
|
866
1132
|
return { text: tokens[0].text, xrefs: [] };
|
|
867
1133
|
}
|
|
1134
|
+
if (list.unterminated) {
|
|
1135
|
+
this.tally("parse-error", OBO_ISSUE.SYNTAX, "an xref list without its closing ]; kept as read", tag, line);
|
|
1136
|
+
}
|
|
1137
|
+
if (tokens.length > 2) {
|
|
1138
|
+
this.tally("parse-error", OBO_ISSUE.SYNTAX, "text after the xref list was ignored", tag, line);
|
|
1139
|
+
}
|
|
868
1140
|
const xrefs = parseXrefList(list.text);
|
|
869
|
-
this.describe(record, xrefs);
|
|
1141
|
+
this.describe(record, xrefs, tag, line);
|
|
870
1142
|
this.xrefQualifiers(record, tag, xrefs);
|
|
871
1143
|
return { text: tokens[0].text, xrefs: xrefs.map((x) => x.id) };
|
|
872
1144
|
}
|
|
@@ -969,8 +1241,31 @@ class OboReader {
|
|
|
969
1241
|
line,
|
|
970
1242
|
);
|
|
971
1243
|
}
|
|
1244
|
+
const listAt = list === undefined ? -1 : tokens.indexOf(list);
|
|
1245
|
+
const stray = tokens.some(
|
|
1246
|
+
(t, k) =>
|
|
1247
|
+
k > 0 && (t.kind === "quoted" || (t.kind === "list" && k !== listAt) || (listAt >= 0 && k > listAt)),
|
|
1248
|
+
);
|
|
1249
|
+
if (stray) {
|
|
1250
|
+
this.tally(
|
|
1251
|
+
"parse-error",
|
|
1252
|
+
OBO_ISSUE.SYNTAX,
|
|
1253
|
+
"a second quoted string, a second xref list or text after the list was ignored",
|
|
1254
|
+
"synonym",
|
|
1255
|
+
line,
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
if (list?.unterminated === true) {
|
|
1259
|
+
this.tally(
|
|
1260
|
+
"parse-error",
|
|
1261
|
+
OBO_ISSUE.SYNTAX,
|
|
1262
|
+
"an xref list without its closing ]; kept as read",
|
|
1263
|
+
"synonym",
|
|
1264
|
+
line,
|
|
1265
|
+
);
|
|
1266
|
+
}
|
|
972
1267
|
const xrefs = list === undefined ? [] : parseXrefList(list.text);
|
|
973
|
-
this.describe(record, xrefs);
|
|
1268
|
+
this.describe(record, xrefs, "synonym", line);
|
|
974
1269
|
this.xrefQualifiers(record, "synonym", xrefs);
|
|
975
1270
|
this.push(
|
|
976
1271
|
record,
|
|
@@ -1009,7 +1304,26 @@ class OboReader {
|
|
|
1009
1304
|
);
|
|
1010
1305
|
return;
|
|
1011
1306
|
}
|
|
1012
|
-
|
|
1307
|
+
if (tokens.some((t) => t.unterminated)) {
|
|
1308
|
+
this.tally(
|
|
1309
|
+
"parse-error",
|
|
1310
|
+
OBO_ISSUE.SYNTAX,
|
|
1311
|
+
"an unterminated quote; the text runs to the end of the line",
|
|
1312
|
+
"property_value",
|
|
1313
|
+
where.line,
|
|
1314
|
+
);
|
|
1315
|
+
}
|
|
1316
|
+
let datatype = tokens.length === 3 ? tokens[2].text : null;
|
|
1317
|
+
if (tokens.length === 3 && tokens[2].kind !== "word") {
|
|
1318
|
+
this.tally(
|
|
1319
|
+
"parse-error",
|
|
1320
|
+
OBO_ISSUE.SYNTAX,
|
|
1321
|
+
"a datatype that is not a word was ignored",
|
|
1322
|
+
"property_value",
|
|
1323
|
+
where.line,
|
|
1324
|
+
);
|
|
1325
|
+
datatype = null;
|
|
1326
|
+
}
|
|
1013
1327
|
this.push(
|
|
1014
1328
|
record,
|
|
1015
1329
|
"property_value",
|
|
@@ -1059,16 +1373,33 @@ class OboReader {
|
|
|
1059
1373
|
}
|
|
1060
1374
|
|
|
1061
1375
|
/**
|
|
1062
|
-
* Record xref descriptions in `xref.descriptions
|
|
1063
|
-
*
|
|
1376
|
+
* Record xref descriptions in `xref.descriptions` (the first description of an id is kept),
|
|
1377
|
+
* warning about a description whose quote never closes.
|
|
1378
|
+
* @param target - the record
|
|
1064
1379
|
* @param xrefs - the xrefs
|
|
1380
|
+
* @param tag - the clause's tag
|
|
1381
|
+
* @param line - the clause's line
|
|
1065
1382
|
*/
|
|
1066
|
-
private describe(
|
|
1383
|
+
private describe(target: NodeRecord, xrefs: readonly Xref[], tag: string, line: number): void {
|
|
1067
1384
|
for (const xref of xrefs) {
|
|
1385
|
+
if (xref.unterminated) {
|
|
1386
|
+
this.tally(
|
|
1387
|
+
"parse-error",
|
|
1388
|
+
OBO_ISSUE.SYNTAX,
|
|
1389
|
+
"an xref description whose quote never closes runs to the end",
|
|
1390
|
+
tag,
|
|
1391
|
+
line,
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1068
1394
|
if (xref.description !== null) {
|
|
1069
|
-
|
|
1070
|
-
map
|
|
1071
|
-
|
|
1395
|
+
let map = target.values.get("xref.descriptions") as Record<string, string> | undefined;
|
|
1396
|
+
if (map === undefined) {
|
|
1397
|
+
map = ownRecord<string>();
|
|
1398
|
+
target.values.set("xref.descriptions", map);
|
|
1399
|
+
}
|
|
1400
|
+
if (!Object.hasOwn(map, xref.id)) {
|
|
1401
|
+
map[xref.id] = xref.description;
|
|
1402
|
+
}
|
|
1072
1403
|
}
|
|
1073
1404
|
}
|
|
1074
1405
|
}
|
|
@@ -1097,7 +1428,7 @@ class OboReader {
|
|
|
1097
1428
|
if (qualifiers === null) {
|
|
1098
1429
|
return;
|
|
1099
1430
|
}
|
|
1100
|
-
const map = (record.values.get("obo.qualifiers")
|
|
1431
|
+
const map = (record.values.get("obo.qualifiers") as Record<string, unknown[]> | undefined) ?? {};
|
|
1101
1432
|
(map[tag] ??= []).push({ value, qualifiers });
|
|
1102
1433
|
record.values.set("obo.qualifiers", map);
|
|
1103
1434
|
}
|
|
@@ -1108,9 +1439,12 @@ class OboReader {
|
|
|
1108
1439
|
* @param clause - the clause
|
|
1109
1440
|
*/
|
|
1110
1441
|
private unrecognized(record: NodeRecord, clause: Clause): void {
|
|
1111
|
-
|
|
1112
|
-
(map
|
|
1113
|
-
|
|
1442
|
+
let map = record.values.get("obo.unrecognized") as Record<string, string[]> | undefined;
|
|
1443
|
+
if (map === undefined) {
|
|
1444
|
+
map = ownRecord<string[]>();
|
|
1445
|
+
record.values.set("obo.unrecognized", map);
|
|
1446
|
+
}
|
|
1447
|
+
append(map, clause.tag, unescapeObo(clause.value));
|
|
1114
1448
|
this.tally(
|
|
1115
1449
|
"validation-error",
|
|
1116
1450
|
OBO_ISSUE.UNKNOWN_ELEMENT,
|
|
@@ -1138,22 +1472,92 @@ class OboReader {
|
|
|
1138
1472
|
}
|
|
1139
1473
|
}
|
|
1140
1474
|
|
|
1141
|
-
/**
|
|
1475
|
+
/**
|
|
1476
|
+
* Record the counted warnings, in the order they were first seen; past MAX_TALLIES_PER_CODE
|
|
1477
|
+
* of one code, the rest are summed up in one warning.
|
|
1478
|
+
*/
|
|
1142
1479
|
flushTallies(): void {
|
|
1480
|
+
const listed = new Map<string, number>();
|
|
1481
|
+
const over = new Map<string, Tally[]>();
|
|
1143
1482
|
for (const t of this.tallies.values()) {
|
|
1483
|
+
const n = (listed.get(t.code) ?? 0) + 1;
|
|
1484
|
+
listed.set(t.code, n);
|
|
1485
|
+
if (n > MAX_TALLIES_PER_CODE) {
|
|
1486
|
+
const rest = over.get(t.code) ?? [];
|
|
1487
|
+
rest.push(t);
|
|
1488
|
+
over.set(t.code, rest);
|
|
1489
|
+
continue;
|
|
1490
|
+
}
|
|
1144
1491
|
this.report.warning(
|
|
1145
1492
|
t.category,
|
|
1146
1493
|
t.code,
|
|
1147
|
-
`${t.element}: ${t.what} (${t.count} time(
|
|
1494
|
+
`${t.element}: ${t.what} (${t.count} time${plural(t.count)}, first at line ${t.line})`,
|
|
1148
1495
|
{
|
|
1149
1496
|
line: t.line,
|
|
1150
1497
|
element: t.element,
|
|
1151
1498
|
},
|
|
1152
1499
|
);
|
|
1153
1500
|
}
|
|
1501
|
+
for (const [code, rest] of over) {
|
|
1502
|
+
this.report.warning(
|
|
1503
|
+
rest[0].category,
|
|
1504
|
+
code,
|
|
1505
|
+
`${rest.length} more element${plural(rest.length)} with this warning, not listed (first: ${rest[0].element}: ${rest[0].what}, line ${rest[0].line})`,
|
|
1506
|
+
{ line: rest[0].line },
|
|
1507
|
+
);
|
|
1508
|
+
}
|
|
1154
1509
|
this.tallies.clear();
|
|
1155
1510
|
}
|
|
1156
1511
|
|
|
1512
|
+
/**
|
|
1513
|
+
* The first line that makes the input not OBO: null unless no frame header and no plausible
|
|
1514
|
+
* header tag was read while some line was neither (an HTML page, a JSON, GML, DOT or CSV file,
|
|
1515
|
+
* UTF-16 read without its BOM). A file of only comments is OBO, if an empty one.
|
|
1516
|
+
* @returns the line, or null
|
|
1517
|
+
*/
|
|
1518
|
+
notObo(): number | null {
|
|
1519
|
+
return this.frameSeen || this.tagClauses > 0 ? null : this.firstBadLine;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
/**
|
|
1523
|
+
* Warn about the header once the file is read: a missing or unknown format-version (the
|
|
1524
|
+
* synonym-scope rule depends on it), a date that is not a real calendar date.
|
|
1525
|
+
*/
|
|
1526
|
+
checkHeader(): void {
|
|
1527
|
+
if (this.version === null) {
|
|
1528
|
+
const empty = this.frameSeen ? "" : " and the file has no frames: nothing was read";
|
|
1529
|
+
this.tally(
|
|
1530
|
+
"validation-error",
|
|
1531
|
+
OBO_ISSUE.FORMAT_VERSION,
|
|
1532
|
+
`the header has no format-version (required by 1.2 and 1.4)${empty}; read as the 1.0 / 1.2 / 1.4 union`,
|
|
1533
|
+
"format-version",
|
|
1534
|
+
1,
|
|
1535
|
+
);
|
|
1536
|
+
} else if (!KNOWN_VERSION.test(this.version)) {
|
|
1537
|
+
this.tally(
|
|
1538
|
+
"validation-error",
|
|
1539
|
+
OBO_ISSUE.FORMAT_VERSION,
|
|
1540
|
+
`format-version ${JSON.stringify(this.version)} is not 1.0, 1.2 or 1.4; read as 1.4`,
|
|
1541
|
+
"format-version",
|
|
1542
|
+
1,
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
const date = this.header.date?.[0];
|
|
1546
|
+
if (
|
|
1547
|
+
date !== undefined &&
|
|
1548
|
+
/^\d{2}:\d{2}:\d{4}\s+\d{2}:\d{2}$/.test(date.trim()) &&
|
|
1549
|
+
oboDate(date.trim()) === null
|
|
1550
|
+
) {
|
|
1551
|
+
this.tally(
|
|
1552
|
+
"validation-error",
|
|
1553
|
+
OBO_ISSUE.SYNTAX,
|
|
1554
|
+
"the header date is not a real calendar date; created is null",
|
|
1555
|
+
"date",
|
|
1556
|
+
1,
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1157
1561
|
/**
|
|
1158
1562
|
* The header as GraphMeta fields (design 4.2): name from `ontology`, the version, `created` from
|
|
1159
1563
|
* the `dd:MM:yyyy HH:mm` date, `creator` from `saved-by`.
|
|
@@ -1211,7 +1615,9 @@ function oboDate(text: string | null): string | null {
|
|
|
1211
1615
|
return null;
|
|
1212
1616
|
}
|
|
1213
1617
|
const [, dd, mm, yyyy, hh, min] = m;
|
|
1214
|
-
|
|
1618
|
+
// a real calendar date: 31:02 and 31:04 do not survive the round trip through Date
|
|
1619
|
+
const day = new Date(Date.UTC(Number(yyyy), Number(mm) - 1, Number(dd)));
|
|
1620
|
+
if (Number(mm) < 1 || Number(mm) > 12 || day.getUTCDate() !== Number(dd) || Number(hh) > 23 || Number(min) > 59) {
|
|
1215
1621
|
return null;
|
|
1216
1622
|
}
|
|
1217
1623
|
return `${yyyy}-${mm}-${dd}T${hh}:${min}`;
|
|
@@ -1339,12 +1745,12 @@ function planGraph(
|
|
|
1339
1745
|
}
|
|
1340
1746
|
}
|
|
1341
1747
|
reader.flushTallies();
|
|
1342
|
-
reportDangling(records, dangling, addMissingNodes, danglingEdges, report);
|
|
1748
|
+
reportDangling(records, dangling, addMissingNodes, danglingEdges, report, new Set(reader.typedefs.keys()));
|
|
1343
1749
|
if (dropped.size > 0) {
|
|
1344
1750
|
report.warning(
|
|
1345
1751
|
"merged",
|
|
1346
1752
|
OBO_ISSUE.OBSOLETE_DROPPED,
|
|
1347
|
-
`${dropped.size} obsolete term(
|
|
1753
|
+
`${dropped.size} obsolete term${plural(dropped.size)} and ${droppedEdges} edge${plural(droppedEdges)} to or from them were left out (obsolete: "drop")`,
|
|
1348
1754
|
{ element: [...dropped][0] },
|
|
1349
1755
|
);
|
|
1350
1756
|
}
|
|
@@ -1363,6 +1769,7 @@ function planGraph(
|
|
|
1363
1769
|
* @param addMissingNodes - whether they became placeholder nodes
|
|
1364
1770
|
* @param droppedEdges - the edges dropped under addMissingNodes false
|
|
1365
1771
|
* @param report - the report
|
|
1772
|
+
* @param typedefs - the ids of the Typedef frames (kept as metadata, so not nodes)
|
|
1366
1773
|
*/
|
|
1367
1774
|
function reportDangling(
|
|
1368
1775
|
records: readonly NodeRecord[],
|
|
@@ -1370,6 +1777,7 @@ function reportDangling(
|
|
|
1370
1777
|
addMissingNodes: boolean,
|
|
1371
1778
|
droppedEdges: number,
|
|
1372
1779
|
report: ImportReportBuilder,
|
|
1780
|
+
typedefs: ReadonlySet<string>,
|
|
1373
1781
|
): void {
|
|
1374
1782
|
if (dangling.length === 0) {
|
|
1375
1783
|
return;
|
|
@@ -1382,16 +1790,19 @@ function reportDangling(
|
|
|
1382
1790
|
}
|
|
1383
1791
|
const shown = dangling.slice(0, 5).map((id) => {
|
|
1384
1792
|
const primary = altOf.get(id);
|
|
1793
|
+
if (typedefs.has(id)) {
|
|
1794
|
+
return `${id} (a Typedef, kept as metadata, not a node)`;
|
|
1795
|
+
}
|
|
1385
1796
|
return primary === undefined ? id : `${id} (an alt_id of ${primary})`;
|
|
1386
1797
|
});
|
|
1387
1798
|
const more = dangling.length > 5 ? `, ... (${dangling.length - 5} more)` : "";
|
|
1388
1799
|
const action = addMissingNodes
|
|
1389
1800
|
? "each became a placeholder node (graphty.placeholder)"
|
|
1390
|
-
: `${droppedEdges} edge(
|
|
1801
|
+
: `${droppedEdges} edge${plural(droppedEdges)} to them ${agree(droppedEdges, "was", "were")} dropped (addMissingNodes false)`;
|
|
1391
1802
|
report.warning(
|
|
1392
1803
|
"validation-error",
|
|
1393
1804
|
OBO_ISSUE.DANGLING_REFERENCE,
|
|
1394
|
-
`${dangling.length} target(
|
|
1805
|
+
`${dangling.length} target${plural(dangling.length)} no frame declares: ${shown.join(", ")}${more}; ${action}`,
|
|
1395
1806
|
{ element: dangling[0] },
|
|
1396
1807
|
);
|
|
1397
1808
|
}
|
|
@@ -1406,6 +1817,9 @@ class Pusher {
|
|
|
1406
1817
|
|
|
1407
1818
|
private readonly signal: AbortSignal | null;
|
|
1408
1819
|
|
|
1820
|
+
/** The original ids restored under restoreMangledIds, by the id the file wrote. */
|
|
1821
|
+
readonly restored = new Map<string, string>();
|
|
1822
|
+
|
|
1409
1823
|
private since = 0;
|
|
1410
1824
|
|
|
1411
1825
|
/**
|
|
@@ -1427,7 +1841,7 @@ class Pusher {
|
|
|
1427
1841
|
* @returns the node id
|
|
1428
1842
|
*/
|
|
1429
1843
|
id(text: string): NodeId {
|
|
1430
|
-
const id = this.coercer.text(text);
|
|
1844
|
+
const id = this.coercer.text(this.restored.get(text) ?? text);
|
|
1431
1845
|
const merge = this.coercer.lastMerge;
|
|
1432
1846
|
if (merge !== null) {
|
|
1433
1847
|
this.report.warning(
|
|
@@ -1490,6 +1904,9 @@ function writeGraph(
|
|
|
1490
1904
|
): void {
|
|
1491
1905
|
const plan = planGraph(reader, report, options.addMissingNodes, obo);
|
|
1492
1906
|
const push = new Pusher(sink, report, options);
|
|
1907
|
+
if (options.restoreMangledIds) {
|
|
1908
|
+
restoreIds(plan.nodes, push.restored);
|
|
1909
|
+
}
|
|
1493
1910
|
const namespace = reader.namespaceDefault();
|
|
1494
1911
|
// columns, declared only when used, in the vocabulary's order
|
|
1495
1912
|
const used = new Set<string>(["type"]);
|
|
@@ -1536,6 +1953,50 @@ function writeGraph(
|
|
|
1536
1953
|
writeEdges(plan, push, options);
|
|
1537
1954
|
}
|
|
1538
1955
|
|
|
1956
|
+
/**
|
|
1957
|
+
* Take back the ids the exporter rewrote under `sanitizeIds: "mangle"`: a frame's
|
|
1958
|
+
* `property_value: graphty:originalId "..."` names its original id, which replaces the written one
|
|
1959
|
+
* wherever the file names it (the property value itself is dropped). An original that another
|
|
1960
|
+
* frame already uses as its id is not restored.
|
|
1961
|
+
* @param records - the node records
|
|
1962
|
+
* @param restored - receives written id to original id
|
|
1963
|
+
*/
|
|
1964
|
+
function restoreIds(records: readonly NodeRecord[], restored: Map<string, string>): void {
|
|
1965
|
+
const taken = new Set(records.map((r) => r.id));
|
|
1966
|
+
for (const record of records) {
|
|
1967
|
+
const values = record.lists.get("property_value") as { relation: string; value: string }[] | undefined;
|
|
1968
|
+
const at = values?.findIndex((v) => v.relation === ORIGINAL_ID) ?? -1;
|
|
1969
|
+
if (values === undefined || at < 0) {
|
|
1970
|
+
continue;
|
|
1971
|
+
}
|
|
1972
|
+
const original = originalText(values[at].value);
|
|
1973
|
+
if (taken.has(original)) {
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
taken.add(original);
|
|
1977
|
+
restored.set(record.id, original);
|
|
1978
|
+
values.splice(at, 1);
|
|
1979
|
+
if (values.length === 0) {
|
|
1980
|
+
record.lists.delete("property_value");
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
/**
|
|
1986
|
+
* The original id of a `graphty:originalId` property value: the exporter writes it as JSON text (so
|
|
1987
|
+
* every character survives); a value that is not a JSON string is the id as it stands.
|
|
1988
|
+
* @param value - the property value
|
|
1989
|
+
* @returns the original id
|
|
1990
|
+
*/
|
|
1991
|
+
function originalText(value: string): string {
|
|
1992
|
+
try {
|
|
1993
|
+
const parsed: unknown = JSON.parse(value);
|
|
1994
|
+
return typeof parsed === "string" ? parsed : value;
|
|
1995
|
+
} catch {
|
|
1996
|
+
return value;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
1539
2000
|
/**
|
|
1540
2001
|
* Push the planned edges through the direction resolver, with their relation and qualifiers.
|
|
1541
2002
|
* @param plan - the plan
|
|
@@ -1576,6 +2037,71 @@ function writeEdges(plan: GraphPlan, push: Pusher, options: ReturnType<typeof re
|
|
|
1576
2037
|
}
|
|
1577
2038
|
}
|
|
1578
2039
|
|
|
2040
|
+
/**
|
|
2041
|
+
* Feed the physical lines to the reader as logical lines: backslash continuations joined (the parts
|
|
2042
|
+
* collected and joined once, so a value continued over many lines costs its length), form feeds
|
|
2043
|
+
* outside quotes split. A backslash inside a `!` comment never continues a line, and a continuation
|
|
2044
|
+
* never swallows a frame header or a blank line.
|
|
2045
|
+
* @param lines - the line reader
|
|
2046
|
+
* @param reader - the OBO reader
|
|
2047
|
+
*/
|
|
2048
|
+
async function readLines(lines: LineReader, reader: OboReader): Promise<void> {
|
|
2049
|
+
const parts: string[] = [];
|
|
2050
|
+
let partLine = 0;
|
|
2051
|
+
const feed = (text: string, line: number): void => {
|
|
2052
|
+
for (const piece of splitFormFeeds(text)) {
|
|
2053
|
+
reader.line(piece, line);
|
|
2054
|
+
}
|
|
2055
|
+
};
|
|
2056
|
+
for await (const physical of lines) {
|
|
2057
|
+
const { line } = lines;
|
|
2058
|
+
if (parts.length > 0) {
|
|
2059
|
+
const head = physical.trimStart();
|
|
2060
|
+
if (head.length === 0 || head.startsWith("[")) {
|
|
2061
|
+
reader.tally(
|
|
2062
|
+
"parse-error",
|
|
2063
|
+
OBO_ISSUE.SYNTAX,
|
|
2064
|
+
"a line continuation before a frame header or a blank line was ignored",
|
|
2065
|
+
"\\",
|
|
2066
|
+
partLine,
|
|
2067
|
+
);
|
|
2068
|
+
feed(parts.join(""), partLine);
|
|
2069
|
+
parts.length = 0;
|
|
2070
|
+
} else {
|
|
2071
|
+
reader.tally(
|
|
2072
|
+
"coercion",
|
|
2073
|
+
OBO_ISSUE.DEPRECATED_SYNTAX,
|
|
2074
|
+
"a backslash line continuation was joined (deprecated in 1.4)",
|
|
2075
|
+
"\\",
|
|
2076
|
+
partLine,
|
|
2077
|
+
);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (endsWithContinuation(physical) && stripComment(physical) === physical) {
|
|
2081
|
+
parts.push(physical.slice(0, -1));
|
|
2082
|
+
partLine = line;
|
|
2083
|
+
continue;
|
|
2084
|
+
}
|
|
2085
|
+
if (parts.length > 0) {
|
|
2086
|
+
parts.push(physical);
|
|
2087
|
+
feed(parts.join(""), line);
|
|
2088
|
+
parts.length = 0;
|
|
2089
|
+
} else {
|
|
2090
|
+
feed(physical, line);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
if (parts.length > 0) {
|
|
2094
|
+
reader.tally(
|
|
2095
|
+
"coercion",
|
|
2096
|
+
OBO_ISSUE.DEPRECATED_SYNTAX,
|
|
2097
|
+
"a backslash line continuation was joined (deprecated in 1.4)",
|
|
2098
|
+
"\\",
|
|
2099
|
+
partLine,
|
|
2100
|
+
);
|
|
2101
|
+
feed(parts.join(""), partLine);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
1579
2105
|
/**
|
|
1580
2106
|
* Confidence that a head is OBO (design 1.5): after a BOM, blank lines and `!` comment lines, 0.9
|
|
1581
2107
|
* when the first significant line is `format-version:` or a `[Term]` / `[Typedef]` / `[Instance]`
|
|
@@ -1615,10 +2141,12 @@ function sniffObo(head: Uint8Array): number {
|
|
|
1615
2141
|
}
|
|
1616
2142
|
|
|
1617
2143
|
/**
|
|
1618
|
-
* The OBO importer plugin
|
|
2144
|
+
* The OBO importer plugin.
|
|
2145
|
+
* @category Built-in formats
|
|
1619
2146
|
*/
|
|
1620
2147
|
export const oboImporter: GraphImporter<OboImportOptions> = Object.freeze({
|
|
1621
2148
|
format: "obo",
|
|
2149
|
+
options: Object.freeze(["obsolete", "typedefs"]),
|
|
1622
2150
|
extensions: Object.freeze([".obo"]),
|
|
1623
2151
|
mimeTypes: Object.freeze(["text/obo", "application/obo"]),
|
|
1624
2152
|
|
|
@@ -1648,39 +2176,24 @@ export const oboImporter: GraphImporter<OboImportOptions> = Object.freeze({
|
|
|
1648
2176
|
const report = new ImportReportBuilder("obo", resolved.errorLimit);
|
|
1649
2177
|
reportSinkOptions(sink, options, report, true);
|
|
1650
2178
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
1651
|
-
const reader = new OboReader(report, resolved.signal);
|
|
1652
|
-
|
|
1653
|
-
let pending: string | null = null;
|
|
1654
|
-
for await (const physical of lines) {
|
|
1655
|
-
const { line } = lines;
|
|
1656
|
-
const text: string = pending === null ? physical : pending + physical;
|
|
1657
|
-
pending = null;
|
|
1658
|
-
if (endsWithContinuation(text)) {
|
|
1659
|
-
reader.tally(
|
|
1660
|
-
"coercion",
|
|
1661
|
-
OBO_ISSUE.DEPRECATED_SYNTAX,
|
|
1662
|
-
"a backslash line continuation was joined (deprecated in 1.4)",
|
|
1663
|
-
"\\",
|
|
1664
|
-
line,
|
|
1665
|
-
);
|
|
1666
|
-
pending = text.slice(0, -1);
|
|
1667
|
-
continue;
|
|
1668
|
-
}
|
|
1669
|
-
// the 1.4 grammar counts form feed as a line end; LineReader does not
|
|
1670
|
-
for (const piece of text.includes("\f") ? text.split("\f") : [text]) {
|
|
1671
|
-
reader.line(piece, line);
|
|
1672
|
-
}
|
|
1673
|
-
}
|
|
1674
|
-
if (pending !== null) {
|
|
1675
|
-
reader.line(pending, lines.line);
|
|
1676
|
-
}
|
|
2179
|
+
const reader = new OboReader(report, resolved.signal, obo.typedefs === "nodes");
|
|
2180
|
+
await readLines(new LineReader(input, report, resolved), reader);
|
|
1677
2181
|
reader.finishFrame();
|
|
1678
2182
|
if (!reader.significant) {
|
|
1679
2183
|
report.fail(OBO_ISSUE.EMPTY_INPUT, "the input is empty");
|
|
1680
2184
|
}
|
|
2185
|
+
const notObo = reader.notObo();
|
|
2186
|
+
if (notObo !== null) {
|
|
2187
|
+
report.fail(
|
|
2188
|
+
OBO_ISSUE.NOT_OBO,
|
|
2189
|
+
`the input is not OBO: it has no frame header and no header tag, and line ${notObo} is not a tag-value line`,
|
|
2190
|
+
{ line: notObo },
|
|
2191
|
+
);
|
|
2192
|
+
}
|
|
1681
2193
|
throwIfAborted(resolved.signal);
|
|
2194
|
+
reader.checkHeader();
|
|
1682
2195
|
writeGraph(reader, sink, report, resolved, obo);
|
|
1683
|
-
const typedefs
|
|
2196
|
+
const typedefs = ownRecord<Record<string, string[]>>();
|
|
1684
2197
|
for (const [id, record] of reader.typedefs) {
|
|
1685
2198
|
typedefs[id] = record.raw;
|
|
1686
2199
|
}
|