@graphty/graph-io 0.0.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -28
  3. package/dist/chunks/children-CL3Cy0ez.js +238 -0
  4. package/dist/chunks/children-CL3Cy0ez.js.map +1 -0
  5. package/dist/chunks/escape-DyI8JofU.js +938 -0
  6. package/dist/chunks/escape-DyI8JofU.js.map +1 -0
  7. package/dist/chunks/importer-CQnJuWJw.js +2987 -0
  8. package/dist/chunks/importer-CQnJuWJw.js.map +1 -0
  9. package/dist/chunks/importer-CpCpfbxr.js +2015 -0
  10. package/dist/chunks/importer-CpCpfbxr.js.map +1 -0
  11. package/dist/chunks/importer-DbnGYr3_.js +2342 -0
  12. package/dist/chunks/importer-DbnGYr3_.js.map +1 -0
  13. package/dist/chunks/importer-GozH8DkN.js +3050 -0
  14. package/dist/chunks/importer-GozH8DkN.js.map +1 -0
  15. package/dist/chunks/records-CGpxszm1.js +605 -0
  16. package/dist/chunks/records-CGpxszm1.js.map +1 -0
  17. package/dist/chunks/text-CajMdVFy.js +189 -0
  18. package/dist/chunks/text-CajMdVFy.js.map +1 -0
  19. package/dist/chunks/writer-DxSKC7TL.js +2842 -0
  20. package/dist/chunks/writer-DxSKC7TL.js.map +1 -0
  21. package/dist/csv.d.ts +1 -0
  22. package/dist/csv.js +1702 -0
  23. package/dist/csv.js.map +1 -0
  24. package/dist/dot.d.ts +1 -0
  25. package/dist/dot.js +8 -0
  26. package/dist/dot.js.map +1 -0
  27. package/dist/gexf.d.ts +1 -0
  28. package/dist/gexf.js +3466 -0
  29. package/dist/gexf.js.map +1 -0
  30. package/dist/gml.d.ts +1 -0
  31. package/dist/gml.js +2647 -0
  32. package/dist/gml.js.map +1 -0
  33. package/dist/graph-io.d.ts +1 -0
  34. package/dist/graph-io.js +790 -0
  35. package/dist/graph-io.js.map +1 -0
  36. package/dist/graphml.d.ts +1 -0
  37. package/dist/graphml.js +8 -0
  38. package/dist/graphml.js.map +1 -0
  39. package/dist/json.d.ts +1 -0
  40. package/dist/json.js +11 -0
  41. package/dist/json.js.map +1 -0
  42. package/dist/neo4j.d.ts +1 -0
  43. package/dist/neo4j.js +2046 -0
  44. package/dist/neo4j.js.map +1 -0
  45. package/dist/pajek.d.ts +1 -0
  46. package/dist/pajek.js +8 -0
  47. package/dist/pajek.js.map +1 -0
  48. package/dist/src/children.d.ts +134 -0
  49. package/dist/src/children.d.ts.map +1 -0
  50. package/dist/src/children.js +274 -0
  51. package/dist/src/children.js.map +1 -0
  52. package/dist/src/common/attributes.d.ts +229 -0
  53. package/dist/src/common/attributes.d.ts.map +1 -0
  54. package/dist/src/common/attributes.js +368 -0
  55. package/dist/src/common/attributes.js.map +1 -0
  56. package/dist/src/common/codes.d.ts +105 -0
  57. package/dist/src/common/codes.d.ts.map +1 -0
  58. package/dist/src/common/codes.js +107 -0
  59. package/dist/src/common/codes.js.map +1 -0
  60. package/dist/src/common/declared-types.d.ts +84 -0
  61. package/dist/src/common/declared-types.d.ts.map +1 -0
  62. package/dist/src/common/declared-types.js +326 -0
  63. package/dist/src/common/declared-types.js.map +1 -0
  64. package/dist/src/common/direction.d.ts +206 -0
  65. package/dist/src/common/direction.d.ts.map +1 -0
  66. package/dist/src/common/direction.js +370 -0
  67. package/dist/src/common/direction.js.map +1 -0
  68. package/dist/src/common/escape.d.ts +92 -0
  69. package/dist/src/common/escape.d.ts.map +1 -0
  70. package/dist/src/common/escape.js +212 -0
  71. package/dist/src/common/escape.js.map +1 -0
  72. package/dist/src/common/export.d.ts +249 -0
  73. package/dist/src/common/export.d.ts.map +1 -0
  74. package/dist/src/common/export.js +594 -0
  75. package/dist/src/common/export.js.map +1 -0
  76. package/dist/src/common/format.d.ts +59 -0
  77. package/dist/src/common/format.d.ts.map +1 -0
  78. package/dist/src/common/format.js +106 -0
  79. package/dist/src/common/format.js.map +1 -0
  80. package/dist/src/common/ids.d.ts +83 -0
  81. package/dist/src/common/ids.d.ts.map +1 -0
  82. package/dist/src/common/ids.js +158 -0
  83. package/dist/src/common/ids.js.map +1 -0
  84. package/dist/src/common/input.d.ts +100 -0
  85. package/dist/src/common/input.d.ts.map +1 -0
  86. package/dist/src/common/input.js +335 -0
  87. package/dist/src/common/input.js.map +1 -0
  88. package/dist/src/common/lists.d.ts +34 -0
  89. package/dist/src/common/lists.d.ts.map +1 -0
  90. package/dist/src/common/lists.js +185 -0
  91. package/dist/src/common/lists.js.map +1 -0
  92. package/dist/src/common/options.d.ts +108 -0
  93. package/dist/src/common/options.d.ts.map +1 -0
  94. package/dist/src/common/options.js +265 -0
  95. package/dist/src/common/options.js.map +1 -0
  96. package/dist/src/common/report.d.ts +187 -0
  97. package/dist/src/common/report.d.ts.map +1 -0
  98. package/dist/src/common/report.js +274 -0
  99. package/dist/src/common/report.js.map +1 -0
  100. package/dist/src/common/temporal.d.ts +71 -0
  101. package/dist/src/common/temporal.d.ts.map +1 -0
  102. package/dist/src/common/temporal.js +266 -0
  103. package/dist/src/common/temporal.js.map +1 -0
  104. package/dist/src/common/text.d.ts +104 -0
  105. package/dist/src/common/text.d.ts.map +1 -0
  106. package/dist/src/common/text.js +255 -0
  107. package/dist/src/common/text.js.map +1 -0
  108. package/dist/src/common/weights.d.ts +77 -0
  109. package/dist/src/common/weights.d.ts.map +1 -0
  110. package/dist/src/common/weights.js +156 -0
  111. package/dist/src/common/weights.js.map +1 -0
  112. package/dist/src/common/writer.d.ts +51 -0
  113. package/dist/src/common/writer.d.ts.map +1 -0
  114. package/dist/src/common/writer.js +108 -0
  115. package/dist/src/common/writer.js.map +1 -0
  116. package/dist/src/common/xml.d.ts +245 -0
  117. package/dist/src/common/xml.d.ts.map +1 -0
  118. package/dist/src/common/xml.js +942 -0
  119. package/dist/src/common/xml.js.map +1 -0
  120. package/dist/src/formats/csv/exporter.d.ts +70 -0
  121. package/dist/src/formats/csv/exporter.d.ts.map +1 -0
  122. package/dist/src/formats/csv/exporter.js +682 -0
  123. package/dist/src/formats/csv/exporter.js.map +1 -0
  124. package/dist/src/formats/csv/header.d.ts +66 -0
  125. package/dist/src/formats/csv/header.d.ts.map +1 -0
  126. package/dist/src/formats/csv/header.js +152 -0
  127. package/dist/src/formats/csv/header.js.map +1 -0
  128. package/dist/src/formats/csv/importer.d.ts +82 -0
  129. package/dist/src/formats/csv/importer.d.ts.map +1 -0
  130. package/dist/src/formats/csv/importer.js +849 -0
  131. package/dist/src/formats/csv/importer.js.map +1 -0
  132. package/dist/src/formats/csv/index.d.ts +60 -0
  133. package/dist/src/formats/csv/index.d.ts.map +1 -0
  134. package/dist/src/formats/csv/index.js +63 -0
  135. package/dist/src/formats/csv/index.js.map +1 -0
  136. package/dist/src/formats/csv/records.d.ts +188 -0
  137. package/dist/src/formats/csv/records.d.ts.map +1 -0
  138. package/dist/src/formats/csv/records.js +702 -0
  139. package/dist/src/formats/csv/records.js.map +1 -0
  140. package/dist/src/formats/csv/values.d.ts +105 -0
  141. package/dist/src/formats/csv/values.d.ts.map +1 -0
  142. package/dist/src/formats/csv/values.js +192 -0
  143. package/dist/src/formats/csv/values.js.map +1 -0
  144. package/dist/src/formats/dot/exporter.d.ts +52 -0
  145. package/dist/src/formats/dot/exporter.d.ts.map +1 -0
  146. package/dist/src/formats/dot/exporter.js +836 -0
  147. package/dist/src/formats/dot/exporter.js.map +1 -0
  148. package/dist/src/formats/dot/importer.d.ts +102 -0
  149. package/dist/src/formats/dot/importer.d.ts.map +1 -0
  150. package/dist/src/formats/dot/importer.js +1291 -0
  151. package/dist/src/formats/dot/importer.js.map +1 -0
  152. package/dist/src/formats/dot/index.d.ts +7 -0
  153. package/dist/src/formats/dot/index.d.ts.map +1 -0
  154. package/dist/src/formats/dot/index.js +7 -0
  155. package/dist/src/formats/dot/index.js.map +1 -0
  156. package/dist/src/formats/dot/names.d.ts +29 -0
  157. package/dist/src/formats/dot/names.d.ts.map +1 -0
  158. package/dist/src/formats/dot/names.js +28 -0
  159. package/dist/src/formats/dot/names.js.map +1 -0
  160. package/dist/src/formats/dot/tokenizer.d.ts +114 -0
  161. package/dist/src/formats/dot/tokenizer.d.ts.map +1 -0
  162. package/dist/src/formats/dot/tokenizer.js +341 -0
  163. package/dist/src/formats/dot/tokenizer.js.map +1 -0
  164. package/dist/src/formats/gexf/exporter.d.ts +56 -0
  165. package/dist/src/formats/gexf/exporter.d.ts.map +1 -0
  166. package/dist/src/formats/gexf/exporter.js +1395 -0
  167. package/dist/src/formats/gexf/exporter.js.map +1 -0
  168. package/dist/src/formats/gexf/importer.d.ts +73 -0
  169. package/dist/src/formats/gexf/importer.d.ts.map +1 -0
  170. package/dist/src/formats/gexf/importer.js +1880 -0
  171. package/dist/src/formats/gexf/importer.js.map +1 -0
  172. package/dist/src/formats/gexf/index.d.ts +96 -0
  173. package/dist/src/formats/gexf/index.d.ts.map +1 -0
  174. package/dist/src/formats/gexf/index.js +97 -0
  175. package/dist/src/formats/gexf/index.js.map +1 -0
  176. package/dist/src/formats/gexf/schema.d.ts +135 -0
  177. package/dist/src/formats/gexf/schema.d.ts.map +1 -0
  178. package/dist/src/formats/gexf/schema.js +323 -0
  179. package/dist/src/formats/gexf/schema.js.map +1 -0
  180. package/dist/src/formats/gml/exporter.d.ts +69 -0
  181. package/dist/src/formats/gml/exporter.d.ts.map +1 -0
  182. package/dist/src/formats/gml/exporter.js +1093 -0
  183. package/dist/src/formats/gml/exporter.js.map +1 -0
  184. package/dist/src/formats/gml/importer.d.ts +66 -0
  185. package/dist/src/formats/gml/importer.d.ts.map +1 -0
  186. package/dist/src/formats/gml/importer.js +1331 -0
  187. package/dist/src/formats/gml/importer.js.map +1 -0
  188. package/dist/src/formats/gml/index.d.ts +85 -0
  189. package/dist/src/formats/gml/index.d.ts.map +1 -0
  190. package/dist/src/formats/gml/index.js +88 -0
  191. package/dist/src/formats/gml/index.js.map +1 -0
  192. package/dist/src/formats/gml/syntax.d.ts +186 -0
  193. package/dist/src/formats/gml/syntax.d.ts.map +1 -0
  194. package/dist/src/formats/gml/syntax.js +467 -0
  195. package/dist/src/formats/gml/syntax.js.map +1 -0
  196. package/dist/src/formats/graphml/constants.d.ts +169 -0
  197. package/dist/src/formats/graphml/constants.d.ts.map +1 -0
  198. package/dist/src/formats/graphml/constants.js +165 -0
  199. package/dist/src/formats/graphml/constants.js.map +1 -0
  200. package/dist/src/formats/graphml/exporter.d.ts +34 -0
  201. package/dist/src/formats/graphml/exporter.d.ts.map +1 -0
  202. package/dist/src/formats/graphml/exporter.js +1176 -0
  203. package/dist/src/formats/graphml/exporter.js.map +1 -0
  204. package/dist/src/formats/graphml/importer.d.ts +31 -0
  205. package/dist/src/formats/graphml/importer.d.ts.map +1 -0
  206. package/dist/src/formats/graphml/importer.js +1607 -0
  207. package/dist/src/formats/graphml/importer.js.map +1 -0
  208. package/dist/src/formats/graphml/index.d.ts +8 -0
  209. package/dist/src/formats/graphml/index.d.ts.map +1 -0
  210. package/dist/src/formats/graphml/index.js +8 -0
  211. package/dist/src/formats/graphml/index.js.map +1 -0
  212. package/dist/src/formats/graphml/tree.d.ts +72 -0
  213. package/dist/src/formats/graphml/tree.d.ts.map +1 -0
  214. package/dist/src/formats/graphml/tree.js +290 -0
  215. package/dist/src/formats/graphml/tree.js.map +1 -0
  216. package/dist/src/formats/json/dialect.d.ts +125 -0
  217. package/dist/src/formats/json/dialect.d.ts.map +1 -0
  218. package/dist/src/formats/json/dialect.js +262 -0
  219. package/dist/src/formats/json/dialect.js.map +1 -0
  220. package/dist/src/formats/json/exporter.d.ts +89 -0
  221. package/dist/src/formats/json/exporter.d.ts.map +1 -0
  222. package/dist/src/formats/json/exporter.js +1358 -0
  223. package/dist/src/formats/json/exporter.js.map +1 -0
  224. package/dist/src/formats/json/importer.d.ts +108 -0
  225. package/dist/src/formats/json/importer.d.ts.map +1 -0
  226. package/dist/src/formats/json/importer.js +1838 -0
  227. package/dist/src/formats/json/importer.js.map +1 -0
  228. package/dist/src/formats/json/index.d.ts +8 -0
  229. package/dist/src/formats/json/index.d.ts.map +1 -0
  230. package/dist/src/formats/json/index.js +8 -0
  231. package/dist/src/formats/json/index.js.map +1 -0
  232. package/dist/src/formats/neo4j/exporter.d.ts +68 -0
  233. package/dist/src/formats/neo4j/exporter.d.ts.map +1 -0
  234. package/dist/src/formats/neo4j/exporter.js +1055 -0
  235. package/dist/src/formats/neo4j/exporter.js.map +1 -0
  236. package/dist/src/formats/neo4j/header.d.ts +52 -0
  237. package/dist/src/formats/neo4j/header.d.ts.map +1 -0
  238. package/dist/src/formats/neo4j/header.js +131 -0
  239. package/dist/src/formats/neo4j/header.js.map +1 -0
  240. package/dist/src/formats/neo4j/importer.d.ts +73 -0
  241. package/dist/src/formats/neo4j/importer.d.ts.map +1 -0
  242. package/dist/src/formats/neo4j/importer.js +932 -0
  243. package/dist/src/formats/neo4j/importer.js.map +1 -0
  244. package/dist/src/formats/neo4j/index.d.ts +79 -0
  245. package/dist/src/formats/neo4j/index.d.ts.map +1 -0
  246. package/dist/src/formats/neo4j/index.js +83 -0
  247. package/dist/src/formats/neo4j/index.js.map +1 -0
  248. package/dist/src/formats/pajek/exporter.d.ts +58 -0
  249. package/dist/src/formats/pajek/exporter.d.ts.map +1 -0
  250. package/dist/src/formats/pajek/exporter.js +825 -0
  251. package/dist/src/formats/pajek/exporter.js.map +1 -0
  252. package/dist/src/formats/pajek/importer.d.ts +88 -0
  253. package/dist/src/formats/pajek/importer.d.ts.map +1 -0
  254. package/dist/src/formats/pajek/importer.js +1047 -0
  255. package/dist/src/formats/pajek/importer.js.map +1 -0
  256. package/dist/src/formats/pajek/index.d.ts +7 -0
  257. package/dist/src/formats/pajek/index.d.ts.map +1 -0
  258. package/dist/src/formats/pajek/index.js +7 -0
  259. package/dist/src/formats/pajek/index.js.map +1 -0
  260. package/dist/src/formats/pajek/syntax.d.ts +112 -0
  261. package/dist/src/formats/pajek/syntax.d.ts.map +1 -0
  262. package/dist/src/formats/pajek/syntax.js +269 -0
  263. package/dist/src/formats/pajek/syntax.js.map +1 -0
  264. package/dist/src/index.d.ts +35 -0
  265. package/dist/src/index.d.ts.map +1 -0
  266. package/dist/src/index.js +39 -0
  267. package/dist/src/index.js.map +1 -0
  268. package/dist/src/registry.d.ts +207 -0
  269. package/dist/src/registry.d.ts.map +1 -0
  270. package/dist/src/registry.js +481 -0
  271. package/dist/src/registry.js.map +1 -0
  272. package/dist/src/sniff.d.ts +104 -0
  273. package/dist/src/sniff.d.ts.map +1 -0
  274. package/dist/src/sniff.js +357 -0
  275. package/dist/src/sniff.js.map +1 -0
  276. package/dist/src/types.d.ts +238 -0
  277. package/dist/src/types.d.ts.map +1 -0
  278. package/dist/src/types.js +29 -0
  279. package/dist/src/types.js.map +1 -0
  280. package/dist/tsconfig.build.tsbuildinfo +1 -0
  281. package/package.json +122 -7
  282. package/src/children.ts +335 -0
  283. package/src/common/attributes.ts +520 -0
  284. package/src/common/codes.ts +153 -0
  285. package/src/common/declared-types.ts +374 -0
  286. package/src/common/direction.ts +518 -0
  287. package/src/common/escape.ts +231 -0
  288. package/src/common/export.ts +817 -0
  289. package/src/common/format.ts +111 -0
  290. package/src/common/ids.ts +176 -0
  291. package/src/common/input.ts +378 -0
  292. package/src/common/lists.ts +196 -0
  293. package/src/common/options.ts +377 -0
  294. package/src/common/report.ts +352 -0
  295. package/src/common/temporal.ts +302 -0
  296. package/src/common/text.ts +294 -0
  297. package/src/common/weights.ts +202 -0
  298. package/src/common/writer.ts +123 -0
  299. package/src/common/xml.ts +1053 -0
  300. package/src/formats/csv/exporter.ts +894 -0
  301. package/src/formats/csv/header.ts +172 -0
  302. package/src/formats/csv/importer.ts +1104 -0
  303. package/src/formats/csv/index.ts +88 -0
  304. package/src/formats/csv/records.ts +813 -0
  305. package/src/formats/csv/values.ts +224 -0
  306. package/src/formats/dot/exporter.ts +1014 -0
  307. package/src/formats/dot/importer.ts +1549 -0
  308. package/src/formats/dot/index.ts +7 -0
  309. package/src/formats/dot/names.ts +40 -0
  310. package/src/formats/dot/tokenizer.ts +384 -0
  311. package/src/formats/gexf/exporter.ts +1696 -0
  312. package/src/formats/gexf/importer.ts +2333 -0
  313. package/src/formats/gexf/index.ts +142 -0
  314. package/src/formats/gexf/schema.ts +361 -0
  315. package/src/formats/gml/exporter.ts +1404 -0
  316. package/src/formats/gml/importer.ts +1591 -0
  317. package/src/formats/gml/index.ts +128 -0
  318. package/src/formats/gml/syntax.ts +545 -0
  319. package/src/formats/graphml/constants.ts +225 -0
  320. package/src/formats/graphml/exporter.ts +1458 -0
  321. package/src/formats/graphml/importer.ts +2027 -0
  322. package/src/formats/graphml/index.ts +8 -0
  323. package/src/formats/graphml/tree.ts +318 -0
  324. package/src/formats/json/dialect.ts +317 -0
  325. package/src/formats/json/exporter.ts +1616 -0
  326. package/src/formats/json/importer.ts +2271 -0
  327. package/src/formats/json/index.ts +8 -0
  328. package/src/formats/neo4j/exporter.ts +1287 -0
  329. package/src/formats/neo4j/header.ts +156 -0
  330. package/src/formats/neo4j/importer.ts +1220 -0
  331. package/src/formats/neo4j/index.ts +116 -0
  332. package/src/formats/pajek/exporter.ts +1000 -0
  333. package/src/formats/pajek/importer.ts +1311 -0
  334. package/src/formats/pajek/index.ts +7 -0
  335. package/src/formats/pajek/syntax.ts +307 -0
  336. package/src/index.ts +244 -0
  337. package/src/registry.ts +617 -0
  338. package/src/sniff.ts +397 -0
  339. package/src/types.ts +262 -0
