@graphty/graph-io 0.3.43 → 0.3.44

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