@treeviz/gedcom-parser 2.0.9 → 2.0.12

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 (50) hide show
  1. package/dist/classes/common.d.ts +6 -1
  2. package/dist/classes/common.d.ts.map +1 -1
  3. package/dist/classes/fam.d.ts +9 -1
  4. package/dist/classes/fam.d.ts.map +1 -1
  5. package/dist/classes/gedcom.d.ts +6 -2
  6. package/dist/classes/gedcom.d.ts.map +1 -1
  7. package/dist/classes/index.js +740 -51
  8. package/dist/classes/indi.d.ts +6 -0
  9. package/dist/classes/indi.d.ts.map +1 -1
  10. package/dist/cli/index.js +3194 -2499
  11. package/dist/constants/constants.d.ts +7 -0
  12. package/dist/constants/constants.d.ts.map +1 -1
  13. package/dist/constants/index.js +1531 -916
  14. package/dist/factories/index.js +654 -40
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5201 -4407
  18. package/dist/interfaces/fam.d.ts +7 -1
  19. package/dist/interfaces/fam.d.ts.map +1 -1
  20. package/dist/interfaces/gedcom.d.ts +4 -0
  21. package/dist/interfaces/gedcom.d.ts.map +1 -1
  22. package/dist/interfaces/indi.d.ts +5 -1
  23. package/dist/interfaces/indi.d.ts.map +1 -1
  24. package/dist/kinship-translator/index.d.ts +1 -0
  25. package/dist/kinship-translator/index.d.ts.map +1 -1
  26. package/dist/kinship-translator/index.js +655 -41
  27. package/dist/kinship-translator/kinship-translator.ru.d.ts +18 -0
  28. package/dist/kinship-translator/kinship-translator.ru.d.ts.map +1 -0
  29. package/dist/kinship-translator/patterns.ru.d.ts +2 -0
  30. package/dist/kinship-translator/patterns.ru.d.ts.map +1 -0
  31. package/dist/kinship-translator/translators.d.ts +1 -0
  32. package/dist/kinship-translator/translators.d.ts.map +1 -1
  33. package/dist/kinship-translator/types.d.ts +1 -1
  34. package/dist/kinship-translator/types.d.ts.map +1 -1
  35. package/dist/structures/gedcom.d.ts +1 -0
  36. package/dist/structures/gedcom.d.ts.map +1 -1
  37. package/dist/types/types.d.ts +10 -0
  38. package/dist/types/types.d.ts.map +1 -1
  39. package/dist/utils/fact-edit.d.ts +22 -0
  40. package/dist/utils/fact-edit.d.ts.map +1 -0
  41. package/dist/utils/family-edit.d.ts +23 -0
  42. package/dist/utils/family-edit.d.ts.map +1 -0
  43. package/dist/utils/gedcom-trailer.d.ts +6 -0
  44. package/dist/utils/gedcom-trailer.d.ts.map +1 -0
  45. package/dist/utils/index.d.ts +4 -1
  46. package/dist/utils/index.d.ts.map +1 -1
  47. package/dist/utils/index.js +850 -56
  48. package/dist/utils/parser.d.ts +3 -0
  49. package/dist/utils/parser.d.ts.map +1 -1
  50. package/package.json +1 -1
@@ -77,12 +77,33 @@ __export(translators_exports, {
77
77
  en: () => KinshipTranslatorEn,
78
78
  es: () => KinshipTranslatorEs,
79
79
  fr: () => KinshipTranslatorFr,
80
- hu: () => KinshipTranslatorHU
80
+ hu: () => KinshipTranslatorHU,
81
+ ru: () => KinshipTranslatorRu
81
82
  });
82
83
 
83
84
  // src/constants/constants.ts
