@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
@@ -292,11 +292,31 @@ var cacheDiscoveredPath = (gedcom, path) => {
292
292
  };
293
293
 
294
294
  // src/constants/constants.ts
295
+ var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
295
296
  var REF_LINE_REG = /^0 (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/gm;
296
297
  var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/;
297
298
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
298
299
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
300
+ var ORIG_HEAD_TAG = "_ORIGHEAD";
299
301
  var MAX_FILE_SIZE_TO_SYNC = 1024 * 1024 * 20;
302
+
303
+ // src/utils/gedcom-trailer.ts
304
+ var TRAILER_LINE = /^0 TRLR\s*$/i;
305
+ var GEDCOM_TRAILER_TAG = "TRLR";
306
+ var isGedcomTrailerTag = (tag) => tag === GEDCOM_TRAILER_TAG;
307
+ var isGedcomTrailerLine = (line) => TRAILER_LINE.test(line);
308
+ var appendGedcomTrailer = (serialized) => {
309
+ const newline = serialized.includes("\r\n") ? "\r\n" : "\n";
310
+ const trimmed = serialized.replace(/(?:\r?\n)+$/, "");
311
+ if (!trimmed) {
312
+ return "0 TRLR";
313
+ }
314
+ const lastLine = trimmed.split(/\r?\n/).at(-1) ?? "";
315
+ if (isGedcomTrailerLine(lastLine)) {
316
+ return trimmed;
317
+ }
318
+ return `${trimmed}${newline}0 TRLR`;
319
+ };
300
320
  var List = class _List {
301
321
  // length = 0;
302
322
  constructor(items) {
@@ -807,6 +827,9 @@ var Common = class _Common {
807
827
  delete this._value;
808
828
  }
809
829
  set(name, value) {
830
+ if (isGedcomTrailerTag(name)) {
831
+ return void 0;
832
+ }
810
833
  if (typeof value === "string") {
811
834
  const usedValue = createCommon(this._gedcom, void 0, this.main);
812
835
  usedValue.value = value;
@@ -817,6 +840,9 @@ var Common = class _Common {
817
840
  return get(this, name);
818
841
  }
819
842
  assign(name, value, unique = false) {
843
+ if (isGedcomTrailerTag(name)) {
844
+ return void 0;
845
+ }
820
846
  let curValue = this.get(name);
821
847
  if (curValue?.isListable) {
822
848
  if (curValue instanceof _Common) {
@@ -1039,30 +1065,47 @@ var Common = class _Common {
1039
1065
  });
1040
1066
  return gedcom;
1041
1067
  }
1068
+ getHeadRecord() {
1069
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1070
+ }
1071
+ getOriginalHeadRecord() {
1072
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
1073
+ }
1074
+ getSourceHeads() {
1075
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
1076
+ return heads.filter((head, index) => {
1077
+ return Boolean(head && heads.indexOf(head) === index);
1078
+ });
1079
+ }
1080
+ getFromSourceHeads(path) {
1081
+ for (const head of this.getSourceHeads()) {
1082
+ const value = get(head, path);
1083
+ if (value !== void 0 && value !== null && value !== "") {
1084
+ return value;
1085
+ }
1086
+ }
1087
+ return void 0;
1088
+ }
1089
+ sourStartsWith(prefix) {
1090
+ return this.getSourceHeads().some((head) => {
1091
+ const sour = get(head, "SOUR.value");
1092
+ return !!sour?.toLowerCase()?.startsWith(prefix);
1093
+ });
1094
+ }
1042
1095
  isGenoPro() {
1043
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1044
- const sour = get(head, "SOUR.value");
1045
- return !!sour?.toLowerCase()?.startsWith("genopro");
1096
+ return this.sourStartsWith("genopro");
1046
1097
  }
1047
1098
  isAhnenblatt() {
1048
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1049
- const sour = get(head, "SOUR.value");
1050
- return !!sour?.toLowerCase()?.startsWith("ahnenblatt");
1099
+ return this.sourStartsWith("ahnenblatt");
1051
1100
  }
1052
1101
  isGeni() {
1053
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1054
- const sour = get(head, "SOUR.value");
1055
- return !!sour?.toLowerCase()?.startsWith("geni");
1102
+ return this.sourStartsWith("geni");
1056
1103
  }
1057
1104
  isAncestry() {
1058
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1059
- const sour = get(head, "SOUR.value");
1060
- return !!sour?.toLowerCase()?.startsWith("ancestry");
1105
+ return this.sourStartsWith("ancestry");
1061
1106
  }
1062
1107
  isMyHeritage() {
1063
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1064
- const sour = get(head, "SOUR.value");
1065
- return !!sour?.toLowerCase()?.startsWith("myheritage");
1108
+ return this.sourStartsWith("myheritage");
1066
1109
  }
1067
1110
  /**
1068
1111
  * Get the source type as a string (for prefixing tree IDs and names)
@@ -1079,22 +1122,22 @@ var Common = class _Common {
1079
1122
  return void 0;
1080
1123
  }
1081
1124
  isFamilySearch() {
1082
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1083
- const sourName = get(head, "SOUR.NAME.value");
1084
- return sourName === "FamilySearch API";
1125
+ return this.getSourceHeads().some((head) => {
1126
+ const sourName = get(head, "SOUR.NAME.value");
1127
+ return sourName === "FamilySearch API";
1128
+ });
1085
1129
  }
1086
1130
  isGNO2GED() {
1087
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1088
- const sour = get(head, "SOUR.value");
1089
- return sour === "GNO2GED";
1131
+ return this.getSourceHeads().some((head) => {
1132
+ const sour = get(head, "SOUR.value");
1133
+ return sour === "GNO2GED";
1134
+ });
1090
1135
  }
1091
1136
  getAncestryTreeId() {
1092
- const path = "HEAD.SOUR._TREE.RIN.value";
1093
- return get(this, path) || get(this.getGedcom(), path);
1137
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
1094
1138
  }
1095
1139
  getMyHeritageTreeId() {
1096
- const path = "HEAD._EXPORTED_FROM_SITE_ID.value";
1097
- return get(this, path) || get(this.getGedcom(), path);
1140
+ 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"));
1098
1141
  }
1099
1142
  getTreeId() {
1100
1143
  if (this?.isAncestry()) {
@@ -1128,8 +1171,7 @@ var Common = class _Common {
1128
1171
  return this.getUniversalTreeId();
1129
1172
  }
1130
1173
  getAncestryTreeName() {
1131
- const path = "HEAD.SOUR._TREE.value";
1132
- return get(this, path) || get(this.getGedcom(), path);
1174
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
1133
1175
  }
1134
1176
  getMyHeritageTreeName() {
1135
1177
  const path = "HEAD.FILE.value";
@@ -1139,14 +1181,14 @@ var Common = class _Common {
1139
1181
  )?.groups?.tree;
1140
1182
  }
1141
1183
  getFamilySearchTreeId() {
1142
- const rin = get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1184
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1143
1185
  if (rin) {
1144
1186
  return rin;
1145
1187
  }
1146
1188
  return "familysearch";
1147
1189
  }
1148
1190
  getFamilySearchTreeName() {
1149
- const treeName = get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1191
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1150
1192
  if (treeName) {
1151
1193
  return treeName;
1152
1194
  }
@@ -1238,11 +1280,11 @@ var Common = class _Common {
1238
1280
  const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1239
1281
  return `${prefix}${cleanName}`;
1240
1282
  }
1241
- const sourName = get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1283
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1242
1284
  if (sourName) {
1243
1285
  return `${prefix}${sourName}`;
1244
1286
  }
1245
- const sourValue = get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1287
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1246
1288
  if (sourValue) {
1247
1289
  return `${prefix}${sourValue}`;
1248
1290
  }
@@ -1308,7 +1350,7 @@ var isOnlyMainProp = (key) => {
1308
1350
  };
1309
1351
  var isValidKey = (common, key) => {
1310
1352
  const prop = get(common, key);
1311
- 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);
1353
+ 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);
1312
1354
  };
1313
1355
  var getValidKeys = (common) => {
1314
1356
  return Object.keys(common).filter((key) => {
@@ -1363,7 +1405,8 @@ __export(translators_exports, {
1363
1405
  en: () => KinshipTranslatorEn,
1364
1406
  es: () => KinshipTranslatorEs,
1365
1407
  fr: () => KinshipTranslatorFr,
1366
- hu: () => KinshipTranslatorHU
1408
+ hu: () => KinshipTranslatorHU,
1409
+ ru: () => KinshipTranslatorRu
1367
1410
  });
1368
1411
 
1369
1412
  // src/constants/filters.ts
@@ -1801,6 +1844,334 @@ var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces =
1801
1844
  };
1802
1845
  };
1803
1846
 
1847
+ // src/utils/fact-edit.ts
1848
+ var SINGLETON_TAGS = /* @__PURE__ */ new Set(["BIRT", "DEAT", "SSN", "SEX", "DSCR"]);
1849
+ var firstRecord = (node) => {
1850
+ if (!node) {
1851
+ return void 0;
1852
+ }
1853
+ if (node instanceof List) {
1854
+ return node.index(0);
1855
+ }
1856
+ return node.index(0) ?? node;
1857
+ };
1858
+ var setDateOn = (gedcom, owner, parent, date) => {
1859
+ const trimmed = date.trim();
1860
+ if (!trimmed) {
1861
+ return;
1862
+ }
1863
+ let dateNode = parent.get("DATE");
1864
+ if (!dateNode || dateNode instanceof List) {
1865
+ dateNode = createCommonDate(gedcom, void 0, owner, parent);
1866
+ dateNode.type = "DATE";
1867
+ parent.set("DATE", dateNode);
1868
+ }
1869
+ dateNode.value = trimmed;
1870
+ };
1871
+ var fillFact = (gedcom, owner, event, input) => {
1872
+ if (input.value?.trim()) {
1873
+ event.value = input.value.trim();
1874
+ }
1875
+ if (input.type?.trim()) {
1876
+ event.set("TYPE", input.type.trim());
1877
+ }
1878
+ if (input.date) {
1879
+ setDateOn(gedcom, owner, event, input.date);
1880
+ }
1881
+ if (input.place?.trim()) {
1882
+ event.set("PLAC", input.place.trim());
1883
+ }
1884
+ if (input.note?.trim()) {
1885
+ event.set("NOTE", input.note.trim());
1886
+ }
1887
+ };
1888
+ var updateFactRecord = (owner, event, input) => {
1889
+ const gedcom = owner.getGedcom();
1890
+ if (!gedcom) {
1891
+ return event;
1892
+ }
1893
+ const asOwner = owner;
1894
+ if (input.value !== void 0) {
1895
+ const trimmed = input.value.trim();
1896
+ if (trimmed) {
1897
+ event.value = trimmed;
1898
+ } else {
1899
+ event.removeValue();
1900
+ }
1901
+ }
1902
+ if (input.type !== void 0) {
1903
+ const trimmed = input.type.trim();
1904
+ if (trimmed) {
1905
+ event.set("TYPE", trimmed);
1906
+ } else {
1907
+ event.remove("TYPE");
1908
+ }
1909
+ }
1910
+ if (input.date !== void 0) {
1911
+ const trimmed = input.date.trim();
1912
+ if (trimmed) {
1913
+ setDateOn(gedcom, asOwner, event, trimmed);
1914
+ } else {
1915
+ event.remove("DATE");
1916
+ }
1917
+ }
1918
+ if (input.place !== void 0) {
1919
+ const trimmed = input.place.trim();
1920
+ if (trimmed) {
1921
+ event.set("PLAC", trimmed);
1922
+ } else {
1923
+ event.remove("PLAC");
1924
+ }
1925
+ }
1926
+ if (input.note !== void 0) {
1927
+ const trimmed = input.note.trim();
1928
+ if (trimmed) {
1929
+ event.set("NOTE", trimmed);
1930
+ } else {
1931
+ event.remove("NOTE");
1932
+ }
1933
+ }
1934
+ return event;
1935
+ };
1936
+ var setFamilyMarriage = (fam, input) => {
1937
+ const existing = firstRecord(fam.get("MARR"));
1938
+ if (existing) {
1939
+ return updateFactRecord(fam, existing, {
1940
+ date: input.date,
1941
+ place: input.place
1942
+ });
1943
+ }
1944
+ return addFamilyFact(fam, {
1945
+ tag: "MARR",
1946
+ date: input.date,
1947
+ place: input.place
1948
+ });
1949
+ };
1950
+ var addIndividualFact = (indi, input) => {
1951
+ const gedcom = indi.getGedcom();
1952
+ if (!gedcom) {
1953
+ return void 0;
1954
+ }
1955
+ const tag = input.tag;
1956
+ let event = SINGLETON_TAGS.has(tag) ? firstRecord(indi.get(tag)) : void 0;
1957
+ if (!event) {
1958
+ event = createCommon(gedcom, void 0, indi);
1959
+ event.type = tag;
1960
+ if (SINGLETON_TAGS.has(tag)) {
1961
+ indi.set(tag, event);
1962
+ } else {
1963
+ indi.assign(tag, event);
1964
+ }
1965
+ }
1966
+ fillFact(gedcom, indi, event, input);
1967
+ return event;
1968
+ };
1969
+ var addFamilyFact = (fam, input) => {
1970
+ const gedcom = fam.getGedcom();
1971
+ if (!gedcom) {
1972
+ return void 0;
1973
+ }
1974
+ const event = createCommon(gedcom, void 0, fam);
1975
+ event.type = input.tag;
1976
+ fam.assign(input.tag, event);
1977
+ fillFact(gedcom, fam, event, input);
1978
+ return event;
1979
+ };
1980
+ var setCauseOfDeath = (indi, text) => {
1981
+ const trimmed = text.trim();
1982
+ if (!trimmed) {
1983
+ return void 0;
1984
+ }
1985
+ const death = firstRecord(indi.get("DEAT")) || addIndividualFact(indi, { tag: "DEAT", value: "Y" });
1986
+ if (!death) {
1987
+ return void 0;
1988
+ }
1989
+ death.set("CAUS", trimmed);
1990
+ return death;
1991
+ };
1992
+ var setDeceased = (indi, deceased) => {
1993
+ if (!deceased) {
1994
+ indi.remove("DEAT");
1995
+ return;
1996
+ }
1997
+ if (firstRecord(indi.get("DEAT"))) {
1998
+ return;
1999
+ }
2000
+ addIndividualFact(indi, { tag: "DEAT", value: "Y" });
2001
+ };
2002
+
2003
+ // src/utils/family-edit.ts
2004
+ var PEDIGREE_VALUES = [
2005
+ "birth" /* BIRTH */,
2006
+ "adopted" /* ADOPTED */,
2007
+ "foster" /* FOSTER */,
2008
+ "step" /* STEP */,
2009
+ "sealing" /* SEALING */
2010
+ ];
2011
+ var toGedcomPedigree = (value) => {
2012
+ const normalized = String(value || "birth" /* BIRTH */).toLowerCase();
2013
+ if (normalized === "biological" /* BIOLOGICAL */ || normalized === "birth" /* BIRTH */) {
2014
+ return "birth" /* BIRTH */;
2015
+ }
2016
+ const match = PEDIGREE_VALUES.find((item) => item === normalized);
2017
+ return match ?? "birth" /* BIRTH */;
2018
+ };
2019
+ var pointerIds = (tag) => {
2020
+ if (!tag) {
2021
+ return [];
2022
+ }
2023
+ if (tag instanceof List) {
2024
+ return tag.values().map((item) => item?.toValue?.()).filter((value2) => !!value2);
2025
+ }
2026
+ const value = tag.toValue?.();
2027
+ return value ? [String(value)] : [];
2028
+ };
2029
+ var firstPointerId = (tag) => pointerIds(tag)[0];
2030
+ var makePointer = (gedcom, parent, value, refType) => {
2031
+ const pointer = createCommon(gedcom, void 0, parent);
2032
+ pointer.value = value;
2033
+ pointer.refType = refType;
2034
+ return pointer;
2035
+ };
2036
+ var nextRecordId = (ids, prefix) => {
2037
+ let max = 0;
2038
+ ids.forEach((id) => {
2039
+ const match = String(id).match(/(\d+)/);
2040
+ const n = match ? Number.parseInt(match[1], 10) : 0;
2041
+ if (n > max) {
2042
+ max = n;
2043
+ }
2044
+ });
2045
+ return `@${prefix}${max + 1}@`;
2046
+ };
2047
+ var familyHasParent = (fam, indiId) => pointerIds(fam.get("HUSB")).includes(indiId) || pointerIds(fam.get("WIFE")).includes(indiId);
2048
+ var familyHasChild = (fam, indiId) => pointerIds(fam.get("CHIL")).includes(indiId);
2049
+ var spouseTagFor = (indi, fam) => {
2050
+ const husbId = firstPointerId(fam.get("HUSB"));
2051
+ const wifeId = firstPointerId(fam.get("WIFE"));
2052
+ if (husbId === indi.id) {
2053
+ return "HUSB";
2054
+ }
2055
+ if (wifeId === indi.id) {
2056
+ return "WIFE";
2057
+ }
2058
+ if (indi.isFemale()) {
2059
+ return wifeId && wifeId !== indi.id ? "HUSB" : "WIFE";
2060
+ }
2061
+ if (indi.isMale()) {
2062
+ return husbId && husbId !== indi.id ? "WIFE" : "HUSB";
2063
+ }
2064
+ return husbId ? "WIFE" : "HUSB";
2065
+ };
2066
+ var canAttachAsSpouse = (fam, indi) => {
2067
+ if (!indi.id) {
2068
+ return false;
2069
+ }
2070
+ if (familyHasParent(fam, indi.id)) {
2071
+ return true;
2072
+ }
2073
+ const tag = spouseTagFor(indi, fam);
2074
+ const occupied = firstPointerId(fam.get(tag));
2075
+ return !occupied || occupied === indi.id;
2076
+ };
2077
+ var pointerByValue = (tag, id) => {
2078
+ if (!tag) {
2079
+ return void 0;
2080
+ }
2081
+ const fromValues = tag.toValueList?.().item(id);
2082
+ if (fromValues) {
2083
+ return fromValues;
2084
+ }
2085
+ return tag.toList()?.values().find((item) => item?.toValue?.() === id);
2086
+ };
2087
+ var setChildPedigree = (fam, child, pedigree, parent) => {
2088
+ if (!fam.id || !child.id) {
2089
+ return;
2090
+ }
2091
+ const pedi = toGedcomPedigree(pedigree);
2092
+ const famc = pointerByValue(child.get("FAMC"), fam.id);
2093
+ if (famc) {
2094
+ famc.set("PEDI", pedi);
2095
+ }
2096
+ const chil = pointerByValue(fam.get("CHIL"), child.id);
2097
+ if (!chil) {
2098
+ return;
2099
+ }
2100
+ const setFather = !parent || parent.isMale() || parent.isUnknownSex();
2101
+ const setMother = !parent || parent.isFemale() || parent.isUnknownSex();
2102
+ if (setFather) {
2103
+ chil.set("_FREL", pedi);
2104
+ }
2105
+ if (setMother) {
2106
+ chil.set("_MREL", pedi);
2107
+ }
2108
+ };
2109
+ var attachSpouseToFamily = (fam, indi) => {
2110
+ const gedcom = fam.getGedcom() ?? indi.getGedcom();
2111
+ if (!indi.id || !fam.id || !gedcom) {
2112
+ return false;
2113
+ }
2114
+ if (!canAttachAsSpouse(fam, indi)) {
2115
+ return false;
2116
+ }
2117
+ const tag = spouseTagFor(indi, fam);
2118
+ if (firstPointerId(fam.get(tag)) !== indi.id) {
2119
+ fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
2120
+ }
2121
+ indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
2122
+ return true;
2123
+ };
2124
+ var attachChildToFamily = (fam, child, pedigree, parent) => {
2125
+ const gedcom = fam.getGedcom() ?? child.getGedcom();
2126
+ if (!child.id || !fam.id || !gedcom) {
2127
+ return false;
2128
+ }
2129
+ if (!familyHasChild(fam, child.id)) {
2130
+ fam.assign("CHIL", makePointer(gedcom, fam, child.id, "INDI"), true);
2131
+ }
2132
+ const existingFamc = pointerByValue(child.get("FAMC"), fam.id);
2133
+ if (!existingFamc) {
2134
+ child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
2135
+ }
2136
+ setChildPedigree(fam, child, pedigree, parent);
2137
+ return true;
2138
+ };
2139
+ var findReusableParentChildFamily = (parent, child) => {
2140
+ if (!parent.id || !child.id) {
2141
+ return void 0;
2142
+ }
2143
+ let reusableFamc;
2144
+ let already = false;
2145
+ child.getFamilies("FAMC")?.forEach((fam) => {
2146
+ if (!fam) {
2147
+ return;
2148
+ }
2149
+ if (familyHasParent(fam, parent.id) && familyHasChild(fam, child.id)) {
2150
+ already = true;
2151
+ return;
2152
+ }
2153
+ if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
2154
+ reusableFamc = fam;
2155
+ }
2156
+ });
2157
+ if (already) {
2158
+ return "already";
2159
+ }
2160
+ if (reusableFamc) {
2161
+ return reusableFamc;
2162
+ }
2163
+ const parentFams = [];
2164
+ parent.getFamilies("FAMS")?.forEach((fam) => {
2165
+ if (fam) {
2166
+ parentFams.push(fam);
2167
+ }
2168
+ });
2169
+ if (parentFams.length === 1) {
2170
+ return parentFams[0];
2171
+ }
2172
+ return void 0;
2173
+ };
2174
+
1804
2175
  // src/utils/get-places.ts
1805
2176
  var PlaceType = /* @__PURE__ */ ((PlaceType2) => {
1806
2177
  PlaceType2["All"] = "ALL";
@@ -2444,6 +2815,31 @@ var Fam = class extends Common {
2444
2815
  getParents() {
2445
2816
  return this.getHusband().copy().merge(this.getWife());
2446
2817
  }
2818
+ hasParent(indi) {
2819
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2820
+ return !!indiId && familyHasParent(this, indiId);
2821
+ }
2822
+ hasChild(indi) {
2823
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2824
+ return !!indiId && familyHasChild(this, indiId);
2825
+ }
2826
+ canAddSpouse(indi) {
2827
+ return canAttachAsSpouse(this, indi);
2828
+ }
2829
+ addSpouse(indi) {
2830
+ return attachSpouseToFamily(this, indi);
2831
+ }
2832
+ addChild(child, pedigree, parent) {
2833
+ return attachChildToFamily(
2834
+ this,
2835
+ child,
2836
+ pedigree,
2837
+ parent
2838
+ );
2839
+ }
2840
+ addFact(input) {
2841
+ return addFamilyFact(this, input);
2842
+ }
2447
2843
  toList() {
2448
2844
  return new Families().concat(
2449
2845
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2549,19 +2945,51 @@ var ALLOWED_FACTS = [
2549
2945
  "OCCU",
2550
2946
  "OCCUPATIONS",
2551
2947
  "RESI",
2948
+ "BIRT",
2949
+ "DEAT",
2552
2950
  "BAPT",
2951
+ "BAPM",
2553
2952
  "CHRI",
2554
2953
  "CHR",
2954
+ "CHRA",
2555
2955
  "BURI",
2556
- "EDUC",
2956
+ "CREM",
2957
+ "ADOP",
2958
+ "BARM",
2959
+ "BASM",
2960
+ "BLES",
2961
+ "CONF",
2962
+ "FCOM",
2963
+ "ORDN",
2964
+ "NATU",
2965
+ "EMIG",
2966
+ "IMMI",
2967
+ "CENS",
2968
+ "PROB",
2969
+ "WILL",
2557
2970
  "GRAD",
2971
+ "RETI",
2972
+ "EDUC",
2558
2973
  "DIV",
2559
- "IMMI",
2560
2974
  "RELI",
2975
+ "CAST",
2976
+ "DSCR",
2977
+ "NATI",
2978
+ "PROP",
2979
+ "SSN",
2980
+ "TITL",
2981
+ "ADDR",
2982
+ "BAPL",
2983
+ "CONL",
2984
+ "ENDL",
2985
+ "SLGC",
2561
2986
  "_MILT",
2562
2987
  "_MILTID",
2563
2988
  "FACT",
2564
- "_ORIG"
2989
+ "_ORIG",
2990
+ "_WLNK",
2991
+ "WWW",
2992
+ "URL"
2565
2993
  ];
2566
2994
  var DISALLOWED_CUSTOM_FACTS = ["DNA Test", "Newspaper"];
2567
2995
  var CustomFactRenderers = {
@@ -3105,7 +3533,7 @@ var Indi = class extends Common {
3105
3533
  return result;
3106
3534
  }
3107
3535
  ancestryLink() {
3108
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3536
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3109
3537
  const tree = this.getAncestryTreeId();
3110
3538
  if (this.id) {
3111
3539
  return `https://${www}/family-tree/person/tree/${tree}/person/${this.id.replace(
@@ -3117,7 +3545,7 @@ var Indi = class extends Common {
3117
3545
  async ancestryMedia(namespace) {
3118
3546
  const list = {};
3119
3547
  const objeList = this.get("OBJE")?.toList().copy();
3120
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3548
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3121
3549
  const tree = this.getAncestryTreeId();
3122
3550
  if (objeList) {
3123
3551
  await Promise.all(
@@ -3177,7 +3605,7 @@ var Indi = class extends Common {
3177
3605
  return list;
3178
3606
  }
3179
3607
  myheritageLink(poolId = 0) {
3180
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.value?.toLowerCase().replace(/^www\./gi, "");
3608
+ const www = (this.getFromSourceHeads("SOUR.CORP.value") || this._gedcom?.HEAD?.SOUR?.CORP?.value)?.toLowerCase().replace(/^www\./gi, "");
3181
3609
  const site = this.getMyHeritageTreeId();
3182
3610
  const file = this._gedcom?.HEAD?.get("FILE")?.toValue()?.match(/Exported by MyHeritage.com from .+ in (?<site>.+) on .+$/)?.groups?.site;
3183
3611
  const normalizedFile = file?.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
@@ -3698,6 +4126,57 @@ var Indi = class extends Common {
3698
4126
  isUnknownSex() {
3699
4127
  return !this.isMale() && !this.isFemale();
3700
4128
  }
4129
+ addSpouse(other) {
4130
+ if (!this.id || !other?.id || this.id === other.id || !this._gedcom) {
4131
+ return void 0;
4132
+ }
4133
+ const existing = getFamilyWith(this, other.id);
4134
+ if (existing) {
4135
+ return existing;
4136
+ }
4137
+ const fam = this._gedcom.createFamily();
4138
+ if (!fam.addSpouse(this) || !fam.addSpouse(other)) {
4139
+ return void 0;
4140
+ }
4141
+ return fam;
4142
+ }
4143
+ addChild(child, pedigree) {
4144
+ if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
4145
+ return void 0;
4146
+ }
4147
+ const existing = findReusableParentChildFamily(
4148
+ this,
4149
+ child
4150
+ );
4151
+ if (existing === "already") {
4152
+ const fam2 = child.getFamilies("FAMC")?.find(
4153
+ (item) => !!item && item.hasParent(this.id) && item.hasChild(child.id)
4154
+ );
4155
+ if (fam2) {
4156
+ setChildPedigree(
4157
+ fam2,
4158
+ child,
4159
+ pedigree,
4160
+ this
4161
+ );
4162
+ }
4163
+ return fam2;
4164
+ }
4165
+ const fam = existing ?? this._gedcom.createFamily();
4166
+ if (!fam.addSpouse(this)) {
4167
+ return void 0;
4168
+ }
4169
+ if (!fam.addChild(child, pedigree, this)) {
4170
+ return void 0;
4171
+ }
4172
+ return fam;
4173
+ }
4174
+ addParent(parent, pedigree) {
4175
+ return parent.addChild(this, pedigree);
4176
+ }
4177
+ addFact(input) {
4178
+ return addIndividualFact(this, input);
4179
+ }
3701
4180
  getParentType(id) {
3702
4181
  let indi;
3703
4182
  if (typeof id === "string" || typeof id === "number") {
@@ -3898,7 +4377,9 @@ var Indi = class extends Common {
3898
4377
  }
3899
4378
  }
3900
4379
  if (kinship !== "child" || !breakAfterSpouse) {
3901
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
4380
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(
4381
+ indi.getFathers().copy().merge(indi.getMothers())
4382
+ ).forEach((relative) => {
3902
4383
  if (!visited.has(relative)) {
3903
4384
  const currentRelation = indi.getParentType(relative);
3904
4385
  const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
@@ -6200,6 +6681,249 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
6200
6681
  }
6201
6682
  };
6202
6683
 
6684
+ // src/kinship-translator/patterns.ru.ts
6685
+ var InLawsRu = {
6686
+ "\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",
6687
+ "\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",
6688
+ "\u043C\u0443\u0436 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C",
6689
+ "\u043C\u0430\u0442\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0451\u0449\u0430",
6690
+ "\u043E\u0442\u0435\u0446 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C",
6691
+ "\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",
6692
+ "\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",
6693
+ "\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",
6694
+ "\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",
6695
+ "(\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",
6696
+ "(\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",
6697
+ "(\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"
6698
+ };
6699
+
6700
+ // src/kinship-translator/kinship-translator.ru.ts
6701
+ var GENITIVE = {
6702
+ \u0441\u044B\u043D: "\u0441\u044B\u043D\u0430",
6703
+ \u0434\u043E\u0447\u044C: "\u0434\u043E\u0447\u0435\u0440\u0438",
6704
+ \u0440\u0435\u0431\u0451\u043D\u043E\u043A: "\u0440\u0435\u0431\u0451\u043D\u043A\u0430",
6705
+ \u0431\u0440\u0430\u0442: "\u0431\u0440\u0430\u0442\u0430",
6706
+ \u0441\u0435\u0441\u0442\u0440\u0430: "\u0441\u0435\u0441\u0442\u0440\u044B",
6707
+ \u0441\u0438\u0431\u043B\u0438\u043D\u0433: "\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430",
6708
+ \u043E\u0442\u0435\u0446: "\u043E\u0442\u0446\u0430",
6709
+ \u043C\u0430\u0442\u044C: "\u043C\u0430\u0442\u0435\u0440\u0438",
6710
+ \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C: "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044F",
6711
+ \u043C\u0443\u0436: "\u043C\u0443\u0436\u0430",
6712
+ \u0436\u0435\u043D\u0430: "\u0436\u0435\u043D\u044B",
6713
+ \u0441\u0443\u043F\u0440\u0443\u0433: "\u0441\u0443\u043F\u0440\u0443\u0433\u0430",
6714
+ \u0441\u0443\u043F\u0440\u0443\u0433\u0430: "\u0441\u0443\u043F\u0440\u0443\u0433\u0438"
6715
+ };
6716
+ var toGenitive = (word) => GENITIVE[word] ?? word;
6717
+ var cousinWord = (degree, sex) => {
6718
+ 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`;
6719
+ if (sex === "m") {
6720
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442`;
6721
+ }
6722
+ if (sex === "f") {
6723
+ return `${stem}\u0430\u044F \u0441\u0435\u0441\u0442\u0440\u0430`;
6724
+ }
6725
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442/\u0441\u0435\u0441\u0442\u0440\u0430`;
6726
+ };
6727
+ var praRepeat = (count) => count > 0 ? "\u043F\u0440\u0430".repeat(count) : "";
6728
+ var sexOf = (person) => {
6729
+ if (person?.isMale?.()) {
6730
+ return "m";
6731
+ }
6732
+ if (person?.isFemale?.()) {
6733
+ return "f";
6734
+ }
6735
+ return "n";
6736
+ };
6737
+ var KinshipTranslatorRu = class extends KinshipTranslatorBasic {
6738
+ indirect() {
6739
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6740
+ if (degree > 0) {
6741
+ degree = degree - 1;
6742
+ }
6743
+ return cousinWord(degree, sexOf(this.personN));
6744
+ }
6745
+ removal() {
6746
+ const level = this.pathN?.level ?? 0;
6747
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6748
+ if (degree > 0) {
6749
+ degree = degree - 1;
6750
+ }
6751
+ if (!degree) {
6752
+ return level < 0 ? this.nibling() : this.auncle();
6753
+ }
6754
+ const removed = Math.abs(level);
6755
+ return `${cousinWord(degree, sexOf(this.personN))} (${removed}\xD7 \u0447\u0435\u0440\u0435\u0437 \u043F\u043E\u043A\u043E\u043B\u0435\u043D\u0438\u0435)`;
6756
+ }
6757
+ auncle() {
6758
+ const level = Math.abs(this.pathN?.level ?? 0);
6759
+ const pra = praRepeat(Math.max(0, level - 2));
6760
+ const sex = sexOf(this.personN);
6761
+ if (sex === "m") {
6762
+ return `${pra}\u0434\u044F\u0434\u044F`;
6763
+ }
6764
+ if (sex === "f") {
6765
+ return `${pra}\u0442\u0451\u0442\u044F`;
6766
+ }
6767
+ return `${pra}\u0434\u044F\u0434\u044F/\u0442\u0451\u0442\u044F`;
6768
+ }
6769
+ nibling() {
6770
+ const level = Math.abs(this.pathN?.level ?? 0);
6771
+ const pra = praRepeat(Math.max(0, level - 2));
6772
+ const sex = sexOf(this.personN);
6773
+ if (sex === "m") {
6774
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A`;
6775
+ }
6776
+ if (sex === "f") {
6777
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6778
+ }
6779
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A/\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6780
+ }
6781
+ parent() {
6782
+ const level = Math.abs(this.pathN?.level ?? 0);
6783
+ const sex = sexOf(this.personN);
6784
+ if (level <= 1) {
6785
+ if (sex === "m") {
6786
+ return "\u043E\u0442\u0435\u0446";
6787
+ }
6788
+ if (sex === "f") {
6789
+ return "\u043C\u0430\u0442\u044C";
6790
+ }
6791
+ return "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C";
6792
+ }
6793
+ if (level === 2) {
6794
+ if (sex === "m") {
6795
+ return "\u0434\u0435\u0434";
6796
+ }
6797
+ if (sex === "f") {
6798
+ return "\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6799
+ }
6800
+ return "\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6801
+ }
6802
+ const pra = praRepeat(level - 2);
6803
+ if (sex === "m") {
6804
+ return `${pra}\u0434\u0435\u0434`;
6805
+ }
6806
+ if (sex === "f") {
6807
+ return `${pra}\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6808
+ }
6809
+ return `${pra}\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6810
+ }
6811
+ child() {
6812
+ const level = Math.abs(this.pathN?.level ?? 0);
6813
+ const sex = sexOf(this.personN);
6814
+ if (level <= 1) {
6815
+ if (sex === "m") {
6816
+ return "\u0441\u044B\u043D";
6817
+ }
6818
+ if (sex === "f") {
6819
+ return "\u0434\u043E\u0447\u044C";
6820
+ }
6821
+ return "\u0440\u0435\u0431\u0451\u043D\u043E\u043A";
6822
+ }
6823
+ if (level === 2) {
6824
+ if (sex === "m") {
6825
+ return "\u0432\u043D\u0443\u043A";
6826
+ }
6827
+ if (sex === "f") {
6828
+ return "\u0432\u043D\u0443\u0447\u043A\u0430";
6829
+ }
6830
+ return "\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430";
6831
+ }
6832
+ const pra = praRepeat(level - 2);
6833
+ if (sex === "m") {
6834
+ return `${pra}\u0432\u043D\u0443\u043A`;
6835
+ }
6836
+ if (sex === "f") {
6837
+ return `${pra}\u0432\u043D\u0443\u0447\u043A\u0430`;
6838
+ }
6839
+ return `${pra}\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430`;
6840
+ }
6841
+ sibling() {
6842
+ const sex = sexOf(this.personN);
6843
+ if (sex === "m") {
6844
+ return "\u0431\u0440\u0430\u0442";
6845
+ }
6846
+ if (sex === "f") {
6847
+ return "\u0441\u0435\u0441\u0442\u0440\u0430";
6848
+ }
6849
+ return "\u0441\u0438\u0431\u043B\u0438\u043D\u0433";
6850
+ }
6851
+ halfBlood(relation) {
6852
+ if (!relation || !this.isHalfBlood) {
6853
+ return relation ?? "";
6854
+ }
6855
+ if (/сестра|дочь|мать|тётя|бабушка|жена|племянница|внучка/.test(
6856
+ relation
6857
+ )) {
6858
+ return `\u0441\u0432\u043E\u0434\u043D\u0430\u044F ${relation}`;
6859
+ }
6860
+ return `\u0441\u0432\u043E\u0434\u043D\u044B\u0439 ${relation}`;
6861
+ }
6862
+ spouse() {
6863
+ const sex = sexOf(this.personN);
6864
+ if (sex === "m") {
6865
+ return "\u043C\u0443\u0436";
6866
+ }
6867
+ if (sex === "f") {
6868
+ return "\u0436\u0435\u043D\u0430";
6869
+ }
6870
+ return "\u0441\u0443\u043F\u0440\u0443\u0433";
6871
+ }
6872
+ ofSpouse(relation) {
6873
+ if (!relation) {
6874
+ return "";
6875
+ }
6876
+ const spouse = this.path?.[1].indi;
6877
+ const sex = sexOf(spouse);
6878
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6879
+ return `${relation} ${toGenitive(spouseType)}`;
6880
+ }
6881
+ spouseOf(relation) {
6882
+ if (!relation) {
6883
+ return "";
6884
+ }
6885
+ const spouse = this.path?.[this.path.length - 1].indi;
6886
+ const sex = sexOf(spouse);
6887
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6888
+ return `${spouseType} ${toGenitive(relation)}`;
6889
+ }
6890
+ of(relation) {
6891
+ const formattedName = nameFormatter(this.path?.[0]?.indi, {
6892
+ nameOrder: "first-last"
6893
+ });
6894
+ let name = formattedName.inOrder.filter(Boolean).join(" ");
6895
+ if (this.displayName === "none") {
6896
+ name = "";
6897
+ } else if (this.displayName === "givenname") {
6898
+ name = formattedName.givenname;
6899
+ } else if (this.displayName === "surname") {
6900
+ name = `${formattedName.surname}`;
6901
+ }
6902
+ if (!name || !relation) {
6903
+ return relation ?? "";
6904
+ }
6905
+ return `${name}: ${relation}`;
6906
+ }
6907
+ inLaw(relation) {
6908
+ let inLawRelation = relation;
6909
+ Object.keys(InLawsRu).find((pattern) => {
6910
+ const regex = new RegExp(`^${pattern}$`);
6911
+ if (relation?.match(regex)) {
6912
+ inLawRelation = InLawsRu[pattern];
6913
+ return true;
6914
+ }
6915
+ return false;
6916
+ });
6917
+ return inLawRelation ?? "";
6918
+ }
6919
+ relationType(relation) {
6920
+ if (!relation || !this.pathN?.relation) {
6921
+ return relation ?? "";
6922
+ }
6923
+ return `${this.pathN.relation} ${relation}`;
6924
+ }
6925
+ };
6926
+
6203
6927
  // src/kinship-translator/kinship-translator.ts
6204
6928
  var KinshipTranslator = class {
6205
6929
  constructor(person1, person2, lang = "en", entirePath, displayName = "givenname") {
@@ -6754,7 +7478,7 @@ var isCommonDate = (value) => {
6754
7478
  // package.json
6755
7479
  var package_default = {
6756
7480
  name: "@treeviz/gedcom-parser",
6757
- version: "2.0.9"};
7481
+ version: "2.0.12"};
6758
7482
 
6759
7483
  // src/utils/get-product-details.ts
6760
7484
  var isDevelopment = () => {
@@ -6958,6 +7682,44 @@ var GedCom = class extends Common {
6958
7682
  fams() {
6959
7683
  return this.getList("@@FAM");
6960
7684
  }
7685
+ nextIndiKey() {
7686
+ const ids = [];
7687
+ this.indis()?.forEach((_indi, id) => {
7688
+ ids.push(String(id));
7689
+ });
7690
+ return nextRecordId(ids, "I");
7691
+ }
7692
+ nextFamKey() {
7693
+ const ids = [];
7694
+ this.fams()?.forEach((_fam, id) => {
7695
+ ids.push(String(id));
7696
+ });
7697
+ return nextRecordId(ids, "F");
7698
+ }
7699
+ createIndividual() {
7700
+ const existing = this.indis();
7701
+ const indis = existing ?? new Individuals();
7702
+ if (!existing) {
7703
+ this.set("@@INDI", indis);
7704
+ }
7705
+ const id = this.nextIndiKey();
7706
+ const indi = createIndi(this, id);
7707
+ indi.type = "INDI";
7708
+ indis.item(id, indi);
7709
+ return indi;
7710
+ }
7711
+ createFamily() {
7712
+ const existing = this.fams();
7713
+ const fams = existing ?? new Families();
7714
+ if (!existing) {
7715
+ this.set("@@FAM", fams);
7716
+ }
7717
+ const id = this.nextFamKey();
7718
+ const fam = createFam(this, id);
7719
+ fam.type = "FAM";
7720
+ fams.item(id, fam);
7721
+ return fam;
7722
+ }
6961
7723
  objes() {
6962
7724
  return this.getList("@@OBJE");
6963
7725
  }
@@ -7045,15 +7807,24 @@ var GedCom = class extends Common {
7045
7807
  getDownloadHeader() {
7046
7808
  const newHead = createCommon();
7047
7809
  Object.assign(newHead, this.get("HEAD") ?? {});
7810
+ const existingSour = this.get("HEAD")?.get("SOUR");
7811
+ const existingTreeName = existingSour?.get("_TREE")?.toValue();
7812
+ const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
7048
7813
  const newSour = createCommon();
7049
- newSour.set("CORP", createCommon());
7050
- newSour.set("CORP.WWW", createCommon());
7051
- newSour.set("NAME", createCommon());
7052
- newSour.set("VERS", createCommon());
7053
- newSour.CORP.value = "TreeViz - The Family Tree Visualiser";
7054
- newSour.CORP.WWW.value = "treeviz.com";
7055
- newSour.NAME.value = "TreeViz - The Family Tree Visualiser";
7056
- newSour.VERS.value = getVersion();
7814
+ newSour.value = APP_BRANDING;
7815
+ newSour.set("NAME", APP_BRANDING);
7816
+ newSour.set("VERS", getVersion());
7817
+ if (typeof existingTreeName === "string" && existingTreeName.trim()) {
7818
+ newSour.set("_TREE", existingTreeName.trim());
7819
+ }
7820
+ if (typeof existingTreeUniqueId === "string" && existingTreeUniqueId.trim()) {
7821
+ newSour.set("_TID", existingTreeUniqueId.trim());
7822
+ }
7823
+ const corp = createCommon();
7824
+ corp.value = APP_BRANDING;
7825
+ corp.set("WWW", "treeviz.com");
7826
+ corp.set("EMAIL", "info@treeviz.com");
7827
+ newSour.set("CORP", corp);
7057
7828
  newHead.set("SOUR", newSour);
7058
7829
  return newHead;
7059
7830
  }
@@ -7082,15 +7853,17 @@ var GedCom = class extends Common {
7082
7853
  }
7083
7854
  toGedcom(tag, level, options) {
7084
7855
  if (options?.super) {
7085
- return super.toGedcom(tag, level, options);
7856
+ return appendGedcomTrailer(
7857
+ super.toGedcom(tag, level, options)
7858
+ );
7086
7859
  }
7087
7860
  const newGedcom = createGedCom();
7861
+ Object.assign(newGedcom, this);
7088
7862
  if (!options?.original) {
7089
7863
  Object.assign(newGedcom, {
7090
7864
  HEAD: this.getDownloadHeader()
7091
7865
  });
7092
7866
  }
7093
- Object.assign(newGedcom, this);
7094
7867
  if (options?.indis?.length) {
7095
7868
  const newContent = this.getIndiRelatedLists(options.indis);
7096
7869
  Object.assign(newGedcom, newContent);
@@ -7620,9 +8393,7 @@ var GedcomTree = {
7620
8393
  }, {}) : {};
7621
8394
  const links = new Individuals();
7622
8395
  const tags = [];
7623
- let lines = `${content.replace(/\n0 TRLR(\r?\n)*$/, "")}
7624
- 0 TRLR
7625
- `.split(/\r?\n/).reduce((acc, line) => {
8396
+ let lines = content.split(/\r?\n/).reduce((acc, line) => {
7626
8397
  const lineMatch = line.match(LINE_REG);
7627
8398
  if (lineMatch) {
7628
8399
  const lineIndent = Number(lineMatch?.groups?.indent ?? 0);
@@ -7635,6 +8406,13 @@ var GedcomTree = {
7635
8406
  linesAcc._hasFile = false;
7636
8407
  return acc;
7637
8408
  }
8409
+ if (lineIndent === 0 && lineType === "TRLR") {
8410
+ if (linesAcc._inHead && filename && !linesAcc._hasFile) {
8411
+ acc.push(`1 FILE ${filename}`);
8412
+ }
8413
+ linesAcc._inHead = false;
8414
+ return acc;
8415
+ }
7638
8416
  if (lineIndent === 0 && linesAcc._inHead) {
7639
8417
  if (filename && !linesAcc._hasFile) {
7640
8418
  acc.push(`1 FILE ${filename}`);
@@ -7689,8 +8467,9 @@ var GedcomTree = {
7689
8467
  ).replace(/0\s(@[^@]+@)(\sSOUR[^\n]*\n)([123456789]\s.+\n)*/, "").replace(/0 HEAD(.*)\n/, "0 HEAD$1\n1 _IS_PURGED true\n");
7690
8468
  lines = linesJoined.split("\n");
7691
8469
  }
8470
+ lines = lines.filter((line) => !isGedcomTrailerLine(line));
7692
8471
  lines.forEach((line, index) => {
7693
- if (line.length === 0) {
8472
+ if (line.length === 0 || isGedcomTrailerLine(line)) {
7694
8473
  return;
7695
8474
  }
7696
8475
  const match = reg.exec(line);
@@ -7827,5 +8606,20 @@ if (isDev) {
7827
8606
  globalThis.GedcomTree = GedcomTree;
7828
8607
  }
7829
8608
  var parser_default = GedcomTree;
8609
+ var createEmptyGedcom = (options) => {
8610
+ const { gedcom } = GedcomTree.parse(
8611
+ `0 HEAD
8612
+ 1 GEDC
8613
+ 2 VERS 5.5.1
8614
+ 2 FORM LINEAGE-LINKED
8615
+ 1 CHAR UTF-8
8616
+ 0 TRLR`,
8617
+ { filename: options?.filename }
8618
+ );
8619
+ if (!gedcom) {
8620
+ throw new Error("Failed to create empty GEDCOM");
8621
+ }
8622
+ return gedcom;
8623
+ };
7830
8624
 
7831
- export { ACCEPTED_DATE_FORMATS, ACCEPTED_DATE_FORMATS_REGEX, parser_default as GedcomTree, PlaceType, cacheDiscoveredPath, commonDateFormatter, create, dateFormatter, extractSeparationYears, extractSplitPoints, findMatchingRangeForSplitRange, fromTuple, generateSplitRanges, getAllProp, getFamilyWith, getFileExtension, getName, getPlaceParts, getPlaces, getRawSize, getVersion, hungarianOrdinalize, implemented, inRange, initializeCache, isDevelopment, isImageFormat, isIntersectedRange, isRangeContained, marriageDateFormatter, nameFormatter, notImplemented, noteDateFormatter, ordinalize, parseRangeBounds, pathCache, placeTranslator, profilePictureCache, relativesCache, resetRelativesCache, setNestedGroup, splitOverlappingRanges, splitRange };
8625
+ export { ACCEPTED_DATE_FORMATS, ACCEPTED_DATE_FORMATS_REGEX, GEDCOM_TRAILER_TAG, parser_default as GedcomTree, PEDIGREE_VALUES, PlaceType, addFamilyFact, addIndividualFact, appendGedcomTrailer, attachChildToFamily, attachSpouseToFamily, cacheDiscoveredPath, canAttachAsSpouse, commonDateFormatter, create, createEmptyGedcom, dateFormatter, extractSeparationYears, extractSplitPoints, familyHasChild, familyHasParent, findMatchingRangeForSplitRange, findReusableParentChildFamily, firstPointerId, fromTuple, generateSplitRanges, getAllProp, getFamilyWith, getFileExtension, getName, getPlaceParts, getPlaces, getRawSize, getVersion, hungarianOrdinalize, implemented, inRange, initializeCache, isDevelopment, isGedcomTrailerLine, isGedcomTrailerTag, isImageFormat, isIntersectedRange, isRangeContained, makePointer, marriageDateFormatter, nameFormatter, nextRecordId, notImplemented, noteDateFormatter, ordinalize, parseRangeBounds, pathCache, placeTranslator, pointerIds, profilePictureCache, relativesCache, resetRelativesCache, setCauseOfDeath, setChildPedigree, setDeceased, setFamilyMarriage, setNestedGroup, splitOverlappingRanges, splitRange, spouseTagFor, toGedcomPedigree, updateFactRecord };