@treeviz/gedcom-parser 2.0.10 → 2.1.0

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 (71) hide show
  1. package/README.md +236 -989
  2. package/dist/classes/common.d.ts +10 -5
  3. package/dist/classes/common.d.ts.map +1 -1
  4. package/dist/classes/date.d.ts +2 -0
  5. package/dist/classes/date.d.ts.map +1 -1
  6. package/dist/classes/fam.d.ts +10 -1
  7. package/dist/classes/fam.d.ts.map +1 -1
  8. package/dist/classes/fams.d.ts.map +1 -1
  9. package/dist/classes/gedcom.d.ts +45 -4
  10. package/dist/classes/gedcom.d.ts.map +1 -1
  11. package/dist/classes/index.js +1770 -279
  12. package/dist/classes/indi.d.ts +24 -1
  13. package/dist/classes/indi.d.ts.map +1 -1
  14. package/dist/classes/list.d.ts.map +1 -1
  15. package/dist/classes/note.d.ts.map +1 -1
  16. package/dist/classes/obje.d.ts +2 -1
  17. package/dist/classes/obje.d.ts.map +1 -1
  18. package/dist/cli/index.js +2802 -1296
  19. package/dist/constants/constants.d.ts +7 -0
  20. package/dist/constants/constants.d.ts.map +1 -1
  21. package/dist/constants/index.js +1599 -989
  22. package/dist/factories/index.js +719 -110
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3837 -2028
  26. package/dist/interfaces/common.d.ts +6 -4
  27. package/dist/interfaces/common.d.ts.map +1 -1
  28. package/dist/interfaces/fam.d.ts +10 -1
  29. package/dist/interfaces/fam.d.ts.map +1 -1
  30. package/dist/interfaces/gedcom.d.ts +25 -1
  31. package/dist/interfaces/gedcom.d.ts.map +1 -1
  32. package/dist/interfaces/indi.d.ts +15 -1
  33. package/dist/interfaces/indi.d.ts.map +1 -1
  34. package/dist/kinship-translator/index.js +719 -110
  35. package/dist/structures/gedcom.d.ts +2 -0
  36. package/dist/structures/gedcom.d.ts.map +1 -1
  37. package/dist/types/types.d.ts +32 -4
  38. package/dist/types/types.d.ts.map +1 -1
  39. package/dist/utils/common-creator.d.ts.map +1 -1
  40. package/dist/utils/fact-edit.d.ts +34 -0
  41. package/dist/utils/fact-edit.d.ts.map +1 -0
  42. package/dist/utils/family-edit.d.ts +23 -0
  43. package/dist/utils/family-edit.d.ts.map +1 -0
  44. package/dist/utils/gedcom-trailer.d.ts +6 -0
  45. package/dist/utils/gedcom-trailer.d.ts.map +1 -0
  46. package/dist/utils/gedcom-version.d.ts +6 -0
  47. package/dist/utils/gedcom-version.d.ts.map +1 -0
  48. package/dist/utils/gedcom7-dates.d.ts +20 -0
  49. package/dist/utils/gedcom7-dates.d.ts.map +1 -0
  50. package/dist/utils/gedcom7-enumerations.d.ts +26 -0
  51. package/dist/utils/gedcom7-enumerations.d.ts.map +1 -0
  52. package/dist/utils/gedcom7-metadata.d.ts +19 -0
  53. package/dist/utils/gedcom7-metadata.d.ts.map +1 -0
  54. package/dist/utils/gedcom7-schema.d.ts +27 -0
  55. package/dist/utils/gedcom7-schema.d.ts.map +1 -0
  56. package/dist/utils/gedzip.d.ts +81 -0
  57. package/dist/utils/gedzip.d.ts.map +1 -0
  58. package/dist/utils/index.d.ts +13 -1
  59. package/dist/utils/index.d.ts.map +1 -1
  60. package/dist/utils/index.js +2225 -416
  61. package/dist/utils/local-media.d.ts +30 -0
  62. package/dist/utils/local-media.d.ts.map +1 -0
  63. package/dist/utils/media-utils.d.ts +10 -6
  64. package/dist/utils/media-utils.d.ts.map +1 -1
  65. package/dist/utils/multimedia.d.ts +38 -0
  66. package/dist/utils/multimedia.d.ts.map +1 -0
  67. package/dist/utils/parser.d.ts +3 -0
  68. package/dist/utils/parser.d.ts.map +1 -1
  69. package/dist/utils/uid.d.ts +6 -0
  70. package/dist/utils/uid.d.ts.map +1 -0
  71. package/package.json +2 -1
@@ -1,5 +1,6 @@
1
1
  import { debounce, set, get, uniqBy, unset, difference, intersectionBy, differenceBy } from 'lodash-es';
2
2
  import { format, isValid, parse } from 'date-fns';
3
+ import JSZip from 'jszip';
3
4
 
4
5
  var __defProp = Object.defineProperty;
5
6
  var __export = (target, all) => {
@@ -292,11 +293,31 @@ var cacheDiscoveredPath = (gedcom, path) => {
292
293
  };
293
294
 
294
295
  // src/constants/constants.ts
295
- 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
- var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/;
296
+ var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
297
+ var REF_LINE_REG = /^0 (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z0-9]{2,}|_?[A-Z][A-Za-z0-9]{2,}) ?(?<value>.*)/gm;
298
+ var LINE_REG = /(?<indent>^[0-9]{1,2}) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z0-9]{2,}|_?[A-Z][A-Za-z0-9]{2,}) ?(?<value>.*)/;
297
299
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
298
300
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
301
+ var ORIG_HEAD_TAG = "_ORIGHEAD";
299
302
  var MAX_FILE_SIZE_TO_SYNC = 1024 * 1024 * 20;
