@graphty/graph-io 0.0.0 → 0.2.1
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/LICENSE +21 -0
- package/README.md +250 -28
- package/dist/chunks/children-CL3Cy0ez.js +238 -0
- package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
- package/dist/chunks/escape-DyI8JofU.js +938 -0
- package/dist/chunks/escape-DyI8JofU.js.map +1 -0
- package/dist/chunks/importer-CQnJuWJw.js +2987 -0
- package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
- package/dist/chunks/importer-CpCpfbxr.js +2015 -0
- package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
- package/dist/chunks/importer-DbnGYr3_.js +2342 -0
- package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
- package/dist/chunks/importer-GozH8DkN.js +3050 -0
- package/dist/chunks/importer-GozH8DkN.js.map +1 -0
- package/dist/chunks/records-CGpxszm1.js +605 -0
- package/dist/chunks/records-CGpxszm1.js.map +1 -0
- package/dist/chunks/text-CajMdVFy.js +189 -0
- package/dist/chunks/text-CajMdVFy.js.map +1 -0
- package/dist/chunks/writer-DxSKC7TL.js +2842 -0
- package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
- package/dist/csv.d.ts +1 -0
- package/dist/csv.js +1702 -0
- package/dist/csv.js.map +1 -0
- package/dist/dot.d.ts +1 -0
- package/dist/dot.js +8 -0
- package/dist/dot.js.map +1 -0
- package/dist/gexf.d.ts +1 -0
- package/dist/gexf.js +3466 -0
- package/dist/gexf.js.map +1 -0
- package/dist/gml.d.ts +1 -0
- package/dist/gml.js +2647 -0
- package/dist/gml.js.map +1 -0
- package/dist/graph-io.d.ts +1 -0
- package/dist/graph-io.js +790 -0
- package/dist/graph-io.js.map +1 -0
- package/dist/graphml.d.ts +1 -0
- package/dist/graphml.js +8 -0
- package/dist/graphml.js.map +1 -0
- package/dist/json.d.ts +1 -0
- package/dist/json.js +11 -0
- package/dist/json.js.map +1 -0
- package/dist/neo4j.d.ts +1 -0
- package/dist/neo4j.js +2046 -0
- package/dist/neo4j.js.map +1 -0
- package/dist/pajek.d.ts +1 -0
- package/dist/pajek.js +8 -0
- package/dist/pajek.js.map +1 -0
- package/dist/src/children.d.ts +134 -0
- package/dist/src/children.d.ts.map +1 -0
- package/dist/src/children.js +274 -0
- package/dist/src/children.js.map +1 -0
- package/dist/src/common/attributes.d.ts +229 -0
- package/dist/src/common/attributes.d.ts.map +1 -0
- package/dist/src/common/attributes.js +368 -0
- package/dist/src/common/attributes.js.map +1 -0
- package/dist/src/common/codes.d.ts +105 -0
- package/dist/src/common/codes.d.ts.map +1 -0
- package/dist/src/common/codes.js +107 -0
- package/dist/src/common/codes.js.map +1 -0
- package/dist/src/common/declared-types.d.ts +84 -0
- package/dist/src/common/declared-types.d.ts.map +1 -0
- package/dist/src/common/declared-types.js +326 -0
- package/dist/src/common/declared-types.js.map +1 -0
- package/dist/src/common/direction.d.ts +206 -0
- package/dist/src/common/direction.d.ts.map +1 -0
- package/dist/src/common/direction.js +370 -0
- package/dist/src/common/direction.js.map +1 -0
- package/dist/src/common/escape.d.ts +92 -0
- package/dist/src/common/escape.d.ts.map +1 -0
- package/dist/src/common/escape.js +212 -0
- package/dist/src/common/escape.js.map +1 -0
- package/dist/src/common/export.d.ts +249 -0
- package/dist/src/common/export.d.ts.map +1 -0
- package/dist/src/common/export.js +594 -0
- package/dist/src/common/export.js.map +1 -0
- package/dist/src/common/format.d.ts +59 -0
- package/dist/src/common/format.d.ts.map +1 -0
- package/dist/src/common/format.js +106 -0
- package/dist/src/common/format.js.map +1 -0
- package/dist/src/common/ids.d.ts +83 -0
- package/dist/src/common/ids.d.ts.map +1 -0
- package/dist/src/common/ids.js +158 -0
- package/dist/src/common/ids.js.map +1 -0
- package/dist/src/common/input.d.ts +100 -0
- package/dist/src/common/input.d.ts.map +1 -0
- package/dist/src/common/input.js +335 -0
- package/dist/src/common/input.js.map +1 -0
- package/dist/src/common/lists.d.ts +34 -0
- package/dist/src/common/lists.d.ts.map +1 -0
- package/dist/src/common/lists.js +185 -0
- package/dist/src/common/lists.js.map +1 -0
- package/dist/src/common/options.d.ts +108 -0
- package/dist/src/common/options.d.ts.map +1 -0
- package/dist/src/common/options.js +265 -0
- package/dist/src/common/options.js.map +1 -0
- package/dist/src/common/report.d.ts +187 -0
- package/dist/src/common/report.d.ts.map +1 -0
- package/dist/src/common/report.js +274 -0
- package/dist/src/common/report.js.map +1 -0
- package/dist/src/common/temporal.d.ts +71 -0
- package/dist/src/common/temporal.d.ts.map +1 -0
- package/dist/src/common/temporal.js +266 -0
- package/dist/src/common/temporal.js.map +1 -0
- package/dist/src/common/text.d.ts +104 -0
- package/dist/src/common/text.d.ts.map +1 -0
- package/dist/src/common/text.js +255 -0
- package/dist/src/common/text.js.map +1 -0
- package/dist/src/common/weights.d.ts +77 -0
- package/dist/src/common/weights.d.ts.map +1 -0
- package/dist/src/common/weights.js +156 -0
- package/dist/src/common/weights.js.map +1 -0
- package/dist/src/common/writer.d.ts +51 -0
- package/dist/src/common/writer.d.ts.map +1 -0
- package/dist/src/common/writer.js +108 -0
- package/dist/src/common/writer.js.map +1 -0
- package/dist/src/common/xml.d.ts +245 -0
- package/dist/src/common/xml.d.ts.map +1 -0
- package/dist/src/common/xml.js +942 -0
- package/dist/src/common/xml.js.map +1 -0
- package/dist/src/formats/csv/exporter.d.ts +70 -0
- package/dist/src/formats/csv/exporter.d.ts.map +1 -0
- package/dist/src/formats/csv/exporter.js +682 -0
- package/dist/src/formats/csv/exporter.js.map +1 -0
- package/dist/src/formats/csv/header.d.ts +66 -0
- package/dist/src/formats/csv/header.d.ts.map +1 -0
- package/dist/src/formats/csv/header.js +152 -0
- package/dist/src/formats/csv/header.js.map +1 -0
- package/dist/src/formats/csv/importer.d.ts +82 -0
- package/dist/src/formats/csv/importer.d.ts.map +1 -0
- package/dist/src/formats/csv/importer.js +849 -0
- package/dist/src/formats/csv/importer.js.map +1 -0
- package/dist/src/formats/csv/index.d.ts +60 -0
- package/dist/src/formats/csv/index.d.ts.map +1 -0
- package/dist/src/formats/csv/index.js +63 -0
- package/dist/src/formats/csv/index.js.map +1 -0
- package/dist/src/formats/csv/records.d.ts +188 -0
- package/dist/src/formats/csv/records.d.ts.map +1 -0
- package/dist/src/formats/csv/records.js +702 -0
- package/dist/src/formats/csv/records.js.map +1 -0
- package/dist/src/formats/csv/values.d.ts +105 -0
- package/dist/src/formats/csv/values.d.ts.map +1 -0
- package/dist/src/formats/csv/values.js +192 -0
- package/dist/src/formats/csv/values.js.map +1 -0
- package/dist/src/formats/dot/exporter.d.ts +52 -0
- package/dist/src/formats/dot/exporter.d.ts.map +1 -0
- package/dist/src/formats/dot/exporter.js +836 -0
- package/dist/src/formats/dot/exporter.js.map +1 -0
- package/dist/src/formats/dot/importer.d.ts +102 -0
- package/dist/src/formats/dot/importer.d.ts.map +1 -0
- package/dist/src/formats/dot/importer.js +1291 -0
- package/dist/src/formats/dot/importer.js.map +1 -0
- package/dist/src/formats/dot/index.d.ts +7 -0
- package/dist/src/formats/dot/index.d.ts.map +1 -0
- package/dist/src/formats/dot/index.js +7 -0
- package/dist/src/formats/dot/index.js.map +1 -0
- package/dist/src/formats/dot/names.d.ts +29 -0
- package/dist/src/formats/dot/names.d.ts.map +1 -0
- package/dist/src/formats/dot/names.js +28 -0
- package/dist/src/formats/dot/names.js.map +1 -0
- package/dist/src/formats/dot/tokenizer.d.ts +114 -0
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
- package/dist/src/formats/dot/tokenizer.js +341 -0
- package/dist/src/formats/dot/tokenizer.js.map +1 -0
- package/dist/src/formats/gexf/exporter.d.ts +56 -0
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
- package/dist/src/formats/gexf/exporter.js +1395 -0
- package/dist/src/formats/gexf/exporter.js.map +1 -0
- package/dist/src/formats/gexf/importer.d.ts +73 -0
- package/dist/src/formats/gexf/importer.d.ts.map +1 -0
- package/dist/src/formats/gexf/importer.js +1880 -0
- package/dist/src/formats/gexf/importer.js.map +1 -0
- package/dist/src/formats/gexf/index.d.ts +96 -0
- package/dist/src/formats/gexf/index.d.ts.map +1 -0
- package/dist/src/formats/gexf/index.js +97 -0
- package/dist/src/formats/gexf/index.js.map +1 -0
- package/dist/src/formats/gexf/schema.d.ts +135 -0
- package/dist/src/formats/gexf/schema.d.ts.map +1 -0
- package/dist/src/formats/gexf/schema.js +323 -0
- package/dist/src/formats/gexf/schema.js.map +1 -0
- package/dist/src/formats/gml/exporter.d.ts +69 -0
- package/dist/src/formats/gml/exporter.d.ts.map +1 -0
- package/dist/src/formats/gml/exporter.js +1093 -0
- package/dist/src/formats/gml/exporter.js.map +1 -0
- package/dist/src/formats/gml/importer.d.ts +66 -0
- package/dist/src/formats/gml/importer.d.ts.map +1 -0
- package/dist/src/formats/gml/importer.js +1331 -0
- package/dist/src/formats/gml/importer.js.map +1 -0
- package/dist/src/formats/gml/index.d.ts +85 -0
- package/dist/src/formats/gml/index.d.ts.map +1 -0
- package/dist/src/formats/gml/index.js +88 -0
- package/dist/src/formats/gml/index.js.map +1 -0
- package/dist/src/formats/gml/syntax.d.ts +186 -0
- package/dist/src/formats/gml/syntax.d.ts.map +1 -0
- package/dist/src/formats/gml/syntax.js +467 -0
- package/dist/src/formats/gml/syntax.js.map +1 -0
- package/dist/src/formats/graphml/constants.d.ts +169 -0
- package/dist/src/formats/graphml/constants.d.ts.map +1 -0
- package/dist/src/formats/graphml/constants.js +165 -0
- package/dist/src/formats/graphml/constants.js.map +1 -0
- package/dist/src/formats/graphml/exporter.d.ts +34 -0
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
- package/dist/src/formats/graphml/exporter.js +1176 -0
- package/dist/src/formats/graphml/exporter.js.map +1 -0
- package/dist/src/formats/graphml/importer.d.ts +31 -0
- package/dist/src/formats/graphml/importer.d.ts.map +1 -0
- package/dist/src/formats/graphml/importer.js +1607 -0
- package/dist/src/formats/graphml/importer.js.map +1 -0
- package/dist/src/formats/graphml/index.d.ts +8 -0
- package/dist/src/formats/graphml/index.d.ts.map +1 -0
- package/dist/src/formats/graphml/index.js +8 -0
- package/dist/src/formats/graphml/index.js.map +1 -0
- package/dist/src/formats/graphml/tree.d.ts +72 -0
- package/dist/src/formats/graphml/tree.d.ts.map +1 -0
- package/dist/src/formats/graphml/tree.js +290 -0
- package/dist/src/formats/graphml/tree.js.map +1 -0
- package/dist/src/formats/json/dialect.d.ts +125 -0
- package/dist/src/formats/json/dialect.d.ts.map +1 -0
- package/dist/src/formats/json/dialect.js +262 -0
- package/dist/src/formats/json/dialect.js.map +1 -0
- package/dist/src/formats/json/exporter.d.ts +89 -0
- package/dist/src/formats/json/exporter.d.ts.map +1 -0
- package/dist/src/formats/json/exporter.js +1358 -0
- package/dist/src/formats/json/exporter.js.map +1 -0
- package/dist/src/formats/json/importer.d.ts +108 -0
- package/dist/src/formats/json/importer.d.ts.map +1 -0
- package/dist/src/formats/json/importer.js +1838 -0
- package/dist/src/formats/json/importer.js.map +1 -0
- package/dist/src/formats/json/index.d.ts +8 -0
- package/dist/src/formats/json/index.d.ts.map +1 -0
- package/dist/src/formats/json/index.js +8 -0
- package/dist/src/formats/json/index.js.map +1 -0
- package/dist/src/formats/neo4j/exporter.d.ts +68 -0
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
- package/dist/src/formats/neo4j/exporter.js +1055 -0
- package/dist/src/formats/neo4j/exporter.js.map +1 -0
- package/dist/src/formats/neo4j/header.d.ts +52 -0
- package/dist/src/formats/neo4j/header.d.ts.map +1 -0
- package/dist/src/formats/neo4j/header.js +131 -0
- package/dist/src/formats/neo4j/header.js.map +1 -0
- package/dist/src/formats/neo4j/importer.d.ts +73 -0
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
- package/dist/src/formats/neo4j/importer.js +932 -0
- package/dist/src/formats/neo4j/importer.js.map +1 -0
- package/dist/src/formats/neo4j/index.d.ts +79 -0
- package/dist/src/formats/neo4j/index.d.ts.map +1 -0
- package/dist/src/formats/neo4j/index.js +83 -0
- package/dist/src/formats/neo4j/index.js.map +1 -0
- package/dist/src/formats/pajek/exporter.d.ts +58 -0
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
- package/dist/src/formats/pajek/exporter.js +825 -0
- package/dist/src/formats/pajek/exporter.js.map +1 -0
- package/dist/src/formats/pajek/importer.d.ts +88 -0
- package/dist/src/formats/pajek/importer.d.ts.map +1 -0
- package/dist/src/formats/pajek/importer.js +1047 -0
- package/dist/src/formats/pajek/importer.js.map +1 -0
- package/dist/src/formats/pajek/index.d.ts +7 -0
- package/dist/src/formats/pajek/index.d.ts.map +1 -0
- package/dist/src/formats/pajek/index.js +7 -0
- package/dist/src/formats/pajek/index.js.map +1 -0
- package/dist/src/formats/pajek/syntax.d.ts +112 -0
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
- package/dist/src/formats/pajek/syntax.js +269 -0
- package/dist/src/formats/pajek/syntax.js.map +1 -0
- package/dist/src/index.d.ts +35 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/registry.d.ts +207 -0
- package/dist/src/registry.d.ts.map +1 -0
- package/dist/src/registry.js +481 -0
- package/dist/src/registry.js.map +1 -0
- package/dist/src/sniff.d.ts +104 -0
- package/dist/src/sniff.d.ts.map +1 -0
- package/dist/src/sniff.js +357 -0
- package/dist/src/sniff.js.map +1 -0
- package/dist/src/types.d.ts +238 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +29 -0
- package/dist/src/types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +122 -7
- package/src/children.ts +335 -0
- package/src/common/attributes.ts +520 -0
- package/src/common/codes.ts +153 -0
- package/src/common/declared-types.ts +374 -0
- package/src/common/direction.ts +518 -0
- package/src/common/escape.ts +231 -0
- package/src/common/export.ts +817 -0
- package/src/common/format.ts +111 -0
- package/src/common/ids.ts +176 -0
- package/src/common/input.ts +378 -0
- package/src/common/lists.ts +196 -0
- package/src/common/options.ts +377 -0
- package/src/common/report.ts +352 -0
- package/src/common/temporal.ts +302 -0
- package/src/common/text.ts +294 -0
- package/src/common/weights.ts +202 -0
- package/src/common/writer.ts +123 -0
- package/src/common/xml.ts +1053 -0
- package/src/formats/csv/exporter.ts +894 -0
- package/src/formats/csv/header.ts +172 -0
- package/src/formats/csv/importer.ts +1104 -0
- package/src/formats/csv/index.ts +88 -0
- package/src/formats/csv/records.ts +813 -0
- package/src/formats/csv/values.ts +224 -0
- package/src/formats/dot/exporter.ts +1014 -0
- package/src/formats/dot/importer.ts +1549 -0
- package/src/formats/dot/index.ts +7 -0
- package/src/formats/dot/names.ts +40 -0
- package/src/formats/dot/tokenizer.ts +384 -0
- package/src/formats/gexf/exporter.ts +1696 -0
- package/src/formats/gexf/importer.ts +2333 -0
- package/src/formats/gexf/index.ts +142 -0
- package/src/formats/gexf/schema.ts +361 -0
- package/src/formats/gml/exporter.ts +1404 -0
- package/src/formats/gml/importer.ts +1591 -0
- package/src/formats/gml/index.ts +128 -0
- package/src/formats/gml/syntax.ts +545 -0
- package/src/formats/graphml/constants.ts +225 -0
- package/src/formats/graphml/exporter.ts +1458 -0
- package/src/formats/graphml/importer.ts +2027 -0
- package/src/formats/graphml/index.ts +8 -0
- package/src/formats/graphml/tree.ts +318 -0
- package/src/formats/json/dialect.ts +317 -0
- package/src/formats/json/exporter.ts +1616 -0
- package/src/formats/json/importer.ts +2271 -0
- package/src/formats/json/index.ts +8 -0
- package/src/formats/neo4j/exporter.ts +1287 -0
- package/src/formats/neo4j/header.ts +156 -0
- package/src/formats/neo4j/importer.ts +1220 -0
- package/src/formats/neo4j/index.ts +116 -0
- package/src/formats/pajek/exporter.ts +1000 -0
- package/src/formats/pajek/importer.ts +1311 -0
- package/src/formats/pajek/index.ts +7 -0
- package/src/formats/pajek/syntax.ts +307 -0
- package/src/index.ts +244 -0
- package/src/registry.ts +617 -0
- package/src/sniff.ts +397 -0
- package/src/types.ts +262 -0
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direction resolution through the sink (design sections 3.6 and 8.4, decision D-DIR): the
|
|
3
|
+
* importer reads the file's header direction, calls `sink.setDirected()` once before the first
|
|
4
|
+
* edge, then pushes every edge as it is read. Mixed files are handled per `onMixedDirection`:
|
|
5
|
+
*
|
|
6
|
+
* - "expand" (default): an undirected edge into a directed sink becomes two logical edges linked
|
|
7
|
+
* by the `graphty.pair` column with `graphty.directed` = false on both halves; a directed edge
|
|
8
|
+
* into an undirected sink triggers ONE `setDirected(true, { expand: true })`, after which the
|
|
9
|
+
* builder has expanded everything pushed so far and the importer continues in directed mode.
|
|
10
|
+
* - "directed" / "undirected": every edge is treated that way (the distinction is dropped and
|
|
11
|
+
* reported once, with the count).
|
|
12
|
+
* - "error": the first edge whose direction differs from the sink's aborts the import.
|
|
13
|
+
*
|
|
14
|
+
* A locked or non-empty sink wins over the file (design section 8.4 precedence): a refused
|
|
15
|
+
* `setDirected` is recorded as a coercion issue and the importer continues with `sink.directed`,
|
|
16
|
+
* expanding every edge that differs. A GEXF `mutual` edge expands like an undirected one with the
|
|
17
|
+
* additional `graphty.mutual` role column set on its primary half.
|
|
18
|
+
*
|
|
19
|
+
* The reserved columns are declared with exactly the builder's own declarations so that a sink the
|
|
20
|
+
* builder already expanded hands back the existing handles.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
type Column,
|
|
25
|
+
type ColumnDecl,
|
|
26
|
+
type ColumnHandle,
|
|
27
|
+
GraphFormatError,
|
|
28
|
+
type GraphSink,
|
|
29
|
+
type GraphSnapshot,
|
|
30
|
+
INVALID_INDEX,
|
|
31
|
+
type NodeId,
|
|
32
|
+
} from "@graphty/graph-format";
|
|
33
|
+
|
|
34
|
+
import { DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, MIXED_DIRECTION_CODE } from "./codes.js";
|
|
35
|
+
import { type ImportReportBuilder } from "./report.js";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The direction of one source edge.
|
|
39
|
+
* Consumed by the per-format importers and exporters under src/formats.
|
|
40
|
+
* @public
|
|
41
|
+
*/
|
|
42
|
+
export type EdgeKind = "directed" | "undirected" | "mutual";
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Where an edge or header was read, for issues.
|
|
46
|
+
* Consumed by the per-format importers and exporters under src/formats.
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
export interface EdgeLocation {
|
|
50
|
+
/** The 1-based line, when known. */
|
|
51
|
+
readonly line?: number | null | undefined;
|
|
52
|
+
/** The edge id or another element name, when known. */
|
|
53
|
+
readonly element?: string | null | undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The name of the bool edge column marking source-directed edges (design section 3.6). */
|
|
57
|
+
export const DIRECTED_COLUMN = "graphty.directed";
|
|
58
|
+
|
|
59
|
+
/** The name of the u32 edge column pairing the halves of an expanded edge (design section 3.6). */
|
|
60
|
+
export const PAIR_COLUMN = "graphty.pair";
|
|
61
|
+
|
|
62
|
+
/** The name of the bool edge column marking GEXF mutual edges (design section 3.6). */
|
|
63
|
+
export const MUTUAL_COLUMN = "graphty.mutual";
|
|
64
|
+
|
|
65
|
+
export { DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, MIXED_DIRECTION_CODE };
|
|
66
|
+
|
|
67
|
+
const DIRECTED_DECL: ColumnDecl = { name: DIRECTED_COLUMN, dtype: "bool", role: "directed" };
|
|
68
|
+
const PAIR_DECL: ColumnDecl = { name: PAIR_COLUMN, dtype: "u32", role: "pair", refersTo: "edge" };
|
|
69
|
+
const MUTUAL_DECL: ColumnDecl = { name: MUTUAL_COLUMN, dtype: "bool", role: "mutual" };
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Pushes edges into a sink while resolving direction per design section 8.4. One instance per
|
|
73
|
+
* import call; `setHeader()` before the first `addEdge()`.
|
|
74
|
+
*/
|
|
75
|
+
export class DirectionResolver {
|
|
76
|
+
private readonly sink: GraphSink;
|
|
77
|
+
|
|
78
|
+
private readonly report: ImportReportBuilder;
|
|
79
|
+
|
|
80
|
+
private readonly policy: "expand" | "directed" | "undirected" | "error";
|
|
81
|
+
|
|
82
|
+
private directedHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
|
|
83
|
+
|
|
84
|
+
private pairHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
|
|
85
|
+
|
|
86
|
+
private mutualHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;
|
|
87
|
+
|
|
88
|
+
/** The first edge index this resolver pushed, for the backfill of graphty.directed; -1 before the first. */
|
|
89
|
+
private firstEdge = -1;
|
|
90
|
+
|
|
91
|
+
/** The next edge index this resolver expects to receive (contiguity between compactions, I16). */
|
|
92
|
+
private nextEdge = -1;
|
|
93
|
+
|
|
94
|
+
private forcedCount = 0;
|
|
95
|
+
|
|
96
|
+
private headerSet = false;
|
|
97
|
+
|
|
98
|
+
private mirrorOfLast: number = INVALID_INDEX;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a resolver.
|
|
102
|
+
* @param sink - the sink edges are pushed into
|
|
103
|
+
* @param report - the report issues are recorded in
|
|
104
|
+
* @param policy - the resolved onMixedDirection option
|
|
105
|
+
*/
|
|
106
|
+
constructor(sink: GraphSink, report: ImportReportBuilder, policy: "expand" | "directed" | "undirected" | "error") {
|
|
107
|
+
this.sink = sink;
|
|
108
|
+
this.report = report;
|
|
109
|
+
this.policy = policy;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The sink's current direction.
|
|
114
|
+
* @returns true when directed
|
|
115
|
+
*/
|
|
116
|
+
get directed(): boolean {
|
|
117
|
+
return this.sink.directed;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether the reserved direction columns exist (the sink holds expanded edges).
|
|
122
|
+
* @returns true once graphty.pair was declared or adopted
|
|
123
|
+
*/
|
|
124
|
+
get expanded(): boolean {
|
|
125
|
+
return this.pairHandle !== INVALID_INDEX;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Rule 1 of design section 8.4: set the sink's direction from the file's header (or the
|
|
130
|
+
* `defaultDirected` option for a file that declares none) before the first edge. Under the
|
|
131
|
+
* "directed" / "undirected" policies the policy's direction is used instead and the difference
|
|
132
|
+
* is reported. A refusal (E_DIRECTED: locked, or a non-empty directed sink for an undirected
|
|
133
|
+
* header) is recorded as a coercion issue and the sink's direction stands; a non-empty unlocked
|
|
134
|
+
* undirected sink with a directed header is expanded in place.
|
|
135
|
+
* @param headerDirected - the direction the file declares (or the default)
|
|
136
|
+
* @param where - the line of the header, when known
|
|
137
|
+
*/
|
|
138
|
+
setHeader(headerDirected: boolean, where?: EdgeLocation): void {
|
|
139
|
+
this.headerSet = true;
|
|
140
|
+
let wanted = headerDirected;
|
|
141
|
+
if (this.policy === "directed" || this.policy === "undirected") {
|
|
142
|
+
wanted = this.policy === "directed";
|
|
143
|
+
if (wanted !== headerDirected) {
|
|
144
|
+
this.report.warning(
|
|
145
|
+
"coercion",
|
|
146
|
+
DIRECTION_FORCED_CODE,
|
|
147
|
+
`the file declares ${direction(headerDirected)} edges; read as ${direction(wanted)} per onMixedDirection`,
|
|
148
|
+
{ line: where?.line ?? null },
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const { sink } = this;
|
|
153
|
+
if (sink.directed === wanted) {
|
|
154
|
+
this.adoptExisting();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
if (wanted && sink.edgeCount > 0 && !sink.directedLocked) {
|
|
159
|
+
const before = sink.edgeCount;
|
|
160
|
+
sink.setDirected(true, { expand: true });
|
|
161
|
+
this.adoptExpansionColumns();
|
|
162
|
+
this.report.counts.expandedMixed += before;
|
|
163
|
+
} else {
|
|
164
|
+
sink.setDirected(wanted);
|
|
165
|
+
}
|
|
166
|
+
} catch (err) {
|
|
167
|
+
if (!(err instanceof GraphFormatError) || err.code !== "E_DIRECTED") {
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
this.report.warning(
|
|
171
|
+
"coercion",
|
|
172
|
+
DIRECTION_REFUSED_CODE,
|
|
173
|
+
`the file is ${direction(wanted)} but the sink is ${direction(sink.directed)} (${reasonOf(err)}); read as ${direction(sink.directed)}`,
|
|
174
|
+
{ line: where?.line ?? null },
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
this.adoptExisting();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Pick up reserved columns a caller's sink already holds (an earlier expansion), so every edge
|
|
182
|
+
* pushed from now on gets its graphty.directed value.
|
|
183
|
+
*/
|
|
184
|
+
private adoptExisting(): void {
|
|
185
|
+
if (this.pairHandle === INVALID_INDEX && this.sink.edgeColumn(PAIR_COLUMN) !== INVALID_INDEX) {
|
|
186
|
+
this.adoptExpansionColumns();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The mirror half of the edge most recently pushed by addEdge(), when it was expanded into a
|
|
192
|
+
* pair; INVALID_INDEX otherwise. An importer that learns an edge's weight after pushing it
|
|
193
|
+
* (a GEXF attvalue) sets it on both halves.
|
|
194
|
+
* @returns the mirror's logical index, or INVALID_INDEX
|
|
195
|
+
*/
|
|
196
|
+
get lastMirror(): number {
|
|
197
|
+
return this.mirrorOfLast;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Rule 2 of design section 8.4: push one source edge. An edge whose direction equals the
|
|
202
|
+
* sink's is one addEdge; one that differs is expanded, forced or refused per the policy.
|
|
203
|
+
* @param source - the source id
|
|
204
|
+
* @param target - the target id
|
|
205
|
+
* @param kind - the edge's direction in the file
|
|
206
|
+
* @param weight - the weight, or undefined when the file gives none
|
|
207
|
+
* @param where - the line and element, for issues
|
|
208
|
+
* @returns the logical index of the (primary) edge
|
|
209
|
+
*/
|
|
210
|
+
addEdge(source: NodeId, target: NodeId, kind: EdgeKind, weight?: number, where?: EdgeLocation): number {
|
|
211
|
+
if (!this.headerSet) {
|
|
212
|
+
throw new GraphFormatError("E_DIRECTED", "DirectionResolver.setHeader() must run before the first edge", {
|
|
213
|
+
reason: "header not set",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const { sink } = this;
|
|
217
|
+
this.mirrorOfLast = INVALID_INDEX;
|
|
218
|
+
const edgeDirected = kind !== "undirected";
|
|
219
|
+
if (edgeDirected === sink.directed && kind !== "mutual") {
|
|
220
|
+
const e = sink.addEdge(source, target, weight);
|
|
221
|
+
this.track(e);
|
|
222
|
+
if (this.directedHandle !== INVALID_INDEX) {
|
|
223
|
+
sink.setEdgeValue(this.directedHandle, e, edgeDirected);
|
|
224
|
+
}
|
|
225
|
+
return e;
|
|
226
|
+
}
|
|
227
|
+
switch (this.policy) {
|
|
228
|
+
case "directed":
|
|
229
|
+
case "undirected": {
|
|
230
|
+
// the sink's direction is the policy's (or a locked sink's); the edge is pushed as the sink's kind
|
|
231
|
+
this.forcedCount++;
|
|
232
|
+
this.report.warnOnce(
|
|
233
|
+
"coercion",
|
|
234
|
+
DIRECTION_FORCED_CODE,
|
|
235
|
+
`${kind} edges read as ${direction(sink.directed)} per onMixedDirection`,
|
|
236
|
+
where,
|
|
237
|
+
);
|
|
238
|
+
const e = sink.addEdge(source, target, weight);
|
|
239
|
+
this.track(e);
|
|
240
|
+
if (this.directedHandle !== INVALID_INDEX) {
|
|
241
|
+
sink.setEdgeValue(this.directedHandle, e, sink.directed);
|
|
242
|
+
}
|
|
243
|
+
return e;
|
|
244
|
+
}
|
|
245
|
+
case "error":
|
|
246
|
+
this.report.error(
|
|
247
|
+
"validation-error",
|
|
248
|
+
MIXED_DIRECTION_CODE,
|
|
249
|
+
`${kind} edge in a ${direction(sink.directed)} graph (onMixedDirection: "error")`,
|
|
250
|
+
where,
|
|
251
|
+
);
|
|
252
|
+
throw this.report.abort("mixed direction refused", { code: MIXED_DIRECTION_CODE });
|
|
253
|
+
case "expand":
|
|
254
|
+
return this.expand(source, target, kind, weight, where);
|
|
255
|
+
default: {
|
|
256
|
+
const name: string = this.policy;
|
|
257
|
+
throw new GraphFormatError("E_UNSUPPORTED", `unknown onMixedDirection ${name}`, {
|
|
258
|
+
option: "onMixedDirection",
|
|
259
|
+
found: name,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* How many edges were pushed with a direction other than their own under the forcing policies.
|
|
267
|
+
* @returns the count
|
|
268
|
+
*/
|
|
269
|
+
get forced(): number {
|
|
270
|
+
return this.forcedCount;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Expand one edge that differs from the sink's direction.
|
|
275
|
+
* @param source - the source id
|
|
276
|
+
* @param target - the target id
|
|
277
|
+
* @param kind - the edge's direction in the file
|
|
278
|
+
* @param weight - the weight, or undefined
|
|
279
|
+
* @param where - the line and element, for issues
|
|
280
|
+
* @returns the primary edge index
|
|
281
|
+
*/
|
|
282
|
+
private expand(
|
|
283
|
+
source: NodeId,
|
|
284
|
+
target: NodeId,
|
|
285
|
+
kind: EdgeKind,
|
|
286
|
+
weight: number | undefined,
|
|
287
|
+
where: EdgeLocation | undefined,
|
|
288
|
+
): number {
|
|
289
|
+
const { sink } = this;
|
|
290
|
+
if (!sink.directed) {
|
|
291
|
+
// a directed (or mutual) edge into an undirected sink: expand the sink once
|
|
292
|
+
try {
|
|
293
|
+
const before = sink.edgeCount;
|
|
294
|
+
sink.setDirected(true, { expand: true });
|
|
295
|
+
this.adoptExpansionColumns();
|
|
296
|
+
this.report.counts.expandedMixed += before;
|
|
297
|
+
} catch (err) {
|
|
298
|
+
if (!(err instanceof GraphFormatError) || err.code !== "E_DIRECTED") {
|
|
299
|
+
throw err;
|
|
300
|
+
}
|
|
301
|
+
this.report.error(
|
|
302
|
+
"coercion",
|
|
303
|
+
DIRECTION_REFUSED_CODE,
|
|
304
|
+
`${direction(true)} edge in a locked ${direction(false)} sink cannot be expanded`,
|
|
305
|
+
where,
|
|
306
|
+
);
|
|
307
|
+
throw this.report.abort("the sink refused the expansion to directed", {
|
|
308
|
+
code: DIRECTION_REFUSED_CODE,
|
|
309
|
+
reason: err.details.reason,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
if (kind === "directed") {
|
|
313
|
+
const e = sink.addEdge(source, target, weight);
|
|
314
|
+
this.track(e);
|
|
315
|
+
sink.setEdgeValue(this.directedHandle, e, true);
|
|
316
|
+
return e;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
this.ensureExpansionColumns();
|
|
320
|
+
const first = sink.addEdge(source, target, weight);
|
|
321
|
+
this.track(first);
|
|
322
|
+
const mutual = kind === "mutual";
|
|
323
|
+
sink.setEdgeValue(this.directedHandle, first, mutual);
|
|
324
|
+
this.report.counts.expandedMixed++;
|
|
325
|
+
if (mutual) {
|
|
326
|
+
this.ensureMutualColumn();
|
|
327
|
+
sink.setEdgeValue(this.mutualHandle, first, true);
|
|
328
|
+
}
|
|
329
|
+
const u = sink.indexOf(source);
|
|
330
|
+
const v = sink.indexOf(target);
|
|
331
|
+
if (u === v) {
|
|
332
|
+
// an undirected self-loop is one arc (I7); the builder does not mirror it either
|
|
333
|
+
return first;
|
|
334
|
+
}
|
|
335
|
+
const mirror = sink.addEdge(target, source, weight);
|
|
336
|
+
this.track(mirror);
|
|
337
|
+
this.mirrorOfLast = mirror;
|
|
338
|
+
sink.setEdgeValue(this.directedHandle, mirror, mutual);
|
|
339
|
+
sink.setEdgeValue(this.pairHandle, first, mirror);
|
|
340
|
+
sink.setEdgeValue(this.pairHandle, mirror, first);
|
|
341
|
+
return first;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Note an edge index this resolver pushed, for the backfill of graphty.directed.
|
|
346
|
+
* @param e - the edge index
|
|
347
|
+
*/
|
|
348
|
+
private track(e: number): void {
|
|
349
|
+
if (this.firstEdge < 0) {
|
|
350
|
+
this.firstEdge = e;
|
|
351
|
+
}
|
|
352
|
+
this.nextEdge = e + 1;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Pick up the reserved columns the builder wrote during its own expansion.
|
|
357
|
+
*/
|
|
358
|
+
private adoptExpansionColumns(): void {
|
|
359
|
+
this.directedHandle = this.sink.declareEdgeColumn(DIRECTED_DECL);
|
|
360
|
+
this.pairHandle = this.sink.declareEdgeColumn(PAIR_DECL);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Declare the reserved columns on first use in a directed sink and backfill graphty.directed
|
|
365
|
+
* = true for every edge this resolver pushed so far (they were directed in the source).
|
|
366
|
+
*/
|
|
367
|
+
private ensureExpansionColumns(): void {
|
|
368
|
+
if (this.pairHandle !== INVALID_INDEX) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
this.directedHandle = this.sink.declareEdgeColumn(DIRECTED_DECL);
|
|
372
|
+
this.pairHandle = this.sink.declareEdgeColumn(PAIR_DECL);
|
|
373
|
+
if (this.firstEdge >= 0) {
|
|
374
|
+
for (let e = this.firstEdge; e < this.nextEdge; e++) {
|
|
375
|
+
this.sink.setEdgeValue(this.directedHandle, e, true);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** Declare graphty.mutual on first use. */
|
|
381
|
+
private ensureMutualColumn(): void {
|
|
382
|
+
if (this.mutualHandle === INVALID_INDEX) {
|
|
383
|
+
this.mutualHandle = this.sink.declareEdgeColumn(MUTUAL_DECL);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* The reason of a refused setDirected(), for messages.
|
|
390
|
+
* @param err - the E_DIRECTED error
|
|
391
|
+
* @returns details.reason when it is a string, "refused" otherwise
|
|
392
|
+
*/
|
|
393
|
+
function reasonOf(err: GraphFormatError): string {
|
|
394
|
+
const { reason } = err.details;
|
|
395
|
+
return typeof reason === "string" ? reason : "refused";
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* The word for a direction, for messages.
|
|
400
|
+
* @param directed - the direction
|
|
401
|
+
* @returns "directed" or "undirected"
|
|
402
|
+
*/
|
|
403
|
+
function direction(directed: boolean): string {
|
|
404
|
+
return directed ? "directed" : "undirected";
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ============================================================ the exporter side
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* The expanded-pair view of a snapshot's logical edges for exporters (design sections 3.6 and
|
|
411
|
+
* 8.5): which edges are the mirror half of an expanded pair (and are folded back into their
|
|
412
|
+
* primary), which were undirected in the source, and which carry the GEXF mutual mark. One
|
|
413
|
+
* implementation for every exporter; the only per-format choice is whether a mutual pair folds
|
|
414
|
+
* back into one edge (`foldMutual`, for formats with a mutual or undirected slot) or is written as
|
|
415
|
+
* two directed edges (the default).
|
|
416
|
+
* Consumed by the per-format exporters under src/formats.
|
|
417
|
+
* @public
|
|
418
|
+
*/
|
|
419
|
+
export interface PairFolding {
|
|
420
|
+
/** Whether the snapshot carries expanded pairs at all (the pair role column exists). */
|
|
421
|
+
readonly expanded: boolean;
|
|
422
|
+
/** The number of mutual primaries (edges with the mutual mark whose mirror exists). */
|
|
423
|
+
readonly mutualCount: number;
|
|
424
|
+
/**
|
|
425
|
+
* Whether an edge is the mirror half of a pair the exporter folds (its primary is written).
|
|
426
|
+
* @param e - the logical edge index
|
|
427
|
+
* @returns true when the edge must be skipped
|
|
428
|
+
*/
|
|
429
|
+
folded(e: number): boolean;
|
|
430
|
+
/**
|
|
431
|
+
* The other half of an expanded pair.
|
|
432
|
+
* @param e - the logical edge index
|
|
433
|
+
* @returns the mate's index, or INVALID_INDEX when the edge is not paired
|
|
434
|
+
*/
|
|
435
|
+
mateOf(e: number): number;
|
|
436
|
+
/**
|
|
437
|
+
* Whether an edge was directed in the source (true for every edge of a snapshot without the
|
|
438
|
+
* directed role column, and for an undirected snapshot's edges as far as the column says).
|
|
439
|
+
* @param e - the logical edge index
|
|
440
|
+
* @returns the source direction
|
|
441
|
+
*/
|
|
442
|
+
sourceDirected(e: number): boolean;
|
|
443
|
+
/**
|
|
444
|
+
* Whether an edge is a mutual primary or mirror (the GEXF `mutual` type).
|
|
445
|
+
* @param e - the logical edge index
|
|
446
|
+
* @returns true for a mutual edge
|
|
447
|
+
*/
|
|
448
|
+
isMutual(e: number): boolean;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** The per-format choice of pairFolding(). */
|
|
452
|
+
export interface PairFoldingOptions {
|
|
453
|
+
/**
|
|
454
|
+
* Fold a mutual pair back into its primary (a format with a mutual or undirected slot);
|
|
455
|
+
* false (the default) writes both halves as directed edges, and the caller reports the mark.
|
|
456
|
+
*/
|
|
457
|
+
readonly foldMutual?: boolean | undefined;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Build the pair-folding view of a snapshot (one pass over the role columns).
|
|
462
|
+
* @param snapshot - the snapshot
|
|
463
|
+
* @param options - the per-format choice
|
|
464
|
+
* @returns the view
|
|
465
|
+
*/
|
|
466
|
+
export function pairFolding(snapshot: GraphSnapshot, options: PairFoldingOptions = {}): PairFolding {
|
|
467
|
+
const foldMutual = options.foldMutual ?? false;
|
|
468
|
+
const directedColumn = roleColumn(snapshot.edges.byRole("directed"), "bool");
|
|
469
|
+
const pairColumn = roleColumn(snapshot.edges.byRole("pair"), "u32");
|
|
470
|
+
const mutualColumn = roleColumn(snapshot.edges.byRole("mutual"), "bool");
|
|
471
|
+
const { edgeCount } = snapshot;
|
|
472
|
+
const mateOf = (e: number): number => {
|
|
473
|
+
if (pairColumn === null || !pairColumn.isSet(e)) {
|
|
474
|
+
return INVALID_INDEX;
|
|
475
|
+
}
|
|
476
|
+
const mate = pairColumn.value(e);
|
|
477
|
+
return typeof mate === "number" && mate !== INVALID_INDEX ? mate : INVALID_INDEX;
|
|
478
|
+
};
|
|
479
|
+
const sourceDirected = (e: number): boolean =>
|
|
480
|
+
directedColumn === null || !directedColumn.isSet(e) || directedColumn.value(e) === true;
|
|
481
|
+
const isMutual = (e: number): boolean => {
|
|
482
|
+
if (mutualColumn === null) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
if (mutualColumn.isSet(e) && mutualColumn.value(e) === true) {
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
const mate = mateOf(e);
|
|
489
|
+
return mate !== INVALID_INDEX && mutualColumn.isSet(mate) && mutualColumn.value(mate) === true;
|
|
490
|
+
};
|
|
491
|
+
const folded = (e: number): boolean => {
|
|
492
|
+
const mate = mateOf(e);
|
|
493
|
+
if (mate === INVALID_INDEX || mate >= e) {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
// an undirected pair always folds; a mutual pair (directed on both halves) folds on request
|
|
497
|
+
return !sourceDirected(e) || (foldMutual && isMutual(e));
|
|
498
|
+
};
|
|
499
|
+
let mutualCount = 0;
|
|
500
|
+
if (mutualColumn !== null) {
|
|
501
|
+
for (let e = 0; e < edgeCount; e++) {
|
|
502
|
+
if (mutualColumn.isSet(e) && mutualColumn.value(e) === true && mateOf(e) > e) {
|
|
503
|
+
mutualCount++;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { expanded: pairColumn !== null, mutualCount, folded, mateOf, sourceDirected, isMutual };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* A role column when it has the dtype the role requires, else null.
|
|
512
|
+
* @param column - the column, or null
|
|
513
|
+
* @param dtype - the required dtype
|
|
514
|
+
* @returns the column or null
|
|
515
|
+
*/
|
|
516
|
+
function roleColumn(column: Column | null, dtype: "bool" | "u32"): Column | null {
|
|
517
|
+
return column !== null && column.dtype === dtype ? column : null;
|
|
518
|
+
}
|