85
+ var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
84
86
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
85
87
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
88
+ var ORIG_HEAD_TAG = "_ORIGHEAD";
89
+
90
+ // src/utils/gedcom-trailer.ts
91
+ var TRAILER_LINE = /^0 TRLR\s*$/i;
92
+ var GEDCOM_TRAILER_TAG = "TRLR";
93
+ var isGedcomTrailerTag = (tag) => tag === GEDCOM_TRAILER_TAG;
94
+ var isGedcomTrailerLine = (line) => TRAILER_LINE.test(line);
95
+ var appendGedcomTrailer = (serialized) => {
96
+ const newline = serialized.includes("\r\n") ? "\r\n" : "\n";
97
+ const trimmed = serialized.replace(/(?:\r?\n)+$/, "");
98
+ if (!trimmed) {
99
+ return "0 TRLR";
100
+ }
101
+ const lastLine = trimmed.split(/\r?\n/).at(-1) ?? "";
102
+ if (isGedcomTrailerLine(lastLine)) {
103
+ return trimmed;
104
+ }
105
+ return `${trimmed}${newline}0 TRLR`;
106
+ };
86
107
  var List = class _List {
87
108
  // length = 0;
88
109
  constructor(items) {
@@ -593,6 +614,9 @@ var Common = class _Common {
593
614
  delete this._value;
594
615
  }
595
616
  set(name, value) {
617
+ if (isGedcomTrailerTag(name)) {
618
+ return void 0;
619
+ }
596
620
  if (typeof value === "string") {
597
621
  const usedValue = createCommon(this._gedcom, void 0, this.main);
598
622
  usedValue.value = value;
@@ -603,6 +627,9 @@ var Common = class _Common {
603
627
  return get(this, name);
604
628
  }
605
629
  assign(name, value, unique = false) {
630
+ if (isGedcomTrailerTag(name)) {
631
+ return void 0;
632
+ }
606
633
  let curValue = this.get(name);
607
634
  if (curValue?.isListable) {
608
635
  if (curValue instanceof _Common) {
@@ -825,30 +852,47 @@ var Common = class _Common {
825
852
  });
826
853
  return gedcom;
827
854
  }
855
+ getHeadRecord() {
856
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
857
+ }
858
+ getOriginalHeadRecord() {
859
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
860
+ }
861
+ getSourceHeads() {
862
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
863
+ return heads.filter((head, index) => {
864
+ return Boolean(head && heads.indexOf(head) === index);
865
+ });
866
+ }
867
+ getFromSourceHeads(path) {
868
+ for (const head of this.getSourceHeads()) {
869
+ const value = get(head, path);
870
+ if (value !== void 0 && value !== null && value !== "") {
871
+ return value;
872
+ }
873
+ }
874
+ return void 0;
875
+ }
876
+ sourStartsWith(prefix) {
877
+ return this.getSourceHeads().some((head) => {
878
+ const sour = get(head, "SOUR.value");
879
+ return !!sour?.toLowerCase()?.startsWith(prefix);
880
+ });
881
+ }
828
882
  isGenoPro() {
829
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
830
- const sour = get(head, "SOUR.value");
831
- return !!sour?.toLowerCase()?.startsWith("genopro");
883
+ return this.sourStartsWith("genopro");
832
884
  }
833
885
  isAhnenblatt() {
834
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
835
- const sour = get(head, "SOUR.value");
836
- return !!sour?.toLowerCase()?.startsWith("ahnenblatt");
886
+ return this.sourStartsWith("ahnenblatt");
837
887
  }
838
888
  isGeni() {
839
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
840
- const sour = get(head, "SOUR.value");
841
- return !!sour?.toLowerCase()?.startsWith("geni");
889
+ return this.sourStartsWith("geni");
842
890
  }
843
891
  isAncestry() {
844
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
845
- const sour = get(head, "SOUR.value");
846
- return !!sour?.toLowerCase()?.startsWith("ancestry");
892
+ return this.sourStartsWith("ancestry");
847
893
  }
848
894
  isMyHeritage() {
849
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
850
- const sour = get(head, "SOUR.value");
851
- return !!sour?.toLowerCase()?.startsWith("myheritage");
895
+ return this.sourStartsWith("myheritage");
852
896
  }
853
897
  /**
854
898
  * Get the source type as a string (for prefixing tree IDs and names)
@@ -865,22 +909,22 @@ var Common = class _Common {
865
909
  return void 0;
866
910
  }
867
911
  isFamilySearch() {
868
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
869
- const sourName = get(head, "SOUR.NAME.value");
870
- return sourName === "FamilySearch API";
912
+ return this.getSourceHeads().some((head) => {
913
+ const sourName = get(head, "SOUR.NAME.value");
914
+ return sourName === "FamilySearch API";
915
+ });
871
916
  }
872
917
  isGNO2GED() {
873
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
874
- const sour = get(head, "SOUR.value");
875
- return sour === "GNO2GED";
918
+ return this.getSourceHeads().some((head) => {
919
+ const sour = get(head, "SOUR.value");
920
+ return sour === "GNO2GED";
921
+ });
876
922
  }
877
923
  getAncestryTreeId() {
878
- const path = "HEAD.SOUR._TREE.RIN.value";
879
- return get(this, path) || get(this.getGedcom(), path);
924
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
880
925
  }
881
926
  getMyHeritageTreeId() {
882
- const path = "HEAD._EXPORTED_FROM_SITE_ID.value";
883
- return get(this, path) || get(this.getGedcom(), path);
927
+ return this.getFromSourceHeads("_EXPORTED_FROM_SITE_ID.value") || (get(this, "HEAD._EXPORTED_FROM_SITE_ID.value") || get(this.getGedcom(), "HEAD._EXPORTED_FROM_SITE_ID.value"));
884
928
  }
885
929
  getTreeId() {
886
930
  if (this?.isAncestry()) {
@@ -914,8 +958,7 @@ var Common = class _Common {
914
958
  return this.getUniversalTreeId();
915
959
  }
916
960
  getAncestryTreeName() {
917
- const path = "HEAD.SOUR._TREE.value";
918
- return get(this, path) || get(this.getGedcom(), path);
961
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
919
962
  }
920
963
  getMyHeritageTreeName() {
921
964
  const path = "HEAD.FILE.value";
@@ -925,14 +968,14 @@ var Common = class _Common {
925
968
  )?.groups?.tree;
926
969
  }
927
970
  getFamilySearchTreeId() {
928
- const rin = get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
971
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
929
972
  if (rin) {
930
973
  return rin;
931
974
  }
932
975
  return "familysearch";
933
976
  }
934
977
  getFamilySearchTreeName() {
935
- const treeName = get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
978
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
936
979
  if (treeName) {
937
980
  return treeName;
938
981
  }
@@ -1024,11 +1067,11 @@ var Common = class _Common {
1024
1067
  const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1025
1068
  return `${prefix}${cleanName}`;
1026
1069
  }
1027
- const sourName = get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1070
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1028
1071
  if (sourName) {
1029
1072
  return `${prefix}${sourName}`;
1030
1073
  }
1031
- const sourValue = get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1074
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1032
1075
  if (sourValue) {
1033
1076
  return `${prefix}${sourValue}`;
1034
1077
  }
@@ -1094,7 +1137,7 @@ var isOnlyMainProp = (key) => {
1094
1137
  };
1095
1138
  var isValidKey = (common, key) => {
1096
1139
  const prop = get(common, key);
1097
- return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
1140
+ return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && !(typeof key === "string" && isGedcomTrailerTag(key)) && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
1098
1141
  };
1099
1142
  var getValidKeys = (common) => {
1100
1143
  return Object.keys(common).filter((key) => {
@@ -2574,6 +2617,249 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
2574
2617
  }
2575
2618
  };
2576
2619
 
2620
+ // src/kinship-translator/patterns.ru.ts
2621
+ var InLawsRu = {
2622
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
2623
+ "\u0436\u0435\u043D\u0430 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
2624
+ "\u043C\u0443\u0436 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C",
2625
+ "\u043C\u0430\u0442\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0451\u0449\u0430",
2626
+ "\u043E\u0442\u0435\u0446 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C",
2627
+ "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C/\u0442\u0451\u0449\u0430",
2628
+ "\u0436\u0435\u043D\u0430 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
2629
+ "\u043C\u0443\u0436 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C",
2630
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
2631
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0435\u0441\u0442\u0440\u0430 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0437\u043E\u043B\u043E\u0432\u043A\u0430",
2632
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0431\u0440\u0430\u0442 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D",
2633
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0438\u0431\u043B\u0438\u043D\u0433 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D/\u0437\u043E\u043B\u043E\u0432\u043A\u0430"
2634
+ };
2635
+
2636
+ // src/kinship-translator/kinship-translator.ru.ts
2637
+ var GENITIVE = {
2638
+ \u0441\u044B\u043D: "\u0441\u044B\u043D\u0430",
2639
+ \u0434\u043E\u0447\u044C: "\u0434\u043E\u0447\u0435\u0440\u0438",
2640
+ \u0440\u0435\u0431\u0451\u043D\u043E\u043A: "\u0440\u0435\u0431\u0451\u043D\u043A\u0430",
2641
+ \u0431\u0440\u0430\u0442: "\u0431\u0440\u0430\u0442\u0430",
2642
+ \u0441\u0435\u0441\u0442\u0440\u0430: "\u0441\u0435\u0441\u0442\u0440\u044B",
2643
+ \u0441\u0438\u0431\u043B\u0438\u043D\u0433: "\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430",
2644
+ \u043E\u0442\u0435\u0446: "\u043E\u0442\u0446\u0430",
2645
+ \u043C\u0430\u0442\u044C: "\u043C\u0430\u0442\u0435\u0440\u0438",
2646
+ \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C: "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044F",
2647
+ \u043C\u0443\u0436: "\u043C\u0443\u0436\u0430",
2648
+ \u0436\u0435\u043D\u0430: "\u0436\u0435\u043D\u044B",
2649
+ \u0441\u0443\u043F\u0440\u0443\u0433: "\u0441\u0443\u043F\u0440\u0443\u0433\u0430",
2650
+ \u0441\u0443\u043F\u0440\u0443\u0433\u0430: "\u0441\u0443\u043F\u0440\u0443\u0433\u0438"
2651
+ };
2652
+ var toGenitive = (word) => GENITIVE[word] ?? word;
2653
+ var cousinWord = (degree, sex) => {
2654
+ const stem = degree <= 1 ? "\u0434\u0432\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 2 ? "\u0442\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 3 ? "\u0447\u0435\u0442\u0432\u0435\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : `${degree + 1}-\u044E\u0440\u043E\u0434\u043D`;
2655
+ if (sex === "m") {
2656
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442`;
2657
+ }
2658
+ if (sex === "f") {
2659
+ return `${stem}\u0430\u044F \u0441\u0435\u0441\u0442\u0440\u0430`;
2660
+ }
2661
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442/\u0441\u0435\u0441\u0442\u0440\u0430`;
2662
+ };
2663
+ var praRepeat = (count) => count > 0 ? "\u043F\u0440\u0430".repeat(count) : "";
2664
+ var sexOf = (person) => {
2665
+ if (person?.isMale?.()) {
2666
+ return "m";
2667
+ }
2668
+ if (person?.isFemale?.()) {
2669
+ return "f";
2670
+ }
2671
+ return "n";
2672
+ };
2673
+ var KinshipTranslatorRu = class extends KinshipTranslatorBasic {
2674
+ indirect() {
2675
+ let degree = Math.abs(this.pathN?.degree ?? 0);
2676
+ if (degree > 0) {
2677
+ degree = degree - 1;
2678
+ }
2679
+ return cousinWord(degree, sexOf(this.personN));
2680
+ }
2681
+ removal() {
2682
+ const level = this.pathN?.level ?? 0;
2683
+ let degree = Math.abs(this.pathN?.degree ?? 0);
2684
+ if (degree > 0) {
2685
+ degree = degree - 1;
2686
+ }
2687
+ if (!degree) {
2688
+ return level < 0 ? this.nibling() : this.auncle();
2689
+ }
2690
+ const removed = Math.abs(level);
2691
+ return `${cousinWord(degree, sexOf(this.personN))} (${removed}\xD7 \u0447\u0435\u0440\u0435\u0437 \u043F\u043E\u043A\u043E\u043B\u0435\u043D\u0438\u0435)`;
2692
+ }
2693
+ auncle() {
2694
+ const level = Math.abs(this.pathN?.level ?? 0);
2695
+ const pra = praRepeat(Math.max(0, level - 2));
2696
+ const sex = sexOf(this.personN);
2697
+ if (sex === "m") {
2698
+ return `${pra}\u0434\u044F\u0434\u044F`;
2699
+ }
2700
+ if (sex === "f") {
2701
+ return `${pra}\u0442\u0451\u0442\u044F`;
2702
+ }
2703
+ return `${pra}\u0434\u044F\u0434\u044F/\u0442\u0451\u0442\u044F`;
2704
+ }
2705
+ nibling() {
2706
+ const level = Math.abs(this.pathN?.level ?? 0);
2707
+ const pra = praRepeat(Math.max(0, level - 2));
2708
+ const sex = sexOf(this.personN);
2709
+ if (sex === "m") {
2710
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A`;
2711
+ }
2712
+ if (sex === "f") {
2713
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
2714
+ }
2715
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A/\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
2716
+ }
2717
+ parent() {
2718
+ const level = Math.abs(this.pathN?.level ?? 0);
2719
+ const sex = sexOf(this.personN);
2720
+ if (level <= 1) {
2721
+ if (sex === "m") {
2722
+ return "\u043E\u0442\u0435\u0446";
2723
+ }
2724
+ if (sex === "f") {
2725
+ return "\u043C\u0430\u0442\u044C";
2726
+ }
2727
+ return "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C";
2728
+ }
2729
+ if (level === 2) {
2730
+ if (sex === "m") {
2731
+ return "\u0434\u0435\u0434";
2732
+ }
2733
+ if (sex === "f") {
2734
+ return "\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
2735
+ }
2736
+ return "\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
2737
+ }
2738
+ const pra = praRepeat(level - 2);
2739
+ if (sex === "m") {
2740
+ return `${pra}\u0434\u0435\u0434`;
2741
+ }
2742
+ if (sex === "f") {
2743
+ return `${pra}\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
2744
+ }
2745
+ return `${pra}\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
2746
+ }
2747
+ child() {
2748
+ const level = Math.abs(this.pathN?.level ?? 0);
2749
+ const sex = sexOf(this.personN);
2750
+ if (level <= 1) {
2751
+ if (sex === "m") {
2752
+ return "\u0441\u044B\u043D";
2753
+ }
2754
+ if (sex === "f") {
2755
+ return "\u0434\u043E\u0447\u044C";
2756
+ }
2757
+ return "\u0440\u0435\u0431\u0451\u043D\u043E\u043A";
2758
+ }
2759
+ if (level === 2) {
2760
+ if (sex === "m") {
2761
+ return "\u0432\u043D\u0443\u043A";
2762
+ }
2763
+ if (sex === "f") {
2764
+ return "\u0432\u043D\u0443\u0447\u043A\u0430";
2765
+ }
2766
+ return "\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430";
2767
+ }
2768
+ const pra = praRepeat(level - 2);
2769
+ if (sex === "m") {
2770
+ return `${pra}\u0432\u043D\u0443\u043A`;
2771
+ }
2772
+ if (sex === "f") {
2773
+ return `${pra}\u0432\u043D\u0443\u0447\u043A\u0430`;
2774
+ }
2775
+ return `${pra}\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430`;
2776
+ }
2777
+ sibling() {
2778
+ const sex = sexOf(this.personN);
2779
+ if (sex === "m") {
2780
+ return "\u0431\u0440\u0430\u0442";
2781
+ }
2782
+ if (sex === "f") {
2783
+ return "\u0441\u0435\u0441\u0442\u0440\u0430";
2784
+ }
2785
+ return "\u0441\u0438\u0431\u043B\u0438\u043D\u0433";
2786
+ }
2787
+ halfBlood(relation) {
2788
+ if (!relation || !this.isHalfBlood) {
2789
+ return relation ?? "";
2790
+ }
2791
+ if (/сестра|дочь|мать|тётя|бабушка|жена|племянница|внучка/.test(
2792
+ relation
2793
+ )) {
2794
+ return `\u0441\u0432\u043E\u0434\u043D\u0430\u044F ${relation}`;
2795
+ }
2796
+ return `\u0441\u0432\u043E\u0434\u043D\u044B\u0439 ${relation}`;
2797
+ }
2798
+ spouse() {
2799
+ const sex = sexOf(this.personN);
2800
+ if (sex === "m") {
2801
+ return "\u043C\u0443\u0436";
2802
+ }
2803
+ if (sex === "f") {
2804
+ return "\u0436\u0435\u043D\u0430";
2805
+ }
2806
+ return "\u0441\u0443\u043F\u0440\u0443\u0433";
2807
+ }
2808
+ ofSpouse(relation) {
2809
+ if (!relation) {
2810
+ return "";
2811
+ }
2812
+ const spouse = this.path?.[1].indi;
2813
+ const sex = sexOf(spouse);
2814
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
2815
+ return `${relation} ${toGenitive(spouseType)}`;
2816
+ }
2817
+ spouseOf(relation) {
2818
+ if (!relation) {
2819
+ return "";
2820
+ }
2821
+ const spouse = this.path?.[this.path.length - 1].indi;
2822
+ const sex = sexOf(spouse);
2823
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
2824
+ return `${spouseType} ${toGenitive(relation)}`;
2825
+ }
2826
+ of(relation) {
2827
+ const formattedName = nameFormatter(this.path?.[0]?.indi, {
2828
+ nameOrder: "first-last"
2829
+ });
2830
+ let name = formattedName.inOrder.filter(Boolean).join(" ");
2831
+ if (this.displayName === "none") {
2832
+ name = "";
2833
+ } else if (this.displayName === "givenname") {
2834
+ name = formattedName.givenname;
2835
+ } else if (this.displayName === "surname") {
2836
+ name = `${formattedName.surname}`;
2837
+ }
2838
+ if (!name || !relation) {
2839
+ return relation ?? "";
2840
+ }
2841
+ return `${name}: ${relation}`;
2842
+ }
2843
+ inLaw(relation) {
2844
+ let inLawRelation = relation;
2845
+ Object.keys(InLawsRu).find((pattern) => {
2846
+ const regex = new RegExp(`^${pattern}$`);
2847
+ if (relation?.match(regex)) {
2848
+ inLawRelation = InLawsRu[pattern];
2849
+ return true;
2850
+ }
2851
+ return false;
2852
+ });
2853
+ return inLawRelation ?? "";
2854
+ }
2855
+ relationType(relation) {
2856
+ if (!relation || !this.pathN?.relation) {
2857
+ return relation ?? "";
2858
+ }
2859
+ return `${this.pathN.relation} ${relation}`;
2860
+ }
2861
+ };
2862
+
2577
2863
  // src/kinship-translator/kinship-translator.ts
2578
2864
  var KinshipTranslator = class {
2579
2865
  constructor(person1, person2, lang = "en", entirePath, displayName = "givenname") {
@@ -3566,6 +3852,250 @@ var cacheDiscoveredPath = (gedcom, path) => {
3566
3852
  }
3567
3853
  };
3568
3854
 
3855
+ // src/utils/fact-edit.ts
3856
+ var SINGLETON_TAGS = /* @__PURE__ */ new Set(["BIRT", "DEAT", "SSN", "SEX", "DSCR"]);
3857
+ var firstRecord = (node) => {
3858
+ if (!node) {
3859
+ return void 0;
3860
+ }
3861
+ if (node instanceof List) {
3862
+ return node.index(0);
3863
+ }
3864
+ return node.index(0) ?? node;
3865
+ };
3866
+ var setDateOn = (gedcom, owner, parent, date) => {
3867
+ const trimmed = date.trim();
3868
+ if (!trimmed) {
3869
+ return;
3870
+ }
3871
+ let dateNode = parent.get("DATE");
3872
+ if (!dateNode || dateNode instanceof List) {
3873
+ dateNode = createCommonDate(gedcom, void 0, owner, parent);
3874
+ dateNode.type = "DATE";
3875
+ parent.set("DATE", dateNode);
3876
+ }
3877
+ dateNode.value = trimmed;
3878
+ };
3879
+ var fillFact = (gedcom, owner, event, input) => {
3880
+ if (input.value?.trim()) {
3881
+ event.value = input.value.trim();
3882
+ }
3883
+ if (input.type?.trim()) {
3884
+ event.set("TYPE", input.type.trim());
3885
+ }
3886
+ if (input.date) {
3887
+ setDateOn(gedcom, owner, event, input.date);
3888
+ }
3889
+ if (input.place?.trim()) {
3890
+ event.set("PLAC", input.place.trim());
3891
+ }
3892
+ if (input.note?.trim()) {
3893
+ event.set("NOTE", input.note.trim());
3894
+ }
3895
+ };
3896
+ var addIndividualFact = (indi, input) => {
3897
+ const gedcom = indi.getGedcom();
3898
+ if (!gedcom) {
3899
+ return void 0;
3900
+ }
3901
+ const tag = input.tag;
3902
+ let event = SINGLETON_TAGS.has(tag) ? firstRecord(indi.get(tag)) : void 0;
3903
+ if (!event) {
3904
+ event = createCommon(gedcom, void 0, indi);
3905
+ event.type = tag;
3906
+ if (SINGLETON_TAGS.has(tag)) {
3907
+ indi.set(tag, event);
3908
+ } else {
3909
+ indi.assign(tag, event);
3910
+ }
3911
+ }
3912
+ fillFact(gedcom, indi, event, input);
3913
+ return event;
3914
+ };
3915
+ var addFamilyFact = (fam, input) => {
3916
+ const gedcom = fam.getGedcom();
3917
+ if (!gedcom) {
3918
+ return void 0;
3919
+ }
3920
+ const event = createCommon(gedcom, void 0, fam);
3921
+ event.type = input.tag;
3922
+ fam.assign(input.tag, event);
3923
+ fillFact(gedcom, fam, event, input);
3924
+ return event;
3925
+ };
3926
+
3927
+ // src/utils/family-edit.ts
3928
+ var PEDIGREE_VALUES = [
3929
+ "birth" /* BIRTH */,
3930
+ "adopted" /* ADOPTED */,
3931
+ "foster" /* FOSTER */,
3932
+ "step" /* STEP */,
3933
+ "sealing" /* SEALING */
3934
+ ];
3935
+ var toGedcomPedigree = (value) => {
3936
+ const normalized = String(value || "birth" /* BIRTH */).toLowerCase();
3937
+ if (normalized === "biological" /* BIOLOGICAL */ || normalized === "birth" /* BIRTH */) {
3938
+ return "birth" /* BIRTH */;
3939
+ }
3940
+ const match = PEDIGREE_VALUES.find((item) => item === normalized);
3941
+ return match ?? "birth" /* BIRTH */;
3942
+ };
3943
+ var pointerIds = (tag) => {
3944
+ if (!tag) {
3945
+ return [];
3946
+ }
3947
+ if (tag instanceof List) {
3948
+ return tag.values().map((item) => item?.toValue?.()).filter((value2) => !!value2);
3949
+ }
3950
+ const value = tag.toValue?.();
3951
+ return value ? [String(value)] : [];
3952
+ };
3953
+ var firstPointerId = (tag) => pointerIds(tag)[0];
3954
+ var makePointer = (gedcom, parent, value, refType) => {
3955
+ const pointer = createCommon(gedcom, void 0, parent);
3956
+ pointer.value = value;
3957
+ pointer.refType = refType;
3958
+ return pointer;
3959
+ };
3960
+ var nextRecordId = (ids, prefix) => {
3961
+ let max = 0;
3962
+ ids.forEach((id) => {
3963
+ const match = String(id).match(/(\d+)/);
3964
+ const n = match ? Number.parseInt(match[1], 10) : 0;
3965
+ if (n > max) {
3966
+ max = n;
3967
+ }
3968
+ });
3969
+ return `@${prefix}${max + 1}@`;
3970
+ };
3971
+ var familyHasParent = (fam, indiId) => pointerIds(fam.get("HUSB")).includes(indiId) || pointerIds(fam.get("WIFE")).includes(indiId);
3972
+ var familyHasChild = (fam, indiId) => pointerIds(fam.get("CHIL")).includes(indiId);
3973
+ var spouseTagFor = (indi, fam) => {
3974
+ const husbId = firstPointerId(fam.get("HUSB"));
3975
+ const wifeId = firstPointerId(fam.get("WIFE"));
3976
+ if (husbId === indi.id) {
3977
+ return "HUSB";
3978
+ }
3979
+ if (wifeId === indi.id) {
3980
+ return "WIFE";
3981
+ }
3982
+ if (indi.isFemale()) {
3983
+ return wifeId && wifeId !== indi.id ? "HUSB" : "WIFE";
3984
+ }
3985
+ if (indi.isMale()) {
3986
+ return husbId && husbId !== indi.id ? "WIFE" : "HUSB";
3987
+ }
3988
+ return husbId ? "WIFE" : "HUSB";
3989
+ };
3990
+ var canAttachAsSpouse = (fam, indi) => {
3991
+ if (!indi.id) {
3992
+ return false;
3993
+ }
3994
+ if (familyHasParent(fam, indi.id)) {
3995
+ return true;
3996
+ }
3997
+ const tag = spouseTagFor(indi, fam);
3998
+ const occupied = firstPointerId(fam.get(tag));
3999
+ return !occupied || occupied === indi.id;
4000
+ };
4001
+ var pointerByValue = (tag, id) => {
4002
+ if (!tag) {
4003
+ return void 0;
4004
+ }
4005
+ const fromValues = tag.toValueList?.().item(id);
4006
+ if (fromValues) {
4007
+ return fromValues;
4008
+ }
4009
+ return tag.toList()?.values().find((item) => item?.toValue?.() === id);
4010
+ };
4011
+ var setChildPedigree = (fam, child, pedigree, parent) => {
4012
+ if (!fam.id || !child.id) {
4013
+ return;
4014
+ }
4015
+ const pedi = toGedcomPedigree(pedigree);
4016
+ const famc = pointerByValue(child.get("FAMC"), fam.id);
4017
+ if (famc) {
4018
+ famc.set("PEDI", pedi);
4019
+ }
4020
+ const chil = pointerByValue(fam.get("CHIL"), child.id);
4021
+ if (!chil) {
4022
+ return;
4023
+ }
4024
+ const setFather = !parent || parent.isMale() || parent.isUnknownSex();
4025
+ const setMother = !parent || parent.isFemale() || parent.isUnknownSex();
4026
+ if (setFather) {
4027
+ chil.set("_FREL", pedi);
4028
+ }
4029
+ if (setMother) {
4030
+ chil.set("_MREL", pedi);
4031
+ }
4032
+ };
4033
+ var attachSpouseToFamily = (fam, indi) => {
4034
+ const gedcom = fam.getGedcom() ?? indi.getGedcom();
4035
+ if (!indi.id || !fam.id || !gedcom) {
4036
+ return false;
4037
+ }
4038
+ if (!canAttachAsSpouse(fam, indi)) {
4039
+ return false;
4040
+ }
4041
+ const tag = spouseTagFor(indi, fam);
4042
+ if (firstPointerId(fam.get(tag)) !== indi.id) {
4043
+ fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
4044
+ }
4045
+ indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
4046
+ return true;
4047
+ };
4048
+ var attachChildToFamily = (fam, child, pedigree, parent) => {
4049
+ const gedcom = fam.getGedcom() ?? child.getGedcom();
4050
+ if (!child.id || !fam.id || !gedcom) {
4051
+ return false;
4052
+ }
4053
+ if (!familyHasChild(fam, child.id)) {
4054
+ fam.assign("CHIL", makePointer(gedcom, fam, child.id, "INDI"), true);
4055
+ }
4056
+ const existingFamc = pointerByValue(child.get("FAMC"), fam.id);
4057
+ if (!existingFamc) {
4058
+ child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
4059
+ }
4060
+ setChildPedigree(fam, child, pedigree, parent);
4061
+ return true;
4062
+ };
4063
+ var findReusableParentChildFamily = (parent, child) => {
4064
+ if (!parent.id || !child.id) {
4065
+ return void 0;
4066
+ }
4067
+ let reusableFamc;
4068
+ let already = false;
4069
+ child.getFamilies("FAMC")?.forEach((fam) => {
4070
+ if (!fam) {
4071
+ return;
4072
+ }
4073
+ if (familyHasParent(fam, parent.id) && familyHasChild(fam, child.id)) {
4074
+ already = true;
4075
+ return;
4076
+ }
4077
+ if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
4078
+ reusableFamc = fam;
4079
+ }
4080
+ });
4081
+ if (already) {
4082
+ return "already";
4083
+ }
4084
+ if (reusableFamc) {
4085
+ return reusableFamc;
4086
+ }
4087
+ const parentFams = [];
4088
+ parent.getFamilies("FAMS")?.forEach((fam) => {
4089
+ if (fam) {
4090
+ parentFams.push(fam);
4091
+ }
4092
+ });
4093
+ if (parentFams.length === 1) {
4094
+ return parentFams[0];
4095
+ }
4096
+ return void 0;
4097
+ };
4098
+
3569
4099
  // src/utils/get-places.ts
3570
4100
  var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, mainKey) => {
3571
4101
  const types = Array.isArray(type) ? type : [type];
@@ -4196,6 +4726,31 @@ var Fam = class extends Common {
4196
4726
  getParents() {
4197
4727
  return this.getHusband().copy().merge(this.getWife());
4198
4728
  }
4729
+ hasParent(indi) {
4730
+ const indiId = typeof indi === "string" ? indi : indi?.id;
4731
+ return !!indiId && familyHasParent(this, indiId);
4732
+ }
4733
+ hasChild(indi) {
4734
+ const indiId = typeof indi === "string" ? indi : indi?.id;
4735
+ return !!indiId && familyHasChild(this, indiId);
4736
+ }
4737
+ canAddSpouse(indi) {
4738
+ return canAttachAsSpouse(this, indi);
4739
+ }
4740
+ addSpouse(indi) {
4741
+ return attachSpouseToFamily(this, indi);
4742
+ }
4743
+ addChild(child, pedigree, parent) {
4744
+ return attachChildToFamily(
4745
+ this,
4746
+ child,
4747
+ pedigree,
4748
+ parent
4749
+ );
4750
+ }
4751
+ addFact(input) {
4752
+ return addFamilyFact(this, input);
4753
+ }
4199
4754
  toList() {
4200
4755
  return new Families().concat(
4201
4756
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -4268,19 +4823,51 @@ var ALLOWED_FACTS = [
4268
4823
  "OCCU",
4269
4824
  "OCCUPATIONS",
4270
4825
  "RESI",
4826
+ "BIRT",
4827
+ "DEAT",
4271
4828
  "BAPT",
4829
+ "BAPM",
4272
4830
  "CHRI",
4273
4831
  "CHR",
4832
+ "CHRA",
4274
4833
  "BURI",
4275
- "EDUC",
4834
+ "CREM",
4835
+ "ADOP",
4836
+ "BARM",
4837
+ "BASM",
4838
+ "BLES",
4839
+ "CONF",
4840
+ "FCOM",
4841
+ "ORDN",
4842
+ "NATU",
4843
+ "EMIG",
4844
+ "IMMI",
4845
+ "CENS",
4846
+ "PROB",
4847
+ "WILL",
4276
4848
  "GRAD",
4849
+ "RETI",
4850
+ "EDUC",
4277
4851
  "DIV",
4278
- "IMMI",
4279
4852
  "RELI",
4853
+ "CAST",
4854
+ "DSCR",
4855
+ "NATI",
4856
+ "PROP",
4857
+ "SSN",
4858
+ "TITL",
4859
+ "ADDR",
4860
+ "BAPL",
4861
+ "CONL",
4862
+ "ENDL",
4863
+ "SLGC",
4280
4864
  "_MILT",
4281
4865
  "_MILTID",
4282
4866
  "FACT",
4283
- "_ORIG"
4867
+ "_ORIG",
4868
+ "_WLNK",
4869
+ "WWW",
4870
+ "URL"
4284
4871
  ];
4285
4872
  var DISALLOWED_CUSTOM_FACTS = ["DNA Test", "Newspaper"];
4286
4873
  var CustomFactRenderers = {
@@ -4824,7 +5411,7 @@ var Indi = class extends Common {
4824
5411
  return result;
4825
5412
  }
4826
5413
  ancestryLink() {
4827
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
5414
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
4828
5415
  const tree = this.getAncestryTreeId();
4829
5416
  if (this.id) {
4830
5417
  return `https://${www}/family-tree/person/tree/${tree}/person/${this.id.replace(
@@ -4836,7 +5423,7 @@ var Indi = class extends Common {
4836
5423
  async ancestryMedia(namespace) {
4837
5424
  const list = {};
4838
5425
  const objeList = this.get("OBJE")?.toList().copy();
4839
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
5426
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
4840
5427
  const tree = this.getAncestryTreeId();
4841
5428
  if (objeList) {
4842
5429
  await Promise.all(
@@ -4896,7 +5483,7 @@ var Indi = class extends Common {
4896
5483
  return list;
4897
5484
  }
4898
5485
  myheritageLink(poolId = 0) {
4899
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.value?.toLowerCase().replace(/^www\./gi, "");
5486
+ const www = (this.getFromSourceHeads("SOUR.CORP.value") || this._gedcom?.HEAD?.SOUR?.CORP?.value)?.toLowerCase().replace(/^www\./gi, "");
4900
5487
  const site = this.getMyHeritageTreeId();
4901
5488
  const file = this._gedcom?.HEAD?.get("FILE")?.toValue()?.match(/Exported by MyHeritage.com from .+ in (?<site>.+) on .+$/)?.groups?.site;
4902
5489
  const normalizedFile = file?.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
@@ -5417,6 +6004,57 @@ var Indi = class extends Common {
5417
6004
  isUnknownSex() {
5418
6005
  return !this.isMale() && !this.isFemale();
5419
6006
  }
6007
+ addSpouse(other) {
6008
+ if (!this.id || !other?.id || this.id === other.id || !this._gedcom) {
6009
+ return void 0;
6010
+ }
6011
+ const existing = getFamilyWith(this, other.id);
6012
+ if (existing) {
6013
+ return existing;
6014
+ }
6015
+ const fam = this._gedcom.createFamily();
6016
+ if (!fam.addSpouse(this) || !fam.addSpouse(other)) {
6017
+ return void 0;
6018
+ }
6019
+ return fam;
6020
+ }
6021
+ addChild(child, pedigree) {
6022
+ if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
6023
+ return void 0;
6024
+ }
6025
+ const existing = findReusableParentChildFamily(
6026
+ this,
6027
+ child
6028
+ );
6029
+ if (existing === "already") {
6030
+ const fam2 = child.getFamilies("FAMC")?.find(
6031
+ (item) => !!item && item.hasParent(this.id) && item.hasChild(child.id)
6032
+ );
6033
+ if (fam2) {
6034
+ setChildPedigree(
6035
+ fam2,
6036
+ child,
6037
+ pedigree,
6038
+ this
6039
+ );
6040
+ }
6041
+ return fam2;
6042
+ }
6043
+ const fam = existing ?? this._gedcom.createFamily();
6044
+ if (!fam.addSpouse(this)) {
6045
+ return void 0;
6046
+ }
6047
+ if (!fam.addChild(child, pedigree, this)) {
6048
+ return void 0;
6049
+ }
6050
+ return fam;
6051
+ }
6052
+ addParent(parent, pedigree) {
6053
+ return parent.addChild(this, pedigree);
6054
+ }
6055
+ addFact(input) {
6056
+ return addIndividualFact(this, input);
6057
+ }
5420
6058
  getParentType(id) {
5421
6059
  let indi;
5422
6060
  if (typeof id === "string" || typeof id === "number") {
@@ -5617,7 +6255,9 @@ var Indi = class extends Common {
5617
6255
  }
5618
6256
  }
5619
6257
  if (kinship !== "child" || !breakAfterSpouse) {
5620
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
6258
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(
6259
+ indi.getFathers().copy().merge(indi.getMothers())
6260
+ ).forEach((relative) => {
5621
6261
  if (!visited.has(relative)) {
5622
6262
  const currentRelation = indi.getParentType(relative);
5623
6263
  const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
@@ -6518,7 +7158,7 @@ var createIndi = (gedcom, id, main, parent) => {
6518
7158
 
6519
7159
  // package.json
6520
7160
  var package_default = {
6521
- version: "2.0.9"};
7161
+ version: "2.0.12"};
6522
7162
 
6523
7163
  // src/utils/get-product-details.ts
6524
7164
  var getVersion = () => package_default.version;
@@ -6718,6 +7358,44 @@ var GedCom = class extends Common {
6718
7358
  fams() {
6719
7359
  return this.getList("@@FAM");
6720
7360
  }
7361
+ nextIndiKey() {
7362
+ const ids = [];
7363
+ this.indis()?.forEach((_indi, id) => {
7364
+ ids.push(String(id));
7365
+ });
7366
+ return nextRecordId(ids, "I");
7367
+ }
7368
+ nextFamKey() {
7369
+ const ids = [];
7370
+ this.fams()?.forEach((_fam, id) => {
7371
+ ids.push(String(id));
7372
+ });
7373
+ return nextRecordId(ids, "F");
7374
+ }
7375
+ createIndividual() {
7376
+ const existing = this.indis();
7377
+ const indis = existing ?? new Individuals();
7378
+ if (!existing) {
7379
+ this.set("@@INDI", indis);
7380
+ }
7381
+ const id = this.nextIndiKey();
7382
+ const indi = createIndi(this, id);
7383
+ indi.type = "INDI";
7384
+ indis.item(id, indi);
7385
+ return indi;
7386
+ }
7387
+ createFamily() {
7388
+ const existing = this.fams();
7389
+ const fams = existing ?? new Families();
7390
+ if (!existing) {
7391
+ this.set("@@FAM", fams);
7392
+ }
7393
+ const id = this.nextFamKey();
7394
+ const fam = createFam(this, id);
7395
+ fam.type = "FAM";
7396
+ fams.item(id, fam);
7397
+ return fam;
7398
+ }
6721
7399
  objes() {
6722
7400
  return this.getList("@@OBJE");
6723
7401
  }
@@ -6805,15 +7483,24 @@ var GedCom = class extends Common {
6805
7483
  getDownloadHeader() {
6806
7484
  const newHead = createCommon();
6807
7485
  Object.assign(newHead, this.get("HEAD") ?? {});
7486
+ const existingSour = this.get("HEAD")?.get("SOUR");
7487
+ const existingTreeName = existingSour?.get("_TREE")?.toValue();
7488
+ const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
6808
7489
  const newSour = createCommon();
6809
- newSour.set("CORP", createCommon());
6810
- newSour.set("CORP.WWW", createCommon());
6811
- newSour.set("NAME", createCommon());
6812
- newSour.set("VERS", createCommon());
6813
- newSour.CORP.value = "TreeViz - The Family Tree Visualiser";
6814
- newSour.CORP.WWW.value = "treeviz.com";
6815
- newSour.NAME.value = "TreeViz - The Family Tree Visualiser";
6816
- newSour.VERS.value = getVersion();
7490
+ newSour.value = APP_BRANDING;
7491
+ newSour.set("NAME", APP_BRANDING);
7492
+ newSour.set("VERS", getVersion());
7493
+ if (typeof existingTreeName === "string" && existingTreeName.trim()) {
7494
+ newSour.set("_TREE", existingTreeName.trim());
7495
+ }
7496
+ if (typeof existingTreeUniqueId === "string" && existingTreeUniqueId.trim()) {
7497
+ newSour.set("_TID", existingTreeUniqueId.trim());
7498
+ }
7499
+ const corp = createCommon();
7500
+ corp.value = APP_BRANDING;
7501
+ corp.set("WWW", "treeviz.com");
7502
+ corp.set("EMAIL", "info@treeviz.com");
7503
+ newSour.set("CORP", corp);
6817
7504
  newHead.set("SOUR", newSour);
6818
7505
  return newHead;
6819
7506
  }
@@ -6842,15 +7529,17 @@ var GedCom = class extends Common {
6842
7529
  }
6843
7530
  toGedcom(tag, level, options) {
6844
7531
  if (options?.super) {
6845
- return super.toGedcom(tag, level, options);
7532
+ return appendGedcomTrailer(
7533
+ super.toGedcom(tag, level, options)
7534
+ );
6846
7535
  }
6847
7536
  const newGedcom = createGedCom();
7537
+ Object.assign(newGedcom, this);
6848
7538
  if (!options?.original) {
6849
7539
  Object.assign(newGedcom, {
6850
7540
  HEAD: this.getDownloadHeader()
6851
7541
  });
6852
7542
  }
6853
- Object.assign(newGedcom, this);
6854
7543
  if (options?.indis?.length) {
6855
7544
  const newContent = this.getIndiRelatedLists(options.indis);
6856
7545
  Object.assign(newGedcom, newContent);