303
+
304
+ // src/utils/gedcom-trailer.ts
305
+ var TRAILER_LINE = /^0 TRLR\s*$/i;
306
+ var GEDCOM_TRAILER_TAG = "TRLR";
307
+ var isGedcomTrailerTag = (tag) => tag === GEDCOM_TRAILER_TAG;
308
+ var isGedcomTrailerLine = (line) => TRAILER_LINE.test(line);
309
+ var appendGedcomTrailer = (serialized) => {
310
+ const newline = serialized.includes("\r\n") ? "\r\n" : "\n";
311
+ const trimmed = serialized.replace(/(?:\r?\n)+$/, "");
312
+ if (!trimmed) {
313
+ return "0 TRLR";
314
+ }
315
+ const lastLine = trimmed.split(/\r?\n/).at(-1) ?? "";
316
+ if (isGedcomTrailerLine(lastLine)) {
317
+ return trimmed;
318
+ }
319
+ return `${trimmed}${newline}0 TRLR`;
320
+ };
300
321
  var List = class _List {
301
322
  // length = 0;
302
323
  constructor(items) {
@@ -672,11 +693,17 @@ var List = class _List {
672
693
  const gedcom = [];
673
694
  this.entries().forEach(([_, item]) => {
674
695
  const validTag = getValidTag(tag);
675
- const validKey = item.id;
676
- const validValue = validKey ? validTag : item.exportValue();
677
- gedcom.push(
678
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
679
- );
696
+ const xref = item.id;
697
+ const payload = item.exportValue?.();
698
+ if (xref) {
699
+ gedcom.push(
700
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
701
+ );
702
+ } else {
703
+ gedcom.push(
704
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
705
+ );
706
+ }
680
707
  gedcom.push(
681
708
  ...item.toGedcomLines(
682
709
  validTag,
@@ -807,6 +834,9 @@ var Common = class _Common {
807
834
  delete this._value;
808
835
  }
809
836
  set(name, value) {
837
+ if (isGedcomTrailerTag(name)) {
838
+ return void 0;
839
+ }
810
840
  if (typeof value === "string") {
811
841
  const usedValue = createCommon(this._gedcom, void 0, this.main);
812
842
  usedValue.value = value;
@@ -817,6 +847,9 @@ var Common = class _Common {
817
847
  return get(this, name);
818
848
  }
819
849
  assign(name, value, unique = false) {
850
+ if (isGedcomTrailerTag(name)) {
851
+ return void 0;
852
+ }
820
853
  let curValue = this.get(name);
821
854
  if (curValue?.isListable) {
822
855
  if (curValue instanceof _Common) {
@@ -928,12 +961,14 @@ var Common = class _Common {
928
961
  );
929
962
  }
930
963
  standardizeObject(tag, options) {
931
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
964
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
932
965
  const standardize = this.standardizeMedia;
933
966
  return standardize.call(
934
967
  this,
935
968
  options.obje.namespace,
936
- options.obje.override
969
+ options.obje.override,
970
+ void 0,
971
+ options.gedcomVersion
937
972
  );
938
973
  }
939
974
  }
@@ -960,7 +995,7 @@ var Common = class _Common {
960
995
  if (typeof prop?.toObject === "function") {
961
996
  if (prop instanceof _Common) {
962
997
  json[validKey] = prop.toObject(validKey, options);
963
- } else {
998
+ } else if (prop instanceof List) {
964
999
  json = {
965
1000
  ...json,
966
1001
  ...prop.toObject(validKey, options)
@@ -1018,8 +1053,12 @@ var Common = class _Common {
1018
1053
  this.standardizeObject(tag, options);
1019
1054
  const validKeys = getValidKeys(this);
1020
1055
  const gedcom = [];
1056
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
1021
1057
  validKeys.forEach((key) => {
1022
1058
  const validKey = key;
1059
+ if (isGedcom7Export && validKey === "CONC") {
1060
+ return;
1061
+ }
1023
1062
  const prop = this.get(validKey);
1024
1063
  if (typeof prop?.toGedcomLines === "function") {
1025
1064
  if (prop instanceof _Common) {
@@ -1030,7 +1069,7 @@ var Common = class _Common {
1030
1069
  gedcom.push(
1031
1070
  ...prop.toGedcomLines(validKey, level + 1, options)
1032
1071
  );
1033
- } else {
1072
+ } else if (prop instanceof List) {
1034
1073
  gedcom.push(
1035
1074
  ...prop.toGedcomLines(validKey, level, options)
1036
1075
  );
@@ -1039,30 +1078,47 @@ var Common = class _Common {
1039
1078
  });
1040
1079
  return gedcom;
1041
1080
  }
1081
+ getHeadRecord() {
1082
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1083
+ }
1084
+ getOriginalHeadRecord() {
1085
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
1086
+ }
1087
+ getSourceHeads() {
1088
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
1089
+ return heads.filter((head, index) => {
1090
+ return Boolean(head && heads.indexOf(head) === index);
1091
+ });
1092
+ }
1093
+ getFromSourceHeads(path) {
1094
+ for (const head of this.getSourceHeads()) {
1095
+ const value = get(head, path);
1096
+ if (value !== void 0 && value !== null && value !== "") {
1097
+ return value;
1098
+ }
1099
+ }
1100
+ return void 0;
1101
+ }
1102
+ sourStartsWith(prefix) {
1103
+ return this.getSourceHeads().some((head) => {
1104
+ const sour = get(head, "SOUR.value");
1105
+ return !!sour?.toLowerCase()?.startsWith(prefix);
1106
+ });
1107
+ }
1042
1108
  isGenoPro() {
1043
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1044
- const sour = get(head, "SOUR.value");
1045
- return !!sour?.toLowerCase()?.startsWith("genopro");
1109
+ return this.sourStartsWith("genopro");
1046
1110
  }
1047
1111
  isAhnenblatt() {
1048
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1049
- const sour = get(head, "SOUR.value");
1050
- return !!sour?.toLowerCase()?.startsWith("ahnenblatt");
1112
+ return this.sourStartsWith("ahnenblatt");
1051
1113
  }
1052
1114
  isGeni() {
1053
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1054
- const sour = get(head, "SOUR.value");
1055
- return !!sour?.toLowerCase()?.startsWith("geni");
1115
+ return this.sourStartsWith("geni");
1056
1116
  }
1057
1117
  isAncestry() {
1058
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1059
- const sour = get(head, "SOUR.value");
1060
- return !!sour?.toLowerCase()?.startsWith("ancestry");
1118
+ return this.sourStartsWith("ancestry");
1061
1119
  }
1062
1120
  isMyHeritage() {
1063
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1064
- const sour = get(head, "SOUR.value");
1065
- return !!sour?.toLowerCase()?.startsWith("myheritage");
1121
+ return this.sourStartsWith("myheritage");
1066
1122
  }
1067
1123
  /**
1068
1124
  * Get the source type as a string (for prefixing tree IDs and names)
@@ -1079,22 +1135,22 @@ var Common = class _Common {
1079
1135
  return void 0;
1080
1136
  }
1081
1137
  isFamilySearch() {
1082
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1083
- const sourName = get(head, "SOUR.NAME.value");
1084
- return sourName === "FamilySearch API";
1138
+ return this.getSourceHeads().some((head) => {
1139
+ const sourName = get(head, "SOUR.NAME.value");
1140
+ return sourName === "FamilySearch API";
1141
+ });
1085
1142
  }
1086
1143
  isGNO2GED() {
1087
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1088
- const sour = get(head, "SOUR.value");
1089
- return sour === "GNO2GED";
1144
+ return this.getSourceHeads().some((head) => {
1145
+ const sour = get(head, "SOUR.value");
1146
+ return sour === "GNO2GED";
1147
+ });
1090
1148
  }
1091
1149
  getAncestryTreeId() {
1092
- const path = "HEAD.SOUR._TREE.RIN.value";
1093
- return get(this, path) || get(this.getGedcom(), path);
1150
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
1094
1151
  }
1095
1152
  getMyHeritageTreeId() {
1096
- const path = "HEAD._EXPORTED_FROM_SITE_ID.value";
1097
- return get(this, path) || get(this.getGedcom(), path);
1153
+ 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
1154
  }
1099
1155
  getTreeId() {
1100
1156
  if (this?.isAncestry()) {
@@ -1128,8 +1184,7 @@ var Common = class _Common {
1128
1184
  return this.getUniversalTreeId();
1129
1185
  }
1130
1186
  getAncestryTreeName() {
1131
- const path = "HEAD.SOUR._TREE.value";
1132
- return get(this, path) || get(this.getGedcom(), path);
1187
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
1133
1188
  }
1134
1189
  getMyHeritageTreeName() {
1135
1190
  const path = "HEAD.FILE.value";
@@ -1139,14 +1194,14 @@ var Common = class _Common {
1139
1194
  )?.groups?.tree;
1140
1195
  }
1141
1196
  getFamilySearchTreeId() {
1142
- const rin = get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1197
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1143
1198
  if (rin) {
1144
1199
  return rin;
1145
1200
  }
1146
1201
  return "familysearch";
1147
1202
  }
1148
1203
  getFamilySearchTreeName() {
1149
- const treeName = get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1204
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1150
1205
  if (treeName) {
1151
1206
  return treeName;
1152
1207
  }
@@ -1238,11 +1293,11 @@ var Common = class _Common {
1238
1293
  const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1239
1294
  return `${prefix}${cleanName}`;
1240
1295
  }
1241
- const sourName = get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1296
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1242
1297
  if (sourName) {
1243
1298
  return `${prefix}${sourName}`;
1244
1299
  }
1245
- const sourValue = get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1300
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1246
1301
  if (sourValue) {
1247
1302
  return `${prefix}${sourValue}`;
1248
1303
  }
@@ -1308,7 +1363,7 @@ var isOnlyMainProp = (key) => {
1308
1363
  };
1309
1364
  var isValidKey = (common, key) => {
1310
1365
  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);
1366
+ 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
1367
  };
1313
1368
  var getValidKeys = (common) => {
1314
1369
  return Object.keys(common).filter((key) => {
@@ -1802,6 +1857,352 @@ var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces =
1802
1857
  };
1803
1858
  };
1804
1859
 
1860
+ // src/utils/fact-edit.ts
1861
+ var SINGLETON_TAGS = /* @__PURE__ */ new Set(["BIRT", "DEAT", "SSN", "SEX", "DSCR"]);
1862
+ var firstRecord = (node) => {
1863
+ if (!node) {
1864
+ return void 0;
1865
+ }
1866
+ if (node instanceof List) {
1867
+ return node.index(0);
1868
+ }
1869
+ return node.index(0) ?? node;
1870
+ };
1871
+ var setDateOn = (gedcom, owner, parent, date) => {
1872
+ const trimmed = date.trim();
1873
+ if (!trimmed) {
1874
+ return;
1875
+ }
1876
+ let dateNode = parent.get("DATE");
1877
+ if (!dateNode || dateNode instanceof List) {
1878
+ dateNode = createCommonDate(gedcom, void 0, owner, parent);
1879
+ dateNode.type = "DATE";
1880
+ parent.set("DATE", dateNode);
1881
+ }
1882
+ dateNode.value = trimmed;
1883
+ };
1884
+ var fillFact = (gedcom, owner, event, input) => {
1885
+ if (input.value?.trim()) {
1886
+ event.value = input.value.trim();
1887
+ }
1888
+ if (input.type?.trim()) {
1889
+ event.set("TYPE", input.type.trim());
1890
+ }
1891
+ if (input.date) {
1892
+ setDateOn(gedcom, owner, event, input.date);
1893
+ }
1894
+ if (input.place?.trim()) {
1895
+ event.set("PLAC", input.place.trim());
1896
+ }
1897
+ if (input.note?.trim()) {
1898
+ event.set("NOTE", input.note.trim());
1899
+ }
1900
+ };
1901
+ var updateFactRecord = (owner, event, input) => {
1902
+ const gedcom = owner.getGedcom();
1903
+ if (!gedcom) {
1904
+ return event;
1905
+ }
1906
+ const asOwner = owner;
1907
+ if (input.value !== void 0) {
1908
+ const trimmed = input.value.trim();
1909
+ if (trimmed) {
1910
+ event.value = trimmed;
1911
+ } else {
1912
+ event.removeValue();
1913
+ }
1914
+ }
1915
+ if (input.type !== void 0) {
1916
+ const trimmed = input.type.trim();
1917
+ if (trimmed) {
1918
+ event.set("TYPE", trimmed);
1919
+ } else {
1920
+ event.remove("TYPE");
1921
+ }
1922
+ }
1923
+ if (input.date !== void 0) {
1924
+ const trimmed = input.date.trim();
1925
+ if (trimmed) {
1926
+ setDateOn(gedcom, asOwner, event, trimmed);
1927
+ } else {
1928
+ event.remove("DATE");
1929
+ }
1930
+ }
1931
+ if (input.place !== void 0) {
1932
+ const trimmed = input.place.trim();
1933
+ if (trimmed) {
1934
+ event.set("PLAC", trimmed);
1935
+ } else {
1936
+ event.remove("PLAC");
1937
+ }
1938
+ }
1939
+ if (input.note !== void 0) {
1940
+ const trimmed = input.note.trim();
1941
+ if (trimmed) {
1942
+ event.set("NOTE", trimmed);
1943
+ } else {
1944
+ event.remove("NOTE");
1945
+ }
1946
+ }
1947
+ return event;
1948
+ };
1949
+ var setFamilyMarriage = (fam, input) => {
1950
+ const existing = firstRecord(fam.get("MARR"));
1951
+ if (existing) {
1952
+ return updateFactRecord(fam, existing, {
1953
+ date: input.date,
1954
+ place: input.place
1955
+ });
1956
+ }
1957
+ return addFamilyFact(fam, {
1958
+ tag: "MARR",
1959
+ date: input.date,
1960
+ place: input.place
1961
+ });
1962
+ };
1963
+ var addIndividualFact = (indi, input) => {
1964
+ const gedcom = indi.getGedcom();
1965
+ if (!gedcom) {
1966
+ return void 0;
1967
+ }
1968
+ const tag = input.tag;
1969
+ let event = SINGLETON_TAGS.has(tag) ? firstRecord(indi.get(tag)) : void 0;
1970
+ if (!event) {
1971
+ event = createCommon(gedcom, void 0, indi);
1972
+ event.type = tag;
1973
+ if (SINGLETON_TAGS.has(tag)) {
1974
+ indi.set(tag, event);
1975
+ } else {
1976
+ indi.assign(tag, event);
1977
+ }
1978
+ }
1979
+ fillFact(gedcom, indi, event, input);
1980
+ return event;
1981
+ };
1982
+ var addFamilyFact = (fam, input) => {
1983
+ const gedcom = fam.getGedcom();
1984
+ if (!gedcom) {
1985
+ return void 0;
1986
+ }
1987
+ const event = createCommon(gedcom, void 0, fam);
1988
+ event.type = input.tag;
1989
+ fam.assign(input.tag, event);
1990
+ fillFact(gedcom, fam, event, input);
1991
+ return event;
1992
+ };
1993
+ var setCauseOfDeath = (indi, text) => {
1994
+ const trimmed = text.trim();
1995
+ if (!trimmed) {
1996
+ return void 0;
1997
+ }
1998
+ const death = firstRecord(indi.get("DEAT")) || addIndividualFact(indi, { tag: "DEAT", value: "Y" });
1999
+ if (!death) {
2000
+ return void 0;
2001
+ }
2002
+ death.set("CAUS", trimmed);
2003
+ return death;
2004
+ };
2005
+ var setDeceased = (indi, deceased) => {
2006
+ if (!deceased) {
2007
+ indi.remove("DEAT");
2008
+ return;
2009
+ }
2010
+ if (firstRecord(indi.get("DEAT"))) {
2011
+ return;
2012
+ }
2013
+ addIndividualFact(indi, { tag: "DEAT", value: "Y" });
2014
+ };
2015
+ var addNonEvent = (owner, input) => {
2016
+ const gedcom = owner.getGedcom();
2017
+ const eventType = input.event?.trim().toUpperCase();
2018
+ if (!gedcom || !eventType) {
2019
+ return void 0;
2020
+ }
2021
+ const no = createCommon(gedcom, void 0, owner);
2022
+ no.type = "NO";
2023
+ no.value = eventType;
2024
+ if (input.date?.trim()) {
2025
+ setDateOn(gedcom, owner, no, input.date);
2026
+ }
2027
+ if (input.note?.trim()) {
2028
+ no.set("NOTE", input.note.trim());
2029
+ }
2030
+ owner.assign("NO", no);
2031
+ return no;
2032
+ };
2033
+
2034
+ // src/utils/family-edit.ts
2035
+ var PEDIGREE_VALUES = [
2036
+ "birth" /* BIRTH */,
2037
+ "adopted" /* ADOPTED */,
2038
+ "foster" /* FOSTER */,
2039
+ "step" /* STEP */,
2040
+ "sealing" /* SEALING */
2041
+ ];
2042
+ var toGedcomPedigree = (value) => {
2043
+ const normalized = String(value || "birth" /* BIRTH */).toLowerCase();
2044
+ if (normalized === "biological" /* BIOLOGICAL */ || normalized === "birth" /* BIRTH */) {
2045
+ return "birth" /* BIRTH */;
2046
+ }
2047
+ const match = PEDIGREE_VALUES.find((item) => item === normalized);
2048
+ return match ?? "birth" /* BIRTH */;
2049
+ };
2050
+ var pointerIds = (tag) => {
2051
+ if (!tag) {
2052
+ return [];
2053
+ }
2054
+ if (tag instanceof List) {
2055
+ return tag.values().map((item) => item?.toValue?.()).filter((value2) => !!value2);
2056
+ }
2057
+ const value = tag.toValue?.();
2058
+ return value ? [String(value)] : [];
2059
+ };
2060
+ var firstPointerId = (tag) => pointerIds(tag)[0];
2061
+ var makePointer = (gedcom, parent, value, refType) => {
2062
+ const pointer = createCommon(gedcom, void 0, parent);
2063
+ pointer.value = value;
2064
+ pointer.refType = refType;
2065
+ return pointer;
2066
+ };
2067
+ var nextRecordId = (ids, prefix) => {
2068
+ let max = 0;
2069
+ ids.forEach((id) => {
2070
+ const match = String(id).match(/(\d+)/);
2071
+ const n = match ? Number.parseInt(match[1], 10) : 0;
2072
+ if (n > max) {
2073
+ max = n;
2074
+ }
2075
+ });
2076
+ return `@${prefix}${max + 1}@`;
2077
+ };
2078
+ var familyHasParent = (fam, indiId) => pointerIds(fam.get("HUSB")).includes(indiId) || pointerIds(fam.get("WIFE")).includes(indiId);
2079
+ var familyHasChild = (fam, indiId) => pointerIds(fam.get("CHIL")).includes(indiId);
2080
+ var spouseTagFor = (indi, fam) => {
2081
+ const husbId = firstPointerId(fam.get("HUSB"));
2082
+ const wifeId = firstPointerId(fam.get("WIFE"));
2083
+ if (husbId === indi.id) {
2084
+ return "HUSB";
2085
+ }
2086
+ if (wifeId === indi.id) {
2087
+ return "WIFE";
2088
+ }
2089
+ if (indi.isFemale()) {
2090
+ return wifeId && wifeId !== indi.id ? "HUSB" : "WIFE";
2091
+ }
2092
+ if (indi.isMale()) {
2093
+ return husbId && husbId !== indi.id ? "WIFE" : "HUSB";
2094
+ }
2095
+ return husbId ? "WIFE" : "HUSB";
2096
+ };
2097
+ var canAttachAsSpouse = (fam, indi) => {
2098
+ if (!indi.id) {
2099
+ return false;
2100
+ }
2101
+ if (familyHasParent(fam, indi.id)) {
2102
+ return true;
2103
+ }
2104
+ const tag = spouseTagFor(indi, fam);
2105
+ const occupied = firstPointerId(fam.get(tag));
2106
+ return !occupied || occupied === indi.id;
2107
+ };
2108
+ var pointerByValue = (tag, id) => {
2109
+ if (!tag) {
2110
+ return void 0;
2111
+ }
2112
+ const fromValues = tag.toValueList?.().item(id);
2113
+ if (fromValues) {
2114
+ return fromValues;
2115
+ }
2116
+ return tag.toList()?.values().find((item) => item?.toValue?.() === id);
2117
+ };
2118
+ var setChildPedigree = (fam, child, pedigree, parent) => {
2119
+ if (!fam.id || !child.id) {
2120
+ return;
2121
+ }
2122
+ const pedi = toGedcomPedigree(pedigree);
2123
+ const famc = pointerByValue(child.get("FAMC"), fam.id);
2124
+ if (famc) {
2125
+ famc.set("PEDI", pedi);
2126
+ }
2127
+ const chil = pointerByValue(fam.get("CHIL"), child.id);
2128
+ if (!chil) {
2129
+ return;
2130
+ }
2131
+ const setFather = !parent || parent.isMale() || parent.isUnknownSex();
2132
+ const setMother = !parent || parent.isFemale() || parent.isUnknownSex();
2133
+ if (setFather) {
2134
+ chil.set("_FREL", pedi);
2135
+ }
2136
+ if (setMother) {
2137
+ chil.set("_MREL", pedi);
2138
+ }
2139
+ };
2140
+ var attachSpouseToFamily = (fam, indi) => {
2141
+ const gedcom = fam.getGedcom() ?? indi.getGedcom();
2142
+ if (!indi.id || !fam.id || !gedcom) {
2143
+ return false;
2144
+ }
2145
+ if (!canAttachAsSpouse(fam, indi)) {
2146
+ return false;
2147
+ }
2148
+ const tag = spouseTagFor(indi, fam);
2149
+ if (firstPointerId(fam.get(tag)) !== indi.id) {
2150
+ fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
2151
+ }
2152
+ indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
2153
+ return true;
2154
+ };
2155
+ var attachChildToFamily = (fam, child, pedigree, parent) => {
2156
+ const gedcom = fam.getGedcom() ?? child.getGedcom();
2157
+ if (!child.id || !fam.id || !gedcom) {
2158
+ return false;
2159
+ }
2160
+ if (!familyHasChild(fam, child.id)) {
2161
+ fam.assign("CHIL", makePointer(gedcom, fam, child.id, "INDI"), true);
2162
+ }
2163
+ const existingFamc = pointerByValue(child.get("FAMC"), fam.id);
2164
+ if (!existingFamc) {
2165
+ child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
2166
+ }
2167
+ setChildPedigree(fam, child, pedigree, parent);
2168
+ return true;
2169
+ };
2170
+ var findReusableParentChildFamily = (parent, child) => {
2171
+ if (!parent.id || !child.id) {
2172
+ return void 0;
2173
+ }
2174
+ let reusableFamc;
2175
+ let already = false;
2176
+ child.getFamilies("FAMC")?.forEach((fam) => {
2177
+ if (!fam) {
2178
+ return;
2179
+ }
2180
+ if (familyHasParent(fam, parent.id) && familyHasChild(fam, child.id)) {
2181
+ already = true;
2182
+ return;
2183
+ }
2184
+ if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
2185
+ reusableFamc = fam;
2186
+ }
2187
+ });
2188
+ if (already) {
2189
+ return "already";
2190
+ }
2191
+ if (reusableFamc) {
2192
+ return reusableFamc;
2193
+ }
2194
+ const parentFams = [];
2195
+ parent.getFamilies("FAMS")?.forEach((fam) => {
2196
+ if (fam) {
2197
+ parentFams.push(fam);
2198
+ }
2199
+ });
2200
+ if (parentFams.length === 1) {
2201
+ return parentFams[0];
2202
+ }
2203
+ return void 0;
2204
+ };
2205
+
1805
2206
  // src/utils/get-places.ts
1806
2207
  var PlaceType = /* @__PURE__ */ ((PlaceType2) => {
1807
2208
  PlaceType2["All"] = "ALL";
@@ -1876,6 +2277,81 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
1876
2277
  });
1877
2278
  return places;
1878
2279
  };
2280
+
2281
+ // src/utils/local-media.ts
2282
+ var localMediaResolver;
2283
+ var mediaContentResolver;
2284
+ var setLocalMediaResolver = (resolver) => {
2285
+ localMediaResolver = resolver;
2286
+ };
2287
+ var setMediaContentResolver = (resolver) => {
2288
+ mediaContentResolver = resolver;
2289
+ };
2290
+ var isRemoteOrEmbeddedMediaUrl = (path) => {
2291
+ if (!path) {
2292
+ return false;
2293
+ }
2294
+ return path.startsWith("data:") || path.startsWith("blob:") || /^https?:\/\//i.test(path);
2295
+ };
2296
+ var resolveLocalMediaUrl = async (path) => {
2297
+ if (!path) {
2298
+ return void 0;
2299
+ }
2300
+ if (isRemoteOrEmbeddedMediaUrl(path)) {
2301
+ return path;
2302
+ }
2303
+ const resolved = await localMediaResolver?.(path);
2304
+ return resolved;
2305
+ };
2306
+ var blobToDataUrl = async (blob) => {
2307
+ const buffer = await blob.arrayBuffer();
2308
+ const bytes = new Uint8Array(buffer);
2309
+ const mime = blob.type || "application/octet-stream";
2310
+ if (typeof Buffer !== "undefined") {
2311
+ return `data:${mime};base64,${Buffer.from(bytes).toString("base64")}`;
2312
+ }
2313
+ let binary = "";
2314
+ const chunk = 32768;
2315
+ for (let i = 0; i < bytes.length; i += chunk) {
2316
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
2317
+ }
2318
+ return `data:${mime};base64,${btoa(binary)}`;
2319
+ };
2320
+ var resolveMediaContent = async (media) => {
2321
+ const fromHost = await mediaContentResolver?.(media);
2322
+ if (fromHost?.content) {
2323
+ return fromHost;
2324
+ }
2325
+ const url = media.url;
2326
+ if (!url) {
2327
+ return void 0;
2328
+ }
2329
+ if (url.startsWith("data:") || url.startsWith("blob:")) {
2330
+ return { content: url, contentType: media.contentType };
2331
+ }
2332
+ if (!isRemoteOrEmbeddedMediaUrl(url)) {
2333
+ const local = await resolveLocalMediaUrl(url);
2334
+ if (local && isRemoteOrEmbeddedMediaUrl(local)) {
2335
+ return { content: local, contentType: media.contentType };
2336
+ }
2337
+ return void 0;
2338
+ }
2339
+ if (typeof fetch !== "function") {
2340
+ return void 0;
2341
+ }
2342
+ try {
2343
+ const response = await fetch(url);
2344
+ if (!response.ok) {
2345
+ return void 0;
2346
+ }
2347
+ const blob = await response.blob();
2348
+ const content = await blobToDataUrl(blob);
2349
+ const contentType = media.contentType || blob.type.split("/").pop() || "bin";
2350
+ return { content, contentType };
2351
+ } catch {
2352
+ return void 0;
2353
+ }
2354
+ };
1879
2355
  var implemented = (type, ...args) => {
1880
2356
  };
1881
2357
  var notImplemented = (type, ...args) => {
@@ -1884,11 +2360,34 @@ var notImplemented = (type, ...args) => {
1884
2360
 
1885
2361
  // src/utils/media-utils.ts
1886
2362
  var getFileExtension = (filename) => {
1887
- const match = filename.match(/\.([^.]+)$/);
1888
- return match ? match[1] : "";
2363
+ if (!filename) {
2364
+ return "";
2365
+ }
2366
+ if (filename.startsWith("data:")) {
2367
+ const mime = filename.slice(5).split(";")[0] || "";
2368
+ const subtype = (mime.split("/")[1] || "").toLowerCase();
2369
+ if (subtype === "jpeg") {
2370
+ return "jpg";
2371
+ }
2372
+ return subtype.split("+")[0] || "";
2373
+ }
2374
+ const withoutQuery = filename.split(/[?#]/)[0] || filename;
2375
+ const match = withoutQuery.match(/\.([^.]+)$/);
2376
+ return match ? match[1].toLowerCase() : "";
1889
2377
  };
1890
2378
  var isImageFormat = (format2) => {
1891
- if (!format2) return false;
2379
+ if (!format2) {
2380
+ return false;
2381
+ }
2382
+ let normalized = format2.toLowerCase().trim();
2383
+ if (normalized.startsWith("data:")) {
2384
+ normalized = getFileExtension(normalized);
2385
+ } else if (normalized.includes("/")) {
2386
+ normalized = normalized.split("/")[1]?.split("+")[0] || "";
2387
+ if (normalized === "jpeg") {
2388
+ normalized = "jpg";
2389
+ }
2390
+ }
1892
2391
  const imageFormats = [
1893
2392
  "jpg",
1894
2393
  "jpeg",
@@ -1900,7 +2399,11 @@ var isImageFormat = (format2) => {
1900
2399
  "tiff",
1901
2400
  "tif"
1902
2401
  ];
1903
- return imageFormats.includes(format2.toLowerCase());
2402
+ return imageFormats.includes(normalized);
2403
+ };
2404
+ var resolveObjeForm = (obje) => {
2405
+ const value = obje?.get?.("FILE.FORM")?.toValue?.() ?? obje?.get?.("FORM")?.toValue?.();
2406
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
1904
2407
  };
1905
2408
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
1906
2409
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -2445,6 +2948,34 @@ var Fam = class extends Common {
2445
2948
  getParents() {
2446
2949
  return this.getHusband().copy().merge(this.getWife());
2447
2950
  }
2951
+ hasParent(indi) {
2952
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2953
+ return !!indiId && familyHasParent(this, indiId);
2954
+ }
2955
+ hasChild(indi) {
2956
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2957
+ return !!indiId && familyHasChild(this, indiId);
2958
+ }
2959
+ canAddSpouse(indi) {
2960
+ return canAttachAsSpouse(this, indi);
2961
+ }
2962
+ addSpouse(indi) {
2963
+ return attachSpouseToFamily(this, indi);
2964
+ }
2965
+ addChild(child, pedigree, parent) {
2966
+ return attachChildToFamily(
2967
+ this,
2968
+ child,
2969
+ pedigree,
2970
+ parent
2971
+ );
2972
+ }
2973
+ addFact(input) {
2974
+ return addFamilyFact(this, input);
2975
+ }
2976
+ addNonEvent(input) {
2977
+ return addNonEvent(this, input);
2978
+ }
2448
2979
  toList() {
2449
2980
  return new Families().concat(
2450
2981
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2550,19 +3081,51 @@ var ALLOWED_FACTS = [
2550
3081
  "OCCU",
2551
3082
  "OCCUPATIONS",
2552
3083
  "RESI",
3084
+ "BIRT",
3085
+ "DEAT",
2553
3086
  "BAPT",
3087
+ "BAPM",
2554
3088
  "CHRI",
2555
3089
  "CHR",
3090
+ "CHRA",
2556
3091
  "BURI",
2557
- "EDUC",
3092
+ "CREM",
3093
+ "ADOP",
3094
+ "BARM",
3095
+ "BASM",
3096
+ "BLES",
3097
+ "CONF",
3098
+ "FCOM",
3099
+ "ORDN",
3100
+ "NATU",
3101
+ "EMIG",
3102
+ "IMMI",
3103
+ "CENS",
3104
+ "PROB",
3105
+ "WILL",
2558
3106
  "GRAD",
3107
+ "RETI",
3108
+ "EDUC",
2559
3109
  "DIV",
2560
- "IMMI",
2561
3110
  "RELI",
3111
+ "CAST",
3112
+ "DSCR",
3113
+ "NATI",
3114
+ "PROP",
3115
+ "SSN",
3116
+ "TITL",
3117
+ "ADDR",
3118
+ "BAPL",
3119
+ "CONL",
3120
+ "ENDL",
3121
+ "SLGC",
2562
3122
  "_MILT",
2563
3123
  "_MILTID",
2564
3124
  "FACT",
2565
- "_ORIG"
3125
+ "_ORIG",
3126
+ "_WLNK",
3127
+ "WWW",
3128
+ "URL"
2566
3129
  ];
2567
3130
  var DISALLOWED_CUSTOM_FACTS = ["DNA Test", "Newspaper"];
2568
3131
  var CustomFactRenderers = {
@@ -3106,7 +3669,7 @@ var Indi = class extends Common {
3106
3669
  return result;
3107
3670
  }
3108
3671
  ancestryLink() {
3109
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3672
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3110
3673
  const tree = this.getAncestryTreeId();
3111
3674
  if (this.id) {
3112
3675
  return `https://${www}/family-tree/person/tree/${tree}/person/${this.id.replace(
@@ -3118,7 +3681,7 @@ var Indi = class extends Common {
3118
3681
  async ancestryMedia(namespace) {
3119
3682
  const list = {};
3120
3683
  const objeList = this.get("OBJE")?.toList().copy();
3121
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3684
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3122
3685
  const tree = this.getAncestryTreeId();
3123
3686
  if (objeList) {
3124
3687
  await Promise.all(
@@ -3136,10 +3699,14 @@ var Indi = class extends Common {
3136
3699
  const clone = obje?.get("_CLON._OID")?.toValue();
3137
3700
  const mser = obje?.get("_MSER._LKID")?.toValue();
3138
3701
  let url = obje?.get("FILE")?.toValue();
3139
- const title = obje?.get("TITL")?.toValue() ?? "";
3140
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3141
- const imgId = clone || mser;
3142
- if (!www || !tree || !this.id) {
3702
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3703
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3704
+ let imgId = clone || mser;
3705
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3706
+ if (!hasEmbeddedOrLocalFile && (!www || !tree || !this.id)) {
3707
+ return;
3708
+ }
3709
+ if (!this.id) {
3143
3710
  return;
3144
3711
  }
3145
3712
  if (!namespace && !url) {
@@ -3157,13 +3724,16 @@ var Indi = class extends Common {
3157
3724
  }
3158
3725
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
3159
3726
  }
3727
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
3728
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
3729
+ }
3160
3730
  if (url && imgId) {
3161
- const id = `${tree}-${this.id}-${imgId}`;
3731
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
3162
3732
  list[id] = {
3163
3733
  key,
3164
3734
  isPrimary,
3165
3735
  id,
3166
- tree,
3736
+ tree: tree || "gedzip",
3167
3737
  imgId,
3168
3738
  person: this.id,
3169
3739
  title,
@@ -3178,7 +3748,7 @@ var Indi = class extends Common {
3178
3748
  return list;
3179
3749
  }
3180
3750
  myheritageLink(poolId = 0) {
3181
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.value?.toLowerCase().replace(/^www\./gi, "");
3751
+ const www = (this.getFromSourceHeads("SOUR.CORP.value") || this._gedcom?.HEAD?.SOUR?.CORP?.value)?.toLowerCase().replace(/^www\./gi, "");
3182
3752
  const site = this.getMyHeritageTreeId();
3183
3753
  const file = this._gedcom?.HEAD?.get("FILE")?.toValue()?.match(/Exported by MyHeritage.com from .+ in (?<site>.+) on .+$/)?.groups?.site;
3184
3754
  const normalizedFile = file?.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
@@ -3209,9 +3779,11 @@ var Indi = class extends Common {
3209
3779
  obje.standardizeMedia();
3210
3780
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3211
3781
  const url = obje?.get("FILE")?.toValue();
3212
- const title = obje?.get("NOTE")?.toValue() ?? "";
3213
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3214
- const imgId = obje?.get("_PHOTO_RIN")?.toValue();
3782
+ const title = obje?.get("FILE.NOTE")?.toValue() ?? obje?.get("NOTE")?.toValue() ?? "";
3783
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3784
+ const photoRin = obje?.get("_PHOTO_RIN")?.toValue();
3785
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3786
+ const imgId = photoRin || (hasEmbeddedOrLocalFile && url ? `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}` : void 0);
3215
3787
  if (url && imgId) {
3216
3788
  const id = `${tree}-${this.id}-${imgId}`;
3217
3789
  list[id] = {
@@ -3334,10 +3906,10 @@ var Indi = class extends Common {
3334
3906
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3335
3907
  const url = obje?.get("FILE")?.toValue();
3336
3908
  const title = obje?.get("TITL")?.toValue() ?? "";
3337
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3909
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3338
3910
  if (url) {
3339
3911
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
3340
- const imgId = urlMatch?.[1] || `img-${index}-${Date.now().toString(36)}`;
3912
+ const imgId = urlMatch?.[1] || (url.startsWith("data:") ? `data-${index}-${getFileExtension(url) || "bin"}` : `img-${index}-${Date.now().toString(36)}`);
3341
3913
  const id = `geni-${geniId}-${imgId}`;
3342
3914
  list[id] = {
3343
3915
  isPrimary,
@@ -3374,10 +3946,10 @@ var Indi = class extends Common {
3374
3946
  obje.standardizeMedia();
3375
3947
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3376
3948
  const url = obje?.get("FILE")?.toValue();
3377
- const title = obje?.get("TITL")?.toValue() ?? "";
3378
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3949
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3950
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3379
3951
  if (url) {
3380
- const imgId = `media-${index}-${url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3952
+ const imgId = `media-${index}-${url.startsWith("data:") ? getFileExtension(url) || "bin" : url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3381
3953
  const id = `${treeId}-${this.id}-${imgId}`;
3382
3954
  list[id] = {
3383
3955
  isPrimary,
@@ -3417,7 +3989,20 @@ var Indi = class extends Common {
3417
3989
  cacheKey
3418
3990
  );
3419
3991
  if (cached !== void 0) {
3420
- return cached;
3992
+ if (cached?.file && !isRemoteOrEmbeddedMediaUrl(cached.file)) {
3993
+ const resolvedCached = await resolveLocalMediaUrl(cached.file);
3994
+ if (resolvedCached && isRemoteOrEmbeddedMediaUrl(resolvedCached)) {
3995
+ const refreshed = { ...cached, file: resolvedCached };
3996
+ profilePictureCache(
3997
+ this._gedcom,
3998
+ cacheKey,
3999
+ refreshed
4000
+ );
4001
+ return refreshed;
4002
+ }
4003
+ } else {
4004
+ return cached;
4005
+ }
3421
4006
  }
3422
4007
  const mediaList = await this.multimedia(namespace);
3423
4008
  if (!mediaList) {
@@ -3429,39 +4014,40 @@ var Indi = class extends Common {
3429
4014
  return void 0;
3430
4015
  }
3431
4016
  const mediaArray = Object.values(mediaList);
3432
- const primaryMedia = mediaArray.find(
3433
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
3434
- );
3435
- if (primaryMedia) {
3436
- const result = {
3437
- file: primaryMedia.url,
3438
- form: primaryMedia.contentType,
3439
- title: primaryMedia.title,
3440
- isPrimary: true
4017
+ const toDisplayableProfile = async (media, isPrimary) => {
4018
+ const resolvedFile = await resolveLocalMediaUrl(media.url);
4019
+ if (!resolvedFile || !isRemoteOrEmbeddedMediaUrl(resolvedFile)) {
4020
+ return void 0;
4021
+ }
4022
+ return {
4023
+ file: resolvedFile,
4024
+ form: media.contentType,
4025
+ title: media.title,
4026
+ isPrimary
3441
4027
  };
3442
- profilePictureCache(this._gedcom, cacheKey, result);
3443
- return result;
3444
- }
3445
- if (onlyPrimary) {
3446
- profilePictureCache(
3447
- this._gedcom,
3448
- cacheKey,
3449
- void 0
3450
- );
3451
- return void 0;
3452
- }
3453
- const secondaryMedia = mediaArray.find(
3454
- (media) => isImageFormat(media.contentType || getFileExtension(media.url))
4028
+ };
4029
+ const candidates = mediaArray.filter(
4030
+ (media) => isImageFormat(
4031
+ media.contentType || media.url || getFileExtension(media.url)
4032
+ )
3455
4033
  );
3456
- if (secondaryMedia) {
3457
- const result = {
3458
- file: secondaryMedia.url,
3459
- form: secondaryMedia.contentType,
3460
- title: secondaryMedia.title,
3461
- isPrimary: false
3462
- };
3463
- profilePictureCache(this._gedcom, cacheKey, result);
3464
- return result;
4034
+ const ordered = [
4035
+ ...candidates.filter((media) => media.isPrimary),
4036
+ ...candidates.filter((media) => !media.isPrimary)
4037
+ ];
4038
+ for (const media of ordered) {
4039
+ if (onlyPrimary && !media.isPrimary) {
4040
+ break;
4041
+ }
4042
+ const result = await toDisplayableProfile(media, !!media.isPrimary);
4043
+ if (result) {
4044
+ profilePictureCache(
4045
+ this._gedcom,
4046
+ cacheKey,
4047
+ result
4048
+ );
4049
+ return result;
4050
+ }
3465
4051
  }
3466
4052
  profilePictureCache(
3467
4053
  this._gedcom,
@@ -3699,31 +4285,181 @@ var Indi = class extends Common {
3699
4285
  isUnknownSex() {
3700
4286
  return !this.isMale() && !this.isFemale();
3701
4287
  }
3702
- getParentType(id) {
3703
- let indi;
3704
- if (typeof id === "string" || typeof id === "number") {
3705
- indi = this._gedcom?.indi(id);
3706
- } else {
3707
- indi = id;
4288
+ addSpouse(other) {
4289
+ if (!this.id || !other?.id || this.id === other.id || !this._gedcom) {
4290
+ return void 0;
3708
4291
  }
3709
- if (!indi?.id || !this.id) {
3710
- return;
4292
+ const existing = getFamilyWith(this, other.id);
4293
+ if (existing) {
4294
+ return existing;
3711
4295
  }
3712
- const parents = this.getParents();
3713
- let parent = parents.item(indi.id) ? indi : void 0;
3714
- let child = this._gedcom?.indi(this.id);
3715
- if (!parent?.id) {
3716
- const children = this.getChildren();
3717
- if (children.item(indi.id)) {
3718
- parent = this._gedcom?.indi(this.id);
3719
- child = indi;
3720
- }
4296
+ const fam = this._gedcom.createFamily();
4297
+ if (!fam.addSpouse(this) || !fam.addSpouse(other)) {
4298
+ return void 0;
3721
4299
  }
3722
- const familiesOfChildren = child?.get("FAMC")?.toValueList();
3723
- if (!parent?.id || !child?.id || !familiesOfChildren) {
3724
- return;
4300
+ return fam;
4301
+ }
4302
+ addChild(child, pedigree) {
4303
+ if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
4304
+ return void 0;
3725
4305
  }
3726
- const families = this.toFamilies(familiesOfChildren);
4306
+ const existing = findReusableParentChildFamily(
4307
+ this,
4308
+ child
4309
+ );
4310
+ if (existing === "already") {
4311
+ const fam2 = child.getFamilies("FAMC")?.find(
4312
+ (item) => !!item && item.hasParent(this.id) && item.hasChild(child.id)
4313
+ );
4314
+ if (fam2) {
4315
+ setChildPedigree(
4316
+ fam2,
4317
+ child,
4318
+ pedigree,
4319
+ this
4320
+ );
4321
+ }
4322
+ return fam2;
4323
+ }
4324
+ const fam = existing ?? this._gedcom.createFamily();
4325
+ if (!fam.addSpouse(this)) {
4326
+ return void 0;
4327
+ }
4328
+ if (!fam.addChild(child, pedigree, this)) {
4329
+ return void 0;
4330
+ }
4331
+ return fam;
4332
+ }
4333
+ addParent(parent, pedigree) {
4334
+ return parent.addChild(this, pedigree);
4335
+ }
4336
+ /**
4337
+ * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
4338
+ */
4339
+ attachMultimedia(objeOrKey, options) {
4340
+ if (!this._gedcom || !this.id) {
4341
+ return void 0;
4342
+ }
4343
+ const obje = typeof objeOrKey === "string" ? this._gedcom.obje(objeOrKey) : objeOrKey;
4344
+ if (!obje?.id) {
4345
+ return void 0;
4346
+ }
4347
+ const already = this.get("OBJE")?.toList()?.values()?.some((item) => item?.toValue?.() === obje.id);
4348
+ if (!already) {
4349
+ const pointer = makePointer(
4350
+ this._gedcom,
4351
+ this,
4352
+ obje.id,
4353
+ "OBJE"
4354
+ );
4355
+ this.assign("OBJE", pointer);
4356
+ }
4357
+ if (options?.primary) {
4358
+ this.get("OBJE")?.toList()?.forEach((item) => {
4359
+ const target = item?.refType === "OBJE" ? this._gedcom?.obje(
4360
+ String(item.toValue())
4361
+ ) : item;
4362
+ if (!target) {
4363
+ return;
4364
+ }
4365
+ if (target.id === obje.id) {
4366
+ target.set("_PRIM", "Y");
4367
+ } else if (target.get("_PRIM")?.toValue() === "Y") {
4368
+ target.set("_PRIM", "N");
4369
+ }
4370
+ });
4371
+ obje.set("_PRIM", "Y");
4372
+ }
4373
+ return obje;
4374
+ }
4375
+ detachMultimedia(objeKey) {
4376
+ const list = this.get("OBJE")?.toList();
4377
+ if (!list) {
4378
+ return;
4379
+ }
4380
+ const toRemove = list.values().filter(
4381
+ (item) => item?.toValue?.() === objeKey || item?.id === objeKey
4382
+ );
4383
+ toRemove.forEach((item) => {
4384
+ if (item) {
4385
+ list.delete(item);
4386
+ }
4387
+ });
4388
+ if (list.length === 0) {
4389
+ this.remove("OBJE");
4390
+ }
4391
+ }
4392
+ /**
4393
+ * Create a multimedia record and attach it to this person.
4394
+ */
4395
+ attachMediaFromUrl(url, options) {
4396
+ if (!this._gedcom || !url) {
4397
+ return void 0;
4398
+ }
4399
+ const obje = this._gedcom.createMultimediaRecord({
4400
+ file: url,
4401
+ form: options?.form,
4402
+ title: options?.title,
4403
+ mediType: options?.mediType,
4404
+ primary: options?.primary,
4405
+ gedcomVersion: options?.gedcomVersion
4406
+ });
4407
+ return this.attachMultimedia(obje, { primary: options?.primary });
4408
+ }
4409
+ /**
4410
+ * Link a shared SNOTE record via `1 NOTE @Nn@` (GEDCOM 7).
4411
+ */
4412
+ attachSharedNote(noteOrKey) {
4413
+ if (!this._gedcom || !this.id) {
4414
+ return void 0;
4415
+ }
4416
+ const note = typeof noteOrKey === "string" ? this._gedcom.snote(noteOrKey) : noteOrKey;
4417
+ if (!note?.id) {
4418
+ return void 0;
4419
+ }
4420
+ const already = this.get("NOTE")?.toList()?.values()?.some((item) => item?.toValue?.() === note.id);
4421
+ if (!already) {
4422
+ const pointer = makePointer(
4423
+ this._gedcom,
4424
+ this,
4425
+ note.id,
4426
+ "SNOTE"
4427
+ );
4428
+ this.assign("NOTE", pointer);
4429
+ }
4430
+ return note;
4431
+ }
4432
+ addFact(input) {
4433
+ return addIndividualFact(this, input);
4434
+ }
4435
+ addNonEvent(input) {
4436
+ return addNonEvent(this, input);
4437
+ }
4438
+ getParentType(id) {
4439
+ let indi;
4440
+ if (typeof id === "string" || typeof id === "number") {
4441
+ indi = this._gedcom?.indi(id);
4442
+ } else {
4443
+ indi = id;
4444
+ }
4445
+ if (!indi?.id || !this.id) {
4446
+ return;
4447
+ }
4448
+ const parents = this.getParents();
4449
+ let parent = parents.item(indi.id) ? indi : void 0;
4450
+ let child = this._gedcom?.indi(this.id);
4451
+ if (!parent?.id) {
4452
+ const children = this.getChildren();
4453
+ if (children.item(indi.id)) {
4454
+ parent = this._gedcom?.indi(this.id);
4455
+ child = indi;
4456
+ }
4457
+ }
4458
+ const familiesOfChildren = child?.get("FAMC")?.toValueList();
4459
+ if (!parent?.id || !child?.id || !familiesOfChildren) {
4460
+ return;
4461
+ }
4462
+ const families = this.toFamilies(familiesOfChildren);
3727
4463
  let childType = "";
3728
4464
  families.forEach((family) => {
3729
4465
  if (childType) {
@@ -3739,7 +4475,7 @@ var Indi = class extends Common {
3739
4475
  if (!famChild) {
3740
4476
  const childIndi = this._gedcom?.indi(child.id);
3741
4477
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
3742
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4478
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
3743
4479
  } else {
3744
4480
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
3745
4481
  }
@@ -3899,7 +4635,9 @@ var Indi = class extends Common {
3899
4635
  }
3900
4636
  }
3901
4637
  if (kinship !== "child" || !breakAfterSpouse) {
3902
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
4638
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(
4639
+ indi.getFathers().copy().merge(indi.getMothers())
4640
+ ).forEach((relative) => {
3903
4641
  if (!visited.has(relative)) {
3904
4642
  const currentRelation = indi.getParentType(relative);
3905
4643
  const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
@@ -4298,7 +5036,7 @@ var Indi = class extends Common {
4298
5036
  return;
4299
5037
  }
4300
5038
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4301
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
5039
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4302
5040
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4303
5041
  children.append(child);
4304
5042
  }
@@ -4406,7 +5144,7 @@ var Indi = class extends Common {
4406
5144
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
4407
5145
  if (!child) {
4408
5146
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4409
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
5147
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4410
5148
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4411
5149
  parents.merge(family.getParents());
4412
5150
  }
@@ -6807,7 +7545,11 @@ var STANDARD_QUALIFIERS = {
6807
7545
  BEF: "Bef.",
6808
7546
  BEFORE: "Bef.",
6809
7547
  AFT: "Aft.",
6810
- AFTER: "Aft."
7548
+ AFTER: "Aft.",
7549
+ CAL: "Abt.",
7550
+ // calculated ≈ about for display
7551
+ EST: "Abt."
7552
+ // estimated ≈ about for display
6811
7553
  };
6812
7554
  var EXPORT_QUALIFIERS = {
6813
7555
  "Abt.": "ABT",
@@ -6815,6 +7557,11 @@ var EXPORT_QUALIFIERS = {
6815
7557
  "Aft.": "AFT"
6816
7558
  };
6817
7559
  var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
7560
+ var CALENDAR_PREFIX = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
7561
+ var isGedcomDateValueForm = (value) => (
7562
+ // Require space/end after the token so display forms like "Abt." are not opaque.
7563
+ /^(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)(\s|$)/i.test(value) || /\bAND\b/i.test(value)
7564
+ );
6818
7565
  var NOTE_MARKER = "####";
6819
7566
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
6820
7567
  const hasNote = baseFormat.includes("NOTE");
@@ -6839,29 +7586,45 @@ var CommonDate = class extends Common {
6839
7586
  super(gedcom, id, main, parent);
6840
7587
  delete this.id;
6841
7588
  }
7589
+ get calendar() {
7590
+ return this._calendar;
7591
+ }
6842
7592
  set value(value) {
6843
7593
  if (value) {
6844
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
6845
- const noteMatch = value.match(noteRegExp)?.groups;
6846
7594
  let validValue = value;
7595
+ const calendarMatch = validValue.match(CALENDAR_PREFIX);
7596
+ if (calendarMatch?.groups?.calendar) {
7597
+ this._calendar = calendarMatch.groups.calendar.toUpperCase();
7598
+ validValue = validValue.slice(calendarMatch[0].length);
7599
+ } else {
7600
+ this._calendar = void 0;
7601
+ }
7602
+ const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
7603
+ const noteMatch = validValue.match(noteRegExp)?.groups;
6847
7604
  if (noteMatch?.note) {
6848
7605
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6849
7606
  this.NOTE.value = noteMatch?.note;
6850
- validValue = value.replace(noteRegExp, "");
7607
+ validValue = validValue.replace(noteRegExp, "");
6851
7608
  } else {
6852
- const qualifierMatch = value.match(
6853
- /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
7609
+ const qualifierMatch = validValue.match(
7610
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER|CAL|EST)\b\s*/i
6854
7611
  );
6855
7612
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
6856
7613
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
6857
7614
  if (note) {
6858
7615
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6859
7616
  this.NOTE.value = note;
6860
- validValue = value.slice(qualifierMatch[0].length);
7617
+ validValue = validValue.slice(qualifierMatch[0].length);
6861
7618
  }
6862
7619
  }
6863
7620
  const acceptedDate = this.isValidDateFormat(validValue);
6864
- if (acceptedDate) {
7621
+ if (isGedcomDateValueForm(validValue.trim())) {
7622
+ delete this.DAY;
7623
+ delete this.MONTH;
7624
+ delete this.YEAR;
7625
+ this._date = void 0;
7626
+ this._value = value;
7627
+ } else if (acceptedDate) {
6865
7628
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
6866
7629
  this.DAY.value = format(acceptedDate, "dd");
6867
7630
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -6964,6 +7727,15 @@ var CommonDate = class extends Common {
6964
7727
  );
6965
7728
  }
6966
7729
  exportValue() {
7730
+ if (typeof this._value === "string") {
7731
+ const withoutCalendar = this._value.replace(CALENDAR_PREFIX, "");
7732
+ if (isGedcomDateValueForm(withoutCalendar.trim())) {
7733
+ return this._value.replace(
7734
+ /^((?:GREGORIAN|JULIAN|FRENCH_R|HEBREW)\s+)?(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)\b/i,
7735
+ (_m, cal, q) => `${cal || ""}${q.toUpperCase()}`
7736
+ );
7737
+ }
7738
+ }
6967
7739
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
6968
7740
  if (!formatted) {
6969
7741
  return formatted;
@@ -6971,10 +7743,14 @@ var CommonDate = class extends Common {
6971
7743
  const note = this.NOTE?.value?.trim();
6972
7744
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
6973
7745
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
6974
- return standardized.replace(
7746
+ const withMonths = standardized.replace(
6975
7747
  GEDCOM_MONTHS,
6976
7748
  (month) => month.toUpperCase()
6977
7749
  );
7750
+ if (this._calendar && this._calendar !== "GREGORIAN") {
7751
+ return `${this._calendar} ${withMonths}`;
7752
+ }
7753
+ return withMonths;
6978
7754
  }
6979
7755
  toGedcomLines(tag, level = 0, options) {
6980
7756
  const internal = new RegExp(
@@ -6995,130 +7771,1025 @@ var isCommonDate = (value) => {
6995
7771
  return !!value && value !== null && typeof value === "object" && "_date" in value;
6996
7772
  };
6997
7773
 
6998
- // package.json
6999
- var package_default = {
7000
- name: "@treeviz/gedcom-parser",
7001
- version: "2.0.10"};
7002
-
7003
- // src/utils/get-product-details.ts
7004
- var isDevelopment = () => {
7005
- return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
7006
- };
7007
- var getVersion = () => package_default.version;
7008
- var getName = () => package_default.name;
7009
-
7010
- // src/classes/gedcom.ts
7011
- var isObjectPatch = (value) => {
7012
- return !!value && typeof value === "object" && !Array.isArray(value);
7013
- };
7014
- var GedCom = class extends Common {
7015
- constructor() {
7016
- super();
7017
- this.tagMembers = {};
7018
- this.reflist = {};
7019
- this.refcount = 0;
7020
- delete this._gedcom;
7021
- delete this.id;
7022
- this.removeValue();
7023
- }
7024
- getMain(type, index) {
7025
- const list = !type || type instanceof List ? type : this.getList(type);
7026
- if (!list) {
7027
- return void 0;
7774
+ // src/utils/gedcom-version.ts
7775
+ var GEDCOM_EXPORT_VERSIONS = ["5.5.1", "7.0"];
7776
+ var DEFAULT_GEDCOM_EXPORT_VERSION = "5.5.1";
7777
+ var normalizeGedcomVersion = (value) => {
7778
+ const trimmed = value?.trim() ?? "";
7779
+ if (trimmed.startsWith("7")) {
7780
+ return "7.0";
7781
+ }
7782
+ return "5.5.1";
7783
+ };
7784
+ var isGedcom7 = (value) => normalizeGedcomVersion(value) === "7.0";
7785
+ var GEDZIP_MIME = "application/vnd.familysearch.gedcom+zip";
7786
+ var GEDZIP_EXTENSION = "gdz";
7787
+ var GEDZIP_GEDCOM_ENTRY = "gedcom.ged";
7788
+ var sanitizePathSegment = (name) => name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^\.+/, "") || "file";
7789
+ var extensionFor = (contentType) => {
7790
+ const raw = (contentType || "bin").replace(/^\./, "").toLowerCase();
7791
+ if (raw.includes("/")) {
7792
+ return raw.split("/").pop() || "bin";
7793
+ }
7794
+ return raw || "bin";
7795
+ };
7796
+ var rewriteGedcomFilePaths = (gedcomText, urlToPath) => {
7797
+ const entries = urlToPath instanceof Map ? urlToPath : new Map(Object.entries(urlToPath));
7798
+ let text = gedcomText;
7799
+ entries.forEach((path, url) => {
7800
+ const escaped = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7801
+ text = text.replace(
7802
+ new RegExp(`(^|\\r?\\n)(\\d+ FILE )${escaped}`, "g"),
7803
+ `$1$2${path}`
7804
+ );
7805
+ });
7806
+ return text;
7807
+ };
7808
+ var assignMediaPaths = (mediaFiles) => {
7809
+ const urlToPath = /* @__PURE__ */ new Map();
7810
+ const usedPaths = /* @__PURE__ */ new Set([GEDZIP_GEDCOM_ENTRY]);
7811
+ const files = [];
7812
+ for (const file of mediaFiles) {
7813
+ if (!file.content || !file.url) {
7814
+ continue;
7028
7815
  }
7029
- if (typeof index === "string") {
7030
- return list.item(index);
7816
+ const ext = extensionFor(file.contentType);
7817
+ const base = sanitizePathSegment(
7818
+ file.imgId || file.title || file.id || "media"
7819
+ );
7820
+ let path = `media/${base}.${ext}`;
7821
+ let suffix = 1;
7822
+ while (usedPaths.has(path)) {
7823
+ path = `media/${base}-${suffix++}.${ext}`;
7824
+ }
7825
+ usedPaths.add(path);
7826
+ urlToPath.set(file.url, path);
7827
+ const marker = "base64,";
7828
+ const idx = file.content.indexOf(marker);
7829
+ if (idx >= 0) {
7830
+ files.push({
7831
+ path,
7832
+ url: file.url,
7833
+ content: file.content.substring(idx + marker.length),
7834
+ base64: true
7835
+ });
7836
+ } else {
7837
+ files.push({
7838
+ path,
7839
+ url: file.url,
7840
+ content: file.content,
7841
+ base64: false
7842
+ });
7031
7843
  }
7032
- const keys = list.keys();
7033
- return list.item(keys[index]);
7034
- }
7035
- getList(type) {
7036
- return this.get(type);
7037
7844
  }
7038
- createNodeFromPatchValue(value, main) {
7039
- if (value === void 0 || value === null) {
7040
- return void 0;
7041
- }
7042
- const node = createCommon(this, void 0, main);
7043
- if (Array.isArray(value)) {
7044
- value.forEach((item) => {
7045
- const childNode = this.createNodeFromPatchValue(item, node);
7046
- if (childNode) {
7047
- node.assign("DATA", childNode);
7048
- }
7049
- });
7050
- return node;
7845
+ return { urlToPath, files };
7846
+ };
7847
+ var buildGedzip = async (gedcomText, mediaFiles = []) => {
7848
+ const zip = new JSZip();
7849
+ const { urlToPath, files } = assignMediaPaths(mediaFiles);
7850
+ for (const file of files) {
7851
+ zip.file(file.path, file.content, { base64: file.base64 });
7852
+ }
7853
+ zip.file(GEDZIP_GEDCOM_ENTRY, rewriteGedcomFilePaths(gedcomText, urlToPath));
7854
+ return zip.generateAsync({ type: "uint8array" });
7855
+ };
7856
+ var normalizeZipPath = (name) => name.replace(/\\/g, "/").replace(/^\.\//, "");
7857
+ var MIME_BY_EXT = {
7858
+ jpg: "image/jpeg",
7859
+ jpeg: "image/jpeg",
7860
+ png: "image/png",
7861
+ gif: "image/gif",
7862
+ webp: "image/webp",
7863
+ bmp: "image/bmp",
7864
+ tif: "image/tiff",
7865
+ tiff: "image/tiff",
7866
+ svg: "image/svg+xml",
7867
+ pdf: "application/pdf"
7868
+ };
7869
+ var mimeForPath = (path) => {
7870
+ const ext = (path.split(".").pop() || "").toLowerCase();
7871
+ return MIME_BY_EXT[ext] || "application/octet-stream";
7872
+ };
7873
+ var bytesToBase64 = (bytes) => {
7874
+ if (typeof Buffer !== "undefined") {
7875
+ return Buffer.from(bytes).toString("base64");
7876
+ }
7877
+ let binary = "";
7878
+ const chunk = 32768;
7879
+ for (let i = 0; i < bytes.length; i += chunk) {
7880
+ binary += String.fromCharCode(...bytes.subarray(i, i + chunk));
7881
+ }
7882
+ return btoa(binary);
7883
+ };
7884
+ var bytesToDataUrl = (bytes, mime) => `data:${mime};base64,${bytesToBase64(bytes)}`;
7885
+ var remountGedzipMediaAsDataUrls = (extracted) => {
7886
+ if (!extracted.mediaEntries.length) {
7887
+ return extracted.gedcomText;
7888
+ }
7889
+ const pathToDataUrl = /* @__PURE__ */ new Map();
7890
+ for (const media of extracted.mediaEntries) {
7891
+ const path = normalizeZipPath(media.path);
7892
+ const dataUrl = bytesToDataUrl(media.content, mimeForPath(path));
7893
+ pathToDataUrl.set(path, dataUrl);
7894
+ pathToDataUrl.set(`./${path}`, dataUrl);
7895
+ const base = path.split("/").pop();
7896
+ if (base) {
7897
+ pathToDataUrl.set(base, dataUrl);
7898
+ pathToDataUrl.set(`media/${base}`, dataUrl);
7899
+ }
7900
+ }
7901
+ return rewriteGedcomFilePaths(extracted.gedcomText, pathToDataUrl);
7902
+ };
7903
+ var extractGedzip = async (data) => {
7904
+ const zip = await JSZip.loadAsync(data);
7905
+ const entries = Object.values(zip.files).filter((entry) => !entry.dir);
7906
+ const preferred = entries.find(
7907
+ (entry) => normalizeZipPath(entry.name).toLowerCase() === GEDZIP_GEDCOM_ENTRY
7908
+ ) ?? entries.find(
7909
+ (entry) => normalizeZipPath(entry.name).toLowerCase().endsWith(".ged")
7910
+ );
7911
+ if (!preferred) {
7912
+ throw new Error("No .ged file found in the archive.");
7913
+ }
7914
+ const gedcomText = await preferred.async("string");
7915
+ const mediaEntries = [];
7916
+ for (const entry of entries) {
7917
+ const path = normalizeZipPath(entry.name);
7918
+ if (path === normalizeZipPath(preferred.name)) {
7919
+ continue;
7051
7920
  }
7052
- if (isObjectPatch(value)) {
7053
- this.applyObjectToCommon(node, value);
7054
- return node;
7921
+ if (path.toLowerCase().endsWith(".ged")) {
7922
+ continue;
7055
7923
  }
7056
- node.value = String(value);
7057
- return node;
7924
+ mediaEntries.push({
7925
+ path,
7926
+ content: await entry.async("uint8array")
7927
+ });
7058
7928
  }
7059
- applyObjectToCommon(target, patch) {
7060
- Object.entries(patch).forEach(([rawTag, value]) => {
7061
- if (rawTag === "value") {
7062
- if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
7929
+ return {
7930
+ gedcomText,
7931
+ entryName: normalizeZipPath(preferred.name),
7932
+ mediaEntries
7933
+ };
7934
+ };
7935
+ var extractGedzipGedcom = async (data, options) => {
7936
+ const extracted = await extractGedzip(data);
7937
+ if (options?.embedMediaAsDataUrls === false) {
7938
+ return extracted.gedcomText;
7939
+ }
7940
+ return remountGedzipMediaAsDataUrls(extracted);
7941
+ };
7942
+ var isGedzipContainer = (file) => {
7943
+ const name = (file.name || "").toLowerCase();
7944
+ const type = (file.type || "").toLowerCase();
7945
+ return name.endsWith(`.${GEDZIP_EXTENSION}`) || name.endsWith(".zip") || type === GEDZIP_MIME || type === "application/zip" || type === "application/x-zip-compressed";
7946
+ };
7947
+ var downloadGedzipMedia = async (media, onProgress) => {
7948
+ const entries = Object.values(media).filter((item) => !!item.url);
7949
+ const total = entries.length;
7950
+ let done = 0;
7951
+ const downloaded = [];
7952
+ await Promise.all(
7953
+ entries.map(async (item) => {
7954
+ try {
7955
+ const resolved = await resolveMediaContent({
7956
+ url: item.url,
7957
+ id: item.id,
7958
+ imgId: item.imgId,
7959
+ key: "key" in item ? item.key : void 0,
7960
+ contentType: item.contentType
7961
+ });
7962
+ if (!resolved?.content) {
7063
7963
  return;
7064
7964
  }
7065
- target.value = String(value);
7066
- return;
7067
- }
7068
- if (rawTag === "id") {
7069
- if (typeof value === "string") {
7070
- target.id = value;
7071
- }
7072
- return;
7073
- }
7074
- const tag = rawTag;
7075
- if (value === void 0) {
7076
- return;
7077
- }
7078
- if (value === null) {
7079
- target.remove(tag);
7080
- return;
7081
- }
7082
- if (Array.isArray(value)) {
7083
- target.remove(tag);
7084
- value.forEach((item) => {
7085
- const listNode = this.createNodeFromPatchValue(
7086
- item,
7087
- target.main ?? this
7088
- );
7089
- if (listNode) {
7090
- target.assign(tag, listNode);
7091
- }
7965
+ downloaded.push({
7966
+ ...item,
7967
+ content: resolved.content,
7968
+ contentType: resolved.contentType || item.contentType
7092
7969
  });
7093
- return;
7094
- }
7095
- if (isObjectPatch(value)) {
7096
- const existing = target.get(tag);
7097
- const objectNode = existing ?? createCommon(this, void 0, target.main ?? this);
7098
- this.applyObjectToCommon(objectNode, value);
7099
- target.set(tag, objectNode);
7100
- return;
7970
+ } catch {
7971
+ } finally {
7972
+ done += 1;
7973
+ onProgress?.(done, total);
7101
7974
  }
7102
- target.set(tag, String(value));
7975
+ })
7976
+ );
7977
+ return downloaded;
7978
+ };
7979
+ var buildGedzipBlob = async (gedcomText, mediaFiles = []) => {
7980
+ const bytes = await buildGedzip(gedcomText, mediaFiles);
7981
+ return new Blob([bytes], { type: GEDZIP_MIME });
7982
+ };
7983
+
7984
+ // src/utils/gedcom7-enumerations.ts
7985
+ var GEDCOM7_SEX_VALUES = ["M", "F", "U", "X", "N"];
7986
+ var GEDCOM7_PEDI_VALUES = [
7987
+ "adopted",
7988
+ "birth",
7989
+ "foster",
7990
+ "sealing"
7991
+ ];
7992
+ var GEDCOM7_ROLE_VALUES = [
7993
+ "CHIL",
7994
+ "CLERGY",
7995
+ "FATH",
7996
+ "FRIEND",
7997
+ "GODP",
7998
+ "HUSB",
7999
+ "MOTH",
8000
+ "MULTIPLE",
8001
+ "NGHBR",
8002
+ "OFFICIATOR",
8003
+ "OTHER",
8004
+ "PARENT",
8005
+ "SPOU",
8006
+ "WIFE",
8007
+ "WITN"
8008
+ ];
8009
+ var GEDCOM7_RESN_VALUES = [
8010
+ "CONFIDENTIAL",
8011
+ "LOCKED",
8012
+ "PRIVACY"
8013
+ ];
8014
+ var isAllowed = (value, allowed) => allowed.includes(value);
8015
+ var moveToPhrase = (node, phrase) => {
8016
+ const previousValue = node.value;
8017
+ const previousPhrase = node.get("PHRASE");
8018
+ const previousPhraseValue = previousPhrase?.toValue();
8019
+ node.removeValue();
8020
+ if (previousPhrase) {
8021
+ previousPhrase.value = phrase;
8022
+ } else {
8023
+ const phraseNode = createCommon(node.getGedcom(), void 0, node.main);
8024
+ phraseNode.value = phrase;
8025
+ node.set("PHRASE", phraseNode);
8026
+ }
8027
+ return () => {
8028
+ if (previousValue === void 0) {
8029
+ node.removeValue();
8030
+ } else {
8031
+ node.value = previousValue;
8032
+ }
8033
+ if (previousPhrase) {
8034
+ previousPhrase.value = previousPhraseValue;
8035
+ } else {
8036
+ node.remove("PHRASE");
8037
+ }
8038
+ };
8039
+ };
8040
+ var normalizeEnum = (node, allowed, mode, restores) => {
8041
+ const value = node?.toValue();
8042
+ if (!node || typeof value !== "string" || !value.trim()) {
8043
+ return;
8044
+ }
8045
+ const normalized = mode === "upper" ? value.trim().toUpperCase() : value.trim().toLowerCase();
8046
+ if (isAllowed(normalized, allowed)) {
8047
+ if (value !== normalized) {
8048
+ const previous = node.value;
8049
+ node.value = normalized;
8050
+ restores.push(() => {
8051
+ node.value = previous;
8052
+ });
8053
+ }
8054
+ return;
8055
+ }
8056
+ restores.push(moveToPhrase(node, value.trim()));
8057
+ };
8058
+ var applyGedcom7Enumerations = (gedcom) => {
8059
+ const restores = [];
8060
+ gedcom.indis()?.forEach((indi) => {
8061
+ if (!indi) return;
8062
+ normalizeEnum(indi.get("SEX"), GEDCOM7_SEX_VALUES, "upper", restores);
8063
+ normalizeEnum(indi.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
8064
+ indi.get("FAMC")?.toList()?.forEach((famc) => {
8065
+ normalizeEnum(
8066
+ famc?.get("PEDI") ?? famc?.get("PEDT"),
8067
+ GEDCOM7_PEDI_VALUES,
8068
+ "lower",
8069
+ restores
8070
+ );
8071
+ });
8072
+ indi.get("ASSO")?.toList()?.forEach((asso) => {
8073
+ normalizeEnum(
8074
+ asso?.get("ROLE"),
8075
+ GEDCOM7_ROLE_VALUES,
8076
+ "upper",
8077
+ restores
8078
+ );
7103
8079
  });
8080
+ });
8081
+ gedcom.fams()?.forEach((fam) => {
8082
+ if (!fam) return;
8083
+ normalizeEnum(fam.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
8084
+ fam.get("ASSO")?.toList()?.forEach((asso) => {
8085
+ normalizeEnum(
8086
+ asso?.get("ROLE"),
8087
+ GEDCOM7_ROLE_VALUES,
8088
+ "upper",
8089
+ restores
8090
+ );
8091
+ });
8092
+ });
8093
+ return () => {
8094
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
8095
+ restores[i]?.();
8096
+ }
8097
+ };
8098
+ };
8099
+
8100
+ // src/utils/gedcom7-dates.ts
8101
+ var GEDCOM7_CALENDARS = [
8102
+ "GREGORIAN",
8103
+ "JULIAN",
8104
+ "FRENCH_R",
8105
+ "HEBREW"
8106
+ ];
8107
+ var CALENDAR_PREFIX2 = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
8108
+ var parseGedcom7CalendarPrefix = (value) => {
8109
+ const match = value.match(CALENDAR_PREFIX2);
8110
+ if (!match?.groups?.calendar) {
8111
+ return { calendar: void 0, rest: value };
7104
8112
  }
7105
- applyObject(patch) {
7106
- this.applyObjectToCommon(this, patch);
7107
- return this;
8113
+ return {
8114
+ calendar: match.groups.calendar.toUpperCase(),
8115
+ rest: value.slice(match[0].length)
8116
+ };
8117
+ };
8118
+ var withGedcom7Calendar = (value, calendar) => {
8119
+ if (!calendar || calendar.toUpperCase() === "GREGORIAN") {
8120
+ return value;
7108
8121
  }
7109
- indis() {
7110
- return this.getList("@@INDI");
8122
+ const upper = calendar.toUpperCase();
8123
+ if (GEDCOM7_CALENDARS.includes(upper)) {
8124
+ const { rest } = parseGedcom7CalendarPrefix(value);
8125
+ return `${upper} ${rest}`.trim();
7111
8126
  }
7112
- cloneIndis(target, source, avoidKeys = [], removeFromOriginalList = true) {
7113
- const hasFAMS = avoidKeys.includes("FAMS");
7114
- const targetIndi = typeof target === "string" ? this.indi(target) : target;
7115
- const sourceIndi = typeof source === "string" ? this.indi(source) : source;
7116
- if (!targetIndi || !sourceIndi) {
7117
- return this;
8127
+ return value;
8128
+ };
8129
+ var applyGedcom7DatePhrases = (gedcom) => {
8130
+ const restores = [];
8131
+ const visitDate = (dateNode) => {
8132
+ if (!dateNode || typeof dateNode.toValue !== "function") {
8133
+ return;
7118
8134
  }
7119
- const spousesFamily = hasFAMS ? sourceIndi.FAMS?.toList() : void 0;
7120
- const childrenFamily = sourceIndi.FAMC?.toList();
7121
- const cloned = targetIndi.clone(false, avoidKeys);
8135
+ const asDate = dateNode;
8136
+ const hasComponents = !!(asDate.DAY || asDate.MONTH || asDate.YEAR);
8137
+ const raw = dateNode.exportValue?.() ?? dateNode.toValue();
8138
+ if (typeof raw !== "string" || !raw.trim()) {
8139
+ return;
8140
+ }
8141
+ const { calendar, rest } = parseGedcom7CalendarPrefix(raw.trim());
8142
+ const looksStructured = hasComponents || /^(ABT|CAL|EST|BEF|AFT|FROM|TO|BET|INT)(\s|$)/i.test(rest) || /\bAND\b/i.test(rest) || /^\d{1,2}\s+[A-Z]{3}\s+\d{1,4}/i.test(rest) || /^[A-Z]{3}\s+\d{1,4}/i.test(rest) || /^\d{1,4}$/.test(rest);
8143
+ if (looksStructured) {
8144
+ if (calendar && calendar !== "GREGORIAN") {
8145
+ const previous = dateNode.value;
8146
+ const next = withGedcom7Calendar(rest, calendar);
8147
+ if (previous !== next) {
8148
+ dateNode.value = next;
8149
+ restores.push(() => {
8150
+ dateNode.value = previous;
8151
+ });
8152
+ }
8153
+ }
8154
+ return;
8155
+ }
8156
+ const previousValue = dateNode.value;
8157
+ const previousPhrase = dateNode.get("PHRASE");
8158
+ const previousPhraseValue = previousPhrase?.toValue();
8159
+ dateNode.removeValue();
8160
+ if (previousPhrase) {
8161
+ previousPhrase.value = raw.trim();
8162
+ } else {
8163
+ const phrase = createCommon(
8164
+ dateNode.getGedcom(),
8165
+ void 0,
8166
+ dateNode.main
8167
+ );
8168
+ phrase.value = raw.trim();
8169
+ dateNode.set("PHRASE", phrase);
8170
+ }
8171
+ restores.push(() => {
8172
+ if (previousValue === void 0) {
8173
+ dateNode.removeValue();
8174
+ } else {
8175
+ dateNode.value = previousValue;
8176
+ }
8177
+ if (previousPhrase) {
8178
+ previousPhrase.value = previousPhraseValue;
8179
+ } else {
8180
+ dateNode.remove("PHRASE");
8181
+ }
8182
+ });
8183
+ };
8184
+ const walk = (node) => {
8185
+ if (!node) return;
8186
+ node.get("DATE")?.toList()?.forEach((item) => visitDate(item));
8187
+ node.get("SDATE")?.toList()?.forEach((item) => visitDate(item));
8188
+ };
8189
+ gedcom.indis()?.forEach((indi) => {
8190
+ if (!indi) return;
8191
+ walk(indi);
8192
+ [
8193
+ "BIRT",
8194
+ "DEAT",
8195
+ "CHR",
8196
+ "BAPM",
8197
+ "BURI",
8198
+ "CREM",
8199
+ "ADOP",
8200
+ "EVEN",
8201
+ "RESI",
8202
+ "OCCU",
8203
+ "MARR"
8204
+ ].forEach((tag) => {
8205
+ indi.get(tag)?.toList()?.forEach((event) => walk(event));
8206
+ });
8207
+ });
8208
+ gedcom.fams()?.forEach((fam) => {
8209
+ if (!fam) return;
8210
+ ["MARR", "DIV", "ENGA", "ANUL", "EVEN"].forEach((tag) => {
8211
+ fam.get(tag)?.toList()?.forEach((event) => walk(event));
8212
+ });
8213
+ });
8214
+ return () => {
8215
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
8216
+ restores[i]?.();
8217
+ }
8218
+ };
8219
+ };
8220
+
8221
+ // src/utils/uid.ts
8222
+ var createGedcomUid = () => {
8223
+ const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
8224
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
8225
+ return cryptoObj.randomUUID();
8226
+ }
8227
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
8228
+ const rand = Math.random() * 16 | 0;
8229
+ const value = char === "x" ? rand : rand & 3 | 8;
8230
+ return value.toString(16);
8231
+ });
8232
+ };
8233
+
8234
+ // src/utils/gedcom7-metadata.ts
8235
+ var GEDCOM_MONTHS2 = [
8236
+ "JAN",
8237
+ "FEB",
8238
+ "MAR",
8239
+ "APR",
8240
+ "MAY",
8241
+ "JUN",
8242
+ "JUL",
8243
+ "AUG",
8244
+ "SEP",
8245
+ "OCT",
8246
+ "NOV",
8247
+ "DEC"
8248
+ ];
8249
+ var formatGedcom7Timestamp = (date = /* @__PURE__ */ new Date()) => {
8250
+ const day = String(date.getUTCDate());
8251
+ const month = GEDCOM_MONTHS2[date.getUTCMonth()];
8252
+ const year = String(date.getUTCFullYear());
8253
+ const hh = String(date.getUTCHours()).padStart(2, "0");
8254
+ const mm = String(date.getUTCMinutes()).padStart(2, "0");
8255
+ const ss = String(date.getUTCSeconds()).padStart(2, "0");
8256
+ return {
8257
+ date: `${day} ${month} ${year}`,
8258
+ time: `${hh}:${mm}:${ss}Z`
8259
+ };
8260
+ };
8261
+ var ensureMetaDate = (gedcom, record, tag, when, restores) => {
8262
+ if (record.get(tag)) {
8263
+ return;
8264
+ }
8265
+ const { date, time } = formatGedcom7Timestamp(when);
8266
+ const meta = createCommon(gedcom, void 0, record);
8267
+ meta.type = tag;
8268
+ const dateNode = createCommonDate(gedcom, void 0, record, meta);
8269
+ dateNode.type = "DATE";
8270
+ dateNode.value = date;
8271
+ dateNode.set("TIME", time);
8272
+ meta.set("DATE", dateNode);
8273
+ record.set(tag, meta);
8274
+ restores.push(() => {
8275
+ record.remove(tag);
8276
+ });
8277
+ };
8278
+ var ensureUid = (record, restores) => {
8279
+ const existing = record.get("UID")?.toValue();
8280
+ if (typeof existing === "string" && existing.trim()) {
8281
+ return;
8282
+ }
8283
+ const uid = createGedcomUid();
8284
+ record.set("UID", uid);
8285
+ restores.push(() => {
8286
+ record.remove("UID");
8287
+ });
8288
+ };
8289
+ var applyGedcom7RecordMetadata = (gedcom, options) => {
8290
+ const restores = [];
8291
+ const now = options?.now ?? /* @__PURE__ */ new Date();
8292
+ const visit = (record) => {
8293
+ if (!record?.id) {
8294
+ return;
8295
+ }
8296
+ ensureUid(record, restores);
8297
+ ensureMetaDate(gedcom, record, "CREA", now, restores);
8298
+ ensureMetaDate(gedcom, record, "CHAN", now, restores);
8299
+ };
8300
+ gedcom.indis()?.forEach((indi) => visit(indi));
8301
+ gedcom.fams()?.forEach((fam) => visit(fam));
8302
+ gedcom.objes()?.forEach((obje) => visit(obje));
8303
+ gedcom.sours()?.forEach((sour) => visit(sour));
8304
+ gedcom.repos()?.forEach((repo) => visit(repo));
8305
+ gedcom.subms()?.forEach((subm) => visit(subm));
8306
+ gedcom.snotes()?.forEach((note) => visit(note));
8307
+ return () => {
8308
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
8309
+ restores[i]?.();
8310
+ }
8311
+ };
8312
+ };
8313
+
8314
+ // src/utils/gedcom7-schema.ts
8315
+ var TREEVIZ_EXTENSION_BASE_URI = "https://treeviz.com/gedcom";
8316
+ var TREEVIZ_EXTENSION_URIS = {
8317
+ _ORIGHEAD: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIGHEAD`,
8318
+ _TREE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREE`,
8319
+ _TID: `${TREEVIZ_EXTENSION_BASE_URI}#_TID`,
8320
+ _PRIM: `${TREEVIZ_EXTENSION_BASE_URI}#_PRIM`,
8321
+ _FREL: `${TREEVIZ_EXTENSION_BASE_URI}#_FREL`,
8322
+ _MREL: `${TREEVIZ_EXTENSION_BASE_URI}#_MREL`,
8323
+ _SREL: `${TREEVIZ_EXTENSION_BASE_URI}#_SREL`,
8324
+ _UID: `${TREEVIZ_EXTENSION_BASE_URI}#_UID`,
8325
+ _WLNK: `${TREEVIZ_EXTENSION_BASE_URI}#_WLNK`,
8326
+ _MTTAG: `${TREEVIZ_EXTENSION_BASE_URI}#_MTTAG`,
8327
+ _MTCAT: `${TREEVIZ_EXTENSION_BASE_URI}#_MTCAT`,
8328
+ _CLON: `${TREEVIZ_EXTENSION_BASE_URI}#_CLON`,
8329
+ _MSER: `${TREEVIZ_EXTENSION_BASE_URI}#_MSER`,
8330
+ _OID: `${TREEVIZ_EXTENSION_BASE_URI}#_OID`,
8331
+ _LKID: `${TREEVIZ_EXTENSION_BASE_URI}#_LKID`,
8332
+ _PHOTO_RIN: `${TREEVIZ_EXTENSION_BASE_URI}#_PHOTO_RIN`,
8333
+ _EXPORTED_FROM_SITE_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_EXPORTED_FROM_SITE_ID`,
8334
+ _FS_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_ID`,
8335
+ _FS_LINK: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_LINK`,
8336
+ _FS_MATCH: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_MATCH`,
8337
+ _FS_SOUR: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_SOUR`,
8338
+ _IS_FS: `${TREEVIZ_EXTENSION_BASE_URI}#_IS_FS`,
8339
+ _LABEL: `${TREEVIZ_EXTENSION_BASE_URI}#_LABEL`,
8340
+ _MILT: `${TREEVIZ_EXTENSION_BASE_URI}#_MILT`,
8341
+ _MILTID: `${TREEVIZ_EXTENSION_BASE_URI}#_MILTID`,
8342
+ _ORIG: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIG`,
8343
+ _ENV: `${TREEVIZ_EXTENSION_BASE_URI}#_ENV`,
8344
+ _TREEVIZ_SETTINGS: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_SETTINGS`,
8345
+ _TREEVIZ_VIEW: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_VIEW`,
8346
+ _TREEVIZ_DETECTIVE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE`,
8347
+ _TREEVIZ_DETECTIVE_STATE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE_STATE`
8348
+ };
8349
+ var parseTagDefinition = (payload) => {
8350
+ const trimmed = payload.trim();
8351
+ const space = trimmed.indexOf(" ");
8352
+ if (space <= 0) {
8353
+ return void 0;
8354
+ }
8355
+ const tag = trimmed.slice(0, space).trim();
8356
+ const uri = trimmed.slice(space + 1).trim();
8357
+ if (!tag || !uri) {
8358
+ return void 0;
8359
+ }
8360
+ return { tag, uri };
8361
+ };
8362
+ var getExtensionSchema = (gedcom) => {
8363
+ const map = /* @__PURE__ */ new Map();
8364
+ const schma = gedcom.get("HEAD")?.get("SCHMA");
8365
+ if (!schma) {
8366
+ return map;
8367
+ }
8368
+ schma.get("TAG")?.toList()?.forEach((tagNode) => {
8369
+ const payload = tagNode?.toValue();
8370
+ if (typeof payload !== "string") {
8371
+ return;
8372
+ }
8373
+ const parsed = parseTagDefinition(payload);
8374
+ if (parsed) {
8375
+ map.set(parsed.tag.toUpperCase(), parsed.uri);
8376
+ }
8377
+ });
8378
+ return map;
8379
+ };
8380
+ var ensureSchemaNode = (gedcom) => {
8381
+ const head = gedcom.get("HEAD");
8382
+ if (!head) {
8383
+ return void 0;
8384
+ }
8385
+ let schma = head.get("SCHMA");
8386
+ if (!schma) {
8387
+ schma = createCommon(gedcom, void 0, head);
8388
+ head.set("SCHMA", schma);
8389
+ }
8390
+ return schma;
8391
+ };
8392
+ var registerExtensionTag = (gedcom, tag, uri) => {
8393
+ const normalizedTag = tag.trim().toUpperCase();
8394
+ const normalizedUri = uri.trim();
8395
+ if (!normalizedTag.startsWith("_") || !normalizedUri) {
8396
+ return void 0;
8397
+ }
8398
+ const schma = ensureSchemaNode(gedcom);
8399
+ if (!schma) {
8400
+ return void 0;
8401
+ }
8402
+ const payload = `${normalizedTag} ${normalizedUri}`;
8403
+ const existingList = schma.get("TAG")?.toList();
8404
+ const existing = existingList?.values().find((node) => {
8405
+ const value = node?.toValue();
8406
+ return typeof value === "string" && value.trim().toUpperCase().startsWith(`${normalizedTag} `);
8407
+ });
8408
+ if (existing) {
8409
+ existing.value = payload;
8410
+ return existing;
8411
+ }
8412
+ const tagNode = createCommon(gedcom, void 0, schma);
8413
+ tagNode.value = payload;
8414
+ schma.assign("TAG", tagNode);
8415
+ return tagNode;
8416
+ };
8417
+ var ensureExtensionSchema = (gedcom, extraTags) => {
8418
+ const head = gedcom.get("HEAD");
8419
+ const hadSchma = !!head?.get("SCHMA");
8420
+ const previousPayloads = head?.get("SCHMA")?.get("TAG")?.toList()?.values().map((node) => ({
8421
+ node,
8422
+ value: node?.toValue()
8423
+ })) ?? [];
8424
+ const tags = /* @__PURE__ */ new Set([
8425
+ ...Object.keys(TREEVIZ_EXTENSION_URIS),
8426
+ ...(extraTags ?? []).map((tag) => tag.toUpperCase())
8427
+ ]);
8428
+ tags.forEach((tag) => {
8429
+ const uri = TREEVIZ_EXTENSION_URIS[tag] ?? `${TREEVIZ_EXTENSION_BASE_URI}#${tag}`;
8430
+ if (tag.startsWith("_")) {
8431
+ registerExtensionTag(gedcom, tag, uri);
8432
+ }
8433
+ });
8434
+ return () => {
8435
+ if (!head) {
8436
+ return;
8437
+ }
8438
+ if (!hadSchma) {
8439
+ head.remove("SCHMA");
8440
+ return;
8441
+ }
8442
+ const schma = head.get("SCHMA");
8443
+ const list = schma?.get("TAG")?.toList();
8444
+ const keep = new Set(
8445
+ previousPayloads.map(
8446
+ (item) => typeof item.value === "string" ? item.value : void 0
8447
+ ).filter((value) => !!value)
8448
+ );
8449
+ list?.values().forEach((node) => {
8450
+ const value = node?.toValue();
8451
+ if (typeof value === "string" && !keep.has(value) && node) {
8452
+ list.delete(node);
8453
+ }
8454
+ });
8455
+ previousPayloads.forEach(({ node, value }) => {
8456
+ if (node && value !== void 0) {
8457
+ node.value = value;
8458
+ }
8459
+ });
8460
+ };
8461
+ };
8462
+ var collectExtensionTagsFrom = (node, into = /* @__PURE__ */ new Set()) => {
8463
+ if (!node) {
8464
+ return into;
8465
+ }
8466
+ const keys = Object.keys(node).filter(
8467
+ (key) => key.startsWith("_") && !key.startsWith("_gedcom") && !key.startsWith("_value") && !key.startsWith("_id") && !key.startsWith("_main") && !key.startsWith("_parent") && !key.startsWith("_unique") && !key.startsWith("_type") && !key.startsWith("_refs") && !key.startsWith("_date")
8468
+ );
8469
+ keys.forEach((key) => {
8470
+ const tag = key.toUpperCase();
8471
+ if (/^_[A-Z0-9_]+$/.test(tag)) {
8472
+ into.add(tag);
8473
+ }
8474
+ });
8475
+ return into;
8476
+ };
8477
+
8478
+ // package.json
8479
+ var package_default = {
8480
+ name: "@treeviz/gedcom-parser",
8481
+ version: "2.1.0"};
8482
+
8483
+ // src/utils/get-product-details.ts
8484
+ var isDevelopment = () => {
8485
+ return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
8486
+ };
8487
+ var getVersion = () => package_default.version;
8488
+ var getName = () => package_default.name;
8489
+
8490
+ // src/utils/multimedia.ts
8491
+ var inferMediaForm = (file, fallback = "bin") => {
8492
+ const withoutQuery = file.split("?")[0] || file;
8493
+ const ext = getFileExtension(withoutQuery);
8494
+ if (ext) {
8495
+ return ext.toLowerCase() === "jpeg" ? "jpg" : ext.toLowerCase();
8496
+ }
8497
+ return fallback;
8498
+ };
8499
+ var mergeMediaLists = (...lists) => {
8500
+ const merged = {};
8501
+ for (const list of lists) {
8502
+ if (!list) continue;
8503
+ Object.assign(merged, list);
8504
+ }
8505
+ return merged;
8506
+ };
8507
+ var isObjePointer = (value) => typeof value === "string" && /^@O\d+@$/.test(value);
8508
+
8509
+ // src/classes/note.ts
8510
+ var CommonNote = class extends Common {
8511
+ constructor(gedcom, id, main, parent) {
8512
+ super(gedcom, id, main, parent);
8513
+ if (!id) {
8514
+ delete this.id;
8515
+ }
8516
+ }
8517
+ set value(value) {
8518
+ const [note, ...contents] = value?.split(/\r?\n/) ?? [];
8519
+ this._value = note;
8520
+ if (contents.length) {
8521
+ const newContents = new List();
8522
+ contents.forEach((c, i) => {
8523
+ const newContent = createCommon(
8524
+ this._gedcom,
8525
+ void 0,
8526
+ this.main
8527
+ );
8528
+ newContent.id = `@U${i}@`;
8529
+ newContent.value = c;
8530
+ newContents.append(newContent);
8531
+ });
8532
+ const firstItem = newContents.index(0);
8533
+ if (firstItem) {
8534
+ if (newContents.length === 1) {
8535
+ this.set("CONT", firstItem);
8536
+ } else {
8537
+ this.set("CONT", newContents);
8538
+ }
8539
+ }
8540
+ }
8541
+ }
8542
+ get value() {
8543
+ const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8544
+ if (contents) {
8545
+ return `${this._value}
8546
+ ${contents}`;
8547
+ }
8548
+ return this._value;
8549
+ }
8550
+ exportValue() {
8551
+ return this._value;
8552
+ }
8553
+ };
8554
+ var createCommonNote = (gedcom, id, main, parent) => {
8555
+ return createProxy(
8556
+ new CommonNote(gedcom, id, main, parent)
8557
+ );
8558
+ };
8559
+
8560
+ // src/classes/obje.ts
8561
+ var Obje = class extends Common {
8562
+ standardizeMedia(namespace, override = true, urlGetter, gedcomVersion) {
8563
+ if (!this._gedcom) {
8564
+ return this;
8565
+ }
8566
+ const rin = this?.get("RIN")?.toValue();
8567
+ const clone = this?.get("_CLON._OID")?.toValue();
8568
+ const mser = this?.get("_MSER._LKID")?.toValue();
8569
+ const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8570
+ const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8571
+ const formRaw = this?.get("FILE.FORM")?.toValue() ?? this?.get("FORM")?.toValue();
8572
+ const mediType = this?.get("FILE.FORM.TYPE")?.toValue() ?? this?.get("FORM.TYPE")?.toValue() ?? this?.get("MEDI")?.toValue();
8573
+ const file = this?.get("FILE")?.toValue();
8574
+ const imgId = rin || clone || mser;
8575
+ const url = file || namespace && urlGetter?.(namespace, imgId);
8576
+ if (!url) {
8577
+ return this;
8578
+ }
8579
+ const version = normalizeGedcomVersion(
8580
+ gedcomVersion ?? this._gedcom.getGedcomVersion?.()
8581
+ );
8582
+ const form = formRaw || inferMediaForm(String(url));
8583
+ const newObject = createObje(this._gedcom, this.id, this.main);
8584
+ if (!override) {
8585
+ Object.assign(newObject, this);
8586
+ }
8587
+ const fileNode = createCommon(this._gedcom, void 0, this.main);
8588
+ fileNode.value = url;
8589
+ if (version === "7.0") {
8590
+ const formNode = createCommon(this._gedcom, void 0, fileNode);
8591
+ formNode.value = form;
8592
+ if (mediType) {
8593
+ formNode.set("TYPE", String(mediType));
8594
+ }
8595
+ fileNode.set("FORM", formNode);
8596
+ if (title) {
8597
+ fileNode.set("TITL", String(title));
8598
+ }
8599
+ if (note) {
8600
+ fileNode.set("NOTE", String(note));
8601
+ }
8602
+ newObject.set("FILE", fileNode);
8603
+ } else {
8604
+ newObject.set("FILE", fileNode);
8605
+ newObject.set("FORM", form);
8606
+ if (mediType) {
8607
+ newObject.set("MEDI", String(mediType));
8608
+ }
8609
+ if (title) {
8610
+ newObject.set("TITL", String(title));
8611
+ if (!note) {
8612
+ newObject.set("NOTE", String(title));
8613
+ }
8614
+ }
8615
+ if (note) {
8616
+ newObject.set("NOTE", String(note));
8617
+ if (!title) {
8618
+ newObject.set("TITL", String(note));
8619
+ }
8620
+ }
8621
+ }
8622
+ const prim = this.get("_PRIM")?.toValue();
8623
+ if (prim === "Y" || prim === "N") {
8624
+ newObject.set("_PRIM", prim);
8625
+ }
8626
+ const photoRin = this.get("_PHOTO_RIN")?.toValue();
8627
+ if (rin) {
8628
+ newObject.set("RIN", String(rin));
8629
+ }
8630
+ if (photoRin) {
8631
+ newObject.set("_PHOTO_RIN", String(photoRin));
8632
+ }
8633
+ const clonNode = this.get("_CLON");
8634
+ if (clonNode) {
8635
+ newObject.set("_CLON", clonNode);
8636
+ }
8637
+ const mserNode = this.get("_MSER");
8638
+ if (mserNode) {
8639
+ newObject.set("_MSER", mserNode);
8640
+ }
8641
+ const oidNode = this.get("_OID");
8642
+ if (oidNode && !newObject.get("_OID")) {
8643
+ newObject.set("_OID", oidNode);
8644
+ }
8645
+ const lkidNode = this.get("_LKID");
8646
+ if (lkidNode && !newObject.get("_LKID")) {
8647
+ newObject.set("_LKID", lkidNode);
8648
+ }
8649
+ if (override) {
8650
+ this.remove("FILE");
8651
+ this.remove("FORM");
8652
+ this.remove("MEDI");
8653
+ this.remove("TITL");
8654
+ this.remove("NOTE");
8655
+ Object.assign(this, newObject);
8656
+ return this;
8657
+ }
8658
+ return newObject;
8659
+ }
8660
+ };
8661
+ var createObje = (gedcom, id, main, parent) => {
8662
+ return createProxy(
8663
+ new Obje(gedcom, id, main, parent)
8664
+ );
8665
+ };
8666
+
8667
+ // src/classes/objes.ts
8668
+ var Objects = class _Objects extends List {
8669
+ copy() {
8670
+ const newList = new _Objects();
8671
+ this.entries().forEach(([key, value]) => {
8672
+ newList.item(key, value);
8673
+ });
8674
+ return newList;
8675
+ }
8676
+ except(item) {
8677
+ return this.copy().delete(item);
8678
+ }
8679
+ };
8680
+
8681
+ // src/classes/gedcom.ts
8682
+ var isObjectPatch = (value) => {
8683
+ return !!value && typeof value === "object" && !Array.isArray(value);
8684
+ };
8685
+ var GedCom = class extends Common {
8686
+ constructor() {
8687
+ super();
8688
+ this.tagMembers = {};
8689
+ this.reflist = {};
8690
+ this.refcount = 0;
8691
+ delete this._gedcom;
8692
+ delete this.id;
8693
+ this.removeValue();
8694
+ }
8695
+ getMain(type, index) {
8696
+ const list = !type || type instanceof List ? type : this.getList(type);
8697
+ if (!list) {
8698
+ return void 0;
8699
+ }
8700
+ if (typeof index === "string") {
8701
+ return list.item(index);
8702
+ }
8703
+ const keys = list.keys();
8704
+ return list.item(keys[index]);
8705
+ }
8706
+ getList(type) {
8707
+ return this.get(type);
8708
+ }
8709
+ createNodeFromPatchValue(value, main) {
8710
+ if (value === void 0 || value === null) {
8711
+ return void 0;
8712
+ }
8713
+ const node = createCommon(this, void 0, main);
8714
+ if (Array.isArray(value)) {
8715
+ value.forEach((item) => {
8716
+ const childNode = this.createNodeFromPatchValue(item, node);
8717
+ if (childNode) {
8718
+ node.assign("DATA", childNode);
8719
+ }
8720
+ });
8721
+ return node;
8722
+ }
8723
+ if (isObjectPatch(value)) {
8724
+ this.applyObjectToCommon(node, value);
8725
+ return node;
8726
+ }
8727
+ node.value = String(value);
8728
+ return node;
8729
+ }
8730
+ applyObjectToCommon(target, patch) {
8731
+ Object.entries(patch).forEach(([rawTag, value]) => {
8732
+ if (rawTag === "value") {
8733
+ if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
8734
+ return;
8735
+ }
8736
+ target.value = String(value);
8737
+ return;
8738
+ }
8739
+ if (rawTag === "id") {
8740
+ if (typeof value === "string") {
8741
+ target.id = value;
8742
+ }
8743
+ return;
8744
+ }
8745
+ const tag = rawTag;
8746
+ if (value === void 0) {
8747
+ return;
8748
+ }
8749
+ if (value === null) {
8750
+ target.remove(tag);
8751
+ return;
8752
+ }
8753
+ if (Array.isArray(value)) {
8754
+ target.remove(tag);
8755
+ value.forEach((item) => {
8756
+ const listNode = this.createNodeFromPatchValue(
8757
+ item,
8758
+ target.main ?? this
8759
+ );
8760
+ if (listNode) {
8761
+ target.assign(tag, listNode);
8762
+ }
8763
+ });
8764
+ return;
8765
+ }
8766
+ if (isObjectPatch(value)) {
8767
+ const existing = target.get(tag);
8768
+ const objectNode = existing ?? createCommon(this, void 0, target.main ?? this);
8769
+ this.applyObjectToCommon(objectNode, value);
8770
+ target.set(tag, objectNode);
8771
+ return;
8772
+ }
8773
+ target.set(tag, String(value));
8774
+ });
8775
+ }
8776
+ applyObject(patch) {
8777
+ this.applyObjectToCommon(this, patch);
8778
+ return this;
8779
+ }
8780
+ indis() {
8781
+ return this.getList("@@INDI");
8782
+ }
8783
+ cloneIndis(target, source, avoidKeys = [], removeFromOriginalList = true) {
8784
+ const hasFAMS = avoidKeys.includes("FAMS");
8785
+ const targetIndi = typeof target === "string" ? this.indi(target) : target;
8786
+ const sourceIndi = typeof source === "string" ? this.indi(source) : source;
8787
+ if (!targetIndi || !sourceIndi) {
8788
+ return this;
8789
+ }
8790
+ const spousesFamily = hasFAMS ? sourceIndi.FAMS?.toList() : void 0;
8791
+ const childrenFamily = sourceIndi.FAMC?.toList();
8792
+ const cloned = targetIndi.clone(false, avoidKeys);
7122
8793
  const newCloned = sourceIndi.merge(cloned);
7123
8794
  newCloned.cloneOf = targetIndi.id;
7124
8795
  targetIndi.clonedBy = newCloned.id;
@@ -7202,6 +8873,189 @@ var GedCom = class extends Common {
7202
8873
  fams() {
7203
8874
  return this.getList("@@FAM");
7204
8875
  }
8876
+ nextIndiKey() {
8877
+ const ids = [];
8878
+ this.indis()?.forEach((_indi, id) => {
8879
+ ids.push(String(id));
8880
+ });
8881
+ return nextRecordId(ids, "I");
8882
+ }
8883
+ nextFamKey() {
8884
+ const ids = [];
8885
+ this.fams()?.forEach((_fam, id) => {
8886
+ ids.push(String(id));
8887
+ });
8888
+ return nextRecordId(ids, "F");
8889
+ }
8890
+ createIndividual() {
8891
+ const existing = this.indis();
8892
+ const indis = existing ?? new Individuals();
8893
+ if (!existing) {
8894
+ this.set("@@INDI", indis);
8895
+ }
8896
+ const id = this.nextIndiKey();
8897
+ const indi = createIndi(this, id);
8898
+ indi.type = "INDI";
8899
+ if (this.getGedcomVersion() === "7.0") {
8900
+ indi.set("UID", createGedcomUid());
8901
+ }
8902
+ indis.item(id, indi);
8903
+ return indi;
8904
+ }
8905
+ createFamily() {
8906
+ const existing = this.fams();
8907
+ const fams = existing ?? new Families();
8908
+ if (!existing) {
8909
+ this.set("@@FAM", fams);
8910
+ }
8911
+ const id = this.nextFamKey();
8912
+ const fam = createFam(this, id);
8913
+ fam.type = "FAM";
8914
+ if (this.getGedcomVersion() === "7.0") {
8915
+ fam.set("UID", createGedcomUid());
8916
+ }
8917
+ fams.item(id, fam);
8918
+ return fam;
8919
+ }
8920
+ nextObjeKey() {
8921
+ const ids = [];
8922
+ this.objes()?.forEach((_obje, id) => {
8923
+ ids.push(String(id));
8924
+ });
8925
+ return nextRecordId(ids, "O");
8926
+ }
8927
+ /**
8928
+ * Create a top-level multimedia record (`0 @On@ OBJE`) with FILE/FORM/TITL.
8929
+ * GEDCOM 7 uses nested `FILE → FORM [→ TYPE]`; 5.5.1 uses sibling FORM/TITL.
8930
+ */
8931
+ createMultimediaRecord(input) {
8932
+ const existing = this.objes();
8933
+ const objes = existing ?? new Objects();
8934
+ if (!existing) {
8935
+ this.set("@@OBJE", objes);
8936
+ }
8937
+ const id = this.nextObjeKey();
8938
+ const obje = createObje(this, id);
8939
+ obje.type = "OBJE";
8940
+ objes.item(id, obje);
8941
+ const version = normalizeGedcomVersion(
8942
+ input.gedcomVersion ?? this.getGedcomVersion()
8943
+ );
8944
+ const form = input.form || inferMediaForm(input.file);
8945
+ const fileNode = createCommon(this, void 0, obje);
8946
+ fileNode.value = input.file;
8947
+ if (version === "7.0") {
8948
+ const formNode = createCommon(
8949
+ this,
8950
+ void 0,
8951
+ fileNode
8952
+ );
8953
+ formNode.value = form;
8954
+ if (input.mediType) {
8955
+ formNode.set("TYPE", input.mediType);
8956
+ }
8957
+ fileNode.set("FORM", formNode);
8958
+ if (input.title) {
8959
+ fileNode.set("TITL", input.title);
8960
+ }
8961
+ obje.set("FILE", fileNode);
8962
+ } else {
8963
+ obje.set("FILE", fileNode);
8964
+ obje.set("FORM", form);
8965
+ if (input.mediType) {
8966
+ obje.set("MEDI", input.mediType);
8967
+ }
8968
+ if (input.title) {
8969
+ obje.set("TITL", input.title);
8970
+ }
8971
+ }
8972
+ if (input.primary) {
8973
+ obje.set("_PRIM", "Y");
8974
+ }
8975
+ if (normalizeGedcomVersion(
8976
+ input.gedcomVersion ?? this.getGedcomVersion()
8977
+ ) === "7.0") {
8978
+ obje.set("UID", createGedcomUid());
8979
+ }
8980
+ return obje;
8981
+ }
8982
+ nextSnoteKey() {
8983
+ const ids = [];
8984
+ this.snotes()?.forEach((_note, id) => {
8985
+ ids.push(String(id));
8986
+ });
8987
+ return nextRecordId(ids, "N");
8988
+ }
8989
+ snotes() {
8990
+ return this.getList("@@SNOTE");
8991
+ }
8992
+ snote(index) {
8993
+ return this.getMain(this.snotes(), index);
8994
+ }
8995
+ /**
8996
+ * Create a top-level shared note record (`0 @Nn@ SNOTE` for GEDCOM 7).
8997
+ */
8998
+ createSharedNote(text) {
8999
+ const existing = this.snotes();
9000
+ const notes = existing ?? new List();
9001
+ if (!existing) {
9002
+ this.set("@@SNOTE", notes);
9003
+ }
9004
+ const id = this.nextSnoteKey();
9005
+ const note = createCommonNote(this, id);
9006
+ note.type = "SNOTE";
9007
+ note.value = text;
9008
+ if (this.getGedcomVersion() === "7.0") {
9009
+ note.set("UID", createGedcomUid());
9010
+ }
9011
+ notes.item(id, note);
9012
+ return note;
9013
+ }
9014
+ registerExtensionTag(tag, uri) {
9015
+ return registerExtensionTag(this, tag, uri);
9016
+ }
9017
+ getExtensionSchema() {
9018
+ return getExtensionSchema(this);
9019
+ }
9020
+ ensureExtensionSchema(extraTags) {
9021
+ ensureExtensionSchema(this, extraTags);
9022
+ return getExtensionSchema(this);
9023
+ }
9024
+ /**
9025
+ * Collect multimedia descriptors via each individual's `multimedia()`.
9026
+ */
9027
+ async collectMultimedia(options) {
9028
+ const people = [];
9029
+ if (options?.indiIds?.length) {
9030
+ options.indiIds.forEach((id) => {
9031
+ const indi = this.indi(id);
9032
+ if (indi) {
9033
+ people.push(indi);
9034
+ }
9035
+ });
9036
+ } else {
9037
+ this.indis()?.forEach((indi) => {
9038
+ if (indi) {
9039
+ people.push(indi);
9040
+ }
9041
+ });
9042
+ }
9043
+ const lists = await Promise.all(
9044
+ people.map((indi) => indi.multimedia(options?.namespace))
9045
+ );
9046
+ return mergeMediaLists(...lists);
9047
+ }
9048
+ /**
9049
+ * Serialize as GEDCOM 7 and pack a FamilySearch GEDZIP (`.gdz`).
9050
+ * Media bytes must be supplied by the caller (cache/fetch outside the parser).
9051
+ */
9052
+ async toGedzip(options) {
9053
+ const text = this.toGedcom(void 0, 0, {
9054
+ ...options,
9055
+ gedcomVersion: "7.0"
9056
+ });
9057
+ return buildGedzip(text, options?.media ?? []);
9058
+ }
7205
9059
  objes() {
7206
9060
  return this.getList("@@OBJE");
7207
9061
  }
@@ -7286,18 +9140,70 @@ var GedCom = class extends Common {
7286
9140
  });
7287
9141
  return lists;
7288
9142
  }
7289
- getDownloadHeader() {
9143
+ getGedcomVersion() {
9144
+ const raw = this.get("HEAD")?.get("GEDC")?.get("VERS")?.toValue();
9145
+ return normalizeGedcomVersion(
9146
+ typeof raw === "string" ? raw : void 0
9147
+ );
9148
+ }
9149
+ /** Rewrite HEAD.GEDC (and CHAR/FORM) for the requested export version. */
9150
+ applyExportVersion(version) {
9151
+ const head = this.get("HEAD");
9152
+ if (!head) {
9153
+ return;
9154
+ }
9155
+ this.applyGedcomSpecToHead(head, normalizeGedcomVersion(version));
9156
+ }
9157
+ applyGedcomSpecToHead(head, version) {
9158
+ const previousGedc = head.get("GEDC");
9159
+ const previousForm = previousGedc?.get("FORM")?.toValue();
9160
+ const previousChar = head.get("CHAR")?.toValue();
9161
+ head.remove("GEDC");
9162
+ const gedc = head.set("GEDC", "");
9163
+ if (!gedc) {
9164
+ return;
9165
+ }
9166
+ gedc.set("VERS", version);
9167
+ if (version === "7.0") {
9168
+ head.remove("CHAR");
9169
+ return;
9170
+ }
9171
+ gedc.set(
9172
+ "FORM",
9173
+ typeof previousForm === "string" && previousForm ? previousForm : "LINEAGE-LINKED"
9174
+ );
9175
+ if (!head.get("CHAR")?.toValue()) {
9176
+ head.set(
9177
+ "CHAR",
9178
+ typeof previousChar === "string" && previousChar ? previousChar : "UTF-8"
9179
+ );
9180
+ }
9181
+ }
9182
+ getDownloadHeader(gedcomVersion) {
7290
9183
  const newHead = createCommon();
7291
9184
  Object.assign(newHead, this.get("HEAD") ?? {});
9185
+ this.applyGedcomSpecToHead(
9186
+ newHead,
9187
+ gedcomVersion ?? this.getGedcomVersion()
9188
+ );
9189
+ const existingSour = this.get("HEAD")?.get("SOUR");
9190
+ const existingTreeName = existingSour?.get("_TREE")?.toValue();
9191
+ const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
7292
9192
  const newSour = createCommon();
7293
- newSour.set("CORP", createCommon());
7294
- newSour.set("CORP.WWW", createCommon());
7295
- newSour.set("NAME", createCommon());
7296
- newSour.set("VERS", createCommon());
7297
- newSour.CORP.value = "TreeViz - The Family Tree Visualiser";
7298
- newSour.CORP.WWW.value = "treeviz.com";
7299
- newSour.NAME.value = "TreeViz - The Family Tree Visualiser";
7300
- newSour.VERS.value = getVersion();
9193
+ newSour.value = APP_BRANDING;
9194
+ newSour.set("NAME", APP_BRANDING);
9195
+ newSour.set("VERS", getVersion());
9196
+ if (typeof existingTreeName === "string" && existingTreeName.trim()) {
9197
+ newSour.set("_TREE", existingTreeName.trim());
9198
+ }
9199
+ if (typeof existingTreeUniqueId === "string" && existingTreeUniqueId.trim()) {
9200
+ newSour.set("_TID", existingTreeUniqueId.trim());
9201
+ }
9202
+ const corp = createCommon();
9203
+ corp.value = APP_BRANDING;
9204
+ corp.set("WWW", "treeviz.com");
9205
+ corp.set("EMAIL", "info@treeviz.com");
9206
+ newSour.set("CORP", corp);
7301
9207
  newHead.set("SOUR", newSour);
7302
9208
  return newHead;
7303
9209
  }
@@ -7326,20 +9232,45 @@ var GedCom = class extends Common {
7326
9232
  }
7327
9233
  toGedcom(tag, level, options) {
7328
9234
  if (options?.super) {
7329
- return super.toGedcom(tag, level, options);
9235
+ return appendGedcomTrailer(super.toGedcom(tag, level, options));
7330
9236
  }
9237
+ const requestedVersion = options?.gedcomVersion;
9238
+ const exportVersion = requestedVersion ? normalizeGedcomVersion(requestedVersion) : this.getGedcomVersion();
9239
+ const exportOptions = {
9240
+ ...options,
9241
+ obje: {
9242
+ standardize: options?.obje?.standardize ?? (requestedVersion !== void 0 || !options?.original),
9243
+ override: options?.obje?.override,
9244
+ namespace: options?.obje?.namespace
9245
+ }
9246
+ };
7331
9247
  const newGedcom = createGedCom();
7332
- if (!options?.original) {
9248
+ Object.assign(newGedcom, this);
9249
+ if (!exportOptions.original) {
7333
9250
  Object.assign(newGedcom, {
7334
- HEAD: this.getDownloadHeader()
9251
+ HEAD: this.getDownloadHeader(exportVersion)
7335
9252
  });
7336
9253
  }
7337
- Object.assign(newGedcom, this);
7338
- if (options?.indis?.length) {
7339
- const newContent = this.getIndiRelatedLists(options.indis);
9254
+ if (exportOptions.indis?.length) {
9255
+ const newContent = this.getIndiRelatedLists(exportOptions.indis);
7340
9256
  Object.assign(newGedcom, newContent);
7341
9257
  }
7342
- return newGedcom.toGedcom(tag, level, { ...options, super: true });
9258
+ const restoreEnums = exportVersion === "7.0" ? applyGedcom7Enumerations(newGedcom) : void 0;
9259
+ const restoreDates = exportVersion === "7.0" ? applyGedcom7DatePhrases(newGedcom) : void 0;
9260
+ const restoreMetadata = exportVersion === "7.0" ? applyGedcom7RecordMetadata(newGedcom) : void 0;
9261
+ const restoreSchema = exportVersion === "7.0" ? ensureExtensionSchema(newGedcom) : void 0;
9262
+ try {
9263
+ return newGedcom.toGedcom(tag, level, {
9264
+ ...exportOptions,
9265
+ gedcomVersion: exportVersion,
9266
+ super: true
9267
+ });
9268
+ } finally {
9269
+ restoreSchema?.();
9270
+ restoreMetadata?.();
9271
+ restoreDates?.();
9272
+ restoreEnums?.();
9273
+ }
7343
9274
  }
7344
9275
  hasTag(tag) {
7345
9276
  const tagName = typeof tag === "string" ? tag : tag?.get("NAME")?.toValue();
@@ -7555,144 +9486,6 @@ var createGedCom = () => {
7555
9486
  return new GedCom();
7556
9487
  };
7557
9488
 
7558
- // src/classes/note.ts
7559
- var CommonNote = class extends Common {
7560
- constructor(gedcom, id, main, parent) {
7561
- super(gedcom, id, main, parent);
7562
- delete this.id;
7563
- }
7564
- set value(value) {
7565
- const [note, ...contents] = value?.split(/\r?\n/) ?? [];
7566
- this._value = note;
7567
- if (contents.length) {
7568
- const newContents = new List();
7569
- contents.forEach((c, i) => {
7570
- const newContent = createCommon(
7571
- this._gedcom,
7572
- void 0,
7573
- this.main
7574
- );
7575
- newContent.id = `@U${i}@`;
7576
- newContent.value = c;
7577
- newContents.append(newContent);
7578
- });
7579
- const firstItem = newContents.index(0);
7580
- if (firstItem) {
7581
- if (newContents.length === 1) {
7582
- this.set("CONT", firstItem);
7583
- } else {
7584
- this.set("CONT", newContents);
7585
- }
7586
- }
7587
- }
7588
- }
7589
- get value() {
7590
- const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
7591
- if (contents) {
7592
- return `${this._value}
7593
- ${contents}`;
7594
- }
7595
- return this._value;
7596
- }
7597
- exportValue() {
7598
- return this._value;
7599
- }
7600
- };
7601
- var createCommonNote = (gedcom, id, main, parent) => {
7602
- return createProxy(
7603
- new CommonNote(gedcom, id, main, parent)
7604
- );
7605
- };
7606
-
7607
- // src/classes/obje.ts
7608
- var Obje = class extends Common {
7609
- standardizeMedia(namespace, override = true, urlGetter) {
7610
- if (!this._gedcom) {
7611
- return this;
7612
- }
7613
- const rin = this?.get("RIN")?.toValue();
7614
- const clone = this?.get("_CLON._OID")?.toValue();
7615
- const mser = this?.get("_MSER._LKID")?.toValue();
7616
- const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
7617
- const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
7618
- const form = this?.get("FILE.FORM")?.toValue();
7619
- const file = this?.get("FILE")?.toValue();
7620
- const imgId = rin || clone || mser;
7621
- const url = file || namespace && urlGetter?.(namespace, imgId);
7622
- if (!url) {
7623
- return this;
7624
- }
7625
- const newObject = createObje(this._gedcom, this.id, this.main);
7626
- if (!override) {
7627
- Object.assign(newObject, this);
7628
- }
7629
- if (form) {
7630
- if (typeof form === "string") {
7631
- const newForm = createCommon(
7632
- this._gedcom,
7633
- void 0,
7634
- this.main
7635
- );
7636
- newForm.value = form;
7637
- newObject.set("FORM", newForm);
7638
- } else {
7639
- newObject.set("FORM", form);
7640
- }
7641
- }
7642
- if (title) {
7643
- if (typeof title === "string") {
7644
- const newTitle = createCommon(
7645
- this._gedcom,
7646
- void 0,
7647
- this.main
7648
- );
7649
- newTitle.value = title;
7650
- newObject.set("TITL", newTitle);
7651
- if (!note) {
7652
- newObject.set("NOTE", newTitle);
7653
- }
7654
- } else {
7655
- newObject.set("TITL", title);
7656
- if (!note) {
7657
- newObject.set("NOTE", title);
7658
- }
7659
- }
7660
- }
7661
- if (note) {
7662
- if (typeof note === "string") {
7663
- const newNote = createCommon(
7664
- this._gedcom,
7665
- void 0,
7666
- this.main
7667
- );
7668
- newNote.value = note;
7669
- newObject.set("NOTE", newNote);
7670
- if (!title) {
7671
- newObject.set("TITL", newNote);
7672
- }
7673
- } else {
7674
- newObject.set("NOTE", note);
7675
- if (!title) {
7676
- newObject.set("TITL", note);
7677
- }
7678
- }
7679
- }
7680
- const newUrl = createCommon(this._gedcom, void 0, this.main);
7681
- newUrl.value = url;
7682
- newObject.set("FILE", newUrl);
7683
- if (override) {
7684
- Object.assign(this, newObject);
7685
- return this;
7686
- }
7687
- return newObject;
7688
- }
7689
- };
7690
- var createObje = (gedcom, id, main, parent) => {
7691
- return createProxy(
7692
- new Obje(gedcom, id, main, parent)
7693
- );
7694
- };
7695
-
7696
9489
  // src/classes/repo.ts
7697
9490
  var Repo = class extends Common {
7698
9491
  };
@@ -7741,6 +9534,8 @@ var create = (gedcom, type, id, nodes) => {
7741
9534
  prevNode = createIndi(gedcom, id);
7742
9535
  } else if (convertType === "FAM") {
7743
9536
  prevNode = createFam(gedcom, id);
9537
+ } else if (convertType === "SNOTE" || convertType === "NOTE") {
9538
+ prevNode = createCommonNote(gedcom, id);
7744
9539
  } else {
7745
9540
  prevNode = createCommon(gedcom, id);
7746
9541
  }
@@ -7756,7 +9551,7 @@ var create = (gedcom, type, id, nodes) => {
7756
9551
  prevNode = createObje(gedcom, void 0, mainNode, curNode);
7757
9552
  } else if (type === "DATE") {
7758
9553
  prevNode = createCommonDate(gedcom, void 0, mainNode, curNode);
7759
- } else if (type === "NOTE") {
9554
+ } else if (type === "NOTE" || type === "SNOTE") {
7760
9555
  prevNode = createCommonNote(gedcom, void 0, mainNode, curNode);
7761
9556
  } else if (type === "NAME" && mainNode instanceof Indi) {
7762
9557
  prevNode = createCommonName(gedcom, void 0, mainNode, curNode);
@@ -7773,20 +9568,6 @@ var getRawSize = (raw) => {
7773
9568
  return `${raw || ""}`?.length ?? 0;
7774
9569
  };
7775
9570
 
7776
- // src/classes/objes.ts
7777
- var Objects = class _Objects extends List {
7778
- copy() {
7779
- const newList = new _Objects();
7780
- this.entries().forEach(([key, value]) => {
7781
- newList.item(key, value);
7782
- });
7783
- return newList;
7784
- }
7785
- except(item) {
7786
- return this.copy().delete(item);
7787
- }
7788
- };
7789
-
7790
9571
  // src/classes/repos.ts
7791
9572
  var Repositories = class _Repositories extends List {
7792
9573
  copy() {
@@ -7864,9 +9645,7 @@ var GedcomTree = {
7864
9645
  }, {}) : {};
7865
9646
  const links = new Individuals();
7866
9647
  const tags = [];
7867
- let lines = `${content.replace(/\n0 TRLR(\r?\n)*$/, "")}
7868
- 0 TRLR
7869
- `.split(/\r?\n/).reduce((acc, line) => {
9648
+ let lines = content.split(/\r?\n/).reduce((acc, line) => {
7870
9649
  const lineMatch = line.match(LINE_REG);
7871
9650
  if (lineMatch) {
7872
9651
  const lineIndent = Number(lineMatch?.groups?.indent ?? 0);
@@ -7879,6 +9658,13 @@ var GedcomTree = {
7879
9658
  linesAcc._hasFile = false;
7880
9659
  return acc;
7881
9660
  }
9661
+ if (lineIndent === 0 && lineType === "TRLR") {
9662
+ if (linesAcc._inHead && filename && !linesAcc._hasFile) {
9663
+ acc.push(`1 FILE ${filename}`);
9664
+ }
9665
+ linesAcc._inHead = false;
9666
+ return acc;
9667
+ }
7882
9668
  if (lineIndent === 0 && linesAcc._inHead) {
7883
9669
  if (filename && !linesAcc._hasFile) {
7884
9670
  acc.push(`1 FILE ${filename}`);
@@ -7890,6 +9676,13 @@ var GedcomTree = {
7890
9676
  if (linesAcc._inHead && lineIndent === 1 && lineType === "FILE") {
7891
9677
  linesAcc._hasFile = true;
7892
9678
  }
9679
+ if (lineType.toUpperCase() === "CONC") {
9680
+ const last = acc[acc.length - 1];
9681
+ if (last !== void 0) {
9682
+ acc[acc.length - 1] = `${last}${lineValue}`;
9683
+ return acc;
9684
+ }
9685
+ }
7893
9686
  if (lineIndent > 0 && lineIndent > prevLineIndent && lineValue && isId(lineValue)) {
7894
9687
  const refLines = lineValue.split(/,\s*/).map((id) => line.replace(lineValue, id));
7895
9688
  if (refLines.length > 1) {
@@ -7933,8 +9726,9 @@ var GedcomTree = {
7933
9726
  ).replace(/0\s(@[^@]+@)(\sSOUR[^\n]*\n)([123456789]\s.+\n)*/, "").replace(/0 HEAD(.*)\n/, "0 HEAD$1\n1 _IS_PURGED true\n");
7934
9727
  lines = linesJoined.split("\n");
7935
9728
  }
9729
+ lines = lines.filter((line) => !isGedcomTrailerLine(line));
7936
9730
  lines.forEach((line, index) => {
7937
- if (line.length === 0) {
9731
+ if (line.length === 0 || isGedcomTrailerLine(line)) {
7938
9732
  return;
7939
9733
  }
7940
9734
  const match = reg.exec(line);
@@ -8071,5 +9865,20 @@ if (isDev) {
8071
9865
  globalThis.GedcomTree = GedcomTree;
8072
9866
  }
8073
9867
  var parser_default = GedcomTree;
9868
+ var createEmptyGedcom = (options) => {
9869
+ const { gedcom } = GedcomTree.parse(
9870
+ `0 HEAD
9871
+ 1 GEDC
9872
+ 2 VERS 5.5.1
9873
+ 2 FORM LINEAGE-LINKED
9874
+ 1 CHAR UTF-8
9875
+ 0 TRLR`,
9876
+ { filename: options?.filename }
9877
+ );
9878
+ if (!gedcom) {
9879
+ throw new Error("Failed to create empty GEDCOM");
9880
+ }
9881
+ return gedcom;
9882
+ };
8074
9883
 
8075
- 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 };
9884
+ export { ACCEPTED_DATE_FORMATS, ACCEPTED_DATE_FORMATS_REGEX, DEFAULT_GEDCOM_EXPORT_VERSION, GEDCOM7_CALENDARS, GEDCOM7_PEDI_VALUES, GEDCOM7_RESN_VALUES, GEDCOM7_ROLE_VALUES, GEDCOM7_SEX_VALUES, GEDCOM_EXPORT_VERSIONS, GEDCOM_TRAILER_TAG, GEDZIP_EXTENSION, GEDZIP_GEDCOM_ENTRY, GEDZIP_MIME, parser_default as GedcomTree, PEDIGREE_VALUES, PlaceType, TREEVIZ_EXTENSION_BASE_URI, TREEVIZ_EXTENSION_URIS, addFamilyFact, addIndividualFact, addNonEvent, appendGedcomTrailer, applyGedcom7DatePhrases, applyGedcom7Enumerations, applyGedcom7RecordMetadata, attachChildToFamily, attachSpouseToFamily, buildGedzip, buildGedzipBlob, bytesToDataUrl, cacheDiscoveredPath, canAttachAsSpouse, collectExtensionTagsFrom, commonDateFormatter, create, createEmptyGedcom, createGedcomUid, dateFormatter, downloadGedzipMedia, ensureExtensionSchema, extractGedzip, extractGedzipGedcom, extractSeparationYears, extractSplitPoints, familyHasChild, familyHasParent, findMatchingRangeForSplitRange, findReusableParentChildFamily, firstPointerId, formatGedcom7Timestamp, fromTuple, generateSplitRanges, getAllProp, getExtensionSchema, getFamilyWith, getFileExtension, getName, getPlaceParts, getPlaces, getRawSize, getVersion, hungarianOrdinalize, implemented, inRange, inferMediaForm, initializeCache, isDevelopment, isGedcom7, isGedcomTrailerLine, isGedcomTrailerTag, isGedzipContainer, isImageFormat, isIntersectedRange, isObjePointer, isRangeContained, isRemoteOrEmbeddedMediaUrl, makePointer, marriageDateFormatter, mergeMediaLists, nameFormatter, nextRecordId, normalizeGedcomVersion, notImplemented, noteDateFormatter, ordinalize, parseGedcom7CalendarPrefix, parseRangeBounds, pathCache, placeTranslator, pointerIds, profilePictureCache, registerExtensionTag, relativesCache, remountGedzipMediaAsDataUrls, resetRelativesCache, resolveLocalMediaUrl, resolveMediaContent, resolveObjeForm, rewriteGedcomFilePaths, setCauseOfDeath, setChildPedigree, setDeceased, setFamilyMarriage, setLocalMediaResolver, setMediaContentResolver, setNestedGroup, splitOverlappingRanges, splitRange, spouseTagFor, toGedcomPedigree, updateFactRecord, withGedcom7Calendar };