@graphty/graph-io 0.3.37 → 0.3.39

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 (141) hide show
  1. package/dist/chunks/{choose-CsO24U-5.js → choose-CS3fvGT1.js} +2 -2
  2. package/dist/chunks/{choose-CsO24U-5.js.map → choose-CS3fvGT1.js.map} +1 -1
  3. package/dist/chunks/{constants-BPRiVyBu.js → constants-DqKZT6cH.js} +2 -2
  4. package/dist/chunks/{constants-BPRiVyBu.js.map → constants-DqKZT6cH.js.map} +1 -1
  5. package/dist/chunks/{escape-BRcyzD77.js → escape-BpYMQZVa.js} +2 -2
  6. package/dist/chunks/{escape-BRcyzD77.js.map → escape-BpYMQZVa.js.map} +1 -1
  7. package/dist/chunks/{export-CTUlibzr.js → export-B-aVCwyG.js} +103 -92
  8. package/dist/chunks/export-B-aVCwyG.js.map +1 -0
  9. package/dist/chunks/{exporter-flsHFoER.js → exporter-BqKs1Q12.js} +5 -5
  10. package/dist/chunks/exporter-BqKs1Q12.js.map +1 -0
  11. package/dist/chunks/{exporter-B_YFZ4ix.js → exporter-CNJZF_BN.js} +7 -7
  12. package/dist/chunks/exporter-CNJZF_BN.js.map +1 -0
  13. package/dist/chunks/{exporter-Bn4jdYfd.js → exporter-DXqCPNrI.js} +4 -4
  14. package/dist/chunks/exporter-DXqCPNrI.js.map +1 -0
  15. package/dist/chunks/{exporter-2UNIxzcu.js → exporter-SFYkDzD0.js} +6 -5
  16. package/dist/chunks/exporter-SFYkDzD0.js.map +1 -0
  17. package/dist/chunks/{importer-oIUx9u_g.js → importer--tQQsY4W.js} +40 -18
  18. package/dist/chunks/importer--tQQsY4W.js.map +1 -0
  19. package/dist/chunks/{importer-yWUPR_nU.js → importer-B7Ir269L.js} +4 -4
  20. package/dist/chunks/{importer-yWUPR_nU.js.map → importer-B7Ir269L.js.map} +1 -1
  21. package/dist/chunks/{importer-Dwqepe_B.js → importer-BfJ1jnNr.js} +10 -4
  22. package/dist/chunks/importer-BfJ1jnNr.js.map +1 -0
  23. package/dist/chunks/{importer-CEuUmyPe.js → importer-CSt-gaYE.js} +6 -6
  24. package/dist/chunks/{importer-CEuUmyPe.js.map → importer-CSt-gaYE.js.map} +1 -1
  25. package/dist/chunks/{importer-Bgb9sgZZ.js → importer-CqgYq4Ld.js} +6 -8
  26. package/dist/chunks/importer-CqgYq4Ld.js.map +1 -0
  27. package/dist/chunks/{importer-BD3mi2QH.js → importer-DvFznt5-.js} +5 -5
  28. package/dist/chunks/importer-DvFznt5-.js.map +1 -0
  29. package/dist/chunks/{importer-DqMtyYLX.js → importer-PVXkeoaS.js} +6 -8
  30. package/dist/chunks/importer-PVXkeoaS.js.map +1 -0
  31. package/dist/chunks/{json-elements-AkglPS3m.js → json-elements-CXs4z1iL.js} +3 -3
  32. package/dist/chunks/{json-elements-AkglPS3m.js.map → json-elements-CXs4z1iL.js.map} +1 -1
  33. package/dist/chunks/{ontology-export-DDoG3egY.js → ontology-export-50L9DOV4.js} +2 -2
  34. package/dist/chunks/{ontology-export-DDoG3egY.js.map → ontology-export-50L9DOV4.js.map} +1 -1
  35. package/dist/chunks/{records-ChmWZGuz.js → records-C9jd7yLD.js} +2 -2
  36. package/dist/chunks/{records-ChmWZGuz.js.map → records-C9jd7yLD.js.map} +1 -1
  37. package/dist/chunks/{text-Btw3sBe_.js → text-Bjfa1xYL.js} +2 -2
  38. package/dist/chunks/{text-Btw3sBe_.js.map → text-Bjfa1xYL.js.map} +1 -1
  39. package/dist/csv.js +4 -4
  40. package/dist/csv.js.map +1 -1
  41. package/dist/cx.js +1 -1
  42. package/dist/cx2.js +2 -2
  43. package/dist/cys.js +1 -1
  44. package/dist/dot.js +1 -1
  45. package/dist/gexf.js +2 -2
  46. package/dist/gml.js +9 -7
  47. package/dist/gml.js.map +1 -1
  48. package/dist/graph-io.js +26 -26
  49. package/dist/graphml.js +2 -2
  50. package/dist/json.js +1 -1
  51. package/dist/neo4j.js +5 -5
  52. package/dist/neo4j.js.map +1 -1
  53. package/dist/obo.js +1 -1
  54. package/dist/pajek.js +1 -1
  55. package/dist/src/common/cx-export.d.ts +5 -2
  56. package/dist/src/common/cx-export.d.ts.map +1 -1
  57. package/dist/src/common/cx-export.js +11 -2
  58. package/dist/src/common/cx-export.js.map +1 -1
  59. package/dist/src/common/direction.js +1 -1
  60. package/dist/src/common/direction.js.map +1 -1
  61. package/dist/src/common/export.d.ts.map +1 -1
  62. package/dist/src/common/export.js +136 -71
  63. package/dist/src/common/export.js.map +1 -1
  64. package/dist/src/common/input.js +1 -1
  65. package/dist/src/common/input.js.map +1 -1
  66. package/dist/src/formats/cx/exporter.js +1 -1
  67. package/dist/src/formats/cx/exporter.js.map +1 -1
  68. package/dist/src/formats/cx2/exporter.js +1 -1
  69. package/dist/src/formats/cx2/exporter.js.map +1 -1
  70. package/dist/src/formats/dot/importer.d.ts +6 -0
  71. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  72. package/dist/src/formats/dot/importer.js +11 -6
  73. package/dist/src/formats/dot/importer.js.map +1 -1
  74. package/dist/src/formats/dot/names.d.ts +5 -0
  75. package/dist/src/formats/dot/names.d.ts.map +1 -1
  76. package/dist/src/formats/dot/names.js +5 -0
  77. package/dist/src/formats/dot/names.js.map +1 -1
  78. package/dist/src/formats/dot/tokenizer.js +9 -9
  79. package/dist/src/formats/dot/tokenizer.js.map +1 -1
  80. package/dist/src/formats/gml/importer.d.ts +6 -0
  81. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  82. package/dist/src/formats/gml/importer.js +6 -3
  83. package/dist/src/formats/gml/importer.js.map +1 -1
  84. package/dist/src/formats/gml/syntax.js +1 -1
  85. package/dist/src/formats/gml/syntax.js.map +1 -1
  86. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  87. package/dist/src/formats/graphml/importer.js +2 -4
  88. package/dist/src/formats/graphml/importer.js.map +1 -1
  89. package/dist/src/formats/json/dialect.js +1 -1
  90. package/dist/src/formats/json/dialect.js.map +1 -1
  91. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  92. package/dist/src/formats/neo4j/exporter.js +1 -4
  93. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  94. package/dist/src/formats/neo4j/header.js +2 -2
  95. package/dist/src/formats/neo4j/header.js.map +1 -1
  96. package/dist/src/formats/obo/importer.d.ts.map +1 -1
  97. package/dist/src/formats/obo/importer.js +2 -1
  98. package/dist/src/formats/obo/importer.js.map +1 -1
  99. package/dist/src/formats/obo/syntax.js +1 -1
  100. package/dist/src/formats/obo/syntax.js.map +1 -1
  101. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  102. package/dist/src/formats/pajek/exporter.js +4 -5
  103. package/dist/src/formats/pajek/exporter.js.map +1 -1
  104. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  105. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  106. package/dist/src/sniff.js +1 -1
  107. package/dist/src/sniff.js.map +1 -1
  108. package/dist/xgmml.js +2 -2
  109. package/package.json +2 -2
  110. package/src/common/cx-export.ts +11 -2
  111. package/src/common/direction.ts +1 -1
  112. package/src/common/export.ts +190 -97
  113. package/src/common/input.ts +1 -1
  114. package/src/formats/csv/importer.ts +1 -1
  115. package/src/formats/cx/exporter.ts +1 -1
  116. package/src/formats/cx2/exporter.ts +1 -1
  117. package/src/formats/dot/importer.ts +36 -5
  118. package/src/formats/dot/names.ts +6 -0
  119. package/src/formats/dot/tokenizer.ts +9 -9
  120. package/src/formats/gml/importer.ts +15 -4
  121. package/src/formats/gml/syntax.ts +1 -1
  122. package/src/formats/graphml/importer.ts +2 -4
  123. package/src/formats/json/dialect.ts +1 -1
  124. package/src/formats/neo4j/exporter.ts +1 -4
  125. package/src/formats/neo4j/header.ts +2 -2
  126. package/src/formats/obo/importer.ts +2 -1
  127. package/src/formats/obo/syntax.ts +1 -1
  128. package/src/formats/pajek/exporter.ts +6 -4
  129. package/src/formats/pajek/importer.ts +1 -1
  130. package/src/formats/xgmml/exporter.ts +4 -1
  131. package/src/sniff.ts +1 -1
  132. package/dist/chunks/export-CTUlibzr.js.map +0 -1
  133. package/dist/chunks/exporter-2UNIxzcu.js.map +0 -1
  134. package/dist/chunks/exporter-B_YFZ4ix.js.map +0 -1
  135. package/dist/chunks/exporter-Bn4jdYfd.js.map +0 -1
  136. package/dist/chunks/exporter-flsHFoER.js.map +0 -1
  137. package/dist/chunks/importer-BD3mi2QH.js.map +0 -1
  138. package/dist/chunks/importer-Bgb9sgZZ.js.map +0 -1
  139. package/dist/chunks/importer-DqMtyYLX.js.map +0 -1
  140. package/dist/chunks/importer-Dwqepe_B.js.map +0 -1
  141. package/dist/chunks/importer-oIUx9u_g.js.map +0 -1
