@graphty/graph-io 0.3.40 → 0.3.41

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 (189) hide show
  1. package/dist/chunks/{choose-CS3fvGT1.js → choose-CwFQamH9.js} +2 -2
  2. package/dist/chunks/{choose-CS3fvGT1.js.map → choose-CwFQamH9.js.map} +1 -1
  3. package/dist/chunks/{constants-DqKZT6cH.js → constants-Cf4Nn6tw.js} +2 -2
  4. package/dist/chunks/{constants-DqKZT6cH.js.map → constants-Cf4Nn6tw.js.map} +1 -1
  5. package/dist/chunks/{escape-BJRLd3vJ.js → escape-DBb8zuNE.js} +2 -2
  6. package/dist/chunks/{escape-BJRLd3vJ.js.map → escape-DBb8zuNE.js.map} +1 -1
  7. package/dist/chunks/{export-B-aVCwyG.js → export-DD3Lvw-F.js} +173 -116
  8. package/dist/chunks/export-DD3Lvw-F.js.map +1 -0
  9. package/dist/chunks/{exporter-Bcp-l4Bo.js → exporter-BqtmlxNr.js} +545 -459
  10. package/dist/chunks/exporter-BqtmlxNr.js.map +1 -0
  11. package/dist/chunks/{exporter-6ocL_9x_.js → exporter-CS35R6Fg.js} +4 -4
  12. package/dist/chunks/{exporter-6ocL_9x_.js.map → exporter-CS35R6Fg.js.map} +1 -1
  13. package/dist/chunks/{exporter-BVXnV9-9.js → exporter-D8r1N2dU.js} +28 -16
  14. package/dist/chunks/exporter-D8r1N2dU.js.map +1 -0
  15. package/dist/chunks/{exporter-SFYkDzD0.js → exporter-NMplSnYn.js} +23 -20
  16. package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-NMplSnYn.js.map} +1 -1
  17. package/dist/chunks/{importer-CPqnIY8m.js → importer-BKC7AmCA.js} +42 -29
  18. package/dist/chunks/importer-BKC7AmCA.js.map +1 -0
  19. package/dist/chunks/{importer-D_srHyj6.js → importer-BdWHmlmG.js} +4 -4
  20. package/dist/chunks/{importer-D_srHyj6.js.map → importer-BdWHmlmG.js.map} +1 -1
  21. package/dist/chunks/{importer-AjwOJrn2.js → importer-BfbzWk6k.js} +202 -181
  22. package/dist/chunks/importer-BfbzWk6k.js.map +1 -0
  23. package/dist/chunks/{importer-BpuS3J8C.js → importer-BmaZcskq.js} +73 -60
  24. package/dist/chunks/importer-BmaZcskq.js.map +1 -0
  25. package/dist/chunks/{importer-DEp_fYjf.js → importer-DviEsHy4.js} +92 -64
  26. package/dist/chunks/importer-DviEsHy4.js.map +1 -0
  27. package/dist/chunks/{importer-DcKB97kW.js → importer-S-abxJHn.js} +5 -5
  28. package/dist/chunks/{importer-DcKB97kW.js.map → importer-S-abxJHn.js.map} +1 -1
  29. package/dist/chunks/{importer-BJlGAOht.js → importer-r29cWnEf.js} +3 -3
  30. package/dist/chunks/{importer-BJlGAOht.js.map → importer-r29cWnEf.js.map} +1 -1
  31. package/dist/chunks/{json-elements-DpO6AXIc.js → json-elements-DdF-etPT.js} +3 -3
  32. package/dist/chunks/{json-elements-DpO6AXIc.js.map → json-elements-DdF-etPT.js.map} +1 -1
  33. package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-BC0zyPhj.js} +2 -2
  34. package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-BC0zyPhj.js.map} +1 -1
  35. package/dist/chunks/{records-r3SFHz11.js → records-Uc3LkABx.js} +15 -12
  36. package/dist/chunks/records-Uc3LkABx.js.map +1 -0
  37. package/dist/chunks/{text-Bjfa1xYL.js → text-Bh9SaAob.js} +20 -11
  38. package/dist/chunks/text-Bh9SaAob.js.map +1 -0
  39. package/dist/csv.js +67 -49
  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 +195 -97
  46. package/dist/gexf.js.map +1 -1
  47. package/dist/gml.js +170 -149
  48. package/dist/gml.js.map +1 -1
  49. package/dist/graph-io.js +26 -26
  50. package/dist/graphml.js +2 -2
  51. package/dist/json.js +1 -1
  52. package/dist/neo4j.js +103 -65
  53. package/dist/neo4j.js.map +1 -1
  54. package/dist/obo.js +1 -1
  55. package/dist/pajek.js +1 -1
  56. package/dist/src/common/attributes.d.ts.map +1 -1
  57. package/dist/src/common/attributes.js +34 -9
  58. package/dist/src/common/attributes.js.map +1 -1
  59. package/dist/src/common/input.d.ts.map +1 -1
  60. package/dist/src/common/input.js +180 -102
  61. package/dist/src/common/input.js.map +1 -1
  62. package/dist/src/common/text.d.ts +7 -0
  63. package/dist/src/common/text.d.ts.map +1 -1
  64. package/dist/src/common/text.js +15 -7
  65. package/dist/src/common/text.js.map +1 -1
  66. package/dist/src/common/zip.d.ts.map +1 -1
  67. package/dist/src/common/zip.js +36 -19
  68. package/dist/src/common/zip.js.map +1 -1
  69. package/dist/src/formats/csv/exporter.d.ts.map +1 -1
  70. package/dist/src/formats/csv/exporter.js +77 -25
  71. package/dist/src/formats/csv/exporter.js.map +1 -1
  72. package/dist/src/formats/csv/importer.d.ts.map +1 -1
  73. package/dist/src/formats/csv/importer.js +16 -6
  74. package/dist/src/formats/csv/importer.js.map +1 -1
  75. package/dist/src/formats/csv/records.d.ts.map +1 -1
  76. package/dist/src/formats/csv/records.js +18 -10
  77. package/dist/src/formats/csv/records.js.map +1 -1
  78. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  79. package/dist/src/formats/cys/tables.js +11 -8
  80. package/dist/src/formats/cys/tables.js.map +1 -1
  81. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  82. package/dist/src/formats/gexf/exporter.js +65 -37
  83. package/dist/src/formats/gexf/exporter.js.map +1 -1
  84. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  85. package/dist/src/formats/gexf/importer.js +138 -50
  86. package/dist/src/formats/gexf/importer.js.map +1 -1
  87. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  88. package/dist/src/formats/gml/exporter.js +51 -35
  89. package/dist/src/formats/gml/exporter.js.map +1 -1
  90. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  91. package/dist/src/formats/gml/importer.js +103 -58
  92. package/dist/src/formats/gml/importer.js.map +1 -1
  93. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  94. package/dist/src/formats/gml/syntax.js +17 -8
  95. package/dist/src/formats/gml/syntax.js.map +1 -1
  96. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  97. package/dist/src/formats/graphml/exporter.js +34 -24
  98. package/dist/src/formats/graphml/exporter.js.map +1 -1
  99. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  100. package/dist/src/formats/graphml/importer.js +17 -13
  101. package/dist/src/formats/graphml/importer.js.map +1 -1
  102. package/dist/src/formats/graphml/tree.js +23 -14
  103. package/dist/src/formats/graphml/tree.js.map +1 -1
  104. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  105. package/dist/src/formats/graphml/yfiles.js +22 -10
  106. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  107. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  108. package/dist/src/formats/json/exporter.js +38 -21
  109. package/dist/src/formats/json/exporter.js.map +1 -1
  110. package/dist/src/formats/json/importer.d.ts.map +1 -1
  111. package/dist/src/formats/json/importer.js +536 -343
  112. package/dist/src/formats/json/importer.js.map +1 -1
  113. package/dist/src/formats/json/obographs.js +29 -17
  114. package/dist/src/formats/json/obographs.js.map +1 -1
  115. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  116. package/dist/src/formats/neo4j/exporter.js +73 -43
  117. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  118. package/dist/src/formats/neo4j/header.d.ts.map +1 -1
  119. package/dist/src/formats/neo4j/header.js +37 -16
  120. package/dist/src/formats/neo4j/header.js.map +1 -1
  121. package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
  122. package/dist/src/formats/neo4j/importer.js +21 -11
  123. package/dist/src/formats/neo4j/importer.js.map +1 -1
  124. package/dist/src/formats/obo/exporter.d.ts.map +1 -1
  125. package/dist/src/formats/obo/exporter.js +18 -7
  126. package/dist/src/formats/obo/exporter.js.map +1 -1
  127. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  128. package/dist/src/formats/pajek/exporter.js +142 -73
  129. package/dist/src/formats/pajek/exporter.js.map +1 -1
  130. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  131. package/dist/src/formats/pajek/importer.js +26 -15
  132. package/dist/src/formats/pajek/importer.js.map +1 -1
  133. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  134. package/dist/src/formats/pajek/syntax.js +85 -58
  135. package/dist/src/formats/pajek/syntax.js.map +1 -1
  136. package/dist/src/formats/xgmml/emit.d.ts +7 -0
  137. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  138. package/dist/src/formats/xgmml/emit.js +79 -40
  139. package/dist/src/formats/xgmml/emit.js.map +1 -1
  140. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  141. package/dist/src/formats/xgmml/exporter.js +38 -12
  142. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  143. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  144. package/dist/src/formats/xgmml/importer.js +38 -18
  145. package/dist/src/formats/xgmml/importer.js.map +1 -1
  146. package/dist/src/sniff.js +109 -57
  147. package/dist/src/sniff.js.map +1 -1
  148. package/dist/xgmml.js +2 -2
  149. package/package.json +2 -2
  150. package/src/common/attributes.ts +59 -19
  151. package/src/common/input.ts +216 -102
  152. package/src/common/text.ts +24 -15
  153. package/src/common/zip.ts +47 -18
  154. package/src/formats/csv/exporter.ts +103 -39
  155. package/src/formats/csv/importer.ts +17 -6
  156. package/src/formats/csv/records.ts +19 -10
  157. package/src/formats/cys/tables.ts +11 -10
  158. package/src/formats/gexf/exporter.ts +67 -36
  159. package/src/formats/gexf/importer.ts +173 -63
  160. package/src/formats/gml/exporter.ts +78 -82
  161. package/src/formats/gml/importer.ts +119 -68
  162. package/src/formats/gml/syntax.ts +18 -8
  163. package/src/formats/graphml/exporter.ts +44 -24
  164. package/src/formats/graphml/importer.ts +18 -11
  165. package/src/formats/graphml/tree.ts +22 -12
  166. package/src/formats/graphml/yfiles.ts +23 -10
  167. package/src/formats/json/exporter.ts +40 -21
  168. package/src/formats/json/importer.ts +701 -385
  169. package/src/formats/json/obographs.ts +35 -15
  170. package/src/formats/neo4j/exporter.ts +79 -41
  171. package/src/formats/neo4j/header.ts +50 -16
  172. package/src/formats/neo4j/importer.ts +21 -10
  173. package/src/formats/obo/exporter.ts +34 -17
  174. package/src/formats/pajek/exporter.ts +180 -117
  175. package/src/formats/pajek/importer.ts +27 -15
  176. package/src/formats/pajek/syntax.ts +99 -58
  177. package/src/formats/xgmml/emit.ts +88 -40
  178. package/src/formats/xgmml/exporter.ts +40 -12
  179. package/src/formats/xgmml/importer.ts +45 -23
  180. package/src/sniff.ts +115 -59
  181. package/dist/chunks/export-B-aVCwyG.js.map +0 -1
  182. package/dist/chunks/exporter-BVXnV9-9.js.map +0 -1
  183. package/dist/chunks/exporter-Bcp-l4Bo.js.map +0 -1
  184. package/dist/chunks/importer-AjwOJrn2.js.map +0 -1
  185. package/dist/chunks/importer-BpuS3J8C.js.map +0 -1
  186. package/dist/chunks/importer-CPqnIY8m.js.map +0 -1
  187. package/dist/chunks/importer-DEp_fYjf.js.map +0 -1
  188. package/dist/chunks/records-r3SFHz11.js.map +0 -1
  189. package/dist/chunks/text-Bjfa1xYL.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"export-B-aVCwyG.js","sources":["../../src/common/codes.ts","../../src/common/plural.ts","../../src/common/temporal.ts","../../src/common/declared-types.ts","../../src/common/lists.ts","../../src/common/attributes.ts","../../src/common/direction.ts","../../src/common/ids.ts","../../src/common/input.ts","../../src/common/options.ts","../../src/types.ts","../../src/common/report.ts","../../src/common/format.ts","../../src/common/weights.ts","../../src/common/export.ts"],"sourcesContent":["/**\n * The issue and loss codes shared by more than one format: one constant per condition, aliased by every format's\n * `<FMT>_ISSUE` / `<FMT>_LOSS` table, so a consumer can switch on a condition without knowing the\n * format and two importers never spell the same condition differently. A condition specific to one\n * format keeps that format's own `E_<FMT>_` / `W_<FMT>_` code next to its importer or exporter.\n *\n * Naming scheme: `E_` for an issue recorded with severity \"error\" (or a loss note whose export()\n * throws), `W_` for a warning; a table key is the code without the severity prefix and without the\n * format prefix.\n */\n\nimport type { GraphFormatErrorCode } from \"@graphty/graph-format\";\n\n// ============================================================ importer issues\n\n/**\n * The URL could not be fetched (network failure, CORS refusal or a non-2xx status).\n * @category Issue and loss codes\n */\nexport const FETCH_CODE = \"E_FETCH\";\n\n/**\n * The element (node, attribute, key, ...) has no id where the format requires one.\n * @category Issue and loss codes\n */\nexport const MISSING_ID_CODE = \"E_MISSING_ID\";\n\n/**\n * An edge has no source or no target.\n * @category Issue and loss codes\n */\nexport const MISSING_ENDPOINT_CODE = \"E_MISSING_ENDPOINT\";\n\n/**\n * A node id declared twice; the second declaration merges into the first.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_NODE_CODE = \"W_DUPLICATE_NODE\";\n\n/**\n * The file declares no graph at all. The import stops.\n * @category Issue and loss codes\n */\nexport const NO_GRAPH_CODE = \"E_NO_GRAPH\";\n\n/**\n * The file holds several graphs and only the first was read (GraphML and GEXF merge them into one graph instead). It is not added when `graphIndex` or `graphName` chose the graph.\n * `importAllGraphs()` reads every one.\n * @category Issue and loss codes\n */\nexport const MULTIPLE_GRAPHS_CODE = \"W_MULTIPLE_GRAPHS\";\n\n/**\n * The input is empty. The import stops.\n * @category Issue and loss codes\n */\nexport const EMPTY_INPUT_CODE = \"E_EMPTY_INPUT\";\n\n/**\n * The text breaks the format's syntax; the message says where and how. The import stops.\n * @category Issue and loss codes\n */\nexport const SYNTAX_CODE = \"E_SYNTAX\";\n\n/**\n * The XML is not well-formed; the message says where. The import stops.\n * @category Issue and loss codes\n */\nexport const XML_SYNTAX_CODE = \"E_XML_SYNTAX\";\n\n/**\n * An element the format does not define at that place was skipped.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_ELEMENT_CODE = \"W_UNKNOWN_ELEMENT\";\n\n/**\n * Text where the format allows only elements was ignored.\n * @category Issue and loss codes\n */\nexport const STRAY_TEXT_CODE = \"W_STRAY_TEXT\";\n\n/**\n * A `pid` / parent reference names a node the document never declares.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_PARENT_CODE = \"E_UNKNOWN_PARENT\";\n\n/**\n * A hyperedge (an edge with more than two ends) while `hyperedges` is \"error\". The import stops.\n * @category Issue and loss codes\n */\nexport const HYPEREDGE_CODE = \"E_HYPEREDGE\";\n\n/**\n * A declaration's key / attribute id is declared twice.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_KEY_CODE = \"E_DUPLICATE_KEY\";\n\n/**\n * An attribute was renamed `<name>#<suffix>` because another attribute already has its name, for example a repeated\n * column header.\n * @category Issue and loss codes\n */\nexport const COLUMN_RENAMED_CODE = \"W_COLUMN_RENAMED\";\n\n/**\n * A node or edge attribute is not written, because the format has nowhere to put it.\n * @category Issue and loss codes\n */\nexport const COLUMN_DROPPED_CODE = \"W_COLUMN_DROPPED\";\n\n/**\n * You read into a graph builder that already has an id, label or position attribute, so this file's one is kept as a\n * plain attribute.\n * @category Issue and loss codes\n */\nexport const ROLE_TAKEN_CODE = \"W_ROLE_TAKEN\";\n\n/**\n * An integer beyond 2^53 was stored as the nearest 64-bit float; pass `long: \"string\"` to keep every digit.\n * @category Issue and loss codes\n */\nexport const PRECISION_CODE = \"W_PRECISION\";\n\n/**\n * Two different id texts became the same number because `ids` is \"number\" (for example, \"042\" and \"42\"), so their nodes were\n * merged.\n * @category Issue and loss codes\n */\nexport const ID_MERGED_CODE = \"W_ID_MERGED\";\n\n/**\n * You read into your own graph builder, which was created with a different `addMissingNodes`, `duplicateEdges`,\n * `selfLoops` or `weightDtype` than the option you passed; the builder's setting applies.\n * @category Issue and loss codes\n */\nexport const SINK_OPTION_CODE = \"W_SINK_OPTION\";\n\n/**\n * You set an option this format does not use; it had no effect. The message names the option.\n * @category Issue and loss codes\n */\nexport const OPTION_IGNORED_CODE = \"W_OPTION_IGNORED\";\n\n/**\n * The input is not valid UTF-8. The import stops.\n * @category Issue and loss codes\n */\nexport const INVALID_UTF8_CODE = \"E_INVALID_UTF8\";\n\n/**\n * Some bytes are not valid in the encoding that was chosen (by a byte order mark, the file's own declaration or the\n * `encoding` option). The import stops.\n * @category Issue and loss codes\n */\nexport const INVALID_ENCODING_CODE = \"E_INVALID_ENCODING\";\n\n/**\n * Bytes that are not valid UTF-8 (and declare no other encoding) were read as windows-1252.\n * @category Issue and loss codes\n */\nexport const ENCODING_FALLBACK_CODE = \"W_ENCODING_FALLBACK\";\n\n/**\n * The file declares an encoding the platform's TextDecoder does not know; the declaration is ignored.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_ENCODING_CODE = \"W_UNKNOWN_ENCODING\";\n\n/**\n * A byte order mark, the `encoding` option and the encoding the file declares disagree, or the file\n * declares UTF-16 over bytes that are not UTF-16; the message names which one applied (a BOM wins\n * over the option and the declaration, the option over the declaration).\n * @category Issue and loss codes\n */\nexport const ENCODING_CONFLICT_CODE = \"W_ENCODING_CONFLICT\";\n\n/**\n * The text holds a control character, such as a NUL byte or an escape character, which is kept in the id or value it\n * is part of; or it ends with a Ctrl-Z end-of-file marker, which was ignored. Reported once per import.\n * @category Issue and loss codes\n */\nexport const CONTROL_CHARACTER_CODE = \"W_CONTROL_CHARACTER\";\n\n/**\n * The input is a known kind of file that is not a graph: an HTML page, a PDF, compressed or zip data, or an image. The\n * import stops.\n * @category Issue and loss codes\n */\nexport const FOREIGN_FORMAT_CODE = \"E_FOREIGN_FORMAT\";\n\n/**\n * More warnings of one code than a report keeps: the first ones are kept and this one warning (the\n * suppressed code as its element) counts the rest.\n * @category Issue and loss codes\n */\nexport const ISSUES_SUPPRESSED_CODE = \"W_ISSUES_SUPPRESSED\";\n\n/**\n * An XML attribute the format does not define (or does not keep) on that element; it is not kept.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_XML_ATTRIBUTE_CODE = \"W_UNKNOWN_XML_ATTRIBUTE\";\n\n/**\n * You read into a graph builder that already holds edges (or whose direction is locked), and its direction differs\n * from the file's, so the file is read with the builder's direction. A builder without edges takes the file's\n * direction.\n * @category Issue and loss codes\n */\nexport const DIRECTION_REFUSED_CODE = \"W_DIRECTION_REFUSED\";\n\n/**\n * `onMixedDirection` (\"directed\" or \"undirected\") made edges take a direction the file did not give them. It can\n * appear twice in one report: once for the direction the file declares and once for the edges that declared\n * their own.\n * @category Issue and loss codes\n */\nexport const DIRECTION_FORCED_CODE = \"W_DIRECTION_FORCED\";\n\n/**\n * The graph has both directed and undirected edges and `onMixedDirection` is \"error\". An import stops; a save to a\n * format that holds one direction per file fails with E_DIRECTED. Pass \"directed\" or \"undirected\" to read or write it\n * anyway.\n * @category Issue and loss codes\n */\nexport const MIXED_DIRECTION_CODE = \"E_MIXED_DIRECTION\";\n\n/**\n * A node or edge count the file announces is too large to reserve room for; it is ignored and the elements are read as\n * they come.\n * @category Issue and loss codes\n */\nexport const COUNT_HINT_CODE = \"W_COUNT_HINT\";\n\n/**\n * The declared type is not one the format defines; the column is kept as string.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_ATTR_TYPE_CODE = \"W_UNKNOWN_ATTR_TYPE\";\n\n/**\n * The declared default does not parse as the declared type; the column has no default.\n * @category Issue and loss codes\n */\nexport const BAD_DEFAULT_CODE = \"W_BAD_DEFAULT\";\n\n/**\n * A declared option does not parse as the declared type; the options are dropped.\n * @category Issue and loss codes\n */\nexport const BAD_OPTIONS_CODE = \"W_BAD_OPTIONS\";\n\n/**\n * A repeated edge id in a format whose edge ids are unique; the second edge is skipped.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_EDGE_ID_CODE = \"E_DUPLICATE_EDGE_ID\";\n\n/**\n * A value does not parse as its declared type; the cell is left unset.\n * @category Issue and loss codes\n */\nexport const BAD_VALUE_CODE = \"E_BAD_VALUE\";\n\n/**\n * An attribute's type was widened because a later value did not fit: an integer above 2^31 in an integer column, or\n * two declared types for one attribute.\n * @category Issue and loss codes\n */\nexport const WIDENED_CODE = \"W_WIDENED\";\n\n/**\n * A reference that is not a containment link (an attribute's element id, a layout or bypass\n * entry, a subnetwork member, a view's network, an undeclared target) names nothing; reported\n * once per kind with the count.\n * @category Issue and loss codes\n */\nexport const DANGLING_REFERENCE_CODE = \"W_DANGLING_REFERENCE\";\n\n/**\n * The same attribute twice on one element; one value is kept (the later, unless the format's specification says the first).\n * @category Issue and loss codes\n */\nexport const DUPLICATE_ATTRIBUTE_CODE = \"W_DUPLICATE_ATTRIBUTE\";\n\n/**\n * A containment link would close a parent cycle; that one link is dropped.\n * @category Issue and loss codes\n */\nexport const PARENT_CYCLE_CODE = \"E_PARENT_CYCLE\";\n\n/**\n * A formula (Cytoscape's `=ABS($x)`) is kept as its text; it is never evaluated.\n * @category Issue and loss codes\n */\nexport const EQUATION_AS_TEXT_CODE = \"W_EQUATION_AS_TEXT\";\n\n/**\n * The file's style rules (default styles, mappings, visual properties) are not applied to the graph. Reported once per\n * import; the message names what was not applied.\n * @category Issue and loss codes\n */\nexport const STYLES_NOT_IMPORTED_CODE = \"W_STYLES_NOT_IMPORTED\";\n\n/**\n * A CX file is an array of blocks, each an object with one key (`{\"nodes\": [...]}`). A member of that array\n * that is not such a block cannot be read and is skipped; the rest of the file is read.\n * @category Issue and loss codes\n */\nexport const BAD_ASPECT_BLOCK_CODE = \"E_BAD_ASPECT_BLOCK\";\n\n/**\n * A CX array member holding several aspects (`{\"nodes\": [...], \"edges\": [...]}`); each array-valued key is read as its own fragment.\n * @category Issue and loss codes\n */\nexport const MULTI_ASPECT_FRAGMENT_CODE = \"W_MULTI_ASPECT_FRAGMENT\";\n\n/**\n * A CX aspect, an array of elements, written as one object (`{\"nodes\": {\"@id\": 1}}`); read as one element.\n * @category Issue and loss codes\n */\nexport const SINGLE_OBJECT_ASPECT_CODE = \"W_SINGLE_OBJECT_ASPECT\";\n\n/**\n * The document uses the bare tokens NaN, Infinity or -Infinity (Python's json writes them), which\n * strict JSON does not allow; read as the numbers.\n * @category Issue and loss codes\n */\nexport const JSON_NONSTANDARD_NUMBER_CODE = \"W_JSON_NONSTANDARD_NUMBER\";\n\n/**\n * A CX aspect out of its place (after the post-metadata, a status that is not last, a block buffered for what it depends on).\n * @category Issue and loss codes\n */\nexport const ASPECT_ORDER_CODE = \"W_ASPECT_ORDER\";\n\n/**\n * A declared element count disagrees with what was read.\n * @category Issue and loss codes\n */\nexport const COUNT_MISMATCH_CODE = \"W_COUNT_MISMATCH\";\n\n/**\n * The program that wrote the file marked it as failed (CX `status.success: false`), so it is incomplete. The import\n * stops.\n * @category Issue and loss codes\n */\nexport const STATUS_FAILED_CODE = \"E_STATUS_FAILED\";\n\n/**\n * The producer marked the document as successful but attached an error text.\n * @category Issue and loss codes\n */\nexport const STATUS_WARNING_CODE = \"W_STATUS_WARNING\";\n\n/**\n * The input is beyond a size limit (a zip's uncompressed total or ratio, a document longer than\n * one string); the same string as graph-format's E_TOO_LARGE, recorded with category \"unsupported\".\n * @category Issue and loss codes\n */\nexport const TOO_LARGE_CODE = \"E_TOO_LARGE\";\n\n/**\n * A value that does not fit its attribute's type. An import records it for a value that does not parse as the\n * type the file declares for it (\"x\" in an integer attribute): GraphML and GEXF leave that value unset, Neo4j CSV\n * skips the row. A save throws it when an attribute value cannot be written in the format; `checkExport()` returns\n * the `E_` note that names the attribute.\n * @category Issue and loss codes\n */\nexport const COLUMN_TYPE_CODE = \"E_COLUMN_TYPE\";\n\n/**\n * `graphIndex` is past the last graph in the file, or `graphName` matches none of them. The import stops, and\n * the message lists the index and name of each graph the file holds.\n * @category Issue and loss codes\n */\nexport const GRAPH_NOT_FOUND_CODE = \"E_GRAPH_NOT_FOUND\";\n\n/**\n * `graphName` matches more than one graph; the message lists their positions. The import stops.\n * @category Issue and loss codes\n */\nexport const AMBIGUOUS_GRAPH_NAME_CODE = \"E_AMBIGUOUS_GRAPH_NAME\";\n\n/**\n * A second edge between the same two nodes under `duplicateEdges: \"error\"`; the import stops.\n * @category Issue and loss codes\n */\nexport const DUPLICATE_EDGE_CODE = \"E_DUPLICATE_EDGE\" satisfies GraphFormatErrorCode;\n\n/**\n * An edge from a node to itself under `selfLoops: \"error\"`; the import stops.\n * @category Issue and loss codes\n */\nexport const SELF_LOOP_CODE = \"E_SELF_LOOP\" satisfies GraphFormatErrorCode;\n\n/**\n * The `duplicateEdges` option merged parallel edges into one edge per pair, so whatever told them apart (an OBO\n * relation, a label) is lost. Recorded once, with the number merged.\n * @category Issue and loss codes\n */\nexport const EDGES_MERGED_CODE = \"W_EDGES_MERGED\";\n\n/**\n * Self-loops were removed by the `selfLoops: \"drop\"` option. Recorded once by importGraph() with the\n * number removed.\n * @category Issue and loss codes\n */\nexport const SELF_LOOPS_DROPPED_CODE = \"W_SELF_LOOPS_DROPPED\";\n\n/**\n * An option name that no format and no load or save function takes, most often a misspelling (`delimeter`). The\n * option has no effect. A load records it as a warning, and `checkExport()` returns it as a note. Reported only while\n * every registered format lists its options, as the built-in formats do.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_OPTION_CODE = \"W_UNKNOWN_OPTION\";\n\n/**\n * A line the `parseLine` function of a format made with defineLineFormat() could not read (it threw). The line is\n * skipped; the message is the one the function threw with.\n * @category Issue and loss codes\n */\nexport const BAD_LINE_CODE = \"E_BAD_LINE\";\n\n/**\n * The `weightFrom` option names an attribute no edge has a value for, so the graph has no weights. The usual cause\n * is one options object shared between formats: GML and Pajek keep their weights in `value`, not `weight`. Recorded\n * once by importGraph().\n * @category Issue and loss codes\n */\nexport const WEIGHT_NOT_FOUND_CODE = \"W_WEIGHT_NOT_FOUND\";\n\n// ============================================================ exporter loss notes\n\n/**\n * An attribute with a role the format has no place for is written as a plain attribute; the role is lost.\n * @category Issue and loss codes\n */\nexport const ROLE_DROPPED_CODE = \"W_ROLE_DROPPED\";\n\n/**\n * A parents (multi-parent) column in a format with single containment only.\n * @category Issue and loss codes\n */\nexport const PARENTS_DROPPED_CODE = \"W_PARENTS_DROPPED\";\n\n/**\n * A start / end / timestamp column in a format without temporal support.\n * @category Issue and loss codes\n */\nexport const TEMPORAL_DROPPED_CODE = \"W_TEMPORAL_DROPPED\";\n\n/**\n * A mutual pair is written as two directed edges without the mutual mark.\n * @category Issue and loss codes\n */\nexport const MUTUAL_EXPANDED_CODE = \"W_MUTUAL_EXPANDED\";\n\n/**\n * A mutual pair is written as one undirected edge; the pair reads back undirected, the mark is lost.\n * @category Issue and loss codes\n */\nexport const MUTUAL_AS_UNDIRECTED_CODE = \"W_MUTUAL_AS_UNDIRECTED\";\n\n/**\n * Node ids whose written text reads back as the other type under the importer's id rule.\n * @category Issue and loss codes\n */\nexport const ID_TEXT_TYPE_CODE = \"W_ID_TEXT_TYPE\";\n\n/**\n * Two node ids would be written as the same text (the number 5 and the text \"5\"); the save fails with E_INVALID_ID.\n * @category Issue and loss codes\n */\nexport const ID_TEXT_COLLISION_CODE = \"E_ID_TEXT_COLLISION\";\n\n/**\n * A `<column>.text` companion the format cannot carry.\n * @category Issue and loss codes\n */\nexport const TEMPORAL_TEXT_DROPPED_CODE = \"W_TEMPORAL_TEXT_DROPPED\";\n\n/**\n * The graph has edge weights and the file does not keep them: every edge reads back with the default weight 1.\n * @category Issue and loss codes\n */\nexport const WEIGHTS_DROPPED_CODE = \"W_WEIGHTS_DROPPED\";\n\n/**\n * An attribute without the weight role is named like the attribute graph-io reads weights from (for example, `weight`), so it\n * reads back as the edge weight.\n * @category Issue and loss codes\n */\nexport const WEIGHT_KEY_CLASH_CODE = \"W_WEIGHT_KEY_CLASH\";\n\n/**\n * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and reads\n * back with that role.\n * @category Issue and loss codes\n */\nexport const ROLE_ASSUMED_CODE = \"W_ROLE_ASSUMED\";\n\n/**\n * An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the name\n * the format's importer gives it.\n * @category Issue and loss codes\n */\nexport const COLUMN_RENAMED_LOSS_CODE = \"W_COLUMN_NAME_CHANGED\";\n\n/**\n * A declared column whose every row is unset is not written by a format without declarations.\n * @category Issue and loss codes\n */\nexport const EMPTY_COLUMN_DROPPED_CODE = \"W_EMPTY_COLUMN_DROPPED\";\n\n/**\n * A text attribute reads back as a dictionary attribute, or the reverse, because the importer chooses by how often its\n * values repeat. The values are the same.\n * @category Issue and loss codes\n */\nexport const STORAGE_CLASS_CODE = \"W_STORAGE_CLASS_CHANGED\";\n\n/**\n * A number attribute whose values are all whole numbers reads back as integers, because the format does not record the\n * type.\n * @category Issue and loss codes\n */\nexport const INTEGRAL_F64_CODE = \"W_INTEGRAL_F64_AS_I32\";\n\n/**\n * A text value that reads back as a number or a boolean, because the format does not record that it was text (the text\n * \"42\" reads back as the number 42).\n * @category Issue and loss codes\n */\nexport const TEXT_INFERRED_CODE = \"W_TEXT_INFERRED\";\n\n/**\n * A text value holds a character XML 1.0 forbids (most control characters); the save fails with E_COLUMN_TYPE.\n * @category Issue and loss codes\n */\nexport const XML_ILLEGAL_CHAR_CODE = \"E_XML_ILLEGAL_CHAR\";\n\n/**\n * An ontology exporter (OBO, OBO Graphs) writes a node column outside its vocabulary as property values; it reads back inside the `property_value` column.\n * @category Issue and loss codes\n */\nexport const COLUMN_AS_PROPERTY_VALUE_CODE = \"W_COLUMN_AS_PROPERTY_VALUE\";\n\n/**\n * An ontology exporter writes an edge without a relation as `is_a` (or the relation the caller chose).\n * @category Issue and loss codes\n */\nexport const RELATION_ASSUMED_CODE = \"W_RELATION_ASSUMED\";\n\n/**\n * An ontology exporter writes Typedef (property) nodes; they read back as nodes only under the importer's `typedefs: \"nodes\"`.\n * @category Issue and loss codes\n */\nexport const TYPEDEF_NODES_CODE = \"W_TYPEDEF_NODES\";\n\n/**\n * A graph attribute is written into the file's metadata and reads back in `snapshot.meta.extra`, not as a graph\n * attribute.\n * @category Issue and loss codes\n */\nexport const GRAPH_COLUMN_AS_METADATA_CODE = \"W_GRAPH_COLUMN_AS_METADATA\";\n\n/**\n * The file cannot record direction: an undirected graph's edges read back as directed, or the whole graph reads back\n * with the importer's default direction.\n * @category Issue and loss codes\n */\nexport const DIRECTION_DROPPED_CODE = \"W_DIRECTION_DROPPED\";\n\n/**\n * NaN and the infinities cannot be written; they are written as null and read back unset.\n * @category Issue and loss codes\n */\nexport const NONFINITE_AS_NULL_CODE = \"W_NONFINITE_AS_NULL\";\n\n/**\n * The nodes read back in a different order.\n * @category Issue and loss codes\n */\nexport const NODE_ORDER_CODE = \"W_NODE_ORDER\";\n\n/**\n * A text attribute stored as a dictionary, without a declared list of allowed values, reads back with its distinct\n * values as that list.\n * @category Issue and loss codes\n */\nexport const OPTIONS_GAINED_CODE = \"W_OPTIONS_GAINED\";\n\n// ============================================================ the graph builder\n\n/**\n * The issue codes every importer can record when the graph refuses an element, by name; every\n * format's `<FMT>_ISSUE` table includes them. Each one skips that element and counts toward\n * `errorLimit`.\n * @category Issue and loss codes\n */\nexport const ELEMENT_ISSUE = Object.freeze({\n /** A node id of a type the graph cannot hold (an empty id, a non-finite number, an object). The node is skipped. */\n INVALID_ID: \"E_INVALID_ID\",\n /** An edge names a node the file never declares, while `addMissingNodes` is false. The edge is skipped. */\n UNKNOWN_NODE: \"E_UNKNOWN_NODE\",\n /**\n * An edge weight that is not a number (text such as \"2,5\" with a decimal comma, NaN, a boolean). The whole edge is\n * skipped, not only its weight.\n */\n INVALID_WEIGHT: \"E_INVALID_WEIGHT\",\n /** A second edge between the same two nodes, while `duplicateEdges` is \"error\". The edge is skipped. */\n DUPLICATE_EDGE: DUPLICATE_EDGE_CODE,\n /** An edge from a node to itself, while `selfLoops` is \"error\". The edge is skipped. */\n SELF_LOOP: SELF_LOOP_CODE,\n /** An edge id declared twice. The second edge is skipped. */\n DUPLICATE_EDGE_ID: DUPLICATE_EDGE_ID_CODE,\n});\n\n// ============================================================ the input layer\n\n/**\n * The issue codes every importer can record for input it cannot read (empty, too large, invalid\n * bytes, not a graph file), by name; every format's `<FMT>_ISSUE` table includes them.\n * @category Issue and loss codes\n */\nexport const INPUT_ISSUE = Object.freeze({\n /** The input is empty or holds only whitespace. The import stops. */\n EMPTY_INPUT: EMPTY_INPUT_CODE,\n /** The input, or one of its lines, is longer than a JavaScript string can hold. The import stops. */\n TOO_LARGE: TOO_LARGE_CODE,\n /** A byte order mark, the encoding option and the declared encoding disagree. */\n ENCODING_CONFLICT: ENCODING_CONFLICT_CODE,\n /** A control character or a stray U+FEFF in the text, or an ignored trailing Ctrl-Z. */\n CONTROL_CHARACTER: CONTROL_CHARACTER_CODE,\n /** The input is an HTML page, a PDF, compressed or zip data, or an image. The import stops. */\n FOREIGN_FORMAT: FOREIGN_FORMAT_CODE,\n /** Warnings of one code beyond the number a report keeps, counted in one warning. */\n ISSUES_SUPPRESSED: ISSUES_SUPPRESSED_CODE,\n});\n","/**\n * Count agreement for messages, so a report says \"1 node is\" and \"3 nodes are\", never \"1 node(s) are\".\n */\n\n/**\n * The plural ending of a regular noun after a count.\n * @param n - the count\n * @returns \"\" for 1, \"s\" otherwise\n */\nexport function plural(n: number): string {\n return n === 1 ? \"\" : \"s\";\n}\n\n/**\n * The form of a verb that agrees with a count.\n * @param n - the count\n * @param one - the form for 1 (\"is\", \"was\", \"holds\")\n * @param many - the form for any other count (\"are\", \"were\", \"hold\")\n * @returns `one` or `many`\n */\nexport function agree(n: number, one: string, many: string): string {\n return n === 1 ? one : many;\n}\n\n/**\n * A count of nodes and edges, leaving out a zero: \"2 nodes and 1 edge\", \"1 edge\".\n * @param nodes - the node count\n * @param edges - the edge count\n * @returns the text\n */\nexport function elementCount(nodes: number, edges: number): string {\n const parts = [];\n if (nodes > 0 || edges === 0) {\n parts.push(`${nodes} node${plural(nodes)}`);\n }\n if (edges > 0) {\n parts.push(`${edges} edge${plural(edges)}`);\n }\n return parts.join(\" and \");\n}\n\n/**\n * A word with its indefinite article, chosen by its first letter: \"an integer\", \"a double\".\n * @param word - the word\n * @returns \"a word\" or \"an word\"\n */\nexport function withArticle(word: string): string {\n return `${/^[aeiou]/i.test(word) ? \"an\" : \"a\"} ${word}`;\n}\n","/**\n * Temporal values (design section 5.1): `date` / `dateTime` attributes, GEXF time bounds and Neo4j\n * temporal types become f64 epoch milliseconds (or the raw numeric time when the file's timeformat\n * is integer / double). When a value's canonical re-formatting would differ from the source text (a\n * UTC offset, fractional seconds beyond milliseconds, a date without a time in a dateTime column)\n * the importer also keeps the lexical form in a companion `<column>.text` column with role\n * `timeText`; the exporter emits the text when present and formats the number otherwise.\n *\n * Only ISO-8601 forms are parsed (XSD date / dateTime / time and Neo4j's literals), by a fixed\n * grammar rather than Date.parse, so two hosts agree on every value.\n */\n\nimport { type ColumnDecl, type ColumnRole, GraphFormatError } from \"@graphty/graph-format\";\n\n/**\n * The temporal kinds a declared type can map to; each fixes the parse grammar and the canonical text.\n * @category Plugin helpers\n */\nexport type TemporalKind = \"date\" | \"dateTime\" | \"localDateTime\" | \"time\" | \"localTime\";\n\n/**\n * The GEXF `timeformat` values (design section 5.9).\n * @public\n * @category Plugin helpers\n */\nexport type TimeFormat = \"integer\" | \"double\" | \"date\" | \"dateTime\";\n\n/**\n * A parsed temporal value: the number stored in the column and the source text when it must be kept.\n * @category Plugin helpers\n */\nexport interface TemporalValue {\n /** Epoch milliseconds (date, dateTime, localDateTime), milliseconds since midnight (time, localTime), or the raw number. */\n readonly value: number;\n /** The source text when formatTemporal(value) would not reproduce it; null otherwise. */\n readonly text: string | null;\n}\n\n/**\n * The role of a companion text column.\n * @category Plugin helpers\n */\nexport const TIME_TEXT_ROLE: ColumnRole = \"timeText\";\n\n/**\n * The suffix of a companion text column's name.\n * @category Plugin helpers\n */\nexport const TIME_TEXT_SUFFIX = \".text\";\n\nconst DATE_TIME =\n /^(-?\\d{4,})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{1,9}))?)?)?(Z|z|[+-]\\d{2}(?::?\\d{2})?)?$/;\nconst TIME = /^(\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.(\\d{1,9}))?)?(Z|z|[+-]\\d{2}(?::?\\d{2})?)?$/;\nconst NUMERIC = /^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?$/;\n\nconst MS_PER_MINUTE = 60_000;\nconst MS_PER_DAY = 86_400_000;\n\n/**\n * Parse an ISO-8601 temporal text of one kind.\n * @param text - the source text (already trimmed)\n * @param kind - the temporal kind\n * @returns the value and, when the canonical form differs, the source text; E_COLUMN_TYPE when the text is not of the kind\n * @category Plugin helpers\n */\nexport function parseTemporal(text: string, kind: TemporalKind): TemporalValue {\n let value: number;\n switch (kind) {\n case \"date\":\n case \"dateTime\":\n case \"localDateTime\":\n value = parseDateTime(text, kind);\n break;\n case \"time\":\n case \"localTime\":\n value = parseTime(text, kind);\n break;\n default: {\n const name: string = kind;\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `unknown temporal kind ${name}`, { kind: name });\n }\n }\n const canonical = formatTemporal(value, kind);\n return { value, text: canonical === text ? null : text };\n}\n\n/**\n * Parse a GEXF time bound (`start`, `end`, `timestamp`, a spell or an attvalue time) under the\n * graph's timeformat: integer / double values are numbers as written; date / dateTime values are\n * ISO text; when the format is unknown (absent header) numeric text is a number and anything else\n * is tried as dateTime.\n * @param text - the source text\n * @param timeFormat - the graph's timeformat, or null when the file declares none\n * @returns the value and the source text when it must be kept\n * @category Plugin helpers\n */\nexport function parseTimeText(text: string, timeFormat: TimeFormat | null): TemporalValue {\n const trimmed = text.trim();\n switch (timeFormat) {\n case \"integer\":\n case \"double\":\n return parseNumericTime(trimmed);\n case \"date\":\n return parseTemporal(trimmed, \"date\");\n case \"dateTime\":\n return parseTemporal(trimmed, \"dateTime\");\n case null:\n return NUMERIC.test(trimmed) ? parseNumericTime(trimmed) : parseTemporal(trimmed, \"dateTime\");\n default: {\n const name: string = timeFormat;\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `unknown timeformat ${name}`, { timeFormat: name });\n }\n }\n}\n\n/**\n * Format a temporal value in its canonical ISO-8601 form.\n * @param value - the number as stored\n * @param kind - the temporal kind\n * @returns the canonical text: `YYYY-MM-DD`, `YYYY-MM-DDTHH:MM:SS[.mmm]Z`, `YYYY-MM-DDTHH:MM:SS[.mmm]`,\n * `HH:MM:SS[.mmm]Z` or `HH:MM:SS[.mmm]`\n * @category Plugin helpers\n */\nexport function formatTemporal(value: number, kind: TemporalKind): string {\n switch (kind) {\n case \"date\": {\n const d = new Date(value);\n return `${year(d)}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;\n }\n case \"dateTime\":\n case \"localDateTime\": {\n const d = new Date(value);\n const date = `${year(d)}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())}`;\n const time = clock(d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), d.getUTCMilliseconds());\n return kind === \"dateTime\" ? `${date}T${time}Z` : `${date}T${time}`;\n }\n case \"time\":\n case \"localTime\": {\n const ms = ((value % MS_PER_DAY) + MS_PER_DAY) % MS_PER_DAY;\n const h = Math.floor(ms / 3_600_000);\n const m = Math.floor((ms % 3_600_000) / MS_PER_MINUTE);\n const s = Math.floor((ms % MS_PER_MINUTE) / 1000);\n const text = clock(h, m, s, ms % 1000);\n return kind === \"time\" ? `${text}Z` : text;\n }\n default: {\n const name: string = kind;\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `unknown temporal kind ${name}`, { kind: name });\n }\n }\n}\n\n/**\n * Format a GEXF time bound under the graph's timeformat: the number's shortest text for integer /\n * double, the canonical ISO form otherwise.\n * @param value - the value as stored\n * @param timeFormat - the graph's timeformat, or null for a file that declares none (double)\n * @returns the text\n * @category Plugin helpers\n */\nexport function formatTimeValue(value: number, timeFormat: TimeFormat | null): string {\n switch (timeFormat) {\n case \"date\":\n return formatTemporal(value, \"date\");\n case \"dateTime\":\n return formatTemporal(value, \"dateTime\");\n case \"integer\":\n case \"double\":\n case null:\n return String(value);\n default: {\n const name: string = timeFormat;\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `unknown timeformat ${name}`, { timeFormat: name });\n }\n }\n}\n\n/**\n * The declaration of the companion text column of a temporal column (design section 5.1).\n * @param column - the temporal column's name\n * @returns a string column named `<column>.text` with role timeText and `extra.for` naming the column\n * @category Plugin helpers\n */\nexport function timeTextCompanion(column: string): ColumnDecl {\n return {\n name: column + TIME_TEXT_SUFFIX,\n dtype: \"string\",\n role: TIME_TEXT_ROLE,\n nullable: true,\n extra: { for: column },\n };\n}\n\n/**\n * Parse a numeric time bound.\n * @param text - the trimmed source text\n * @returns the number; the text is kept when String(value) differs from it\n */\nfunction parseNumericTime(text: string): TemporalValue {\n if (!NUMERIC.test(text)) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is not a numeric time`, { value: text });\n }\n const value = Number(text);\n return { value, text: String(value) === text ? null : text };\n}\n\n/**\n * Parse a date or date-time text into epoch milliseconds.\n * @param text - the source text\n * @param kind - date (a time part is accepted and kept in the companion), dateTime or localDateTime\n * @returns epoch milliseconds\n */\nfunction parseDateTime(text: string, kind: \"date\" | \"dateTime\" | \"localDateTime\"): number {\n const m = DATE_TIME.exec(text);\n if (m === null) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is not an ISO-8601 ${kind}`, { value: text, kind });\n }\n const y = Number(m[1]);\n const month = Number(m[2]);\n const day = Number(m[3]);\n const hour = m[4] === undefined ? 0 : Number(m[4]);\n const minute = m[5] === undefined ? 0 : Number(m[5]);\n const second = m[6] === undefined ? 0 : Number(m[6]);\n const ms = m[7] === undefined ? 0 : fractionMs(m[7]);\n if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 24 || minute > 59 || second > 60) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is out of range for a ${kind}`, { value: text, kind });\n }\n // Date.UTC maps years 0..99 to 1900..1999; the setters do not\n const d = new Date(0);\n d.setUTCFullYear(y, month - 1, day);\n d.setUTCHours(hour, minute, second, ms);\n if (d.getUTCDate() !== day && hour !== 24) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is not a calendar date`, { value: text, kind });\n }\n const offset = m[8] === undefined ? 0 : offsetMinutes(m[8]);\n return d.getTime() - offset * MS_PER_MINUTE;\n}\n\n/**\n * Parse a time-of-day text into milliseconds since midnight (UTC for `time` with a zone).\n * @param text - the source text\n * @param kind - time or localTime\n * @returns milliseconds since midnight in 0..86400000\n */\nfunction parseTime(text: string, kind: \"time\" | \"localTime\"): number {\n const m = TIME.exec(text);\n if (m === null) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is not an ISO-8601 ${kind}`, { value: text, kind });\n }\n const hour = Number(m[1]);\n const minute = Number(m[2]);\n const second = m[3] === undefined ? 0 : Number(m[3]);\n const ms = m[4] === undefined ? 0 : fractionMs(m[4]);\n if (hour > 24 || minute > 59 || second > 60) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${text}\" is out of range for a ${kind}`, { value: text, kind });\n }\n const local = ((hour * 60 + minute) * 60 + second) * 1000 + ms;\n const offset = m[5] === undefined ? 0 : offsetMinutes(m[5]);\n const utc = local - offset * MS_PER_MINUTE;\n return ((utc % MS_PER_DAY) + MS_PER_DAY) % MS_PER_DAY;\n}\n\n/**\n * Milliseconds of a fractional-second text (1 to 9 digits), truncated to whole milliseconds.\n * @param digits - the digits after the decimal point\n * @returns 0..999\n */\nfunction fractionMs(digits: string): number {\n return Math.floor(Number(`0.${digits}`) * 1000);\n}\n\n/**\n * The minutes of a zone designator.\n * @param zone - \"Z\", \"+HH:MM\", \"+HHMM\" or \"+HH\"\n * @returns signed minutes east of UTC\n */\nfunction offsetMinutes(zone: string): number {\n if (zone === \"Z\" || zone === \"z\") {\n return 0;\n }\n const sign = zone.startsWith(\"-\") ? -1 : 1;\n const digits = zone.slice(1).replace(\":\", \"\");\n const hours = Number(digits.slice(0, 2));\n const minutes = digits.length > 2 ? Number(digits.slice(2, 4)) : 0;\n return sign * (hours * 60 + minutes);\n}\n\n/**\n * Two-digit zero-padded text.\n * @param n - 0..99\n * @returns the text\n */\nfunction pad2(n: number): string {\n return n < 10 ? `0${n}` : String(n);\n}\n\n/**\n * The year of a UTC date as at least four digits (negative years keep their sign).\n * @param d - the date\n * @returns the text\n */\nfunction year(d: Date): string {\n const y = d.getUTCFullYear();\n const abs = String(Math.abs(y)).padStart(4, \"0\");\n return y < 0 ? `-${abs}` : abs;\n}\n\n/**\n * `HH:MM:SS` with `.mmm` appended when the milliseconds are not zero.\n * @param h - hours\n * @param m - minutes\n * @param s - seconds\n * @param ms - milliseconds\n * @returns the text\n */\nfunction clock(h: number, m: number, s: number, ms: number): string {\n const base = `${pad2(h)}:${pad2(m)}:${pad2(s)}`;\n return ms === 0 ? base : `${base}.${String(ms).padStart(3, \"0\")}`;\n}\n","/**\n * The declared-type table of design section 5.1: what the type text of a GEXF `<attribute>`, a\n * GraphML `<key>`, a GML value class or a Neo4j CSV header maps to, and how a value's text is\n * parsed once the column dtype is known.\n *\n * Declared types map exactly: `double` and `long` -> f64, `float` -> f32, `int` / `integer` /\n * `short` / `byte` -> i32, `boolean` -> bool, `string` / `anyURI` / `char` -> string, `bigdecimal`\n * / `biginteger` -> string, `date` / `dateTime` and the Neo4j temporal types -> f64 (epoch or\n * time-of-day milliseconds, design section 5.1), `list*` and `[]` -> list of the item type, GML\n * `record` and Neo4j `point` -> json. A declared `long` is stored as f64 with a precision flag\n * (values beyond 2^53 are a `precision` issue) unless the importer option `long: \"string\"` keeps the\n * whole column as text. Neo4j `duration` has no epoch and is kept as text. The declared type text is\n * recorded in `origin.type` in every case so the exporter can restore the declaration.\n */\n\nimport { type Dtype, GraphFormatError, type ScalarDtype } from \"@graphty/graph-format\";\n\nimport { withArticle } from \"./plural.js\";\nimport { parseTemporal, type TemporalKind } from \"./temporal.js\";\n\n/**\n * The formats whose declarations this table covers.\n * @category Plugin helpers\n */\nexport type DeclaringFormat = \"gexf\" | \"graphml\" | \"gml\" | \"neo4j\";\n\n/** How the text of one scalar value (or one list item) is parsed. */\ntype ValueKind =\n \"boolean\" | \"integer\" | \"long\" | \"float\" | \"double\" | \"string\" | \"temporal\" | \"duration\" | \"point\" | \"json\";\n\n/**\n * The resolved storage of a declared type.\n * @category Plugin helpers\n */\nexport interface DeclaredTypeSpec {\n /** The declared type text as given by the file (origin.type). */\n readonly declared: string;\n /** The column dtype. */\n readonly dtype: Dtype;\n /** The item dtype of a list column, null otherwise. */\n readonly itemDtype: ScalarDtype | null;\n /** How each scalar value or list item text is parsed. */\n readonly kind: ValueKind;\n /** Whether the column is a list. */\n readonly list: boolean;\n /** The temporal kind of a temporal value, null otherwise. */\n readonly temporal: TemporalKind | null;\n /** Whether values may exceed 2^53 (a long stored as f64), so the importer checks Number.isSafeInteger. */\n readonly precision: boolean;\n}\n\n/** A scalar entry of the table before the list flag and the long mode are applied. */\ninterface ScalarEntry {\n readonly dtype: ScalarDtype;\n readonly kind: ValueKind;\n readonly temporal?: TemporalKind;\n readonly precision?: boolean;\n}\n\nconst BOOLEAN: ScalarEntry = { dtype: \"bool\", kind: \"boolean\" };\nconst INTEGER: ScalarEntry = { dtype: \"i32\", kind: \"integer\" };\nconst LONG: ScalarEntry = { dtype: \"f64\", kind: \"long\", precision: true };\nconst FLOAT: ScalarEntry = { dtype: \"f32\", kind: \"float\" };\nconst DOUBLE: ScalarEntry = { dtype: \"f64\", kind: \"double\" };\nconst STRING: ScalarEntry = { dtype: \"string\", kind: \"string\" };\nconst DATE: ScalarEntry = { dtype: \"f64\", kind: \"temporal\", temporal: \"date\" };\nconst DATE_TIME: ScalarEntry = { dtype: \"f64\", kind: \"temporal\", temporal: \"dateTime\" };\nconst LOCAL_DATE_TIME: ScalarEntry = { dtype: \"f64\", kind: \"temporal\", temporal: \"localDateTime\" };\nconst TIME: ScalarEntry = { dtype: \"f64\", kind: \"temporal\", temporal: \"time\" };\nconst LOCAL_TIME: ScalarEntry = { dtype: \"f64\", kind: \"temporal\", temporal: \"localTime\" };\nconst DURATION: ScalarEntry = { dtype: \"string\", kind: \"duration\" };\nconst POINT: ScalarEntry = { dtype: \"json\", kind: \"point\" };\nconst RECORD: ScalarEntry = { dtype: \"json\", kind: \"json\" };\n\n/** GEXF 1.2 / 1.3 scalar types (the `list*` variants are derived). */\nconst GEXF_SCALARS: Readonly<Record<string, ScalarEntry>> = {\n integer: INTEGER,\n long: LONG,\n double: DOUBLE,\n float: FLOAT,\n boolean: BOOLEAN,\n string: STRING,\n anyuri: STRING,\n char: STRING,\n byte: INTEGER,\n short: INTEGER,\n bigdecimal: STRING,\n biginteger: STRING,\n date: DATE,\n datetime: DATE_TIME,\n};\n\n/** GraphML `attr.type` values. */\nconst GRAPHML_SCALARS: Readonly<Record<string, ScalarEntry>> = {\n boolean: BOOLEAN,\n int: INTEGER,\n long: LONG,\n float: FLOAT,\n double: DOUBLE,\n string: STRING,\n};\n\n/** GML value classes (per value in the file; the GML importer names them per column). */\nconst GML_SCALARS: Readonly<Record<string, ScalarEntry>> = {\n int: INTEGER,\n real: DOUBLE,\n string: STRING,\n record: RECORD,\n};\n\n/** Neo4j CSV header types and Cypher property type names. */\nconst NEO4J_SCALARS: Readonly<Record<string, ScalarEntry>> = {\n int: INTEGER,\n integer: LONG,\n long: LONG,\n float: FLOAT,\n double: DOUBLE,\n boolean: BOOLEAN,\n byte: INTEGER,\n short: INTEGER,\n char: STRING,\n string: STRING,\n point: POINT,\n date: DATE,\n localtime: LOCAL_TIME,\n time: TIME,\n localdatetime: LOCAL_DATE_TIME,\n datetime: DATE_TIME,\n duration: DURATION,\n};\n\nconst TABLES: Readonly<Record<DeclaringFormat, Readonly<Record<string, ScalarEntry>>>> = {\n gexf: GEXF_SCALARS,\n graphml: GRAPHML_SCALARS,\n gml: GML_SCALARS,\n neo4j: NEO4J_SCALARS,\n};\n\nconst INTEGER_TEXT = /^[+-]?\\d+$/;\nconst DECIMAL_TEXT = /^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][+-]?\\d+)?$/;\nconst I32_MIN = -2147483648;\nconst I32_MAX = 2147483647;\n\n/**\n * Resolve a declared type text of one format to its storage.\n * @param format - the declaring format\n * @param type - the type text as written (matched case-insensitively; GEXF `list*` and Neo4j `[]` denote lists)\n * @param long - how declared long / integer-64 columns are stored (the importer option)\n * @returns the spec, or null when the format does not define the type (the importer reports it and keeps the text)\n * @category Plugin helpers\n */\nexport function mapDeclaredType(\n format: DeclaringFormat,\n type: string,\n long: \"f64\" | \"string\",\n): DeclaredTypeSpec | null {\n const table = TABLES[format];\n let key = type.trim().toLowerCase();\n let list = false;\n if (format === \"gexf\" && key.startsWith(\"list\") && key.length > 4) {\n list = true;\n key = key.slice(4);\n } else if (format === \"neo4j\" && key.endsWith(\"[]\")) {\n list = true;\n key = key.slice(0, -2);\n } else if (format === \"gml\" && key === \"list\") {\n return {\n declared: type,\n dtype: \"list\",\n itemDtype: \"json\",\n kind: \"json\",\n list: true,\n temporal: null,\n precision: false,\n };\n }\n let entry = table[key];\n if (entry === undefined) {\n return null;\n }\n if (entry.kind === \"long\" && long === \"string\") {\n entry = STRING;\n }\n return {\n declared: type,\n dtype: list ? \"list\" : entry.dtype,\n itemDtype: list ? entry.dtype : null,\n kind: entry.kind,\n list,\n temporal: entry.temporal ?? null,\n precision: entry.precision ?? false,\n };\n}\n\n/**\n * The spec of an untyped attribute: a string column with the given (or absent) type text.\n * @param declared - the type text to record in origin.type, or null\n * @returns a string spec\n * @category Plugin helpers\n */\nexport function stringSpec(declared: string | null): DeclaredTypeSpec {\n return {\n declared: declared ?? \"\",\n dtype: \"string\",\n itemDtype: null,\n kind: \"string\",\n list: false,\n temporal: null,\n precision: false,\n };\n}\n\n/**\n * Parse the text of one scalar value (or list item) by its kind. Numeric and boolean kinds trim\n * surrounding whitespace (pretty-printed XML puts newlines around `<data>` text); string kinds keep\n * the text exactly.\n * @param text - the value text\n * @param kind - the value kind\n * @param temporal - the temporal kind when `kind` is \"temporal\"\n * @returns a boolean, a number, a string or (point / json) a JSON value; E_COLUMN_TYPE when the text is not of the kind\n * @category Plugin helpers\n */\nexport function parseScalarText(text: string, kind: ValueKind, temporal: TemporalKind | null = null): unknown {\n switch (kind) {\n case \"boolean\": {\n const value = parseBooleanText(text);\n if (value === null) {\n throw typeError(text, kind);\n }\n return value;\n }\n case \"integer\": {\n const trimmed = text.trim();\n if (!INTEGER_TEXT.test(trimmed)) {\n throw typeError(text, kind);\n }\n const value = Number(trimmed);\n if (value < I32_MIN || value > I32_MAX) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${trimmed}\" is outside the 32-bit integer range`, {\n value: trimmed,\n kind,\n });\n }\n return value;\n }\n case \"long\": {\n const trimmed = text.trim();\n if (!INTEGER_TEXT.test(trimmed)) {\n throw typeError(text, kind);\n }\n return Number(trimmed);\n }\n case \"float\":\n case \"double\":\n return parseDecimalText(text, kind);\n case \"string\":\n case \"duration\":\n return text;\n case \"temporal\": {\n if (temporal === null) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", \"a temporal value needs its temporal kind\", { kind });\n }\n return parseTemporal(text.trim(), temporal).value;\n }\n case \"point\":\n return parsePointText(text);\n case \"json\":\n return parseJsonText(text);\n default: {\n const name: string = kind;\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `unknown value kind ${name}`, { kind: name });\n }\n }\n}\n\n/**\n * Parse a boolean text the way the formats write it: `true` / `false` in any case, or `1` / `0`.\n * @param text - the value text\n * @returns the boolean, or null when the text is neither\n * @category Plugin helpers\n */\nexport function parseBooleanText(text: string): boolean | null {\n switch (text.trim().toLowerCase()) {\n case \"true\":\n case \"1\":\n return true;\n case \"false\":\n case \"0\":\n return false;\n default:\n return null;\n }\n}\n\n/**\n * Parse a float / double text: a decimal or exponent literal, or the XSD / JSON spellings of the\n * non-finite values (`INF`, `-INF`, `Infinity`, `-Infinity`, `NaN`).\n * @param text - the value text\n * @param kind - float or double, for the error\n * @returns the number\n * @category Plugin helpers\n */\nexport function parseDecimalText(text: string, kind: ValueKind = \"double\"): number {\n const trimmed = text.trim();\n if (DECIMAL_TEXT.test(trimmed)) {\n return Number(trimmed);\n }\n switch (trimmed) {\n case \"INF\":\n case \"+INF\":\n case \"Infinity\":\n case \"+Infinity\":\n return Infinity;\n case \"-INF\":\n case \"-Infinity\":\n return -Infinity;\n case \"NaN\":\n return Number.NaN;\n default:\n throw typeError(text, kind);\n }\n}\n\n/**\n * Whether a number text is finite as written but too large for a double (`1e400`, a 400-digit\n * integer), so it parses as Infinity: a value the importer should report, unlike `INF`.\n * @param text - the value text\n * @returns true when the text overflows\n * @category Plugin helpers\n */\nexport function overflowsToInfinity(text: string): boolean {\n const trimmed = text.trim();\n return /^[+-]?[0-9.]/.test(trimmed) && Number(trimmed) === (trimmed.startsWith(\"-\") ? -Infinity : Infinity);\n}\n\n/**\n * Parse a Neo4j point literal `{x:1.0, y:2.0, crs:'cartesian'}` (also `latitude` / `longitude` /\n * `height` / `z` / `srid`) into a JSON object; a JSON object text is accepted as well.\n * @param text - the value text\n * @returns an object with numeric coordinates and string crs\n * @category Plugin helpers\n */\nexport function parsePointText(text: string): Readonly<Record<string, number | string>> {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") || !trimmed.endsWith(\"}\")) {\n throw typeError(text, \"point\");\n }\n const out: Record<string, number | string> = {};\n const inner = trimmed.slice(1, -1).trim();\n if (inner.length === 0) {\n return out;\n }\n for (const part of inner.split(\",\")) {\n const colon = part.indexOf(\":\");\n if (colon < 0) {\n throw typeError(text, \"point\");\n }\n const key = part\n .slice(0, colon)\n .trim()\n .replaceAll(/(?:^[\"'])|(?:[\"']$)/g, \"\");\n const raw = part.slice(colon + 1).trim();\n if (key.length === 0) {\n throw typeError(text, \"point\");\n }\n if (DECIMAL_TEXT.test(raw)) {\n out[key] = Number(raw);\n } else if ((raw.startsWith(\"'\") && raw.endsWith(\"'\")) || (raw.startsWith('\"') && raw.endsWith('\"'))) {\n out[key] = raw.slice(1, -1);\n } else {\n throw typeError(text, \"point\");\n }\n }\n return out;\n}\n\n/**\n * Parse JSON text into a value.\n * @param text - the JSON text\n * @returns the parsed value\n */\nfunction parseJsonText(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n throw typeError(text, \"json\");\n }\n}\n\n/**\n * The E_COLUMN_TYPE error of a text that is not of a kind.\n * @param text - the text\n * @param kind - the kind\n * @returns the error\n */\nfunction typeError(text: string, kind: ValueKind): GraphFormatError {\n const shown = text.length > 40 ? `${text.slice(0, 40)}...` : text;\n return new GraphFormatError(\"E_COLUMN_TYPE\", `\"${shown}\" is not ${withArticle(kind)}`, { value: text, kind });\n}\n","/**\n * List value syntaxes of the text formats (design section 5.1, research note 07 section 2.1):\n * GEXF 1.3 bracket lists `[1, 2, 3]` / `[foo, 'bar baz']`, GEXF 1.2 `liststring` values separated\n * by `|`, `,` or `;` (\"an unsafe type\"), Gephi and Neo4j `;`-separated arrays. The importer splits\n * the text into item texts with splitListText() and parses each item by the declared item type.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\n/**\n * The list syntaxes an importer or exporter names.\n * @category Plugin helpers\n */\nexport type ListSyntax = \"gexf\" | \"brackets\" | \"pipe\" | \"comma\" | \"semicolon\";\n\n/**\n * Split a list value into its item texts.\n *\n * - \"gexf\": a bracketed text `[a, b]` is parsed with single or double quotes around items that\n * contain commas; otherwise the 1.2 rule applies: the text is split on `|` when it contains one,\n * else on `,` when it contains one, else on `;` when it contains one, else it is one item.\n * - \"brackets\": bracketed only; an unbracketed text is one item.\n * - \"pipe\" / \"comma\" / \"semicolon\": split on that separator.\n *\n * Items are trimmed; an empty text (or `[]`) is an empty list; an empty item between separators\n * is kept as an empty string.\n * @param text - the value text\n * @param syntax - the syntax\n * @returns the item texts\n * @category Plugin helpers\n */\nexport function splitListText(text: string, syntax: ListSyntax): string[] {\n const trimmed = text.trim();\n if (trimmed.length === 0) {\n return [];\n }\n switch (syntax) {\n case \"gexf\": {\n if (isBracketed(trimmed)) {\n return splitBracketed(trimmed);\n }\n const separator = firstSeparator(trimmed, [\"|\", \",\", \";\"]);\n return separator === null ? [trimmed] : splitOn(trimmed, separator);\n }\n case \"brackets\":\n return isBracketed(trimmed) ? splitBracketed(trimmed) : [trimmed];\n case \"pipe\":\n return splitOn(trimmed, \"|\");\n case \"comma\":\n return splitOn(trimmed, \",\");\n case \"semicolon\":\n return splitOn(trimmed, \";\");\n default: {\n const name: string = syntax;\n throw new GraphFormatError(\"E_UNSUPPORTED\", `unknown list syntax ${name}`, {\n option: \"listSyntax\",\n found: name,\n });\n }\n }\n}\n\n/**\n * Join item texts into a list value of one syntax, the inverse of splitListText() for the exporters.\n * Bracket syntaxes quote an item that contains a comma, a bracket or a quote (with double quotes,\n * a double quote inside doubled); separator syntaxes cannot escape and leave items as they are.\n * @param items - the item texts\n * @param syntax - the syntax; \"gexf\" writes the 1.3 bracket form\n * @returns the list text\n * @category Plugin helpers\n */\nexport function joinListText(items: readonly string[], syntax: ListSyntax): string {\n switch (syntax) {\n case \"gexf\":\n case \"brackets\":\n return `[${items.map(quoteBracketItem).join(\", \")}]`;\n case \"pipe\":\n return items.join(\"|\");\n case \"comma\":\n return items.join(\",\");\n case \"semicolon\":\n return items.join(\";\");\n default: {\n const name: string = syntax;\n throw new GraphFormatError(\"E_UNSUPPORTED\", `unknown list syntax ${name}`, {\n option: \"listSyntax\",\n found: name,\n });\n }\n }\n}\n\n/**\n * Whether a trimmed text is `[...]`.\n * @param text - the trimmed text\n * @returns true when bracketed\n */\nfunction isBracketed(text: string): boolean {\n return text.length >= 2 && text.startsWith(\"[\") && text.endsWith(\"]\");\n}\n\n/**\n * The first of several separators that occurs in a text.\n * @param text - the text\n * @param candidates - separators in priority order\n * @returns the first candidate found, or null\n */\nfunction firstSeparator(text: string, candidates: readonly string[]): string | null {\n for (const candidate of candidates) {\n if (text.includes(candidate)) {\n return candidate;\n }\n }\n return null;\n}\n\n/**\n * Split on a separator and trim every item.\n * @param text - the text\n * @param separator - the separator\n * @returns the trimmed items\n */\nfunction splitOn(text: string, separator: string): string[] {\n return text.split(separator).map((item) => item.trim());\n}\n\n/**\n * Parse the inside of a bracketed list: comma-separated items, each optionally wrapped in single\n * or double quotes (a doubled quote inside a quoted item is one quote).\n * @param text - the bracketed text\n * @returns the items\n */\nfunction splitBracketed(text: string): string[] {\n const inner = text.slice(1, -1);\n const items: string[] = [];\n const n = inner.length;\n if (inner.trim().length === 0) {\n return items;\n }\n let i = 0;\n for (;;) {\n while (i < n && isSpace(inner.charCodeAt(i))) {\n i++;\n }\n let item = \"\";\n if (i < n && (inner[i] === '\"' || inner[i] === \"'\")) {\n const quote = inner[i];\n i++;\n while (i < n) {\n if (inner[i] === quote) {\n if (inner[i + 1] === quote) {\n item += quote;\n i += 2;\n continue;\n }\n i++;\n break;\n }\n item += inner[i];\n i++;\n }\n }\n // unquoted text, or text after a closing quote that is not a separator, runs to the next comma\n let end = inner.indexOf(\",\", i);\n if (end < 0) {\n end = n;\n }\n item += inner.slice(i, end).trim();\n items.push(item);\n i = end;\n if (i >= n) {\n return items;\n }\n i++;\n if (inner.slice(i).trim().length === 0) {\n items.push(\"\");\n return items;\n }\n }\n}\n\n/**\n * Whether a char code is ASCII whitespace.\n * @param c - the char code\n * @returns true for space, tab, CR, LF\n */\nfunction isSpace(c: number): boolean {\n return c === 32 || c === 9 || c === 13 || c === 10;\n}\n\n/**\n * Quote one item for a bracket list when it needs it.\n * @param item - the item text\n * @returns the item, quoted when it contains a comma, a bracket, a quote or surrounding whitespace\n */\nfunction quoteBracketItem(item: string): string {\n if (item.length === 0 || /[,[\\]\"']/.test(item) || item !== item.trim()) {\n return `\"${item.replaceAll('\"', '\"\"')}\"`;\n }\n return item;\n}\n","/**\n * Attribute declaration shared by the typed importers (design sections 5.1, 5.5 and 5.6): turn a\n * GEXF `<attribute>`, a GraphML `<key>` or a Neo4j header column into a ColumnDecl with the right\n * dtype, default, options and origin metadata; parse values (scalars and lists) by the declaration;\n * derive the deterministic `<name>#<origin.id>` name when a declared name collides; and the dict\n * heuristic of design section 5.4.\n *\n * Naming rule (design section 5.6): a column's name is the GEXF `title` / GraphML `attr.name`,\n * falling back to the attribute `id` when the title is absent; `origin.id` always keeps the source\n * id and `origin.title` the title when it differs from the resolved name.\n */\n\nimport {\n type ColumnDecl,\n type ColumnHandle,\n type ColumnRole,\n GraphFormatError,\n type GraphSink,\n INVALID_INDEX,\n} from \"@graphty/graph-format\";\n\nimport { type IssueCategory } from \"../types.js\";\nimport {\n BAD_DEFAULT_CODE,\n BAD_OPTIONS_CODE,\n COLUMN_RENAMED_CODE,\n ROLE_TAKEN_CODE,\n UNKNOWN_ATTR_TYPE_CODE,\n} from \"./codes.js\";\nimport {\n type DeclaredTypeSpec,\n type DeclaringFormat,\n mapDeclaredType,\n parseScalarText,\n stringSpec,\n} from \"./declared-types.js\";\nimport { type ListSyntax, splitListText } from \"./lists.js\";\nimport { type ImportReportBuilder, type IssueLocation } from \"./report.js\";\nimport { parseTemporal, type TemporalValue, timeTextCompanion } from \"./temporal.js\";\n\n/**\n * What an importer knows about a declared attribute.\n * @public\n * @category Plugin helpers\n */\nexport interface AttributeDeclarationInput {\n /** The declaring format. */\n readonly format: DeclaringFormat;\n /** The attribute id (GEXF `id`, GraphML key `id`, Neo4j header name); null when the format has none. */\n readonly id: string | null;\n /** The GEXF `title` / GraphML `attr.name`; null when absent (yFiles keys omit it). */\n readonly title: string | null;\n /** The declared type text; null for an untyped declaration (stored as string). */\n readonly type: string | null;\n /** The declaring namespace (\"viz\", \"yfiles\", \"neo4j\"), or null. */\n readonly namespace?: string | null | undefined;\n /** The `<default>` text, or null. */\n readonly defaultText?: string | null | undefined;\n /** The GEXF `<options>` text, or null. */\n readonly optionsText?: string | null | undefined;\n /** The list syntax of defaults, options and values; \"gexf\" by default. */\n readonly listSyntax?: ListSyntax | undefined;\n /** A role to give the column, or null. */\n readonly role?: ColumnRole | null | undefined;\n /** Whether the attribute is dynamic (GEXF mode=\"dynamic\"). */\n readonly dynamic?: boolean | undefined;\n /** The importer's `long` option. */\n readonly long: \"f64\" | \"string\";\n /** Whether a column name is already taken in the target table; drives the `#id` rename. */\n readonly taken?: ((name: string) => boolean) | undefined;\n}\n\n/**\n * A non-fatal problem with a declaration, for the importer to record as a warning.\n * @public\n * @category Plugin helpers\n */\nexport interface DeclarationIssue {\n /** The issue category. */\n readonly category: IssueCategory;\n /** The stable code. */\n readonly code: string;\n /** A plain-ASCII message. */\n readonly message: string;\n}\n\n/**\n * A declared attribute: the ColumnDecl to push, the spec to parse values with, and what to report.\n * @public\n * @category Plugin helpers\n */\nexport interface DeclaredAttribute {\n /** The declaration for declareNodeColumn / declareEdgeColumn. */\n readonly decl: ColumnDecl;\n /** How values are parsed. */\n readonly spec: DeclaredTypeSpec;\n /** The list syntax values are split with. */\n readonly listSyntax: ListSyntax;\n /** The companion text column of a temporal attribute (design section 5.1), or null. */\n readonly companion: ColumnDecl | null;\n /** Whether the name received the `#id` suffix. */\n readonly renamed: boolean;\n /** Problems the importer records as warnings (unknown type, unparsable default or options). */\n readonly issues: readonly DeclarationIssue[];\n}\n\n/**\n * The declared type is not one the format defines; the column is kept as string.\n * @category Issue and loss codes\n */\nexport const UNKNOWN_TYPE_CODE = UNKNOWN_ATTR_TYPE_CODE;\n/**\n * The column was renamed `<name>#<id>` because the name was taken.\n * @category Issue and loss codes\n */\nexport const RENAMED_CODE = COLUMN_RENAMED_CODE;\n\nexport { BAD_DEFAULT_CODE, BAD_OPTIONS_CODE, PRECISION_CODE, ROLE_TAKEN_CODE } from \"./codes.js\";\n\n/**\n * Rows sampled before the dict heuristic decides (design section 5.4).\n * @category Plugin helpers\n */\nexport const DICT_SAMPLE_ROWS = 1024;\n\n/**\n * Build the column declaration of a declared attribute.\n * @param input - what the file declares\n * @returns the declaration, the value spec and any warnings to record\n * @category Plugin helpers\n */\nexport function declareAttribute(input: AttributeDeclarationInput): DeclaredAttribute {\n const issues: DeclarationIssue[] = [];\n const listSyntax = input.listSyntax ?? \"gexf\";\n let spec: DeclaredTypeSpec;\n if (input.type === null) {\n spec = stringSpec(null);\n } else {\n const mapped = mapDeclaredType(input.format, input.type, input.long);\n if (mapped === null) {\n spec = stringSpec(input.type);\n issues.push({\n category: \"unsupported\",\n code: UNKNOWN_TYPE_CODE,\n message: `${input.format} type \"${input.type}\" is not supported; values kept as text`,\n });\n } else {\n spec = mapped;\n }\n }\n const { name, renamed } = resolveName(input);\n if (renamed) {\n issues.push({\n category: \"coercion\",\n code: RENAMED_CODE,\n message: `attribute \"${input.title ?? input.id ?? \"\"}\" renamed to \"${name}\": the name was taken`,\n });\n }\n const decl: ColumnDecl = {\n name,\n dtype: spec.dtype,\n nullable: true,\n origin: {\n format: input.format,\n id: input.id,\n title: input.title !== null && input.title !== name ? input.title : null,\n type: input.type,\n namespace: input.namespace ?? null,\n },\n };\n if (spec.list) {\n decl.itemDtype = spec.itemDtype ?? \"string\";\n }\n if (input.role !== undefined && input.role !== null) {\n decl.role = input.role;\n }\n if (input.dynamic === true) {\n decl.dynamic = true;\n }\n const optionsText = input.optionsText ?? null;\n if (optionsText !== null) {\n try {\n const options = splitListText(optionsText, listSyntax).map((item) => parseItem(item, spec));\n decl.options = options;\n if (spec.dtype === \"string\" && !spec.list && options.every((o) => typeof o === \"string\")) {\n decl.dtype = \"dict\";\n spec = { ...spec, dtype: \"dict\" };\n }\n } catch (err) {\n issues.push({\n category: \"validation-error\",\n code: BAD_OPTIONS_CODE,\n message: `options \"${optionsText}\" of attribute \"${name}\" do not parse as ${spec.declared}: ${messageOf(err)}`,\n });\n }\n }\n const defaultText = input.defaultText ?? null;\n if (defaultText !== null) {\n try {\n decl.default = parseDeclaredValue(defaultText, spec, listSyntax);\n } catch (err) {\n issues.push({\n category: \"validation-error\",\n code: BAD_DEFAULT_CODE,\n message: `default \"${defaultText}\" of attribute \"${name}\" does not parse as ${spec.declared}: ${messageOf(err)}`,\n });\n }\n }\n const companion = spec.temporal !== null && !spec.list ? timeTextCompanion(name) : null;\n return { decl, spec, listSyntax, companion, renamed, issues };\n}\n\n/**\n * Parse a value text by a declaration: a scalar, or a list split by the syntax with each item\n * parsed by the item kind.\n * @param text - the value text\n * @param spec - the declared type\n * @param listSyntax - the list syntax\n * @returns the value to push (a boolean, a number, a string, a JSON value or an array)\n * @category Plugin helpers\n */\nexport function parseDeclaredValue(text: string, spec: DeclaredTypeSpec, listSyntax: ListSyntax): unknown {\n if (spec.list) {\n return splitListText(text, listSyntax).map((item) => parseItem(item, spec));\n }\n return parseItem(text, spec);\n}\n\n/**\n * Parse a temporal value text by a declaration, keeping the source text when its canonical form\n * differs (for the companion column).\n * @param text - the value text\n * @param spec - a scalar temporal spec\n * @returns the value and the text to keep, or null text\n * @category Plugin helpers\n */\nexport function parseDeclaredTemporal(text: string, spec: DeclaredTypeSpec): TemporalValue {\n if (spec.temporal === null || spec.list) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", `\"${spec.declared}\" is not a scalar temporal type`, {\n declared: spec.declared,\n });\n }\n return parseTemporal(text.trim(), spec.temporal);\n}\n\nconst INTEGER_TEXT = /^[+-]?\\d+$/;\n\n/**\n * Whether a long value's text lost precision when parsed to f64 (design section 5.1: a value with\n * |v| > 2^53 is stored as the nearest f64 and recorded as a `precision` issue). Decided from the\n * text, since the parsed number alone cannot tell 2^53 from 2^53 + 1.\n * @param spec - the declared type\n * @param text - the value text (trimmed or not)\n * @returns true when the importer should record a `precision` issue\n * @category Plugin helpers\n */\nexport function losesPrecision(spec: DeclaredTypeSpec, text: string): boolean {\n if (!spec.precision) {\n return false;\n }\n const trimmed = text.trim();\n if (!INTEGER_TEXT.test(trimmed)) {\n return false;\n }\n const n = Number(trimmed);\n return !Number.isFinite(n) || BigInt(trimmed) !== BigInt(n);\n}\n\n/**\n * Declare a column on the sink for one domain.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the declaration\n * @returns the column handle\n * @category Plugin helpers\n */\nexport function declareOn(sink: GraphSink, domain: \"node\" | \"edge\", decl: ColumnDecl): ColumnHandle {\n return domain === \"node\" ? sink.declareNodeColumn(decl) : sink.declareEdgeColumn(decl);\n}\n\n/**\n * The outcome of declareResolved(): the handle and the declaration as it was applied.\n * @public\n * @category Plugin helpers\n */\nexport interface ResolvedDeclaration {\n /** The column handle. */\n readonly handle: ColumnHandle;\n /** The declaration as applied (its name and role may differ from the request). */\n readonly decl: ColumnDecl;\n /** Whether the column was renamed `<name>#<id>` (W_COLUMN_RENAMED recorded). */\n readonly renamed: boolean;\n /** Whether the role was dropped because the table already holds it (W_ROLE_TAKEN recorded). */\n readonly roleDropped: boolean;\n}\n\n/**\n * The column naming rule in one place, so every importer agrees: declare a column on\n * the sink and, when the sink already holds the name with another shape (an earlier import, a\n * caller's column), rename it `<name>#<origin.id>` (a counter when the source has no id) and\n * record W_COLUMN_RENAMED (coercion); when the table already holds the role, declare without the role and record W_ROLE_TAKEN (coercion). The\n * same shape under the same name returns the existing handle, as the sink does.\n * @param sink - the sink\n * @param domain - node or edge\n * @param decl - the wanted declaration\n * @param report - the report the coercion issues are recorded in\n * @param where - the line and element, when known\n * @returns the handle and the declaration as applied\n * @category Plugin helpers\n */\nexport function declareResolved(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n decl: ColumnDecl,\n report: ImportReportBuilder,\n where?: IssueLocation,\n): ResolvedDeclaration {\n let current = decl;\n let renamed = false;\n let roleDropped = false;\n for (;;) {\n try {\n return { handle: declareOn(sink, domain, current), decl: current, renamed, roleDropped };\n } catch (err) {\n if (!(err instanceof GraphFormatError)) {\n throw err;\n }\n if (err.code === \"E_COLUMN_EXISTS\" && !renamed) {\n const taken = takenIn(sink, domain);\n const id = current.origin?.id ?? null;\n const name = uniqueColumnName(current.name, id, taken);\n report.warning(\n \"coercion\",\n COLUMN_RENAMED_CODE,\n `${domain} column \"${current.name}\" renamed to \"${name}\": the sink already holds that name with another shape`,\n { ...where, element: where?.element ?? current.name },\n );\n current = { ...current, name };\n renamed = true;\n continue;\n }\n if (err.code === \"E_DUPLICATE_ROLE\" && current.role !== undefined && !roleDropped) {\n const holder = typeof err.details.holder === \"string\" ? err.details.holder : \"another column\";\n report.warning(\n \"coercion\",\n ROLE_TAKEN_CODE,\n `${domain} column \"${current.name}\" loses the ${current.role} role: \"${holder}\" already holds it`,\n { ...where, element: where?.element ?? current.name },\n );\n const { role: _role, ...withoutRole } = current;\n current = withoutRole;\n roleDropped = true;\n continue;\n }\n throw err;\n }\n }\n}\n\n/**\n * Declare the companion text column of a temporal column. A table holds at\n * most one column per role, so when another companion already carries `timeText` this one is\n * declared without the role (reported once per table); exporters find every companion through\n * `extra.for`.\n * @param sink - the sink\n * @param domain - node or edge\n * @param companion - the declaration from declareAttribute()\n * @param report - the report the role loss is recorded in; when omitted the loss is silent\n * @returns the column handle\n * @category Plugin helpers\n */\nexport function declareCompanion(\n sink: GraphSink,\n domain: \"node\" | \"edge\",\n companion: ColumnDecl,\n report?: ImportReportBuilder,\n): ColumnHandle {\n try {\n return declareOn(sink, domain, companion);\n } catch (err) {\n if (err instanceof GraphFormatError && err.code === \"E_DUPLICATE_ROLE\") {\n report?.warnOnce(\n \"coercion\",\n ROLE_TAKEN_CODE,\n `${domain} column \"${companion.name}\" is declared without the timeText role: another companion holds it (exporters use extra.for)`,\n { element: companion.name },\n );\n const { role: _role, ...withoutRole } = companion;\n return declareOn(sink, domain, withoutRole);\n }\n throw err;\n }\n}\n\n/**\n * Whether a column name is taken in a sink's table, the usual `taken` callback.\n * @param sink - the sink\n * @param domain - node or edge\n * @returns a predicate over column names\n * @category Plugin helpers\n */\nexport function takenIn(sink: GraphSink, domain: \"node\" | \"edge\"): (name: string) => boolean {\n return (name: string): boolean =>\n (domain === \"node\" ? sink.nodeColumn(name) : sink.edgeColumn(name)) !== INVALID_INDEX;\n}\n\n/**\n * The deterministic name of a declared attribute whose preferred name is taken (design section\n * 5.6): `<name>#<origin.id>`, and `<name>#<origin.id>#2`, `#3`... should that be taken as well.\n * @param name - the preferred name\n * @param id - the source id (the attribute id or key id); null falls back to a counter\n * @param taken - whether a name is already used in the table\n * @returns a free name\n * @category Plugin helpers\n */\nexport function uniqueColumnName(name: string, id: string | null, taken: (name: string) => boolean): string {\n if (!taken(name)) {\n return name;\n }\n const base = id === null ? name : `${name}#${id}`;\n if (id !== null && !taken(base)) {\n return base;\n }\n for (let n = 2; ; n++) {\n const candidate = `${base}#${n}`;\n if (!taken(candidate)) {\n return candidate;\n }\n }\n}\n\n/**\n * The dict heuristic of design section 5.4 for an untyped string column: observe values until the\n * sample is full; the column is a dict when the distinct count stays below half the rows, a string\n * otherwise. The importer buffers the sampled values and declares the column on the decision.\n * @category Plugin helpers\n */\nexport class DictHeuristic {\n private readonly distinct = new Set<string>();\n\n private rowsSeen = 0;\n\n private readonly sampleRows: number;\n\n /**\n * Create a heuristic.\n * @param sampleRows - rows observed before deciding; DICT_SAMPLE_ROWS by default\n */\n constructor(sampleRows: number = DICT_SAMPLE_ROWS) {\n this.sampleRows = sampleRows;\n }\n\n /**\n * Rows observed so far.\n * @returns the count\n */\n get rows(): number {\n return this.rowsSeen;\n }\n\n /**\n * Distinct values observed so far.\n * @returns the count\n */\n get distinctCount(): number {\n return this.distinct.size;\n }\n\n /**\n * Whether the sample is full.\n * @returns true once sampleRows values were observed\n */\n get decided(): boolean {\n return this.rowsSeen >= this.sampleRows;\n }\n\n /**\n * Observe one value.\n * @param value - the value text\n * @returns true when this observation completed the sample\n */\n observe(value: string): boolean {\n this.rowsSeen++;\n if (this.distinct.size < this.rowsSeen) {\n this.distinct.add(value);\n }\n return this.rowsSeen === this.sampleRows;\n }\n\n /**\n * The dtype the sample suggests: dict when the distinct count is below rows / 2 (and at least\n * one row was seen), string otherwise. Valid before the sample is full (end of input).\n * @returns \"dict\" or \"string\"\n */\n decide(): \"dict\" | \"string\" {\n return this.rowsSeen > 0 && this.distinct.size * 2 < this.rowsSeen ? \"dict\" : \"string\";\n }\n}\n\n/**\n * The resolved name of a declaration and whether it was renamed.\n * @param input - the declaration input\n * @returns the name and the rename flag; E_COLUMN_TYPE when neither a title nor an id exists\n */\nfunction resolveName(input: AttributeDeclarationInput): { name: string; renamed: boolean } {\n const preferred = input.title !== null && input.title.length > 0 ? input.title : input.id;\n if (preferred === null || preferred.length === 0) {\n throw new GraphFormatError(\"E_COLUMN_TYPE\", \"an attribute declaration needs a title or an id\", {\n field: \"name\",\n format: input.format,\n });\n }\n if (input.taken === undefined) {\n return { name: preferred, renamed: false };\n }\n const name = uniqueColumnName(preferred, input.id, input.taken);\n return { name, renamed: name !== preferred };\n}\n\n/**\n * Parse one scalar value or list item by a spec.\n * @param text - the item text\n * @param spec - the declared type\n * @returns the parsed value\n */\nfunction parseItem(text: string, spec: DeclaredTypeSpec): unknown {\n return parseScalarText(text, spec.kind, spec.temporal);\n}\n\n/**\n * The message of a caught value.\n * @param err - the thrown value\n * @returns the message\n */\nfunction messageOf(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n","/**\n * Direction resolution through the sink (design sections 3.6 and 8.4, decision D-DIR): the\n * importer reads the file's header direction, calls `sink.setDirected()` once before the first\n * edge, then pushes every edge as it is read. Mixed files are handled per `onMixedDirection`:\n *\n * - \"expand\" (default): an undirected edge into a directed sink becomes two logical edges linked\n * by the `graphty.pair` column with `graphty.directed` = false on both halves; a directed edge\n * into an undirected sink triggers ONE `setDirected(true, { expand: true })`, after which the\n * builder has expanded everything pushed so far and the importer continues in directed mode.\n * - \"directed\" / \"undirected\": every edge is treated that way (the distinction is dropped and\n * reported once, with the count).\n * - \"error\": the first edge whose direction differs from the sink's aborts the import.\n *\n * A locked or non-empty sink wins over the file (design section 8.4 precedence): a refused\n * `setDirected` is recorded as a coercion issue and the importer continues with `sink.directed`,\n * expanding every edge that differs. A GEXF `mutual` edge expands like an undirected one with the\n * additional `graphty.mutual` role column set on its primary half.\n *\n * The reserved columns are declared with exactly the builder's own declarations so that a sink the\n * builder already expanded hands back the existing handles.\n */\n\nimport {\n type Column,\n type ColumnDecl,\n type ColumnHandle,\n GraphFormatError,\n type GraphSink,\n type GraphSnapshot,\n INVALID_INDEX,\n type NodeId,\n} from \"@graphty/graph-format\";\n\nimport { DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, MIXED_DIRECTION_CODE } from \"./codes.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\n\n/**\n * The direction of one source edge.\n * @public\n * @category Plugin helpers\n */\nexport type EdgeKind = \"directed\" | \"undirected\" | \"mutual\";\n\n/**\n * Where an edge or header was read, for issues.\n * @public\n * @category Plugin helpers\n */\nexport interface EdgeLocation {\n /** The 1-based line, when known. */\n readonly line?: number | null | undefined;\n /** The edge id or another element name, when known. */\n readonly element?: string | null | undefined;\n}\n\n/**\n * The name of the bool edge column that, in a graph with both directed and undirected edges, says\n * which edges were directed in the file (true) and which were undirected (false).\n * @category Plugin helpers\n */\nexport const DIRECTED_COLUMN = \"graphty.directed\";\n\n/**\n * The name of the edge column that links the two edges an undirected edge is stored as, in a graph\n * with both directed and undirected edges: each holds the index of the other.\n * @category Plugin helpers\n */\nexport const PAIR_COLUMN = \"graphty.pair\";\n\n/**\n * The name of the bool edge column that marks a GEXF `mutual` edge.\n * @category Plugin helpers\n */\nexport const MUTUAL_COLUMN = \"graphty.mutual\";\n\nexport { DIRECTION_FORCED_CODE, DIRECTION_REFUSED_CODE, MIXED_DIRECTION_CODE } from \"./codes.js\";\n\nconst DIRECTED_DECL: ColumnDecl = { name: DIRECTED_COLUMN, dtype: \"bool\", role: \"directed\" };\nconst PAIR_DECL: ColumnDecl = { name: PAIR_COLUMN, dtype: \"u32\", role: \"pair\", refersTo: \"edge\" };\nconst MUTUAL_DECL: ColumnDecl = { name: MUTUAL_COLUMN, dtype: \"bool\", role: \"mutual\" };\n\n/**\n * Adds edges to the sink and handles files whose edges have different directions, the way\n * `onMixedDirection` asks. Make one per import call, call `setHeader()` once with the direction the\n * file declares, then `addEdge()` for each edge. In a directed graph an undirected edge is stored as\n * two edges, one each way, linked so an exporter writes them back as one.\n * @category Writing a format\n */\nexport class DirectionResolver {\n private readonly sink: GraphSink;\n\n private readonly report: ImportReportBuilder;\n\n private readonly policy: \"expand\" | \"directed\" | \"undirected\" | \"error\";\n\n private directedHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private pairHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n private mutualHandle: ColumnHandle = INVALID_INDEX as ColumnHandle;\n\n /** The first edge index this resolver pushed, for the backfill of graphty.directed; -1 before the first. */\n private firstEdge = -1;\n\n /** The next edge index this resolver expects to receive (contiguity between compactions, I16). */\n private nextEdge = -1;\n\n private forcedCount = 0;\n\n private headerSet = false;\n\n private mirrorOfLast: number = INVALID_INDEX;\n\n /**\n * Create a resolver.\n * @param sink - the sink edges are pushed into\n * @param report - the report issues are recorded in\n * @param policy - the resolved onMixedDirection option\n */\n constructor(sink: GraphSink, report: ImportReportBuilder, policy: \"expand\" | \"directed\" | \"undirected\" | \"error\") {\n this.sink = sink;\n this.report = report;\n this.policy = policy;\n }\n\n /**\n * The sink's current direction.\n * @returns true when directed\n */\n get directed(): boolean {\n return this.sink.directed;\n }\n\n /**\n * Whether the graph holds undirected edges stored as two edges.\n * @returns true once the first such edge was added\n */\n get expanded(): boolean {\n return this.pairHandle !== INVALID_INDEX;\n }\n\n /**\n * Set the graph's direction before the first edge: the direction the file declares, or the\n * `defaultDirected` option for a file that declares none. Under `onMixedDirection` \"directed\" or\n * \"undirected\" that direction is used instead, with a warning when it differs. When the sink\n * cannot change direction (a caller's builder that already holds edges or was locked), the\n * sink's direction stands and a warning is recorded.\n * @param headerDirected - the direction the file declares (or the default)\n * @param where - the line of the header, when known\n */\n setHeader(headerDirected: boolean, where?: EdgeLocation): void {\n this.headerSet = true;\n let wanted = headerDirected;\n if (this.policy === \"directed\" || this.policy === \"undirected\") {\n wanted = this.policy === \"directed\";\n if (wanted !== headerDirected) {\n // once: this says it for every edge, so no edge repeats it\n this.report.warnOnce(\n \"coercion\",\n DIRECTION_FORCED_CODE,\n `the file declares ${direction(headerDirected)} edges; read as ${direction(wanted)} per onMixedDirection`,\n { line: where?.line ?? null },\n `${DIRECTION_FORCED_CODE}:${direction(headerDirected)}`,\n );\n }\n }\n const { sink } = this;\n if (sink.directed === wanted) {\n this.adoptExisting();\n return;\n }\n try {\n if (wanted && sink.edgeCount > 0 && !sink.directedLocked) {\n const before = sink.edgeCount;\n sink.setDirected(true, { expand: true });\n this.adoptExpansionColumns();\n this.report.counts.expandedMixed += before;\n } else {\n sink.setDirected(wanted);\n }\n } catch (err) {\n if (!(err instanceof GraphFormatError) || err.code !== \"E_DIRECTED\") {\n throw err;\n }\n this.report.warning(\n \"coercion\",\n DIRECTION_REFUSED_CODE,\n `the file is ${direction(wanted)} but the sink is ${direction(sink.directed)} (${reasonOf(err)}); read as ${direction(sink.directed)}`,\n { line: where?.line ?? null },\n );\n }\n this.adoptExisting();\n }\n\n /**\n * Pick up reserved columns a caller's sink already holds (an earlier expansion), so every edge\n * pushed from now on gets its graphty.directed value.\n */\n private adoptExisting(): void {\n if (this.pairHandle === INVALID_INDEX && this.sink.edgeColumn(PAIR_COLUMN) !== INVALID_INDEX) {\n this.adoptExpansionColumns();\n }\n }\n\n /**\n * The second edge of the edge the last addEdge() call added, when that edge was stored as two;\n * otherwise INVALID_INDEX (4294967295). An edge attribute or weight you set after addEdge()\n * belongs on both: set it on the index addEdge() returned and, when this is not INVALID_INDEX,\n * on this index too.\n * @returns the second edge's index, or INVALID_INDEX\n */\n get lastMirror(): number {\n return this.mirrorOfLast;\n }\n\n /**\n * Add one edge. An edge with the graph's direction is added as it is; one with the other\n * direction is stored as two edges, given the graph's direction, or refused, as\n * `onMixedDirection` says. When it is stored as two, the return value is the first and\n * `lastMirror` the second.\n * @param source - the source id\n * @param target - the target id\n * @param kind - the edge's direction in the file\n * @param weight - the weight, or undefined when the file gives none\n * @param where - the line and element, for issues\n * @returns the index of the edge added (the first one, when it was stored as two)\n */\n addEdge(source: NodeId, target: NodeId, kind: EdgeKind, weight?: number, where?: EdgeLocation): number {\n if (!this.headerSet) {\n throw new GraphFormatError(\"E_DIRECTED\", \"DirectionResolver.setHeader() must run before the first edge\", {\n reason: \"header not set\",\n });\n }\n const { sink } = this;\n this.mirrorOfLast = INVALID_INDEX;\n const edgeDirected = kind !== \"undirected\";\n if (edgeDirected === sink.directed && kind !== \"mutual\") {\n const e = sink.addEdge(source, target, weight);\n this.track(e);\n if (this.directedHandle !== INVALID_INDEX) {\n sink.setEdgeValue(this.directedHandle, e, edgeDirected);\n }\n return e;\n }\n switch (this.policy) {\n case \"directed\":\n case \"undirected\": {\n // the sink's direction is the policy's (or a locked sink's); the edge is pushed as the sink's kind\n this.forcedCount++;\n this.report.warnOnce(\n \"coercion\",\n DIRECTION_FORCED_CODE,\n `${kind} edges read as ${direction(sink.directed)} per onMixedDirection`,\n where,\n `${DIRECTION_FORCED_CODE}:${kind}`,\n );\n const e = sink.addEdge(source, target, weight);\n this.track(e);\n if (this.directedHandle !== INVALID_INDEX) {\n sink.setEdgeValue(this.directedHandle, e, sink.directed);\n }\n return e;\n }\n case \"error\":\n this.report.error(\n \"validation-error\",\n MIXED_DIRECTION_CODE,\n `${kind} edge in a ${direction(sink.directed)} graph (onMixedDirection: \"error\")`,\n where,\n );\n throw this.report.abort(\"mixed direction refused\", { code: MIXED_DIRECTION_CODE });\n case \"expand\":\n return this.expand(source, target, kind, weight, where);\n default: {\n const name: string = this.policy;\n throw new GraphFormatError(\"E_UNSUPPORTED\", `unknown onMixedDirection ${name}`, {\n option: \"onMixedDirection\",\n found: name,\n });\n }\n }\n }\n\n /**\n * How many edges were given a direction other than their own, under `onMixedDirection`\n * \"directed\" or \"undirected\".\n * @returns the count\n */\n get forced(): number {\n return this.forcedCount;\n }\n\n /**\n * Expand one edge that differs from the sink's direction.\n * @param source - the source id\n * @param target - the target id\n * @param kind - the edge's direction in the file\n * @param weight - the weight, or undefined\n * @param where - the line and element, for issues\n * @returns the primary edge index\n */\n private expand(\n source: NodeId,\n target: NodeId,\n kind: EdgeKind,\n weight: number | undefined,\n where: EdgeLocation | undefined,\n ): number {\n const { sink } = this;\n if (!sink.directed) {\n // a directed (or mutual) edge into an undirected sink: expand the sink once\n try {\n const before = sink.edgeCount;\n sink.setDirected(true, { expand: true });\n this.adoptExpansionColumns();\n this.report.counts.expandedMixed += before;\n } catch (err) {\n if (!(err instanceof GraphFormatError) || err.code !== \"E_DIRECTED\") {\n throw err;\n }\n this.report.error(\n \"coercion\",\n DIRECTION_REFUSED_CODE,\n `${direction(true)} edge in a locked ${direction(false)} sink cannot be expanded`,\n where,\n );\n throw this.report.abort(\"the sink refused the expansion to directed\", {\n code: DIRECTION_REFUSED_CODE,\n reason: err.details.reason,\n });\n }\n if (kind === \"directed\") {\n const e = sink.addEdge(source, target, weight);\n this.track(e);\n sink.setEdgeValue(this.directedHandle, e, true);\n return e;\n }\n }\n this.ensureExpansionColumns();\n const first = sink.addEdge(source, target, weight);\n this.track(first);\n const mutual = kind === \"mutual\";\n sink.setEdgeValue(this.directedHandle, first, mutual);\n this.report.counts.expandedMixed++;\n if (mutual) {\n this.ensureMutualColumn();\n sink.setEdgeValue(this.mutualHandle, first, true);\n }\n const u = sink.indexOf(source);\n const v = sink.indexOf(target);\n if (u === v) {\n // an undirected self-loop is one arc (I7); the builder does not mirror it either\n return first;\n }\n const mirror = sink.addEdge(target, source, weight); // NOSONAR(S2234): the mirror arc runs the other way on purpose\n this.track(mirror);\n this.mirrorOfLast = mirror;\n sink.setEdgeValue(this.directedHandle, mirror, mutual);\n sink.setEdgeValue(this.pairHandle, first, mirror);\n sink.setEdgeValue(this.pairHandle, mirror, first);\n return first;\n }\n\n /**\n * Note an edge index this resolver pushed, for the backfill of graphty.directed.\n * @param e - the edge index\n */\n private track(e: number): void {\n if (this.firstEdge < 0) {\n this.firstEdge = e;\n }\n this.nextEdge = e + 1;\n }\n\n /**\n * Pick up the reserved columns the builder wrote during its own expansion.\n */\n private adoptExpansionColumns(): void {\n this.directedHandle = this.sink.declareEdgeColumn(DIRECTED_DECL);\n this.pairHandle = this.sink.declareEdgeColumn(PAIR_DECL);\n }\n\n /**\n * Declare the reserved columns on first use in a directed sink and backfill graphty.directed\n * = true for every edge this resolver pushed so far (they were directed in the source).\n */\n private ensureExpansionColumns(): void {\n if (this.pairHandle !== INVALID_INDEX) {\n return;\n }\n this.directedHandle = this.sink.declareEdgeColumn(DIRECTED_DECL);\n this.pairHandle = this.sink.declareEdgeColumn(PAIR_DECL);\n if (this.firstEdge >= 0) {\n for (let e = this.firstEdge; e < this.nextEdge; e++) {\n this.sink.setEdgeValue(this.directedHandle, e, true);\n }\n }\n }\n\n /** Declare graphty.mutual on first use. */\n private ensureMutualColumn(): void {\n if (this.mutualHandle === INVALID_INDEX) {\n this.mutualHandle = this.sink.declareEdgeColumn(MUTUAL_DECL);\n }\n }\n}\n\n/**\n * The reason of a refused setDirected(), for messages.\n * @param err - the E_DIRECTED error\n * @returns details.reason when it is a string, \"refused\" otherwise\n */\nfunction reasonOf(err: GraphFormatError): string {\n const { reason } = err.details;\n return typeof reason === \"string\" ? reason : \"refused\";\n}\n\n/**\n * The word for a direction, for messages.\n * @param directed - the direction\n * @returns \"directed\" or \"undirected\"\n */\nfunction direction(directed: boolean): string {\n return directed ? \"directed\" : \"undirected\";\n}\n\n// ============================================================ the exporter side\n\n/**\n * What an exporter needs to know about edge direction, from `pairFolding(snapshot)`. In a graph\n * with both directed and undirected edges, each undirected edge is stored as two edges, one each\n * way. `folded(e)` tells you which of the two to skip, so the edge is written once, and\n * `sourceDirected(e)` whether an edge was directed. A GEXF `mutual` edge is also stored as two\n * edges; it is written as two directed edges unless you pass `foldMutual: true`.\n * @public\n * @category Plugin helpers\n */\nexport interface PairFolding {\n /** Whether the graph holds any edge stored as two edges. When false, every method answers as for a plain edge. */\n readonly expanded: boolean;\n /** How many GEXF mutual edges the graph holds, each counted once. */\n readonly mutualCount: number;\n /**\n * Whether to skip an edge: true for the second of the two edges an undirected edge (or, with\n * `foldMutual`, a mutual edge) is stored as. Write the first and skip this one.\n * @param e - the edge index\n * @returns true when the edge must be skipped\n */\n folded(e: number): boolean;\n /**\n * The other of the two edges an undirected or mutual edge is stored as.\n * @param e - the edge index\n * @returns the other edge's index, or INVALID_INDEX (4294967295) for an edge stored once\n */\n mateOf(e: number): number;\n /**\n * Whether an edge was directed in the file it was read from: false for an undirected edge of a\n * graph with both kinds. It is true for every edge of a graph without mixed direction, an\n * undirected graph included, so the direction to write for edge `e` is\n * `snapshot.directed && sourceDirected(e)`.\n * @param e - the edge index\n * @returns whether the edge is directed\n */\n sourceDirected(e: number): boolean;\n /**\n * Whether an edge is either of the two edges a GEXF `mutual` edge is stored as.\n * @param e - the edge index\n * @returns true for a mutual edge\n */\n isMutual(e: number): boolean;\n}\n\n/**\n * The per-format choice of pairFolding().\n * @category Plugin helpers\n */\nexport interface PairFoldingOptions {\n /**\n * Write a GEXF mutual edge once, for a format that can say an edge goes both ways. When false\n * (the default), `folded()` keeps both of its edges, so it is written as two directed edges.\n */\n readonly foldMutual?: boolean | undefined;\n}\n\n/**\n * Read how a snapshot stores edge direction, for an exporter: which edges to skip so each\n * undirected edge is written once, and which edges were directed.\n * @param snapshot - the graph to write\n * @param options - whether a GEXF mutual edge is written once\n * @returns the answers, per edge\n * @category Writing a format\n */\nexport function pairFolding(snapshot: GraphSnapshot, options: PairFoldingOptions = {}): PairFolding {\n const foldMutual = options.foldMutual ?? false;\n const directedColumn = roleColumn(snapshot.edges.byRole(\"directed\"), \"bool\");\n const pairColumn = roleColumn(snapshot.edges.byRole(\"pair\"), \"u32\");\n const mutualColumn = roleColumn(snapshot.edges.byRole(\"mutual\"), \"bool\");\n const { edgeCount } = snapshot;\n const mateOf = (e: number): number => {\n if (!pairColumn?.isSet(e)) {\n return INVALID_INDEX;\n }\n const mate = pairColumn.value(e);\n return typeof mate === \"number\" && mate !== INVALID_INDEX ? mate : INVALID_INDEX;\n };\n const sourceDirected = (e: number): boolean =>\n directedColumn === null || !directedColumn.isSet(e) || directedColumn.value(e) === true;\n const isMutual = (e: number): boolean => {\n if (mutualColumn === null) {\n return false;\n }\n if (mutualColumn.isSet(e) && mutualColumn.value(e) === true) {\n return true;\n }\n const mate = mateOf(e);\n return mate !== INVALID_INDEX && mutualColumn.isSet(mate) && mutualColumn.value(mate) === true;\n };\n const folded = (e: number): boolean => {\n const mate = mateOf(e);\n if (mate === INVALID_INDEX || mate >= e) {\n return false;\n }\n // an undirected pair always folds; a mutual pair (directed on both halves) folds on request\n return !sourceDirected(e) || (foldMutual && isMutual(e));\n };\n let mutualCount = 0;\n if (mutualColumn !== null) {\n for (let e = 0; e < edgeCount; e++) {\n if (mutualColumn.isSet(e) && mutualColumn.value(e) === true && mateOf(e) > e) {\n mutualCount++;\n }\n }\n }\n return { expanded: pairColumn !== null, mutualCount, folded, mateOf, sourceDirected, isMutual };\n}\n\n/**\n * A role column when it has the dtype the role requires, else null.\n * @param column - the column, or null\n * @param dtype - the required dtype\n * @returns the column or null\n */\nfunction roleColumn(column: Column | null, dtype: \"bool\" | \"u32\"): Column | null {\n return column !== null && column.dtype === dtype ? column : null;\n}\n","/**\n * Id coercion (design section 4.1): the importer-side rule that turns a text cell or a JSON value\n * into a NodeId before it reaches the sink. The core never coerces.\n *\n * - \"keep\": typed values pass through; anything that is not a string or a number is E_INVALID_ID.\n * - \"canonical\": text becomes a number iff it is canonical integer text (`/^-?(0|[1-9][0-9]*)$/`\n * and a safe integer, excluding \"-0\" which would collide with \"0\"); every other text stays a\n * string. Injective on text, so `String(id)` on export reproduces the cell exactly: \"01\", \"1.0\"\n * and \"+1\" stay strings, \"1\" becomes 1. Typed values pass through as under \"keep\".\n * - \"string\": `String(v)` for strings, numbers, booleans, bigints and null; anything else is\n * E_INVALID_ID.\n * - \"number\": `Number(text)` for text (empty or whitespace-only text, NaN and non-finite results\n * are E_INVALID_ID); numbers pass through; anything else is E_INVALID_ID. This rule can merge\n * distinct cells (\"01\" and \"1\"); IdCoercer counts such merges so the importer can report them as\n * coercion issues.\n */\n\nimport { GraphFormatError, type IdCoercion, type NodeId } from \"@graphty/graph-format\";\n\nconst CANONICAL_INTEGER = /^-?(0|[1-9]\\d*)$/;\n\nexport { ID_MERGED_CODE } from \"./codes.js\";\n\n/**\n * The \"canonical\" rule on one text cell.\n * @param text - the cell text, exactly as read\n * @returns the number for canonical safe-integer text (never for \"-0\"), the text itself otherwise\n * @category Plugin helpers\n */\nexport function canonicalId(text: string): NodeId {\n if (text !== \"-0\" && CANONICAL_INTEGER.test(text)) {\n const n = Number(text);\n if (Number.isSafeInteger(n)) {\n return n;\n }\n }\n return text;\n}\n\n/**\n * Whether a text cell is canonical integer text under the \"canonical\" rule (a number after import).\n * @param text - the cell text\n * @returns true when canonicalId(text) returns a number\n * @category Plugin helpers\n */\nexport function isCanonicalIntegerText(text: string): boolean {\n return typeof canonicalId(text) === \"number\";\n}\n\n/**\n * Coerce a text cell under a rule.\n * @param text - the cell text, exactly as read\n * @param mode - the coercion rule\n * @returns the id; E_INVALID_ID when the rule rejects the text\n * @category Plugin helpers\n */\nexport function coerceIdText(text: string, mode: IdCoercion): NodeId {\n switch (mode) {\n case \"keep\":\n case \"string\":\n return text;\n case \"canonical\":\n return canonicalId(text);\n case \"number\": {\n if (text.trim().length === 0) {\n throw invalidId(text, \"empty text is not a number\");\n }\n const n = Number(text);\n if (!Number.isFinite(n)) {\n throw invalidId(text, \"not a finite number\");\n }\n return n;\n }\n default: {\n const name: string = mode;\n throw new GraphFormatError(\"E_UNSUPPORTED\", `unknown id coercion ${name}`, { option: \"ids\", found: name });\n }\n }\n}\n\n/**\n * Coerce a typed value (a JSON scalar, a record field) under a rule.\n * @param value - the value\n * @param mode - the coercion rule\n * @returns the id; E_INVALID_ID when the rule rejects the value\n * @category Plugin helpers\n */\nexport function coerceId(value: unknown, mode: IdCoercion): NodeId {\n if (typeof value === \"string\") {\n return coerceIdText(value, mode);\n }\n if (typeof value === \"number\") {\n if (!Number.isFinite(value)) {\n throw invalidId(value, \"not a finite number\");\n }\n return mode === \"string\" ? String(value) : value;\n }\n if (mode === \"string\" && (typeof value === \"boolean\" || typeof value === \"bigint\" || value === null)) {\n return String(value);\n }\n throw invalidId(value, `a ${value === null ? \"null\" : typeof value} is not an id under ids: \"${mode}\"`);\n}\n\n/**\n * The E_INVALID_ID error of a rejected value.\n * @param value - the rejected value\n * @param reason - why\n * @returns the error\n */\nfunction invalidId(value: unknown, reason: string): GraphFormatError {\n const shown = typeof value === \"string\" || typeof value === \"number\" ? value : typeof value;\n return new GraphFormatError(\"E_INVALID_ID\", `invalid node id ${JSON.stringify(shown)}: ${reason}`, {\n reason,\n value: typeof value === \"bigint\" ? value.toString() : value,\n });\n}\n\n/**\n * A stateful coercer for one import call: applies the rule and, under \"number\", detects merges\n * (two distinct texts mapping to one number) so the importer can report them as coercion issues.\n * @category Writing a format\n */\nexport class IdCoercer {\n /** The rule. */\n readonly mode: IdCoercion;\n\n /** Texts merged into an id another text already produced. */\n mergeCount = 0;\n\n /** The text that first produced each numeric id, kept only under \"number\". */\n private readonly firstText: Map<number, string> | null;\n\n /** The merge detected by the most recent text() call, or null. */\n lastMerge: {\n /** The number both texts became. */\n readonly id: number;\n /** The text just read. */\n readonly text: string;\n /** The first text that became the same number. */\n readonly previousText: string;\n } | null = null;\n\n /**\n * Create a coercer.\n * @param mode - the rule\n */\n constructor(mode: IdCoercion) {\n this.mode = mode;\n this.firstText = mode === \"number\" ? new Map() : null;\n }\n\n /**\n * Coerce a text cell, recording a merge under \"number\" when a different text already produced\n * the same number.\n * @param text - the cell text\n * @returns the id\n */\n text(text: string): NodeId {\n const id = coerceIdText(text, this.mode);\n this.lastMerge = null;\n if (this.firstText !== null && typeof id === \"number\") {\n const previous = this.firstText.get(id);\n if (previous === undefined) {\n this.firstText.set(id, text);\n } else if (previous !== text) {\n this.mergeCount++;\n this.lastMerge = { id, text, previousText: previous };\n }\n }\n return id;\n }\n\n /**\n * Coerce a typed value.\n * @param value - the value\n * @returns the id\n */\n value(value: unknown): NodeId {\n if (typeof value === \"string\") {\n return this.text(value);\n }\n this.lastMerge = null;\n return coerceId(value, this.mode);\n }\n}\n","/**\n * Input handling shared by every importer (design section 8.4): the ImportInput union (whole\n * text, whole bytes, a byte stream, an async iterable of text or byte chunks) read as a sequence of\n * text chunks, as lines, or as one string, with BOM handling, cancellation through an AbortSignal\n * and byte progress.\n *\n * Bytes are decoded ONCE, here, for every importer. The encoding is, in order of precedence: a\n * byte order mark (UTF-8, UTF-16LE, UTF-16BE; a UTF-32 one is E_INVALID_ENCODING); the caller's\n * `encoding` option; the encoding the file declares (the importer's `declaredEncoding` reader: the\n * XML prolog for GEXF and GraphML, the `charset` attribute for DOT); else UTF-8. Whenever two of\n * them disagree the one that applies is named in a W_ENCODING_CONFLICT warning. BOM-less UTF-16\n * (every other byte 0) is E_INVALID_ENCODING asking for the option, and a known non-graph file (an\n * HTML page, a PDF, gzip or zip data, a PNG) is E_FOREIGN_FORMAT. Decoding is strict (`fatal: true`): an invalid sequence\n * is never a silent U+FFFD that could alias two ids. Only in the last case, when bytes that are not\n * valid UTF-8 appear while everything before them was ASCII, is the rest of the input read as\n * windows-1252 (the superset of ISO 8859-1 that Excel, Pajek and older tools write), with the\n * warning W_ENCODING_FALLBACK; invalid UTF-8 after valid non-ASCII UTF-8, in data holding control\n * bytes (binary data), or cut at the end of the input is E_INVALID_UTF8.\n *\n * The decoded text, whatever the input shape, is then checked once: leading U+FEFFs are stripped, a\n * trailing Ctrl-Z (the DOS end-of-file marker) is dropped, the first control character or stray\n * U+FEFF is reported W_CONTROL_CHARACTER, and a text that is empty or only whitespace is the fatal\n * E_EMPTY_INPUT.\n */\n\nimport { GraphFormatError } from \"@graphty/graph-format\";\n\nimport { type ImportInput } from \"../types.js\";\nimport {\n CONTROL_CHARACTER_CODE,\n EMPTY_INPUT_CODE,\n ENCODING_CONFLICT_CODE,\n ENCODING_FALLBACK_CODE,\n FOREIGN_FORMAT_CODE,\n INVALID_ENCODING_CODE,\n INVALID_UTF8_CODE,\n OPTION_IGNORED_CODE,\n TOO_LARGE_CODE,\n UNKNOWN_ENCODING_CODE,\n} from \"./codes.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\n\nexport { INVALID_UTF8_CODE } from \"./codes.js\";\n\n/**\n * Cancellation and progress hooks of the reader; the resolved importer options satisfy this shape.\n * @public\n * @category Plugin helpers\n */\nexport interface ReadOptions {\n /** The cancellation signal, or null / undefined for none. */\n readonly signal?: AbortSignal | null | undefined;\n /** The progress callback, or null / undefined for none. */\n readonly onProgress?: ((bytesDone: number, bytesTotal?: number) => void) | null | undefined;\n /** The caller's `encoding` option (a label TextDecoder knows), or null / undefined for detection. */\n readonly encoding?: string | null | undefined;\n /**\n * The format's reader of a declared encoding: given the head of the input decoded as\n * windows-1252 (ASCII-compatible), the encoding label it declares, or null.\n */\n readonly declaredEncoding?: ((head: string) => string | null) | undefined;\n /** How many leading bytes the declaration reader sees (default 1024). */\n readonly declarationBytes?: number | undefined;\n /**\n * Whether BOM-less UTF-16 that the NUL pattern of its head shows is read in that encoding (JSON:\n * RFC 8259 section 8.1, RFC 4627 section 3) instead of failing with E_INVALID_ENCODING.\n */\n readonly bomlessUtf16?: boolean | undefined;\n /** Whether an input with no text but whitespace is valid (else E_EMPTY_INPUT). */\n readonly allowEmpty?: boolean | undefined;\n /**\n * Whether the format is XML: an (X)HTML document is then left to the format's own root-element\n * check (an XHTML page may embed the format's elements) instead of being E_FOREIGN_FORMAT.\n */\n readonly xml?: boolean | undefined;\n /**\n * Whether a NUL byte in undeclared input fails the import as binary data (or BOM-less UTF-16)\n * even where the bytes are valid UTF-8: for formats whose grammar never reports a NUL itself\n * (CSV). False by default, so a format's own syntax error names it.\n */\n readonly nulIsBinary?: boolean | undefined;\n}\n\n/** How many leading bytes the declaration check sees (an XML prolog, a DOT `charset` near the top). */\nconst HEAD_BYTES = 1024;\n\n/** How many leading bytes the BOM check needs (a UTF-32 mark is four). */\nconst BOM_BYTES = 4;\n\n/**\n * The canonical name of an encoding label, or null when the platform's TextDecoder does not know it.\n * @param label - a WHATWG encoding label (\"latin1\", \"UTF-16LE\", \"cp1252\", ...)\n * @returns the canonical name (\"windows-1252\", \"utf-16le\", ...) or null\n * @category Plugin helpers\n */\nexport function canonicalEncoding(label: string): string | null {\n try {\n return new TextDecoder(label.trim()).encoding;\n } catch {\n return null;\n }\n}\n\n/**\n * The encoding a byte order mark announces. The UTF-32 marks are checked first: FF FE 00 00 is the\n * UTF-32LE mark, not a UTF-16LE one.\n * @param head - the first bytes\n * @returns \"utf-8\", \"utf-16le\", \"utf-16be\", \"utf-32le\", \"utf-32be\" or null\n */\nfunction bomEncoding(head: Uint8Array): string | null {\n if (startsWithBytes(head, [0xff, 0xfe, 0x00, 0x00])) {\n return \"utf-32le\";\n }\n if (startsWithBytes(head, [0x00, 0x00, 0xfe, 0xff])) {\n return \"utf-32be\";\n }\n if (startsWithBytes(head, [0xef, 0xbb, 0xbf])) {\n return \"utf-8\";\n }\n if (startsWithBytes(head, [0xff, 0xfe])) {\n return \"utf-16le\";\n }\n if (startsWithBytes(head, [0xfe, 0xff])) {\n return \"utf-16be\";\n }\n return null;\n}\n\n/**\n * Whether bytes start with a signature.\n * @param bytes - the bytes\n * @param signature - the leading bytes to look for\n * @returns true when every signature byte matches\n */\nfunction startsWithBytes(bytes: Uint8Array, signature: readonly number[]): boolean {\n return bytes.byteLength >= signature.length && signature.every((b, i) => bytes[i] === b);\n}\n\n/**\n * The UTF-16 byte order of a head without a BOM whose every other byte is 0 (ASCII text in UTF-16),\n * or null. At least 90% of the first 512 code units must follow the pattern.\n * @param head - the first bytes\n * @returns \"utf-16le\", \"utf-16be\" or null\n */\nfunction bomlessUtf16(head: Uint8Array): string | null {\n const pairs = Math.min(head.byteLength >> 1, 512);\n if (pairs < 2) {\n return null;\n }\n let le = 0;\n let be = 0;\n for (let i = 0; i < pairs; i++) {\n const even = head[2 * i];\n const odd = head[2 * i + 1];\n if (even !== 0 && odd === 0) {\n le++;\n } else if (even === 0 && odd !== 0) {\n be++;\n }\n }\n if (le >= pairs * 0.9) {\n return \"utf-16le\";\n }\n return be >= pairs * 0.9 ? \"utf-16be\" : null;\n}\n\n/**\n * The UTF-16 byte order of BOM-less UTF-16 input that starts with an XML declaration (XML 1.0\n * Appendix F: `<?` as 3C 00 3F 00 or 00 3C 00 3F).\n * @param head - the first bytes\n * @returns \"utf-16le\", \"utf-16be\" or null\n */\nfunction xmlDeclarationUtf16(head: Uint8Array): string | null {\n if (startsWithBytes(head, [0x3c, 0x00, 0x3f, 0x00])) {\n return \"utf-16le\";\n }\n return startsWithBytes(head, [0x00, 0x3c, 0x00, 0x3f]) ? \"utf-16be\" : null;\n}\n\n/**\n * What a head is when it is a known file type that is no graph format: compressed or archived data,\n * an image, a PDF, an HTML page. Graph formats are text (or, for a Cytoscape session, a zip the\n * registry hands to its own importer), so such an input is never read as text.\n * @param head - the first bytes or characters of the input\n * @returns a description such as \"gzip-compressed data (decompress it first)\", or null\n * @category Plugin helpers\n */\nexport function foreignKind(head: Uint8Array | string): string | null {\n if (typeof head !== \"string\") {\n if (startsWithBytes(head, [0x1f, 0x8b])) {\n return \"gzip-compressed data (decompress it first)\";\n }\n if (startsWithBytes(head, [0x50, 0x4b, 0x03, 0x04]) || startsWithBytes(head, [0x50, 0x4b, 0x05, 0x06])) {\n return \"a zip archive (a Cytoscape session (.cys) is read by the cys importer; extract any other graph file from it first)\";\n }\n if (startsWithBytes(head, [0x89, 0x50, 0x4e, 0x47])) {\n return \"a PNG image\";\n }\n }\n const text = typeof head === \"string\" ? head.slice(0, 64) : String.fromCodePoint(...head.subarray(0, 64));\n const utf8Bom = String.fromCodePoint(0xef, 0xbb, 0xbf);\n let body = text.startsWith(utf8Bom) ? text.slice(3) : text;\n body = body.startsWith(BOM) ? body.slice(1) : body;\n if (body.startsWith(\"%PDF-\")) {\n return \"a PDF document\";\n }\n if (/^\\s*(<!doctype\\s+html[\\s>]|<html[\\s>])/i.test(body)) {\n return \"an HTML document (likely an error page saved in place of the file)\";\n }\n return null;\n}\n\n/**\n * Fail the import with E_FOREIGN_FORMAT when its head is a known non-graph file; an (X)HTML page\n * is left to an XML format's own root-element check.\n * @param head - the first bytes or the first text\n * @param report - the report\n * @param options - whether the format is XML\n */\nfunction refuseForeign(head: Uint8Array | string, report: ImportReportBuilder, options: ReadOptions): void {\n const foreign = foreignKind(head);\n if (foreign !== null && !(options.xml === true && foreign.startsWith(\"an HTML\"))) {\n report.fail(FOREIGN_FORMAT_CODE, `the input is ${foreign}, not ${report.format} text`);\n }\n}\n\n/**\n * Whether two encodings name the same family for a declaration check: equal, or both UTF-16 (a\n * prolog's \"UTF-16\" says nothing about the byte order the BOM gives).\n * @param a - a canonical encoding name\n * @param b - another\n * @returns true when they agree\n */\nfunction sameFamily(a: string, b: string): boolean {\n return a === b || (a.startsWith(\"utf-16\") && b.startsWith(\"utf-16\"));\n}\n\n/**\n * The one byte decoder of an import: picks the encoding from the head (option, BOM, declaration,\n * UTF-8) and decodes strictly, switching to windows-1252 with a warning when undeclared input\n * turns out not to be UTF-8 while everything decoded so far was ASCII.\n */\nclass ByteDecoder {\n private decoder: TextDecoder | null = null;\n\n private encoding = \"utf-8\";\n\n /** Whether a failed UTF-8 decode may switch to windows-1252 (nothing chose UTF-8 explicitly). */\n private mayFallBack = false;\n\n /** Whether every character decoded so far was ASCII (so switching encodings changes no earlier text). */\n private asciiSoFar = true;\n\n /** While asciiSoFar: the bytes the UTF-8 decoder holds back (a sequence split across chunks). */\n private carry: Uint8Array = new Uint8Array(0);\n\n /** Bytes decoded so far, for error positions. */\n private offset = 0;\n\n /** Whether the decoder switched to windows-1252 because undeclared input was not UTF-8. */\n private fellBack = false;\n\n /**\n * The first control byte (other than TAB, LF, FF, CR and a final Ctrl-Z) of undeclared input\n * that may still turn out not to be UTF-8: its offset and value, or null. Tracked from the\n * start of the input, so the binary-data verdict never depends on how the input was chunked.\n */\n private control: { readonly at: number; readonly byte: number } | null = null;\n\n /** The offset of a Ctrl-Z that ended the previous chunk: control data if more bytes follow. */\n private subAt = -1;\n\n /** Set while flushing because a text chunk follows: a sequence cut there is not windows-1252. */\n private textFollows = false;\n\n /** Whether the file declares UTF-8 (read like an undeclared file, but the fallback warning says so). */\n private declaredUtf8 = false;\n\n /** After the windows-1252 fallback: whether later bytes holding valid UTF-8 are still to be reported (once). */\n private mixedPending = false;\n\n /**\n * Create the decoder of one import.\n * @param report - where warnings and the fatal decode error go\n * @param options - the encoding option and the format's declaration reader\n */\n constructor(\n private readonly report: ImportReportBuilder,\n private readonly options: ReadOptions,\n ) {}\n\n /**\n * Whether start() ran.\n * @returns true once the encoding is chosen\n */\n get started(): boolean {\n return this.decoder !== null;\n }\n\n /**\n * Choose the encoding from the first bytes of the input: a BOM, else the option, else the\n * declaration, else UTF-8 (see the module comment), warning where two of them disagree.\n * @param head - the first bytes (up to the declaration size; fewer when the input is shorter)\n */\n start(head: Uint8Array): void {\n refuseForeign(head, this.report, this.options);\n const explicit = this.options.encoding ?? null;\n const bom = bomEncoding(head);\n if (bom === \"utf-32le\" || bom === \"utf-32be\") {\n this.report.fail(\n INVALID_ENCODING_CODE,\n `the input starts with a ${bom.toUpperCase()} byte order mark; UTF-32 is not supported (convert the file to UTF-8)`,\n undefined,\n { byteOffset: 0 },\n );\n }\n if (bom !== null) {\n const option = explicit === null ? null : canonicalEncoding(explicit);\n if (option !== null && option !== bom) {\n this.conflict(`the encoding option says ${option} but the byte order mark says ${bom}; read as ${bom}`);\n }\n this.checkDeclaration(head, bom, \"its byte order mark says\");\n this.use(bom, false);\n return;\n }\n if (explicit !== null) {\n this.use(explicit, false);\n this.checkDeclaration(head, this.encoding, \"the encoding option says\");\n return;\n }\n const xmlUtf16 = xmlDeclarationUtf16(head);\n if (xmlUtf16 !== null) {\n this.checkDeclaration(head, xmlUtf16, \"its XML declaration's byte pattern says\");\n this.use(xmlUtf16, false);\n return;\n }\n const utf16 = bomlessUtf16(head);\n if (utf16 !== null && this.options.bomlessUtf16 === true) {\n this.use(utf16, false);\n return;\n }\n if (utf16 !== null) {\n this.report.fail(\n INVALID_ENCODING_CODE,\n `the input looks like ${utf16} text without a byte order mark (every other byte is 0); pass the encoding option ${JSON.stringify(utf16)} to read it`,\n undefined,\n { byteOffset: 0 },\n );\n }\n const declared = this.declared(head, \"windows-1252\");\n if (declared !== null) {\n const canonical = canonicalEncoding(declared);\n if (canonical === null) {\n this.report.warning(\n \"unsupported\",\n UNKNOWN_ENCODING_CODE,\n `the input declares the encoding ${JSON.stringify(declared)}, which this platform cannot decode; reading it as UTF-8`,\n { element: declared },\n );\n } else if (canonical.startsWith(\"utf-16\")) {\n // a declared UTF-16 over bytes that are not UTF-16 (no BOM, no NUL pattern): the\n // declaration itself was readable as ASCII\n this.conflict(\n `the input declares ${JSON.stringify(declared)} but has no byte order mark and its bytes are not UTF-16; read as UTF-8`,\n );\n } else if (canonical === \"utf-8\") {\n this.declaredUtf8 = true;\n } else {\n // a declared UTF-8 gets the undeclared treatment: tools often write the default\n // prolog over Latin-1 bytes\n this.use(canonical, false);\n return;\n }\n }\n this.use(\"utf-8\", true);\n }\n\n /**\n * The encoding the head declares, read through the format's declaration reader.\n * @param head - the first bytes\n * @param encoding - the encoding to read the head in for the reader\n * @returns the declared label, or null (also for a format without declarations)\n */\n private declared(head: Uint8Array, encoding: string): string | null {\n const reader = this.options.declaredEncoding;\n return reader === undefined ? null : reader(new TextDecoder(encoding).decode(head));\n }\n\n /**\n * Warn when the file declares an encoding other than the one a BOM or the option chose.\n * @param head - the first bytes\n * @param chosen - the encoding the input is read in\n * @param why - what chose it, for the message\n */\n private checkDeclaration(head: Uint8Array, chosen: string, why: string): void {\n const declared = this.declared(head, chosen);\n const canonical = declared === null ? null : canonicalEncoding(declared);\n if (canonical !== null && !sameFamily(canonical, chosen)) {\n this.conflict(\n `the input declares the encoding ${JSON.stringify(declared)} but ${why} ${chosen}; read as ${chosen}`,\n );\n }\n }\n\n /**\n * Record a W_ENCODING_CONFLICT warning.\n * @param message - which sources disagree and which applies\n */\n private conflict(message: string): void {\n this.report.warning(\"coercion\", ENCODING_CONFLICT_CODE, message);\n }\n\n /**\n * Decode the next bytes.\n * @param input - the bytes\n * @param stream - whether more bytes follow\n * @returns the text\n */\n decode(input: Uint8Array, stream: boolean): string {\n // a browser TextDecoder refuses a view of shared memory: decode a private copy\n const bytes =\n typeof SharedArrayBuffer === \"function\" && input.buffer instanceof SharedArrayBuffer\n ? input.slice()\n : input;\n const decoder = this.decoder as TextDecoder;\n if (this.fellBack || (this.mayFallBack && this.asciiSoFar)) {\n this.watch(bytes, !stream);\n if (this.fellBack && this.control !== null) {\n this.binary(this.control);\n }\n }\n const text = this.decodeOrRecover(decoder, bytes, stream);\n if (this.mixedPending) {\n this.reportUtf8After(bytes, this.offset);\n }\n if (this.asciiSoFar && this.mayFallBack) {\n this.trackAscii(bytes, text);\n }\n if (this.mayFallBack && this.options.nulIsBinary === true && text.includes(\"\\0\")) {\n // a NUL never occurs in a text file: undeclared bytes holding one are binary or BOM-less UTF-16\n this.binary({ at: this.offset + bytes.indexOf(0), byte: 0 });\n }\n this.offset += bytes.byteLength;\n return text;\n }\n\n /**\n * Decode bytes, recovering from bytes that are not valid in the encoding.\n * @param decoder - the decoder\n * @param bytes - the bytes\n * @param stream - whether more bytes follow\n * @returns the text\n */\n private decodeOrRecover(decoder: TextDecoder, bytes: Uint8Array, stream: boolean): string {\n try {\n return decoder.decode(bytes, { stream });\n } catch (err) {\n if (!(err instanceof TypeError)) {\n throw err;\n }\n return this.recover(bytes, stream);\n }\n }\n\n /**\n * Track whether the input is ASCII so far. While it is, every character is one byte, so the\n * bytes not yet emitted are held back for a fallback.\n * @param bytes - the bytes just decoded\n * @param text - their text\n */\n private trackAscii(bytes: Uint8Array, text: string): void {\n if (/[\\u0080-\\uffff]/.test(text)) {\n this.asciiSoFar = false;\n this.carry = new Uint8Array(0);\n return;\n }\n const all = this.carry.byteLength === 0 ? bytes : concatBytes([this.carry, bytes]);\n this.carry = all.slice(text.length);\n }\n\n /**\n * Flush the bytes held back because a text chunk follows (the input switches from bytes to\n * strings): a multi-byte sequence cut there is invalid UTF-8, never windows-1252 text.\n * @param flush - the flush to run (the decoder's own, or the head's)\n * @returns the text of the held-back bytes\n */\n flushBeforeText(flush: () => string): string {\n this.textFollows = true;\n try {\n return flush();\n } finally {\n this.textFollows = false;\n }\n }\n\n /**\n * A decode failed: switch to windows-1252 when allowed, else fail the import with the position\n * of the first byte that is not valid.\n * @param bytes - the bytes that failed\n * @param stream - whether more bytes follow\n * @returns the bytes decoded as windows-1252\n */\n private recover(bytes: Uint8Array, stream: boolean): string {\n const all = this.carry.byteLength === 0 ? bytes : concatBytes([this.carry, bytes]);\n const base = this.offset - this.carry.byteLength;\n const bad = this.encoding === \"utf-8\" ? invalidUtf8(all) : null;\n const at = base + (bad === null ? 0 : bad.index);\n if (this.mayFallBack && this.asciiSoFar) {\n // windows-1252 text holds no control bytes but TAB, LF, FF and CR: one anywhere in\n // undeclared input that is not UTF-8 marks binary data (watch() saw every byte so far)\n if (this.control !== null) {\n return this.binary(this.control);\n }\n if (this.textFollows && bad?.cut === true) {\n return this.report.fail(\n INVALID_UTF8_CODE,\n `invalid UTF-8 at byte ${at}: a byte chunk ends inside a multi-byte sequence and a text chunk follows`,\n undefined,\n { byteOffset: at },\n );\n }\n if (!stream && bad?.truncated === true) {\n return this.report.fail(\n INVALID_UTF8_CODE,\n `the input ends in the middle of a UTF-8 sequence at byte ${at} (it may be truncated)`,\n undefined,\n { byteOffset: at },\n );\n }\n if (!startsWithUtf8(all)) {\n const cutNote =\n !stream && bad?.cut === true\n ? \": it ends inside a multi-byte UTF-8 sequence, so it may be truncated\"\n : \"\";\n this.report.warning(\n \"coercion\",\n ENCODING_FALLBACK_CODE,\n this.declaredUtf8\n ? `the input declares UTF-8 but is not valid UTF-8 (at byte ${at}); everything before was ASCII, so it is read as windows-1252 (pass the encoding option to choose another)`\n : `the input is not valid UTF-8 (at byte ${at}${cutNote}) and declares no encoding; read as windows-1252 (pass the encoding option to choose another)`,\n );\n this.use(\"windows-1252\", false);\n this.fellBack = true;\n this.mixedPending = true;\n this.reportUtf8After(all, base);\n return (this.decoder as TextDecoder).decode(all, { stream });\n }\n }\n if (this.encoding === \"utf-8\") {\n const after = this.mayFallBack ? \" after valid non-ASCII UTF-8 text; pass the encoding option\" : \"\";\n return this.report.fail(INVALID_UTF8_CODE, `invalid UTF-8 at byte ${at}${after}`, undefined, {\n byteOffset: at,\n });\n }\n const end = this.offset + bytes.byteLength;\n const odd = !stream && this.encoding.startsWith(\"utf-16\") && end % 2 === 1;\n return this.report.fail(\n INVALID_ENCODING_CODE,\n odd\n ? `the input ends with half a ${this.encoding} code unit at byte ${end - 1} (it may be truncated)`\n : `the bytes near byte ${base} are not valid ${this.encoding}`,\n undefined,\n { byteOffset: odd ? end - 1 : base },\n );\n }\n\n /**\n * After the windows-1252 fallback, report once that later bytes hold a valid multi-byte UTF-8\n * sequence: the input mixes encodings, and those characters are read as two or three\n * windows-1252 characters each.\n * @param bytes - bytes being decoded as windows-1252\n * @param start - the byte offset of the first of them\n */\n private reportUtf8After(bytes: Uint8Array, start: number): void {\n const at = utf8SequenceIndex(bytes);\n if (at < 0) {\n return;\n }\n this.mixedPending = false;\n this.report.warning(\n \"coercion\",\n ENCODING_FALLBACK_CODE,\n `bytes read as windows-1252 hold valid UTF-8 (at byte ${start + at}): the input mixes encodings, and its UTF-8 characters are read as windows-1252 (pass the encoding option to choose one)`,\n );\n }\n\n /**\n * Note the first control byte of the next bytes (see `control`).\n * @param bytes - the bytes, starting at `offset`\n * @param final - whether they end the input\n */\n private watch(bytes: Uint8Array, final: boolean): void {\n if (this.control !== null || bytes.byteLength === 0) {\n return;\n }\n if (this.subAt >= 0) {\n this.control = { at: this.subAt, byte: 0x1a };\n return;\n }\n // a Ctrl-Z at the end of a chunk is held until it is known whether more bytes follow\n const index = controlByte(bytes, true);\n if (index >= 0) {\n this.control = { at: this.offset + index, byte: bytes[index] };\n } else if (!final && bytes[bytes.byteLength - 1] === 0x1a) {\n this.subAt = this.offset + bytes.byteLength - 1;\n }\n }\n\n /**\n * Fail the import as binary data.\n * @param control - the control byte that shows it\n * @param control.at - its offset\n * @param control.byte - its value\n * @returns never\n */\n private binary(control: { readonly at: number; readonly byte: number }): never {\n const hex = control.byte.toString(16).padStart(2, \"0\");\n return this.report.fail(\n INVALID_UTF8_CODE,\n `the input is binary data, not text: control byte 0x${hex} at byte ${control.at}`,\n undefined,\n { byteOffset: control.at },\n );\n }\n\n /**\n * Switch to an encoding.\n * @param label - the encoding label\n * @param mayFallBack - whether a UTF-8 failure may switch to windows-1252\n */\n private use(label: string, mayFallBack: boolean): void {\n this.decoder = new TextDecoder(label, { fatal: true, ignoreBOM: true });\n this.encoding = this.decoder.encoding;\n this.mayFallBack = mayFallBack;\n }\n}\n\n/**\n * Where the first complete, valid multi-byte UTF-8 sequence of some bytes starts.\n * @param bytes - the bytes\n * @returns its index, or -1\n */\nfunction utf8SequenceIndex(bytes: Uint8Array): number {\n for (let i = 0; i < bytes.byteLength; i++) {\n if (bytes[i] >= 0xc2 && isUtf8SequenceAt(bytes, i)) {\n return i;\n }\n }\n return -1;\n}\n\n/**\n * Whether the first non-ASCII byte of a chunk starts a valid UTF-8 sequence: then the chunk holds\n * valid non-ASCII UTF-8 before whatever made it fail, and the input is not windows-1252.\n * @param bytes - a chunk that failed to decode as UTF-8\n * @returns true when its first non-ASCII sequence is valid UTF-8\n */\nfunction startsWithUtf8(bytes: Uint8Array): boolean {\n const i = bytes.findIndex((b) => b >= 0x80);\n return i >= 0 && isUtf8SequenceAt(bytes, i);\n}\n\n/** A strict UTF-8 decoder for checking single sequences (decode() without stream keeps no state). */\nconst STRICT_UTF8 = new TextDecoder(\"utf-8\", { fatal: true });\n\n/**\n * Whether a complete, valid multi-byte UTF-8 sequence starts at an index.\n * @param bytes - the bytes\n * @param i - the index of the lead byte\n * @returns true when the sequence is complete and valid\n */\nfunction isUtf8SequenceAt(bytes: Uint8Array, i: number): boolean {\n const lead = bytes[i];\n let length = 0;\n if (lead >= 0xc2 && lead <= 0xdf) {\n length = 2;\n } else if (lead >= 0xe0 && lead <= 0xef) {\n length = 3;\n } else if (lead >= 0xf0 && lead <= 0xf4) {\n length = 4;\n }\n if (length === 0 || i + length > bytes.byteLength) {\n return false;\n }\n try {\n STRICT_UTF8.decode(bytes.subarray(i, i + length));\n return true;\n } catch {\n return false;\n }\n}\n\n/** The upper half (0x80-0xFF) of code page 437, the zip format's legacy name encoding (APPNOTE appendix D). */\nconst CP437_HIGH: readonly number[] = [\n 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec,\n 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2,\n 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310,\n 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,\n 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c,\n 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559,\n 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df,\n 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,\n 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2,\n 0x25a0, 0x00a0,\n];\n\n/**\n * The first invalid UTF-8 sequence of a chunk (the WHATWG decoder's rules: no overlong forms, no\n * surrogates, nothing above U+10FFFF).\n * @param bytes - the bytes\n * @returns its index, whether it is a valid sequence cut by the end of the bytes (`cut`), and whether\n * that cut sequence holds at least one continuation byte (truncation rather than a stray windows-1252\n * byte); index 0 when none is found\n */\nfunction invalidUtf8(bytes: Uint8Array): Utf8Problem {\n let i = 0;\n while (i < bytes.byteLength) {\n const lead = bytes[i];\n const sequence = lead < 0x80 ? ASCII_SEQUENCE : utf8Sequence(lead);\n if (sequence === null) {\n return { index: i, truncated: false, cut: false };\n }\n const problem = checkContinuation(bytes, i, sequence);\n if (problem !== null) {\n return problem;\n }\n i += sequence.need + 1;\n }\n return { index: 0, truncated: false, cut: false };\n}\n\n/** Where a UTF-8 check stopped, and why. */\ninterface Utf8Problem {\n readonly index: number;\n readonly truncated: boolean;\n readonly cut: boolean;\n}\n\n/** The continuation bytes a UTF-8 lead byte needs, and the range of the first one. */\ninterface Utf8Sequence {\n readonly need: number;\n readonly low: number;\n readonly high: number;\n}\n\nconst ASCII_SEQUENCE: Utf8Sequence = { need: 0, low: 0x80, high: 0xbf };\n\n/**\n * What a UTF-8 lead byte needs after it (no overlong forms, no surrogates, nothing above U+10FFFF).\n * @param lead - the lead byte, 0x80 or above\n * @returns the sequence, or null when the byte cannot lead one\n */\nfunction utf8Sequence(lead: number): Utf8Sequence | null {\n if (lead >= 0xc2 && lead <= 0xdf) {\n return { need: 1, low: 0x80, high: 0xbf };\n }\n if (lead >= 0xe0 && lead <= 0xef) {\n return { need: 2, low: lead === 0xe0 ? 0xa0 : 0x80, high: lead === 0xed ? 0x9f : 0xbf };\n }\n if (lead >= 0xf0 && lead <= 0xf4) {\n return { need: 3, low: lead === 0xf0 ? 0x90 : 0x80, high: lead === 0xf4 ? 0x8f : 0xbf };\n }\n return null;\n}\n\n/**\n * Check the continuation bytes of the sequence led at `i`.\n * @param bytes - the bytes\n * @param i - the lead byte's index\n * @param sequence - what the lead byte needs\n * @returns the problem, or null when the sequence is valid\n */\nfunction checkContinuation(bytes: Uint8Array, i: number, sequence: Utf8Sequence): Utf8Problem | null {\n for (let k = 1; k <= sequence.need; k++) {\n if (i + k >= bytes.byteLength) {\n return { index: i, truncated: k > 1, cut: true };\n }\n const next = bytes[i + k];\n const low = k === 1 ? sequence.low : 0x80;\n const high = k === 1 ? sequence.high : 0xbf;\n if (next < low || next > high) {\n return { index: i, truncated: false, cut: false };\n }\n }\n return null;\n}\n\n/**\n * The first control byte of a chunk that text never holds: below 0x20 but TAB, LF, FF and CR, and\n * not a Ctrl-Z that ends the input (the DOS end-of-file marker).\n * @param bytes - the bytes\n * @param final - whether the bytes end the input\n * @returns its index, or -1\n */\nfunction controlByte(bytes: Uint8Array, final: boolean): number {\n const last = bytes.byteLength - 1;\n return bytes.findIndex(\n (b, i) =>\n b < 0x20 && b !== 0x09 && b !== 0x0a && b !== 0x0c && b !== 0x0d && !(b === 0x1a && final && i === last),\n );\n}\n\n/**\n * A zip entry name as text (APPNOTE 4.4.17 and appendix D): UTF-8 when the bytes are valid UTF-8,\n * whether or not the entry sets the language-encoding flag (bit 11); otherwise code page 437 when\n * the flag is clear (what Windows and older tools write), and windows-1252 when the entry claims\n * UTF-8 (or no flags are known). Never throws: a name is a matching key, never a path, so an\n * undecodable one still has to name its entry.\n * @param bytes - the name bytes from the central directory or a local header\n * @param cp437 - the entry's bit 11 is clear: decode a non-UTF-8 name as code page 437\n * @returns the name\n * @category Plugin helpers\n */\nexport function decodeEntryName(bytes: Uint8Array, cp437 = false): string {\n try {\n return new TextDecoder(\"utf-8\", { fatal: true, ignoreBOM: true }).decode(bytes);\n } catch {\n if (!cp437) {\n return new TextDecoder(\"windows-1252\").decode(bytes);\n }\n let out = \"\";\n for (const byte of bytes) {\n out += String.fromCodePoint(byte < 0x80 ? byte : CP437_HIGH[byte - 0x80]);\n }\n return out;\n }\n}\n\n/**\n * Join byte chunks.\n * @param chunks - the chunks\n * @returns one array holding them in order\n */\nfunction concatBytes(chunks: readonly Uint8Array[]): Uint8Array {\n if (chunks.length === 1) {\n return chunks[0];\n }\n const out = new Uint8Array(chunks.reduce((sum, c) => sum + c.byteLength, 0));\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n}\n\n/**\n * Bytes decoded per call when the input is one in-memory Uint8Array, and characters per chunk of a\n * string input: every consumer of textChunks() then sees bounded chunks whatever the input shape (a\n * line reader or a record reader that holds one parse result per chunk never holds more than this\n * much text at once), and progress stays granular.\n */\nconst DECODE_SLICE = 256 * 1024;\n\nconst BOM = String.fromCharCode(0xfeff);\n\n/** The DOS end-of-file marker, Ctrl-Z. */\nconst SUB = String.fromCodePoint(0x1a);\n\n/**\n * The longest string V8 makes (2^29 - 24 UTF-16 code units); longer text cannot be one string.\n * @category Plugin helpers\n */\nexport const MAX_TEXT_LENGTH = 2 ** 29 - 24;\n\n/**\n * A character text keeps only by accident: a C0 control but TAB, LF, FF and CR, DEL, a C1 control\n * (also what windows-1252 bytes it leaves undefined decode to), and U+FEFF inside the text.\n */\nconst CONTROL_CHARACTER = new RegExp(\n `[${String.fromCodePoint(0x00)}-${String.fromCodePoint(0x08, 0x0b, 0x0e)}-${String.fromCodePoint(0x1f, 0x7f)}-${String.fromCodePoint(0x9f)}${BOM}]`,\n);\n\n/**\n * Whether a value is an ImportInput this module can read.\n * @param input - any value\n * @returns true for a string, a Uint8Array, a ReadableStream or an async iterable\n * @category Plugin helpers\n */\nexport function isImportInput(input: unknown): input is ImportInput {\n if (typeof input === \"string\" || asBytes(input) !== null) {\n return true;\n }\n if (typeof input !== \"object\" || input === null) {\n return false;\n }\n return typeof (input as { getReader?: unknown }).getReader === \"function\" || Symbol.asyncIterator in input;\n}\n\n/**\n * A Uint8Array, also one from another realm (a vm context, an iframe), as a Uint8Array of this one.\n * @param value - any value\n * @returns the bytes, or null when the value is not a Uint8Array\n */\nfunction asBytes(value: unknown): Uint8Array | null {\n if (value instanceof Uint8Array) {\n return value;\n }\n if (ArrayBuffer.isView(value) && Object.prototype.toString.call(value) === \"[object Uint8Array]\") {\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n return null;\n}\n\n/**\n * The input as one of the ImportInput shapes, or E_UNSUPPORTED naming what was passed and how to\n * pass it instead (an ArrayBuffer, a Blob, a fetch Response, null from a failed read).\n * @param input - what the caller passed\n * @returns the input (a Uint8Array from another realm re-viewed in this one)\n * @category Plugin helpers\n */\nexport function normalizeInput(input: unknown): ImportInput {\n if (typeof input === \"string\") {\n return input;\n }\n const bytes = asBytes(input);\n if (bytes !== null) {\n return bytes;\n }\n if (isImportInput(input)) {\n return input;\n }\n const tag = Object.prototype.toString.call(input).slice(8, -1);\n let found: string = input === null ? \"null\" : typeof input;\n let fix = \"\";\n if (tag === \"ArrayBuffer\" || tag === \"SharedArrayBuffer\") {\n found = `an ${tag}`;\n fix = \"; wrap it as new Uint8Array(buffer)\";\n } else if (ArrayBuffer.isView(input)) {\n found = `a ${tag}`;\n fix = \"; pass new Uint8Array(view.buffer, view.byteOffset, view.byteLength)\";\n } else if (typeof input === \"object\" && input !== null) {\n const object = input as { stream?: unknown; size?: unknown; body?: unknown; arrayBuffer?: unknown };\n if (typeof object.stream === \"function\" && typeof object.size === \"number\") {\n found = \"a Blob or File\";\n fix = \"; pass blob.stream()\";\n } else if (\"body\" in object && typeof object.arrayBuffer === \"function\") {\n found = \"a fetch Response\";\n fix = \"; pass response.body\";\n } else {\n found = `an object (${tag})`;\n }\n }\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `the input is ${found}; an importer reads a string, a Uint8Array, a ReadableStream or an async iterable of strings and Uint8Arrays${fix}`,\n { reason: \"input type\", found },\n );\n}\n\n/**\n * The total size of an in-memory input (bytes for a Uint8Array, UTF-16 code units for a string),\n * null for a stream or an iterable.\n * @param input - the input\n * @returns the size, or null when unknown up front\n * @category Plugin helpers\n */\nexport function inputLength(input: ImportInput): number | null {\n if (typeof input === \"string\") {\n return input.length;\n }\n if (input instanceof Uint8Array) {\n return input.byteLength;\n }\n return null;\n}\n\n/**\n * The UTF-8 length of a text, the unit onProgress counts text in (a lone surrogate counts as the\n * three bytes of U+FFFD).\n * @param text - the text\n * @returns its length in UTF-8 bytes\n */\nfunction utf8Length(text: string): number {\n let bytes = 0;\n let i = 0;\n while (i < text.length) {\n const c = text.charCodeAt(i); // NOSONAR(S7758): reads UTF-16 code units on purpose\n const pair = c >= 0xd800 && c <= 0xdbff && (text.charCodeAt(i + 1) & 0xfc00) === 0xdc00; // NOSONAR(S7758): reads UTF-16 code units on purpose\n if (c < 0x80) {\n bytes++;\n } else if (c < 0x800) {\n bytes += 2;\n } else {\n bytes += pair ? 4 : 3;\n }\n i += pair ? 2 : 1;\n }\n return bytes;\n}\n\n/**\n * Throw the signal's reason when it is aborted, exactly as the platform's\n * `AbortSignal.throwIfAborted()` does: the reason as it is (the DOMException named \"AbortError\"\n * of a reason-less abort, or whatever the caller passed to `abort(reason)`, an Error or not), so a\n * caller can compare the rejection with `signal.reason`. Only a runtime that stores no reason at\n * all gets a synthesized AbortError.\n * @param signal - the signal, or null\n * @category Writing a format\n */\nexport function throwIfAborted(signal: AbortSignal | null | undefined): void {\n if (!signal?.aborted) {\n return;\n }\n const { reason }: { reason: unknown } = signal;\n if (reason === undefined) {\n throw abortError();\n }\n // the platform contract: the reason itself, whatever its type\n throw reason as Error; // NOSONAR(S3696): rethrows the abort signal's reason, as the platform does\n}\n\n/**\n * An abort error for a signal that carries no reason.\n * @returns a DOMException named \"AbortError\" where DOMException exists, else an Error with that name\n */\nfunction abortError(): Error {\n const message = \"The import was aborted\";\n if (typeof DOMException === \"function\") {\n return new DOMException(message, \"AbortError\");\n }\n const err = new Error(message);\n err.name = \"AbortError\";\n return err;\n}\n\n/**\n * A promise that also settles when the signal fires, rejecting with its reason: a read that stalls\n * (a network body that never delivers) cannot keep an aborted import pending. The caller checks\n * the signal before calling.\n * @param promise - the pending operation\n * @param signal - the cancellation signal, or null\n * @returns the operation's result, or the abort reason as a rejection\n * @category Plugin helpers\n */\nexport function abortable<T>(promise: Promise<T>, signal: AbortSignal | null): Promise<T> {\n if (signal === null || typeof signal.addEventListener !== \"function\") {\n return promise;\n }\n return new Promise<T>((resolve, reject) => {\n const onAbort = (): void => {\n try {\n throwIfAborted(signal);\n } catch (err) {\n reject(err);\n }\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n promise.then(\n (value) => {\n signal.removeEventListener(\"abort\", onAbort);\n resolve(value);\n },\n (err: unknown) => {\n signal.removeEventListener(\"abort\", onAbort);\n reject(err);\n },\n );\n });\n}\n\n/**\n * A reader of a stream, or E_UNSUPPORTED when another reader holds the stream already.\n * @param stream - the stream\n * @returns the reader\n * @category Plugin helpers\n */\nexport function lockReader(stream: ReadableStream<Uint8Array>): ReadableStreamDefaultReader<Uint8Array> {\n if (stream.locked) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n \"the input ReadableStream is locked: another reader holds it (release it, or pass a fresh stream)\",\n { reason: \"stream locked\" },\n );\n }\n return stream.getReader();\n}\n\n/**\n * The checks every decoded text passes once, whatever the input shape: leading U+FEFFs stripped,\n * a known non-graph document (HTML, PDF) refused, a trailing Ctrl-Z held back and dropped at the\n * end, the first control character reported, and an input with no text but whitespace refused.\n */\nclass TextFilter {\n private first = true;\n\n private content = false;\n\n private heldSub = false;\n\n private warned = false;\n\n private strayBom = false;\n\n /**\n * Create the filter of one import.\n * @param report - where the warnings and the fatal errors go\n * @param options - whether an empty input is valid, whether the format is XML\n */\n constructor(\n private readonly report: ImportReportBuilder,\n private readonly options: ReadOptions,\n ) {}\n\n /**\n * Filter the next decoded text.\n * @param chunk - the text\n * @returns the text to hand on\n */\n push(chunk: string): string {\n let text = this.heldSub ? SUB + chunk : chunk;\n this.heldSub = false;\n if (text.length === 0) {\n return text;\n }\n if (this.first) {\n text = this.skipByteOrderMarks(text);\n if (text.length === 0) {\n return text;\n }\n this.first = false;\n // ponytail: only the first non-empty chunk is checked, so a document arriving one\n // character per chunk is not recognized; the parser then fails on it instead\n refuseForeign(text, this.report, this.options);\n }\n if (text.endsWith(SUB)) {\n this.heldSub = true;\n text = text.slice(0, -1);\n }\n if (!this.content && /\\S/.test(text)) {\n this.content = true;\n }\n if (!this.warned) {\n this.warnControl(text);\n }\n return text;\n }\n\n /**\n * Drop the byte order marks the input starts with, warning when there is more than one.\n * @param text - the first text\n * @returns the text without them\n */\n private skipByteOrderMarks(text: string): string {\n let start = 0;\n while (text.codePointAt(start) === 0xfeff) {\n start++;\n }\n if (start > 1 && !this.strayBom) {\n // the first U+FEFF is the byte order mark; another one is a stray character\n this.strayBom = true;\n this.report.warning(\n \"validation-error\",\n CONTROL_CHARACTER_CODE,\n `the input starts with ${start} byte order marks (U+FEFF); the extra ones were ignored`,\n { element: \"U+FEFF\" },\n );\n }\n return text.slice(start);\n }\n\n /**\n * Warn, once, about the first control character the text holds.\n * @param text - the text\n */\n private warnControl(text: string): void {\n const match = CONTROL_CHARACTER.exec(text);\n if (match === null) {\n return;\n }\n this.warned = true;\n const code = (match[0].codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, \"0\");\n const around = JSON.stringify(text.slice(Math.max(0, match.index - 20), match.index + 20));\n this.report.warning(\n \"validation-error\",\n CONTROL_CHARACTER_CODE,\n `the input holds the control character U+${code} (in ${around}); it is kept in the id or value it is part of`,\n { element: `U+${code}` },\n );\n }\n\n /** The input ended: drop a held Ctrl-Z with a warning, and refuse an input without content. */\n end(): void {\n if (this.heldSub) {\n this.report.warning(\n \"validation-error\",\n CONTROL_CHARACTER_CODE,\n \"the input ends with a Ctrl-Z (0x1A), the DOS end-of-file marker; it was ignored\",\n { element: \"U+001A\" },\n );\n }\n if (!this.content && this.options.allowEmpty !== true) {\n this.report.fail(EMPTY_INPUT_CODE, this.first ? \"the input is empty\" : \"the input holds only whitespace\");\n }\n }\n}\n\n/**\n * Record W_OPTION_IGNORED for an encoding option given with text input, which is already decoded.\n * @param report - the report\n * @param encoding - the option, or null / undefined\n */\nfunction reportTextEncoding(report: ImportReportBuilder, encoding: string | null | undefined): void {\n if (encoding !== null && encoding !== undefined) {\n report.warning(\n \"unsupported\",\n OPTION_IGNORED_CODE,\n \"option encoding has no effect on text input, which is already decoded\",\n { element: \"encoding\" },\n );\n }\n}\n\n/**\n * Read an ImportInput as a sequence of decoded text chunks. Chunk boundaries carry no meaning:\n * a caller that needs lines uses LineReader, one that needs the whole document uses readText().\n * The signal is checked before every chunk and while a stream read is pending; a stream is\n * canceled when the consumer stops early or the signal fires. Progress is reported after every\n * chunk, in bytes (text counted as UTF-8).\n * @param rawInput - the input\n * @param report - the report decode errors and the text checks are recorded in (E_INVALID_UTF8,\n * E_EMPTY_INPUT, ... then ImportError)\n * @param options - cancellation, progress and the encoding\n * @yields decoded text; leading BOMs and a trailing Ctrl-Z removed\n * @returns nothing\n * @category Writing a format\n */\nexport async function* textChunks(\n rawInput: ImportInput,\n report: ImportReportBuilder,\n options: ReadOptions = {},\n): AsyncGenerator<string, void, undefined> {\n const input = normalizeInput(rawInput);\n const signal = options.signal ?? null;\n const onProgress = options.onProgress ?? null;\n const filter = new TextFilter(report, options);\n throwIfAborted(signal);\n if (typeof input === \"string\") {\n reportTextEncoding(report, options.encoding);\n const total = onProgress === null ? 0 : utf8Length(input);\n let done = 0;\n let start = 0;\n while (start < input.length) {\n throwIfAborted(signal);\n let end = Math.min(start + DECODE_SLICE, input.length);\n const highSurrogate = (input.charCodeAt(end - 1) & 0xfc00) === 0xd800; // NOSONAR(S7758): reads UTF-16 code units on purpose\n if (end < input.length && highSurrogate) {\n end++; // never split a surrogate pair\n }\n const piece = start === 0 && end === input.length ? input : input.slice(start, end);\n start = end;\n const text = filter.push(piece);\n if (text.length > 0) {\n yield text;\n }\n if (onProgress !== null) {\n done += utf8Length(piece);\n onProgress(done, total);\n }\n }\n if (input.length === 0) {\n onProgress?.(0, 0);\n }\n filter.end();\n return;\n }\n const decoder = new ByteDecoder(report, options);\n const headBytes = Math.max(options.declarationBytes ?? HEAD_BYTES, BOM_BYTES);\n if (input instanceof Uint8Array) {\n const length = input.byteLength;\n decoder.start(input.subarray(0, headBytes));\n for (let offset = 0; offset < length; offset += DECODE_SLICE) {\n throwIfAborted(signal);\n if (input.byteLength !== length) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n \"the input's buffer was detached (transferred) or resized during the import\",\n { reason: \"detached\" },\n );\n }\n const slice = input.subarray(offset, Math.min(offset + DECODE_SLICE, length));\n const text = filter.push(decoder.decode(slice, offset + DECODE_SLICE < length));\n if (text.length > 0) {\n yield text;\n }\n onProgress?.(Math.min(offset + DECODE_SLICE, length), length);\n }\n if (length === 0) {\n onProgress?.(0, 0);\n }\n filter.end();\n return;\n }\n // the first bytes of a stream are held back until the BOM check (and the declaration check,\n // when the format has one) can see enough of them, or the input ended, or text arrived, so a\n // BOM or a declaration split across small chunks is still seen\n const needed = options.declaredEncoding === undefined ? BOM_BYTES : headBytes;\n let done = 0;\n let head: Uint8Array[] = [];\n let headLength = 0;\n const flushHead = (stream: boolean): string => {\n const bytes = concatBytes(head);\n head = [];\n headLength = 0;\n decoder.start(bytes.subarray(0, headBytes));\n return decoder.decode(bytes, stream);\n };\n const chunks = isReadableStream(input) ? streamChunks(input, signal) : input;\n for await (const chunk of chunks) {\n throwIfAborted(signal);\n let text: string;\n const bytes = typeof chunk === \"string\" ? null : asBytes(chunk);\n if (typeof chunk === \"string\") {\n // finish any byte sequence still pending in the decoder before switching to text; the\n // BOM and declaration checks apply to the start of the input only, so bytes after text\n // are never taken for a BOM\n let pending = \"\";\n if (headLength > 0) {\n pending = decoder.flushBeforeText(() => flushHead(false));\n } else if (decoder.started) {\n pending = decoder.flushBeforeText(() => decoder.decode(new Uint8Array(0), false));\n } else {\n decoder.start(new Uint8Array(0));\n }\n text = filter.push(pending + chunk);\n done += onProgress === null ? 0 : utf8Length(chunk);\n } else if (bytes !== null) {\n done += bytes.byteLength;\n if (decoder.started) {\n text = filter.push(decoder.decode(bytes, true));\n } else {\n head.push(bytes);\n headLength += bytes.byteLength;\n text = headLength >= needed ? filter.push(flushHead(true)) : \"\";\n }\n } else {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"an input chunk must be a string or a Uint8Array\", {\n reason: \"chunk type\",\n found: typeof chunk,\n });\n }\n if (text.length > 0) {\n yield text;\n }\n onProgress?.(done);\n }\n let tail = \"\";\n if (headLength > 0) {\n tail = flushHead(false);\n } else if (decoder.started) {\n tail = decoder.decode(new Uint8Array(0), false);\n }\n tail = filter.push(tail);\n if (tail.length > 0) {\n yield tail;\n }\n filter.end();\n onProgress?.(done, done);\n}\n\n/**\n * Whether an input is a ReadableStream (by duck type, so a stream from another realm qualifies).\n * @param input - a non-string, non-Uint8Array input\n * @returns true for a ReadableStream\n */\nfunction isReadableStream(\n input: ReadableStream<Uint8Array> | AsyncIterable<string | Uint8Array>,\n): input is ReadableStream<Uint8Array> {\n return typeof (input as { getReader?: unknown }).getReader === \"function\";\n}\n\n/**\n * Iterate a ReadableStream through a reader, cancelling the stream (with the signal's reason) when\n * iteration stops early or the signal fires, also while a read is pending.\n * @param stream - the stream\n * @param signal - the cancellation signal, or null\n * @yields the stream's chunks\n * @returns nothing\n */\nasync function* streamChunks(\n stream: ReadableStream<Uint8Array>,\n signal: AbortSignal | null,\n): AsyncGenerator<Uint8Array, void, undefined> {\n const reader = lockReader(stream);\n let finished = false;\n try {\n for (;;) {\n throwIfAborted(signal);\n const { done, value } = await abortable(reader.read(), signal);\n if (done) {\n finished = true;\n return;\n }\n yield value;\n }\n } finally {\n if (!finished) {\n await reader.cancel(signal?.reason).catch(() => undefined);\n }\n reader.releaseLock();\n }\n}\n\n/**\n * Read the whole input as bytes, for a binary format (a zip): nothing is decoded. The signal is\n * checked before every chunk and progress reported after it; a stream is canceled when the read\n * stops early.\n * @param rawInput - the input\n * @param options - cancellation and progress (the encoding is ignored)\n * @returns the bytes, or null when the input is text (a string, or a chunk that is a string),\n * which a binary format cannot read\n */\nexport async function readBytes(rawInput: ImportInput, options: ReadOptions = {}): Promise<Uint8Array | null> {\n const input = normalizeInput(rawInput);\n const signal = options.signal ?? null;\n throwIfAborted(signal);\n if (typeof input === \"string\") {\n return null;\n }\n if (input instanceof Uint8Array) {\n options.onProgress?.(input.byteLength, input.byteLength);\n return input;\n }\n const parts: Uint8Array[] = [];\n let done = 0;\n for await (const chunk of isReadableStream(input) ? streamChunks(input, signal) : input) {\n throwIfAborted(signal);\n if (typeof chunk === \"string\") {\n return null;\n }\n const bytes = asBytes(chunk);\n if (bytes === null) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"an input chunk must be a string or a Uint8Array\", {\n reason: \"chunk type\",\n found: typeof chunk,\n });\n }\n parts.push(bytes);\n done += bytes.byteLength;\n options.onProgress?.(done);\n }\n options.onProgress?.(done, done);\n return parts.length === 0 ? new Uint8Array(0) : concatBytes(parts);\n}\n\n/**\n * Fail an import whose text is longer than one JavaScript string can hold.\n * @param report - the report\n * @param message - what is too long\n * @param line - the line, when known\n * @category Plugin helpers\n */\nexport function tooLarge(report: ImportReportBuilder, message: string, line?: number): never {\n report.failWith(\"unsupported\", TOO_LARGE_CODE, message, line === undefined ? undefined : { line });\n}\n\n/**\n * Read the whole input as one string (what the GML, DOT and JSON importers do), failing with\n * E_TOO_LARGE (category unsupported) before the join when it is longer than one JavaScript string\n * can hold. A string input is used as it is (one progress call), after the same text checks.\n * @param rawInput - the input\n * @param report - the report the decode error is recorded in\n * @param options - cancellation and progress\n * @returns the decoded text without a leading BOM or a trailing Ctrl-Z\n * @category Writing a format\n */\nexport async function readText(\n rawInput: ImportInput,\n report: ImportReportBuilder,\n options: ReadOptions = {},\n): Promise<string> {\n const input = normalizeInput(rawInput);\n if (typeof input === \"string\") {\n throwIfAborted(options.signal);\n reportTextEncoding(report, options.encoding);\n const filter = new TextFilter(report, options);\n const text = filter.push(input);\n filter.end();\n if (options.onProgress !== null && options.onProgress !== undefined) {\n const total = utf8Length(input);\n options.onProgress(total, total);\n }\n return text;\n }\n const parts: string[] = [];\n let length = 0;\n for await (const chunk of textChunks(input, report, options)) {\n length += chunk.length;\n if (length > MAX_TEXT_LENGTH) {\n tooLarge(\n report,\n `the document is longer than ${MAX_TEXT_LENGTH} characters, the most one JavaScript string holds`,\n );\n }\n parts.push(chunk);\n }\n return parts.length === 1 ? parts[0] : parts.join(\"\");\n}\n\n/**\n * Lines of an ImportInput without allocating anything per line but the string itself: iterate with\n * `for await (const text of reader)` and read `reader.line` (1-based) for the line just yielded.\n * `\\n`, `\\r\\n` and lone `\\r` all end a line; the terminator is not part of the text; a final\n * line without a terminator is yielded when non-empty, and every line in between is yielded even\n * when empty (the importer decides what a blank line means).\n * @category Writing a format\n */\nexport class LineReader implements AsyncIterable<string> {\n private readonly input: ImportInput;\n\n private readonly report: ImportReportBuilder;\n\n private readonly options: ReadOptions;\n\n private lineNumber = 0;\n\n /**\n * Create a reader over an input; nothing is read until iteration starts.\n * @param input - the input\n * @param report - the report decode errors are recorded in\n * @param options - cancellation and progress\n */\n constructor(input: ImportInput, report: ImportReportBuilder, options: ReadOptions = {}) {\n this.input = input;\n this.report = report;\n this.options = options;\n }\n\n /**\n * The 1-based number of the line most recently yielded.\n * @returns the line number; 0 before the first line\n */\n get line(): number {\n return this.lineNumber;\n }\n\n /**\n * The E_TOO_LARGE message of a line longer than one string.\n * @returns the message\n */\n private lineTooLong(): string {\n return `a line is longer than ${MAX_TEXT_LENGTH} characters, the most one JavaScript string holds`;\n }\n\n /**\n * Iterate the lines.\n * @yields one line at a time, terminator removed\n * @returns nothing\n */\n async *[Symbol.asyncIterator](): AsyncGenerator<string, void, undefined> {\n // The pieces of the line in progress (none of them holds a line break, except that the last\n // may end with a `\\r` whose meaning the next chunk decides); joined once when the line ends,\n // so a line spanning many chunks costs its length, not its length times the chunk count.\n const pending: string[] = [];\n let pendingLength = 0;\n let trailingCr = false;\n for await (const chunk of textChunks(this.input, this.report, this.options)) {\n let start = 0;\n const end = chunk.length;\n if (trailingCr) {\n // the previous chunk ended with `\\r`: that ended a line, and a leading `\\n` here is\n // the second half of the same terminator\n trailingCr = false;\n this.lineNumber++;\n const joined = pending.join(\"\");\n pending.length = 0;\n pendingLength = 0;\n yield joined.slice(0, -1);\n if (chunk.charCodeAt(0) === 10) {\n start = 1;\n }\n }\n // the next `\\n` and `\\r` at or after `start`; each is searched for once per chunk and\n // again only after it was consumed, so a chunk is scanned once whatever its line count\n let nl = chunk.indexOf(\"\\n\", start);\n let cr = chunk.indexOf(\"\\r\", start);\n while (nl >= 0 || cr >= 0) {\n let cut: number;\n let next: number;\n if (cr >= 0 && (nl < 0 || cr < nl)) {\n if (cr === end - 1) {\n // a trailing \\r may be the first half of \\r\\n split across chunks\n break;\n }\n cut = cr;\n next = chunk.charCodeAt(cr + 1) === 10 ? cr + 2 : cr + 1;\n } else {\n cut = nl;\n next = nl + 1;\n }\n this.lineNumber++;\n const piece = chunk.slice(start, cut);\n if (pending.length === 0) {\n yield piece;\n } else {\n if (pendingLength + piece.length > MAX_TEXT_LENGTH) {\n tooLarge(this.report, this.lineTooLong(), this.lineNumber);\n }\n pending.push(piece);\n const joined = pending.join(\"\");\n pending.length = 0;\n pendingLength = 0;\n yield joined;\n }\n start = next;\n if (nl >= 0 && nl < next) {\n nl = chunk.indexOf(\"\\n\", next);\n }\n if (cr >= 0 && cr < next) {\n cr = chunk.indexOf(\"\\r\", next);\n }\n }\n if (start < end) {\n pendingLength += end - start;\n if (pendingLength > MAX_TEXT_LENGTH) {\n tooLarge(this.report, this.lineTooLong(), this.lineNumber + 1);\n }\n pending.push(start === 0 ? chunk : chunk.slice(start));\n trailingCr = chunk.charCodeAt(end - 1) === 13;\n }\n }\n if (pending.length > 0) {\n this.lineNumber++;\n const joined = pending.join(\"\");\n yield joined.endsWith(\"\\r\") ? joined.slice(0, -1) : joined;\n }\n }\n}\n","/**\n * Option normalization for importers and exporters (design sections 8.4 and 8.5): every common\n * option resolved to its documented default, enum values checked (E_UNSUPPORTED, the core's\n * convention for an option outside its set), and the per-format defaults (`ids`, `defaultDirected`,\n * `weightFrom`, `addMissingNodes`) supplied by the importer that calls resolveImportOptions().\n */\n\nimport { type DuplicatePolicy, GraphFormatError, type GraphSink, type IdCoercion } from \"@graphty/graph-format\";\n\nimport { type CommonExportOptions, type CommonImportOptions, type GraphChoiceOptions } from \"../types.js\";\nimport {\n AMBIGUOUS_GRAPH_NAME_CODE,\n GRAPH_NOT_FOUND_CODE,\n NO_GRAPH_CODE,\n OPTION_IGNORED_CODE,\n SINK_OPTION_CODE,\n} from \"./codes.js\";\nimport { canonicalEncoding } from \"./input.js\";\nimport { plural } from \"./plural.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\n\n/**\n * The defaults an importer supplies for the options whose default is per format.\n * @category Plugin helpers\n */\nexport interface ImportFormatDefaults {\n /** \"canonical\" for text-cell formats, \"keep\" for JSON. */\n readonly ids: IdCoercion;\n /** The direction assumed when the file declares none. */\n readonly defaultDirected: boolean;\n /** The attribute that becomes the weight (\"weight\", GML \"value\"), or null for an unweighted format. */\n readonly weightFrom: string | null;\n /** Whether edges may reference undeclared nodes; true unless the format says otherwise (GEXF: false). */\n readonly addMissingNodes?: boolean | undefined;\n}\n\n/**\n * CommonImportOptions with every field present.\n * @public\n * @category Plugin helpers\n */\nexport interface ResolvedImportOptions {\n /** The id coercion rule. */\n readonly ids: IdCoercion;\n /** Which field becomes the node id. */\n readonly nodeIdFrom: \"id\" | \"label\" | \"index\";\n /** Whether an edge may reference an undeclared node. */\n readonly addMissingNodes: boolean;\n /** The builder's duplicate-edge policy seed. */\n readonly duplicateEdges: DuplicatePolicy;\n /** The builder's self-loop policy seed. */\n readonly selfLoops: \"keep\" | \"drop\" | \"error\";\n /** The mixed-direction policy. */\n readonly onMixedDirection: \"expand\" | \"directed\" | \"undirected\" | \"error\";\n /** The direction assumed when the file declares none. */\n readonly defaultDirected: boolean;\n /** The weight attribute, or null for unweighted. */\n readonly weightFrom: string | null;\n /** The weight staging precision. */\n readonly weightDtype: \"f32\" | \"f64\";\n /** How declared long columns are stored. */\n readonly long: \"f64\" | \"string\";\n /** Whether mangled ids are restored. */\n readonly restoreMangledIds: boolean;\n /** The hyperedge policy. */\n readonly hyperedges: \"error\" | \"skip\" | \"star\" | \"clique\";\n /** Errors tolerated before aborting. */\n readonly errorLimit: number;\n /** The cancellation signal, or null. */\n readonly signal: AbortSignal | null;\n /** The progress callback, or null. */\n readonly onProgress: ((bytesDone: number, bytesTotal?: number) => void) | null;\n /** The caller's byte encoding (canonical name), or null to detect it. */\n readonly encoding: string | null;\n}\n\n/**\n * CommonExportOptions with every field present.\n * @category Plugin helpers\n */\nexport interface ResolvedExportOptions {\n /** \"error\" never renames a node; \"mangle\" rewrites and keeps the original. */\n readonly sanitizeIds: \"error\" | \"mangle\";\n /** What a format without mixed-direction support does with a mixed snapshot. */\n readonly onMixedDirection: \"error\" | \"directed\" | \"undirected\";\n}\n\nconst ID_COERCIONS: ReadonlySet<string> = new Set([\"keep\", \"canonical\", \"string\", \"number\"]);\nconst NODE_ID_SOURCES: ReadonlySet<string> = new Set([\"id\", \"label\", \"index\"]);\nconst DUPLICATE_POLICIES: ReadonlySet<string> = new Set([\"keep\", \"error\", \"first\", \"last\", \"sum\", \"min\", \"max\"]);\nconst SELF_LOOP_POLICIES: ReadonlySet<string> = new Set([\"keep\", \"drop\", \"error\"]);\nconst IMPORT_MIXED_POLICIES: ReadonlySet<string> = new Set([\"expand\", \"directed\", \"undirected\", \"error\"]);\nconst EXPORT_MIXED_POLICIES: ReadonlySet<string> = new Set([\"error\", \"directed\", \"undirected\"]);\nconst WEIGHT_DTYPES: ReadonlySet<string> = new Set([\"f32\", \"f64\"]);\nconst LONG_MODES: ReadonlySet<string> = new Set([\"f64\", \"string\"]);\nconst HYPEREDGE_POLICIES: ReadonlySet<string> = new Set([\"error\", \"skip\", \"star\", \"clique\"]);\nconst SANITIZE_MODES: ReadonlySet<string> = new Set([\"error\", \"mangle\"]);\n\n/**\n * The default error limit: errors tolerated before an import aborts.\n * @category Reports and errors\n */\nexport const DEFAULT_ERROR_LIMIT = 100;\n\nexport { SINK_OPTION_CODE } from \"./codes.js\";\n\n/** The builder-policy options a sink exposes read-only as `sink.options` (design section 8.4). */\nconst SINK_OPTION_NAMES = [\"addMissingNodes\", \"duplicateEdges\", \"selfLoops\", \"weightDtype\"] as const;\n\n/**\n * Report every builder-policy option the caller explicitly requested that the sink does not use,\n * one `W_SINK_OPTION` warning per option with the option name as\n * the element. Options left undefined are never reported: they are defaults, not requests. On the\n * registry's builder, which is seeded from the same options, nothing is ever reported.\n * @param sink - the sink the importer pushes into\n * @param options - the caller's raw options, possibly undefined\n * @param report - the report to record into\n * @param enforcesMissingNodes - true when the importer applies `addMissingNodes: false` itself (it\n * refuses unknown endpoints before the sink sees them), so that request is honored on any sink and\n * only `addMissingNodes: true` against a refusing sink is reported\n * @returns the number of warnings recorded\n * @category Writing a format\n */\nexport function reportSinkOptions(\n sink: GraphSink,\n options: CommonImportOptions | undefined,\n report: ImportReportBuilder,\n enforcesMissingNodes = false,\n): number {\n if (options === undefined) {\n return 0;\n }\n let recorded = 0;\n for (const name of SINK_OPTION_NAMES) {\n const wanted: unknown = options[name];\n const actual: unknown = sink.options[name];\n if (wanted === undefined || wanted === actual) {\n continue;\n }\n if (name === \"addMissingNodes\" && enforcesMissingNodes && wanted === false) {\n continue;\n }\n report.warning(\n \"coercion\",\n SINK_OPTION_CODE,\n `option ${name}: ${JSON.stringify(wanted)} requested but the sink uses ${JSON.stringify(actual)}; the sink's setting applies`,\n { element: name },\n );\n recorded++;\n }\n return recorded;\n}\n\n/** The common options a format may leave unused; `signal`, `onProgress` and `errorLimit` apply everywhere. */\nconst IGNORABLE_OPTION_NAMES = [\n \"ids\",\n \"nodeIdFrom\",\n \"addMissingNodes\",\n \"duplicateEdges\",\n \"selfLoops\",\n \"onMixedDirection\",\n \"defaultDirected\",\n \"weightFrom\",\n \"weightDtype\",\n \"long\",\n \"restoreMangledIds\",\n \"hyperedges\",\n] as const;\n\n/** The defaults that are the same in every format (the others, such as `defaultDirected`, differ by format). */\nconst FIXED_DEFAULTS: Readonly<Partial<Record<(typeof IGNORABLE_OPTION_NAMES)[number], unknown>>> = {\n nodeIdFrom: \"id\",\n duplicateEdges: \"keep\",\n selfLoops: \"keep\",\n onMixedDirection: \"expand\",\n weightDtype: \"f64\",\n long: \"f64\",\n restoreMangledIds: true,\n hyperedges: \"skip\",\n};\n\n/**\n * Report every common option the caller set to a non-default value that the format has no use\n * for: one\n * `W_OPTION_IGNORED` warning (category `unsupported`) per option, the option name as the element.\n * The builder-policy options are reportSinkOptions()'s and are skipped here.\n * @param options - the caller's raw options, possibly undefined\n * @param report - the report to record into\n * @param used - the common option names the importer reads\n * @returns the number of warnings recorded\n * @category Writing a format\n */\nexport function reportUnusedOptions(\n options: CommonImportOptions | undefined,\n report: ImportReportBuilder,\n used: ReadonlySet<keyof CommonImportOptions>,\n): number {\n if (options === undefined) {\n return 0;\n }\n let recorded = 0;\n for (const name of IGNORABLE_OPTION_NAMES) {\n if (used.has(name) || (SINK_OPTION_NAMES as readonly string[]).includes(name)) {\n continue;\n }\n const value: unknown = options[name];\n if (value === undefined || value === FIXED_DEFAULTS[name]) {\n // an option spelled out at its default changes nothing, whether or not the format reads it\n continue;\n }\n report.warning(\n \"unsupported\",\n OPTION_IGNORED_CODE,\n `option ${name}: ${JSON.stringify(value)} has no effect on the ${report.format} importer`,\n { element: name },\n );\n recorded++;\n }\n return recorded;\n}\n\n/**\n * Whether the caller chose a graph with `graphIndex` or `graphName`. An importer warns\n * W_MULTIPLE_GRAPHS about the graphs it skipped only when it read the first by default.\n * @param options - the caller's graphIndex / graphName\n * @returns true when either option is set\n * @category Writing a format\n */\nexport function graphChosen(options: GraphChoiceOptions | undefined): boolean {\n return options?.graphIndex !== undefined || options?.graphName !== undefined;\n}\n\n/**\n * The graphs of an input as an E_GRAPH_NOT_FOUND message lists them: their indexes and names, so the\n * caller can choose without a second call.\n * @param names - each graph's name, null for an unnamed graph\n * @returns the text to append, starting with \"; \"\n */\nfunction graphNamesText(names: readonly (string | null)[]): string {\n const shown = names.slice(0, 10).map((n, i) => `${i} ${n === null ? \"(unnamed)\" : JSON.stringify(n)}`);\n const more = names.length > shown.length ? `, and ${names.length - shown.length} more` : \"\";\n return `; the file holds ${shown.join(\", \")}${more}`;\n}\n\n/**\n * The graph an importer reads from an input that holds several: the one `graphIndex` or\n * `graphName` names, else the first. A choice that names no graph is E_GRAPH_NOT_FOUND, a name two\n * graphs share is E_AMBIGUOUS_GRAPH_NAME, and an input with no graph at all is E_NO_GRAPH, each\n * fatal (the report fails with ImportError).\n * @param names - each graph's name (null for an unnamed graph), in document order\n * @param options - the caller's graphIndex / graphName\n * @param report - the report a failure is recorded in\n * @returns the index of the graph to read; E_UNSUPPORTED for an option of the wrong type, or both\n * @category Writing a format\n */\nexport function chooseGraph(\n names: readonly (string | null)[],\n options: GraphChoiceOptions | undefined,\n report: ImportReportBuilder,\n): number {\n const { graphIndex, graphName } = options ?? {};\n if (\n graphIndex !== undefined &&\n (typeof graphIndex !== \"number\" || !Number.isInteger(graphIndex) || graphIndex < 0)\n ) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option graphIndex: ${describe(graphIndex)} is not a non-negative integer`,\n {\n option: \"graphIndex\",\n found: graphIndex,\n },\n );\n }\n if (graphName !== undefined && typeof graphName !== \"string\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option graphName: ${describe(graphName)} is not a string`, {\n option: \"graphName\",\n found: graphName,\n });\n }\n if (graphIndex !== undefined && graphName !== undefined) {\n throw new GraphFormatError(\"E_UNSUPPORTED\", \"options graphIndex and graphName both choose a graph; pass one\", {\n option: \"graphName\",\n found: graphName,\n });\n }\n if (names.length === 0) {\n return report.fail(NO_GRAPH_CODE, \"the input holds no graph\");\n }\n if (graphName !== undefined) {\n const matches = names.flatMap((name, i) => (name === graphName ? [i] : []));\n if (matches.length > 1) {\n return report.fail(\n AMBIGUOUS_GRAPH_NAME_CODE,\n `graphName ${JSON.stringify(graphName)} names the graphs at indexes ${matches.join(\", \")}; pass graphIndex`,\n { element: graphName },\n );\n }\n if (matches.length === 0) {\n return report.fail(\n GRAPH_NOT_FOUND_CODE,\n `graphName ${JSON.stringify(graphName)} names none of the ${names.length} graph${plural(names.length)}${graphNamesText(names)}`,\n { element: graphName },\n { names: [...names] },\n );\n }\n return matches[0];\n }\n const index = graphIndex ?? 0;\n if (index >= names.length) {\n return report.fail(\n GRAPH_NOT_FOUND_CODE,\n `graphIndex ${index} is beyond the ${names.length} graph${plural(names.length)}${graphNamesText(names)}`,\n );\n }\n return index;\n}\n\n/**\n * Apply the documented defaults to an importer's common options and check every enum\n * value. Format-specific options in the same object are ignored here.\n * @param options - the caller's options, possibly undefined\n * @param defaults - the importer's per-format defaults\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n * @category Writing a format\n */\nexport function resolveImportOptions(\n options: CommonImportOptions | undefined,\n defaults: ImportFormatDefaults,\n): ResolvedImportOptions {\n const o: CommonImportOptions = options ?? {};\n return Object.freeze({\n ids: enumOption(\"ids\", o.ids, defaults.ids, ID_COERCIONS),\n nodeIdFrom: enumOption(\"nodeIdFrom\", o.nodeIdFrom, \"id\", NODE_ID_SOURCES),\n addMissingNodes: booleanOption(\"addMissingNodes\", o.addMissingNodes, defaults.addMissingNodes ?? true),\n duplicateEdges: enumOption(\"duplicateEdges\", o.duplicateEdges, \"keep\", DUPLICATE_POLICIES),\n selfLoops: enumOption(\"selfLoops\", o.selfLoops, \"keep\", SELF_LOOP_POLICIES),\n onMixedDirection: enumOption(\"onMixedDirection\", o.onMixedDirection, \"expand\", IMPORT_MIXED_POLICIES),\n defaultDirected: booleanOption(\"defaultDirected\", o.defaultDirected, defaults.defaultDirected),\n weightFrom: weightFromOption(o.weightFrom, defaults.weightFrom),\n weightDtype: enumOption(\"weightDtype\", o.weightDtype, \"f64\", WEIGHT_DTYPES),\n long: enumOption(\"long\", o.long, \"f64\", LONG_MODES),\n restoreMangledIds: booleanOption(\"restoreMangledIds\", o.restoreMangledIds, true),\n hyperedges: enumOption(\"hyperedges\", o.hyperedges, \"skip\", HYPEREDGE_POLICIES),\n errorLimit: errorLimitOption(o.errorLimit),\n signal: signalOption(o.signal),\n onProgress: progressOption(o.onProgress),\n encoding: encodingOption(o.encoding),\n });\n}\n\n/**\n * Apply the documented defaults to an exporter's common options and check the enum values.\n * @param options - the caller's options, possibly undefined\n * @returns the resolved options; E_UNSUPPORTED for a value outside its set\n * @category Writing a format\n */\nexport function resolveExportOptions(options: CommonExportOptions | undefined): ResolvedExportOptions {\n const o: CommonExportOptions = options ?? {};\n return Object.freeze({\n sanitizeIds: enumOption(\"sanitizeIds\", o.sanitizeIds, \"error\", SANITIZE_MODES),\n onMixedDirection: enumOption(\"onMixedDirection\", o.onMixedDirection, \"error\", EXPORT_MIXED_POLICIES),\n });\n}\n\n/**\n * Resolve one enum-valued option.\n * @param name - the option name, for the error\n * @param value - the caller's value\n * @param fallback - the default\n * @param allowed - the accepted values\n * @returns the value or the default; E_UNSUPPORTED when outside the set\n */\nfunction enumOption<T extends string>(name: string, value: unknown, fallback: T, allowed: ReadonlySet<string>): T {\n if (value === undefined) {\n return fallback;\n }\n if (typeof value !== \"string\" || !allowed.has(value)) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option ${name}: ${describe(value)} is not one of ${list(allowed)}`,\n {\n option: name,\n found: value,\n supported: [...allowed],\n },\n );\n }\n return value as T;\n}\n\n/**\n * Resolve one boolean option.\n * @param name - the option name, for the error\n * @param value - the caller's value\n * @param fallback - the default\n * @returns the value or the default; E_UNSUPPORTED when not a boolean\n */\nfunction booleanOption(name: string, value: unknown, fallback: boolean): boolean {\n if (value === undefined) {\n return fallback;\n }\n if (typeof value !== \"boolean\") {\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option ${name}: ${describe(value)} is not a boolean`, {\n option: name,\n found: value,\n });\n }\n return value;\n}\n\n/**\n * Resolve the weightFrom option: a non-empty attribute name, null for unweighted, or the format default.\n * @param value - the caller's value\n * @param fallback - the format default\n * @returns the resolved value; E_UNSUPPORTED for anything else\n */\nfunction weightFromOption(value: unknown, fallback: string | null): string | null {\n if (value === undefined) {\n return fallback;\n }\n if (value === null || (typeof value === \"string\" && value.length > 0)) {\n return value;\n }\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option weightFrom: ${describe(value)} is not an attribute name or null`,\n { option: \"weightFrom\", found: value },\n );\n}\n\n/**\n * Resolve the error limit: a non-negative integer or Infinity.\n * @param value - the caller's value\n * @returns the limit or the default\n */\nfunction errorLimitOption(value: unknown): number {\n if (value === undefined) {\n return DEFAULT_ERROR_LIMIT;\n }\n if (typeof value === \"number\" && value >= 0 && (Number.isInteger(value) || value === Infinity)) {\n return value;\n }\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option errorLimit: ${describe(value)} is not a non-negative integer or Infinity`,\n { option: \"errorLimit\", found: value },\n );\n}\n\n/**\n * Resolve the signal option by duck type, so a signal from another realm is accepted.\n * @param value - the caller's value\n * @returns the signal or null\n */\nfunction signalOption(value: unknown): AbortSignal | null {\n if (value === undefined || value === null) {\n return null;\n }\n if (typeof value === \"object\" && typeof (value as { aborted?: unknown }).aborted === \"boolean\") {\n return value as AbortSignal;\n }\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option signal: ${describe(value)} is not an AbortSignal`, {\n option: \"signal\",\n found: typeof value,\n });\n}\n\n/**\n * Resolve the progress callback option.\n * @param value - the caller's value\n * @returns the callback or null\n */\nfunction progressOption(value: unknown): ((bytesDone: number, bytesTotal?: number) => void) | null {\n if (value === undefined || value === null) {\n return null;\n }\n if (typeof value === \"function\") {\n return value as (bytesDone: number, bytesTotal?: number) => void;\n }\n throw new GraphFormatError(\"E_UNSUPPORTED\", `option onProgress: ${describe(value)} is not a function`, {\n option: \"onProgress\",\n found: typeof value,\n });\n}\n\n/**\n * Resolve the encoding option: a label the platform's TextDecoder knows.\n * @param value - the caller's value\n * @returns the canonical encoding name, or null for detection\n */\nfunction encodingOption(value: unknown): string | null {\n if (value === undefined || value === null) {\n return null;\n }\n const canonical = typeof value === \"string\" ? canonicalEncoding(value) : null;\n if (canonical === null) {\n throw new GraphFormatError(\n \"E_UNSUPPORTED\",\n `option encoding: ${describe(value)} is not an encoding this platform's TextDecoder knows`,\n { option: \"encoding\", found: value },\n );\n }\n return canonical;\n}\n\n/**\n * The options to pass to another importer that you hand text you already decoded with readText():\n * the caller's options without `encoding` and `onProgress`, which that text no longer needs. Without\n * this, the other importer would warn that `encoding` has no effect on text, and report progress a\n * second time.\n * @example\n * ```ts\n * const text = await readText(input, report, opts);\n * report.include(await csvImporter.import(text, sink, forDecodedText(options)));\n * ```\n * @param options - the caller's import options\n * @returns a copy without `encoding` and `onProgress`\n * @category Writing a format\n */\nexport function forDecodedText<O extends CommonImportOptions>(options: O | undefined): O {\n return { ...options, encoding: undefined, onProgress: undefined } as O;\n}\n\n/**\n * The dialect names of the built-in formats with a `dialect` save option. One options object shared between formats\n * can hold one format's dialect, and the other formats ignore it, as they ignore any option they do not have.\n */\nexport const FORMAT_DIALECTS: Readonly<Record<string, readonly string[]>> = Object.freeze({\n csv: Object.freeze([\"gephi\", \"generic\"]),\n json: Object.freeze([\"node-link\", \"d3\", \"jgf\", \"cytoscape\", \"graphology\", \"vis\", \"obographs\"]),\n});\n\n/**\n * Whether a `dialect` value names another format's dialect, which this format ignores rather than refuses.\n * @param format - this format\n * @param value - the caller's `dialect` option\n * @returns true for another built-in format's dialect name\n */\nexport function otherFormatDialect(format: string, value: unknown): boolean {\n return (\n typeof value === \"string\" &&\n Object.entries(FORMAT_DIALECTS).some(([f, names]) => f !== format && names.includes(value))\n );\n}\n\n/**\n * A short description of an option value for an error message.\n * @param value - the value\n * @returns the JSON text of a primitive, or the type name otherwise\n * @category Plugin helpers\n */\nexport function describe(value: unknown): string {\n switch (typeof value) {\n case \"string\":\n return JSON.stringify(value);\n case \"number\":\n case \"boolean\":\n return String(value);\n default:\n return value === null ? \"null\" : typeof value;\n }\n}\n\n/**\n * The accepted values of an enum option, for an error message.\n * @param allowed - the set\n * @returns the quoted values joined by commas\n */\nfunction list(allowed: ReadonlySet<string>): string {\n return [...allowed].map((v) => JSON.stringify(v)).join(\", \");\n}\n","/**\n * The io contract types of @graphty/graph-io: what every importer\n * and exporter implements and what every caller of one programs against. They are declared here\n * rather than in the core because ImportInput and CommonImportOptions reference ReadableStream and\n * AbortSignal, which need the DOM lib (or @types/node >= 18); the core's public types reference only\n * ES2020 globals (decision D-IO-TYPES). Every declaration is transcribed verbatim from the design;\n * the behavior behind each option is specified in sections 8.4 (import), 8.5 (export) and 8.6\n * (report and error aggregation).\n */\n\nimport {\n type Dtype,\n type DuplicatePolicy,\n GraphFormatError,\n type GraphSink,\n type GraphSnapshot,\n type IdCoercion,\n} from \"@graphty/graph-format\";\n\n/**\n * What an importer reads: whole text, whole bytes, a byte stream (a browser\n * `File.stream()`, a fetch body) or an async iterable of text or byte chunks. Bytes are decoded\n * strictly in the encoding the `encoding` option, a byte order mark or the file's own declaration\n * names, else as UTF-8 (undeclared bytes that are not UTF-8 are read as windows-1252 with a\n * warning), so an invalid sequence is a parse-error and never a silent U+FFFD that could alias two ids.\n * @category Loading\n */\nexport type ImportInput = string | Uint8Array | ReadableStream<Uint8Array> | AsyncIterable<string | Uint8Array>;\n\n/**\n * The options every importer accepts next to its format-specific ones. The\n * builder-policy fields (`addMissingNodes`, `duplicateEdges`, `selfLoops`, `weightDtype`) seed the\n * registry's builder; on a caller's sink they are read back from `sink.options` and every option the\n * sink cannot honor is reported.\n * @category Loading\n */\nexport interface CommonImportOptions {\n /**\n * How id text becomes a node id. \"canonical\": integer text such as \"42\" becomes the number 42\n * and everything else (\"042\", \"4.2\", \"a\") stays text. \"string\": every id stays text. \"number\":\n * every id is read as a number, so \"042\" and \"42\" become one node (with a warning) and text that\n * is not a number is an error. \"keep\": ids keep the type the file gives them. The default is\n * \"keep\" for JSON, OBO, XGMML, CX, CX2 and Cytoscape sessions, and \"canonical\" for the other\n * formats.\n * @defaultValue per format\n */\n ids?: IdCoercion | undefined;\n /**\n * Which value becomes the node id: the file's own id (\"id\"), the node's label (\"label\"), or its\n * position among the nodes, counting from 0 (\"index\"). Use it when a file's ids are meaningless\n * numbers and the labels are the real names. In d3 JSON, \"index\" also reads edges whose ends are\n * node positions. In GML and Pajek it only renames: every node still needs its id key or vertex\n * number, because edges refer to nodes by it, and a GML node without an `id` key is skipped\n * with E_MISSING_ID. CSV reads this option only when there is a node table, and it does not\n * just rename: with \"label\" the node table's label column (`label`, or the `labelColumn`\n * option) gives the ids, so the edge table must name its nodes by those labels.\n * @defaultValue \"id\"\n */\n nodeIdFrom?: \"id\" | \"label\" | \"index\" | undefined;\n /**\n * Whether an edge may name a node the file never declares; that node is then created. When\n * false, such an edge is skipped as an error. The default is false for GEXF, XGMML, CX, CX2 and\n * Cytoscape sessions, whose files must declare every node, and true for the other formats.\n * @defaultValue per format\n */\n addMissingNodes?: boolean | undefined;\n /**\n * What to do with a second edge between the same two nodes: \"keep\" keeps both, \"first\" / \"last\"\n * keep one, \"sum\" / \"min\" / \"max\" keep one with the weights combined, and \"error\" stops the\n * import. Merged edges are reported once as W_EDGES_MERGED.\n * @defaultValue \"keep\"\n */\n duplicateEdges?: DuplicatePolicy | undefined;\n /**\n * What to do with an edge from a node to itself: \"keep\", \"drop\" (reported once as\n * W_SELF_LOOPS_DROPPED with the number removed), or \"error\" to stop the import.\n * @defaultValue \"keep\"\n */\n selfLoops?: \"keep\" | \"drop\" | \"error\" | undefined;\n /**\n * How edge direction is read. \"expand\" (the default) keeps the file's direction, and for a file\n * that has both directed and undirected edges makes a directed graph in which each undirected\n * edge is two edges, marked so an export can write them back as one. \"directed\" / \"undirected\"\n * read every edge of any file that way, including a file whose edges all have the other\n * direction, with a W_DIRECTION_FORCED warning. \"error\" stops the import at the first edge whose\n * direction differs from the file's.\n * @defaultValue \"expand\"\n */\n onMixedDirection?: \"expand\" | \"directed\" | \"undirected\" | \"error\" | undefined;\n /**\n * Whether the graph is directed when the file does not say. The default is undirected for\n * GraphML, GEXF, GML, JSON and XGMML, and directed for CSV, Pajek and Cytoscape sessions. DOT,\n * Neo4j, CX, CX2 and OBO files always settle the direction themselves (DOT by `graph` or\n * `digraph`; the others are always directed), so those formats do not read this option and\n * report it as W_OPTION_IGNORED.\n * @defaultValue per format\n */\n defaultDirected?: boolean | undefined;\n /**\n * The edge attribute read as the edge weight. The default is \"weight\", except \"value\" for GML\n * and Pajek and none for OBO. Pass null to read every attribute as a plain attribute and leave\n * the graph unweighted. A name no edge has leaves the graph unweighted, with a W_WEIGHT_NOT_FOUND\n * warning: one options object with `weightFrom: \"weight\"` shared across formats reads GML and\n * Pajek files, which keep their weights in `value`, without weights. An edge whose weight cell is\n * empty or missing gets the default weight 1, and an export writes no weight for it.\n * @defaultValue per format\n */\n weightFrom?: string | null | undefined;\n /**\n * Whether weights a 32-bit float cannot hold exactly are also kept exactly. \"f64\" keeps them in\n * an edge column that `snapshot.edges.byRole(\"weight\")` finds, so 0.1 and 16777217\n * stay exact. \"f32\" keeps no such column and uses less memory: the 32-bit weight arrays\n * `snapshot.weights` and `snapshot.edgeList().weights`, which every import fills, are then the\n * only weights.\n * @defaultValue \"f64\"\n */\n weightDtype?: \"f32\" | \"f64\" | undefined;\n /**\n * How a column the file declares as a 64-bit integer is stored: \"f64\" (a number, exact up to\n * 2^53) or \"string\" (every digit kept, as text). GML declares no types, so there it applies to a\n * key whose integer values go beyond 2^53.\n * @defaultValue \"f64\"\n */\n long?: \"f64\" | \"string\" | undefined;\n /**\n * Whether to give back the original ids that an export with `sanitizeIds: \"mangle\"` had to\n * rewrite. The export writes them to the file in an attribute whose name each format page\n * gives. When false, the rewritten ids stay the ids and the originals are an ordinary node\n * attribute, named after the format's spelling: `graphty.originalId` in GraphML,\n * `graphty_originalId` in GML and Pajek, `graphty:originalId` in CX, CX2 and Cytoscape sessions,\n * and a `graphty:originalId` entry of the `property_value` attribute in OBO.\n * @defaultValue true\n */\n restoreMangledIds?: boolean | undefined;\n /**\n * What to do with a GraphML or JGF hyperedge (an edge with more than two ends): \"skip\" leaves it\n * out with a warning, \"error\" stops the import, and \"star\" and \"clique\" turn it into ordinary\n * edges. \"clique\" joins every pair of ends. \"star\" works differently per format: GraphML adds a\n * new hub node joined to every end, while JGF makes the hyperedge's first node the hub, so no\n * node is added. A directed JGF hyperedge (`source` and `target` arrays) becomes one edge from\n * every source to every target under both. In JGF every edge made from a hyperedge carries the\n * hyperedge's id, label, relation and metadata; GraphML drops a hyperedge's `<data>` and its\n * `<desc>`, with a warning.\n * @defaultValue \"skip\"\n */\n hyperedges?: \"error\" | \"skip\" | \"star\" | \"clique\" | undefined;\n /**\n * How many errors an import tolerates. Each error skips one node, edge or value and is listed in\n * the report; one error more than this stops the import with an ImportError. Pass 0 to stop at\n * the first error.\n * @defaultValue 100\n */\n errorLimit?: number | undefined;\n /**\n * Cancels the import. When it aborts, the import stops and rejects with the signal's reason\n * (an AbortError, or a TimeoutError from `AbortSignal.timeout()`).\n */\n signal?: AbortSignal | undefined;\n /**\n * Called as the input is read, with the bytes read so far and the total. For a string or a\n * Uint8Array the total is known from the first call. For a stream (which includes loadFromUrl()\n * and loadFromFile()) it is undefined until the last call, which always has\n * `bytesDone === bytesTotal`. Text counts its UTF-8 length.\n */\n onProgress?: ((bytesDone: number, bytesTotal?: number) => void) | undefined;\n /**\n * The character encoding of byte input, as a label such as \"utf-8\", \"windows-1252\" or\n * \"utf-16le\". Without it graph-io uses a byte order mark, then the encoding the file declares\n * (an XML declaration, DOT's `charset`), then UTF-8, and reads bytes that are not UTF-8 as\n * windows-1252 with a warning. A byte order mark wins over this option. A string input is\n * already text: setting this option for one adds a W_OPTION_IGNORED warning, once per input (a\n * CSV import with a `nodes` table has two inputs).\n */\n encoding?: string | undefined;\n}\n\n/**\n * Which graph to read from a file that holds several: by position or by name, never both. Without\n * either, the first graph is read.\n * @category Loading\n */\nexport interface GraphChoiceOptions {\n /**\n * The 0-based position of the graph to read from a file that holds several (`listGraphs()`\n * gives each graph's `index`). A position past the last graph stops the import with an\n * ImportError whose `issue.code` is E_GRAPH_NOT_FOUND; its message lists the file's graphs.\n * @defaultValue 0\n */\n graphIndex?: number | undefined;\n /**\n * The name of the graph to read from a file that holds several (`listGraphs()` gives each\n * graph's `name`). A name no graph has stops the import with an ImportError whose `issue.code`\n * is E_GRAPH_NOT_FOUND, and a name two graphs share with one whose `issue.code` is\n * E_AMBIGUOUS_GRAPH_NAME.\n */\n graphName?: string | undefined;\n}\n\n/**\n * One graph of an input that can hold several, as an importer's listGraphs() describes it.\n * @category Loading\n */\nexport interface GraphListing {\n /** The value graphIndex takes to read it. */\n readonly index: number;\n /** The value graphName takes to read it, or null when the graph has no name. */\n readonly name: string | null;\n /** The node count when the input states it cheaply, else null. */\n readonly nodes: number | null;\n /** The edge count when the input states it cheaply, else null. */\n readonly edges: number | null;\n}\n\n/**\n * A format's reader. It reads the input once, from start to end, and adds each node, edge and\n * attribute to a graph builder (`GraphSink`, which `GraphBuilder` from @graphty/graph-format\n * implements); importGraph() then builds the snapshot. Register one with\n * `registry.registerImporter()` to teach graph-io a new format. `Opts` is the type of the format's\n * own options.\n * @category Writing a format\n */\nexport interface GraphImporter<Opts = unknown> {\n /** The name callers pass as `format`, such as \"graphml\" or \"csv\"; lowercase, unique in a registry. */\n readonly format: string;\n /** File extensions with the leading dot, most common first; used to detect the format from a file name. */\n readonly extensions: readonly string[];\n /** MIME types the format is served as, most specific first; used to detect the format from a Content-Type. */\n readonly mimeTypes: readonly string[];\n /**\n * The names of this format's own options (`[\"separator\"]`), when you want misspelled options\n * reported. While every registered importer lists its options, a load warns `W_UNKNOWN_OPTION` for\n * a name that no format and no load function takes. Leave it out to report nothing.\n */\n readonly options?: readonly string[] | undefined;\n /**\n * How sure you are that `head`, the first bytes of a file (up to SNIFF_HEAD_BYTES), is this\n * format: 0 for no, up to 1 for certain. Below 0.5 counts as a weak guess, which loses to a\n * file extension another format claims. Without this method the format is detected only by\n * file name and MIME type.\n * @param head - the first bytes of the input\n * @returns a confidence in 0..1\n */\n sniff?(head: Uint8Array): number;\n /**\n * Read `input` into `sink`. Record each problem in an ImportReportBuilder and skip the element;\n * the builder throws ImportError once there are more errors than `errorLimit`.\n * @param input - the text, bytes or stream to read\n * @param sink - the graph builder to add nodes, edges and attributes to\n * @param options - the format's own options and the options every importer takes\n * @returns the import report (`report.finish()`)\n */\n import(input: ImportInput, sink: GraphSink, options?: Opts & CommonImportOptions): Promise<ImportReport>;\n /**\n * Optional, for a format whose files can hold several graphs: read every graph, each into its\n * own sink. importAllGraphs() calls it; with it, importGraph() also honors `graphIndex` and\n * `graphName` by reading every graph and returning the chosen one. `import()` should read the\n * first graph and warn W_MULTIPLE_GRAPHS with the number skipped.\n * @param input - the text, bytes or stream to read\n * @param sinkFor - called once per graph, in document order, before that graph's first push\n * @param options - format-specific and common options\n * @returns one report per graph, in document order\n */\n importAll?(\n input: ImportInput,\n sinkFor: (index: number) => GraphSink,\n options?: Opts & CommonImportOptions,\n ): Promise<ImportReport[]>;\n /**\n * Optional: list the graphs of a file without importing them, so a caller can offer a choice.\n * listGraphs() calls it. An importer that has it must also honor `graphIndex` and `graphName`\n * in `import()` itself (`chooseGraph()` resolves them); one without it lists nothing and the\n * registry makes the choice through `importAll()`.\n * @param input - the text, bytes or stream to read\n * @param options - format-specific and common options\n * @returns one listing per graph, in document order\n */\n listGraphs?(input: ImportInput, options?: Opts & CommonImportOptions): Promise<readonly GraphListing[]>;\n}\n\n/**\n * What a format can store exactly. checkCapabilities() compares a graph against it and returns a\n * loss note for everything the graph has that the format cannot store.\n * @category Saving\n */\nexport interface ExportCapabilities {\n /**\n * Whether one file can hold directed and undirected edges together. When false, a graph with both\n * needs `onMixedDirection` (\"directed\" or \"undirected\") to be saved.\n */\n readonly mixedDirection: boolean;\n /**\n * Whether the file can hold two edges between the same pair of nodes. When false, the extra\n * edges are not kept as separate edges.\n */\n readonly multiEdges: boolean;\n /** Whether the file can hold an edge from a node to itself. When false, such edges are lost. */\n readonly selfLoops: boolean;\n /**\n * Whether edges carry ids. \"required\": every edge has one, and ids are made up (e0, e1, ...)\n * for a graph without them. \"optional\": edge ids are written when the graph has them. \"none\":\n * edge ids are lost.\n */\n readonly edgeIds: \"required\" | \"optional\" | \"none\";\n /**\n * Which node ids the format can write as they are: \"any\"; \"nmtoken\" (XML name tokens: letters,\n * digits and `. - _ :`, no spaces); \"integer\"; or \"dense-1-based\" (the nodes are always numbered\n * 1 to N). Other ids need `sanitizeIds: \"mangle\"`, which writes the original ids too.\n */\n readonly idCharset: \"any\" | \"nmtoken\" | \"integer\" | \"dense-1-based\";\n /**\n * The attribute types the format keeps exactly: \"bool\", \"i32\" (32-bit integer), \"u32\" (unsigned\n * 32-bit integer), \"u8\" (byte), \"f32\" (32-bit float), \"f64\" (64-bit float, a JavaScript number),\n * \"string\" (text), \"dict\" (text stored as a dictionary of repeated values), \"list\" and \"json\".\n * An attribute of another type is written as the nearest one the format has, with a\n * W_DTYPE_UNSUPPORTED note.\n */\n readonly dtypes: readonly Dtype[];\n /**\n * Whether an attribute other than the node position can hold several numbers per node or edge\n * (a vector). When false, such an attribute does not read back as one attribute. Positions\n * are covered by `positions`.\n */\n readonly components: boolean;\n /** Whether an attribute can hold a list per node or edge. When false, list attributes are lost or flattened. */\n readonly lists: boolean;\n /**\n * Whether an attribute can hold nested JSON objects and arrays. When false, such attributes are\n * written as text or lost.\n */\n readonly json: boolean;\n /** Whether the file can declare a default value for an attribute. When false, declared defaults are lost. */\n readonly defaults: boolean;\n /**\n * Whether the file can declare the allowed values of an attribute (GEXF `options`). When false,\n * those declarations are lost; the values themselves are kept.\n */\n readonly options: boolean;\n /** Whether nodes can sit inside other nodes (groups or clusters). When false, the nesting is lost. */\n readonly hierarchy: boolean;\n /**\n * What the file can say about time: \"none\" (time attributes are lost), \"intervals\" (one start\n * and end per element), \"spells\" (several intervals per element), or \"dynamic-values\"\n * (attribute values that change over time, as well).\n */\n readonly temporal: \"none\" | \"intervals\" | \"spells\" | \"dynamic-values\";\n /** Whether the file can hold attributes of the graph itself. When false, graph attributes are lost. */\n readonly graphAttributes: boolean;\n /** Whether the file can hold node positions. When false, the layout is lost. */\n readonly positions: boolean;\n /** Whether the file can hold node and edge color, size, shape and thickness. When false, they are lost. */\n readonly viz: boolean;\n}\n\n/**\n * One thing a format cannot store, returned by checkExport() before anything is written.\n * @category Saving\n */\nexport interface LossNote {\n /**\n * A stable code. Codes starting with \"E_\" mean the export refuses to write under these options:\n * the exportGraph*() functions throw a GraphFormatError. Codes starting with \"W_\" mean the file\n * is written, but this part will not read back the same.\n */\n readonly code: string;\n /** A plain-English explanation. */\n readonly message: string;\n /** The attribute column involved, or null. */\n readonly column: string | null;\n /** How many nodes, edges or values are affected, or null when not counted. */\n readonly count: number | null;\n /**\n * Every column the note is about, when it is about several and `column` is null: the graph\n * attributes a format has no place for, or the node columns a CSV edge table leaves for the node\n * table. Absent on a note about one column or none.\n */\n readonly columns?: readonly string[];\n}\n\n/**\n * The options every exporter accepts next to its format-specific ones.\n * @category Saving\n */\nexport interface CommonExportOptions {\n /**\n * What to do with node ids the format cannot hold. \"error\": the export throws (E_INVALID_ID) and\n * no node is renamed. \"mangle\": such ids are rewritten and the originals are written to the\n * file too, so an import with `restoreMangledIds` (on by default) reads the original ids back.\n * Pajek is the exception: it always numbers nodes 1 to N and keeps the old ids as labels.\n * @defaultValue \"error\"\n */\n sanitizeIds?: \"error\" | \"mangle\" | undefined;\n /**\n * What a format with one direction per file does with a graph that has both directed and\n * undirected edges: \"error\" throws (E_DIRECTED), \"directed\" writes every edge as directed (an\n * undirected edge once, as one directed edge), \"undirected\" writes the whole graph undirected.\n * A format that keeps each edge's own direction (GraphML, GEXF, CSV, Pajek, XGMML, Cytoscape\n * sessions, and the JGF and graphology JSON dialects) ignores it and writes both kinds; to make\n * such a graph all one direction, read it with the import option `onMixedDirection`.\n * @defaultValue \"error\"\n */\n onMixedDirection?: \"error\" | \"directed\" | \"undirected\" | undefined;\n}\n\n/**\n * A format's writer. Register one with `registry.registerExporter()` to make a format writable by\n * every save function. `Opts` is the type of the format's own options.\n * @category Writing a format\n */\nexport interface GraphExporter<Opts = unknown> {\n /** The name callers pass as `format`; the same as the importer's when the format has both. */\n readonly format: string;\n /** What the format can store exactly; build it with `capabilities()`. */\n readonly capabilities: ExportCapabilities;\n /**\n * File extensions with the leading dot, most common first. Optional; listFormats() uses them\n * when no importer is registered for the format (an export-only format).\n */\n readonly extensions?: readonly string[] | undefined;\n /** MIME types, most specific first. Optional; used the same way as `extensions`. */\n readonly mimeTypes?: readonly string[] | undefined;\n /**\n * The names of this format's own save options, when you want misspelled options reported. While\n * every registered exporter lists its options, `checkExport()` returns a `W_UNKNOWN_OPTION` note for\n * a name that no format and no save function takes. Leave it out to report nothing.\n */\n readonly options?: readonly string[] | undefined;\n /**\n * What a save would lose, without writing anything; checkExport(snapshot, format, options) calls\n * it. Start from `checkCapabilities(snapshot, capabilities, resolveExportOptions(options))` and\n * add a note for anything else your format changes. Every difference a re-import would show\n * needs a note.\n * @param snapshot - the graph to check\n * @param options - the format's own options and the options every exporter takes\n * @returns the loss notes, empty when the save is exact\n */\n check(snapshot: GraphSnapshot, options?: Opts & CommonExportOptions): readonly LossNote[];\n /**\n * Write the graph as byte chunks: UTF-8 text for a text format (`encodeChunks()` turns string\n * parts into them), the raw bytes for a binary one. Throw a GraphFormatError before writing for\n * every \"E_\" note `check()` returns.\n * @param snapshot - the graph to write\n * @param options - the format's own options and the options every exporter takes\n * @returns the file's bytes, chunk by chunk\n */\n export(snapshot: GraphSnapshot, options?: Opts & CommonExportOptions): AsyncIterable<Uint8Array>;\n /**\n * Write the graph as one string (`joinText()` joins string parts). A binary format rejects with\n * a GraphFormatError E_UNSUPPORTED (`details.reason` \"binary\") instead.\n * @param snapshot - the graph to write\n * @param options - the format's own options and the options every exporter takes\n * @returns the whole file\n */\n exportToString(snapshot: GraphSnapshot, options?: Opts & CommonExportOptions): Promise<string>;\n}\n\n/**\n * What an ImportIssue is about, for errors and warnings alike: \"parse-error\" (the text is not valid\n * in the format), \"missing-value\" (something required is absent), \"validation-error\" (a value\n * breaks a rule of the format or of an option; an error skips it, a warning keeps it as written,\n * such as an id with spaces around it), \"unsupported\" (the file uses something graph-io does not\n * read), \"precision\" (a value was rounded), \"coercion\" (a value or id changed type) and \"merged\"\n * (elements were combined). Sort by `severity` first: the category says what kind of problem it\n * is, not how serious.\n * @category Reports and errors\n */\nexport type IssueCategory =\n \"parse-error\" | \"missing-value\" | \"validation-error\" | \"unsupported\" | \"precision\" | \"coercion\" | \"merged\";\n\n/**\n * One problem found while importing.\n * @category Reports and errors\n */\nexport interface ImportIssue {\n /** The category. */\n readonly category: IssueCategory;\n /**\n * \"error\": the element was skipped and counts toward `errorLimit`. \"warning\": the element was\n * kept, possibly changed.\n */\n readonly severity: \"error\" | \"warning\";\n /** A stable code; E_ for errors, W_ for warnings (\"E_UNKNOWN_NODE\", \"W_WIDENED\"). */\n readonly code: string;\n /** A plain-English message. */\n readonly message: string;\n /** The 1-based line in the input, or null when the format has no lines or it is unknown. */\n readonly line: number | null;\n /** The node id, edge id or attribute name involved, or null. */\n readonly element: string | null;\n}\n\n/**\n * What happened while a file was read.\n * @category Reports and errors\n */\nexport interface ImportReport {\n /** The format the file was read as. */\n readonly format: string;\n /**\n * What was read from the file, counted before the graph was built: `duplicateEdges` and\n * `selfLoops: \"drop\"` can leave fewer edges in the snapshot (each reported as a warning).\n */\n readonly counts: {\n /** Nodes read. */\n readonly nodes: number;\n /** Edges read (an undirected edge expanded into two by onMixedDirection \"expand\" counts twice). */\n readonly edges: number;\n /** Nodes skipped after an error. */\n readonly skippedNodes: number;\n /** Edges skipped after an error. */\n readonly skippedEdges: number;\n /** Undirected edges expanded into two by onMixedDirection \"expand\". */\n readonly expandedMixed: number;\n };\n /** Every issue recorded, in order. */\n readonly issues: readonly ImportIssue[];\n /** Issues with severity \"error\". */\n readonly errorCount: number;\n /** Issues with severity \"warning\". */\n readonly warningCount: number;\n /** Whether the error limit was reached and the import aborted. */\n readonly truncated: boolean;\n /** What the importer could not represent. */\n readonly lossy: readonly LossNote[];\n /** How long reading the file took, in milliseconds. */\n readonly durationMs: number;\n}\n\n/**\n * What a load function throws when the input cannot be read: the URL failed, the format was not\n * recognized, the file could not be parsed, or there were more errors than `errorLimit`. It is a\n * GraphFormatError with code \"E_IMPORT\"; `issue` says what stopped the import and `report` holds\n * everything recorded until then.\n * @category Reports and errors\n */\nexport class ImportError extends GraphFormatError {\n /**\n * Everything recorded up to the point the import stopped. It is not an enumerable property, so an\n * uncaught ImportError prints its message and issue, not every issue of the report.\n */\n declare readonly report: ImportReport;\n\n /**\n * The issue that stopped the import: its `code` (\"E_FETCH\", \"E_UNKNOWN_FORMAT\", \"E_CSV_UNCLOSED_QUOTE\", ...),\n * `message`, `line` and `element`. This is the value to branch on; `code` is always \"E_IMPORT\".\n * Null only for an ImportError constructed by hand without a matching issue.\n * @example\n * ```ts\n * if (err instanceof ImportError) {\n * switch (err.issue?.code) {\n * case \"E_FETCH\":\n * console.error(`download failed with status ${String(err.details.status)}`);\n * break;\n * case \"E_UNKNOWN_FORMAT\":\n * console.error(\"not a graph file this library can read; pass `format`\");\n * break;\n * default:\n * console.error(err.message);\n * }\n * }\n * ```\n */\n readonly issue: ImportIssue | null;\n\n /**\n * Create an ImportError.\n * @param message - a plain-ASCII human-readable message\n * @param report - the partial report\n * @param details - optional machine-readable context; its `code` names the issue that stopped the import\n */\n constructor(message: string, report: ImportReport, details?: Readonly<Record<string, unknown>>) {\n super(\"E_IMPORT\", message, details);\n this.name = \"ImportError\";\n Object.defineProperty(this, \"report\", { value: report, enumerable: false });\n this.issue = stoppingIssue(report.issues, details?.code);\n }\n}\n\n/**\n * The issue that stopped an import: the last one with the code the abort named, else the last error.\n * @param issues - the report's issues\n * @param code - the code the abort named, if any\n * @returns the issue, or null\n */\nfunction stoppingIssue(issues: readonly ImportIssue[], code: unknown): ImportIssue | null {\n let lastError: ImportIssue | null = null;\n for (let i = issues.length - 1; i >= 0; i--) {\n if (issues[i].code === code) {\n return issues[i];\n }\n if (lastError === null && issues[i].severity === \"error\") {\n lastError = issues[i];\n }\n }\n return lastError;\n}\n","/**\n * The ImportReport builder shared by every importer (design section 8.6): issues by category and\n * severity, the element counts, loss notes, the error limit with truncation, and the ImportError\n * that carries the partial report when the importer aborts.\n *\n * Layering (design section 8.6): the builder throws on the first hard error; the importer catches\n * per element, records an ImportIssue through this class, skips the element and continues until the\n * error limit, then aborts with E_IMPORT. `recordError()` is that catch: it turns a thrown\n * GraphFormatError into an issue with the category its code implies and re-throws everything else\n * (an ImportError, an abort reason, a programming error) unchanged.\n */\n\nimport { GraphFormatError, type GraphFormatErrorCode } from \"@graphty/graph-format\";\n\nimport { ImportError, type ImportIssue, type ImportReport, type IssueCategory, type LossNote } from \"../types.js\";\nimport { ISSUES_SUPPRESSED_CODE } from \"./codes.js\";\nimport { agree, plural } from \"./plural.js\";\n\n/**\n * The most warnings of one code a report keeps; later ones are counted in one W_ISSUES_SUPPRESSED\n * warning, so a per-row warning on a large file cannot grow the report without bound.\n * @category Issue and loss codes\n */\nexport const MAX_WARNINGS_PER_CODE = 1000;\n\n/**\n * Where an issue was found: the 1-based line and the element (id or attribute name) when known.\n * @public\n * @category Writing a format\n */\nexport interface IssueLocation {\n /** The 1-based source line, when known. */\n readonly line?: number | null | undefined;\n /** The element the issue is about, when known. */\n readonly element?: string | null | undefined;\n}\n\n/**\n * The mutable element counters of a report in progress; importers increment them directly in hot loops.\n * @public\n * @category Plugin helpers\n */\nexport interface MutableCounts {\n /** Nodes pushed. */\n nodes: number;\n /** Logical edges pushed (both halves of an expanded edge count). */\n edges: number;\n /** Nodes skipped after an error. */\n skippedNodes: number;\n /** Edges skipped after an error. */\n skippedEdges: number;\n /** Source edges expanded into two logical edges. */\n expandedMixed: number;\n}\n\n/**\n * The category a thrown GraphFormatError maps to when an importer catches it per element: the codes\n * that mean \"the file referenced something it never declared\" are missing values, a refused\n * direction change is a coercion, size and feature limits are unsupported, everything else is a\n * validation error of the element.\n */\nconst CATEGORY_BY_CODE: Readonly<Partial<Record<GraphFormatErrorCode, IssueCategory>>> = {\n E_UNKNOWN_NODE: \"missing-value\",\n E_UNKNOWN_COLUMN: \"missing-value\",\n E_DIRECTED: \"coercion\",\n E_TOO_LARGE: \"unsupported\",\n E_UNSUPPORTED: \"unsupported\",\n E_GPU_INELIGIBLE: \"unsupported\",\n};\n\n/**\n * No longer recorded; kept so code that switches on older reports still compiles.\n * @category Issue and loss codes\n */\nexport const PARSE_ERROR_CODE = \"E_PARSE\";\n\n/**\n * An issue message as the ImportError shows it: prefixed with its line when the issue has one and the\n * message does not already name it.\n * @param message - the issue message\n * @param where - the issue location\n * @returns the message for the error\n */\nfunction withLine(message: string, where: IssueLocation | undefined): string {\n const line = where?.line;\n return line === undefined || line === null || message.includes(`line ${line}`)\n ? message\n : `line ${line}: ${message}`;\n}\n\n/**\n * Accumulates an ImportReport while an importer runs. Errors count toward the error limit; the\n * error that takes the count beyond the limit is still recorded, `truncated` is set, and an\n * ImportError carrying the report so far is thrown. Warnings never abort.\n * @category Writing a format\n */\nexport class ImportReportBuilder {\n /** The importer's format name. */\n readonly format: string;\n\n /** The error limit the report was created with. */\n readonly errorLimit: number;\n\n /** The element counters; importers increment them directly. */\n readonly counts: MutableCounts = { nodes: 0, edges: 0, skippedNodes: 0, skippedEdges: 0, expandedMixed: 0 };\n\n private readonly issueList: ImportIssue[] = [];\n\n private readonly lossList: LossNote[] = [];\n\n private readonly onceCodes = new Set<string>();\n\n /** Warnings recorded per code, kept or not. */\n private readonly warningsByCode = new Map<string, number>();\n\n private errors = 0;\n\n private warnings = 0;\n\n private truncatedFlag = false;\n\n private readonly startedAt: number;\n\n /**\n * Create a builder for one import call.\n * @param format - the importer's format name\n * @param errorLimit - errors tolerated before the import aborts; Infinity for no limit\n */\n constructor(format: string, errorLimit: number) {\n this.format = format;\n this.errorLimit = errorLimit;\n this.startedAt = now();\n }\n\n /**\n * Issues recorded so far, in order.\n * @returns the live list (not copied; finish() copies)\n */\n get issues(): readonly ImportIssue[] {\n return this.issueList;\n }\n\n /**\n * Issues with severity \"error\".\n * @returns the count\n */\n get errorCount(): number {\n return this.errors;\n }\n\n /**\n * Issues with severity \"warning\".\n * @returns the count\n */\n get warningCount(): number {\n return this.warnings;\n }\n\n /**\n * Whether the error limit was exceeded.\n * @returns true once an error beyond the limit was recorded\n */\n get truncated(): boolean {\n return this.truncatedFlag;\n }\n\n /**\n * Loss notes recorded so far.\n * @returns the live list\n */\n get lossy(): readonly LossNote[] {\n return this.lossList;\n }\n\n /**\n * Record an error. When the count goes beyond the error limit the report is marked truncated\n * and an ImportError carrying it is thrown; the importer does not catch that.\n * @param category - the issue category\n * @param code - a stable code such as \"E_UNKNOWN_NODE\"\n * @param message - a plain-ASCII message\n * @param where - the line and element, when known\n * @returns the recorded issue\n */\n error(category: IssueCategory, code: string, message: string, where?: IssueLocation): ImportIssue {\n const issue = makeIssue(category, \"error\", code, message, where);\n this.issueList.push(issue);\n this.errors++;\n if (this.errors > this.errorLimit) {\n this.truncatedFlag = true;\n throw this.abort(`error limit of ${this.errorLimit} exceeded: ${withLine(message, where)}`, {\n code,\n limit: this.errorLimit,\n });\n }\n return issue;\n }\n\n /**\n * Record a warning; warnings never count toward the limit. Beyond MAX_WARNINGS_PER_CODE\n * warnings of one code the warning is counted, not kept: finish() adds one W_ISSUES_SUPPRESSED\n * warning per such code with the number dropped.\n * @param category - the issue category\n * @param code - a stable code such as \"W_WIDENED\"\n * @param message - a plain-ASCII message\n * @param where - the line and element, when known\n * @returns the recorded issue\n */\n warning(category: IssueCategory, code: string, message: string, where?: IssueLocation): ImportIssue {\n const issue = makeIssue(category, \"warning\", code, message, where);\n const seen = (this.warningsByCode.get(code) ?? 0) + 1;\n this.warningsByCode.set(code, seen);\n if (seen <= MAX_WARNINGS_PER_CODE) {\n this.issueList.push(issue);\n this.warnings++;\n }\n return issue;\n }\n\n /**\n * Record a warning the first time its key is seen and ignore later repeats, for conditions\n * that would otherwise produce one warning per row.\n * @param category - the issue category\n * @param code - the stable code\n * @param message - a plain-ASCII message\n * @param where - the line and element, when known\n * @param key - what repeats are deduplicated on; the code itself by default (pass `code:column`\n * to warn once per column)\n * @returns the recorded issue, or null when the key was already recorded\n */\n warnOnce(\n category: IssueCategory,\n code: string,\n message: string,\n where?: IssueLocation,\n key: string = code,\n ): ImportIssue | null {\n if (this.onceCodes.has(key)) {\n return null;\n }\n this.onceCodes.add(key);\n return this.warning(category, code, message, where);\n }\n\n /**\n * Record something the importer could not represent.\n * @param code - a stable code\n * @param message - a plain-ASCII message\n * @param column - the affected column, or null\n * @param count - the affected count, or null\n * @returns the recorded note\n */\n loss(code: string, message: string, column: string | null = null, count: number | null = null): LossNote {\n const note: LossNote = Object.freeze({ code, message, column, count });\n this.lossList.push(note);\n return note;\n }\n\n /**\n * The per-element catch: record a thrown GraphFormatError as an error\n * issue with its code and the category the code implies. Anything else is re-thrown unchanged:\n * an ImportError (the import already aborted), an abort reason, and any other thrown value (a\n * TypeError or RangeError is a programming error in the importer or the sink, never a defect of\n * the input, so it must surface as an exception rather than as an issue blamed on the file).\n * @param err - the thrown value\n * @param where - the line and element, when known\n * @returns the recorded issue\n */\n recordError(err: unknown, where?: IssueLocation): ImportIssue {\n if (err instanceof GraphFormatError && !(err instanceof ImportError)) {\n const category = CATEGORY_BY_CODE[err.code] ?? \"validation-error\";\n if (err.code === \"E_TOO_LARGE\") {\n // the sink is full: every later element would fail the same way, so the import stops\n return this.failWith(category, err.code, err.message, where);\n }\n return this.error(category, err.code, err.message, where);\n }\n throw err;\n }\n\n /**\n * A builder that starts from everything this one recorded so far: issues, counts, loss notes,\n * the warnOnce keys, the error and warning counts and the truncation flag. For an importer that\n * reads its input once and emits several graphs, each with its own report.\n * @returns the copy; later records to either builder do not reach the other\n */\n fork(): ImportReportBuilder {\n const copy = new ImportReportBuilder(this.format, this.errorLimit);\n Object.assign(copy.counts, this.counts);\n copy.issueList.push(...this.issueList);\n copy.lossList.push(...this.lossList);\n for (const key of this.onceCodes) {\n copy.onceCodes.add(key);\n }\n for (const [code, count] of this.warningsByCode) {\n copy.warningsByCode.set(code, count);\n }\n copy.errors = this.errors;\n copy.warnings = this.warnings;\n copy.truncatedFlag = this.truncatedFlag;\n return copy;\n }\n\n /**\n * Build the ImportError that aborts the import, carrying the report so far. The caller throws\n * it; this method only constructs it so it can be used in expression position.\n * @param message - a plain-ASCII message\n * @param details - optional machine-readable context\n * @returns the error to throw\n */\n abort(message: string, details?: Readonly<Record<string, unknown>>): ImportError {\n return new ImportError(message, this.finish(), details);\n }\n\n /**\n * Record a fatal parse error and abort: the issue is recorded (even beyond the error limit)\n * and an ImportError carrying the report is thrown.\n * @param code - a stable code such as \"E_INVALID_UTF8\"\n * @param message - a plain-ASCII message\n * @param where - the line and element, when known\n * @param details - optional machine-readable context for the ImportError\n */\n fail(code: string, message: string, where?: IssueLocation, details?: Readonly<Record<string, unknown>>): never {\n this.failWith(\"parse-error\", code, message, where, details);\n }\n\n /**\n * Record a fatal error of any category and abort, as fail() does for a parse error: a size\n * limit (E_TOO_LARGE, category \"unsupported\") stops an import without being a defect of the\n * syntax. A fatal error is not the error limit: `truncated` stays as it was.\n * @param category - the issue category\n * @param code - a stable code\n * @param message - a plain-ASCII message\n * @param where - the line and element, when known\n * @param details - optional machine-readable context for the ImportError\n */\n failWith(\n category: IssueCategory,\n code: string,\n message: string,\n where?: IssueLocation,\n details?: Readonly<Record<string, unknown>>,\n ): never {\n this.issueList.push(makeIssue(category, \"error\", code, message, where));\n this.errors++;\n throw this.abort(withLine(message, where), { code, ...details });\n }\n\n /**\n * Add a finished report to this one: its issues after the ones recorded so far, its counts, its\n * loss notes and its truncated flag. Use it when an importer hands part of the work to another\n * importer (for example, a wrapper around the CSV importer) and returns one report for both. The issues\n * keep their codes; errors added this way do not count toward this report's error limit, since\n * the other importer applied its own.\n * @param report - the report the other importer returned\n * @returns this builder\n * @example\n * ```ts\n * const report = new ImportReportBuilder(\"netscope\", opts.errorLimit);\n * const text = await readText(input, report, opts);\n * report.include(await csvImporter.import(text, sink, options));\n * return report.finish();\n * ```\n */\n include(report: ImportReport): this {\n for (const issue of report.issues) {\n this.issueList.push(issue);\n if (issue.severity === \"error\") {\n this.errors++;\n } else {\n this.warnings++;\n }\n }\n const { counts } = report;\n this.counts.nodes += counts.nodes;\n this.counts.edges += counts.edges;\n this.counts.skippedNodes += counts.skippedNodes;\n this.counts.skippedEdges += counts.skippedEdges;\n this.counts.expandedMixed += counts.expandedMixed;\n this.lossList.push(...report.lossy);\n this.truncatedFlag ||= report.truncated;\n return this;\n }\n\n /**\n * The report as it stands: a frozen snapshot with the parse duration so far. Can be called more\n * than once; each call reflects everything recorded up to that point.\n * @returns the report\n */\n finish(): ImportReport {\n const { nodes, edges, skippedNodes, skippedEdges, expandedMixed } = this.counts;\n const suppressed: ImportIssue[] = [];\n for (const [code, seen] of this.warningsByCode) {\n if (seen > MAX_WARNINGS_PER_CODE) {\n const more = seen - MAX_WARNINGS_PER_CODE;\n suppressed.push(\n makeIssue(\n \"unsupported\",\n \"warning\",\n ISSUES_SUPPRESSED_CODE,\n `${more} more ${code} warning${plural(more)} ${agree(more, \"was\", \"were\")} not kept (the report keeps the first ${MAX_WARNINGS_PER_CODE})`,\n { element: code },\n ),\n );\n }\n }\n return Object.freeze({\n format: this.format,\n counts: Object.freeze({ nodes, edges, skippedNodes, skippedEdges, expandedMixed }),\n issues: Object.freeze([...this.issueList, ...suppressed]),\n errorCount: this.errors,\n warningCount: this.warnings + suppressed.length,\n truncated: this.truncatedFlag,\n lossy: Object.freeze([...this.lossList]),\n durationMs: now() - this.startedAt,\n });\n }\n}\n\n/**\n * Build a frozen ImportIssue.\n * @param category - the category\n * @param severity - error or warning\n * @param code - the stable code\n * @param message - the message\n * @param where - the location, when known\n * @returns the issue\n */\nfunction makeIssue(\n category: IssueCategory,\n severity: \"error\" | \"warning\",\n code: string,\n message: string,\n where: IssueLocation | undefined,\n): ImportIssue {\n return Object.freeze({\n category,\n severity,\n code,\n message,\n line: where?.line ?? null,\n element: where?.element ?? null,\n });\n}\n\n/**\n * Whether a thrown value means the load was cancelled rather than failed: the DOMException or Error\n * named \"AbortError\" that an aborted signal throws, or the \"TimeoutError\" of AbortSignal.timeout().\n * Use it in a `catch` block to say nothing to the user when they cancelled. A custom reason passed\n * to `abort(reason)` is not recognized: compare it with `signal.reason`. A format plugin uses it to\n * let a cancellation through untouched.\n * @param err - the thrown value\n * @returns true for an AbortError or a TimeoutError\n * @example\n * ```ts\n * try {\n * await loadFromUrl(url, { signal });\n * } catch (err) {\n * if (!isAbortError(err)) {\n * showError(err);\n * }\n * }\n * ```\n * @category Reports and errors\n */\nexport function isAbortError(err: unknown): boolean {\n if (typeof err !== \"object\" || err === null) {\n return false;\n }\n const { name } = err as { name?: unknown };\n return name === \"AbortError\" || name === \"TimeoutError\";\n}\n\n/**\n * The message of a thrown value: an Error's message, a string as is, anything else described by type.\n * @param err - the thrown value\n * @returns a plain message\n * @category Plugin helpers\n */\nexport function messageOf(err: unknown): string {\n if (err instanceof Error) {\n return err.message;\n }\n if (typeof err === \"string\") {\n return err;\n }\n return `non-error thrown (${typeof err})`;\n}\n\n/**\n * A monotonic millisecond clock: performance.now() where it exists, Date.now() otherwise.\n * @returns milliseconds\n */\nfunction now(): number {\n return typeof performance === \"object\" ? performance.now() : Date.now();\n}\n","/**\n * Number formatting for exporters (design section 3.7): an f32 weight is written as the shortest\n * decimal that round-trips through Math.fround (never `String(x)`, which prints\n * 0.10000000149011612 for a stored 0.1); an f64 value as its shortest JS text; a GML real always\n * with a decimal point so the dtype survives (design section 8.5); non-finite values as the\n * spelling the target syntax accepts.\n */\n\nimport type { NodeId } from \"@graphty/graph-format\";\n\n/**\n * The shortest decimal text that reads back to the same f32 value through Math.fround.\n * @param value - an f32 value (a JS number holding one)\n * @returns the text; \"Infinity\" / \"-Infinity\" / \"NaN\" for the non-finite values, \"-0\" for negative zero\n * @category Plugin helpers\n */\nexport function formatF32(value: number): string {\n if (!Number.isFinite(value)) {\n return String(value);\n }\n if (value === 0) {\n return Object.is(value, -0) ? \"-0\" : \"0\";\n }\n for (let digits = 1; digits <= 9; digits++) {\n const text = value.toPrecision(digits);\n if (Math.fround(Number(text)) === value) {\n return String(Number(text));\n }\n }\n return String(value);\n}\n\n/**\n * The shortest text of an f64 value: `String(x)`, which is already the shortest round-tripping\n * decimal in JS, except for negative zero, which `String()` writes as \"0\".\n * @param value - the value\n * @returns the text; \"Infinity\" / \"-Infinity\" / \"NaN\" for the non-finite values, \"-0\" for negative zero\n * @category Plugin helpers\n */\nexport function formatF64(value: number): string {\n return Object.is(value, -0) ? \"-0\" : String(value);\n}\n\n/**\n * The text of a node id for the text formats that write ids bare (DOT, Pajek, XGMML): the string\n * itself, or the shortest decimal of a number (formatF64).\n * @param id - the id\n * @returns the text\n */\nexport function idText(id: NodeId): string {\n return typeof id === \"number\" ? formatF64(id) : id;\n}\n\n/**\n * A decimal text with a decimal point or an exponent guaranteed for a finite value (`2.0`,\n * `1e+21`, `1.5e-7`), so an\n * untyped re-import keeps the column f64 rather than i32; negative zero is\n * written `-0.0` so it reads back as -0; non-finite values are the JS spellings (`Infinity`,\n * `-Infinity`, `NaN`), which the CSV / DOT / Pajek importers read back as text. The one\n * implementation of the \"decimal point guaranteed\" rule for every text format; GML has its own\n * spellings of the non-finite values in formatGmlReal().\n * @param value - the value\n * @param dtype - the column dtype the value comes from; f32 values use the shortest fround-round-trip text\n * @returns the text\n * @category Plugin helpers\n */\nexport function formatDecimal(value: number, dtype: \"f32\" | \"f64\" | \"i32\" | \"u32\" | \"u8\" = \"f64\"): string {\n if (!Number.isFinite(value)) {\n return String(value);\n }\n if (Object.is(value, -0)) {\n return \"-0.0\";\n }\n const text = formatNumber(value, dtype);\n // an exponent form (`1e+21`, `1.5e-7`) is already f64 text under the 5.1 grammar\n return text.includes(\".\") || text.includes(\"e\") ? text : `${text}.0`;\n}\n\n/**\n * A GML real: the shortest text with a decimal point guaranteed (`2.0`, `1.0e-7`), so a re-import\n * keeps the column real rather than int. Non-finite values have no GML spelling and are written\n * as the texts NetworkX's writer emits and its reader accepts, `+INF`, `-INF` and `NAN` (the\n * lowercase `inf` / `nan` would lex as keys there).\n * @param value - the value\n * @param dtype - the column dtype the value comes from; f32 values use the shortest fround-round-trip text\n * @returns the text\n * @category Plugin helpers\n */\nexport function formatGmlReal(value: number, dtype: \"f32\" | \"f64\" | \"i32\" | \"u32\" | \"u8\" = \"f64\"): string {\n if (Number.isNaN(value)) {\n return \"NAN\";\n }\n if (!Number.isFinite(value)) {\n return value > 0 ? \"+INF\" : \"-INF\";\n }\n const text = formatDecimal(value, dtype);\n if (text.includes(\".\")) {\n return text;\n }\n // NetworkX's real pattern requires a decimal point even in the exponent form (`1.0e-7`)\n const e = text.indexOf(\"e\");\n return `${text.slice(0, e)}.0${text.slice(e)}`;\n}\n\n/**\n * An integer text for a value known to be integral (an i32 / u32 / u8 cell, an f64 that holds an\n * integer), avoiding the exponent form `String()` uses above 1e21.\n * @param value - an integral value\n * @returns the digits\n * @category Plugin helpers\n */\nexport function formatInteger(value: number): string {\n if (Number.isSafeInteger(value) || !Number.isFinite(value)) {\n return String(value);\n }\n return BigInt(value).toString();\n}\n\n/**\n * The text of a numeric cell of any numeric dtype, dispatching on the dtype: f32 through\n * formatF32, everything else through formatF64.\n * @param value - the value\n * @param dtype - the column dtype the value came from\n * @returns the text\n * @category Plugin helpers\n */\nexport function formatNumber(value: number, dtype: \"f32\" | \"f64\" | \"i32\" | \"u32\" | \"u8\"): string {\n return dtype === \"f32\" ? formatF32(value) : formatF64(value);\n}\n","/**\n * Weight resolution shared by every importer (design sections 3.7 and 8.4): which source field is\n * THE weight (`weightFrom`, per-format default, null = unweighted), how its text or JSON value\n * becomes the number passed to `addEdge`, and the E_INVALID_WEIGHT boundary.\n *\n * An edge whose weight field is absent or blank is pushed WITHOUT a weight argument, never with an\n * explicit 1: the builder's weightSet bitmap then records that the weight was defaulted, and the\n * exporter re-emits no weight for it (decision D-WSET). NaN and non-numeric text are\n * E_INVALID_WEIGHT before the sink is touched, so the importer records the issue and skips the edge\n * with the sink unchanged; Infinity and negative values are legal weights (design section 3.7).\n */\n\nimport { type Column, GraphFormatError, type GraphSnapshot } from \"@graphty/graph-format\";\n\nimport { PRECISION_CODE } from \"./codes.js\";\nimport { parseDecimalText } from \"./declared-types.js\";\nimport { formatF32, formatF64, formatInteger } from \"./format.js\";\nimport { type ImportReportBuilder } from \"./report.js\";\n\n/**\n * The weight of an edge added without one (design section 3.7).\n * @category Plugin helpers\n */\nexport const DEFAULT_WEIGHT = 1;\n\n/**\n * Whether a source field is the weight field under the resolved `weightFrom` option.\n * @param name - the field name (attribute title, CSV header, JSON key)\n * @param weightFrom - the resolved option; null means unweighted\n * @returns true when the field is THE weight\n * @category Plugin helpers\n */\nexport function isWeightField(name: string, weightFrom: string | null): boolean {\n return weightFrom !== null && name === weightFrom;\n}\n\n/**\n * The weight argument of addEdge from a text cell: undefined for a blank cell (the weight is\n * omitted), the number otherwise.\n * @param text - the cell text\n * @param report - the import report; when given, an integer beyond 2^53 that the nearest double\n * changes is reported once as W_PRECISION\n * @returns the weight, or undefined when blank; E_INVALID_WEIGHT for NaN or non-numeric text\n * @category Writing a format\n */\nexport function parseWeightText(text: string, report?: ImportReportBuilder): number | undefined {\n const trimmed = text.trim();\n if (trimmed.length === 0) {\n return undefined;\n }\n let value: number;\n try {\n value = parseDecimalText(trimmed);\n } catch (err) {\n throw invalidWeight(text, err);\n }\n if (Number.isNaN(value)) {\n throw invalidWeight(text, null);\n }\n if (report !== undefined) {\n reportWeightPrecision(trimmed, value, report);\n }\n return value;\n}\n\n/**\n * Report once (W_PRECISION) an integer weight beyond 2^53 that the nearest double changes, which\n * the weights and the exact `graphty.weight` column would otherwise hold silently.\n * @param text - the weight as written in the file\n * @param value - the number it was read as\n * @param report - the import report\n */\nexport function reportWeightPrecision(text: string, value: number, report: ImportReportBuilder): void {\n if (\n !Number.isSafeInteger(value) &&\n Number.isFinite(value) &&\n /^[+-]?\\d+$/.test(text) &&\n BigInt(text) !== BigInt(value)\n ) {\n report.warnOnce(\n \"precision\",\n PRECISION_CODE,\n `edge weights: integers beyond 2^53 are stored as the nearest double (first: ${text})`,\n undefined,\n `${PRECISION_CODE}:edge weight`,\n );\n }\n}\n\n/**\n * The weight argument of addEdge from a typed value (JSON, a record field): undefined for null /\n * undefined, the number for a finite or infinite number, the parsed number for numeric text.\n * @param value - the field value\n * @returns the weight, or undefined when absent; E_INVALID_WEIGHT for NaN, a boolean, an object or non-numeric text\n * @category Plugin helpers\n */\nexport function weightFromValue(value: unknown): number | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) {\n throw invalidWeight(value, null);\n }\n return value;\n }\n if (typeof value === \"string\") {\n return parseWeightText(value);\n }\n throw invalidWeight(value, null);\n}\n\n/**\n * The E_INVALID_WEIGHT error of a rejected value.\n * @param value - the rejected value\n * @param cause - the parse error, or null\n * @returns the error\n */\nfunction invalidWeight(value: unknown, cause: unknown): GraphFormatError {\n let shown: string;\n switch (typeof value) {\n case \"string\":\n shown = JSON.stringify(value);\n break;\n case \"number\":\n shown = String(value);\n break;\n default:\n shown = typeof value;\n }\n return new GraphFormatError(\"E_INVALID_WEIGHT\", `invalid edge weight ${shown}`, {\n value: typeof value === \"object\" ? typeof value : value,\n cause: cause instanceof Error ? cause.message : null,\n });\n}\n\n// ============================================================ the exporter side\n\n/**\n * The explicit weights of a snapshot for exporters: the weight\n * role column when present (its validity says which edges had an explicit weight, its dtype how\n * the value is written), else `edgeList().weights` as f32 for every edge of a weighted snapshot;\n * nothing for an unweighted one. One implementation for every exporter.\n * @public\n * @category Plugin helpers\n */\nexport interface ExplicitWeights {\n /** Whether any edge can have an explicit weight (the snapshot is weighted). */\n readonly weighted: boolean;\n /** The dtype the values are read from: the shadow column's, or f32 for the arc array. */\n readonly dtype: \"f32\" | \"f64\";\n /**\n * Whether an edge's weight was explicit in the source.\n * @param e - the logical edge index\n * @returns true when the exporter must write it\n */\n isExplicit(e: number): boolean;\n /**\n * The weight value of an edge (meaningful when explicit).\n * @param e - the logical edge index\n * @returns the value\n */\n value(e: number): number;\n /**\n * The weight text of an edge: the shortest round-tripping decimal for its dtype, or the\n * integer digits when `integral` is requested and the value is an integer; null when the\n * weight was defaulted.\n * @param e - the logical edge index\n * @param integral - write an integral value without a decimal part (formats declaring an int weight)\n * @returns the text, or null\n */\n text(e: number, integral?: boolean): string | null;\n}\n\n/** A weight of -0 keeps its sign: every importer's weight grammar reads \"-0\" back as -0. */\nconst NEGATIVE_ZERO_TEXT = \"-0\";\n\n/** The dtypes a weight role column may have (an integer column declared by a caller included). */\nconst NUMERIC_DTYPES: ReadonlySet<string> = new Set([\"f32\", \"f64\", \"i32\", \"u32\", \"u8\"]);\n\n/**\n * The shortest round-tripping text of a weight by the shadow column's dtype.\n * @param dtype - the shadow column's dtype\n * @returns the formatter\n */\nfunction weightFormatter(dtype: string): (value: number) => string {\n switch (dtype) {\n case \"f32\":\n return formatF32;\n case \"f64\":\n return formatF64;\n default:\n return formatInteger;\n }\n}\n\n/**\n * Build the explicit-weight view of a snapshot.\n * @param snapshot - the snapshot\n * @returns the view\n * @category Writing a format\n */\nexport function explicitWeights(snapshot: GraphSnapshot): ExplicitWeights {\n const shadowColumn = snapshot.edges.byRole(\"weight\");\n const shadow: Column | null = shadowColumn !== null && NUMERIC_DTYPES.has(shadowColumn.dtype) ? shadowColumn : null;\n if (shadow !== null) {\n const dtype = shadow.dtype === \"f32\" ? \"f32\" : \"f64\";\n const format = weightFormatter(shadow.dtype);\n return {\n weighted: true,\n dtype,\n isExplicit: (e: number): boolean => shadow.isSet(e),\n value: (e: number): number => shadow.value(e) as number,\n text: (e: number, integral = false): string | null => {\n if (!shadow.isSet(e)) {\n return null;\n }\n const value = shadow.value(e) as number;\n if (Object.is(value, -0)) {\n return NEGATIVE_ZERO_TEXT;\n }\n return integral && Number.isInteger(value) ? formatInteger(value) : format(value);\n },\n };\n }\n const { weights } = snapshot.edgeList();\n if (weights === null || !snapshot.flags.weighted) {\n return {\n weighted: false,\n dtype: \"f32\",\n isExplicit: (): boolean => false,\n value: (): number => DEFAULT_WEIGHT,\n text: (): string | null => null,\n };\n }\n return {\n weighted: true,\n dtype: \"f32\",\n isExplicit: (): boolean => true,\n value: (e: number): number => weights[e],\n text: (e: number, integral = false): string => {\n const value = weights[e];\n if (Object.is(value, -0)) {\n return NEGATIVE_ZERO_TEXT;\n }\n return integral && Number.isInteger(value) ? formatInteger(value) : formatF32(value);\n },\n };\n}\n\n/**\n * The weight of every edge, in edge order (`weights[e]` is edge `e`'s weight), exactly as the file\n * wrote it, or null for a graph without weights. `snapshot.edgeList().weights` holds the same\n * weights as 32-bit floats, in which 0.1 reads back as 0.10000000149011612; this reads the exact\n * values the import also kept. An edge without a weight of its own has the weight 1.\n * @example\n * ```ts\n * const { snapshot } = await importGraph(\"source,target,weight\\na,b,0.1\\n\", { format: \"csv\" });\n * edgeWeights(snapshot); // Float64Array [ 0.1 ]\n * ```\n * @param snapshot - the graph\n * @returns one weight per edge, or null when the graph has no weights\n * @category Loading\n */\nexport function edgeWeights(snapshot: GraphSnapshot): Float64Array | null {\n const { weights } = snapshot.edgeList();\n if (weights === null) {\n return null;\n }\n const out = Float64Array.from(weights);\n const exact = snapshot.edges.byRole(\"weight\");\n if (exact !== null) {\n for (let e = 0; e < out.length; e++) {\n const v = exact.value(e);\n if (typeof v === \"number\") {\n out[e] = v;\n }\n }\n }\n return out;\n}\n","/**\n * The exporter-side machinery of design section 8.5: the ExportCapabilities table helper, the\n * generic check() that turns capability gaps into LossNotes with shared codes, and id sanitising\n * (`sanitizeIds: \"error\"` by default; an exporter never silently renames a node; \"mangle\" rewrites\n * the ids the format cannot hold and hands the exporter the originals to write into its\n * `graphty:originalId` attribute).\n *\n * Every exporter's check() calls checkCapabilities() first and appends its format-specific notes;\n * export() calls\n * sanitizeIds() before writing anything.\n */\n\nimport {\n type Column,\n type Dtype,\n GraphFormatError,\n type GraphSnapshot,\n type IdCoercion,\n INVALID_INDEX,\n type NodeId,\n} from \"@graphty/graph-format\";\n\nimport { type ExportCapabilities, type LossNote } from \"../types.js\";\nimport {\n COLUMN_DROPPED_CODE,\n COLUMN_RENAMED_LOSS_CODE,\n EMPTY_COLUMN_DROPPED_CODE,\n ID_TEXT_COLLISION_CODE,\n ID_TEXT_TYPE_CODE,\n INTEGRAL_F64_CODE,\n MIXED_DIRECTION_CODE,\n MUTUAL_AS_UNDIRECTED_CODE,\n MUTUAL_EXPANDED_CODE,\n NONFINITE_AS_NULL_CODE,\n OPTIONS_GAINED_CODE,\n PARENTS_DROPPED_CODE,\n ROLE_ASSUMED_CODE,\n ROLE_DROPPED_CODE,\n STORAGE_CLASS_CODE,\n TEMPORAL_DROPPED_CODE,\n TEMPORAL_TEXT_DROPPED_CODE,\n TEXT_INFERRED_CODE,\n WEIGHT_KEY_CLASH_CODE,\n WEIGHTS_DROPPED_CODE,\n XML_ILLEGAL_CHAR_CODE,\n} from \"./codes.js\";\nimport { coerceIdText } from \"./ids.js\";\nimport { type ResolvedExportOptions } from \"./options.js\";\nimport { agree, plural } from \"./plural.js\";\nimport { explicitWeights } from \"./weights.js\";\n\n/** The code of node ids the format cannot write under `sanitizeIds: \"error\"`. */\nconst ID_CHARSET_REFUSAL = \"E_ID_CHARSET\";\n\n/**\n * The shared LossNote codes of checkCapabilities().\n * @category Issue and loss codes\n */\nexport const LOSS = Object.freeze({\n /**\n * A node or edge attribute is not written: the format has no attributes, or none of this kind\n * (`CheckExtras.attributes` false).\n */\n COLUMN_DROPPED: COLUMN_DROPPED_CODE,\n /**\n * The graph has both directed and undirected edges, the format holds one direction per file, and\n * `onMixedDirection` is \"directed\" or \"undirected\": every edge is written with that direction.\n */\n MIXED_DIRECTION: \"W_MIXED_DIRECTION\",\n /**\n * The graph has both directed and undirected edges and `onMixedDirection` is \"error\". An import stops; a save to a\n * format that holds one direction per file fails with E_DIRECTED. Pass \"directed\" or \"undirected\" to read or write\n * it anyway.\n */\n MIXED_DIRECTION_ERROR: MIXED_DIRECTION_CODE,\n /** Parallel edges in a format without them. */\n MULTI_EDGES: \"W_MULTI_EDGES\",\n /** Self-loops in a format without them. */\n SELF_LOOPS: \"W_SELF_LOOPS\",\n /**\n * The format needs an id on every edge and the graph has none, so the file gets ids e0, e1, e2 and so on, which\n * read back as edge ids.\n */\n EDGE_IDS_GENERATED: \"W_EDGE_IDS_GENERATED\",\n /** The graph has edge ids and the format has no place for them, so they are not written. */\n EDGE_IDS_DROPPED: \"W_EDGE_IDS_DROPPED\",\n /** Ids outside the format's charset are rewritten (sanitizeIds \"mangle\"). */\n ID_MANGLED: \"W_ID_MANGLED\",\n /**\n * Node ids the format cannot write, under `sanitizeIds: \"error\"`; the save fails with E_INVALID_ID. Pass\n * `sanitizeIds: \"mangle\"` to rewrite them.\n */\n ID_CHARSET: ID_CHARSET_REFUSAL,\n /** The format numbers nodes 1..N; ids that are not their 1-based index are kept as labels only. */\n ID_RENUMBERED: \"W_ID_RENUMBERED\",\n /**\n * An attribute's type is one the format cannot store as it is (for example, a 32-bit float in a format that has only 64-bit\n * numbers), so it reads back with the nearest type the format has.\n */\n DTYPE: \"W_DTYPE_UNSUPPORTED\",\n /**\n * An attribute with several numbers per element (other than a position) is written as separate values or a list,\n * and does not read back as one attribute.\n */\n COMPONENTS: \"W_COMPONENTS_FLATTENED\",\n /** A list column in a format without lists. */\n LIST: \"W_LIST_UNSUPPORTED\",\n /** A json column in a format without nested values. */\n JSON: \"W_JSON_UNSUPPORTED\",\n /** A declared default in a format without defaults. */\n DEFAULT: \"W_DEFAULT_DROPPED\",\n /** Declared options in a format without enumerations. */\n OPTIONS: \"W_OPTIONS_DROPPED\",\n /** A parent / parents column in a format without containment. */\n HIERARCHY: \"W_HIERARCHY_DROPPED\",\n /** A start / end / timestamp column in a format without temporal support. */\n TEMPORAL: TEMPORAL_DROPPED_CODE,\n /** A `<column>.text` companion the format cannot carry. */\n TEMPORAL_TEXT: TEMPORAL_TEXT_DROPPED_CODE,\n /** An attribute with a role the format has no place for is written as a plain attribute; the role is lost. */\n ROLE: ROLE_DROPPED_CODE,\n /** A parents (multi-parent) column in a format with single containment only. */\n PARENTS: PARENTS_DROPPED_CODE,\n /** A mutual pair written as two directed edges without the mutual mark. */\n MUTUAL_EXPANDED: MUTUAL_EXPANDED_CODE,\n /** A mutual pair written as one undirected edge; the mark is lost and the pair reads back undirected. */\n MUTUAL_AS_UNDIRECTED: MUTUAL_AS_UNDIRECTED_CODE,\n /** Node ids whose written text reads back as the other type under the importer's id rule. */\n ID_TEXT_TYPE: ID_TEXT_TYPE_CODE,\n /**\n * Two node ids would be written as the same text (the number 5 and the text \"5\"); the save fails with\n * E_INVALID_ID.\n */\n ID_TEXT_COLLISION: ID_TEXT_COLLISION_CODE,\n /**\n * An attribute without the weight role is named like the attribute graph-io reads weights from (for example, `weight`), so\n * it reads back as the edge weight.\n */\n WEIGHT_KEY_CLASH: WEIGHT_KEY_CLASH_CODE,\n /**\n * An attribute without a role is written where the format keeps a role (for example, a `name` column as the label), and\n * reads back with that role.\n */\n ROLE_ASSUMED: ROLE_ASSUMED_CODE,\n /**\n * An attribute with a role (for example, the label) is written where the format keeps that role, and reads back under the\n * name the format's importer gives it.\n */\n COLUMN_NAME_CHANGED: COLUMN_RENAMED_LOSS_CODE,\n /** A declared column whose every row is unset vanishes through a format without declarations. */\n EMPTY_COLUMN: EMPTY_COLUMN_DROPPED_CODE,\n /**\n * A text attribute reads back as a dictionary attribute, or the reverse, because the importer chooses by how often\n * its values repeat. The values are the same.\n */\n STORAGE_CLASS: STORAGE_CLASS_CODE,\n /**\n * A number attribute whose values are all whole numbers reads back as integers, because the format does not record\n * the type.\n */\n INTEGRAL_F64: INTEGRAL_F64_CODE,\n /**\n * A text value that reads back as a number or a boolean, because the format does not record that it was text (the\n * text \"42\" reads back as the number 42).\n */\n TEXT_INFERRED: TEXT_INFERRED_CODE,\n /** A text value holds a character XML 1.0 forbids (most control characters); the save fails with E_COLUMN_TYPE. */\n XML_ILLEGAL_CHAR: XML_ILLEGAL_CHAR_CODE,\n /**\n * A text attribute stored as a dictionary, without a declared list of allowed values, reads back with its distinct\n * values as that list.\n */\n OPTIONS_GAINED: OPTIONS_GAINED_CODE,\n /** A spells column in a format without spells. */\n SPELLS: \"W_SPELLS_DROPPED\",\n /** Dynamic attribute values (extension tables) in a format without them. */\n DYNAMIC_VALUES: \"W_DYNAMIC_VALUES_DROPPED\",\n /** An open-interval column in a format without open intervals. */\n OPEN_INTERVAL: \"W_OPEN_INTERVAL\",\n /** Graph-level attributes in a format without them. */\n GRAPH_ATTRIBUTES: \"W_GRAPH_ATTRIBUTES_DROPPED\",\n /** A position column in a format without positions. */\n POSITIONS: \"W_POSITIONS_DROPPED\",\n /** Visual columns in a format without them. */\n VIZ: \"W_VIZ_DROPPED\",\n /** An extension table the format cannot carry. */\n EXTENSION_TABLE: \"W_EXTENSION_TABLE_DROPPED\",\n /** A NaN or infinite number the format cannot spell; not written (or written as null), it reads back unset. */\n NONFINITE_AS_NULL: NONFINITE_AS_NULL_CODE,\n /**\n * The graph has edge weights and the file does not keep them (the format has no weights, or an option turned them\n * off): every edge reads back with the default weight 1.\n */\n WEIGHTS_DROPPED: WEIGHTS_DROPPED_CODE,\n});\n\n/**\n * The error a save throws for the first `E_` note of a check() result, or null when there is none.\n * An exporter calls it before it writes anything, so every format refuses a save the same way: an\n * id note (E_ID_CHARSET, E_ID_TEXT_COLLISION) throws E_INVALID_ID, E_MIXED_DIRECTION throws\n * E_DIRECTED, E_XML_ILLEGAL_CHAR throws E_COLUMN_TYPE, and any other note E_UNSUPPORTED. Map your\n * own note codes onto these with `codes`. The error's `details.code` is the note's code.\n * @param notes - what check() returned\n * @param codes - the error code for each of your own note codes, such as\n * `{ E_PAIRS_BAD_ID: \"E_INVALID_ID\" }`\n * @returns the error to throw, or null\n * @category Writing a format\n */\nexport function refusedSave(\n notes: readonly LossNote[],\n codes: Readonly<Record<string, \"E_INVALID_ID\" | \"E_DIRECTED\" | \"E_COLUMN_TYPE\" | \"E_UNSUPPORTED\">> = {},\n): GraphFormatError | null {\n const note = notes.find((n) => n.code.startsWith(\"E_\"));\n if (note === undefined) {\n return null;\n }\n const code = codes[note.code] ?? REFUSAL_CODES[note.code] ?? \"E_UNSUPPORTED\";\n return new GraphFormatError(code, note.message, { code: note.code, column: note.column, count: note.count });\n}\n\n/** The error codes a refused save throws. */\ntype RefusalCode = \"E_INVALID_ID\" | \"E_DIRECTED\" | \"E_COLUMN_TYPE\" | \"E_UNSUPPORTED\";\n\n/** The error code a save throws for each shared refusal note. */\nconst REFUSAL_CODES: Readonly<Record<string, RefusalCode>> = {\n [ID_CHARSET_REFUSAL]: \"E_INVALID_ID\",\n [ID_TEXT_COLLISION_CODE]: \"E_INVALID_ID\",\n [MIXED_DIRECTION_CODE]: \"E_DIRECTED\",\n [XML_ILLEGAL_CHAR_CODE]: \"E_COLUMN_TYPE\",\n};\n\n/**\n * The capabilities of a format that keeps nothing beyond plain topology; the base every exporter overrides.\n * @category Plugin helpers\n */\nexport const NO_CAPABILITIES: ExportCapabilities = Object.freeze({\n mixedDirection: false,\n multiEdges: false,\n selfLoops: false,\n edgeIds: \"none\",\n idCharset: \"any\",\n dtypes: Object.freeze([]),\n components: false,\n lists: false,\n json: false,\n defaults: false,\n options: false,\n hierarchy: false,\n temporal: \"none\",\n graphAttributes: false,\n positions: false,\n viz: false,\n});\n\n/**\n * Build a capabilities table from the fields a format supports; every field left out is the\n * conservative NO_CAPABILITIES value, so an exporter states what it keeps and nothing is assumed.\n * @param supported - the fields the format supports\n * @returns a frozen table\n * @category Writing a format\n */\nexport function capabilities(supported: Partial<ExportCapabilities>): ExportCapabilities {\n return Object.freeze({ ...NO_CAPABILITIES, ...supported, dtypes: Object.freeze([...(supported.dtypes ?? [])]) });\n}\n\n/**\n * Format facts checkCapabilities() needs that the capabilities table does not carry.\n * @public\n * @category Writing a format\n */\nexport interface CheckExtras {\n /** Whether the format can write open intervals (GEXF 1.2 startopen / endopen); default false. */\n readonly openIntervals?: boolean | undefined;\n /**\n * Whether the format carries the `<column>.text` companion columns (the lexical\n * form of a temporal value); default false, and every companion is then reported as\n * W_TEMPORAL_TEXT_DROPPED.\n */\n readonly temporalText?: boolean | undefined;\n /**\n * The dtype the format's importer gives a position column; an f32 / f64 position of the other\n * dtype is reported as W_DTYPE_UNSUPPORTED because its values differ at the other precision.\n * Undefined skips the check.\n */\n readonly positionDtype?: Dtype | undefined;\n /**\n * The roles beyond the structural, position, viz, hierarchy and temporal ones that the format\n * has a slot for (`label`, `kind`, `labels`, `idSpace`, `sourcePort`, `targetPort`, ...); a\n * column with any other role is written as a plain attribute and reported as W_ROLE_DROPPED.\n * Defaults to none.\n */\n readonly roles?: ReadonlySet<string> | undefined;\n /**\n * Roles the format writes into a fixed slot whose importer reads them back under a fixed name;\n * a role column named otherwise is reported as W_COLUMN_NAME_CHANGED (role -> the name the\n * importer gives it). Defaults to none. `nodeRoleNames` and `edgeRoleNames` override it for one\n * kind, for a format whose slot exists for nodes only (or edges only).\n */\n readonly roleNames?: Readonly<Record<string, string>> | undefined;\n /** Like `roleNames`, for node columns only. */\n readonly nodeRoleNames?: Readonly<Record<string, string>> | undefined;\n /** Like `roleNames`, for edge columns only: `{}` for a format with no edge slot for any role. */\n readonly edgeRoleNames?: Readonly<Record<string, string>> | undefined;\n /**\n * False for a format that writes no node or edge attributes except the ones whose role is in\n * `roles`: every other attribute column is reported once as W_COLUMN_DROPPED, instead of the\n * type and role notes a written column gets. Default true. `nodeAttributes` and\n * `edgeAttributes` override it for one kind.\n */\n readonly attributes?: boolean | undefined;\n /** Like `attributes`, for node attributes only: false for a format that writes no node attributes. */\n readonly nodeAttributes?: boolean | undefined;\n /** Like `attributes`, for edge attributes only: false for a format that writes no edge attributes. */\n readonly edgeAttributes?: boolean | undefined;\n /**\n * Columns the format writes although it writes no other attributes: a column a format option\n * names, such as an edge label column. Keyed by `node` or `edge`. They get no W_COLUMN_DROPPED\n * note; the type notes of a written column still apply.\n */\n readonly writtenColumns?: Readonly<Partial<Record<\"node\" | \"edge\", readonly string[]>>> | undefined;\n /**\n * False for a format (or an option) that writes no edge weights: a weighted graph gets one\n * W_WEIGHTS_DROPPED note. Default true.\n */\n readonly weights?: boolean | undefined;\n /**\n * The tables the export writes, of \"node\", \"edge\" and \"graph\"; a gap in a table it does not\n * write is not a loss of this export and gets no note. Default all three. A CSV node table is\n * `[\"node\"]`, so its notes are about node ids and node columns only.\n */\n readonly tables?: readonly (\"node\" | \"edge\" | \"graph\")[] | undefined;\n /**\n * How the format's importer turns id text back into ids (\"canonical\" for most text formats), so\n * an id that comes back with another type (the text \"7\" as the number 7) is reported as\n * W_ID_TEXT_TYPE. Omit it when the format records each id's type.\n */\n readonly idsReadBack?: IdCoercion | undefined;\n}\n\n/** The roles checkCapabilities() treats structurally rather than as attribute columns. */\nconst STRUCTURAL_ROLES: ReadonlySet<string> = new Set([\"directed\", \"pair\", \"mutual\", \"weight\", \"originalId\"]);\nconst VIZ_ROLES: ReadonlySet<string> = new Set([\"color\", \"size\", \"shape\", \"thickness\"]);\nconst TEMPORAL_ROLES: ReadonlySet<string> = new Set([\"start\", \"end\", \"timestamp\", \"timestamps\"]);\nconst HIERARCHY_ROLES: ReadonlySet<string> = new Set([\"parent\", \"parents\"]);\n\n/**\n * The generic pre-flight check: compare a snapshot with a format's capabilities\n * and return one LossNote per gap, with the shared codes of LOSS. Format-specific notes are the\n * exporter's to append.\n * @param snapshot - the snapshot about to be exported\n * @param caps - the exporter's capabilities\n * @param options - the resolved common export options\n * @param extras - format facts the capabilities table does not carry\n * @returns the notes, empty when the export is exact\n * @category Writing a format\n */\nexport function checkCapabilities(\n snapshot: GraphSnapshot,\n caps: ExportCapabilities,\n options: ResolvedExportOptions,\n extras: CheckExtras = {},\n): LossNote[] {\n const notes: LossNote[] = [];\n const note = (code: string, message: string, column: string | null = null, count: number | null = null): void => {\n notes.push(Object.freeze({ code, message, column, count }));\n };\n const tables = extras.tables ?? [\"node\", \"edge\", \"graph\"];\n const edges = tables.includes(\"edge\");\n\n if (edges) {\n checkEdgeStructure(snapshot, caps, options, note);\n }\n\n const unrepresentable = countUnrepresentableIds(snapshot, caps.idCharset);\n if (unrepresentable > 0) {\n if (caps.idCharset === \"dense-1-based\") {\n note(\n LOSS.ID_RENUMBERED,\n options.sanitizeIds === \"mangle\"\n ? `${unrepresentable} node id${plural(unrepresentable)} ${agree(unrepresentable, \"is\", \"are\")} not their 1-based index; nodes are numbered 1..N, the original ids are written too, and an import with restoreMangledIds: true reads them back as the ids; they are also the labels of nodes without a label value`\n : `${unrepresentable} node id${plural(unrepresentable)} ${agree(unrepresentable, \"is\", \"are\")} not their 1-based index; nodes are numbered 1..N and ids kept as labels of the nodes without a label value (a node with one loses its id)`,\n null,\n unrepresentable,\n );\n } else if (options.sanitizeIds === \"mangle\") {\n note(\n LOSS.ID_MANGLED,\n `${unrepresentable} node id${plural(unrepresentable)} ${charsetText(caps.idCharset, unrepresentable)}, so they are rewritten; the original ids are written too, and an import with restoreMangledIds: true reads them back`,\n null,\n unrepresentable,\n );\n } else {\n note(\n LOSS.ID_CHARSET,\n `${unrepresentable} node id${plural(unrepresentable)} ${charsetText(caps.idCharset, unrepresentable)}, so the save fails unless sanitizeIds is \"mangle\"`,\n null,\n unrepresentable,\n );\n }\n }\n\n if (extras.idsReadBack !== undefined) {\n let retyped = 0;\n for (let i = 0; i < snapshot.nodeCount; i++) {\n const id = snapshot.ids.idOf(i);\n if (coerceIdText(String(id), extras.idsReadBack) !== id) {\n retyped++;\n }\n }\n if (retyped > 0) {\n note(\n LOSS.ID_TEXT_TYPE,\n `${retyped} node id${plural(retyped)} ${agree(retyped, \"reads\", \"read\")} back with another type (a number as text, or text as a number)`,\n null,\n retyped,\n );\n }\n }\n\n if (edges) {\n checkWeights(snapshot, extras, note);\n }\n if (tables.includes(\"node\")) {\n checkColumns(snapshot.nodes, \"node\", caps, extras, note);\n }\n if (edges) {\n checkColumns(snapshot.edges, \"edge\", caps, extras, note);\n }\n const graphColumns = tables.includes(\"graph\") ? snapshot.graph.names() : [];\n if (graphColumns.length > 0) {\n if (caps.graphAttributes) {\n checkColumns(snapshot.graph, \"graph\", caps, extras, note);\n } else {\n notes.push(\n Object.freeze({\n code: LOSS.GRAPH_ATTRIBUTES,\n message: `${graphColumns.length} graph attribute${plural(graphColumns.length)} cannot be written`,\n column: null,\n count: graphColumns.length,\n columns: Object.freeze([...graphColumns]),\n }),\n );\n }\n }\n\n for (const [name, table] of snapshot.extensions) {\n // \"temporal:node:<column>\" is the node table's, \"temporal:edge:<column>\" the edge table's;\n // any other extension table is the graph's.\n const [, kind] = name.split(\":\");\n const owner = name.startsWith(\"temporal:\") && (kind === \"node\" || kind === \"edge\") ? kind : \"graph\";\n if (!tables.includes(owner)) {\n continue;\n }\n if (name.startsWith(\"temporal:\")) {\n if (caps.temporal !== \"dynamic-values\") {\n note(\n LOSS.DYNAMIC_VALUES,\n `dynamic values of \"${name}\" (${table.rowCount} row${plural(table.rowCount)}) cannot be written`,\n name,\n table.rowCount,\n );\n }\n } else {\n note(\n LOSS.EXTENSION_TABLE,\n `extension table \"${name}\" (${table.rowCount} row${plural(table.rowCount)}) cannot be written`,\n name,\n table.rowCount,\n );\n }\n }\n return notes;\n}\n\n/**\n * The edge-structure checks of checkCapabilities(): direction, parallel edges, self-loops and\n * edge ids. Skipped for an export that writes no edge table.\n * @param snapshot - the snapshot about to be exported\n * @param caps - the exporter's capabilities\n * @param options - the resolved common export options\n * @param note - the recorder\n */\nfunction checkEdgeStructure(\n snapshot: GraphSnapshot,\n caps: ExportCapabilities,\n options: ResolvedExportOptions,\n note: NoteFn,\n): void {\n // mixed direction (design section 3.6): an expanded snapshot carries the pair / directed roles\n const mixed = countMixedEdges(snapshot);\n if (mixed > 0 && !caps.mixedDirection) {\n if (options.onMixedDirection === \"error\") {\n note(\n LOSS.MIXED_DIRECTION_ERROR,\n `${mixed} undirected edge${plural(mixed)} in a directed graph, and the format holds one direction per file, so the save fails unless onMixedDirection is \"directed\" or \"undirected\"`,\n null,\n mixed,\n );\n } else {\n note(\n LOSS.MIXED_DIRECTION,\n `${mixed} undirected edge${plural(mixed)} in a directed graph ${agree(mixed, \"is\", \"are\")} written as ${options.onMixedDirection} (onMixedDirection), and the format holds one direction per file`,\n null,\n mixed,\n );\n }\n }\n\n if (snapshot.flags.multigraph && !caps.multiEdges) {\n const parallel = countParallelArcs(snapshot);\n note(\n LOSS.MULTI_EDGES,\n `${parallel} parallel edge${plural(parallel)}; the format has no parallel edges`,\n null,\n parallel,\n );\n }\n if (snapshot.selfLoopCount > 0 && !caps.selfLoops) {\n note(\n LOSS.SELF_LOOPS,\n `${snapshot.selfLoopCount} self-loop${plural(snapshot.selfLoopCount)}; the format has no self-loops`,\n null,\n snapshot.selfLoopCount,\n );\n }\n\n const edgeIds = snapshot.edges.byRole(\"id\");\n if (caps.edgeIds === \"required\" && edgeIds === null && snapshot.edgeCount > 0) {\n note(\n LOSS.EDGE_IDS_GENERATED,\n \"the format requires edge ids, and the graph has none; the edges are written with generated ids, which read back as a new edge id column\",\n null,\n snapshot.edgeCount,\n );\n } else if (caps.edgeIds === \"none\" && edgeIds !== null) {\n note(\n LOSS.EDGE_IDS_DROPPED,\n `edge id column \"${edgeIds.meta.name}\" cannot be written`,\n edgeIds.meta.name,\n snapshot.edgeCount,\n );\n }\n}\n\n/**\n * The ids a charset cannot hold, as a message phrase.\n * @param charset - the format's id charset\n * @param n - how many ids, for the verb\n * @returns the phrase\n */\nfunction charsetText(charset: ExportCapabilities[\"idCharset\"], n: number): string {\n const are = agree(n, \"is\", \"are\");\n switch (charset) {\n case \"nmtoken\":\n return `${are} not XML name tokens (letters, digits and . - _ : only; no spaces)`;\n case \"integer\":\n return `${are} not integers`;\n default:\n return `cannot be written as ${charset} ids`;\n }\n}\n\n/**\n * The role names of one kind (CheckExtras.nodeRoleNames / edgeRoleNames, else roleNames).\n * @param domain - \"node\", \"edge\" or \"graph\"\n * @param extras - format extras\n * @returns the role names, or undefined when the extras name none\n */\nfunction roleNamesOf(domain: string, extras: CheckExtras): Readonly<Record<string, string>> | undefined {\n if (domain === \"node\") {\n return extras.nodeRoleNames ?? extras.roleNames;\n }\n if (domain === \"edge\") {\n return extras.edgeRoleNames ?? extras.roleNames;\n }\n return extras.roleNames;\n}\n\n/**\n * Whether a format writes the attributes of one kind (CheckExtras.nodeAttributes / edgeAttributes, else attributes).\n * @param domain - \"node\", \"edge\" or \"graph\"\n * @param extras - format extras\n * @returns false when it writes none, undefined when the extras do not say\n */\nfunction writesAttributes(domain: string, extras: CheckExtras): boolean | undefined {\n if (domain === \"node\") {\n return extras.nodeAttributes ?? extras.attributes;\n }\n if (domain === \"edge\") {\n return extras.edgeAttributes ?? extras.attributes;\n }\n return extras.attributes;\n}\n\n/**\n * The note of a format (or an option) that writes no weights, for a weighted graph.\n * @param snapshot - the snapshot\n * @param extras - format extras\n * @param note - the recorder\n */\nfunction checkWeights(snapshot: GraphSnapshot, extras: CheckExtras, note: NoteFn): void {\n if (extras.weights === false && explicitWeights(snapshot).weighted) {\n note(\n LOSS.WEIGHTS_DROPPED,\n `the edge weights are not written; ${snapshot.edgeCount} edge${plural(snapshot.edgeCount)} ${agree(snapshot.edgeCount, \"reads\", \"read\")} back with weight 1`,\n null,\n snapshot.edgeCount,\n );\n }\n}\n\n/** A note-recording callback. */\ntype NoteFn = (code: string, message: string, column?: string | null, count?: number | null) => void;\n\n/** How messages name what a column of a role holds. */\nconst ROLE_WORDS: Readonly<Record<string, string>> = {\n label: \"labels\",\n weight: \"weights\",\n id: \"ids\",\n position: \"positions\",\n originalId: \"original ids\",\n};\n\n/**\n * The per-column checks of checkCapabilities().\n * @param table - the table\n * @param domain - the domain, for messages\n * @param caps - the capabilities\n * @param extras - format extras\n * @param note - the recorder\n */\nfunction checkColumns(\n table: Iterable<Column>,\n domain: string,\n caps: ExportCapabilities,\n extras: CheckExtras,\n note: NoteFn,\n): void {\n for (const column of table) {\n const { name, role } = column.meta;\n if (role !== null && STRUCTURAL_ROLES.has(role)) {\n continue;\n }\n const label = `${domain} column \"${name}\"`;\n noteRenamedRole(column, label, roleNamesOf(domain, extras), note);\n if (domain === \"edge\" && role === \"id\") {\n continue;\n }\n if (isDroppedColumn(column, domain, extras)) {\n note(LOSS.COLUMN_DROPPED, `${label} is not written`, name, column.length - column.nullCount);\n continue;\n }\n if (!checkSlotRole(column, label, caps, extras, note)) {\n checkAttributeColumn(column, label, caps, extras, note);\n }\n }\n}\n\n/**\n * The note of a role column the format writes under its own fixed name.\n * @param column - the column\n * @param label - the column's label for messages\n * @param roleNames - the format's fixed names per role, if any\n * @param note - the recorder\n */\nfunction noteRenamedRole(\n column: Column,\n label: string,\n roleNames: Readonly<Record<string, string>> | undefined,\n note: NoteFn,\n): void {\n const { name, role } = column.meta;\n if (role === null || roleNames === undefined) {\n return;\n }\n const fixed = roleNames[role];\n if (fixed !== undefined && fixed !== name) {\n note(\n LOSS.COLUMN_NAME_CHANGED,\n `${label} (the ${ROLE_WORDS[role] ?? role}) is written as the format's own ${role} and reads back as \"${fixed}\"`,\n name,\n column.length - column.nullCount,\n );\n }\n}\n\n/**\n * Whether a format that writes no attributes of this kind drops the column (it is neither named\n * in CheckExtras.writtenColumns nor of a role the format has a slot for).\n * @param column - the column\n * @param domain - \"node\", \"edge\" or \"graph\"\n * @param extras - format extras\n * @returns true when the column is not written\n */\nfunction isDroppedColumn(column: Column, domain: string, extras: CheckExtras): boolean {\n const { name, role } = column.meta;\n const named = extras.writtenColumns?.[domain as \"node\" | \"edge\"]?.includes(name) ?? false;\n return (\n writesAttributes(domain, extras) === false && !named && !(role !== null && (extras.roles?.has(role) ?? false))\n );\n}\n\n/** How a format writes one slot role: what it loses when it cannot, and when it can. */\ninterface SlotRule {\n readonly code: string;\n /** What the message calls the column; the role itself when absent. */\n readonly what?: string;\n readonly written: (caps: ExportCapabilities, extras: CheckExtras) => boolean;\n}\n\n/**\n * The roles (other than position) a format writes in its own syntax rather than as an attribute.\n * @returns the rules by role\n */\nfunction slotRules(): ReadonlyMap<string, SlotRule> {\n const rules = new Map<string, SlotRule>();\n const add = (roles: Iterable<string>, rule: SlotRule): void => {\n for (const role of roles) {\n rules.set(role, rule);\n }\n };\n add([\"timeText\"], {\n code: LOSS.TEMPORAL_TEXT,\n what: \"the original text of a date or time column\",\n written: (_caps, extras) => extras.temporalText === true,\n });\n add(VIZ_ROLES, { code: LOSS.VIZ, written: (caps) => caps.viz });\n add(HIERARCHY_ROLES, { code: LOSS.HIERARCHY, written: (caps) => caps.hierarchy });\n add(TEMPORAL_ROLES, { code: LOSS.TEMPORAL, written: (caps) => caps.temporal !== \"none\" });\n add([\"spells\"], {\n code: LOSS.SPELLS,\n what: \"spells\",\n written: (caps) => caps.temporal === \"spells\" || caps.temporal === \"dynamic-values\",\n });\n // The open bits are written by the format's own temporal syntax when it has one; the\n // column's u8 storage is never an attribute dtype, so no dtype note either way.\n add([\"open\", \"spellsOpen\"], {\n code: LOSS.OPEN_INTERVAL,\n what: \"open intervals\",\n written: (_caps, extras) => extras.openIntervals === true,\n });\n return rules;\n}\n\nconst SLOT_RULES = slotRules();\n\n/**\n * The checks of a column whose role the format writes in its own syntax (positions, viz,\n * hierarchy, temporal roles, spells, open intervals, original date text), not as an attribute.\n * @param column - the column\n * @param label - the column's label for messages\n * @param caps - the capabilities\n * @param extras - format extras\n * @param note - the recorder\n * @returns true when the role is one of those, so no attribute checks follow\n */\nfunction checkSlotRole(\n column: Column,\n label: string,\n caps: ExportCapabilities,\n extras: CheckExtras,\n note: NoteFn,\n): boolean {\n const { name, role } = column.meta;\n if (role === \"position\") {\n checkPositionColumn(column, label, caps, extras, note);\n return true;\n }\n const rule = role === null ? undefined : SLOT_RULES.get(role);\n if (role === null || rule === undefined) {\n return false;\n }\n if (!rule.written(caps, extras)) {\n note(rule.code, `${label} (${rule.what ?? role}) cannot be written`, name, column.length - column.nullCount);\n }\n return true;\n}\n\n/**\n * The checks of a position column: whether the format has positions, and their dtype.\n * @param column - the column\n * @param label - the column's label for messages\n * @param caps - the capabilities\n * @param extras - format extras\n * @param note - the recorder\n */\nfunction checkPositionColumn(\n column: Column,\n label: string,\n caps: ExportCapabilities,\n extras: CheckExtras,\n note: NoteFn,\n): void {\n const { meta } = column;\n if (!caps.positions) {\n note(LOSS.POSITIONS, `${label} (positions) cannot be written`, meta.name, column.length - column.nullCount);\n } else if (extras.positionDtype !== undefined && meta.dtype !== extras.positionDtype) {\n note(\n LOSS.DTYPE,\n `${label} (positions) is ${meta.dtype}; the format reads positions back as ${extras.positionDtype}`,\n meta.name,\n column.length - column.nullCount,\n );\n }\n}\n\n/**\n * The checks of a column written as a plain attribute: a role the format has no slot for,\n * dynamic values, the dtype, a declared default and declared options.\n * @param column - the column\n * @param label - the column's label for messages\n * @param caps - the capabilities\n * @param extras - format extras\n * @param note - the recorder\n */\nfunction checkAttributeColumn(\n column: Column,\n label: string,\n caps: ExportCapabilities,\n extras: CheckExtras,\n note: NoteFn,\n): void {\n const { meta } = column;\n const { name, role } = meta;\n if (role !== null && !(extras.roles?.has(role) ?? false)) {\n note(\n LOSS.ROLE,\n `${label} (${role}) is written as a plain attribute; the format has no ${role} slot and the role is lost`,\n name,\n column.length - column.nullCount,\n );\n }\n if (meta.dynamic && caps.temporal !== \"dynamic-values\") {\n note(LOSS.DYNAMIC_VALUES, `${label} is dynamic; only its static value can be written`, name, null);\n }\n checkDtype(column, label, caps, note);\n if (meta.default !== undefined && !caps.defaults) {\n note(LOSS.DEFAULT, `${label} declares a default; the format has no defaults`, name, null);\n }\n if (meta.options !== null && !caps.options) {\n note(LOSS.OPTIONS, `${label} declares options; the format has no enumerations`, name, null);\n }\n}\n\n/**\n * The dtype checks of one attribute column: lists, nested values, scalar dtypes and strides\n * against the capability table.\n * @param column - the column\n * @param label - the column's label for messages\n * @param caps - the capabilities\n * @param note - the recorder\n */\nfunction checkDtype(column: Column, label: string, caps: ExportCapabilities, note: NoteFn): void {\n const { meta } = column;\n const { name } = meta;\n const set = column.length - column.nullCount;\n switch (meta.dtype) {\n case \"list\":\n if (!caps.lists) {\n note(LOSS.LIST, `${label} is a list; the format has no lists`, name, set);\n } else if (meta.itemDtype !== null && !caps.dtypes.includes(meta.itemDtype)) {\n note(\n LOSS.DTYPE,\n `${label} holds ${meta.itemDtype} items; the format cannot keep that dtype`,\n name,\n set,\n );\n } else if (meta.itemDtype === \"dict\") {\n // no format declares an enumeration for list items: they read back as strings\n note(LOSS.DTYPE, `${label} holds dict items; list items read back as string`, name, set);\n }\n break;\n case \"json\":\n if (!caps.json) {\n note(LOSS.JSON, `${label} holds nested values; the format has no nested values`, name, set);\n }\n break;\n default:\n if (!caps.dtypes.includes(meta.dtype)) {\n note(LOSS.DTYPE, `${label} is ${meta.dtype}; the format cannot keep that dtype`, name, set);\n }\n if (meta.components > 1 && !caps.components) {\n note(\n LOSS.COMPONENTS,\n `${label} has ${meta.components} components; the format has no strides`,\n name,\n set,\n );\n }\n break;\n }\n}\n\n/**\n * The number of source edges that were undirected in a directed snapshot (the halves of an\n * expanded pair count once; an expanded self-loop has no mirror and counts once).\n * @param snapshot - the snapshot\n * @returns the count; 0 for an undirected snapshot or one without the directed role\n * @category Plugin helpers\n */\nexport function countMixedEdges(snapshot: GraphSnapshot): number {\n if (!snapshot.directed) {\n return 0;\n }\n const directed = snapshot.edges.byRole(\"directed\");\n if (directed?.dtype !== \"bool\") {\n return 0;\n }\n const pair = snapshot.edges.byRole(\"pair\");\n let undirectedRows = 0;\n let paired = 0;\n for (let e = 0; e < snapshot.edgeCount; e++) {\n if (directed.isSet(e) && directed.value(e) === false) {\n undirectedRows++;\n if (pair !== null && pair.dtype === \"u32\" && pair.isSet(e) && pair.data[e] !== INVALID_INDEX) {\n paired++;\n }\n }\n }\n return paired / 2 + (undirectedRows - paired);\n}\n\n/**\n * The number of arcs that repeat the previous arc's target within a row (parallel edges), counted\n * over the forward CSR; an undirected parallel edge is counted in both rows.\n * @param snapshot - the snapshot\n * @returns the count\n * @category Plugin helpers\n */\nexport function countParallelArcs(snapshot: GraphSnapshot): number {\n const { rowPtr, colIdx, nodeCount } = snapshot;\n let count = 0;\n for (let u = 0; u < nodeCount; u++) {\n for (let a = rowPtr[u] + 1; a < rowPtr[u + 1]; a++) {\n if (colIdx[a] === colIdx[a - 1]) {\n count++;\n }\n }\n }\n return snapshot.directed ? count : Math.ceil(count / 2);\n}\n\n// ============================================================ id sanitising\n\n/**\n * The id charsets of ExportCapabilities.\n * @public\n * @category Plugin helpers\n */\nexport type IdCharset = ExportCapabilities[\"idCharset\"];\n\n/**\n * The ids to write for every node after sanitising.\n * @public\n * @category Writing a format\n */\nexport interface SanitizedIds {\n /** The charset applied. */\n readonly charset: IdCharset;\n /** How many ids differ from the snapshot's. */\n readonly changed: number;\n /**\n * The id to write for a node.\n * @param index - the node index\n * @returns the id as the format will read it back\n */\n idAt(index: number): NodeId;\n /**\n * Whether a node's written id differs from its original.\n * @param index - the node index\n * @returns true when the exporter must record the original\n */\n isChanged(index: number): boolean;\n /**\n * The original id of a node.\n * @param index - the node index\n * @returns the snapshot's id\n */\n originalAt(index: number): NodeId;\n}\n\n/** XML NameChar ranges (XML 1.0 fifth edition), as inclusive code point pairs. */\nconst NAME_CHAR_RANGES: readonly (readonly [number, number])[] = [\n [0x2d, 0x2e],\n [0x30, 0x3a],\n [0x41, 0x5a],\n [0x5f, 0x5f],\n [0x61, 0x7a],\n [0xb7, 0xb7],\n [0xc0, 0xd6],\n [0xd8, 0xf6],\n [0xf8, 0x37d],\n [0x37f, 0x1fff],\n [0x200c, 0x200d],\n [0x203f, 0x2040],\n [0x2070, 0x218f],\n [0x2c00, 0x2fef],\n [0x3001, 0xd7ff],\n [0xf900, 0xfdcf],\n [0xfdf0, 0xfffd],\n [0x10000, 0xeffff],\n];\n\n/**\n * Whether a code point is an XML NameChar.\n * @param cp - the code point\n * @returns true when it may appear in an NMTOKEN\n * @category Plugin helpers\n */\nexport function isNameChar(cp: number): boolean {\n for (const [lo, hi] of NAME_CHAR_RANGES) {\n if (cp < lo) {\n return false;\n }\n if (cp <= hi) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Whether a text is an XML NMTOKEN (one or more NameChars; GraphML node and edge ids).\n * @param text - the text\n * @returns true for a non-empty text of NameChars\n * @category Plugin helpers\n */\nexport function isNmtoken(text: string): boolean {\n if (text.length === 0) {\n return false;\n }\n for (const ch of text) {\n const cp = ch.codePointAt(0);\n if (cp === undefined || !isNameChar(cp)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Whether an id can be written unchanged under a charset.\n * @param id - the id\n * @param charset - the charset\n * @returns true when the id needs no rewriting (\"dense-1-based\" is never true: see sanitizeIds)\n * @category Plugin helpers\n */\nexport function isRepresentableId(id: NodeId, charset: IdCharset): boolean {\n switch (charset) {\n case \"any\":\n return true;\n case \"nmtoken\":\n return isNmtoken(String(id));\n case \"integer\":\n return typeof id === \"number\" && Number.isSafeInteger(id);\n case \"dense-1-based\":\n return false;\n default: {\n const name: string = charset;\n throw new GraphFormatError(\"E_UNSUPPORTED\", `unknown id charset ${name}`, { charset: name });\n }\n }\n}\n\n/**\n * How many node ids a charset cannot hold as they are (for check()); under \"dense-1-based\" the\n * ids that are not their own 1-based index.\n * @param snapshot - the snapshot\n * @param charset - the charset\n * @returns the count\n * @category Plugin helpers\n */\nexport function countUnrepresentableIds(snapshot: GraphSnapshot, charset: IdCharset): number {\n const { ids } = snapshot;\n if (charset === \"any\") {\n return 0;\n }\n if (charset === \"dense-1-based\") {\n if (ids.kind === \"identity\" && ids.offset === 1) {\n return 0;\n }\n let changed = 0;\n for (let i = 0; i < ids.size; i++) {\n if (ids.idOf(i) !== i + 1) {\n changed++;\n }\n }\n return changed;\n }\n let count = 0;\n for (let i = 0; i < ids.size; i++) {\n if (!isRepresentableId(ids.idOf(i), charset)) {\n count++;\n }\n }\n return count;\n}\n\n/**\n * Sanitise the node ids for a charset. Under \"error\" any id the charset\n * cannot hold is E_INVALID_ID (details.reason \"charset\") and nothing is written; under \"mangle\"\n * such ids are rewritten deterministically and uniquely: NMTOKEN by replacing every other\n * character with `_` (an empty result becomes `_`) and suffixing `_2`, `_3`... on collision;\n * integer by the smallest unused non-negative integers in index order. \"dense-1-based\" always\n * writes the 1-based index (Pajek numbers vertices; the exporter writes the original id as the\n * label), which is a renumbering rather than a mangling, so it never throws under \"error\"; under\n * \"mangle\" the exporter also writes the original id under its originalId attribute.\n * @param snapshot - the snapshot\n * @param charset - the charset\n * @param mode - the resolved sanitizeIds option\n * @returns the ids to write\n * @category Writing a format\n */\nexport function sanitizeIds(snapshot: GraphSnapshot, charset: IdCharset, mode: \"error\" | \"mangle\"): SanitizedIds {\n const { ids } = snapshot;\n const n = ids.size;\n if (charset === \"dense-1-based\") {\n const changed = countUnrepresentableIds(snapshot, charset);\n return {\n charset,\n changed,\n idAt: (i: number): NodeId => i + 1,\n isChanged: (i: number): boolean => ids.idOf(i) !== i + 1,\n originalAt: (i: number): NodeId => ids.idOf(i),\n };\n }\n const unchanged: SanitizedIds = {\n charset,\n changed: 0,\n idAt: (i: number): NodeId => ids.idOf(i),\n isChanged: (): boolean => false,\n originalAt: (i: number): NodeId => ids.idOf(i),\n };\n if (charset === \"any\") {\n return unchanged;\n }\n const bad: number[] = [];\n for (let i = 0; i < n; i++) {\n if (!isRepresentableId(ids.idOf(i), charset)) {\n bad.push(i);\n }\n }\n if (bad.length === 0) {\n return unchanged;\n }\n if (mode === \"error\") {\n const first = ids.idOf(bad[0]);\n throw new GraphFormatError(\n \"E_INVALID_ID\",\n `${bad.length} node id${plural(bad.length)} ${charsetText(charset, bad.length)} (first: ${JSON.stringify(first)} at index ${bad[0]}); pass sanitizeIds: \"mangle\" to rewrite them`,\n { reason: \"charset\", charset, count: bad.length, id: first, index: bad[0] },\n );\n }\n const written = new Map<number, NodeId>();\n if (charset === \"nmtoken\") {\n const used = new Set<string>();\n for (let i = 0; i < n; i++) {\n const id = ids.idOf(i);\n if (isRepresentableId(id, charset)) {\n used.add(String(id));\n }\n }\n for (const i of bad) {\n const base = mangleNmtoken(String(ids.idOf(i)));\n let candidate = base;\n for (let k = 2; used.has(candidate); k++) {\n candidate = `${base}_${k}`;\n }\n used.add(candidate);\n written.set(i, candidate);\n }\n } else {\n const used = new Set<number>();\n for (let i = 0; i < n; i++) {\n const id = ids.idOf(i);\n if (typeof id === \"number\" && Number.isSafeInteger(id)) {\n used.add(id);\n }\n }\n let next = 0;\n for (const i of bad) {\n while (used.has(next)) {\n next++;\n }\n used.add(next);\n written.set(i, next);\n next++;\n }\n }\n return {\n charset,\n changed: bad.length,\n idAt: (i: number): NodeId => written.get(i) ?? ids.idOf(i),\n isChanged: (i: number): boolean => written.has(i),\n originalAt: (i: number): NodeId => ids.idOf(i),\n };\n}\n\n/**\n * Rewrite a text as an NMTOKEN: every character that is not a NameChar becomes `_`.\n * @param text - the text\n * @returns a non-empty NMTOKEN\n * @category Plugin helpers\n */\nexport function mangleNmtoken(text: string): string {\n let out = \"\";\n for (const ch of text) {\n const cp = ch.codePointAt(0);\n out += cp !== undefined && isNameChar(cp) ? ch : \"_\";\n }\n return out.length === 0 ? \"_\" : out;\n}\n"],"names":["DATE_TIME","TIME","INTEGER_TEXT","list","messageOf","done"],"mappings":";AAmBO,MAAM,aAAa;AAMnB,MAAM,kBAAkB;AAMxB,MAAM,wBAAwB;AAM9B,MAAM,sBAAsB;AAM5B,MAAM,gBAAgB;AAOtB,MAAM,uBAAuB;AAM7B,MAAM,mBAAmB;AAMzB,MAAM,cAAc;AAMpB,MAAM,kBAAkB;AAMxB,MAAM,uBAAuB;AAM7B,MAAM,kBAAkB;AAMxB,MAAM,sBAAsB;AAM5B,MAAM,iBAAiB;AAMvB,MAAM,qBAAqB;AAO3B,MAAM,sBAAsB;AAM5B,MAAM,sBAAsB;AAO5B,MAAM,kBAAkB;AAMxB,MAAM,iBAAiB;AAOvB,MAAM,iBAAiB;AAOvB,MAAM,mBAAmB;AAMzB,MAAM,sBAAsB;AAM5B,MAAM,oBAAoB;AAO1B,MAAM,wBAAwB;AAM9B,MAAM,yBAAyB;AAM/B,MAAM,wBAAwB;AAQ9B,MAAM,yBAAyB;AAO/B,MAAM,yBAAyB;AAO/B,MAAM,sBAAsB;AAO5B,MAAM,yBAAyB;AAM/B,MAAM,6BAA6B;AAQnC,MAAM,yBAAyB;AAQ/B,MAAM,wBAAwB;AAQ9B,MAAM,uBAAuB;AAO7B,MAAM,kBAAkB;AAMxB,MAAM,yBAAyB;AAM/B,MAAM,mBAAmB;AAMzB,MAAM,mBAAmB;AAMzB,MAAM,yBAAyB;AAM/B,MAAM,iBAAiB;AAOvB,MAAM,eAAe;AAQrB,MAAM,0BAA0B;AAMhC,MAAM,2BAA2B;AAMjC,MAAM,oBAAoB;AAM1B,MAAM,wBAAwB;AAO9B,MAAM,2BAA2B;AAOjC,MAAM,wBAAwB;AAM9B,MAAM,6BAA6B;AAMnC,MAAM,4BAA4B;AAOlC,MAAM,+BAA+B;AAMrC,MAAM,oBAAoB;AAM1B,MAAM,sBAAsB;AAO5B,MAAM,qBAAqB;AAM3B,MAAM,sBAAsB;AAO5B,MAAM,iBAAiB;AASvB,MAAM,mBAAmB;AAOzB,MAAM,uBAAuB;AAM7B,MAAM,4BAA4B;AAMlC,MAAM,sBAAsB;AAM5B,MAAM,iBAAiB;AAOvB,MAAM,oBAAoB;AAO1B,MAAM,0BAA0B;AAQhC,MAAM,sBAAsB;AAO5B,MAAM,gBAAgB;AAQtB,MAAM,wBAAwB;AAQ9B,MAAM,oBAAoB;AAM1B,MAAM,uBAAuB;AAM7B,MAAM,wBAAwB;AAM9B,MAAM,uBAAuB;AAM7B,MAAM,4BAA4B;AAMlC,MAAM,oBAAoB;AAM1B,MAAM,yBAAyB;AAM/B,MAAM,6BAA6B;AAMnC,MAAM,uBAAuB;AAO7B,MAAM,wBAAwB;AAO9B,MAAM,oBAAoB;AAO1B,MAAM,2BAA2B;AAMjC,MAAM,4BAA4B;AAOlC,MAAM,qBAAqB;AAO3B,MAAM,oBAAoB;AAO1B,MAAM,qBAAqB;AAM3B,MAAM,wBAAwB;AAM9B,MAAM,gCAAgC;AAMtC,MAAM,wBAAwB;AAM9B,MAAM,qBAAqB;AAO3B,MAAM,gCAAgC;AAOtC,MAAM,yBAAyB;AAM/B,MAAM,yBAAyB;AAM/B,MAAM,kBAAkB;AAOxB,MAAM,sBAAsB;AAU5B,MAAM,gBAAgB,OAAO,OAAO;AAAA;AAAA,EAEvC,YAAY;AAAA;AAAA,EAEZ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKd,gBAAgB;AAAA;AAAA,EAEhB,gBAAgB;AAAA;AAAA,EAEhB,WAAW;AAAA;AAAA,EAEX,mBAAmB;AACvB,CAAC;AASM,MAAM,cAAc,OAAO,OAAO;AAAA;AAAA,EAErC,aAAa;AAAA;AAAA,EAEb,WAAW;AAAA;AAAA,EAEX,mBAAmB;AAAA;AAAA,EAEnB,mBAAmB;AAAA;AAAA,EAEnB,gBAAgB;AAAA;AAAA,EAEhB,mBAAmB;AACvB,CAAC;AC1nBM,SAAS,OAAO,GAAmB;AACtC,SAAO,MAAM,IAAI,KAAK;AAC1B;AASO,SAAS,MAAM,GAAW,KAAa,MAAsB;AAChE,SAAO,MAAM,IAAI,MAAM;AAC3B;AAQO,SAAS,aAAa,OAAe,OAAuB;AAC/D,QAAM,QAAQ,CAAA;AACd,MAAI,QAAQ,KAAK,UAAU,GAAG;AAC1B,UAAM,KAAK,GAAG,KAAK,QAAQ,OAAO,KAAK,CAAC,EAAE;AAAA,EAC9C;AACA,MAAI,QAAQ,GAAG;AACX,UAAM,KAAK,GAAG,KAAK,QAAQ,OAAO,KAAK,CAAC,EAAE;AAAA,EAC9C;AACA,SAAO,MAAM,KAAK,OAAO;AAC7B;AAOO,SAAS,YAAY,MAAsB;AAC9C,SAAO,GAAG,YAAY,KAAK,IAAI,IAAI,OAAO,GAAG,IAAI,IAAI;AACzD;ACNO,MAAM,iBAA6B;AAMnC,MAAM,mBAAmB;AAEhC,MAAMA,cACF;AACJ,MAAMC,SAAO;AACb,MAAM,UAAU;AAEhB,MAAM,gBAAgB;AACtB,MAAM,aAAa;AASZ,SAAS,cAAc,MAAc,MAAmC;AAC3E,MAAI;AACJ,UAAQ,MAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,cAAQ,cAAc,MAAM,IAAI;AAChC;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AACD,cAAQ,UAAU,MAAM,IAAI;AAC5B;AAAA,IACJ,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,yBAAyB,IAAI,IAAI,EAAE,MAAM,MAAM;AAAA,IAC/F;AAAA,EAAA;AAEJ,QAAM,YAAY,eAAe,OAAO,IAAI;AAC5C,SAAO,EAAE,OAAO,MAAM,cAAc,OAAO,OAAO,KAAA;AACtD;AAYO,SAAS,cAAc,MAAc,YAA8C;AACtF,QAAM,UAAU,KAAK,KAAA;AACrB,UAAQ,YAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AACD,aAAO,iBAAiB,OAAO;AAAA,IACnC,KAAK;AACD,aAAO,cAAc,SAAS,MAAM;AAAA,IACxC,KAAK;AACD,aAAO,cAAc,SAAS,UAAU;AAAA,IAC5C,KAAK;AACD,aAAO,QAAQ,KAAK,OAAO,IAAI,iBAAiB,OAAO,IAAI,cAAc,SAAS,UAAU;AAAA,IAChG,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,sBAAsB,IAAI,IAAI,EAAE,YAAY,MAAM;AAAA,IAClG;AAAA,EAAA;AAER;AAUO,SAAS,eAAe,OAAe,MAA4B;AACtE,UAAQ,MAAA;AAAA,IACJ,KAAK,QAAQ;AACT,YAAM,IAAI,IAAI,KAAK,KAAK;AACxB,aAAO,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,EAAE,YAAA,IAAgB,CAAC,CAAC,IAAI,KAAK,EAAE,WAAA,CAAY,CAAC;AAAA,IAC1E;AAAA,IACA,KAAK;AAAA,IACL,KAAK,iBAAiB;AAClB,YAAM,IAAI,IAAI,KAAK,KAAK;AACxB,YAAM,OAAO,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,EAAE,YAAA,IAAgB,CAAC,CAAC,IAAI,KAAK,EAAE,WAAA,CAAY,CAAC;AAC5E,YAAM,OAAO,MAAM,EAAE,YAAA,GAAe,EAAE,cAAA,GAAiB,EAAE,cAAA,GAAiB,EAAE,oBAAoB;AAChG,aAAO,SAAS,aAAa,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG,IAAI,IAAI,IAAI;AAAA,IACrE;AAAA,IACA,KAAK;AAAA,IACL,KAAK,aAAa;AACd,YAAM,MAAO,QAAQ,aAAc,cAAc;AACjD,YAAM,IAAI,KAAK,MAAM,KAAK,IAAS;AACnC,YAAM,IAAI,KAAK,MAAO,KAAK,OAAa,aAAa;AACrD,YAAM,IAAI,KAAK,MAAO,KAAK,gBAAiB,GAAI;AAChD,YAAM,OAAO,MAAM,GAAG,GAAG,GAAG,KAAK,GAAI;AACrC,aAAO,SAAS,SAAS,GAAG,IAAI,MAAM;AAAA,IAC1C;AAAA,IACA,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,yBAAyB,IAAI,IAAI,EAAE,MAAM,MAAM;AAAA,IAC/F;AAAA,EAAA;AAER;AAUO,SAAS,gBAAgB,OAAe,YAAuC;AAClF,UAAQ,YAAA;AAAA,IACJ,KAAK;AACD,aAAO,eAAe,OAAO,MAAM;AAAA,IACvC,KAAK;AACD,aAAO,eAAe,OAAO,UAAU;AAAA,IAC3C,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,aAAO,OAAO,KAAK;AAAA,IACvB,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,sBAAsB,IAAI,IAAI,EAAE,YAAY,MAAM;AAAA,IAClG;AAAA,EAAA;AAER;AAQO,SAAS,kBAAkB,QAA4B;AAC1D,SAAO;AAAA,IACH,MAAM,SAAS;AAAA,IACf,OAAO;AAAA,IACP,MAAM;AAAA,IACN,UAAU;AAAA,IACV,OAAO,EAAE,KAAK,OAAA;AAAA,EAAO;AAE7B;AAOA,SAAS,iBAAiB,MAA6B;AACnD,MAAI,CAAC,QAAQ,KAAK,IAAI,GAAG;AACrB,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,2BAA2B,EAAE,OAAO,MAAM;AAAA,EAClG;AACA,QAAM,QAAQ,OAAO,IAAI;AACzB,SAAO,EAAE,OAAO,MAAM,OAAO,KAAK,MAAM,OAAO,OAAO,KAAA;AAC1D;AAQA,SAAS,cAAc,MAAc,MAAqD;AACtF,QAAM,IAAID,YAAU,KAAK,IAAI;AAC7B,MAAI,MAAM,MAAM;AACZ,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,wBAAwB,IAAI,IAAI,EAAE,OAAO,MAAM,KAAA,CAAM;AAAA,EAC7G;AACA,QAAM,IAAI,OAAO,EAAE,CAAC,CAAC;AACrB,QAAM,QAAQ,OAAO,EAAE,CAAC,CAAC;AACzB,QAAM,MAAM,OAAO,EAAE,CAAC,CAAC;AACvB,QAAM,OAAO,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC;AACjD,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC;AACnD,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC;AACnD,QAAM,KAAK,EAAE,CAAC,MAAM,SAAY,IAAI,WAAW,EAAE,CAAC,CAAC;AACnD,MAAI,QAAQ,KAAK,QAAQ,MAAM,MAAM,KAAK,MAAM,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,IAAI;AAC3F,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,2BAA2B,IAAI,IAAI,EAAE,OAAO,MAAM,KAAA,CAAM;AAAA,EAChH;AAEA,QAAM,IAAI,oBAAI,KAAK,CAAC;AACpB,IAAE,eAAe,GAAG,QAAQ,GAAG,GAAG;AAClC,IAAE,YAAY,MAAM,QAAQ,QAAQ,EAAE;AACtC,MAAI,EAAE,WAAA,MAAiB,OAAO,SAAS,IAAI;AACvC,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,4BAA4B,EAAE,OAAO,MAAM,KAAA,CAAM;AAAA,EACzG;AACA,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,cAAc,EAAE,CAAC,CAAC;AAC1D,SAAO,EAAE,YAAY,SAAS;AAClC;AAQA,SAAS,UAAU,MAAc,MAAoC;AACjE,QAAM,IAAIC,OAAK,KAAK,IAAI;AACxB,MAAI,MAAM,MAAM;AACZ,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,wBAAwB,IAAI,IAAI,EAAE,OAAO,MAAM,KAAA,CAAM;AAAA,EAC7G;AACA,QAAM,OAAO,OAAO,EAAE,CAAC,CAAC;AACxB,QAAM,SAAS,OAAO,EAAE,CAAC,CAAC;AAC1B,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,OAAO,EAAE,CAAC,CAAC;AACnD,QAAM,KAAK,EAAE,CAAC,MAAM,SAAY,IAAI,WAAW,EAAE,CAAC,CAAC;AACnD,MAAI,OAAO,MAAM,SAAS,MAAM,SAAS,IAAI;AACzC,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,IAAI,2BAA2B,IAAI,IAAI,EAAE,OAAO,MAAM,KAAA,CAAM;AAAA,EAChH;AACA,QAAM,UAAU,OAAO,KAAK,UAAU,KAAK,UAAU,MAAO;AAC5D,QAAM,SAAS,EAAE,CAAC,MAAM,SAAY,IAAI,cAAc,EAAE,CAAC,CAAC;AAC1D,QAAM,MAAM,QAAQ,SAAS;AAC7B,UAAS,MAAM,aAAc,cAAc;AAC/C;AAOA,SAAS,WAAW,QAAwB;AACxC,SAAO,KAAK,MAAM,OAAO,KAAK,MAAM,EAAE,IAAI,GAAI;AAClD;AAOA,SAAS,cAAc,MAAsB;AACzC,MAAI,SAAS,OAAO,SAAS,KAAK;AAC9B,WAAO;AAAA,EACX;AACA,QAAM,OAAO,KAAK,WAAW,GAAG,IAAI,KAAK;AACzC,QAAM,SAAS,KAAK,MAAM,CAAC,EAAE,QAAQ,KAAK,EAAE;AAC5C,QAAM,QAAQ,OAAO,OAAO,MAAM,GAAG,CAAC,CAAC;AACvC,QAAM,UAAU,OAAO,SAAS,IAAI,OAAO,OAAO,MAAM,GAAG,CAAC,CAAC,IAAI;AACjE,SAAO,QAAQ,QAAQ,KAAK;AAChC;AAOA,SAAS,KAAK,GAAmB;AAC7B,SAAO,IAAI,KAAK,IAAI,CAAC,KAAK,OAAO,CAAC;AACtC;AAOA,SAAS,KAAK,GAAiB;AAC3B,QAAM,IAAI,EAAE,eAAA;AACZ,QAAM,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC,EAAE,SAAS,GAAG,GAAG;AAC/C,SAAO,IAAI,IAAI,IAAI,GAAG,KAAK;AAC/B;AAUA,SAAS,MAAM,GAAW,GAAW,GAAW,IAAoB;AAChE,QAAM,OAAO,GAAG,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AAC7C,SAAO,OAAO,IAAI,OAAO,GAAG,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AACnE;ACnQA,MAAM,UAAuB,EAAE,OAAO,QAAQ,MAAM,UAAA;AACpD,MAAM,UAAuB,EAAE,OAAO,OAAO,MAAM,UAAA;AACnD,MAAM,OAAoB,EAAE,OAAO,OAAO,MAAM,QAAQ,WAAW,KAAA;AACnE,MAAM,QAAqB,EAAE,OAAO,OAAO,MAAM,QAAA;AACjD,MAAM,SAAsB,EAAE,OAAO,OAAO,MAAM,SAAA;AAClD,MAAM,SAAsB,EAAE,OAAO,UAAU,MAAM,SAAA;AACrD,MAAM,OAAoB,EAAE,OAAO,OAAO,MAAM,YAAY,UAAU,OAAA;AACtE,MAAM,YAAyB,EAAE,OAAO,OAAO,MAAM,YAAY,UAAU,WAAA;AAC3E,MAAM,kBAA+B,EAAE,OAAO,OAAO,MAAM,YAAY,UAAU,gBAAA;AACjF,MAAM,OAAoB,EAAE,OAAO,OAAO,MAAM,YAAY,UAAU,OAAA;AACtE,MAAM,aAA0B,EAAE,OAAO,OAAO,MAAM,YAAY,UAAU,YAAA;AAC5E,MAAM,WAAwB,EAAE,OAAO,UAAU,MAAM,WAAA;AACvD,MAAM,QAAqB,EAAE,OAAO,QAAQ,MAAM,QAAA;AAClD,MAAM,SAAsB,EAAE,OAAO,QAAQ,MAAM,OAAA;AAGnD,MAAM,eAAsD;AAAA,EACxD,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,UAAU;AACd;AAGA,MAAM,kBAAyD;AAAA,EAC3D,SAAS;AAAA,EACT,KAAK;AAAA,EACL,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AACZ;AAGA,MAAM,cAAqD;AAAA,EACvD,KAAK;AAAA,EACL,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AACZ;AAGA,MAAM,gBAAuD;AAAA,EACzD,KAAK;AAAA,EACL,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AAAA,EACN,OAAO;AAAA,EACP,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,WAAW;AAAA,EACX,MAAM;AAAA,EACN,eAAe;AAAA,EACf,UAAU;AAAA,EACV,UAAU;AACd;AAEA,MAAM,SAAmF;AAAA,EACrF,MAAM;AAAA,EACN,SAAS;AAAA,EACT,KAAK;AAAA,EACL,OAAO;AACX;AAEA,MAAMC,iBAAe;AACrB,MAAM,eAAe;AACrB,MAAM,UAAU;AAChB,MAAM,UAAU;AAUT,SAAS,gBACZ,QACA,MACA,MACuB;AACvB,QAAM,QAAQ,OAAO,MAAM;AAC3B,MAAI,MAAM,KAAK,KAAA,EAAO,YAAA;AACtB,MAAIC,QAAO;AACX,MAAI,WAAW,UAAU,IAAI,WAAW,MAAM,KAAK,IAAI,SAAS,GAAG;AAC/D,IAAAA,QAAO;AACP,UAAM,IAAI,MAAM,CAAC;AAAA,EACrB,WAAW,WAAW,WAAW,IAAI,SAAS,IAAI,GAAG;AACjD,IAAAA,QAAO;AACP,UAAM,IAAI,MAAM,GAAG,EAAE;AAAA,EACzB,WAAW,WAAW,SAAS,QAAQ,QAAQ;AAC3C,WAAO;AAAA,MACH,UAAU;AAAA,MACV,OAAO;AAAA,MACP,WAAW;AAAA,MACX,MAAM;AAAA,MACN,MAAM;AAAA,MACN,UAAU;AAAA,MACV,WAAW;AAAA,IAAA;AAAA,EAEnB;AACA,MAAI,QAAQ,MAAM,GAAG;AACrB,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AACA,MAAI,MAAM,SAAS,UAAU,SAAS,UAAU;AAC5C,YAAQ;AAAA,EACZ;AACA,SAAO;AAAA,IACH,UAAU;AAAA,IACV,OAAOA,QAAO,SAAS,MAAM;AAAA,IAC7B,WAAWA,QAAO,MAAM,QAAQ;AAAA,IAChC,MAAM,MAAM;AAAA,IACZ,MAAAA;AAAA,IACA,UAAU,MAAM,YAAY;AAAA,IAC5B,WAAW,MAAM,aAAa;AAAA,EAAA;AAEtC;AAQO,SAAS,WAAW,UAA2C;AAClE,SAAO;AAAA,IACH,UAAU,YAAY;AAAA,IACtB,OAAO;AAAA,IACP,WAAW;AAAA,IACX,MAAM;AAAA,IACN,MAAM;AAAA,IACN,UAAU;AAAA,IACV,WAAW;AAAA,EAAA;AAEnB;AAYO,SAAS,gBAAgB,MAAc,MAAiB,WAAgC,MAAe;AAC1G,UAAQ,MAAA;AAAA,IACJ,KAAK,WAAW;AACZ,YAAM,QAAQ,iBAAiB,IAAI;AACnC,UAAI,UAAU,MAAM;AAChB,cAAM,UAAU,MAAM,IAAI;AAAA,MAC9B;AACA,aAAO;AAAA,IACX;AAAA,IACA,KAAK,WAAW;AACZ,YAAM,UAAU,KAAK,KAAA;AACrB,UAAI,CAACD,eAAa,KAAK,OAAO,GAAG;AAC7B,cAAM,UAAU,MAAM,IAAI;AAAA,MAC9B;AACA,YAAM,QAAQ,OAAO,OAAO;AAC5B,UAAI,QAAQ,WAAW,QAAQ,SAAS;AACpC,cAAM,IAAI,iBAAiB,iBAAiB,IAAI,OAAO,yCAAyC;AAAA,UAC5F,OAAO;AAAA,UACP;AAAA,QAAA,CACH;AAAA,MACL;AACA,aAAO;AAAA,IACX;AAAA,IACA,KAAK,QAAQ;AACT,YAAM,UAAU,KAAK,KAAA;AACrB,UAAI,CAACA,eAAa,KAAK,OAAO,GAAG;AAC7B,cAAM,UAAU,MAAM,IAAI;AAAA,MAC9B;AACA,aAAO,OAAO,OAAO;AAAA,IACzB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AACD,aAAO,iBAAiB,MAAM,IAAI;AAAA,IACtC,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK,YAAY;AACb,UAAI,aAAa,MAAM;AACnB,cAAM,IAAI,iBAAiB,iBAAiB,4CAA4C,EAAE,MAAM;AAAA,MACpG;AACA,aAAO,cAAc,KAAK,KAAA,GAAQ,QAAQ,EAAE;AAAA,IAChD;AAAA,IACA,KAAK;AACD,aAAO,eAAe,IAAI;AAAA,IAC9B,KAAK;AACD,aAAO,cAAc,IAAI;AAAA,IAC7B,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,sBAAsB,IAAI,IAAI,EAAE,MAAM,MAAM;AAAA,IAC5F;AAAA,EAAA;AAER;AAQO,SAAS,iBAAiB,MAA8B;AAC3D,UAAQ,KAAK,KAAA,EAAO,YAAA,GAAY;AAAA,IAC5B,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB;AAUO,SAAS,iBAAiB,MAAc,OAAkB,UAAkB;AAC/E,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,aAAa,KAAK,OAAO,GAAG;AAC5B,WAAO,OAAO,OAAO;AAAA,EACzB;AACA,UAAQ,SAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO,OAAO;AAAA,IAClB;AACI,YAAM,UAAU,MAAM,IAAI;AAAA,EAAA;AAEtC;AASO,SAAS,oBAAoB,MAAuB;AACvD,QAAM,UAAU,KAAK,KAAA;AACrB,SAAO,eAAe,KAAK,OAAO,KAAK,OAAO,OAAO,OAAO,QAAQ,WAAW,GAAG,IAAI,YAAY;AACtG;AASO,SAAS,eAAe,MAAyD;AACpF,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,SAAS,GAAG,GAAG;AACpD,UAAM,UAAU,MAAM,OAAO;AAAA,EACjC;AACA,QAAM,MAAuC,CAAA;AAC7C,QAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAA;AACnC,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO;AAAA,EACX;AACA,aAAW,QAAQ,MAAM,MAAM,GAAG,GAAG;AACjC,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,QAAI,QAAQ,GAAG;AACX,YAAM,UAAU,MAAM,OAAO;AAAA,IACjC;AACA,UAAM,MAAM,KACP,MAAM,GAAG,KAAK,EACd,OACA,WAAW,wBAAwB,EAAE;AAC1C,UAAM,MAAM,KAAK,MAAM,QAAQ,CAAC,EAAE,KAAA;AAClC,QAAI,IAAI,WAAW,GAAG;AAClB,YAAM,UAAU,MAAM,OAAO;AAAA,IACjC;AACA,QAAI,aAAa,KAAK,GAAG,GAAG;AACxB,UAAI,GAAG,IAAI,OAAO,GAAG;AAAA,IACzB,WAAY,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,KAAO,IAAI,WAAW,GAAG,KAAK,IAAI,SAAS,GAAG,GAAI;AACjG,UAAI,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE;AAAA,IAC9B,OAAO;AACH,YAAM,UAAU,MAAM,OAAO;AAAA,IACjC;AAAA,EACJ;AACA,SAAO;AACX;AAOA,SAAS,cAAc,MAAuB;AAC1C,MAAI;AACA,WAAO,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACJ,UAAM,UAAU,MAAM,MAAM;AAAA,EAChC;AACJ;AAQA,SAAS,UAAU,MAAc,MAAmC;AAChE,QAAM,QAAQ,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC,QAAQ;AAC7D,SAAO,IAAI,iBAAiB,iBAAiB,IAAI,KAAK,YAAY,YAAY,IAAI,CAAC,IAAI,EAAE,OAAO,MAAM,MAAM;AAChH;AC/WO,SAAS,cAAc,MAAc,QAA8B;AACtE,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,QAAQ,WAAW,GAAG;AACtB,WAAO,CAAA;AAAA,EACX;AACA,UAAQ,QAAA;AAAA,IACJ,KAAK,QAAQ;AACT,UAAI,YAAY,OAAO,GAAG;AACtB,eAAO,eAAe,OAAO;AAAA,MACjC;AACA,YAAM,YAAY,eAAe,SAAS,CAAC,KAAK,KAAK,GAAG,CAAC;AACzD,aAAO,cAAc,OAAO,CAAC,OAAO,IAAI,QAAQ,SAAS,SAAS;AAAA,IACtE;AAAA,IACA,KAAK;AACD,aAAO,YAAY,OAAO,IAAI,eAAe,OAAO,IAAI,CAAC,OAAO;AAAA,IACpE,KAAK;AACD,aAAO,QAAQ,SAAS,GAAG;AAAA,IAC/B,KAAK;AACD,aAAO,QAAQ,SAAS,GAAG;AAAA,IAC/B,KAAK;AACD,aAAO,QAAQ,SAAS,GAAG;AAAA,IAC/B,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,uBAAuB,IAAI,IAAI;AAAA,QACvE,QAAQ;AAAA,QACR,OAAO;AAAA,MAAA,CACV;AAAA,IACL;AAAA,EAAA;AAER;AAWO,SAAS,aAAa,OAA0B,QAA4B;AAC/E,UAAQ,QAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AACD,aAAO,IAAI,MAAM,IAAI,gBAAgB,EAAE,KAAK,IAAI,CAAC;AAAA,IACrD,KAAK;AACD,aAAO,MAAM,KAAK,GAAG;AAAA,IACzB,KAAK;AACD,aAAO,MAAM,KAAK,GAAG;AAAA,IACzB,KAAK;AACD,aAAO,MAAM,KAAK,GAAG;AAAA,IACzB,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,uBAAuB,IAAI,IAAI;AAAA,QACvE,QAAQ;AAAA,QACR,OAAO;AAAA,MAAA,CACV;AAAA,IACL;AAAA,EAAA;AAER;AAOA,SAAS,YAAY,MAAuB;AACxC,SAAO,KAAK,UAAU,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG;AACxE;AAQA,SAAS,eAAe,MAAc,YAA8C;AAChF,aAAW,aAAa,YAAY;AAChC,QAAI,KAAK,SAAS,SAAS,GAAG;AAC1B,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAQA,SAAS,QAAQ,MAAc,WAA6B;AACxD,SAAO,KAAK,MAAM,SAAS,EAAE,IAAI,CAAC,SAAS,KAAK,MAAM;AAC1D;AAQA,SAAS,eAAe,MAAwB;AAC5C,QAAM,QAAQ,KAAK,MAAM,GAAG,EAAE;AAC9B,QAAM,QAAkB,CAAA;AACxB,QAAM,IAAI,MAAM;AAChB,MAAI,MAAM,OAAO,WAAW,GAAG;AAC3B,WAAO;AAAA,EACX;AACA,MAAI,IAAI;AACR,aAAS;AACL,WAAO,IAAI,KAAK,QAAQ,MAAM,WAAW,CAAC,CAAC,GAAG;AAC1C;AAAA,IACJ;AACA,QAAI,OAAO;AACX,QAAI,IAAI,MAAM,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,MAAM;AACjD,YAAM,QAAQ,MAAM,CAAC;AACrB;AACA,aAAO,IAAI,GAAG;AACV,YAAI,MAAM,CAAC,MAAM,OAAO;AACpB,cAAI,MAAM,IAAI,CAAC,MAAM,OAAO;AACxB,oBAAQ;AACR,iBAAK;AACL;AAAA,UACJ;AACA;AACA;AAAA,QACJ;AACA,gBAAQ,MAAM,CAAC;AACf;AAAA,MACJ;AAAA,IACJ;AAEA,QAAI,MAAM,MAAM,QAAQ,KAAK,CAAC;AAC9B,QAAI,MAAM,GAAG;AACT,YAAM;AAAA,IACV;AACA,YAAQ,MAAM,MAAM,GAAG,GAAG,EAAE,KAAA;AAC5B,UAAM,KAAK,IAAI;AACf,QAAI;AACJ,QAAI,KAAK,GAAG;AACR,aAAO;AAAA,IACX;AACA;AACA,QAAI,MAAM,MAAM,CAAC,EAAE,KAAA,EAAO,WAAW,GAAG;AACpC,YAAM,KAAK,EAAE;AACb,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAOA,SAAS,QAAQ,GAAoB;AACjC,SAAO,MAAM,MAAM,MAAM,KAAK,MAAM,MAAM,MAAM;AACpD;AAOA,SAAS,iBAAiB,MAAsB;AAC5C,MAAI,KAAK,WAAW,KAAK,WAAW,KAAK,IAAI,KAAK,SAAS,KAAK,QAAQ;AACpE,WAAO,IAAI,KAAK,WAAW,KAAK,IAAI,CAAC;AAAA,EACzC;AACA,SAAO;AACX;AC1FO,MAAM,oBAAoB;AAK1B,MAAM,eAAe;AAQrB,MAAM,mBAAmB;AAQzB,SAAS,iBAAiB,OAAqD;AAClF,QAAM,SAA6B,CAAA;AACnC,QAAM,aAAa,MAAM,cAAc;AACvC,MAAI;AACJ,MAAI,MAAM,SAAS,MAAM;AACrB,WAAO,WAAW,IAAI;AAAA,EAC1B,OAAO;AACH,UAAM,SAAS,gBAAgB,MAAM,QAAQ,MAAM,MAAM,MAAM,IAAI;AACnE,QAAI,WAAW,MAAM;AACjB,aAAO,WAAW,MAAM,IAAI;AAC5B,aAAO,KAAK;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,GAAG,MAAM,MAAM,UAAU,MAAM,IAAI;AAAA,MAAA,CAC/C;AAAA,IACL,OAAO;AACH,aAAO;AAAA,IACX;AAAA,EACJ;AACA,QAAM,EAAE,MAAM,YAAY,YAAY,KAAK;AAC3C,MAAI,SAAS;AACT,WAAO,KAAK;AAAA,MACR,UAAU;AAAA,MACV,MAAM;AAAA,MACN,SAAS,cAAc,MAAM,SAAS,MAAM,MAAM,EAAE,iBAAiB,IAAI;AAAA,IAAA,CAC5E;AAAA,EACL;AACA,QAAM,OAAmB;AAAA,IACrB;AAAA,IACA,OAAO,KAAK;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,MACJ,QAAQ,MAAM;AAAA,MACd,IAAI,MAAM;AAAA,MACV,OAAO,MAAM,UAAU,QAAQ,MAAM,UAAU,OAAO,MAAM,QAAQ;AAAA,MACpE,MAAM,MAAM;AAAA,MACZ,WAAW,MAAM,aAAa;AAAA,IAAA;AAAA,EAClC;AAEJ,MAAI,KAAK,MAAM;AACX,SAAK,YAAY,KAAK,aAAa;AAAA,EACvC;AACA,MAAI,MAAM,SAAS,UAAa,MAAM,SAAS,MAAM;AACjD,SAAK,OAAO,MAAM;AAAA,EACtB;AACA,MAAI,MAAM,YAAY,MAAM;AACxB,SAAK,UAAU;AAAA,EACnB;AACA,QAAM,cAAc,MAAM,eAAe;AACzC,MAAI,gBAAgB,MAAM;AACtB,QAAI;AACA,YAAM,UAAU,cAAc,aAAa,UAAU,EAAE,IAAI,CAAC,SAAS,UAAU,MAAM,IAAI,CAAC;AAC1F,WAAK,UAAU;AACf,UAAI,KAAK,UAAU,YAAY,CAAC,KAAK,QAAQ,QAAQ,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AACtF,aAAK,QAAQ;AACb,eAAO,EAAE,GAAG,MAAM,OAAO,OAAA;AAAA,MAC7B;AAAA,IACJ,SAAS,KAAK;AACV,aAAO,KAAK;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,YAAY,WAAW,mBAAmB,IAAI,qBAAqB,KAAK,QAAQ,KAAKE,YAAU,GAAG,CAAC;AAAA,MAAA,CAC/G;AAAA,IACL;AAAA,EACJ;AACA,QAAM,cAAc,MAAM,eAAe;AACzC,MAAI,gBAAgB,MAAM;AACtB,QAAI;AACA,WAAK,UAAU,mBAAmB,aAAa,MAAM,UAAU;AAAA,IACnE,SAAS,KAAK;AACV,aAAO,KAAK;AAAA,QACR,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,YAAY,WAAW,mBAAmB,IAAI,uBAAuB,KAAK,QAAQ,KAAKA,YAAU,GAAG,CAAC;AAAA,MAAA,CACjH;AAAA,IACL;AAAA,EACJ;AACA,QAAM,YAAY,KAAK,aAAa,QAAQ,CAAC,KAAK,OAAO,kBAAkB,IAAI,IAAI;AACnF,SAAO,EAAE,MAAM,MAAM,YAAY,WAAW,SAAS,OAAA;AACzD;AAWO,SAAS,mBAAmB,MAAc,MAAwB,YAAiC;AACtG,MAAI,KAAK,MAAM;AACX,WAAO,cAAc,MAAM,UAAU,EAAE,IAAI,CAAC,SAAS,UAAU,MAAM,IAAI,CAAC;AAAA,EAC9E;AACA,SAAO,UAAU,MAAM,IAAI;AAC/B;AAUO,SAAS,sBAAsB,MAAc,MAAuC;AACvF,MAAI,KAAK,aAAa,QAAQ,KAAK,MAAM;AACrC,UAAM,IAAI,iBAAiB,iBAAiB,IAAI,KAAK,QAAQ,mCAAmC;AAAA,MAC5F,UAAU,KAAK;AAAA,IAAA,CAClB;AAAA,EACL;AACA,SAAO,cAAc,KAAK,KAAA,GAAQ,KAAK,QAAQ;AACnD;AAEA,MAAM,eAAe;AAWd,SAAS,eAAe,MAAwB,MAAuB;AAC1E,MAAI,CAAC,KAAK,WAAW;AACjB,WAAO;AAAA,EACX;AACA,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,CAAC,aAAa,KAAK,OAAO,GAAG;AAC7B,WAAO;AAAA,EACX;AACA,QAAM,IAAI,OAAO,OAAO;AACxB,SAAO,CAAC,OAAO,SAAS,CAAC,KAAK,OAAO,OAAO,MAAM,OAAO,CAAC;AAC9D;AAUO,SAAS,UAAU,MAAiB,QAAyB,MAAgC;AAChG,SAAO,WAAW,SAAS,KAAK,kBAAkB,IAAI,IAAI,KAAK,kBAAkB,IAAI;AACzF;AAgCO,SAAS,gBACZ,MACA,QACA,MACA,QACA,OACmB;AACnB,MAAI,UAAU;AACd,MAAI,UAAU;AACd,MAAI,cAAc;AAClB,aAAS;AACL,QAAI;AACA,aAAO,EAAE,QAAQ,UAAU,MAAM,QAAQ,OAAO,GAAG,MAAM,SAAS,SAAS,YAAA;AAAA,IAC/E,SAAS,KAAK;AACV,UAAI,EAAE,eAAe,mBAAmB;AACpC,cAAM;AAAA,MACV;AACA,UAAI,IAAI,SAAS,qBAAqB,CAAC,SAAS;AAC5C,cAAM,QAAQ,QAAQ,MAAM,MAAM;AAClC,cAAM,KAAK,QAAQ,QAAQ,MAAM;AACjC,cAAM,OAAO,iBAAiB,QAAQ,MAAM,IAAI,KAAK;AACrD,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA,GAAG,MAAM,YAAY,QAAQ,IAAI,iBAAiB,IAAI;AAAA,UACtD,EAAE,GAAG,OAAO,SAAS,OAAO,WAAW,QAAQ,KAAA;AAAA,QAAK;AAExD,kBAAU,EAAE,GAAG,SAAS,KAAA;AACxB,kBAAU;AACV;AAAA,MACJ;AACA,UAAI,IAAI,SAAS,sBAAsB,QAAQ,SAAS,UAAa,CAAC,aAAa;AAC/E,cAAM,SAAS,OAAO,IAAI,QAAQ,WAAW,WAAW,IAAI,QAAQ,SAAS;AAC7E,eAAO;AAAA,UACH;AAAA,UACA;AAAA,UACA,GAAG,MAAM,YAAY,QAAQ,IAAI,eAAe,QAAQ,IAAI,WAAW,MAAM;AAAA,UAC7E,EAAE,GAAG,OAAO,SAAS,OAAO,WAAW,QAAQ,KAAA;AAAA,QAAK;AAExD,cAAM,EAAE,MAAM,OAAO,GAAG,gBAAgB;AACxC,kBAAU;AACV,sBAAc;AACd;AAAA,MACJ;AACA,YAAM;AAAA,IACV;AAAA,EACJ;AACJ;AAcO,SAAS,iBACZ,MACA,QACA,WACA,QACY;AACZ,MAAI;AACA,WAAO,UAAU,MAAM,QAAQ,SAAS;AAAA,EAC5C,SAAS,KAAK;AACV,QAAI,eAAe,oBAAoB,IAAI,SAAS,oBAAoB;AACpE,cAAQ;AAAA,QACJ;AAAA,QACA;AAAA,QACA,GAAG,MAAM,YAAY,UAAU,IAAI;AAAA,QACnC,EAAE,SAAS,UAAU,KAAA;AAAA,MAAK;AAE9B,YAAM,EAAE,MAAM,OAAO,GAAG,gBAAgB;AACxC,aAAO,UAAU,MAAM,QAAQ,WAAW;AAAA,IAC9C;AACA,UAAM;AAAA,EACV;AACJ;AASO,SAAS,QAAQ,MAAiB,QAAoD;AACzF,SAAO,CAAC,UACH,WAAW,SAAS,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,OAAO;AAChF;AAWO,SAAS,iBAAiB,MAAc,IAAmB,OAA0C;AACxG,MAAI,CAAC,MAAM,IAAI,GAAG;AACd,WAAO;AAAA,EACX;AACA,QAAM,OAAO,OAAO,OAAO,OAAO,GAAG,IAAI,IAAI,EAAE;AAC/C,MAAI,OAAO,QAAQ,CAAC,MAAM,IAAI,GAAG;AAC7B,WAAO;AAAA,EACX;AACA,WAAS,IAAI,KAAK,KAAK;AACnB,UAAM,YAAY,GAAG,IAAI,IAAI,CAAC;AAC9B,QAAI,CAAC,MAAM,SAAS,GAAG;AACnB,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;AAQO,MAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAWvB,YAAY,aAAqB,kBAAkB;AAVnD,SAAiB,+BAAe,IAAA;AAEhC,SAAQ,WAAW;AASf,SAAK,aAAa;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAe;AACf,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,gBAAwB;AACxB,WAAO,KAAK,SAAS;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,UAAmB;AACnB,WAAO,KAAK,YAAY,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,OAAwB;AAC5B,SAAK;AACL,QAAI,KAAK,SAAS,OAAO,KAAK,UAAU;AACpC,WAAK,SAAS,IAAI,KAAK;AAAA,IAC3B;AACA,WAAO,KAAK,aAAa,KAAK;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAA4B;AACxB,WAAO,KAAK,WAAW,KAAK,KAAK,SAAS,OAAO,IAAI,KAAK,WAAW,SAAS;AAAA,EAClF;AACJ;AAOA,SAAS,YAAY,OAAsE;AACvF,QAAM,YAAY,MAAM,UAAU,QAAQ,MAAM,MAAM,SAAS,IAAI,MAAM,QAAQ,MAAM;AACvF,MAAI,cAAc,QAAQ,UAAU,WAAW,GAAG;AAC9C,UAAM,IAAI,iBAAiB,iBAAiB,mDAAmD;AAAA,MAC3F,OAAO;AAAA,MACP,QAAQ,MAAM;AAAA,IAAA,CACjB;AAAA,EACL;AACA,MAAI,MAAM,UAAU,QAAW;AAC3B,WAAO,EAAE,MAAM,WAAW,SAAS,MAAA;AAAA,EACvC;AACA,QAAM,OAAO,iBAAiB,WAAW,MAAM,IAAI,MAAM,KAAK;AAC9D,SAAO,EAAE,MAAM,SAAS,SAAS,UAAA;AACrC;AAQA,SAAS,UAAU,MAAc,MAAiC;AAC9D,SAAO,gBAAgB,MAAM,KAAK,MAAM,KAAK,QAAQ;AACzD;AAOA,SAASA,YAAU,KAAsB;AACrC,SAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC1D;AC5dO,MAAM,kBAAkB;AAOxB,MAAM,cAAc;AAMpB,MAAM,gBAAgB;AAI7B,MAAM,gBAA4B,EAAE,MAAM,iBAAiB,OAAO,QAAQ,MAAM,WAAA;AAChF,MAAM,YAAwB,EAAE,MAAM,aAAa,OAAO,OAAO,MAAM,QAAQ,UAAU,OAAA;AACzF,MAAM,cAA0B,EAAE,MAAM,eAAe,OAAO,QAAQ,MAAM,SAAA;AASrE,MAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+B3B,YAAY,MAAiB,QAA6B,QAAwD;AAxBlH,SAAQ,iBAA+B;AAEvC,SAAQ,aAA2B;AAEnC,SAAQ,eAA6B;AAGrC,SAAQ,YAAY;AAGpB,SAAQ,WAAW;AAEnB,SAAQ,cAAc;AAEtB,SAAQ,YAAY;AAEpB,SAAQ,eAAuB;AAS3B,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,WAAoB;AACpB,WAAO,KAAK,KAAK;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,WAAoB;AACpB,WAAO,KAAK,eAAe;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,UAAU,gBAAyB,OAA4B;AAC3D,SAAK,YAAY;AACjB,QAAI,SAAS;AACb,QAAI,KAAK,WAAW,cAAc,KAAK,WAAW,cAAc;AAC5D,eAAS,KAAK,WAAW;AACzB,UAAI,WAAW,gBAAgB;AAE3B,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,qBAAqB,UAAU,cAAc,CAAC,mBAAmB,UAAU,MAAM,CAAC;AAAA,UAClF,EAAE,MAAM,OAAO,QAAQ,KAAA;AAAA,UACvB,GAAG,qBAAqB,IAAI,UAAU,cAAc,CAAC;AAAA,QAAA;AAAA,MAE7D;AAAA,IACJ;AACA,UAAM,EAAE,SAAS;AACjB,QAAI,KAAK,aAAa,QAAQ;AAC1B,WAAK,cAAA;AACL;AAAA,IACJ;AACA,QAAI;AACA,UAAI,UAAU,KAAK,YAAY,KAAK,CAAC,KAAK,gBAAgB;AACtD,cAAM,SAAS,KAAK;AACpB,aAAK,YAAY,MAAM,EAAE,QAAQ,MAAM;AACvC,aAAK,sBAAA;AACL,aAAK,OAAO,OAAO,iBAAiB;AAAA,MACxC,OAAO;AACH,aAAK,YAAY,MAAM;AAAA,MAC3B;AAAA,IACJ,SAAS,KAAK;AACV,UAAI,EAAE,eAAe,qBAAqB,IAAI,SAAS,cAAc;AACjE,cAAM;AAAA,MACV;AACA,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA,eAAe,UAAU,MAAM,CAAC,oBAAoB,UAAU,KAAK,QAAQ,CAAC,KAAK,SAAS,GAAG,CAAC,cAAc,UAAU,KAAK,QAAQ,CAAC;AAAA,QACpI,EAAE,MAAM,OAAO,QAAQ,KAAA;AAAA,MAAK;AAAA,IAEpC;AACA,SAAK,cAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAsB;AAC1B,QAAI,KAAK,eAAe,iBAAiB,KAAK,KAAK,WAAW,WAAW,MAAM,eAAe;AAC1F,WAAK,sBAAA;AAAA,IACT;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,IAAI,aAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,QAAQ,QAAgB,QAAgB,MAAgB,QAAiB,OAA8B;AACnG,QAAI,CAAC,KAAK,WAAW;AACjB,YAAM,IAAI,iBAAiB,cAAc,gEAAgE;AAAA,QACrG,QAAQ;AAAA,MAAA,CACX;AAAA,IACL;AACA,UAAM,EAAE,SAAS;AACjB,SAAK,eAAe;AACpB,UAAM,eAAe,SAAS;AAC9B,QAAI,iBAAiB,KAAK,YAAY,SAAS,UAAU;AACrD,YAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ,MAAM;AAC7C,WAAK,MAAM,CAAC;AACZ,UAAI,KAAK,mBAAmB,eAAe;AACvC,aAAK,aAAa,KAAK,gBAAgB,GAAG,YAAY;AAAA,MAC1D;AACA,aAAO;AAAA,IACX;AACA,YAAQ,KAAK,QAAA;AAAA,MACT,KAAK;AAAA,MACL,KAAK,cAAc;AAEf,aAAK;AACL,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,GAAG,IAAI,kBAAkB,UAAU,KAAK,QAAQ,CAAC;AAAA,UACjD;AAAA,UACA,GAAG,qBAAqB,IAAI,IAAI;AAAA,QAAA;AAEpC,cAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ,MAAM;AAC7C,aAAK,MAAM,CAAC;AACZ,YAAI,KAAK,mBAAmB,eAAe;AACvC,eAAK,aAAa,KAAK,gBAAgB,GAAG,KAAK,QAAQ;AAAA,QAC3D;AACA,eAAO;AAAA,MACX;AAAA,MACA,KAAK;AACD,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,GAAG,IAAI,cAAc,UAAU,KAAK,QAAQ,CAAC;AAAA,UAC7C;AAAA,QAAA;AAEJ,cAAM,KAAK,OAAO,MAAM,2BAA2B,EAAE,MAAM,sBAAsB;AAAA,MACrF,KAAK;AACD,eAAO,KAAK,OAAO,QAAQ,QAAQ,MAAM,QAAQ,KAAK;AAAA,MAC1D,SAAS;AACL,cAAM,OAAe,KAAK;AAC1B,cAAM,IAAI,iBAAiB,iBAAiB,4BAA4B,IAAI,IAAI;AAAA,UAC5E,QAAQ;AAAA,UACR,OAAO;AAAA,QAAA,CACV;AAAA,MACL;AAAA,IAAA;AAAA,EAER;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,SAAiB;AACjB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,OACJ,QACA,QACA,MACA,QACA,OACM;AACN,UAAM,EAAE,SAAS;AACjB,QAAI,CAAC,KAAK,UAAU;AAEhB,UAAI;AACA,cAAM,SAAS,KAAK;AACpB,aAAK,YAAY,MAAM,EAAE,QAAQ,MAAM;AACvC,aAAK,sBAAA;AACL,aAAK,OAAO,OAAO,iBAAiB;AAAA,MACxC,SAAS,KAAK;AACV,YAAI,EAAE,eAAe,qBAAqB,IAAI,SAAS,cAAc;AACjE,gBAAM;AAAA,QACV;AACA,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,GAAG,UAAU,IAAI,CAAC,qBAAqB,UAAU,KAAK,CAAC;AAAA,UACvD;AAAA,QAAA;AAEJ,cAAM,KAAK,OAAO,MAAM,8CAA8C;AAAA,UAClE,MAAM;AAAA,UACN,QAAQ,IAAI,QAAQ;AAAA,QAAA,CACvB;AAAA,MACL;AACA,UAAI,SAAS,YAAY;AACrB,cAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ,MAAM;AAC7C,aAAK,MAAM,CAAC;AACZ,aAAK,aAAa,KAAK,gBAAgB,GAAG,IAAI;AAC9C,eAAO;AAAA,MACX;AAAA,IACJ;AACA,SAAK,uBAAA;AACL,UAAM,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,MAAM;AACjD,SAAK,MAAM,KAAK;AAChB,UAAM,SAAS,SAAS;AACxB,SAAK,aAAa,KAAK,gBAAgB,OAAO,MAAM;AACpD,SAAK,OAAO,OAAO;AACnB,QAAI,QAAQ;AACR,WAAK,mBAAA;AACL,WAAK,aAAa,KAAK,cAAc,OAAO,IAAI;AAAA,IACpD;AACA,UAAM,IAAI,KAAK,QAAQ,MAAM;AAC7B,UAAM,IAAI,KAAK,QAAQ,MAAM;AAC7B,QAAI,MAAM,GAAG;AAET,aAAO;AAAA,IACX;AACA,UAAM,SAAS,KAAK,QAAQ,QAAQ,QAAQ,MAAM;AAClD,SAAK,MAAM,MAAM;AACjB,SAAK,eAAe;AACpB,SAAK,aAAa,KAAK,gBAAgB,QAAQ,MAAM;AACrD,SAAK,aAAa,KAAK,YAAY,OAAO,MAAM;AAChD,SAAK,aAAa,KAAK,YAAY,QAAQ,KAAK;AAChD,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,MAAM,GAAiB;AAC3B,QAAI,KAAK,YAAY,GAAG;AACpB,WAAK,YAAY;AAAA,IACrB;AACA,SAAK,WAAW,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKQ,wBAA8B;AAClC,SAAK,iBAAiB,KAAK,KAAK,kBAAkB,aAAa;AAC/D,SAAK,aAAa,KAAK,KAAK,kBAAkB,SAAS;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAA+B;AACnC,QAAI,KAAK,eAAe,eAAe;AACnC;AAAA,IACJ;AACA,SAAK,iBAAiB,KAAK,KAAK,kBAAkB,aAAa;AAC/D,SAAK,aAAa,KAAK,KAAK,kBAAkB,SAAS;AACvD,QAAI,KAAK,aAAa,GAAG;AACrB,eAAS,IAAI,KAAK,WAAW,IAAI,KAAK,UAAU,KAAK;AACjD,aAAK,KAAK,aAAa,KAAK,gBAAgB,GAAG,IAAI;AAAA,MACvD;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA,EAGQ,qBAA2B;AAC/B,QAAI,KAAK,iBAAiB,eAAe;AACrC,WAAK,eAAe,KAAK,KAAK,kBAAkB,WAAW;AAAA,IAC/D;AAAA,EACJ;AACJ;AAOA,SAAS,SAAS,KAA+B;AAC7C,QAAM,EAAE,WAAW,IAAI;AACvB,SAAO,OAAO,WAAW,WAAW,SAAS;AACjD;AAOA,SAAS,UAAU,UAA2B;AAC1C,SAAO,WAAW,aAAa;AACnC;AAoEO,SAAS,YAAY,UAAyB,UAA8B,IAAiB;AAChG,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,iBAAiB,WAAW,SAAS,MAAM,OAAO,UAAU,GAAG,MAAM;AAC3E,QAAM,aAAa,WAAW,SAAS,MAAM,OAAO,MAAM,GAAG,KAAK;AAClE,QAAM,eAAe,WAAW,SAAS,MAAM,OAAO,QAAQ,GAAG,MAAM;AACvE,QAAM,EAAE,cAAc;AACtB,QAAM,SAAS,CAAC,MAAsB;AAClC,QAAI,CAAC,YAAY,MAAM,CAAC,GAAG;AACvB,aAAO;AAAA,IACX;AACA,UAAM,OAAO,WAAW,MAAM,CAAC;AAC/B,WAAO,OAAO,SAAS,YAAY,SAAS,gBAAgB,OAAO;AAAA,EACvE;AACA,QAAM,iBAAiB,CAAC,MACpB,mBAAmB,QAAQ,CAAC,eAAe,MAAM,CAAC,KAAK,eAAe,MAAM,CAAC,MAAM;AACvF,QAAM,WAAW,CAAC,MAAuB;AACrC,QAAI,iBAAiB,MAAM;AACvB,aAAO;AAAA,IACX;AACA,QAAI,aAAa,MAAM,CAAC,KAAK,aAAa,MAAM,CAAC,MAAM,MAAM;AACzD,aAAO;AAAA,IACX;AACA,UAAM,OAAO,OAAO,CAAC;AACrB,WAAO,SAAS,iBAAiB,aAAa,MAAM,IAAI,KAAK,aAAa,MAAM,IAAI,MAAM;AAAA,EAC9F;AACA,QAAM,SAAS,CAAC,MAAuB;AACnC,UAAM,OAAO,OAAO,CAAC;AACrB,QAAI,SAAS,iBAAiB,QAAQ,GAAG;AACrC,aAAO;AAAA,IACX;AAEA,WAAO,CAAC,eAAe,CAAC,KAAM,cAAc,SAAS,CAAC;AAAA,EAC1D;AACA,MAAI,cAAc;AAClB,MAAI,iBAAiB,MAAM;AACvB,aAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAChC,UAAI,aAAa,MAAM,CAAC,KAAK,aAAa,MAAM,CAAC,MAAM,QAAQ,OAAO,CAAC,IAAI,GAAG;AAC1E;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,SAAO,EAAE,UAAU,eAAe,MAAM,aAAa,QAAQ,QAAQ,gBAAgB,SAAA;AACzF;AAQA,SAAS,WAAW,QAAuB,OAAsC;AAC7E,SAAO,WAAW,QAAQ,OAAO,UAAU,QAAQ,SAAS;AAChE;AC7gBA,MAAM,oBAAoB;AAUnB,SAAS,YAAY,MAAsB;AAC9C,MAAI,SAAS,QAAQ,kBAAkB,KAAK,IAAI,GAAG;AAC/C,UAAM,IAAI,OAAO,IAAI;AACrB,QAAI,OAAO,cAAc,CAAC,GAAG;AACzB,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAQO,SAAS,uBAAuB,MAAuB;AAC1D,SAAO,OAAO,YAAY,IAAI,MAAM;AACxC;AASO,SAAS,aAAa,MAAc,MAA0B;AACjE,UAAQ,MAAA;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO,YAAY,IAAI;AAAA,IAC3B,KAAK,UAAU;AACX,UAAI,KAAK,OAAO,WAAW,GAAG;AAC1B,cAAM,UAAU,MAAM,4BAA4B;AAAA,MACtD;AACA,YAAM,IAAI,OAAO,IAAI;AACrB,UAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACrB,cAAM,UAAU,MAAM,qBAAqB;AAAA,MAC/C;AACA,aAAO;AAAA,IACX;AAAA,IACA,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,uBAAuB,IAAI,IAAI,EAAE,QAAQ,OAAO,OAAO,KAAA,CAAM;AAAA,IAC7G;AAAA,EAAA;AAER;AASO,SAAS,SAAS,OAAgB,MAA0B;AAC/D,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO,aAAa,OAAO,IAAI;AAAA,EACnC;AACA,MAAI,OAAO,UAAU,UAAU;AAC3B,QAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AACzB,YAAM,UAAU,OAAO,qBAAqB;AAAA,IAChD;AACA,WAAO,SAAS,WAAW,OAAO,KAAK,IAAI;AAAA,EAC/C;AACA,MAAI,SAAS,aAAa,OAAO,UAAU,aAAa,OAAO,UAAU,YAAY,UAAU,OAAO;AAClG,WAAO,OAAO,KAAK;AAAA,EACvB;AACA,QAAM,UAAU,OAAO,KAAK,UAAU,OAAO,SAAS,OAAO,KAAK,6BAA6B,IAAI,GAAG;AAC1G;AAQA,SAAS,UAAU,OAAgB,QAAkC;AACjE,QAAM,QAAQ,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW,QAAQ,OAAO;AACtF,SAAO,IAAI,iBAAiB,gBAAgB,mBAAmB,KAAK,UAAU,KAAK,CAAC,KAAK,MAAM,IAAI;AAAA,IAC/F;AAAA,IACA,OAAO,OAAO,UAAU,WAAW,MAAM,aAAa;AAAA,EAAA,CACzD;AACL;AAOO,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA,EAwBnB,YAAY,MAAkB;AAnB9B,SAAA,aAAa;AAMb,SAAA,YAOW;AAOP,SAAK,OAAO;AACZ,SAAK,YAAY,SAAS,WAAW,oBAAI,QAAQ;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KAAK,MAAsB;AACvB,UAAM,KAAK,aAAa,MAAM,KAAK,IAAI;AACvC,SAAK,YAAY;AACjB,QAAI,KAAK,cAAc,QAAQ,OAAO,OAAO,UAAU;AACnD,YAAM,WAAW,KAAK,UAAU,IAAI,EAAE;AACtC,UAAI,aAAa,QAAW;AACxB,aAAK,UAAU,IAAI,IAAI,IAAI;AAAA,MAC/B,WAAW,aAAa,MAAM;AAC1B,aAAK;AACL,aAAK,YAAY,EAAE,IAAI,MAAM,cAAc,SAAA;AAAA,MAC/C;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAwB;AAC1B,QAAI,OAAO,UAAU,UAAU;AAC3B,aAAO,KAAK,KAAK,KAAK;AAAA,IAC1B;AACA,SAAK,YAAY;AACjB,WAAO,SAAS,OAAO,KAAK,IAAI;AAAA,EACpC;AACJ;ACpGA,MAAM,aAAa;AAGnB,MAAM,YAAY;AAQX,SAAS,kBAAkB,OAA8B;AAC5D,MAAI;AACA,WAAO,IAAI,YAAY,MAAM,KAAA,CAAM,EAAE;AAAA,EACzC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAQA,SAAS,YAAY,MAAiC;AAClD,MAAI,gBAAgB,MAAM,CAAC,KAAM,KAAM,GAAM,CAAI,CAAC,GAAG;AACjD,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,MAAM,CAAC,GAAM,GAAM,KAAM,GAAI,CAAC,GAAG;AACjD,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,MAAM,CAAC,KAAM,KAAM,GAAI,CAAC,GAAG;AAC3C,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,MAAM,CAAC,KAAM,GAAI,CAAC,GAAG;AACrC,WAAO;AAAA,EACX;AACA,MAAI,gBAAgB,MAAM,CAAC,KAAM,GAAI,CAAC,GAAG;AACrC,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAQA,SAAS,gBAAgB,OAAmB,WAAuC;AAC/E,SAAO,MAAM,cAAc,UAAU,UAAU,UAAU,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;AAC3F;AAQA,SAAS,aAAa,MAAiC;AACnD,QAAM,QAAQ,KAAK,IAAI,KAAK,cAAc,GAAG,GAAG;AAChD,MAAI,QAAQ,GAAG;AACX,WAAO;AAAA,EACX;AACA,MAAI,KAAK;AACT,MAAI,KAAK;AACT,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC5B,UAAM,OAAO,KAAK,IAAI,CAAC;AACvB,UAAM,MAAM,KAAK,IAAI,IAAI,CAAC;AAC1B,QAAI,SAAS,KAAK,QAAQ,GAAG;AACzB;AAAA,IACJ,WAAW,SAAS,KAAK,QAAQ,GAAG;AAChC;AAAA,IACJ;AAAA,EACJ;AACA,MAAI,MAAM,QAAQ,KAAK;AACnB,WAAO;AAAA,EACX;AACA,SAAO,MAAM,QAAQ,MAAM,aAAa;AAC5C;AAQA,SAAS,oBAAoB,MAAiC;AAC1D,MAAI,gBAAgB,MAAM,CAAC,IAAM,GAAM,IAAM,CAAI,CAAC,GAAG;AACjD,WAAO;AAAA,EACX;AACA,SAAO,gBAAgB,MAAM,CAAC,GAAM,IAAM,GAAM,EAAI,CAAC,IAAI,aAAa;AAC1E;AAUO,SAAS,YAAY,MAA0C;AAClE,MAAI,OAAO,SAAS,UAAU;AAC1B,QAAI,gBAAgB,MAAM,CAAC,IAAM,GAAI,CAAC,GAAG;AACrC,aAAO;AAAA,IACX;AACA,QAAI,gBAAgB,MAAM,CAAC,IAAM,IAAM,GAAM,CAAI,CAAC,KAAK,gBAAgB,MAAM,CAAC,IAAM,IAAM,GAAM,CAAI,CAAC,GAAG;AACpG,aAAO;AAAA,IACX;AACA,QAAI,gBAAgB,MAAM,CAAC,KAAM,IAAM,IAAM,EAAI,CAAC,GAAG;AACjD,aAAO;AAAA,IACX;AAAA,EACJ;AACA,QAAM,OAAO,OAAO,SAAS,WAAW,KAAK,MAAM,GAAG,EAAE,IAAI,OAAO,cAAc,GAAG,KAAK,SAAS,GAAG,EAAE,CAAC;AACxG,QAAM,UAAU,OAAO,cAAc,KAAM,KAAM,GAAI;AACrD,MAAI,OAAO,KAAK,WAAW,OAAO,IAAI,KAAK,MAAM,CAAC,IAAI;AACtD,SAAO,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;AAC9C,MAAI,KAAK,WAAW,OAAO,GAAG;AAC1B,WAAO;AAAA,EACX;AACA,MAAI,0CAA0C,KAAK,IAAI,GAAG;AACtD,WAAO;AAAA,EACX;AACA,SAAO;AACX;AASA,SAAS,cAAc,MAA2B,QAA6B,SAA4B;AACvG,QAAM,UAAU,YAAY,IAAI;AAChC,MAAI,YAAY,QAAQ,EAAE,QAAQ,QAAQ,QAAQ,QAAQ,WAAW,SAAS,IAAI;AAC9E,WAAO,KAAK,qBAAqB,gBAAgB,OAAO,SAAS,OAAO,MAAM,OAAO;AAAA,EACzF;AACJ;AASA,SAAS,WAAW,GAAW,GAAoB;AAC/C,SAAO,MAAM,KAAM,EAAE,WAAW,QAAQ,KAAK,EAAE,WAAW,QAAQ;AACtE;AAOA,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4Cd,YACqB,QACA,SACnB;AAFmB,SAAA,SAAA;AACA,SAAA,UAAA;AA7CrB,SAAQ,UAA8B;AAEtC,SAAQ,WAAW;AAGnB,SAAQ,cAAc;AAGtB,SAAQ,aAAa;AAGrB,SAAQ,QAAoB,IAAI,WAAW,CAAC;AAG5C,SAAQ,SAAS;AAGjB,SAAQ,WAAW;AAOnB,SAAQ,UAAiE;AAGzE,SAAQ,QAAQ;AAGhB,SAAQ,cAAc;AAGtB,SAAQ,eAAe;AAGvB,SAAQ,eAAe;AAAA,EAUpB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMH,IAAI,UAAmB;AACnB,WAAO,KAAK,YAAY;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAwB;AAC1B,kBAAc,MAAM,KAAK,QAAQ,KAAK,OAAO;AAC7C,UAAM,WAAW,KAAK,QAAQ,YAAY;AAC1C,UAAM,MAAM,YAAY,IAAI;AAC5B,QAAI,QAAQ,cAAc,QAAQ,YAAY;AAC1C,WAAK,OAAO;AAAA,QACR;AAAA,QACA,2BAA2B,IAAI,YAAA,CAAa;AAAA,QAC5C;AAAA,QACA,EAAE,YAAY,EAAA;AAAA,MAAE;AAAA,IAExB;AACA,QAAI,QAAQ,MAAM;AACd,YAAM,SAAS,aAAa,OAAO,OAAO,kBAAkB,QAAQ;AACpE,UAAI,WAAW,QAAQ,WAAW,KAAK;AACnC,aAAK,SAAS,4BAA4B,MAAM,iCAAiC,GAAG,aAAa,GAAG,EAAE;AAAA,MAC1G;AACA,WAAK,iBAAiB,MAAM,KAAK,0BAA0B;AAC3D,WAAK,IAAI,KAAK,KAAK;AACnB;AAAA,IACJ;AACA,QAAI,aAAa,MAAM;AACnB,WAAK,IAAI,UAAU,KAAK;AACxB,WAAK,iBAAiB,MAAM,KAAK,UAAU,0BAA0B;AACrE;AAAA,IACJ;AACA,UAAM,WAAW,oBAAoB,IAAI;AACzC,QAAI,aAAa,MAAM;AACnB,WAAK,iBAAiB,MAAM,UAAU,yCAAyC;AAC/E,WAAK,IAAI,UAAU,KAAK;AACxB;AAAA,IACJ;AACA,UAAM,QAAQ,aAAa,IAAI;AAC/B,QAAI,UAAU,QAAQ,KAAK,QAAQ,iBAAiB,MAAM;AACtD,WAAK,IAAI,OAAO,KAAK;AACrB;AAAA,IACJ;AACA,QAAI,UAAU,MAAM;AAChB,WAAK,OAAO;AAAA,QACR;AAAA,QACA,wBAAwB,KAAK,qFAAqF,KAAK,UAAU,KAAK,CAAC;AAAA,QACvI;AAAA,QACA,EAAE,YAAY,EAAA;AAAA,MAAE;AAAA,IAExB;AACA,UAAM,WAAW,KAAK,SAAS,MAAM,cAAc;AACnD,QAAI,aAAa,MAAM;AACnB,YAAM,YAAY,kBAAkB,QAAQ;AAC5C,UAAI,cAAc,MAAM;AACpB,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,mCAAmC,KAAK,UAAU,QAAQ,CAAC;AAAA,UAC3D,EAAE,SAAS,SAAA;AAAA,QAAS;AAAA,MAE5B,WAAW,UAAU,WAAW,QAAQ,GAAG;AAGvC,aAAK;AAAA,UACD,sBAAsB,KAAK,UAAU,QAAQ,CAAC;AAAA,QAAA;AAAA,MAEtD,WAAW,cAAc,SAAS;AAC9B,aAAK,eAAe;AAAA,MACxB,OAAO;AAGH,aAAK,IAAI,WAAW,KAAK;AACzB;AAAA,MACJ;AAAA,IACJ;AACA,SAAK,IAAI,SAAS,IAAI;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,SAAS,MAAkB,UAAiC;AAChE,UAAM,SAAS,KAAK,QAAQ;AAC5B,WAAO,WAAW,SAAY,OAAO,OAAO,IAAI,YAAY,QAAQ,EAAE,OAAO,IAAI,CAAC;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,MAAkB,QAAgB,KAAmB;AAC1E,UAAM,WAAW,KAAK,SAAS,MAAM,MAAM;AAC3C,UAAM,YAAY,aAAa,OAAO,OAAO,kBAAkB,QAAQ;AACvE,QAAI,cAAc,QAAQ,CAAC,WAAW,WAAW,MAAM,GAAG;AACtD,WAAK;AAAA,QACD,mCAAmC,KAAK,UAAU,QAAQ,CAAC,QAAQ,GAAG,IAAI,MAAM,aAAa,MAAM;AAAA,MAAA;AAAA,IAE3G;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,SAAS,SAAuB;AACpC,SAAK,OAAO,QAAQ,YAAY,wBAAwB,OAAO;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,OAAmB,QAAyB;AAE/C,UAAM,QACF,OAAO,sBAAsB,cAAc,MAAM,kBAAkB,oBAC7D,MAAM,MAAA,IACN;AACV,UAAM,UAAU,KAAK;AACrB,QAAI,KAAK,YAAa,KAAK,eAAe,KAAK,YAAa;AACxD,WAAK,MAAM,OAAO,CAAC,MAAM;AACzB,UAAI,KAAK,YAAY,KAAK,YAAY,MAAM;AACxC,aAAK,OAAO,KAAK,OAAO;AAAA,MAC5B;AAAA,IACJ;AACA,UAAM,OAAO,KAAK,gBAAgB,SAAS,OAAO,MAAM;AACxD,QAAI,KAAK,cAAc;AACnB,WAAK,gBAAgB,OAAO,KAAK,MAAM;AAAA,IAC3C;AACA,QAAI,KAAK,cAAc,KAAK,aAAa;AACrC,WAAK,WAAW,OAAO,IAAI;AAAA,IAC/B;AACA,QAAI,KAAK,eAAe,KAAK,QAAQ,gBAAgB,QAAQ,KAAK,SAAS,IAAI,GAAG;AAE9E,WAAK,OAAO,EAAE,IAAI,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,MAAM,EAAA,CAAG;AAAA,IAC/D;AACA,SAAK,UAAU,MAAM;AACrB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,gBAAgB,SAAsB,OAAmB,QAAyB;AACtF,QAAI;AACA,aAAO,QAAQ,OAAO,OAAO,EAAE,QAAQ;AAAA,IAC3C,SAAS,KAAK;AACV,UAAI,EAAE,eAAe,YAAY;AAC7B,cAAM;AAAA,MACV;AACA,aAAO,KAAK,QAAQ,OAAO,MAAM;AAAA,IACrC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,OAAmB,MAAoB;AACtD,QAAI,kBAAkB,KAAK,IAAI,GAAG;AAC9B,WAAK,aAAa;AAClB,WAAK,QAAQ,IAAI,WAAW,CAAC;AAC7B;AAAA,IACJ;AACA,UAAM,MAAM,KAAK,MAAM,eAAe,IAAI,QAAQ,YAAY,CAAC,KAAK,OAAO,KAAK,CAAC;AACjF,SAAK,QAAQ,IAAI,MAAM,KAAK,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAgB,OAA6B;AACzC,SAAK,cAAc;AACnB,QAAI;AACA,aAAO,MAAA;AAAA,IACX,UAAA;AACI,WAAK,cAAc;AAAA,IACvB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,QAAQ,OAAmB,QAAyB;AACxD,UAAM,MAAM,KAAK,MAAM,eAAe,IAAI,QAAQ,YAAY,CAAC,KAAK,OAAO,KAAK,CAAC;AACjF,UAAM,OAAO,KAAK,SAAS,KAAK,MAAM;AACtC,UAAM,MAAM,KAAK,aAAa,UAAU,YAAY,GAAG,IAAI;AAC3D,UAAM,KAAK,QAAQ,QAAQ,OAAO,IAAI,IAAI;AAC1C,QAAI,KAAK,eAAe,KAAK,YAAY;AAGrC,UAAI,KAAK,YAAY,MAAM;AACvB,eAAO,KAAK,OAAO,KAAK,OAAO;AAAA,MACnC;AACA,UAAI,KAAK,eAAe,KAAK,QAAQ,MAAM;AACvC,eAAO,KAAK,OAAO;AAAA,UACf;AAAA,UACA,yBAAyB,EAAE;AAAA,UAC3B;AAAA,UACA,EAAE,YAAY,GAAA;AAAA,QAAG;AAAA,MAEzB;AACA,UAAI,CAAC,UAAU,KAAK,cAAc,MAAM;AACpC,eAAO,KAAK,OAAO;AAAA,UACf;AAAA,UACA,4DAA4D,EAAE;AAAA,UAC9D;AAAA,UACA,EAAE,YAAY,GAAA;AAAA,QAAG;AAAA,MAEzB;AACA,UAAI,CAAC,eAAe,GAAG,GAAG;AACtB,cAAM,UACF,CAAC,UAAU,KAAK,QAAQ,OAClB,yEACA;AACV,aAAK,OAAO;AAAA,UACR;AAAA,UACA;AAAA,UACA,KAAK,eACC,4DAA4D,EAAE,+GAC9D,yCAAyC,EAAE,GAAG,OAAO;AAAA,QAAA;AAE/D,aAAK,IAAI,gBAAgB,KAAK;AAC9B,aAAK,WAAW;AAChB,aAAK,eAAe;AACpB,aAAK,gBAAgB,KAAK,IAAI;AAC9B,eAAQ,KAAK,QAAwB,OAAO,KAAK,EAAE,QAAQ;AAAA,MAC/D;AAAA,IACJ;AACA,QAAI,KAAK,aAAa,SAAS;AAC3B,YAAM,QAAQ,KAAK,cAAc,gEAAgE;AACjG,aAAO,KAAK,OAAO,KAAK,mBAAmB,yBAAyB,EAAE,GAAG,KAAK,IAAI,QAAW;AAAA,QACzF,YAAY;AAAA,MAAA,CACf;AAAA,IACL;AACA,UAAM,MAAM,KAAK,SAAS,MAAM;AAChC,UAAM,MAAM,CAAC,UAAU,KAAK,SAAS,WAAW,QAAQ,KAAK,MAAM,MAAM;AACzE,WAAO,KAAK,OAAO;AAAA,MACf;AAAA,MACA,MACM,8BAA8B,KAAK,QAAQ,sBAAsB,MAAM,CAAC,2BACxE,uBAAuB,IAAI,kBAAkB,KAAK,QAAQ;AAAA,MAChE;AAAA,MACA,EAAE,YAAY,MAAM,MAAM,IAAI,KAAA;AAAA,IAAK;AAAA,EAE3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,gBAAgB,OAAmB,OAAqB;AAC5D,UAAM,KAAK,kBAAkB,KAAK;AAClC,QAAI,KAAK,GAAG;AACR;AAAA,IACJ;AACA,SAAK,eAAe;AACpB,SAAK,OAAO;AAAA,MACR;AAAA,MACA;AAAA,MACA,wDAAwD,QAAQ,EAAE;AAAA,IAAA;AAAA,EAE1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,MAAM,OAAmB,OAAsB;AACnD,QAAI,KAAK,YAAY,QAAQ,MAAM,eAAe,GAAG;AACjD;AAAA,IACJ;AACA,QAAI,KAAK,SAAS,GAAG;AACjB,WAAK,UAAU,EAAE,IAAI,KAAK,OAAO,MAAM,GAAA;AACvC;AAAA,IACJ;AAEA,UAAM,QAAQ,YAAY,OAAO,IAAI;AACrC,QAAI,SAAS,GAAG;AACZ,WAAK,UAAU,EAAE,IAAI,KAAK,SAAS,OAAO,MAAM,MAAM,KAAK,EAAA;AAAA,IAC/D,WAAW,CAAC,SAAS,MAAM,MAAM,aAAa,CAAC,MAAM,IAAM;AACvD,WAAK,QAAQ,KAAK,SAAS,MAAM,aAAa;AAAA,IAClD;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,OAAO,SAAgE;AAC3E,UAAM,MAAM,QAAQ,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACrD,WAAO,KAAK,OAAO;AAAA,MACf;AAAA,MACA,sDAAsD,GAAG,YAAY,QAAQ,EAAE;AAAA,MAC/E;AAAA,MACA,EAAE,YAAY,QAAQ,GAAA;AAAA,IAAG;AAAA,EAEjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,IAAI,OAAe,aAA4B;AACnD,SAAK,UAAU,IAAI,YAAY,OAAO,EAAE,OAAO,MAAM,WAAW,MAAM;AACtE,SAAK,WAAW,KAAK,QAAQ;AAC7B,SAAK,cAAc;AAAA,EACvB;AACJ;AAOA,SAAS,kBAAkB,OAA2B;AAClD,WAAS,IAAI,GAAG,IAAI,MAAM,YAAY,KAAK;AACvC,QAAI,MAAM,CAAC,KAAK,OAAQ,iBAAiB,OAAO,CAAC,GAAG;AAChD,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAQA,SAAS,eAAe,OAA4B;AAChD,QAAM,IAAI,MAAM,UAAU,CAAC,MAAM,KAAK,GAAI;AAC1C,SAAO,KAAK,KAAK,iBAAiB,OAAO,CAAC;AAC9C;AAGA,MAAM,cAAc,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM;AAQ5D,SAAS,iBAAiB,OAAmB,GAAoB;AAC7D,QAAM,OAAO,MAAM,CAAC;AACpB,MAAI,SAAS;AACb,MAAI,QAAQ,OAAQ,QAAQ,KAAM;AAC9B,aAAS;AAAA,EACb,WAAW,QAAQ,OAAQ,QAAQ,KAAM;AACrC,aAAS;AAAA,EACb,WAAW,QAAQ,OAAQ,QAAQ,KAAM;AACrC,aAAS;AAAA,EACb;AACA,MAAI,WAAW,KAAK,IAAI,SAAS,MAAM,YAAY;AAC/C,WAAO;AAAA,EACX;AACA,MAAI;AACA,gBAAY,OAAO,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC;AAChD,WAAO;AAAA,EACX,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAGA,MAAM,aAAgC;AAAA,EAClC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACxG;AAAA,EAAQ;AACZ;AAUA,SAAS,YAAY,OAAgC;AACjD,MAAI,IAAI;AACR,SAAO,IAAI,MAAM,YAAY;AACzB,UAAM,OAAO,MAAM,CAAC;AACpB,UAAM,WAAW,OAAO,MAAO,iBAAiB,aAAa,IAAI;AACjE,QAAI,aAAa,MAAM;AACnB,aAAO,EAAE,OAAO,GAAG,WAAW,OAAO,KAAK,MAAA;AAAA,IAC9C;AACA,UAAM,UAAU,kBAAkB,OAAO,GAAG,QAAQ;AACpD,QAAI,YAAY,MAAM;AAClB,aAAO;AAAA,IACX;AACA,SAAK,SAAS,OAAO;AAAA,EACzB;AACA,SAAO,EAAE,OAAO,GAAG,WAAW,OAAO,KAAK,MAAA;AAC9C;AAgBA,MAAM,iBAA+B,EAAE,MAAM,GAAG,KAAK,KAAM,MAAM,IAAA;AAOjE,SAAS,aAAa,MAAmC;AACrD,MAAI,QAAQ,OAAQ,QAAQ,KAAM;AAC9B,WAAO,EAAE,MAAM,GAAG,KAAK,KAAM,MAAM,IAAA;AAAA,EACvC;AACA,MAAI,QAAQ,OAAQ,QAAQ,KAAM;AAC9B,WAAO,EAAE,MAAM,GAAG,KAAK,SAAS,MAAO,MAAO,KAAM,MAAM,SAAS,MAAO,MAAO,IAAA;AAAA,EACrF;AACA,MAAI,QAAQ,OAAQ,QAAQ,KAAM;AAC9B,WAAO,EAAE,MAAM,GAAG,KAAK,SAAS,MAAO,MAAO,KAAM,MAAM,SAAS,MAAO,MAAO,IAAA;AAAA,EACrF;AACA,SAAO;AACX;AASA,SAAS,kBAAkB,OAAmB,GAAW,UAA4C;AACjG,WAAS,IAAI,GAAG,KAAK,SAAS,MAAM,KAAK;AACrC,QAAI,IAAI,KAAK,MAAM,YAAY;AAC3B,aAAO,EAAE,OAAO,GAAG,WAAW,IAAI,GAAG,KAAK,KAAA;AAAA,IAC9C;AACA,UAAM,OAAO,MAAM,IAAI,CAAC;AACxB,UAAM,MAAM,MAAM,IAAI,SAAS,MAAM;AACrC,UAAM,OAAO,MAAM,IAAI,SAAS,OAAO;AACvC,QAAI,OAAO,OAAO,OAAO,MAAM;AAC3B,aAAO,EAAE,OAAO,GAAG,WAAW,OAAO,KAAK,MAAA;AAAA,IAC9C;AAAA,EACJ;AACA,SAAO;AACX;AASA,SAAS,YAAY,OAAmB,OAAwB;AAC5D,QAAM,OAAO,MAAM,aAAa;AAChC,SAAO,MAAM;AAAA,IACT,CAAC,GAAG,MACA,IAAI,MAAQ,MAAM,KAAQ,MAAM,MAAQ,MAAM,MAAQ,MAAM,MAAQ,EAAE,MAAM,MAAQ,SAAS,MAAM;AAAA,EAAA;AAE/G;AAaO,SAAS,gBAAgB,OAAmB,QAAQ,OAAe;AACtE,MAAI;AACA,WAAO,IAAI,YAAY,SAAS,EAAE,OAAO,MAAM,WAAW,KAAA,CAAM,EAAE,OAAO,KAAK;AAAA,EAClF,QAAQ;AACJ,QAAI,CAAC,OAAO;AACR,aAAO,IAAI,YAAY,cAAc,EAAE,OAAO,KAAK;AAAA,IACvD;AACA,QAAI,MAAM;AACV,eAAW,QAAQ,OAAO;AACtB,aAAO,OAAO,cAAc,OAAO,MAAO,OAAO,WAAW,OAAO,GAAI,CAAC;AAAA,IAC5E;AACA,WAAO;AAAA,EACX;AACJ;AAOA,SAAS,YAAY,QAA2C;AAC5D,MAAI,OAAO,WAAW,GAAG;AACrB,WAAO,OAAO,CAAC;AAAA,EACnB;AACA,QAAM,MAAM,IAAI,WAAW,OAAO,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC,CAAC;AAC3E,MAAI,SAAS;AACb,aAAW,SAAS,QAAQ;AACxB,QAAI,IAAI,OAAO,MAAM;AACrB,cAAU,MAAM;AAAA,EACpB;AACA,SAAO;AACX;AAQA,MAAM,eAAe,MAAM;AAE3B,MAAM,MAAM,OAAO,aAAa,KAAM;AAGtC,MAAM,MAAM,OAAO,cAAc,EAAI;AAM9B,MAAM,kBAAkB,KAAK,KAAK;AAMzC,MAAM,oBAAoB,IAAI;AAAA,EAC1B,IAAI,OAAO,cAAc,CAAI,CAAC,IAAI,OAAO,cAAc,GAAM,IAAM,EAAI,CAAC,IAAI,OAAO,cAAc,IAAM,GAAI,CAAC,IAAI,OAAO,cAAc,GAAI,CAAC,GAAG,GAAG;AACpJ;AAQO,SAAS,cAAc,OAAsC;AAChE,MAAI,OAAO,UAAU,YAAY,QAAQ,KAAK,MAAM,MAAM;AACtD,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC7C,WAAO;AAAA,EACX;AACA,SAAO,OAAQ,MAAkC,cAAc,cAAc,OAAO,iBAAiB;AACzG;AAOA,SAAS,QAAQ,OAAmC;AAChD,MAAI,iBAAiB,YAAY;AAC7B,WAAO;AAAA,EACX;AACA,MAAI,YAAY,OAAO,KAAK,KAAK,OAAO,UAAU,SAAS,KAAK,KAAK,MAAM,uBAAuB;AAC9F,WAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AAAA,EAC1E;AACA,SAAO;AACX;AASO,SAAS,eAAe,OAA6B;AACxD,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AACA,QAAM,QAAQ,QAAQ,KAAK;AAC3B,MAAI,UAAU,MAAM;AAChB,WAAO;AAAA,EACX;AACA,MAAI,cAAc,KAAK,GAAG;AACtB,WAAO;AAAA,EACX;AACA,QAAM,MAAM,OAAO,UAAU,SAAS,KAAK,KAAK,EAAE,MAAM,GAAG,EAAE;AAC7D,MAAI,QAAgB,UAAU,OAAO,SAAS,OAAO;AACrD,MAAI,MAAM;AACV,MAAI,QAAQ,iBAAiB,QAAQ,qBAAqB;AACtD,YAAQ,MAAM,GAAG;AACjB,UAAM;AAAA,EACV,WAAW,YAAY,OAAO,KAAK,GAAG;AAClC,YAAQ,KAAK,GAAG;AAChB,UAAM;AAAA,EACV,WAAW,OAAO,UAAU,YAAY,UAAU,MAAM;AACpD,UAAM,SAAS;AACf,QAAI,OAAO,OAAO,WAAW,cAAc,OAAO,OAAO,SAAS,UAAU;AACxE,cAAQ;AACR,YAAM;AAAA,IACV,WAAW,UAAU,UAAU,OAAO,OAAO,gBAAgB,YAAY;AACrE,cAAQ;AACR,YAAM;AAAA,IACV,OAAO;AACH,cAAQ,cAAc,GAAG;AAAA,IAC7B;AAAA,EACJ;AACA,QAAM,IAAI;AAAA,IACN;AAAA,IACA,gBAAgB,KAAK,+GAA+G,GAAG;AAAA,IACvI,EAAE,QAAQ,cAAc,MAAA;AAAA,EAAM;AAEtC;AASO,SAAS,YAAY,OAAmC;AAC3D,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO,MAAM;AAAA,EACjB;AACA,MAAI,iBAAiB,YAAY;AAC7B,WAAO,MAAM;AAAA,EACjB;AACA,SAAO;AACX;AAQA,SAAS,WAAW,MAAsB;AACtC,MAAI,QAAQ;AACZ,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,QAAQ;AACpB,UAAM,IAAI,KAAK,WAAW,CAAC;AAC3B,UAAM,OAAO,KAAK,SAAU,KAAK,UAAW,KAAK,WAAW,IAAI,CAAC,IAAI,WAAY;AACjF,QAAI,IAAI,KAAM;AACV;AAAA,IACJ,WAAW,IAAI,MAAO;AAClB,eAAS;AAAA,IACb,OAAO;AACH,eAAS,OAAO,IAAI;AAAA,IACxB;AACA,SAAK,OAAO,IAAI;AAAA,EACpB;AACA,SAAO;AACX;AAWO,SAAS,eAAe,QAA8C;AACzE,MAAI,CAAC,QAAQ,SAAS;AAClB;AAAA,EACJ;AACA,QAAM,EAAE,WAAgC;AACxC,MAAI,WAAW,QAAW;AACtB,UAAM,WAAA;AAAA,EACV;AAEA,QAAM;AACV;AAMA,SAAS,aAAoB;AACzB,QAAM,UAAU;AAChB,MAAI,OAAO,iBAAiB,YAAY;AACpC,WAAO,IAAI,aAAa,SAAS,YAAY;AAAA,EACjD;AACA,QAAM,MAAM,IAAI,MAAM,OAAO;AAC7B,MAAI,OAAO;AACX,SAAO;AACX;AAWO,SAAS,UAAa,SAAqB,QAAwC;AACtF,MAAI,WAAW,QAAQ,OAAO,OAAO,qBAAqB,YAAY;AAClE,WAAO;AAAA,EACX;AACA,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACvC,UAAM,UAAU,MAAY;AACxB,UAAI;AACA,uBAAe,MAAM;AAAA,MACzB,SAAS,KAAK;AACV,eAAO,GAAG;AAAA,MACd;AAAA,IACJ;AACA,WAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM;AACxD,YAAQ;AAAA,MACJ,CAAC,UAAU;AACP,eAAO,oBAAoB,SAAS,OAAO;AAC3C,gBAAQ,KAAK;AAAA,MACjB;AAAA,MACA,CAAC,QAAiB;AACd,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,GAAG;AAAA,MACd;AAAA,IAAA;AAAA,EAER,CAAC;AACL;AAQO,SAAS,WAAW,QAA6E;AACpG,MAAI,OAAO,QAAQ;AACf,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,MACA,EAAE,QAAQ,gBAAA;AAAA,IAAgB;AAAA,EAElC;AACA,SAAO,OAAO,UAAA;AAClB;AAOA,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBb,YACqB,QACA,SACnB;AAFmB,SAAA,SAAA;AACA,SAAA,UAAA;AAjBrB,SAAQ,QAAQ;AAEhB,SAAQ,UAAU;AAElB,SAAQ,UAAU;AAElB,SAAQ,SAAS;AAEjB,SAAQ,WAAW;AAAA,EAUhB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOH,KAAK,OAAuB;AACxB,QAAI,OAAO,KAAK,UAAU,MAAM,QAAQ;AACxC,SAAK,UAAU;AACf,QAAI,KAAK,WAAW,GAAG;AACnB,aAAO;AAAA,IACX;AACA,QAAI,KAAK,OAAO;AACZ,aAAO,KAAK,mBAAmB,IAAI;AACnC,UAAI,KAAK,WAAW,GAAG;AACnB,eAAO;AAAA,MACX;AACA,WAAK,QAAQ;AAGb,oBAAc,MAAM,KAAK,QAAQ,KAAK,OAAO;AAAA,IACjD;AACA,QAAI,KAAK,SAAS,GAAG,GAAG;AACpB,WAAK,UAAU;AACf,aAAO,KAAK,MAAM,GAAG,EAAE;AAAA,IAC3B;AACA,QAAI,CAAC,KAAK,WAAW,KAAK,KAAK,IAAI,GAAG;AAClC,WAAK,UAAU;AAAA,IACnB;AACA,QAAI,CAAC,KAAK,QAAQ;AACd,WAAK,YAAY,IAAI;AAAA,IACzB;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,mBAAmB,MAAsB;AAC7C,QAAI,QAAQ;AACZ,WAAO,KAAK,YAAY,KAAK,MAAM,OAAQ;AACvC;AAAA,IACJ;AACA,QAAI,QAAQ,KAAK,CAAC,KAAK,UAAU;AAE7B,WAAK,WAAW;AAChB,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA,yBAAyB,KAAK;AAAA,QAC9B,EAAE,SAAS,SAAA;AAAA,MAAS;AAAA,IAE5B;AACA,WAAO,KAAK,MAAM,KAAK;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,MAAoB;AACpC,UAAM,QAAQ,kBAAkB,KAAK,IAAI;AACzC,QAAI,UAAU,MAAM;AAChB;AAAA,IACJ;AACA,SAAK,SAAS;AACd,UAAM,QAAQ,MAAM,CAAC,EAAE,YAAY,CAAC,KAAK,GAAG,SAAS,EAAE,EAAE,YAAA,EAAc,SAAS,GAAG,GAAG;AACtF,UAAM,SAAS,KAAK,UAAU,KAAK,MAAM,KAAK,IAAI,GAAG,MAAM,QAAQ,EAAE,GAAG,MAAM,QAAQ,EAAE,CAAC;AACzF,SAAK,OAAO;AAAA,MACR;AAAA,MACA;AAAA,MACA,2CAA2C,IAAI,QAAQ,MAAM;AAAA,MAC7D,EAAE,SAAS,KAAK,IAAI,GAAA;AAAA,IAAG;AAAA,EAE/B;AAAA;AAAA,EAGA,MAAY;AACR,QAAI,KAAK,SAAS;AACd,WAAK,OAAO;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,QACA,EAAE,SAAS,SAAA;AAAA,MAAS;AAAA,IAE5B;AACA,QAAI,CAAC,KAAK,WAAW,KAAK,QAAQ,eAAe,MAAM;AACnD,WAAK,OAAO,KAAK,kBAAkB,KAAK,QAAQ,uBAAuB,iCAAiC;AAAA,IAC5G;AAAA,EACJ;AACJ;AAOA,SAAS,mBAAmB,QAA6B,UAA2C;AAChG,MAAI,aAAa,QAAQ,aAAa,QAAW;AAC7C,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA,EAAE,SAAS,WAAA;AAAA,IAAW;AAAA,EAE9B;AACJ;AAgBA,gBAAuB,WACnB,UACA,QACA,UAAuB,CAAA,GACgB;AACvC,QAAM,QAAQ,eAAe,QAAQ;AACrC,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,aAAa,QAAQ,cAAc;AACzC,QAAM,SAAS,IAAI,WAAW,QAAQ,OAAO;AAC7C,iBAAe,MAAM;AACrB,MAAI,OAAO,UAAU,UAAU;AAC3B,uBAAmB,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,QAAQ,eAAe,OAAO,IAAI,WAAW,KAAK;AACxD,QAAIC,QAAO;AACX,QAAI,QAAQ;AACZ,WAAO,QAAQ,MAAM,QAAQ;AACzB,qBAAe,MAAM;AACrB,UAAI,MAAM,KAAK,IAAI,QAAQ,cAAc,MAAM,MAAM;AACrD,YAAM,iBAAiB,MAAM,WAAW,MAAM,CAAC,IAAI,WAAY;AAC/D,UAAI,MAAM,MAAM,UAAU,eAAe;AACrC;AAAA,MACJ;AACA,YAAM,QAAQ,UAAU,KAAK,QAAQ,MAAM,SAAS,QAAQ,MAAM,MAAM,OAAO,GAAG;AAClF,cAAQ;AACR,YAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,UAAI,KAAK,SAAS,GAAG;AACjB,cAAM;AAAA,MACV;AACA,UAAI,eAAe,MAAM;AACrBA,iBAAQ,WAAW,KAAK;AACxB,mBAAWA,OAAM,KAAK;AAAA,MAC1B;AAAA,IACJ;AACA,QAAI,MAAM,WAAW,GAAG;AACpB,mBAAa,GAAG,CAAC;AAAA,IACrB;AACA,WAAO,IAAA;AACP;AAAA,EACJ;AACA,QAAM,UAAU,IAAI,YAAY,QAAQ,OAAO;AAC/C,QAAM,YAAY,KAAK,IAAI,QAAQ,oBAAoB,YAAY,SAAS;AAC5E,MAAI,iBAAiB,YAAY;AAC7B,UAAM,SAAS,MAAM;AACrB,YAAQ,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC;AAC1C,aAAS,SAAS,GAAG,SAAS,QAAQ,UAAU,cAAc;AAC1D,qBAAe,MAAM;AACrB,UAAI,MAAM,eAAe,QAAQ;AAC7B,cAAM,IAAI;AAAA,UACN;AAAA,UACA;AAAA,UACA,EAAE,QAAQ,WAAA;AAAA,QAAW;AAAA,MAE7B;AACA,YAAM,QAAQ,MAAM,SAAS,QAAQ,KAAK,IAAI,SAAS,cAAc,MAAM,CAAC;AAC5E,YAAM,OAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,SAAS,eAAe,MAAM,CAAC;AAC9E,UAAI,KAAK,SAAS,GAAG;AACjB,cAAM;AAAA,MACV;AACA,mBAAa,KAAK,IAAI,SAAS,cAAc,MAAM,GAAG,MAAM;AAAA,IAChE;AACA,QAAI,WAAW,GAAG;AACd,mBAAa,GAAG,CAAC;AAAA,IACrB;AACA,WAAO,IAAA;AACP;AAAA,EACJ;AAIA,QAAM,SAAS,QAAQ,qBAAqB,SAAY,YAAY;AACpE,MAAI,OAAO;AACX,MAAI,OAAqB,CAAA;AACzB,MAAI,aAAa;AACjB,QAAM,YAAY,CAAC,WAA4B;AAC3C,UAAM,QAAQ,YAAY,IAAI;AAC9B,WAAO,CAAA;AACP,iBAAa;AACb,YAAQ,MAAM,MAAM,SAAS,GAAG,SAAS,CAAC;AAC1C,WAAO,QAAQ,OAAO,OAAO,MAAM;AAAA,EACvC;AACA,QAAM,SAAS,iBAAiB,KAAK,IAAI,aAAa,OAAO,MAAM,IAAI;AACvE,mBAAiB,SAAS,QAAQ;AAC9B,mBAAe,MAAM;AACrB,QAAI;AACJ,UAAM,QAAQ,OAAO,UAAU,WAAW,OAAO,QAAQ,KAAK;AAC9D,QAAI,OAAO,UAAU,UAAU;AAI3B,UAAI,UAAU;AACd,UAAI,aAAa,GAAG;AAChB,kBAAU,QAAQ,gBAAgB,MAAM,UAAU,KAAK,CAAC;AAAA,MAC5D,WAAW,QAAQ,SAAS;AACxB,kBAAU,QAAQ,gBAAgB,MAAM,QAAQ,OAAO,IAAI,WAAW,CAAC,GAAG,KAAK,CAAC;AAAA,MACpF,OAAO;AACH,gBAAQ,MAAM,IAAI,WAAW,CAAC,CAAC;AAAA,MACnC;AACA,aAAO,OAAO,KAAK,UAAU,KAAK;AAClC,cAAQ,eAAe,OAAO,IAAI,WAAW,KAAK;AAAA,IACtD,WAAW,UAAU,MAAM;AACvB,cAAQ,MAAM;AACd,UAAI,QAAQ,SAAS;AACjB,eAAO,OAAO,KAAK,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,MAClD,OAAO;AACH,aAAK,KAAK,KAAK;AACf,sBAAc,MAAM;AACpB,eAAO,cAAc,SAAS,OAAO,KAAK,UAAU,IAAI,CAAC,IAAI;AAAA,MACjE;AAAA,IACJ,OAAO;AACH,YAAM,IAAI,iBAAiB,iBAAiB,mDAAmD;AAAA,QAC3F,QAAQ;AAAA,QACR,OAAO,OAAO;AAAA,MAAA,CACjB;AAAA,IACL;AACA,QAAI,KAAK,SAAS,GAAG;AACjB,YAAM;AAAA,IACV;AACA,iBAAa,IAAI;AAAA,EACrB;AACA,MAAI,OAAO;AACX,MAAI,aAAa,GAAG;AAChB,WAAO,UAAU,KAAK;AAAA,EAC1B,WAAW,QAAQ,SAAS;AACxB,WAAO,QAAQ,OAAO,IAAI,WAAW,CAAC,GAAG,KAAK;AAAA,EAClD;AACA,SAAO,OAAO,KAAK,IAAI;AACvB,MAAI,KAAK,SAAS,GAAG;AACjB,UAAM;AAAA,EACV;AACA,SAAO,IAAA;AACP,eAAa,MAAM,IAAI;AAC3B;AAOA,SAAS,iBACL,OACmC;AACnC,SAAO,OAAQ,MAAkC,cAAc;AACnE;AAUA,gBAAgB,aACZ,QACA,QAC2C;AAC3C,QAAM,SAAS,WAAW,MAAM;AAChC,MAAI,WAAW;AACf,MAAI;AACA,eAAS;AACL,qBAAe,MAAM;AACrB,YAAM,EAAE,MAAM,UAAU,MAAM,UAAU,OAAO,KAAA,GAAQ,MAAM;AAC7D,UAAI,MAAM;AACN,mBAAW;AACX;AAAA,MACJ;AACA,YAAM;AAAA,IACV;AAAA,EACJ,UAAA;AACI,QAAI,CAAC,UAAU;AACX,YAAM,OAAO,OAAO,QAAQ,MAAM,EAAE,MAAM,MAAM,MAAS;AAAA,IAC7D;AACA,WAAO,YAAA;AAAA,EACX;AACJ;AAWA,eAAsB,UAAU,UAAuB,UAAuB,IAAgC;AAC1G,QAAM,QAAQ,eAAe,QAAQ;AACrC,QAAM,SAAS,QAAQ,UAAU;AACjC,iBAAe,MAAM;AACrB,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO;AAAA,EACX;AACA,MAAI,iBAAiB,YAAY;AAC7B,YAAQ,aAAa,MAAM,YAAY,MAAM,UAAU;AACvD,WAAO;AAAA,EACX;AACA,QAAM,QAAsB,CAAA;AAC5B,MAAI,OAAO;AACX,mBAAiB,SAAS,iBAAiB,KAAK,IAAI,aAAa,OAAO,MAAM,IAAI,OAAO;AACrF,mBAAe,MAAM;AACrB,QAAI,OAAO,UAAU,UAAU;AAC3B,aAAO;AAAA,IACX;AACA,UAAM,QAAQ,QAAQ,KAAK;AAC3B,QAAI,UAAU,MAAM;AAChB,YAAM,IAAI,iBAAiB,iBAAiB,mDAAmD;AAAA,QAC3F,QAAQ;AAAA,QACR,OAAO,OAAO;AAAA,MAAA,CACjB;AAAA,IACL;AACA,UAAM,KAAK,KAAK;AAChB,YAAQ,MAAM;AACd,YAAQ,aAAa,IAAI;AAAA,EAC7B;AACA,UAAQ,aAAa,MAAM,IAAI;AAC/B,SAAO,MAAM,WAAW,IAAI,IAAI,WAAW,CAAC,IAAI,YAAY,KAAK;AACrE;AASO,SAAS,SAAS,QAA6B,SAAiB,MAAsB;AACzF,SAAO,SAAS,eAAe,gBAAgB,SAAS,SAAS,SAAY,SAAY,EAAE,MAAM;AACrG;AAYA,eAAsB,SAClB,UACA,QACA,UAAuB,CAAA,GACR;AACf,QAAM,QAAQ,eAAe,QAAQ;AACrC,MAAI,OAAO,UAAU,UAAU;AAC3B,mBAAe,QAAQ,MAAM;AAC7B,uBAAmB,QAAQ,QAAQ,QAAQ;AAC3C,UAAM,SAAS,IAAI,WAAW,QAAQ,OAAO;AAC7C,UAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,WAAO,IAAA;AACP,QAAI,QAAQ,eAAe,QAAQ,QAAQ,eAAe,QAAW;AACjE,YAAM,QAAQ,WAAW,KAAK;AAC9B,cAAQ,WAAW,OAAO,KAAK;AAAA,IACnC;AACA,WAAO;AAAA,EACX;AACA,QAAM,QAAkB,CAAA;AACxB,MAAI,SAAS;AACb,mBAAiB,SAAS,WAAW,OAAO,QAAQ,OAAO,GAAG;AAC1D,cAAU,MAAM;AAChB,QAAI,SAAS,iBAAiB;AAC1B;AAAA,QACI;AAAA,QACA,+BAA+B,eAAe;AAAA,MAAA;AAAA,IAEtD;AACA,UAAM,KAAK,KAAK;AAAA,EACpB;AACA,SAAO,MAAM,WAAW,IAAI,MAAM,CAAC,IAAI,MAAM,KAAK,EAAE;AACxD;AAUO,MAAM,WAA4C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAerD,YAAY,OAAoB,QAA6B,UAAuB,CAAA,GAAI;AARxF,SAAQ,aAAa;AASjB,SAAK,QAAQ;AACb,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,OAAe;AACf,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,cAAsB;AAC1B,WAAO,yBAAyB,eAAe;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,OAAO,aAAa,IAA6C;AAIrE,UAAM,UAAoB,CAAA;AAC1B,QAAI,gBAAgB;AACpB,QAAI,aAAa;AACjB,qBAAiB,SAAS,WAAW,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,GAAG;AACzE,UAAI,QAAQ;AACZ,YAAM,MAAM,MAAM;AAClB,UAAI,YAAY;AAGZ,qBAAa;AACb,aAAK;AACL,cAAM,SAAS,QAAQ,KAAK,EAAE;AAC9B,gBAAQ,SAAS;AACjB,wBAAgB;AAChB,cAAM,OAAO,MAAM,GAAG,EAAE;AACxB,YAAI,MAAM,WAAW,CAAC,MAAM,IAAI;AAC5B,kBAAQ;AAAA,QACZ;AAAA,MACJ;AAGA,UAAI,KAAK,MAAM,QAAQ,MAAM,KAAK;AAClC,UAAI,KAAK,MAAM,QAAQ,MAAM,KAAK;AAClC,aAAO,MAAM,KAAK,MAAM,GAAG;AACvB,YAAI;AACJ,YAAI;AACJ,YAAI,MAAM,MAAM,KAAK,KAAK,KAAK,KAAK;AAChC,cAAI,OAAO,MAAM,GAAG;AAEhB;AAAA,UACJ;AACA,gBAAM;AACN,iBAAO,MAAM,WAAW,KAAK,CAAC,MAAM,KAAK,KAAK,IAAI,KAAK;AAAA,QAC3D,OAAO;AACH,gBAAM;AACN,iBAAO,KAAK;AAAA,QAChB;AACA,aAAK;AACL,cAAM,QAAQ,MAAM,MAAM,OAAO,GAAG;AACpC,YAAI,QAAQ,WAAW,GAAG;AACtB,gBAAM;AAAA,QACV,OAAO;AACH,cAAI,gBAAgB,MAAM,SAAS,iBAAiB;AAChD,qBAAS,KAAK,QAAQ,KAAK,YAAA,GAAe,KAAK,UAAU;AAAA,UAC7D;AACA,kBAAQ,KAAK,KAAK;AAClB,gBAAM,SAAS,QAAQ,KAAK,EAAE;AAC9B,kBAAQ,SAAS;AACjB,0BAAgB;AAChB,gBAAM;AAAA,QACV;AACA,gBAAQ;AACR,YAAI,MAAM,KAAK,KAAK,MAAM;AACtB,eAAK,MAAM,QAAQ,MAAM,IAAI;AAAA,QACjC;AACA,YAAI,MAAM,KAAK,KAAK,MAAM;AACtB,eAAK,MAAM,QAAQ,MAAM,IAAI;AAAA,QACjC;AAAA,MACJ;AACA,UAAI,QAAQ,KAAK;AACb,yBAAiB,MAAM;AACvB,YAAI,gBAAgB,iBAAiB;AACjC,mBAAS,KAAK,QAAQ,KAAK,eAAe,KAAK,aAAa,CAAC;AAAA,QACjE;AACA,gBAAQ,KAAK,UAAU,IAAI,QAAQ,MAAM,MAAM,KAAK,CAAC;AACrD,qBAAa,MAAM,WAAW,MAAM,CAAC,MAAM;AAAA,MAC/C;AAAA,IACJ;AACA,QAAI,QAAQ,SAAS,GAAG;AACpB,WAAK;AACL,YAAM,SAAS,QAAQ,KAAK,EAAE;AAC9B,YAAM,OAAO,SAAS,IAAI,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAAA,IACxD;AAAA,EACJ;AACJ;ACpgDA,MAAM,mCAAwC,IAAI,CAAC,QAAQ,aAAa,UAAU,QAAQ,CAAC;AAC3F,MAAM,kBAAuC,oBAAI,IAAI,CAAC,MAAM,SAAS,OAAO,CAAC;AAC7E,MAAM,qBAA0C,oBAAI,IAAI,CAAC,QAAQ,SAAS,SAAS,QAAQ,OAAO,OAAO,KAAK,CAAC;AAC/G,MAAM,qBAA0C,oBAAI,IAAI,CAAC,QAAQ,QAAQ,OAAO,CAAC;AACjF,MAAM,4CAAiD,IAAI,CAAC,UAAU,YAAY,cAAc,OAAO,CAAC;AACxG,MAAM,wBAA6C,oBAAI,IAAI,CAAC,SAAS,YAAY,YAAY,CAAC;AAC9F,MAAM,gBAAqC,oBAAI,IAAI,CAAC,OAAO,KAAK,CAAC;AACjE,MAAM,aAAkC,oBAAI,IAAI,CAAC,OAAO,QAAQ,CAAC;AACjE,MAAM,yCAA8C,IAAI,CAAC,SAAS,QAAQ,QAAQ,QAAQ,CAAC;AAC3F,MAAM,iBAAsC,oBAAI,IAAI,CAAC,SAAS,QAAQ,CAAC;AAMhE,MAAM,sBAAsB;AAKnC,MAAM,oBAAoB,CAAC,mBAAmB,kBAAkB,aAAa,aAAa;AAgBnF,SAAS,kBACZ,MACA,SACA,QACA,uBAAuB,OACjB;AACN,MAAI,YAAY,QAAW;AACvB,WAAO;AAAA,EACX;AACA,MAAI,WAAW;AACf,aAAW,QAAQ,mBAAmB;AAClC,UAAM,SAAkB,QAAQ,IAAI;AACpC,UAAM,SAAkB,KAAK,QAAQ,IAAI;AACzC,QAAI,WAAW,UAAa,WAAW,QAAQ;AAC3C;AAAA,IACJ;AACA,QAAI,SAAS,qBAAqB,wBAAwB,WAAW,OAAO;AACxE;AAAA,IACJ;AACA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,UAAU,IAAI,KAAK,KAAK,UAAU,MAAM,CAAC,gCAAgC,KAAK,UAAU,MAAM,CAAC;AAAA,MAC/F,EAAE,SAAS,KAAA;AAAA,IAAK;AAEpB;AAAA,EACJ;AACA,SAAO;AACX;AAGA,MAAM,yBAAyB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAGA,MAAM,iBAA8F;AAAA,EAChG,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,kBAAkB;AAAA,EAClB,aAAa;AAAA,EACb,MAAM;AAAA,EACN,mBAAmB;AAAA,EACnB,YAAY;AAChB;AAaO,SAAS,oBACZ,SACA,QACA,MACM;AACN,MAAI,YAAY,QAAW;AACvB,WAAO;AAAA,EACX;AACA,MAAI,WAAW;AACf,aAAW,QAAQ,wBAAwB;AACvC,QAAI,KAAK,IAAI,IAAI,KAAM,kBAAwC,SAAS,IAAI,GAAG;AAC3E;AAAA,IACJ;AACA,UAAM,QAAiB,QAAQ,IAAI;AACnC,QAAI,UAAU,UAAa,UAAU,eAAe,IAAI,GAAG;AAEvD;AAAA,IACJ;AACA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,UAAU,IAAI,KAAK,KAAK,UAAU,KAAK,CAAC,yBAAyB,OAAO,MAAM;AAAA,MAC9E,EAAE,SAAS,KAAA;AAAA,IAAK;AAEpB;AAAA,EACJ;AACA,SAAO;AACX;AASO,SAAS,YAAY,SAAkD;AAC1E,SAAO,SAAS,eAAe,UAAa,SAAS,cAAc;AACvE;AAQA,SAAS,eAAe,OAA2C;AAC/D,QAAM,QAAQ,MAAM,MAAM,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG,MAAM,GAAG,CAAC,IAAI,MAAM,OAAO,cAAc,KAAK,UAAU,CAAC,CAAC,EAAE;AACrG,QAAM,OAAO,MAAM,SAAS,MAAM,SAAS,SAAS,MAAM,SAAS,MAAM,MAAM,UAAU;AACzF,SAAO,oBAAoB,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI;AACtD;AAaO,SAAS,YACZ,OACA,SACA,QACM;AACN,QAAM,EAAE,YAAY,UAAA,IAAc,WAAW,CAAA;AAC7C,MACI,eAAe,WACd,OAAO,eAAe,YAAY,CAAC,OAAO,UAAU,UAAU,KAAK,aAAa,IACnF;AACE,UAAM,IAAI;AAAA,MACN;AAAA,MACA,sBAAsB,SAAS,UAAU,CAAC;AAAA,MAC1C;AAAA,QACI,QAAQ;AAAA,QACR,OAAO;AAAA,MAAA;AAAA,IACX;AAAA,EAER;AACA,MAAI,cAAc,UAAa,OAAO,cAAc,UAAU;AAC1D,UAAM,IAAI,iBAAiB,iBAAiB,qBAAqB,SAAS,SAAS,CAAC,oBAAoB;AAAA,MACpG,QAAQ;AAAA,MACR,OAAO;AAAA,IAAA,CACV;AAAA,EACL;AACA,MAAI,eAAe,UAAa,cAAc,QAAW;AACrD,UAAM,IAAI,iBAAiB,iBAAiB,kEAAkE;AAAA,MAC1G,QAAQ;AAAA,MACR,OAAO;AAAA,IAAA,CACV;AAAA,EACL;AACA,MAAI,MAAM,WAAW,GAAG;AACpB,WAAO,OAAO,KAAK,eAAe,0BAA0B;AAAA,EAChE;AACA,MAAI,cAAc,QAAW;AACzB,UAAM,UAAU,MAAM,QAAQ,CAAC,MAAM,MAAO,SAAS,YAAY,CAAC,CAAC,IAAI,CAAA,CAAG;AAC1E,QAAI,QAAQ,SAAS,GAAG;AACpB,aAAO,OAAO;AAAA,QACV;AAAA,QACA,aAAa,KAAK,UAAU,SAAS,CAAC,gCAAgC,QAAQ,KAAK,IAAI,CAAC;AAAA,QACxF,EAAE,SAAS,UAAA;AAAA,MAAU;AAAA,IAE7B;AACA,QAAI,QAAQ,WAAW,GAAG;AACtB,aAAO,OAAO;AAAA,QACV;AAAA,QACA,aAAa,KAAK,UAAU,SAAS,CAAC,sBAAsB,MAAM,MAAM,SAAS,OAAO,MAAM,MAAM,CAAC,GAAG,eAAe,KAAK,CAAC;AAAA,QAC7H,EAAE,SAAS,UAAA;AAAA,QACX,EAAE,OAAO,CAAC,GAAG,KAAK,EAAA;AAAA,MAAE;AAAA,IAE5B;AACA,WAAO,QAAQ,CAAC;AAAA,EACpB;AACA,QAAM,QAAQ,cAAc;AAC5B,MAAI,SAAS,MAAM,QAAQ;AACvB,WAAO,OAAO;AAAA,MACV;AAAA,MACA,cAAc,KAAK,kBAAkB,MAAM,MAAM,SAAS,OAAO,MAAM,MAAM,CAAC,GAAG,eAAe,KAAK,CAAC;AAAA,IAAA;AAAA,EAE9G;AACA,SAAO;AACX;AAUO,SAAS,qBACZ,SACA,UACqB;AACrB,QAAM,IAAyB,WAAW,CAAA;AAC1C,SAAO,OAAO,OAAO;AAAA,IACjB,KAAK,WAAW,OAAO,EAAE,KAAK,SAAS,KAAK,YAAY;AAAA,IACxD,YAAY,WAAW,cAAc,EAAE,YAAY,MAAM,eAAe;AAAA,IACxE,iBAAiB,cAAc,mBAAmB,EAAE,iBAAiB,SAAS,mBAAmB,IAAI;AAAA,IACrG,gBAAgB,WAAW,kBAAkB,EAAE,gBAAgB,QAAQ,kBAAkB;AAAA,IACzF,WAAW,WAAW,aAAa,EAAE,WAAW,QAAQ,kBAAkB;AAAA,IAC1E,kBAAkB,WAAW,oBAAoB,EAAE,kBAAkB,UAAU,qBAAqB;AAAA,IACpG,iBAAiB,cAAc,mBAAmB,EAAE,iBAAiB,SAAS,eAAe;AAAA,IAC7F,YAAY,iBAAiB,EAAE,YAAY,SAAS,UAAU;AAAA,IAC9D,aAAa,WAAW,eAAe,EAAE,aAAa,OAAO,aAAa;AAAA,IAC1E,MAAM,WAAW,QAAQ,EAAE,MAAM,OAAO,UAAU;AAAA,IAClD,mBAAmB,cAAc,qBAAqB,EAAE,mBAAmB,IAAI;AAAA,IAC/E,YAAY,WAAW,cAAc,EAAE,YAAY,QAAQ,kBAAkB;AAAA,IAC7E,YAAY,iBAAiB,EAAE,UAAU;AAAA,IACzC,QAAQ,aAAa,EAAE,MAAM;AAAA,IAC7B,YAAY,eAAe,EAAE,UAAU;AAAA,IACvC,UAAU,eAAe,EAAE,QAAQ;AAAA,EAAA,CACtC;AACL;AAQO,SAAS,qBAAqB,SAAiE;AAClG,QAAM,IAAyB,WAAW,CAAA;AAC1C,SAAO,OAAO,OAAO;AAAA,IACjB,aAAa,WAAW,eAAe,EAAE,aAAa,SAAS,cAAc;AAAA,IAC7E,kBAAkB,WAAW,oBAAoB,EAAE,kBAAkB,SAAS,qBAAqB;AAAA,EAAA,CACtG;AACL;AAUA,SAAS,WAA6B,MAAc,OAAgB,UAAa,SAAiC;AAC9G,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,YAAY,CAAC,QAAQ,IAAI,KAAK,GAAG;AAClD,UAAM,IAAI;AAAA,MACN;AAAA,MACA,UAAU,IAAI,KAAK,SAAS,KAAK,CAAC,kBAAkB,KAAK,OAAO,CAAC;AAAA,MACjE;AAAA,QACI,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,WAAW,CAAC,GAAG,OAAO;AAAA,MAAA;AAAA,IAC1B;AAAA,EAER;AACA,SAAO;AACX;AASA,SAAS,cAAc,MAAc,OAAgB,UAA4B;AAC7E,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,WAAW;AAC5B,UAAM,IAAI,iBAAiB,iBAAiB,UAAU,IAAI,KAAK,SAAS,KAAK,CAAC,qBAAqB;AAAA,MAC/F,QAAQ;AAAA,MACR,OAAO;AAAA,IAAA,CACV;AAAA,EACL;AACA,SAAO;AACX;AAQA,SAAS,iBAAiB,OAAgB,UAAwC;AAC9E,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AACA,MAAI,UAAU,QAAS,OAAO,UAAU,YAAY,MAAM,SAAS,GAAI;AACnE,WAAO;AAAA,EACX;AACA,QAAM,IAAI;AAAA,IACN;AAAA,IACA,sBAAsB,SAAS,KAAK,CAAC;AAAA,IACrC,EAAE,QAAQ,cAAc,OAAO,MAAA;AAAA,EAAM;AAE7C;AAOA,SAAS,iBAAiB,OAAwB;AAC9C,MAAI,UAAU,QAAW;AACrB,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,YAAY,SAAS,MAAM,OAAO,UAAU,KAAK,KAAK,UAAU,WAAW;AAC5F,WAAO;AAAA,EACX;AACA,QAAM,IAAI;AAAA,IACN;AAAA,IACA,sBAAsB,SAAS,KAAK,CAAC;AAAA,IACrC,EAAE,QAAQ,cAAc,OAAO,MAAA;AAAA,EAAM;AAE7C;AAOA,SAAS,aAAa,OAAoC;AACtD,MAAI,UAAU,UAAa,UAAU,MAAM;AACvC,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,YAAY,OAAQ,MAAgC,YAAY,WAAW;AAC5F,WAAO;AAAA,EACX;AACA,QAAM,IAAI,iBAAiB,iBAAiB,kBAAkB,SAAS,KAAK,CAAC,0BAA0B;AAAA,IACnG,QAAQ;AAAA,IACR,OAAO,OAAO;AAAA,EAAA,CACjB;AACL;AAOA,SAAS,eAAe,OAA2E;AAC/F,MAAI,UAAU,UAAa,UAAU,MAAM;AACvC,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,YAAY;AAC7B,WAAO;AAAA,EACX;AACA,QAAM,IAAI,iBAAiB,iBAAiB,sBAAsB,SAAS,KAAK,CAAC,sBAAsB;AAAA,IACnG,QAAQ;AAAA,IACR,OAAO,OAAO;AAAA,EAAA,CACjB;AACL;AAOA,SAAS,eAAe,OAA+B;AACnD,MAAI,UAAU,UAAa,UAAU,MAAM;AACvC,WAAO;AAAA,EACX;AACA,QAAM,YAAY,OAAO,UAAU,WAAW,kBAAkB,KAAK,IAAI;AACzE,MAAI,cAAc,MAAM;AACpB,UAAM,IAAI;AAAA,MACN;AAAA,MACA,oBAAoB,SAAS,KAAK,CAAC;AAAA,MACnC,EAAE,QAAQ,YAAY,OAAO,MAAA;AAAA,IAAM;AAAA,EAE3C;AACA,SAAO;AACX;AAgBO,SAAS,eAA8C,SAA2B;AACrF,SAAO,EAAE,GAAG,SAAS,UAAU,QAAW,YAAY,OAAA;AAC1D;AAMO,MAAM,kBAA+D,OAAO,OAAO;AAAA,EACtF,KAAK,OAAO,OAAO,CAAC,SAAS,SAAS,CAAC;AAAA,EACvC,MAAM,OAAO,OAAO,CAAC,aAAa,MAAM,OAAO,aAAa,cAAc,OAAO,WAAW,CAAC;AACjG,CAAC;AAQM,SAAS,mBAAmB,QAAgB,OAAyB;AACxE,SACI,OAAO,UAAU,YACjB,OAAO,QAAQ,eAAe,EAAE,KAAK,CAAC,CAAC,GAAG,KAAK,MAAM,MAAM,UAAU,MAAM,SAAS,KAAK,CAAC;AAElG;AAQO,SAAS,SAAS,OAAwB;AAC7C,UAAQ,OAAO,OAAA;AAAA,IACX,KAAK;AACD,aAAO,KAAK,UAAU,KAAK;AAAA,IAC/B,KAAK;AAAA,IACL,KAAK;AACD,aAAO,OAAO,KAAK;AAAA,IACvB;AACI,aAAO,UAAU,OAAO,SAAS,OAAO;AAAA,EAAA;AAEpD;AAOA,SAAS,KAAK,SAAsC;AAChD,SAAO,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,EAAE,KAAK,IAAI;AAC/D;ACrCO,MAAM,oBAAoB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmC9C,YAAY,SAAiB,QAAsB,SAA6C;AAC5F,UAAM,YAAY,SAAS,OAAO;AAClC,SAAK,OAAO;AACZ,WAAO,eAAe,MAAM,UAAU,EAAE,OAAO,QAAQ,YAAY,OAAO;AAC1E,SAAK,QAAQ,cAAc,OAAO,QAAQ,SAAS,IAAI;AAAA,EAC3D;AACJ;AAQA,SAAS,cAAc,QAAgC,MAAmC;AACtF,MAAI,YAAgC;AACpC,WAAS,IAAI,OAAO,SAAS,GAAG,KAAK,GAAG,KAAK;AACzC,QAAI,OAAO,CAAC,EAAE,SAAS,MAAM;AACzB,aAAO,OAAO,CAAC;AAAA,IACnB;AACA,QAAI,cAAc,QAAQ,OAAO,CAAC,EAAE,aAAa,SAAS;AACtD,kBAAY,OAAO,CAAC;AAAA,IACxB;AAAA,EACJ;AACA,SAAO;AACX;AC3jBO,MAAM,wBAAwB;AAsCrC,MAAM,mBAAmF;AAAA,EACrF,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,eAAe;AAAA,EACf,kBAAkB;AACtB;AAMO,MAAM,mBAAmB;AAShC,SAAS,SAAS,SAAiB,OAA0C;AACzE,QAAM,OAAO,OAAO;AACpB,SAAO,SAAS,UAAa,SAAS,QAAQ,QAAQ,SAAS,QAAQ,IAAI,EAAE,IACvE,UACA,QAAQ,IAAI,KAAK,OAAO;AAClC;AAQO,MAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgC7B,YAAY,QAAgB,YAAoB;AAxBhD,SAAS,SAAwB,EAAE,OAAO,GAAG,OAAO,GAAG,cAAc,GAAG,cAAc,GAAG,eAAe,EAAA;AAExG,SAAiB,YAA2B,CAAA;AAE5C,SAAiB,WAAuB,CAAA;AAExC,SAAiB,gCAAgB,IAAA;AAGjC,SAAiB,qCAAqB,IAAA;AAEtC,SAAQ,SAAS;AAEjB,SAAQ,WAAW;AAEnB,SAAQ,gBAAgB;AAUpB,SAAK,SAAS;AACd,SAAK,aAAa;AAClB,SAAK,YAAY,IAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,SAAiC;AACjC,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,aAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,eAAuB;AACvB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,YAAqB;AACrB,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,QAA6B;AAC7B,WAAO,KAAK;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,UAAyB,MAAc,SAAiB,OAAoC;AAC9F,UAAM,QAAQ,UAAU,UAAU,SAAS,MAAM,SAAS,KAAK;AAC/D,SAAK,UAAU,KAAK,KAAK;AACzB,SAAK;AACL,QAAI,KAAK,SAAS,KAAK,YAAY;AAC/B,WAAK,gBAAgB;AACrB,YAAM,KAAK,MAAM,kBAAkB,KAAK,UAAU,cAAc,SAAS,SAAS,KAAK,CAAC,IAAI;AAAA,QACxF;AAAA,QACA,OAAO,KAAK;AAAA,MAAA,CACf;AAAA,IACL;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,QAAQ,UAAyB,MAAc,SAAiB,OAAoC;AAChG,UAAM,QAAQ,UAAU,UAAU,WAAW,MAAM,SAAS,KAAK;AACjE,UAAM,QAAQ,KAAK,eAAe,IAAI,IAAI,KAAK,KAAK;AACpD,SAAK,eAAe,IAAI,MAAM,IAAI;AAClC,QAAI,QAAQ,uBAAuB;AAC/B,WAAK,UAAU,KAAK,KAAK;AACzB,WAAK;AAAA,IACT;AACA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,SACI,UACA,MACA,SACA,OACA,MAAc,MACI;AAClB,QAAI,KAAK,UAAU,IAAI,GAAG,GAAG;AACzB,aAAO;AAAA,IACX;AACA,SAAK,UAAU,IAAI,GAAG;AACtB,WAAO,KAAK,QAAQ,UAAU,MAAM,SAAS,KAAK;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,KAAK,MAAc,SAAiB,SAAwB,MAAM,QAAuB,MAAgB;AACrG,UAAM,OAAiB,OAAO,OAAO,EAAE,MAAM,SAAS,QAAQ,OAAO;AACrE,SAAK,SAAS,KAAK,IAAI;AACvB,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,YAAY,KAAc,OAAoC;AAC1D,QAAI,eAAe,oBAAoB,EAAE,eAAe,cAAc;AAClE,YAAM,WAAW,iBAAiB,IAAI,IAAI,KAAK;AAC/C,UAAI,IAAI,SAAS,eAAe;AAE5B,eAAO,KAAK,SAAS,UAAU,IAAI,MAAM,IAAI,SAAS,KAAK;AAAA,MAC/D;AACA,aAAO,KAAK,MAAM,UAAU,IAAI,MAAM,IAAI,SAAS,KAAK;AAAA,IAC5D;AACA,UAAM;AAAA,EACV;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAA4B;AACxB,UAAM,OAAO,IAAI,oBAAoB,KAAK,QAAQ,KAAK,UAAU;AACjE,WAAO,OAAO,KAAK,QAAQ,KAAK,MAAM;AACtC,SAAK,UAAU,KAAK,GAAG,KAAK,SAAS;AACrC,SAAK,SAAS,KAAK,GAAG,KAAK,QAAQ;AACnC,eAAW,OAAO,KAAK,WAAW;AAC9B,WAAK,UAAU,IAAI,GAAG;AAAA,IAC1B;AACA,eAAW,CAAC,MAAM,KAAK,KAAK,KAAK,gBAAgB;AAC7C,WAAK,eAAe,IAAI,MAAM,KAAK;AAAA,IACvC;AACA,SAAK,SAAS,KAAK;AACnB,SAAK,WAAW,KAAK;AACrB,SAAK,gBAAgB,KAAK;AAC1B,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,SAAiB,SAA0D;AAC7E,WAAO,IAAI,YAAY,SAAS,KAAK,OAAA,GAAU,OAAO;AAAA,EAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,KAAK,MAAc,SAAiB,OAAuB,SAAoD;AAC3G,SAAK,SAAS,eAAe,MAAM,SAAS,OAAO,OAAO;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,SACI,UACA,MACA,SACA,OACA,SACK;AACL,SAAK,UAAU,KAAK,UAAU,UAAU,SAAS,MAAM,SAAS,KAAK,CAAC;AACtE,SAAK;AACL,UAAM,KAAK,MAAM,SAAS,SAAS,KAAK,GAAG,EAAE,MAAM,GAAG,SAAS;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,QAAQ,QAA4B;AAChC,eAAW,SAAS,OAAO,QAAQ;AAC/B,WAAK,UAAU,KAAK,KAAK;AACzB,UAAI,MAAM,aAAa,SAAS;AAC5B,aAAK;AAAA,MACT,OAAO;AACH,aAAK;AAAA,MACT;AAAA,IACJ;AACA,UAAM,EAAE,WAAW;AACnB,SAAK,OAAO,SAAS,OAAO;AAC5B,SAAK,OAAO,SAAS,OAAO;AAC5B,SAAK,OAAO,gBAAgB,OAAO;AACnC,SAAK,OAAO,gBAAgB,OAAO;AACnC,SAAK,OAAO,iBAAiB,OAAO;AACpC,SAAK,SAAS,KAAK,GAAG,OAAO,KAAK;AAClC,SAAK,kBAAkB,OAAO;AAC9B,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAuB;AACnB,UAAM,EAAE,OAAO,OAAO,cAAc,cAAc,cAAA,IAAkB,KAAK;AACzE,UAAM,aAA4B,CAAA;AAClC,eAAW,CAAC,MAAM,IAAI,KAAK,KAAK,gBAAgB;AAC5C,UAAI,OAAO,uBAAuB;AAC9B,cAAM,OAAO,OAAO;AACpB,mBAAW;AAAA,UACP;AAAA,YACI;AAAA,YACA;AAAA,YACA;AAAA,YACA,GAAG,IAAI,SAAS,IAAI,WAAW,OAAO,IAAI,CAAC,IAAI,MAAM,MAAM,OAAO,MAAM,CAAC,yCAAyC,qBAAqB;AAAA,YACvI,EAAE,SAAS,KAAA;AAAA,UAAK;AAAA,QACpB;AAAA,MAER;AAAA,IACJ;AACA,WAAO,OAAO,OAAO;AAAA,MACjB,QAAQ,KAAK;AAAA,MACb,QAAQ,OAAO,OAAO,EAAE,OAAO,OAAO,cAAc,cAAc,eAAe;AAAA,MACjF,QAAQ,OAAO,OAAO,CAAC,GAAG,KAAK,WAAW,GAAG,UAAU,CAAC;AAAA,MACxD,YAAY,KAAK;AAAA,MACjB,cAAc,KAAK,WAAW,WAAW;AAAA,MACzC,WAAW,KAAK;AAAA,MAChB,OAAO,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC;AAAA,MACvC,YAAY,IAAA,IAAQ,KAAK;AAAA,IAAA,CAC5B;AAAA,EACL;AACJ;AAWA,SAAS,UACL,UACA,UACA,MACA,SACA,OACW;AACX,SAAO,OAAO,OAAO;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,OAAO,QAAQ;AAAA,IACrB,SAAS,OAAO,WAAW;AAAA,EAAA,CAC9B;AACL;AAsBO,SAAS,aAAa,KAAuB;AAChD,MAAI,OAAO,QAAQ,YAAY,QAAQ,MAAM;AACzC,WAAO;AAAA,EACX;AACA,QAAM,EAAE,SAAS;AACjB,SAAO,SAAS,gBAAgB,SAAS;AAC7C;AAQO,SAAS,UAAU,KAAsB;AAC5C,MAAI,eAAe,OAAO;AACtB,WAAO,IAAI;AAAA,EACf;AACA,MAAI,OAAO,QAAQ,UAAU;AACzB,WAAO;AAAA,EACX;AACA,SAAO,qBAAqB,OAAO,GAAG;AAC1C;AAMA,SAAS,MAAc;AACnB,SAAO,OAAO,gBAAgB,WAAW,YAAY,IAAA,IAAQ,KAAK,IAAA;AACtE;AC9dO,SAAS,UAAU,OAAuB;AAC7C,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AACzB,WAAO,OAAO,KAAK;AAAA,EACvB;AACA,MAAI,UAAU,GAAG;AACb,WAAO,OAAO,GAAG,OAAO,EAAE,IAAI,OAAO;AAAA,EACzC;AACA,WAAS,SAAS,GAAG,UAAU,GAAG,UAAU;AACxC,UAAM,OAAO,MAAM,YAAY,MAAM;AACrC,QAAI,KAAK,OAAO,OAAO,IAAI,CAAC,MAAM,OAAO;AACrC,aAAO,OAAO,OAAO,IAAI,CAAC;AAAA,IAC9B;AAAA,EACJ;AACA,SAAO,OAAO,KAAK;AACvB;AASO,SAAS,UAAU,OAAuB;AAC7C,SAAO,OAAO,GAAG,OAAO,EAAE,IAAI,OAAO,OAAO,KAAK;AACrD;AAQO,SAAS,OAAO,IAAoB;AACvC,SAAO,OAAO,OAAO,WAAW,UAAU,EAAE,IAAI;AACpD;AAeO,SAAS,cAAc,OAAe,QAA8C,OAAe;AACtG,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AACzB,WAAO,OAAO,KAAK;AAAA,EACvB;AACA,MAAI,OAAO,GAAG,OAAO,EAAE,GAAG;AACtB,WAAO;AAAA,EACX;AACA,QAAM,OAAO,aAAa,OAAO,KAAK;AAEtC,SAAO,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,IAAI;AACpE;AAYO,SAAS,cAAc,OAAe,QAA8C,OAAe;AACtG,MAAI,OAAO,MAAM,KAAK,GAAG;AACrB,WAAO;AAAA,EACX;AACA,MAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AACzB,WAAO,QAAQ,IAAI,SAAS;AAAA,EAChC;AACA,QAAM,OAAO,cAAc,OAAO,KAAK;AACvC,MAAI,KAAK,SAAS,GAAG,GAAG;AACpB,WAAO;AAAA,EACX;AAEA,QAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,SAAO,GAAG,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC;AAChD;AASO,SAAS,cAAc,OAAuB;AACjD,MAAI,OAAO,cAAc,KAAK,KAAK,CAAC,OAAO,SAAS,KAAK,GAAG;AACxD,WAAO,OAAO,KAAK;AAAA,EACvB;AACA,SAAO,OAAO,KAAK,EAAE,SAAA;AACzB;AAUO,SAAS,aAAa,OAAe,OAAqD;AAC7F,SAAO,UAAU,QAAQ,UAAU,KAAK,IAAI,UAAU,KAAK;AAC/D;ACzGO,MAAM,iBAAiB;AASvB,SAAS,cAAc,MAAc,YAAoC;AAC5E,SAAO,eAAe,QAAQ,SAAS;AAC3C;AAWO,SAAS,gBAAgB,MAAc,QAAkD;AAC5F,QAAM,UAAU,KAAK,KAAA;AACrB,MAAI,QAAQ,WAAW,GAAG;AACtB,WAAO;AAAA,EACX;AACA,MAAI;AACJ,MAAI;AACA,YAAQ,iBAAiB,OAAO;AAAA,EACpC,SAAS,KAAK;AACV,UAAM,cAAc,MAAM,GAAG;AAAA,EACjC;AACA,MAAI,OAAO,MAAM,KAAK,GAAG;AACrB,UAAM,cAAc,MAAM,IAAI;AAAA,EAClC;AACA,MAAI,WAAW,QAAW;AACtB,0BAAsB,SAAS,OAAO,MAAM;AAAA,EAChD;AACA,SAAO;AACX;AASO,SAAS,sBAAsB,MAAc,OAAe,QAAmC;AAClG,MACI,CAAC,OAAO,cAAc,KAAK,KAC3B,OAAO,SAAS,KAAK,KACrB,aAAa,KAAK,IAAI,KACtB,OAAO,IAAI,MAAM,OAAO,KAAK,GAC/B;AACE,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,+EAA+E,IAAI;AAAA,MACnF;AAAA,MACA,GAAG,cAAc;AAAA,IAAA;AAAA,EAEzB;AACJ;AASO,SAAS,gBAAgB,OAAoC;AAChE,MAAI,UAAU,UAAa,UAAU,MAAM;AACvC,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,UAAU;AAC3B,QAAI,OAAO,MAAM,KAAK,GAAG;AACrB,YAAM,cAAc,OAAO,IAAI;AAAA,IACnC;AACA,WAAO;AAAA,EACX;AACA,MAAI,OAAO,UAAU,UAAU;AAC3B,WAAO,gBAAgB,KAAK;AAAA,EAChC;AACA,QAAM,cAAc,OAAO,IAAI;AACnC;AAQA,SAAS,cAAc,OAAgB,OAAkC;AACrE,MAAI;AACJ,UAAQ,OAAO,OAAA;AAAA,IACX,KAAK;AACD,cAAQ,KAAK,UAAU,KAAK;AAC5B;AAAA,IACJ,KAAK;AACD,cAAQ,OAAO,KAAK;AACpB;AAAA,IACJ;AACI,cAAQ,OAAO;AAAA,EAAA;AAEvB,SAAO,IAAI,iBAAiB,oBAAoB,uBAAuB,KAAK,IAAI;AAAA,IAC5E,OAAO,OAAO,UAAU,WAAW,OAAO,QAAQ;AAAA,IAClD,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,EAAA,CACnD;AACL;AAyCA,MAAM,qBAAqB;AAG3B,MAAM,qCAA0C,IAAI,CAAC,OAAO,OAAO,OAAO,OAAO,IAAI,CAAC;AAOtF,SAAS,gBAAgB,OAA0C;AAC/D,UAAQ,OAAA;AAAA,IACJ,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO;AAAA,IACX;AACI,aAAO;AAAA,EAAA;AAEnB;AAQO,SAAS,gBAAgB,UAA0C;AACtE,QAAM,eAAe,SAAS,MAAM,OAAO,QAAQ;AACnD,QAAM,SAAwB,iBAAiB,QAAQ,eAAe,IAAI,aAAa,KAAK,IAAI,eAAe;AAC/G,MAAI,WAAW,MAAM;AACjB,UAAM,QAAQ,OAAO,UAAU,QAAQ,QAAQ;AAC/C,UAAM,SAAS,gBAAgB,OAAO,KAAK;AAC3C,WAAO;AAAA,MACH,UAAU;AAAA,MACV;AAAA,MACA,YAAY,CAAC,MAAuB,OAAO,MAAM,CAAC;AAAA,MAClD,OAAO,CAAC,MAAsB,OAAO,MAAM,CAAC;AAAA,MAC5C,MAAM,CAAC,GAAW,WAAW,UAAyB;AAClD,YAAI,CAAC,OAAO,MAAM,CAAC,GAAG;AAClB,iBAAO;AAAA,QACX;AACA,cAAM,QAAQ,OAAO,MAAM,CAAC;AAC5B,YAAI,OAAO,GAAG,OAAO,EAAE,GAAG;AACtB,iBAAO;AAAA,QACX;AACA,eAAO,YAAY,OAAO,UAAU,KAAK,IAAI,cAAc,KAAK,IAAI,OAAO,KAAK;AAAA,MACpF;AAAA,IAAA;AAAA,EAER;AACA,QAAM,EAAE,QAAA,IAAY,SAAS,SAAA;AAC7B,MAAI,YAAY,QAAQ,CAAC,SAAS,MAAM,UAAU;AAC9C,WAAO;AAAA,MACH,UAAU;AAAA,MACV,OAAO;AAAA,MACP,YAAY,MAAe;AAAA,MAC3B,OAAO,MAAc;AAAA,MACrB,MAAM,MAAqB;AAAA,IAAA;AAAA,EAEnC;AACA,SAAO;AAAA,IACH,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY,MAAe;AAAA,IAC3B,OAAO,CAAC,MAAsB,QAAQ,CAAC;AAAA,IACvC,MAAM,CAAC,GAAW,WAAW,UAAkB;AAC3C,YAAM,QAAQ,QAAQ,CAAC;AACvB,UAAI,OAAO,GAAG,OAAO,EAAE,GAAG;AACtB,eAAO;AAAA,MACX;AACA,aAAO,YAAY,OAAO,UAAU,KAAK,IAAI,cAAc,KAAK,IAAI,UAAU,KAAK;AAAA,IACvF;AAAA,EAAA;AAER;AAgBO,SAAS,YAAY,UAA8C;AACtE,QAAM,EAAE,QAAA,IAAY,SAAS,SAAA;AAC7B,MAAI,YAAY,MAAM;AAClB,WAAO;AAAA,EACX;AACA,QAAM,MAAM,aAAa,KAAK,OAAO;AACrC,QAAM,QAAQ,SAAS,MAAM,OAAO,QAAQ;AAC5C,MAAI,UAAU,MAAM;AAChB,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACjC,YAAM,IAAI,MAAM,MAAM,CAAC;AACvB,UAAI,OAAO,MAAM,UAAU;AACvB,YAAI,CAAC,IAAI;AAAA,MACb;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;ACpOA,MAAM,qBAAqB;AAMpB,MAAM,OAAO,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhB,iBAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjB,uBAAuB;AAAA;AAAA,EAEvB,aAAa;AAAA;AAAA,EAEb,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKZ,oBAAoB;AAAA;AAAA,EAEpB,kBAAkB;AAAA;AAAA,EAElB,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,EAKZ,YAAY;AAAA;AAAA,EAEZ,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAKf,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,EAKP,YAAY;AAAA;AAAA,EAEZ,MAAM;AAAA;AAAA,EAEN,MAAM;AAAA;AAAA,EAEN,SAAS;AAAA;AAAA,EAET,SAAS;AAAA;AAAA,EAET,WAAW;AAAA;AAAA,EAEX,UAAU;AAAA;AAAA,EAEV,eAAe;AAAA;AAAA,EAEf,MAAM;AAAA;AAAA,EAEN,SAAS;AAAA;AAAA,EAET,iBAAiB;AAAA;AAAA,EAEjB,sBAAsB;AAAA;AAAA,EAEtB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKd,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnB,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKd,qBAAqB;AAAA;AAAA,EAErB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKd,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,EAKf,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA,EAKd,eAAe;AAAA;AAAA,EAEf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlB,gBAAgB;AAAA;AAAA,EAEhB,QAAQ;AAAA;AAAA,EAER,gBAAgB;AAAA;AAAA,EAEhB,eAAe;AAAA;AAAA,EAEf,kBAAkB;AAAA;AAAA,EAElB,WAAW;AAAA;AAAA,EAEX,KAAK;AAAA;AAAA,EAEL,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnB,iBAAiB;AACrB,CAAC;AAcM,SAAS,YACZ,OACA,QAAqG,IAC9E;AACvB,QAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,KAAK,WAAW,IAAI,CAAC;AACtD,MAAI,SAAS,QAAW;AACpB,WAAO;AAAA,EACX;AACA,QAAM,OAAO,MAAM,KAAK,IAAI,KAAK,cAAc,KAAK,IAAI,KAAK;AAC7D,SAAO,IAAI,iBAAiB,MAAM,KAAK,SAAS,EAAE,MAAM,KAAK,MAAM,QAAQ,KAAK,QAAQ,OAAO,KAAK,OAAO;AAC/G;AAMA,MAAM,gBAAuD;AAAA,EACzD,CAAC,kBAAkB,GAAG;AAAA,EACtB,CAAC,sBAAsB,GAAG;AAAA,EAC1B,CAAC,oBAAoB,GAAG;AAAA,EACxB,CAAC,qBAAqB,GAAG;AAC7B;AAMO,MAAM,kBAAsC,OAAO,OAAO;AAAA,EAC7D,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ,OAAO,OAAO,EAAE;AAAA,EACxB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU;AAAA,EACV,SAAS;AAAA,EACT,WAAW;AAAA,EACX,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,KAAK;AACT,CAAC;AASM,SAAS,aAAa,WAA4D;AACrF,SAAO,OAAO,OAAO,EAAE,GAAG,iBAAiB,GAAG,WAAW,QAAQ,OAAO,OAAO,CAAC,GAAI,UAAU,UAAU,CAAA,CAAG,CAAC,GAAG;AACnH;AA6EA,MAAM,uCAA4C,IAAI,CAAC,YAAY,QAAQ,UAAU,UAAU,YAAY,CAAC;AAC5G,MAAM,gCAAqC,IAAI,CAAC,SAAS,QAAQ,SAAS,WAAW,CAAC;AACtF,MAAM,qCAA0C,IAAI,CAAC,SAAS,OAAO,aAAa,YAAY,CAAC;AAC/F,MAAM,kBAAuC,oBAAI,IAAI,CAAC,UAAU,SAAS,CAAC;AAanE,SAAS,kBACZ,UACA,MACA,SACA,SAAsB,CAAA,GACZ;AACV,QAAM,QAAoB,CAAA;AAC1B,QAAM,OAAO,CAAC,MAAc,SAAiB,SAAwB,MAAM,QAAuB,SAAe;AAC7G,UAAM,KAAK,OAAO,OAAO,EAAE,MAAM,SAAS,QAAQ,MAAA,CAAO,CAAC;AAAA,EAC9D;AACA,QAAM,SAAS,OAAO,UAAU,CAAC,QAAQ,QAAQ,OAAO;AACxD,QAAM,QAAQ,OAAO,SAAS,MAAM;AAEpC,MAAI,OAAO;AACP,uBAAmB,UAAU,MAAM,SAAS,IAAI;AAAA,EACpD;AAEA,QAAM,kBAAkB,wBAAwB,UAAU,KAAK,SAAS;AACxE,MAAI,kBAAkB,GAAG;AACrB,QAAI,KAAK,cAAc,iBAAiB;AACpC;AAAA,QACI,KAAK;AAAA,QACL,QAAQ,gBAAgB,WAClB,GAAG,eAAe,WAAW,OAAO,eAAe,CAAC,IAAI,MAAM,iBAAiB,MAAM,KAAK,CAAC,wNAC3F,GAAG,eAAe,WAAW,OAAO,eAAe,CAAC,IAAI,MAAM,iBAAiB,MAAM,KAAK,CAAC;AAAA,QACjG;AAAA,QACA;AAAA,MAAA;AAAA,IAER,WAAW,QAAQ,gBAAgB,UAAU;AACzC;AAAA,QACI,KAAK;AAAA,QACL,GAAG,eAAe,WAAW,OAAO,eAAe,CAAC,IAAI,YAAY,KAAK,WAAW,eAAe,CAAC;AAAA,QACpG;AAAA,QACA;AAAA,MAAA;AAAA,IAER,OAAO;AACH;AAAA,QACI,KAAK;AAAA,QACL,GAAG,eAAe,WAAW,OAAO,eAAe,CAAC,IAAI,YAAY,KAAK,WAAW,eAAe,CAAC;AAAA,QACpG;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AAEA,MAAI,OAAO,gBAAgB,QAAW;AAClC,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,SAAS,WAAW,KAAK;AACzC,YAAM,KAAK,SAAS,IAAI,KAAK,CAAC;AAC9B,UAAI,aAAa,OAAO,EAAE,GAAG,OAAO,WAAW,MAAM,IAAI;AACrD;AAAA,MACJ;AAAA,IACJ;AACA,QAAI,UAAU,GAAG;AACb;AAAA,QACI,KAAK;AAAA,QACL,GAAG,OAAO,WAAW,OAAO,OAAO,CAAC,IAAI,MAAM,SAAS,SAAS,MAAM,CAAC;AAAA,QACvE;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AAEA,MAAI,OAAO;AACP,iBAAa,UAAU,QAAQ,IAAI;AAAA,EACvC;AACA,MAAI,OAAO,SAAS,MAAM,GAAG;AACzB,iBAAa,SAAS,OAAO,QAAQ,MAAM,QAAQ,IAAI;AAAA,EAC3D;AACA,MAAI,OAAO;AACP,iBAAa,SAAS,OAAO,QAAQ,MAAM,QAAQ,IAAI;AAAA,EAC3D;AACA,QAAM,eAAe,OAAO,SAAS,OAAO,IAAI,SAAS,MAAM,MAAA,IAAU,CAAA;AACzE,MAAI,aAAa,SAAS,GAAG;AACzB,QAAI,KAAK,iBAAiB;AACtB,mBAAa,SAAS,OAAO,SAAS,MAAM,QAAQ,IAAI;AAAA,IAC5D,OAAO;AACH,YAAM;AAAA,QACF,OAAO,OAAO;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,GAAG,aAAa,MAAM,mBAAmB,OAAO,aAAa,MAAM,CAAC;AAAA,UAC7E,QAAQ;AAAA,UACR,OAAO,aAAa;AAAA,UACpB,SAAS,OAAO,OAAO,CAAC,GAAG,YAAY,CAAC;AAAA,QAAA,CAC3C;AAAA,MAAA;AAAA,IAET;AAAA,EACJ;AAEA,aAAW,CAAC,MAAM,KAAK,KAAK,SAAS,YAAY;AAG7C,UAAM,CAAA,EAAG,IAAI,IAAI,KAAK,MAAM,GAAG;AAC/B,UAAM,QAAQ,KAAK,WAAW,WAAW,MAAM,SAAS,UAAU,SAAS,UAAU,OAAO;AAC5F,QAAI,CAAC,OAAO,SAAS,KAAK,GAAG;AACzB;AAAA,IACJ;AACA,QAAI,KAAK,WAAW,WAAW,GAAG;AAC9B,UAAI,KAAK,aAAa,kBAAkB;AACpC;AAAA,UACI,KAAK;AAAA,UACL,sBAAsB,IAAI,MAAM,MAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,CAAC;AAAA,UAC3E;AAAA,UACA,MAAM;AAAA,QAAA;AAAA,MAEd;AAAA,IACJ,OAAO;AACH;AAAA,QACI,KAAK;AAAA,QACL,oBAAoB,IAAI,MAAM,MAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,CAAC;AAAA,QACzE;AAAA,QACA,MAAM;AAAA,MAAA;AAAA,IAEd;AAAA,EACJ;AACA,SAAO;AACX;AAUA,SAAS,mBACL,UACA,MACA,SACA,MACI;AAEJ,QAAM,QAAQ,gBAAgB,QAAQ;AACtC,MAAI,QAAQ,KAAK,CAAC,KAAK,gBAAgB;AACnC,QAAI,QAAQ,qBAAqB,SAAS;AACtC;AAAA,QACI,KAAK;AAAA,QACL,GAAG,KAAK,mBAAmB,OAAO,KAAK,CAAC;AAAA,QACxC;AAAA,QACA;AAAA,MAAA;AAAA,IAER,OAAO;AACH;AAAA,QACI,KAAK;AAAA,QACL,GAAG,KAAK,mBAAmB,OAAO,KAAK,CAAC,wBAAwB,MAAM,OAAO,MAAM,KAAK,CAAC,eAAe,QAAQ,gBAAgB;AAAA,QAChI;AAAA,QACA;AAAA,MAAA;AAAA,IAER;AAAA,EACJ;AAEA,MAAI,SAAS,MAAM,cAAc,CAAC,KAAK,YAAY;AAC/C,UAAM,WAAW,kBAAkB,QAAQ;AAC3C;AAAA,MACI,KAAK;AAAA,MACL,GAAG,QAAQ,iBAAiB,OAAO,QAAQ,CAAC;AAAA,MAC5C;AAAA,MACA;AAAA,IAAA;AAAA,EAER;AACA,MAAI,SAAS,gBAAgB,KAAK,CAAC,KAAK,WAAW;AAC/C;AAAA,MACI,KAAK;AAAA,MACL,GAAG,SAAS,aAAa,aAAa,OAAO,SAAS,aAAa,CAAC;AAAA,MACpE;AAAA,MACA,SAAS;AAAA,IAAA;AAAA,EAEjB;AAEA,QAAM,UAAU,SAAS,MAAM,OAAO,IAAI;AAC1C,MAAI,KAAK,YAAY,cAAc,YAAY,QAAQ,SAAS,YAAY,GAAG;AAC3E;AAAA,MACI,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IAAA;AAAA,EAEjB,WAAW,KAAK,YAAY,UAAU,YAAY,MAAM;AACpD;AAAA,MACI,KAAK;AAAA,MACL,mBAAmB,QAAQ,KAAK,IAAI;AAAA,MACpC,QAAQ,KAAK;AAAA,MACb,SAAS;AAAA,IAAA;AAAA,EAEjB;AACJ;AAQA,SAAS,YAAY,SAA0C,GAAmB;AAC9E,QAAM,MAAM,MAAM,GAAG,MAAM,KAAK;AAChC,UAAQ,SAAA;AAAA,IACJ,KAAK;AACD,aAAO,GAAG,GAAG;AAAA,IACjB,KAAK;AACD,aAAO,GAAG,GAAG;AAAA,IACjB;AACI,aAAO,wBAAwB,OAAO;AAAA,EAAA;AAElD;AAQA,SAAS,YAAY,QAAgB,QAAmE;AACpG,MAAI,WAAW,QAAQ;AACnB,WAAO,OAAO,iBAAiB,OAAO;AAAA,EAC1C;AACA,MAAI,WAAW,QAAQ;AACnB,WAAO,OAAO,iBAAiB,OAAO;AAAA,EAC1C;AACA,SAAO,OAAO;AAClB;AAQA,SAAS,iBAAiB,QAAgB,QAA0C;AAChF,MAAI,WAAW,QAAQ;AACnB,WAAO,OAAO,kBAAkB,OAAO;AAAA,EAC3C;AACA,MAAI,WAAW,QAAQ;AACnB,WAAO,OAAO,kBAAkB,OAAO;AAAA,EAC3C;AACA,SAAO,OAAO;AAClB;AAQA,SAAS,aAAa,UAAyB,QAAqB,MAAoB;AACpF,MAAI,OAAO,YAAY,SAAS,gBAAgB,QAAQ,EAAE,UAAU;AAChE;AAAA,MACI,KAAK;AAAA,MACL,qCAAqC,SAAS,SAAS,QAAQ,OAAO,SAAS,SAAS,CAAC,IAAI,MAAM,SAAS,WAAW,SAAS,MAAM,CAAC;AAAA,MACvI;AAAA,MACA,SAAS;AAAA,IAAA;AAAA,EAEjB;AACJ;AAMA,MAAM,aAA+C;AAAA,EACjD,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,UAAU;AAAA,EACV,YAAY;AAChB;AAUA,SAAS,aACL,OACA,QACA,MACA,QACA,MACI;AACJ,aAAW,UAAU,OAAO;AACxB,UAAM,EAAE,MAAM,KAAA,IAAS,OAAO;AAC9B,QAAI,SAAS,QAAQ,iBAAiB,IAAI,IAAI,GAAG;AAC7C;AAAA,IACJ;AACA,UAAM,QAAQ,GAAG,MAAM,YAAY,IAAI;AACvC,oBAAgB,QAAQ,OAAO,YAAY,QAAQ,MAAM,GAAG,IAAI;AAChE,QAAI,WAAW,UAAU,SAAS,MAAM;AACpC;AAAA,IACJ;AACA,QAAI,gBAAgB,QAAQ,QAAQ,MAAM,GAAG;AACzC,WAAK,KAAK,gBAAgB,GAAG,KAAK,mBAAmB,MAAM,OAAO,SAAS,OAAO,SAAS;AAC3F;AAAA,IACJ;AACA,QAAI,CAAC,cAAc,QAAQ,OAAO,MAAM,QAAQ,IAAI,GAAG;AACnD,2BAAqB,QAAQ,OAAO,MAAM,QAAQ,IAAI;AAAA,IAC1D;AAAA,EACJ;AACJ;AASA,SAAS,gBACL,QACA,OACA,WACA,MACI;AACJ,QAAM,EAAE,MAAM,KAAA,IAAS,OAAO;AAC9B,MAAI,SAAS,QAAQ,cAAc,QAAW;AAC1C;AAAA,EACJ;AACA,QAAM,QAAQ,UAAU,IAAI;AAC5B,MAAI,UAAU,UAAa,UAAU,MAAM;AACvC;AAAA,MACI,KAAK;AAAA,MACL,GAAG,KAAK,SAAS,WAAW,IAAI,KAAK,IAAI,oCAAoC,IAAI,uBAAuB,KAAK;AAAA,MAC7G;AAAA,MACA,OAAO,SAAS,OAAO;AAAA,IAAA;AAAA,EAE/B;AACJ;AAUA,SAAS,gBAAgB,QAAgB,QAAgB,QAA8B;AACnF,QAAM,EAAE,MAAM,KAAA,IAAS,OAAO;AAC9B,QAAM,QAAQ,OAAO,iBAAiB,MAAyB,GAAG,SAAS,IAAI,KAAK;AACpF,SACI,iBAAiB,QAAQ,MAAM,MAAM,SAAS,CAAC,SAAS,EAAE,SAAS,SAAS,OAAO,OAAO,IAAI,IAAI,KAAK;AAE/G;AAcA,SAAS,YAA2C;AAChD,QAAM,4BAAY,IAAA;AAClB,QAAM,MAAM,CAAC,OAAyB,SAAyB;AAC3D,eAAW,QAAQ,OAAO;AACtB,YAAM,IAAI,MAAM,IAAI;AAAA,IACxB;AAAA,EACJ;AACA,MAAI,CAAC,UAAU,GAAG;AAAA,IACd,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,SAAS,CAAC,OAAO,WAAW,OAAO,iBAAiB;AAAA,EAAA,CACvD;AACD,MAAI,WAAW,EAAE,MAAM,KAAK,KAAK,SAAS,CAAC,SAAS,KAAK,KAAK;AAC9D,MAAI,iBAAiB,EAAE,MAAM,KAAK,WAAW,SAAS,CAAC,SAAS,KAAK,WAAW;AAChF,MAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,SAAS,CAAC,SAAS,KAAK,aAAa,OAAA,CAAQ;AACxF,MAAI,CAAC,QAAQ,GAAG;AAAA,IACZ,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,SAAS,CAAC,SAAS,KAAK,aAAa,YAAY,KAAK,aAAa;AAAA,EAAA,CACtE;AAGD,MAAI,CAAC,QAAQ,YAAY,GAAG;AAAA,IACxB,MAAM,KAAK;AAAA,IACX,MAAM;AAAA,IACN,SAAS,CAAC,OAAO,WAAW,OAAO,kBAAkB;AAAA,EAAA,CACxD;AACD,SAAO;AACX;AAEA,MAAM,aAAa,UAAA;AAYnB,SAAS,cACL,QACA,OACA,MACA,QACA,MACO;AACP,QAAM,EAAE,MAAM,KAAA,IAAS,OAAO;AAC9B,MAAI,SAAS,YAAY;AACrB,wBAAoB,QAAQ,OAAO,MAAM,QAAQ,IAAI;AACrD,WAAO;AAAA,EACX;AACA,QAAM,OAAO,SAAS,OAAO,SAAY,WAAW,IAAI,IAAI;AAC5D,MAAI,SAAS,QAAQ,SAAS,QAAW;AACrC,WAAO;AAAA,EACX;AACA,MAAI,CAAC,KAAK,QAAQ,MAAM,MAAM,GAAG;AAC7B,SAAK,KAAK,MAAM,GAAG,KAAK,KAAK,KAAK,QAAQ,IAAI,uBAAuB,MAAM,OAAO,SAAS,OAAO,SAAS;AAAA,EAC/G;AACA,SAAO;AACX;AAUA,SAAS,oBACL,QACA,OACA,MACA,QACA,MACI;AACJ,QAAM,EAAE,SAAS;AACjB,MAAI,CAAC,KAAK,WAAW;AACjB,SAAK,KAAK,WAAW,GAAG,KAAK,kCAAkC,KAAK,MAAM,OAAO,SAAS,OAAO,SAAS;AAAA,EAC9G,WAAW,OAAO,kBAAkB,UAAa,KAAK,UAAU,OAAO,eAAe;AAClF;AAAA,MACI,KAAK;AAAA,MACL,GAAG,KAAK,mBAAmB,KAAK,KAAK,wCAAwC,OAAO,aAAa;AAAA,MACjG,KAAK;AAAA,MACL,OAAO,SAAS,OAAO;AAAA,IAAA;AAAA,EAE/B;AACJ;AAWA,SAAS,qBACL,QACA,OACA,MACA,QACA,MACI;AACJ,QAAM,EAAE,SAAS;AACjB,QAAM,EAAE,MAAM,KAAA,IAAS;AACvB,MAAI,SAAS,QAAQ,EAAE,OAAO,OAAO,IAAI,IAAI,KAAK,QAAQ;AACtD;AAAA,MACI,KAAK;AAAA,MACL,GAAG,KAAK,KAAK,IAAI,wDAAwD,IAAI;AAAA,MAC7E;AAAA,MACA,OAAO,SAAS,OAAO;AAAA,IAAA;AAAA,EAE/B;AACA,MAAI,KAAK,WAAW,KAAK,aAAa,kBAAkB;AACpD,SAAK,KAAK,gBAAgB,GAAG,KAAK,qDAAqD,MAAM,IAAI;AAAA,EACrG;AACA,aAAW,QAAQ,OAAO,MAAM,IAAI;AACpC,MAAI,KAAK,YAAY,UAAa,CAAC,KAAK,UAAU;AAC9C,SAAK,KAAK,SAAS,GAAG,KAAK,mDAAmD,MAAM,IAAI;AAAA,EAC5F;AACA,MAAI,KAAK,YAAY,QAAQ,CAAC,KAAK,SAAS;AACxC,SAAK,KAAK,SAAS,GAAG,KAAK,qDAAqD,MAAM,IAAI;AAAA,EAC9F;AACJ;AAUA,SAAS,WAAW,QAAgB,OAAe,MAA0B,MAAoB;AAC7F,QAAM,EAAE,SAAS;AACjB,QAAM,EAAE,SAAS;AACjB,QAAM,MAAM,OAAO,SAAS,OAAO;AACnC,UAAQ,KAAK,OAAA;AAAA,IACT,KAAK;AACD,UAAI,CAAC,KAAK,OAAO;AACb,aAAK,KAAK,MAAM,GAAG,KAAK,uCAAuC,MAAM,GAAG;AAAA,MAC5E,WAAW,KAAK,cAAc,QAAQ,CAAC,KAAK,OAAO,SAAS,KAAK,SAAS,GAAG;AACzE;AAAA,UACI,KAAK;AAAA,UACL,GAAG,KAAK,UAAU,KAAK,SAAS;AAAA,UAChC;AAAA,UACA;AAAA,QAAA;AAAA,MAER,WAAW,KAAK,cAAc,QAAQ;AAElC,aAAK,KAAK,OAAO,GAAG,KAAK,qDAAqD,MAAM,GAAG;AAAA,MAC3F;AACA;AAAA,IACJ,KAAK;AACD,UAAI,CAAC,KAAK,MAAM;AACZ,aAAK,KAAK,MAAM,GAAG,KAAK,yDAAyD,MAAM,GAAG;AAAA,MAC9F;AACA;AAAA,IACJ;AACI,UAAI,CAAC,KAAK,OAAO,SAAS,KAAK,KAAK,GAAG;AACnC,aAAK,KAAK,OAAO,GAAG,KAAK,OAAO,KAAK,KAAK,uCAAuC,MAAM,GAAG;AAAA,MAC9F;AACA,UAAI,KAAK,aAAa,KAAK,CAAC,KAAK,YAAY;AACzC;AAAA,UACI,KAAK;AAAA,UACL,GAAG,KAAK,QAAQ,KAAK,UAAU;AAAA,UAC/B;AAAA,UACA;AAAA,QAAA;AAAA,MAER;AACA;AAAA,EAAA;AAEZ;AASO,SAAS,gBAAgB,UAAiC;AAC7D,MAAI,CAAC,SAAS,UAAU;AACpB,WAAO;AAAA,EACX;AACA,QAAM,WAAW,SAAS,MAAM,OAAO,UAAU;AACjD,MAAI,UAAU,UAAU,QAAQ;AAC5B,WAAO;AAAA,EACX;AACA,QAAM,OAAO,SAAS,MAAM,OAAO,MAAM;AACzC,MAAI,iBAAiB;AACrB,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,SAAS,WAAW,KAAK;AACzC,QAAI,SAAS,MAAM,CAAC,KAAK,SAAS,MAAM,CAAC,MAAM,OAAO;AAClD;AACA,UAAI,SAAS,QAAQ,KAAK,UAAU,SAAS,KAAK,MAAM,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,eAAe;AAC1F;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,SAAO,SAAS,KAAK,iBAAiB;AAC1C;AASO,SAAS,kBAAkB,UAAiC;AAC/D,QAAM,EAAE,QAAQ,QAAQ,UAAA,IAAc;AACtC,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAChC,aAAS,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,OAAO,IAAI,CAAC,GAAG,KAAK;AAChD,UAAI,OAAO,CAAC,MAAM,OAAO,IAAI,CAAC,GAAG;AAC7B;AAAA,MACJ;AAAA,IACJ;AAAA,EACJ;AACA,SAAO,SAAS,WAAW,QAAQ,KAAK,KAAK,QAAQ,CAAC;AAC1D;AA0CA,MAAM,mBAA2D;AAAA,EAC7D,CAAC,IAAM,EAAI;AAAA,EACX,CAAC,IAAM,EAAI;AAAA,EACX,CAAC,IAAM,EAAI;AAAA,EACX,CAAC,IAAM,EAAI;AAAA,EACX,CAAC,IAAM,GAAI;AAAA,EACX,CAAC,KAAM,GAAI;AAAA,EACX,CAAC,KAAM,GAAI;AAAA,EACX,CAAC,KAAM,GAAI;AAAA,EACX,CAAC,KAAM,GAAK;AAAA,EACZ,CAAC,KAAO,IAAM;AAAA,EACd,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAQ,KAAM;AAAA,EACf,CAAC,OAAS,MAAO;AACrB;AAQO,SAAS,WAAW,IAAqB;AAC5C,aAAW,CAAC,IAAI,EAAE,KAAK,kBAAkB;AACrC,QAAI,KAAK,IAAI;AACT,aAAO;AAAA,IACX;AACA,QAAI,MAAM,IAAI;AACV,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAQO,SAAS,UAAU,MAAuB;AAC7C,MAAI,KAAK,WAAW,GAAG;AACnB,WAAO;AAAA,EACX;AACA,aAAW,MAAM,MAAM;AACnB,UAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,QAAI,OAAO,UAAa,CAAC,WAAW,EAAE,GAAG;AACrC,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AASO,SAAS,kBAAkB,IAAY,SAA6B;AACvE,UAAQ,SAAA;AAAA,IACJ,KAAK;AACD,aAAO;AAAA,IACX,KAAK;AACD,aAAO,UAAU,OAAO,EAAE,CAAC;AAAA,IAC/B,KAAK;AACD,aAAO,OAAO,OAAO,YAAY,OAAO,cAAc,EAAE;AAAA,IAC5D,KAAK;AACD,aAAO;AAAA,IACX,SAAS;AACL,YAAM,OAAe;AACrB,YAAM,IAAI,iBAAiB,iBAAiB,sBAAsB,IAAI,IAAI,EAAE,SAAS,MAAM;AAAA,IAC/F;AAAA,EAAA;AAER;AAUO,SAAS,wBAAwB,UAAyB,SAA4B;AACzF,QAAM,EAAE,QAAQ;AAChB,MAAI,YAAY,OAAO;AACnB,WAAO;AAAA,EACX;AACA,MAAI,YAAY,iBAAiB;AAC7B,QAAI,IAAI,SAAS,cAAc,IAAI,WAAW,GAAG;AAC7C,aAAO;AAAA,IACX;AACA,QAAI,UAAU;AACd,aAAS,IAAI,GAAG,IAAI,IAAI,MAAM,KAAK;AAC/B,UAAI,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG;AACvB;AAAA,MACJ;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACA,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,IAAI,MAAM,KAAK;AAC/B,QAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC,GAAG,OAAO,GAAG;AAC1C;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AACX;AAiBO,SAAS,YAAY,UAAyB,SAAoB,MAAwC;AAC7G,QAAM,EAAE,QAAQ;AAChB,QAAM,IAAI,IAAI;AACd,MAAI,YAAY,iBAAiB;AAC7B,UAAM,UAAU,wBAAwB,UAAU,OAAO;AACzD,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA,MAAM,CAAC,MAAsB,IAAI;AAAA,MACjC,WAAW,CAAC,MAAuB,IAAI,KAAK,CAAC,MAAM,IAAI;AAAA,MACvD,YAAY,CAAC,MAAsB,IAAI,KAAK,CAAC;AAAA,IAAA;AAAA,EAErD;AACA,QAAM,YAA0B;AAAA,IAC5B;AAAA,IACA,SAAS;AAAA,IACT,MAAM,CAAC,MAAsB,IAAI,KAAK,CAAC;AAAA,IACvC,WAAW,MAAe;AAAA,IAC1B,YAAY,CAAC,MAAsB,IAAI,KAAK,CAAC;AAAA,EAAA;AAEjD,MAAI,YAAY,OAAO;AACnB,WAAO;AAAA,EACX;AACA,QAAM,MAAgB,CAAA;AACtB,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,QAAI,CAAC,kBAAkB,IAAI,KAAK,CAAC,GAAG,OAAO,GAAG;AAC1C,UAAI,KAAK,CAAC;AAAA,IACd;AAAA,EACJ;AACA,MAAI,IAAI,WAAW,GAAG;AAClB,WAAO;AAAA,EACX;AACA,MAAI,SAAS,SAAS;AAClB,UAAM,QAAQ,IAAI,KAAK,IAAI,CAAC,CAAC;AAC7B,UAAM,IAAI;AAAA,MACN;AAAA,MACA,GAAG,IAAI,MAAM,WAAW,OAAO,IAAI,MAAM,CAAC,IAAI,YAAY,SAAS,IAAI,MAAM,CAAC,YAAY,KAAK,UAAU,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;AAAA,MAClI,EAAE,QAAQ,WAAW,SAAS,OAAO,IAAI,QAAQ,IAAI,OAAO,OAAO,IAAI,CAAC,EAAA;AAAA,IAAE;AAAA,EAElF;AACA,QAAM,8BAAc,IAAA;AACpB,MAAI,YAAY,WAAW;AACvB,UAAM,2BAAW,IAAA;AACjB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,YAAM,KAAK,IAAI,KAAK,CAAC;AACrB,UAAI,kBAAkB,IAAI,OAAO,GAAG;AAChC,aAAK,IAAI,OAAO,EAAE,CAAC;AAAA,MACvB;AAAA,IACJ;AACA,eAAW,KAAK,KAAK;AACjB,YAAM,OAAO,cAAc,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC;AAC9C,UAAI,YAAY;AAChB,eAAS,IAAI,GAAG,KAAK,IAAI,SAAS,GAAG,KAAK;AACtC,oBAAY,GAAG,IAAI,IAAI,CAAC;AAAA,MAC5B;AACA,WAAK,IAAI,SAAS;AAClB,cAAQ,IAAI,GAAG,SAAS;AAAA,IAC5B;AAAA,EACJ,OAAO;AACH,UAAM,2BAAW,IAAA;AACjB,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AACxB,YAAM,KAAK,IAAI,KAAK,CAAC;AACrB,UAAI,OAAO,OAAO,YAAY,OAAO,cAAc,EAAE,GAAG;AACpD,aAAK,IAAI,EAAE;AAAA,MACf;AAAA,IACJ;AACA,QAAI,OAAO;AACX,eAAW,KAAK,KAAK;AACjB,aAAO,KAAK,IAAI,IAAI,GAAG;AACnB;AAAA,MACJ;AACA,WAAK,IAAI,IAAI;AACb,cAAQ,IAAI,GAAG,IAAI;AACnB;AAAA,IACJ;AAAA,EACJ;AACA,SAAO;AAAA,IACH;AAAA,IACA,SAAS,IAAI;AAAA,IACb,MAAM,CAAC,MAAsB,QAAQ,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC;AAAA,IACzD,WAAW,CAAC,MAAuB,QAAQ,IAAI,CAAC;AAAA,IAChD,YAAY,CAAC,MAAsB,IAAI,KAAK,CAAC;AAAA,EAAA;AAErD;AAQO,SAAS,cAAc,MAAsB;AAChD,MAAI,MAAM;AACV,aAAW,MAAM,MAAM;AACnB,UAAM,KAAK,GAAG,YAAY,CAAC;AAC3B,WAAO,OAAO,UAAa,WAAW,EAAE,IAAI,KAAK;AAAA,EACrD;AACA,SAAO,IAAI,WAAW,IAAI,MAAM;AACpC;"}