@graphty/graph-io 0.3.30 → 0.3.32

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 (72) hide show
  1. package/dist/chunks/{choose-BfOLq6OX.js → choose-BJq_vp1b.js} +2 -2
  2. package/dist/chunks/{choose-BfOLq6OX.js.map → choose-BJq_vp1b.js.map} +1 -1
  3. package/dist/chunks/{constants-M8Qsjz1C.js → constants-Bw-6JQuC.js} +2 -2
  4. package/dist/chunks/{constants-M8Qsjz1C.js.map → constants-Bw-6JQuC.js.map} +1 -1
  5. package/dist/chunks/{escape-UMMpQZqs.js → escape-CnTfa_y8.js} +2 -2
  6. package/dist/chunks/{escape-UMMpQZqs.js.map → escape-CnTfa_y8.js.map} +1 -1
  7. package/dist/chunks/{export-Dq4U8Ubq.js → export-ZucVKiU4.js} +13 -3
  8. package/dist/chunks/{export-Dq4U8Ubq.js.map → export-ZucVKiU4.js.map} +1 -1
  9. package/dist/chunks/{exporter-BUosqV0K.js → exporter-BOLJIzvA.js} +5 -5
  10. package/dist/chunks/exporter-BOLJIzvA.js.map +1 -0
  11. package/dist/chunks/{exporter-Bd_VZ9ph.js → exporter-DRNkA2k0.js} +5 -5
  12. package/dist/chunks/{exporter-Bd_VZ9ph.js.map → exporter-DRNkA2k0.js.map} +1 -1
  13. package/dist/chunks/{exporter-DEk_XLLs.js → exporter-DgDjwLOm.js} +7 -8
  14. package/dist/chunks/exporter-DgDjwLOm.js.map +1 -0
  15. package/dist/chunks/{exporter-COgVm-ff.js → exporter-hc2ZuqVe.js} +4 -4
  16. package/dist/chunks/{exporter-COgVm-ff.js.map → exporter-hc2ZuqVe.js.map} +1 -1
  17. package/dist/chunks/{importer-Cj4-lXlv.js → importer-BAlFKpHP.js} +6 -6
  18. package/dist/chunks/{importer-Cj4-lXlv.js.map → importer-BAlFKpHP.js.map} +1 -1
  19. package/dist/chunks/{importer-AQ3KpNHz.js → importer-ByCd2CAY.js} +5 -5
  20. package/dist/chunks/{importer-AQ3KpNHz.js.map → importer-ByCd2CAY.js.map} +1 -1
  21. package/dist/chunks/{importer-DwSZvMDq.js → importer-CaLIrkjL.js} +5 -5
  22. package/dist/chunks/{importer-DwSZvMDq.js.map → importer-CaLIrkjL.js.map} +1 -1
  23. package/dist/chunks/{importer-CV8au8gH.js → importer-D7xB8oVV.js} +4 -4
  24. package/dist/chunks/{importer-CV8au8gH.js.map → importer-D7xB8oVV.js.map} +1 -1
  25. package/dist/chunks/{importer-BsZa2Phx.js → importer-DsNj_zoG.js} +3 -3
  26. package/dist/chunks/{importer-BsZa2Phx.js.map → importer-DsNj_zoG.js.map} +1 -1
  27. package/dist/chunks/{importer-Dkn79TjB.js → importer-WPz-AUz8.js} +4 -4
  28. package/dist/chunks/{importer-Dkn79TjB.js.map → importer-WPz-AUz8.js.map} +1 -1
  29. package/dist/chunks/{importer-DQsgmhxc.js → importer-rjFCmKfx.js} +4 -4
  30. package/dist/chunks/{importer-DQsgmhxc.js.map → importer-rjFCmKfx.js.map} +1 -1
  31. package/dist/chunks/{json-elements-CreeKoaH.js → json-elements-CkC87El2.js} +3 -3
  32. package/dist/chunks/{json-elements-CreeKoaH.js.map → json-elements-CkC87El2.js.map} +1 -1
  33. package/dist/chunks/{ontology-export-C5Xd3W-7.js → ontology-export-BRErBO6J.js} +11 -9
  34. package/dist/chunks/ontology-export-BRErBO6J.js.map +1 -0
  35. package/dist/chunks/{records-BwaH_2ll.js → records-DwfSjH8c.js} +2 -2
  36. package/dist/chunks/{records-BwaH_2ll.js.map → records-DwfSjH8c.js.map} +1 -1
  37. package/dist/chunks/{text-4HHe6YH3.js → text-BEEWD-G1.js} +2 -2
  38. package/dist/chunks/{text-4HHe6YH3.js.map → text-BEEWD-G1.js.map} +1 -1
  39. package/dist/csv.js +4 -4
  40. package/dist/cx.js +1 -1
  41. package/dist/cx2.js +2 -2
  42. package/dist/cys.js +1 -1
  43. package/dist/dot.js +1 -1
  44. package/dist/gexf.js +2 -2
  45. package/dist/gml.js +3 -3
  46. package/dist/graph-io.js +26 -26
  47. package/dist/graphml.js +2 -2
  48. package/dist/json.js +1 -1
  49. package/dist/neo4j.js +3 -3
  50. package/dist/obo.js +1 -1
  51. package/dist/pajek.js +1 -1
  52. package/dist/src/common/export.d.ts +6 -1
  53. package/dist/src/common/export.d.ts.map +1 -1
  54. package/dist/src/common/export.js +18 -2
  55. package/dist/src/common/export.js.map +1 -1
  56. package/dist/src/common/ontology-export.d.ts.map +1 -1
  57. package/dist/src/common/ontology-export.js +11 -15
  58. package/dist/src/common/ontology-export.js.map +1 -1
  59. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  60. package/dist/src/formats/cx2/exporter.js +2 -4
  61. package/dist/src/formats/cx2/exporter.js.map +1 -1
  62. package/dist/src/formats/obo/exporter.d.ts +2 -2
  63. package/dist/src/formats/obo/exporter.js +2 -2
  64. package/dist/xgmml.js +2 -2
  65. package/package.json +2 -2
  66. package/src/common/export.ts +25 -3
  67. package/src/common/ontology-export.ts +12 -17
  68. package/src/formats/cx2/exporter.ts +2 -4
  69. package/src/formats/obo/exporter.ts +2 -2
  70. package/dist/chunks/exporter-BUosqV0K.js.map +0 -1
  71. package/dist/chunks/exporter-DEk_XLLs.js.map +0 -1
  72. package/dist/chunks/ontology-export-C5Xd3W-7.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graphty/graph-io",