@@ -450,7 +450,7 @@ function plan(snapshot: GraphSnapshot, common: ResolvedExportOptions): Plan {
450
450
  const position = snapshot.nodes.byRole("position");
451
451
  nonFiniteNote(snapshot, [...nodeAttrs, ...edgeAttrs, ...graphAttrs], weights, position, note);
452
452
 
453
- const edgeIds = planEdgeIds(snapshot, note);
453
+ const edgeIds = planEdgeIds(snapshot, note, ids);
454
454
  const z = [...snapshot.nodes].find(isZ) ?? null;
455
455
  const positionZ = position !== null && z === null && position.meta.extra.sourceDims === 3;
456
456
  return {
@@ -97,6 +97,7 @@ import {
97
97
  PARENT_COLUMN,
98
98
  PIN_ATTRIBUTE,
99
99
  POS_ATTRIBUTE,
100
+ POSITION_COLUMN,
100
101
  SOURCE_PORT_COLUMN,
101
102
  TARGET_PORT_COLUMN,
102
103
  } from "./names.js";
@@ -122,6 +123,12 @@ export interface DotImportOptions extends GraphChoiceOptions, CommonImportOption
122
123
  * @defaultValue true
123
124
  */
124
125
  positions?: boolean | undefined;
126
+ /**
127
+ * With `positions`, also keep `pos` as the text the file wrote; the position column is then
128
+ * named `graphty.position`, so a reader gets both.
129
+ * @defaultValue false
130
+ */
131
+ keepPositionSource?: boolean | undefined;
125
132
  }
126
133
 
127
134
  /**
@@ -323,7 +330,7 @@ interface Scope {
323
330
  */
324
331
  export const dotImporter: GraphImporter<DotImportOptions> = Object.freeze({
325
332
  format: DOT_FORMAT,
326
- options: Object.freeze(["mismatchedEdgeOperator", "positions"]),
333
+ options: Object.freeze(["keepPositionSource", "mismatchedEdgeOperator", "positions"]),
327
334
  extensions: EXTENSIONS,
328
335
  mimeTypes: MIME_TYPES,
329
336
 
@@ -376,7 +383,15 @@ export const dotImporter: GraphImporter<DotImportOptions> = Object.freeze({
376
383
  const reports = { current: report };
377
384
  const lexer = dotLexer(text, reports);
378
385
  const first = guard(report, () => lexer.next());
379
- const parser = new DotParser(lexer, sink, report, resolved, mismatch, options?.positions !== false);
386
+ const parser = new DotParser(
387
+ lexer,
388
+ sink,
389
+ report,
390
+ resolved,
391
+ mismatch,
392
+ options?.positions !== false,
393
+ options?.keepPositionSource === true,
394
+ );
380
395
  parser.headCharset = head.charset;
381
396
  const trailing = parseGraph(parser, report, first);
382
397
  if (trailing.kind !== "eof") {
@@ -429,7 +444,15 @@ export const dotImporter: GraphImporter<DotImportOptions> = Object.freeze({
429
444
  reports.current = report;
430
445
  const sink = sinkFor(done.length);
431
446
  reportSinkOptions(sink, options, report);
432
- const parser = new DotParser(lexer, sink, report, resolved, mismatch, options?.positions !== false);
447
+ const parser = new DotParser(
448
+ lexer,
449
+ sink,
450
+ report,
451
+ resolved,
452
+ mismatch,
453
+ options?.positions !== false,
454
+ options?.keepPositionSource === true,
455
+ );
433
456
  parser.headCharset = head.charset;
434
457
  token = parseGraph(parser, report, token);
435
458
  throwIfAborted(resolved.signal);
@@ -578,7 +601,7 @@ function dotCharset(head: string): string | null {
578
601
  * @param value - the caller's value
579
602
  * @returns the value or the default; E_UNSUPPORTED for anything else
580
603
  */
581
- function mismatchOption(value: unknown): "operator" | "header" | "error" {
604
+ function mismatchOption(value: unknown): NonNullable<DotImportOptions["mismatchedEdgeOperator"]> {
582
605
  if (value === undefined) {
583
606
  return "operator";
584
607
  }
@@ -686,6 +709,8 @@ class DotParser {
686
709
  private readonly mismatch: "operator" | "header" | "error";
687
710
  private readonly positions: boolean;
688
711
 
712
+ private readonly keepPositionSource: boolean;
713
+
689
714
  private readonly ids: IdCoercer;
690
715
 
691
716
  private readonly resolver: DirectionResolver;
@@ -758,6 +783,7 @@ class DotParser {
758
783
  * @param options - the resolved common options
759
784
  * @param mismatch - the resolved mismatchedEdgeOperator option
760
785
  * @param positions - whether `pos` goes to the position column
786
+ * @param keepPositionSource - whether `pos` also stays as text
761
787
  */
762
788
  constructor(
763
789
  lexer: DotTokenizer,
@@ -766,6 +792,7 @@ class DotParser {
766
792
  options: ResolvedImportOptions,
767
793
  mismatch: "operator" | "header" | "error",
768
794
  positions: boolean,
795
+ keepPositionSource: boolean,
769
796
  ) {
770
797
  this.lexer = lexer;
771
798
  this.sink = sink;
@@ -773,6 +800,7 @@ class DotParser {
773
800
  this.options = options;
774
801
  this.mismatch = mismatch;
775
802
  this.positions = positions;
803
+ this.keepPositionSource = keepPositionSource;
776
804
  this.ids = new IdCoercer(options.ids);
777
805
  this.resolver = new DirectionResolver(sink, report, options.onMixedDirection);
778
806
  }
@@ -1586,6 +1614,9 @@ class DotParser {
1586
1614
  this.sink.setNodeValue(this.nodeLabel(), index, value);
1587
1615
  } else if (name === POS_ATTRIBUTE && this.positions) {
1588
1616
  this.setPosition(index, value, line, element);
1617
+ if (this.keepPositionSource) {
1618
+ this.textWriter("node", name).write(index, value);
1619
+ }
1589
1620
  } else {
1590
1621
  this.textWriter("node", name).write(index, value);
1591
1622
  }
@@ -1766,7 +1797,7 @@ class DotParser {
1766
1797
  private positionColumn(dims: 2 | 3): ColumnHandle {
1767
1798
  if (this.positionHandle === INVALID_INDEX) {
1768
1799
  this.positionHandle = this.declare("node", {
1769
- name: POS_ATTRIBUTE,
1800
+ name: this.keepPositionSource ? POSITION_COLUMN : POS_ATTRIBUTE,
1770
1801
  dtype: "f64",
1771
1802
  components: 3,
1772
1803
  nullable: true,
@@ -54,6 +54,12 @@ export const LABEL_ATTRIBUTE = "label";
54
54
  */
55
55
  export const POS_ATTRIBUTE = "pos";
56
56
 
57
+ /**
58
+ * The node position column when the import keeps `pos` as text too (the `keepPositionSource` option).
59
+ * @category Plugin helpers
60
+ */
61
+ export const POSITION_COLUMN = "graphty.position";
62
+
57
63
  /**
58
64
  * The edge attribute cgraph uses as an edge's identity within its endpoints, mapped to the edge id role.
59
65
  * @category Plugin helpers
@@ -148,15 +148,15 @@ export class DotTokenizer {
148
148
  const { line } = this;
149
149
  const c = text.charCodeAt(start);
150
150
  switch (c) {
151
- case 0x7b: // {
152
- case 0x7d: // }
153
- case 0x5b: // [
154
- case 0x5d: // ]
155
- case 0x3b: // ;
156
- case 0x2c: // ,
157
- case 0x3d: // =
158
- case 0x3a: // :
159
- case 0x2b: // +
151
+ case 0x7b: // "{"
152
+ case 0x7d: // "}"
153
+ case 0x5b: // "["
154
+ case 0x5d: // "]"
155
+ case 0x3b: // ";"
156
+ case 0x2c: // ","
157
+ case 0x3d: // "="
158
+ case 0x3a: // ":"
159
+ case 0x2b: // "+"
160
160
  this.pos++;
161
161
  return { kind: "punct", text: text[start], quoted: false, html: false, line };
162
162
  case 0x2d: {
@@ -97,6 +97,12 @@ export interface GmlImportOptions extends GraphChoiceOptions, CommonImportOption
97
97
  * @defaultValue true
98
98
  */
99
99
  positions?: boolean | undefined;
100
+ /**
101
+ * With `positions`, also keep the whole `graphics` record, x / y / z included, as the JSON
102
+ * attribute named `graphics`, so a reader gets both the position and the record as written.
103
+ * @defaultValue false
104
+ */
105
+ keepPositionSource?: boolean | undefined;
100
106
  /**
101
107
  * Store a text attribute whose values repeat a lot (fewer distinct values than half the rows)
102
108
  * as a dictionary column, which uses less memory and reads the same. Such a column reports
@@ -353,7 +359,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
353
359
  * @param key - the key
354
360
  * @returns the role, or null
355
361
  */
356
- function roleOf(domain: "node" | "edge" | "graph", key: string): ColumnRole | null {
362
+ function roleOf(domain: GmlDomain, key: string): ColumnRole | null {
357
363
  if (key === "label" && domain !== "graph") {
358
364
  return "label";
359
365
  }
@@ -378,6 +384,8 @@ class GmlImport {
378
384
 
379
385
  private readonly positions: boolean;
380
386
 
387
+ private readonly keepPositionSource: boolean;
388
+
381
389
  private readonly dictionaries: boolean;
382
390
 
383
391
  private readonly coercer: IdCoercer;
@@ -470,6 +478,7 @@ class GmlImport {
470
478
  this.report = report;
471
479
  this.options = options;
472
480
  this.positions = format?.positions ?? true;
481
+ this.keepPositionSource = format?.keepPositionSource ?? false;
473
482
  this.dictionaries = format?.dictionaries ?? true;
474
483
  this.coercer = new IdCoercer(options.ids);
475
484
  }
@@ -755,7 +764,8 @@ class GmlImport {
755
764
  }
756
765
  const positioned = x && y;
757
766
  entry.position ||= positioned;
758
- entry.rest ||= positioned ? other : true;
767
+ // with keepPositionSource the whole record, x / y / z included, goes to the json column too
768
+ entry.rest ||= positioned ? other || this.keepPositionSource : true;
759
769
  }
760
770
 
761
771
  // ============================================================ pass 2: the push
@@ -1541,7 +1551,8 @@ class GmlImport {
1541
1551
  const positionPlan = plan.position;
1542
1552
  let rest: unknown = first;
1543
1553
  if (isRecord(first) && positionPlan !== null) {
1544
- rest = this.writePosition(positionPlan, index, first, where);
1554
+ const left = this.writePosition(positionPlan, index, this.keepPositionSource ? { ...first } : first, where);
1555
+ rest = this.keepPositionSource ? first : left;
1545
1556
  } else if (!isRecord(first)) {
1546
1557
  this.graphicsWarning("scalar", `node ${where.element}'s graphics is not a record`, where);
1547
1558
  }
@@ -1924,7 +1935,7 @@ function sniffGml(head: Uint8Array): number {
1924
1935
  */
1925
1936
  export const gmlImporter: GraphImporter<GmlImportOptions> = Object.freeze({
1926
1937
  format: "gml",
1927
- options: Object.freeze(["dictionaries", "positions"]),
1938
+ options: Object.freeze(["dictionaries", "keepPositionSource", "positions"]),
1928
1939
  extensions: Object.freeze([".gml"]),
1929
1940
  mimeTypes: Object.freeze(["text/x-gml", "text/plain"]),
1930
1941
  sniff: sniffGml,
@@ -209,7 +209,7 @@ export class GmlTokens {
209
209
  // a line break inside a string is part of it, as LF whatever the file's line ends
210
210
  text = text.replaceAll(/\r\n?/g, "\n");
211
211
  }
212
- if (text.indexOf("&") < 0) {
212
+ if (!text.includes("&")) {
213
213
  return text;
214
214
  }
215
215
  const report = this.onUnknownEntity;
@@ -512,16 +512,14 @@ class GraphmlReader implements XmlHandler {
512
512
  const parent = this.ctx.at(-1) ?? null;
513
513
  switch (parent) {
514
514
  case Ctx.Capture:
515
+ case Ctx.Data:
516
+ case Ctx.Default:
515
517
  this.captureStart(name, attrs);
516
518
  return;
517
519
  case Ctx.Skip:
518
520
  case Ctx.Desc:
519
521
  this.ctx.push(Ctx.Skip);
520
522
  return;
521
- case Ctx.Data:
522
- case Ctx.Default:
523
- this.captureStart(name, attrs);
524
- return;
525
523
  case Ctx.Dropped:
526
524
  this.dropStart(localName(name));
527
525
  return;
@@ -124,7 +124,7 @@ export function isJsonObject(value: unknown): value is Record<string, unknown> {
124
124
  * @category Plugin helpers
125
125
  */
126
126
  export function hasKey(record: Readonly<Record<string, unknown>>, key: string): boolean {
127
- return Object.prototype.hasOwnProperty.call(record, key);
127
+ return Object.hasOwn(record, key);
128
128
  }
129
129
 
130
130
  /**
@@ -1169,10 +1169,7 @@ class ExportPlan {
1169
1169
  return null;
1170
1170
  }
1171
1171
  if (labels.dtype === "list") {
1172
- return labels
1173
- .sliceOf(index)
1174
- .map((item) => String(item))
1175
- .join(this.options.arrayDelimiter);
1172
+ return labels.sliceOf(index).map(String).join(this.options.arrayDelimiter);
1176
1173
  }
1177
1174
  return textOf(labels, index);
1178
1175
  }
@@ -59,8 +59,8 @@ export function isHeaderRecord(cells: readonly string[], count: number, quoted?:
59
59
  let quotedMarker = false;
60
60
  for (let i = 0; i < count; i++) {
61
61
  const cell = cells[i];
62
- // every header marker holds a colon; the indexOf keeps the regex off the data rows
63
- if (cell.indexOf(":") >= 0 && HEADER_MARKER.test(cell.trim())) {
62
+ // every header marker holds a colon; the includes keeps the regex off the data rows
63
+ if (cell.includes(":") && HEADER_MARKER.test(cell.trim())) {
64
64
  if (quoted?.[i] !== true) {
65
65
  return true;
66
66
  }
@@ -1429,7 +1429,8 @@ class OboReader {
1429
1429
  return;
1430
1430
  }
1431
1431
  const map = (record.values.get("obo.qualifiers") as Record<string, unknown[]> | undefined) ?? {};
1432
- (map[tag] ??= []).push({ value, qualifiers });
1432
+ map[tag] ??= [];
1433
+ map[tag].push({ value, qualifiers });
1433
1434
  record.values.set("obo.qualifiers", map);
1434
1435
  }
1435
1436
 
@@ -304,7 +304,7 @@ export function unclosedBlock(value: string): number {
304
304
  */
305
305
  function addQualifier(record: Qualifiers, name: string, value: string | string[]): void {
306
306
  const values = Array.isArray(value) ? value : [value];
307
- if (!Object.prototype.hasOwnProperty.call(record, name)) {
307
+ if (!Object.hasOwn(record, name)) {
308
308
  record[name] = Array.isArray(value) ? [...value] : value;
309
309
  return;
310
310
  }
@@ -979,10 +979,12 @@ export const pajekExporter: GraphExporter<PajekExportOptions> = Object.freeze({
979
979
  // keyword and the relation name (dict columns written as keywords)
980
980
  const owned = new Set<string>();
981
981
  for (const { column, slot } of [...nodePlan, ...edgePlan]) {
982
- if (slot === "shape" || slot === "relation") {
983
- owned.add(`${column.meta.domain}:${column.meta.name}`);
984
- } else if (slot === "param" && column.meta.domain === "node" && column.meta.name === SHAPE_COLUMN) {
985
- // the shape-as-parameter note already says the column reads back as string
982
+ // a shape written as a parameter is owned too: its note already says the column reads back as string
983
+ if (
984
+ slot === "shape" ||
985
+ slot === "relation" ||
986
+ (slot === "param" && column.meta.domain === "node" && column.meta.name === SHAPE_COLUMN)
987
+ ) {
986
988
  owned.add(`${column.meta.domain}:${column.meta.name}`);
987
989
  }
988
990
  }
@@ -386,7 +386,7 @@ class PajekParser {
386
386
 
387
387
  private readonly coercer: IdCoercer;
388
388
 
389
- private readonly firstVertex: 0 | 1 | "auto";
389
+ private readonly firstVertex: NonNullable<PajekImportOptions["firstVertex"]>;
390
390
 
391
391
  private section: Section = "none";
392
392
 
@@ -145,8 +145,11 @@ const RESERVED_NODE_NAMES: ReadonlySet<string> = new Set([
145
145
  /** Edge column names the importer owns. */
146
146
  const RESERVED_EDGE_NAMES: ReadonlySet<string> = new Set([EDGE_ID_COLUMN, LABEL_COLUMN, GRAPHICS_COLUMN]);
147
147
 
148
+ /** The table an attribute name belongs to. */
149
+ type Domain = "graph" | "node" | "edge";
150
+
148
151
  /** The importer-owned names of each table. */
149
- const RESERVED_NAMES: Readonly<Record<"graph" | "node" | "edge", ReadonlySet<string>>> = {
152
+ const RESERVED_NAMES: Readonly<Record<Domain, ReadonlySet<string>>> = {
150
153
  graph: new Set([GRAPHICS_COLUMN]),
151
154
  node: RESERVED_NODE_NAMES,
152
155
  edge: RESERVED_EDGE_NAMES,
package/src/sniff.ts CHANGED
@@ -557,5 +557,5 @@ function clamp(value: number): number {
557
557
  if (!(value > 0)) {
558
558
  return 0;
559
559
  }
560
- return value > 1 ? 1 : value;
560
+ return Math.min(value, 1);
561
561
  }