@@ -0,0 +1,520 @@
1
+ /**
2
+ * Attribute declaration shared by the typed importers (design sections 5.1, 5.5 and 5.6): turn a
3
+ * GEXF `<attribute>`, a GraphML `<key>` or a Neo4j header column into a ColumnDecl with the right
4
+ * dtype, default, options and origin metadata; parse values (scalars and lists) by the declaration;
5
+ * derive the deterministic `<name>#<origin.id>` name when a declared name collides; and the dict
6
+ * heuristic of design section 5.4.
7
+ *
8
+ * Naming rule (design section 5.6): a column's name is the GEXF `title` / GraphML `attr.name`,
9
+ * falling back to the attribute `id` when the title is absent; `origin.id` always keeps the source
10
+ * id and `origin.title` the title when it differs from the resolved name.
11
+ */
12
+
13
+ import {
14
+ type ColumnDecl,
15
+ type ColumnHandle,
16
+ type ColumnRole,
17
+ GraphFormatError,
18
+ type GraphSink,
19
+ INVALID_INDEX,
20
+ } from "@graphty/graph-format";
21
+
22
+ import { type IssueCategory } from "../types.js";
23
+ import {
24
+ BAD_DEFAULT_CODE,
25
+ BAD_OPTIONS_CODE,
26
+ COLUMN_RENAMED_CODE,
27
+ PRECISION_CODE,
28
+ ROLE_TAKEN_CODE,
29
+ UNKNOWN_ATTR_TYPE_CODE,
30
+ } from "./codes.js";
31
+ import {
32
+ type DeclaredTypeSpec,
33
+ type DeclaringFormat,
34
+ mapDeclaredType,
35
+ parseScalarText,
36
+ stringSpec,
37
+ } from "./declared-types.js";
38
+ import { type ListSyntax, splitListText } from "./lists.js";
39
+ import { type ImportReportBuilder, type IssueLocation } from "./report.js";
40
+ import { parseTemporal, type TemporalValue, timeTextCompanion } from "./temporal.js";
41
+
42
+ /**
43
+ * What an importer knows about a declared attribute.
44
+ * Consumed by the per-format importers and exporters under src/formats.
45
+ * @public
46
+ */
47
+ export interface AttributeDeclarationInput {
48
+ /** The declaring format. */
49
+ readonly format: DeclaringFormat;
50
+ /** The attribute id (GEXF `id`, GraphML key `id`, Neo4j header name); null when the format has none. */
51
+ readonly id: string | null;
52
+ /** The GEXF `title` / GraphML `attr.name`; null when absent (yFiles keys omit it). */
53
+ readonly title: string | null;
54
+ /** The declared type text; null for an untyped declaration (stored as string). */
55
+ readonly type: string | null;
56
+ /** The declaring namespace ("viz", "yfiles", "neo4j"), or null. */
57
+ readonly namespace?: string | null | undefined;
58
+ /** The `<default>` text, or null. */
59
+ readonly defaultText?: string | null | undefined;
60
+ /** The GEXF `<options>` text, or null. */
61
+ readonly optionsText?: string | null | undefined;
62
+ /** The list syntax of defaults, options and values; "gexf" by default. */
63
+ readonly listSyntax?: ListSyntax | undefined;
64
+ /** A role to give the column, or null. */
65
+ readonly role?: ColumnRole | null | undefined;
66
+ /** Whether the attribute is dynamic (GEXF mode="dynamic"). */
67
+ readonly dynamic?: boolean | undefined;
68
+ /** The importer's `long` option. */
69
+ readonly long: "f64" | "string";
70
+ /** Whether a column name is already taken in the target table; drives the `#id` rename. */
71
+ readonly taken?: ((name: string) => boolean) | undefined;
72
+ }
73
+
74
+ /**
75
+ * A non-fatal problem with a declaration, for the importer to record as a warning.
76
+ * Consumed by the per-format importers and exporters under src/formats.
77
+ * @public
78
+ */
79
+ export interface DeclarationIssue {
80
+ /** The issue category. */
81
+ readonly category: IssueCategory;
82
+ /** The stable code. */
83
+ readonly code: string;
84
+ /** A plain-ASCII message. */
85
+ readonly message: string;
86
+ }
87
+
88
+ /**
89
+ * A declared attribute: the ColumnDecl to push, the spec to parse values with, and what to report.
90
+ * Consumed by the per-format importers and exporters under src/formats.
91
+ * @public
92
+ */
93
+ export interface DeclaredAttribute {
94
+ /** The declaration for declareNodeColumn / declareEdgeColumn. */
95
+ readonly decl: ColumnDecl;
96
+ /** How values are parsed. */
97
+ readonly spec: DeclaredTypeSpec;
98
+ /** The list syntax values are split with. */
99
+ readonly listSyntax: ListSyntax;
100
+ /** The companion text column of a temporal attribute (design section 5.1), or null. */
101
+ readonly companion: ColumnDecl | null;
102
+ /** Whether the name received the `#id` suffix. */
103
+ readonly renamed: boolean;
104
+ /** Problems the importer records as warnings (unknown type, unparsable default or options). */
105
+ readonly issues: readonly DeclarationIssue[];
106
+ }
107
+
108
+ /** Issue code: the declared type is not one the format defines; the column is kept as string. */
109
+ export const UNKNOWN_TYPE_CODE = UNKNOWN_ATTR_TYPE_CODE;
110
+ /** Issue code: the column was renamed `<name>#<id>` because the name was taken (design section 5.6). */
111
+ export const RENAMED_CODE = COLUMN_RENAMED_CODE;
112
+
113
+ export { BAD_DEFAULT_CODE, BAD_OPTIONS_CODE, PRECISION_CODE, ROLE_TAKEN_CODE };
114
+
115
+ /** Rows sampled before the dict heuristic decides (design section 5.4). */
116
+ export const DICT_SAMPLE_ROWS = 1024;
117
+
118
+ /**
119
+ * Build the column declaration of a declared attribute.
120
+ * @param input - what the file declares
121
+ * @returns the declaration, the value spec and any warnings to record
122
+ */
123
+ export function declareAttribute(input: AttributeDeclarationInput): DeclaredAttribute {
124
+ const issues: DeclarationIssue[] = [];
125
+ const listSyntax = input.listSyntax ?? "gexf";
126
+ let spec: DeclaredTypeSpec;
127
+ if (input.type === null) {
128
+ spec = stringSpec(null);
129
+ } else {
130
+ const mapped = mapDeclaredType(input.format, input.type, input.long);
131
+ if (mapped === null) {
132
+ spec = stringSpec(input.type);
133
+ issues.push({
134
+ category: "unsupported",
135
+ code: UNKNOWN_TYPE_CODE,
136
+ message: `${input.format} type "${input.type}" is not supported; values kept as text`,
137
+ });
138
+ } else {
139
+ spec = mapped;
140
+ }
141
+ }
142
+ const { name, renamed } = resolveName(input);
143
+ if (renamed) {
144
+ issues.push({
145
+ category: "coercion",
146
+ code: RENAMED_CODE,
147
+ message: `attribute "${input.title ?? input.id ?? ""}" renamed to "${name}": the name was taken`,
148
+ });
149
+ }
150
+ const decl: ColumnDecl = {
151
+ name,
152
+ dtype: spec.dtype,
153
+ nullable: true,
154
+ origin: {
155
+ format: input.format,
156
+ id: input.id,
157
+ title: input.title !== null && input.title !== name ? input.title : null,
158
+ type: input.type,
159
+ namespace: input.namespace ?? null,
160
+ },
161
+ };
162
+ if (spec.list) {
163
+ decl.itemDtype = spec.itemDtype ?? "string";
164
+ }
165
+ if (input.role !== undefined && input.role !== null) {
166
+ decl.role = input.role;
167
+ }
168
+ if (input.dynamic === true) {
169
+ decl.dynamic = true;
170
+ }
171
+ const optionsText = input.optionsText ?? null;
172
+ if (optionsText !== null) {
173
+ try {
174
+ const options = splitListText(optionsText, listSyntax).map((item) => parseItem(item, spec));
175
+ decl.options = options;
176
+ if (spec.dtype === "string" && !spec.list && options.every((o) => typeof o === "string")) {
177
+ decl.dtype = "dict";
178
+ spec = { ...spec, dtype: "dict" };
179
+ }
180
+ } catch (err) {
181
+ issues.push({
182
+ category: "validation-error",
183
+ code: BAD_OPTIONS_CODE,
184
+ message: `options "${optionsText}" of attribute "${name}" do not parse as ${spec.declared}: ${messageOf(err)}`,
185
+ });
186
+ }
187
+ }
188
+ const defaultText = input.defaultText ?? null;
189
+ if (defaultText !== null) {
190
+ try {
191
+ decl.default = parseDeclaredValue(defaultText, spec, listSyntax);
192
+ } catch (err) {
193
+ issues.push({
194
+ category: "validation-error",
195
+ code: BAD_DEFAULT_CODE,
196
+ message: `default "${defaultText}" of attribute "${name}" does not parse as ${spec.declared}: ${messageOf(err)}`,
197
+ });
198
+ }
199
+ }
200
+ const companion = spec.temporal !== null && !spec.list ? timeTextCompanion(name) : null;
201
+ return { decl, spec, listSyntax, companion, renamed, issues };
202
+ }
203
+
204
+ /**
205
+ * Parse a value text by a declaration: a scalar, or a list split by the syntax with each item
206
+ * parsed by the item kind.
207
+ * @param text - the value text
208
+ * @param spec - the declared type
209
+ * @param listSyntax - the list syntax
210
+ * @returns the value to push (a boolean, a number, a string, a JSON value or an array)
211
+ */
212
+ export function parseDeclaredValue(text: string, spec: DeclaredTypeSpec, listSyntax: ListSyntax): unknown {
213
+ if (spec.list) {
214
+ return splitListText(text, listSyntax).map((item) => parseItem(item, spec));
215
+ }
216
+ return parseItem(text, spec);
217
+ }
218
+
219
+ /**
220
+ * Parse a temporal value text by a declaration, keeping the source text when its canonical form
221
+ * differs (for the companion column).
222
+ * @param text - the value text
223
+ * @param spec - a scalar temporal spec
224
+ * @returns the value and the text to keep, or null text
225
+ */
226
+ export function parseDeclaredTemporal(text: string, spec: DeclaredTypeSpec): TemporalValue {
227
+ if (spec.temporal === null || spec.list) {
228
+ throw new GraphFormatError("E_COLUMN_TYPE", `"${spec.declared}" is not a scalar temporal type`, {
229
+ declared: spec.declared,
230
+ });
231
+ }
232
+ return parseTemporal(text.trim(), spec.temporal);
233
+ }
234
+
235
+ const INTEGER_TEXT = /^[+-]?[0-9]+$/;
236
+
237
+ /**
238
+ * Whether a long value's text lost precision when parsed to f64 (design section 5.1: a value with
239
+ * |v| > 2^53 is stored as the nearest f64 and recorded as a `precision` issue). Decided from the
240
+ * text, since the parsed number alone cannot tell 2^53 from 2^53 + 1.
241
+ * @param spec - the declared type
242
+ * @param text - the value text (trimmed or not)
243
+ * @returns true when the importer should record a `precision` issue
244
+ */
245
+ export function losesPrecision(spec: DeclaredTypeSpec, text: string): boolean {
246
+ if (!spec.precision) {
247
+ return false;
248
+ }
249
+ const trimmed = text.trim();
250
+ if (!INTEGER_TEXT.test(trimmed)) {
251
+ return false;
252
+ }
253
+ const n = Number(trimmed);
254
+ return !Number.isFinite(n) || BigInt(trimmed) !== BigInt(n);
255
+ }
256
+
257
+ /**
258
+ * Declare a column on the sink for one domain.
259
+ * @param sink - the sink
260
+ * @param domain - node or edge
261
+ * @param decl - the declaration
262
+ * @returns the column handle
263
+ */
264
+ export function declareOn(sink: GraphSink, domain: "node" | "edge", decl: ColumnDecl): ColumnHandle {
265
+ return domain === "node" ? sink.declareNodeColumn(decl) : sink.declareEdgeColumn(decl);
266
+ }
267
+
268
+ /**
269
+ * The outcome of declareResolved(): the handle and the declaration as it was applied.
270
+ * Consumed by the per-format importers under src/formats.
271
+ * @public
272
+ */
273
+ export interface ResolvedDeclaration {
274
+ /** The column handle. */
275
+ readonly handle: ColumnHandle;
276
+ /** The declaration as applied (its name and role may differ from the request). */
277
+ readonly decl: ColumnDecl;
278
+ /** Whether the column was renamed `<name>#<id>` (W_COLUMN_RENAMED recorded). */
279
+ readonly renamed: boolean;
280
+ /** Whether the role was dropped because the table already holds it (W_ROLE_TAKEN recorded). */
281
+ readonly roleDropped: boolean;
282
+ }
283
+
284
+ /**
285
+ * The io rule of design section 5.6 in one place, so every importer agrees: declare a column on
286
+ * the sink and, when the sink already holds the name with another shape (an earlier import, a
287
+ * caller's column), rename it `<name>#<origin.id>` (a counter when the source has no id) and
288
+ * record W_COLUMN_RENAMED (coercion); when the table already holds the role (design section 5.5:
289
+ * at most one column per role), declare without the role and record W_ROLE_TAKEN (coercion). The
290
+ * same shape under the same name returns the existing handle, as the sink does.
291
+ * @param sink - the sink
292
+ * @param domain - node or edge
293
+ * @param decl - the wanted declaration
294
+ * @param report - the report the coercion issues are recorded in
295
+ * @param where - the line and element, when known
296
+ * @returns the handle and the declaration as applied
297
+ */
298
+ export function declareResolved(
299
+ sink: GraphSink,
300
+ domain: "node" | "edge",
301
+ decl: ColumnDecl,
302
+ report: ImportReportBuilder,
303
+ where?: IssueLocation,
304
+ ): ResolvedDeclaration {
305
+ let current = decl;
306
+ let renamed = false;
307
+ let roleDropped = false;
308
+ for (;;) {
309
+ try {
310
+ return { handle: declareOn(sink, domain, current), decl: current, renamed, roleDropped };
311
+ } catch (err) {
312
+ if (!(err instanceof GraphFormatError)) {
313
+ throw err;
314
+ }
315
+ if (err.code === "E_COLUMN_EXISTS" && !renamed) {
316
+ const taken = takenIn(sink, domain);
317
+ const id = current.origin?.id ?? null;
318
+ const name = uniqueColumnName(current.name, id, taken);
319
+ report.warning(
320
+ "coercion",
321
+ COLUMN_RENAMED_CODE,
322
+ `${domain} column "${current.name}" renamed to "${name}": the sink already holds that name with another shape`,
323
+ { ...where, element: where?.element ?? current.name },
324
+ );
325
+ current = { ...current, name };
326
+ renamed = true;
327
+ continue;
328
+ }
329
+ if (err.code === "E_DUPLICATE_ROLE" && current.role !== undefined && !roleDropped) {
330
+ const holder = typeof err.details.holder === "string" ? err.details.holder : "another column";
331
+ report.warning(
332
+ "coercion",
333
+ ROLE_TAKEN_CODE,
334
+ `${domain} column "${current.name}" loses the ${current.role} role: "${holder}" already holds it`,
335
+ { ...where, element: where?.element ?? current.name },
336
+ );
337
+ const { role: _role, ...withoutRole } = current;
338
+ current = withoutRole;
339
+ roleDropped = true;
340
+ continue;
341
+ }
342
+ throw err;
343
+ }
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Declare the companion text column of a temporal column (design section 5.1). A table holds at
349
+ * most one column per role, so when another companion already carries `timeText` this one is
350
+ * declared without the role (reported once per table); exporters find every companion through
351
+ * `extra.for`.
352
+ * @param sink - the sink
353
+ * @param domain - node or edge
354
+ * @param companion - the declaration from declareAttribute()
355
+ * @param report - the report the role loss is recorded in; when omitted the loss is silent
356
+ * @returns the column handle
357
+ */
358
+ export function declareCompanion(
359
+ sink: GraphSink,
360
+ domain: "node" | "edge",
361
+ companion: ColumnDecl,
362
+ report?: ImportReportBuilder,
363
+ ): ColumnHandle {
364
+ try {
365
+ return declareOn(sink, domain, companion);
366
+ } catch (err) {
367
+ if (err instanceof GraphFormatError && err.code === "E_DUPLICATE_ROLE") {
368
+ report?.warnOnce(
369
+ "coercion",
370
+ ROLE_TAKEN_CODE,
371
+ `${domain} column "${companion.name}" is declared without the timeText role: another companion holds it (exporters use extra.for)`,
372
+ { element: companion.name },
373
+ );
374
+ const { role: _role, ...withoutRole } = companion;
375
+ return declareOn(sink, domain, withoutRole);
376
+ }
377
+ throw err;
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Whether a column name is taken in a sink's table, the usual `taken` callback.
383
+ * @param sink - the sink
384
+ * @param domain - node or edge
385
+ * @returns a predicate over column names
386
+ */
387
+ export function takenIn(sink: GraphSink, domain: "node" | "edge"): (name: string) => boolean {
388
+ return (name: string): boolean =>
389
+ (domain === "node" ? sink.nodeColumn(name) : sink.edgeColumn(name)) !== INVALID_INDEX;
390
+ }
391
+
392
+ /**
393
+ * The deterministic name of a declared attribute whose preferred name is taken (design section
394
+ * 5.6): `<name>#<origin.id>`, and `<name>#<origin.id>#2`, `#3`... should that be taken as well.
395
+ * @param name - the preferred name
396
+ * @param id - the source id (the attribute id or key id); null falls back to a counter
397
+ * @param taken - whether a name is already used in the table
398
+ * @returns a free name
399
+ */
400
+ export function uniqueColumnName(name: string, id: string | null, taken: (name: string) => boolean): string {
401
+ if (!taken(name)) {
402
+ return name;
403
+ }
404
+ const base = id === null ? name : `${name}#${id}`;
405
+ if (id !== null && !taken(base)) {
406
+ return base;
407
+ }
408
+ for (let n = 2; ; n++) {
409
+ const candidate = `${base}#${n}`;
410
+ if (!taken(candidate)) {
411
+ return candidate;
412
+ }
413
+ }
414
+ }
415
+
416
+ /**
417
+ * The dict heuristic of design section 5.4 for an untyped string column: observe values until the
418
+ * sample is full; the column is a dict when the distinct count stays below half the rows, a string
419
+ * otherwise. The importer buffers the sampled values and declares the column on the decision.
420
+ */
421
+ export class DictHeuristic {
422
+ private readonly distinct = new Set<string>();
423
+
424
+ private rowsSeen = 0;
425
+
426
+ private readonly sampleRows: number;
427
+
428
+ /**
429
+ * Create a heuristic.
430
+ * @param sampleRows - rows observed before deciding; DICT_SAMPLE_ROWS by default
431
+ */
432
+ constructor(sampleRows: number = DICT_SAMPLE_ROWS) {
433
+ this.sampleRows = sampleRows;
434
+ }
435
+
436
+ /**
437
+ * Rows observed so far.
438
+ * @returns the count
439
+ */
440
+ get rows(): number {
441
+ return this.rowsSeen;
442
+ }
443
+
444
+ /**
445
+ * Distinct values observed so far.
446
+ * @returns the count
447
+ */
448
+ get distinctCount(): number {
449
+ return this.distinct.size;
450
+ }
451
+
452
+ /**
453
+ * Whether the sample is full.
454
+ * @returns true once sampleRows values were observed
455
+ */
456
+ get decided(): boolean {
457
+ return this.rowsSeen >= this.sampleRows;
458
+ }
459
+
460
+ /**
461
+ * Observe one value.
462
+ * @param value - the value text
463
+ * @returns true when this observation completed the sample
464
+ */
465
+ observe(value: string): boolean {
466
+ this.rowsSeen++;
467
+ if (this.distinct.size < this.rowsSeen) {
468
+ this.distinct.add(value);
469
+ }
470
+ return this.rowsSeen === this.sampleRows;
471
+ }
472
+
473
+ /**
474
+ * The dtype the sample suggests: dict when the distinct count is below rows / 2 (and at least
475
+ * one row was seen), string otherwise. Valid before the sample is full (end of input).
476
+ * @returns "dict" or "string"
477
+ */
478
+ decide(): "dict" | "string" {
479
+ return this.rowsSeen > 0 && this.distinct.size * 2 < this.rowsSeen ? "dict" : "string";
480
+ }
481
+ }
482
+
483
+ /**
484
+ * The resolved name of a declaration and whether it was renamed.
485
+ * @param input - the declaration input
486
+ * @returns the name and the rename flag; E_COLUMN_TYPE when neither a title nor an id exists
487
+ */
488
+ function resolveName(input: AttributeDeclarationInput): { name: string; renamed: boolean } {
489
+ const preferred = input.title !== null && input.title.length > 0 ? input.title : input.id;
490
+ if (preferred === null || preferred.length === 0) {
491
+ throw new GraphFormatError("E_COLUMN_TYPE", "an attribute declaration needs a title or an id", {
492
+ field: "name",
493
+ format: input.format,
494
+ });
495
+ }
496
+ if (input.taken === undefined) {
497
+ return { name: preferred, renamed: false };
498
+ }
499
+ const name = uniqueColumnName(preferred, input.id, input.taken);
500
+ return { name, renamed: name !== preferred };
501
+ }
502
+
503
+ /**
504
+ * Parse one scalar value or list item by a spec.
505
+ * @param text - the item text
506
+ * @param spec - the declared type
507
+ * @returns the parsed value
508
+ */
509
+ function parseItem(text: string, spec: DeclaredTypeSpec): unknown {
510
+ return parseScalarText(text, spec.kind, spec.temporal);
511
+ }
512
+
513
+ /**
514
+ * The message of a caught value.
515
+ * @param err - the thrown value
516
+ * @returns the message
517
+ */
518
+ function messageOf(err: unknown): string {
519
+ return err instanceof Error ? err.message : String(err);
520
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * The issue and loss codes shared by more than one format (design section 8.6: "a stable code
3
+ * such as E_UNKNOWN_NODE or W_WIDENED"): one constant per condition, aliased by every format's
4
+ * `<FMT>_ISSUE` / `<FMT>_LOSS` table, so a consumer can switch on a condition without knowing the
5
+ * format and two importers never spell the same condition differently. A condition specific to one
6
+ * format keeps that format's own `E_<FMT>_` / `W_<FMT>_` code next to its importer or exporter.
7
+ *
8
+ * Naming scheme: `E_` for an issue recorded with severity "error" (or a loss note whose export()
9
+ * throws), `W_` for a warning; a table key is the code without the severity prefix and without the
10
+ * format prefix.
11
+ */
12
+
13
+ // ============================================================ importer issues
14
+
15
+ /** The element (node, attribute, key, ...) has no id where the format requires one. */
16
+ export const MISSING_ID_CODE = "E_MISSING_ID";
17
+
18
+ /** An edge has no source or no target. */
19
+ export const MISSING_ENDPOINT_CODE = "E_MISSING_ENDPOINT";
20
+
21
+ /** A node id declared twice; the second declaration merges into the first. */
22
+ export const DUPLICATE_NODE_CODE = "W_DUPLICATE_NODE";
23
+
24
+ /** The document declares no graph at all (fatal). */
25
+ export const NO_GRAPH_CODE = "E_NO_GRAPH";
26
+
27
+ /** A second graph in one document is merged into the first. */
28
+ export const MULTIPLE_GRAPHS_CODE = "W_MULTIPLE_GRAPHS";
29
+
30
+ /** The input is empty (fatal). */
31
+ export const EMPTY_INPUT_CODE = "E_EMPTY_INPUT";
32
+
33
+ /** The text grammar of the format is violated (fatal; the message carries the detail). */
34
+ export const SYNTAX_CODE = "E_SYNTAX";
35
+
36
+ /** The XML is not well-formed (fatal; the message carries the detail and the line). */
37
+ export const XML_SYNTAX_CODE = "E_XML_SYNTAX";
38
+
39
+ /** An element the format does not define at that place was skipped. */
40
+ export const UNKNOWN_ELEMENT_CODE = "W_UNKNOWN_ELEMENT";
41
+
42
+ /** Text where the format allows only elements was ignored. */
43
+ export const STRAY_TEXT_CODE = "W_STRAY_TEXT";
44
+
45
+ /** A `pid` / parent reference names a node the document never declares. */
46
+ export const UNKNOWN_PARENT_CODE = "E_UNKNOWN_PARENT";
47
+
48
+ /** A hyperedge under the `hyperedges: "error"` policy (fatal). */
49
+ export const HYPEREDGE_CODE = "E_HYPEREDGE";
50
+
51
+ /** A declaration's key / attribute id is declared twice. */
52
+ export const DUPLICATE_KEY_CODE = "E_DUPLICATE_KEY";
53
+
54
+ /**
55
+ * A column was renamed `<name>#<origin.id>` because the name was taken in the sink's table
56
+ * (design section 5.6).
57
+ */
58
+ export const COLUMN_RENAMED_CODE = "W_COLUMN_RENAMED";
59
+
60
+ /** A column's role was dropped because another column of the table already holds it (design section 5.5). */
61
+ export const ROLE_TAKEN_CODE = "W_ROLE_TAKEN";
62
+
63
+ /** A long value beyond 2^53 was stored as the nearest f64 (design section 5.1). */
64
+ export const PRECISION_CODE = "W_PRECISION";
65
+
66
+ /** Two distinct id texts became one number under ids "number" (design section 4.1). */
67
+ export const ID_MERGED_CODE = "W_ID_MERGED";
68
+
69
+ /** A builder-policy option the caller asked for that the sink does not honour (design section 8.4). */
70
+ export const SINK_OPTION_CODE = "W_SINK_OPTION";
71
+
72
+ /** A common option the format has no use for (or cannot honour) was given a non-default value (design section 8.4). */
73
+ export const OPTION_IGNORED_CODE = "W_OPTION_IGNORED";
74
+
75
+ /** An invalid UTF-8 sequence in the input (fatal). */
76
+ export const INVALID_UTF8_CODE = "E_INVALID_UTF8";
77
+
78
+ /** The sink refused the file's direction (locked or non-empty); the file is read as the sink's. */
79
+ export const DIRECTION_REFUSED_CODE = "W_DIRECTION_REFUSED";
80
+
81
+ /** Edges of the other direction were forced to the policy's direction. */
82
+ export const DIRECTION_FORCED_CODE = "W_DIRECTION_FORCED";
83
+
84
+ /** A mixed-direction file under onMixedDirection "error" (import), or a mixed snapshot under the same export policy. */
85
+ export const MIXED_DIRECTION_CODE = "E_MIXED_DIRECTION";
86
+
87
+ /** A `count` / `parse.nodes` hint the sink cannot reserve (ignored). */
88
+ export const COUNT_HINT_CODE = "W_COUNT_HINT";
89
+
90
+ /** The declared type is not one the format defines; the column is kept as string. */
91
+ export const UNKNOWN_ATTR_TYPE_CODE = "W_UNKNOWN_ATTR_TYPE";
92
+
93
+ /** The declared default does not parse as the declared type; the column has no default. */
94
+ export const BAD_DEFAULT_CODE = "W_BAD_DEFAULT";
95
+
96
+ /** A declared option does not parse as the declared type; the options are dropped. */
97
+ export const BAD_OPTIONS_CODE = "W_BAD_OPTIONS";
98
+
99
+ /** A repeated edge id in a format whose edge ids are unique; the second edge is skipped. */
100
+ export const DUPLICATE_EDGE_ID_CODE = "E_DUPLICATE_EDGE_ID";
101
+
102
+ // ============================================================ exporter loss notes
103
+
104
+ /** A role column the format has no slot for is written as a plain attribute (the role is lost). */
105
+ export const ROLE_DROPPED_CODE = "W_ROLE_DROPPED";
106
+
107
+ /** A parents (multi-parent) column in a format with single containment only. */
108
+ export const PARENTS_DROPPED_CODE = "W_PARENTS_DROPPED";
109
+
110
+ /** A start / end / timestamp column in a format without temporal support. */
111
+ export const TEMPORAL_DROPPED_CODE = "W_TEMPORAL_DROPPED";
112
+
113
+ /** A mutual pair is written as two directed edges without the mutual mark. */
114
+ export const MUTUAL_EXPANDED_CODE = "W_MUTUAL_EXPANDED";
115
+
116
+ /** A mutual pair is written as one undirected edge; the pair reads back undirected, the mark is lost. */
117
+ export const MUTUAL_AS_UNDIRECTED_CODE = "W_MUTUAL_AS_UNDIRECTED";
118
+
119
+ /** Node ids whose written text reads back as the other type under the importer's id rule. */
120
+ export const ID_TEXT_TYPE_CODE = "W_ID_TEXT_TYPE";
121
+
122
+ /** Two node ids share one written text; export() throws E_INVALID_ID. */
123
+ export const ID_TEXT_COLLISION_CODE = "E_ID_TEXT_COLLISION";
124
+
125
+ /** A `<column>.text` companion (design section 5.1) the format cannot carry. */
126
+ export const TEMPORAL_TEXT_DROPPED_CODE = "W_TEMPORAL_TEXT_DROPPED";
127
+
128
+ /** A plain column named like the importer's weight key reads back as THE weight. */
129
+ export const WEIGHT_KEY_CLASH_CODE = "W_WEIGHT_KEY_CLASH";
130
+
131
+ /** A role-less column whose written name the importer maps to a role. */
132
+ export const ROLE_ASSUMED_CODE = "W_ROLE_ASSUMED";
133
+
134
+ /** A role column written into the format's slot reads back under the importer's fixed name. */
135
+ export const COLUMN_RENAMED_LOSS_CODE = "W_COLUMN_NAME_CHANGED";
136
+
137
+ /** A declared column whose every row is unset is not written by a format without declarations. */
138
+ export const EMPTY_COLUMN_DROPPED_CODE = "W_EMPTY_COLUMN_DROPPED";
139
+
140
+ /** A string / dict column whose cardinality makes the importer read it back as the other storage class. */
141
+ export const STORAGE_CLASS_CODE = "W_STORAGE_CLASS_CHANGED";
142
+
143
+ /** An f64 column whose set values are all integral reads back as i32 through an untyped format. */
144
+ export const INTEGRAL_F64_CODE = "W_INTEGRAL_F64_AS_I32";
145
+
146
+ /** A text cell that reads back as a number or boolean under the 5.1 grammar (its lexical form may change). */
147
+ export const TEXT_INFERRED_CODE = "W_TEXT_INFERRED";
148
+
149
+ /** A string cell holding a character XML 1.0 forbids; export() throws E_COLUMN_TYPE. */
150
+ export const XML_ILLEGAL_CHAR_CODE = "E_XML_ILLEGAL_CHAR";
151
+
152
+ /** A dict column without declared options gains one from its dictionary on re-import. */
153
+ export const OPTIONS_GAINED_CODE = "W_OPTIONS_GAINED";