@treeviz/gedcom-parser 1.0.13 → 1.0.15

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 (367) hide show
  1. package/dist/classes/index.d.ts +2 -28
  2. package/dist/classes/index.js +6713 -21
  3. package/dist/classes/index.js.map +1 -0
  4. package/dist/cli/index.d.ts +0 -2
  5. package/dist/cli/index.js +7826 -24
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/constants/index.d.ts +49 -4
  8. package/dist/constants/index.js +5797 -4
  9. package/dist/constants/index.js.map +1 -0
  10. package/dist/factories/index.d.ts +202 -13
  11. package/dist/factories/index.js +5782 -8
  12. package/dist/factories/index.js.map +1 -0
  13. package/dist/index-BvW-KWJP.d.ts +1654 -0
  14. package/dist/index.d.ts +9 -28
  15. package/dist/index.js +7380 -35
  16. package/dist/index.js.map +1 -0
  17. package/dist/interfaces/index.d.ts +2 -12
  18. package/dist/interfaces/index.js +3 -1
  19. package/dist/interfaces/index.js.map +1 -0
  20. package/dist/kinship-translator/index.d.ts +146 -11
  21. package/dist/kinship-translator/index.js +5727 -9
  22. package/dist/kinship-translator/index.js.map +1 -0
  23. package/dist/place-parser-RlXCXZ8F.d.ts +37 -0
  24. package/dist/structures/index.d.ts +25 -27
  25. package/dist/structures/index.js +3 -1
  26. package/dist/structures/index.js.map +1 -0
  27. package/dist/types/index.d.ts +69 -8
  28. package/dist/types/index.js +31 -6
  29. package/dist/types/index.js.map +1 -0
  30. package/dist/utils/index.d.ts +91 -18
  31. package/dist/utils/index.js +6953 -17
  32. package/dist/utils/index.js.map +1 -0
  33. package/package.json +4 -3
  34. package/dist/classes/common.d.ts +0 -91
  35. package/dist/classes/common.d.ts.map +0 -1
  36. package/dist/classes/common.js +0 -488
  37. package/dist/classes/date.d.ts +0 -27
  38. package/dist/classes/date.d.ts.map +0 -1
  39. package/dist/classes/date.js +0 -178
  40. package/dist/classes/fam.d.ts +0 -19
  41. package/dist/classes/fam.d.ts.map +0 -1
  42. package/dist/classes/fam.js +0 -38
  43. package/dist/classes/fams.d.ts +0 -16
  44. package/dist/classes/fams.d.ts.map +0 -1
  45. package/dist/classes/fams.js +0 -41
  46. package/dist/classes/gedcom.d.ts +0 -139
  47. package/dist/classes/gedcom.d.ts.map +0 -1
  48. package/dist/classes/gedcom.js +0 -910
  49. package/dist/classes/index.d.ts.map +0 -1
  50. package/dist/classes/indi.d.ts +0 -323
  51. package/dist/classes/indi.d.ts.map +0 -1
  52. package/dist/classes/indi.js +0 -2131
  53. package/dist/classes/indis.d.ts +0 -55
  54. package/dist/classes/indis.d.ts.map +0 -1
  55. package/dist/classes/indis.js +0 -457
  56. package/dist/classes/list.d.ts +0 -55
  57. package/dist/classes/list.d.ts.map +0 -1
  58. package/dist/classes/list.js +0 -440
  59. package/dist/classes/name.d.ts +0 -20
  60. package/dist/classes/name.d.ts.map +0 -1
  61. package/dist/classes/name.js +0 -48
  62. package/dist/classes/note.d.ts +0 -12
  63. package/dist/classes/note.d.ts.map +0 -1
  64. package/dist/classes/note.js +0 -46
  65. package/dist/classes/obje.d.ts +0 -12
  66. package/dist/classes/obje.d.ts.map +0 -1
  67. package/dist/classes/obje.js +0 -81
  68. package/dist/classes/objes.d.ts +0 -8
  69. package/dist/classes/objes.d.ts.map +0 -1
  70. package/dist/classes/objes.js +0 -13
  71. package/dist/classes/repo.d.ts +0 -11
  72. package/dist/classes/repo.d.ts.map +0 -1
  73. package/dist/classes/repo.js +0 -6
  74. package/dist/classes/repos.d.ts +0 -8
  75. package/dist/classes/repos.d.ts.map +0 -1
  76. package/dist/classes/repos.js +0 -13
  77. package/dist/classes/sour.d.ts +0 -11
  78. package/dist/classes/sour.d.ts.map +0 -1
  79. package/dist/classes/sour.js +0 -6
  80. package/dist/classes/sours.d.ts +0 -8
  81. package/dist/classes/sours.d.ts.map +0 -1
  82. package/dist/classes/sours.js +0 -13
  83. package/dist/classes/subm.d.ts +0 -10
  84. package/dist/classes/subm.d.ts.map +0 -1
  85. package/dist/classes/subm.js +0 -6
  86. package/dist/classes/subms.d.ts +0 -8
  87. package/dist/classes/subms.d.ts.map +0 -1
  88. package/dist/classes/subms.js +0 -13
  89. package/dist/cli/commands/convert.d.ts +0 -3
  90. package/dist/cli/commands/convert.d.ts.map +0 -1
  91. package/dist/cli/commands/convert.js +0 -83
  92. package/dist/cli/commands/extract.d.ts +0 -3
  93. package/dist/cli/commands/extract.d.ts.map +0 -1
  94. package/dist/cli/commands/extract.js +0 -85
  95. package/dist/cli/commands/find.d.ts +0 -3
  96. package/dist/cli/commands/find.d.ts.map +0 -1
  97. package/dist/cli/commands/find.js +0 -97
  98. package/dist/cli/commands/info.d.ts +0 -3
  99. package/dist/cli/commands/info.d.ts.map +0 -1
  100. package/dist/cli/commands/info.js +0 -80
  101. package/dist/cli/commands/merge.d.ts +0 -3
  102. package/dist/cli/commands/merge.d.ts.map +0 -1
  103. package/dist/cli/commands/merge.js +0 -61
  104. package/dist/cli/commands/relatives.d.ts +0 -3
  105. package/dist/cli/commands/relatives.d.ts.map +0 -1
  106. package/dist/cli/commands/relatives.js +0 -107
  107. package/dist/cli/commands/show.d.ts +0 -3
  108. package/dist/cli/commands/show.d.ts.map +0 -1
  109. package/dist/cli/commands/show.js +0 -176
  110. package/dist/cli/commands/stats.d.ts +0 -3
  111. package/dist/cli/commands/stats.d.ts.map +0 -1
  112. package/dist/cli/commands/stats.js +0 -59
  113. package/dist/cli/commands/validate.d.ts +0 -3
  114. package/dist/cli/commands/validate.d.ts.map +0 -1
  115. package/dist/cli/commands/validate.js +0 -148
  116. package/dist/cli/index.d.ts.map +0 -1
  117. package/dist/cli/utils/formatters.d.ts +0 -69
  118. package/dist/cli/utils/formatters.d.ts.map +0 -1
  119. package/dist/cli/utils/formatters.js +0 -125
  120. package/dist/cli/utils/helpers.d.ts +0 -21
  121. package/dist/cli/utils/helpers.d.ts.map +0 -1
  122. package/dist/cli/utils/helpers.js +0 -58
  123. package/dist/constants/constants.d.ts +0 -14
  124. package/dist/constants/constants.d.ts.map +0 -1
  125. package/dist/constants/constants.js +0 -13
  126. package/dist/constants/filters.d.ts +0 -18
  127. package/dist/constants/filters.d.ts.map +0 -1
  128. package/dist/constants/filters.js +0 -44
  129. package/dist/constants/index.d.ts.map +0 -1
  130. package/dist/constants/orders.d.ts +0 -17
  131. package/dist/constants/orders.d.ts.map +0 -1
  132. package/dist/constants/orders.js +0 -240
  133. package/dist/factories/cache-factory.d.ts +0 -30
  134. package/dist/factories/cache-factory.d.ts.map +0 -1
  135. package/dist/factories/cache-factory.js +0 -43
  136. package/dist/factories/date-locale-factory.d.ts +0 -30
  137. package/dist/factories/date-locale-factory.d.ts.map +0 -1
  138. package/dist/factories/date-locale-factory.js +0 -34
  139. package/dist/factories/i18n-factory.d.ts +0 -40
  140. package/dist/factories/i18n-factory.d.ts.map +0 -1
  141. package/dist/factories/i18n-factory.js +0 -44
  142. package/dist/factories/index.d.ts.map +0 -1
  143. package/dist/factories/kinship-factory.d.ts +0 -38
  144. package/dist/factories/kinship-factory.d.ts.map +0 -1
  145. package/dist/factories/kinship-factory.js +0 -35
  146. package/dist/factories/place-parser-provider.d.ts +0 -32
  147. package/dist/factories/place-parser-provider.d.ts.map +0 -1
  148. package/dist/factories/place-parser-provider.js +0 -35
  149. package/dist/factories/place-translator-provider.d.ts +0 -32
  150. package/dist/factories/place-translator-provider.d.ts.map +0 -1
  151. package/dist/factories/place-translator-provider.js +0 -35
  152. package/dist/index.d.ts.map +0 -1
  153. package/dist/interfaces/common.d.ts +0 -43
  154. package/dist/interfaces/common.d.ts.map +0 -1
  155. package/dist/interfaces/common.js +0 -1
  156. package/dist/interfaces/fam.d.ts +0 -11
  157. package/dist/interfaces/fam.d.ts.map +0 -1
  158. package/dist/interfaces/fam.js +0 -1
  159. package/dist/interfaces/fams.d.ts +0 -9
  160. package/dist/interfaces/fams.d.ts.map +0 -1
  161. package/dist/interfaces/fams.js +0 -1
  162. package/dist/interfaces/gedcom.d.ts +0 -30
  163. package/dist/interfaces/gedcom.d.ts.map +0 -1
  164. package/dist/interfaces/gedcom.js +0 -1
  165. package/dist/interfaces/index.d.ts.map +0 -1
  166. package/dist/interfaces/indi.d.ts +0 -113
  167. package/dist/interfaces/indi.d.ts.map +0 -1
  168. package/dist/interfaces/indi.js +0 -1
  169. package/dist/interfaces/indis.d.ts +0 -31
  170. package/dist/interfaces/indis.d.ts.map +0 -1
  171. package/dist/interfaces/indis.js +0 -1
  172. package/dist/interfaces/list.d.ts +0 -52
  173. package/dist/interfaces/list.d.ts.map +0 -1
  174. package/dist/interfaces/list.js +0 -1
  175. package/dist/interfaces/obje.d.ts +0 -7
  176. package/dist/interfaces/obje.d.ts.map +0 -1
  177. package/dist/interfaces/obje.js +0 -1
  178. package/dist/interfaces/repo.d.ts +0 -7
  179. package/dist/interfaces/repo.d.ts.map +0 -1
  180. package/dist/interfaces/repo.js +0 -1
  181. package/dist/interfaces/sour.d.ts +0 -7
  182. package/dist/interfaces/sour.d.ts.map +0 -1
  183. package/dist/interfaces/sour.js +0 -1
  184. package/dist/interfaces/subm.d.ts +0 -6
  185. package/dist/interfaces/subm.d.ts.map +0 -1
  186. package/dist/interfaces/subm.js +0 -1
  187. package/dist/kinship-translator/index.d.ts.map +0 -1
  188. package/dist/kinship-translator/kinship-translator.basic.d.ts +0 -30
  189. package/dist/kinship-translator/kinship-translator.basic.d.ts.map +0 -1
  190. package/dist/kinship-translator/kinship-translator.basic.js +0 -74
  191. package/dist/kinship-translator/kinship-translator.d.ts +0 -26
  192. package/dist/kinship-translator/kinship-translator.d.ts.map +0 -1
  193. package/dist/kinship-translator/kinship-translator.de.d.ts +0 -18
  194. package/dist/kinship-translator/kinship-translator.de.d.ts.map +0 -1
  195. package/dist/kinship-translator/kinship-translator.de.js +0 -180
  196. package/dist/kinship-translator/kinship-translator.en.d.ts +0 -18
  197. package/dist/kinship-translator/kinship-translator.en.d.ts.map +0 -1
  198. package/dist/kinship-translator/kinship-translator.en.js +0 -182
  199. package/dist/kinship-translator/kinship-translator.es.d.ts +0 -18
  200. package/dist/kinship-translator/kinship-translator.es.d.ts.map +0 -1
  201. package/dist/kinship-translator/kinship-translator.es.js +0 -181
  202. package/dist/kinship-translator/kinship-translator.fr.d.ts +0 -18
  203. package/dist/kinship-translator/kinship-translator.fr.d.ts.map +0 -1
  204. package/dist/kinship-translator/kinship-translator.fr.js +0 -181
  205. package/dist/kinship-translator/kinship-translator.hu.d.ts +0 -19
  206. package/dist/kinship-translator/kinship-translator.hu.d.ts.map +0 -1
  207. package/dist/kinship-translator/kinship-translator.hu.js +0 -226
  208. package/dist/kinship-translator/kinship-translator.interface.d.ts +0 -19
  209. package/dist/kinship-translator/kinship-translator.interface.d.ts.map +0 -1
  210. package/dist/kinship-translator/kinship-translator.interface.js +0 -1
  211. package/dist/kinship-translator/kinship-translator.js +0 -103
  212. package/dist/kinship-translator/patterns.de.d.ts +0 -2
  213. package/dist/kinship-translator/patterns.de.d.ts.map +0 -1
  214. package/dist/kinship-translator/patterns.de.js +0 -14
  215. package/dist/kinship-translator/patterns.en.d.ts +0 -2
  216. package/dist/kinship-translator/patterns.en.d.ts.map +0 -1
  217. package/dist/kinship-translator/patterns.en.js +0 -14
  218. package/dist/kinship-translator/patterns.es.d.ts +0 -2
  219. package/dist/kinship-translator/patterns.es.d.ts.map +0 -1
  220. package/dist/kinship-translator/patterns.es.js +0 -14
  221. package/dist/kinship-translator/patterns.fr.d.ts +0 -2
  222. package/dist/kinship-translator/patterns.fr.d.ts.map +0 -1
  223. package/dist/kinship-translator/patterns.fr.js +0 -14
  224. package/dist/kinship-translator/patterns.hu.d.ts +0 -6
  225. package/dist/kinship-translator/patterns.hu.d.ts.map +0 -1
  226. package/dist/kinship-translator/patterns.hu.js +0 -97
  227. package/dist/kinship-translator/translators.d.ts +0 -6
  228. package/dist/kinship-translator/translators.d.ts.map +0 -1
  229. package/dist/kinship-translator/translators.js +0 -5
  230. package/dist/kinship-translator/types.d.ts +0 -9
  231. package/dist/kinship-translator/types.d.ts.map +0 -1
  232. package/dist/kinship-translator/types.js +0 -1
  233. package/dist/structures/address.d.ts +0 -14
  234. package/dist/structures/address.d.ts.map +0 -1
  235. package/dist/structures/address.js +0 -1
  236. package/dist/structures/association.d.ts +0 -13
  237. package/dist/structures/association.d.ts.map +0 -1
  238. package/dist/structures/association.js +0 -1
  239. package/dist/structures/change-date.d.ts +0 -8
  240. package/dist/structures/change-date.d.ts.map +0 -1
  241. package/dist/structures/change-date.js +0 -1
  242. package/dist/structures/creation-date.d.ts +0 -7
  243. package/dist/structures/creation-date.d.ts.map +0 -1
  244. package/dist/structures/creation-date.js +0 -1
  245. package/dist/structures/date.d.ts +0 -9
  246. package/dist/structures/date.d.ts.map +0 -1
  247. package/dist/structures/date.js +0 -1
  248. package/dist/structures/event-detail-structure.d.ts +0 -25
  249. package/dist/structures/event-detail-structure.d.ts.map +0 -1
  250. package/dist/structures/event-detail-structure.js +0 -1
  251. package/dist/structures/family.d.ts +0 -34
  252. package/dist/structures/family.d.ts.map +0 -1
  253. package/dist/structures/family.js +0 -1
  254. package/dist/structures/gedcom.d.ts +0 -59
  255. package/dist/structures/gedcom.d.ts.map +0 -1
  256. package/dist/structures/gedcom.js +0 -1
  257. package/dist/structures/index.d.ts.map +0 -1
  258. package/dist/structures/individual-event-detail-structure.d.ts +0 -9
  259. package/dist/structures/individual-event-detail-structure.d.ts.map +0 -1
  260. package/dist/structures/individual-event-detail-structure.js +0 -1
  261. package/dist/structures/individual-event-structure.d.ts +0 -83
  262. package/dist/structures/individual-event-structure.d.ts.map +0 -1
  263. package/dist/structures/individual-event-structure.js +0 -1
  264. package/dist/structures/individual.d.ts +0 -40
  265. package/dist/structures/individual.d.ts.map +0 -1
  266. package/dist/structures/individual.js +0 -1
  267. package/dist/structures/lds-ordinance-detail.d.ts +0 -17
  268. package/dist/structures/lds-ordinance-detail.d.ts.map +0 -1
  269. package/dist/structures/lds-ordinance-detail.js +0 -1
  270. package/dist/structures/lds-spouse-sealing.d.ts +0 -7
  271. package/dist/structures/lds-spouse-sealing.d.ts.map +0 -1
  272. package/dist/structures/lds-spouse-sealing.js +0 -1
  273. package/dist/structures/marriage-date.d.ts +0 -11
  274. package/dist/structures/marriage-date.d.ts.map +0 -1
  275. package/dist/structures/marriage-date.js +0 -1
  276. package/dist/structures/multimedia-link.d.ts +0 -16
  277. package/dist/structures/multimedia-link.d.ts.map +0 -1
  278. package/dist/structures/multimedia-link.js +0 -1
  279. package/dist/structures/non-event.d.ts +0 -12
  280. package/dist/structures/non-event.d.ts.map +0 -1
  281. package/dist/structures/non-event.js +0 -1
  282. package/dist/structures/note.d.ts +0 -16
  283. package/dist/structures/note.d.ts.map +0 -1
  284. package/dist/structures/note.js +0 -1
  285. package/dist/structures/personal-name-pieces.d.ts +0 -11
  286. package/dist/structures/personal-name-pieces.d.ts.map +0 -1
  287. package/dist/structures/personal-name-pieces.js +0 -1
  288. package/dist/structures/personal-name.d.ts +0 -16
  289. package/dist/structures/personal-name.d.ts.map +0 -1
  290. package/dist/structures/personal-name.js +0 -1
  291. package/dist/structures/place.d.ts +0 -20
  292. package/dist/structures/place.d.ts.map +0 -1
  293. package/dist/structures/place.js +0 -1
  294. package/dist/structures/repository.d.ts +0 -6
  295. package/dist/structures/repository.d.ts.map +0 -1
  296. package/dist/structures/repository.js +0 -1
  297. package/dist/structures/source-citation.d.ts +0 -27
  298. package/dist/structures/source-citation.d.ts.map +0 -1
  299. package/dist/structures/source-citation.js +0 -1
  300. package/dist/structures/source-repository-citation.d.ts +0 -13
  301. package/dist/structures/source-repository-citation.d.ts.map +0 -1
  302. package/dist/structures/source-repository-citation.js +0 -1
  303. package/dist/structures/source.d.ts +0 -28
  304. package/dist/structures/source.d.ts.map +0 -1
  305. package/dist/structures/source.js +0 -1
  306. package/dist/types/ancestry-media.d.ts +0 -65
  307. package/dist/types/ancestry-media.d.ts.map +0 -1
  308. package/dist/types/ancestry-media.js +0 -1
  309. package/dist/types/index.d.ts.map +0 -1
  310. package/dist/types/settings.d.ts +0 -12
  311. package/dist/types/settings.d.ts.map +0 -1
  312. package/dist/types/settings.js +0 -1
  313. package/dist/types/types.d.ts +0 -225
  314. package/dist/types/types.d.ts.map +0 -1
  315. package/dist/types/types.js +0 -26
  316. package/dist/utils/cache.d.ts +0 -22
  317. package/dist/utils/cache.d.ts.map +0 -1
  318. package/dist/utils/cache.js +0 -57
  319. package/dist/utils/common-creator.d.ts +0 -13
  320. package/dist/utils/common-creator.d.ts.map +0 -1
  321. package/dist/utils/common-creator.js +0 -72
  322. package/dist/utils/date-formatter.d.ts +0 -35
  323. package/dist/utils/date-formatter.d.ts.map +0 -1
  324. package/dist/utils/date-formatter.js +0 -207
  325. package/dist/utils/get-all-prop.d.ts +0 -4
  326. package/dist/utils/get-all-prop.d.ts.map +0 -1
  327. package/dist/utils/get-all-prop.js +0 -5
  328. package/dist/utils/get-family-with.d.ts +0 -5
  329. package/dist/utils/get-family-with.d.ts.map +0 -1
  330. package/dist/utils/get-family-with.js +0 -11
  331. package/dist/utils/get-places.d.ts +0 -21
  332. package/dist/utils/get-places.d.ts.map +0 -1
  333. package/dist/utils/get-places.js +0 -77
  334. package/dist/utils/get-product-details.d.ts +0 -4
  335. package/dist/utils/get-product-details.d.ts.map +0 -1
  336. package/dist/utils/get-product-details.js +0 -6
  337. package/dist/utils/get-raw-size.d.ts +0 -2
  338. package/dist/utils/get-raw-size.d.ts.map +0 -1
  339. package/dist/utils/get-raw-size.js +0 -3
  340. package/dist/utils/index.d.ts.map +0 -1
  341. package/dist/utils/logger.d.ts +0 -3
  342. package/dist/utils/logger.d.ts.map +0 -1
  343. package/dist/utils/logger.js +0 -11
  344. package/dist/utils/name-formatter.d.ts +0 -10
  345. package/dist/utils/name-formatter.d.ts.map +0 -1
  346. package/dist/utils/name-formatter.js +0 -45
  347. package/dist/utils/nested-group.d.ts +0 -4
  348. package/dist/utils/nested-group.d.ts.map +0 -1
  349. package/dist/utils/nested-group.js +0 -34
  350. package/dist/utils/ordinalize.d.ts +0 -3
  351. package/dist/utils/ordinalize.d.ts.map +0 -1
  352. package/dist/utils/ordinalize.js +0 -104
  353. package/dist/utils/parser.d.ts +0 -17
  354. package/dist/utils/parser.d.ts.map +0 -1
  355. package/dist/utils/parser.js +0 -321
  356. package/dist/utils/place-parser.d.ts +0 -16
  357. package/dist/utils/place-parser.d.ts.map +0 -1
  358. package/dist/utils/place-parser.js +0 -46
  359. package/dist/utils/place-translator.d.ts +0 -6
  360. package/dist/utils/place-translator.d.ts.map +0 -1
  361. package/dist/utils/place-translator.js +0 -8
  362. package/dist/utils/place-types.d.ts +0 -27
  363. package/dist/utils/place-types.d.ts.map +0 -1
  364. package/dist/utils/place-types.js +0 -14
  365. package/dist/utils/range.d.ts +0 -19
  366. package/dist/utils/range.d.ts.map +0 -1
  367. package/dist/utils/range.js +0 -265
