@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
|
@@ -1,26 +1,986 @@
|
|
|
1
|
-
import { INVALID_INDEX } from "@graphty/graph-format";
|
|
2
|
-
import {
|
|
3
|
-
import { s as
|
|
4
|
-
import { w as weightFromValue } from "./
|
|
5
|
-
import { z as
|
|
1
|
+
import { GraphFormatError, INVALID_INDEX } from "@graphty/graph-format";
|
|
2
|
+
import { c as childrenCsr } from "./children-CL3Cy0ez.js";
|
|
3
|
+
import { s as stringify, f as aspectBlock, d as directionNotes, p as planNodeIds, b as planEdgeIds, O as ORIGINAL_ID_ATTRIBUTE, h as nextNodeId, i as noteList, g as declaredType, z as zAsOption } from "./importer-DfVu6j59.js";
|
|
4
|
+
import { c as capabilities, L as LOSS, r as resolveExportOptions, p as pairFolding, b as plural, a as checkCapabilities, e as explicitWeights, ad as TOO_LARGE_CODE, S as SINK_OPTION_CODE, O as OPTION_IGNORED_CODE, D as DIRECTION_FORCED_CODE, d as DIRECTION_REFUSED_CODE, R as ROLE_TAKEN_CODE, C as COLUMN_RENAMED_CODE, N as NO_GRAPH_CODE, ae as AMBIGUOUS_GRAPH_NAME_CODE, af as GRAPH_NOT_FOUND_CODE, l as MULTIPLE_GRAPHS_CODE, I as ID_MERGED_CODE, P as PRECISION_CODE, J as ID_TEXT_TYPE_CODE, a2 as JSON_NONSTANDARD_NUMBER_CODE, ag as SINGLE_OBJECT_ASPECT_CODE, W as MULTI_ASPECT_FRAGMENT_CODE, g as UNKNOWN_ELEMENT_CODE, m as DUPLICATE_EDGE_ID_CODE, n as DUPLICATE_NODE_CODE, s as MISSING_ENDPOINT_CODE, t as MISSING_ID_CODE, ah as STYLES_NOT_IMPORTED_CODE, ai as PARENT_CYCLE_CODE, q as UNKNOWN_PARENT_CODE, o as DUPLICATE_ATTRIBUTE_CODE, i as DANGLING_REFERENCE_CODE, U as UNKNOWN_ATTR_TYPE_CODE, aj as WIDENED_CODE, a3 as BAD_VALUE_CODE, j as COUNT_MISMATCH_CODE, Y as ASPECT_ORDER_CODE, V as BAD_ASPECT_BLOCK_CODE, _ as STATUS_WARNING_CODE, Z as STATUS_FAILED_CODE, w as UNKNOWN_ENCODING_CODE, x as ENCODING_FALLBACK_CODE, y as INVALID_ENCODING_CODE, z as INVALID_UTF8_CODE, ak as SYNTAX_CODE, E as EMPTY_INPUT_CODE, F as ELEMENT_ISSUE, G as INPUT_ISSUE, al as resolveImportOptions, a8 as ImportReportBuilder, a4 as textChunks, am as throwIfAborted, an as reportUnusedOptions, ao as reportSinkOptions, a7 as chooseGraph, ap as graphChosen, aq as DirectionResolver, ar as IdCoercer, $ as agree, a1 as declareResolved, a0 as uniqueColumnName, as as weightFromValue } from "./export-BTI4yQgy.js";
|
|
5
|
+
import { i as isRecord, P as POSITION_COLUMN, h as headText, s as scanAspects, r as reportTooDeep, a as reportSharedBlock, C as CxStructure, J as JsonScanError, E as ExactInteger, c as cxId, p as plainJson, f as fitsF32, b as flipY, z as zDecl, d as positionDecl, e as declareFresh, g as inexactLiteral, k as keptPrecision } from "./json-elements-3tnyt5uZ.js";
|
|
6
|
+
import { j as joinText, e as encodeChunks } from "./writer-DxjgHwSe.js";
|
|
7
|
+
const CX_FORMAT$1 = "cx";
|
|
8
|
+
const CX_LOSS = Object.freeze({
|
|
9
|
+
/**
|
|
10
|
+
* Every edge is written as directed, so an undirected graph, or the undirected edges of a mixed graph, read back
|
|
11
|
+
* as directed.
|
|
12
|
+
*/
|
|
13
|
+
UNDIRECTED_AS_DIRECTED: "W_CX_UNDIRECTED_AS_DIRECTED",
|
|
14
|
+
/** A nested (json) column is written as a string attribute holding its JSON text. */
|
|
15
|
+
JSON_AS_STRING: "W_CX_JSON_AS_STRING",
|
|
16
|
+
/** A mutual pair is written as two directed edges without its mark. */
|
|
17
|
+
MUTUAL_EXPANDED: LOSS.MUTUAL_EXPANDED,
|
|
18
|
+
/**
|
|
19
|
+
* An attribute named `weight` without the weight role reads back as the edge weight, or is not written when the
|
|
20
|
+
* graph has weights of its own.
|
|
21
|
+
*/
|
|
22
|
+
WEIGHT_KEY_CLASH: LOSS.WEIGHT_KEY_CLASH,
|
|
23
|
+
/** A start / end / timestamp column: CX has no time. */
|
|
24
|
+
TEMPORAL_DROPPED: LOSS.TEMPORAL,
|
|
25
|
+
/** An attribute with a role the format has no place for is written as a plain attribute; the role is lost. */
|
|
26
|
+
ROLE_DROPPED: LOSS.ROLE,
|
|
27
|
+
/**
|
|
28
|
+
* An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and
|
|
29
|
+
* reads back with that role.
|
|
30
|
+
*/
|
|
31
|
+
ROLE_ASSUMED: LOSS.ROLE_ASSUMED,
|
|
32
|
+
/**
|
|
33
|
+
* An attribute type CX stores as another (a 32-bit float as double, an unsigned integer as long, a byte as
|
|
34
|
+
* integer, a dictionary as string); it reads back with that type.
|
|
35
|
+
*/
|
|
36
|
+
DTYPE_UNSUPPORTED: LOSS.DTYPE,
|
|
37
|
+
/** A multi-component column (a second view's `position@2`, a vector) is written as a list of doubles. */
|
|
38
|
+
COMPONENTS_FLATTENED: LOSS.COMPONENTS,
|
|
39
|
+
/** A declared default: CX has none. */
|
|
40
|
+
DEFAULT_DROPPED: LOSS.DEFAULT,
|
|
41
|
+
/** Declared options: CX has no enumerations. */
|
|
42
|
+
OPTIONS_DROPPED: LOSS.OPTIONS,
|
|
43
|
+
/**
|
|
44
|
+
* Node ids the format cannot write, under `sanitizeIds: "error"`; the save fails with E_INVALID_ID. Pass
|
|
45
|
+
* `sanitizeIds: "mangle"` to rewrite them.
|
|
46
|
+
*/
|
|
47
|
+
ID_CHARSET: LOSS.ID_CHARSET,
|
|
48
|
+
/** Node ids that are not integers under sanitizeIds "mangle": renumbered, originals kept. */
|
|
49
|
+
ID_MANGLED: LOSS.ID_MANGLED,
|
|
50
|
+
/** Edges without a usable integer id get generated ids. */
|
|
51
|
+
EDGE_IDS_GENERATED: LOSS.EDGE_IDS_GENERATED,
|
|
52
|
+
/**
|
|
53
|
+
* An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the
|
|
54
|
+
* name the format's importer gives it.
|
|
55
|
+
*/
|
|
56
|
+
COLUMN_NAME_CHANGED: LOSS.COLUMN_NAME_CHANGED,
|
|
57
|
+
/** An extension table other than the `cx:citations` / `cx:supports` tables a CX import creates. */
|
|
58
|
+
EXTENSION_TABLE_DROPPED: LOSS.EXTENSION_TABLE,
|
|
59
|
+
/** A position, stacking order or weight that is NaN or infinite: CX spells no such number there. */
|
|
60
|
+
NONFINITE_AS_NULL: LOSS.NONFINITE_AS_NULL,
|
|
61
|
+
/** Visual columns (color, size, shape, thickness roles): CX keeps style as visual properties, not roles. */
|
|
62
|
+
VIZ_DROPPED: LOSS.VIZ
|
|
63
|
+
});
|
|
64
|
+
const CX_CAPABILITIES = capabilities({
|
|
65
|
+
mixedDirection: false,
|
|
66
|
+
multiEdges: true,
|
|
67
|
+
selfLoops: true,
|
|
68
|
+
edgeIds: "required",
|
|
69
|
+
idCharset: "integer",
|
|
70
|
+
dtypes: ["string", "f64", "i32", "bool"],
|
|
71
|
+
components: false,
|
|
72
|
+
lists: true,
|
|
73
|
+
json: false,
|
|
74
|
+
defaults: false,
|
|
75
|
+
options: false,
|
|
76
|
+
hierarchy: true,
|
|
77
|
+
temporal: "none",
|
|
78
|
+
graphAttributes: true,
|
|
79
|
+
positions: true,
|
|
80
|
+
viz: false
|
|
81
|
+
});
|
|
82
|
+
const SLOT_ROLES = /* @__PURE__ */ new Set(["label", "position", "id"]);
|
|
83
|
+
const ROLE_NAMES = Object.freeze({
|
|
84
|
+
label: "name",
|
|
85
|
+
position: POSITION_COLUMN,
|
|
86
|
+
id: "id",
|
|
87
|
+
parent: "parent"
|
|
88
|
+
});
|
|
89
|
+
const SKIPPED_ROLES = /* @__PURE__ */ new Set([
|
|
90
|
+
"directed",
|
|
91
|
+
"pair",
|
|
92
|
+
"mutual",
|
|
93
|
+
"weight",
|
|
94
|
+
"timeText",
|
|
95
|
+
"originalId",
|
|
96
|
+
"parent",
|
|
97
|
+
"parents",
|
|
98
|
+
"start",
|
|
99
|
+
"end",
|
|
100
|
+
"timestamp",
|
|
101
|
+
"timestamps",
|
|
102
|
+
"spells",
|
|
103
|
+
"open",
|
|
104
|
+
"spellsOpen",
|
|
105
|
+
"position",
|
|
106
|
+
"color",
|
|
107
|
+
"size",
|
|
108
|
+
"shape",
|
|
109
|
+
"thickness",
|
|
110
|
+
"id"
|
|
111
|
+
]);
|
|
112
|
+
const BYPASS_NAMESPACE = "cx.bypass";
|
|
113
|
+
const PROVENANCE_TABLES = Object.freeze({
|
|
114
|
+
"cx:citations": "citations",
|
|
115
|
+
"cx:supports": "supports"
|
|
116
|
+
});
|
|
117
|
+
const NEVER_WRITTEN = /* @__PURE__ */ new Set([
|
|
118
|
+
"nodes",
|
|
119
|
+
"edges",
|
|
120
|
+
"nodeAttributes",
|
|
121
|
+
"edgeAttributes",
|
|
122
|
+
"networkAttributes",
|
|
123
|
+
"cartesianLayout",
|
|
124
|
+
"cyGroups",
|
|
125
|
+
"cyVisualProperties",
|
|
126
|
+
"cyTableColumn",
|
|
127
|
+
"cySubNetworks",
|
|
128
|
+
"cyNetworkRelations",
|
|
129
|
+
"cyViews",
|
|
130
|
+
"CX Element ID",
|
|
131
|
+
"citations",
|
|
132
|
+
"supports",
|
|
133
|
+
"nodeCitations",
|
|
134
|
+
"edgeCitations",
|
|
135
|
+
"nodeSupports",
|
|
136
|
+
"edgeSupports",
|
|
137
|
+
"functionTerms",
|
|
138
|
+
"reifiedEdges",
|
|
139
|
+
"numberVerification",
|
|
140
|
+
"metaData",
|
|
141
|
+
"status",
|
|
142
|
+
"groups",
|
|
143
|
+
"subnetwork"
|
|
144
|
+
]);
|
|
145
|
+
const WEIGHT_ATTRIBUTE = "weight";
|
|
146
|
+
const NUMERIC_DTYPES = /* @__PURE__ */ new Set(["f32", "f64", "i32", "u32", "u8"]);
|
|
147
|
+
function fromCx(column, namespace) {
|
|
148
|
+
return column.meta.origin?.format === CX_FORMAT$1 && column.meta.origin.namespace === namespace;
|
|
149
|
+
}
|
|
150
|
+
function isBypass(column) {
|
|
151
|
+
return column.meta.origin?.namespace === BYPASS_NAMESPACE;
|
|
152
|
+
}
|
|
153
|
+
function isZ(column) {
|
|
154
|
+
return column.meta.extra.cytoscape === "z";
|
|
155
|
+
}
|
|
156
|
+
function isLink(column) {
|
|
157
|
+
return fromCx(column, "provenance") && column.meta.dtype === "list" && (column.meta.name === "citations" || column.meta.name === "supports");
|
|
158
|
+
}
|
|
159
|
+
function coreNeedsText(column) {
|
|
160
|
+
const { dtype } = column.meta;
|
|
161
|
+
return dtype !== "string" && dtype !== "dict" && dtype !== "json";
|
|
162
|
+
}
|
|
163
|
+
function genericNotes(snapshot, common, several, own, notes, note) {
|
|
164
|
+
let fatal = null;
|
|
165
|
+
const edgeLabel = snapshot.edges.byRole("label")?.meta.name;
|
|
166
|
+
const ownNames = new Set(own.filter((c) => c !== null).map((c) => c.meta.name));
|
|
167
|
+
for (const gen of checkCapabilities(snapshot, CX_CAPABILITIES, common, {
|
|
168
|
+
roles: SLOT_ROLES,
|
|
169
|
+
// a parents column reads back as parents only when some node has several parents
|
|
170
|
+
roleNames: several ? { ...ROLE_NAMES, parents: "parents" } : ROLE_NAMES
|
|
171
|
+
})) {
|
|
172
|
+
if (writtenOtherwise(gen, ownNames)) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (gen.code === LOSS.JSON) {
|
|
176
|
+
const which = gen.column === null ? "a column" : `column "${gen.column}"`;
|
|
177
|
+
note(
|
|
178
|
+
CX_LOSS.JSON_AS_STRING,
|
|
179
|
+
`${which} holds nested values; written as a string attribute holding their JSON text`,
|
|
180
|
+
gen.column,
|
|
181
|
+
gen.count
|
|
182
|
+
);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (isEdgeLabelRename(gen, edgeLabel)) {
|
|
186
|
+
note(
|
|
187
|
+
LOSS.ROLE,
|
|
188
|
+
`edge column "${edgeLabel ?? ""}" (label) is written as a plain attribute; CX edges have no label slot and the role is lost`,
|
|
189
|
+
gen.column,
|
|
190
|
+
gen.count
|
|
191
|
+
);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (gen.code === LOSS.MIXED_DIRECTION_ERROR && fatal === null) {
|
|
195
|
+
fatal = new GraphFormatError("E_DIRECTED", gen.message, { reason: "mixed direction" });
|
|
196
|
+
}
|
|
197
|
+
notes.push(gen);
|
|
198
|
+
}
|
|
199
|
+
if (edgeLabel === "name") {
|
|
200
|
+
note(
|
|
201
|
+
LOSS.ROLE,
|
|
202
|
+
`edge column "name" (label) is written as a plain attribute; CX edges have no label slot and the role is lost`,
|
|
203
|
+
"name",
|
|
204
|
+
null
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
return fatal;
|
|
208
|
+
}
|
|
209
|
+
function writtenOtherwise(gen, ownNames) {
|
|
210
|
+
if (gen.column !== null && ownNames.has(gen.column) && (gen.code === LOSS.JSON || gen.code === LOSS.DTYPE)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
if (gen.code === LOSS.ID_CHARSET || gen.code === LOSS.ID_MANGLED) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
return gen.code === LOSS.EXTENSION_TABLE && gen.column !== null && gen.column in PROVENANCE_TABLES;
|
|
217
|
+
}
|
|
218
|
+
function isEdgeLabelRename(gen, edgeLabel) {
|
|
219
|
+
return gen.code === LOSS.COLUMN_NAME_CHANGED && gen.column === edgeLabel && gen.message.startsWith("edge column");
|
|
220
|
+
}
|
|
221
|
+
function planPositions(snapshot, note) {
|
|
222
|
+
const position = snapshot.nodes.byRole("position");
|
|
223
|
+
const zColumn = [...snapshot.nodes].find(isZ) ?? null;
|
|
224
|
+
const z = zColumn !== null && zFitsLayout(zColumn, position) ? zColumn : null;
|
|
225
|
+
if (position === null) {
|
|
226
|
+
return { position, z, positionZ: false, nonfinite: 0 };
|
|
227
|
+
}
|
|
228
|
+
let nonfinite = nonFiniteCells(position, (v) => [v[0], v[1]]);
|
|
229
|
+
const depth = nonFiniteCells(position, (v) => v[2] === 0 ? [] : [Number.NaN]);
|
|
230
|
+
const positionZ = zColumn === null && (position.meta.extra.sourceDims === 3 || depth > 0);
|
|
231
|
+
if (positionZ) {
|
|
232
|
+
nonfinite += nonFiniteCells(position, (v) => Number.isFinite(v[0]) && Number.isFinite(v[1]) ? [v[2]] : []);
|
|
233
|
+
note(
|
|
234
|
+
LOSS.COMPONENTS,
|
|
235
|
+
`node column "${position.meta.name}" (position) has a third coordinate; CX writes it as the layout's z, which reads back in the z column (a stacking order), not in the position`,
|
|
236
|
+
position.meta.name,
|
|
237
|
+
depth
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return { position, z, positionZ, nonfinite };
|
|
241
|
+
}
|
|
242
|
+
function graphColumnNotes(snapshot, note) {
|
|
243
|
+
for (const c of snapshot.graph) {
|
|
244
|
+
const named = c.meta.name === "name" || c.meta.name === "description";
|
|
245
|
+
if (!isBypass(c) && named && (c.dtype === "string" || c.dtype === "dict")) {
|
|
246
|
+
note(
|
|
247
|
+
LOSS.ROLE_ASSUMED,
|
|
248
|
+
`graph column "${c.meta.name}" is written as the network attribute ${c.meta.name}, which reads back as the graph's ${c.meta.name}, not as a column`,
|
|
249
|
+
c.meta.name,
|
|
250
|
+
null
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function plan(snapshot, common) {
|
|
256
|
+
const { notes, note } = noteList();
|
|
257
|
+
const functionTerm = [...snapshot.nodes].find((c) => fromCx(c, "provenance") && c.meta.name === "functionTerm") ?? null;
|
|
258
|
+
const reifiedEdge = [...snapshot.nodes].find((c) => fromCx(c, "reifiedEdges") && c.dtype === "u32") ?? null;
|
|
259
|
+
const children = childrenCsr(snapshot);
|
|
260
|
+
const several = children.column?.meta.role === "parents" && severalParents(children.column);
|
|
261
|
+
let fatal = genericNotes(snapshot, common, several, [functionTerm, reifiedEdge], notes, note);
|
|
262
|
+
const folding = pairFolding(snapshot);
|
|
263
|
+
directionNotes(snapshot, folding, common, CX_LOSS.UNDIRECTED_AS_DIRECTED, note);
|
|
264
|
+
const planned = planNodeIds(snapshot, common, "CX", ORIGINAL_ID_ATTRIBUTE, note);
|
|
265
|
+
const ids = planned instanceof GraphFormatError ? null : planned;
|
|
266
|
+
if (planned instanceof GraphFormatError) {
|
|
267
|
+
fatal ??= planned;
|
|
268
|
+
}
|
|
269
|
+
const edgeIds = planEdgeIds(snapshot, note);
|
|
270
|
+
if (snapshot.nodes.has(ORIGINAL_ID_ATTRIBUTE)) {
|
|
271
|
+
note(
|
|
272
|
+
LOSS.ROLE_ASSUMED,
|
|
273
|
+
`node column "${ORIGINAL_ID_ATTRIBUTE}" is the importer's record of mangled ids: it reads back as the node ids unless restoreMangledIds is false`,
|
|
274
|
+
ORIGINAL_ID_ATTRIBUTE,
|
|
275
|
+
null
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
const positions = planPositions(snapshot, note);
|
|
279
|
+
const { position, z, positionZ } = positions;
|
|
280
|
+
let { nonfinite } = positions;
|
|
281
|
+
const collapsed = [...snapshot.nodes].find((c) => fromCx(c, "cyGroups") && c.dtype === "bool") ?? null;
|
|
282
|
+
const nodeCore = corePlans(snapshot, note);
|
|
283
|
+
const edgeCore = [];
|
|
284
|
+
const interaction = snapshot.edges.get("interaction");
|
|
285
|
+
if (interaction !== null && (interaction.meta.role === null || !SKIPPED_ROLES.has(interaction.meta.role))) {
|
|
286
|
+
edgeCore.push({ key: "i", column: interaction });
|
|
287
|
+
coreTextNote(interaction, "edge", note);
|
|
288
|
+
}
|
|
289
|
+
const coreColumns = new Set([...nodeCore, ...edgeCore].map((c) => c.column));
|
|
290
|
+
const special = (c) => coreColumns.has(c) || isBypass(c) || isLink(c) || c === z || c === collapsed || c === functionTerm || c === reifiedEdge || c.meta.role !== null && SKIPPED_ROLES.has(c.meta.role);
|
|
291
|
+
const label = snapshot.nodes.byRole("label");
|
|
292
|
+
const renamedName = label !== null && label.meta.name !== "name" ? snapshot.nodes.get("name") : null;
|
|
293
|
+
if (renamedName !== null) {
|
|
294
|
+
note(
|
|
295
|
+
LOSS.COLUMN_NAME_CHANGED,
|
|
296
|
+
`node column "name" is written as the attribute "name#2": the label column "${label?.meta.name ?? ""}" is written as n, which reads back as "name"`,
|
|
297
|
+
"name",
|
|
298
|
+
renamedName.length - renamedName.nullCount
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
const nodeAttrs = attributePlans(snapshot.nodes, special, (c) => c === renamedName ? "name#2" : c.meta.name);
|
|
302
|
+
const weight = planWeight(snapshot, folding, note);
|
|
303
|
+
const plainWeight = snapshot.edges.get(WEIGHT_ATTRIBUTE);
|
|
304
|
+
const edgeAttrs = attributePlans(
|
|
305
|
+
snapshot.edges,
|
|
306
|
+
(c) => special(c) || c === plainWeight && c.meta.role === null,
|
|
307
|
+
(c) => c.meta.name
|
|
308
|
+
);
|
|
309
|
+
nonfinite += weight.nonfinite;
|
|
310
|
+
graphColumnNotes(snapshot, note);
|
|
311
|
+
const graphAttrs = attributePlans(snapshot.graph, isBypass, (c) => c.meta.name);
|
|
312
|
+
if (children.column !== null && children.column.meta.role === "parents" && !several) {
|
|
313
|
+
note(
|
|
314
|
+
LOSS.COLUMN_NAME_CHANGED,
|
|
315
|
+
`node column "${children.column.meta.name}" (parents) holds at most one parent per node; it reads back as the single-parent column "parent"`,
|
|
316
|
+
children.column.meta.name,
|
|
317
|
+
children.column.length - children.column.nullCount
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (nonfinite > 0) {
|
|
321
|
+
note(
|
|
322
|
+
LOSS.NONFINITE_AS_NULL,
|
|
323
|
+
`${nonfinite} NaN or infinite position${plural(nonfinite)}, stacking orders or weights cannot be written in CX; they read back unset`,
|
|
324
|
+
null,
|
|
325
|
+
nonfinite
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
notes,
|
|
330
|
+
fatal,
|
|
331
|
+
ids,
|
|
332
|
+
folding,
|
|
333
|
+
edgeIds,
|
|
334
|
+
weight: weight.plan,
|
|
335
|
+
nodeCore,
|
|
336
|
+
edgeCore,
|
|
337
|
+
nodeAttrs,
|
|
338
|
+
edgeAttrs,
|
|
339
|
+
graphAttrs,
|
|
340
|
+
position,
|
|
341
|
+
positionZ,
|
|
342
|
+
z,
|
|
343
|
+
bypass: {
|
|
344
|
+
node: [...snapshot.nodes].filter(isBypass),
|
|
345
|
+
edge: [...snapshot.edges].filter(isBypass),
|
|
346
|
+
graph: [...snapshot.graph].filter(isBypass)
|
|
347
|
+
},
|
|
348
|
+
collapsed,
|
|
349
|
+
links: linkPlans(snapshot),
|
|
350
|
+
functionTerm,
|
|
351
|
+
reifiedEdge
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function corePlans(snapshot, note) {
|
|
355
|
+
const out = [];
|
|
356
|
+
const label = snapshot.nodes.byRole("label");
|
|
357
|
+
const plainName = snapshot.nodes.get("name");
|
|
358
|
+
const n = label ?? (plainName !== null && plainName.meta.role === null ? plainName : null);
|
|
359
|
+
if (n !== null) {
|
|
360
|
+
out.push({ key: "n", column: n });
|
|
361
|
+
coreTextNote(n, "node", note);
|
|
362
|
+
if (n !== label) {
|
|
363
|
+
note(
|
|
364
|
+
LOSS.ROLE_ASSUMED,
|
|
365
|
+
`node column "name" is written as n and reads back as the label`,
|
|
366
|
+
"name",
|
|
367
|
+
n.length - n.nullCount
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const represents = snapshot.nodes.get("represents");
|
|
372
|
+
if (represents !== null && represents !== n && (represents.meta.role === null || !SKIPPED_ROLES.has(represents.meta.role))) {
|
|
373
|
+
out.push({ key: "r", column: represents });
|
|
374
|
+
coreTextNote(represents, "node", note);
|
|
375
|
+
}
|
|
376
|
+
return out;
|
|
377
|
+
}
|
|
378
|
+
function coreTextNote(column, domain, note) {
|
|
379
|
+
if (coreNeedsText(column)) {
|
|
380
|
+
note(
|
|
381
|
+
LOSS.DTYPE,
|
|
382
|
+
`${domain} column "${column.meta.name}" is ${column.dtype}; CX writes it as text and it reads back as string`,
|
|
383
|
+
column.meta.name,
|
|
384
|
+
column.length - column.nullCount
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function attributePlans(table, skip, rename) {
|
|
389
|
+
return [...table].filter((c) => !skip(c)).map((column) => ({ column, name: rename(column), d: declaredType(column) }));
|
|
390
|
+
}
|
|
391
|
+
function planWeight(snapshot, folding, note) {
|
|
392
|
+
const weights = explicitWeights(snapshot);
|
|
393
|
+
const plain = snapshot.edges.get(WEIGHT_ATTRIBUTE);
|
|
394
|
+
const clash = plain !== null && plain.meta.role === null;
|
|
395
|
+
let plan2 = null;
|
|
396
|
+
if (weights.weighted) {
|
|
397
|
+
plan2 = { value: (e) => weights.isExplicit(e) ? weights.value(e) : void 0 };
|
|
398
|
+
if (clash) {
|
|
399
|
+
note(
|
|
400
|
+
LOSS.WEIGHT_KEY_CLASH,
|
|
401
|
+
`edge column "${WEIGHT_ATTRIBUTE}" is not written: the explicit weights are written under that name`,
|
|
402
|
+
WEIGHT_ATTRIBUTE,
|
|
403
|
+
null
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
} else if (clash) {
|
|
407
|
+
const numeric = NUMERIC_DTYPES.has(plain.dtype);
|
|
408
|
+
note(
|
|
409
|
+
LOSS.WEIGHT_KEY_CLASH,
|
|
410
|
+
numeric ? `edge column "${WEIGHT_ATTRIBUTE}" is written as the weight attribute and reads back as the edge weight` : `edge column "${WEIGHT_ATTRIBUTE}" is ${plain.dtype}, and CX reads that attribute as the edge weight; it is not written`,
|
|
411
|
+
WEIGHT_ATTRIBUTE,
|
|
412
|
+
null
|
|
413
|
+
);
|
|
414
|
+
if (numeric) {
|
|
415
|
+
plan2 = { value: (e) => plain.isSet(e) ? plain.value(e) : void 0 };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return { plan: plan2, nonfinite: plan2 === null ? 0 : nanWeights(snapshot.edgeCount, plan2, folding) };
|
|
419
|
+
}
|
|
420
|
+
function nanWeights(edgeCount, plan2, folding) {
|
|
421
|
+
let nan = 0;
|
|
422
|
+
for (let e = 0; e < edgeCount; e++) {
|
|
423
|
+
const w = plan2.value(e);
|
|
424
|
+
if (!folding.folded(e) && w !== void 0 && Number.isNaN(w)) {
|
|
425
|
+
nan++;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return nan;
|
|
429
|
+
}
|
|
430
|
+
function zFitsLayout(z, position) {
|
|
431
|
+
for (let i = 0; i < z.length; i++) {
|
|
432
|
+
if (!z.isSet(i)) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
const value = z.value(i);
|
|
436
|
+
const point = position?.isSet(i) === true ? position.value(i) : null;
|
|
437
|
+
if (typeof value !== "number" || !Number.isFinite(value) || point === null || !Number.isFinite(point[0]) || !Number.isFinite(point[1])) {
|
|
438
|
+
return false;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return true;
|
|
442
|
+
}
|
|
443
|
+
function nonFiniteCells(column, pick) {
|
|
444
|
+
let count = 0;
|
|
445
|
+
for (let i = 0; i < column.length; i++) {
|
|
446
|
+
if (column.isSet(i) && pick(column.value(i)).some((v) => !Number.isFinite(v))) {
|
|
447
|
+
count++;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return count;
|
|
451
|
+
}
|
|
452
|
+
function severalParents(column) {
|
|
453
|
+
for (let i = 0; i < column.length; i++) {
|
|
454
|
+
if (column.isSet(i) && column.value(i).length > 1) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
function linkPlans(snapshot) {
|
|
461
|
+
const out = [];
|
|
462
|
+
for (const [domain, table] of [
|
|
463
|
+
["node", snapshot.nodes],
|
|
464
|
+
["edge", snapshot.edges]
|
|
465
|
+
]) {
|
|
466
|
+
for (const column of table) {
|
|
467
|
+
if (isLink(column)) {
|
|
468
|
+
const key = column.meta.name;
|
|
469
|
+
out.push({ aspect: `${domain}${key === "citations" ? "Citations" : "Supports"}`, key, domain, column });
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return out;
|
|
474
|
+
}
|
|
475
|
+
function scalarText(value) {
|
|
476
|
+
if (typeof value === "number") {
|
|
477
|
+
return Object.is(value, -0) ? "-0" : String(value);
|
|
478
|
+
}
|
|
479
|
+
if (typeof value === "string") {
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
return typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
483
|
+
}
|
|
484
|
+
function cellValue(column, d, row) {
|
|
485
|
+
if (!column.isSet(row)) {
|
|
486
|
+
return void 0;
|
|
487
|
+
}
|
|
488
|
+
const value = column.value(row);
|
|
489
|
+
if (column.meta.dtype === "json") {
|
|
490
|
+
return JSON.stringify(value);
|
|
491
|
+
}
|
|
492
|
+
if (column.meta.dtype === "list" || column.meta.components > 1) {
|
|
493
|
+
const items = Array.from(value);
|
|
494
|
+
return d === "string" ? JSON.stringify(items) : items.map(scalarText);
|
|
495
|
+
}
|
|
496
|
+
return scalarText(value);
|
|
497
|
+
}
|
|
498
|
+
function coreText(column, row) {
|
|
499
|
+
if (!column.isSet(row)) {
|
|
500
|
+
return void 0;
|
|
501
|
+
}
|
|
502
|
+
const value = column.value(row);
|
|
503
|
+
if (column.meta.dtype === "json" || column.meta.dtype === "list" || column.meta.components > 1) {
|
|
504
|
+
return JSON.stringify(column.meta.dtype === "json" ? value : Array.from(value));
|
|
505
|
+
}
|
|
506
|
+
return scalarText(value);
|
|
507
|
+
}
|
|
508
|
+
function writtenEdges(snapshot, folding) {
|
|
509
|
+
const out = [];
|
|
510
|
+
for (let e = 0; e < snapshot.edgeCount; e++) {
|
|
511
|
+
if (!folding.folded(e)) {
|
|
512
|
+
out.push(e);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
517
|
+
function aspects(snapshot, p, ids) {
|
|
518
|
+
const { src, dst } = snapshot.edgeList();
|
|
519
|
+
const edges = writtenEdges(snapshot, p.folding);
|
|
520
|
+
const nodeRange = () => Array.from({ length: snapshot.nodeCount }, (_, i2) => i2);
|
|
521
|
+
const nodeCore = (key) => p.nodeCore.find((c) => c.key === key)?.column ?? null;
|
|
522
|
+
const n = nodeCore("n");
|
|
523
|
+
const r = nodeCore("r");
|
|
524
|
+
const i = p.edgeCore[0]?.column ?? null;
|
|
525
|
+
const kept = snapshot.meta.sourceFormat === CX_FORMAT$1 && isRecord(snapshot.meta.extra.cx) ? snapshot.meta.extra.cx : {};
|
|
526
|
+
const out = [
|
|
527
|
+
{
|
|
528
|
+
name: "nodes",
|
|
529
|
+
idCounter: nextNodeId(ids, snapshot.nodeCount),
|
|
530
|
+
*elements() {
|
|
531
|
+
for (let row = 0; row < snapshot.nodeCount; row++) {
|
|
532
|
+
const element = { "@id": ids.idAt(row) };
|
|
533
|
+
const name = n === null ? void 0 : coreText(n, row);
|
|
534
|
+
const represents = r === null ? void 0 : coreText(r, row);
|
|
535
|
+
if (name !== void 0) {
|
|
536
|
+
element.n = name;
|
|
537
|
+
}
|
|
538
|
+
if (represents !== void 0) {
|
|
539
|
+
element.r = represents;
|
|
540
|
+
}
|
|
541
|
+
yield element;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
{
|
|
546
|
+
name: "edges",
|
|
547
|
+
idCounter: edges.reduce((max, e) => Math.max(max, p.edgeIds[e] + 1), 0),
|
|
548
|
+
*elements() {
|
|
549
|
+
for (const e of edges) {
|
|
550
|
+
const element = {
|
|
551
|
+
"@id": p.edgeIds[e],
|
|
552
|
+
s: ids.idAt(src[e]),
|
|
553
|
+
t: ids.idAt(dst[e])
|
|
554
|
+
};
|
|
555
|
+
const interaction = i === null ? void 0 : coreText(i, e);
|
|
556
|
+
if (interaction !== void 0) {
|
|
557
|
+
element.i = interaction;
|
|
558
|
+
}
|
|
559
|
+
yield element;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
{
|
|
564
|
+
name: "nodeAttributes",
|
|
565
|
+
*elements() {
|
|
566
|
+
for (const a of p.nodeAttrs) {
|
|
567
|
+
for (let row = 0; row < snapshot.nodeCount; row++) {
|
|
568
|
+
const v = cellValue(a.column, a.d, row);
|
|
569
|
+
if (v !== void 0) {
|
|
570
|
+
yield { po: ids.idAt(row), n: a.name, v, d: a.d };
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
for (let row = 0; row < snapshot.nodeCount; row++) {
|
|
575
|
+
if (ids.isChanged(row)) {
|
|
576
|
+
yield {
|
|
577
|
+
po: ids.idAt(row),
|
|
578
|
+
n: ORIGINAL_ID_ATTRIBUTE,
|
|
579
|
+
v: String(ids.originalAt(row)),
|
|
580
|
+
d: "string"
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
name: "edgeAttributes",
|
|
588
|
+
*elements() {
|
|
589
|
+
for (const a of p.edgeAttrs) {
|
|
590
|
+
for (const e of edges) {
|
|
591
|
+
const v = cellValue(a.column, a.d, e);
|
|
592
|
+
if (v !== void 0) {
|
|
593
|
+
yield { po: p.edgeIds[e], n: a.name, v, d: a.d };
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (p.weight !== null) {
|
|
598
|
+
for (const e of edges) {
|
|
599
|
+
const w = p.weight.value(e);
|
|
600
|
+
if (w !== void 0 && !Number.isNaN(w)) {
|
|
601
|
+
yield { po: p.edgeIds[e], n: WEIGHT_ATTRIBUTE, v: scalarText(w), d: "double" };
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
name: "networkAttributes",
|
|
609
|
+
*elements() {
|
|
610
|
+
for (const a of p.graphAttrs) {
|
|
611
|
+
const v = cellValue(a.column, a.d, 0);
|
|
612
|
+
if (v !== void 0) {
|
|
613
|
+
yield { n: a.name, v, d: a.d };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const { meta } = snapshot;
|
|
617
|
+
for (const [key, value] of [
|
|
618
|
+
["name", meta.name],
|
|
619
|
+
["description", meta.description]
|
|
620
|
+
]) {
|
|
621
|
+
if (value !== null && !p.graphAttrs.some((a) => a.name === key)) {
|
|
622
|
+
yield { n: key, v: value, d: "string" };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
},
|
|
627
|
+
{ name: "cyTableColumn", elements: () => tableColumns(p, ids) },
|
|
628
|
+
{ name: "cartesianLayout", elements: () => layoutElements(p, ids, nodeRange()) },
|
|
629
|
+
{ name: "cyGroups", elements: () => groupElements(snapshot, p, ids, edges) },
|
|
630
|
+
{ name: "cyVisualProperties", elements: () => visualProperties(p, ids, nodeRange(), edges, kept) }
|
|
631
|
+
];
|
|
632
|
+
out.push(...provenanceAspects(snapshot, p, ids, edges));
|
|
633
|
+
for (const [name, value] of Object.entries(kept)) {
|
|
634
|
+
if (!NEVER_WRITTEN.has(name) && Array.isArray(value)) {
|
|
635
|
+
out.push({ name, elements: () => keptElements(value, kept.subnetwork, STYLE_ASPECTS.has(name)) });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return out;
|
|
639
|
+
}
|
|
640
|
+
function* tableColumns(p, ids) {
|
|
641
|
+
for (const { key, column } of [...p.nodeCore, ...p.edgeCore]) {
|
|
642
|
+
const n = { n: "name", r: "represents", i: "interaction" }[key];
|
|
643
|
+
if (n !== void 0 && column !== null) {
|
|
644
|
+
yield { applies_to: key === "i" ? "edge_table" : "node_table", n, d: "string" };
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const a of p.nodeAttrs) {
|
|
648
|
+
yield { applies_to: "node_table", n: a.name, d: a.d };
|
|
649
|
+
}
|
|
650
|
+
if (ids.changed > 0) {
|
|
651
|
+
yield { applies_to: "node_table", n: ORIGINAL_ID_ATTRIBUTE, d: "string" };
|
|
652
|
+
}
|
|
653
|
+
for (const a of p.edgeAttrs) {
|
|
654
|
+
yield { applies_to: "edge_table", n: a.name, d: a.d };
|
|
655
|
+
}
|
|
656
|
+
if (p.weight !== null) {
|
|
657
|
+
yield { applies_to: "edge_table", n: WEIGHT_ATTRIBUTE, d: "double" };
|
|
658
|
+
}
|
|
659
|
+
for (const a of p.graphAttrs) {
|
|
660
|
+
yield { applies_to: "network_table", n: a.name, d: a.d };
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const STYLE_ASPECTS = /* @__PURE__ */ new Set(["tableVisualProperties", "cyTableVisualProperties"]);
|
|
664
|
+
function* keptElements(elements, subnetwork, style) {
|
|
665
|
+
for (const element of elements) {
|
|
666
|
+
if (!isRecord(element)) {
|
|
667
|
+
yield element;
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
if (element.s !== void 0 && element.s !== null && element.s !== subnetwork) {
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
const { s: _scope, ...rest } = element;
|
|
674
|
+
yield style ? rewriteView(rest) : rest;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function rewriteView(element) {
|
|
678
|
+
const out = { ...element };
|
|
679
|
+
if ("view" in out) {
|
|
680
|
+
out.view = 0;
|
|
681
|
+
}
|
|
682
|
+
if (typeof out.applies_to === "number") {
|
|
683
|
+
out.applies_to = 0;
|
|
684
|
+
}
|
|
685
|
+
return out;
|
|
686
|
+
}
|
|
687
|
+
function* layoutElements(p, ids, rows) {
|
|
688
|
+
const { position } = p;
|
|
689
|
+
if (position === null) {
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
for (const row of rows) {
|
|
693
|
+
if (!position.isSet(row)) {
|
|
694
|
+
continue;
|
|
695
|
+
}
|
|
696
|
+
const point = position.value(row);
|
|
697
|
+
if (!Number.isFinite(point[0]) || !Number.isFinite(point[1])) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
const entry = { node: ids.idAt(row), x: point[0], y: point[1] === 0 ? 0 : -point[1] };
|
|
701
|
+
if (p.z?.isSet(row) === true) {
|
|
702
|
+
entry.z = p.z.value(row);
|
|
703
|
+
} else if (p.positionZ && Number.isFinite(point[2])) {
|
|
704
|
+
entry.z = point[2];
|
|
705
|
+
}
|
|
706
|
+
yield entry;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function* groupElements(snapshot, p, ids, edges) {
|
|
710
|
+
const children = childrenCsr(snapshot);
|
|
711
|
+
const groupsOf = /* @__PURE__ */ new Map();
|
|
712
|
+
const groups = [];
|
|
713
|
+
for (let g = 0; g < snapshot.nodeCount; g++) {
|
|
714
|
+
const members = children.childrenOf(g);
|
|
715
|
+
if (members.length > 0 || p.collapsed?.isSet(g) === true) {
|
|
716
|
+
groups.push(g);
|
|
717
|
+
}
|
|
718
|
+
for (const c of members) {
|
|
719
|
+
groupsOf.set(c, [...groupsOf.get(c) ?? [], g]);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (groups.length === 0) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
const { internal, external } = groupEdges(snapshot, p, groupsOf, edges);
|
|
726
|
+
const label = p.nodeCore.find((c) => c.key === "n")?.column ?? null;
|
|
727
|
+
for (const g of groups) {
|
|
728
|
+
const entry = { "@id": ids.idAt(g) };
|
|
729
|
+
const name = label === null ? void 0 : coreText(label, g);
|
|
730
|
+
if (name !== void 0) {
|
|
731
|
+
entry.n = name;
|
|
732
|
+
}
|
|
733
|
+
entry.nodes = Array.from(children.childrenOf(g), (c) => ids.idAt(c));
|
|
734
|
+
entry.internal_edges = internal.get(g) ?? [];
|
|
735
|
+
entry.external_edges = external.get(g) ?? [];
|
|
736
|
+
if (p.collapsed?.isSet(g) === true) {
|
|
737
|
+
entry.collapsed = p.collapsed.value(g);
|
|
738
|
+
}
|
|
739
|
+
yield entry;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function groupEdges(snapshot, p, groupsOf, edges) {
|
|
743
|
+
const internal = /* @__PURE__ */ new Map();
|
|
744
|
+
const external = /* @__PURE__ */ new Map();
|
|
745
|
+
const add = (table, g, id) => {
|
|
746
|
+
table.set(g, [...table.get(g) ?? [], id]);
|
|
747
|
+
};
|
|
748
|
+
const { src, dst } = snapshot.edgeList();
|
|
749
|
+
for (const e of edges) {
|
|
750
|
+
const ofSource = groupsOf.get(src[e]) ?? [];
|
|
751
|
+
const ofTarget = groupsOf.get(dst[e]) ?? [];
|
|
752
|
+
for (const g of ofSource) {
|
|
753
|
+
add(ofTarget.includes(g) ? internal : external, g, p.edgeIds[e]);
|
|
754
|
+
}
|
|
755
|
+
for (const g of ofTarget.filter((t) => !ofSource.includes(t))) {
|
|
756
|
+
add(external, g, p.edgeIds[e]);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return { internal, external };
|
|
760
|
+
}
|
|
761
|
+
function bypassValues(columns, row) {
|
|
762
|
+
let out = null;
|
|
763
|
+
for (const column of columns) {
|
|
764
|
+
if (column.isSet(row)) {
|
|
765
|
+
const property = column.meta.origin?.id;
|
|
766
|
+
out ??= {};
|
|
767
|
+
out[typeof property === "string" && property.length > 0 ? property : column.meta.name] = scalarText(
|
|
768
|
+
column.value(row)
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return out;
|
|
773
|
+
}
|
|
774
|
+
function* visualProperties(p, ids, rows, edges, kept) {
|
|
775
|
+
const network = bypassValues(p.bypass.graph, 0);
|
|
776
|
+
if (network !== null) {
|
|
777
|
+
yield { properties_of: "network", properties: network };
|
|
778
|
+
}
|
|
779
|
+
const view = ownView(kept);
|
|
780
|
+
for (const entry of Array.isArray(kept.cyVisualProperties) ? kept.cyVisualProperties : []) {
|
|
781
|
+
if (isRecord(entry) && !["nodes", "edges", "network"].includes(String(entry.properties_of)) && (view === null || entry.view === void 0 || entry.view === null || entry.view === view)) {
|
|
782
|
+
yield rewriteView(entry);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
for (const row of rows) {
|
|
786
|
+
const properties = bypassValues(p.bypass.node, row);
|
|
787
|
+
if (properties !== null) {
|
|
788
|
+
yield { properties_of: "nodes", applies_to: ids.idAt(row), properties };
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
for (const e of edges) {
|
|
792
|
+
const properties = bypassValues(p.bypass.edge, e);
|
|
793
|
+
if (properties !== null) {
|
|
794
|
+
yield { properties_of: "edges", applies_to: p.edgeIds[e], properties };
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
function ownView(kept) {
|
|
799
|
+
const sub = kept.subnetwork;
|
|
800
|
+
for (const r of Array.isArray(kept.cyNetworkRelations) ? kept.cyNetworkRelations : []) {
|
|
801
|
+
if (isRecord(r) && r.r === "view" && (sub === void 0 || r.p === sub)) {
|
|
802
|
+
return r.c;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
for (const v of Array.isArray(kept.cyViews) ? kept.cyViews : []) {
|
|
806
|
+
if (isRecord(v) && (sub === void 0 || v.s === sub)) {
|
|
807
|
+
return v["@id"];
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
function provenanceAspects(snapshot, p, ids, edges) {
|
|
813
|
+
const out = [];
|
|
814
|
+
for (const [table, aspect2] of Object.entries(PROVENANCE_TABLES)) {
|
|
815
|
+
const ext = snapshot.extensions.get(table);
|
|
816
|
+
if (ext === void 0) {
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
const idColumn = ext.get("id");
|
|
820
|
+
const fields = [...ext].filter((c) => c !== idColumn);
|
|
821
|
+
let max = -1;
|
|
822
|
+
for (let row = 0; row < ext.rowCount; row++) {
|
|
823
|
+
if (idColumn?.isSet(row) === true) {
|
|
824
|
+
max = Math.max(max, Number(idColumn.value(row)));
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
out.push({
|
|
828
|
+
name: aspect2,
|
|
829
|
+
idCounter: max + 1,
|
|
830
|
+
*elements() {
|
|
831
|
+
for (let row = 0; row < ext.rowCount; row++) {
|
|
832
|
+
const element = {};
|
|
833
|
+
if (idColumn?.isSet(row) === true) {
|
|
834
|
+
element["@id"] = idColumn.value(row);
|
|
835
|
+
}
|
|
836
|
+
for (const field of fields) {
|
|
837
|
+
if (field.isSet(row)) {
|
|
838
|
+
element[field.meta.name] = field.value(row);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
yield element;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
for (const link of p.links) {
|
|
847
|
+
out.push({
|
|
848
|
+
name: link.aspect,
|
|
849
|
+
*elements() {
|
|
850
|
+
const rows = link.domain === "node" ? Array.from({ length: snapshot.nodeCount }, (_, i) => i) : edges;
|
|
851
|
+
for (const row of rows) {
|
|
852
|
+
if (link.column.isSet(row)) {
|
|
853
|
+
const po = link.domain === "node" ? ids.idAt(row) : p.edgeIds[row];
|
|
854
|
+
yield { po: [po], [link.key]: Array.from(link.column.value(row)) };
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
const terms = p.functionTerm;
|
|
861
|
+
if (terms !== null) {
|
|
862
|
+
out.push({
|
|
863
|
+
name: "functionTerms",
|
|
864
|
+
*elements() {
|
|
865
|
+
for (let row = 0; row < snapshot.nodeCount; row++) {
|
|
866
|
+
const term = terms.isSet(row) ? terms.value(row) : null;
|
|
867
|
+
if (isRecord(term)) {
|
|
868
|
+
yield { po: ids.idAt(row), ...term };
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
const reified = p.reifiedEdge;
|
|
875
|
+
if (reified !== null) {
|
|
876
|
+
out.push({
|
|
877
|
+
name: "reifiedEdges",
|
|
878
|
+
*elements() {
|
|
879
|
+
for (let row = 0; row < snapshot.nodeCount; row++) {
|
|
880
|
+
const edge = reified.isSet(row) ? reified.value(row) : -1;
|
|
881
|
+
if (edge >= 0 && edge < snapshot.edgeCount) {
|
|
882
|
+
yield { node: ids.idAt(row), edge: p.edgeIds[edge] };
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
return out;
|
|
889
|
+
}
|
|
890
|
+
function* write(snapshot, p) {
|
|
891
|
+
if (p.fatal !== null) {
|
|
892
|
+
throw p.fatal;
|
|
893
|
+
}
|
|
894
|
+
const { ids } = p;
|
|
895
|
+
if (ids === null) {
|
|
896
|
+
throw new GraphFormatError("E_INVALID_ID", "node ids cannot be written as CX integers", { reason: "charset" });
|
|
897
|
+
}
|
|
898
|
+
const all = aspects(snapshot, p, ids);
|
|
899
|
+
const written = [];
|
|
900
|
+
for (const aspect2 of all) {
|
|
901
|
+
let count = 0;
|
|
902
|
+
for (const _ of aspect2.elements()) {
|
|
903
|
+
count++;
|
|
904
|
+
}
|
|
905
|
+
if (count > 0 || aspect2.name === "nodes") {
|
|
906
|
+
written.push({ aspect: aspect2, count });
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
const metaData = written.map(({ aspect: aspect2, count }) => ({
|
|
910
|
+
name: aspect2.name,
|
|
911
|
+
version: "1.0",
|
|
912
|
+
...aspect2.idCounter === void 0 ? {} : { idCounter: aspect2.idCounter },
|
|
913
|
+
elementCount: count,
|
|
914
|
+
consistencyGroup: 1
|
|
915
|
+
}));
|
|
916
|
+
yield '[{"numberVerification":[{"longNumber":281474976710655}]},\n';
|
|
917
|
+
yield `{"metaData":${stringify(metaData)}},
|
|
918
|
+
`;
|
|
919
|
+
for (const { aspect: aspect2 } of written) {
|
|
920
|
+
yield* aspectBlock(aspect2.name, mapIterable(aspect2.elements(), stringify));
|
|
921
|
+
}
|
|
922
|
+
yield '{"status":[{"error":"","success":true}]}]\n';
|
|
923
|
+
}
|
|
924
|
+
function* mapIterable(items, fn) {
|
|
925
|
+
for (const item of items) {
|
|
926
|
+
yield fn(item);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
const cxExporter = Object.freeze({
|
|
930
|
+
format: CX_FORMAT$1,
|
|
931
|
+
options: Object.freeze([]),
|
|
932
|
+
capabilities: CX_CAPABILITIES,
|
|
933
|
+
/**
|
|
934
|
+
* Pre-flight: what export() would lose, without writing anything.
|
|
935
|
+
* @param snapshot - the snapshot
|
|
936
|
+
* @param options - the common options
|
|
937
|
+
* @returns the notes, empty when the export is exact
|
|
938
|
+
*/
|
|
939
|
+
check(snapshot, options) {
|
|
940
|
+
return Object.freeze([...plan(snapshot, resolveExportOptions(options)).notes]);
|
|
941
|
+
},
|
|
942
|
+
/**
|
|
943
|
+
* Write the document as UTF-8 chunks.
|
|
944
|
+
* @param snapshot - the snapshot
|
|
945
|
+
* @param options - the common options
|
|
946
|
+
* @returns the chunks
|
|
947
|
+
*/
|
|
948
|
+
export(snapshot, options) {
|
|
949
|
+
return encodeChunks(write(snapshot, plan(snapshot, resolveExportOptions(options))));
|
|
950
|
+
},
|
|
951
|
+
/**
|
|
952
|
+
* Write the document as one string.
|
|
953
|
+
* @param snapshot - the snapshot
|
|
954
|
+
* @param options - the common options
|
|
955
|
+
* @returns the document
|
|
956
|
+
*/
|
|
957
|
+
exportToString(snapshot, options) {
|
|
958
|
+
return joinText(write(snapshot, plan(snapshot, resolveExportOptions(options))));
|
|
959
|
+
}
|
|
960
|
+
});
|
|
6
961
|
const CX_FORMAT = "cx";
|
|
7
962
|
const CX_BYPASS_NAMESPACE = "cx.bypass";
|
|
8
963
|
const CX_ISSUE = Object.freeze({
|
|
9
|
-
|
|
964
|
+
...INPUT_ISSUE,
|
|
965
|
+
...ELEMENT_ISSUE,
|
|
966
|
+
/** The input is empty. The import stops. */
|
|
10
967
|
EMPTY_INPUT: EMPTY_INPUT_CODE,
|
|
11
|
-
/** The text is not JSON
|
|
968
|
+
/** The text is not valid JSON. The import stops. */
|
|
12
969
|
SYNTAX: SYNTAX_CODE,
|
|
13
|
-
/**
|
|
970
|
+
/** The input is not valid UTF-8. The import stops. */
|
|
14
971
|
INVALID_UTF8: INVALID_UTF8_CODE,
|
|
15
|
-
/**
|
|
972
|
+
/**
|
|
973
|
+
* Some bytes are not valid in the encoding that was chosen (by a byte order mark or the `encoding` option). The
|
|
974
|
+
* import stops.
|
|
975
|
+
*/
|
|
16
976
|
INVALID_ENCODING: INVALID_ENCODING_CODE,
|
|
17
977
|
/** Bytes that are not UTF-8 were read as windows-1252. */
|
|
18
978
|
ENCODING_FALLBACK: ENCODING_FALLBACK_CODE,
|
|
19
979
|
/** An encoding the platform cannot decode was ignored. */
|
|
20
980
|
UNKNOWN_ENCODING: UNKNOWN_ENCODING_CODE,
|
|
21
|
-
/** The document is not a CX array, or it is CX2 (
|
|
981
|
+
/** The document is not a CX array, or it is CX2 (the message says so; read it as `cx2`). The import stops. */
|
|
22
982
|
NOT_CX: "E_CX_NOT_CX",
|
|
23
|
-
/**
|
|
983
|
+
/** NumberVerification holds another value than 2^48 - 1, or comes twice. */
|
|
24
984
|
NUMBER_VERIFICATION: "W_CX_NUMBER_VERIFICATION",
|
|
25
985
|
/** An old Cytoscape aspect name (visualProperties, subNetworks, ...) read under its cy name. */
|
|
26
986
|
OLD_ASPECT_NAME: "W_CX_OLD_ASPECT_NAME",
|
|
@@ -28,11 +988,14 @@ const CX_ISSUE = Object.freeze({
|
|
|
28
988
|
GROUP_NODE_ADDED: "W_CX_GROUP_NODE_ADDED",
|
|
29
989
|
/** Nodes or edges of the root network that no subnetwork holds: Cytoscape shows them in no network; not read. */
|
|
30
990
|
ROOT_ONLY: "W_CX_ROOT_ONLY",
|
|
31
|
-
/** The
|
|
991
|
+
/** The program that wrote the file marked it as failed, so it is incomplete. The import stops. */
|
|
32
992
|
STATUS_FAILED: STATUS_FAILED_CODE,
|
|
33
|
-
/** The
|
|
993
|
+
/** The program that wrote the file marked it as successful but added an error message; the message is shown. */
|
|
34
994
|
STATUS_WARNING: STATUS_WARNING_CODE,
|
|
35
|
-
/**
|
|
995
|
+
/**
|
|
996
|
+
* A member of the file's top-level array that is not a block with one key (`{"nodes": [...]}`), or an element of a
|
|
997
|
+
* block that is not an object. It is skipped; the rest of the file is read.
|
|
998
|
+
*/
|
|
36
999
|
BAD_ASPECT_BLOCK: BAD_ASPECT_BLOCK_CODE,
|
|
37
1000
|
/** An aspect after the post-metadata or after the status, a third metaData. */
|
|
38
1001
|
ASPECT_ORDER: ASPECT_ORDER_CODE,
|
|
@@ -40,7 +1003,10 @@ const CX_ISSUE = Object.freeze({
|
|
|
40
1003
|
COUNT_MISMATCH: COUNT_MISMATCH_CODE,
|
|
41
1004
|
/** A value that does not parse as its data type; the cell is unset. */
|
|
42
1005
|
BAD_VALUE: BAD_VALUE_CODE,
|
|
43
|
-
/**
|
|
1006
|
+
/**
|
|
1007
|
+
* An attribute's type was widened because a later value did not fit: an integer above 2^31 in an integer column,
|
|
1008
|
+
* or two declared types for one attribute.
|
|
1009
|
+
*/
|
|
44
1010
|
WIDENED: WIDENED_CODE,
|
|
45
1011
|
/** A data type CX does not define; the value is kept as text. */
|
|
46
1012
|
UNKNOWN_ATTR_TYPE: UNKNOWN_ATTR_TYPE_CODE,
|
|
@@ -52,9 +1018,9 @@ const CX_ISSUE = Object.freeze({
|
|
|
52
1018
|
UNKNOWN_PARENT: UNKNOWN_PARENT_CODE,
|
|
53
1019
|
/** A group membership that would close a parent cycle; dropped. */
|
|
54
1020
|
PARENT_CYCLE: PARENT_CYCLE_CODE,
|
|
55
|
-
/** The style rules of cyVisualProperties are not applied
|
|
1021
|
+
/** The style rules of cyVisualProperties are not applied; they are kept so a CX export writes them back. */
|
|
56
1022
|
STYLES_NOT_IMPORTED: STYLES_NOT_IMPORTED_CODE,
|
|
57
|
-
/** A node without an
|
|
1023
|
+
/** A node without an id (its `@id` key). The node is skipped. */
|
|
58
1024
|
MISSING_ID: MISSING_ID_CODE,
|
|
59
1025
|
/** An edge without s or t. */
|
|
60
1026
|
MISSING_ENDPOINT: MISSING_ENDPOINT_CODE,
|
|
@@ -64,37 +1030,63 @@ const CX_ISSUE = Object.freeze({
|
|
|
64
1030
|
DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,
|
|
65
1031
|
/** An id that is not an integer. */
|
|
66
1032
|
INVALID_ID: "E_INVALID_ID",
|
|
1033
|
+
/**
|
|
1034
|
+
* A node or edge key CX does not define, an aspect graph-io keeps for writing back, or a table CX does not define;
|
|
1035
|
+
* it is skipped.
|
|
1036
|
+
*/
|
|
1037
|
+
UNKNOWN_ELEMENT: UNKNOWN_ELEMENT_CODE,
|
|
1038
|
+
/** A member holding several aspects; each array-valued key is read as its own fragment. */
|
|
1039
|
+
MULTI_ASPECT_FRAGMENT: MULTI_ASPECT_FRAGMENT_CODE,
|
|
1040
|
+
/** An aspect written as one object, not an array of elements; read as one element. */
|
|
1041
|
+
SINGLE_OBJECT_ASPECT: SINGLE_OBJECT_ASPECT_CODE,
|
|
1042
|
+
/** The bare tokens NaN / Infinity / -Infinity (Python's json writes them), read as numbers. */
|
|
1043
|
+
JSON_NONSTANDARD_NUMBER: JSON_NONSTANDARD_NUMBER_CODE,
|
|
67
1044
|
/** An edge endpoint naming no node of the graph (addMissingNodes false, the default). */
|
|
68
1045
|
UNKNOWN_NODE: "E_UNKNOWN_NODE",
|
|
69
1046
|
/** A weight that is not a number. */
|
|
70
1047
|
INVALID_WEIGHT: "E_INVALID_WEIGHT",
|
|
71
1048
|
/** An id spelled as a string or a non-integer literal; read as the integer. */
|
|
72
1049
|
ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,
|
|
73
|
-
/** An integer beyond 2^53
|
|
1050
|
+
/** An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: "string"` to keep every digit. */
|
|
74
1051
|
PRECISION: PRECISION_CODE,
|
|
75
1052
|
/** Two id texts merged under `ids: "number"`. */
|
|
76
1053
|
ID_MERGED: ID_MERGED_CODE,
|
|
77
|
-
/**
|
|
1054
|
+
/**
|
|
1055
|
+
* The file holds several graphs and only the first was read. It is not added when `graphIndex` or `graphName` chose the graph.
|
|
1056
|
+
* `importAllGraphs()` reads every one.
|
|
1057
|
+
*/
|
|
78
1058
|
MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
|
|
79
|
-
/** graphIndex or graphName
|
|
1059
|
+
/** `graphIndex` or `graphName` matches no subnetwork. The import stops. */
|
|
80
1060
|
GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
|
|
81
|
-
/** graphName
|
|
1061
|
+
/** `graphName` matches several subnetworks. The import stops. */
|
|
82
1062
|
AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
|
|
83
|
-
/** The input holds no graph
|
|
1063
|
+
/** The input holds no graph. The import stops. */
|
|
84
1064
|
NO_GRAPH: NO_GRAPH_CODE,
|
|
85
|
-
/**
|
|
1065
|
+
/**
|
|
1066
|
+
* An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example
|
|
1067
|
+
* two attributes declared with the same name.
|
|
1068
|
+
*/
|
|
86
1069
|
COLUMN_RENAMED: COLUMN_RENAMED_CODE,
|
|
87
|
-
/**
|
|
1070
|
+
/**
|
|
1071
|
+
* You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as
|
|
1072
|
+
* a plain attribute.
|
|
1073
|
+
*/
|
|
88
1074
|
ROLE_TAKEN: ROLE_TAKEN_CODE,
|
|
89
|
-
/**
|
|
1075
|
+
/**
|
|
1076
|
+
* You read into a graph builder whose direction is already set, or which already holds edges, so the file is read
|
|
1077
|
+
* with the builder's direction instead of its own.
|
|
1078
|
+
*/
|
|
90
1079
|
DIRECTION_REFUSED: DIRECTION_REFUSED_CODE,
|
|
91
|
-
/** Edges
|
|
1080
|
+
/** Edges of the other direction were read with the direction `onMixedDirection` chose. */
|
|
92
1081
|
DIRECTION_FORCED: DIRECTION_FORCED_CODE,
|
|
93
|
-
/**
|
|
1082
|
+
/** You set an option this format does not use; it had no effect. The message names the option. */
|
|
94
1083
|
OPTION_IGNORED: OPTION_IGNORED_CODE,
|
|
95
|
-
/**
|
|
1084
|
+
/**
|
|
1085
|
+
* You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,
|
|
1086
|
+
* `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.
|
|
1087
|
+
*/
|
|
96
1088
|
SINK_OPTION: SINK_OPTION_CODE,
|
|
97
|
-
/** The input is
|
|
1089
|
+
/** The input is larger than graph-io's size limit. The import stops. */
|
|
98
1090
|
TOO_LARGE: TOO_LARGE_CODE
|
|
99
1091
|
});
|
|
100
1092
|
const USED_OPTIONS = /* @__PURE__ */ new Set([
|
|
@@ -106,6 +1098,7 @@ const USED_OPTIONS = /* @__PURE__ */ new Set([
|
|
|
106
1098
|
"weightFrom",
|
|
107
1099
|
"weightDtype",
|
|
108
1100
|
"long",
|
|
1101
|
+
"restoreMangledIds",
|
|
109
1102
|
"errorLimit",
|
|
110
1103
|
"signal",
|
|
111
1104
|
"onProgress"
|
|
@@ -118,12 +1111,15 @@ const FORMAT_DEFAULTS = {
|
|
|
118
1111
|
};
|
|
119
1112
|
const ABORT_CHECK_INTERVAL = 64;
|
|
120
1113
|
const NUMBER_VERIFICATION_VALUES = /* @__PURE__ */ new Set(["281474976710655", "9223372036854775807"]);
|
|
121
|
-
const OLD_ASPECT_NAMES =
|
|
122
|
-
visualProperties
|
|
123
|
-
subNetworks
|
|
124
|
-
networkRelations
|
|
125
|
-
hiddenAttributes
|
|
126
|
-
|
|
1114
|
+
const OLD_ASPECT_NAMES = /* @__PURE__ */ new Map([
|
|
1115
|
+
["visualProperties", "cyVisualProperties"],
|
|
1116
|
+
["subNetworks", "cySubNetworks"],
|
|
1117
|
+
["networkRelations", "cyNetworkRelations"],
|
|
1118
|
+
["hiddenAttributes", "cyHiddenAttributes"]
|
|
1119
|
+
]);
|
|
1120
|
+
const NODE_KEYS = /* @__PURE__ */ new Set(["@id", "n", "r"]);
|
|
1121
|
+
const EDGE_KEYS = /* @__PURE__ */ new Set(["@id", "s", "t", "i"]);
|
|
1122
|
+
const TABLES = /* @__PURE__ */ new Set(["node_table", "edge_table", "network_table"]);
|
|
127
1123
|
const READ_ASPECTS = /* @__PURE__ */ new Set([
|
|
128
1124
|
"nodes",
|
|
129
1125
|
"edges",
|
|
@@ -159,6 +1155,7 @@ const ALSO_KEPT = /* @__PURE__ */ new Set([
|
|
|
159
1155
|
"metaData"
|
|
160
1156
|
]);
|
|
161
1157
|
const TABLE_STYLE_ASPECTS = ["tableVisualProperties", "cyTableVisualProperties"];
|
|
1158
|
+
const OWN_EXTRA_KEYS = ["groups", "subnetwork"];
|
|
162
1159
|
const CX_FIRST_KEYS = ["numberVerification", "metaData"];
|
|
163
1160
|
const CX_ASPECT_KEYS = [
|
|
164
1161
|
"nodes",
|
|
@@ -185,14 +1182,14 @@ const CX_ASPECT_KEYS = [
|
|
|
185
1182
|
"supports",
|
|
186
1183
|
"functionTerms"
|
|
187
1184
|
];
|
|
188
|
-
const SCALARS =
|
|
189
|
-
string
|
|
190
|
-
boolean
|
|
191
|
-
integer
|
|
192
|
-
long
|
|
193
|
-
double
|
|
194
|
-
float
|
|
195
|
-
|
|
1185
|
+
const SCALARS = /* @__PURE__ */ new Map([
|
|
1186
|
+
["string", "string"],
|
|
1187
|
+
["boolean", "boolean"],
|
|
1188
|
+
["integer", "integer"],
|
|
1189
|
+
["long", "long"],
|
|
1190
|
+
["double", "double"],
|
|
1191
|
+
["float", "double"]
|
|
1192
|
+
]);
|
|
196
1193
|
function cxType(d) {
|
|
197
1194
|
if (d === void 0 || d === null) {
|
|
198
1195
|
return { scalar: "string", list: false };
|
|
@@ -201,7 +1198,7 @@ function cxType(d) {
|
|
|
201
1198
|
return null;
|
|
202
1199
|
}
|
|
203
1200
|
const list = d.startsWith("list_of_");
|
|
204
|
-
const scalar = SCALARS
|
|
1201
|
+
const scalar = SCALARS.get(list ? d.slice("list_of_".length) : d);
|
|
205
1202
|
return scalar === void 0 ? null : { scalar, list };
|
|
206
1203
|
}
|
|
207
1204
|
function typeText(type) {
|
|
@@ -290,7 +1287,14 @@ function parseScalar(value, scalar, long, onPrecision) {
|
|
|
290
1287
|
if (value === "Infinity" || value === "-Infinity") {
|
|
291
1288
|
return Number(value);
|
|
292
1289
|
}
|
|
293
|
-
|
|
1290
|
+
if (!DECIMAL_TEXT.test(value)) {
|
|
1291
|
+
return BAD;
|
|
1292
|
+
}
|
|
1293
|
+
const n = Number(value);
|
|
1294
|
+
if (!Number.isFinite(n)) {
|
|
1295
|
+
onPrecision(value);
|
|
1296
|
+
}
|
|
1297
|
+
return n;
|
|
294
1298
|
}
|
|
295
1299
|
}
|
|
296
1300
|
}
|
|
@@ -369,13 +1373,13 @@ function shown(value) {
|
|
|
369
1373
|
function aspect(doc, name) {
|
|
370
1374
|
return doc.aspects.get(name) ?? [];
|
|
371
1375
|
}
|
|
372
|
-
function inexactBits(text, keys) {
|
|
1376
|
+
function inexactBits(text, keys, depth = 1) {
|
|
373
1377
|
if (!/[0-9][.eE]/.test(text)) {
|
|
374
1378
|
return 0;
|
|
375
1379
|
}
|
|
376
1380
|
let bits = 0;
|
|
377
1381
|
keys.forEach((key, i) => {
|
|
378
|
-
if (inexactLiteral(text, key)) {
|
|
1382
|
+
if (inexactLiteral(text, key, depth)) {
|
|
379
1383
|
bits |= 1 << i;
|
|
380
1384
|
}
|
|
381
1385
|
});
|
|
@@ -405,18 +1409,99 @@ function checkNumberVerification(value, count, report, line) {
|
|
|
405
1409
|
);
|
|
406
1410
|
}
|
|
407
1411
|
}
|
|
1412
|
+
function isCxAspect(name) {
|
|
1413
|
+
return READ_ASPECTS.has(name) || OLD_ASPECT_NAMES.has(name) || CX_ASPECT_KEYS.includes(name) || TABLE_STYLE_ASPECTS.includes(name);
|
|
1414
|
+
}
|
|
1415
|
+
const ID_KEYS = /* @__PURE__ */ new Map([
|
|
1416
|
+
["nodes", ["@id"]],
|
|
1417
|
+
["edges", ["@id", "s", "t"]]
|
|
1418
|
+
]);
|
|
1419
|
+
function fileAttribute(table, name, held, report) {
|
|
1420
|
+
const { value, line } = held;
|
|
1421
|
+
const n = isRecord(value) ? value.n : void 0;
|
|
1422
|
+
if (n === void 0 || n === null) {
|
|
1423
|
+
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
|
|
1424
|
+
line,
|
|
1425
|
+
element: name
|
|
1426
|
+
});
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
if (typeof n !== "string" || n === "") {
|
|
1430
|
+
report.error(
|
|
1431
|
+
"validation-error",
|
|
1432
|
+
BAD_VALUE_CODE,
|
|
1433
|
+
`a ${name} element has the attribute name n ${shown(n)}, not a non-empty string; skipped`,
|
|
1434
|
+
{ line, element: name }
|
|
1435
|
+
);
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
if (!table.has(n)) {
|
|
1439
|
+
table.set(n, []);
|
|
1440
|
+
}
|
|
1441
|
+
table.get(n)?.push(held);
|
|
1442
|
+
}
|
|
1443
|
+
function collectElement(state, element) {
|
|
1444
|
+
const { doc, report } = state;
|
|
1445
|
+
const { name, value, text, line, exact, depth } = element;
|
|
1446
|
+
doc.structure.element(name, value, line);
|
|
1447
|
+
if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
|
|
1448
|
+
if (!doc.kept.has(name)) {
|
|
1449
|
+
doc.kept.set(name, []);
|
|
1450
|
+
}
|
|
1451
|
+
const onPrecision = name === "numberVerification" ? void 0 : state.onKeptPrecision;
|
|
1452
|
+
doc.kept.get(name)?.push(exact ? plainJson(value, onPrecision) : value);
|
|
1453
|
+
}
|
|
1454
|
+
if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (name === "numberVerification") {
|
|
1458
|
+
checkNumberVerification(value, ++state.verifications, report, line);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
const ids = ID_KEYS.get(name);
|
|
1462
|
+
const held = { value, line, inexact: ids === void 0 ? 0 : inexactBits(text, ids, depth) };
|
|
1463
|
+
const table = attributeTable(doc, name);
|
|
1464
|
+
if (table !== null) {
|
|
1465
|
+
fileAttribute(table, name, held, report);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (!doc.aspects.has(name)) {
|
|
1469
|
+
doc.aspects.set(name, []);
|
|
1470
|
+
}
|
|
1471
|
+
doc.aspects.get(name)?.push(held);
|
|
1472
|
+
}
|
|
408
1473
|
async function readDocument(input, report, options) {
|
|
409
|
-
const structure = new CxStructure(report);
|
|
1474
|
+
const structure = new CxStructure(report, (name) => OLD_ASPECT_NAMES.get(name) ?? name);
|
|
410
1475
|
const doc = {
|
|
411
1476
|
aspects: /* @__PURE__ */ new Map(),
|
|
412
1477
|
attributes: { node: /* @__PURE__ */ new Map(), edge: /* @__PURE__ */ new Map(), network: /* @__PURE__ */ new Map() },
|
|
413
1478
|
kept: /* @__PURE__ */ new Map(),
|
|
414
1479
|
structure
|
|
415
1480
|
};
|
|
1481
|
+
const state = {
|
|
1482
|
+
doc,
|
|
1483
|
+
report,
|
|
1484
|
+
verifications: 0,
|
|
1485
|
+
onKeptPrecision: keptPrecision(report, "meta.extra.cx")
|
|
1486
|
+
};
|
|
416
1487
|
let sinceCheck = 0;
|
|
417
|
-
let
|
|
1488
|
+
let members = 0;
|
|
1489
|
+
let known = 0;
|
|
1490
|
+
const unknownNames = /* @__PURE__ */ new Set();
|
|
1491
|
+
const lastMember = { block: -1 };
|
|
1492
|
+
const countMember = (block, name) => {
|
|
1493
|
+
if (block !== lastMember.block) {
|
|
1494
|
+
lastMember.block = block;
|
|
1495
|
+
members++;
|
|
1496
|
+
}
|
|
1497
|
+
if (name !== null && isCxAspect(name)) {
|
|
1498
|
+
known++;
|
|
1499
|
+
} else if (name !== null) {
|
|
1500
|
+
unknownNames.add(name);
|
|
1501
|
+
}
|
|
1502
|
+
};
|
|
418
1503
|
const canonical = (name, line) => {
|
|
419
|
-
const renamed = OLD_ASPECT_NAMES
|
|
1504
|
+
const renamed = OLD_ASPECT_NAMES.get(name);
|
|
420
1505
|
if (renamed === void 0) {
|
|
421
1506
|
return name;
|
|
422
1507
|
}
|
|
@@ -429,51 +1514,11 @@ async function readDocument(input, report, options) {
|
|
|
429
1514
|
);
|
|
430
1515
|
return renamed;
|
|
431
1516
|
};
|
|
432
|
-
const collect = (name, value, text, line, exact) => {
|
|
433
|
-
|
|
434
|
-
if (ALSO_KEPT.has(name) || !READ_ASPECTS.has(name)) {
|
|
435
|
-
if (!doc.kept.has(name)) {
|
|
436
|
-
doc.kept.set(name, []);
|
|
437
|
-
}
|
|
438
|
-
doc.kept.get(name)?.push(exact ? plainJson(value) : value);
|
|
439
|
-
}
|
|
440
|
-
if (!READ_ASPECTS.has(name) || name === "metaData" || name === "status") {
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (name === "numberVerification") {
|
|
444
|
-
checkNumberVerification(value, ++verifications, report, line);
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
let inexact = 0;
|
|
448
|
-
if (name === "nodes") {
|
|
449
|
-
inexact = inexactBits(text, ["@id"]);
|
|
450
|
-
} else if (name === "edges") {
|
|
451
|
-
inexact = inexactBits(text, ["@id", "s", "t"]);
|
|
452
|
-
}
|
|
453
|
-
const held = { value, line, inexact };
|
|
454
|
-
const table = attributeTable(doc, name);
|
|
455
|
-
if (table !== null) {
|
|
456
|
-
if (!isRecord(value) || typeof value.n !== "string") {
|
|
457
|
-
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no attribute name n; skipped`, {
|
|
458
|
-
line,
|
|
459
|
-
element: name
|
|
460
|
-
});
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
const key = value.n;
|
|
464
|
-
if (!table.has(key)) {
|
|
465
|
-
table.set(key, []);
|
|
466
|
-
}
|
|
467
|
-
table.get(key)?.push(held);
|
|
468
|
-
return;
|
|
469
|
-
}
|
|
470
|
-
if (!doc.aspects.has(name)) {
|
|
471
|
-
doc.aspects.set(name, []);
|
|
472
|
-
}
|
|
473
|
-
doc.aspects.get(name)?.push(held);
|
|
1517
|
+
const collect = (name, value, text, line, exact, depth = 1) => {
|
|
1518
|
+
collectElement(state, { name, value, text, line, exact, depth });
|
|
474
1519
|
};
|
|
475
1520
|
try {
|
|
476
|
-
for await (const event of scanAspects(textChunks(input, report, options))) {
|
|
1521
|
+
for await (const event of scanAspects(textChunks(input, report, options), report)) {
|
|
477
1522
|
if (++sinceCheck >= ABORT_CHECK_INTERVAL) {
|
|
478
1523
|
sinceCheck = 0;
|
|
479
1524
|
throwIfAborted(options.signal);
|
|
@@ -482,7 +1527,7 @@ async function readDocument(input, report, options) {
|
|
|
482
1527
|
case "root":
|
|
483
1528
|
report.fail(
|
|
484
1529
|
CX_ISSUE.NOT_CX,
|
|
485
|
-
`a CX document is a JSON array of aspects; found ${
|
|
1530
|
+
`a CX document is a JSON array of aspects; found ${event.object ? "an object" : shown(event.value)}`,
|
|
486
1531
|
{ line: event.line }
|
|
487
1532
|
);
|
|
488
1533
|
break;
|
|
@@ -497,11 +1542,21 @@ async function readDocument(input, report, options) {
|
|
|
497
1542
|
}
|
|
498
1543
|
const keys = isRecord(value) ? Object.keys(value) : [];
|
|
499
1544
|
if (isRecord(value) && keys.length === 1 && isRecord(value[keys[0]])) {
|
|
1545
|
+
countMember(event.block, keys[0]);
|
|
500
1546
|
const name = canonical(keys[0], event.line);
|
|
1547
|
+
if (isCxAspect(name)) {
|
|
1548
|
+
report.warning(
|
|
1549
|
+
"validation-error",
|
|
1550
|
+
CX_ISSUE.SINGLE_OBJECT_ASPECT,
|
|
1551
|
+
`the "${keys[0]}" aspect is one object, not an array of elements; read as one element`,
|
|
1552
|
+
{ line: event.line, element: keys[0] }
|
|
1553
|
+
);
|
|
1554
|
+
}
|
|
501
1555
|
structure.block(name, event.line);
|
|
502
|
-
collect(name, value[keys[0]],
|
|
1556
|
+
collect(name, value[keys[0]], event.text, event.line, event.exact, 2);
|
|
503
1557
|
break;
|
|
504
1558
|
}
|
|
1559
|
+
countMember(event.block, null);
|
|
505
1560
|
report.error(
|
|
506
1561
|
"parse-error",
|
|
507
1562
|
BAD_ASPECT_BLOCK_CODE,
|
|
@@ -511,21 +1566,24 @@ async function readDocument(input, report, options) {
|
|
|
511
1566
|
break;
|
|
512
1567
|
}
|
|
513
1568
|
case "block":
|
|
1569
|
+
countMember(event.block, event.aspect);
|
|
1570
|
+
reportSharedBlock(report, event);
|
|
514
1571
|
structure.block(canonical(event.aspect, event.line), event.line);
|
|
515
1572
|
break;
|
|
516
1573
|
case "element": {
|
|
517
|
-
const name = OLD_ASPECT_NAMES
|
|
1574
|
+
const name = OLD_ASPECT_NAMES.get(event.aspect) ?? event.aspect;
|
|
518
1575
|
collect(name, event.value, event.text, event.line, event.exact);
|
|
519
1576
|
break;
|
|
520
1577
|
}
|
|
521
1578
|
case "deep":
|
|
1579
|
+
countMember(event.block, event.aspect);
|
|
522
1580
|
reportTooDeep(report, event);
|
|
523
1581
|
break;
|
|
524
1582
|
case "extraKeys":
|
|
525
1583
|
report.error(
|
|
526
1584
|
"parse-error",
|
|
527
1585
|
BAD_ASPECT_BLOCK_CODE,
|
|
528
|
-
`the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}); a fragment has one key, the others are skipped`,
|
|
1586
|
+
`the "${event.aspect}" fragment holds more keys (${event.keys.join(", ")}) whose values are not arrays; a fragment has one key, the others are skipped`,
|
|
529
1587
|
{ line: event.line, element: event.aspect }
|
|
530
1588
|
);
|
|
531
1589
|
break;
|
|
@@ -539,99 +1597,88 @@ async function readDocument(input, report, options) {
|
|
|
539
1597
|
}
|
|
540
1598
|
throw err;
|
|
541
1599
|
}
|
|
1600
|
+
if (members > 0 && known === 0) {
|
|
1601
|
+
const found = [...unknownNames].slice(0, 5).map((n) => JSON.stringify(n));
|
|
1602
|
+
const hint = unknownNames.has("data") ? ": Cytoscape.js elements?" : "";
|
|
1603
|
+
const seen = found.length > 0 ? ` (found ${found.join(", ")}${hint})` : "";
|
|
1604
|
+
report.fail(CX_ISSUE.NOT_CX, `no member of the array is a CX aspect${seen}; the input is not CX`);
|
|
1605
|
+
}
|
|
1606
|
+
if (structure.hasStatus && !structure.statusWellFormed()) {
|
|
1607
|
+
report.error(
|
|
1608
|
+
"validation-error",
|
|
1609
|
+
BAD_VALUE_CODE,
|
|
1610
|
+
`the status element is ${shown(structure.status ?? null)}, not an object with a boolean success; ignored`,
|
|
1611
|
+
{ element: "status" }
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
542
1614
|
structure.checkCounts();
|
|
543
1615
|
return doc;
|
|
544
1616
|
}
|
|
545
1617
|
function refId(raw) {
|
|
546
1618
|
try {
|
|
547
|
-
return cxId(raw).id;
|
|
548
|
-
} catch {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
function memberSet(raw) {
|
|
553
|
-
if (raw === "all") {
|
|
554
|
-
return null;
|
|
555
|
-
}
|
|
556
|
-
const out = /* @__PURE__ */ new Set();
|
|
557
|
-
if (Array.isArray(raw)) {
|
|
558
|
-
for (const item of raw) {
|
|
559
|
-
const id = refId(item);
|
|
560
|
-
if (id !== null) {
|
|
561
|
-
out.add(id);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return out;
|
|
566
|
-
}
|
|
567
|
-
function graphPlans(doc) {
|
|
568
|
-
const subs = /* @__PURE__ */ new Map();
|
|
569
|
-
for (const { value } of aspect(doc, "cySubNetworks")) {
|
|
570
|
-
const id = isRecord(value) ? refId(value["@id"]) : null;
|
|
571
|
-
if (id !== null && isRecord(value) && !subs.has(id)) {
|
|
572
|
-
subs.set(id, { nodes: memberSet(value.nodes), edges: memberSet(value.edges) });
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
const relationNames = /* @__PURE__ */ new Map();
|
|
576
|
-
const order = [];
|
|
577
|
-
const views = /* @__PURE__ */ new Map();
|
|
578
|
-
const addView = (sub, view) => {
|
|
579
|
-
const list = views.get(sub) ?? [];
|
|
580
|
-
if (!list.includes(view)) {
|
|
581
|
-
list.push(view);
|
|
582
|
-
}
|
|
583
|
-
views.set(sub, list);
|
|
584
|
-
};
|
|
585
|
-
for (const { value } of aspect(doc, "cyNetworkRelations")) {
|
|
586
|
-
if (!isRecord(value)) {
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
const child = refId(value.c);
|
|
590
|
-
if (child === null) {
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
if (value.r === "view") {
|
|
594
|
-
const parent = refId(value.p);
|
|
595
|
-
if (parent !== null) {
|
|
596
|
-
addView(parent, child);
|
|
597
|
-
}
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
if (typeof value.name === "string") {
|
|
601
|
-
relationNames.set(child, value.name);
|
|
602
|
-
}
|
|
603
|
-
if (subs.has(child) && !order.includes(child)) {
|
|
604
|
-
order.push(child);
|
|
605
|
-
}
|
|
1619
|
+
return cxId(raw).id;
|
|
1620
|
+
} catch {
|
|
1621
|
+
return null;
|
|
606
1622
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
addView(sub, view);
|
|
612
|
-
}
|
|
1623
|
+
}
|
|
1624
|
+
function memberSet(raw, onBad) {
|
|
1625
|
+
if (raw === "all") {
|
|
1626
|
+
return null;
|
|
613
1627
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
1628
|
+
const out = /* @__PURE__ */ new Set();
|
|
1629
|
+
if (Array.isArray(raw)) {
|
|
1630
|
+
for (const item of raw) {
|
|
1631
|
+
const id = refId(item);
|
|
1632
|
+
if (id === null) {
|
|
1633
|
+
onBad?.(CX_ISSUE.INVALID_ID, `the member ${shown(item)} is not an id; ignored`);
|
|
1634
|
+
} else {
|
|
1635
|
+
out.add(id);
|
|
1636
|
+
}
|
|
617
1637
|
}
|
|
1638
|
+
} else if (raw !== void 0) {
|
|
1639
|
+
onBad?.(BAD_VALUE_CODE, `the member list ${shown(raw)} is neither a list of ids nor "all"; read as empty`);
|
|
618
1640
|
}
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
1641
|
+
return out;
|
|
1642
|
+
}
|
|
1643
|
+
function structuralId(held, name, key, report) {
|
|
1644
|
+
const { value, line } = held;
|
|
1645
|
+
if (!isRecord(value)) {
|
|
1646
|
+
report.error(
|
|
1647
|
+
"parse-error",
|
|
1648
|
+
BAD_ASPECT_BLOCK_CODE,
|
|
1649
|
+
`a ${name} element is ${shown(value)}, not an object; skipped`,
|
|
1650
|
+
{
|
|
1651
|
+
line,
|
|
1652
|
+
element: name
|
|
628
1653
|
}
|
|
629
|
-
|
|
630
|
-
|
|
1654
|
+
);
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1657
|
+
if (value[key] === void 0 || value[key] === null) {
|
|
1658
|
+
report.error("missing-value", MISSING_ID_CODE, `a ${name} element has no ${key}; skipped`, {
|
|
1659
|
+
line,
|
|
1660
|
+
element: name
|
|
1661
|
+
});
|
|
1662
|
+
return null;
|
|
1663
|
+
}
|
|
1664
|
+
const id = refId(value[key]);
|
|
1665
|
+
if (id === null) {
|
|
1666
|
+
report.error(
|
|
1667
|
+
"validation-error",
|
|
1668
|
+
CX_ISSUE.INVALID_ID,
|
|
1669
|
+
`a ${name} element has the ${key} ${shown(value[key])}, not an integer id; skipped`,
|
|
1670
|
+
{
|
|
1671
|
+
line,
|
|
1672
|
+
element: name
|
|
631
1673
|
}
|
|
632
|
-
|
|
633
|
-
return
|
|
634
|
-
}
|
|
1674
|
+
);
|
|
1675
|
+
return null;
|
|
1676
|
+
}
|
|
1677
|
+
return { value, id };
|
|
1678
|
+
}
|
|
1679
|
+
function graphPlans(doc, report) {
|
|
1680
|
+
const subs = readSubnetworks(doc, report);
|
|
1681
|
+
const { relationNames, order, views } = readRelations(doc, subs, report);
|
|
635
1682
|
if (order.length <= 1) {
|
|
636
1683
|
const sub = order.length === 1 ? order[0] : null;
|
|
637
1684
|
const members = sub === null ? void 0 : subs.get(sub);
|
|
@@ -642,10 +1689,11 @@ function graphPlans(doc) {
|
|
|
642
1689
|
if (graphViews.length === 0) {
|
|
643
1690
|
graphViews = layoutViews(doc);
|
|
644
1691
|
}
|
|
1692
|
+
const relationName = sub === null ? null : relationNames.get(sub) ?? null;
|
|
645
1693
|
return [
|
|
646
1694
|
{
|
|
647
1695
|
index: 0,
|
|
648
|
-
name:
|
|
1696
|
+
name: relationName ?? networkName(doc, sub),
|
|
649
1697
|
subnetwork: sub,
|
|
650
1698
|
nodes: members?.nodes ?? null,
|
|
651
1699
|
edges: members?.edges ?? null,
|
|
@@ -655,13 +1703,99 @@ function graphPlans(doc) {
|
|
|
655
1703
|
}
|
|
656
1704
|
return order.map((sub, index) => ({
|
|
657
1705
|
index,
|
|
658
|
-
name: relationNames.get(sub) ?? networkName(sub),
|
|
1706
|
+
name: relationNames.get(sub) ?? networkName(doc, sub),
|
|
659
1707
|
subnetwork: sub,
|
|
660
1708
|
nodes: subs.get(sub)?.nodes ?? null,
|
|
661
1709
|
edges: subs.get(sub)?.edges ?? null,
|
|
662
1710
|
views: views.get(sub) ?? []
|
|
663
1711
|
}));
|
|
664
1712
|
}
|
|
1713
|
+
function readSubnetworks(doc, report) {
|
|
1714
|
+
const subs = /* @__PURE__ */ new Map();
|
|
1715
|
+
for (const held of aspect(doc, "cySubNetworks")) {
|
|
1716
|
+
const sub = structuralId(held, "cySubNetworks", "@id", report);
|
|
1717
|
+
if (sub === null) {
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
const { value, id } = sub;
|
|
1721
|
+
if (subs.has(id)) {
|
|
1722
|
+
report.error(
|
|
1723
|
+
"validation-error",
|
|
1724
|
+
BAD_ASPECT_BLOCK_CODE,
|
|
1725
|
+
`a second cySubNetworks element has the @id ${String(id)}; skipped, the first is used`,
|
|
1726
|
+
{ line: held.line, element: "cySubNetworks" }
|
|
1727
|
+
);
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
const onBad = (code, message) => {
|
|
1731
|
+
report.error("validation-error", code, `subnetwork ${String(id)}: ${message}`, {
|
|
1732
|
+
line: held.line,
|
|
1733
|
+
element: "cySubNetworks"
|
|
1734
|
+
});
|
|
1735
|
+
};
|
|
1736
|
+
subs.set(id, { nodes: memberSet(value.nodes, onBad), edges: memberSet(value.edges, onBad) });
|
|
1737
|
+
}
|
|
1738
|
+
return subs;
|
|
1739
|
+
}
|
|
1740
|
+
function readRelations(doc, subs, report) {
|
|
1741
|
+
const relationNames = /* @__PURE__ */ new Map();
|
|
1742
|
+
const order = [];
|
|
1743
|
+
const views = /* @__PURE__ */ new Map();
|
|
1744
|
+
const addView = (sub, view) => {
|
|
1745
|
+
const list = views.get(sub) ?? [];
|
|
1746
|
+
if (!list.includes(view)) {
|
|
1747
|
+
list.push(view);
|
|
1748
|
+
}
|
|
1749
|
+
views.set(sub, list);
|
|
1750
|
+
};
|
|
1751
|
+
for (const held of aspect(doc, "cyNetworkRelations")) {
|
|
1752
|
+
const relation = structuralId(held, "cyNetworkRelations", "c", report);
|
|
1753
|
+
if (relation !== null) {
|
|
1754
|
+
fileRelation(relation, subs, { relationNames, order, addView });
|
|
1755
|
+
}
|
|
1756
|
+
}
|
|
1757
|
+
for (const held of aspect(doc, "cyViews")) {
|
|
1758
|
+
const view = structuralId(held, "cyViews", "@id", report);
|
|
1759
|
+
const sub = view === null ? null : refId(view.value.s);
|
|
1760
|
+
if (view !== null && sub !== null) {
|
|
1761
|
+
addView(sub, view.id);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
order.push(...[...subs.keys()].filter((id) => !order.includes(id)));
|
|
1765
|
+
return { relationNames, order, views };
|
|
1766
|
+
}
|
|
1767
|
+
function fileRelation(relation, subs, out) {
|
|
1768
|
+
const { value, id: child } = relation;
|
|
1769
|
+
if (value.r === "view") {
|
|
1770
|
+
const parent = refId(value.p);
|
|
1771
|
+
if (parent !== null) {
|
|
1772
|
+
out.addView(parent, child);
|
|
1773
|
+
}
|
|
1774
|
+
return;
|
|
1775
|
+
}
|
|
1776
|
+
if (typeof value.name === "string") {
|
|
1777
|
+
out.relationNames.set(child, value.name);
|
|
1778
|
+
}
|
|
1779
|
+
if (subs.has(child) && !out.order.includes(child)) {
|
|
1780
|
+
out.order.push(child);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
function networkName(doc, sub) {
|
|
1784
|
+
let found = null;
|
|
1785
|
+
for (const { value } of doc.attributes.network.get("name") ?? []) {
|
|
1786
|
+
if (!isRecord(value) || typeof value.v !== "string") {
|
|
1787
|
+
continue;
|
|
1788
|
+
}
|
|
1789
|
+
const scope = value.s === void 0 ? null : refId(value.s);
|
|
1790
|
+
if (scope === sub) {
|
|
1791
|
+
return value.v;
|
|
1792
|
+
}
|
|
1793
|
+
if (scope === null) {
|
|
1794
|
+
found ??= value.v;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return found;
|
|
1798
|
+
}
|
|
665
1799
|
function layoutViews(doc) {
|
|
666
1800
|
const out = [];
|
|
667
1801
|
for (const { value } of aspect(doc, "cartesianLayout")) {
|
|
@@ -685,11 +1819,13 @@ class CxReader {
|
|
|
685
1819
|
* @param doc - the collected document
|
|
686
1820
|
* @param plan - the graph to read
|
|
687
1821
|
*/
|
|
688
|
-
constructor(sink, report, options, zAs, doc,
|
|
1822
|
+
constructor(sink, report, options, zAs, doc, plan2) {
|
|
689
1823
|
this.rootNodes = /* @__PURE__ */ new Set();
|
|
690
1824
|
this.rootEdges = /* @__PURE__ */ new Set();
|
|
691
1825
|
this.nodeRows = /* @__PURE__ */ new Map();
|
|
1826
|
+
this.sinkRows = /* @__PURE__ */ new Map();
|
|
692
1827
|
this.edgeRows = /* @__PURE__ */ new Map();
|
|
1828
|
+
this.sinkIds = /* @__PURE__ */ new Map();
|
|
693
1829
|
this.subnetworks = /* @__PURE__ */ new Set();
|
|
694
1830
|
this.dangling = /* @__PURE__ */ new Map();
|
|
695
1831
|
this.nameHandle = INVALID_INDEX;
|
|
@@ -700,13 +1836,13 @@ class CxReader {
|
|
|
700
1836
|
this.idType = "integer";
|
|
701
1837
|
this.weighted = false;
|
|
702
1838
|
this.coreValues = /* @__PURE__ */ new Map();
|
|
703
|
-
this.weights =
|
|
1839
|
+
this.weights = /* @__PURE__ */ new Map();
|
|
704
1840
|
this.sink = sink;
|
|
705
1841
|
this.report = report;
|
|
706
1842
|
this.options = options;
|
|
707
1843
|
this.zAs = zAs;
|
|
708
1844
|
this.doc = doc;
|
|
709
|
-
this.plan =
|
|
1845
|
+
this.plan = plan2;
|
|
710
1846
|
this.direction = new DirectionResolver(sink, report, options.onMixedDirection);
|
|
711
1847
|
this.coercer = new IdCoercer(options.ids);
|
|
712
1848
|
for (const { value } of aspect(doc, "nodes")) {
|
|
@@ -734,6 +1870,7 @@ class CxReader {
|
|
|
734
1870
|
this.direction.setHeader(true);
|
|
735
1871
|
this.checkMembers();
|
|
736
1872
|
this.checkRootOnly();
|
|
1873
|
+
this.checkTableColumns();
|
|
737
1874
|
const nodeColumns = this.declareAttributes("node");
|
|
738
1875
|
this.readNodes();
|
|
739
1876
|
this.readGroups();
|
|
@@ -746,9 +1883,14 @@ class CxReader {
|
|
|
746
1883
|
this.readVisualProperties();
|
|
747
1884
|
this.readProvenance();
|
|
748
1885
|
for (const [kind, count] of this.dangling) {
|
|
749
|
-
report.warning(
|
|
750
|
-
|
|
751
|
-
|
|
1886
|
+
report.warning(
|
|
1887
|
+
"validation-error",
|
|
1888
|
+
DANGLING_REFERENCE_CODE,
|
|
1889
|
+
`${count} ${kind}${plural(count)} ${agree(count, "names", "name")} nothing; ignored`,
|
|
1890
|
+
{
|
|
1891
|
+
element: kind
|
|
1892
|
+
}
|
|
1893
|
+
);
|
|
752
1894
|
}
|
|
753
1895
|
this.setMeta();
|
|
754
1896
|
}
|
|
@@ -775,15 +1917,6 @@ class CxReader {
|
|
|
775
1917
|
inGraph(id) {
|
|
776
1918
|
return this.plan.nodes === null || this.plan.nodes.has(id);
|
|
777
1919
|
}
|
|
778
|
-
/**
|
|
779
|
-
* The key of the node and edge row maps for a CX id as an aspect writes it: the id the `ids`
|
|
780
|
-
* option makes of it, which is what idOf() stored the element under.
|
|
781
|
-
* @param raw - the CX id
|
|
782
|
-
* @returns the row key
|
|
783
|
-
*/
|
|
784
|
-
key(raw) {
|
|
785
|
-
return this.options.ids === "keep" ? raw : this.coercer.value(raw);
|
|
786
|
-
}
|
|
787
1920
|
/**
|
|
788
1921
|
* Whether a scoped value applies to this graph: unscoped, or scoped to its subnetwork. A scope
|
|
789
1922
|
* naming no subnetwork is counted as dangling.
|
|
@@ -804,14 +1937,16 @@ class CxReader {
|
|
|
804
1937
|
return 0;
|
|
805
1938
|
}
|
|
806
1939
|
/**
|
|
807
|
-
* The id of an element
|
|
1940
|
+
* The id of an element by the CX id rule, as the document's references name it (the `ids`
|
|
1941
|
+
* option applies only to the id the sink holds, sinkId()).
|
|
808
1942
|
* @param raw - the parsed id
|
|
809
1943
|
* @param inexact - whether the literal was not a plain integer
|
|
810
1944
|
* @param element - the element name for issues
|
|
1945
|
+
* @param line - the element's line
|
|
811
1946
|
* @param edgeId - whether this is an edge's own id (stored in the f64 id column, not kept as digits)
|
|
812
1947
|
* @returns the id, or null when it was reported
|
|
813
1948
|
*/
|
|
814
|
-
idOf(raw, inexact, element, edgeId = false) {
|
|
1949
|
+
idOf(raw, inexact, element, line, edgeId = false) {
|
|
815
1950
|
try {
|
|
816
1951
|
const parsed = cxId(raw, inexact);
|
|
817
1952
|
if (parsed.note === "text") {
|
|
@@ -819,14 +1954,14 @@ class CxReader {
|
|
|
819
1954
|
"coercion",
|
|
820
1955
|
ID_TEXT_TYPE_CODE,
|
|
821
1956
|
`${element}: the id ${shown(raw)} is not written as an integer; read as ${String(parsed.id)}`,
|
|
822
|
-
{ element }
|
|
1957
|
+
{ line, element }
|
|
823
1958
|
);
|
|
824
1959
|
} else if (parsed.note === "precision" && edgeId) {
|
|
825
1960
|
this.report.warnOnce(
|
|
826
1961
|
"precision",
|
|
827
1962
|
PRECISION_CODE,
|
|
828
1963
|
`${element}: the edge id ${String(parsed.id)} is beyond 2^53; the id column holds the nearest double`,
|
|
829
|
-
{ element },
|
|
1964
|
+
{ line, element },
|
|
830
1965
|
`${PRECISION_CODE}:edge`
|
|
831
1966
|
);
|
|
832
1967
|
} else if (parsed.note === "precision") {
|
|
@@ -835,29 +1970,93 @@ class CxReader {
|
|
|
835
1970
|
"precision",
|
|
836
1971
|
PRECISION_CODE,
|
|
837
1972
|
`${element}: the id ${String(parsed.id)} is beyond 2^53; kept as its digits (a string id)`,
|
|
838
|
-
{ element }
|
|
1973
|
+
{ line, element }
|
|
839
1974
|
);
|
|
840
1975
|
}
|
|
841
|
-
return
|
|
1976
|
+
return parsed.id;
|
|
842
1977
|
} catch (err) {
|
|
843
|
-
this.report.recordError(err, { element });
|
|
1978
|
+
this.report.recordError(err, { line, element });
|
|
844
1979
|
return null;
|
|
845
1980
|
}
|
|
846
1981
|
}
|
|
1982
|
+
/**
|
|
1983
|
+
* The id the sink holds a CX node under: its restored original id, else the `ids` option applied.
|
|
1984
|
+
* @param id - the CX id
|
|
1985
|
+
* @returns the sink id
|
|
1986
|
+
*/
|
|
1987
|
+
sinkId(id) {
|
|
1988
|
+
const original = this.sinkIds.get(id);
|
|
1989
|
+
if (original !== void 0) {
|
|
1990
|
+
return original;
|
|
1991
|
+
}
|
|
1992
|
+
return this.options.ids === "keep" ? id : this.coercer.value(id);
|
|
1993
|
+
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Add a node to the sink (once per sink id) and map its CX id to the row.
|
|
1996
|
+
* @param id - the CX id
|
|
1997
|
+
* @returns the row, and whether the sink id was new
|
|
1998
|
+
*/
|
|
1999
|
+
addNode(id) {
|
|
2000
|
+
const sinkId = this.sinkId(id);
|
|
2001
|
+
const existing = this.sinkRows.get(sinkId);
|
|
2002
|
+
if (existing !== void 0) {
|
|
2003
|
+
this.nodeRows.set(id, existing);
|
|
2004
|
+
return { row: existing, added: false };
|
|
2005
|
+
}
|
|
2006
|
+
const row = this.sink.addNode(sinkId);
|
|
2007
|
+
this.sinkRows.set(sinkId, row);
|
|
2008
|
+
this.nodeRows.set(id, row);
|
|
2009
|
+
return { row, added: true };
|
|
2010
|
+
}
|
|
847
2011
|
/**
|
|
848
2012
|
* Record a long value beyond 2^53 (once).
|
|
849
2013
|
* @param name - the attribute
|
|
850
2014
|
* @param digits - the value
|
|
851
2015
|
*/
|
|
852
2016
|
precision(name, digits) {
|
|
2017
|
+
const overflow = !Number.isFinite(Number(digits));
|
|
853
2018
|
this.report.warnOnce(
|
|
854
2019
|
"precision",
|
|
855
2020
|
PRECISION_CODE,
|
|
856
|
-
`"${name}": ${digits} is beyond 2^53; stored as the nearest double`,
|
|
2021
|
+
overflow ? `"${name}": ${digits} is beyond the range of a double; stored as ${String(Number(digits))}` : `"${name}": ${digits} is beyond 2^53; stored as the nearest double`,
|
|
857
2022
|
{ element: name },
|
|
858
2023
|
`${PRECISION_CODE}:value`
|
|
859
2024
|
);
|
|
860
2025
|
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Report the keys of a node or edge element CX does not define, once per key.
|
|
2028
|
+
* @param record - the element
|
|
2029
|
+
* @param known - the keys it defines
|
|
2030
|
+
* @param what - nodes or edges
|
|
2031
|
+
* @param line - its line
|
|
2032
|
+
*/
|
|
2033
|
+
unknownKeys(record, known, what, line) {
|
|
2034
|
+
for (const key of Object.keys(record)) {
|
|
2035
|
+
if (!known.has(key)) {
|
|
2036
|
+
this.report.warnOnce(
|
|
2037
|
+
"unsupported",
|
|
2038
|
+
UNKNOWN_ELEMENT_CODE,
|
|
2039
|
+
`the ${what} key "${key}" is not defined by CX; its values are not read`,
|
|
2040
|
+
{ line, element: key },
|
|
2041
|
+
`${UNKNOWN_ELEMENT_CODE}:${what}:${key}`
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
/** Report cyTableColumn entries for a table CX does not define (once per table name). */
|
|
2047
|
+
checkTableColumns() {
|
|
2048
|
+
for (const { value, line } of aspect(this.doc, "cyTableColumn")) {
|
|
2049
|
+
if (isRecord(value) && typeof value.applies_to === "string" && !TABLES.has(value.applies_to)) {
|
|
2050
|
+
this.report.warnOnce(
|
|
2051
|
+
"unsupported",
|
|
2052
|
+
UNKNOWN_ELEMENT_CODE,
|
|
2053
|
+
`a cyTableColumn entry applies to "${value.applies_to}", which is not node_table, edge_table or network_table; ignored`,
|
|
2054
|
+
{ line, element: value.applies_to },
|
|
2055
|
+
`${UNKNOWN_ELEMENT_CODE}:table:${value.applies_to}`
|
|
2056
|
+
);
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
861
2060
|
/** Count the subnetwork members that name no node or edge. */
|
|
862
2061
|
checkMembers() {
|
|
863
2062
|
for (const id of this.plan.nodes ?? []) {
|
|
@@ -902,7 +2101,7 @@ class CxReader {
|
|
|
902
2101
|
this.report.warning(
|
|
903
2102
|
"unsupported",
|
|
904
2103
|
CX_ISSUE.ROOT_ONLY,
|
|
905
|
-
`${nodes} node(
|
|
2104
|
+
`${nodes} node${plural(nodes)} and ${edges} edge${plural(edges)} of the root network belong to no subnetwork (cySubNetworks); they are not read`,
|
|
906
2105
|
{ element: "cySubNetworks" }
|
|
907
2106
|
);
|
|
908
2107
|
}
|
|
@@ -930,7 +2129,7 @@ class CxReader {
|
|
|
930
2129
|
const declared = this.tableColumns(domain === "node" ? "node_table" : "edge_table");
|
|
931
2130
|
const names = /* @__PURE__ */ new Set([...declared.keys(), ...this.doc.attributes[domain].keys()]);
|
|
932
2131
|
for (const name of names) {
|
|
933
|
-
if (
|
|
2132
|
+
if (domain === "edge" && name === this.options.weightFrom || domain === "node" && name === ORIGINAL_ID_ATTRIBUTE && this.options.restoreMangledIds) {
|
|
934
2133
|
continue;
|
|
935
2134
|
}
|
|
936
2135
|
const types = [];
|
|
@@ -1023,7 +2222,15 @@ class CxReader {
|
|
|
1023
2222
|
continue;
|
|
1024
2223
|
}
|
|
1025
2224
|
const type = cxType(value.d);
|
|
1026
|
-
if (type
|
|
2225
|
+
if (type === null) {
|
|
2226
|
+
this.report.warnOnce(
|
|
2227
|
+
"unsupported",
|
|
2228
|
+
UNKNOWN_ATTR_TYPE_CODE,
|
|
2229
|
+
`cyTableColumn declares "${value.n}" with the data type ${shown(value.d)}, which CX does not define; its values are typed by their own d`,
|
|
2230
|
+
{ element: value.n },
|
|
2231
|
+
`${UNKNOWN_ATTR_TYPE_CODE}:table:${table}:${value.n}`
|
|
2232
|
+
);
|
|
2233
|
+
} else {
|
|
1027
2234
|
out.set(value.n, type);
|
|
1028
2235
|
}
|
|
1029
2236
|
}
|
|
@@ -1078,14 +2285,6 @@ class CxReader {
|
|
|
1078
2285
|
if (scope === 0) {
|
|
1079
2286
|
continue;
|
|
1080
2287
|
}
|
|
1081
|
-
const target = refId(value.po);
|
|
1082
|
-
const row = target === null ? void 0 : rows.get(this.key(target));
|
|
1083
|
-
if (row === void 0) {
|
|
1084
|
-
if (target === null || !root.has(target)) {
|
|
1085
|
-
this.dangle(`${domain} attribute target`);
|
|
1086
|
-
}
|
|
1087
|
-
continue;
|
|
1088
|
-
}
|
|
1089
2288
|
if (value.v === void 0) {
|
|
1090
2289
|
this.report.error(
|
|
1091
2290
|
"missing-value",
|
|
@@ -1098,23 +2297,33 @@ class CxReader {
|
|
|
1098
2297
|
);
|
|
1099
2298
|
continue;
|
|
1100
2299
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
2300
|
+
for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
|
|
2301
|
+
const target = refId(raw);
|
|
2302
|
+
const row = target === null ? void 0 : rows.get(target);
|
|
2303
|
+
if (row === void 0) {
|
|
2304
|
+
if (target === null || !root.has(target)) {
|
|
2305
|
+
this.dangle(`${domain} attribute target`);
|
|
2306
|
+
}
|
|
2307
|
+
continue;
|
|
2308
|
+
}
|
|
2309
|
+
if (value.v === null) {
|
|
2310
|
+
continue;
|
|
2311
|
+
}
|
|
2312
|
+
const previous = chosen.get(row);
|
|
2313
|
+
if (previous !== void 0 && previous.scope > scope) {
|
|
2314
|
+
continue;
|
|
2315
|
+
}
|
|
2316
|
+
if (previous?.scope === scope && JSON.stringify(previous.value) !== JSON.stringify(plainJson(value.v))) {
|
|
2317
|
+
this.report.warnOnce(
|
|
2318
|
+
"validation-error",
|
|
2319
|
+
DUPLICATE_ATTRIBUTE_CODE,
|
|
2320
|
+
`${domain} ${shown(raw)} has the attribute "${column.name}" twice; the later value wins`,
|
|
2321
|
+
{ line, element: column.name },
|
|
2322
|
+
`${DUPLICATE_ATTRIBUTE_CODE}:${domain}:${column.name}`
|
|
2323
|
+
);
|
|
2324
|
+
}
|
|
2325
|
+
chosen.set(row, { scope, value: value.v, d: value.d, line });
|
|
1116
2326
|
}
|
|
1117
|
-
chosen.set(row, { scope, value: value.v, d: value.d, line });
|
|
1118
2327
|
}
|
|
1119
2328
|
for (const [row, { value, d, line }] of chosen) {
|
|
1120
2329
|
this.writeCell(domain, column, row, value, d, line);
|
|
@@ -1160,6 +2369,15 @@ class CxReader {
|
|
|
1160
2369
|
}
|
|
1161
2370
|
let { handle } = column;
|
|
1162
2371
|
if (this.isStructural(domain, column.name)) {
|
|
2372
|
+
if (Array.isArray(raw)) {
|
|
2373
|
+
this.report.error(
|
|
2374
|
+
"validation-error",
|
|
2375
|
+
BAD_VALUE_CODE,
|
|
2376
|
+
`${element}: ${shown(raw)} is a list; "${column.name}" holds one string, the cell is unset`,
|
|
2377
|
+
{ line, element: column.name }
|
|
2378
|
+
);
|
|
2379
|
+
return;
|
|
2380
|
+
}
|
|
1163
2381
|
handle = this.coreColumn(domain, column.name);
|
|
1164
2382
|
const prior = this.coreValue(domain, row, column.name);
|
|
1165
2383
|
if (prior !== void 0 && prior !== value) {
|
|
@@ -1229,7 +2447,8 @@ class CxReader {
|
|
|
1229
2447
|
// ------------------------------------------------------------ nodes, groups, layout
|
|
1230
2448
|
/** Read the nodes of this graph. */
|
|
1231
2449
|
readNodes() {
|
|
1232
|
-
const { report
|
|
2450
|
+
const { report } = this;
|
|
2451
|
+
const originals = this.originalIds();
|
|
1233
2452
|
for (const held of aspect(this.doc, "nodes")) {
|
|
1234
2453
|
this.checkAbort();
|
|
1235
2454
|
const { value, line } = held;
|
|
@@ -1251,38 +2470,64 @@ class CxReader {
|
|
|
1251
2470
|
report.counts.skippedNodes++;
|
|
1252
2471
|
continue;
|
|
1253
2472
|
}
|
|
2473
|
+
this.unknownKeys(value, NODE_KEYS, "node", line);
|
|
1254
2474
|
const element = `node ${shown(value["@id"])}`;
|
|
1255
|
-
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element);
|
|
2475
|
+
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line);
|
|
1256
2476
|
if (id === null) {
|
|
1257
2477
|
report.counts.skippedNodes++;
|
|
1258
2478
|
continue;
|
|
1259
2479
|
}
|
|
1260
|
-
if (!this.inGraph(
|
|
2480
|
+
if (!this.inGraph(id)) {
|
|
1261
2481
|
continue;
|
|
1262
2482
|
}
|
|
1263
2483
|
let row = this.nodeRows.get(id);
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
} else {
|
|
2484
|
+
let added = false;
|
|
2485
|
+
if (row === void 0) {
|
|
2486
|
+
const original = originals.get(refId(value["@id"]) ?? id);
|
|
2487
|
+
if (original !== void 0) {
|
|
2488
|
+
this.idType = "mixed";
|
|
2489
|
+
this.sinkIds.set(id, original);
|
|
2490
|
+
}
|
|
1272
2491
|
try {
|
|
1273
|
-
row =
|
|
2492
|
+
({ row, added } = this.addNode(id));
|
|
1274
2493
|
} catch (err) {
|
|
1275
2494
|
report.recordError(err, { line, element });
|
|
1276
2495
|
report.counts.skippedNodes++;
|
|
1277
2496
|
continue;
|
|
1278
2497
|
}
|
|
1279
|
-
|
|
2498
|
+
}
|
|
2499
|
+
if (added) {
|
|
1280
2500
|
report.counts.nodes++;
|
|
2501
|
+
} else {
|
|
2502
|
+
report.warning(
|
|
2503
|
+
"merged",
|
|
2504
|
+
DUPLICATE_NODE_CODE,
|
|
2505
|
+
`${element} is declared more than once; its fields are merged (the later values win)`,
|
|
2506
|
+
{ line, element }
|
|
2507
|
+
);
|
|
1281
2508
|
}
|
|
1282
2509
|
this.writeCore("node", row, "name", value.n, element, line);
|
|
1283
2510
|
this.writeCore("node", row, "represents", value.r, element, line);
|
|
1284
2511
|
}
|
|
1285
2512
|
}
|
|
2513
|
+
/**
|
|
2514
|
+
* The original ids the exporter's `sanitizeIds: "mangle"` kept in the `graphty:originalId`
|
|
2515
|
+
* attribute, when they are to be restored.
|
|
2516
|
+
* @returns CX node id -> original id
|
|
2517
|
+
*/
|
|
2518
|
+
originalIds() {
|
|
2519
|
+
const out = /* @__PURE__ */ new Map();
|
|
2520
|
+
if (!this.options.restoreMangledIds) {
|
|
2521
|
+
return out;
|
|
2522
|
+
}
|
|
2523
|
+
for (const { value } of this.doc.attributes.node.get(ORIGINAL_ID_ATTRIBUTE) ?? []) {
|
|
2524
|
+
const po = isRecord(value) && this.scopePeek(value.s) !== 0 ? refId(value.po) : null;
|
|
2525
|
+
if (po !== null && isRecord(value) && typeof value.v === "string") {
|
|
2526
|
+
out.set(po, value.v);
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
return out;
|
|
2530
|
+
}
|
|
1286
2531
|
/**
|
|
1287
2532
|
* Write a core string field (n, r, i).
|
|
1288
2533
|
* @param domain - node or edge
|
|
@@ -1334,30 +2579,40 @@ class CxReader {
|
|
|
1334
2579
|
}
|
|
1335
2580
|
const parents = /* @__PURE__ */ new Map();
|
|
1336
2581
|
const collapsed = [];
|
|
1337
|
-
for (const
|
|
1338
|
-
|
|
2582
|
+
for (const held of groups) {
|
|
2583
|
+
const group = structuralId(held, "cyGroups", "@id", this.report);
|
|
2584
|
+
if (group === null) {
|
|
1339
2585
|
continue;
|
|
1340
2586
|
}
|
|
1341
|
-
const id =
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
2587
|
+
const { value, id } = group;
|
|
2588
|
+
const { line } = held;
|
|
2589
|
+
if (value.nodes !== void 0 && value.nodes !== null && !Array.isArray(value.nodes)) {
|
|
2590
|
+
this.report.error(
|
|
2591
|
+
"validation-error",
|
|
2592
|
+
BAD_VALUE_CODE,
|
|
2593
|
+
`group ${String(id)}: nodes is ${shown(value.nodes)}, not a list of node ids; no member is read`,
|
|
2594
|
+
{ line, element: String(id) }
|
|
2595
|
+
);
|
|
2596
|
+
}
|
|
2597
|
+
if (value.collapsed !== void 0 && value.collapsed !== null && typeof value.collapsed !== "boolean") {
|
|
2598
|
+
this.report.error(
|
|
2599
|
+
"validation-error",
|
|
2600
|
+
BAD_VALUE_CODE,
|
|
2601
|
+
`group ${String(id)}: collapsed is ${shown(value.collapsed)}, not a boolean; unset`,
|
|
2602
|
+
{ line, element: String(id) }
|
|
2603
|
+
);
|
|
1348
2604
|
}
|
|
1349
|
-
let groupRow = this.nodeRows.get(
|
|
2605
|
+
let groupRow = this.nodeRows.get(id);
|
|
1350
2606
|
if (groupRow === void 0) {
|
|
1351
2607
|
if (this.rootNodes.has(id) || !this.hasMemberIn(value.nodes)) {
|
|
1352
2608
|
continue;
|
|
1353
2609
|
}
|
|
1354
2610
|
try {
|
|
1355
|
-
groupRow = this.
|
|
2611
|
+
groupRow = this.addNode(id).row;
|
|
1356
2612
|
} catch (err) {
|
|
1357
2613
|
this.report.recordError(err, { line, element: String(id) });
|
|
1358
2614
|
continue;
|
|
1359
2615
|
}
|
|
1360
|
-
this.nodeRows.set(this.key(id), groupRow);
|
|
1361
2616
|
this.report.counts.nodes++;
|
|
1362
2617
|
this.report.warning(
|
|
1363
2618
|
"coercion",
|
|
@@ -1374,7 +2629,7 @@ class CxReader {
|
|
|
1374
2629
|
}
|
|
1375
2630
|
for (const raw of Array.isArray(value.nodes) ? value.nodes : []) {
|
|
1376
2631
|
const member = refId(raw);
|
|
1377
|
-
const row = member === null ? void 0 : this.nodeRows.get(
|
|
2632
|
+
const row = member === null ? void 0 : this.nodeRows.get(member);
|
|
1378
2633
|
if (row === void 0) {
|
|
1379
2634
|
if (member === null || !this.rootNodes.has(member)) {
|
|
1380
2635
|
this.report.error(
|
|
@@ -1428,7 +2683,7 @@ class CxReader {
|
|
|
1428
2683
|
hasMemberIn(raw) {
|
|
1429
2684
|
return Array.isArray(raw) && raw.some((item) => {
|
|
1430
2685
|
const id = refId(item);
|
|
1431
|
-
return id !== null && this.nodeRows.has(
|
|
2686
|
+
return id !== null && this.nodeRows.has(id);
|
|
1432
2687
|
});
|
|
1433
2688
|
}
|
|
1434
2689
|
/**
|
|
@@ -1489,6 +2744,12 @@ class CxReader {
|
|
|
1489
2744
|
for (const { value, line } of entries) {
|
|
1490
2745
|
this.checkAbort();
|
|
1491
2746
|
if (!isRecord(value)) {
|
|
2747
|
+
this.report.error(
|
|
2748
|
+
"parse-error",
|
|
2749
|
+
BAD_ASPECT_BLOCK_CODE,
|
|
2750
|
+
`a cartesianLayout element is ${shown(value)}, not an object; skipped`,
|
|
2751
|
+
{ line, element: "cartesianLayout" }
|
|
2752
|
+
);
|
|
1492
2753
|
continue;
|
|
1493
2754
|
}
|
|
1494
2755
|
const view = value.view === void 0 || value.view === null ? null : refId(value.view);
|
|
@@ -1509,14 +2770,31 @@ class CxReader {
|
|
|
1509
2770
|
);
|
|
1510
2771
|
continue;
|
|
1511
2772
|
}
|
|
2773
|
+
if (!fitsF32(value.x) || !fitsF32(value.y)) {
|
|
2774
|
+
this.report.error(
|
|
2775
|
+
"validation-error",
|
|
2776
|
+
BAD_VALUE_CODE,
|
|
2777
|
+
`a cartesianLayout element for node ${shown(value.node)} has the coordinates ${String(value.x)}, ${String(value.y)}, beyond what the f32 position column holds; skipped`,
|
|
2778
|
+
{ line, element: "cartesianLayout" }
|
|
2779
|
+
);
|
|
2780
|
+
continue;
|
|
2781
|
+
}
|
|
1512
2782
|
const node = refId(value.node);
|
|
1513
|
-
const row = node === null ? void 0 : this.nodeRows.get(
|
|
2783
|
+
const row = node === null ? void 0 : this.nodeRows.get(node);
|
|
1514
2784
|
if (row === void 0) {
|
|
1515
2785
|
if (node === null || !this.rootNodes.has(node)) {
|
|
1516
2786
|
this.dangle("layout entry");
|
|
1517
2787
|
}
|
|
1518
2788
|
continue;
|
|
1519
2789
|
}
|
|
2790
|
+
if (value.z !== void 0 && value.z !== null && typeof value.z !== "number") {
|
|
2791
|
+
this.report.error(
|
|
2792
|
+
"validation-error",
|
|
2793
|
+
BAD_VALUE_CODE,
|
|
2794
|
+
`a cartesianLayout element for node ${shown(value.node)} has the z ${shown(value.z)}, not a number; z is unset`,
|
|
2795
|
+
{ line, element: "cartesianLayout" }
|
|
2796
|
+
);
|
|
2797
|
+
}
|
|
1520
2798
|
const key = `${slot}:${row}`;
|
|
1521
2799
|
if (seen.has(key)) {
|
|
1522
2800
|
this.report.warnOnce(
|
|
@@ -1588,6 +2866,7 @@ class CxReader {
|
|
|
1588
2866
|
readEdges() {
|
|
1589
2867
|
const { report, sink } = this;
|
|
1590
2868
|
const members = this.plan.edges;
|
|
2869
|
+
this.weights = this.edgeWeights();
|
|
1591
2870
|
for (const held of aspect(this.doc, "edges")) {
|
|
1592
2871
|
this.checkAbort();
|
|
1593
2872
|
const { value, line } = held;
|
|
@@ -1609,13 +2888,14 @@ class CxReader {
|
|
|
1609
2888
|
report.counts.skippedEdges++;
|
|
1610
2889
|
continue;
|
|
1611
2890
|
}
|
|
2891
|
+
this.unknownKeys(value, EDGE_KEYS, "edge", line);
|
|
1612
2892
|
const element = `edge ${shown(value["@id"])}`;
|
|
1613
|
-
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, true);
|
|
2893
|
+
const id = this.idOf(value["@id"], (held.inexact & 1) !== 0, element, line, true);
|
|
1614
2894
|
if (id === null) {
|
|
1615
2895
|
report.counts.skippedEdges++;
|
|
1616
2896
|
continue;
|
|
1617
2897
|
}
|
|
1618
|
-
if (members !== null && !members.has(
|
|
2898
|
+
if (members !== null && !members.has(id)) {
|
|
1619
2899
|
continue;
|
|
1620
2900
|
}
|
|
1621
2901
|
if (value.s === void 0 || value.s === null || value.t === void 0 || value.t === null) {
|
|
@@ -1631,8 +2911,8 @@ class CxReader {
|
|
|
1631
2911
|
report.counts.skippedEdges++;
|
|
1632
2912
|
continue;
|
|
1633
2913
|
}
|
|
1634
|
-
const s = this.idOf(value.s, (held.inexact & 2) !== 0, element);
|
|
1635
|
-
const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element);
|
|
2914
|
+
const s = this.idOf(value.s, (held.inexact & 2) !== 0, element, line);
|
|
2915
|
+
const t = s === null ? null : this.idOf(value.t, (held.inexact & 4) !== 0, element, line);
|
|
1636
2916
|
if (s === null || t === null) {
|
|
1637
2917
|
report.counts.skippedEdges++;
|
|
1638
2918
|
continue;
|
|
@@ -1662,7 +2942,7 @@ class CxReader {
|
|
|
1662
2942
|
const before = sink.edgeCount;
|
|
1663
2943
|
let edge;
|
|
1664
2944
|
try {
|
|
1665
|
-
edge = this.direction.addEdge(s, t, "directed", weight, { line, element });
|
|
2945
|
+
edge = this.direction.addEdge(this.sinkId(s), this.sinkId(t), "directed", weight, { line, element });
|
|
1666
2946
|
} catch (err) {
|
|
1667
2947
|
report.recordError(err, { line, element });
|
|
1668
2948
|
report.counts.skippedEdges++;
|
|
@@ -1704,34 +2984,76 @@ class CxReader {
|
|
|
1704
2984
|
return false;
|
|
1705
2985
|
}
|
|
1706
2986
|
try {
|
|
1707
|
-
|
|
2987
|
+
if (this.addNode(id).added) {
|
|
2988
|
+
this.report.counts.nodes++;
|
|
2989
|
+
}
|
|
1708
2990
|
} catch (err) {
|
|
1709
2991
|
this.report.recordError(err, { line, element });
|
|
1710
2992
|
return false;
|
|
1711
2993
|
}
|
|
1712
|
-
this.report.counts.nodes++;
|
|
1713
2994
|
return true;
|
|
1714
2995
|
}
|
|
1715
2996
|
/**
|
|
1716
|
-
* The
|
|
1717
|
-
*
|
|
1718
|
-
*
|
|
2997
|
+
* The weightFrom attribute values by edge id: this subnetwork's own value beats the unscoped
|
|
2998
|
+
* one, and of two in one scope the later wins (W_DUPLICATE_ATTRIBUTE when they differ), as
|
|
2999
|
+
* writeAttributes() rules every other attribute.
|
|
3000
|
+
* @returns the values (parsed JSON) by edge id
|
|
1719
3001
|
*/
|
|
1720
|
-
|
|
3002
|
+
edgeWeights() {
|
|
1721
3003
|
const { weightFrom } = this.options;
|
|
3004
|
+
const chosen = /* @__PURE__ */ new Map();
|
|
1722
3005
|
if (weightFrom === null) {
|
|
1723
|
-
return
|
|
3006
|
+
return /* @__PURE__ */ new Map();
|
|
1724
3007
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
const po = refId(value.po);
|
|
1730
|
-
if (po !== null && value.v !== void 0 && value.v !== null) {
|
|
1731
|
-
this.weights.set(this.key(po), plainJson(value.v));
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
3008
|
+
for (const { value, line } of this.doc.attributes.edge.get(weightFrom) ?? []) {
|
|
3009
|
+
const scope = isRecord(value) ? this.scopePeek(value.s) : 0;
|
|
3010
|
+
if (!isRecord(value) || scope === 0 || value.v === void 0 || value.v === null) {
|
|
3011
|
+
continue;
|
|
1734
3012
|
}
|
|
3013
|
+
const v = plainJson(value.v);
|
|
3014
|
+
for (const raw of Array.isArray(value.po) ? value.po : [value.po]) {
|
|
3015
|
+
this.chooseWeight(chosen, raw, { scope, value: v }, line, weightFrom);
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
return new Map([...chosen].map(([id, { value }]) => [id, value]));
|
|
3019
|
+
}
|
|
3020
|
+
/**
|
|
3021
|
+
* Keep one weight attribute element's value for one edge, unless a more specific scope already
|
|
3022
|
+
* gave it one; a second value in the same scope wins, with a warning.
|
|
3023
|
+
* @param chosen - the value chosen so far per edge
|
|
3024
|
+
* @param raw - the edge reference as written
|
|
3025
|
+
* @param weight - the value and the scope it applies in
|
|
3026
|
+
* @param weight.scope - the scope: higher is more specific
|
|
3027
|
+
* @param weight.value - the value
|
|
3028
|
+
* @param line - the element's line
|
|
3029
|
+
* @param weightFrom - the attribute name
|
|
3030
|
+
*/
|
|
3031
|
+
chooseWeight(chosen, raw, weight, line, weightFrom) {
|
|
3032
|
+
const po = refId(raw);
|
|
3033
|
+
const previous = po === null ? void 0 : chosen.get(po);
|
|
3034
|
+
if (po === null || previous !== void 0 && previous.scope > weight.scope) {
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
if (previous?.scope === weight.scope && JSON.stringify(previous.value) !== JSON.stringify(weight.value)) {
|
|
3038
|
+
this.report.warnOnce(
|
|
3039
|
+
"validation-error",
|
|
3040
|
+
DUPLICATE_ATTRIBUTE_CODE,
|
|
3041
|
+
`edge ${shown(raw)} has the attribute "${weightFrom}" twice; the later value wins`,
|
|
3042
|
+
{ line, element: weightFrom },
|
|
3043
|
+
`${DUPLICATE_ATTRIBUTE_CODE}:edge:${weightFrom}`
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
chosen.set(po, weight);
|
|
3047
|
+
}
|
|
3048
|
+
/**
|
|
3049
|
+
* The weight of an edge: its weightFrom attribute, parsed as a number (the declared d is not
|
|
3050
|
+
* consulted: a value that is not a number is E_INVALID_WEIGHT whatever its type says).
|
|
3051
|
+
* @param id - the edge id
|
|
3052
|
+
* @returns the weight, or undefined; E_INVALID_WEIGHT for a non-number
|
|
3053
|
+
*/
|
|
3054
|
+
weightOf(id) {
|
|
3055
|
+
if (this.options.weightFrom === null) {
|
|
3056
|
+
return void 0;
|
|
1735
3057
|
}
|
|
1736
3058
|
const raw = this.weights.get(id);
|
|
1737
3059
|
if (typeof raw === "string" && isNullText(raw)) {
|
|
@@ -1745,17 +3067,38 @@ class CxReader {
|
|
|
1745
3067
|
for (const [name, elements] of this.doc.attributes.network) {
|
|
1746
3068
|
let chosen = null;
|
|
1747
3069
|
let chosenScope = 0;
|
|
1748
|
-
for (const { value } of elements) {
|
|
3070
|
+
for (const { value, line } of elements) {
|
|
1749
3071
|
if (!isRecord(value)) {
|
|
1750
3072
|
continue;
|
|
1751
3073
|
}
|
|
1752
3074
|
const scope = this.scopeOf(value.s);
|
|
1753
|
-
if (scope
|
|
3075
|
+
if (scope === 0) {
|
|
3076
|
+
continue;
|
|
3077
|
+
}
|
|
3078
|
+
if (value.v === void 0) {
|
|
3079
|
+
this.report.error(
|
|
3080
|
+
"missing-value",
|
|
3081
|
+
BAD_VALUE_CODE,
|
|
3082
|
+
`a networkAttributes element for "${name}" has no v; skipped`,
|
|
3083
|
+
{ line, element: name }
|
|
3084
|
+
);
|
|
3085
|
+
continue;
|
|
3086
|
+
}
|
|
3087
|
+
if (chosen !== null && scope === chosenScope && JSON.stringify(plainJson(chosen.v)) !== JSON.stringify(plainJson(value.v))) {
|
|
3088
|
+
this.report.warnOnce(
|
|
3089
|
+
"validation-error",
|
|
3090
|
+
DUPLICATE_ATTRIBUTE_CODE,
|
|
3091
|
+
`the network attribute "${name}" is given twice; the later value wins`,
|
|
3092
|
+
{ line, element: name },
|
|
3093
|
+
`${DUPLICATE_ATTRIBUTE_CODE}:network:${name}`
|
|
3094
|
+
);
|
|
3095
|
+
}
|
|
3096
|
+
if (scope >= chosenScope) {
|
|
1754
3097
|
chosen = value;
|
|
1755
3098
|
chosenScope = scope;
|
|
1756
3099
|
}
|
|
1757
3100
|
}
|
|
1758
|
-
if (chosen
|
|
3101
|
+
if (chosen?.v === void 0 || chosen.v === null) {
|
|
1759
3102
|
continue;
|
|
1760
3103
|
}
|
|
1761
3104
|
const type = cxType(chosen.d);
|
|
@@ -1830,7 +3173,7 @@ class CxReader {
|
|
|
1830
3173
|
const domain = value.properties_of === "nodes" ? "node" : "edge";
|
|
1831
3174
|
const target = refId(value.applies_to);
|
|
1832
3175
|
const rows = domain === "node" ? this.nodeRows : this.edgeRows;
|
|
1833
|
-
const row = target === null ? void 0 : rows.get(
|
|
3176
|
+
const row = target === null ? void 0 : rows.get(target);
|
|
1834
3177
|
if (row === void 0) {
|
|
1835
3178
|
if (target === null || !(domain === "node" ? this.rootNodes : this.rootEdges).has(target)) {
|
|
1836
3179
|
this.dangle(`${domain} visual property entry`);
|
|
@@ -1863,7 +3206,7 @@ class CxReader {
|
|
|
1863
3206
|
name: property,
|
|
1864
3207
|
dtype: "string",
|
|
1865
3208
|
nullable: true,
|
|
1866
|
-
origin: { format: CX_FORMAT, id:
|
|
3209
|
+
origin: { format: CX_FORMAT, id: property, namespace: CX_BYPASS_NAMESPACE }
|
|
1867
3210
|
},
|
|
1868
3211
|
this.report
|
|
1869
3212
|
);
|
|
@@ -1888,15 +3231,15 @@ class CxReader {
|
|
|
1888
3231
|
}
|
|
1889
3232
|
if (defaults + mappings + dependencies + tableStyles > 0) {
|
|
1890
3233
|
const parts = [
|
|
1891
|
-
`cyVisualProperties: ${defaults} default(
|
|
3234
|
+
`cyVisualProperties: ${defaults} default${plural(defaults)}, ${mappings} mapping${plural(mappings)}, ${dependencies} dependenc(ies)`
|
|
1892
3235
|
];
|
|
1893
3236
|
if (tableStyles > 0) {
|
|
1894
|
-
parts.push(`${tableStyles} table style element(
|
|
3237
|
+
parts.push(`${tableStyles} table style element${plural(tableStyles)}`);
|
|
1895
3238
|
}
|
|
1896
3239
|
this.report.warning(
|
|
1897
3240
|
"unsupported",
|
|
1898
3241
|
STYLES_NOT_IMPORTED_CODE,
|
|
1899
|
-
`the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx
|
|
3242
|
+
`the file's style rules are not applied (${parts.join("; ")}); they are kept in meta.extra.cx`,
|
|
1900
3243
|
{ element: "cyVisualProperties" }
|
|
1901
3244
|
);
|
|
1902
3245
|
}
|
|
@@ -1924,7 +3267,7 @@ class CxReader {
|
|
|
1924
3267
|
continue;
|
|
1925
3268
|
}
|
|
1926
3269
|
const po = refId(value.po);
|
|
1927
|
-
const row = po === null ? void 0 : this.nodeRows.get(
|
|
3270
|
+
const row = po === null ? void 0 : this.nodeRows.get(po);
|
|
1928
3271
|
if (row === void 0) {
|
|
1929
3272
|
if (po === null || !this.rootNodes.has(po)) {
|
|
1930
3273
|
this.dangle("functionTerms entry");
|
|
@@ -1942,8 +3285,8 @@ class CxReader {
|
|
|
1942
3285
|
}
|
|
1943
3286
|
const node = refId(value.node);
|
|
1944
3287
|
const edge = refId(value.edge);
|
|
1945
|
-
const row = node === null ? void 0 : this.nodeRows.get(
|
|
1946
|
-
const edgeRow = edge === null ? void 0 : this.edgeRows.get(
|
|
3288
|
+
const row = node === null ? void 0 : this.nodeRows.get(node);
|
|
3289
|
+
const edgeRow = edge === null ? void 0 : this.edgeRows.get(edge);
|
|
1947
3290
|
if (row === void 0 || edgeRow === void 0) {
|
|
1948
3291
|
if (node === null || !this.rootNodes.has(node) || edge === null || !this.rootEdges.has(edge)) {
|
|
1949
3292
|
this.dangle("reifiedEdges entry");
|
|
@@ -1981,16 +3324,35 @@ class CxReader {
|
|
|
1981
3324
|
return;
|
|
1982
3325
|
}
|
|
1983
3326
|
const fields = [...new Set(records.flatMap((r) => Object.keys(r)).filter((k) => k !== "@id"))];
|
|
3327
|
+
const names = /* @__PURE__ */ new Set(["id"]);
|
|
1984
3328
|
const decls = [
|
|
1985
3329
|
{ name: "id", dtype: "f64", nullable: true },
|
|
1986
|
-
...fields.map((field) =>
|
|
3330
|
+
...fields.map((field) => {
|
|
3331
|
+
const column = uniqueColumnName(field, null, (n) => names.has(n));
|
|
3332
|
+
names.add(column);
|
|
3333
|
+
if (column !== field) {
|
|
3334
|
+
this.report.warning(
|
|
3335
|
+
"coercion",
|
|
3336
|
+
COLUMN_RENAMED_CODE,
|
|
3337
|
+
`${name} column "${field}" renamed to "${column}": the table's own id column holds that name`,
|
|
3338
|
+
{ element: field }
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3341
|
+
return { name: column, dtype: "json", nullable: true };
|
|
3342
|
+
})
|
|
1987
3343
|
];
|
|
1988
3344
|
const table = this.sink.addExtensionTable(name, decls);
|
|
3345
|
+
const precision = (digits) => {
|
|
3346
|
+
this.precision(name, digits);
|
|
3347
|
+
};
|
|
1989
3348
|
for (const record of records) {
|
|
1990
3349
|
const id = refId(record["@id"]);
|
|
3350
|
+
if (typeof id === "string") {
|
|
3351
|
+
precision(id);
|
|
3352
|
+
}
|
|
1991
3353
|
this.sink.addExtensionRow(table, [
|
|
1992
3354
|
id === null ? null : Number(id),
|
|
1993
|
-
...fields.map((field) => record[field] === void 0 ? null : plainJson(record[field]))
|
|
3355
|
+
...fields.map((field) => record[field] === void 0 ? null : plainJson(record[field], precision))
|
|
1994
3356
|
]);
|
|
1995
3357
|
}
|
|
1996
3358
|
}
|
|
@@ -2012,7 +3374,7 @@ class CxReader {
|
|
|
2012
3374
|
const ids = (Array.isArray(value[column]) ? value[column] : []).map(refId).filter((id) => id !== null).map(Number);
|
|
2013
3375
|
for (const raw of targets) {
|
|
2014
3376
|
const target = refId(raw);
|
|
2015
|
-
const row = target === null ? void 0 : rows.get(
|
|
3377
|
+
const row = target === null ? void 0 : rows.get(target);
|
|
2016
3378
|
if (row === void 0) {
|
|
2017
3379
|
if (target === null || !root.has(target)) {
|
|
2018
3380
|
this.dangle(`${aspectName} entry`);
|
|
@@ -2071,12 +3433,24 @@ class CxReader {
|
|
|
2071
3433
|
};
|
|
2072
3434
|
const extra = Object.fromEntries(this.doc.kept);
|
|
2073
3435
|
const groups = aspect(this.doc, "cyGroups").map((h) => plainJson(h.value));
|
|
3436
|
+
const own = {};
|
|
2074
3437
|
if (groups.length > 0) {
|
|
2075
|
-
|
|
3438
|
+
own.groups = groups;
|
|
2076
3439
|
}
|
|
2077
3440
|
if (this.plan.subnetwork !== null) {
|
|
2078
|
-
|
|
3441
|
+
own.subnetwork = this.plan.subnetwork;
|
|
3442
|
+
}
|
|
3443
|
+
for (const key of OWN_EXTRA_KEYS) {
|
|
3444
|
+
if (key in own && this.doc.kept.has(key)) {
|
|
3445
|
+
this.report.warning(
|
|
3446
|
+
"unsupported",
|
|
3447
|
+
UNKNOWN_ELEMENT_CODE,
|
|
3448
|
+
`the aspect "${key}" is not kept: meta.extra.cx.${key} holds the importer's own ${key === "groups" ? "cyGroups" : "subnetwork"} data`,
|
|
3449
|
+
{ element: key }
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
2079
3452
|
}
|
|
3453
|
+
Object.assign(extra, own);
|
|
2080
3454
|
const { weightFrom } = this.options;
|
|
2081
3455
|
const name = this.plan.name ?? describe("name");
|
|
2082
3456
|
const description = describe("description");
|
|
@@ -2098,7 +3472,7 @@ async function run(input, options, sinkFor, pick) {
|
|
|
2098
3472
|
const first = new ImportReportBuilder(CX_FORMAT, resolved.errorLimit);
|
|
2099
3473
|
reportUnusedOptions(options, first, USED_OPTIONS);
|
|
2100
3474
|
const doc = await readDocument(input, first, resolved);
|
|
2101
|
-
const plans = graphPlans(doc);
|
|
3475
|
+
const plans = graphPlans(doc, first);
|
|
2102
3476
|
const chosen = pick(plans, first);
|
|
2103
3477
|
const reports = [];
|
|
2104
3478
|
chosen.forEach((index, k) => {
|
|
@@ -2113,6 +3487,7 @@ async function run(input, options, sinkFor, pick) {
|
|
|
2113
3487
|
}
|
|
2114
3488
|
const cxImporter = Object.freeze({
|
|
2115
3489
|
format: CX_FORMAT,
|
|
3490
|
+
options: Object.freeze(["zAs"]),
|
|
2116
3491
|
extensions: Object.freeze([".cx"]),
|
|
2117
3492
|
mimeTypes: Object.freeze(["application/json"]),
|
|
2118
3493
|
/**
|
|
@@ -2123,8 +3498,7 @@ const cxImporter = Object.freeze({
|
|
|
2123
3498
|
* @returns the confidence
|
|
2124
3499
|
*/
|
|
2125
3500
|
sniff(head) {
|
|
2126
|
-
const
|
|
2127
|
-
const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(text);
|
|
3501
|
+
const match = /^\s*\[\s*\{\s*"((?:[^"\\]|\\.)*)"\s*:/.exec(headText(head));
|
|
2128
3502
|
if (match === null) {
|
|
2129
3503
|
return 0;
|
|
2130
3504
|
}
|
|
@@ -2152,11 +3526,11 @@ const cxImporter = Object.freeze({
|
|
|
2152
3526
|
options,
|
|
2153
3527
|
first
|
|
2154
3528
|
);
|
|
2155
|
-
if (plans.length > 1) {
|
|
3529
|
+
if (plans.length > 1 && !graphChosen(options)) {
|
|
2156
3530
|
first.warning(
|
|
2157
3531
|
"unsupported",
|
|
2158
3532
|
MULTIPLE_GRAPHS_CODE,
|
|
2159
|
-
`the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (
|
|
3533
|
+
`the collection holds ${plans.length} subnetworks; graph ${index} is read and ${plans.length - 1} skipped (importAllGraphs() reads every one)`
|
|
2160
3534
|
);
|
|
2161
3535
|
}
|
|
2162
3536
|
return [index];
|
|
@@ -2187,18 +3561,21 @@ const cxImporter = Object.freeze({
|
|
|
2187
3561
|
const doc = await readDocument(input, report, resolved);
|
|
2188
3562
|
const nodes = aspect(doc, "nodes").length;
|
|
2189
3563
|
const edges = aspect(doc, "edges").length;
|
|
2190
|
-
return graphPlans(doc).map(
|
|
2191
|
-
(
|
|
2192
|
-
index:
|
|
2193
|
-
name:
|
|
2194
|
-
nodes: memberCount(
|
|
2195
|
-
edges: memberCount(
|
|
3564
|
+
return graphPlans(doc, report).map(
|
|
3565
|
+
(plan2) => Object.freeze({
|
|
3566
|
+
index: plan2.index,
|
|
3567
|
+
name: plan2.name,
|
|
3568
|
+
nodes: memberCount(plan2.nodes, nodes),
|
|
3569
|
+
edges: memberCount(plan2.edges, edges)
|
|
2196
3570
|
})
|
|
2197
3571
|
);
|
|
2198
3572
|
}
|
|
2199
3573
|
});
|
|
2200
3574
|
export {
|
|
2201
|
-
|
|
2202
|
-
|
|
3575
|
+
CX_CAPABILITIES as C,
|
|
3576
|
+
CX_ISSUE as a,
|
|
3577
|
+
CX_LOSS as b,
|
|
3578
|
+
cxExporter as c,
|
|
3579
|
+
cxImporter as d
|
|
2203
3580
|
};
|
|
2204
|
-
//# sourceMappingURL=importer-
|
|
3581
|
+
//# sourceMappingURL=importer-DCjOvO7U.js.map
|