@graphty/graph-io 0.3.17 → 0.3.19

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.
Files changed (154) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +64 -3
  3. package/dist/chunks/{escape-scjHxjpr.js → escape-CWExcecC.js} +3 -2
  4. package/dist/chunks/{escape-scjHxjpr.js.map → escape-CWExcecC.js.map} +1 -1
  5. package/dist/chunks/exporter-CbMZyVt-.js +735 -0
  6. package/dist/chunks/exporter-CbMZyVt-.js.map +1 -0
  7. package/dist/chunks/importer-BAP4PrxR.js +2194 -0
  8. package/dist/chunks/importer-BAP4PrxR.js.map +1 -0
  9. package/dist/chunks/{importer-Xg07gk7p.js → importer-BNFuV1-K.js} +5 -3
  10. package/dist/chunks/{importer-Xg07gk7p.js.map → importer-BNFuV1-K.js.map} +1 -1
  11. package/dist/chunks/{importer-BmOl9gLW.js → importer-BVGtU1NA.js} +5 -3
  12. package/dist/chunks/{importer-BmOl9gLW.js.map → importer-BVGtU1NA.js.map} +1 -1
  13. package/dist/chunks/{importer-ruMWWvVs.js → importer-BW-Ft2ps.js} +5 -3
  14. package/dist/chunks/{importer-ruMWWvVs.js.map → importer-BW-Ft2ps.js.map} +1 -1
  15. package/dist/chunks/{importer-B6rKRxzv.js → importer-Bm1_5vPS.js} +781 -104
  16. package/dist/chunks/importer-Bm1_5vPS.js.map +1 -0
  17. package/dist/chunks/importer-ByPGO-09.js +1614 -0
  18. package/dist/chunks/importer-ByPGO-09.js.map +1 -0
  19. package/dist/chunks/importer-DOepkgnG.js +1713 -0
  20. package/dist/chunks/importer-DOepkgnG.js.map +1 -0
  21. package/dist/chunks/json-elements-CZY1wiZh.js +779 -0
  22. package/dist/chunks/json-elements-CZY1wiZh.js.map +1 -0
  23. package/dist/chunks/ontology-BnrJ4I98.js +113 -0
  24. package/dist/chunks/ontology-BnrJ4I98.js.map +1 -0
  25. package/dist/chunks/{records-DSpbTE5s.js → records-BzNicMsf.js} +2 -2
  26. package/dist/chunks/{records-DSpbTE5s.js.map → records-BzNicMsf.js.map} +1 -1
  27. package/dist/chunks/{writer-DHHfHn11.js → report-BcWboivV.js} +106 -928
  28. package/dist/chunks/report-BcWboivV.js.map +1 -0
  29. package/dist/chunks/weights-CwISIpCP.js +176 -0
  30. package/dist/chunks/weights-CwISIpCP.js.map +1 -0
  31. package/dist/chunks/writer-DQiKgQJc.js +656 -0
  32. package/dist/chunks/writer-DQiKgQJc.js.map +1 -0
  33. package/dist/csv.js +5 -3
  34. package/dist/csv.js.map +1 -1
  35. package/dist/cx.d.ts +1 -0
  36. package/dist/cx.js +6 -0
  37. package/dist/cx.js.map +1 -0
  38. package/dist/cx2.d.ts +1 -0
  39. package/dist/cx2.js +10 -0
  40. package/dist/cx2.js.map +1 -0
  41. package/dist/dot.js +1 -1
  42. package/dist/gexf.js +15 -3
  43. package/dist/gexf.js.map +1 -1
  44. package/dist/gml.js +5 -3
  45. package/dist/gml.js.map +1 -1
  46. package/dist/graph-io.js +158 -383
  47. package/dist/graph-io.js.map +1 -1
  48. package/dist/graphml.js +1 -1
  49. package/dist/json.js +1 -1
  50. package/dist/neo4j.js +5 -3
  51. package/dist/neo4j.js.map +1 -1
  52. package/dist/obo.d.ts +1 -0
  53. package/dist/obo.js +6 -0
  54. package/dist/obo.js.map +1 -0
  55. package/dist/pajek.js +1 -1
  56. package/dist/src/common/json-elements.d.ts +286 -0
  57. package/dist/src/common/json-elements.d.ts.map +1 -0
  58. package/dist/src/common/json-elements.js +926 -0
  59. package/dist/src/common/json-elements.js.map +1 -0
  60. package/dist/src/common/ontology.d.ts +59 -0
  61. package/dist/src/common/ontology.d.ts.map +1 -0
  62. package/dist/src/common/ontology.js +147 -0
  63. package/dist/src/common/ontology.js.map +1 -0
  64. package/dist/src/formats/cx/importer.d.ts +133 -0
  65. package/dist/src/formats/cx/importer.d.ts.map +1 -0
  66. package/dist/src/formats/cx/importer.js +2220 -0
  67. package/dist/src/formats/cx/importer.js.map +1 -0
  68. package/dist/src/formats/cx/index.d.ts +7 -0
  69. package/dist/src/formats/cx/index.d.ts.map +1 -0
  70. package/dist/src/formats/cx/index.js +7 -0
  71. package/dist/src/formats/cx/index.js.map +1 -0
  72. package/dist/src/formats/cx2/exporter.d.ts +59 -0
  73. package/dist/src/formats/cx2/exporter.d.ts.map +1 -0
  74. package/dist/src/formats/cx2/exporter.js +864 -0
  75. package/dist/src/formats/cx2/exporter.js.map +1 -0
  76. package/dist/src/formats/cx2/importer.d.ts +169 -0
  77. package/dist/src/formats/cx2/importer.d.ts.map +1 -0
  78. package/dist/src/formats/cx2/importer.js +1652 -0
  79. package/dist/src/formats/cx2/importer.js.map +1 -0
  80. package/dist/src/formats/cx2/index.d.ts +7 -0
  81. package/dist/src/formats/cx2/index.d.ts.map +1 -0
  82. package/dist/src/formats/cx2/index.js +7 -0
  83. package/dist/src/formats/cx2/index.js.map +1 -0
  84. package/dist/src/formats/gexf/exporter.d.ts +2 -0
  85. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  86. package/dist/src/formats/gexf/exporter.js +6 -0
  87. package/dist/src/formats/gexf/exporter.js.map +1 -1
  88. package/dist/src/formats/gexf/importer.d.ts +2 -4
  89. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  90. package/dist/src/formats/gexf/importer.js +2 -4
  91. package/dist/src/formats/gexf/importer.js.map +1 -1
  92. package/dist/src/formats/gexf/index.d.ts +1 -1
  93. package/dist/src/formats/gexf/index.js +2 -2
  94. package/dist/src/formats/gexf/index.js.map +1 -1
  95. package/dist/src/formats/gml/exporter.js +1 -1
  96. package/dist/src/formats/gml/exporter.js.map +1 -1
  97. package/dist/src/formats/json/dialect.d.ts +8 -6
  98. package/dist/src/formats/json/dialect.d.ts.map +1 -1
  99. package/dist/src/formats/json/dialect.js +26 -3
  100. package/dist/src/formats/json/dialect.js.map +1 -1
  101. package/dist/src/formats/json/importer.d.ts +324 -7
  102. package/dist/src/formats/json/importer.d.ts.map +1 -1
  103. package/dist/src/formats/json/importer.js +159 -126
  104. package/dist/src/formats/json/importer.js.map +1 -1
  105. package/dist/src/formats/json/obographs.d.ts +21 -0
  106. package/dist/src/formats/json/obographs.d.ts.map +1 -0
  107. package/dist/src/formats/json/obographs.js +476 -0
  108. package/dist/src/formats/json/obographs.js.map +1 -0
  109. package/dist/src/formats/obo/importer.d.ts +90 -0
  110. package/dist/src/formats/obo/importer.d.ts.map +1 -0
  111. package/dist/src/formats/obo/importer.js +1248 -0
  112. package/dist/src/formats/obo/importer.js.map +1 -0
  113. package/dist/src/formats/obo/index.d.ts +7 -0
  114. package/dist/src/formats/obo/index.d.ts.map +1 -0
  115. package/dist/src/formats/obo/index.js +7 -0
  116. package/dist/src/formats/obo/index.js.map +1 -0
  117. package/dist/src/formats/obo/syntax.d.ts +121 -0
  118. package/dist/src/formats/obo/syntax.d.ts.map +1 -0
  119. package/dist/src/formats/obo/syntax.js +424 -0
  120. package/dist/src/formats/obo/syntax.js.map +1 -0
  121. package/dist/src/index.d.ts +3 -0
  122. package/dist/src/index.d.ts.map +1 -1
  123. package/dist/src/index.js +3 -0
  124. package/dist/src/index.js.map +1 -1
  125. package/dist/src/registry.d.ts.map +1 -1
  126. package/dist/src/registry.js +8 -1
  127. package/dist/src/registry.js.map +1 -1
  128. package/dist/src/sniff.d.ts +1 -1
  129. package/dist/src/sniff.d.ts.map +1 -1
  130. package/dist/src/sniff.js +25 -3
  131. package/dist/src/sniff.js.map +1 -1
  132. package/package.json +16 -1
  133. package/src/common/json-elements.ts +1147 -0
  134. package/src/common/ontology.ts +169 -0
  135. package/src/formats/cx/importer.ts +2733 -0
  136. package/src/formats/cx/index.ts +7 -0
  137. package/src/formats/cx2/exporter.ts +1036 -0
  138. package/src/formats/cx2/importer.ts +2187 -0
  139. package/src/formats/cx2/index.ts +7 -0
  140. package/src/formats/gexf/exporter.ts +11 -0
  141. package/src/formats/gexf/importer.ts +2 -2
  142. package/src/formats/gexf/index.ts +1 -1
  143. package/src/formats/gml/exporter.ts +1 -1
  144. package/src/formats/json/dialect.ts +37 -7
  145. package/src/formats/json/importer.ts +210 -132
  146. package/src/formats/json/obographs.ts +563 -0
  147. package/src/formats/obo/importer.ts +1695 -0
  148. package/src/formats/obo/index.ts +7 -0
  149. package/src/formats/obo/syntax.ts +466 -0
  150. package/src/index.ts +11 -0
  151. package/src/registry.ts +8 -1
  152. package/src/sniff.ts +48 -5
  153. package/dist/chunks/importer-B6rKRxzv.js.map +0 -1
  154. package/dist/chunks/writer-DHHfHn11.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The `@graphty/graph-io/cx2` subpath (design/graph-io/cytoscape-and-obo/design.md section 1.3):
