@treeviz/gedcom-parser 2.0.12 → 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 (67) hide show
  1. package/README.md +236 -989
  2. package/dist/classes/common.d.ts +4 -4
  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 +2 -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 +39 -2
  10. package/dist/classes/gedcom.d.ts.map +1 -1
  11. package/dist/classes/index.js +1364 -318
  12. package/dist/classes/indi.d.ts +19 -2
  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 +1368 -313
  19. package/dist/constants/constants.d.ts.map +1 -1
  20. package/dist/constants/index.js +312 -73
  21. package/dist/factories/index.js +310 -71
  22. package/dist/index.d.ts +10 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1547 -288
  25. package/dist/interfaces/common.d.ts +6 -4
  26. package/dist/interfaces/common.d.ts.map +1 -1
  27. package/dist/interfaces/fam.d.ts +3 -0
  28. package/dist/interfaces/fam.d.ts.map +1 -1
  29. package/dist/interfaces/gedcom.d.ts +21 -1
  30. package/dist/interfaces/gedcom.d.ts.map +1 -1
  31. package/dist/interfaces/indi.d.ts +11 -1
  32. package/dist/interfaces/indi.d.ts.map +1 -1
  33. package/dist/kinship-translator/index.js +310 -71
  34. package/dist/structures/gedcom.d.ts +1 -0
  35. package/dist/structures/gedcom.d.ts.map +1 -1
  36. package/dist/types/types.d.ts +22 -4
  37. package/dist/types/types.d.ts.map +1 -1
  38. package/dist/utils/common-creator.d.ts.map +1 -1
  39. package/dist/utils/fact-edit.d.ts +12 -0
  40. package/dist/utils/fact-edit.d.ts.map +1 -1
  41. package/dist/utils/family-edit.d.ts +2 -2
  42. package/dist/utils/family-edit.d.ts.map +1 -1
  43. package/dist/utils/gedcom-version.d.ts +6 -0
  44. package/dist/utils/gedcom-version.d.ts.map +1 -0
  45. package/dist/utils/gedcom7-dates.d.ts +20 -0
  46. package/dist/utils/gedcom7-dates.d.ts.map +1 -0
  47. package/dist/utils/gedcom7-enumerations.d.ts +26 -0
  48. package/dist/utils/gedcom7-enumerations.d.ts.map +1 -0
  49. package/dist/utils/gedcom7-metadata.d.ts +19 -0
  50. package/dist/utils/gedcom7-metadata.d.ts.map +1 -0
  51. package/dist/utils/gedcom7-schema.d.ts +27 -0
  52. package/dist/utils/gedcom7-schema.d.ts.map +1 -0
  53. package/dist/utils/gedzip.d.ts +81 -0
  54. package/dist/utils/gedzip.d.ts.map +1 -0
  55. package/dist/utils/index.d.ts +9 -0
  56. package/dist/utils/index.d.ts.map +1 -1
  57. package/dist/utils/index.js +1547 -288
  58. package/dist/utils/local-media.d.ts +30 -0
  59. package/dist/utils/local-media.d.ts.map +1 -0
  60. package/dist/utils/media-utils.d.ts +10 -6
  61. package/dist/utils/media-utils.d.ts.map +1 -1
  62. package/dist/utils/multimedia.d.ts +38 -0
  63. package/dist/utils/multimedia.d.ts.map +1 -0
  64. package/dist/utils/parser.d.ts.map +1 -1
  65. package/dist/utils/uid.d.ts +6 -0
  66. package/dist/utils/uid.d.ts.map +1 -0
  67. 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) => {
@@ -293,8 +294,8 @@ var cacheDiscoveredPath = (gedcom, path) => {
293
294
 
294
295
  // src/constants/constants.ts
295
296
  var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
296
- var REF_LINE_REG = /^0 (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/gm;
297
- var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/;
297
+ 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>.*)/;
298
299
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
299
300
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
300
301
  var ORIG_HEAD_TAG = "_ORIGHEAD";
@@ -692,11 +693,17 @@ var List = class _List {
692
693
  const gedcom = [];
693
694
  this.entries().forEach(([_, item]) => {
694
695
  const validTag = getValidTag(tag);
695
- const validKey = item.id;
696
- const validValue = validKey ? validTag : item.exportValue();
697
- gedcom.push(
698
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
699
- );
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
+ }
700
707
  gedcom.push(
701
708
  ...item.toGedcomLines(
702
709
  validTag,
@@ -954,12 +961,14 @@ var Common = class _Common {
954
961
  );
955
962
  }
956
963
  standardizeObject(tag, options) {
957
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
964
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
958
965
  const standardize = this.standardizeMedia;
959
966
  return standardize.call(
960
967
  this,
961
968
  options.obje.namespace,
962
- options.obje.override
969
+ options.obje.override,
970
+ void 0,
971
+ options.gedcomVersion
963
972
  );
964
973
  }
965
974
  }
@@ -986,7 +995,7 @@ var Common = class _Common {
986
995
  if (typeof prop?.toObject === "function") {
987
996
  if (prop instanceof _Common) {
988
997
  json[validKey] = prop.toObject(validKey, options);
989
- } else {
998
+ } else if (prop instanceof List) {
990
999
  json = {
991
1000
  ...json,
992
1001
  ...prop.toObject(validKey, options)
@@ -1044,8 +1053,12 @@ var Common = class _Common {
1044
1053
  this.standardizeObject(tag, options);
1045
1054
  const validKeys = getValidKeys(this);
1046
1055
  const gedcom = [];
1056
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
1047
1057
  validKeys.forEach((key) => {
1048
1058
  const validKey = key;
1059
+ if (isGedcom7Export && validKey === "CONC") {
1060
+ return;
1061
+ }
1049
1062
  const prop = this.get(validKey);
1050
1063
  if (typeof prop?.toGedcomLines === "function") {
1051
1064
  if (prop instanceof _Common) {
@@ -1056,7 +1069,7 @@ var Common = class _Common {
1056
1069
  gedcom.push(
1057
1070
  ...prop.toGedcomLines(validKey, level + 1, options)
1058
1071
  );
1059
- } else {
1072
+ } else if (prop instanceof List) {
1060
1073
  gedcom.push(
1061
1074
  ...prop.toGedcomLines(validKey, level, options)
1062
1075
  );
@@ -1999,6 +2012,24 @@ var setDeceased = (indi, deceased) => {
1999
2012
  }
2000
2013
  addIndividualFact(indi, { tag: "DEAT", value: "Y" });
2001
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
+ };
2002
2033
 
2003
2034
  // src/utils/family-edit.ts
2004
2035
  var PEDIGREE_VALUES = [
@@ -2246,6 +2277,81 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
2246
2277
  });
2247
2278
  return places;
2248
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
+ };
2249
2355
  var implemented = (type, ...args) => {
2250
2356
  };
2251
2357
  var notImplemented = (type, ...args) => {
@@ -2254,11 +2360,34 @@ var notImplemented = (type, ...args) => {
2254
2360
 
2255
2361
  // src/utils/media-utils.ts
2256
2362
  var getFileExtension = (filename) => {
2257
- const match = filename.match(/\.([^.]+)$/);
2258
- 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() : "";
2259
2377
  };
2260
2378
  var isImageFormat = (format2) => {
2261
- 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
+ }
2262
2391
  const imageFormats = [
2263
2392
  "jpg",
2264
2393
  "jpeg",
@@ -2270,7 +2399,11 @@ var isImageFormat = (format2) => {
2270
2399
  "tiff",
2271
2400
  "tif"
2272
2401
  ];
2273
- 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;
2274
2407
  };
2275
2408
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
2276
2409
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -2840,6 +2973,9 @@ var Fam = class extends Common {
2840
2973
  addFact(input) {
2841
2974
  return addFamilyFact(this, input);
2842
2975
  }
2976
+ addNonEvent(input) {
2977
+ return addNonEvent(this, input);
2978
+ }
2843
2979
  toList() {
2844
2980
  return new Families().concat(
2845
2981
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -3563,10 +3699,14 @@ var Indi = class extends Common {
3563
3699
  const clone = obje?.get("_CLON._OID")?.toValue();
3564
3700
  const mser = obje?.get("_MSER._LKID")?.toValue();
3565
3701
  let url = obje?.get("FILE")?.toValue();
3566
- const title = obje?.get("TITL")?.toValue() ?? "";
3567
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3568
- const imgId = clone || mser;
3569
- 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) {
3570
3710
  return;
3571
3711
  }
3572
3712
  if (!namespace && !url) {
@@ -3584,13 +3724,16 @@ var Indi = class extends Common {
3584
3724
  }
3585
3725
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
3586
3726
  }
3727
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
3728
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
3729
+ }
3587
3730
  if (url && imgId) {
3588
- const id = `${tree}-${this.id}-${imgId}`;
3731
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
3589
3732
  list[id] = {
3590
3733
  key,
3591
3734
  isPrimary,
3592
3735
  id,
3593
- tree,
3736
+ tree: tree || "gedzip",
3594
3737
  imgId,
3595
3738
  person: this.id,
3596
3739
  title,
@@ -3636,9 +3779,11 @@ var Indi = class extends Common {
3636
3779
  obje.standardizeMedia();
3637
3780
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3638
3781
  const url = obje?.get("FILE")?.toValue();
3639
- const title = obje?.get("NOTE")?.toValue() ?? "";
3640
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3641
- 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);
3642
3787
  if (url && imgId) {
3643
3788
  const id = `${tree}-${this.id}-${imgId}`;
3644
3789
  list[id] = {
@@ -3761,10 +3906,10 @@ var Indi = class extends Common {
3761
3906
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3762
3907
  const url = obje?.get("FILE")?.toValue();
3763
3908
  const title = obje?.get("TITL")?.toValue() ?? "";
3764
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3909
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3765
3910
  if (url) {
3766
3911
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
3767
- 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)}`);
3768
3913
  const id = `geni-${geniId}-${imgId}`;
3769
3914
  list[id] = {
3770
3915
  isPrimary,
@@ -3801,10 +3946,10 @@ var Indi = class extends Common {
3801
3946
  obje.standardizeMedia();
3802
3947
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3803
3948
  const url = obje?.get("FILE")?.toValue();
3804
- const title = obje?.get("TITL")?.toValue() ?? "";
3805
- 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";
3806
3951
  if (url) {
3807
- 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)}`;
3808
3953
  const id = `${treeId}-${this.id}-${imgId}`;
3809
3954
  list[id] = {
3810
3955
  isPrimary,
@@ -3844,7 +3989,20 @@ var Indi = class extends Common {
3844
3989
  cacheKey
3845
3990
  );
3846
3991
  if (cached !== void 0) {
3847
- 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
+ }
3848
4006
  }
3849
4007
  const mediaList = await this.multimedia(namespace);
3850
4008
  if (!mediaList) {
@@ -3856,39 +4014,40 @@ var Indi = class extends Common {
3856
4014
  return void 0;
3857
4015
  }
3858
4016
  const mediaArray = Object.values(mediaList);
3859
- const primaryMedia = mediaArray.find(
3860
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
3861
- );
3862
- if (primaryMedia) {
3863
- const result = {
3864
- file: primaryMedia.url,
3865
- form: primaryMedia.contentType,
3866
- title: primaryMedia.title,
3867
- 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
3868
4027
  };
3869
- profilePictureCache(this._gedcom, cacheKey, result);
3870
- return result;
3871
- }
3872
- if (onlyPrimary) {
3873
- profilePictureCache(
3874
- this._gedcom,
3875
- cacheKey,
3876
- void 0
3877
- );
3878
- return void 0;
3879
- }
3880
- const secondaryMedia = mediaArray.find(
3881
- (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
+ )
3882
4033
  );
3883
- if (secondaryMedia) {
3884
- const result = {
3885
- file: secondaryMedia.url,
3886
- form: secondaryMedia.contentType,
3887
- title: secondaryMedia.title,
3888
- isPrimary: false
3889
- };
3890
- profilePictureCache(this._gedcom, cacheKey, result);
3891
- 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
+ }
3892
4051
  }
3893
4052
  profilePictureCache(
3894
4053
  this._gedcom,
@@ -4174,9 +4333,108 @@ var Indi = class extends Common {
4174
4333
  addParent(parent, pedigree) {
4175
4334
  return parent.addChild(this, pedigree);
4176
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
+ }
4177
4432
  addFact(input) {
4178
4433
  return addIndividualFact(this, input);
4179
4434
  }
4435
+ addNonEvent(input) {
4436
+ return addNonEvent(this, input);
4437
+ }
4180
4438
  getParentType(id) {
4181
4439
  let indi;
4182
4440
  if (typeof id === "string" || typeof id === "number") {
@@ -4217,7 +4475,7 @@ var Indi = class extends Common {
4217
4475
  if (!famChild) {
4218
4476
  const childIndi = this._gedcom?.indi(child.id);
4219
4477
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4220
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4478
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4221
4479
  } else {
4222
4480
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
4223
4481
  }
@@ -4778,7 +5036,7 @@ var Indi = class extends Common {
4778
5036
  return;
4779
5037
  }
4780
5038
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4781
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
5039
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4782
5040
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4783
5041
  children.append(child);
4784
5042
  }
@@ -4886,7 +5144,7 @@ var Indi = class extends Common {
4886
5144
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
4887
5145
  if (!child) {
4888
5146
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4889
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
5147
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4890
5148
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4891
5149
  parents.merge(family.getParents());
4892
5150
  }
@@ -7287,7 +7545,11 @@ var STANDARD_QUALIFIERS = {
7287
7545
  BEF: "Bef.",
7288
7546
  BEFORE: "Bef.",
7289
7547
  AFT: "Aft.",
7290
- AFTER: "Aft."
7548
+ AFTER: "Aft.",
7549
+ CAL: "Abt.",
7550
+ // calculated ≈ about for display
7551
+ EST: "Abt."
7552
+ // estimated ≈ about for display
7291
7553
  };
7292
7554
  var EXPORT_QUALIFIERS = {
7293
7555
  "Abt.": "ABT",
@@ -7295,6 +7557,11 @@ var EXPORT_QUALIFIERS = {
7295
7557
  "Aft.": "AFT"
7296
7558
  };
7297
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
+ );
7298
7565
  var NOTE_MARKER = "####";
7299
7566
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
7300
7567
  const hasNote = baseFormat.includes("NOTE");
@@ -7319,29 +7586,45 @@ var CommonDate = class extends Common {
7319
7586
  super(gedcom, id, main, parent);
7320
7587
  delete this.id;
7321
7588
  }
7589
+ get calendar() {
7590
+ return this._calendar;
7591
+ }
7322
7592
  set value(value) {
7323
7593
  if (value) {
7324
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
7325
- const noteMatch = value.match(noteRegExp)?.groups;
7326
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;
7327
7604
  if (noteMatch?.note) {
7328
7605
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
7329
7606
  this.NOTE.value = noteMatch?.note;
7330
- validValue = value.replace(noteRegExp, "");
7607
+ validValue = validValue.replace(noteRegExp, "");
7331
7608
  } else {
7332
- const qualifierMatch = value.match(
7333
- /^(?<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
7334
7611
  );
7335
7612
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
7336
7613
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
7337
7614
  if (note) {
7338
7615
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
7339
7616
  this.NOTE.value = note;
7340
- validValue = value.slice(qualifierMatch[0].length);
7617
+ validValue = validValue.slice(qualifierMatch[0].length);
7341
7618
  }
7342
7619
  }
7343
7620
  const acceptedDate = this.isValidDateFormat(validValue);
7344
- 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) {
7345
7628
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
7346
7629
  this.DAY.value = format(acceptedDate, "dd");
7347
7630
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -7444,6 +7727,15 @@ var CommonDate = class extends Common {
7444
7727
  );
7445
7728
  }
7446
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
+ }
7447
7739
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
7448
7740
  if (!formatted) {
7449
7741
  return formatted;
@@ -7451,10 +7743,14 @@ var CommonDate = class extends Common {
7451
7743
  const note = this.NOTE?.value?.trim();
7452
7744
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
7453
7745
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
7454
- return standardized.replace(
7746
+ const withMonths = standardized.replace(
7455
7747
  GEDCOM_MONTHS,
7456
7748
  (month) => month.toUpperCase()
7457
7749
  );
7750
+ if (this._calendar && this._calendar !== "GREGORIAN") {
7751
+ return `${this._calendar} ${withMonths}`;
7752
+ }
7753
+ return withMonths;
7458
7754
  }
7459
7755
  toGedcomLines(tag, level = 0, options) {
7460
7756
  const internal = new RegExp(
@@ -7475,70 +7771,965 @@ var isCommonDate = (value) => {
7475
7771
  return !!value && value !== null && typeof value === "object" && "_date" in value;
7476
7772
  };
7477
7773
 
7478
- // package.json
7479
- var package_default = {
7480
- name: "@treeviz/gedcom-parser",
7481
- version: "2.0.12"};
7482
-
7483
- // src/utils/get-product-details.ts
7484
- var isDevelopment = () => {
7485
- return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
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;
7815
+ }
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
+ });
7843
+ }
7844
+ }
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;
7920
+ }
7921
+ if (path.toLowerCase().endsWith(".ged")) {
7922
+ continue;
7923
+ }
7924
+ mediaEntries.push({
7925
+ path,
7926
+ content: await entry.async("uint8array")
7927
+ });
7928
+ }
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) {
7963
+ return;
7964
+ }
7965
+ downloaded.push({
7966
+ ...item,
7967
+ content: resolved.content,
7968
+ contentType: resolved.contentType || item.contentType
7969
+ });
7970
+ } catch {
7971
+ } finally {
7972
+ done += 1;
7973
+ onProgress?.(done, total);
7974
+ }
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 });
7486
7982
  };
7487
- var getVersion = () => package_default.version;
7488
- var getName = () => package_default.name;
7489
7983
 
7490
- // src/classes/gedcom.ts
7491
- var isObjectPatch = (value) => {
7492
- return !!value && typeof value === "object" && !Array.isArray(value);
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
+ };
7493
8039
  };
7494
- var GedCom = class extends Common {
7495
- constructor() {
7496
- super();
7497
- this.tagMembers = {};
7498
- this.reflist = {};
7499
- this.refcount = 0;
7500
- delete this._gedcom;
7501
- delete this.id;
7502
- this.removeValue();
8040
+ var normalizeEnum = (node, allowed, mode, restores) => {
8041
+ const value = node?.toValue();
8042
+ if (!node || typeof value !== "string" || !value.trim()) {
8043
+ return;
7503
8044
  }
7504
- getMain(type, index) {
7505
- const list = !type || type instanceof List ? type : this.getList(type);
7506
- if (!list) {
7507
- return void 0;
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
+ });
7508
8053
  }
7509
- if (typeof index === "string") {
7510
- return list.item(index);
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
+ );
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]?.();
7511
8096
  }
7512
- const keys = list.keys();
7513
- return list.item(keys[index]);
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 };
7514
8112
  }
7515
- getList(type) {
7516
- return this.get(type);
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;
7517
8121
  }
7518
- createNodeFromPatchValue(value, main) {
7519
- if (value === void 0 || value === null) {
7520
- return void 0;
8122
+ const upper = calendar.toUpperCase();
8123
+ if (GEDCOM7_CALENDARS.includes(upper)) {
8124
+ const { rest } = parseGedcom7CalendarPrefix(value);
8125
+ return `${upper} ${rest}`.trim();
8126
+ }
8127
+ return value;
8128
+ };
8129
+ var applyGedcom7DatePhrases = (gedcom) => {
8130
+ const restores = [];
8131
+ const visitDate = (dateNode) => {
8132
+ if (!dateNode || typeof dateNode.toValue !== "function") {
8133
+ return;
7521
8134
  }
7522
- const node = createCommon(this, void 0, main);
7523
- if (Array.isArray(value)) {
7524
- value.forEach((item) => {
7525
- const childNode = this.createNodeFromPatchValue(item, node);
7526
- if (childNode) {
7527
- node.assign("DATA", childNode);
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
+ });
7528
8152
  }
7529
- });
7530
- return node;
8153
+ }
8154
+ return;
7531
8155
  }
7532
- if (isObjectPatch(value)) {
7533
- this.applyObjectToCommon(node, value);
7534
- return node;
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);
7535
8170
  }
7536
- node.value = String(value);
7537
- return node;
7538
- }
7539
- applyObjectToCommon(target, patch) {
7540
- Object.entries(patch).forEach(([rawTag, value]) => {
7541
- if (rawTag === "value") {
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") {
7542
8733
  if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
7543
8734
  return;
7544
8735
  }
@@ -7705,6 +8896,9 @@ var GedCom = class extends Common {
7705
8896
  const id = this.nextIndiKey();
7706
8897
  const indi = createIndi(this, id);
7707
8898
  indi.type = "INDI";
8899
+ if (this.getGedcomVersion() === "7.0") {
8900
+ indi.set("UID", createGedcomUid());
8901
+ }
7708
8902
  indis.item(id, indi);
7709
8903
  return indi;
7710
8904
  }
@@ -7717,9 +8911,151 @@ var GedCom = class extends Common {
7717
8911
  const id = this.nextFamKey();
7718
8912
  const fam = createFam(this, id);
7719
8913
  fam.type = "FAM";
8914
+ if (this.getGedcomVersion() === "7.0") {
8915
+ fam.set("UID", createGedcomUid());
8916
+ }
7720
8917
  fams.item(id, fam);
7721
8918
  return fam;
7722
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
+ }
7723
9059
  objes() {
7724
9060
  return this.getList("@@OBJE");
7725
9061
  }
@@ -7804,9 +9140,52 @@ var GedCom = class extends Common {
7804
9140
  });
7805
9141
  return lists;
7806
9142
  }
7807
- 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) {
7808
9183
  const newHead = createCommon();
7809
9184
  Object.assign(newHead, this.get("HEAD") ?? {});
9185
+ this.applyGedcomSpecToHead(
9186
+ newHead,
9187
+ gedcomVersion ?? this.getGedcomVersion()
9188
+ );
7810
9189
  const existingSour = this.get("HEAD")?.get("SOUR");
7811
9190
  const existingTreeName = existingSour?.get("_TREE")?.toValue();
7812
9191
  const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
@@ -7853,22 +9232,45 @@ var GedCom = class extends Common {
7853
9232
  }
7854
9233
  toGedcom(tag, level, options) {
7855
9234
  if (options?.super) {
7856
- return appendGedcomTrailer(
7857
- super.toGedcom(tag, level, options)
7858
- );
9235
+ return appendGedcomTrailer(super.toGedcom(tag, level, options));
7859
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
+ };
7860
9247
  const newGedcom = createGedCom();
7861
9248
  Object.assign(newGedcom, this);
7862
- if (!options?.original) {
9249
+ if (!exportOptions.original) {
7863
9250
  Object.assign(newGedcom, {
7864
- HEAD: this.getDownloadHeader()
9251
+ HEAD: this.getDownloadHeader(exportVersion)
7865
9252
  });
7866
9253
  }
7867
- if (options?.indis?.length) {
7868
- const newContent = this.getIndiRelatedLists(options.indis);
9254
+ if (exportOptions.indis?.length) {
9255
+ const newContent = this.getIndiRelatedLists(exportOptions.indis);
7869
9256
  Object.assign(newGedcom, newContent);
7870
9257
  }
7871
- 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
+ }
7872
9274
  }
7873
9275
  hasTag(tag) {
7874
9276
  const tagName = typeof tag === "string" ? tag : tag?.get("NAME")?.toValue();
@@ -8084,144 +9486,6 @@ var createGedCom = () => {
8084
9486
  return new GedCom();
8085
9487
  };
8086
9488
 
8087
- // src/classes/note.ts
8088
- var CommonNote = class extends Common {
8089
- constructor(gedcom, id, main, parent) {
8090
- super(gedcom, id, main, parent);
8091
- delete this.id;
8092
- }
8093
- set value(value) {
8094
- const [note, ...contents] = value?.split(/\r?\n/) ?? [];
8095
- this._value = note;
8096
- if (contents.length) {
8097
- const newContents = new List();
8098
- contents.forEach((c, i) => {
8099
- const newContent = createCommon(
8100
- this._gedcom,
8101
- void 0,
8102
- this.main
8103
- );
8104
- newContent.id = `@U${i}@`;
8105
- newContent.value = c;
8106
- newContents.append(newContent);
8107
- });
8108
- const firstItem = newContents.index(0);
8109
- if (firstItem) {
8110
- if (newContents.length === 1) {
8111
- this.set("CONT", firstItem);
8112
- } else {
8113
- this.set("CONT", newContents);
8114
- }
8115
- }
8116
- }
8117
- }
8118
- get value() {
8119
- const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8120
- if (contents) {
8121
- return `${this._value}
8122
- ${contents}`;
8123
- }
8124
- return this._value;
8125
- }
8126
- exportValue() {
8127
- return this._value;
8128
- }
8129
- };
8130
- var createCommonNote = (gedcom, id, main, parent) => {
8131
- return createProxy(
8132
- new CommonNote(gedcom, id, main, parent)
8133
- );
8134
- };
8135
-
8136
- // src/classes/obje.ts
8137
- var Obje = class extends Common {
8138
- standardizeMedia(namespace, override = true, urlGetter) {
8139
- if (!this._gedcom) {
8140
- return this;
8141
- }
8142
- const rin = this?.get("RIN")?.toValue();
8143
- const clone = this?.get("_CLON._OID")?.toValue();
8144
- const mser = this?.get("_MSER._LKID")?.toValue();
8145
- const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8146
- const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8147
- const form = this?.get("FILE.FORM")?.toValue();
8148
- const file = this?.get("FILE")?.toValue();
8149
- const imgId = rin || clone || mser;
8150
- const url = file || namespace && urlGetter?.(namespace, imgId);
8151
- if (!url) {
8152
- return this;
8153
- }
8154
- const newObject = createObje(this._gedcom, this.id, this.main);
8155
- if (!override) {
8156
- Object.assign(newObject, this);
8157
- }
8158
- if (form) {
8159
- if (typeof form === "string") {
8160
- const newForm = createCommon(
8161
- this._gedcom,
8162
- void 0,
8163
- this.main
8164
- );
8165
- newForm.value = form;
8166
- newObject.set("FORM", newForm);
8167
- } else {
8168
- newObject.set("FORM", form);
8169
- }
8170
- }
8171
- if (title) {
8172
- if (typeof title === "string") {
8173
- const newTitle = createCommon(
8174
- this._gedcom,
8175
- void 0,
8176
- this.main
8177
- );
8178
- newTitle.value = title;
8179
- newObject.set("TITL", newTitle);
8180
- if (!note) {
8181
- newObject.set("NOTE", newTitle);
8182
- }
8183
- } else {
8184
- newObject.set("TITL", title);
8185
- if (!note) {
8186
- newObject.set("NOTE", title);
8187
- }
8188
- }
8189
- }
8190
- if (note) {
8191
- if (typeof note === "string") {
8192
- const newNote = createCommon(
8193
- this._gedcom,
8194
- void 0,
8195
- this.main
8196
- );
8197
- newNote.value = note;
8198
- newObject.set("NOTE", newNote);
8199
- if (!title) {
8200
- newObject.set("TITL", newNote);
8201
- }
8202
- } else {
8203
- newObject.set("NOTE", note);
8204
- if (!title) {
8205
- newObject.set("TITL", note);
8206
- }
8207
- }
8208
- }
8209
- const newUrl = createCommon(this._gedcom, void 0, this.main);
8210
- newUrl.value = url;
8211
- newObject.set("FILE", newUrl);
8212
- if (override) {
8213
- Object.assign(this, newObject);
8214
- return this;
8215
- }
8216
- return newObject;
8217
- }
8218
- };
8219
- var createObje = (gedcom, id, main, parent) => {
8220
- return createProxy(
8221
- new Obje(gedcom, id, main, parent)
8222
- );
8223
- };
8224
-
8225
9489
  // src/classes/repo.ts
8226
9490
  var Repo = class extends Common {
8227
9491
  };
@@ -8270,6 +9534,8 @@ var create = (gedcom, type, id, nodes) => {
8270
9534
  prevNode = createIndi(gedcom, id);
8271
9535
  } else if (convertType === "FAM") {
8272
9536
  prevNode = createFam(gedcom, id);
9537
+ } else if (convertType === "SNOTE" || convertType === "NOTE") {
9538
+ prevNode = createCommonNote(gedcom, id);
8273
9539
  } else {
8274
9540
  prevNode = createCommon(gedcom, id);
8275
9541
  }
@@ -8285,7 +9551,7 @@ var create = (gedcom, type, id, nodes) => {
8285
9551
  prevNode = createObje(gedcom, void 0, mainNode, curNode);
8286
9552
  } else if (type === "DATE") {
8287
9553
  prevNode = createCommonDate(gedcom, void 0, mainNode, curNode);
8288
- } else if (type === "NOTE") {
9554
+ } else if (type === "NOTE" || type === "SNOTE") {
8289
9555
  prevNode = createCommonNote(gedcom, void 0, mainNode, curNode);
8290
9556
  } else if (type === "NAME" && mainNode instanceof Indi) {
8291
9557
  prevNode = createCommonName(gedcom, void 0, mainNode, curNode);
@@ -8302,20 +9568,6 @@ var getRawSize = (raw) => {
8302
9568
  return `${raw || ""}`?.length ?? 0;
8303
9569
  };
8304
9570
 
8305
- // src/classes/objes.ts
8306
- var Objects = class _Objects extends List {
8307
- copy() {
8308
- const newList = new _Objects();
8309
- this.entries().forEach(([key, value]) => {
8310
- newList.item(key, value);
8311
- });
8312
- return newList;
8313
- }
8314
- except(item) {
8315
- return this.copy().delete(item);
8316
- }
8317
- };
8318
-
8319
9571
  // src/classes/repos.ts
8320
9572
  var Repositories = class _Repositories extends List {
8321
9573
  copy() {
@@ -8424,6 +9676,13 @@ var GedcomTree = {
8424
9676
  if (linesAcc._inHead && lineIndent === 1 && lineType === "FILE") {
8425
9677
  linesAcc._hasFile = true;
8426
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
+ }
8427
9686
  if (lineIndent > 0 && lineIndent > prevLineIndent && lineValue && isId(lineValue)) {
8428
9687
  const refLines = lineValue.split(/,\s*/).map((id) => line.replace(lineValue, id));
8429
9688
  if (refLines.length > 1) {
@@ -8622,4 +9881,4 @@ var createEmptyGedcom = (options) => {
8622
9881
  return gedcom;
8623
9882
  };
8624
9883
 
8625
- export { ACCEPTED_DATE_FORMATS, ACCEPTED_DATE_FORMATS_REGEX, GEDCOM_TRAILER_TAG, parser_default as GedcomTree, PEDIGREE_VALUES, PlaceType, addFamilyFact, addIndividualFact, appendGedcomTrailer, attachChildToFamily, attachSpouseToFamily, cacheDiscoveredPath, canAttachAsSpouse, commonDateFormatter, create, createEmptyGedcom, dateFormatter, extractSeparationYears, extractSplitPoints, familyHasChild, familyHasParent, findMatchingRangeForSplitRange, findReusableParentChildFamily, firstPointerId, fromTuple, generateSplitRanges, getAllProp, getFamilyWith, getFileExtension, getName, getPlaceParts, getPlaces, getRawSize, getVersion, hungarianOrdinalize, implemented, inRange, initializeCache, isDevelopment, isGedcomTrailerLine, isGedcomTrailerTag, isImageFormat, isIntersectedRange, isRangeContained, makePointer, marriageDateFormatter, nameFormatter, nextRecordId, notImplemented, noteDateFormatter, ordinalize, parseRangeBounds, pathCache, placeTranslator, pointerIds, profilePictureCache, relativesCache, resetRelativesCache, setCauseOfDeath, setChildPedigree, setDeceased, setFamilyMarriage, setNestedGroup, splitOverlappingRanges, splitRange, spouseTagFor, toGedcomPedigree, updateFactRecord };
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 };