3
- "version": "0.3.30",
3
+ "version": "0.3.32",
4
4
  "description": "Read and write 13 graph file formats (JSON, GraphML, GEXF, CSV, GML, DOT, Pajek, Neo4j CSV, XGMML, CX2, CX, OBO, Cytoscape sessions) as @graphty/graph-format snapshots",
5
5
  "author": "Adam Powers <apowers@ato.ms>",
6
6
  "type": "module",
@@ -121,7 +121,7 @@
121
121
  },
122
122
  "homepage": "https://graphty.app/docs/graph-io/",
123
123
  "dependencies": {
124
- "@graphty/graph-format": "^1.3.8"
124
+ "@graphty/graph-format": "^1.3.10"
125
125
  },
126
126
  "peerDependencies": {
127
127
  "@graphty/graph-format": "^1.0.0"
@@ -293,9 +293,14 @@ export interface CheckExtras {
293
293
  /**
294
294
  * Roles the format writes into a fixed slot whose importer reads them back under a fixed name;
295
295
  * a role column named otherwise is reported as W_COLUMN_NAME_CHANGED (role -> the name the
296
- * importer gives it). Defaults to none.
296
+ * importer gives it). Defaults to none. `nodeRoleNames` and `edgeRoleNames` override it for one
297
+ * kind, for a format whose slot exists for nodes only (or edges only).
297
298
  */
298
299
  readonly roleNames?: Readonly<Record<string, string>> | undefined;
300
+ /** Like `roleNames`, for node columns only. */
301
+ readonly nodeRoleNames?: Readonly<Record<string, string>> | undefined;
302
+ /** Like `roleNames`, for edge columns only: `{}` for a format with no edge slot for any role. */
303
+ readonly edgeRoleNames?: Readonly<Record<string, string>> | undefined;
299
304
  /**
300
305
  * False for a format that writes no node or edge attributes except the ones whose role is in
301
306
  * `roles`: every other attribute column is reported once as W_COLUMN_DROPPED, instead of the
@@ -555,6 +560,22 @@ function charsetText(charset: ExportCapabilities["idCharset"], n: number): strin
555
560
  }
556
561
  }
557
562
 
563
+ /**
564
+ * The role names of one kind (CheckExtras.nodeRoleNames / edgeRoleNames, else roleNames).
565
+ * @param domain - "node", "edge" or "graph"
566
+ * @param extras - format extras
567
+ * @returns the role names, or undefined when the extras name none
568
+ */
569
+ function roleNamesOf(domain: string, extras: CheckExtras): Readonly<Record<string, string>> | undefined {
570
+ if (domain === "node") {
571
+ return extras.nodeRoleNames ?? extras.roleNames;
572
+ }
573
+ if (domain === "edge") {
574
+ return extras.edgeRoleNames ?? extras.roleNames;
575
+ }
576
+ return extras.roleNames;
577
+ }
578
+
558
579
  /**
559
580
  * Whether a format writes the attributes of one kind (CheckExtras.nodeAttributes / edgeAttributes, else attributes).
560
581
  * @param domain - "node", "edge" or "graph"
@@ -622,8 +643,9 @@ function checkColumns(
622
643
  continue;
623
644
  }
624
645
  const label = `${domain} column "${name}"`;
625
- if (role !== null && extras.roleNames !== undefined) {
626
- const fixed = extras.roleNames[role];
646
+ const roleNames = roleNamesOf(domain, extras);
647
+ if (role !== null && roleNames !== undefined) {
648
+ const fixed = roleNames[role];
627
649
  if (fixed !== undefined && fixed !== name) {
628
650
  note(
629
651
  LOSS.COLUMN_NAME_CHANGED,
@@ -8,7 +8,7 @@
8
8
 
9
9
  import { type Column, GraphFormatError, type GraphSnapshot } from "@graphty/graph-format";
10
10
 
11
- import { type ExportCapabilities, type LossNote } from "../types.js";
11
+ import { type ExportCapabilities } from "../types.js";
12
12
  import { COLUMN_AS_PROPERTY_VALUE_CODE, ROLE_ASSUMED_CODE } from "./codes.js";
13
13
  import { checkCapabilities, LOSS } from "./export.js";
14
14
  import { formatNumber } from "./format.js";
@@ -32,7 +32,8 @@ const SUPERSEDED: ReadonlySet<string> = new Set([
32
32
 
33
33
  /** The roles the ontology formats have a slot for, and the column names their importers give them. */
34
34
  const SLOT_ROLES: ReadonlySet<string> = new Set(["label", "kind"]);
35
- const ROLE_NAMES: Readonly<Record<string, string>> = Object.freeze({ label: "name", kind: "relation" });
35
+ const NODE_ROLE_NAMES: Readonly<Record<string, string>> = Object.freeze({ label: "name" });
36
+ const EDGE_ROLE_NAMES: Readonly<Record<string, string>> = Object.freeze({ kind: "relation" });
36
37
 
37
38
  /**
38
39
  * The roles whose columns are never written as values (their loss notes come from checkCapabilities()).
@@ -335,8 +336,15 @@ export function capabilityNotes(
335
336
  const { label, note } = spec;
336
337
  let fatal: GraphFormatError | null = null;
337
338
  const kind = snapshot.edges.byRole("kind");
338
- for (const gen of checkCapabilities(snapshot, spec.caps, common, { roles: SLOT_ROLES, roleNames: ROLE_NAMES })) {
339
- if (SUPERSEDED.has(gen.code) || (gen.code === LOSS.COLUMN_NAME_CHANGED && !isSlotRename(gen, label, kind))) {
339
+ // a node label goes into the name slot and an edge kind into the relation slot, when written there;
340
+ // an edge label has no slot (it becomes edge qualifiers or metadata), so it is never renamed
341
+ const extras = {
342
+ roles: SLOT_ROLES,
343
+ nodeRoleNames: label === null ? {} : NODE_ROLE_NAMES,
344
+ edgeRoleNames: kind !== null && isText(kind) ? EDGE_ROLE_NAMES : {},
345
+ };
346
+ for (const gen of checkCapabilities(snapshot, spec.caps, common, extras)) {
347
+ if (SUPERSEDED.has(gen.code)) {
340
348
  continue;
341
349
  }
342
350
  if (gen.code === LOSS.MIXED_DIRECTION_ERROR && fatal === null) {
@@ -384,19 +392,6 @@ function slotlessRoleNotes(snapshot: GraphSnapshot, spec: CapabilityNoteSpec): v
384
392
  }
385
393
  }
386
394
 
387
- /**
388
- * Whether a name-change note is about a column written into its slot: the node label, or a text
389
- * edge kind column.
390
- * @param gen - the note
391
- * @param label - the node label column, or null
392
- * @param kind - the edge kind column, or null
393
- * @returns true when it is
394
- */
395
- function isSlotRename(gen: LossNote, label: Column | null, kind: Column | null): boolean {
396
- const isLabel = label !== null && gen.column === label.meta.name;
397
- return isLabel || (kind !== null && gen.column === kind.meta.name && isText(kind));
398
- }
399
-
400
395
  /**
401
396
  * Record the columns that read back absent: a node or edge column without a set cell on a written
402
397
  * element (the importers declare a column when a value needs it).
@@ -297,6 +297,8 @@ function capabilityNotes(
297
297
  for (const gen of checkCapabilities(snapshot, CX2_CAPABILITIES, common, {
298
298
  roles: SLOT_ROLES,
299
299
  roleNames: ROLE_NAMES,
300
+ // only the node label is written into the "n" slot; an edge label keeps its own name
301
+ edgeRoleNames: {},
300
302
  })) {
301
303
  if (!keepGenericNote(gen, bypassNames)) {
302
304
  continue;
@@ -337,10 +339,6 @@ function keepGenericNote(gen: LossNote, bypassNames: ReadonlySet<string>): boole
337
339
  if (gen.column !== null && bypassNames.has(gen.column) && (gen.code === LOSS.JSON || gen.code === LOSS.DTYPE)) {
338
340
  return false;
339
341
  }
340
- if (gen.code === LOSS.COLUMN_NAME_CHANGED && gen.message.startsWith("edge column")) {
341
- // only the node label is written into the "n" slot; an edge label keeps its own name
342
- return false;
343
- }
344
342
  // ids are counted by planNodeIds(): CX2 also keeps integer ids beyond 2^53, which the generic rule refuses
345
343
  return gen.code !== LOSS.ID_CHARSET && gen.code !== LOSS.ID_MANGLED;
346
344
  }
@@ -150,8 +150,8 @@ export const OBO_LOSS = Object.freeze({
150
150
  */
151
151
  ROLE_ASSUMED: ROLE_ASSUMED_CODE,
152
152
  /**
153
- * An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the
154
- * name the format's importer gives it.
153
+ * The node label column is written as each node's `name` and reads back as the column `name`. An edge label has no slot:
154
+ * it is written as a qualifier (W_ROLE_DROPPED and W_OBO_EDGE_COLUMN_AS_QUALIFIER), never under a new name.
155
155
  */
156
156
  COLUMN_NAME_CHANGED: LOSS.COLUMN_NAME_CHANGED,
157
157
  /**