@graphty/graph-io 0.3.16 → 0.3.18
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 +1 -1
- package/README.md +29 -3
- package/dist/chunks/{escape-D1f9-cwf.js → escape-D-gZWO26.js} +3 -2
- package/dist/chunks/{escape-D1f9-cwf.js.map → escape-D-gZWO26.js.map} +1 -1
- package/dist/chunks/{importer-D7ZcGCeb.js → importer-Br_QeAeE.js} +4 -3
- package/dist/chunks/{importer-D7ZcGCeb.js.map → importer-Br_QeAeE.js.map} +1 -1
- package/dist/chunks/{importer-CXEiicAN.js → importer-DHagxvDD.js} +4 -3
- package/dist/chunks/{importer-CXEiicAN.js.map → importer-DHagxvDD.js.map} +1 -1
- package/dist/chunks/{importer-C7mnGdr_.js → importer-Du5crN9l.js} +510 -26
- package/dist/chunks/importer-Du5crN9l.js.map +1 -0
- package/dist/chunks/importer-aNJfe0qu.js +1614 -0
- package/dist/chunks/importer-aNJfe0qu.js.map +1 -0
- package/dist/chunks/{importer-B8lsjFWx.js → importer-d0uQxFp6.js} +4 -3
- package/dist/chunks/{importer-B8lsjFWx.js.map → importer-d0uQxFp6.js.map} +1 -1
- package/dist/chunks/ontology-BnrJ4I98.js +113 -0
- package/dist/chunks/ontology-BnrJ4I98.js.map +1 -0
- package/dist/chunks/{records-IHsCfv7s.js → records-Bk9jgodz.js} +2 -2
- package/dist/chunks/{records-IHsCfv7s.js.map → records-Bk9jgodz.js.map} +1 -1
- package/dist/chunks/{writer-BtWpUaiH.js → report-BOk0p5y8.js} +181 -912
- package/dist/chunks/report-BOk0p5y8.js.map +1 -0
- package/dist/chunks/writer-GAdltGmC.js +827 -0
- package/dist/chunks/writer-GAdltGmC.js.map +1 -0
- package/dist/csv.js +4 -3
- package/dist/csv.js.map +1 -1
- package/dist/dot.js +1 -1
- package/dist/gexf.js +3 -2
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +3 -2
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +191 -134
- package/dist/graph-io.js.map +1 -1
- package/dist/graphml.js +1 -1
- package/dist/json.js +1 -1
- package/dist/neo4j.js +4 -3
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.d.ts +1 -0
- package/dist/obo.js +6 -0
- package/dist/obo.js.map +1 -0
- package/dist/pajek.js +1 -1
- package/dist/src/common/codes.d.ts +43 -0
- package/dist/src/common/codes.d.ts.map +1 -1
- package/dist/src/common/codes.js +43 -0
- package/dist/src/common/codes.js.map +1 -1
- package/dist/src/common/input.d.ts +9 -0
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +16 -0
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/ontology.d.ts +59 -0
- package/dist/src/common/ontology.d.ts.map +1 -0
- package/dist/src/common/ontology.js +147 -0
- package/dist/src/common/ontology.js.map +1 -0
- package/dist/src/common/options.d.ts +12 -1
- package/dist/src/common/options.d.ts.map +1 -1
- package/dist/src/common/options.js +51 -1
- package/dist/src/common/options.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts +8 -6
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +26 -3
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts +324 -7
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +154 -23
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/obographs.d.ts +21 -0
- package/dist/src/formats/json/obographs.d.ts.map +1 -0
- package/dist/src/formats/json/obographs.js +476 -0
- package/dist/src/formats/json/obographs.js.map +1 -0
- package/dist/src/formats/obo/importer.d.ts +90 -0
- package/dist/src/formats/obo/importer.d.ts.map +1 -0
- package/dist/src/formats/obo/importer.js +1248 -0
- package/dist/src/formats/obo/importer.js.map +1 -0
- package/dist/src/formats/obo/index.d.ts +7 -0
- package/dist/src/formats/obo/index.d.ts.map +1 -0
- package/dist/src/formats/obo/index.js +7 -0
- package/dist/src/formats/obo/index.js.map +1 -0
- package/dist/src/formats/obo/syntax.d.ts +121 -0
- package/dist/src/formats/obo/syntax.d.ts.map +1 -0
- package/dist/src/formats/obo/syntax.js +424 -0
- package/dist/src/formats/obo/syntax.js.map +1 -0
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/registry.d.ts +21 -3
- package/dist/src/registry.d.ts.map +1 -1
- package/dist/src/registry.js +32 -1
- package/dist/src/registry.js.map +1 -1
- package/dist/src/sniff.d.ts +1 -1
- package/dist/src/sniff.d.ts.map +1 -1
- package/dist/src/sniff.js +23 -3
- package/dist/src/sniff.js.map +1 -1
- package/dist/src/types.d.ts +31 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +6 -1
- package/src/common/codes.ts +58 -0
- package/src/common/input.ts +16 -0
- package/src/common/ontology.ts +169 -0
- package/src/common/options.ts +78 -2
- package/src/formats/json/dialect.ts +37 -7
- package/src/formats/json/importer.ts +206 -28
- package/src/formats/json/obographs.ts +563 -0
- package/src/formats/obo/importer.ts +1695 -0
- package/src/formats/obo/index.ts +7 -0
- package/src/formats/obo/syntax.ts +466 -0
- package/src/index.ts +6 -0
- package/src/registry.ts +38 -3
- package/src/sniff.ts +35 -5
- package/src/types.ts +40 -1
- package/dist/chunks/importer-C7mnGdr_.js.map +0 -1
- package/dist/chunks/writer-BtWpUaiH.js.map +0 -1
|
@@ -43,10 +43,14 @@ import {
|
|
|
43
43
|
|
|
44
44
|
import { uniqueColumnName } from "../../common/attributes.js";
|
|
45
45
|
import {
|
|
46
|
+
AMBIGUOUS_GRAPH_NAME_CODE,
|
|
47
|
+
BAD_VALUE_CODE,
|
|
48
|
+
DANGLING_REFERENCE_CODE,
|
|
46
49
|
DUPLICATE_EDGE_ID_CODE,
|
|
47
50
|
DUPLICATE_NODE_CODE,
|
|
48
51
|
EMPTY_INPUT_CODE,
|
|
49
52
|
ENCODING_FALLBACK_CODE,
|
|
53
|
+
GRAPH_NOT_FOUND_CODE,
|
|
50
54
|
HYPEREDGE_CODE,
|
|
51
55
|
INVALID_ENCODING_CODE,
|
|
52
56
|
INVALID_UTF8_CODE,
|
|
@@ -55,13 +59,15 @@ import {
|
|
|
55
59
|
MULTIPLE_GRAPHS_CODE,
|
|
56
60
|
OPTION_IGNORED_CODE,
|
|
57
61
|
SYNTAX_CODE,
|
|
62
|
+
TOO_LARGE_CODE,
|
|
58
63
|
UNKNOWN_ENCODING_CODE,
|
|
59
64
|
UNKNOWN_PARENT_CODE,
|
|
60
65
|
} from "../../common/codes.js";
|
|
61
66
|
import { DirectionResolver, type EdgeKind } from "../../common/direction.js";
|
|
62
67
|
import { ID_MERGED_CODE, IdCoercer } from "../../common/ids.js";
|
|
63
|
-
import { readText, throwIfAborted } from "../../common/input.js";
|
|
68
|
+
import { readText, textChunks, throwIfAborted } from "../../common/input.js";
|
|
64
69
|
import {
|
|
70
|
+
chooseGraph,
|
|
65
71
|
type ImportFormatDefaults,
|
|
66
72
|
reportSinkOptions,
|
|
67
73
|
reportUnusedOptions,
|
|
@@ -71,7 +77,14 @@ import {
|
|
|
71
77
|
} from "../../common/options.js";
|
|
72
78
|
import { ImportReportBuilder } from "../../common/report.js";
|
|
73
79
|
import { weightFromValue } from "../../common/weights.js";
|
|
74
|
-
import {
|
|
80
|
+
import {
|
|
81
|
+
type CommonImportOptions,
|
|
82
|
+
type GraphChoiceOptions,
|
|
83
|
+
type GraphImporter,
|
|
84
|
+
type GraphListing,
|
|
85
|
+
type ImportInput,
|
|
86
|
+
type ImportReport,
|
|
87
|
+
} from "../../types.js";
|
|
75
88
|
import {
|
|
76
89
|
CLASSES_COLUMN,
|
|
77
90
|
CYTOSCAPE_ELEMENT_KEYS,
|
|
@@ -91,9 +104,14 @@ import {
|
|
|
91
104
|
sniffJsonDialect,
|
|
92
105
|
SUFFIX,
|
|
93
106
|
} from "./dialect.js";
|
|
107
|
+
import { importObographs } from "./obographs.js";
|
|
94
108
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
109
|
+
/**
|
|
110
|
+
* The format-specific options of the JSON importer. `graphIndex` / `graphName` choose one graph of
|
|
111
|
+
* a JGF or OBO Graphs `graphs` array (the first by default); a graph's name is its `id`, else its
|
|
112
|
+
* label.
|
|
113
|
+
*/
|
|
114
|
+
export interface JsonImportOptions extends GraphChoiceOptions {
|
|
97
115
|
/** The dialect to read; "auto" (default) sniffs the parsed document. */
|
|
98
116
|
dialect?: JsonImportDialect | "auto" | undefined;
|
|
99
117
|
/**
|
|
@@ -112,8 +130,18 @@ export interface JsonImportOptions {
|
|
|
112
130
|
* every endpoint is an integer below the node count and no node id is a number.
|
|
113
131
|
*/
|
|
114
132
|
indexLinks?: boolean | "auto" | undefined;
|
|
115
|
-
/**
|
|
116
|
-
|
|
133
|
+
/**
|
|
134
|
+
* obographs: "curie" (default) reads `http://purl.obolibrary.org/obo/GO_0008150` as `GO:0008150`
|
|
135
|
+
* and `.../obo/go#regulates` as `regulates`, the identifiers the `.obo` file of the same ontology
|
|
136
|
+
* writes; "iri" keeps every IRI as written.
|
|
137
|
+
*/
|
|
138
|
+
oboIds?: "curie" | "iri" | undefined;
|
|
139
|
+
/**
|
|
140
|
+
* obographs: "metadata" (default) keeps PROPERTY nodes and their subPropertyOf / inverseOf edges
|
|
141
|
+
* in `meta.extra.obographs`, as the OBO importer keeps `[Typedef]` frames; "nodes" makes them
|
|
142
|
+
* nodes and edges.
|
|
143
|
+
*/
|
|
144
|
+
typedefs?: "metadata" | "nodes" | undefined;
|
|
117
145
|
/**
|
|
118
146
|
* node-link / d3 / vis / graphology: where the node array is, as a dotted path of object keys
|
|
119
147
|
* from the document root (`"data.nodes"`); the object holding it is read as the graph record
|
|
@@ -157,7 +185,7 @@ export const JSON_ISSUE = Object.freeze({
|
|
|
157
185
|
/** An index endpoint is not an integer below the node count, or names a skipped node. */
|
|
158
186
|
BAD_INDEX: "E_BAD_INDEX",
|
|
159
187
|
/** A declared field has the wrong JSON type (JGF label / relation / metadata, Cytoscape position / classes). */
|
|
160
|
-
BAD_VALUE:
|
|
188
|
+
BAD_VALUE: BAD_VALUE_CODE,
|
|
161
189
|
/** A graph-level flag (`directed`, `multigraph`, graphology `options`) has the wrong type; the default is used. */
|
|
162
190
|
BAD_FLAG: "W_BAD_FLAG",
|
|
163
191
|
/** A Cytoscape `data.parent` names an unknown node. */
|
|
@@ -170,8 +198,18 @@ export const JSON_ISSUE = Object.freeze({
|
|
|
170
198
|
ID_MERGED: ID_MERGED_CODE,
|
|
171
199
|
/** Edge ids of mixed JSON types were stored as text. */
|
|
172
200
|
EDGE_ID_STRINGIFIED: "W_EDGE_ID_STRINGIFIED",
|
|
173
|
-
/** A JGF `graphs` array holds more than one graph; only
|
|
201
|
+
/** A JGF or OBO Graphs `graphs` array holds more than one graph; only the chosen one is read. */
|
|
174
202
|
MULTIPLE_GRAPHS: MULTIPLE_GRAPHS_CODE,
|
|
203
|
+
/** `graphIndex` is beyond the `graphs` array, or `graphName` names none of its graphs (fatal). */
|
|
204
|
+
GRAPH_NOT_FOUND: GRAPH_NOT_FOUND_CODE,
|
|
205
|
+
/** `graphName` names more than one graph of the `graphs` array (fatal). */
|
|
206
|
+
AMBIGUOUS_GRAPH_NAME: AMBIGUOUS_GRAPH_NAME_CODE,
|
|
207
|
+
/** obographs: an edge uses the outdated `subj` key of the OBO Graphs README; it is read as `sub`. */
|
|
208
|
+
OBOGRAPHS_SUBJ: "W_JSON_OBOGRAPHS_SUBJ",
|
|
209
|
+
/** obographs: an edge endpoint missing from `nodes` (a placeholder node is made, or the edge dropped under addMissingNodes false). */
|
|
210
|
+
DANGLING_REFERENCE: DANGLING_REFERENCE_CODE,
|
|
211
|
+
/** The document is longer than one JavaScript string can hold (fatal; category unsupported). */
|
|
212
|
+
TOO_LARGE: TOO_LARGE_CODE,
|
|
175
213
|
/** JGF hyperedges under the "error" policy. */
|
|
176
214
|
HYPEREDGE: HYPEREDGE_CODE,
|
|
177
215
|
/** JGF hyperedges skipped under the default "skip" policy. */
|
|
@@ -257,7 +295,7 @@ const GRAPHOLOGY_EDGE_KEYS: ReadonlySet<string> = new Set(["key", "source", "tar
|
|
|
257
295
|
const VIS_SOURCE_KEYS: readonly string[] = Object.freeze(["from"]);
|
|
258
296
|
const VIS_TARGET_KEYS: readonly string[] = Object.freeze(["to"]);
|
|
259
297
|
|
|
260
|
-
type JsonRecord = Record<string, unknown>;
|
|
298
|
+
export type JsonRecord = Record<string, unknown>;
|
|
261
299
|
|
|
262
300
|
/** An edge id column declared from a scan of the file's edge ids. */
|
|
263
301
|
interface EdgeIdColumn {
|
|
@@ -285,6 +323,12 @@ interface ResolvedJsonOptions {
|
|
|
285
323
|
readonly targetKey: string | null;
|
|
286
324
|
readonly indexLinks: boolean | "auto";
|
|
287
325
|
readonly graphIndex: number;
|
|
326
|
+
/** The caller's graphIndex / graphName, as given, for chooseGraph(). */
|
|
327
|
+
readonly choice: GraphChoiceOptions;
|
|
328
|
+
/** obographs: CURIE or IRI ids. */
|
|
329
|
+
readonly oboIds: "curie" | "iri";
|
|
330
|
+
/** obographs: PROPERTY nodes as metadata or as nodes. */
|
|
331
|
+
readonly typedefs: "metadata" | "nodes";
|
|
288
332
|
/** The dotted path segments of the node array, or null for the root's own nodes key. */
|
|
289
333
|
readonly nodesPath: readonly string[] | null;
|
|
290
334
|
/** The dotted path segments of the edge array, or null for the edges / links key beside the nodes. */
|
|
@@ -312,6 +356,14 @@ function resolveJsonOptions(options: (JsonImportOptions & CommonImportOptions) |
|
|
|
312
356
|
if (!Number.isInteger(graphIndex) || graphIndex < 0) {
|
|
313
357
|
throw unsupportedOption("graphIndex", graphIndex, ["a non-negative integer"]);
|
|
314
358
|
}
|
|
359
|
+
const oboIds = o.oboIds ?? "curie";
|
|
360
|
+
if (oboIds !== "curie" && oboIds !== "iri") {
|
|
361
|
+
throw unsupportedOption("oboIds", oboIds, ["curie", "iri"]);
|
|
362
|
+
}
|
|
363
|
+
const typedefs = o.typedefs ?? "metadata";
|
|
364
|
+
if (typedefs !== "metadata" && typedefs !== "nodes") {
|
|
365
|
+
throw unsupportedOption("typedefs", typedefs, ["metadata", "nodes"]);
|
|
366
|
+
}
|
|
315
367
|
const nodesPath = pathOption("nodesPath", o.nodesPath);
|
|
316
368
|
const edgesPath = pathOption("edgesPath", o.edgesPath);
|
|
317
369
|
if ((nodesPath !== null || edgesPath !== null) && dialect !== "auto" && !PATH_DIALECTS.has(dialect)) {
|
|
@@ -325,6 +377,9 @@ function resolveJsonOptions(options: (JsonImportOptions & CommonImportOptions) |
|
|
|
325
377
|
targetKey: keyOption("targetKey", o.targetKey),
|
|
326
378
|
indexLinks,
|
|
327
379
|
graphIndex,
|
|
380
|
+
choice: { graphIndex: o.graphIndex, graphName: o.graphName },
|
|
381
|
+
oboIds,
|
|
382
|
+
typedefs,
|
|
328
383
|
nodesPath,
|
|
329
384
|
edgesPath,
|
|
330
385
|
};
|
|
@@ -665,7 +720,7 @@ class AttributeWriter {
|
|
|
665
720
|
/**
|
|
666
721
|
* Everything one import call shares between the dialect readers.
|
|
667
722
|
*/
|
|
668
|
-
class ImportContext {
|
|
723
|
+
export class ImportContext {
|
|
669
724
|
readonly sink: GraphSink;
|
|
670
725
|
|
|
671
726
|
readonly report: ImportReportBuilder;
|
|
@@ -2338,38 +2393,140 @@ function importJgf(ctx: ImportContext, root: JsonRecord): void {
|
|
|
2338
2393
|
}
|
|
2339
2394
|
|
|
2340
2395
|
/**
|
|
2341
|
-
* The graph object of a JGF document: `graph`, or `graphs
|
|
2396
|
+
* The graph object of a JGF document: `graph`, or the graph of `graphs` that graphIndex /
|
|
2397
|
+
* graphName choose (chooseGraph()).
|
|
2342
2398
|
* @param ctx - the context
|
|
2343
2399
|
* @param root - the document
|
|
2344
2400
|
* @returns the graph object; the import fails when there is none
|
|
2345
2401
|
*/
|
|
2346
2402
|
function jgfGraphOf(ctx: ImportContext, root: JsonRecord): JsonRecord {
|
|
2403
|
+
return isJsonObject(root.graph) ? root.graph : chosenGraph(ctx, root, "JGF");
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
/**
|
|
2407
|
+
* The graph of a `graphs` array (JGF, OBO Graphs) that graphIndex / graphName choose, with
|
|
2408
|
+
* W_MULTIPLE_GRAPHS when the others are skipped; importAll() reads graphs[graphIndex].
|
|
2409
|
+
* @param ctx - the context
|
|
2410
|
+
* @param root - the document
|
|
2411
|
+
* @param what - the dialect's name, for the messages
|
|
2412
|
+
* @returns the graph object; the import fails when there is none
|
|
2413
|
+
*/
|
|
2414
|
+
export function chosenGraph(ctx: ImportContext, root: JsonRecord, what: string): JsonRecord {
|
|
2347
2415
|
const { report } = ctx;
|
|
2348
|
-
if (isJsonObject(root.graph)) {
|
|
2349
|
-
return root.graph;
|
|
2350
|
-
}
|
|
2351
2416
|
const graphs = arraySection(root.graphs, "graphs", report) ?? [];
|
|
2352
2417
|
if (graphs.length === 0) {
|
|
2353
|
-
report.fail(JSON_ISSUE.SHAPE,
|
|
2418
|
+
report.fail(JSON_ISSUE.SHAPE, `a ${what} document needs a graph object or a non-empty graphs array`);
|
|
2354
2419
|
}
|
|
2420
|
+
const index =
|
|
2421
|
+
ctx.json.all === true ? ctx.json.graphIndex : chooseGraph(graphs.map(graphNameOf), ctx.json.choice, report);
|
|
2355
2422
|
if (graphs.length > 1 && ctx.json.all !== true) {
|
|
2356
2423
|
report.warning(
|
|
2357
2424
|
"unsupported",
|
|
2358
2425
|
JSON_ISSUE.MULTIPLE_GRAPHS,
|
|
2359
|
-
`the document holds ${graphs.length} graphs; only graphs[${
|
|
2426
|
+
`the document holds ${graphs.length} graphs; only graphs[${index}] is read (${graphs.length - 1} skipped), importAll() reads every one`,
|
|
2360
2427
|
{ element: "graphs" },
|
|
2361
2428
|
);
|
|
2362
2429
|
}
|
|
2363
|
-
|
|
2364
|
-
report.fail(JSON_ISSUE.SHAPE, `graphIndex ${ctx.json.graphIndex} is beyond the ${graphs.length} graph(s)`);
|
|
2365
|
-
}
|
|
2366
|
-
const graph = graphs[ctx.json.graphIndex];
|
|
2430
|
+
const graph = graphs[index];
|
|
2367
2431
|
if (!isJsonObject(graph)) {
|
|
2368
|
-
return report.fail(JSON_ISSUE.SHAPE, `graphs[${
|
|
2432
|
+
return report.fail(JSON_ISSUE.SHAPE, `graphs[${index}] is not an object`);
|
|
2369
2433
|
}
|
|
2370
2434
|
return graph;
|
|
2371
2435
|
}
|
|
2372
2436
|
|
|
2437
|
+
/**
|
|
2438
|
+
* The name a graph of a `graphs` array is listed and chosen by: its `id`, else its `label` (JGF)
|
|
2439
|
+
* or `lbl` (OBO Graphs).
|
|
2440
|
+
* @param graph - the graph
|
|
2441
|
+
* @returns the name, or null
|
|
2442
|
+
*/
|
|
2443
|
+
function graphNameOf(graph: unknown): string | null {
|
|
2444
|
+
if (!isJsonObject(graph)) {
|
|
2445
|
+
return null;
|
|
2446
|
+
}
|
|
2447
|
+
for (const key of ["id", "label", "lbl"]) {
|
|
2448
|
+
if (typeof graph[key] === "string") {
|
|
2449
|
+
return graph[key];
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
return null;
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
/**
|
|
2456
|
+
* How many elements a nodes or edges section holds: an array's length, an object's key count, 0
|
|
2457
|
+
* when absent, null for anything else.
|
|
2458
|
+
* @param section - the section
|
|
2459
|
+
* @returns the count, or null
|
|
2460
|
+
*/
|
|
2461
|
+
function countOf(section: unknown): number | null {
|
|
2462
|
+
if (section === undefined || section === null) {
|
|
2463
|
+
return 0;
|
|
2464
|
+
}
|
|
2465
|
+
if (Array.isArray(section)) {
|
|
2466
|
+
return section.length;
|
|
2467
|
+
}
|
|
2468
|
+
return isJsonObject(section) ? Object.keys(section).length : null;
|
|
2469
|
+
}
|
|
2470
|
+
|
|
2471
|
+
/**
|
|
2472
|
+
* The graphs of a parsed document, for listGraphs(): each entry of a JGF or OBO Graphs `graphs`
|
|
2473
|
+
* array with its name and counts; any other document holds one graph.
|
|
2474
|
+
* @param root - the parsed document
|
|
2475
|
+
* @param dialect - its dialect
|
|
2476
|
+
* @returns the listings
|
|
2477
|
+
*/
|
|
2478
|
+
function listingsOf(root: unknown, dialect: JsonImportDialect): GraphListing[] {
|
|
2479
|
+
if ((dialect === "jgf" || dialect === "obographs") && isJsonObject(root)) {
|
|
2480
|
+
if (Array.isArray(root.graphs) && !isJsonObject(root.graph)) {
|
|
2481
|
+
return root.graphs.map((graph: unknown, index) => ({
|
|
2482
|
+
index,
|
|
2483
|
+
name: graphNameOf(graph),
|
|
2484
|
+
nodes: isJsonObject(graph) ? countOf(graph.nodes) : null,
|
|
2485
|
+
edges: isJsonObject(graph) ? countOf(graph.edges) : null,
|
|
2486
|
+
}));
|
|
2487
|
+
}
|
|
2488
|
+
if (isJsonObject(root.graph)) {
|
|
2489
|
+
const { graph } = root;
|
|
2490
|
+
return [{ index: 0, name: graphNameOf(graph), nodes: countOf(graph.nodes), edges: countOf(graph.edges) }];
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
return [{ index: 0, name: null, nodes: null, edges: null }];
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
/** The longest string V8 makes (2^29 - 24 UTF-16 code units); a longer document cannot be one JSON.parse input. */
|
|
2497
|
+
const MAX_TEXT_LENGTH = 2 ** 29 - 24;
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* Read the whole input as one string, failing with E_TOO_LARGE (category unsupported) before the
|
|
2501
|
+
* join when it is longer than one JavaScript string can hold (OBO Graphs files such as
|
|
2502
|
+
* ncbitaxon.json are; design 7.1 defers streaming the JSON reader).
|
|
2503
|
+
* @param input - the input
|
|
2504
|
+
* @param report - the report
|
|
2505
|
+
* @param options - cancellation, progress and encoding
|
|
2506
|
+
* @returns the text
|
|
2507
|
+
*/
|
|
2508
|
+
async function readJsonText(
|
|
2509
|
+
input: ImportInput,
|
|
2510
|
+
report: ImportReportBuilder,
|
|
2511
|
+
options: ResolvedImportOptions,
|
|
2512
|
+
): Promise<string> {
|
|
2513
|
+
if (typeof input === "string") {
|
|
2514
|
+
return readText(input, report, options);
|
|
2515
|
+
}
|
|
2516
|
+
const parts: string[] = [];
|
|
2517
|
+
let length = 0;
|
|
2518
|
+
for await (const chunk of textChunks(input, report, options)) {
|
|
2519
|
+
length += chunk.length;
|
|
2520
|
+
if (length > MAX_TEXT_LENGTH) {
|
|
2521
|
+
const message = `the document is longer than ${MAX_TEXT_LENGTH} characters, the most one JavaScript string holds`;
|
|
2522
|
+
report.error("unsupported", JSON_ISSUE.TOO_LARGE, message);
|
|
2523
|
+
throw report.abort(message, { code: JSON_ISSUE.TOO_LARGE });
|
|
2524
|
+
}
|
|
2525
|
+
parts.push(chunk);
|
|
2526
|
+
}
|
|
2527
|
+
return parts.length === 1 ? parts[0] : parts.join("");
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2373
2530
|
/**
|
|
2374
2531
|
* Push a JGF node: `label` into the declared column, `metadata` as attributes.
|
|
2375
2532
|
* @param ctx - the context
|
|
@@ -2840,12 +2997,32 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
2840
2997
|
const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
|
|
2841
2998
|
const json = resolveJsonOptions(options);
|
|
2842
2999
|
const report = new ImportReportBuilder("json", resolved.errorLimit);
|
|
2843
|
-
const text = await
|
|
3000
|
+
const text = await readJsonText(input, report, resolved);
|
|
2844
3001
|
const { root, dialect } = documentOf(parseDocument(text, report), json, report);
|
|
2845
3002
|
readGraph(root, dialect, sink, report, resolved, json, options);
|
|
2846
3003
|
return report.finish();
|
|
2847
3004
|
},
|
|
2848
3005
|
|
|
3006
|
+
/**
|
|
3007
|
+
* List the graphs of a JSON document without importing them: each entry of a JGF or OBO
|
|
3008
|
+
* Graphs `graphs` array with its name (`id`, else its label) and node and edge counts; any
|
|
3009
|
+
* other document holds one graph.
|
|
3010
|
+
* @param input - the text, bytes or stream
|
|
3011
|
+
* @param options - format-specific and common options
|
|
3012
|
+
* @returns one listing per graph
|
|
3013
|
+
*/
|
|
3014
|
+
async listGraphs(
|
|
3015
|
+
input: ImportInput,
|
|
3016
|
+
options?: JsonImportOptions & CommonImportOptions,
|
|
3017
|
+
): Promise<readonly GraphListing[]> {
|
|
3018
|
+
const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
|
|
3019
|
+
const json = resolveJsonOptions(options);
|
|
3020
|
+
const report = new ImportReportBuilder("json", resolved.errorLimit);
|
|
3021
|
+
const text = await readJsonText(input, report, resolved);
|
|
3022
|
+
const { root, dialect } = documentOf(parseDocument(text, report), json, report);
|
|
3023
|
+
return listingsOf(root, dialect);
|
|
3024
|
+
},
|
|
3025
|
+
|
|
2849
3026
|
/**
|
|
2850
3027
|
* Read every graph of a JSON document: each entry of a JGF `graphs` array into its own sink;
|
|
2851
3028
|
* any other document holds one graph.
|
|
@@ -2862,12 +3039,9 @@ export const jsonImporter: GraphImporter<JsonImportOptions> = Object.freeze({
|
|
|
2862
3039
|
const resolved = resolveImportOptions(options, FORMAT_DEFAULTS);
|
|
2863
3040
|
const json = resolveJsonOptions(options);
|
|
2864
3041
|
const first = new ImportReportBuilder("json", resolved.errorLimit);
|
|
2865
|
-
const text = await
|
|
3042
|
+
const text = await readJsonText(input, first, resolved);
|
|
2866
3043
|
const { root, dialect } = documentOf(parseDocument(text, first), json, first);
|
|
2867
|
-
const graphs =
|
|
2868
|
-
dialect === "jgf" && isJsonObject(root) && !isJsonObject(root.graph) && Array.isArray(root.graphs)
|
|
2869
|
-
? root.graphs.length
|
|
2870
|
-
: 1;
|
|
3044
|
+
const graphs = listingsOf(root, dialect).length;
|
|
2871
3045
|
const reports: ImportReport[] = [];
|
|
2872
3046
|
for (let i = 0; i < Math.max(graphs, 1); i++) {
|
|
2873
3047
|
const report = i === 0 ? first : new ImportReportBuilder("json", resolved.errorLimit);
|
|
@@ -2898,7 +3072,8 @@ function readGraph(
|
|
|
2898
3072
|
options: (JsonImportOptions & CommonImportOptions) | undefined,
|
|
2899
3073
|
): void {
|
|
2900
3074
|
const ctx = new ImportContext(sink, report, resolved, json, options?.defaultDirected !== undefined);
|
|
2901
|
-
|
|
3075
|
+
// the obographs reader refuses missing endpoints itself, so addMissingNodes false holds on any sink
|
|
3076
|
+
reportSinkOptions(sink, options, report, dialect === "obographs");
|
|
2902
3077
|
reportUnusedOptions(options, report, USED_OPTIONS);
|
|
2903
3078
|
if (dialect === "cytoscape") {
|
|
2904
3079
|
importCytoscape(ctx, root);
|
|
@@ -2928,6 +3103,9 @@ function readGraph(
|
|
|
2928
3103
|
case "tree":
|
|
2929
3104
|
importTree(ctx, doc);
|
|
2930
3105
|
break;
|
|
3106
|
+
case "obographs":
|
|
3107
|
+
importObographs(ctx, doc);
|
|
3108
|
+
break;
|
|
2931
3109
|
default: {
|
|
2932
3110
|
const name: string = dialect;
|
|
2933
3111
|
throw new GraphFormatError("E_UNSUPPORTED", `unknown dialect ${name}`, {
|