@graphty/graph-io 0.3.43 → 0.3.45

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 (674) hide show
  1. package/dist/chunks/approximations-DJMHdDvv.js +40 -0
  2. package/dist/chunks/approximations-DJMHdDvv.js.map +1 -0
  3. package/dist/chunks/block-CPCHkX42.js +2777 -0
  4. package/dist/chunks/block-CPCHkX42.js.map +1 -0
  5. package/dist/chunks/{choose-DvpbdRuz.js → choose--jM0yYCo.js} +3 -2
  6. package/dist/chunks/{choose-DvpbdRuz.js.map → choose--jM0yYCo.js.map} +1 -1
  7. package/dist/chunks/{constants-fT3E8JYY.js → constants-BKwqelYD.js} +6 -2
  8. package/dist/chunks/constants-BKwqelYD.js.map +1 -0
  9. package/dist/chunks/convert-BcQ8_jMo.js +59 -0
  10. package/dist/chunks/convert-BcQ8_jMo.js.map +1 -0
  11. package/dist/chunks/emit-DZy6JgdA.js +3456 -0
  12. package/dist/chunks/emit-DZy6JgdA.js.map +1 -0
  13. package/dist/chunks/{escape-B62ZGM_a.js → escape-CgzxiYPg.js} +44 -21
  14. package/dist/chunks/escape-CgzxiYPg.js.map +1 -0
  15. package/dist/chunks/{export-rKLeGQMo.js → export-BvqAXCKh.js} +196 -792
  16. package/dist/chunks/export-BvqAXCKh.js.map +1 -0
  17. package/dist/chunks/{exporter-yLxKBvLE.js → exporter--Mk9gXCJ.js} +4 -3
  18. package/dist/chunks/{exporter-yLxKBvLE.js.map → exporter--Mk9gXCJ.js.map} +1 -1
  19. package/dist/chunks/{exporter-RVtXVetw.js → exporter-C-9cgZKa.js} +1805 -287
  20. package/dist/chunks/exporter-C-9cgZKa.js.map +1 -0
  21. package/dist/chunks/{exporter-8hLNemFk.js → exporter-DFqJhBnj.js} +133 -74
  22. package/dist/chunks/exporter-DFqJhBnj.js.map +1 -0
  23. package/dist/chunks/{exporter-BWwEuOPT.js → exporter-DteVeJki.js} +22 -12
  24. package/dist/chunks/exporter-DteVeJki.js.map +1 -0
  25. package/dist/chunks/format-BpQBx8MZ.js +676 -0
  26. package/dist/chunks/format-BpQBx8MZ.js.map +1 -0
  27. package/dist/chunks/generic-BGO71QBv.js +1347 -0
  28. package/dist/chunks/generic-BGO71QBv.js.map +1 -0
  29. package/dist/chunks/{importer-C98dpSH4.js → importer-B-9Rc1zO.js} +141 -82
  30. package/dist/chunks/importer-B-9Rc1zO.js.map +1 -0
  31. package/dist/chunks/{importer-D794XUTe.js → importer-BVVR740x.js} +31 -8
  32. package/dist/chunks/importer-BVVR740x.js.map +1 -0
  33. package/dist/chunks/{importer-WrGRZb0N.js → importer-C0AjA3Ax.js} +449 -102
  34. package/dist/chunks/importer-C0AjA3Ax.js.map +1 -0
  35. package/dist/chunks/{importer-BbNb7eNg.js → importer-C7pvJe1L.js} +1398 -129
  36. package/dist/chunks/importer-C7pvJe1L.js.map +1 -0
  37. package/dist/chunks/{importer-xZ-3suY1.js → importer-ClwoVx3O.js} +791 -135
  38. package/dist/chunks/importer-ClwoVx3O.js.map +1 -0
  39. package/dist/chunks/{importer-Ht7LNg8L.js → importer-D0sC_6bO.js} +116 -296
  40. package/dist/chunks/importer-D0sC_6bO.js.map +1 -0
  41. package/dist/chunks/{importer-Dz7tI8Sa.js → importer-EHQk1jbM.js} +617 -115
  42. package/dist/chunks/importer-EHQk1jbM.js.map +1 -0
  43. package/dist/chunks/{json-elements-DKSqpNW1.js → json-elements-DJZRZ6jd.js} +123 -32
  44. package/dist/chunks/json-elements-DJZRZ6jd.js.map +1 -0
  45. package/dist/chunks/lexicon-WMsNddyU.js +509 -0
  46. package/dist/chunks/lexicon-WMsNddyU.js.map +1 -0
  47. package/dist/chunks/{ontology-export-CtRXjg_i.js → ontology-export-G4MqRdux.js} +3 -2
  48. package/dist/chunks/{ontology-export-CtRXjg_i.js.map → ontology-export-G4MqRdux.js.map} +1 -1
  49. package/dist/chunks/{records-Cu4bxYOm.js → records-CiaWZrFH.js} +2 -2
  50. package/dist/chunks/{records-Cu4bxYOm.js.map → records-CiaWZrFH.js.map} +1 -1
  51. package/dist/chunks/styles-DvoFBKCK.js +690 -0
  52. package/dist/chunks/styles-DvoFBKCK.js.map +1 -0
  53. package/dist/chunks/styles-yed-Cz9rLBCP.js +842 -0
  54. package/dist/chunks/styles-yed-Cz9rLBCP.js.map +1 -0
  55. package/dist/chunks/styles-zNTmz5R7.js +2102 -0
  56. package/dist/chunks/styles-zNTmz5R7.js.map +1 -0
  57. package/dist/chunks/{text-DHOJXnED.js → text-CzJNFOS9.js} +2 -2
  58. package/dist/chunks/{text-DHOJXnED.js.map → text-CzJNFOS9.js.map} +1 -1
  59. package/dist/chunks/units-XMZFIFe1.js +19 -0
  60. package/dist/chunks/units-XMZFIFe1.js.map +1 -0
  61. package/dist/chunks/writer-gQ8kx99H.js +448 -0
  62. package/dist/chunks/writer-gQ8kx99H.js.map +1 -0
  63. package/dist/csv.js +5 -4
  64. package/dist/csv.js.map +1 -1
  65. package/dist/cx.js +1 -1
  66. package/dist/cx2.js +2 -2
  67. package/dist/cys.js +1 -1
  68. package/dist/dot.js +5 -2
  69. package/dist/dot.js.map +1 -1
  70. package/dist/gexf.js +292 -412
  71. package/dist/gexf.js.map +1 -1
  72. package/dist/gml.js +927 -74
  73. package/dist/gml.js.map +1 -1
  74. package/dist/graph-io.js +351 -236
  75. package/dist/graph-io.js.map +1 -1
  76. package/dist/graphml.js +4 -2
  77. package/dist/graphml.js.map +1 -1
  78. package/dist/json.js +1 -1
  79. package/dist/neo4j.js +4 -3
  80. package/dist/neo4j.js.map +1 -1
  81. package/dist/obo.js +1 -1
  82. package/dist/pajek.js +4 -2
  83. package/dist/positions.d.ts +1 -0
  84. package/dist/positions.js +427 -0
  85. package/dist/positions.js.map +1 -0
  86. package/dist/src/common/codes.d.ts +6 -0
  87. package/dist/src/common/codes.d.ts.map +1 -1
  88. package/dist/src/common/codes.js +6 -0
  89. package/dist/src/common/codes.js.map +1 -1
  90. package/dist/src/common/escape.d.ts.map +1 -1
  91. package/dist/src/common/escape.js +4 -2
  92. package/dist/src/common/escape.js.map +1 -1
  93. package/dist/src/common/export.d.ts +10 -0
  94. package/dist/src/common/export.d.ts.map +1 -1
  95. package/dist/src/common/export.js +123 -100
  96. package/dist/src/common/export.js.map +1 -1
  97. package/dist/src/common/input.d.ts.map +1 -1
  98. package/dist/src/common/input.js +8 -1
  99. package/dist/src/common/input.js.map +1 -1
  100. package/dist/src/common/json-elements.d.ts +12 -9
  101. package/dist/src/common/json-elements.d.ts.map +1 -1
  102. package/dist/src/common/json-elements.js +160 -37
  103. package/dist/src/common/json-elements.js.map +1 -1
  104. package/dist/src/common/notes.d.ts +42 -0
  105. package/dist/src/common/notes.d.ts.map +1 -0
  106. package/dist/src/common/notes.js +72 -0
  107. package/dist/src/common/notes.js.map +1 -0
  108. package/dist/src/common/report.d.ts +18 -0
  109. package/dist/src/common/report.d.ts.map +1 -1
  110. package/dist/src/common/report.js +36 -1
  111. package/dist/src/common/report.js.map +1 -1
  112. package/dist/src/common/units.d.ts +24 -0
  113. package/dist/src/common/units.d.ts.map +1 -0
  114. package/dist/src/common/units.js +26 -0
  115. package/dist/src/common/units.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +18 -0
  117. package/dist/src/common/xml.d.ts.map +1 -1
  118. package/dist/src/common/xml.js +42 -5
  119. package/dist/src/common/xml.js.map +1 -1
  120. package/dist/src/formats/cx/exporter.d.ts +6 -0
  121. package/dist/src/formats/cx/exporter.d.ts.map +1 -1
  122. package/dist/src/formats/cx/exporter.js +71 -8
  123. package/dist/src/formats/cx/exporter.js.map +1 -1
  124. package/dist/src/formats/cx/importer.d.ts +5 -2
  125. package/dist/src/formats/cx/importer.d.ts.map +1 -1
  126. package/dist/src/formats/cx/importer.js +102 -72
  127. package/dist/src/formats/cx/importer.js.map +1 -1
  128. package/dist/src/formats/cx/styles.d.ts +102 -0
  129. package/dist/src/formats/cx/styles.d.ts.map +1 -0
  130. package/dist/src/formats/cx/styles.js +225 -0
  131. package/dist/src/formats/cx/styles.js.map +1 -0
  132. package/dist/src/formats/cx2/exporter.d.ts +5 -0
  133. package/dist/src/formats/cx2/exporter.d.ts.map +1 -1
  134. package/dist/src/formats/cx2/exporter.js +181 -71
  135. package/dist/src/formats/cx2/exporter.js.map +1 -1
  136. package/dist/src/formats/cx2/importer.d.ts +4 -1
  137. package/dist/src/formats/cx2/importer.d.ts.map +1 -1
  138. package/dist/src/formats/cx2/importer.js +19 -1
  139. package/dist/src/formats/cx2/importer.js.map +1 -1
  140. package/dist/src/formats/cx2/styles.d.ts +18 -0
  141. package/dist/src/formats/cx2/styles.d.ts.map +1 -0
  142. package/dist/src/formats/cx2/styles.js +24 -0
  143. package/dist/src/formats/cx2/styles.js.map +1 -0
  144. package/dist/src/formats/cys/constants.d.ts +2 -0
  145. package/dist/src/formats/cys/constants.d.ts.map +1 -1
  146. package/dist/src/formats/cys/constants.js +2 -0
  147. package/dist/src/formats/cys/constants.js.map +1 -1
  148. package/dist/src/formats/cys/exporter.d.ts +6 -2
  149. package/dist/src/formats/cys/exporter.d.ts.map +1 -1
  150. package/dist/src/formats/cys/exporter.js +215 -53
  151. package/dist/src/formats/cys/exporter.js.map +1 -1
  152. package/dist/src/formats/cys/importer.d.ts +2 -1
  153. package/dist/src/formats/cys/importer.d.ts.map +1 -1
  154. package/dist/src/formats/cys/importer.js +119 -6
  155. package/dist/src/formats/cys/importer.js.map +1 -1
  156. package/dist/src/formats/cys/session.d.ts +2 -0
  157. package/dist/src/formats/cys/session.d.ts.map +1 -1
  158. package/dist/src/formats/cys/session.js +5 -0
  159. package/dist/src/formats/cys/session.js.map +1 -1
  160. package/dist/src/formats/cys/styles.d.ts +38 -0
  161. package/dist/src/formats/cys/styles.d.ts.map +1 -0
  162. package/dist/src/formats/cys/styles.js +213 -0
  163. package/dist/src/formats/cys/styles.js.map +1 -0
  164. package/dist/src/formats/cys/tables.d.ts +8 -3
  165. package/dist/src/formats/cys/tables.d.ts.map +1 -1
  166. package/dist/src/formats/cys/tables.js +88 -34
  167. package/dist/src/formats/cys/tables.js.map +1 -1
  168. package/dist/src/formats/dot/exporter.d.ts +2 -0
  169. package/dist/src/formats/dot/exporter.d.ts.map +1 -1
  170. package/dist/src/formats/dot/exporter.js +195 -81
  171. package/dist/src/formats/dot/exporter.js.map +1 -1
  172. package/dist/src/formats/dot/importer.d.ts.map +1 -1
  173. package/dist/src/formats/dot/importer.js +100 -20
  174. package/dist/src/formats/dot/importer.js.map +1 -1
  175. package/dist/src/formats/dot/index.d.ts +1 -0
  176. package/dist/src/formats/dot/index.d.ts.map +1 -1
  177. package/dist/src/formats/dot/index.js +1 -0
  178. package/dist/src/formats/dot/index.js.map +1 -1
  179. package/dist/src/formats/dot/positions.d.ts +14 -0
  180. package/dist/src/formats/dot/positions.d.ts.map +1 -0
  181. package/dist/src/formats/dot/positions.js +70 -0
  182. package/dist/src/formats/dot/positions.js.map +1 -0
  183. package/dist/src/formats/dot/styles.d.ts +82 -0
  184. package/dist/src/formats/dot/styles.d.ts.map +1 -0
  185. package/dist/src/formats/dot/styles.js +1496 -0
  186. package/dist/src/formats/dot/styles.js.map +1 -0
  187. package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
  188. package/dist/src/formats/gexf/exporter.js +20 -5
  189. package/dist/src/formats/gexf/exporter.js.map +1 -1
  190. package/dist/src/formats/gexf/importer.d.ts +8 -1
  191. package/dist/src/formats/gexf/importer.d.ts.map +1 -1
  192. package/dist/src/formats/gexf/importer.js +251 -131
  193. package/dist/src/formats/gexf/importer.js.map +1 -1
  194. package/dist/src/formats/gexf/index.d.ts +8 -0
  195. package/dist/src/formats/gexf/index.d.ts.map +1 -1
  196. package/dist/src/formats/gexf/index.js +10 -2
  197. package/dist/src/formats/gexf/index.js.map +1 -1
  198. package/dist/src/formats/gexf/schema.d.ts +5 -0
  199. package/dist/src/formats/gexf/schema.d.ts.map +1 -1
  200. package/dist/src/formats/gexf/schema.js +5 -0
  201. package/dist/src/formats/gexf/schema.js.map +1 -1
  202. package/dist/src/formats/gexf/styles.d.ts +74 -0
  203. package/dist/src/formats/gexf/styles.d.ts.map +1 -0
  204. package/dist/src/formats/gexf/styles.js +604 -0
  205. package/dist/src/formats/gexf/styles.js.map +1 -0
  206. package/dist/src/formats/gml/exporter.d.ts +1 -5
  207. package/dist/src/formats/gml/exporter.d.ts.map +1 -1
  208. package/dist/src/formats/gml/exporter.js +19 -8
  209. package/dist/src/formats/gml/exporter.js.map +1 -1
  210. package/dist/src/formats/gml/importer.d.ts.map +1 -1
  211. package/dist/src/formats/gml/importer.js +4 -0
  212. package/dist/src/formats/gml/importer.js.map +1 -1
  213. package/dist/src/formats/gml/index.d.ts +2 -1
  214. package/dist/src/formats/gml/index.d.ts.map +1 -1
  215. package/dist/src/formats/gml/index.js +2 -1
  216. package/dist/src/formats/gml/index.js.map +1 -1
  217. package/dist/src/formats/gml/positions.d.ts +14 -0
  218. package/dist/src/formats/gml/positions.d.ts.map +1 -0
  219. package/dist/src/formats/gml/positions.js +46 -0
  220. package/dist/src/formats/gml/positions.js.map +1 -0
  221. package/dist/src/formats/gml/styles.d.ts +59 -0
  222. package/dist/src/formats/gml/styles.d.ts.map +1 -0
  223. package/dist/src/formats/gml/styles.js +1056 -0
  224. package/dist/src/formats/gml/styles.js.map +1 -0
  225. package/dist/src/formats/gml/syntax.d.ts +11 -0
  226. package/dist/src/formats/gml/syntax.d.ts.map +1 -1
  227. package/dist/src/formats/gml/syntax.js +151 -62
  228. package/dist/src/formats/gml/syntax.js.map +1 -1
  229. package/dist/src/formats/graphml/constants.d.ts +4 -0
  230. package/dist/src/formats/graphml/constants.d.ts.map +1 -1
  231. package/dist/src/formats/graphml/constants.js +5 -1
  232. package/dist/src/formats/graphml/constants.js.map +1 -1
  233. package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
  234. package/dist/src/formats/graphml/exporter.js +133 -65
  235. package/dist/src/formats/graphml/exporter.js.map +1 -1
  236. package/dist/src/formats/graphml/importer.d.ts +4 -1
  237. package/dist/src/formats/graphml/importer.d.ts.map +1 -1
  238. package/dist/src/formats/graphml/importer.js +100 -44
  239. package/dist/src/formats/graphml/importer.js.map +1 -1
  240. package/dist/src/formats/graphml/index.d.ts +1 -0
  241. package/dist/src/formats/graphml/index.d.ts.map +1 -1
  242. package/dist/src/formats/graphml/index.js +1 -0
  243. package/dist/src/formats/graphml/index.js.map +1 -1
  244. package/dist/src/formats/graphml/styles-plain.d.ts +19 -0
  245. package/dist/src/formats/graphml/styles-plain.d.ts.map +1 -0
  246. package/dist/src/formats/graphml/styles-plain.js +187 -0
  247. package/dist/src/formats/graphml/styles-plain.js.map +1 -0
  248. package/dist/src/formats/graphml/styles-yed.d.ts +179 -0
  249. package/dist/src/formats/graphml/styles-yed.d.ts.map +1 -0
  250. package/dist/src/formats/graphml/styles-yed.js +1086 -0
  251. package/dist/src/formats/graphml/styles-yed.js.map +1 -0
  252. package/dist/src/formats/graphml/styles-yfiles3.d.ts +19 -0
  253. package/dist/src/formats/graphml/styles-yfiles3.d.ts.map +1 -0
  254. package/dist/src/formats/graphml/styles-yfiles3.js +508 -0
  255. package/dist/src/formats/graphml/styles-yfiles3.js.map +1 -0
  256. package/dist/src/formats/graphml/tree.d.ts.map +1 -1
  257. package/dist/src/formats/graphml/tree.js +34 -19
  258. package/dist/src/formats/graphml/tree.js.map +1 -1
  259. package/dist/src/formats/graphml/yfiles.d.ts +16 -2
  260. package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
  261. package/dist/src/formats/graphml/yfiles.js +83 -12
  262. package/dist/src/formats/graphml/yfiles.js.map +1 -1
  263. package/dist/src/formats/json/exporter.d.ts +5 -0
  264. package/dist/src/formats/json/exporter.d.ts.map +1 -1
  265. package/dist/src/formats/json/exporter.js +68 -25
  266. package/dist/src/formats/json/exporter.js.map +1 -1
  267. package/dist/src/formats/json/importer.d.ts +22 -3
  268. package/dist/src/formats/json/importer.d.ts.map +1 -1
  269. package/dist/src/formats/json/importer.js +439 -264
  270. package/dist/src/formats/json/importer.js.map +1 -1
  271. package/dist/src/formats/json/positions.d.ts +16 -0
  272. package/dist/src/formats/json/positions.d.ts.map +1 -0
  273. package/dist/src/formats/json/positions.js +44 -0
  274. package/dist/src/formats/json/positions.js.map +1 -0
  275. package/dist/src/formats/json/styles.d.ts +102 -0
  276. package/dist/src/formats/json/styles.d.ts.map +1 -0
  277. package/dist/src/formats/json/styles.js +1753 -0
  278. package/dist/src/formats/json/styles.js.map +1 -0
  279. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
  280. package/dist/src/formats/neo4j/exporter.js +2 -1
  281. package/dist/src/formats/neo4j/exporter.js.map +1 -1
  282. package/dist/src/formats/pajek/exporter.d.ts +2 -0
  283. package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
  284. package/dist/src/formats/pajek/exporter.js +142 -76
  285. package/dist/src/formats/pajek/exporter.js.map +1 -1
  286. package/dist/src/formats/pajek/importer.d.ts +4 -0
  287. package/dist/src/formats/pajek/importer.d.ts.map +1 -1
  288. package/dist/src/formats/pajek/importer.js +60 -6
  289. package/dist/src/formats/pajek/importer.js.map +1 -1
  290. package/dist/src/formats/pajek/index.d.ts +2 -0
  291. package/dist/src/formats/pajek/index.d.ts.map +1 -1
  292. package/dist/src/formats/pajek/index.js +2 -0
  293. package/dist/src/formats/pajek/index.js.map +1 -1
  294. package/dist/src/formats/pajek/styles.d.ts +50 -0
  295. package/dist/src/formats/pajek/styles.d.ts.map +1 -0
  296. package/dist/src/formats/pajek/styles.js +569 -0
  297. package/dist/src/formats/pajek/styles.js.map +1 -0
  298. package/dist/src/formats/pajek/syntax.d.ts +6 -0
  299. package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
  300. package/dist/src/formats/pajek/syntax.js +6 -0
  301. package/dist/src/formats/pajek/syntax.js.map +1 -1
  302. package/dist/src/formats/xgmml/columns.d.ts.map +1 -1
  303. package/dist/src/formats/xgmml/columns.js +34 -9
  304. package/dist/src/formats/xgmml/columns.js.map +1 -1
  305. package/dist/src/formats/xgmml/constants.d.ts +2 -0
  306. package/dist/src/formats/xgmml/constants.d.ts.map +1 -1
  307. package/dist/src/formats/xgmml/constants.js +3 -1
  308. package/dist/src/formats/xgmml/constants.js.map +1 -1
  309. package/dist/src/formats/xgmml/document.d.ts +12 -8
  310. package/dist/src/formats/xgmml/document.d.ts.map +1 -1
  311. package/dist/src/formats/xgmml/document.js +56 -13
  312. package/dist/src/formats/xgmml/document.js.map +1 -1
  313. package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
  314. package/dist/src/formats/xgmml/emit.js +12 -6
  315. package/dist/src/formats/xgmml/emit.js.map +1 -1
  316. package/dist/src/formats/xgmml/exporter.d.ts +3 -1
  317. package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
  318. package/dist/src/formats/xgmml/exporter.js +22 -9
  319. package/dist/src/formats/xgmml/exporter.js.map +1 -1
  320. package/dist/src/formats/xgmml/importer.d.ts +2 -1
  321. package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
  322. package/dist/src/formats/xgmml/importer.js +31 -24
  323. package/dist/src/formats/xgmml/importer.js.map +1 -1
  324. package/dist/src/formats/xgmml/styles.d.ts +104 -0
  325. package/dist/src/formats/xgmml/styles.d.ts.map +1 -0
  326. package/dist/src/formats/xgmml/styles.js +310 -0
  327. package/dist/src/formats/xgmml/styles.js.map +1 -0
  328. package/dist/src/index.d.ts +9 -7
  329. package/dist/src/index.d.ts.map +1 -1
  330. package/dist/src/index.js +7 -7
  331. package/dist/src/index.js.map +1 -1
  332. package/dist/src/positions/codes.d.ts +31 -0
  333. package/dist/src/positions/codes.d.ts.map +1 -0
  334. package/dist/src/positions/codes.js +26 -0
  335. package/dist/src/positions/codes.js.map +1 -0
  336. package/dist/src/positions/convert.d.ts +58 -0
  337. package/dist/src/positions/convert.d.ts.map +1 -0
  338. package/dist/src/positions/convert.js +107 -0
  339. package/dist/src/positions/convert.js.map +1 -0
  340. package/dist/src/positions/extract.d.ts +27 -0
  341. package/dist/src/positions/extract.d.ts.map +1 -0
  342. package/dist/src/positions/extract.js +526 -0
  343. package/dist/src/positions/extract.js.map +1 -0
  344. package/dist/src/positions/index.d.ts +10 -0
  345. package/dist/src/positions/index.d.ts.map +1 -0
  346. package/dist/src/positions/index.js +9 -0
  347. package/dist/src/positions/index.js.map +1 -0
  348. package/dist/src/positions/types.d.ts +208 -0
  349. package/dist/src/positions/types.d.ts.map +1 -0
  350. package/dist/src/positions/types.js +10 -0
  351. package/dist/src/positions/types.js.map +1 -0
  352. package/dist/src/registry.d.ts +30 -0
  353. package/dist/src/registry.d.ts.map +1 -1
  354. package/dist/src/registry.js +130 -9
  355. package/dist/src/registry.js.map +1 -1
  356. package/dist/src/styles/annotations.d.ts +132 -0
  357. package/dist/src/styles/annotations.d.ts.map +1 -0
  358. package/dist/src/styles/annotations.js +320 -0
  359. package/dist/src/styles/annotations.js.map +1 -0
  360. package/dist/src/styles/budget.d.ts +45 -0
  361. package/dist/src/styles/budget.d.ts.map +1 -0
  362. package/dist/src/styles/budget.js +58 -0
  363. package/dist/src/styles/budget.js.map +1 -0
  364. package/dist/src/styles/canon/bends.d.ts +32 -0
  365. package/dist/src/styles/canon/bends.d.ts.map +1 -0
  366. package/dist/src/styles/canon/bends.js +74 -0
  367. package/dist/src/styles/canon/bends.js.map +1 -0
  368. package/dist/src/styles/canon/color-tables.d.ts +22 -0
  369. package/dist/src/styles/canon/color-tables.d.ts.map +1 -0
  370. package/dist/src/styles/canon/color-tables.js +369 -0
  371. package/dist/src/styles/canon/color-tables.js.map +1 -0
  372. package/dist/src/styles/canon/color.d.ts +66 -0
  373. package/dist/src/styles/canon/color.d.ts.map +1 -0
  374. package/dist/src/styles/canon/color.js +548 -0
  375. package/dist/src/styles/canon/color.js.map +1 -0
  376. package/dist/src/styles/canon/enums.d.ts +25 -0
  377. package/dist/src/styles/canon/enums.d.ts.map +1 -0
  378. package/dist/src/styles/canon/enums.js +187 -0
  379. package/dist/src/styles/canon/enums.js.map +1 -0
  380. package/dist/src/styles/canon/graphviz.d.ts +48 -0
  381. package/dist/src/styles/canon/graphviz.d.ts.map +1 -0
  382. package/dist/src/styles/canon/graphviz.js +160 -0
  383. package/dist/src/styles/canon/graphviz.js.map +1 -0
  384. package/dist/src/styles/canon/index.d.ts +13 -0
  385. package/dist/src/styles/canon/index.d.ts.map +1 -0
  386. package/dist/src/styles/canon/index.js +13 -0
  387. package/dist/src/styles/canon/index.js.map +1 -0
  388. package/dist/src/styles/canon/number.d.ts +59 -0
  389. package/dist/src/styles/canon/number.d.ts.map +1 -0
  390. package/dist/src/styles/canon/number.js +142 -0
  391. package/dist/src/styles/canon/number.js.map +1 -0
  392. package/dist/src/styles/canon/site.d.ts +51 -0
  393. package/dist/src/styles/canon/site.d.ts.map +1 -0
  394. package/dist/src/styles/canon/site.js +63 -0
  395. package/dist/src/styles/canon/site.js.map +1 -0
  396. package/dist/src/styles/canon/text.d.ts +61 -0
  397. package/dist/src/styles/canon/text.d.ts.map +1 -0
  398. package/dist/src/styles/canon/text.js +199 -0
  399. package/dist/src/styles/canon/text.js.map +1 -0
  400. package/dist/src/styles/codes.d.ts +95 -0
  401. package/dist/src/styles/codes.d.ts.map +1 -0
  402. package/dist/src/styles/codes.js +97 -0
  403. package/dist/src/styles/codes.js.map +1 -0
  404. package/dist/src/styles/cyjs/cascade.d.ts +134 -0
  405. package/dist/src/styles/cyjs/cascade.d.ts.map +1 -0
  406. package/dist/src/styles/cyjs/cascade.js +2252 -0
  407. package/dist/src/styles/cyjs/cascade.js.map +1 -0
  408. package/dist/src/styles/cyjs/defaults-table.d.ts +25 -0
  409. package/dist/src/styles/cyjs/defaults-table.d.ts.map +1 -0
  410. package/dist/src/styles/cyjs/defaults-table.js +76 -0
  411. package/dist/src/styles/cyjs/defaults-table.js.map +1 -0
  412. package/dist/src/styles/cyjs/emit.d.ts +82 -0
  413. package/dist/src/styles/cyjs/emit.d.ts.map +1 -0
  414. package/dist/src/styles/cyjs/emit.js +791 -0
  415. package/dist/src/styles/cyjs/emit.js.map +1 -0
  416. package/dist/src/styles/cyjs/index.d.ts +13 -0
  417. package/dist/src/styles/cyjs/index.d.ts.map +1 -0
  418. package/dist/src/styles/cyjs/index.js +13 -0
  419. package/dist/src/styles/cyjs/index.js.map +1 -0
  420. package/dist/src/styles/cyjs/live.d.ts +46 -0
  421. package/dist/src/styles/cyjs/live.d.ts.map +1 -0
  422. package/dist/src/styles/cyjs/live.js +108 -0
  423. package/dist/src/styles/cyjs/live.js.map +1 -0
  424. package/dist/src/styles/cyjs/mappers.d.ts +147 -0
  425. package/dist/src/styles/cyjs/mappers.d.ts.map +1 -0
  426. package/dist/src/styles/cyjs/mappers.js +744 -0
  427. package/dist/src/styles/cyjs/mappers.js.map +1 -0
  428. package/dist/src/styles/cyjs/selector.d.ts +113 -0
  429. package/dist/src/styles/cyjs/selector.d.ts.map +1 -0
  430. package/dist/src/styles/cyjs/selector.js +611 -0
  431. package/dist/src/styles/cyjs/selector.js.map +1 -0
  432. package/dist/src/styles/cyjs/stylesheet-text.d.ts +37 -0
  433. package/dist/src/styles/cyjs/stylesheet-text.d.ts.map +1 -0
  434. package/dist/src/styles/cyjs/stylesheet-text.js +181 -0
  435. package/dist/src/styles/cyjs/stylesheet-text.js.map +1 -0
  436. package/dist/src/styles/cytoscape/emit.d.ts +125 -0
  437. package/dist/src/styles/cytoscape/emit.d.ts.map +1 -0
  438. package/dist/src/styles/cytoscape/emit.js +1025 -0
  439. package/dist/src/styles/cytoscape/emit.js.map +1 -0
  440. package/dist/src/styles/cytoscape/index.d.ts +10 -0
  441. package/dist/src/styles/cytoscape/index.d.ts.map +1 -0
  442. package/dist/src/styles/cytoscape/index.js +10 -0
  443. package/dist/src/styles/cytoscape/index.js.map +1 -0
  444. package/dist/src/styles/cytoscape/lexicon.d.ts +49 -0
  445. package/dist/src/styles/cytoscape/lexicon.d.ts.map +1 -0
  446. package/dist/src/styles/cytoscape/lexicon.js +605 -0
  447. package/dist/src/styles/cytoscape/lexicon.js.map +1 -0
  448. package/dist/src/styles/cytoscape/mappings.d.ts +171 -0
  449. package/dist/src/styles/cytoscape/mappings.d.ts.map +1 -0
  450. package/dist/src/styles/cytoscape/mappings.js +460 -0
  451. package/dist/src/styles/cytoscape/mappings.js.map +1 -0
  452. package/dist/src/styles/cytoscape/resolve.d.ts +137 -0
  453. package/dist/src/styles/cytoscape/resolve.d.ts.map +1 -0
  454. package/dist/src/styles/cytoscape/resolve.js +381 -0
  455. package/dist/src/styles/cytoscape/resolve.js.map +1 -0
  456. package/dist/src/styles/cytoscape/translate.d.ts +50 -0
  457. package/dist/src/styles/cytoscape/translate.d.ts.map +1 -0
  458. package/dist/src/styles/cytoscape/translate.js +156 -0
  459. package/dist/src/styles/cytoscape/translate.js.map +1 -0
  460. package/dist/src/styles/defaults.d.ts +90 -0
  461. package/dist/src/styles/defaults.d.ts.map +1 -0
  462. package/dist/src/styles/defaults.js +414 -0
  463. package/dist/src/styles/defaults.js.map +1 -0
  464. package/dist/src/styles/index.d.ts +15 -0
  465. package/dist/src/styles/index.d.ts.map +1 -0
  466. package/dist/src/styles/index.js +14 -0
  467. package/dist/src/styles/index.js.map +1 -0
  468. package/dist/src/styles/internal.d.ts +17 -0
  469. package/dist/src/styles/internal.d.ts.map +1 -0
  470. package/dist/src/styles/internal.js +16 -0
  471. package/dist/src/styles/internal.js.map +1 -0
  472. package/dist/src/styles/io/approximations.d.ts +24 -0
  473. package/dist/src/styles/io/approximations.d.ts.map +1 -0
  474. package/dist/src/styles/io/approximations.js +51 -0
  475. package/dist/src/styles/io/approximations.js.map +1 -0
  476. package/dist/src/styles/io/block.d.ts +166 -0
  477. package/dist/src/styles/io/block.d.ts.map +1 -0
  478. package/dist/src/styles/io/block.js +930 -0
  479. package/dist/src/styles/io/block.js.map +1 -0
  480. package/dist/src/styles/io/generic.d.ts +88 -0
  481. package/dist/src/styles/io/generic.d.ts.map +1 -0
  482. package/dist/src/styles/io/generic.js +516 -0
  483. package/dist/src/styles/io/generic.js.map +1 -0
  484. package/dist/src/styles/io/index.d.ts +8 -0
  485. package/dist/src/styles/io/index.d.ts.map +1 -0
  486. package/dist/src/styles/io/index.js +7 -0
  487. package/dist/src/styles/io/index.js.map +1 -0
  488. package/dist/src/styles/io/writer.d.ts +89 -0
  489. package/dist/src/styles/io/writer.d.ts.map +1 -0
  490. package/dist/src/styles/io/writer.js +626 -0
  491. package/dist/src/styles/io/writer.js.map +1 -0
  492. package/dist/src/styles/notes.d.ts +47 -0
  493. package/dist/src/styles/notes.d.ts.map +1 -0
  494. package/dist/src/styles/notes.js +140 -0
  495. package/dist/src/styles/notes.js.map +1 -0
  496. package/dist/src/styles/plan/classes.d.ts +31 -0
  497. package/dist/src/styles/plan/classes.d.ts.map +1 -0
  498. package/dist/src/styles/plan/classes.js +139 -0
  499. package/dist/src/styles/plan/classes.js.map +1 -0
  500. package/dist/src/styles/plan/continuous.d.ts +32 -0
  501. package/dist/src/styles/plan/continuous.d.ts.map +1 -0
  502. package/dist/src/styles/plan/continuous.js +165 -0
  503. package/dist/src/styles/plan/continuous.js.map +1 -0
  504. package/dist/src/styles/plan/discrete.d.ts +39 -0
  505. package/dist/src/styles/plan/discrete.d.ts.map +1 -0
  506. package/dist/src/styles/plan/discrete.js +127 -0
  507. package/dist/src/styles/plan/discrete.js.map +1 -0
  508. package/dist/src/styles/plan/families.d.ts +31 -0
  509. package/dist/src/styles/plan/families.d.ts.map +1 -0
  510. package/dist/src/styles/plan/families.js +95 -0
  511. package/dist/src/styles/plan/families.js.map +1 -0
  512. package/dist/src/styles/plan/features.d.ts +58 -0
  513. package/dist/src/styles/plan/features.d.ts.map +1 -0
  514. package/dist/src/styles/plan/features.js +185 -0
  515. package/dist/src/styles/plan/features.js.map +1 -0
  516. package/dist/src/styles/plan/index.d.ts +7 -0
  517. package/dist/src/styles/plan/index.d.ts.map +1 -0
  518. package/dist/src/styles/plan/index.js +7 -0
  519. package/dist/src/styles/plan/index.js.map +1 -0
  520. package/dist/src/styles/plan/plan.d.ts +71 -0
  521. package/dist/src/styles/plan/plan.d.ts.map +1 -0
  522. package/dist/src/styles/plan/plan.js +893 -0
  523. package/dist/src/styles/plan/plan.js.map +1 -0
  524. package/dist/src/styles/plan/resolve.d.ts +137 -0
  525. package/dist/src/styles/plan/resolve.d.ts.map +1 -0
  526. package/dist/src/styles/plan/resolve.js +587 -0
  527. package/dist/src/styles/plan/resolve.js.map +1 -0
  528. package/dist/src/styles/types.d.ts +702 -0
  529. package/dist/src/styles/types.d.ts.map +1 -0
  530. package/dist/src/styles/types.js +10 -0
  531. package/dist/src/styles/types.js.map +1 -0
  532. package/dist/src/styles/vocabulary.d.ts +41 -0
  533. package/dist/src/styles/vocabulary.d.ts.map +1 -0
  534. package/dist/src/styles/vocabulary.js +140 -0
  535. package/dist/src/styles/vocabulary.js.map +1 -0
  536. package/dist/src/types.d.ts +64 -1
  537. package/dist/src/types.d.ts.map +1 -1
  538. package/dist/src/types.js.map +1 -1
  539. package/dist/styles.d.ts +1 -0
  540. package/dist/styles.js +322 -0
  541. package/dist/styles.js.map +1 -0
  542. package/dist/xgmml.js +5 -4
  543. package/dist/xgmml.js.map +1 -1
  544. package/package.json +13 -2
  545. package/src/common/codes.ts +7 -0
  546. package/src/common/escape.ts +5 -2
  547. package/src/common/export.ts +179 -140
  548. package/src/common/input.ts +8 -4
  549. package/src/common/json-elements.ts +169 -38
  550. package/src/common/notes.ts +94 -0
  551. package/src/common/report.ts +41 -1
  552. package/src/common/units.ts +42 -0
  553. package/src/common/xml.ts +45 -5
  554. package/src/formats/cx/exporter.ts +95 -11
  555. package/src/formats/cx/importer.ts +126 -90
  556. package/src/formats/cx/styles.ts +304 -0
  557. package/src/formats/cx2/exporter.ts +234 -77
  558. package/src/formats/cx2/importer.ts +19 -1
  559. package/src/formats/cx2/styles.ts +29 -0
  560. package/src/formats/cys/constants.ts +3 -0
  561. package/src/formats/cys/exporter.ts +246 -60
  562. package/src/formats/cys/importer.ts +125 -9
  563. package/src/formats/cys/session.ts +6 -0
  564. package/src/formats/cys/styles.ts +254 -0
  565. package/src/formats/cys/tables.ts +117 -48
  566. package/src/formats/dot/exporter.ts +262 -126
  567. package/src/formats/dot/importer.ts +111 -20
  568. package/src/formats/dot/index.ts +1 -0
  569. package/src/formats/dot/positions.ts +77 -0
  570. package/src/formats/dot/styles.ts +1755 -0
  571. package/src/formats/gexf/exporter.ts +27 -11
  572. package/src/formats/gexf/importer.ts +308 -158
  573. package/src/formats/gexf/index.ts +10 -0
  574. package/src/formats/gexf/schema.ts +5 -0
  575. package/src/formats/gexf/styles.ts +719 -0
  576. package/src/formats/gml/exporter.ts +33 -14
  577. package/src/formats/gml/importer.ts +4 -0
  578. package/src/formats/gml/index.ts +2 -1
  579. package/src/formats/gml/positions.ts +52 -0
  580. package/src/formats/gml/styles.ts +1248 -0
  581. package/src/formats/gml/syntax.ts +173 -62
  582. package/src/formats/graphml/constants.ts +5 -0
  583. package/src/formats/graphml/exporter.ts +163 -71
  584. package/src/formats/graphml/importer.ts +119 -45
  585. package/src/formats/graphml/index.ts +1 -0
  586. package/src/formats/graphml/styles-plain.ts +236 -0
  587. package/src/formats/graphml/styles-yed.ts +1221 -0
  588. package/src/formats/graphml/styles-yfiles3.ts +573 -0
  589. package/src/formats/graphml/tree.ts +36 -20
  590. package/src/formats/graphml/yfiles.ts +89 -13
  591. package/src/formats/json/exporter.ts +76 -26
  592. package/src/formats/json/importer.ts +541 -322
  593. package/src/formats/json/positions.ts +50 -0
  594. package/src/formats/json/styles.ts +2082 -0
  595. package/src/formats/neo4j/exporter.ts +2 -1
  596. package/src/formats/pajek/exporter.ts +195 -106
  597. package/src/formats/pajek/importer.ts +78 -9
  598. package/src/formats/pajek/index.ts +2 -0
  599. package/src/formats/pajek/styles.ts +685 -0
  600. package/src/formats/pajek/syntax.ts +7 -0
  601. package/src/formats/xgmml/columns.ts +39 -9
  602. package/src/formats/xgmml/constants.ts +3 -0
  603. package/src/formats/xgmml/document.ts +54 -12
  604. package/src/formats/xgmml/emit.ts +11 -6
  605. package/src/formats/xgmml/exporter.ts +32 -13
  606. package/src/formats/xgmml/importer.ts +43 -29
  607. package/src/formats/xgmml/styles.ts +400 -0
  608. package/src/index.ts +16 -0
  609. package/src/positions/codes.ts +32 -0
  610. package/src/positions/convert.ts +123 -0
  611. package/src/positions/extract.ts +619 -0
  612. package/src/positions/index.ts +23 -0
  613. package/src/positions/types.ts +221 -0
  614. package/src/registry.ts +179 -8
  615. package/src/styles/annotations.ts +478 -0
  616. package/src/styles/budget.ts +60 -0
  617. package/src/styles/canon/bends.ts +84 -0
  618. package/src/styles/canon/color-tables.ts +386 -0
  619. package/src/styles/canon/color.ts +624 -0
  620. package/src/styles/canon/enums.ts +211 -0
  621. package/src/styles/canon/graphviz.ts +189 -0
  622. package/src/styles/canon/index.ts +13 -0
  623. package/src/styles/canon/number.ts +152 -0
  624. package/src/styles/canon/site.ts +87 -0
  625. package/src/styles/canon/text.ts +221 -0
  626. package/src/styles/codes.ts +104 -0
  627. package/src/styles/cyjs/cascade.ts +2670 -0
  628. package/src/styles/cyjs/defaults-table.ts +82 -0
  629. package/src/styles/cyjs/emit.ts +952 -0
  630. package/src/styles/cyjs/index.ts +37 -0
  631. package/src/styles/cyjs/live.ts +147 -0
  632. package/src/styles/cyjs/mappers.ts +879 -0
  633. package/src/styles/cyjs/selector.ts +778 -0
  634. package/src/styles/cyjs/stylesheet-text.ts +214 -0
  635. package/src/styles/cytoscape/emit.ts +1264 -0
  636. package/src/styles/cytoscape/index.ts +40 -0
  637. package/src/styles/cytoscape/lexicon.ts +658 -0
  638. package/src/styles/cytoscape/mappings.ts +603 -0
  639. package/src/styles/cytoscape/resolve.ts +521 -0
  640. package/src/styles/cytoscape/translate.ts +237 -0
  641. package/src/styles/defaults.ts +504 -0
  642. package/src/styles/index.ts +57 -0
  643. package/src/styles/internal.ts +17 -0
  644. package/src/styles/io/approximations.ts +47 -0
  645. package/src/styles/io/block.ts +1138 -0
  646. package/src/styles/io/generic.ts +687 -0
  647. package/src/styles/io/index.ts +18 -0
  648. package/src/styles/io/writer.ts +780 -0
  649. package/src/styles/notes.ts +165 -0
  650. package/src/styles/plan/classes.ts +166 -0
  651. package/src/styles/plan/continuous.ts +218 -0
  652. package/src/styles/plan/discrete.ts +165 -0
  653. package/src/styles/plan/families.ts +115 -0
  654. package/src/styles/plan/features.ts +234 -0
  655. package/src/styles/plan/index.ts +7 -0
  656. package/src/styles/plan/plan.ts +1129 -0
  657. package/src/styles/plan/resolve.ts +685 -0
  658. package/src/styles/types.ts +814 -0
  659. package/src/styles/vocabulary.ts +172 -0
  660. package/src/types.ts +76 -1
  661. package/dist/chunks/constants-fT3E8JYY.js.map +0 -1
  662. package/dist/chunks/escape-B62ZGM_a.js.map +0 -1
  663. package/dist/chunks/export-rKLeGQMo.js.map +0 -1
  664. package/dist/chunks/exporter-8hLNemFk.js.map +0 -1
  665. package/dist/chunks/exporter-BWwEuOPT.js.map +0 -1
  666. package/dist/chunks/exporter-RVtXVetw.js.map +0 -1
  667. package/dist/chunks/importer-BbNb7eNg.js.map +0 -1
  668. package/dist/chunks/importer-C98dpSH4.js.map +0 -1
  669. package/dist/chunks/importer-D794XUTe.js.map +0 -1
  670. package/dist/chunks/importer-Dz7tI8Sa.js.map +0 -1
  671. package/dist/chunks/importer-Ht7LNg8L.js.map +0 -1
  672. package/dist/chunks/importer-WrGRZb0N.js.map +0 -1
  673. package/dist/chunks/importer-xZ-3suY1.js.map +0 -1
  674. package/dist/chunks/json-elements-DKSqpNW1.js.map +0 -1
