@treeviz/gedcom-parser 2.0.12 → 2.1.1

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
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { Command } from 'commander';
6
6
  import { debounce, set, get, uniqBy, unset, difference, intersectionBy, differenceBy } from 'lodash-es';
7
7
  import { format, isValid, parse } from 'date-fns';
8
+ import JSZip from 'jszip';
8
9
  import chalk from 'chalk';
9
10
  import * as readline from 'readline';
10
11
 
@@ -16,8 +17,8 @@ var __export = (target, all) => {
16
17
 
17
18
  // src/constants/constants.ts
18
19
  var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
19
- var REF_LINE_REG = /^0 (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/gm;
20
- var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/;
20
+ 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;
21
+ 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>.*)/;
21
22
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
22
23
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
23
24
  var ORIG_HEAD_TAG = "_ORIGHEAD";
@@ -415,11 +416,17 @@ var List = class _List {
415
416
  const gedcom = [];
416
417
  this.entries().forEach(([_, item]) => {
417
418
  const validTag = getValidTag(tag);
418
- const validKey = item.id;
419
- const validValue = validKey ? validTag : item.exportValue();
420
- gedcom.push(
421
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
422
- );
419
+ const xref = item.id;
420
+ const payload = item.exportValue?.();
421
+ if (xref) {
422
+ gedcom.push(
423
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
424
+ );
425
+ } else {
426
+ gedcom.push(
427
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
428
+ );
429
+ }
423
430
  gedcom.push(
424
431
  ...item.toGedcomLines(
425
432
  validTag,
@@ -677,12 +684,14 @@ var Common = class _Common {
677
684
  );
678
685
  }
679
686
  standardizeObject(tag, options) {
680
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
687
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
681
688
  const standardize = this.standardizeMedia;
682
689
  return standardize.call(
683
690
  this,
684
691
  options.obje.namespace,
685
- options.obje.override
692
+ options.obje.override,
693
+ void 0,
694
+ options.gedcomVersion
686
695
  );
687
696
  }
688
697
  }
@@ -709,7 +718,7 @@ var Common = class _Common {
709
718
  if (typeof prop?.toObject === "function") {
710
719
  if (prop instanceof _Common) {
711
720
  json[validKey] = prop.toObject(validKey, options);
712
- } else {
721
+ } else if (prop instanceof List) {
713
722
  json = {
714
723
  ...json,
715
724
  ...prop.toObject(validKey, options)
@@ -767,8 +776,12 @@ var Common = class _Common {
767
776
  this.standardizeObject(tag, options);
768
777
  const validKeys = getValidKeys(this);
769
778
  const gedcom = [];
779
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
770
780
  validKeys.forEach((key) => {
771
781
  const validKey = key;
782
+ if (isGedcom7Export && validKey === "CONC") {
783
+ return;
784
+ }
772
785
  const prop = this.get(validKey);
773
786
  if (typeof prop?.toGedcomLines === "function") {
774
787
  if (prop instanceof _Common) {
@@ -779,7 +792,7 @@ var Common = class _Common {
779
792
  gedcom.push(
780
793
  ...prop.toGedcomLines(validKey, level + 1, options)
781
794
  );
782
- } else {
795
+ } else if (prop instanceof List) {
783
796
  gedcom.push(
784
797
  ...prop.toGedcomLines(validKey, level, options)
785
798
  );
@@ -2054,16 +2067,58 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
2054
2067
  });
2055
2068
  return places;
2056
2069
  };
2070
+
2071
+ // src/utils/local-media.ts
2072
+ var localMediaResolver;
2073
+ var isRemoteOrEmbeddedMediaUrl = (path) => {
2074
+ if (!path) {
2075
+ return false;
2076
+ }
2077
+ return path.startsWith("data:") || path.startsWith("blob:") || /^https?:\/\//i.test(path);
2078
+ };
2079
+ var resolveLocalMediaUrl = async (path) => {
2080
+ if (!path) {
2081
+ return void 0;
2082
+ }
2083
+ if (isRemoteOrEmbeddedMediaUrl(path)) {
2084
+ return path;
2085
+ }
2086
+ const resolved = await localMediaResolver?.(path);
2087
+ return resolved;
2088
+ };
2057
2089
  var implemented = (type, ...args) => {
2058
2090
  };
2059
2091
 
2060
2092
  // src/utils/media-utils.ts
2061
2093
  var getFileExtension = (filename) => {
2062
- const match = filename.match(/\.([^.]+)$/);
2063
- return match ? match[1] : "";
2094
+ if (!filename) {
2095
+ return "";
2096
+ }
2097
+ if (filename.startsWith("data:")) {
2098
+ const mime = filename.slice(5).split(";")[0] || "";
2099
+ const subtype = (mime.split("/")[1] || "").toLowerCase();
2100
+ if (subtype === "jpeg") {
2101
+ return "jpg";
2102
+ }
2103
+ return subtype.split("+")[0] || "";
2104
+ }
2105
+ const withoutQuery = filename.split(/[?#]/)[0] || filename;
2106
+ const match = withoutQuery.match(/\.([^.]+)$/);
2107
+ return match ? match[1].toLowerCase() : "";
2064
2108
  };
2065
2109
  var isImageFormat = (format2) => {
2066
- if (!format2) return false;
2110
+ if (!format2) {
2111
+ return false;
2112
+ }
2113
+ let normalized = format2.toLowerCase().trim();
2114
+ if (normalized.startsWith("data:")) {
2115
+ normalized = getFileExtension(normalized);
2116
+ } else if (normalized.includes("/")) {
2117
+ normalized = normalized.split("/")[1]?.split("+")[0] || "";
2118
+ if (normalized === "jpeg") {
2119
+ normalized = "jpg";
2120
+ }
2121
+ }
2067
2122
  const imageFormats = [
2068
2123
  "jpg",
2069
2124
  "jpeg",
@@ -2075,7 +2130,11 @@ var isImageFormat = (format2) => {
2075
2130
  "tiff",
2076
2131
  "tif"
2077
2132
  ];
2078
- return imageFormats.includes(format2.toLowerCase());
2133
+ return imageFormats.includes(normalized);
2134
+ };
2135
+ var resolveObjeForm = (obje) => {
2136
+ const value = obje?.get?.("FILE.FORM")?.toValue?.() ?? obje?.get?.("FORM")?.toValue?.();
2137
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
2079
2138
  };
2080
2139
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
2081
2140
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -3265,10 +3324,14 @@ var Indi = class extends Common {
3265
3324
  const clone = obje?.get("_CLON._OID")?.toValue();
3266
3325
  const mser = obje?.get("_MSER._LKID")?.toValue();
3267
3326
  let url = obje?.get("FILE")?.toValue();
3268
- const title = obje?.get("TITL")?.toValue() ?? "";
3269
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3270
- const imgId = clone || mser;
3271
- if (!www || !tree || !this.id) {
3327
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3328
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3329
+ let imgId = clone || mser;
3330
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3331
+ if (!hasEmbeddedOrLocalFile && (!www || !tree || !this.id)) {
3332
+ return;
3333
+ }
3334
+ if (!this.id) {
3272
3335
  return;
3273
3336
  }
3274
3337
  if (!namespace && !url) {
@@ -3286,13 +3349,16 @@ var Indi = class extends Common {
3286
3349
  }
3287
3350
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
3288
3351
  }
3352
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
3353
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
3354
+ }
3289
3355
  if (url && imgId) {
3290
- const id = `${tree}-${this.id}-${imgId}`;
3356
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
3291
3357
  list[id] = {
3292
3358
  key,
3293
3359
  isPrimary,
3294
3360
  id,
3295
- tree,
3361
+ tree: tree || "gedzip",
3296
3362
  imgId,
3297
3363
  person: this.id,
3298
3364
  title,
@@ -3338,9 +3404,11 @@ var Indi = class extends Common {
3338
3404
  obje.standardizeMedia();
3339
3405
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3340
3406
  const url = obje?.get("FILE")?.toValue();
3341
- const title = obje?.get("NOTE")?.toValue() ?? "";
3342
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3343
- const imgId = obje?.get("_PHOTO_RIN")?.toValue();
3407
+ const title = obje?.get("FILE.NOTE")?.toValue() ?? obje?.get("NOTE")?.toValue() ?? "";
3408
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3409
+ const photoRin = obje?.get("_PHOTO_RIN")?.toValue();
3410
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3411
+ const imgId = photoRin || (hasEmbeddedOrLocalFile && url ? `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}` : void 0);
3344
3412
  if (url && imgId) {
3345
3413
  const id = `${tree}-${this.id}-${imgId}`;
3346
3414
  list[id] = {
@@ -3463,10 +3531,10 @@ var Indi = class extends Common {
3463
3531
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3464
3532
  const url = obje?.get("FILE")?.toValue();
3465
3533
  const title = obje?.get("TITL")?.toValue() ?? "";
3466
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3534
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3467
3535
  if (url) {
3468
3536
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
3469
- const imgId = urlMatch?.[1] || `img-${index}-${Date.now().toString(36)}`;
3537
+ const imgId = urlMatch?.[1] || (url.startsWith("data:") ? `data-${index}-${getFileExtension(url) || "bin"}` : `img-${index}-${Date.now().toString(36)}`);
3470
3538
  const id = `geni-${geniId}-${imgId}`;
3471
3539
  list[id] = {
3472
3540
  isPrimary,
@@ -3503,10 +3571,10 @@ var Indi = class extends Common {
3503
3571
  obje.standardizeMedia();
3504
3572
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3505
3573
  const url = obje?.get("FILE")?.toValue();
3506
- const title = obje?.get("TITL")?.toValue() ?? "";
3507
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3574
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3575
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3508
3576
  if (url) {
3509
- const imgId = `media-${index}-${url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3577
+ const imgId = `media-${index}-${url.startsWith("data:") ? getFileExtension(url) || "bin" : url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3510
3578
  const id = `${treeId}-${this.id}-${imgId}`;
3511
3579
  list[id] = {
3512
3580
  isPrimary,
@@ -3546,7 +3614,20 @@ var Indi = class extends Common {
3546
3614
  cacheKey
3547
3615
  );
3548
3616
  if (cached !== void 0) {
3549
- return cached;
3617
+ if (cached?.file && !isRemoteOrEmbeddedMediaUrl(cached.file)) {
3618
+ const resolvedCached = await resolveLocalMediaUrl(cached.file);
3619
+ if (resolvedCached && isRemoteOrEmbeddedMediaUrl(resolvedCached)) {
3620
+ const refreshed = { ...cached, file: resolvedCached };
3621
+ profilePictureCache(
3622
+ this._gedcom,
3623
+ cacheKey,
3624
+ refreshed
3625
+ );
3626
+ return refreshed;
3627
+ }
3628
+ } else {
3629
+ return cached;
3630
+ }
3550
3631
  }
3551
3632
  const mediaList = await this.multimedia(namespace);
3552
3633
  if (!mediaList) {
@@ -3558,39 +3639,40 @@ var Indi = class extends Common {
3558
3639
  return void 0;
3559
3640
  }
3560
3641
  const mediaArray = Object.values(mediaList);
3561
- const primaryMedia = mediaArray.find(
3562
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
3563
- );
3564
- if (primaryMedia) {
3565
- const result = {
3566
- file: primaryMedia.url,
3567
- form: primaryMedia.contentType,
3568
- title: primaryMedia.title,
3569
- isPrimary: true
3642
+ const toDisplayableProfile = async (media, isPrimary) => {
3643
+ const resolvedFile = await resolveLocalMediaUrl(media.url);
3644
+ if (!resolvedFile || !isRemoteOrEmbeddedMediaUrl(resolvedFile)) {
3645
+ return void 0;
3646
+ }
3647
+ return {
3648
+ file: resolvedFile,
3649
+ form: media.contentType,
3650
+ title: media.title,
3651
+ isPrimary
3570
3652
  };
3571
- profilePictureCache(this._gedcom, cacheKey, result);
3572
- return result;
3573
- }
3574
- if (onlyPrimary) {
3575
- profilePictureCache(
3576
- this._gedcom,
3577
- cacheKey,
3578
- void 0
3579
- );
3580
- return void 0;
3581
- }
3582
- const secondaryMedia = mediaArray.find(
3583
- (media) => isImageFormat(media.contentType || getFileExtension(media.url))
3653
+ };
3654
+ const candidates = mediaArray.filter(
3655
+ (media) => isImageFormat(
3656
+ media.contentType || media.url || getFileExtension(media.url)
3657
+ )
3584
3658
  );
3585
- if (secondaryMedia) {
3586
- const result = {
3587
- file: secondaryMedia.url,
3588
- form: secondaryMedia.contentType,
3589
- title: secondaryMedia.title,
3590
- isPrimary: false
3591
- };
3592
- profilePictureCache(this._gedcom, cacheKey, result);
3593
- return result;
3659
+ const ordered = [
3660
+ ...candidates.filter((media) => media.isPrimary),
3661
+ ...candidates.filter((media) => !media.isPrimary)
3662
+ ];
3663
+ for (const media of ordered) {
3664
+ if (onlyPrimary && !media.isPrimary) {
3665
+ break;
3666
+ }
3667
+ const result = await toDisplayableProfile(media, !!media.isPrimary);
3668
+ if (result) {
3669
+ profilePictureCache(
3670
+ this._gedcom,
3671
+ cacheKey,
3672
+ result
3673
+ );
3674
+ return result;
3675
+ }
3594
3676
  }
3595
3677
  profilePictureCache(
3596
3678
  this._gedcom,
@@ -3876,9 +3958,108 @@ var Indi = class extends Common {
3876
3958
  addParent(parent, pedigree) {
3877
3959
  return parent.addChild(this, pedigree);
3878
3960
  }
3961
+ /**
3962
+ * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
3963
+ */
3964
+ attachMultimedia(objeOrKey, options) {
3965
+ if (!this._gedcom || !this.id) {
3966
+ return void 0;
3967
+ }
3968
+ const obje = typeof objeOrKey === "string" ? this._gedcom.obje(objeOrKey) : objeOrKey;
3969
+ if (!obje?.id) {
3970
+ return void 0;
3971
+ }
3972
+ const already = this.get("OBJE")?.toList()?.values()?.some((item) => item?.toValue?.() === obje.id);
3973
+ if (!already) {
3974
+ const pointer = makePointer(
3975
+ this._gedcom,
3976
+ this,
3977
+ obje.id,
3978
+ "OBJE"
3979
+ );
3980
+ this.assign("OBJE", pointer);
3981
+ }
3982
+ if (options?.primary) {
3983
+ this.get("OBJE")?.toList()?.forEach((item) => {
3984
+ const target = item?.refType === "OBJE" ? this._gedcom?.obje(
3985
+ String(item.toValue())
3986
+ ) : item;
3987
+ if (!target) {
3988
+ return;
3989
+ }
3990
+ if (target.id === obje.id) {
3991
+ target.set("_PRIM", "Y");
3992
+ } else if (target.get("_PRIM")?.toValue() === "Y") {
3993
+ target.set("_PRIM", "N");
3994
+ }
3995
+ });
3996
+ obje.set("_PRIM", "Y");
3997
+ }
3998
+ return obje;
3999
+ }
4000
+ detachMultimedia(objeKey) {
4001
+ const list = this.get("OBJE")?.toList();
4002
+ if (!list) {
4003
+ return;
4004
+ }
4005
+ const toRemove = list.values().filter(
4006
+ (item) => item?.toValue?.() === objeKey || item?.id === objeKey
4007
+ );
4008
+ toRemove.forEach((item) => {
4009
+ if (item) {
4010
+ list.delete(item);
4011
+ }
4012
+ });
4013
+ if (list.length === 0) {
4014
+ this.remove("OBJE");
4015
+ }
4016
+ }
4017
+ /**
4018
+ * Create a multimedia record and attach it to this person.
4019
+ */
4020
+ attachMediaFromUrl(url, options) {
4021
+ if (!this._gedcom || !url) {
4022
+ return void 0;
4023
+ }
4024
+ const obje = this._gedcom.createMultimediaRecord({
4025
+ file: url,
4026
+ form: options?.form,
4027
+ title: options?.title,
4028
+ mediType: options?.mediType,
4029
+ primary: options?.primary,
4030
+ gedcomVersion: options?.gedcomVersion
4031
+ });
4032
+ return this.attachMultimedia(obje, { primary: options?.primary });
4033
+ }
4034
+ /**
4035
+ * Link a shared SNOTE record via `1 NOTE @Nn@` (GEDCOM 7).
4036
+ */
4037
+ attachSharedNote(noteOrKey) {
4038
+ if (!this._gedcom || !this.id) {
4039
+ return void 0;
4040
+ }
4041
+ const note = typeof noteOrKey === "string" ? this._gedcom.snote(noteOrKey) : noteOrKey;
4042
+ if (!note?.id) {
4043
+ return void 0;
4044
+ }
4045
+ const already = this.get("NOTE")?.toList()?.values()?.some((item) => item?.toValue?.() === note.id);
4046
+ if (!already) {
4047
+ const pointer = makePointer(
4048
+ this._gedcom,
4049
+ this,
4050
+ note.id,
4051
+ "SNOTE"
4052
+ );
4053
+ this.assign("NOTE", pointer);
4054
+ }
4055
+ return note;
4056
+ }
3879
4057
  addFact(input) {
3880
4058
  return addIndividualFact(this, input);
3881
4059
  }
4060
+ addNonEvent(input) {
4061
+ return addNonEvent(this, input);
4062
+ }
3882
4063
  getParentType(id) {
3883
4064
  let indi;
3884
4065
  if (typeof id === "string" || typeof id === "number") {
@@ -3919,7 +4100,7 @@ var Indi = class extends Common {
3919
4100
  if (!famChild) {
3920
4101
  const childIndi = this._gedcom?.indi(child.id);
3921
4102
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
3922
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4103
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
3923
4104
  } else {
3924
4105
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
3925
4106
  }
@@ -4480,7 +4661,7 @@ var Indi = class extends Common {
4480
4661
  return;
4481
4662
  }
4482
4663
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4483
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4664
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4484
4665
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4485
4666
  children.append(child);
4486
4667
  }
@@ -4588,7 +4769,7 @@ var Indi = class extends Common {
4588
4769
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
4589
4770
  if (!child) {
4590
4771
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4591
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4772
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4592
4773
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4593
4774
  parents.merge(family.getParents());
4594
4775
  }
@@ -6787,7 +6968,11 @@ var STANDARD_QUALIFIERS = {
6787
6968
  BEF: "Bef.",
6788
6969
  BEFORE: "Bef.",
6789
6970
  AFT: "Aft.",
6790
- AFTER: "Aft."
6971
+ AFTER: "Aft.",
6972
+ CAL: "Abt.",
6973
+ // calculated ≈ about for display
6974
+ EST: "Abt."
6975
+ // estimated ≈ about for display
6791
6976
  };
6792
6977
  var EXPORT_QUALIFIERS = {
6793
6978
  "Abt.": "ABT",
@@ -6795,6 +6980,11 @@ var EXPORT_QUALIFIERS = {
6795
6980
  "Aft.": "AFT"
6796
6981
  };
6797
6982
  var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
6983
+ var CALENDAR_PREFIX = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
6984
+ var isGedcomDateValueForm = (value) => (
6985
+ // Require space/end after the token so display forms like "Abt." are not opaque.
6986
+ /^(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)(\s|$)/i.test(value) || /\bAND\b/i.test(value)
6987
+ );
6798
6988
  var NOTE_MARKER = "####";
6799
6989
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
6800
6990
  const hasNote = baseFormat.includes("NOTE");
@@ -6819,29 +7009,45 @@ var CommonDate = class extends Common {
6819
7009
  super(gedcom, id, main, parent);
6820
7010
  delete this.id;
6821
7011
  }
7012
+ get calendar() {
7013
+ return this._calendar;
7014
+ }
6822
7015
  set value(value) {
6823
7016
  if (value) {
6824
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
6825
- const noteMatch = value.match(noteRegExp)?.groups;
6826
7017
  let validValue = value;
7018
+ const calendarMatch = validValue.match(CALENDAR_PREFIX);
7019
+ if (calendarMatch?.groups?.calendar) {
7020
+ this._calendar = calendarMatch.groups.calendar.toUpperCase();
7021
+ validValue = validValue.slice(calendarMatch[0].length);
7022
+ } else {
7023
+ this._calendar = void 0;
7024
+ }
7025
+ const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
7026
+ const noteMatch = validValue.match(noteRegExp)?.groups;
6827
7027
  if (noteMatch?.note) {
6828
7028
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6829
7029
  this.NOTE.value = noteMatch?.note;
6830
- validValue = value.replace(noteRegExp, "");
7030
+ validValue = validValue.replace(noteRegExp, "");
6831
7031
  } else {
6832
- const qualifierMatch = value.match(
6833
- /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
7032
+ const qualifierMatch = validValue.match(
7033
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER|CAL|EST)\b\s*/i
6834
7034
  );
6835
7035
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
6836
7036
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
6837
7037
  if (note) {
6838
7038
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6839
7039
  this.NOTE.value = note;
6840
- validValue = value.slice(qualifierMatch[0].length);
7040
+ validValue = validValue.slice(qualifierMatch[0].length);
6841
7041
  }
6842
7042
  }
6843
7043
  const acceptedDate = this.isValidDateFormat(validValue);
6844
- if (acceptedDate) {
7044
+ if (isGedcomDateValueForm(validValue.trim())) {
7045
+ delete this.DAY;
7046
+ delete this.MONTH;
7047
+ delete this.YEAR;
7048
+ this._date = void 0;
7049
+ this._value = value;
7050
+ } else if (acceptedDate) {
6845
7051
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
6846
7052
  this.DAY.value = format(acceptedDate, "dd");
6847
7053
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -6944,6 +7150,15 @@ var CommonDate = class extends Common {
6944
7150
  );
6945
7151
  }
6946
7152
  exportValue() {
7153
+ if (typeof this._value === "string") {
7154
+ const withoutCalendar = this._value.replace(CALENDAR_PREFIX, "");
7155
+ if (isGedcomDateValueForm(withoutCalendar.trim())) {
7156
+ return this._value.replace(
7157
+ /^((?:GREGORIAN|JULIAN|FRENCH_R|HEBREW)\s+)?(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)\b/i,
7158
+ (_m, cal, q) => `${cal || ""}${q.toUpperCase()}`
7159
+ );
7160
+ }
7161
+ }
6947
7162
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
6948
7163
  if (!formatted) {
6949
7164
  return formatted;
@@ -6951,10 +7166,14 @@ var CommonDate = class extends Common {
6951
7166
  const note = this.NOTE?.value?.trim();
6952
7167
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
6953
7168
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
6954
- return standardized.replace(
7169
+ const withMonths = standardized.replace(
6955
7170
  GEDCOM_MONTHS,
6956
7171
  (month) => month.toUpperCase()
6957
7172
  );
7173
+ if (this._calendar && this._calendar !== "GREGORIAN") {
7174
+ return `${this._calendar} ${withMonths}`;
7175
+ }
7176
+ return withMonths;
6958
7177
  }
6959
7178
  toGedcomLines(tag, level = 0, options) {
6960
7179
  const internal = new RegExp(
@@ -7046,6 +7265,24 @@ var addFamilyFact = (fam, input) => {
7046
7265
  fillFact(gedcom, fam, event, input);
7047
7266
  return event;
7048
7267
  };
7268
+ var addNonEvent = (owner, input) => {
7269
+ const gedcom = owner.getGedcom();
7270
+ const eventType = input.event?.trim().toUpperCase();
7271
+ if (!gedcom || !eventType) {
7272
+ return void 0;
7273
+ }
7274
+ const no = createCommon(gedcom, void 0, owner);
7275
+ no.type = "NO";
7276
+ no.value = eventType;
7277
+ if (input.date?.trim()) {
7278
+ setDateOn(gedcom, owner, no, input.date);
7279
+ }
7280
+ if (input.note?.trim()) {
7281
+ no.set("NOTE", input.note.trim());
7282
+ }
7283
+ owner.assign("NO", no);
7284
+ return no;
7285
+ };
7049
7286
 
7050
7287
  // src/classes/fam.ts
7051
7288
  var Fam = class extends Common {
@@ -7097,6 +7334,9 @@ var Fam = class extends Common {
7097
7334
  addFact(input) {
7098
7335
  return addFamilyFact(this, input);
7099
7336
  }
7337
+ addNonEvent(input) {
7338
+ return addNonEvent(this, input);
7339
+ }
7100
7340
  toList() {
7101
7341
  return new Families().concat(
7102
7342
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -7150,98 +7390,845 @@ var Families = class _Families extends List {
7150
7390
  }
7151
7391
  };
7152
7392
 
7153
- // package.json
7154
- var package_default = {
7155
- version: "2.0.12"};
7156
-
7157
- // src/utils/get-product-details.ts
7158
- var isDevelopment = () => {
7159
- return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
7393
+ // src/utils/gedcom-version.ts
7394
+ var normalizeGedcomVersion = (value) => {
7395
+ const trimmed = value?.trim() ?? "";
7396
+ if (trimmed.startsWith("7")) {
7397
+ return "7.0";
7398
+ }
7399
+ return "5.5.1";
7400
+ };
7401
+ var GEDZIP_GEDCOM_ENTRY = "gedcom.ged";
7402
+ var sanitizePathSegment = (name) => name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^\.+/, "") || "file";
7403
+ var extensionFor = (contentType) => {
7404
+ const raw = (contentType || "bin").replace(/^\./, "").toLowerCase();
7405
+ if (raw.includes("/")) {
7406
+ return raw.split("/").pop() || "bin";
7407
+ }
7408
+ return raw || "bin";
7409
+ };
7410
+ var rewriteGedcomFilePaths = (gedcomText, urlToPath) => {
7411
+ const entries = urlToPath instanceof Map ? urlToPath : new Map(Object.entries(urlToPath));
7412
+ let text = gedcomText;
7413
+ entries.forEach((path, url) => {
7414
+ const escaped = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7415
+ text = text.replace(
7416
+ new RegExp(`(^|\\r?\\n)(\\d+ FILE )${escaped}`, "g"),
7417
+ `$1$2${path}`
7418
+ );
7419
+ });
7420
+ return text;
7421
+ };
7422
+ var assignMediaPaths = (mediaFiles) => {
7423
+ const urlToPath = /* @__PURE__ */ new Map();
7424
+ const usedPaths = /* @__PURE__ */ new Set([GEDZIP_GEDCOM_ENTRY]);
7425
+ const files = [];
7426
+ for (const file of mediaFiles) {
7427
+ if (!file.content || !file.url) {
7428
+ continue;
7429
+ }
7430
+ const ext = extensionFor(file.contentType);
7431
+ const base = sanitizePathSegment(
7432
+ file.imgId || file.title || file.id || "media"
7433
+ );
7434
+ let path = `media/${base}.${ext}`;
7435
+ let suffix = 1;
7436
+ while (usedPaths.has(path)) {
7437
+ path = `media/${base}-${suffix++}.${ext}`;
7438
+ }
7439
+ usedPaths.add(path);
7440
+ urlToPath.set(file.url, path);
7441
+ const marker = "base64,";
7442
+ const idx = file.content.indexOf(marker);
7443
+ if (idx >= 0) {
7444
+ files.push({
7445
+ path,
7446
+ url: file.url,
7447
+ content: file.content.substring(idx + marker.length),
7448
+ base64: true
7449
+ });
7450
+ } else {
7451
+ files.push({
7452
+ path,
7453
+ url: file.url,
7454
+ content: file.content,
7455
+ base64: false
7456
+ });
7457
+ }
7458
+ }
7459
+ return { urlToPath, files };
7160
7460
  };
7161
- var getVersion = () => package_default.version;
7162
-
7163
- // src/classes/gedcom.ts
7164
- var isObjectPatch = (value) => {
7165
- return !!value && typeof value === "object" && !Array.isArray(value);
7461
+ var buildGedzip = async (gedcomText, mediaFiles = []) => {
7462
+ const zip = new JSZip();
7463
+ const { urlToPath, files } = assignMediaPaths(mediaFiles);
7464
+ for (const file of files) {
7465
+ zip.file(file.path, file.content, { base64: file.base64 });
7466
+ }
7467
+ zip.file(GEDZIP_GEDCOM_ENTRY, rewriteGedcomFilePaths(gedcomText, urlToPath));
7468
+ return zip.generateAsync({ type: "uint8array" });
7166
7469
  };
7167
- var GedCom = class extends Common {
7168
- constructor() {
7169
- super();
7170
- this.tagMembers = {};
7171
- this.reflist = {};
7172
- this.refcount = 0;
7173
- delete this._gedcom;
7174
- delete this.id;
7175
- this.removeValue();
7470
+
7471
+ // src/utils/gedcom7-enumerations.ts
7472
+ var GEDCOM7_SEX_VALUES = ["M", "F", "U", "X", "N"];
7473
+ var GEDCOM7_PEDI_VALUES = [
7474
+ "adopted",
7475
+ "birth",
7476
+ "foster",
7477
+ "sealing"
7478
+ ];
7479
+ var GEDCOM7_ROLE_VALUES = [
7480
+ "CHIL",
7481
+ "CLERGY",
7482
+ "FATH",
7483
+ "FRIEND",
7484
+ "GODP",
7485
+ "HUSB",
7486
+ "MOTH",
7487
+ "MULTIPLE",
7488
+ "NGHBR",
7489
+ "OFFICIATOR",
7490
+ "OTHER",
7491
+ "PARENT",
7492
+ "SPOU",
7493
+ "WIFE",
7494
+ "WITN"
7495
+ ];
7496
+ var GEDCOM7_RESN_VALUES = [
7497
+ "CONFIDENTIAL",
7498
+ "LOCKED",
7499
+ "PRIVACY"
7500
+ ];
7501
+ var isAllowed = (value, allowed) => allowed.includes(value);
7502
+ var moveToPhrase = (node, phrase) => {
7503
+ const previousValue = node.value;
7504
+ const previousPhrase = node.get("PHRASE");
7505
+ const previousPhraseValue = previousPhrase?.toValue();
7506
+ node.removeValue();
7507
+ if (previousPhrase) {
7508
+ previousPhrase.value = phrase;
7509
+ } else {
7510
+ const phraseNode = createCommon(node.getGedcom(), void 0, node.main);
7511
+ phraseNode.value = phrase;
7512
+ node.set("PHRASE", phraseNode);
7176
7513
  }
7177
- getMain(type, index) {
7178
- const list = !type || type instanceof List ? type : this.getList(type);
7179
- if (!list) {
7180
- return void 0;
7514
+ return () => {
7515
+ if (previousValue === void 0) {
7516
+ node.removeValue();
7517
+ } else {
7518
+ node.value = previousValue;
7181
7519
  }
7182
- if (typeof index === "string") {
7183
- return list.item(index);
7520
+ if (previousPhrase) {
7521
+ previousPhrase.value = previousPhraseValue;
7522
+ } else {
7523
+ node.remove("PHRASE");
7184
7524
  }
7185
- const keys = list.keys();
7186
- return list.item(keys[index]);
7187
- }
7188
- getList(type) {
7189
- return this.get(type);
7525
+ };
7526
+ };
7527
+ var normalizeEnum = (node, allowed, mode, restores) => {
7528
+ const value = node?.toValue();
7529
+ if (!node || typeof value !== "string" || !value.trim()) {
7530
+ return;
7190
7531
  }
7191
- createNodeFromPatchValue(value, main) {
7192
- if (value === void 0 || value === null) {
7193
- return void 0;
7194
- }
7195
- const node = createCommon(this, void 0, main);
7196
- if (Array.isArray(value)) {
7197
- value.forEach((item) => {
7198
- const childNode = this.createNodeFromPatchValue(item, node);
7199
- if (childNode) {
7200
- node.assign("DATA", childNode);
7201
- }
7532
+ const normalized = mode === "upper" ? value.trim().toUpperCase() : value.trim().toLowerCase();
7533
+ if (isAllowed(normalized, allowed)) {
7534
+ if (value !== normalized) {
7535
+ const previous = node.value;
7536
+ node.value = normalized;
7537
+ restores.push(() => {
7538
+ node.value = previous;
7202
7539
  });
7203
- return node;
7204
7540
  }
7205
- if (isObjectPatch(value)) {
7206
- this.applyObjectToCommon(node, value);
7207
- return node;
7541
+ return;
7542
+ }
7543
+ restores.push(moveToPhrase(node, value.trim()));
7544
+ };
7545
+ var applyGedcom7Enumerations = (gedcom) => {
7546
+ const restores = [];
7547
+ gedcom.indis()?.forEach((indi) => {
7548
+ if (!indi) return;
7549
+ normalizeEnum(indi.get("SEX"), GEDCOM7_SEX_VALUES, "upper", restores);
7550
+ normalizeEnum(indi.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
7551
+ indi.get("FAMC")?.toList()?.forEach((famc) => {
7552
+ normalizeEnum(
7553
+ famc?.get("PEDI") ?? famc?.get("PEDT"),
7554
+ GEDCOM7_PEDI_VALUES,
7555
+ "lower",
7556
+ restores
7557
+ );
7558
+ });
7559
+ indi.get("ASSO")?.toList()?.forEach((asso) => {
7560
+ normalizeEnum(
7561
+ asso?.get("ROLE"),
7562
+ GEDCOM7_ROLE_VALUES,
7563
+ "upper",
7564
+ restores
7565
+ );
7566
+ });
7567
+ });
7568
+ gedcom.fams()?.forEach((fam) => {
7569
+ if (!fam) return;
7570
+ normalizeEnum(fam.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
7571
+ fam.get("ASSO")?.toList()?.forEach((asso) => {
7572
+ normalizeEnum(
7573
+ asso?.get("ROLE"),
7574
+ GEDCOM7_ROLE_VALUES,
7575
+ "upper",
7576
+ restores
7577
+ );
7578
+ });
7579
+ });
7580
+ return () => {
7581
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7582
+ restores[i]?.();
7208
7583
  }
7209
- node.value = String(value);
7210
- return node;
7584
+ };
7585
+ };
7586
+
7587
+ // src/utils/gedcom7-dates.ts
7588
+ var GEDCOM7_CALENDARS = [
7589
+ "GREGORIAN",
7590
+ "JULIAN",
7591
+ "FRENCH_R",
7592
+ "HEBREW"
7593
+ ];
7594
+ var CALENDAR_PREFIX2 = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
7595
+ var parseGedcom7CalendarPrefix = (value) => {
7596
+ const match = value.match(CALENDAR_PREFIX2);
7597
+ if (!match?.groups?.calendar) {
7598
+ return { calendar: void 0, rest: value };
7211
7599
  }
7212
- applyObjectToCommon(target, patch) {
7213
- Object.entries(patch).forEach(([rawTag, value]) => {
7214
- if (rawTag === "value") {
7215
- if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
7216
- return;
7217
- }
7218
- target.value = String(value);
7219
- return;
7220
- }
7221
- if (rawTag === "id") {
7222
- if (typeof value === "string") {
7223
- target.id = value;
7600
+ return {
7601
+ calendar: match.groups.calendar.toUpperCase(),
7602
+ rest: value.slice(match[0].length)
7603
+ };
7604
+ };
7605
+ var withGedcom7Calendar = (value, calendar) => {
7606
+ if (!calendar || calendar.toUpperCase() === "GREGORIAN") {
7607
+ return value;
7608
+ }
7609
+ const upper = calendar.toUpperCase();
7610
+ if (GEDCOM7_CALENDARS.includes(upper)) {
7611
+ const { rest } = parseGedcom7CalendarPrefix(value);
7612
+ return `${upper} ${rest}`.trim();
7613
+ }
7614
+ return value;
7615
+ };
7616
+ var applyGedcom7DatePhrases = (gedcom) => {
7617
+ const restores = [];
7618
+ const visitDate = (dateNode) => {
7619
+ if (!dateNode || typeof dateNode.toValue !== "function") {
7620
+ return;
7621
+ }
7622
+ const asDate = dateNode;
7623
+ const hasComponents = !!(asDate.DAY || asDate.MONTH || asDate.YEAR);
7624
+ const raw = dateNode.exportValue?.() ?? dateNode.toValue();
7625
+ if (typeof raw !== "string" || !raw.trim()) {
7626
+ return;
7627
+ }
7628
+ const { calendar, rest } = parseGedcom7CalendarPrefix(raw.trim());
7629
+ 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);
7630
+ if (looksStructured) {
7631
+ if (calendar && calendar !== "GREGORIAN") {
7632
+ const previous = dateNode.value;
7633
+ const next = withGedcom7Calendar(rest, calendar);
7634
+ if (previous !== next) {
7635
+ dateNode.value = next;
7636
+ restores.push(() => {
7637
+ dateNode.value = previous;
7638
+ });
7224
7639
  }
7225
- return;
7226
7640
  }
7227
- const tag = rawTag;
7228
- if (value === void 0) {
7229
- return;
7641
+ return;
7642
+ }
7643
+ const previousValue = dateNode.value;
7644
+ const previousPhrase = dateNode.get("PHRASE");
7645
+ const previousPhraseValue = previousPhrase?.toValue();
7646
+ dateNode.removeValue();
7647
+ if (previousPhrase) {
7648
+ previousPhrase.value = raw.trim();
7649
+ } else {
7650
+ const phrase = createCommon(
7651
+ dateNode.getGedcom(),
7652
+ void 0,
7653
+ dateNode.main
7654
+ );
7655
+ phrase.value = raw.trim();
7656
+ dateNode.set("PHRASE", phrase);
7657
+ }
7658
+ restores.push(() => {
7659
+ if (previousValue === void 0) {
7660
+ dateNode.removeValue();
7661
+ } else {
7662
+ dateNode.value = previousValue;
7230
7663
  }
7231
- if (value === null) {
7232
- target.remove(tag);
7233
- return;
7664
+ if (previousPhrase) {
7665
+ previousPhrase.value = previousPhraseValue;
7666
+ } else {
7667
+ dateNode.remove("PHRASE");
7234
7668
  }
7235
- if (Array.isArray(value)) {
7236
- target.remove(tag);
7237
- value.forEach((item) => {
7238
- const listNode = this.createNodeFromPatchValue(
7239
- item,
7240
- target.main ?? this
7241
- );
7242
- if (listNode) {
7243
- target.assign(tag, listNode);
7244
- }
7669
+ });
7670
+ };
7671
+ const walk = (node) => {
7672
+ if (!node) return;
7673
+ node.get("DATE")?.toList()?.forEach((item) => visitDate(item));
7674
+ node.get("SDATE")?.toList()?.forEach((item) => visitDate(item));
7675
+ };
7676
+ gedcom.indis()?.forEach((indi) => {
7677
+ if (!indi) return;
7678
+ walk(indi);
7679
+ [
7680
+ "BIRT",
7681
+ "DEAT",
7682
+ "CHR",
7683
+ "BAPM",
7684
+ "BURI",
7685
+ "CREM",
7686
+ "ADOP",
7687
+ "EVEN",
7688
+ "RESI",
7689
+ "OCCU",
7690
+ "MARR"
7691
+ ].forEach((tag) => {
7692
+ indi.get(tag)?.toList()?.forEach((event) => walk(event));
7693
+ });
7694
+ });
7695
+ gedcom.fams()?.forEach((fam) => {
7696
+ if (!fam) return;
7697
+ ["MARR", "DIV", "ENGA", "ANUL", "EVEN"].forEach((tag) => {
7698
+ fam.get(tag)?.toList()?.forEach((event) => walk(event));
7699
+ });
7700
+ });
7701
+ return () => {
7702
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7703
+ restores[i]?.();
7704
+ }
7705
+ };
7706
+ };
7707
+
7708
+ // src/utils/uid.ts
7709
+ var createGedcomUid = () => {
7710
+ const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
7711
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7712
+ return cryptoObj.randomUUID();
7713
+ }
7714
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
7715
+ const rand = Math.random() * 16 | 0;
7716
+ const value = char === "x" ? rand : rand & 3 | 8;
7717
+ return value.toString(16);
7718
+ });
7719
+ };
7720
+
7721
+ // src/utils/gedcom7-metadata.ts
7722
+ var GEDCOM_MONTHS2 = [
7723
+ "JAN",
7724
+ "FEB",
7725
+ "MAR",
7726
+ "APR",
7727
+ "MAY",
7728
+ "JUN",
7729
+ "JUL",
7730
+ "AUG",
7731
+ "SEP",
7732
+ "OCT",
7733
+ "NOV",
7734
+ "DEC"
7735
+ ];
7736
+ var formatGedcom7Timestamp = (date = /* @__PURE__ */ new Date()) => {
7737
+ const day = String(date.getUTCDate());
7738
+ const month = GEDCOM_MONTHS2[date.getUTCMonth()];
7739
+ const year = String(date.getUTCFullYear());
7740
+ const hh = String(date.getUTCHours()).padStart(2, "0");
7741
+ const mm = String(date.getUTCMinutes()).padStart(2, "0");
7742
+ const ss = String(date.getUTCSeconds()).padStart(2, "0");
7743
+ return {
7744
+ date: `${day} ${month} ${year}`,
7745
+ time: `${hh}:${mm}:${ss}Z`
7746
+ };
7747
+ };
7748
+ var ensureMetaDate = (gedcom, record, tag, when, restores) => {
7749
+ if (record.get(tag)) {
7750
+ return;
7751
+ }
7752
+ const { date, time } = formatGedcom7Timestamp(when);
7753
+ const meta = createCommon(gedcom, void 0, record);
7754
+ meta.type = tag;
7755
+ const dateNode = createCommonDate(gedcom, void 0, record, meta);
7756
+ dateNode.type = "DATE";
7757
+ dateNode.value = date;
7758
+ dateNode.set("TIME", time);
7759
+ meta.set("DATE", dateNode);
7760
+ record.set(tag, meta);
7761
+ restores.push(() => {
7762
+ record.remove(tag);
7763
+ });
7764
+ };
7765
+ var ensureUid = (record, restores) => {
7766
+ const existing = record.get("UID")?.toValue();
7767
+ if (typeof existing === "string" && existing.trim()) {
7768
+ return;
7769
+ }
7770
+ const uid = createGedcomUid();
7771
+ record.set("UID", uid);
7772
+ restores.push(() => {
7773
+ record.remove("UID");
7774
+ });
7775
+ };
7776
+ var applyGedcom7RecordMetadata = (gedcom, options) => {
7777
+ const restores = [];
7778
+ const now = /* @__PURE__ */ new Date();
7779
+ const visit = (record) => {
7780
+ if (!record?.id) {
7781
+ return;
7782
+ }
7783
+ ensureUid(record, restores);
7784
+ ensureMetaDate(gedcom, record, "CREA", now, restores);
7785
+ ensureMetaDate(gedcom, record, "CHAN", now, restores);
7786
+ };
7787
+ gedcom.indis()?.forEach((indi) => visit(indi));
7788
+ gedcom.fams()?.forEach((fam) => visit(fam));
7789
+ gedcom.objes()?.forEach((obje) => visit(obje));
7790
+ gedcom.sours()?.forEach((sour) => visit(sour));
7791
+ gedcom.repos()?.forEach((repo) => visit(repo));
7792
+ gedcom.subms()?.forEach((subm) => visit(subm));
7793
+ gedcom.snotes()?.forEach((note) => visit(note));
7794
+ return () => {
7795
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7796
+ restores[i]?.();
7797
+ }
7798
+ };
7799
+ };
7800
+
7801
+ // src/utils/gedcom7-schema.ts
7802
+ var TREEVIZ_EXTENSION_BASE_URI = "https://treeviz.com/gedcom";
7803
+ var TREEVIZ_EXTENSION_URIS = {
7804
+ _ORIGHEAD: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIGHEAD`,
7805
+ _TREE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREE`,
7806
+ _TID: `${TREEVIZ_EXTENSION_BASE_URI}#_TID`,
7807
+ _PRIM: `${TREEVIZ_EXTENSION_BASE_URI}#_PRIM`,
7808
+ _FREL: `${TREEVIZ_EXTENSION_BASE_URI}#_FREL`,
7809
+ _MREL: `${TREEVIZ_EXTENSION_BASE_URI}#_MREL`,
7810
+ _SREL: `${TREEVIZ_EXTENSION_BASE_URI}#_SREL`,
7811
+ _UID: `${TREEVIZ_EXTENSION_BASE_URI}#_UID`,
7812
+ _WLNK: `${TREEVIZ_EXTENSION_BASE_URI}#_WLNK`,
7813
+ _MTTAG: `${TREEVIZ_EXTENSION_BASE_URI}#_MTTAG`,
7814
+ _MTCAT: `${TREEVIZ_EXTENSION_BASE_URI}#_MTCAT`,
7815
+ _CLON: `${TREEVIZ_EXTENSION_BASE_URI}#_CLON`,
7816
+ _MSER: `${TREEVIZ_EXTENSION_BASE_URI}#_MSER`,
7817
+ _OID: `${TREEVIZ_EXTENSION_BASE_URI}#_OID`,
7818
+ _LKID: `${TREEVIZ_EXTENSION_BASE_URI}#_LKID`,
7819
+ _PHOTO_RIN: `${TREEVIZ_EXTENSION_BASE_URI}#_PHOTO_RIN`,
7820
+ _EXPORTED_FROM_SITE_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_EXPORTED_FROM_SITE_ID`,
7821
+ _FS_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_ID`,
7822
+ _FS_LINK: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_LINK`,
7823
+ _FS_MATCH: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_MATCH`,
7824
+ _FS_SOUR: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_SOUR`,
7825
+ _IS_FS: `${TREEVIZ_EXTENSION_BASE_URI}#_IS_FS`,
7826
+ _LABEL: `${TREEVIZ_EXTENSION_BASE_URI}#_LABEL`,
7827
+ _MILT: `${TREEVIZ_EXTENSION_BASE_URI}#_MILT`,
7828
+ _MILTID: `${TREEVIZ_EXTENSION_BASE_URI}#_MILTID`,
7829
+ _ORIG: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIG`,
7830
+ _ENV: `${TREEVIZ_EXTENSION_BASE_URI}#_ENV`,
7831
+ _TREEVIZ_SETTINGS: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_SETTINGS`,
7832
+ _TREEVIZ_VIEW: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_VIEW`,
7833
+ _TREEVIZ_DETECTIVE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE`,
7834
+ _TREEVIZ_DETECTIVE_STATE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE_STATE`
7835
+ };
7836
+ var parseTagDefinition = (payload) => {
7837
+ const trimmed = payload.trim();
7838
+ const space = trimmed.indexOf(" ");
7839
+ if (space <= 0) {
7840
+ return void 0;
7841
+ }
7842
+ const tag = trimmed.slice(0, space).trim();
7843
+ const uri = trimmed.slice(space + 1).trim();
7844
+ if (!tag || !uri) {
7845
+ return void 0;
7846
+ }
7847
+ return { tag, uri };
7848
+ };
7849
+ var getExtensionSchema = (gedcom) => {
7850
+ const map = /* @__PURE__ */ new Map();
7851
+ const schma = gedcom.get("HEAD")?.get("SCHMA");
7852
+ if (!schma) {
7853
+ return map;
7854
+ }
7855
+ schma.get("TAG")?.toList()?.forEach((tagNode) => {
7856
+ const payload = tagNode?.toValue();
7857
+ if (typeof payload !== "string") {
7858
+ return;
7859
+ }
7860
+ const parsed = parseTagDefinition(payload);
7861
+ if (parsed) {
7862
+ map.set(parsed.tag.toUpperCase(), parsed.uri);
7863
+ }
7864
+ });
7865
+ return map;
7866
+ };
7867
+ var ensureSchemaNode = (gedcom) => {
7868
+ const head = gedcom.get("HEAD");
7869
+ if (!head) {
7870
+ return void 0;
7871
+ }
7872
+ let schma = head.get("SCHMA");
7873
+ if (!schma) {
7874
+ schma = createCommon(gedcom, void 0, head);
7875
+ head.set("SCHMA", schma);
7876
+ }
7877
+ return schma;
7878
+ };
7879
+ var registerExtensionTag = (gedcom, tag, uri) => {
7880
+ const normalizedTag = tag.trim().toUpperCase();
7881
+ const normalizedUri = uri.trim();
7882
+ if (!normalizedTag.startsWith("_") || !normalizedUri) {
7883
+ return void 0;
7884
+ }
7885
+ const schma = ensureSchemaNode(gedcom);
7886
+ if (!schma) {
7887
+ return void 0;
7888
+ }
7889
+ const payload = `${normalizedTag} ${normalizedUri}`;
7890
+ const existingList = schma.get("TAG")?.toList();
7891
+ const existing = existingList?.values().find((node) => {
7892
+ const value = node?.toValue();
7893
+ return typeof value === "string" && value.trim().toUpperCase().startsWith(`${normalizedTag} `);
7894
+ });
7895
+ if (existing) {
7896
+ existing.value = payload;
7897
+ return existing;
7898
+ }
7899
+ const tagNode = createCommon(gedcom, void 0, schma);
7900
+ tagNode.value = payload;
7901
+ schma.assign("TAG", tagNode);
7902
+ return tagNode;
7903
+ };
7904
+ var ensureExtensionSchema = (gedcom, extraTags) => {
7905
+ const head = gedcom.get("HEAD");
7906
+ const hadSchma = !!head?.get("SCHMA");
7907
+ const previousPayloads = head?.get("SCHMA")?.get("TAG")?.toList()?.values().map((node) => ({
7908
+ node,
7909
+ value: node?.toValue()
7910
+ })) ?? [];
7911
+ const tags = /* @__PURE__ */ new Set([
7912
+ ...Object.keys(TREEVIZ_EXTENSION_URIS),
7913
+ ...(extraTags ?? []).map((tag) => tag.toUpperCase())
7914
+ ]);
7915
+ tags.forEach((tag) => {
7916
+ const uri = TREEVIZ_EXTENSION_URIS[tag] ?? `${TREEVIZ_EXTENSION_BASE_URI}#${tag}`;
7917
+ if (tag.startsWith("_")) {
7918
+ registerExtensionTag(gedcom, tag, uri);
7919
+ }
7920
+ });
7921
+ return () => {
7922
+ if (!head) {
7923
+ return;
7924
+ }
7925
+ if (!hadSchma) {
7926
+ head.remove("SCHMA");
7927
+ return;
7928
+ }
7929
+ const schma = head.get("SCHMA");
7930
+ const list = schma?.get("TAG")?.toList();
7931
+ const keep = new Set(
7932
+ previousPayloads.map(
7933
+ (item) => typeof item.value === "string" ? item.value : void 0
7934
+ ).filter((value) => !!value)
7935
+ );
7936
+ list?.values().forEach((node) => {
7937
+ const value = node?.toValue();
7938
+ if (typeof value === "string" && !keep.has(value) && node) {
7939
+ list.delete(node);
7940
+ }
7941
+ });
7942
+ previousPayloads.forEach(({ node, value }) => {
7943
+ if (node && value !== void 0) {
7944
+ node.value = value;
7945
+ }
7946
+ });
7947
+ };
7948
+ };
7949
+
7950
+ // package.json
7951
+ var package_default = {
7952
+ version: "2.1.1"};
7953
+
7954
+ // src/utils/get-product-details.ts
7955
+ var isDevelopment = () => {
7956
+ return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
7957
+ };
7958
+ var getVersion = () => package_default.version;
7959
+
7960
+ // src/utils/multimedia.ts
7961
+ var inferMediaForm = (file, fallback = "bin") => {
7962
+ const withoutQuery = file.split("?")[0] || file;
7963
+ const ext = getFileExtension(withoutQuery);
7964
+ if (ext) {
7965
+ return ext.toLowerCase() === "jpeg" ? "jpg" : ext.toLowerCase();
7966
+ }
7967
+ return fallback;
7968
+ };
7969
+ var mergeMediaLists = (...lists) => {
7970
+ const merged = {};
7971
+ for (const list of lists) {
7972
+ if (!list) continue;
7973
+ Object.assign(merged, list);
7974
+ }
7975
+ return merged;
7976
+ };
7977
+
7978
+ // src/classes/note.ts
7979
+ var CommonNote = class extends Common {
7980
+ constructor(gedcom, id, main, parent) {
7981
+ super(gedcom, id, main, parent);
7982
+ if (!id) {
7983
+ delete this.id;
7984
+ }
7985
+ }
7986
+ set value(value) {
7987
+ const [note, ...contents] = value?.split(/\r?\n/) ?? [];
7988
+ this._value = note;
7989
+ if (contents.length) {
7990
+ const newContents = new List();
7991
+ contents.forEach((c, i) => {
7992
+ const newContent = createCommon(
7993
+ this._gedcom,
7994
+ void 0,
7995
+ this.main
7996
+ );
7997
+ newContent.id = `@U${i}@`;
7998
+ newContent.value = c;
7999
+ newContents.append(newContent);
8000
+ });
8001
+ const firstItem = newContents.index(0);
8002
+ if (firstItem) {
8003
+ if (newContents.length === 1) {
8004
+ this.set("CONT", firstItem);
8005
+ } else {
8006
+ this.set("CONT", newContents);
8007
+ }
8008
+ }
8009
+ }
8010
+ }
8011
+ get value() {
8012
+ const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8013
+ if (contents) {
8014
+ return `${this._value}
8015
+ ${contents}`;
8016
+ }
8017
+ return this._value;
8018
+ }
8019
+ exportValue() {
8020
+ return this._value;
8021
+ }
8022
+ };
8023
+ var createCommonNote = (gedcom, id, main, parent) => {
8024
+ return createProxy(
8025
+ new CommonNote(gedcom, id, main, parent)
8026
+ );
8027
+ };
8028
+
8029
+ // src/classes/obje.ts
8030
+ var Obje = class extends Common {
8031
+ standardizeMedia(namespace, override = true, urlGetter, gedcomVersion) {
8032
+ if (!this._gedcom) {
8033
+ return this;
8034
+ }
8035
+ const rin = this?.get("RIN")?.toValue();
8036
+ const clone = this?.get("_CLON._OID")?.toValue();
8037
+ const mser = this?.get("_MSER._LKID")?.toValue();
8038
+ const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8039
+ const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8040
+ const formRaw = this?.get("FILE.FORM")?.toValue() ?? this?.get("FORM")?.toValue();
8041
+ const mediType = this?.get("FILE.FORM.TYPE")?.toValue() ?? this?.get("FORM.TYPE")?.toValue() ?? this?.get("MEDI")?.toValue();
8042
+ const file = this?.get("FILE")?.toValue();
8043
+ const imgId = rin || clone || mser;
8044
+ const url = file || namespace && urlGetter?.(namespace, imgId);
8045
+ if (!url) {
8046
+ return this;
8047
+ }
8048
+ const version = normalizeGedcomVersion(
8049
+ gedcomVersion ?? this._gedcom.getGedcomVersion?.()
8050
+ );
8051
+ const form = formRaw || inferMediaForm(String(url));
8052
+ const newObject = createObje(this._gedcom, this.id, this.main);
8053
+ if (!override) {
8054
+ Object.assign(newObject, this);
8055
+ }
8056
+ const fileNode = createCommon(this._gedcom, void 0, this.main);
8057
+ fileNode.value = url;
8058
+ if (version === "7.0") {
8059
+ const formNode = createCommon(this._gedcom, void 0, fileNode);
8060
+ formNode.value = form;
8061
+ if (mediType) {
8062
+ formNode.set("TYPE", String(mediType));
8063
+ }
8064
+ fileNode.set("FORM", formNode);
8065
+ if (title) {
8066
+ fileNode.set("TITL", String(title));
8067
+ }
8068
+ if (note) {
8069
+ fileNode.set("NOTE", String(note));
8070
+ }
8071
+ newObject.set("FILE", fileNode);
8072
+ } else {
8073
+ newObject.set("FILE", fileNode);
8074
+ newObject.set("FORM", form);
8075
+ if (mediType) {
8076
+ newObject.set("MEDI", String(mediType));
8077
+ }
8078
+ if (title) {
8079
+ newObject.set("TITL", String(title));
8080
+ if (!note) {
8081
+ newObject.set("NOTE", String(title));
8082
+ }
8083
+ }
8084
+ if (note) {
8085
+ newObject.set("NOTE", String(note));
8086
+ if (!title) {
8087
+ newObject.set("TITL", String(note));
8088
+ }
8089
+ }
8090
+ }
8091
+ const prim = this.get("_PRIM")?.toValue();
8092
+ if (prim === "Y" || prim === "N") {
8093
+ newObject.set("_PRIM", prim);
8094
+ }
8095
+ const photoRin = this.get("_PHOTO_RIN")?.toValue();
8096
+ if (rin) {
8097
+ newObject.set("RIN", String(rin));
8098
+ }
8099
+ if (photoRin) {
8100
+ newObject.set("_PHOTO_RIN", String(photoRin));
8101
+ }
8102
+ const clonNode = this.get("_CLON");
8103
+ if (clonNode) {
8104
+ newObject.set("_CLON", clonNode);
8105
+ }
8106
+ const mserNode = this.get("_MSER");
8107
+ if (mserNode) {
8108
+ newObject.set("_MSER", mserNode);
8109
+ }
8110
+ const oidNode = this.get("_OID");
8111
+ if (oidNode && !newObject.get("_OID")) {
8112
+ newObject.set("_OID", oidNode);
8113
+ }
8114
+ const lkidNode = this.get("_LKID");
8115
+ if (lkidNode && !newObject.get("_LKID")) {
8116
+ newObject.set("_LKID", lkidNode);
8117
+ }
8118
+ if (override) {
8119
+ this.remove("FILE");
8120
+ this.remove("FORM");
8121
+ this.remove("MEDI");
8122
+ this.remove("TITL");
8123
+ this.remove("NOTE");
8124
+ Object.assign(this, newObject);
8125
+ return this;
8126
+ }
8127
+ return newObject;
8128
+ }
8129
+ };
8130
+ var createObje = (gedcom, id, main, parent) => {
8131
+ return createProxy(
8132
+ new Obje(gedcom, id, main, parent)
8133
+ );
8134
+ };
8135
+
8136
+ // src/classes/objes.ts
8137
+ var Objects = class _Objects extends List {
8138
+ copy() {
8139
+ const newList = new _Objects();
8140
+ this.entries().forEach(([key, value]) => {
8141
+ newList.item(key, value);
8142
+ });
8143
+ return newList;
8144
+ }
8145
+ except(item) {
8146
+ return this.copy().delete(item);
8147
+ }
8148
+ };
8149
+
8150
+ // src/classes/gedcom.ts
8151
+ var isObjectPatch = (value) => {
8152
+ return !!value && typeof value === "object" && !Array.isArray(value);
8153
+ };
8154
+ var GedCom = class extends Common {
8155
+ constructor() {
8156
+ super();
8157
+ this.tagMembers = {};
8158
+ this.reflist = {};
8159
+ this.refcount = 0;
8160
+ delete this._gedcom;
8161
+ delete this.id;
8162
+ this.removeValue();
8163
+ }
8164
+ getMain(type, index) {
8165
+ const list = !type || type instanceof List ? type : this.getList(type);
8166
+ if (!list) {
8167
+ return void 0;
8168
+ }
8169
+ if (typeof index === "string") {
8170
+ return list.item(index);
8171
+ }
8172
+ const keys = list.keys();
8173
+ return list.item(keys[index]);
8174
+ }
8175
+ getList(type) {
8176
+ return this.get(type);
8177
+ }
8178
+ createNodeFromPatchValue(value, main) {
8179
+ if (value === void 0 || value === null) {
8180
+ return void 0;
8181
+ }
8182
+ const node = createCommon(this, void 0, main);
8183
+ if (Array.isArray(value)) {
8184
+ value.forEach((item) => {
8185
+ const childNode = this.createNodeFromPatchValue(item, node);
8186
+ if (childNode) {
8187
+ node.assign("DATA", childNode);
8188
+ }
8189
+ });
8190
+ return node;
8191
+ }
8192
+ if (isObjectPatch(value)) {
8193
+ this.applyObjectToCommon(node, value);
8194
+ return node;
8195
+ }
8196
+ node.value = String(value);
8197
+ return node;
8198
+ }
8199
+ applyObjectToCommon(target, patch) {
8200
+ Object.entries(patch).forEach(([rawTag, value]) => {
8201
+ if (rawTag === "value") {
8202
+ if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
8203
+ return;
8204
+ }
8205
+ target.value = String(value);
8206
+ return;
8207
+ }
8208
+ if (rawTag === "id") {
8209
+ if (typeof value === "string") {
8210
+ target.id = value;
8211
+ }
8212
+ return;
8213
+ }
8214
+ const tag = rawTag;
8215
+ if (value === void 0) {
8216
+ return;
8217
+ }
8218
+ if (value === null) {
8219
+ target.remove(tag);
8220
+ return;
8221
+ }
8222
+ if (Array.isArray(value)) {
8223
+ target.remove(tag);
8224
+ value.forEach((item) => {
8225
+ const listNode = this.createNodeFromPatchValue(
8226
+ item,
8227
+ target.main ?? this
8228
+ );
8229
+ if (listNode) {
8230
+ target.assign(tag, listNode);
8231
+ }
7245
8232
  });
7246
8233
  return;
7247
8234
  }
@@ -7378,6 +8365,9 @@ var GedCom = class extends Common {
7378
8365
  const id = this.nextIndiKey();
7379
8366
  const indi = createIndi(this, id);
7380
8367
  indi.type = "INDI";
8368
+ if (this.getGedcomVersion() === "7.0") {
8369
+ indi.set("UID", createGedcomUid());
8370
+ }
7381
8371
  indis.item(id, indi);
7382
8372
  return indi;
7383
8373
  }
@@ -7390,9 +8380,151 @@ var GedCom = class extends Common {
7390
8380
  const id = this.nextFamKey();
7391
8381
  const fam = createFam(this, id);
7392
8382
  fam.type = "FAM";
8383
+ if (this.getGedcomVersion() === "7.0") {
8384
+ fam.set("UID", createGedcomUid());
8385
+ }
7393
8386
  fams.item(id, fam);
7394
8387
  return fam;
7395
8388
  }
8389
+ nextObjeKey() {
8390
+ const ids = [];
8391
+ this.objes()?.forEach((_obje, id) => {
8392
+ ids.push(String(id));
8393
+ });
8394
+ return nextRecordId(ids, "O");
8395
+ }
8396
+ /**
8397
+ * Create a top-level multimedia record (`0 @On@ OBJE`) with FILE/FORM/TITL.
8398
+ * GEDCOM 7 uses nested `FILE → FORM [→ TYPE]`; 5.5.1 uses sibling FORM/TITL.
8399
+ */
8400
+ createMultimediaRecord(input) {
8401
+ const existing = this.objes();
8402
+ const objes = existing ?? new Objects();
8403
+ if (!existing) {
8404
+ this.set("@@OBJE", objes);
8405
+ }
8406
+ const id = this.nextObjeKey();
8407
+ const obje = createObje(this, id);
8408
+ obje.type = "OBJE";
8409
+ objes.item(id, obje);
8410
+ const version = normalizeGedcomVersion(
8411
+ input.gedcomVersion ?? this.getGedcomVersion()
8412
+ );
8413
+ const form = input.form || inferMediaForm(input.file);
8414
+ const fileNode = createCommon(this, void 0, obje);
8415
+ fileNode.value = input.file;
8416
+ if (version === "7.0") {
8417
+ const formNode = createCommon(
8418
+ this,
8419
+ void 0,
8420
+ fileNode
8421
+ );
8422
+ formNode.value = form;
8423
+ if (input.mediType) {
8424
+ formNode.set("TYPE", input.mediType);
8425
+ }
8426
+ fileNode.set("FORM", formNode);
8427
+ if (input.title) {
8428
+ fileNode.set("TITL", input.title);
8429
+ }
8430
+ obje.set("FILE", fileNode);
8431
+ } else {
8432
+ obje.set("FILE", fileNode);
8433
+ obje.set("FORM", form);
8434
+ if (input.mediType) {
8435
+ obje.set("MEDI", input.mediType);
8436
+ }
8437
+ if (input.title) {
8438
+ obje.set("TITL", input.title);
8439
+ }
8440
+ }
8441
+ if (input.primary) {
8442
+ obje.set("_PRIM", "Y");
8443
+ }
8444
+ if (normalizeGedcomVersion(
8445
+ input.gedcomVersion ?? this.getGedcomVersion()
8446
+ ) === "7.0") {
8447
+ obje.set("UID", createGedcomUid());
8448
+ }
8449
+ return obje;
8450
+ }
8451
+ nextSnoteKey() {
8452
+ const ids = [];
8453
+ this.snotes()?.forEach((_note, id) => {
8454
+ ids.push(String(id));
8455
+ });
8456
+ return nextRecordId(ids, "N");
8457
+ }
8458
+ snotes() {
8459
+ return this.getList("@@SNOTE");
8460
+ }
8461
+ snote(index) {
8462
+ return this.getMain(this.snotes(), index);
8463
+ }
8464
+ /**
8465
+ * Create a top-level shared note record (`0 @Nn@ SNOTE` for GEDCOM 7).
8466
+ */
8467
+ createSharedNote(text) {
8468
+ const existing = this.snotes();
8469
+ const notes = existing ?? new List();
8470
+ if (!existing) {
8471
+ this.set("@@SNOTE", notes);
8472
+ }
8473
+ const id = this.nextSnoteKey();
8474
+ const note = createCommonNote(this, id);
8475
+ note.type = "SNOTE";
8476
+ note.value = text;
8477
+ if (this.getGedcomVersion() === "7.0") {
8478
+ note.set("UID", createGedcomUid());
8479
+ }
8480
+ notes.item(id, note);
8481
+ return note;
8482
+ }
8483
+ registerExtensionTag(tag, uri) {
8484
+ return registerExtensionTag(this, tag, uri);
8485
+ }
8486
+ getExtensionSchema() {
8487
+ return getExtensionSchema(this);
8488
+ }
8489
+ ensureExtensionSchema(extraTags) {
8490
+ ensureExtensionSchema(this, extraTags);
8491
+ return getExtensionSchema(this);
8492
+ }
8493
+ /**
8494
+ * Collect multimedia descriptors via each individual's `multimedia()`.
8495
+ */
8496
+ async collectMultimedia(options) {
8497
+ const people = [];
8498
+ if (options?.indiIds?.length) {
8499
+ options.indiIds.forEach((id) => {
8500
+ const indi = this.indi(id);
8501
+ if (indi) {
8502
+ people.push(indi);
8503
+ }
8504
+ });
8505
+ } else {
8506
+ this.indis()?.forEach((indi) => {
8507
+ if (indi) {
8508
+ people.push(indi);
8509
+ }
8510
+ });
8511
+ }
8512
+ const lists = await Promise.all(
8513
+ people.map((indi) => indi.multimedia(options?.namespace))
8514
+ );
8515
+ return mergeMediaLists(...lists);
8516
+ }
8517
+ /**
8518
+ * Serialize as GEDCOM 7 and pack a FamilySearch GEDZIP (`.gdz`).
8519
+ * Media bytes must be supplied by the caller (cache/fetch outside the parser).
8520
+ */
8521
+ async toGedzip(options) {
8522
+ const text = this.toGedcom(void 0, 0, {
8523
+ ...options,
8524
+ gedcomVersion: "7.0"
8525
+ });
8526
+ return buildGedzip(text, options?.media ?? []);
8527
+ }
7396
8528
  objes() {
7397
8529
  return this.getList("@@OBJE");
7398
8530
  }
@@ -7477,9 +8609,52 @@ var GedCom = class extends Common {
7477
8609
  });
7478
8610
  return lists;
7479
8611
  }
7480
- getDownloadHeader() {
8612
+ getGedcomVersion() {
8613
+ const raw = this.get("HEAD")?.get("GEDC")?.get("VERS")?.toValue();
8614
+ return normalizeGedcomVersion(
8615
+ typeof raw === "string" ? raw : void 0
8616
+ );
8617
+ }
8618
+ /** Rewrite HEAD.GEDC (and CHAR/FORM) for the requested export version. */
8619
+ applyExportVersion(version) {
8620
+ const head = this.get("HEAD");
8621
+ if (!head) {
8622
+ return;
8623
+ }
8624
+ this.applyGedcomSpecToHead(head, normalizeGedcomVersion(version));
8625
+ }
8626
+ applyGedcomSpecToHead(head, version) {
8627
+ const previousGedc = head.get("GEDC");
8628
+ const previousForm = previousGedc?.get("FORM")?.toValue();
8629
+ const previousChar = head.get("CHAR")?.toValue();
8630
+ head.remove("GEDC");
8631
+ const gedc = head.set("GEDC", "");
8632
+ if (!gedc) {
8633
+ return;
8634
+ }
8635
+ gedc.set("VERS", version);
8636
+ if (version === "7.0") {
8637
+ head.remove("CHAR");
8638
+ return;
8639
+ }
8640
+ gedc.set(
8641
+ "FORM",
8642
+ typeof previousForm === "string" && previousForm ? previousForm : "LINEAGE-LINKED"
8643
+ );
8644
+ if (!head.get("CHAR")?.toValue()) {
8645
+ head.set(
8646
+ "CHAR",
8647
+ typeof previousChar === "string" && previousChar ? previousChar : "UTF-8"
8648
+ );
8649
+ }
8650
+ }
8651
+ getDownloadHeader(gedcomVersion) {
7481
8652
  const newHead = createCommon();
7482
8653
  Object.assign(newHead, this.get("HEAD") ?? {});
8654
+ this.applyGedcomSpecToHead(
8655
+ newHead,
8656
+ gedcomVersion ?? this.getGedcomVersion()
8657
+ );
7483
8658
  const existingSour = this.get("HEAD")?.get("SOUR");
7484
8659
  const existingTreeName = existingSour?.get("_TREE")?.toValue();
7485
8660
  const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
@@ -7526,22 +8701,45 @@ var GedCom = class extends Common {
7526
8701
  }
7527
8702
  toGedcom(tag, level, options) {
7528
8703
  if (options?.super) {
7529
- return appendGedcomTrailer(
7530
- super.toGedcom(tag, level, options)
7531
- );
8704
+ return appendGedcomTrailer(super.toGedcom(tag, level, options));
7532
8705
  }
8706
+ const requestedVersion = options?.gedcomVersion;
8707
+ const exportVersion = requestedVersion ? normalizeGedcomVersion(requestedVersion) : this.getGedcomVersion();
8708
+ const exportOptions = {
8709
+ ...options,
8710
+ obje: {
8711
+ standardize: options?.obje?.standardize ?? (requestedVersion !== void 0 || !options?.original),
8712
+ override: options?.obje?.override,
8713
+ namespace: options?.obje?.namespace
8714
+ }
8715
+ };
7533
8716
  const newGedcom = createGedCom();
7534
8717
  Object.assign(newGedcom, this);
7535
- if (!options?.original) {
8718
+ if (!exportOptions.original) {
7536
8719
  Object.assign(newGedcom, {
7537
- HEAD: this.getDownloadHeader()
8720
+ HEAD: this.getDownloadHeader(exportVersion)
7538
8721
  });
7539
8722
  }
7540
- if (options?.indis?.length) {
7541
- const newContent = this.getIndiRelatedLists(options.indis);
8723
+ if (exportOptions.indis?.length) {
8724
+ const newContent = this.getIndiRelatedLists(exportOptions.indis);
7542
8725
  Object.assign(newGedcom, newContent);
7543
8726
  }
7544
- return newGedcom.toGedcom(tag, level, { ...options, super: true });
8727
+ const restoreEnums = exportVersion === "7.0" ? applyGedcom7Enumerations(newGedcom) : void 0;
8728
+ const restoreDates = exportVersion === "7.0" ? applyGedcom7DatePhrases(newGedcom) : void 0;
8729
+ const restoreMetadata = exportVersion === "7.0" ? applyGedcom7RecordMetadata(newGedcom) : void 0;
8730
+ const restoreSchema = exportVersion === "7.0" ? ensureExtensionSchema(newGedcom) : void 0;
8731
+ try {
8732
+ return newGedcom.toGedcom(tag, level, {
8733
+ ...exportOptions,
8734
+ gedcomVersion: exportVersion,
8735
+ super: true
8736
+ });
8737
+ } finally {
8738
+ restoreSchema?.();
8739
+ restoreMetadata?.();
8740
+ restoreDates?.();
8741
+ restoreEnums?.();
8742
+ }
7545
8743
  }
7546
8744
  hasTag(tag) {
7547
8745
  const tagName = typeof tag === "string" ? tag : tag?.get("NAME")?.toValue();
@@ -7985,20 +9183,6 @@ var mergeGedcoms = (targetGedcom, sourceGedcom, strategy = "id") => {
7985
9183
  return mergedGedcom;
7986
9184
  };
7987
9185
 
7988
- // src/classes/objes.ts
7989
- var Objects = class _Objects extends List {
7990
- copy() {
7991
- const newList = new _Objects();
7992
- this.entries().forEach(([key, value]) => {
7993
- newList.item(key, value);
7994
- });
7995
- return newList;
7996
- }
7997
- except(item) {
7998
- return this.copy().delete(item);
7999
- }
8000
- };
8001
-
8002
9186
  // src/classes/repos.ts
8003
9187
  var Repositories = class _Repositories extends List {
8004
9188
  copy() {
@@ -8041,144 +9225,6 @@ var Submitters = class _Submitters extends List {
8041
9225
  }
8042
9226
  };
8043
9227
 
8044
- // src/classes/note.ts
8045
- var CommonNote = class extends Common {
8046
- constructor(gedcom, id, main, parent) {
8047
- super(gedcom, id, main, parent);
8048
- delete this.id;
8049
- }
8050
- set value(value) {
8051
- const [note, ...contents] = value?.split(/\r?\n/) ?? [];
8052
- this._value = note;
8053
- if (contents.length) {
8054
- const newContents = new List();
8055
- contents.forEach((c, i) => {
8056
- const newContent = createCommon(
8057
- this._gedcom,
8058
- void 0,
8059
- this.main
8060
- );
8061
- newContent.id = `@U${i}@`;
8062
- newContent.value = c;
8063
- newContents.append(newContent);
8064
- });
8065
- const firstItem = newContents.index(0);
8066
- if (firstItem) {
8067
- if (newContents.length === 1) {
8068
- this.set("CONT", firstItem);
8069
- } else {
8070
- this.set("CONT", newContents);
8071
- }
8072
- }
8073
- }
8074
- }
8075
- get value() {
8076
- const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8077
- if (contents) {
8078
- return `${this._value}
8079
- ${contents}`;
8080
- }
8081
- return this._value;
8082
- }
8083
- exportValue() {
8084
- return this._value;
8085
- }
8086
- };
8087
- var createCommonNote = (gedcom, id, main, parent) => {
8088
- return createProxy(
8089
- new CommonNote(gedcom, id, main, parent)
8090
- );
8091
- };
8092
-
8093
- // src/classes/obje.ts
8094
- var Obje = class extends Common {
8095
- standardizeMedia(namespace, override = true, urlGetter) {
8096
- if (!this._gedcom) {
8097
- return this;
8098
- }
8099
- const rin = this?.get("RIN")?.toValue();
8100
- const clone = this?.get("_CLON._OID")?.toValue();
8101
- const mser = this?.get("_MSER._LKID")?.toValue();
8102
- const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8103
- const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8104
- const form = this?.get("FILE.FORM")?.toValue();
8105
- const file = this?.get("FILE")?.toValue();
8106
- const imgId = rin || clone || mser;
8107
- const url = file || namespace && urlGetter?.(namespace, imgId);
8108
- if (!url) {
8109
- return this;
8110
- }
8111
- const newObject = createObje(this._gedcom, this.id, this.main);
8112
- if (!override) {
8113
- Object.assign(newObject, this);
8114
- }
8115
- if (form) {
8116
- if (typeof form === "string") {
8117
- const newForm = createCommon(
8118
- this._gedcom,
8119
- void 0,
8120
- this.main
8121
- );
8122
- newForm.value = form;
8123
- newObject.set("FORM", newForm);
8124
- } else {
8125
- newObject.set("FORM", form);
8126
- }
8127
- }
8128
- if (title) {
8129
- if (typeof title === "string") {
8130
- const newTitle = createCommon(
8131
- this._gedcom,
8132
- void 0,
8133
- this.main
8134
- );
8135
- newTitle.value = title;
8136
- newObject.set("TITL", newTitle);
8137
- if (!note) {
8138
- newObject.set("NOTE", newTitle);
8139
- }
8140
- } else {
8141
- newObject.set("TITL", title);
8142
- if (!note) {
8143
- newObject.set("NOTE", title);
8144
- }
8145
- }
8146
- }
8147
- if (note) {
8148
- if (typeof note === "string") {
8149
- const newNote = createCommon(
8150
- this._gedcom,
8151
- void 0,
8152
- this.main
8153
- );
8154
- newNote.value = note;
8155
- newObject.set("NOTE", newNote);
8156
- if (!title) {
8157
- newObject.set("TITL", newNote);
8158
- }
8159
- } else {
8160
- newObject.set("NOTE", note);
8161
- if (!title) {
8162
- newObject.set("TITL", note);
8163
- }
8164
- }
8165
- }
8166
- const newUrl = createCommon(this._gedcom, void 0, this.main);
8167
- newUrl.value = url;
8168
- newObject.set("FILE", newUrl);
8169
- if (override) {
8170
- Object.assign(this, newObject);
8171
- return this;
8172
- }
8173
- return newObject;
8174
- }
8175
- };
8176
- var createObje = (gedcom, id, main, parent) => {
8177
- return createProxy(
8178
- new Obje(gedcom, id, main, parent)
8179
- );
8180
- };
8181
-
8182
9228
  // src/classes/repo.ts
8183
9229
  var Repo = class extends Common {
8184
9230
  };
@@ -8227,6 +9273,8 @@ var create = (gedcom, type, id, nodes) => {
8227
9273
  prevNode = createIndi(gedcom, id);
8228
9274
  } else if (convertType === "FAM") {
8229
9275
  prevNode = createFam(gedcom, id);
9276
+ } else if (convertType === "SNOTE" || convertType === "NOTE") {
9277
+ prevNode = createCommonNote(gedcom, id);
8230
9278
  } else {
8231
9279
  prevNode = createCommon(gedcom, id);
8232
9280
  }
@@ -8242,7 +9290,7 @@ var create = (gedcom, type, id, nodes) => {
8242
9290
  prevNode = createObje(gedcom, void 0, mainNode, curNode);
8243
9291
  } else if (type === "DATE") {
8244
9292
  prevNode = createCommonDate(gedcom, void 0, mainNode, curNode);
8245
- } else if (type === "NOTE") {
9293
+ } else if (type === "NOTE" || type === "SNOTE") {
8246
9294
  prevNode = createCommonNote(gedcom, void 0, mainNode, curNode);
8247
9295
  } else if (type === "NAME" && mainNode instanceof Indi) {
8248
9296
  prevNode = createCommonName(gedcom, void 0, mainNode, curNode);
@@ -8325,6 +9373,13 @@ var GedcomTree = {
8325
9373
  if (linesAcc._inHead && lineIndent === 1 && lineType === "FILE") {
8326
9374
  linesAcc._hasFile = true;
8327
9375
  }
9376
+ if (lineType.toUpperCase() === "CONC") {
9377
+ const last = acc[acc.length - 1];
9378
+ if (last !== void 0) {
9379
+ acc[acc.length - 1] = `${last}${lineValue}`;
9380
+ return acc;
9381
+ }
9382
+ }
8328
9383
  if (lineIndent > 0 && lineIndent > prevLineIndent && lineValue && isId(lineValue)) {
8329
9384
  const refLines = lineValue.split(/,\s*/).map((id) => line.replace(lineValue, id));
8330
9385
  if (refLines.length > 1) {