@@ -1,910 +0,0 @@
1
- import { getVersion } from "../utils/get-product-details.js";
2
- import { Common, createCommon } from "./common.js";
3
- import { CustomTags } from "./indi.js";
4
- import { List } from "./list.js";
5
- export class GedCom extends Common {
6
- constructor() {
7
- super();
8
- this.tagMembers = {};
9
- this.reflist = {};
10
- this.refcount = 0;
11
- delete this._gedcom;
12
- delete this.id;
13
- this.removeValue();
14
- }
15
- getMain(type, index) {
16
- const list = !type || type instanceof List ? type : this.getList(type);
17
- if (!list) {
18
- return undefined;
19
- }
20
- if (typeof index === "string") {
21
- return list.item(index);
22
- }
23
- // const keyProbe: IdType[] = [
24
- // `@I${index}@`,
25
- // `@P${index}@`,
26
- // `@XI${index}@`,
27
- // `@XXI${index}@`,
28
- // ];
29
- // let itemProbe: T | undefined;
30
- // keyProbe.find((key) => {
31
- // const i = list.item(key);
32
- // if (i) {
33
- // itemProbe = i as T;
34
- // return true;
35
- // }
36
- // return false;
37
- // });
38
- // if (itemProbe) {
39
- // return itemProbe as T;
40
- // }
41
- const keys = list.keys();
42
- return list.item(keys[index]);
43
- }
44
- getList(type) {
45
- return this.get(type);
46
- }
47
- indis() {
48
- return this.getList("@@INDI");
49
- }
50
- cloneIndis(target, source, avoidKeys = [], removeFromOriginalList = true) {
51
- const hasFAMS = avoidKeys.includes("FAMS");
52
- const targetIndi = typeof target === "string" ? this.indi(target) : target;
53
- const sourceIndi = typeof source === "string" ? this.indi(source) : source;
54
- if (!targetIndi || !sourceIndi) {
55
- return this;
56
- }
57
- const spousesFamily = hasFAMS ? sourceIndi.FAMS?.toList() : undefined;
58
- const childrenFamily = sourceIndi.FAMC?.toList();
59
- const cloned = targetIndi.clone(false, avoidKeys);
60
- const newCloned = sourceIndi.merge(cloned);
61
- newCloned.cloneOf = targetIndi.id;
62
- targetIndi.clonedBy = newCloned.id;
63
- if (newCloned.id) {
64
- this.getList("@@INDI")?.item(newCloned.id, newCloned);
65
- if (childrenFamily?.length) {
66
- childrenFamily.forEach((fam) => {
67
- if (!fam.ref) {
68
- return;
69
- }
70
- const newChild = createCommon(this, undefined, fam.ref);
71
- newChild.value = newCloned.id;
72
- fam.ref.assign("CHIL", newChild, true);
73
- });
74
- }
75
- if (spousesFamily?.length) {
76
- spousesFamily.forEach((fam) => {
77
- if (!fam.ref) {
78
- return;
79
- }
80
- const newSpouse = createCommon(this, undefined, fam.ref);
81
- newSpouse.value = newCloned.id;
82
- if (newCloned.isMale()) {
83
- fam.ref.assign("HUSB", newSpouse, true);
84
- }
85
- else if (newCloned.isFemale()) {
86
- fam.ref.assign("WIFE", newSpouse, true);
87
- }
88
- });
89
- }
90
- if (removeFromOriginalList &&
91
- newCloned.type &&
92
- newCloned.type !== targetIndi.type) {
93
- this.getList(`@@${newCloned.type}`)?.removeItem(newCloned.id);
94
- }
95
- newCloned.type = "INDI";
96
- }
97
- }
98
- mergeIndis(target, source, removeFromOriginalList = true) {
99
- const targetIndi = typeof target === "string" ? this.indi(target) : target;
100
- const sourceIndi = typeof source === "string" ? this.indi(source) : source;
101
- if (!targetIndi || !sourceIndi) {
102
- return this;
103
- }
104
- const sourceIndiId = sourceIndi.id;
105
- const spousesFamily = sourceIndi.FAMS?.toList();
106
- const childrenFamily = sourceIndi.FAMC?.toList();
107
- const newLinked = targetIndi.merge(sourceIndi);
108
- const mergedId = newLinked.id;
109
- if (sourceIndiId && (spousesFamily?.length || childrenFamily?.length)) {
110
- if (childrenFamily?.length && mergedId) {
111
- childrenFamily.forEach((fam) => {
112
- if (!fam.ref) {
113
- return;
114
- }
115
- const newChild = createCommon(this, undefined, fam.ref);
116
- newChild.value = mergedId;
117
- fam.ref.assign("CHIL", newChild, true);
118
- });
119
- }
120
- if (spousesFamily?.length && mergedId) {
121
- spousesFamily.forEach((fam) => {
122
- if (!fam.ref) {
123
- return;
124
- }
125
- const newSpouse = createCommon(this, undefined, fam.ref);
126
- newSpouse.value = mergedId;
127
- if (newLinked?.isMale()) {
128
- fam.ref.assign("HUSB", newSpouse, true);
129
- }
130
- else if (newLinked?.isFemale()) {
131
- fam.ref.assign("WIFE", newSpouse, true);
132
- }
133
- });
134
- }
135
- }
136
- if (removeFromOriginalList && sourceIndi?.type && sourceIndi.id) {
137
- this.getList(`@@${sourceIndi.type}`)?.removeItem(sourceIndi.id);
138
- }
139
- }
140
- fams() {
141
- return this.getList("@@FAM");
142
- }
143
- objes() {
144
- return this.getList("@@OBJE");
145
- }
146
- sours() {
147
- return this.getList("@@SOUR");
148
- }
149
- repos() {
150
- return this.getList("@@REPO");
151
- }
152
- subms() {
153
- return this.getList("@@SUBM");
154
- }
155
- tags() {
156
- return this.getList("@@_MTTAG");
157
- }
158
- customTags() {
159
- return this.getList("@@_MTTAG")?.filter((tag) => {
160
- return tag?.get("_MTCAT.NAME")?.toValue() === "Custom";
161
- });
162
- }
163
- indi(index) {
164
- return this.getMain(this.indis(), index);
165
- }
166
- fam(index) {
167
- return this.getMain(this.fams(), index);
168
- }
169
- obje(index) {
170
- return this.getMain(this.objes(), index);
171
- }
172
- sour(index) {
173
- return this.getMain(this.sours(), index);
174
- }
175
- repo(index) {
176
- return this.getMain(this.repos(), index);
177
- }
178
- subm(index) {
179
- return this.getMain(this.subms(), index);
180
- }
181
- tag(index) {
182
- return this.getMain(this.tags(), index);
183
- }
184
- fromList(id) {
185
- return id ? this.reflist?.[id] : undefined;
186
- }
187
- tagByName(name) {
188
- return this.tags()?.find((tag) => {
189
- const tagName = tag?.get("NAME")?.toValue();
190
- return tagName === name;
191
- });
192
- }
193
- customTag(index) {
194
- return this.getMain(this.customTags(), index);
195
- }
196
- getIndiRelatedLists(indis) {
197
- const refs = {};
198
- const individuals = this.indis()?.filter((indi, indiKey) => {
199
- if (indis.includes(indiKey)) {
200
- indi.getRefs()
201
- ?.values()
202
- ?.forEach((ref) => {
203
- const refKey = ref?.value;
204
- if (ref && refKey && !refs[refKey]) {
205
- refs[refKey] = ref;
206
- }
207
- });
208
- return true;
209
- }
210
- return false;
211
- });
212
- const usedLists = Object.entries(this).filter(([prop, list]) => {
213
- return (prop !== "@@INDI" &&
214
- prop.startsWith("@@") &&
215
- list instanceof List);
216
- });
217
- const lists = {};
218
- if (individuals) {
219
- lists["@@INDI"] = individuals;
220
- }
221
- usedLists.forEach(([key, list]) => {
222
- const validKey = key;
223
- if (list) {
224
- lists[validKey] = list.filter((item) => {
225
- return Boolean(item.id && refs[item.id]);
226
- });
227
- }
228
- });
229
- return lists;
230
- }
231
- getDownloadHeader() {
232
- const newHead = createCommon();
233
- Object.assign(newHead, this.get("HEAD") ?? {});
234
- const newSour = createCommon();
235
- newSour.set("CORP", createCommon());
236
- newSour.set("CORP.WWW", createCommon());
237
- newSour.set("NAME", createCommon());
238
- newSour.set("VERS", createCommon());
239
- newSour.CORP.value = "TreeViz - The Family Tree Visualiser";
240
- newSour.CORP.WWW.value = "treeviz.com";
241
- newSour.NAME.value = "TreeViz - The Family Tree Visualiser";
242
- newSour.VERS.value = getVersion();
243
- newHead.set("SOUR", newSour);
244
- return newHead;
245
- }
246
- toFiltered(indis) {
247
- if (!indis.length) {
248
- return this;
249
- }
250
- const newGedcom = createGedCom();
251
- const newContent = this.getIndiRelatedLists(indis);
252
- Object.assign(newGedcom, this, newContent, {
253
- HEAD: this.getDownloadHeader(),
254
- });
255
- return newGedcom;
256
- }
257
- toJson(tag, options) {
258
- if (!options?.indis?.length) {
259
- return super.toJson(tag, options);
260
- }
261
- const newGedcom = createGedCom();
262
- const newContent = this.getIndiRelatedLists(options.indis);
263
- Object.assign(newGedcom, this, newContent, {
264
- HEAD: this.getDownloadHeader(),
265
- });
266
- delete options.indis;
267
- return newGedcom.toJson(tag, options);
268
- }
269
- toGedcom(tag, level, options) {
270
- if (options?.super) {
271
- return super.toGedcom(tag, level, options);
272
- }
273
- const newGedcom = createGedCom();
274
- if (!options?.original) {
275
- Object.assign(newGedcom, {
276
- HEAD: this.getDownloadHeader(),
277
- });
278
- }
279
- Object.assign(newGedcom, this);
280
- if (options?.indis?.length) {
281
- const newContent = this.getIndiRelatedLists(options.indis);
282
- Object.assign(newGedcom, newContent);
283
- }
284
- return newGedcom.toGedcom(tag, level, { ...options, super: true });
285
- }
286
- hasTag(tag) {
287
- const tagName = typeof tag === "string"
288
- ? tag
289
- : tag?.get("NAME")?.toValue();
290
- if (!tagName) {
291
- return false;
292
- }
293
- return !!this.tagMembers?.[tagName]?.indis?.length;
294
- }
295
- hasUnknownAncestor() {
296
- return !!this.tagMembers?.[CustomTags.UnknownAncestor]?.indis?.length;
297
- }
298
- hasIgnoredMember() {
299
- return !!this.tagMembers?.[CustomTags.IgnoredMember]?.indis?.length;
300
- }
301
- hasUnattachedMember() {
302
- return !!this.tagMembers?.[CustomTags.UnattachedMember]?.indis?.length;
303
- }
304
- hasUnknownGivenname() {
305
- return !!this.tagMembers?.[CustomTags.UnknownGivenname]?.indis?.length;
306
- }
307
- hasUnknownSurname() {
308
- return !!this.tagMembers?.[CustomTags.UnknownSurname]?.indis?.length;
309
- }
310
- hasNonRelevant() {
311
- return this.hasUnknownAncestor() || this.hasUnattachedMember();
312
- }
313
- /**
314
- * Get all places from the GEDCOM with occurrence counts
315
- * @returns Record mapping place names to their occurrence count
316
- */
317
- getAllPlaces() {
318
- const indis = this.indis();
319
- const allPlaces = {};
320
- indis?.forEach((indi) => {
321
- if (!indi.id)
322
- return;
323
- const places = indi.getPlaces();
324
- places.forEach((placeObj) => {
325
- const placeName = placeObj?.place;
326
- if (placeName && typeof placeName === "string") {
327
- allPlaces[placeName] = (allPlaces[placeName] || 0) + 1;
328
- }
329
- });
330
- });
331
- return allPlaces;
332
- }
333
- /**
334
- * Get all places from the GEDCOM with associated individual IDs
335
- * @param usedIndis Optional array of individual IDs to filter by
336
- * @returns Record mapping place names to arrays of individual IDs
337
- */
338
- getAllPlacesWithIndis(usedIndis = []) {
339
- const indis = this.indis();
340
- const allPlaces = {};
341
- indis?.forEach((indi) => {
342
- if (!indi.id ||
343
- (usedIndis.length && !usedIndis.includes(indi.id))) {
344
- return;
345
- }
346
- const places = indi.getPlaces();
347
- places.forEach((placeObj) => {
348
- const placeName = placeObj?.place;
349
- if (placeName && typeof placeName === "string" && indi.id) {
350
- if (!allPlaces[placeName]) {
351
- allPlaces[placeName] = [];
352
- }
353
- allPlaces[placeName].push(indi.id);
354
- }
355
- });
356
- });
357
- return allPlaces;
358
- }
359
- /**
360
- * Generate statistics about the GEDCOM file
361
- * @param individuals Optional list of individuals to calculate statistics for. If not provided, all individuals from the GEDCOM will be used.
362
- * @returns Object containing various statistics about the GEDCOM data
363
- */
364
- stats(individuals) {
365
- const indis = individuals ?? this.indis();
366
- // Build families list based on whether individuals filter is provided
367
- let families;
368
- if (individuals) {
369
- // If individuals filter is provided, filter families by references
370
- const familyIds = new Set();
371
- indis?.forEach((indi) => {
372
- // Add spouse families
373
- indi.FAMS?.toList()?.forEach((famRef) => {
374
- const famId = famRef.value;
375
- if (famId)
376
- familyIds.add(famId);
377
- });
378
- // Add parent families
379
- indi.FAMC?.toList()?.forEach((famRef) => {
380
- const famId = famRef.value;
381
- if (famId)
382
- familyIds.add(famId);
383
- });
384
- });
385
- families = this.fams()?.filter((fam) => fam.id ? familyIds.has(fam.id) : false);
386
- }
387
- else {
388
- // No filter provided, use all families directly
389
- families = this.fams();
390
- } // Calculate statistics
391
- const totalIndividuals = indis?.length || 0;
392
- const totalFamilies = families?.length || 0;
393
- // Count by sex
394
- let males = 0;
395
- let females = 0;
396
- let unknownSex = 0;
397
- indis?.forEach((indi) => {
398
- const sex = indi.SEX?.value;
399
- if (sex === "M")
400
- males++;
401
- else if (sex === "F")
402
- females++;
403
- else
404
- unknownSex++;
405
- });
406
- // Most common surnames
407
- const surnames = new Map();
408
- indis?.forEach((indi) => {
409
- const name = indi.NAME?.toValue();
410
- if (name) {
411
- const match = name.match(/\/(.+?)\//);
412
- if (match) {
413
- const surname = match[1];
414
- surnames.set(surname, (surnames.get(surname) || 0) + 1);
415
- }
416
- }
417
- });
418
- const topSurnames = Array.from(surnames.entries())
419
- .sort((a, b) => b[1] - a[1])
420
- .slice(0, 10)
421
- .map(([surname, count]) => ({ surname, count }));
422
- // Most common birth places
423
- const birthPlaces = new Map();
424
- indis?.forEach((indi) => {
425
- const place = indi.BIRT?.PLAC?.value;
426
- if (place) {
427
- birthPlaces.set(place, (birthPlaces.get(place) || 0) + 1);
428
- }
429
- });
430
- const topBirthPlaces = Array.from(birthPlaces.entries())
431
- .sort((a, b) => b[1] - a[1])
432
- .slice(0, 10)
433
- .map(([place, count]) => ({ place, count }));
434
- // Date range
435
- const years = [];
436
- indis?.forEach((indi) => {
437
- const birthDate = indi.BIRT?.DATE?.toValue();
438
- if (birthDate) {
439
- const match = birthDate.match(/\d{4}/);
440
- if (match) {
441
- years.push(parseInt(match[0], 10));
442
- }
443
- }
444
- const deathDate = indi.DEAT?.DATE?.toValue();
445
- if (deathDate) {
446
- const match = deathDate.match(/\d{4}/);
447
- if (match) {
448
- years.push(parseInt(match[0], 10));
449
- }
450
- }
451
- });
452
- const minYear = years.length > 0 ? Math.min(...years) : null;
453
- const maxYear = years.length > 0 ? Math.max(...years) : null;
454
- // Average lifespan
455
- const lifespans = [];
456
- indis?.forEach((indi) => {
457
- const birthDate = indi.BIRT?.DATE?.toValue();
458
- const deathDate = indi.DEAT?.DATE?.toValue();
459
- if (birthDate && deathDate) {
460
- const birthMatch = birthDate.match(/\d{4}/);
461
- const deathMatch = deathDate.match(/\d{4}/);
462
- if (birthMatch && deathMatch) {
463
- const birthYear = parseInt(birthMatch[0], 10);
464
- const deathYear = parseInt(deathMatch[0], 10);
465
- if (deathYear > birthYear) {
466
- lifespans.push(deathYear - birthYear);
467
- }
468
- }
469
- }
470
- });
471
- const avgLifespan = lifespans.length > 0
472
- ? lifespans.reduce((sum, age) => sum + age, 0) /
473
- lifespans.length
474
- : null;
475
- // First and last person events with type information
476
- const firstPerson = indis?.getFirstEvent();
477
- const firstBirth = firstPerson?.BIRT?.toList().index(0);
478
- const firstDeath = firstPerson?.DEAT?.toList().index(0);
479
- let firstPersonEvent = null;
480
- const firstBirthDate = firstBirth?.DATE
481
- ?.rawValue;
482
- const firstDeathDate = firstDeath?.DATE
483
- ?.rawValue;
484
- if (firstBirthDate || firstDeathDate) {
485
- const isBirth = !firstBirthDate ||
486
- (firstDeathDate && firstDeathDate < firstBirthDate)
487
- ? false
488
- : true;
489
- firstPersonEvent = {
490
- type: isBirth ? "BIRT" : "DEAT",
491
- event: isBirth ? firstBirth : firstDeath,
492
- person: firstPerson,
493
- };
494
- }
495
- const lastPerson = indis?.getLastEvent();
496
- const lastBirth = lastPerson?.BIRT?.toList().index(0);
497
- const lastDeath = lastPerson?.DEAT?.toList().index(0);
498
- let lastPersonEvent = null;
499
- const lastBirthDate = lastBirth
500
- ?.DATE?.rawValue;
501
- const lastDeathDate = lastDeath
502
- ?.DATE?.rawValue;
503
- if (lastBirthDate || lastDeathDate) {
504
- const isBirth = !lastDeathDate ||
505
- (lastBirthDate && lastDeathDate < lastBirthDate)
506
- ? true
507
- : false;
508
- lastPersonEvent = {
509
- type: isBirth ? "BIRT" : "DEAT",
510
- event: isBirth ? lastBirth : lastDeath,
511
- person: lastPerson,
512
- };
513
- }
514
- return {
515
- totalIndividuals,
516
- totalFamilies,
517
- byGender: {
518
- males,
519
- females,
520
- unknown: unknownSex,
521
- },
522
- dateRange: {
523
- earliest: minYear,
524
- latest: maxYear,
525
- },
526
- averageLifespan: avgLifespan
527
- ? Math.round(avgLifespan * 10) / 10
528
- : null,
529
- topSurnames,
530
- topBirthPlaces,
531
- firstPersonEvent,
532
- lastPersonEvent,
533
- };
534
- }
535
- }
536
- export const createGedCom = () => {
537
- return new GedCom();
538
- };
539
- export const isGedcomString = (gedcomString) => {
540
- return (typeof gedcomString === "string" &&
541
- gedcomString[0] === "0" &&
542
- gedcomString
543
- .split("\n")
544
- .slice(0, 1000)
545
- .every((l) => !l || /^\d+\s+/.test(l)));
546
- };
547
- /**
548
- * Validates if a string is a valid GEDCOM file content
549
- * @param content - The file content to validate
550
- * @returns An object with `valid` boolean and optional `error` message
551
- */
552
- export const validateGedcomContent = (content) => {
553
- if (!content) {
554
- return { valid: false, error: "File is empty" };
555
- }
556
- const trimmedContent = content.trim();
557
- if (!trimmedContent) {
558
- return { valid: false, error: "File is empty" };
559
- }
560
- // Check for common binary file signatures
561
- const firstBytes = trimmedContent.substring(0, 10);
562
- const isBinary = firstBytes.startsWith("\x89PNG") || // PNG
563
- firstBytes.startsWith("\xFF\xD8\xFF") || // JPEG
564
- firstBytes.startsWith("GIF8") || // GIF
565
- firstBytes.startsWith("BM") || // BMP
566
- firstBytes.startsWith("PK") || // ZIP
567
- firstBytes.startsWith("%PDF") || // PDF
568
- // eslint-disable-next-line no-control-regex
569
- /[\x00-\x08\x0E-\x1F]/.test(firstBytes); // Other binary content
570
- if (isBinary) {
571
- return {
572
- valid: false,
573
- error: "File appears to be a binary file (image, PDF, etc.), not a GEDCOM text file",
574
- };
575
- }
576
- // Check if content starts with GEDCOM header
577
- // GEDCOM files must start with "0 HEAD" or "0 head" (case-insensitive)
578
- const startsWithHeader = /^0\s+(HEAD|head)/i.test(trimmedContent);
579
- if (!startsWithHeader) {
580
- return {
581
- valid: false,
582
- error: "Invalid GEDCOM file format. File must start with '0 HEAD' record",
583
- };
584
- }
585
- /**
586
- * Turning this off, because it's invalidating multiline gedcom property
587
- * (I know these kind of GEDCOMs are not standard, but need to allow any user files)
588
- **/
589
- // Additional check using the existing isGedcomString logic
590
- // if (!isGedcomString(trimmedContent)) {
591
- // return {
592
- // valid: false,
593
- // error: "Invalid GEDCOM file format. File contains invalid line formats",
594
- // };
595
- // }
596
- return { valid: true };
597
- };
598
- // Constants for merge operation
599
- const MERGE_ID_INCREMENT_MULTIPLIER = 1000;
600
- /**
601
- * Helper function to convert a Common or List value to a string for comparison
602
- */
603
- const valueToString = (value) => {
604
- if (!value)
605
- return "";
606
- return (value.toString?.() ||
607
- String(value?.toValue?.() || ""));
608
- };
609
- /**
610
- * Merge two GEDCOM objects into a single result using a configurable matching strategy
611
- * @param targetGedcom - The base GEDCOM (kept as the primary source)
612
- * @param sourceGedcom - The GEDCOM to be merged into the target
613
- * @param strategy - Matching strategy: "id" (default) to match by individual ID, or any MultiTag (e.g., "NAME", "BIRT.DATE") to match by that field's value
614
- * @returns The merged GedComType with all individuals and families combined
615
- *
616
- * @example
617
- * // Merge by ID (individuals with same ID are considered the same person)
618
- * const merged = await mergeGedcoms(target, source, "id");
619
- *
620
- * @example
621
- * // Merge by NAME (individuals with same name are considered the same person)
622
- * const merged = await mergeGedcoms(target, source, "NAME");
623
- *
624
- * @remarks
625
- * - Source individuals are always assigned new unique IDs to avoid conflicts
626
- * - When individuals match by strategy, they are merged (data and relationships combined)
627
- * - All family relationships (FAMS/FAMC) are preserved with updated ID references
628
- */
629
- export const mergeGedcoms = (targetGedcom, sourceGedcom, strategy = "id") => {
630
- // Work directly with the target GEDCOM (no serialization needed)
631
- const mergedGedcom = targetGedcom;
632
- // Track ID mapping: source ID -> new ID in merged GEDCOM
633
- const idMap = new Map();
634
- // Track matching: sourceIndiId -> targetIndiId (for individuals that match by strategy)
635
- const matchMap = new Map();
636
- // Get source individuals and families
637
- const sourceIndis = sourceGedcom.indis();
638
- const sourceFams = sourceGedcom.fams();
639
- const targetIndis = mergedGedcom.indis();
640
- const targetFams = mergedGedcom.fams();
641
- // Step 1: Identify matches and create ID mappings for individuals
642
- sourceIndis?.forEach((sourceIndi) => {
643
- if (!sourceIndi.id)
644
- return;
645
- let matchedTargetIndi;
646
- if (strategy === "id") {
647
- // Match by ID directly - check if target has this exact ID
648
- matchedTargetIndi = targetIndis?.item(sourceIndi.id);
649
- }
650
- else {
651
- // Match by specified MultiTag value
652
- const sourceValueRaw = sourceIndi.get(strategy);
653
- const sourceValue = valueToString(sourceValueRaw);
654
- if (sourceValue) {
655
- // Find target individual with same value
656
- matchedTargetIndi = targetIndis?.find((targetIndi) => {
657
- const targetValueRaw = targetIndi.get(strategy);
658
- const targetValue = valueToString(targetValueRaw);
659
- return Boolean(targetValue && targetValue === sourceValue);
660
- });
661
- }
662
- }
663
- if (matchedTargetIndi && matchedTargetIndi.id) {
664
- // Found a match - map source ID to existing target ID
665
- // Note: This means source individual will merge into target individual
666
- matchMap.set(sourceIndi.id, matchedTargetIndi.id);
667
- idMap.set(sourceIndi.id, matchedTargetIndi.id);
668
- }
669
- else {
670
- // No match - need to create a new unique ID
671
- const baseId = sourceIndi.id;
672
- let newId = baseId;
673
- let counter = 1;
674
- // Generate unique ID that doesn't conflict with target
675
- while (targetIndis?.item(newId) || idMap.has(newId)) {
676
- // Extract number from ID like @I123@ and increment
677
- const numMatch = baseId.match(/\d+/);
678
- const prefix = baseId.match(/^@[A-Z]+/)?.[0] || "@I";
679
- const baseNum = numMatch ? parseInt(numMatch[0]) : 1;
680
- newId =
681
- `${prefix}${baseNum + counter * MERGE_ID_INCREMENT_MULTIPLIER}@`;
682
- counter++;
683
- }
684
- idMap.set(sourceIndi.id, newId);
685
- }
686
- });
687
- // Step 2: Map family IDs and match families with same members
688
- const famMatchMap = new Map(); // source family ID -> target family ID
689
- sourceFams?.forEach((sourceFam) => {
690
- if (!sourceFam.id)
691
- return;
692
- // Get the member IDs for this source family
693
- const sourceHusbId = sourceFam.HUSB?.value;
694
- const sourceWifeId = sourceFam.WIFE?.value;
695
- const sourceChildIds = sourceFam.CHIL?.toList()
696
- ?.map((c) => c.value)
697
- .filter(Boolean) || [];
698
- // Map to their final IDs (after individual matching by strategy)
699
- const finalHusbId = sourceHusbId
700
- ? idMap.get(sourceHusbId)
701
- : undefined;
702
- const finalWifeId = sourceWifeId
703
- ? idMap.get(sourceWifeId)
704
- : undefined;
705
- const finalChildIds = sourceChildIds
706
- .map((id) => idMap.get(id))
707
- .filter(Boolean);
708
- // Try to find a matching family in target that has the same members (after remapping)
709
- let matchedTargetFam;
710
- targetFams?.forEach((targetFam) => {
711
- if (matchedTargetFam)
712
- return; // Already found a match
713
- const targetHusbId = targetFam.HUSB?.value;
714
- const targetWifeId = targetFam.WIFE?.value;
715
- const targetChildIds = targetFam.CHIL?.toList()
716
- ?.map((c) => c.value)
717
- .filter(Boolean) || [];
718
- // Check if husband matches (either both undefined or same final ID)
719
- const husbMatch = (!finalHusbId && !targetHusbId) || finalHusbId === targetHusbId;
720
- // Check if wife matches (either both undefined or same final ID)
721
- const wifeMatch = (!finalWifeId && !targetWifeId) || finalWifeId === targetWifeId;
722
- // For a family to match, both husband and wife must match
723
- // (if one is undefined in both, that's also a match)
724
- if (husbMatch && wifeMatch) {
725
- // Also check children overlap
726
- const childOverlap = finalChildIds.filter((id) => id && targetChildIds.includes(id)).length;
727
- const totalUniqueChildren = new Set([
728
- ...finalChildIds,
729
- ...targetChildIds,
730
- ]).size;
731
- // Match if:
732
- // 1. At least one spouse is present (to avoid matching empty families), OR
733
- // 2. At least 50% of children overlap
734
- const hasSpouses = finalHusbId ||
735
- finalWifeId ||
736
- targetHusbId ||
737
- targetWifeId;
738
- const hasMatchingChildren = totalUniqueChildren > 0 &&
739
- childOverlap / totalUniqueChildren >= 0.5;
740
- if (hasSpouses || hasMatchingChildren) {
741
- matchedTargetFam = targetFam;
742
- }
743
- }
744
- });
745
- if (matchedTargetFam && matchedTargetFam.id) {
746
- // Found a matching family - use the target family ID
747
- famMatchMap.set(sourceFam.id, matchedTargetFam.id);
748
- idMap.set(sourceFam.id, matchedTargetFam.id);
749
- }
750
- else {
751
- // No match - generate a new unique family ID
752
- const baseId = sourceFam.id;
753
- let newId = baseId;
754
- let counter = 1;
755
- // Generate unique family ID
756
- while (targetFams?.item(newId) || idMap.has(newId)) {
757
- const numMatch = baseId.match(/\d+/);
758
- const prefix = baseId.match(/^@[A-Z]+/)?.[0] || "@F";
759
- const baseNum = numMatch ? parseInt(numMatch[0]) : 1;
760
- newId =
761
- `${prefix}${baseNum + counter * MERGE_ID_INCREMENT_MULTIPLIER}@`;
762
- counter++;
763
- }
764
- idMap.set(sourceFam.id, newId);
765
- }
766
- });
767
- // Step 3: Clone and remap individuals from source
768
- const clonedIndis = new Map();
769
- sourceIndis?.forEach((sourceIndi) => {
770
- if (!sourceIndi.id)
771
- return;
772
- const newId = idMap.get(sourceIndi.id);
773
- if (!newId)
774
- return;
775
- // Clone the individual
776
- const cloned = sourceIndi.clone(false);
777
- cloned.id = newId;
778
- cloned.setGedcom(mergedGedcom);
779
- clonedIndis.set(sourceIndi.id, cloned);
780
- });
781
- // Step 4: Clone and remap families from source
782
- const clonedFams = new Map();
783
- sourceFams?.forEach((sourceFam) => {
784
- if (!sourceFam.id)
785
- return;
786
- const newFamId = idMap.get(sourceFam.id);
787
- if (!newFamId)
788
- return;
789
- // Clone the family
790
- const clonedFam = sourceFam.clone(false);
791
- clonedFam.id = newFamId;
792
- clonedFam.setGedcom(mergedGedcom);
793
- clonedFams.set(sourceFam.id, clonedFam);
794
- });
795
- // Step 5: Update FAMS and FAMC references in cloned individuals
796
- clonedIndis.forEach((clonedIndi, originalId) => {
797
- const sourceIndi = sourceIndis?.item(originalId);
798
- if (!sourceIndi)
799
- return;
800
- // Clear and rebuild FAMS references with remapped IDs
801
- clonedIndi.remove("FAMS");
802
- const sourceFAMS = sourceIndi.FAMS?.toList();
803
- sourceFAMS?.forEach((famRef) => {
804
- const oldFamId = famRef.value;
805
- if (oldFamId) {
806
- const newFamId = idMap.get(oldFamId);
807
- if (newFamId) {
808
- const newFamRef = createCommon(mergedGedcom, undefined, clonedIndi);
809
- newFamRef.value = newFamId;
810
- clonedIndi.assign("FAMS", newFamRef, true);
811
- }
812
- }
813
- });
814
- // Clear and rebuild FAMC references with remapped IDs
815
- clonedIndi.remove("FAMC");
816
- const sourceFAMC = sourceIndi.FAMC?.toList();
817
- sourceFAMC?.forEach((famRef) => {
818
- const oldFamId = famRef.value;
819
- if (oldFamId) {
820
- const newFamId = idMap.get(oldFamId);
821
- if (newFamId) {
822
- const newFamRef = createCommon(mergedGedcom, undefined, clonedIndi);
823
- newFamRef.value = newFamId;
824
- clonedIndi.assign("FAMC", newFamRef, true);
825
- }
826
- }
827
- });
828
- });
829
- // Step 6: Update HUSB, WIFE, and CHIL references in cloned families
830
- clonedFams.forEach((clonedFam, originalFamId) => {
831
- const sourceFam = sourceFams?.item(originalFamId);
832
- if (!sourceFam)
833
- return;
834
- // Update HUSB reference
835
- const sourceHusb = sourceFam.HUSB?.value;
836
- if (sourceHusb) {
837
- const newHusbId = idMap.get(sourceHusb);
838
- if (newHusbId) {
839
- const newHusbRef = createCommon(mergedGedcom, undefined, clonedFam);
840
- newHusbRef.value = newHusbId;
841
- clonedFam.set("HUSB", newHusbRef);
842
- }
843
- }
844
- // Update WIFE reference
845
- const sourceWife = sourceFam.WIFE?.value;
846
- if (sourceWife) {
847
- const newWifeId = idMap.get(sourceWife);
848
- if (newWifeId) {
849
- const newWifeRef = createCommon(mergedGedcom, undefined, clonedFam);
850
- newWifeRef.value = newWifeId;
851
- clonedFam.set("WIFE", newWifeRef);
852
- }
853
- }
854
- // Update CHIL references
855
- clonedFam.remove("CHIL");
856
- const sourceChildren = sourceFam.CHIL?.toList();
857
- sourceChildren?.forEach((childRef) => {
858
- const oldChildId = childRef.value;
859
- if (oldChildId) {
860
- const newChildId = idMap.get(oldChildId);
861
- if (newChildId) {
862
- const newChildRef = createCommon(mergedGedcom, undefined, clonedFam);
863
- newChildRef.value = newChildId;
864
- clonedFam.assign("CHIL", newChildRef, true);
865
- }
866
- }
867
- });
868
- });
869
- // Step 7: Add or merge individuals into target GEDCOM
870
- clonedIndis.forEach((clonedIndi, originalId) => {
871
- const matchedTargetId = matchMap.get(originalId);
872
- if (matchedTargetId) {
873
- // This individual matches an existing one - merge data and relationships
874
- const targetIndi = mergedGedcom.indis()?.item(matchedTargetId);
875
- if (targetIndi) {
876
- // Merge without overriding existing data
877
- // If using a strategy field (not "id"), avoid merging that field since it's already the same
878
- const avoidKeys = strategy !== "id" ? [strategy] : [];
879
- targetIndi.merge(clonedIndi, false, avoidKeys);
880
- }
881
- }
882
- else {
883
- // This is a new individual - add it to merged GEDCOM
884
- if (clonedIndi.id) {
885
- mergedGedcom.indis()?.item(clonedIndi.id, clonedIndi);
886
- }
887
- }
888
- });
889
- // Step 8: Add or merge families into target GEDCOM
890
- clonedFams.forEach((clonedFam, originalFamId) => {
891
- if (!clonedFam.id)
892
- return;
893
- const matchedTargetFamId = famMatchMap.get(originalFamId);
894
- if (matchedTargetFamId) {
895
- // This family matches an existing one - merge children and data
896
- const targetFam = mergedGedcom.fams()?.item(matchedTargetFamId);
897
- if (targetFam) {
898
- // Merge family data without overriding
899
- targetFam.merge(clonedFam, false);
900
- // Note: Children from clonedFam are already added via merge above
901
- // The CHIL references in clonedFam point to the correct remapped IDs
902
- }
903
- }
904
- else {
905
- // This is a new family - add it to merged GEDCOM
906
- mergedGedcom.fams()?.item(clonedFam.id, clonedFam);
907
- }
908
- });
909
- return mergedGedcom;
910
- };