3
+ * the CX2 importer and exporter with their option types and code tables.
4
+ */
5
+
6
+ export { CX2_CAPABILITIES, CX2_LOSS, cx2Exporter, type Cx2ExportOptions } from "./exporter.js";
7
+ export { CX2_ISSUE, cx2Importer, type Cx2ImportOptions } from "./importer.js";
@@ -73,6 +73,8 @@ export const GEXF_LOSS = Object.freeze({
73
73
  DECLARED_TYPE: "W_DECLARED_TYPE",
74
74
  /** A node id whose text reads back as the other type under the canonical rule (design section 4.1): a non-integer number, a string of integer text. */
75
75
  ID_TEXT_TYPE: LOSS.ID_TEXT_TYPE,
76
+ /** A numeric edge id column: GEXF edge ids read back as strings. */
77
+ EDGE_ID_TEXT: "W_GEXF_EDGE_ID_TEXT",
76
78
  /** A viz role column (position, color, size, thickness) that is not f32; the importer reads viz values as f32. */
77
79
  VIZ_DTYPE: "W_GEXF_VIZ_DTYPE",
78
80
  /** A plain `weight` edge column reads back as THE weight (the importer's weightFrom default). */
@@ -267,6 +269,15 @@ function planExport(
267
269
  typeChanges,
268
270
  );
269
271
  }
272
+ const edgeIds = snapshot.edges.byRole("id");
273
+ if (edgeIds !== null && edgeIds.dtype !== "string" && edgeIds.dtype !== "dict") {
274
+ note(
275
+ GEXF_LOSS.EDGE_ID_TEXT,
276
+ `edge id column "${edgeIds.meta.name}" is ${edgeIds.dtype}; GEXF edge ids read back as strings`,
277
+ edgeIds.meta.name,
278
+ snapshot.edgeCount - edgeIds.nullCount,
279
+ );
280
+ }
270
281
  const tables = collectTemporalTables(snapshot, version, note);
271
282
  const nodeRoles = collectRoles(snapshot.nodes, "node", note);
272
283
  const edgeRoles = collectRoles(snapshot.edges, "edge", note);
@@ -51,6 +51,7 @@ import {
51
51
  } from "../../common/attributes.js";
52
52
  import {
53
53
  COUNT_HINT_CODE,
54
+ DUPLICATE_ATTRIBUTE_CODE,
54
55
  DUPLICATE_EDGE_ID_CODE,
55
56
  DUPLICATE_NODE_CODE,
56
57
  ID_MERGED_CODE,
@@ -122,8 +123,6 @@ export const ATTRIBUTES_CLASS_CODE = "E_GEXF_ATTRIBUTES_CLASS";
122
123
  export const ATTRIBUTE_ID_CODE = "E_GEXF_ATTRIBUTE_ID";
123
124
  /** Issue code: a graph header value (`defaultedgetype`, `mode`, `timeformat`, ...) outside its set. */
124
125
  export const HEADER_VALUE_CODE = "W_GEXF_HEADER_VALUE";
125
- /** Issue code: a second `<attribute>` with the same id in the same class (the first is kept). */
126
- export const DUPLICATE_ATTRIBUTE_CODE = "W_GEXF_DUPLICATE_ATTRIBUTE";
127
126
  /** Issue code: an `<attribute>` without a type (read as string). */
128
127
  export const ATTRIBUTE_TYPE_CODE = "W_GEXF_ATTRIBUTE_TYPE";
129
128
  /** Issue code: an `<attvalue>` naming an attribute the document never declares (once per id). */
@@ -149,6 +148,7 @@ export const VIZ_VALUE_CODE = "W_GEXF_VIZ_VALUE";
149
148
  export const OPEN_BOUND_CONFLICT_CODE = "W_GEXF_OPEN_BOUND_CONFLICT";
150
149
 
151
150
  export {
151
+ DUPLICATE_ATTRIBUTE_CODE,
152
152
  DUPLICATE_NODE_CODE,
153
153
  ID_MERGED_CODE,
154
154
  MISSING_ENDPOINT_CODE,
@@ -11,6 +11,7 @@ import {
11
11
  COUNT_HINT_CODE,
12
12
  DIRECTION_FORCED_CODE,
13
13
  DIRECTION_REFUSED_CODE,
14
+ DUPLICATE_ATTRIBUTE_CODE,
14
15
  DUPLICATE_EDGE_ID_CODE,
15
16
  DUPLICATE_NODE_CODE,
16
17
  ENCODING_FALLBACK_CODE,
@@ -37,7 +38,6 @@ import {
37
38
  ATTRIBUTE_TYPE_CODE,
38
39
  ATTRIBUTES_CLASS_CODE,
39
40
  ATTVALUE_SHAPE_CODE,
40
- DUPLICATE_ATTRIBUTE_CODE,
41
41
  EDGE_TYPE_CODE,
42
42
  HEADER_VALUE_CODE,
43
43
  MISSING_NODES_CODE,
@@ -140,7 +140,7 @@ const SKIPPED_ROLES: ReadonlySet<string> = new Set([
140
140
  ]);
141
141
 
142
142
  const NODE_RESERVED: ReadonlySet<string> = new Set(["id"]);
143
- const EDGE_RESERVED: ReadonlySet<string> = new Set(["source", "target"]);
143
+ const EDGE_RESERVED: ReadonlySet<string> = new Set(["source", "target", "directed"]);
144
144
  const GRAPH_RESERVED: ReadonlySet<string> = new Set(["node", "edge", "directed", "multigraph"]);
145
145
  const TOP_RESERVED: ReadonlySet<string> = new Set(["graph"]);
146
146
  const NUMBER_TEXT = /^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$/;
@@ -32,17 +32,19 @@ export const JSON_DIALECTS: readonly JsonDialect[] = Object.freeze([
32
32
  ]);
33
33
 
34
34
  /**
35
- * The dialects the importer reads: every JsonDialect plus two it only reads, NetworkX
36
- * adjacency_data (`nodes` plus one neighbour list per node under `adjacency`) and tree_data (a
37
- * nested `id` / `children` record). The exporter writes neither; write node-link instead.
35
+ * The dialects the importer reads: every JsonDialect plus three it only reads, NetworkX
36
+ * adjacency_data (`nodes` plus one neighbour list per node under `adjacency`), tree_data (a
37
+ * nested `id` / `children` record) and OBO Graphs (`graphs[]` of `sub` / `pred` / `obj` edges, the
38
+ * JSON form of the Gene Ontology and the OBO Foundry ontologies). The exporter writes none of them.
38
39
  */
39
- export type JsonImportDialect = JsonDialect | "adjacency" | "tree";
40
+ export type JsonImportDialect = JsonDialect | "adjacency" | "tree" | "obographs";
40
41
 
41
42
  /** Every dialect the importer reads, for option checking and messages. */
42
43
  export const JSON_IMPORT_DIALECTS: readonly JsonImportDialect[] = Object.freeze([
43
44
  ...JSON_DIALECTS,
44
45
  "adjacency",
45
46
  "tree",
47
+ "obographs",
46
48
  ]);
47
49
 
48
50
  /** The key under `meta.extra` that holds the shape record (design section 8.5). */
@@ -116,8 +118,9 @@ export function isJsonImportDialect(value: unknown): value is JsonImportDialect
116
118
 
117
119
  /**
118
120
  * The dialect of a parsed JSON document, by the shape rules of design section 8.2 (Cytoscape:
119
- * `elements` or a top-level array of `{ data }` elements; JGF: `graph.nodes` / `graph.edges` or
120
- * `graphs[]`; graphology: `options.type` / `options.multi`, `key` nodes without `id`, edges with
121
+ * `elements` or a top-level array of `{ data }` elements; OBO Graphs: `graphs[]` whose first graph
122
+ * has an edge with `sub` (or `subj`) and `obj` or a `pred`, or a node with `lbl`, `meta` or an OWL
123
+ * `type`; JGF: `graph.nodes` / `graph.edges` or any other `graphs[]`; graphology: `options.type` / `options.multi`, `key` nodes without `id`, edges with
121
124
  * `undirected` or an `attributes` record; vis: edges with `from` / `to`; d3: `links` without
122
125
  * `directed` / `multigraph` / `graph`; NetworkX adjacency_data: `nodes` and `adjacency` without
123
126
  * `links` / `edges`; NetworkX tree_data: `children` without `nodes` / `links` / `edges`; else
@@ -141,7 +144,7 @@ export function sniffJsonDialect(root: unknown): JsonImportDialect | null {
141
144
  return "jgf";
142
145
  }
143
146
  if (Array.isArray(root.graphs)) {
144
- return "jgf";
147
+ return isOboGraph(firstJsonObject(root.graphs)) ? "obographs" : "jgf";
145
148
  }
146
149
  if (!hasKey(root, "edges") && !hasKey(root, "links")) {
147
150
  if (hasKey(root, "nodes") && hasKey(root, "adjacency")) {
@@ -171,6 +174,31 @@ export function sniffJsonDialect(root: unknown): JsonImportDialect | null {
171
174
  return bare && hasKey(root, "links") ? "d3" : "node-link";
172
175
  }
173
176
 
177
+ /** The node types of OBO Graphs (the OWL entity kinds). */
178
+ const OBOGRAPHS_NODE_TYPES: ReadonlySet<unknown> = new Set(["CLASS", "INDIVIDUAL", "PROPERTY"]);
179
+
180
+ /**
181
+ * Whether a graph of a `graphs[]` document is an OBO Graphs graph rather than JGF: anywhere in its
182
+ * nodes or edges, an edge with `sub` (or the outdated `subj`) and `obj`, or a `pred`, or a node
183
+ * with `lbl`, `meta` or a `type` of CLASS / INDIVIDUAL / PROPERTY (design 1.6: looking only at the
184
+ * first node and edge misses graphs with no edges and graphs whose first node has no `lbl`).
185
+ * @param graph - the first graph, or null
186
+ * @returns true for OBO Graphs
187
+ */
188
+ function isOboGraph(graph: Record<string, unknown> | null): boolean {
189
+ if (graph === null) {
190
+ return false;
191
+ }
192
+ const isEdge = (e: unknown): boolean =>
193
+ isJsonObject(e) && ((hasKey(e, "obj") && (hasKey(e, "sub") || hasKey(e, "subj"))) || hasKey(e, "pred"));
194
+ const isNode = (n: unknown): boolean =>
195
+ isJsonObject(n) && (hasKey(n, "lbl") || hasKey(n, "meta") || OBOGRAPHS_NODE_TYPES.has(n.type));
196
+ return (
197
+ (Array.isArray(graph.edges) && graph.edges.some(isEdge)) ||
198
+ (Array.isArray(graph.nodes) && graph.nodes.some(isNode))
199
+ );
200
+ }
201
+
174
202
  /**
175
203
  * The first object of an array, or null.
176
204
  * @param value - maybe an array
@@ -223,6 +251,8 @@ export const DIALECT_DEFAULT_DIRECTED: Readonly<Record<JsonImportDialect, boolea
223
251
  // networkx adjacency_data declares `directed`; tree_graph always builds a DiGraph
224
252
  adjacency: false,
225
253
  tree: true,
254
+ // OBO Graphs edges point from the subject (child) to the object (parent)
255
+ obographs: true,
226
256
  });
227
257
 
228
258
  /**