@@ -0,0 +1,952 @@
1
+ /**
2
+ * Neutral styles written as a Cytoscape.js stylesheet: the rules in order as stylesheet rules, the per-element values
3
+ * those rules read as element data under flat keys, and per-element bypasses for the values no rule gives.
4
+ *
5
+ * - A short predicate becomes a selector (`all`, `has`, `eq`, `in`, `not`, one `and`); a predicate a selector cannot
6
+ * state exactly (a computed degree, a boolean value, a column whose name a selector cannot spell) becomes a
7
+ * membership value written into element data and tested with `[key]`.
8
+ * - A passthrough becomes `data(key)` over the values, written ready for Cytoscape.js; a discrete mapping becomes one
9
+ * rule per table key; a continuous mapping becomes `mapData()`, one per segment; every mapped rule is scoped to the
10
+ * elements it gives a value, because a Cytoscape.js mapper that finds nothing resets the property to its default.
11
+ * - The stylesheet is then applied to the element data with graph-io's own Cytoscape.js engine, and every element
12
+ * whose drawn value still differs from the styles gets a bypass (its own `style`), so the result is exact.
13
+ * - With an import id, every element gets `graphty_import: <id>` and every selector tests it, so two imports in one
14
+ * core never style each other's elements.
15
+ * - Cytoscape.js draws what it draws regardless of any stylesheet: a loop is always curved, and its own default rules
16
+ * (compound parents, selected elements) apply beneath the stylesheet. A file Cytoscape.js wrote keeps those
17
+ * defaults as Cytoscape.js's own; they are not written again.
18
+ */
19
+
20
+ import { STYLE_ISSUE } from "../codes.js";
21
+ import { StyleNotes } from "../notes.js";
22
+ import { matches, refSource, resolveRules } from "../plan/resolve.js";
23
+ import type {
24
+ ColumnRef,
25
+ ExtractedStyles,
26
+ Mapping,
27
+ Predicate,
28
+ Scalar,
29
+ StyleNote,
30
+ StyleRule,
31
+ StyleTarget,
32
+ StyleValue,
33
+ } from "../types.js";
34
+ import { STYLE_PROPERTIES } from "../vocabulary.js";
35
+ import { evaluateCytoscapeJs } from "./cascade.js";
36
+ import { CYJS_EDGE_PROPERTIES, CYJS_NODE_PROPERTIES, type CyjsPropertySpec, isFontFamily } from "./mappers.js";
37
+ import { parseCyjsSelector } from "./selector.js";
38
+
39
+ /** One rule of a Cytoscape.js stylesheet. */
40
+ export interface CytoscapeStylesheetRule {
41
+ /** The selector. */
42
+ readonly selector: string;
43
+ /** Property name to value, as Cytoscape.js reads them. */
44
+ readonly style: Readonly<Record<string, string | number>>;
45
+ }
46
+
47
+ /** A value written into an element's `data`. */
48
+ export type CytoscapeDataValue = string | number | boolean;
49
+
50
+ /** Per target, one object per element (by element index), or null where the element needs nothing. */
51
+ export interface CytoscapePerElement<T> {
52
+ /** Nodes. */
53
+ readonly node: readonly (Readonly<Record<string, T>> | null)[];
54
+ /** Edges. */
55
+ readonly edge: readonly (Readonly<Record<string, T>> | null)[];
56
+ }
57
+
58
+ /** Options of `toCytoscapeStylesheet()`. */
59
+ export interface CytoscapeStylesheetOptions {
60
+ /**
61
+ * Scopes the stylesheet to one import: every element gets `graphty_import: <importId>` in its data and every
62
+ * selector tests it. Quotes and backslashes are replaced with `_`, in the data and the selectors alike.
63
+ */
64
+ readonly importId?: string;
65
+ }
66
+
67
+ /** The stylesheet, the element data it reads and the bypasses that make it exact. */
68
+ export interface CytoscapeStylesheetResult {
69
+ /** The rules, in order; append them to a core's stylesheet. */
70
+ readonly stylesheet: CytoscapeStylesheetRule[];
71
+ /** What to merge into each element's `data`. */
72
+ readonly elementData: CytoscapePerElement<CytoscapeDataValue>;
73
+ /** What to give each element as its own `style` (a bypass over every rule). */
74
+ readonly bypass: CytoscapePerElement<string | number>;
75
+ /** Every style that could not be written, or only approximately. */
76
+ readonly notes: readonly StyleNote[];
77
+ }
78
+
79
+ /** The element data key that holds the import id. */
80
+ export const CYTOSCAPE_IMPORT_KEY = "graphty_import";
81
+
82
+ /**
83
+ * The flat element data key of a style column: Cytoscape.js reads a dot in a data selector as a nested path, so the
84
+ * key has none (`member.3` is `graphty_style_member_3`).
85
+ * @param key - the style column's key
86
+ * @returns the element data key
87
+ * @category Styles
88
+ */
89
+ export function cytoscapeStyleKey(key: string): string {
90
+ return `graphty_style_${key.replaceAll(/\W/g, "_")}`;
91
+ }
92
+
93
+ /** A name a selector and a mapper can both spell without escapes or paths. */
94
+ const PLAIN_NAME = /^[A-Za-z_]\w*$/;
95
+
96
+ /** A neutral value written as Cytoscape.js properties, and whether that is only approximate. */
97
+ interface Written {
98
+ readonly props: Record<string, string | number>;
99
+ readonly approximate: boolean;
100
+ }
101
+
102
+ const SHAPES_TO_CYJS: ReadonlyMap<string, string> = new Map([
103
+ ["ellipse", "ellipse"],
104
+ ["rectangle", "rectangle"],
105
+ ["round-rectangle", "round-rectangle"],
106
+ ["triangle", "triangle"],
107
+ ["diamond", "diamond"],
108
+ ["pentagon", "pentagon"],
109
+ ["hexagon", "hexagon"],
110
+ ["octagon", "octagon"],
111
+ ["parallelogram", "rhomboid"],
112
+ ["star", "star"],
113
+ ["vee", "vee"],
114
+ ]);
115
+ const ARROWS_TO_CYJS: ReadonlyMap<string, string> = new Map([
116
+ ["none", "none"],
117
+ ["triangle", "triangle"],
118
+ ["vee", "vee"],
119
+ ["tee", "tee"],
120
+ ["diamond", "diamond"],
121
+ ["circle", "circle"],
122
+ ["square", "square"],
123
+ ]);
124
+ const ARROWS_NEAREST: ReadonlyMap<string, string> = new Map([
125
+ ["triangle-open", "triangle"],
126
+ ["diamond-open", "diamond"],
127
+ ["circle-open", "circle"],
128
+ ["half", "triangle"],
129
+ ["inverted", "triangle"],
130
+ ["crow", "vee"],
131
+ ]);
132
+
133
+ /**
134
+ * A neutral value as Cytoscape.js properties.
135
+ * @param target - nodes or edges
136
+ * @param property - the neutral property
137
+ * @param value - the value
138
+ * @returns the properties, or null when Cytoscape.js has no place for it
139
+ */
140
+ function toCyjs(target: StyleTarget, property: string, value: StyleValue): Written | null {
141
+ const specs = target === "node" ? CYJS_NODE_PROPERTIES : CYJS_EDGE_PROPERTIES;
142
+ switch (property) {
143
+ case "visible":
144
+ return exact({ display: value === false ? "none" : "element" });
145
+ case "labelItalic":
146
+ return exact({ "font-style": value === true ? "italic" : "normal" });
147
+ case "curved":
148
+ // `bezier` bows only parallel edges; `unbundled-bezier` bows every edge
149
+ return exact({ "curve-style": value === true ? "unbundled-bezier" : "straight" });
150
+ case "labelPosition":
151
+ return labelPositionToCyjs(String(value));
152
+ case "shape":
153
+ return enumToCyjs("shape", String(value), SHAPES_TO_CYJS, specs);
154
+ case "borderStyle":
155
+ return borderStyleToCyjs(String(value));
156
+ case "lineStyle":
157
+ return lineStyleToCyjs(String(value));
158
+ case "sourceArrow":
159
+ case "targetArrow":
160
+ return arrowToCyjs(
161
+ property === "sourceArrow" ? "source-arrow-shape" : "target-arrow-shape",
162
+ String(value),
163
+ specs,
164
+ );
165
+ case "labelFont":
166
+ return isFontFamily(String(value)) ? exact({ "font-family": String(value) }) : null;
167
+ default:
168
+ return propertyToCyjs(property, value, specs);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Properties written exactly.
174
+ * @param props - the properties
175
+ * @returns the written value
176
+ */
177
+ function exact(props: Record<string, string | number>): Written {
178
+ return { props, approximate: false };
179
+ }
180
+
181
+ /**
182
+ * Properties written as the nearest Cytoscape.js has.
183
+ * @param props - the properties
184
+ * @returns the written value
185
+ */
186
+ function near(props: Record<string, string | number>): Written {
187
+ return { props, approximate: true };
188
+ }
189
+
190
+ /**
191
+ * A label position as the vertical and horizontal alignments.
192
+ * @param text - the position
193
+ * @returns the written value
194
+ */
195
+ function labelPositionToCyjs(text: string): Written {
196
+ const v = ["top", "bottom"].find((x) => text.startsWith(x)) ?? "center";
197
+ const h = ["left", "right"].find((x) => text.endsWith(x)) ?? "center";
198
+ return exact({ "text-valign": v, "text-halign": h });
199
+ }
200
+
201
+ /**
202
+ * An enumeration value: Cytoscape.js's name for it, or an `other:` value Cytoscape.js has.
203
+ * @param name - the Cytoscape.js property
204
+ * @param value - the neutral value
205
+ * @param table - the neutral values Cytoscape.js has, by their Cytoscape.js names
206
+ * @param specs - the target's properties
207
+ * @returns the written value, or null when Cytoscape.js has none
208
+ */
209
+ function enumToCyjs(
210
+ name: string,
211
+ value: string,
212
+ table: ReadonlyMap<string, string>,
213
+ specs: ReadonlyMap<string, CyjsPropertySpec>,
214
+ ): Written | null {
215
+ const cy = table.get(value);
216
+ if (cy !== undefined) {
217
+ return exact({ [name]: cy });
218
+ }
219
+ const other = value.startsWith("other:") ? value.slice(6) : null;
220
+ return other !== null && specs.get(name)?.enums?.includes(other) === true ? exact({ [name]: other }) : null;
221
+ }
222
+
223
+ /**
224
+ * An arrow, or the nearest arrow Cytoscape.js has.
225
+ * @param name - the Cytoscape.js property
226
+ * @param value - the neutral arrow
227
+ * @param specs - the target's properties
228
+ * @returns the written value, or null when Cytoscape.js has none near
229
+ */
230
+ function arrowToCyjs(name: string, value: string, specs: ReadonlyMap<string, CyjsPropertySpec>): Written | null {
231
+ const own = enumToCyjs(name, value, ARROWS_TO_CYJS, specs);
232
+ if (own !== null) {
233
+ return own;
234
+ }
235
+ const nearest = ARROWS_NEAREST.get(value);
236
+ return nearest === undefined ? null : near({ [name]: nearest });
237
+ }
238
+
239
+ /**
240
+ * A border style: dash-dot is drawn dashed.
241
+ * @param value - the neutral style
242
+ * @returns the written value, or null when Cytoscape.js has none
243
+ */
244
+ function borderStyleToCyjs(value: string): Written | null {
245
+ if (value === "dash-dot") {
246
+ return near({ "border-style": "dashed" });
247
+ }
248
+ return ["solid", "dotted", "dashed", "double"].includes(value) ? exact({ "border-style": value }) : null;
249
+ }
250
+
251
+ /**
252
+ * A line style: the dashed kinds are drawn dashed, any other solid.
253
+ * @param value - the neutral style
254
+ * @returns the written value
255
+ */
256
+ function lineStyleToCyjs(value: string): Written {
257
+ if (["solid", "dotted", "dashed"].includes(value)) {
258
+ return exact({ "line-style": value });
259
+ }
260
+ return near({ "line-style": value === "dash-dot" || value === "long-dash" ? "dashed" : "solid" });
261
+ }
262
+
263
+ /**
264
+ * Any other property: the Cytoscape.js property that carries it, the value as it is.
265
+ * @param property - the neutral property
266
+ * @param value - the value
267
+ * @param specs - the target's properties
268
+ * @returns the written value, or null when no property carries it
269
+ */
270
+ function propertyToCyjs(
271
+ property: string,
272
+ value: StyleValue,
273
+ specs: ReadonlyMap<string, CyjsPropertySpec>,
274
+ ): Written | null {
275
+ const entry = Array.from(specs).find(([, spec]) => spec.property === property);
276
+ if (entry === undefined) {
277
+ return null;
278
+ }
279
+ const [name, spec] = entry;
280
+ if (spec.type === "positive" && !(typeof value === "number" && value > 0)) {
281
+ return null;
282
+ }
283
+ // Cytoscape.js takes an empty bypass as no bypass at all; a blank, which it trims, draws no text
284
+ if (value === "" && /label$/i.test(property)) {
285
+ return exact({ [name]: " " });
286
+ }
287
+ return exact({ [name]: typeof value === "boolean" ? String(value) : value });
288
+ }
289
+
290
+ /**
291
+ * A selector value literal: a string in quotes (when it holds no quote of that kind and no backslash) or a number.
292
+ * @param v - the value
293
+ * @returns the literal, or null when a selector cannot state it
294
+ */
295
+ function literal(v: Scalar): string | null {
296
+ if (typeof v === "number") {
297
+ return Number.isFinite(v) ? String(v) : null;
298
+ }
299
+ if (typeof v !== "string" || v.includes("\\") || /[\n\r\u2028\u2029]/.test(v)) {
300
+ return null;
301
+ }
302
+ if (!v.includes('"')) {
303
+ return `"${v}"`;
304
+ }
305
+ return v.includes("'") ? null : `'${v}'`;
306
+ }
307
+
308
+ /** The state of one emit. */
309
+ interface Emit {
310
+ readonly extracted: ExtractedStyles;
311
+ readonly notes: StyleNotes;
312
+ readonly scope: string;
313
+ readonly stylesheet: CytoscapeStylesheetRule[];
314
+ readonly data: { node: Record<string, CytoscapeDataValue>[]; edge: Record<string, CytoscapeDataValue>[] };
315
+ /** Per target, the source (a style column key or a generated name) to the flat key written for it. */
316
+ readonly keys: { readonly node: Map<string, string>; readonly edge: Map<string, string> };
317
+ /** Per target, the flat keys in use. */
318
+ readonly used: { readonly node: Set<string>; readonly edge: Set<string> };
319
+ }
320
+
321
+ /**
322
+ * A flat element data key for a source, unique within the target's data.
323
+ * @param e - the emit
324
+ * @param target - nodes or edges
325
+ * @param source - the source: a style column key, or a generated name
326
+ * @returns the flat key, and whether it is new
327
+ */
328
+ function flatKey(e: Emit, target: StyleTarget, source: string): { key: string; fresh: boolean } {
329
+ const known = e.keys[target].get(source);
330
+ if (known !== undefined) {
331
+ return { key: known, fresh: false };
332
+ }
333
+ let key = cytoscapeStyleKey(source);
334
+ let k = 2;
335
+ while (e.used[target].has(key)) {
336
+ key = `${cytoscapeStyleKey(source)}_${String(k)}`;
337
+ k++;
338
+ }
339
+ e.keys[target].set(source, key);
340
+ e.used[target].add(key);
341
+ return { key, fresh: true };
342
+ }
343
+
344
+ /**
345
+ * Writes a column into element data.
346
+ * @param e - the emit
347
+ * @param target - nodes or edges
348
+ * @param key - the flat key
349
+ * @param values - the values by element index; null and false are not written
350
+ */
351
+ function writeColumn(
352
+ e: Emit,
353
+ target: StyleTarget,
354
+ key: string,
355
+ values: ArrayLike<CytoscapeDataValue | null | undefined>,
356
+ ): void {
357
+ const rows = e.data[target];
358
+ for (let i = 0; i < rows.length && i < values.length; i++) {
359
+ const v = values[i];
360
+ if (v !== null && v !== undefined && v !== false) {
361
+ rows[i][key] = v;
362
+ }
363
+ }
364
+ }
365
+
366
+ /**
367
+ * The selector tests of a predicate, as alternatives of conjunctions; null when a selector cannot state it exactly.
368
+ * @param e - the emit
369
+ * @param target - nodes or edges
370
+ * @param where - the predicate
371
+ * @returns the alternatives (each a string of tests), or null
372
+ */
373
+ function testsOf(e: Emit, target: StyleTarget, where: Predicate): string[] | null {
374
+ if ("all" in where) {
375
+ return where.all ? [""] : null;
376
+ }
377
+ if ("has" in where) {
378
+ const k = keyOf(e, target, where.has);
379
+ return k === null || !("style" in where.has) ? null : [`[${k}]`];
380
+ }
381
+ if ("eq" in where) {
382
+ return equalTest(e, target, where.eq, "=");
383
+ }
384
+ if ("in" in where) {
385
+ const k = keyOf(e, target, where.in[0]);
386
+ const values = where.in[1].map((x) => literal(x));
387
+ if (values.length === 0) {
388
+ return [];
389
+ }
390
+ return k === null || values.includes(null) ? null : values.map((v) => `[${k} = ${String(v)}]`);
391
+ }
392
+ if ("not" in where) {
393
+ return notTests(e, target, where.not);
394
+ }
395
+ if ("and" in where) {
396
+ return andTests(e, target, where.and);
397
+ }
398
+ if ("or" in where) {
399
+ return orTests(e, target, where.or);
400
+ }
401
+ return null;
402
+ }
403
+
404
+ /**
405
+ * An equality test, or its negation.
406
+ * @param e - the emit
407
+ * @param target - nodes or edges
408
+ * @param eq - the column and the value
409
+ * @param op - `=` or `!=`
410
+ * @returns the test, or null when a selector cannot state it
411
+ */
412
+ function equalTest(e: Emit, target: StyleTarget, eq: readonly [ColumnRef, Scalar], op: "=" | "!="): string[] | null {
413
+ const k = keyOf(e, target, eq[0]);
414
+ const v = literal(eq[1]);
415
+ return k === null || v === null ? null : [`[${k} ${op} ${v}]`];
416
+ }
417
+
418
+ /**
419
+ * A negated predicate: only a style column's absence and an inequality are stated.
420
+ * @param e - the emit
421
+ * @param target - nodes or edges
422
+ * @param inner - the predicate negated
423
+ * @returns the tests, or null
424
+ */
425
+ function notTests(e: Emit, target: StyleTarget, inner: Predicate): string[] | null {
426
+ if ("has" in inner && "style" in inner.has) {
427
+ const k = keyOf(e, target, inner.has);
428
+ return k === null ? null : [`[^${k}]`];
429
+ }
430
+ return "eq" in inner ? equalTest(e, target, inner.eq, "!=") : null;
431
+ }
432
+
433
+ /**
434
+ * A conjunction: every combination of its parts' alternatives, at most 8.
435
+ * @param e - the emit
436
+ * @param target - nodes or edges
437
+ * @param parts - the predicates joined
438
+ * @returns the alternatives, or null
439
+ */
440
+ function andTests(e: Emit, target: StyleTarget, parts: readonly Predicate[]): string[] | null {
441
+ let alts = [""];
442
+ for (const p of parts) {
443
+ const t = testsOf(e, target, p);
444
+ if (t === null) {
445
+ return null;
446
+ }
447
+ alts = alts.flatMap((a) => t.map((b) => a + b));
448
+ if (alts.length > 8) {
449
+ return null;
450
+ }
451
+ }
452
+ return alts;
453
+ }
454
+
455
+ /**
456
+ * A disjunction: its parts' alternatives, at most 8.
457
+ * @param e - the emit
458
+ * @param target - nodes or edges
459
+ * @param parts - the predicates joined
460
+ * @returns the alternatives, or null
461
+ */
462
+ function orTests(e: Emit, target: StyleTarget, parts: readonly Predicate[]): string[] | null {
463
+ const out: string[] = [];
464
+ for (const p of parts) {
465
+ const t = testsOf(e, target, p);
466
+ if (t === null) {
467
+ return null;
468
+ }
469
+ out.push(...t);
470
+ }
471
+ return out.length > 8 ? null : out;
472
+ }
473
+
474
+ /**
475
+ * The element data key a column reference is read from: a style column's flat key (its values written into element
476
+ * data) or a data column's own name when a selector can spell it; null for anything else.
477
+ * @param e - the emit
478
+ * @param target - nodes or edges
479
+ * @param ref - the reference
480
+ * @returns the key, or null
481
+ */
482
+ function keyOf(e: Emit, target: StyleTarget, ref: ColumnRef): string | null {
483
+ if ("style" in ref) {
484
+ const col = e.extracted.columns[target].get(ref.style);
485
+ if (col === undefined) {
486
+ return null;
487
+ }
488
+ const { key, fresh } = flatKey(e, target, ref.style);
489
+ if (fresh) {
490
+ writeColumn(e, target, key, col);
491
+ }
492
+ return key;
493
+ }
494
+ if (
495
+ "data" in ref &&
496
+ PLAIN_NAME.test(ref.data) &&
497
+ ref.data !== CYTOSCAPE_IMPORT_KEY &&
498
+ !ref.data.startsWith("graphty_")
499
+ ) {
500
+ return ref.data;
501
+ }
502
+ return null;
503
+ }
504
+
505
+ /**
506
+ * The selectors of a rule: its predicate as tests, or a membership value written into element data.
507
+ * @param e - the emit
508
+ * @param rule - the rule
509
+ * @param index - the rule's position
510
+ * @returns the selector prefixes (group and scope included), one per alternative
511
+ */
512
+ function selectorsOf(e: Emit, rule: StyleRule, index: number): string[] {
513
+ const head = `${rule.target}${e.scope}`;
514
+ const tests = testsOf(e, rule.target, rule.where);
515
+ if (tests !== null) {
516
+ return tests.map((t) => head + t);
517
+ }
518
+ const src = refSource(
519
+ rule.target,
520
+ e.data[rule.target].length,
521
+ e.extracted.columns[rule.target],
522
+ e.extracted.features[rule.target],
523
+ );
524
+ const { key } = flatKey(e, rule.target, `emit.where.${String(index)}`);
525
+ const col = new Array<boolean | null>(src.n);
526
+ for (let i = 0; i < src.n; i++) {
527
+ col[i] = matches(rule.where, src, i) ? true : null;
528
+ }
529
+ writeColumn(e, rule.target, key, col);
530
+ return [`${head}[${key}]`];
531
+ }
532
+
533
+ /**
534
+ * Emits one rule's constants and mappings.
535
+ * @param e - the emit
536
+ * @param rule - the rule
537
+ * @param index - the rule's position
538
+ */
539
+ function emitRule(e: Emit, rule: StyleRule, index: number): void {
540
+ const { target } = rule;
541
+ const selectors = selectorsOf(e, rule, index);
542
+ const constants: Record<string, string | number> = {};
543
+ for (const [p, v] of Object.entries<StyleValue | undefined>(rule.set ?? {})) {
544
+ if (v === undefined) {
545
+ continue;
546
+ }
547
+ const w = written(e, target, p, v);
548
+ if (w !== null) {
549
+ Object.assign(constants, w);
550
+ }
551
+ }
552
+ if (Object.keys(constants).length > 0) {
553
+ for (const sel of selectors) {
554
+ e.stylesheet.push({ selector: sel, style: constants });
555
+ // Cytoscape.js curves a straight loop, but keeps a loop it is told to draw as a haystack
556
+ if (target === "edge" && constants["curve-style"] === "straight") {
557
+ e.stylesheet.push({ selector: `${sel}:loop`, style: { "curve-style": "haystack" } });
558
+ }
559
+ }
560
+ }
561
+ for (const [p, m] of Object.entries<Mapping | undefined>(rule.map ?? {})) {
562
+ if (m !== undefined) {
563
+ emitMapping(e, rule, index, selectors, p, m);
564
+ }
565
+ }
566
+ }
567
+
568
+ /**
569
+ * Whether Cytoscape.js holds a neutral value exactly: a value it cannot hold, or holds only as a nearest one, reads back
570
+ * as something else.
571
+ * @param target - nodes or edges
572
+ * @param property - the neutral property
573
+ * @param value - the value
574
+ * @returns true when it is written as it is
575
+ */
576
+ export function cyjsHoldsExactly(target: StyleTarget, property: string, value: StyleValue): boolean {
577
+ const w = toCyjs(target, property, value);
578
+ return w !== null && !w.approximate;
579
+ }
580
+
581
+ /**
582
+ * A value as Cytoscape.js properties, noted when it cannot be written or only approximately.
583
+ * @param e - the emit
584
+ * @param target - nodes or edges
585
+ * @param property - the neutral property
586
+ * @param value - the value
587
+ * @returns the properties, or null
588
+ */
589
+ function written(
590
+ e: Emit,
591
+ target: StyleTarget,
592
+ property: string,
593
+ value: StyleValue,
594
+ ): Record<string, string | number> | null {
595
+ const w = toCyjs(target, property, value);
596
+ if (w === null) {
597
+ e.notes.add(STYLE_ISSUE.NOT_WRITTEN, target, property, { sample: value });
598
+ return null;
599
+ }
600
+ if (w.approximate) {
601
+ e.notes.add(STYLE_ISSUE.APPROXIMATED, target, property, { sample: value });
602
+ }
603
+ return w.props;
604
+ }
605
+
606
+ /**
607
+ * Emits one mapping of a rule.
608
+ * @param e - the emit
609
+ * @param rule - the rule
610
+ * @param index - the rule's position
611
+ * @param selectors - the rule's selectors
612
+ * @param property - the property
613
+ * @param m - the mapping
614
+ */
615
+ function emitMapping(
616
+ e: Emit,
617
+ rule: StyleRule,
618
+ index: number,
619
+ selectors: readonly string[],
620
+ property: string,
621
+ m: Mapping,
622
+ ): void {
623
+ const { target } = rule;
624
+ if (m.kind === "discrete") {
625
+ // one rule per table key, when a selector can state every key; else the per-element values
626
+ const key = keyOf(e, target, m.by);
627
+ const keys = m.table.map(([k]) => literal(k));
628
+ if (key === null || keys.includes(null)) {
629
+ emitValues(e, rule, index, selectors, property);
630
+ return;
631
+ }
632
+ m.table.forEach(([, v], j) => {
633
+ const w = written(e, target, property, v);
634
+ if (w === null) {
635
+ return;
636
+ }
637
+ for (const sel of selectors) {
638
+ e.stylesheet.push({ selector: `${sel}[${key} = ${String(keys[j])}]`, style: w });
639
+ }
640
+ });
641
+ return;
642
+ }
643
+ if (m.kind === "continuous" && isNumericOutput(target, property)) {
644
+ const key = numericKey(e, target, m.by, index);
645
+ if (key !== null) {
646
+ emitContinuous(e, target, selectors, property, key, m.points);
647
+ return;
648
+ }
649
+ }
650
+ emitValues(e, rule, index, selectors, property);
651
+ }
652
+
653
+ /**
654
+ * Whether a property's values are numbers or colors, which `mapData()` interpolates.
655
+ * @param target - nodes or edges
656
+ * @param property - the property
657
+ * @returns true for a number, opacity or color property
658
+ */
659
+ function isNumericOutput(target: StyleTarget, property: string): boolean {
660
+ const kind = STYLE_PROPERTIES[target].get(property);
661
+ return kind === "number" || kind === "opacity" || kind === "color";
662
+ }
663
+
664
+ /**
665
+ * The element data key of a mapping's numeric input: a data column a mapper can spell, or the input copied into
666
+ * element data.
667
+ * @param e - the emit
668
+ * @param target - nodes or edges
669
+ * @param by - the input
670
+ * @param index - the rule's position
671
+ * @returns the key, or null when the input has no values
672
+ */
673
+ function numericKey(e: Emit, target: StyleTarget, by: ColumnRef, index: number): string | null {
674
+ const src = refSource(target, e.data[target].length, e.extracted.columns[target], e.extracted.features[target]);
675
+ const col = src.column(by);
676
+ if (col === null) {
677
+ return null;
678
+ }
679
+ if ("data" in by && PLAIN_NAME.test(by.data) && !by.data.startsWith("graphty_")) {
680
+ return by.data;
681
+ }
682
+ const { key } = flatKey(e, target, `emit.input.${String(index)}`);
683
+ const values = new Array<number | null>(src.n);
684
+ for (let i = 0; i < src.n; i++) {
685
+ const v = col[i];
686
+ values[i] = typeof v === "number" && Number.isFinite(v) ? v : null;
687
+ }
688
+ writeColumn(e, target, key, values);
689
+ return key;
690
+ }
691
+
692
+ /**
693
+ * A continuous mapping as `mapData()` rules: one per segment, the values below the first point and above the last,
694
+ * and the exact values at the points, each scoped to its input range.
695
+ * @param e - the emit
696
+ * @param target - nodes or edges
697
+ * @param selectors - the rule's selectors
698
+ * @param property - the property
699
+ * @param key - the input's element data key
700
+ * @param points - the points
701
+ */
702
+ function emitContinuous(
703
+ e: Emit,
704
+ target: StyleTarget,
705
+ selectors: readonly string[],
706
+ property: string,
707
+ key: string,
708
+ points: readonly { at: number; below: StyleValue; value: StyleValue; above: StyleValue }[],
709
+ ): void {
710
+ if (points.length === 0) {
711
+ return;
712
+ }
713
+ const lit = String;
714
+ const push = (test: string, style: Record<string, string | number> | null): void => {
715
+ if (style === null) {
716
+ return;
717
+ }
718
+ for (const sel of selectors) {
719
+ e.stylesheet.push({ selector: `${sel}${test}`, style });
720
+ }
721
+ };
722
+ const first = points[0];
723
+ const last = points.at(-1) ?? first;
724
+ push(`[${key} < ${lit(first.at)}]`, written(e, target, property, first.below));
725
+ for (let k = 0; k + 1 < points.length; k++) {
726
+ const a = points[k];
727
+ const b = points[k + 1];
728
+ const wa = written(e, target, property, a.above);
729
+ const wb = written(e, target, property, b.below);
730
+ if (wa === null || wb === null) {
731
+ continue;
732
+ }
733
+ const style: Record<string, string | number> = {};
734
+ for (const name of Object.keys(wa)) {
735
+ style[name] = `mapData(${key}, ${lit(a.at)}, ${lit(b.at)}, ${String(wa[name])}, ${String(wb[name])})`;
736
+ }
737
+ push(`[${key} > ${lit(a.at)}][${key} < ${lit(b.at)}]`, style);
738
+ }
739
+ push(`[${key} > ${lit(last.at)}]`, written(e, target, property, last.above));
740
+ for (const p of points) {
741
+ push(`[${key} = ${lit(p.at)}]`, written(e, target, property, p.value));
742
+ }
743
+ }
744
+
745
+ /**
746
+ * A mapping as its per-element values: what the rule gives each element, written ready for Cytoscape.js into element
747
+ * data and read with `data()`, scoped to the elements that have a value.
748
+ * @param e - the emit
749
+ * @param rule - the rule
750
+ * @param index - the rule's position
751
+ * @param selectors - the rule's selectors
752
+ * @param property - the property
753
+ */
754
+ function emitValues(e: Emit, rule: StyleRule, index: number, selectors: readonly string[], property: string): void {
755
+ const { target } = rule;
756
+ const n = e.data[target].length;
757
+ const src = refSource(target, n, e.extracted.columns[target], e.extracted.features[target]);
758
+ const mapping = new Map(Object.entries<Mapping | undefined>(rule.map ?? {})).get(property);
759
+ const only: StyleRule = { ...rule, where: { all: true }, set: undefined, map: { [property]: mapping } };
760
+ const values = resolveRules([only], target, property, src);
761
+ const names = new Map<string, (string | number | null)[]>();
762
+ for (let i = 0; i < n; i++) {
763
+ const v = values[i];
764
+ if (v === null) {
765
+ continue;
766
+ }
767
+ const w = written(e, target, property, v);
768
+ if (w === null) {
769
+ continue;
770
+ }
771
+ for (const [name, x] of Object.entries(w)) {
772
+ let col = names.get(name);
773
+ if (col === undefined) {
774
+ col = new Array<string | number | null>(n).fill(null);
775
+ names.set(name, col);
776
+ }
777
+ col[i] = x;
778
+ }
779
+ }
780
+ for (const [name, col] of names) {
781
+ const { key } = flatKey(e, target, `emit.value.${String(index)}.${name}`);
782
+ writeColumn(e, target, key, col);
783
+ for (const sel of selectors) {
784
+ e.stylesheet.push({ selector: `${sel}[${key}]`, style: { [name]: `data(${key})` } });
785
+ }
786
+ }
787
+ }
788
+
789
+ /**
790
+ * Writes neutral styles as a Cytoscape.js stylesheet, the element data it reads and the bypasses that make it exact.
791
+ * @param extracted - the styles
792
+ * @param options - the import id that scopes the stylesheet
793
+ * @returns the stylesheet, the element data, the bypasses and the notes of what could not be written
794
+ * @category Styles
795
+ */
796
+ export function toCytoscapeStylesheet(
797
+ extracted: ExtractedStyles,
798
+ options: CytoscapeStylesheetOptions = {},
799
+ ): CytoscapeStylesheetResult {
800
+ const notes = new StyleNotes();
801
+ const importId = options.importId?.replaceAll(/["'\\\n\r\u2028\u2029]/g, "_");
802
+ const { nodes } = extracted.fingerprint;
803
+ const { edges } = extracted.fingerprint;
804
+ const e: Emit = {
805
+ extracted,
806
+ notes,
807
+ scope: importId === undefined ? "" : `[${CYTOSCAPE_IMPORT_KEY} = "${importId}"]`,
808
+ stylesheet: [],
809
+ data: {
810
+ node: Array.from({ length: nodes }, () => ({})),
811
+ edge: Array.from({ length: edges }, () => ({})),
812
+ },
813
+ keys: { node: new Map(), edge: new Map() },
814
+ // a flat key never takes the name of a data field the elements already hold
815
+ used: {
816
+ node: new Set([CYTOSCAPE_IMPORT_KEY, ...dataNames(extracted, "node")]),
817
+ edge: new Set([CYTOSCAPE_IMPORT_KEY, ...dataNames(extracted, "edge")]),
818
+ },
819
+ };
820
+ if (importId !== undefined) {
821
+ for (const rows of [e.data.node, e.data.edge]) {
822
+ for (const row of rows) {
823
+ row[CYTOSCAPE_IMPORT_KEY] = importId;
824
+ }
825
+ }
826
+ }
827
+ // Cytoscape.js draws its own defaults: the rules that state them are not written again
828
+ const rules =
829
+ extracted.styles.source.tool === "cytoscape-js"
830
+ ? extracted.styles.rules.filter((r) => r.origin !== "tool")
831
+ : extracted.styles.rules;
832
+ rules.forEach((rule, k) => {
833
+ if (rule.role !== "exceptions") {
834
+ emitRule(e, rule, k);
835
+ }
836
+ });
837
+ const bypass = exactness(e, rules);
838
+ for (const sel of e.stylesheet) {
839
+ if (parseCyjsSelector(sel.selector).invalid) {
840
+ notes.add(STYLE_ISSUE.NOT_WRITTEN, null, "selector", { sample: sel.selector });
841
+ }
842
+ }
843
+ const rows = (list: Record<string, CytoscapeDataValue>[]): (Record<string, CytoscapeDataValue> | null)[] =>
844
+ list.map((r) => (Object.keys(r).length > 0 ? r : null));
845
+ return {
846
+ stylesheet: e.stylesheet,
847
+ elementData: { node: rows(e.data.node), edge: rows(e.data.edge) },
848
+ bypass,
849
+ notes: notes.list(),
850
+ };
851
+ }
852
+
853
+ /**
854
+ * Applies the stylesheet to the element data with graph-io's Cytoscape.js engine and gives every element whose drawn
855
+ * value differs from the styles a bypass with the right value.
856
+ * @param e - the emit
857
+ * @param rules - the rules written (Cytoscape.js's own defaults left out)
858
+ * @returns the bypasses
859
+ */
860
+ function exactness(e: Emit, rules: readonly StyleRule[]): CytoscapePerElement<string | number> {
861
+ const { extracted } = e;
862
+ const nodes = e.data.node.length;
863
+ const elements: unknown[] = [];
864
+ for (let i = 0; i < nodes; i++) {
865
+ elements.push({
866
+ group: "nodes",
867
+ data: { ...dataColumns(extracted, "node", i), ...e.data.node[i], id: `n${String(i)}` },
868
+ });
869
+ }
870
+ // the edges join two nodes (no loops: Cytoscape.js draws a loop curved whatever the stylesheet says)
871
+ const far = nodes > 1 ? "n1" : "n0";
872
+ for (let i = 0; i < e.data.edge.length && nodes > 0; i++) {
873
+ elements.push({
874
+ group: "edges",
875
+ data: {
876
+ ...dataColumns(extracted, "edge", i),
877
+ ...e.data.edge[i],
878
+ id: `e${String(i)}`,
879
+ source: "n0",
880
+ target: far,
881
+ },
882
+ });
883
+ }
884
+ const drawn = evaluateCytoscapeJs({ elements, style: e.stylesheet, toolDefaults: false });
885
+ const out = {
886
+ node: [] as (Record<string, string | number> | null)[],
887
+ edge: [] as (Record<string, string | number> | null)[],
888
+ };
889
+ for (const target of ["node", "edge"] as const) {
890
+ const n = e.data[target].length;
891
+ const src = refSource(target, n, extracted.columns[target], extracted.features[target]);
892
+ const rows = new Array<Record<string, string | number> | null>(n).fill(null);
893
+ for (const property of STYLE_PROPERTIES[target].keys()) {
894
+ const want = resolveRules(rules, target, property, src);
895
+ addBypasses(rows, target, property, want, drawn[target].values[property]);
896
+ }
897
+ out[target] = rows;
898
+ }
899
+ return out;
900
+ }
901
+
902
+ /**
903
+ * A bypass of one property for every element whose drawn value differs from the styles' and that Cytoscape.js can
904
+ * hold exactly.
905
+ * @param rows - the bypasses, changed in place
906
+ * @param target - nodes or edges
907
+ * @param property - the property
908
+ * @param want - the styles' values
909
+ * @param got - what is drawn, or undefined for nothing
910
+ */
911
+ function addBypasses(
912
+ rows: (Record<string, string | number> | null)[],
913
+ target: StyleTarget,
914
+ property: string,
915
+ want: readonly (StyleValue | null)[],
916
+ got: readonly (StyleValue | null)[] | undefined,
917
+ ): void {
918
+ for (let i = 0; i < rows.length; i++) {
919
+ const w = want[i];
920
+ const conv = w === null || (got?.[i] ?? null) === w ? null : toCyjs(target, property, w);
921
+ if (conv !== null && !conv.approximate) {
922
+ rows[i] = { ...rows[i], ...conv.props };
923
+ }
924
+ }
925
+ }
926
+
927
+ /**
928
+ * The names of the data columns among the styles' features.
929
+ * @param extracted - the styles
930
+ * @param target - nodes or edges
931
+ * @returns the names
932
+ */
933
+ function dataNames(extracted: ExtractedStyles, target: StyleTarget): string[] {
934
+ return extracted.features[target].flatMap((f) => ("data" in f.ref ? [f.ref.data] : []));
935
+ }
936
+
937
+ /**
938
+ * An element's data columns from the styles' features: what a consumer writes into element data from the graph.
939
+ * @param extracted - the styles
940
+ * @param target - nodes or edges
941
+ * @param i - the element
942
+ * @returns the data fields
943
+ */
944
+ function dataColumns(extracted: ExtractedStyles, target: StyleTarget, i: number): Record<string, unknown> {
945
+ const out: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
946
+ for (const f of extracted.features[target]) {
947
+ if ("data" in f.ref && f.values[i] !== null) {
948
+ out[f.ref.data] = f.values[i];
949
+ }
950
+ }
951
+ return out;
952
+ }