@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
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `obographs` dialect of the JSON exporter: writes a snapshot as an OBO Graphs document
|
|
3
|
+
* (`{ "graphs": [{ "id", "meta", "nodes", "edges", ... }] }`), the JSON form the Gene Ontology and
|
|
4
|
+
* the OBO Foundry publish, and the form the JSON importer's `obographs` dialect reads back.
|
|
5
|
+
*
|
|
6
|
+
* Nodes carry `lbl` from the label column, `type` from the `type` column (Term as CLASS, Instance
|
|
7
|
+
* as INDIVIDUAL, Typedef as PROPERTY) and a `meta` built from the OBO vocabulary columns
|
|
8
|
+
* (src/common/ontology.ts: definition, comments, subsets, xrefs, synonyms, deprecated, and the
|
|
9
|
+
* basicPropertyValues of namespace, alt_id, created_by, creation_date, replaced_by, consider and
|
|
10
|
+
* property_value); any other node column becomes basicPropertyValues named after the column. Edges
|
|
11
|
+
* are `{ sub, pred, obj }` with the edge's `meta` column and its other columns in `meta`. Ids are
|
|
12
|
+
* written as IRIs (`GO:0008150` as `http://purl.obolibrary.org/obo/GO_0008150`, a relation by the
|
|
13
|
+
* IRI of the property whose shorthand it is) whenever the importer compacts the IRI back to the
|
|
14
|
+
* same id. The graph metadata, the PROPERTY nodes, the property edges and the axioms an OBO Graphs
|
|
15
|
+
* import kept in `meta.extra.obographs` are written back.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { type Column, GraphFormatError, type GraphSnapshot, type NodeId } from "@graphty/graph-format";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
COLUMN_AS_PROPERTY_VALUE_CODE,
|
|
22
|
+
DIRECTION_DROPPED_CODE,
|
|
23
|
+
GRAPH_COLUMN_AS_METADATA_CODE,
|
|
24
|
+
NODE_ORDER_CODE,
|
|
25
|
+
NONFINITE_AS_NULL_CODE,
|
|
26
|
+
RELATION_ASSUMED_CODE,
|
|
27
|
+
TYPEDEF_NODES_CODE,
|
|
28
|
+
} from "../../common/codes.js";
|
|
29
|
+
import { type PairFolding, pairFolding } from "../../common/direction.js";
|
|
30
|
+
import { LOSS } from "../../common/export.js";
|
|
31
|
+
import {
|
|
32
|
+
compactOboIri,
|
|
33
|
+
OBO_IN_OWL,
|
|
34
|
+
OBO_NODE_COLUMNS,
|
|
35
|
+
OBO_PURL,
|
|
36
|
+
OBOGRAPHS_PREDICATE_TAGS,
|
|
37
|
+
PLACEHOLDER_COLUMN,
|
|
38
|
+
SYNONYM_SCOPES,
|
|
39
|
+
} from "../../common/ontology.js";
|
|
40
|
+
import {
|
|
41
|
+
capabilityNotes,
|
|
42
|
+
cellOf,
|
|
43
|
+
emptyColumnNotes,
|
|
44
|
+
fitsVocabulary,
|
|
45
|
+
framelessNodes,
|
|
46
|
+
movedNodes,
|
|
47
|
+
type NoteFn,
|
|
48
|
+
notePropertyColumn,
|
|
49
|
+
slotColumn,
|
|
50
|
+
textOf,
|
|
51
|
+
UNWRITTEN_ROLES,
|
|
52
|
+
valuesOf,
|
|
53
|
+
} from "../../common/ontology-export.js";
|
|
54
|
+
import { type ResolvedExportOptions } from "../../common/options.js";
|
|
55
|
+
import { agree, plural } from "../../common/plural.js";
|
|
56
|
+
import { type ExplicitWeights, explicitWeights } from "../../common/weights.js";
|
|
57
|
+
import { type LossNote } from "../../types.js";
|
|
58
|
+
import { dialectCapabilities, exponentIfUnsafe, isJsonObject } from "./dialect.js";
|
|
59
|
+
import { BUILTIN_PREDICATES, FRAME_TYPES, META_KEYS, NODE_KEYS, PROPERTY_PREDICATES } from "./obographs.js";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* The loss codes only the `obographs` dialect records.
|
|
63
|
+
* @category Built-in formats
|
|
64
|
+
*/
|
|
65
|
+
export const OBOGRAPHS_LOSS = Object.freeze({
|
|
66
|
+
/** An edge column (or the explicit weights) is written into the edge's `meta` and reads back inside the `meta` column. */
|
|
67
|
+
EDGE_COLUMN_AS_META: "W_OBOGRAPHS_EDGE_COLUMN_AS_META",
|
|
68
|
+
/** A node id or relation is written as an IRI the importer's default `oboIds: "curie"` reads back as a different id. */
|
|
69
|
+
ID_CHANGED: "W_OBOGRAPHS_ID_CHANGED",
|
|
70
|
+
/** A `property_value` with an xsd datatype: basicPropertyValues have no datatype, so it reads back without one. */
|
|
71
|
+
DATATYPE_DROPPED: "W_OBOGRAPHS_DATATYPE_DROPPED",
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The codes the dialect shares with the other exporters, under the names JSON_LOSS gives them.
|
|
76
|
+
* @category Built-in formats
|
|
77
|
+
*/
|
|
78
|
+
export const OBOGRAPHS_SHARED_LOSS = Object.freeze({
|
|
79
|
+
/** A node column outside the OBO vocabulary reads back inside the `property_value` column. */
|
|
80
|
+
COLUMN_AS_PROPERTY_VALUE: COLUMN_AS_PROPERTY_VALUE_CODE,
|
|
81
|
+
/** An edge without a relation is written with the predicate `is_a`. */
|
|
82
|
+
RELATION_ASSUMED: RELATION_ASSUMED_CODE,
|
|
83
|
+
/** Typedef nodes are written as PROPERTY nodes, which read back as nodes only under `typedefs: "nodes"`. */
|
|
84
|
+
TYPEDEF_NODES: TYPEDEF_NODES_CODE,
|
|
85
|
+
/** A graph attribute is written into the graph's `meta` and reads back in `snapshot.meta.extra.obographs`. */
|
|
86
|
+
GRAPH_COLUMN_AS_METADATA: GRAPH_COLUMN_AS_METADATA_CODE,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
/** The settings of one export. */
|
|
90
|
+
interface ObographsSettings {
|
|
91
|
+
readonly common: ResolvedExportOptions;
|
|
92
|
+
readonly indent: string;
|
|
93
|
+
/** The ontology IRI unprefixed ids are written under, or null for the kept or derived one. */
|
|
94
|
+
readonly ontologyIri: string | null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** A planned export: the notes and the writer. */
|
|
98
|
+
interface ObographsPlan {
|
|
99
|
+
readonly notes: LossNote[];
|
|
100
|
+
/** The document's text parts. */
|
|
101
|
+
write(): Generator<string, void, undefined>;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The node type of each OBO frame type. */
|
|
105
|
+
const NODE_TYPES: Readonly<Record<string, string>> = Object.freeze({
|
|
106
|
+
Term: "CLASS",
|
|
107
|
+
Instance: "INDIVIDUAL",
|
|
108
|
+
Typedef: "PROPERTY",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/** The basicPropertyValues predicate of each OBO tag (the reverse of OBOGRAPHS_PREDICATE_TAGS). */
|
|
112
|
+
const TAG_PREDICATES: ReadonlyMap<string, string> = new Map(
|
|
113
|
+
[...OBOGRAPHS_PREDICATE_TAGS].map(([pred, tag]) => [tag, pred] as const),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
/** The OBO Graphs pred of the relations the importer reads from a built-in pred. */
|
|
117
|
+
const REVERSE_PREDICATES: Readonly<Record<string, string>> = Object.freeze({ is_a: "is_a", instance_of: "type" });
|
|
118
|
+
|
|
119
|
+
/** The vocabulary columns the dialect has a place for; every other node column is property values. */
|
|
120
|
+
const PLACED: ReadonlySet<string> = new Set([
|
|
121
|
+
"type",
|
|
122
|
+
"propertyType",
|
|
123
|
+
"def",
|
|
124
|
+
"def.xrefs",
|
|
125
|
+
"comment",
|
|
126
|
+
"subset",
|
|
127
|
+
"xref",
|
|
128
|
+
"synonym",
|
|
129
|
+
"is_obsolete",
|
|
130
|
+
"namespace",
|
|
131
|
+
"created_by",
|
|
132
|
+
"creation_date",
|
|
133
|
+
"alt_id",
|
|
134
|
+
"replaced_by",
|
|
135
|
+
"consider",
|
|
136
|
+
"property_value",
|
|
137
|
+
"obo.unrecognized",
|
|
138
|
+
]);
|
|
139
|
+
|
|
140
|
+
/** The basicPropertyValues columns, in the order they are written. */
|
|
141
|
+
const PROPERTY_TAGS: readonly string[] = [
|
|
142
|
+
"namespace",
|
|
143
|
+
"alt_id",
|
|
144
|
+
"created_by",
|
|
145
|
+
"creation_date",
|
|
146
|
+
"replaced_by",
|
|
147
|
+
"consider",
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
/** An IRI with a scheme. */
|
|
151
|
+
const IRI = /^[A-Za-z][A-Za-z0-9+.-]*:\/\//;
|
|
152
|
+
|
|
153
|
+
/** A CURIE whose prefix the OBO PURL mapping keeps (no underscore). */
|
|
154
|
+
const CURIE = /^([A-Za-z][A-Za-z0-9.-]*):(.+)$/;
|
|
155
|
+
|
|
156
|
+
/** The keyed OBO Graphs records an import kept. */
|
|
157
|
+
interface Kept {
|
|
158
|
+
readonly ids: "curie" | "iri";
|
|
159
|
+
readonly graph: Record<string, unknown>;
|
|
160
|
+
readonly document: Record<string, unknown>;
|
|
161
|
+
readonly properties: Record<string, Record<string, unknown>>;
|
|
162
|
+
readonly propertyEdges: unknown[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The records an OBO Graphs import kept under `meta.extra.obographs`.
|
|
167
|
+
* @param snapshot - the snapshot
|
|
168
|
+
* @returns the records, empty when the snapshot did not come from OBO Graphs
|
|
169
|
+
*/
|
|
170
|
+
function keptOf(snapshot: GraphSnapshot): Kept {
|
|
171
|
+
const raw = snapshot.meta.extra.obographs;
|
|
172
|
+
const r = isJsonObject(raw) ? raw : {};
|
|
173
|
+
const properties: Record<string, Record<string, unknown>> = {};
|
|
174
|
+
if (isJsonObject(r.properties)) {
|
|
175
|
+
for (const [iri, entry] of Object.entries(r.properties)) {
|
|
176
|
+
if (isJsonObject(entry)) {
|
|
177
|
+
properties[iri] = entry;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
ids: r.ids === "iri" ? "iri" : "curie",
|
|
183
|
+
graph: isJsonObject(r.graph) ? r.graph : {},
|
|
184
|
+
document: isJsonObject(r.document) ? r.document : {},
|
|
185
|
+
properties,
|
|
186
|
+
propertyEdges: Array.isArray(r.propertyEdges) ? r.propertyEdges : [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The shorthand a property's meta declares (the importer names the property by it).
|
|
192
|
+
* @param meta - the property's meta
|
|
193
|
+
* @returns the shorthand, or null
|
|
194
|
+
*/
|
|
195
|
+
function shorthandOf(meta: unknown): string | null {
|
|
196
|
+
if (!isJsonObject(meta) || !Array.isArray(meta.basicPropertyValues)) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
for (const pv of meta.basicPropertyValues) {
|
|
200
|
+
if (
|
|
201
|
+
isJsonObject(pv) &&
|
|
202
|
+
typeof pv.pred === "string" &&
|
|
203
|
+
OBOGRAPHS_PREDICATE_TAGS.get(pv.pred) === "shorthand" &&
|
|
204
|
+
typeof pv.val === "string"
|
|
205
|
+
) {
|
|
206
|
+
return pv.val;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The ontology IRI unprefixed ids are written under: the option, the kept graph id, else the OBO
|
|
214
|
+
* PURL of the ontology id (the kept OBO header's, or the graph name's).
|
|
215
|
+
* @param snapshot - the snapshot
|
|
216
|
+
* @param kept - the kept records
|
|
217
|
+
* @param option - the ontologyIri option, or null
|
|
218
|
+
* @returns the IRI
|
|
219
|
+
*/
|
|
220
|
+
function ontologyIriOf(snapshot: GraphSnapshot, kept: Kept, option: string | null): string {
|
|
221
|
+
if (option !== null) {
|
|
222
|
+
return option;
|
|
223
|
+
}
|
|
224
|
+
if (typeof kept.graph.id === "string") {
|
|
225
|
+
return kept.graph.id;
|
|
226
|
+
}
|
|
227
|
+
const { obo } = snapshot.meta.extra;
|
|
228
|
+
const header = isJsonObject(obo) && isJsonObject(obo.header) ? obo.header : {};
|
|
229
|
+
const ontology =
|
|
230
|
+
Array.isArray(header.ontology) && typeof header.ontology[0] === "string" ? header.ontology[0].trim() : null;
|
|
231
|
+
const name = ontology ?? snapshot.meta.name;
|
|
232
|
+
const id = name === null || name.length === 0 ? "graph" : name.replaceAll(/[^A-Za-z0-9_.\-/]/g, "_");
|
|
233
|
+
return `${OBO_PURL}${id}.owl`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* JSON text of a value with -0 kept and the non-finite numbers written as null (counted).
|
|
238
|
+
* @param value - the value
|
|
239
|
+
* @param nonfinite - the counter
|
|
240
|
+
* @param nonfinite.count - the count
|
|
241
|
+
* @returns the JSON text
|
|
242
|
+
*/
|
|
243
|
+
function json(value: unknown, nonfinite: { count: number }): string {
|
|
244
|
+
if (typeof value === "number") {
|
|
245
|
+
if (!Number.isFinite(value)) {
|
|
246
|
+
nonfinite.count++;
|
|
247
|
+
return "null";
|
|
248
|
+
}
|
|
249
|
+
return Object.is(value, -0) ? "-0" : exponentIfUnsafe(JSON.stringify(value));
|
|
250
|
+
}
|
|
251
|
+
if (Array.isArray(value)) {
|
|
252
|
+
return `[${value.map((v: unknown) => json(v, nonfinite)).join(",")}]`;
|
|
253
|
+
}
|
|
254
|
+
if (isJsonObject(value)) {
|
|
255
|
+
return `{${Object.entries(value)
|
|
256
|
+
.filter(([, v]) => v !== undefined)
|
|
257
|
+
.map(([k, v]) => `${JSON.stringify(k)}:${json(v, nonfinite)}`)
|
|
258
|
+
.join(",")}}`;
|
|
259
|
+
}
|
|
260
|
+
return JSON.stringify(value) ?? "null";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Whether a value is a non-empty array of strings.
|
|
265
|
+
* @param value - the value
|
|
266
|
+
* @param empty - whether an empty array is allowed
|
|
267
|
+
* @returns true for such an array
|
|
268
|
+
*/
|
|
269
|
+
function strings(value: unknown, empty = false): value is string[] {
|
|
270
|
+
return Array.isArray(value) && (empty || value.length > 0) && value.every((v) => typeof v === "string");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** A synonym as the vocabulary holds it. */
|
|
274
|
+
interface Synonym {
|
|
275
|
+
readonly text: string | null;
|
|
276
|
+
readonly scope: string | null;
|
|
277
|
+
readonly type: string | null;
|
|
278
|
+
readonly xrefs: string[];
|
|
279
|
+
/** The synonym's own `meta`, kept by the importer as it was. */
|
|
280
|
+
readonly meta?: unknown;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** One OBO Graphs export: the plan (ids, columns, notes) and the writer. */
|
|
284
|
+
/**
|
|
285
|
+
* The basicPropertyValue texts a property column gives one node: one per item of a list, JSON text
|
|
286
|
+
* for nested values, none when unset.
|
|
287
|
+
* @param column - the column
|
|
288
|
+
* @param i - the node
|
|
289
|
+
* @returns the texts
|
|
290
|
+
*/
|
|
291
|
+
function propertyTexts(column: Column, i: number): string[] {
|
|
292
|
+
const v = cellOf(column, i);
|
|
293
|
+
if (v === undefined) {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
if (column.dtype === "list") {
|
|
297
|
+
return (v as readonly unknown[]).map((item) => textOf(item, column.child.dtype));
|
|
298
|
+
}
|
|
299
|
+
const nested = column.dtype === "json" || column.meta.components > 1;
|
|
300
|
+
return [nested ? (JSON.stringify(v) ?? "null") : textOf(v, column.dtype)];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The graph columns as basicPropertyValues of the graph's meta.
|
|
305
|
+
* @param snapshot - the snapshot
|
|
306
|
+
* @returns one record per value
|
|
307
|
+
*/
|
|
308
|
+
function graphPropertyValues(snapshot: GraphSnapshot): { pred: string; val: string }[] {
|
|
309
|
+
const values: { pred: string; val: string }[] = [];
|
|
310
|
+
for (const column of snapshot.graph) {
|
|
311
|
+
const dtype = column.dtype === "list" ? column.child.dtype : column.dtype;
|
|
312
|
+
for (const item of valuesOf(cellOf(column, 0))) {
|
|
313
|
+
values.push({ pred: column.meta.name, val: typeof item === "string" ? item : textOf(item, dtype) });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return values;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Note a placed node column whose dtype reads back as another.
|
|
321
|
+
* @param note - records a note
|
|
322
|
+
* @param want - the dtype the column reads back as
|
|
323
|
+
* @param column - the column
|
|
324
|
+
*/
|
|
325
|
+
function dtypeNote(note: NoteFn, want: string, column: Column): void {
|
|
326
|
+
const listItems = column.dtype === "list" && column.child.dtype !== "string";
|
|
327
|
+
if (((want === "string" || want === "dict") && column.dtype !== want) || listItems) {
|
|
328
|
+
note(
|
|
329
|
+
LOSS.DTYPE,
|
|
330
|
+
`node column "${column.meta.name}" holds ${listItems ? "non-string items" : column.dtype}; it reads back as ${listItems ? "string items" : want}`,
|
|
331
|
+
column.meta.name,
|
|
332
|
+
column.length - column.nullCount,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* The property values of the written nodes that carry an xsd datatype.
|
|
339
|
+
* @param pv - the property_value column
|
|
340
|
+
* @param rows - the written nodes
|
|
341
|
+
* @returns the count
|
|
342
|
+
*/
|
|
343
|
+
function countDatatypes(pv: Column, rows: readonly number[]): number {
|
|
344
|
+
let datatypes = 0;
|
|
345
|
+
for (const i of rows) {
|
|
346
|
+
for (const item of (cellOf(pv, i) ?? []) as { datatype: unknown }[]) {
|
|
347
|
+
datatypes += item.datatype === null ? 0 : 1;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return datatypes;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
class ObographsExport {
|
|
354
|
+
readonly notes: LossNote[] = [];
|
|
355
|
+
|
|
356
|
+
private readonly note: NoteFn = (code, message, column = null, count = null) => {
|
|
357
|
+
this.notes.push(Object.freeze({ code, message, column, count }));
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
private readonly kept: Kept;
|
|
361
|
+
|
|
362
|
+
private readonly ontologyIri: string;
|
|
363
|
+
|
|
364
|
+
/** IRI to the shorthand the importer names it by. */
|
|
365
|
+
private readonly shorthands = new Map<string, string>();
|
|
366
|
+
|
|
367
|
+
/** Shorthand to the IRI of its property. */
|
|
368
|
+
private readonly shorthandIris = new Map<string, string>();
|
|
369
|
+
|
|
370
|
+
/** The IRI written for each node. */
|
|
371
|
+
private readonly iris: string[] = [];
|
|
372
|
+
|
|
373
|
+
/** The nodes whose IRI reads back as another id. */
|
|
374
|
+
private readonly changedRows = new Set<number>();
|
|
375
|
+
|
|
376
|
+
/** The Typedef nodes written with a shorthand, by node. */
|
|
377
|
+
private readonly typedefShorthands = new Map<number, string>();
|
|
378
|
+
|
|
379
|
+
private readonly written: number[] = [];
|
|
380
|
+
|
|
381
|
+
private rows: number[] = [];
|
|
382
|
+
|
|
383
|
+
private frameless: Uint8Array = new Uint8Array(0);
|
|
384
|
+
|
|
385
|
+
private label: Column | null = null;
|
|
386
|
+
|
|
387
|
+
private readonly placed = new Map<string, Column>();
|
|
388
|
+
|
|
389
|
+
private readonly properties: Column[] = [];
|
|
390
|
+
|
|
391
|
+
private metaColumn: Column | null = null;
|
|
392
|
+
|
|
393
|
+
private readonly edgeColumns: Column[] = [];
|
|
394
|
+
|
|
395
|
+
private readonly preds = new Map<number, string>();
|
|
396
|
+
|
|
397
|
+
private readonly weights: ExplicitWeights;
|
|
398
|
+
|
|
399
|
+
private readonly folding: PairFolding;
|
|
400
|
+
|
|
401
|
+
private readonly fatal: GraphFormatError | null;
|
|
402
|
+
|
|
403
|
+
private readonly nonfinite = { count: 0 };
|
|
404
|
+
|
|
405
|
+
private readonly indent: string;
|
|
406
|
+
|
|
407
|
+
/** The edges' endpoints. */
|
|
408
|
+
private readonly ends: { readonly src: ArrayLike<number>; readonly dst: ArrayLike<number> };
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Plan the export.
|
|
412
|
+
* @param snapshot - the snapshot
|
|
413
|
+
* @param settings - the resolved settings
|
|
414
|
+
*/
|
|
415
|
+
constructor(
|
|
416
|
+
private readonly snapshot: GraphSnapshot,
|
|
417
|
+
settings: ObographsSettings,
|
|
418
|
+
) {
|
|
419
|
+
this.kept = keptOf(snapshot);
|
|
420
|
+
this.ontologyIri = ontologyIriOf(snapshot, this.kept, settings.ontologyIri);
|
|
421
|
+
this.indent = settings.indent !== "" ? `\n${settings.indent}` : "";
|
|
422
|
+
for (const [iri, entry] of Object.entries(this.kept.properties)) {
|
|
423
|
+
const shorthand = shorthandOf(entry.meta);
|
|
424
|
+
if (shorthand !== null) {
|
|
425
|
+
this.shorthands.set(iri, shorthand);
|
|
426
|
+
this.shorthandIris.set(shorthand, iri);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
this.folding = pairFolding(snapshot);
|
|
430
|
+
this.ends = snapshot.edgeList();
|
|
431
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
432
|
+
if (!this.folding.folded(e)) {
|
|
433
|
+
this.written.push(e);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
this.weights = explicitWeights(snapshot);
|
|
437
|
+
this.planIds();
|
|
438
|
+
this.planNodes();
|
|
439
|
+
this.fatal =
|
|
440
|
+
capabilityNotes(snapshot, settings.common, {
|
|
441
|
+
caps: dialectCapabilities("obographs"),
|
|
442
|
+
label: this.label,
|
|
443
|
+
labelAssumed: slotColumn(snapshot.nodes, "label", "name").assumed,
|
|
444
|
+
edgeSlot: "edge meta",
|
|
445
|
+
note: this.note,
|
|
446
|
+
}) ?? this.emptyIdError();
|
|
447
|
+
this.planColumnNotes();
|
|
448
|
+
this.planEdges(settings.common);
|
|
449
|
+
emptyColumnNotes(snapshot, this.rows, this.frameless, this.written, this.note);
|
|
450
|
+
// a dry run counts the non-finite numbers the document writes as null
|
|
451
|
+
if (this.fatal === null) {
|
|
452
|
+
const parts = this.write();
|
|
453
|
+
while (parts.next().done !== true) {
|
|
454
|
+
// only the counts the writing leaves behind are wanted
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (this.nonfinite.count > 0) {
|
|
458
|
+
this.note(
|
|
459
|
+
NONFINITE_AS_NULL_CODE,
|
|
460
|
+
`${this.nonfinite.count} NaN or infinite value${plural(this.nonfinite.count)} ${agree(this.nonfinite.count, "is", "are")} written as null`,
|
|
461
|
+
null,
|
|
462
|
+
this.nonfinite.count,
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* The id the importer reads an IRI back as: the shorthand of the property it names, else the
|
|
469
|
+
* compacted IRI.
|
|
470
|
+
* @param iri - the IRI
|
|
471
|
+
* @returns the id
|
|
472
|
+
*/
|
|
473
|
+
private reread(iri: string): string {
|
|
474
|
+
return this.shorthands.get(iri) ?? compactOboIri(iri);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* An id as an IRI: an IRI as it is, a CURIE as its OBO PURL, anything else under the
|
|
479
|
+
* ontology IRI.
|
|
480
|
+
* @param id - the id
|
|
481
|
+
* @returns the IRI
|
|
482
|
+
*/
|
|
483
|
+
private expand(id: string): string {
|
|
484
|
+
if (IRI.test(id)) {
|
|
485
|
+
return id;
|
|
486
|
+
}
|
|
487
|
+
const curie = CURIE.exec(id);
|
|
488
|
+
if (curie !== null && !curie[1].includes("_")) {
|
|
489
|
+
return `${OBO_PURL}${curie[1]}_${curie[2]}`;
|
|
490
|
+
}
|
|
491
|
+
return `${this.ontologyIri}#${id}`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* The IRI to write for an id: the first form that reads back as the id (the property of a
|
|
496
|
+
* shorthand, the expanded IRI, the id itself); the id as it stands for a snapshot read with
|
|
497
|
+
* oboIds "iri".
|
|
498
|
+
* @param id - the id
|
|
499
|
+
* @returns the IRI and whether it reads back as the id
|
|
500
|
+
*/
|
|
501
|
+
private iriOf(id: string): { iri: string; exact: boolean } {
|
|
502
|
+
const candidates =
|
|
503
|
+
this.kept.ids === "iri"
|
|
504
|
+
? [id]
|
|
505
|
+
: [this.shorthandIris.get(id), this.expand(id), id].filter((c): c is string => c !== undefined);
|
|
506
|
+
const found = candidates.find((c) => this.reread(c) === id);
|
|
507
|
+
return found === undefined ? { iri: candidates[0], exact: false } : { iri: found, exact: true };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* The pred to write for a relation: `is_a`, `type` for instance_of, the property of a
|
|
512
|
+
* shorthand, the expanded IRI or the relation itself, whichever reads back as the relation.
|
|
513
|
+
* @param r - the relation
|
|
514
|
+
* @returns the pred and whether it reads back as the relation
|
|
515
|
+
*/
|
|
516
|
+
private predOf(r: string): { pred: string; exact: boolean } {
|
|
517
|
+
const reverse = REVERSE_PREDICATES[r];
|
|
518
|
+
const candidates = [reverse, this.shorthandIris.get(r), this.expand(r), r].filter(
|
|
519
|
+
(c): c is string => c !== undefined && !PROPERTY_PREDICATES.has(c),
|
|
520
|
+
);
|
|
521
|
+
const reread = (c: string): string => BUILTIN_PREDICATES[c] ?? this.shorthands.get(c) ?? compactOboIri(c);
|
|
522
|
+
const found =
|
|
523
|
+
this.kept.ids === "iri"
|
|
524
|
+
? candidates.find((c) => c === r && reread(c) === r)
|
|
525
|
+
: candidates.find((c) => reread(c) === r);
|
|
526
|
+
if (found !== undefined) {
|
|
527
|
+
return { pred: found, exact: true };
|
|
528
|
+
}
|
|
529
|
+
return { pred: this.kept.ids === "iri" ? r : candidates[0], exact: false };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* An empty node id: OBO Graphs gives every node a non-empty id, and the importer skips a node
|
|
534
|
+
* without one, so the export is refused.
|
|
535
|
+
* @returns the error, or null when every id has text
|
|
536
|
+
*/
|
|
537
|
+
private emptyIdError(): GraphFormatError | null {
|
|
538
|
+
const { snapshot } = this;
|
|
539
|
+
let count = 0;
|
|
540
|
+
let first = -1;
|
|
541
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
542
|
+
if (String(snapshot.ids.idOf(i)) === "") {
|
|
543
|
+
count++;
|
|
544
|
+
first = first < 0 ? i : first;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (count === 0) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
const message = `${count} node id${plural(count)} ${agree(count, "is", "are")} empty; an OBO Graphs node needs an id, so the save fails`;
|
|
551
|
+
this.note(LOSS.ID_CHARSET, message, null, count);
|
|
552
|
+
return new GraphFormatError("E_INVALID_ID", message, { reason: "charset", count, id: "", index: first });
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/** The IRI of every node, the Typedefs named by a shorthand, and the numeric-id note. */
|
|
556
|
+
private planIds(): void {
|
|
557
|
+
const { snapshot } = this;
|
|
558
|
+
const typeColumn = snapshot.nodes.get("type");
|
|
559
|
+
let numeric = 0;
|
|
560
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
561
|
+
const id: NodeId = snapshot.ids.idOf(i);
|
|
562
|
+
numeric += typeof id === "number" ? 1 : 0;
|
|
563
|
+
const text = String(id);
|
|
564
|
+
let { iri, exact } = this.iriOf(text);
|
|
565
|
+
const typedef = typeColumn !== null && typeColumn.isSet(i) && typeColumn.value(i) === "Typedef";
|
|
566
|
+
if (!exact && typedef && !IRI.test(text) && this.kept.ids === "curie") {
|
|
567
|
+
// a Typedef named by its shorthand: written under an IRI with the shorthand declared
|
|
568
|
+
iri = this.expand(text);
|
|
569
|
+
this.shorthands.set(iri, text);
|
|
570
|
+
this.shorthandIris.set(text, iri);
|
|
571
|
+
this.typedefShorthands.set(i, text);
|
|
572
|
+
exact = true;
|
|
573
|
+
}
|
|
574
|
+
this.iris.push(iri);
|
|
575
|
+
if (!exact) {
|
|
576
|
+
this.changedRows.add(i);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (numeric > 0) {
|
|
580
|
+
this.note(
|
|
581
|
+
LOSS.ID_TEXT_TYPE,
|
|
582
|
+
`${numeric} numeric node id${plural(numeric)} ${agree(numeric, "is", "are")} written as text and read back as strings`,
|
|
583
|
+
null,
|
|
584
|
+
numeric,
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** The written nodes (placeholders aside), the node order, and the columns with a place. */
|
|
590
|
+
private planNodes(): void {
|
|
591
|
+
const { snapshot } = this;
|
|
592
|
+
const { src, dst } = this.ends;
|
|
593
|
+
this.frameless = framelessNodes(snapshot, this.written, {
|
|
594
|
+
outgoingAllowed: true,
|
|
595
|
+
changed: (i) => this.changedRows.has(i),
|
|
596
|
+
});
|
|
597
|
+
const moved = movedNodes(
|
|
598
|
+
this.frameless,
|
|
599
|
+
this.written.flatMap((e) => [src[e], dst[e]]),
|
|
600
|
+
);
|
|
601
|
+
if (moved > 0) {
|
|
602
|
+
this.note(
|
|
603
|
+
NODE_ORDER_CODE,
|
|
604
|
+
`${moved} node${plural(moved)} ${agree(moved, "reads", "read")} back at another position: placeholder nodes are not written and read back after the written nodes`,
|
|
605
|
+
null,
|
|
606
|
+
moved,
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
this.rows = [];
|
|
610
|
+
for (let i = 0; i < snapshot.nodeCount; i++) {
|
|
611
|
+
if (this.frameless[i] === 0) {
|
|
612
|
+
this.rows.push(i);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
this.label = slotColumn(snapshot.nodes, "label", "name").column;
|
|
616
|
+
this.placeColumns();
|
|
617
|
+
this.planProperties();
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** The node columns written in their own places of a node (def, synonyms, xrefs...), not as property values. */
|
|
621
|
+
private placeColumns(): void {
|
|
622
|
+
const { snapshot } = this;
|
|
623
|
+
const reproducible = (id: unknown): boolean => typeof id === "string" && this.iriOf(id).exact;
|
|
624
|
+
for (const name of PLACED) {
|
|
625
|
+
const column = snapshot.nodes.get(name);
|
|
626
|
+
if (
|
|
627
|
+
column !== null &&
|
|
628
|
+
column !== this.label &&
|
|
629
|
+
column.meta.role === null &&
|
|
630
|
+
carries(name, column, this.rows, reproducible)
|
|
631
|
+
) {
|
|
632
|
+
this.placed.set(name, column);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
const def = this.placed.get("def");
|
|
636
|
+
const defXrefs = this.placed.get("def.xrefs");
|
|
637
|
+
if (
|
|
638
|
+
defXrefs !== undefined &&
|
|
639
|
+
(def === undefined || this.rows.some((i) => def.isSet(i) !== defXrefs.isSet(i)))
|
|
640
|
+
) {
|
|
641
|
+
this.placed.delete("def.xrefs");
|
|
642
|
+
}
|
|
643
|
+
if (def !== undefined && !this.placed.has("def.xrefs") && snapshot.nodes.get("def.xrefs") !== null) {
|
|
644
|
+
this.placed.delete("def");
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/** The node columns written as property values, each with its note. */
|
|
649
|
+
private planProperties(): void {
|
|
650
|
+
const { snapshot } = this;
|
|
651
|
+
const slotted = new Set<Column>([...this.placed.values(), ...(this.label === null ? [] : [this.label])]);
|
|
652
|
+
for (const column of snapshot.nodes) {
|
|
653
|
+
const { role, name } = column.meta;
|
|
654
|
+
if (slotted.has(column) || (role !== null && UNWRITTEN_ROLES.has(role))) {
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (name === PLACEHOLDER_COLUMN && this.rows.every((i) => !column.isSet(i))) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
this.properties.push(column);
|
|
661
|
+
notePropertyColumn(this.note, column, this.rows, `basicPropertyValues (pred ${JSON.stringify(name)})`);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/** The notes about node and graph columns: Typedef nodes, dtypes, datatypes, graph columns. */
|
|
666
|
+
private planColumnNotes(): void {
|
|
667
|
+
const { snapshot, rows, label, note } = this;
|
|
668
|
+
const type = this.placed.get("type");
|
|
669
|
+
const typedefs =
|
|
670
|
+
type === undefined ? 0 : rows.filter((i) => type.isSet(i) && type.value(i) === "Typedef").length;
|
|
671
|
+
if (typedefs > 0) {
|
|
672
|
+
note(
|
|
673
|
+
TYPEDEF_NODES_CODE,
|
|
674
|
+
`${typedefs} node${plural(typedefs)} ${agree(typedefs, "is", "are")} written as PROPERTY nodes; they read back as nodes only when imported with typedefs: "nodes", and then their edges with them`,
|
|
675
|
+
"type",
|
|
676
|
+
typedefs,
|
|
677
|
+
);
|
|
678
|
+
}
|
|
679
|
+
for (const [name, column] of [...this.placed, ...(label === null ? [] : ([["name", label]] as const))]) {
|
|
680
|
+
dtypeNote(note, name === "name" ? "string" : OBO_NODE_COLUMNS[name].dtype, column);
|
|
681
|
+
}
|
|
682
|
+
const pv = this.placed.get("property_value");
|
|
683
|
+
const datatypes = pv === undefined ? 0 : countDatatypes(pv, rows);
|
|
684
|
+
if (datatypes > 0) {
|
|
685
|
+
note(
|
|
686
|
+
OBOGRAPHS_LOSS.DATATYPE_DROPPED,
|
|
687
|
+
`${datatypes} property value${plural(datatypes)} ${agree(datatypes, "carries", "carry")} an xsd datatype; OBO Graphs basicPropertyValues have none, so they read back without it`,
|
|
688
|
+
"property_value",
|
|
689
|
+
datatypes,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
for (const column of snapshot.graph) {
|
|
693
|
+
note(
|
|
694
|
+
GRAPH_COLUMN_AS_METADATA_CODE,
|
|
695
|
+
`graph column "${column.meta.name}" is written as a basicPropertyValue of the graph's meta and reads back in meta.extra.obographs, not as a column`,
|
|
696
|
+
column.meta.name,
|
|
697
|
+
column.length - column.nullCount,
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* The edges' preds, meta and other columns, and their notes (relation, direction, ids that
|
|
704
|
+
* change).
|
|
705
|
+
* @param common - the common options
|
|
706
|
+
*/
|
|
707
|
+
private planEdges(common: ResolvedExportOptions): void {
|
|
708
|
+
const { snapshot, written, note } = this;
|
|
709
|
+
const slot = slotColumn(snapshot.edges, "kind", "relation");
|
|
710
|
+
const relation = slot.column;
|
|
711
|
+
if (slot.assumed && relation !== null) {
|
|
712
|
+
note(
|
|
713
|
+
LOSS.ROLE_ASSUMED,
|
|
714
|
+
`edge column "relation" has no role, so it is taken as the relation of each edge: it is written as each edge's pred, and reads back with the relation role ("kind")`,
|
|
715
|
+
"relation",
|
|
716
|
+
relation.length - relation.nullCount,
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
const meta = snapshot.edges.get("meta");
|
|
720
|
+
const metaOk =
|
|
721
|
+
meta !== null &&
|
|
722
|
+
meta.dtype === "json" &&
|
|
723
|
+
meta.meta.role === null &&
|
|
724
|
+
written.every((e) => !meta.isSet(e) || isJsonObject(cellOf(meta, e)));
|
|
725
|
+
this.metaColumn = metaOk ? meta : null;
|
|
726
|
+
for (const column of snapshot.edges) {
|
|
727
|
+
const { role } = column.meta;
|
|
728
|
+
if (
|
|
729
|
+
column === relation ||
|
|
730
|
+
column === this.metaColumn ||
|
|
731
|
+
role === "id" ||
|
|
732
|
+
(role !== null && UNWRITTEN_ROLES.has(role))
|
|
733
|
+
) {
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
this.edgeColumns.push(column);
|
|
737
|
+
const count = written.filter((e) => column.isSet(e)).length;
|
|
738
|
+
if (count > 0) {
|
|
739
|
+
note(
|
|
740
|
+
OBOGRAPHS_LOSS.EDGE_COLUMN_AS_META,
|
|
741
|
+
`edge column "${column.meta.name}" is written into each edge's meta and reads back inside the meta column`,
|
|
742
|
+
column.meta.name,
|
|
743
|
+
count,
|
|
744
|
+
);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
const { weights } = this;
|
|
748
|
+
const weighted = weights.weighted ? written.filter((e) => weights.isExplicit(e)).length : 0;
|
|
749
|
+
if (weighted > 0) {
|
|
750
|
+
note(
|
|
751
|
+
OBOGRAPHS_LOSS.EDGE_COLUMN_AS_META,
|
|
752
|
+
`${weighted} explicit edge weight${plural(weighted)} ${agree(weighted, "is", "are")} written as "weight" in each edge's meta and read back inside the meta column, not as weights`,
|
|
753
|
+
snapshot.edges.byRole("weight")?.meta.name ?? null,
|
|
754
|
+
weighted,
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
this.planPreds(relation);
|
|
758
|
+
this.directionNotes(common);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* The pred of every written edge, with the relation and id notes.
|
|
763
|
+
* @param relation - the relation column, or null
|
|
764
|
+
*/
|
|
765
|
+
private planPreds(relation: Column | null): void {
|
|
766
|
+
let assumed = 0;
|
|
767
|
+
let relationChanges = 0;
|
|
768
|
+
for (const e of this.written) {
|
|
769
|
+
const value = relation === null ? undefined : cellOf(relation, e);
|
|
770
|
+
if (typeof value !== "string") {
|
|
771
|
+
assumed++;
|
|
772
|
+
this.preds.set(e, "is_a");
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
const { pred, exact } = this.predOf(value);
|
|
776
|
+
this.preds.set(e, pred);
|
|
777
|
+
relationChanges += exact ? 0 : 1;
|
|
778
|
+
}
|
|
779
|
+
if (assumed > 0) {
|
|
780
|
+
this.note(
|
|
781
|
+
RELATION_ASSUMED_CODE,
|
|
782
|
+
`${assumed} edge${plural(assumed)} ${agree(assumed, "has", "have")} no relation; written with the pred is_a (an ontology reads is_a as subclassing)`,
|
|
783
|
+
relation?.meta.name ?? null,
|
|
784
|
+
assumed,
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
const idChanges = this.rows.filter((i) => this.changedRows.has(i)).length;
|
|
788
|
+
if (idChanges + relationChanges > 0) {
|
|
789
|
+
this.note(
|
|
790
|
+
OBOGRAPHS_LOSS.ID_CHANGED,
|
|
791
|
+
`${idChanges} node id${plural(idChanges)} and ${relationChanges} relation${plural(relationChanges)} are written as IRIs the importer reads back as other ids under its default oboIds "curie"`,
|
|
792
|
+
null,
|
|
793
|
+
idChanges + relationChanges,
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* The direction notes: every OBO Graphs edge is directed.
|
|
800
|
+
* @param common - the common options
|
|
801
|
+
*/
|
|
802
|
+
private directionNotes(common: ResolvedExportOptions): void {
|
|
803
|
+
const { snapshot, written, folding, note } = this;
|
|
804
|
+
if (!snapshot.directed) {
|
|
805
|
+
note(
|
|
806
|
+
DIRECTION_DROPPED_CODE,
|
|
807
|
+
`the snapshot is undirected; OBO Graphs edges are directed and read back directed (${written.length} edge${plural(written.length)})`,
|
|
808
|
+
null,
|
|
809
|
+
written.length,
|
|
810
|
+
);
|
|
811
|
+
} else if (common.onMixedDirection !== "error") {
|
|
812
|
+
const undirected = written.filter((e) => !folding.sourceDirected(e)).length;
|
|
813
|
+
if (undirected > 0) {
|
|
814
|
+
note(
|
|
815
|
+
DIRECTION_DROPPED_CODE,
|
|
816
|
+
`${undirected} undirected edge${plural(undirected)} ${agree(undirected, "is", "are")} written as one directed edge each`,
|
|
817
|
+
null,
|
|
818
|
+
undirected,
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
if (folding.mutualCount > 0) {
|
|
823
|
+
note(
|
|
824
|
+
LOSS.MUTUAL_EXPANDED,
|
|
825
|
+
`${folding.mutualCount} mutual pair${plural(folding.mutualCount)} ${agree(folding.mutualCount, "is", "are")} written as two edges; the mutual mark is lost`,
|
|
826
|
+
null,
|
|
827
|
+
folding.mutualCount,
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* A placed column's cell.
|
|
834
|
+
* @param name - the vocabulary name
|
|
835
|
+
* @param i - the node
|
|
836
|
+
* @returns the value, or undefined
|
|
837
|
+
*/
|
|
838
|
+
private at(name: string, i: number): unknown {
|
|
839
|
+
const column = this.placed.get(name);
|
|
840
|
+
return column === undefined ? undefined : cellOf(column, i);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* One node record as JSON.
|
|
845
|
+
* @param i - the node
|
|
846
|
+
* @returns the JSON text
|
|
847
|
+
*/
|
|
848
|
+
private nodeRecord(i: number): string {
|
|
849
|
+
const node: Record<string, unknown> = { id: this.iris[i] };
|
|
850
|
+
const lbl = this.label === null ? undefined : cellOf(this.label, i);
|
|
851
|
+
if (typeof lbl === "string") {
|
|
852
|
+
node.lbl = lbl;
|
|
853
|
+
}
|
|
854
|
+
// without a type column every node is a class; the type also marks the document as OBO Graphs
|
|
855
|
+
const untyped = this.snapshot.nodes.get("type") === null ? "Term" : undefined;
|
|
856
|
+
const type = this.placed.has("type") ? this.at("type", i) : untyped;
|
|
857
|
+
if (typeof type === "string") {
|
|
858
|
+
node.type = NODE_TYPES[type] ?? type;
|
|
859
|
+
}
|
|
860
|
+
const propertyType = this.at("propertyType", i);
|
|
861
|
+
if (propertyType !== undefined) {
|
|
862
|
+
node.propertyType = propertyType;
|
|
863
|
+
}
|
|
864
|
+
const meta = this.nodeMeta(i);
|
|
865
|
+
const extra = this.at("obo.unrecognized", i);
|
|
866
|
+
if (isJsonObject(extra)) {
|
|
867
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
868
|
+
if (key.startsWith("meta.")) {
|
|
869
|
+
meta[key.slice(5)] = value;
|
|
870
|
+
} else {
|
|
871
|
+
node[key] = value;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (Object.keys(meta).length > 0) {
|
|
876
|
+
node.meta = meta;
|
|
877
|
+
}
|
|
878
|
+
return json(node, this.nonfinite);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* One node's meta: definition, comments, subsets, xrefs, synonyms, deprecated and the
|
|
883
|
+
* basicPropertyValues.
|
|
884
|
+
* @param i - the node
|
|
885
|
+
* @returns the meta record
|
|
886
|
+
*/
|
|
887
|
+
private nodeMeta(i: number): Record<string, unknown> {
|
|
888
|
+
const meta: Record<string, unknown> = {};
|
|
889
|
+
const def = this.at("def", i);
|
|
890
|
+
if (typeof def === "string") {
|
|
891
|
+
meta.definition = { val: def, xrefs: this.at("def.xrefs", i) ?? [] };
|
|
892
|
+
}
|
|
893
|
+
const comment = this.at("comment", i);
|
|
894
|
+
if (typeof comment === "string") {
|
|
895
|
+
meta.comments = [comment];
|
|
896
|
+
}
|
|
897
|
+
const subsets = this.at("subset", i);
|
|
898
|
+
if (Array.isArray(subsets)) {
|
|
899
|
+
meta.subsets = subsets.map((s) => this.iriOf(String(s)).iri);
|
|
900
|
+
}
|
|
901
|
+
const xrefs = this.at("xref", i);
|
|
902
|
+
if (Array.isArray(xrefs)) {
|
|
903
|
+
meta.xrefs = xrefs.map((val) => ({ val }));
|
|
904
|
+
}
|
|
905
|
+
const synonyms = this.at("synonym", i);
|
|
906
|
+
if (Array.isArray(synonyms)) {
|
|
907
|
+
meta.synonyms = (synonyms as Synonym[]).map((s) => this.synonymRecord(s));
|
|
908
|
+
}
|
|
909
|
+
const obsolete = this.at("is_obsolete", i);
|
|
910
|
+
if (typeof obsolete === "boolean") {
|
|
911
|
+
meta.deprecated = obsolete;
|
|
912
|
+
}
|
|
913
|
+
const values = this.propertyValues(i);
|
|
914
|
+
if (values.length > 0) {
|
|
915
|
+
meta.basicPropertyValues = values;
|
|
916
|
+
}
|
|
917
|
+
return meta;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* One synonym record.
|
|
922
|
+
* @param s - the synonym
|
|
923
|
+
* @returns the record
|
|
924
|
+
*/
|
|
925
|
+
private synonymRecord(s: Synonym): Record<string, unknown> {
|
|
926
|
+
const out: Record<string, unknown> = {
|
|
927
|
+
pred: s.scope === null ? "hasSynonym" : `has${s.scope[0]}${s.scope.slice(1).toLowerCase()}Synonym`,
|
|
928
|
+
};
|
|
929
|
+
if (s.text !== null) {
|
|
930
|
+
out.val = s.text;
|
|
931
|
+
}
|
|
932
|
+
if (s.xrefs.length > 0) {
|
|
933
|
+
out.xrefs = s.xrefs;
|
|
934
|
+
}
|
|
935
|
+
if (s.type !== null) {
|
|
936
|
+
out.synonymType = this.iriOf(s.type).iri;
|
|
937
|
+
}
|
|
938
|
+
if (s.meta !== undefined) {
|
|
939
|
+
out.meta = s.meta;
|
|
940
|
+
}
|
|
941
|
+
return out;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* One node's basicPropertyValues: the tags OBO Graphs writes as annotations, the Typedef
|
|
946
|
+
* shorthand, the property values, then the columns outside the vocabulary.
|
|
947
|
+
* @param i - the node
|
|
948
|
+
* @returns the records
|
|
949
|
+
*/
|
|
950
|
+
private propertyValues(i: number): Record<string, unknown>[] {
|
|
951
|
+
const values: Record<string, unknown>[] = [];
|
|
952
|
+
for (const tag of PROPERTY_TAGS) {
|
|
953
|
+
const v = this.at(tag, i);
|
|
954
|
+
const pred = TAG_PREDICATES.get(tag) ?? tag;
|
|
955
|
+
for (const item of valuesOf(v)) {
|
|
956
|
+
const text = String(item);
|
|
957
|
+
values.push({ pred, val: tag === "replaced_by" || tag === "consider" ? this.iriOf(text).iri : text });
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
const shorthand = this.typedefShorthands.get(i);
|
|
961
|
+
if (shorthand !== undefined) {
|
|
962
|
+
values.push({ pred: `${OBO_IN_OWL}shorthand`, val: shorthand });
|
|
963
|
+
}
|
|
964
|
+
for (const item of (this.at("property_value", i) ?? []) as { relation: string; value: string }[]) {
|
|
965
|
+
values.push({ pred: this.iriOf(item.relation).iri, val: item.value });
|
|
966
|
+
}
|
|
967
|
+
for (const column of this.properties) {
|
|
968
|
+
for (const val of propertyTexts(column, i)) {
|
|
969
|
+
values.push({ pred: column.meta.name, val });
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return values;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* One edge record as JSON.
|
|
977
|
+
* @param e - the edge
|
|
978
|
+
* @returns the JSON text
|
|
979
|
+
*/
|
|
980
|
+
private edgeRecord(e: number): string {
|
|
981
|
+
const { src, dst } = this.ends;
|
|
982
|
+
const edge: Record<string, unknown> = {
|
|
983
|
+
sub: this.iris[src[e]],
|
|
984
|
+
pred: this.preds.get(e),
|
|
985
|
+
obj: this.iris[dst[e]],
|
|
986
|
+
};
|
|
987
|
+
const base = this.metaColumn === null ? undefined : cellOf(this.metaColumn, e);
|
|
988
|
+
const meta: Record<string, unknown> = isJsonObject(base) ? { ...base } : {};
|
|
989
|
+
for (const column of this.edgeColumns) {
|
|
990
|
+
const v = cellOf(column, e);
|
|
991
|
+
if (v !== undefined) {
|
|
992
|
+
meta[column.meta.name] = v;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (this.weights.weighted && this.weights.isExplicit(e)) {
|
|
996
|
+
meta.weight = this.weights.value(e);
|
|
997
|
+
}
|
|
998
|
+
if (Object.keys(meta).length > 0 || base !== undefined) {
|
|
999
|
+
edge.meta = meta;
|
|
1000
|
+
}
|
|
1001
|
+
return json(edge, this.nonfinite);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
/**
|
|
1005
|
+
* The graph record up to its nodes: the id, the label and the meta (the graph columns as
|
|
1006
|
+
* basicPropertyValues).
|
|
1007
|
+
* @returns the JSON text without its closing brace
|
|
1008
|
+
*/
|
|
1009
|
+
private graphHead(): string {
|
|
1010
|
+
const { kept, snapshot } = this;
|
|
1011
|
+
const graph: Record<string, unknown> = {
|
|
1012
|
+
id: typeof kept.graph.id === "string" ? kept.graph.id : this.ontologyIri,
|
|
1013
|
+
};
|
|
1014
|
+
const named = snapshot.meta.name !== null && kept.graph.id === undefined;
|
|
1015
|
+
const lbl = kept.graph.lbl ?? (named ? snapshot.meta.name : undefined);
|
|
1016
|
+
if (lbl !== undefined) {
|
|
1017
|
+
graph.lbl = lbl;
|
|
1018
|
+
}
|
|
1019
|
+
const meta: Record<string, unknown> = isJsonObject(kept.graph.meta) ? { ...kept.graph.meta } : {};
|
|
1020
|
+
const values: unknown[] = Array.isArray(meta.basicPropertyValues)
|
|
1021
|
+
? [...(meta.basicPropertyValues as unknown[])]
|
|
1022
|
+
: [];
|
|
1023
|
+
values.push(...graphPropertyValues(snapshot));
|
|
1024
|
+
if (values.length > 0) {
|
|
1025
|
+
meta.basicPropertyValues = values;
|
|
1026
|
+
}
|
|
1027
|
+
if (Object.keys(meta).length > 0) {
|
|
1028
|
+
graph.meta = meta;
|
|
1029
|
+
}
|
|
1030
|
+
return json(graph, this.nonfinite).slice(0, -1);
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* The graph keys after the edges (the kept axioms and other keys).
|
|
1035
|
+
* @returns the JSON text from the comma to the closing brace
|
|
1036
|
+
*/
|
|
1037
|
+
private graphTail(): string {
|
|
1038
|
+
const rest: Record<string, unknown> = {};
|
|
1039
|
+
for (const [key, value] of Object.entries(this.kept.graph)) {
|
|
1040
|
+
if (key !== "id" && key !== "lbl" && key !== "meta" && key !== "nodes" && key !== "edges") {
|
|
1041
|
+
rest[key] = value;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
const text = json(rest, this.nonfinite);
|
|
1045
|
+
return text === "{}" ? "}" : `,${text.slice(1)}`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* The kept PROPERTY nodes no written node stands for.
|
|
1050
|
+
* @returns their JSON texts
|
|
1051
|
+
*/
|
|
1052
|
+
private propertyNodes(): string[] {
|
|
1053
|
+
const writtenIris = new Set(this.rows.map((i) => this.iris[i]));
|
|
1054
|
+
const out: string[] = [];
|
|
1055
|
+
for (const [iri, entry] of Object.entries(this.kept.properties)) {
|
|
1056
|
+
if (writtenIris.has(iri)) {
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
const node: Record<string, unknown> = { id: iri, lbl: entry.lbl, type: "PROPERTY" };
|
|
1060
|
+
node.propertyType = entry.propertyType;
|
|
1061
|
+
node.meta = entry.meta;
|
|
1062
|
+
out.push(json(node, this.nonfinite));
|
|
1063
|
+
}
|
|
1064
|
+
return out;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* The document's text parts.
|
|
1069
|
+
* @yields the graph head, one part per node and edge, the tail
|
|
1070
|
+
* @returns nothing
|
|
1071
|
+
*/
|
|
1072
|
+
*write(): Generator<string, void, undefined> {
|
|
1073
|
+
if (this.fatal !== null) {
|
|
1074
|
+
throw this.fatal;
|
|
1075
|
+
}
|
|
1076
|
+
const { indent } = this;
|
|
1077
|
+
yield `{"graphs":[${this.graphHead()},"nodes":[`;
|
|
1078
|
+
let first = true;
|
|
1079
|
+
const item = (text: string): string => {
|
|
1080
|
+
const out = `${first ? "" : ","}${indent}${text}`;
|
|
1081
|
+
first = false;
|
|
1082
|
+
return out;
|
|
1083
|
+
};
|
|
1084
|
+
for (const i of this.rows) {
|
|
1085
|
+
yield item(this.nodeRecord(i));
|
|
1086
|
+
}
|
|
1087
|
+
for (const node of this.propertyNodes()) {
|
|
1088
|
+
yield item(node);
|
|
1089
|
+
}
|
|
1090
|
+
yield `],"edges":[`;
|
|
1091
|
+
first = true;
|
|
1092
|
+
for (const e of this.written) {
|
|
1093
|
+
yield item(this.edgeRecord(e));
|
|
1094
|
+
}
|
|
1095
|
+
for (const edge of this.kept.propertyEdges) {
|
|
1096
|
+
yield item(json(edge, this.nonfinite));
|
|
1097
|
+
}
|
|
1098
|
+
yield `]${this.graphTail()}]`;
|
|
1099
|
+
const document = json(this.kept.document, this.nonfinite);
|
|
1100
|
+
yield document === "{}" ? "}\n" : `,${document.slice(1)}\n`;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Plan an OBO Graphs export.
|
|
1106
|
+
* @param snapshot - the snapshot
|
|
1107
|
+
* @param settings - the resolved settings
|
|
1108
|
+
* @returns the notes and the writer
|
|
1109
|
+
* @category Plugin helpers
|
|
1110
|
+
*/
|
|
1111
|
+
export function planObographs(snapshot: GraphSnapshot, settings: ObographsSettings): ObographsPlan {
|
|
1112
|
+
const plan = new ObographsExport(snapshot, settings);
|
|
1113
|
+
return { notes: plan.notes, write: () => plan.write() };
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
/**
|
|
1117
|
+
* Whether a vocabulary column is written into its OBO Graphs place and read back unchanged on
|
|
1118
|
+
* every written node.
|
|
1119
|
+
* @param name - the column name (a PLACED name)
|
|
1120
|
+
* @param column - the column
|
|
1121
|
+
* @param rows - the written nodes
|
|
1122
|
+
* @param reproducible - whether an id is written as an IRI that reads back as itself
|
|
1123
|
+
* @returns true when the place carries the column
|
|
1124
|
+
*/
|
|
1125
|
+
function carries(
|
|
1126
|
+
name: string,
|
|
1127
|
+
column: Column,
|
|
1128
|
+
rows: readonly number[],
|
|
1129
|
+
reproducible: (id: unknown) => boolean,
|
|
1130
|
+
): boolean {
|
|
1131
|
+
const spec = OBO_NODE_COLUMNS[name];
|
|
1132
|
+
if (!fitsVocabulary(spec.dtype, column) || column.meta.components > 1) {
|
|
1133
|
+
return false;
|
|
1134
|
+
}
|
|
1135
|
+
const valid = (v: unknown): boolean => {
|
|
1136
|
+
switch (name) {
|
|
1137
|
+
case "type":
|
|
1138
|
+
return typeof v === "string" && !Object.hasOwn(FRAME_TYPES, v);
|
|
1139
|
+
case "propertyType":
|
|
1140
|
+
case "def":
|
|
1141
|
+
case "comment":
|
|
1142
|
+
case "namespace":
|
|
1143
|
+
case "created_by":
|
|
1144
|
+
case "creation_date":
|
|
1145
|
+
return typeof v === "string";
|
|
1146
|
+
case "is_obsolete":
|
|
1147
|
+
return typeof v === "boolean";
|
|
1148
|
+
case "def.xrefs":
|
|
1149
|
+
return strings(v, true);
|
|
1150
|
+
case "xref":
|
|
1151
|
+
case "alt_id":
|
|
1152
|
+
return strings(v);
|
|
1153
|
+
case "subset":
|
|
1154
|
+
case "replaced_by":
|
|
1155
|
+
case "consider":
|
|
1156
|
+
return strings(v) && v.every(reproducible);
|
|
1157
|
+
case "synonym":
|
|
1158
|
+
return (
|
|
1159
|
+
Array.isArray(v) &&
|
|
1160
|
+
v.length > 0 &&
|
|
1161
|
+
v.every(
|
|
1162
|
+
(s) =>
|
|
1163
|
+
isJsonObject(s) &&
|
|
1164
|
+
Object.keys(s).length === ("meta" in s ? 5 : 4) &&
|
|
1165
|
+
(s.text === null || typeof s.text === "string") &&
|
|
1166
|
+
(s.scope === null || (typeof s.scope === "string" && SYNONYM_SCOPES.has(s.scope))) &&
|
|
1167
|
+
(s.type === null || reproducible(s.type)) &&
|
|
1168
|
+
strings(s.xrefs, true),
|
|
1169
|
+
)
|
|
1170
|
+
);
|
|
1171
|
+
case "property_value":
|
|
1172
|
+
return (
|
|
1173
|
+
Array.isArray(v) &&
|
|
1174
|
+
v.length > 0 &&
|
|
1175
|
+
v.every(
|
|
1176
|
+
(x) =>
|
|
1177
|
+
isJsonObject(x) &&
|
|
1178
|
+
Object.keys(x).length === 3 &&
|
|
1179
|
+
reproducible(x.relation) &&
|
|
1180
|
+
!OBOGRAPHS_PREDICATE_TAGS.has(String(x.relation)) &&
|
|
1181
|
+
typeof x.value === "string" &&
|
|
1182
|
+
(x.datatype === null || typeof x.datatype === "string"),
|
|
1183
|
+
)
|
|
1184
|
+
);
|
|
1185
|
+
case "obo.unrecognized":
|
|
1186
|
+
return (
|
|
1187
|
+
isJsonObject(v) &&
|
|
1188
|
+
Object.keys(v).every((key) =>
|
|
1189
|
+
key.startsWith("meta.") ? !META_KEYS.has(key.slice(5)) && key.length > 5 : !NODE_KEYS.has(key),
|
|
1190
|
+
)
|
|
1191
|
+
);
|
|
1192
|
+
default:
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
return rows.every((i) => !column.isSet(i) || valid(cellOf(column, i)));
|
|
1197
|
+
}
|