@graphty/graph-io 0.0.0 → 0.2.0

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 (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -28
  3. package/dist/chunks/children-CL3Cy0ez.js +238 -0
  4. package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
  5. package/dist/chunks/escape-DyI8JofU.js +938 -0
  6. package/dist/chunks/escape-DyI8JofU.js.map +1 -0
  7. package/dist/chunks/importer-CQnJuWJw.js +2987 -0
  8. package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
  9. package/dist/chunks/importer-CpCpfbxr.js +2015 -0
  10. package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
  11. package/dist/chunks/importer-DbnGYr3_.js +2342 -0
  12. package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
  13. package/dist/chunks/importer-GozH8DkN.js +3050 -0
  14. package/dist/chunks/importer-GozH8DkN.js.map +1 -0
  15. package/dist/chunks/records-CGpxszm1.js +605 -0
  16. package/dist/chunks/records-CGpxszm1.js.map +1 -0
  17. package/dist/chunks/text-CajMdVFy.js +189 -0
  18. package/dist/chunks/text-CajMdVFy.js.map +1 -0
  19. package/dist/chunks/writer-DxSKC7TL.js +2842 -0
  20. package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
  21. package/dist/csv.d.ts +1 -0
  22. package/dist/csv.js +1702 -0
  23. package/dist/csv.js.map +1 -0
  24. package/dist/dot.d.ts +1 -0
  25. package/dist/dot.js +8 -0
  26. package/dist/dot.js.map +1 -0
  27. package/dist/gexf.d.ts +1 -0
  28. package/dist/gexf.js +3466 -0
  29. package/dist/gexf.js.map +1 -0
  30. package/dist/gml.d.ts +1 -0
  31. package/dist/gml.js +2647 -0
  32. package/dist/gml.js.map +1 -0
  33. package/dist/graph-io.d.ts +1 -0
  34. package/dist/graph-io.js +790 -0
  35. package/dist/graph-io.js.map +1 -0
  36. package/dist/graphml.d.ts +1 -0
  37. package/dist/graphml.js +8 -0
  38. package/dist/graphml.js.map +1 -0
  39. package/dist/json.d.ts +1 -0
  40. package/dist/json.js +11 -0
  41. package/dist/json.js.map +1 -0
  42. package/dist/neo4j.d.ts +1 -0
  43. package/dist/neo4j.js +2046 -0
  44. package/dist/neo4j.js.map +1 -0
  45. package/dist/pajek.d.ts +1 -0
  46. package/dist/pajek.js +8 -0
  47. package/dist/pajek.js.map +1 -0
  48. package/dist/src/children.d.ts +134 -0
  49. package/dist/src/children.d.ts.map +1 -0
  50. package/dist/src/children.js +274 -0
  51. package/dist/src/children.js.map +1 -0
  52. package/dist/src/common/attributes.d.ts +229 -0
  53. package/dist/src/common/attributes.d.ts.map +1 -0
  54. package/dist/src/common/attributes.js +368 -0
  55. package/dist/src/common/attributes.js.map +1 -0
  56. package/dist/src/common/codes.d.ts +105 -0
  57. package/dist/src/common/codes.d.ts.map +1 -0
  58. package/dist/src/common/codes.js +107 -0
  59. package/dist/src/common/codes.js.map +1 -0
  60. package/dist/src/common/declared-types.d.ts +84 -0
  61. package/dist/src/common/declared-types.d.ts.map +1 -0
  62. package/dist/src/common/declared-types.js +326 -0
  63. package/dist/src/common/declared-types.js.map +1 -0
  64. package/dist/src/common/direction.d.ts +206 -0
  65. package/dist/src/common/direction.d.ts.map +1 -0
  66. package/dist/src/common/direction.js +370 -0
  67. package/dist/src/common/direction.js.map +1 -0
  68. package/dist/src/common/escape.d.ts +92 -0
  69. package/dist/src/common/escape.d.ts.map +1 -0
  70. package/dist/src/common/escape.js +212 -0
  71. package/dist/src/common/escape.js.map +1 -0
  72. package/dist/src/common/export.d.ts +249 -0
  73. package/dist/src/common/export.d.ts.map +1 -0
  74. package/dist/src/common/export.js +594 -0
  75. package/dist/src/common/export.js.map +1 -0
  76. package/dist/src/common/format.d.ts +59 -0
  77. package/dist/src/common/format.d.ts.map +1 -0
  78. package/dist/src/common/format.js +106 -0
  79. package/dist/src/common/format.js.map +1 -0
  80. package/dist/src/common/ids.d.ts +83 -0
  81. package/dist/src/common/ids.d.ts.map +1 -0
  82. package/dist/src/common/ids.js +158 -0
  83. package/dist/src/common/ids.js.map +1 -0
  84. package/dist/src/common/input.d.ts +100 -0
  85. package/dist/src/common/input.d.ts.map +1 -0
  86. package/dist/src/common/input.js +335 -0
  87. package/dist/src/common/input.js.map +1 -0
  88. package/dist/src/common/lists.d.ts +34 -0
  89. package/dist/src/common/lists.d.ts.map +1 -0
  90. package/dist/src/common/lists.js +185 -0
  91. package/dist/src/common/lists.js.map +1 -0
  92. package/dist/src/common/options.d.ts +108 -0
  93. package/dist/src/common/options.d.ts.map +1 -0
  94. package/dist/src/common/options.js +265 -0
  95. package/dist/src/common/options.js.map +1 -0
  96. package/dist/src/common/report.d.ts +187 -0
  97. package/dist/src/common/report.d.ts.map +1 -0
  98. package/dist/src/common/report.js +274 -0
  99. package/dist/src/common/report.js.map +1 -0
  100. package/dist/src/common/temporal.d.ts +71 -0
  101. package/dist/src/common/temporal.d.ts.map +1 -0
  102. package/dist/src/common/temporal.js +266 -0
  103. package/dist/src/common/temporal.js.map +1 -0
  104. package/dist/src/common/text.d.ts +104 -0
  105. package/dist/src/common/text.d.ts.map +1 -0
  106. package/dist/src/common/text.js +255 -0
  107. package/dist/src/common/text.js.map +1 -0
  108. package/dist/src/common/weights.d.ts +77 -0
  109. package/dist/src/common/weights.d.ts.map +1 -0
  110. package/dist/src/common/weights.js +156 -0
  111. package/dist/src/common/weights.js.map +1 -0
  112. package/dist/src/common/writer.d.ts +51 -0
  113. package/dist/src/common/writer.d.ts.map +1 -0
  114. package/dist/src/common/writer.js +108 -0
  115. package/dist/src/common/writer.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +245 -0
  117. package/dist/src/common/xml.d.ts.map +1 -0
  118. package/dist/src/common/xml.js +942 -0
  119. package/dist/src/common/xml.js.map +1 -0
  120. package/dist/src/formats/csv/exporter.d.ts +70 -0
  121. package/dist/src/formats/csv/exporter.d.ts.map +1 -0
  122. package/dist/src/formats/csv/exporter.js +682 -0
  123. package/dist/src/formats/csv/exporter.js.map +1 -0
  124. package/dist/src/formats/csv/header.d.ts +66 -0
  125. package/dist/src/formats/csv/header.d.ts.map +1 -0
  126. package/dist/src/formats/csv/header.js +152 -0
  127. package/dist/src/formats/csv/header.js.map +1 -0
  128. package/dist/src/formats/csv/importer.d.ts +82 -0
  129. package/dist/src/formats/csv/importer.d.ts.map +1 -0
  130. package/dist/src/formats/csv/importer.js +849 -0
  131. package/dist/src/formats/csv/importer.js.map +1 -0
  132. package/dist/src/formats/csv/index.d.ts +60 -0
  133. package/dist/src/formats/csv/index.d.ts.map +1 -0
  134. package/dist/src/formats/csv/index.js +63 -0
  135. package/dist/src/formats/csv/index.js.map +1 -0
  136. package/dist/src/formats/csv/records.d.ts +188 -0
  137. package/dist/src/formats/csv/records.d.ts.map +1 -0
  138. package/dist/src/formats/csv/records.js +702 -0
  139. package/dist/src/formats/csv/records.js.map +1 -0
  140. package/dist/src/formats/csv/values.d.ts +105 -0
  141. package/dist/src/formats/csv/values.d.ts.map +1 -0
  142. package/dist/src/formats/csv/values.js +192 -0
  143. package/dist/src/formats/csv/values.js.map +1 -0
  144. package/dist/src/formats/dot/exporter.d.ts +52 -0
  145. package/dist/src/formats/dot/exporter.d.ts.map +1 -0
  146. package/dist/src/formats/dot/exporter.js +836 -0
  147. package/dist/src/formats/dot/exporter.js.map +1 -0
  148. package/dist/src/formats/dot/importer.d.ts +102 -0
  149. package/dist/src/formats/dot/importer.d.ts.map +1 -0
  150. package/dist/src/formats/dot/importer.js +1291 -0
  151. package/dist/src/formats/dot/importer.js.map +1 -0
  152. package/dist/src/formats/dot/index.d.ts +7 -0
  153. package/dist/src/formats/dot/index.d.ts.map +1 -0
  154. package/dist/src/formats/dot/index.js +7 -0
  155. package/dist/src/formats/dot/index.js.map +1 -0
  156. package/dist/src/formats/dot/names.d.ts +29 -0
  157. package/dist/src/formats/dot/names.d.ts.map +1 -0
  158. package/dist/src/formats/dot/names.js +28 -0
  159. package/dist/src/formats/dot/names.js.map +1 -0
  160. package/dist/src/formats/dot/tokenizer.d.ts +114 -0
  161. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
  162. package/dist/src/formats/dot/tokenizer.js +341 -0
  163. package/dist/src/formats/dot/tokenizer.js.map +1 -0
  164. package/dist/src/formats/gexf/exporter.d.ts +56 -0
  165. package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
  166. package/dist/src/formats/gexf/exporter.js +1395 -0
  167. package/dist/src/formats/gexf/exporter.js.map +1 -0
  168. package/dist/src/formats/gexf/importer.d.ts +73 -0
  169. package/dist/src/formats/gexf/importer.d.ts.map +1 -0
  170. package/dist/src/formats/gexf/importer.js +1880 -0
  171. package/dist/src/formats/gexf/importer.js.map +1 -0
  172. package/dist/src/formats/gexf/index.d.ts +96 -0
  173. package/dist/src/formats/gexf/index.d.ts.map +1 -0
  174. package/dist/src/formats/gexf/index.js +97 -0
  175. package/dist/src/formats/gexf/index.js.map +1 -0
  176. package/dist/src/formats/gexf/schema.d.ts +135 -0
  177. package/dist/src/formats/gexf/schema.d.ts.map +1 -0
  178. package/dist/src/formats/gexf/schema.js +323 -0
  179. package/dist/src/formats/gexf/schema.js.map +1 -0
  180. package/dist/src/formats/gml/exporter.d.ts +69 -0
  181. package/dist/src/formats/gml/exporter.d.ts.map +1 -0
  182. package/dist/src/formats/gml/exporter.js +1093 -0
  183. package/dist/src/formats/gml/exporter.js.map +1 -0
  184. package/dist/src/formats/gml/importer.d.ts +66 -0
  185. package/dist/src/formats/gml/importer.d.ts.map +1 -0
  186. package/dist/src/formats/gml/importer.js +1331 -0
  187. package/dist/src/formats/gml/importer.js.map +1 -0
  188. package/dist/src/formats/gml/index.d.ts +85 -0
  189. package/dist/src/formats/gml/index.d.ts.map +1 -0
  190. package/dist/src/formats/gml/index.js +88 -0
  191. package/dist/src/formats/gml/index.js.map +1 -0
  192. package/dist/src/formats/gml/syntax.d.ts +186 -0
  193. package/dist/src/formats/gml/syntax.d.ts.map +1 -0
  194. package/dist/src/formats/gml/syntax.js +467 -0
  195. package/dist/src/formats/gml/syntax.js.map +1 -0
  196. package/dist/src/formats/graphml/constants.d.ts +169 -0
  197. package/dist/src/formats/graphml/constants.d.ts.map +1 -0
  198. package/dist/src/formats/graphml/constants.js +165 -0
  199. package/dist/src/formats/graphml/constants.js.map +1 -0
  200. package/dist/src/formats/graphml/exporter.d.ts +34 -0
  201. package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
  202. package/dist/src/formats/graphml/exporter.js +1176 -0
  203. package/dist/src/formats/graphml/exporter.js.map +1 -0
  204. package/dist/src/formats/graphml/importer.d.ts +31 -0
  205. package/dist/src/formats/graphml/importer.d.ts.map +1 -0
  206. package/dist/src/formats/graphml/importer.js +1607 -0
  207. package/dist/src/formats/graphml/importer.js.map +1 -0
  208. package/dist/src/formats/graphml/index.d.ts +8 -0
  209. package/dist/src/formats/graphml/index.d.ts.map +1 -0
  210. package/dist/src/formats/graphml/index.js +8 -0
  211. package/dist/src/formats/graphml/index.js.map +1 -0
  212. package/dist/src/formats/graphml/tree.d.ts +72 -0
  213. package/dist/src/formats/graphml/tree.d.ts.map +1 -0
  214. package/dist/src/formats/graphml/tree.js +290 -0
  215. package/dist/src/formats/graphml/tree.js.map +1 -0
  216. package/dist/src/formats/json/dialect.d.ts +125 -0
  217. package/dist/src/formats/json/dialect.d.ts.map +1 -0
  218. package/dist/src/formats/json/dialect.js +262 -0
  219. package/dist/src/formats/json/dialect.js.map +1 -0
  220. package/dist/src/formats/json/exporter.d.ts +89 -0
  221. package/dist/src/formats/json/exporter.d.ts.map +1 -0
  222. package/dist/src/formats/json/exporter.js +1358 -0
  223. package/dist/src/formats/json/exporter.js.map +1 -0
  224. package/dist/src/formats/json/importer.d.ts +108 -0
  225. package/dist/src/formats/json/importer.d.ts.map +1 -0
  226. package/dist/src/formats/json/importer.js +1838 -0
  227. package/dist/src/formats/json/importer.js.map +1 -0
  228. package/dist/src/formats/json/index.d.ts +8 -0
  229. package/dist/src/formats/json/index.d.ts.map +1 -0
  230. package/dist/src/formats/json/index.js +8 -0
  231. package/dist/src/formats/json/index.js.map +1 -0
  232. package/dist/src/formats/neo4j/exporter.d.ts +68 -0
  233. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
  234. package/dist/src/formats/neo4j/exporter.js +1055 -0
  235. package/dist/src/formats/neo4j/exporter.js.map +1 -0
  236. package/dist/src/formats/neo4j/header.d.ts +52 -0
  237. package/dist/src/formats/neo4j/header.d.ts.map +1 -0
  238. package/dist/src/formats/neo4j/header.js +131 -0
  239. package/dist/src/formats/neo4j/header.js.map +1 -0
  240. package/dist/src/formats/neo4j/importer.d.ts +73 -0
  241. package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
  242. package/dist/src/formats/neo4j/importer.js +932 -0
  243. package/dist/src/formats/neo4j/importer.js.map +1 -0
  244. package/dist/src/formats/neo4j/index.d.ts +79 -0
  245. package/dist/src/formats/neo4j/index.d.ts.map +1 -0
  246. package/dist/src/formats/neo4j/index.js +83 -0
  247. package/dist/src/formats/neo4j/index.js.map +1 -0
  248. package/dist/src/formats/pajek/exporter.d.ts +58 -0
  249. package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
  250. package/dist/src/formats/pajek/exporter.js +825 -0
  251. package/dist/src/formats/pajek/exporter.js.map +1 -0
  252. package/dist/src/formats/pajek/importer.d.ts +88 -0
  253. package/dist/src/formats/pajek/importer.d.ts.map +1 -0
  254. package/dist/src/formats/pajek/importer.js +1047 -0
  255. package/dist/src/formats/pajek/importer.js.map +1 -0
  256. package/dist/src/formats/pajek/index.d.ts +7 -0
  257. package/dist/src/formats/pajek/index.d.ts.map +1 -0
  258. package/dist/src/formats/pajek/index.js +7 -0
  259. package/dist/src/formats/pajek/index.js.map +1 -0
  260. package/dist/src/formats/pajek/syntax.d.ts +112 -0
  261. package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
  262. package/dist/src/formats/pajek/syntax.js +269 -0
  263. package/dist/src/formats/pajek/syntax.js.map +1 -0
  264. package/dist/src/index.d.ts +35 -0
  265. package/dist/src/index.d.ts.map +1 -0
  266. package/dist/src/index.js +39 -0
  267. package/dist/src/index.js.map +1 -0
  268. package/dist/src/registry.d.ts +207 -0
  269. package/dist/src/registry.d.ts.map +1 -0
  270. package/dist/src/registry.js +481 -0
  271. package/dist/src/registry.js.map +1 -0
  272. package/dist/src/sniff.d.ts +104 -0
  273. package/dist/src/sniff.d.ts.map +1 -0
  274. package/dist/src/sniff.js +357 -0
  275. package/dist/src/sniff.js.map +1 -0
  276. package/dist/src/types.d.ts +238 -0
  277. package/dist/src/types.d.ts.map +1 -0
  278. package/dist/src/types.js +29 -0
  279. package/dist/src/types.js.map +1 -0
  280. package/dist/tsconfig.build.tsbuildinfo +1 -0
  281. package/package.json +122 -7
  282. package/src/children.ts +335 -0
  283. package/src/common/attributes.ts +520 -0
  284. package/src/common/codes.ts +153 -0
  285. package/src/common/declared-types.ts +374 -0
  286. package/src/common/direction.ts +518 -0
  287. package/src/common/escape.ts +231 -0
  288. package/src/common/export.ts +817 -0
  289. package/src/common/format.ts +111 -0
  290. package/src/common/ids.ts +176 -0
  291. package/src/common/input.ts +378 -0
  292. package/src/common/lists.ts +196 -0
  293. package/src/common/options.ts +377 -0
  294. package/src/common/report.ts +352 -0
  295. package/src/common/temporal.ts +302 -0
  296. package/src/common/text.ts +294 -0
  297. package/src/common/weights.ts +202 -0
  298. package/src/common/writer.ts +123 -0
  299. package/src/common/xml.ts +1053 -0
  300. package/src/formats/csv/exporter.ts +894 -0
  301. package/src/formats/csv/header.ts +172 -0
  302. package/src/formats/csv/importer.ts +1104 -0
  303. package/src/formats/csv/index.ts +88 -0
  304. package/src/formats/csv/records.ts +813 -0
  305. package/src/formats/csv/values.ts +224 -0
  306. package/src/formats/dot/exporter.ts +1014 -0
  307. package/src/formats/dot/importer.ts +1549 -0
  308. package/src/formats/dot/index.ts +7 -0
  309. package/src/formats/dot/names.ts +40 -0
  310. package/src/formats/dot/tokenizer.ts +384 -0
  311. package/src/formats/gexf/exporter.ts +1696 -0
  312. package/src/formats/gexf/importer.ts +2333 -0
  313. package/src/formats/gexf/index.ts +142 -0
  314. package/src/formats/gexf/schema.ts +361 -0
  315. package/src/formats/gml/exporter.ts +1404 -0
  316. package/src/formats/gml/importer.ts +1591 -0
  317. package/src/formats/gml/index.ts +128 -0
  318. package/src/formats/gml/syntax.ts +545 -0
  319. package/src/formats/graphml/constants.ts +225 -0
  320. package/src/formats/graphml/exporter.ts +1458 -0
  321. package/src/formats/graphml/importer.ts +2027 -0
  322. package/src/formats/graphml/index.ts +8 -0
  323. package/src/formats/graphml/tree.ts +318 -0
  324. package/src/formats/json/dialect.ts +317 -0
  325. package/src/formats/json/exporter.ts +1616 -0
  326. package/src/formats/json/importer.ts +2271 -0
  327. package/src/formats/json/index.ts +8 -0
  328. package/src/formats/neo4j/exporter.ts +1287 -0
  329. package/src/formats/neo4j/header.ts +156 -0
  330. package/src/formats/neo4j/importer.ts +1220 -0
  331. package/src/formats/neo4j/index.ts +116 -0
  332. package/src/formats/pajek/exporter.ts +1000 -0
  333. package/src/formats/pajek/importer.ts +1311 -0
  334. package/src/formats/pajek/index.ts +7 -0
  335. package/src/formats/pajek/syntax.ts +307 -0
  336. package/src/index.ts +244 -0
  337. package/src/registry.ts +617 -0
  338. package/src/sniff.ts +397 -0
  339. package/src/types.ts +262 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Adam Powers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,45 +1,267 @@
1
1
  # @graphty/graph-io
2
2
 
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
3
+ [![CI](https://github.com/graphty-org/graphty-monorepo/actions/workflows/ci.yml/badge.svg)](https://github.com/graphty-org/graphty-monorepo/actions/workflows/ci.yml)
4
+ [![Coverage Status](https://coveralls.io/repos/github/graphty-org/graphty-monorepo/badge.svg?branch=master)](https://coveralls.io/github/graphty-org/graphty-monorepo?branch=master)
5
+ [![npm version](https://img.shields.io/npm/v/@graphty/graph-io.svg)](https://www.npmjs.com/package/@graphty/graph-io)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
7
 
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
8
+ Importers and exporters for the [@graphty/graph-format](https://www.npmjs.com/package/@graphty/graph-format)
9
+ snapshot: GEXF, GraphML, GML, DOT (Graphviz), Pajek NET, CSV / TSV, JSON (NetworkX node-link, d3,
10
+ JSON Graph Format, Cytoscape, graphology, vis.js) and Neo4j (`neo4j-admin import` CSV).
6
11
 
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
12
+ Every importer streams its input into a `GraphSink` (a `GraphBuilder` or your own sink) one scalar at
13
+ a time and reports what it could not represent instead of dropping it; every exporter says what it
14
+ would lose before it writes a byte. The core format package stays zero-dependency; this package owns
15
+ the parsers and the io contract types.
8
16
 
9
- ## Purpose
17
+ ## Installation
10
18
 
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@graphty/graph-io`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
19
+ ```bash
20
+ npm install @graphty/graph-io @graphty/graph-format
21
+ ```
15
22
 
16
- ## What is OIDC Trusted Publishing?
23
+ ESM only. Node >= 18.19.0 or any browser with ES2020 support. `@graphty/graph-format` is a peer
24
+ dependency so an application that installs several consumers gets one copy of the format.
17
25
 
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
26
+ ## Quick start
19
27
 
20
- ## Setup Instructions
28
+ `importGraph()` sniffs the format from the file name, the MIME type and the first bytes, creates a
29
+ builder, imports and freezes:
21
30
 
22
- To properly configure OIDC trusted publishing for this package:
31
+ ```ts
32
+ import { importGraph, exportGraphToString, ImportError } from "@graphty/graph-io";
23
33
 
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
34
+ const bytes = await fetch("/data/lesmiserables.gexf").then((r) => r.arrayBuffer());
35
+ try {
36
+ const { snapshot, report, freeze, format } = await importGraph(new Uint8Array(bytes), {
37
+ filename: "lesmiserables.gexf", // a hint; the content decides
38
+ ids: "canonical", // "1" becomes the number 1, "01" stays a string (the default for text formats)
39
+ weightDtype: "f64", // the default: 0.1 and 16777217 survive the round trip
40
+ errorLimit: 100, // recoverable errors tolerated before ImportError
41
+ });
42
+ console.log(format, snapshot.nodeCount, snapshot.edgeCount, report.warningCount, freeze.compacted);
43
+ for (const issue of report.issues) {
44
+ console.log(issue.severity, issue.code, issue.line, issue.message);
45
+ }
46
+ const gml = await exportGraphToString(snapshot, "gml");
47
+ } catch (err) {
48
+ if (err instanceof ImportError) {
49
+ // the input could not be read at all, or the error limit was reached
50
+ console.log(err.report.issues);
51
+ }
52
+ }
53
+ ```
28
54
 
29
- ## DO NOT USE THIS PACKAGE
55
+ A caller who owns a builder (an application that appends several files into one graph) uses an
56
+ importer directly through its subpath, so a CSV-only bundle never loads the XML formats:
30
57
 
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
58
+ ```ts
59
+ import { GraphBuilder } from "@graphty/graph-format";
60
+ import { csvImporter, csvExporter } from "@graphty/graph-io/csv";
36
61
 
37
- ## More Information
62
+ const builder = new GraphBuilder({ directed: true, weightDtype: "f64" });
63
+ const report = await csvImporter.import(file.stream(), builder, {
64
+ delimiter: ",",
65
+ nodes: nodeTableText, // an optional paired node table (Gephi's nodes.csv + edges.csv)
66
+ onProgress: (done, total) => console.log(done, total),
67
+ signal: controller.signal,
68
+ });
69
+ const snapshot = builder.freeze();
38
70
 
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
71
+ const notes = csvExporter.check(snapshot, { dialect: "gephi" }); // what export() would lose; [] when exact
72
+ for await (const chunk of csvExporter.export(snapshot, { dialect: "gephi" })) {
73
+ writable.write(chunk); // Uint8Array chunks
74
+ }
75
+ ```
42
76
 
43
- ---
77
+ Inputs may be a `string`, a `Uint8Array`, a `ReadableStream<Uint8Array>` (a `File.stream()`, a fetch
78
+ body) or an async iterable of text or byte chunks. Bytes are decoded as UTF-8 with `fatal: true`, so
79
+ an invalid sequence is a `parse-error`, never a silent U+FFFD.
44
80
 
45
- **Maintained for OIDC setup purposes only**
81
+ ### Common import options
82
+
83
+ | Option | Default | Meaning |
84
+ | ----------------------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
85
+ | `ids` | `"canonical"` (text formats), `"keep"` (JSON) | How an id cell becomes a `NodeId`: canonical integer text becomes a number, everything else stays a string; `"string"`, `"number"` and `"keep"` too. |
86
+ | `nodeIdFrom` | `"id"` | `"label"` or `"index"` for GML / Pajek / d3 files whose ids are ambiguous. |
87
+ | `weightFrom` | `"weight"` (GML `"value"`) | The attribute that becomes THE edge weight; `null` = unweighted. |
88
+ | `weightDtype` | `"f64"` | Weight staging precision; `"f32"` is an explicit opt-in. |
89
+ | `onMixedDirection` | `"expand"` | A file whose edges disagree on direction: expand into a directed graph with `graphty.directed` / `graphty.pair` columns, force `"directed"` / `"undirected"`, or `"error"`. |
90
+ | `defaultDirected` | per format (GEXF / GML / JSON undirected, DOT / CSV / Pajek directed) | The direction assumed when the file says nothing. |
91
+ | `addMissingNodes` | `true` (GEXF `false`) | Whether an edge may name a node the file never declared. |
92
+ | `duplicateEdges`, `selfLoops` | `"keep"` | Builder policies, applied at freeze. |
93
+ | `long` | `"f64"` | How a declared 64-bit integer column is stored (`"string"` keeps every digit). |
94
+ | `restoreMangledIds` | `true` | Restore ids an exporter rewrote under `sanitizeIds: "mangle"` from the `graphty:originalId` attribute. |
95
+ | `hyperedges` | `"skip"` | GraphML / JGF hyperedges: `"error"`, `"skip"` with a report entry, `"star"` or `"clique"`. |
96
+ | `errorLimit` | `100` | Recoverable errors tolerated before the importer throws `ImportError` with the partial report. |
97
+ | `signal`, `onProgress` | | Cancellation (rejects with the signal's reason) and byte progress (`bytesTotal` known for in-memory input). |
98
+
99
+ On a caller's builder the builder-policy options (`addMissingNodes`, `duplicateEdges`, `selfLoops`,
100
+ `weightDtype`) are read from the sink; an explicit request the sink does not honour is reported once
101
+ as a `W_SINK_OPTION` warning. `importGraph()` seeds its own builder from them.
102
+
103
+ ### Common export options
104
+
105
+ `sanitizeIds: "error" | "mangle"` (default `"error"`: an exporter never silently renames a node;
106
+ `"mangle"` rewrites ids the format cannot hold and keeps the original in `graphty:originalId`) and
107
+ `onMixedDirection: "error" | "directed" | "undirected"` for formats without mixed-direction support.
108
+ `check(snapshot, options)` returns the `LossNote[]` that `export()` would incur; the shared codes
109
+ are in `LOSS` (for example `LOSS.MIXED_DIRECTION`, `LOSS.DTYPE`, `LOSS.LIST`, `LOSS.ID_MANGLED`),
110
+ the per-format ones in `<FORMAT>_LOSS`.
111
+
112
+ ## Formats
113
+
114
+ Subpath, extensions, and what each exporter keeps as declared (its `capabilities` table; `check()`
115
+ reports every column or feature outside it):
116
+
117
+ | Format | Subpath | Extensions | Mixed dir. | Multi-edges | Edge ids | Id charset | Dtypes kept | Lists | json | Defaults | Hierarchy | Temporal | Graph attrs | Positions | Viz |
118
+ | ------- | --------------------------- | ---------------------------------- | ----------- | ----------- | ----------- | ------------- | -------------------------------------- | ----- | ---- | -------- | --------- | -------------- | ----------- | --------- | --- |
119
+ | GEXF | `@graphty/graph-io/gexf` | `.gexf` | yes | yes (1.3) | optional | any | f32 f64 i32 bool dict string | yes | no | yes | yes | dynamic-values | no | yes | yes |
120
+ | GraphML | `@graphty/graph-io/graphml` | `.graphml` `.xml` | yes | yes | optional | NMTOKEN | bool i32 f32 f64 string (long as text) | no | no | yes | yes | none | yes | no | no |
121
+ | GML | `@graphty/graph-io/gml` | `.gml` | no | yes | optional | integer | i32 f64 string dict json | yes | yes | no | no | none | yes | yes | no |
122
+ | DOT | `@graphty/graph-io/dot` | `.dot` `.gv` | no | yes | optional | any | bool i32 f64 string | no | no | no | yes | none | yes | yes | no |
123
+ | Pajek | `@graphty/graph-io/pajek` | `.net` `.paj` | yes | yes | none | dense 1-based | f64 i32 bool string | no | no | no | no | spells | no | yes | no |
124
+ | CSV | `@graphty/graph-io/csv` | `.csv` `.tsv` `.edges` `.edgelist` | yes | yes | optional | any | bool i32 f64 string dict | no | no | no | no | none | no | no | no |
125
+ | JSON | `@graphty/graph-io/json` | `.json` | per dialect | yes | per dialect | any | f64 i32 bool string (no declarations) | no | yes | no | Cytoscape | none | per dialect | Cytoscape | no |
126
+ | Neo4j | `@graphty/graph-io/neo4j` | `.csv` `.tsv` | no | yes | none | any | f32 f64 i32 bool string | yes | no | no | no | none | no | no | no |
127
+
128
+ Every importer reads the whole corpus of research note 07 with the manifest counts and every
129
+ exporter round-trips it (import -> export -> import gives the same ids, topology, orientation,
130
+ weights and declared columns; the per-format caveats, such as Pajek's 0-based files or JSON's
131
+ untyped columns, are listed in the staging STATUS.md). `check()` predicts every difference the
132
+ format's own importer produces on re-import, including the importer's own rules, with one code per
133
+ concept across the formats (`W_ROLE_DROPPED` a role column written as a plain attribute,
134
+ `W_COLUMN_NAME_CHANGED` a role column read back under the importer's fixed name, `W_ROLE_ASSUMED` a
135
+ role-less column read back with a role, `W_WEIGHT_KEY_CLASH` a plain `weight` column read back as
136
+ THE weight, `W_ID_TEXT_TYPE` ids whose text reads back as the other type, `W_STORAGE_CLASS_CHANGED`
137
+ a string / dict column read back as the other, `W_INTEGRAL_F64_AS_I32`, `W_TEXT_INFERRED`,
138
+ `W_EMPTY_COLUMN_DROPPED`, `W_OPTIONS_GAINED`, `W_TEMPORAL_TEXT_DROPPED`, `W_MUTUAL_EXPANDED` /
139
+ `W_MUTUAL_AS_UNDIRECTED`, `E_XML_ILLEGAL_CHAR`, `E_ID_TEXT_COLLISION`). Every subpath exports its
140
+ `<FMT>_ISSUE` and `<FMT>_LOSS` tables; a key is the code without its `E_` / `W_` and format
141
+ prefixes. Under `onMixedDirection: "directed"` every exporter without mixed direction folds an
142
+ expanded pair back to one directed edge; `"undirected"` writes the whole graph undirected. Known
143
+ losses and format rules, in addition to the table:
144
+
145
+ - **GEXF**: 1.3 by default, 1.2 on request (`version: "1.2"`, no parallel edges, edge ids required).
146
+ Dynamic attribute values become `temporal:<node|edge>:<name>` extension tables; `viz:color` is an
147
+ f32 x4 rgba column in 0..1, `viz:position` an f32 x3 column; XML-derived column names (`label`,
148
+ `parent`, `start`, ...) are reserved, a declared attribute with such a title is renamed
149
+ `<title>#<id>`. Node ids that are non-integer numbers read back as text and string ids of integer
150
+ text as numbers (`W_ID_TEXT_TYPE`; the importer reads ids by the canonical rule whatever `idtype`
151
+ says, `ids: "string"` keeps the texts). A dict column without declared options gains one from
152
+ its dictionary (`W_OPTIONS_GAINED`); text with a character XML 1.0 forbids is refused
153
+ (`E_XML_ILLEGAL_CHAR`, `export()` throws).
154
+ - **GraphML**: parsed by the shared streaming XML tokenizer (no whole-document tree). `key for="all"`
155
+ is declared in the node, edge and graph tables; yFiles trees are kept as `json` columns (structure
156
+ preserved, not byte-exact); any other `json` column is written as JSON text and reads back as
157
+ string (`W_JSON_UNSUPPORTED`). Ids outside NMTOKEN need `sanitizeIds: "mangle"` (restored on
158
+ re-import). A label role column is written as the key titled `label` (the importer's label slot;
159
+ `W_COLUMN_NAME_CHANGED` when it was named otherwise); edge ids and ports are the XML attributes
160
+ (`id`, `sourceport`, `targetport`); a plain column titled like one of them reads back renamed
161
+ `<name>#<key>`. A mutual pair is written as one undirected edge (`W_MUTUAL_AS_UNDIRECTED`).
162
+ Lists, positions, viz and temporal columns are written as JSON text or reported.
163
+ - **GML**: NetworkX conventions (`_networkx_list_start`, `#` comments, `+INF` / `-INF` / `NAN`);
164
+ `real` columns are written with a decimal point so the dtype survives; `graphics [ x y z ]` maps
165
+ to the position role; records map to `json` and `check()` reports `W_GML_RECORD_NUMBER_TYPE` for
166
+ numbers inside them (GML cannot keep int versus real inside a record). Node ids must be integers
167
+ (`sanitizeIds: "mangle"` renumbers and keeps the original in `graphty_originalId`); column names
168
+ outside `[A-Za-z][0-9A-Za-z_]*` are refused or mangled (`sanitizeKeys`).
169
+ - **DOT**: a Graphviz-faithful parser (grammar violations are fatal, as in Graphviz); clusters are
170
+ container nodes with the `parent` role; ports are kept; HTML strings keep their brackets; `pos`
171
+ maps to the position role. Mixed direction is folded per `onMixedDirection`; a text with a
172
+ backslash before a quote or at its end cannot be written (`E_DOT_TRAILING_BACKSLASH`: Graphviz's
173
+ scanner consumes backslash pairs, so such a text has no quoted spelling).
174
+ - **Pajek**: `*Vertices N` bounds the id space (ids 1..N; a 0-based file is detected and reported;
175
+ a count the sink cannot reserve is fatal); `*Arcs` / `*Edges` sections give per-section
176
+ direction; time intervals map to the spells role; vertex / line parameters are plain columns read
177
+ through the 5.1 text grammar (`2.0` stays f64, lexical forms of a string column are kept); a
178
+ `.paj` project file's `*Partition` / `*Vector` sections are skipped with an issue. Nodes are
179
+ always written 1..N (`W_ID_RENUMBERED`: the id text is kept as the label of a node without a
180
+ label value, a node with one loses its id; `W_PAJEK_LABEL_GAINED` when a line's parameters force
181
+ a label); `sanitizeIds: "mangle"` also writes every renumbered vertex's original id as a
182
+ `graphty_originalId` parameter, which the importer restores under `restoreMangledIds` (the
183
+ default; `W_ID_TEXT_TYPE` when a string id of integer text reads back as a number); a `shape`
184
+ column whose values are not all shape keywords is written as a parameter; labels holding a
185
+ double quote or a line break cannot be written.
186
+ - **CSV**: header names resolve the endpoints (`source` / `target`, `from` / `to`, Gephi `Source` /
187
+ `Target` / `Type` / `Id` / `Label` / `Weight`); a paired node table comes through the `nodes`
188
+ option; the delimiter is sniffed. Leading `#` (SNAP) and `%` (KONECT) comment lines are skipped
189
+ and read for the direction they declare (`# Directed graph`, `% sym` / `% asym`). A quoted empty
190
+ cell is a set empty string, a bare one is unset; text after a closing quote is a fatal
191
+ `E_CSV_QUOTE`. The Gephi dialect keeps per-row direction, the generic dialect drops it
192
+ (`W_CSV_DIRECTION_DROPPED`). Untyped cells follow the 5.1 text grammar per column (`2.0` stays
193
+ f64, `1e5` and `-0` keep their spelling in a string column). An edge table cannot carry an
194
+ isolated node or the node order (`W_CSV_ISOLATED_NODES`, `W_CSV_NODE_ORDER`; write the node table).
195
+ - **JSON**: the dialect is sniffed from the document (`dialect` forces it); the importer records the
196
+ shape under `meta.extra.json` so a re-export keeps it (a d3 document is written back bare, a
197
+ graphology one with only the options it declared). JSON declares no types: the capability table
198
+ lists the inferred dtypes only, and `check()` names every f32 / u32 / u8 / dict / list / vector
199
+ column (they come back f64 / i32 / string / json), integral f64 columns (i32) and every role the
200
+ dialect has no slot for. Edge ids exist in JGF, Cytoscape, graphology and vis only; positions in
201
+ Cytoscape only. A repeated node id is merged with `W_DUPLICATE_NODE`, a repeated edge id skipped
202
+ with `E_DUPLICATE_EDGE_ID`; an out-of-range d3 index link is `E_BAD_INDEX`. Non-finite numbers
203
+ are written as `null` and reported.
204
+ - **Neo4j**: `neo4j-admin import` headers (`:ID`, `:LABEL`, `:START_ID`, `:END_ID`, `:TYPE`, typed
205
+ properties, id spaces, arrays); one file may hold several sections; a `weight` property becomes
206
+ THE weight; a quoted empty `:ID` is the id `""`. Everything is directed (an undirected snapshot,
207
+ or the folded pairs of a mixed one under `onMixedDirection: "directed"` / `"undirected"`, is
208
+ written with a `W_NEO4J_UNDIRECTED_AS_DIRECTED` note); `.text` companions keep the source text of
209
+ temporal values whose canonical form differs; a dict column reads back as string and a position
210
+ or visual column as a plain property.
211
+
212
+ ## Format detection
213
+
214
+ `sniff({ filename?, mimeType?, head? })` ranks the registered importers: a content match scores
215
+ at least 0.5 (`0.5 + 0.35 * content + 0.1 * extension + 0.05 * MIME`), a hint alone at most 0.4,
216
+ so the content always beats a misleading extension (a `.csv` with a neo4j-admin header is Neo4j, a
217
+ `.xml` is GEXF or GraphML by its root element). `sniffJsonDialectHead()` reports the JSON dialect a
218
+ head suggests. `importGraph()` reads at most `SNIFF_HEAD_BYTES` (8 KiB) of a stream before deciding
219
+ and replays them to the importer.
220
+
221
+ ## The import report
222
+
223
+ Every import returns an `ImportReport` (design section 8.6):
224
+
225
+ ```ts
226
+ interface ImportReport {
227
+ format: string; // "gexf", ...
228
+ counts: { nodes; edges; skippedNodes; skippedEdges; expandedMixed }; // edges counts both halves of an expanded edge
229
+ issues: ImportIssue[]; // in order: { category, severity, code, message, line, element }
230
+ errorCount: number; // issues with severity "error"; they count toward errorLimit
231
+ warningCount: number;
232
+ truncated: boolean; // the error limit was reached and the import aborted
233
+ lossy: LossNote[]; // what the importer could not represent: { code, message, column, count }
234
+ durationMs: number; // the parse phase; the freeze reports separately
235
+ }
236
+ ```
237
+
238
+ Issue categories are `parse-error`, `missing-value`, `validation-error`, `unsupported`, `precision`,
239
+ `coercion` and `merged`. Codes are stable strings (`E_*` errors, `W_*` warnings) exported per format
240
+ (`GEXF_ISSUE`, `GRAPHML_ISSUE`, `GML_ISSUE`, `DOT_ISSUE`, `PAJEK_ISSUE`, `CSV_ISSUE`,
241
+ `JSON_ISSUE`, `NEO4J_ISSUE`) and shared across formats (`SINK_OPTION_CODE`, `ID_MERGED_CODE`,
242
+ `DIRECTION_REFUSED_CODE`, `DIRECTION_FORCED_CODE`, `RENAMED_CODE`, `PRECISION_CODE`,
243
+ `INVALID_UTF8_CODE`, `PARSE_ERROR_CODE`). The builder throws on the first hard error; the importer
244
+ catches it per element, records an issue, skips the element and continues until `errorLimit`, then
245
+ throws `ImportError` (`code === "E_IMPORT"`) carrying the partial report. An input that cannot be
246
+ read at all (invalid UTF-8, malformed XML, no recognisable format) is an `ImportError` at once.
247
+
248
+ ## Writing a plugin
249
+
250
+ `GraphImporter` and `GraphExporter` (design section 12.4) are plain objects; the helpers every
251
+ built-in format is built on are exported for third-party plugins: `ImportReportBuilder` (issues,
252
+ error limit, `warnOnce`, `ImportError`), `textChunks` / `readText` / `LineReader` (streaming UTF-8
253
+ input with cancellation and progress), `tokenizeXml` / `XmlTokenizer` (the streaming XML tokenizer
254
+ behind GEXF and GraphML), `resolveImportOptions` / `resolveExportOptions` / `reportSinkOptions` /
255
+ `reportUnusedOptions`, `DirectionResolver` (the mixed-direction rules of section 8.4) and
256
+ `pairFolding` (its inverse for exporters), `IdCoercer`, `parseTextCell` and `TextCellWriter` (the
257
+ text grammar of section 5.1, per column, with the lexical forms kept), `declareResolved` (the 5.6
258
+ collision rule), `explicitWeights` (the weight role column's validity, section 3.7),
259
+ `checkCapabilities` (with `CheckExtras` for the roles a format has a slot for) / `sanitizeIds` /
260
+ `LOSS` / `xmlIllegalTextNotes`, the shared issue and loss codes of `codes.ts`, `formatDecimal` and
261
+ `encodeChunks` / `joinText`. Register a plugin with `registry.registerImporter()` /
262
+ `registerExporter()` or build a registry of your own with `new FormatRegistry()`. The `children`
263
+ CSR helper (`childrenCsr`) inverts a `parent` / `parents` containment column for nested writers.
264
+
265
+ ## License
266
+
267
+ MIT
@@ -0,0 +1,238 @@
1
+ import { INVALID_INDEX } from "@graphty/graph-format";
2
+ class ChildrenCsr {
3
+ /**
4
+ * Build the CSR; use childrenCsr() rather than the constructor.
5
+ * @param nodeCount - the node count
6
+ * @param rowPtr - the row pointers
7
+ * @param children - the child list
8
+ * @param parentCount - parents per node (0 for a root)
9
+ * @param dropped - references ignored
10
+ * @param column - the source column, or null
11
+ */
12
+ constructor(nodeCount, rowPtr, children, parentCount, dropped, column) {
13
+ this.nodeCount = nodeCount;
14
+ this.rowPtr = rowPtr;
15
+ this.children = children;
16
+ this.parentCount = parentCount;
17
+ this.edgeCount = children.length;
18
+ this.dropped = dropped;
19
+ this.column = column;
20
+ let rootCount = 0;
21
+ for (let u = 0; u < nodeCount; u++) {
22
+ if (parentCount[u] === 0) {
23
+ rootCount++;
24
+ }
25
+ }
26
+ const roots = new Uint32Array(rootCount);
27
+ let r = 0;
28
+ for (let u = 0; u < nodeCount; u++) {
29
+ if (parentCount[u] === 0) {
30
+ roots[r++] = u;
31
+ }
32
+ }
33
+ this.roots = roots;
34
+ this.unreachable = nodeCount - this.reachableCount();
35
+ }
36
+ /**
37
+ * The children of a node, as a view into `children` (do not modify).
38
+ * @param node - the node index
39
+ * @returns the child indices in ascending order; empty for a leaf
40
+ */
41
+ childrenOf(node) {
42
+ return this.children.subarray(this.rowPtr[node], this.rowPtr[node + 1]);
43
+ }
44
+ /**
45
+ * How many children a node has.
46
+ * @param node - the node index
47
+ * @returns the count
48
+ */
49
+ childCount(node) {
50
+ return this.rowPtr[node + 1] - this.rowPtr[node];
51
+ }
52
+ /**
53
+ * Whether a node is a container (has at least one child).
54
+ * @param node - the node index
55
+ * @returns true when it has children
56
+ */
57
+ hasChildren(node) {
58
+ return this.rowPtr[node + 1] > this.rowPtr[node];
59
+ }
60
+ /**
61
+ * How many parents a node has: 0 for a root, 1 under a `parent` column, any number under `parents`.
62
+ * @param node - the node index
63
+ * @returns the parent count
64
+ */
65
+ parentCountOf(node) {
66
+ return this.parentCount[node];
67
+ }
68
+ /**
69
+ * Whether a node has no parent.
70
+ * @param node - the node index
71
+ * @returns true for a root
72
+ */
73
+ isRoot(node) {
74
+ return this.parentCount[node] === 0;
75
+ }
76
+ /**
77
+ * Whether the snapshot has any containment at all.
78
+ * @returns true when at least one child reference was kept
79
+ */
80
+ isEmpty() {
81
+ return this.edgeCount === 0;
82
+ }
83
+ /**
84
+ * A pre-order depth-first traversal from the roots in index order (children in ascending
85
+ * order), then every node not yet visited (a cycle member, or a multi-parent node reached
86
+ * only through a cycle) in index order at depth 0. Each node appears exactly once, so nested
87
+ * writers emit every element once even for a `parents` column that shares a child.
88
+ * @returns the order, the depth of every node and whether the order differs from index order
89
+ */
90
+ depthFirst() {
91
+ const n = this.nodeCount;
92
+ const order = new Uint32Array(n);
93
+ const depth = new Uint32Array(n);
94
+ const visited = new Uint8Array(n);
95
+ const stackNode = [];
96
+ const stackCursor = [];
97
+ let position = 0;
98
+ let reordered = false;
99
+ const place = (u, d) => {
100
+ visited[u] = 1;
101
+ if (u !== position) {
102
+ reordered = true;
103
+ }
104
+ order[position++] = u;
105
+ depth[u] = d;
106
+ };
107
+ const visit = (root) => {
108
+ place(root, 0);
109
+ stackNode.push(root);
110
+ stackCursor.push(this.rowPtr[root]);
111
+ while (stackNode.length > 0) {
112
+ const top = stackNode.length - 1;
113
+ const u = stackNode[top];
114
+ const cursor = stackCursor[top];
115
+ if (cursor >= this.rowPtr[u + 1]) {
116
+ stackNode.pop();
117
+ stackCursor.pop();
118
+ continue;
119
+ }
120
+ stackCursor[top] = cursor + 1;
121
+ const v = this.children[cursor];
122
+ if (visited[v] === 1) {
123
+ continue;
124
+ }
125
+ place(v, stackNode.length);
126
+ stackNode.push(v);
127
+ stackCursor.push(this.rowPtr[v]);
128
+ }
129
+ };
130
+ for (const root of this.roots) {
131
+ visit(root);
132
+ }
133
+ for (let u = 0; u < n; u++) {
134
+ if (visited[u] === 0) {
135
+ visit(u);
136
+ }
137
+ }
138
+ return { order, depth, reordered };
139
+ }
140
+ /**
141
+ * The nodes reachable from the roots by following child references.
142
+ * @returns how many nodes a traversal from the roots reaches
143
+ */
144
+ reachableCount() {
145
+ const visited = new Uint8Array(this.nodeCount);
146
+ const stack = new Uint32Array(this.nodeCount);
147
+ let top = 0;
148
+ let count = 0;
149
+ for (const root of this.roots) {
150
+ visited[root] = 1;
151
+ count++;
152
+ stack[top++] = root;
153
+ while (top > 0) {
154
+ const u = stack[--top];
155
+ for (let k = this.rowPtr[u]; k < this.rowPtr[u + 1]; k++) {
156
+ const v = this.children[k];
157
+ if (visited[v] === 0) {
158
+ visited[v] = 1;
159
+ count++;
160
+ stack[top++] = v;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return count;
166
+ }
167
+ }
168
+ function childrenCsr(snapshot, options = {}) {
169
+ const column = resolveColumn(snapshot, options.column === void 0 ? "auto" : options.column);
170
+ return childrenFromColumn(snapshot.nodeCount, column);
171
+ }
172
+ function childrenFromColumn(nodeCount, column) {
173
+ const rowPtr = new Uint32Array(nodeCount + 1);
174
+ const parentCount = new Uint32Array(nodeCount);
175
+ let dropped = 0;
176
+ const references = [];
177
+ if (column !== null) {
178
+ const rows = Math.min(column.length, nodeCount);
179
+ const keep = (child, parent) => {
180
+ if (!Number.isInteger(parent) || parent === INVALID_INDEX || parent >= nodeCount || parent === child) {
181
+ dropped++;
182
+ return;
183
+ }
184
+ references.push(child, parent);
185
+ rowPtr[parent + 1]++;
186
+ parentCount[child]++;
187
+ };
188
+ if (column.dtype === "u32") {
189
+ const { data } = column;
190
+ for (let i = 0; i < rows; i++) {
191
+ if (column.isSet(i)) {
192
+ keep(i, data[i]);
193
+ }
194
+ }
195
+ } else if (column.dtype === "list") {
196
+ for (let i = 0; i < rows; i++) {
197
+ if (!column.isSet(i)) {
198
+ continue;
199
+ }
200
+ for (const item of column.sliceOf(i)) {
201
+ if (typeof item === "number") {
202
+ keep(i, item);
203
+ } else {
204
+ dropped++;
205
+ }
206
+ }
207
+ }
208
+ } else {
209
+ throw new TypeError(
210
+ `a containment column must be u32 or a list of u32, found ${column.dtype} column "${column.meta.name}"`
211
+ );
212
+ }
213
+ }
214
+ for (let u = 0; u < nodeCount; u++) {
215
+ rowPtr[u + 1] += rowPtr[u];
216
+ }
217
+ const children = new Uint32Array(rowPtr[nodeCount]);
218
+ const fill = rowPtr.slice(0, nodeCount);
219
+ for (let k = 0; k < references.length; k += 2) {
220
+ children[fill[references[k + 1]]++] = references[k];
221
+ }
222
+ return new ChildrenCsr(nodeCount, rowPtr, children, parentCount, dropped, column);
223
+ }
224
+ function resolveColumn(snapshot, choice) {
225
+ if (typeof choice !== "string") {
226
+ return choice;
227
+ }
228
+ if (choice === "parent" || choice === "parents") {
229
+ return snapshot.nodes.byRole(choice);
230
+ }
231
+ return snapshot.nodes.byRole("parent") ?? snapshot.nodes.byRole("parents");
232
+ }
233
+ export {
234
+ ChildrenCsr as C,
235
+ childrenFromColumn as a,
236
+ childrenCsr as c
237
+ };
238
+ //# sourceMappingURL=children-CL3Cy0ez.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"children-CL3Cy0ez.js","sources":["../../src/children.ts"],"sourcesContent":["/**\n * The children CSR over a containment column (design sections 5.10, 7.1 and 8.2): containment\n * (GEXF `pid` / `<parents>`, Cytoscape `parent`, DOT clusters, GraphML nested graphs) is a node\n * column with the `parent` role (u32, refersTo node) or the `parents` role (list of u32) and never\n * enters the CSR of the snapshot. Importers and exporters that need to walk containers top-down\n * (nested subgraphs, cluster blocks, parent lists) build this inverse index once: for every node\n * `u`, `children[rowPtr[u] .. rowPtr[u + 1])` are its children in ascending index order, `roots`\n * are the nodes with no parent, and `depthFirst()` is the pre-order the nested writers use.\n *\n * The helper is deliberately outside the core (7.1: it computes a property the arrays do not\n * already imply and only io needs it). A reference is ignored when it is unset, INVALID_INDEX, out\n * of range, or the node itself; a cycle (a parent chain that never reaches a root) is reported\n * through `unreachable` and its members are appended to the depth-first order in index order so\n * every node is written exactly once.\n */\n\nimport { type Column, type GraphSnapshot, INVALID_INDEX } from \"@graphty/graph-format\";\n\n/** The role of the column a children CSR is built over. */\nexport type ContainmentRole = \"parent\" | \"parents\";\n\n/** How the CSR is built: from a role column of the snapshot (default `parent`, then `parents`) or an explicit column. */\nexport interface ChildrenOptions {\n /**\n * The column to read: a role name (`parent` for the u32 single-parent column, `parents` for\n * the list column) or a column object of either shape; \"auto\" (default) takes the `parent`\n * role column when the snapshot has one, else `parents`, else no containment; null builds\n * the empty CSR (every node a root) whatever the snapshot holds.\n */\n readonly column?: ContainmentRole | Column | \"auto\" | null | undefined;\n}\n\n/** One depth-first traversal of the containment forest. */\nexport interface DepthFirstOrder {\n /** Every node index exactly once: roots in index order, each followed by its descendants, cycle members last. */\n readonly order: Uint32Array;\n /** The depth of every node in the traversal (0 for a root or a cycle member forced to the top). */\n readonly depth: Uint32Array;\n /** Whether `order` differs from `0..n-1`. */\n readonly reordered: boolean;\n}\n\n/**\n * The children CSR of a snapshot's containment column (design sections 5.10 and 7.1): the inverse\n * of the parent relation as a compressed sparse row structure over node indices.\n */\nexport class ChildrenCsr {\n /** The number of nodes (the row count of the CSR). */\n readonly nodeCount: number;\n /** `rowPtr[u] .. rowPtr[u + 1]` is the range of `children` holding the children of `u`; length nodeCount + 1. */\n readonly rowPtr: Uint32Array;\n /** Child node indices, ascending within each parent's range. */\n readonly children: Uint32Array;\n /** The nodes with no (valid) parent, in index order. */\n readonly roots: Uint32Array;\n /** Every (child, parent) reference kept; equal to `children.length`. */\n readonly edgeCount: number;\n /** References dropped because they were INVALID_INDEX, out of range or self-references. */\n readonly dropped: number;\n /** Nodes whose parent chain never reaches a root (members of a cycle), counted once each. */\n readonly unreachable: number;\n /** The column the CSR was built from, or null when the snapshot has no containment. */\n readonly column: Column | null;\n\n private readonly parentCount: Uint32Array;\n\n /**\n * Build the CSR; use childrenCsr() rather than the constructor.\n * @param nodeCount - the node count\n * @param rowPtr - the row pointers\n * @param children - the child list\n * @param parentCount - parents per node (0 for a root)\n * @param dropped - references ignored\n * @param column - the source column, or null\n */\n constructor(\n nodeCount: number,\n rowPtr: Uint32Array,\n children: Uint32Array,\n parentCount: Uint32Array,\n dropped: number,\n column: Column | null,\n ) {\n this.nodeCount = nodeCount;\n this.rowPtr = rowPtr;\n this.children = children;\n this.parentCount = parentCount;\n this.edgeCount = children.length;\n this.dropped = dropped;\n this.column = column;\n let rootCount = 0;\n for (let u = 0; u < nodeCount; u++) {\n if (parentCount[u] === 0) {\n rootCount++;\n }\n }\n const roots = new Uint32Array(rootCount);\n let r = 0;\n for (let u = 0; u < nodeCount; u++) {\n if (parentCount[u] === 0) {\n roots[r++] = u;\n }\n }\n this.roots = roots;\n this.unreachable = nodeCount - this.reachableCount();\n }\n\n /**\n * The children of a node, as a view into `children` (do not modify).\n * @param node - the node index\n * @returns the child indices in ascending order; empty for a leaf\n */\n childrenOf(node: number): Uint32Array {\n return this.children.subarray(this.rowPtr[node], this.rowPtr[node + 1]);\n }\n\n /**\n * How many children a node has.\n * @param node - the node index\n * @returns the count\n */\n childCount(node: number): number {\n return this.rowPtr[node + 1] - this.rowPtr[node];\n }\n\n /**\n * Whether a node is a container (has at least one child).\n * @param node - the node index\n * @returns true when it has children\n */\n hasChildren(node: number): boolean {\n return this.rowPtr[node + 1] > this.rowPtr[node];\n }\n\n /**\n * How many parents a node has: 0 for a root, 1 under a `parent` column, any number under `parents`.\n * @param node - the node index\n * @returns the parent count\n */\n parentCountOf(node: number): number {\n return this.parentCount[node];\n }\n\n /**\n * Whether a node has no parent.\n * @param node - the node index\n * @returns true for a root\n */\n isRoot(node: number): boolean {\n return this.parentCount[node] === 0;\n }\n\n /**\n * Whether the snapshot has any containment at all.\n * @returns true when at least one child reference was kept\n */\n isEmpty(): boolean {\n return this.edgeCount === 0;\n }\n\n /**\n * A pre-order depth-first traversal from the roots in index order (children in ascending\n * order), then every node not yet visited (a cycle member, or a multi-parent node reached\n * only through a cycle) in index order at depth 0. Each node appears exactly once, so nested\n * writers emit every element once even for a `parents` column that shares a child.\n * @returns the order, the depth of every node and whether the order differs from index order\n */\n depthFirst(): DepthFirstOrder {\n const n = this.nodeCount;\n const order = new Uint32Array(n);\n const depth = new Uint32Array(n);\n const visited = new Uint8Array(n);\n const stackNode: number[] = [];\n const stackCursor: number[] = [];\n let position = 0;\n let reordered = false;\n const place = (u: number, d: number): void => {\n visited[u] = 1;\n if (u !== position) {\n reordered = true;\n }\n order[position++] = u;\n depth[u] = d;\n };\n const visit = (root: number): void => {\n place(root, 0);\n stackNode.push(root);\n stackCursor.push(this.rowPtr[root]);\n while (stackNode.length > 0) {\n const top = stackNode.length - 1;\n const u = stackNode[top];\n const cursor = stackCursor[top];\n if (cursor >= this.rowPtr[u + 1]) {\n stackNode.pop();\n stackCursor.pop();\n continue;\n }\n stackCursor[top] = cursor + 1;\n const v = this.children[cursor];\n if (visited[v] === 1) {\n continue;\n }\n place(v, stackNode.length);\n stackNode.push(v);\n stackCursor.push(this.rowPtr[v]);\n }\n };\n for (const root of this.roots) {\n visit(root);\n }\n for (let u = 0; u < n; u++) {\n if (visited[u] === 0) {\n visit(u);\n }\n }\n return { order, depth, reordered };\n }\n\n /**\n * The nodes reachable from the roots by following child references.\n * @returns how many nodes a traversal from the roots reaches\n */\n private reachableCount(): number {\n const visited = new Uint8Array(this.nodeCount);\n // every node is pushed at most once, so a stack of nodeCount entries never overflows\n const stack = new Uint32Array(this.nodeCount);\n let top = 0;\n let count = 0;\n for (const root of this.roots) {\n visited[root] = 1;\n count++;\n stack[top++] = root;\n while (top > 0) {\n const u = stack[--top];\n for (let k = this.rowPtr[u]; k < this.rowPtr[u + 1]; k++) {\n const v = this.children[k];\n if (visited[v] === 0) {\n visited[v] = 1;\n count++;\n stack[top++] = v;\n }\n }\n }\n }\n return count;\n }\n}\n\n/**\n * Build the children CSR of a snapshot (design section 7.1: a graph-io helper, not a core view).\n * @param snapshot - the snapshot\n * @param options - which column to read; the `parent` role, then `parents`, by default\n * @returns the CSR; empty (every node a root) when the snapshot has no containment column\n */\nexport function childrenCsr(snapshot: GraphSnapshot, options: ChildrenOptions = {}): ChildrenCsr {\n const column = resolveColumn(snapshot, options.column === undefined ? \"auto\" : options.column);\n return childrenFromColumn(snapshot.nodeCount, column);\n}\n\n/**\n * Build the children CSR over one containment column without a snapshot (an importer that holds\n * the column before freezing, a test).\n * @param nodeCount - the node count the references index into\n * @param column - a u32 `parent`-shaped column, a list-of-u32 `parents`-shaped column, or null for none\n * @returns the CSR\n */\nexport function childrenFromColumn(nodeCount: number, column: Column | null): ChildrenCsr {\n const rowPtr = new Uint32Array(nodeCount + 1);\n const parentCount = new Uint32Array(nodeCount);\n let dropped = 0;\n const references: number[] = [];\n if (column !== null) {\n const rows = Math.min(column.length, nodeCount);\n const keep = (child: number, parent: number): void => {\n if (!Number.isInteger(parent) || parent === INVALID_INDEX || parent >= nodeCount || parent === child) {\n dropped++;\n return;\n }\n references.push(child, parent);\n rowPtr[parent + 1]++;\n parentCount[child]++;\n };\n if (column.dtype === \"u32\") {\n const { data } = column;\n for (let i = 0; i < rows; i++) {\n if (column.isSet(i)) {\n keep(i, data[i]);\n }\n }\n } else if (column.dtype === \"list\") {\n for (let i = 0; i < rows; i++) {\n if (!column.isSet(i)) {\n continue;\n }\n for (const item of column.sliceOf(i)) {\n if (typeof item === \"number\") {\n keep(i, item);\n } else {\n dropped++;\n }\n }\n }\n } else {\n throw new TypeError(\n `a containment column must be u32 or a list of u32, found ${column.dtype} column \"${column.meta.name}\"`,\n );\n }\n }\n for (let u = 0; u < nodeCount; u++) {\n rowPtr[u + 1] += rowPtr[u];\n }\n const children = new Uint32Array(rowPtr[nodeCount]);\n const fill = rowPtr.slice(0, nodeCount);\n // references were collected in ascending child order, so each parent's range fills ascending\n for (let k = 0; k < references.length; k += 2) {\n children[fill[references[k + 1]]++] = references[k];\n }\n return new ChildrenCsr(nodeCount, rowPtr, children, parentCount, dropped, column);\n}\n\n/**\n * The column a ChildrenOptions selects.\n * @param snapshot - the snapshot\n * @param choice - the option value\n * @returns the column, or null when the snapshot has none of that shape\n */\nfunction resolveColumn(snapshot: GraphSnapshot, choice: ContainmentRole | Column | \"auto\" | null): Column | null {\n if (typeof choice !== \"string\") {\n return choice;\n }\n if (choice === \"parent\" || choice === \"parents\") {\n return snapshot.nodes.byRole(choice);\n }\n return snapshot.nodes.byRole(\"parent\") ?? snapshot.nodes.byRole(\"parents\");\n}\n"],"names":[],"mappings":"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"}