@graphty/graph-io 0.3.42 → 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-CpRyHZ3H.js → exporter-quPUCBiC.js} +1807 -288
  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 +295 -414
  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 +23 -7
  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 +441 -265
  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 +30 -13
  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 +543 -323
  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-CpRyHZ3H.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,1129 @@
1
+ /**
2
+ * The planner: turns a file's per-element style values into a few readable rules that reproduce them exactly. It
3
+ * groups properties into families, explains each family by a discrete or continuous mapping on one feature, a
4
+ * passthrough on an existing column, style classes or a copy, orders the rules, caps them at 15 per target and
5
+ * replays them: every element whose resolved value differs from the file's goes into one sparse exceptions column
6
+ * per property on top. One counted budget bounds the work; when it runs out the rest is copied.
7
+ */
8
+
9
+ import { StyleBudget } from "../budget.js";
10
+ import { STYLE_ISSUE } from "../codes.js";
11
+ import { toolDefaults } from "../defaults.js";
12
+ import { StyleNotes } from "../notes.js";
13
+ import type {
14
+ ExtractedStyles,
15
+ GraphStyle,
16
+ Mapping,
17
+ ReaderOutput,
18
+ StyleColumn,
19
+ StyleDialect,
20
+ StyleFeature,
21
+ StyleFeatures,
22
+ StyleFingerprint,
23
+ StyleRule,
24
+ StyleRuleFacts,
25
+ StyleTarget,
26
+ StyleTool,
27
+ StyleValue,
28
+ StyleWorkBudget,
29
+ } from "../types.js";
30
+ import { isStyleValue, STYLE_PROPERTIES } from "../vocabulary.js";
31
+ import { CLASS_COLUMN, styleClasses } from "./classes.js";
32
+ import { type ContinuousCandidate, continuousCandidate } from "./continuous.js";
33
+ import { type DiscreteCandidate, discreteCandidate } from "./discrete.js";
34
+ import { familiesOf, type Family } from "./families.js";
35
+ import { describeFeature, type FeatureInfo } from "./features.js";
36
+ import {
37
+ canonicalFor,
38
+ evalContinuous,
39
+ isShortPredicate,
40
+ matches,
41
+ numberOf,
42
+ refSource,
43
+ resolveRules,
44
+ within,
45
+ } from "./resolve.js";
46
+
47
+ /**
48
+ * The most rules per target: the planner keeps at most 15 plus the exceptions rule.
49
+ * @category Styles
50
+ */
51
+ export const MAX_STYLE_RULES = 16;
52
+ /** The most candidate features tested per target, fewest distinct values first. */
53
+ const MAX_FEATURES = 64;
54
+ /** Lengths a tool states in its own unit; they are left out when the file's unit differs. */
55
+ const LENGTHS: ReadonlySet<string> = new Set(["width", "height", "borderWidth", "labelSize"]);
56
+
57
+ /**
58
+ * What the planner is given: the canonical per-element values a style reader produced, the rules the file declares,
59
+ * and the graph they belong to.
60
+ * @category Styles
61
+ */
62
+ export interface StyleValues {
63
+ /** The graph-io format id. */
64
+ readonly format: string;
65
+ /** How the file spelled its values. */
66
+ readonly dialect?: StyleDialect | null;
67
+ /** The program that wrote the file, whose defaults fill in what the file leaves unset; null when unknown. */
68
+ readonly tool?: StyleTool | null;
69
+ /** The unit of every length; defaults to pixels. */
70
+ readonly units?: "px" | "relative";
71
+ /** The graph's own style. */
72
+ readonly graph?: GraphStyle;
73
+ /** Node values, declared rules, reader columns and extra features. */
74
+ readonly node?: ReaderOutput;
75
+ /** Edge values, declared rules, reader columns and extra features. */
76
+ readonly edge?: ReaderOutput;
77
+ /** The graph the values belong to; its counts give the number of elements. */
78
+ readonly fingerprint: StyleFingerprint;
79
+ }
80
+
81
+ /**
82
+ * Options of `planStyles()`.
83
+ * @category Styles
84
+ */
85
+ export interface PlanOptions {
86
+ /** Whether to add the writing tool's defaults as a rule at the bottom; defaults to true. */
87
+ readonly toolDefaults?: boolean;
88
+ /** The counted work budget; defaults to a fresh `StyleBudget`. */
89
+ readonly budget?: StyleWorkBudget;
90
+ /**
91
+ * Where notes are collected; the reader's notes, when it used the same collector, are kept.
92
+ * @internal
93
+ */
94
+ readonly notes?: StyleNotes;
95
+ /** Data columns the reader read the values from: they are never offered as explanations of themselves. */
96
+ readonly rawColumns?: {
97
+ /** Node columns. */
98
+ readonly node?: readonly string[];
99
+ /** Edge columns. */
100
+ readonly edge?: readonly string[];
101
+ };
102
+ /** Cancels the planning. */
103
+ readonly signal?: AbortSignal;
104
+ }
105
+
106
+ /** A rule being built. */
107
+ interface Draft {
108
+ id: string;
109
+ readonly target: StyleTarget;
110
+ readonly role: string;
111
+ readonly origin: string;
112
+ where: StyleRule["where"];
113
+ set?: Record<string, StyleValue>;
114
+ map?: Record<string, Mapping>;
115
+ facts?: StyleRuleFacts;
116
+ readonly ext?: Readonly<Record<string, unknown>>;
117
+ }
118
+
119
+ /** The planning state of one target. */
120
+ interface TargetPlan {
121
+ readonly target: StyleTarget;
122
+ readonly n: number;
123
+ readonly notes: StyleNotes;
124
+ readonly budget: StyleWorkBudget;
125
+ readonly spend: (ops: number) => void;
126
+ readonly columns: Map<string, StyleColumn>;
127
+ readonly ids: Set<string>;
128
+ }
129
+
130
+ /**
131
+ * Plans a file's styles: the rules that reproduce its per-element values, with the columns they read.
132
+ * @param values - the canonical values, the declared rules and the graph they belong to
133
+ * @param features - the candidate explaining columns per target (`featuresOf()` of the snapshot)
134
+ * @param options - tool defaults, the budget, the notes collector, the raw columns and a cancel signal
135
+ * @returns the styles, exact for every element
136
+ * @category Styles
137
+ */
138
+ export function planStyles(values: StyleValues, features: StyleFeatures, options: PlanOptions = {}): ExtractedStyles {
139
+ const notes = options.notes ?? new StyleNotes();
140
+ const budget = options.budget ?? new StyleBudget();
141
+ const defaults =
142
+ options.toolDefaults !== false && values.tool !== undefined && values.tool !== null
143
+ ? toolDefaults(values.tool)
144
+ : null;
145
+ const units = values.units ?? "px";
146
+ const rules: StyleRule[] = [];
147
+ const columns = { node: new Map<string, StyleColumn>(), edge: new Map<string, StyleColumn>() };
148
+ const allFeatures = { node: [] as StyleFeature[], edge: [] as StyleFeature[] };
149
+ for (const target of ["node", "edge"] as const) {
150
+ const given = values[target];
151
+ // a reader's own tool-default rules (Gephi's arrows on directed edges) go with the tool's constants
152
+ const out =
153
+ options.toolDefaults === false && given?.rules !== undefined
154
+ ? { ...given, rules: given.rules.filter((r) => r.origin !== "tool") }
155
+ : given;
156
+ allFeatures[target] = [...features[target], ...(out?.features ?? [])];
157
+ const toolSet = defaults === null ? null : toolSetOf(defaults, target, units);
158
+ const plan: TargetPlan = {
159
+ target,
160
+ n: target === "node" ? values.fingerprint.nodes : values.fingerprint.edges,
161
+ notes,
162
+ budget,
163
+ spend: (ops) => {
164
+ budget.spend(target, ops);
165
+ },
166
+ columns: columns[target],
167
+ ids: new Set(),
168
+ };
169
+ rules.push(
170
+ ...planTarget(
171
+ plan,
172
+ out,
173
+ allFeatures[target],
174
+ new Set(options.rawColumns?.[target] ?? []),
175
+ toolSet,
176
+ options.signal,
177
+ ),
178
+ );
179
+ }
180
+ const graph = graphStyles(defaults, values.graph ?? {});
181
+ return {
182
+ styles: {
183
+ version: 1,
184
+ source: {
185
+ format: values.format,
186
+ dialect: values.dialect ?? null,
187
+ tool: defaults === null ? null : defaults.tool,
188
+ },
189
+ units: { length: units },
190
+ graph: graph,
191
+ rules,
192
+ notes: notes.list(),
193
+ },
194
+ columns,
195
+ features: allFeatures,
196
+ fingerprint: values.fingerprint,
197
+ };
198
+ }
199
+
200
+ /**
201
+ * The tool's default values of a target, as one defaults rule sets them; a length in other units than the file's is
202
+ * left out.
203
+ * @param defaults - the tool's defaults
204
+ * @param target - nodes or edges
205
+ * @param units - the file's length units
206
+ * @returns the values, or null when none applies
207
+ */
208
+ function toolSetOf(
209
+ defaults: NonNullable<ReturnType<typeof toolDefaults>>,
210
+ target: StyleTarget,
211
+ units: string,
212
+ ): Record<string, StyleValue> | null {
213
+ const toolSet: Record<string, StyleValue> = {};
214
+ for (const [p, d] of Object.entries(defaults[target]) as [string, { value: unknown } | undefined][]) {
215
+ const fits = d !== undefined && isStyleValue(target, p, d.value);
216
+ if (fits && !(LENGTHS.has(p) && defaults.units !== units)) {
217
+ toolSet[p] = d.value as StyleValue;
218
+ }
219
+ }
220
+ return Object.keys(toolSet).length === 0 ? null : toolSet;
221
+ }
222
+
223
+ /**
224
+ * The graph's styles: the tool's defaults, then the file's own values over them.
225
+ * @param defaults - the tool's defaults, or null
226
+ * @param given - the file's graph values
227
+ * @returns the values
228
+ */
229
+ function graphStyles(
230
+ defaults: ReturnType<typeof toolDefaults> | null,
231
+ given: Readonly<Record<string, unknown>>,
232
+ ): Record<string, StyleValue> {
233
+ const graph: Record<string, StyleValue> = {};
234
+ const own = Object.entries(defaults?.graph ?? {}) as [string, { value: unknown } | undefined][];
235
+ for (const [p, d] of own) {
236
+ if (d !== undefined && isStyleValue("graph", p, d.value)) {
237
+ graph[p] = d.value as StyleValue;
238
+ }
239
+ }
240
+ for (const [p, v] of Object.entries(given)) {
241
+ if (isStyleValue("graph", p, v)) {
242
+ graph[p] = v as StyleValue;
243
+ }
244
+ }
245
+ return graph;
246
+ }
247
+
248
+ /**
249
+ * A rule id not used yet in this target.
250
+ * @param plan - the target's state
251
+ * @param base - the wanted id
252
+ * @returns the id, suffixed when taken
253
+ */
254
+ function uniqueId(plan: TargetPlan, base: string): string {
255
+ let id = base;
256
+ let k = 2;
257
+ while (plan.ids.has(id)) {
258
+ id = `${base}#${String(k)}`;
259
+ k++;
260
+ }
261
+ plan.ids.add(id);
262
+ return id;
263
+ }
264
+
265
+ /**
266
+ * Sets one property's mapping of a rule, making the rule's mappings on first use.
267
+ * @param rule - the rule, changed in place
268
+ * @param property - the property
269
+ * @param mapping - the mapping
270
+ */
271
+ function setMapping(rule: Draft, property: string, mapping: Mapping): void {
272
+ rule.map ??= {};
273
+ rule.map[property] = mapping;
274
+ }
275
+
276
+ /**
277
+ * The type-checked per-element values of a target: a value of the wrong kind is noted and dropped.
278
+ * @param plan - the target's state
279
+ * @param out - the reader's output
280
+ * @returns the values per property that have at least one value
281
+ */
282
+ function checkedTruth(plan: TargetPlan, out: ReaderOutput | undefined): Map<string, (StyleValue | null)[]> {
283
+ const truth = new Map<string, (StyleValue | null)[]>();
284
+ for (const [p, arr] of Object.entries<StyleColumn | undefined>(out?.values ?? {})) {
285
+ if (arr === undefined) {
286
+ continue;
287
+ }
288
+ if (!STYLE_PROPERTIES[plan.target].has(p) && !p.startsWith("graphty:")) {
289
+ plan.notes.add(STYLE_ISSUE.NOT_CARRIED, plan.target, p, { sample: p });
290
+ continue;
291
+ }
292
+ const vals = checkedValues(plan, p, arr);
293
+ plan.spend(plan.n);
294
+ if (vals.some((v) => v !== null)) {
295
+ truth.set(p, vals);
296
+ }
297
+ }
298
+ return truth;
299
+ }
300
+
301
+ /**
302
+ * One property's values, a value of the wrong kind noted and dropped.
303
+ * @param plan - the target's state
304
+ * @param p - the property
305
+ * @param arr - the reader's values
306
+ * @returns the values, one per element
307
+ */
308
+ function checkedValues(plan: TargetPlan, p: string, arr: StyleColumn): (StyleValue | null)[] {
309
+ const vals = new Array<StyleValue | null>(plan.n).fill(null);
310
+ for (let i = 0; i < plan.n && i < arr.length; i++) {
311
+ const v = arr[i] ?? null;
312
+ if (v !== null && isStyleValue(plan.target, p, v)) {
313
+ vals[i] = v;
314
+ } else if (v !== null) {
315
+ plan.notes.add(STYLE_ISSUE.BAD_VALUE, plan.target, p, { sample: v, element: i });
316
+ }
317
+ }
318
+ return vals;
319
+ }
320
+
321
+ /**
322
+ * A copy of a property record without its undefined entries.
323
+ * @param o - the record
324
+ * @returns the copy, or undefined
325
+ */
326
+ function defined<T>(o: Readonly<Record<string, T | undefined>> | undefined): Record<string, T> | undefined {
327
+ if (o === undefined) {
328
+ return undefined;
329
+ }
330
+ const out: Record<string, T> = {};
331
+ for (const [k, v] of Object.entries(o)) {
332
+ if (v !== undefined) {
333
+ out[k] = v;
334
+ }
335
+ }
336
+ return out;
337
+ }
338
+
339
+ /**
340
+ * The properties a rule writes.
341
+ * @param r - the rule
342
+ * @returns the property names
343
+ */
344
+ function writes(r: Draft | StyleRule): string[] {
345
+ return [...Object.keys(r.set ?? {}), ...Object.keys(r.map ?? {})];
346
+ }
347
+
348
+ /**
349
+ * The declared rules, with every predicate longer than a short one evaluated into a membership style column and
350
+ * runs of rules that set values on every element merged into one defaults rule.
351
+ * @param plan - the target's state
352
+ * @param declared - the reader's rules
353
+ * @param features - the features, for evaluating predicates over data columns
354
+ * @returns the rules to keep
355
+ */
356
+ function prepareDeclared(plan: TargetPlan, declared: readonly StyleRule[], features: readonly StyleFeature[]): Draft[] {
357
+ const src = refSource(plan.target, plan.n, plan.columns, features);
358
+ const drafts: Draft[] = [];
359
+ for (const r of declared.filter((x) => x.target === plan.target)) {
360
+ const d: Draft = { ...r, set: defined(r.set), map: defined(r.map) };
361
+ if (d.set === undefined) {
362
+ delete d.set;
363
+ }
364
+ if (d.map === undefined) {
365
+ delete d.map;
366
+ }
367
+ if (!isShortPredicate(r.where)) {
368
+ asMembership(plan, d, r, src);
369
+ }
370
+ drafts.push(d);
371
+ }
372
+ const merged = mergedDefaults(plan.target, drafts);
373
+ for (const d of merged) {
374
+ d.id = uniqueId(plan, d.id);
375
+ }
376
+ return merged;
377
+ }
378
+
379
+ /**
380
+ * Evaluates a long predicate into a membership style column the rule selects instead.
381
+ * @param plan - the target's state
382
+ * @param d - the rule's draft, changed in place
383
+ * @param r - the declared rule
384
+ * @param src - the reference source
385
+ */
386
+ function asMembership(plan: TargetPlan, d: Draft, r: StyleRule, src: ReturnType<typeof refSource>): void {
387
+ let member = 1;
388
+ while (plan.columns.has(`member.${String(member)}`)) {
389
+ member++;
390
+ }
391
+ const key = `member.${String(member)}`;
392
+ const col = new Array<StyleValue | null>(plan.n).fill(null);
393
+ let count = 0;
394
+ for (let i = 0; i < plan.n; i++) {
395
+ if (matches(r.where, src, i)) {
396
+ col[i] = true;
397
+ count++;
398
+ }
399
+ }
400
+ plan.spend(plan.n * 8);
401
+ plan.columns.set(key, col);
402
+ d.where = { has: { style: key } };
403
+ d.facts = { ...r.facts, elements: count, reason: r.facts?.reason ?? "SELECTOR" };
404
+ plan.notes.add(STYLE_ISSUE.SELECTOR_AS_COLUMN, plan.target, r.id, { count });
405
+ }
406
+
407
+ /**
408
+ * Consecutive declared rules that select everything and only set values, as one defaults rule (later wins).
409
+ * @param target - nodes or edges
410
+ * @param drafts - the declared rules
411
+ * @returns the rules, runs merged
412
+ */
413
+ function mergedDefaults(target: StyleTarget, drafts: readonly Draft[]): Draft[] {
414
+ const merged: Draft[] = [];
415
+ const plain = (x: Draft): boolean => "all" in x.where && x.set !== undefined && x.map === undefined;
416
+ let i = 0;
417
+ while (i < drafts.length) {
418
+ let j = i + 1;
419
+ while (plain(drafts[i]) && j < drafts.length && plain(drafts[j])) {
420
+ j++;
421
+ }
422
+ if (j - i >= 2) {
423
+ merged.push({
424
+ id: drafts[i].id,
425
+ target,
426
+ role: "defaults",
427
+ origin: "declared",
428
+ where: { all: true },
429
+ set: Object.assign({}, ...drafts.slice(i, j).map((x) => x.set)) as Record<string, StyleValue>,
430
+ });
431
+ } else {
432
+ merged.push(drafts[i]);
433
+ }
434
+ i = j;
435
+ }
436
+ return merged;
437
+ }
438
+
439
+ /** A passthrough on existing columns that explains a family. */
440
+ interface PassCandidate {
441
+ readonly type: "passdata";
442
+ readonly cols: ReadonlyMap<string, FeatureInfo>;
443
+ readonly raw: boolean;
444
+ readonly cost: number;
445
+ }
446
+
447
+ /** The floor: classes or a copy. */
448
+ interface OpaqueCandidate {
449
+ readonly type: "opaque";
450
+ readonly cost: number;
451
+ }
452
+
453
+ type Candidate = DiscreteCandidate | ContinuousCandidate | PassCandidate | OpaqueCandidate;
454
+ const ORDER = ["passdata", "discrete", "continuous", "opaque"];
455
+
456
+ /**
457
+ * Tests a passthrough on existing data columns: one column per property whose canonical value equals the style on
458
+ * every element.
459
+ * @param plan - the target's state
460
+ * @param fam - the family
461
+ * @param truth - the values per property
462
+ * @param infos - the features
463
+ * @returns the candidate, or null
464
+ */
465
+ function passCandidate(
466
+ plan: TargetPlan,
467
+ fam: Family,
468
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
469
+ infos: readonly FeatureInfo[],
470
+ ): PassCandidate | null {
471
+ const cols = new Map<string, FeatureInfo>();
472
+ for (const p of fam.props) {
473
+ const canon = canonicalFor(plan.target, p);
474
+ const isColor = STYLE_PROPERTIES[plan.target].get(p) === "color";
475
+ const t = truth.get(p) ?? [];
476
+ const hit = infos.find((f) => {
477
+ if (!("data" in f.feature.ref)) {
478
+ return false;
479
+ }
480
+ plan.spend(plan.n);
481
+ for (let i = 0; i < plan.n; i++) {
482
+ const raw = f.feature.values[i];
483
+ // a color column is reused only when it already holds "#rrggbb": other spellings read differently
484
+ if (isColor && raw !== null && !(typeof raw === "string" && /^#[0-9a-fA-F]{6}$/.test(raw))) {
485
+ return false;
486
+ }
487
+ if ((raw === null ? null : canon(raw)) !== t[i]) {
488
+ return false;
489
+ }
490
+ }
491
+ return true;
492
+ });
493
+ if (hit === undefined) {
494
+ return null;
495
+ }
496
+ cols.set(p, hit);
497
+ }
498
+ const raw = Array.from(cols.values()).some((f) => f.raw);
499
+ // a passthrough on the raw style column itself recovers nothing: nearly as opaque as a copy
500
+ return { type: "passdata", cols, raw, cost: raw ? 240 : 50 };
501
+ }
502
+
503
+ /**
504
+ * Plans one target.
505
+ * @param plan - the target's state
506
+ * @param out - the reader's output
507
+ * @param features - the candidate features
508
+ * @param rawNames - the data columns the values were read from
509
+ * @param toolSet - the tool's default values, or null
510
+ * @param signal - cancels the planning
511
+ * @returns the target's rules, bottom to top
512
+ */
513
+ function planTarget(
514
+ plan: TargetPlan,
515
+ out: ReaderOutput | undefined,
516
+ features: readonly StyleFeature[],
517
+ rawNames: ReadonlySet<string>,
518
+ toolSet: Record<string, StyleValue> | null,
519
+ signal: AbortSignal | undefined,
520
+ ): StyleRule[] {
521
+ const { target, n } = plan;
522
+ for (const [k, v] of out?.columns ?? []) {
523
+ plan.columns.set(k, v);
524
+ }
525
+ const truth = checkedTruth(plan, out);
526
+ const toolRule: Draft[] =
527
+ toolSet === null
528
+ ? []
529
+ : [
530
+ {
531
+ id: uniqueId(plan, `${target}.tool`),
532
+ target,
533
+ role: "defaults",
534
+ origin: "tool",
535
+ where: { all: true },
536
+ set: toolSet,
537
+ },
538
+ ];
539
+ const declared = prepareDeclared(plan, out?.rules ?? [], features);
540
+ const declaredProps = new Set(declared.flatMap(writes));
541
+
542
+ const { defaults, rest } = splitDefaults(n, truth, declaredProps);
543
+ plan.spend(n * truth.size);
544
+ const inferred: Draft[] = [];
545
+ if (Object.keys(defaults).length > 0) {
546
+ inferred.push({
547
+ id: uniqueId(plan, `${target}.defaults`),
548
+ target,
549
+ role: "defaults",
550
+ origin: "inferred",
551
+ where: { all: true },
552
+ set: defaults,
553
+ });
554
+ }
555
+
556
+ const families = familiesOf(rest);
557
+ plan.spend(n * rest.length);
558
+ const infos = features
559
+ .map((f) => describeFeature(f, "data" in f.ref && rawNames.has(f.ref.data)))
560
+ .sort((a, b) => a.distinct - b.distinct);
561
+ plan.spend(n * features.length);
562
+ const testable = infos.filter((f) => f.categorical || f.numeric).slice(0, MAX_FEATURES);
563
+
564
+ const groups: Draft[] = [];
565
+ const mappings = new Map<string, Draft>();
566
+ const passData: Draft = {
567
+ id: `${target}.passthrough`,
568
+ target,
569
+ role: "passthrough",
570
+ origin: "inferred",
571
+ where: { all: true },
572
+ map: {},
573
+ };
574
+ const passRaw: Draft = {
575
+ id: `${target}.passthrough.raw`,
576
+ target,
577
+ role: "passthrough",
578
+ origin: "inferred",
579
+ where: { all: true },
580
+ map: {},
581
+ facts: { reason: "NO_EXPLANATION" },
582
+ };
583
+ const copy: Draft = {
584
+ id: `${target}.copy`,
585
+ target,
586
+ role: "passthrough",
587
+ origin: "inferred",
588
+ where: { all: true },
589
+ map: {},
590
+ facts: { reason: "NO_EXPLANATION" },
591
+ };
592
+ const opaque: { fam: Family; raw: ReadonlyMap<string, FeatureInfo> | null }[] = [];
593
+ const tolerated = new Map<string, Set<number>>();
594
+ let budgetHit = 0;
595
+
596
+ const st: Inference = { groups, mappings, passData, passRaw, copy, opaque, tolerated };
597
+ for (const fam of families) {
598
+ signal?.throwIfAborted();
599
+ if (plan.budget.exhausted(target)) {
600
+ budgetHit++;
601
+ opaque.push({ fam, raw: null });
602
+ continue;
603
+ }
604
+ applyChoice(plan, st, fam, chooseCandidate(plan, fam, testable, infos, truth), truth);
605
+ }
606
+ if (budgetHit > 0) {
607
+ plan.notes.add(STYLE_ISSUE.BUDGET, target, null, { count: budgetHit });
608
+ }
609
+ poolOpaque(plan, st, budgetHit, truth);
610
+
611
+ const nonEmpty = (r: Draft): boolean => Object.keys(r.map ?? {}).length > 0;
612
+ const sortedMappings = Array.from(mappings.values()).sort((a, b) => compareText(a.id, b.id));
613
+ const drafts: Draft[] = [
614
+ ...toolRule,
615
+ ...declared,
616
+ ...inferred,
617
+ ...groups,
618
+ ...sortedMappings,
619
+ ...[passData, passRaw, copy].filter(nonEmpty),
620
+ ];
621
+ for (const d of drafts.slice(toolRule.length + declared.length + inferred.length)) {
622
+ d.id = uniqueId(plan, d.id);
623
+ }
624
+
625
+ const src = refSource(target, n, plan.columns, features);
626
+ enforceCap(plan, drafts, truth, src);
627
+ const exceptions = replay(plan, drafts, truth, toolSet, tolerated, src);
628
+ if (exceptions !== null) {
629
+ drafts.push(exceptions);
630
+ }
631
+ return drafts.map(finish);
632
+ }
633
+
634
+ /**
635
+ * The inferred defaults: each property non-null and equal on every element; the others are left to explain. A
636
+ * property a declared rule writes is the declared rule's.
637
+ * @param n - the element count
638
+ * @param truth - the values per property
639
+ * @param declaredProps - the properties the declared rules write
640
+ * @returns the defaults, and the properties left
641
+ */
642
+ function splitDefaults(
643
+ n: number,
644
+ truth: ReadonlyMap<string, (StyleValue | null)[]>,
645
+ declaredProps: ReadonlySet<string>,
646
+ ): { defaults: Record<string, StyleValue>; rest: [string, (StyleValue | null)[]][] } {
647
+ const defaults: Record<string, StyleValue> = {};
648
+ const rest: [string, (StyleValue | null)[]][] = [];
649
+ for (const [p, a] of truth) {
650
+ const first = a[0];
651
+ if (declaredProps.has(p)) {
652
+ continue;
653
+ }
654
+ if (n > 0 && first !== null && a.every((v) => v === first)) {
655
+ defaults[p] = first;
656
+ } else {
657
+ rest.push([p, a]);
658
+ }
659
+ }
660
+ return { defaults, rest };
661
+ }
662
+
663
+ /** What the inference of one target collects. */
664
+ interface Inference {
665
+ readonly groups: Draft[];
666
+ readonly mappings: Map<string, Draft>;
667
+ readonly passData: Draft;
668
+ readonly passRaw: Draft;
669
+ readonly copy: Draft;
670
+ readonly opaque: { fam: Family; raw: ReadonlyMap<string, FeatureInfo> | null }[];
671
+ readonly tolerated: Map<string, Set<number>>;
672
+ }
673
+
674
+ /**
675
+ * The cheapest explanation of one family: a discrete or continuous mapping over a feature, a passthrough over existing
676
+ * columns, or the opaque floor.
677
+ * @param plan - the target's state
678
+ * @param fam - the family
679
+ * @param testable - the features worth testing
680
+ * @param infos - every feature
681
+ * @param truth - the values per property
682
+ * @returns the chosen candidate
683
+ */
684
+ function chooseCandidate(
685
+ plan: TargetPlan,
686
+ fam: Family,
687
+ testable: readonly FeatureInfo[],
688
+ infos: readonly FeatureInfo[],
689
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
690
+ ): Candidate {
691
+ const { target, n } = plan;
692
+ const cands: Candidate[] = [];
693
+ // text is per element by nature: a label is never explained by another column's table
694
+ const textual = fam.props.some((p) => STYLE_PROPERTIES[target].get(p) === "text");
695
+ if (!textual) {
696
+ for (const f of testable) {
697
+ const d = discreteCandidate(fam, f, n, plan.spend);
698
+ if (d !== null) {
699
+ cands.push(d);
700
+ }
701
+ const c = continuousCandidate(fam, f, truth, plan.spend);
702
+ if (c !== null) {
703
+ cands.push(c);
704
+ }
705
+ if (plan.budget.exhausted(target)) {
706
+ break;
707
+ }
708
+ }
709
+ }
710
+ const pass = passCandidate(plan, fam, truth, infos);
711
+ if (pass !== null) {
712
+ cands.push(pass);
713
+ }
714
+ cands.push({ type: "opaque", cost: 250 });
715
+ cands.sort((a, b) => a.cost - b.cost || ORDER.indexOf(a.type) - ORDER.indexOf(b.type));
716
+ return cands[0];
717
+ }
718
+
719
+ /**
720
+ * Records a family's chosen explanation in the target's inference.
721
+ * @param plan - the target's state
722
+ * @param st - the inference so far; changed in place
723
+ * @param fam - the family
724
+ * @param c - the chosen candidate
725
+ * @param truth - the values per property
726
+ */
727
+ function applyChoice(
728
+ plan: TargetPlan,
729
+ st: Inference,
730
+ fam: Family,
731
+ c: Candidate,
732
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
733
+ ): void {
734
+ if (c.type === "discrete") {
735
+ applyDiscrete(plan, st, fam, c, truth);
736
+ } else if (c.type === "continuous") {
737
+ applyContinuous(plan, st, fam, c, truth);
738
+ } else if (c.type === "passdata" && !c.raw) {
739
+ for (const p of fam.props) {
740
+ setMapping(st.passData, p, { kind: "passthrough", by: (c.cols.get(p) as FeatureInfo).feature.ref });
741
+ }
742
+ } else {
743
+ // the raw style column recovers nothing: the family joins the opaque pool, where it may become part
744
+ // of a style class, and otherwise reads the raw column
745
+ st.opaque.push({ fam, raw: c.type === "passdata" ? c.cols : null });
746
+ }
747
+ }
748
+
749
+ /**
750
+ * The mapping rule of a feature, made on first use; one rule holds every family the feature explains.
751
+ * @param plan - the target's state
752
+ * @param st - the inference so far; changed in place
753
+ * @param key - the rule's key among the mappings
754
+ * @param id - its id
755
+ * @returns the rule
756
+ */
757
+ function mappingRule(plan: TargetPlan, st: Inference, key: string, id: string): Draft {
758
+ let r = st.mappings.get(key);
759
+ if (r === undefined) {
760
+ r = { id, target: plan.target, role: "mapping", origin: "inferred", where: { all: true }, map: {} };
761
+ st.mappings.set(key, r);
762
+ }
763
+ return r;
764
+ }
765
+
766
+ /**
767
+ * Makes a feature computed for the inference (a style column) a column of the styles.
768
+ * @param plan - the target's state
769
+ * @param info - the feature
770
+ */
771
+ function materialize(plan: TargetPlan, info: FeatureInfo): void {
772
+ if ("style" in info.feature.ref) {
773
+ plan.columns.set(info.feature.ref.style, info.feature.values);
774
+ }
775
+ }
776
+
777
+ /**
778
+ * Records a discrete mapping: one table per property, and a group for the elements the feature gives no value.
779
+ * @param plan - the target's state
780
+ * @param st - the inference so far; changed in place
781
+ * @param fam - the family
782
+ * @param c - the candidate
783
+ * @param truth - the values per property
784
+ */
785
+ function applyDiscrete(
786
+ plan: TargetPlan,
787
+ st: Inference,
788
+ fam: Family,
789
+ c: DiscreteCandidate,
790
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
791
+ ): void {
792
+ const { target } = plan;
793
+ materialize(plan, c.info);
794
+ const rule = mappingRule(plan, st, `discrete|${c.info.name}`, `${target}.by.${c.info.name}`);
795
+ // the first element of each block stands for the block
796
+ const rep = new Map<number, number>();
797
+ for (const i of fam.styled) {
798
+ if (!rep.has(fam.blocks[i])) {
799
+ rep.set(fam.blocks[i], i);
800
+ }
801
+ }
802
+ for (const p of fam.props) {
803
+ const t = truth.get(p) ?? [];
804
+ const table = Array.from(c.table.values(), ({ raw, block }): [string | number | boolean, StyleValue] => [
805
+ raw,
806
+ t[rep.get(block) ?? -1] as StyleValue,
807
+ ]);
808
+ setMapping(rule, p, { kind: "discrete", by: c.info.feature.ref, table });
809
+ }
810
+ if (c.bucket === null) {
811
+ return;
812
+ }
813
+ const set: Record<string, StyleValue> = {};
814
+ for (const p of fam.props) {
815
+ set[p] = (truth.get(p) ?? [])[rep.get(c.bucket) ?? -1] as StyleValue;
816
+ }
817
+ st.groups.push({
818
+ id: `${target}.missing.${c.info.name}`,
819
+ target,
820
+ role: "group",
821
+ origin: "inferred",
822
+ where: { not: { has: c.info.feature.ref } },
823
+ set,
824
+ });
825
+ }
826
+
827
+ /**
828
+ * Records a continuous mapping: one ramp per property; a ramp that matches only within the tool's rounding tolerates
829
+ * those elements, noted.
830
+ * @param plan - the target's state
831
+ * @param st - the inference so far; changed in place
832
+ * @param fam - the family
833
+ * @param c - the candidate
834
+ * @param truth - the values per property
835
+ */
836
+ function applyContinuous(
837
+ plan: TargetPlan,
838
+ st: Inference,
839
+ fam: Family,
840
+ c: ContinuousCandidate,
841
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
842
+ ): void {
843
+ const { target } = plan;
844
+ materialize(plan, c.info);
845
+ const rule = mappingRule(plan, st, `continuous|${c.info.name}`, `${target}.ramp.${c.info.name}`);
846
+ for (const p of fam.props) {
847
+ const points = c.points.get(p) ?? [];
848
+ setMapping(rule, p, { kind: "continuous", by: c.info.feature.ref, points });
849
+ if (!c.tolerant) {
850
+ continue;
851
+ }
852
+ const t = truth.get(p) ?? [];
853
+ const set = new Set<number>();
854
+ for (const i of fam.styled) {
855
+ if (evalContinuous(points, numberOf(c.info.feature.values[i])) !== t[i]) {
856
+ set.add(i);
857
+ plan.notes.add(STYLE_ISSUE.RAMP_ROUNDED, target, p, { element: i, sample: t[i] });
858
+ }
859
+ }
860
+ st.tolerated.set(p, set);
861
+ }
862
+ }
863
+
864
+ /**
865
+ * The opaque pool: style classes when the file uses few combinations, else one copy per property.
866
+ * @param plan - the target's state
867
+ * @param st - the inference so far; changed in place
868
+ * @param budgetHit - how many families the budget sent to the pool
869
+ * @param truth - the values per property
870
+ */
871
+ function poolOpaque(
872
+ plan: TargetPlan,
873
+ st: Inference,
874
+ budgetHit: number,
875
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
876
+ ): void {
877
+ const { target, n } = plan;
878
+ if (st.opaque.length === 0) {
879
+ return;
880
+ }
881
+ const props = st.opaque.flatMap((o) => o.fam.props);
882
+ const classes = budgetHit > 0 ? null : styleClasses(target, props, truth, n, plan.spend);
883
+ const classed = new Set(classes?.classed ?? []);
884
+ if (classes !== null) {
885
+ addClassRules(plan, st, classes);
886
+ }
887
+ let distinct = 0;
888
+ for (const { fam, raw } of st.opaque) {
889
+ for (const p of fam.props.filter((x) => !classed.has(x))) {
890
+ const viaRaw = raw?.get(p);
891
+ if (viaRaw === undefined) {
892
+ distinct = Math.max(distinct, copyProperty(plan, st.copy, p, truth.get(p) ?? []));
893
+ } else {
894
+ setMapping(st.passRaw, p, { kind: "passthrough", by: viaRaw.feature.ref });
895
+ }
896
+ }
897
+ }
898
+ st.copy.facts = { reason: budgetHit > 0 ? "BUDGET" : "NO_EXPLANATION", distinctValues: distinct };
899
+ }
900
+
901
+ /**
902
+ * Adds the style classes' column and rules.
903
+ * @param plan - the target's state
904
+ * @param st - the inference so far; changed in place
905
+ * @param classes - the style classes
906
+ */
907
+ function addClassRules(plan: TargetPlan, st: Inference, classes: NonNullable<ReturnType<typeof styleClasses>>): void {
908
+ plan.columns.set(CLASS_COLUMN, classes.column);
909
+ for (const r of classes.rules) {
910
+ if (r.role === "group") {
911
+ st.groups.push({ ...r, id: r.id, set: { ...r.set } } as Draft);
912
+ } else {
913
+ st.mappings.set(r.id, { ...r, map: { ...r.map } } as Draft);
914
+ }
915
+ }
916
+ }
917
+
918
+ /**
919
+ * Copies a property's values into a style column the copy rule passes through.
920
+ * @param plan - the target's state
921
+ * @param copy - the copy rule, changed in place
922
+ * @param p - the property
923
+ * @param t - its values
924
+ * @returns how many distinct values it holds
925
+ */
926
+ function copyProperty(plan: TargetPlan, copy: Draft, p: string, t: readonly (StyleValue | null)[]): number {
927
+ plan.columns.set(`copy.${p}`, t.slice());
928
+ setMapping(copy, p, { kind: "passthrough", by: { style: `copy.${p}` } });
929
+ plan.spend(plan.n);
930
+ return new Set(t.filter((v) => v !== null)).size;
931
+ }
932
+
933
+ /**
934
+ * The rule as a plain JSON-stable object.
935
+ * @param d - the draft
936
+ * @returns the rule
937
+ */
938
+ function finish(d: Draft): StyleRule {
939
+ const r: Record<string, unknown> = { id: d.id, target: d.target, role: d.role, origin: d.origin, where: d.where };
940
+ if (d.set !== undefined && Object.keys(d.set).length > 0) {
941
+ r.set = d.set;
942
+ }
943
+ if (d.map !== undefined && Object.keys(d.map).length > 0) {
944
+ r.map = d.map;
945
+ }
946
+ if (d.facts !== undefined) {
947
+ r.facts = Object.fromEntries(Object.entries(d.facts).filter(([, v]) => v !== undefined));
948
+ }
949
+ if (d.ext !== undefined) {
950
+ r.ext = d.ext;
951
+ }
952
+ return r as unknown as StyleRule;
953
+ }
954
+
955
+ /**
956
+ * Compares two texts by code unit, the same everywhere.
957
+ * @param a - one text
958
+ * @param b - the other
959
+ * @returns negative, zero or positive
960
+ */
961
+ function compareText(a: string, b: string): number {
962
+ if (a === b) {
963
+ return 0;
964
+ }
965
+ return a < b ? -1 : 1;
966
+ }
967
+
968
+ /**
969
+ * The entries a person would edit in a rule.
970
+ * @param r - the rule
971
+ * @returns its size
972
+ */
973
+ function ruleSize(r: Draft): number {
974
+ let size = Object.keys(r.set ?? {}).length;
975
+ for (const m of Object.values(r.map ?? {})) {
976
+ if (m.kind === "discrete") {
977
+ size += m.table.length;
978
+ } else if (m.kind === "continuous") {
979
+ size += m.points.length;
980
+ } else {
981
+ size += 1;
982
+ }
983
+ }
984
+ return size;
985
+ }
986
+
987
+ /** The order in which rules are folded into the copy: inferred first, the tool's defaults last. */
988
+ const FOLD_ORDER: Readonly<Record<string, number>> = { inferred: 0, declared: 1, tool: 2 };
989
+
990
+ /**
991
+ * Keeps at most 15 rules (the exceptions rule makes 16) by moving the least readable rules into the copy, with each
992
+ * property's values as they resolved: exact, never a silent delete.
993
+ * @param plan - the target's state
994
+ * @param drafts - the rules, bottom to top; changed in place
995
+ * @param truth - the file's values per property
996
+ * @param src - the reference source
997
+ */
998
+ function enforceCap(
999
+ plan: TargetPlan,
1000
+ drafts: Draft[],
1001
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
1002
+ src: ReturnType<typeof refSource>,
1003
+ ): void {
1004
+ const cap = MAX_STYLE_RULES - 1;
1005
+ const copyId = `${plan.target}.copy`;
1006
+ let folded = 0;
1007
+ while (drafts.length > cap) {
1008
+ const cands = drafts.filter((r) => r.id !== copyId);
1009
+ if (cands.length === 0) {
1010
+ break;
1011
+ }
1012
+ cands.sort((a, b) => {
1013
+ const pa = a.role === "passthrough" ? 1 : 0;
1014
+ const pb = b.role === "passthrough" ? 1 : 0;
1015
+ return (
1016
+ pa - pb ||
1017
+ (FOLD_ORDER[a.origin] ?? 1) - (FOLD_ORDER[b.origin] ?? 1) ||
1018
+ ruleSize(b) - ruleSize(a) ||
1019
+ drafts.indexOf(b) - drafts.indexOf(a)
1020
+ );
1021
+ });
1022
+ const victim = cands[0];
1023
+ const before = drafts.map(finish);
1024
+ drafts.splice(drafts.indexOf(victim), 1);
1025
+ let copy = drafts.find((r) => r.id === copyId);
1026
+ if (copy === undefined) {
1027
+ copy = {
1028
+ id: copyId,
1029
+ target: plan.target,
1030
+ role: "passthrough",
1031
+ origin: "inferred",
1032
+ where: { all: true },
1033
+ map: {},
1034
+ };
1035
+ plan.ids.add(copyId);
1036
+ } else {
1037
+ drafts.splice(drafts.indexOf(copy), 1);
1038
+ }
1039
+ for (const p of new Set(writes(victim))) {
1040
+ // the property's values as the whole stack resolved them, supplied on top for every element
1041
+ const t = truth.get(p);
1042
+ const vals = t === undefined ? resolveRules(before, plan.target, p, src, plan.budget) : t.slice();
1043
+ plan.columns.set(`copy.${p}`, vals);
1044
+ setMapping(copy, p, { kind: "passthrough", by: { style: `copy.${p}` } });
1045
+ }
1046
+ copy.facts = { ...copy.facts, reason: "LAYER_CAP" };
1047
+ drafts.push(copy);
1048
+ folded++;
1049
+ }
1050
+ if (folded > 0) {
1051
+ plan.notes.add(STYLE_ISSUE.LAYER_CAP, plan.target, null, { count: folded });
1052
+ }
1053
+ }
1054
+
1055
+ /**
1056
+ * The elements whose resolved value is not the one wanted, with the wanted value; none where nothing is wanted, and
1057
+ * none where a ramp is tolerated within the tool's rounding.
1058
+ * @param n - the element count
1059
+ * @param got - the resolved values
1060
+ * @param want - the value wanted for an element, or null for none
1061
+ * @param tolerated - the elements a ramp matches only within the tool's rounding
1062
+ * @returns the wanted values where they differ, or null when none does
1063
+ */
1064
+ function differences(
1065
+ n: number,
1066
+ got: readonly (StyleValue | null)[],
1067
+ want: (i: number) => StyleValue | null,
1068
+ tolerated: ReadonlySet<number> | undefined,
1069
+ ): (StyleValue | null)[] | null {
1070
+ let col: (StyleValue | null)[] | null = null;
1071
+ for (let i = 0; i < n; i++) {
1072
+ const w = want(i);
1073
+ const differs = w !== null && got[i] !== w && !(tolerated?.has(i) === true && within(got[i], w));
1074
+ if (differs) {
1075
+ col ??= new Array<StyleValue | null>(n).fill(null);
1076
+ col[i] = w;
1077
+ }
1078
+ }
1079
+ return col;
1080
+ }
1081
+
1082
+ /**
1083
+ * The replay: resolves every property the file gives and compares it with the file's value, or the tool's default
1084
+ * where the file gives none. Each difference goes into one sparse exceptions column per property.
1085
+ * @param plan - the target's state
1086
+ * @param drafts - the rules, bottom to top
1087
+ * @param truth - the file's values per property
1088
+ * @param toolSet - the tool's default values, or null
1089
+ * @param tolerated - per property, the elements a ramp matches only within the tool's rounding
1090
+ * @param src - the reference source
1091
+ * @returns the exceptions rule, or null when nothing differs
1092
+ */
1093
+ function replay(
1094
+ plan: TargetPlan,
1095
+ drafts: readonly Draft[],
1096
+ truth: ReadonlyMap<string, readonly (StyleValue | null)[]>,
1097
+ toolSet: Record<string, StyleValue> | null,
1098
+ tolerated: ReadonlyMap<string, ReadonlySet<number>>,
1099
+ src: ReturnType<typeof refSource>,
1100
+ ): Draft | null {
1101
+ const rules = drafts.map(finish);
1102
+ // where the file gives no value, the tool draws its default: its constants, and where a reader adds tool rules
1103
+ // of its own (Graphviz's target arrow on directed edges), what those rules give over the constants
1104
+ const toolRules = rules.filter((r) => r.origin === "tool");
1105
+ const ownToolRules = toolRules.length > (toolSet === null ? 0 : 1);
1106
+ const map: Record<string, Mapping> = {};
1107
+ for (const [p, t] of truth) {
1108
+ const got = resolveRules(rules, plan.target, p, src, plan.budget);
1109
+ const perElement = ownToolRules ? resolveRules(toolRules, plan.target, p, src, plan.budget) : null;
1110
+ const constant = toolSet?.[p] ?? null;
1111
+ const want = (i: number): StyleValue | null => t[i] ?? (perElement === null ? constant : perElement[i]);
1112
+ const col = differences(plan.n, got, want, tolerated.get(p));
1113
+ if (col !== null) {
1114
+ plan.columns.set(`exceptions.${p}`, col);
1115
+ map[p] = { kind: "passthrough", by: { style: `exceptions.${p}` } };
1116
+ }
1117
+ }
1118
+ if (Object.keys(map).length === 0) {
1119
+ return null;
1120
+ }
1121
+ return {
1122
+ id: uniqueId(plan, `${plan.target}.exceptions`),
1123
+ target: plan.target,
1124
+ role: "exceptions",
1125
+ origin: "inferred",
1126
+ where: { all: true },
1127
+ map,
1128
+ };
1129
+ }