@treeviz/gedcom-parser 2.0.10 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +236 -989
  2. package/dist/classes/common.d.ts +10 -5
  3. package/dist/classes/common.d.ts.map +1 -1
  4. package/dist/classes/date.d.ts +2 -0
  5. package/dist/classes/date.d.ts.map +1 -1
  6. package/dist/classes/fam.d.ts +10 -1
  7. package/dist/classes/fam.d.ts.map +1 -1
  8. package/dist/classes/fams.d.ts.map +1 -1
  9. package/dist/classes/gedcom.d.ts +45 -4
  10. package/dist/classes/gedcom.d.ts.map +1 -1
  11. package/dist/classes/index.js +1770 -279
  12. package/dist/classes/indi.d.ts +24 -1
  13. package/dist/classes/indi.d.ts.map +1 -1
  14. package/dist/classes/list.d.ts.map +1 -1
  15. package/dist/classes/note.d.ts.map +1 -1
  16. package/dist/classes/obje.d.ts +2 -1
  17. package/dist/classes/obje.d.ts.map +1 -1
  18. package/dist/cli/index.js +2802 -1296
  19. package/dist/constants/constants.d.ts +7 -0
  20. package/dist/constants/constants.d.ts.map +1 -1
  21. package/dist/constants/index.js +1599 -989
  22. package/dist/factories/index.js +719 -110
  23. package/dist/index.d.ts +12 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +3837 -2028
  26. package/dist/interfaces/common.d.ts +6 -4
  27. package/dist/interfaces/common.d.ts.map +1 -1
  28. package/dist/interfaces/fam.d.ts +10 -1
  29. package/dist/interfaces/fam.d.ts.map +1 -1
  30. package/dist/interfaces/gedcom.d.ts +25 -1
  31. package/dist/interfaces/gedcom.d.ts.map +1 -1
  32. package/dist/interfaces/indi.d.ts +15 -1
  33. package/dist/interfaces/indi.d.ts.map +1 -1
  34. package/dist/kinship-translator/index.js +719 -110
  35. package/dist/structures/gedcom.d.ts +2 -0
  36. package/dist/structures/gedcom.d.ts.map +1 -1
  37. package/dist/types/types.d.ts +32 -4
  38. package/dist/types/types.d.ts.map +1 -1
  39. package/dist/utils/common-creator.d.ts.map +1 -1
  40. package/dist/utils/fact-edit.d.ts +34 -0
  41. package/dist/utils/fact-edit.d.ts.map +1 -0
  42. package/dist/utils/family-edit.d.ts +23 -0
  43. package/dist/utils/family-edit.d.ts.map +1 -0
  44. package/dist/utils/gedcom-trailer.d.ts +6 -0
  45. package/dist/utils/gedcom-trailer.d.ts.map +1 -0
  46. package/dist/utils/gedcom-version.d.ts +6 -0
  47. package/dist/utils/gedcom-version.d.ts.map +1 -0
  48. package/dist/utils/gedcom7-dates.d.ts +20 -0
  49. package/dist/utils/gedcom7-dates.d.ts.map +1 -0
  50. package/dist/utils/gedcom7-enumerations.d.ts +26 -0
  51. package/dist/utils/gedcom7-enumerations.d.ts.map +1 -0
  52. package/dist/utils/gedcom7-metadata.d.ts +19 -0
  53. package/dist/utils/gedcom7-metadata.d.ts.map +1 -0
  54. package/dist/utils/gedcom7-schema.d.ts +27 -0
  55. package/dist/utils/gedcom7-schema.d.ts.map +1 -0
  56. package/dist/utils/gedzip.d.ts +81 -0
  57. package/dist/utils/gedzip.d.ts.map +1 -0
  58. package/dist/utils/index.d.ts +13 -1
  59. package/dist/utils/index.d.ts.map +1 -1
  60. package/dist/utils/index.js +2225 -416
  61. package/dist/utils/local-media.d.ts +30 -0
  62. package/dist/utils/local-media.d.ts.map +1 -0
  63. package/dist/utils/media-utils.d.ts +10 -6
  64. package/dist/utils/media-utils.d.ts.map +1 -1
  65. package/dist/utils/multimedia.d.ts +38 -0
  66. package/dist/utils/multimedia.d.ts.map +1 -0
  67. package/dist/utils/parser.d.ts +3 -0
  68. package/dist/utils/parser.d.ts.map +1 -1
  69. package/dist/utils/uid.d.ts +6 -0
  70. package/dist/utils/uid.d.ts.map +1 -0
  71. package/package.json +2 -1
@@ -8,11 +8,13 @@ var __export = (target, all) => {
8
8
  };
9
9
 
10
10
  // src/constants/constants.ts
11
- var REF_LINE_REG = /^0 (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/gm;
12
- var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{2,}) ?(?<value>.*)/;
11
+ var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
12
+ 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;
13
+ 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>.*)/;
13
14
  var ID_REG = /^@[_a-zA-Z0-9]+@/;
14
15
  var ID_GETTER_REG = /^(?<at>@)?(?<letter>[A-Z])?/i;
15
16
  var ID_SPLIT_REG = /^@[_a-zA-Z0-9]+@:/;
17
+ var ORIG_HEAD_TAG = "_ORIGHEAD";
16
18
  var MAX_FILE_SIZE_TO_SYNC = 1024 * 1024 * 20;
17
19
 
18
20
  // src/constants/filters.ts
@@ -543,1050 +545,1347 @@ var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces =
543
545
  } : {}
544
546
  };
545
547
  };
546
- var Common = class _Common {
547
- constructor(gedcom, id, main, parent) {
548
+
549
+ // src/utils/gedcom-trailer.ts
550
+ var GEDCOM_TRAILER_TAG = "TRLR";
551
+ var isGedcomTrailerTag = (tag) => tag === GEDCOM_TRAILER_TAG;
552
+ var List = class _List {
553
+ // length = 0;
554
+ constructor(items) {
555
+ this.items = {};
548
556
  this.isListable = true;
549
- this._gedcom = gedcom;
550
- this._main = main;
551
- this._parent = parent;
552
- this.initUniqueId();
553
- if (id) {
554
- this._id = id;
555
- } else {
556
- delete this._id;
557
+ this.marker = void 0;
558
+ if (items) {
559
+ this.concat(items);
557
560
  }
558
561
  }
559
- set type(type) {
560
- if (type) {
561
- this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
562
- }
563
- this._type = type;
562
+ get length() {
563
+ return this.values().length;
564
564
  }
565
- get type() {
566
- return this._type;
565
+ has(indi) {
566
+ return !!this.item(
567
+ typeof indi === "string" ? indi : indi?.id ?? ""
568
+ );
567
569
  }
568
- initUniqueId() {
569
- if (this._gedcom) {
570
- this.uniqueId = `~Common:${this._gedcom.refcount++}`;
571
- }
570
+ get(name) {
571
+ const propList = new _List();
572
+ this.entries().forEach(([key, item]) => {
573
+ propList.item(key, item.get(name));
574
+ });
575
+ return propList;
572
576
  }
573
- set uniqueId(id) {
574
- const currentId = this._uniqueId;
575
- if (this._gedcom?.reflist && id) {
576
- this._uniqueId = id;
577
- if (currentId && this._gedcom.reflist[currentId]) {
578
- delete this._gedcom.reflist[currentId];
577
+ getIf(name, condition, name2) {
578
+ const propList = new _List();
579
+ this.entries().forEach(([key, item]) => {
580
+ const passedItem = item.getIf(name, condition, name2);
581
+ if (passedItem) {
582
+ propList.item(key, passedItem);
579
583
  }
580
- this._gedcom.reflist[id] = this;
581
- }
584
+ });
585
+ return propList;
582
586
  }
583
- get uniqueId() {
584
- return this._uniqueId;
587
+ keys() {
588
+ return Object.keys(this.items);
585
589
  }
586
- set id(id) {
587
- this._id = idGetter(id);
590
+ values() {
591
+ return Object.values(this.items);
588
592
  }
589
- get id() {
590
- return idGetter(this._id);
593
+ entries() {
594
+ return Object.entries(this.items);
591
595
  }
592
- set value(value) {
593
- this._value = value;
596
+ setLength(_type) {
594
597
  }
595
- get value() {
596
- return this._value;
598
+ first() {
599
+ return this.index(0);
597
600
  }
598
- /**
599
- * Set the gedcom reference for this object
600
- * @param gedcom - The GedComType to set as the gedcom reference
601
- */
602
- setGedcom(gedcom) {
603
- this._gedcom = gedcom;
601
+ last() {
602
+ return this.index(this.keys().length - 1);
604
603
  }
605
- // avoid to override
606
- get originalValue() {
607
- return this._value;
604
+ index(index) {
605
+ const keys = this.keys();
606
+ const key = keys[index];
607
+ return key && this.items[key];
608
608
  }
609
- get ref() {
610
- if (typeof this.value === "string" && isId(this.value) && this.refType) {
611
- return this._gedcom?.[getListTag(this.refType)]?.item(
612
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
613
- this.value
614
- );
609
+ item(name, value) {
610
+ if (arguments.length > 1) {
611
+ set(this.items, name, value);
612
+ this.setLength();
615
613
  }
616
- return this;
614
+ return get(this.items, name);
617
615
  }
618
- get main() {
619
- return this._main;
616
+ removeItem(name) {
617
+ unset(this.items, name);
618
+ this.setLength();
620
619
  }
621
- get parent() {
622
- return this._parent;
620
+ exclude(excludedList) {
621
+ excludedList.keys().forEach((itemId) => {
622
+ delete this.items[itemId];
623
+ });
624
+ this.setLength();
625
+ return this;
623
626
  }
624
- addRef(refNode) {
625
- if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
626
- if (!this._refs) {
627
- this._refs = new List();
628
- }
629
- this._refs.item(refNode.value, refNode);
627
+ concat(items) {
628
+ if (Array.isArray(items)) {
629
+ items.forEach((item) => {
630
+ this.append(item);
631
+ });
632
+ } else {
633
+ Object.assign(this.items, items ?? {});
630
634
  }
635
+ this.setLength();
636
+ return this;
631
637
  }
632
- getRefs() {
633
- return this._refs;
634
- }
635
- exportValue() {
636
- return this.value;
638
+ merge(mergedList) {
639
+ this.concat(mergedList?.items);
640
+ return this;
637
641
  }
638
- removeValue() {
639
- delete this._value;
642
+ intersection(mergedList) {
643
+ const inter = intersectionBy(
644
+ this.values(),
645
+ mergedList?.values() ?? [],
646
+ "id"
647
+ );
648
+ return new _List(inter);
640
649
  }
641
- set(name, value) {
642
- if (typeof value === "string") {
643
- const usedValue = createCommon(this._gedcom, void 0, this.main);
644
- usedValue.value = value;
645
- set(this, name, usedValue);
646
- } else {
647
- set(this, name, value);
648
- }
649
- return get(this, name);
650
+ difference(mergedList) {
651
+ const inter = differenceBy(
652
+ this.values(),
653
+ mergedList?.values() ?? [],
654
+ "id"
655
+ );
656
+ return new _List(inter);
650
657
  }
651
- assign(name, value, unique = false) {
652
- let curValue = this.get(name);
653
- if (curValue?.isListable) {
654
- if (curValue instanceof _Common) {
655
- curValue = new List().concat({ ...[curValue] });
656
- this.set(name, curValue);
658
+ prepend(item) {
659
+ if (item.id) {
660
+ let increase = true;
661
+ if (this.items[item.id]) {
662
+ delete this.items[item.id];
663
+ increase = false;
664
+ }
665
+ this.items = Object.assign(
666
+ {
667
+ [item.id]: item
668
+ },
669
+ this.items
670
+ );
671
+ if (increase) {
672
+ this.setLength("increase");
657
673
  }
658
- const values = [
659
- ...curValue.values(),
660
- ...value instanceof List ? value.values() : [value]
661
- ];
662
- curValue.concat({
663
- ...!unique ? values : uniqBy(
664
- values,
665
- (value2) => value2 instanceof _Common ? value2?.value : void 0
666
- )
667
- });
668
- } else {
669
- this.set(name, value);
670
674
  }
671
- return get(this, name);
675
+ return this;
672
676
  }
673
- get(name) {
674
- if (!name.includes(".")) {
675
- return get(this, name);
676
- }
677
- const nameParts = name.split(".");
678
- let last = this;
679
- for (const part of nameParts) {
680
- const indexPart = Number(part);
681
- const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
682
- const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
683
- const usedPart = itemId || itemIndex;
684
- if (last instanceof List) {
685
- if (!isNaN(indexPart)) {
686
- last = last.index(indexPart);
687
- } else {
688
- if (usedPart) {
689
- last = last.item(usedPart);
690
- } else {
691
- last = last?.index(0)?.get(part);
692
- }
693
- }
694
- } else {
695
- const usedIndex = Number(itemIndex ?? indexPart);
696
- if (!isNaN(usedIndex)) {
697
- last = last?.toList().index(usedIndex);
698
- } else {
699
- last = last?.get(itemId ?? part);
700
- }
677
+ append(item) {
678
+ if (item.id) {
679
+ let increase = true;
680
+ if (this.items[item.id]) {
681
+ delete this.items[item.id];
682
+ increase = false;
701
683
  }
702
- if (!last) {
703
- break;
684
+ this.items[item.id] = item;
685
+ if (increase) {
686
+ this.setLength("increase");
704
687
  }
705
688
  }
706
- return last;
689
+ return this;
707
690
  }
708
- remove(name) {
709
- unset(this, name);
691
+ delete(item) {
692
+ if (item.id && this.items[item.id]) {
693
+ delete this.items[item.id];
694
+ this.setLength("decrease");
695
+ }
696
+ return this;
710
697
  }
711
- getGedcom() {
712
- return this._gedcom;
698
+ copy(ClassName = _List) {
699
+ const newList = new ClassName();
700
+ Object.assign(newList.items, this.items);
701
+ newList.setLength();
702
+ return newList;
713
703
  }
714
- getIf(name, condition, name2) {
715
- const nameParts = name.split(".");
716
- const name2Parts = name2.split(".");
717
- if (nameParts.length !== name2Parts.length) {
718
- throw new Error("Tag pathes must be of the same depth");
719
- }
720
- if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
721
- throw new Error("Final tags must be siblings");
722
- }
723
- const valueCommon = this.get(name);
724
- if (valueCommon instanceof _Common) {
725
- return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
726
- }
727
- const valueCommon2 = this.get(name2)?.toList();
728
- const ifList = new List();
729
- valueCommon?.forEach((item, key) => {
730
- const pair = valueCommon2?.item(key);
731
- ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
732
- });
733
- return ifList;
734
- }
735
- toString() {
736
- return this.value?.toString() || "";
737
- }
738
- toValue() {
739
- return this.value;
740
- }
741
- toProp(tag) {
742
- const prop = this.get(tag);
743
- return prop;
744
- }
745
- toList() {
746
- return new List().concat(
747
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
748
- this.id ? { [this.id]: this } : { ...[this] }
749
- );
750
- }
751
- index(i) {
752
- return this.toList()?.index(i);
704
+ except(item, ClassName = _List) {
705
+ return this.copy(ClassName).delete(item);
753
706
  }
754
- toValueList() {
755
- return new List().concat(
756
- this.value ? { [this.value]: this } : (
757
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
758
- { ...[this] }
759
- )
760
- );
707
+ forEach(iterate) {
708
+ this.entries().forEach(([key, item], index) => {
709
+ iterate(item, key, index);
710
+ });
761
711
  }
762
- standardizeObject(tag, options) {
763
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
764
- const standardize = this.standardizeMedia;
765
- return standardize.call(
766
- this,
767
- options.obje.namespace,
768
- options.obje.override
769
- );
770
- }
712
+ map(iterate) {
713
+ return this.entries().map(([key, item], index) => {
714
+ return iterate(item, key, index);
715
+ });
771
716
  }
772
- toJson(tag, options) {
773
- const json = this.toObject(tag, options);
774
- return JSON.stringify(json);
717
+ reduce(iterate, initialValue) {
718
+ return this.entries().reduce((acc, [key, item], index) => {
719
+ return iterate(acc, item, key, index);
720
+ }, initialValue);
775
721
  }
776
- toObject(tag, options) {
777
- this.standardizeObject(tag, options);
778
- const validKeys = getValidKeys(this);
779
- let json = {};
780
- validKeys.forEach((key) => {
781
- if (key === "id" && this.id !== void 0) {
782
- json.id = this.id;
783
- } else if (key === "_id" && this._id !== void 0) {
784
- json.id = this._id;
785
- } else if (key === "value" && this.value !== void 0) {
786
- json.value = this.value;
787
- } else if (key === "_value" && this._value !== void 0) {
788
- json.value = this._value;
789
- } else {
790
- const validKey = key;
791
- const prop = this.get(validKey);
792
- if (typeof prop?.toObject === "function") {
793
- if (prop instanceof _Common) {
794
- json[validKey] = prop.toObject(validKey, options);
795
- } else {
796
- json = {
797
- ...json,
798
- ...prop.toObject(validKey, options)
799
- };
800
- }
722
+ filter(filters, ClassName = _List) {
723
+ const newList = new ClassName();
724
+ const isIterator = typeof filters === "function";
725
+ this.entries().forEach(([itemId, item], index) => {
726
+ if (isIterator) {
727
+ filters(item, itemId, index) && newList.item(itemId, item);
728
+ return;
729
+ }
730
+ if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
731
+ let itemValue;
732
+ if (key === "id") {
733
+ itemValue = item.toProp("id");
734
+ } else {
735
+ itemValue = item.toProp(key)?.toValue();
801
736
  }
737
+ if (Array.isArray(value)) {
738
+ return value.includes(itemValue);
739
+ }
740
+ return itemValue === value;
741
+ })) {
742
+ newList.item(itemId, item);
802
743
  }
803
744
  });
804
- return json;
745
+ return newList;
805
746
  }
806
- merge(other, override = false, avoidKeys = []) {
807
- if (!other) {
808
- return this;
809
- }
810
- const validKeys = getValidKeys(other);
811
- validKeys.forEach((validKey) => {
812
- if (avoidKeys.includes(validKey)) {
813
- return;
747
+ find(filters, _ClassName = _List) {
748
+ let foundItem;
749
+ const isIterator = typeof filters === "function";
750
+ this.entries().find(([itemId, item], index) => {
751
+ if (isIterator) {
752
+ if (filters(item, itemId, index)) {
753
+ foundItem = item;
754
+ return true;
755
+ }
756
+ return false;
814
757
  }
815
- const current = this.get(validKey);
816
- const mergeValue = other.get(validKey);
817
- if (mergeValue) {
818
- if (current instanceof _Common || current instanceof List) {
819
- this.assign(validKey, mergeValue, true);
820
- } else if (!current || override && validKey !== "id" && validKey !== "_id") {
821
- this.set(validKey, mergeValue);
758
+ if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
759
+ let itemValue;
760
+ if (key === "id") {
761
+ itemValue = item.toProp("id");
762
+ } else {
763
+ itemValue = item.toProp(key)?.toValue();
764
+ }
765
+ if (Array.isArray(value)) {
766
+ return value.includes(itemValue);
822
767
  }
768
+ return itemValue === value;
769
+ })) {
770
+ foundItem = item;
771
+ return true;
823
772
  }
773
+ return false;
824
774
  });
825
- return this;
775
+ return foundItem;
826
776
  }
827
- clone(newId = false, avoidKeys = []) {
828
- const ctor = this.constructor;
829
- const instance = new ctor(
830
- this._gedcom,
831
- this._id,
832
- this._main,
833
- this._parent
834
- );
835
- if (newId && this._id) {
836
- instance.id = `IFT${this._id.replace(/[^0-9]/g, "")}`;
777
+ orderBy(orders, ClassName = _List) {
778
+ const newList = new ClassName();
779
+ const isIterator = typeof orders === "function";
780
+ if (!isIterator && (!orders || !Object.keys(orders).length)) {
781
+ return newList.merge(this);
837
782
  }
838
- const cloned = createProxy(instance);
839
- cloned.merge(this, true, avoidKeys);
840
- return cloned;
841
- }
842
- // fromGedcom<T = typeof this>(value: string) {
843
- fromGedcom(_value) {
844
- }
845
- toGedcom(tag, level = 0, options) {
846
- const gedcom = this.toGedcomLines(tag, level, options);
847
- return gedcom.join("\n");
848
- }
849
- toGedcomLines(tag, level = 0, options) {
850
- this.standardizeObject(tag, options);
851
- const validKeys = getValidKeys(this);
852
- const gedcom = [];
853
- validKeys.forEach((key) => {
854
- const validKey = key;
855
- const prop = this.get(validKey);
856
- if (typeof prop?.toGedcomLines === "function") {
857
- if (prop instanceof _Common) {
858
- const value = prop.exportValue();
859
- gedcom.push(
860
- `${level} ${validKey}${value ? ` ${value}` : ""}`
861
- );
862
- gedcom.push(
863
- ...prop.toGedcomLines(validKey, level + 1, options)
864
- );
865
- } else {
866
- gedcom.push(
867
- ...prop.toGedcomLines(validKey, level, options)
868
- );
783
+ const sortedItems = this.entries().toSorted(
784
+ ([itemAId, itemA], [itemBId, itemB]) => {
785
+ if (isIterator) {
786
+ return orders(itemA, itemAId, itemB, itemBId);
787
+ }
788
+ const [key, { direction, getter }] = Object.entries(
789
+ typeof orders === "string" ? { [orders]: { direction: "ASC" } } : orders
790
+ )[0];
791
+ const rawA = itemA.get(key);
792
+ const rawB = itemB.get(key);
793
+ let valueA = rawA?.toValue();
794
+ let valueB = rawB?.toValue();
795
+ if (getter && typeof getter === "function") {
796
+ valueA = getter(valueA, rawA);
797
+ valueB = getter(valueB, rawB);
798
+ }
799
+ let sortValue2 = 0;
800
+ if (!valueA && valueA !== 0 && !valueB && valueB !== 0) {
801
+ sortValue2 = 0;
802
+ } else if (!valueB && valueB !== 0) {
803
+ sortValue2 = 1;
804
+ } else if (!valueA && valueA !== 0) {
805
+ sortValue2 = -1;
806
+ } else if (valueA < valueB) {
807
+ sortValue2 = -1;
808
+ } else if (valueA > valueB) {
809
+ sortValue2 = 1;
869
810
  }
811
+ return sortValue2 * (direction === "DESC" ? -1 : 1);
870
812
  }
813
+ );
814
+ sortedItems.forEach(([key, value]) => {
815
+ newList.item(key, value);
871
816
  });
872
- return gedcom;
873
- }
874
- isGenoPro() {
875
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
876
- const sour = get(head, "SOUR.value");
877
- return !!sour?.toLowerCase()?.startsWith("genopro");
878
- }
879
- isAhnenblatt() {
880
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
881
- const sour = get(head, "SOUR.value");
882
- return !!sour?.toLowerCase()?.startsWith("ahnenblatt");
883
- }
884
- isGeni() {
885
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
886
- const sour = get(head, "SOUR.value");
887
- return !!sour?.toLowerCase()?.startsWith("geni");
888
- }
889
- isAncestry() {
890
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
891
- const sour = get(head, "SOUR.value");
892
- return !!sour?.toLowerCase()?.startsWith("ancestry");
893
- }
894
- isMyHeritage() {
895
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
896
- const sour = get(head, "SOUR.value");
897
- return !!sour?.toLowerCase()?.startsWith("myheritage");
817
+ return newList;
898
818
  }
899
- /**
900
- * Get the source type as a string (for prefixing tree IDs and names)
901
- * Returns the detected source type or undefined if unknown
902
- */
903
- getSourceType() {
904
- if (this.isAncestry()) return "Ancestry";
905
- if (this.isMyHeritage()) return "MyHeritage";
906
- if (this.isFamilySearch()) return "FamilySearch";
907
- if (this.isGNO2GED()) return "GNO2GED";
908
- if (this.isGenoPro()) return "GenoPro";
909
- if (this.isAhnenblatt()) return "Ahnenblatt";
910
- if (this.isGeni()) return "Geni";
911
- return void 0;
819
+ groupBy(groups, sort, minOccurance = -1, lessOccuranceLabel = "Appearing less than 5 times", ClassName = _List) {
820
+ const groupped = {};
821
+ const isIterator = typeof groups === "function";
822
+ if (!isIterator && (!groups || !Object.keys(groups).length)) {
823
+ groupped.Unknown = new ClassName().merge(this);
824
+ groupped.Unknown.marker = "Unknown";
825
+ return groupped;
826
+ }
827
+ this.forEach((item, itemId) => {
828
+ const group = [];
829
+ if (isIterator) {
830
+ const newGroups = groups(item, itemId);
831
+ group.push(
832
+ ...Array.isArray(newGroups) ? newGroups : [newGroups]
833
+ );
834
+ } else {
835
+ const [key, { getter }] = Object.entries(
836
+ typeof groups === "string" ? { [groups]: {} } : groups
837
+ )[0];
838
+ const raw = item.get(key)?.index(0);
839
+ let value = raw?.toValue();
840
+ if (getter && typeof getter === "function") {
841
+ value = getter(value, raw);
842
+ }
843
+ if (value) {
844
+ group.push(value);
845
+ }
846
+ }
847
+ if (!group.length) {
848
+ group.push("Unknown");
849
+ }
850
+ group.forEach((g) => {
851
+ const newGroup = typeof g === "string" ? { group: g } : g;
852
+ if (!groupped[newGroup.group]) {
853
+ groupped[newGroup.group] = new ClassName();
854
+ groupped[newGroup.group].marker = newGroup.marker ?? newGroup.group;
855
+ }
856
+ groupped[newGroup.group]?.append(item);
857
+ });
858
+ });
859
+ if (sort) {
860
+ return Object.entries(groupped).toSorted(([ak, a], [bk, b]) => {
861
+ if (typeof sort === "function") {
862
+ return sort(a, ak, b, bk);
863
+ }
864
+ if (ak === "Unknown") {
865
+ return 1;
866
+ }
867
+ if (bk === "Unknown") {
868
+ return -1;
869
+ }
870
+ if (sort === "group") {
871
+ return ak.localeCompare(bk);
872
+ }
873
+ return (b?.length ?? 0) - (a?.length ?? 0);
874
+ }).reduce((acc, [key, value]) => {
875
+ if (minOccurance > -1 && (value?.length ?? 0) < minOccurance) {
876
+ const otherKey = lessOccuranceLabel;
877
+ acc[otherKey] = acc[otherKey] && value ? acc[otherKey].merge(value) : value;
878
+ } else {
879
+ acc[key] = value;
880
+ }
881
+ return acc;
882
+ }, {});
883
+ }
884
+ return groupped;
912
885
  }
913
- isFamilySearch() {
914
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
915
- const sourName = get(head, "SOUR.NAME.value");
916
- return sourName === "FamilySearch API";
886
+ findIndex(item) {
887
+ if (!item?.id) {
888
+ return -1;
889
+ }
890
+ return this.keys().findIndex((key) => key === item.id);
917
891
  }
918
- isGNO2GED() {
919
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
920
- const sour = get(head, "SOUR.value");
921
- return sour === "GNO2GED";
892
+ getItems() {
893
+ return this.items;
922
894
  }
923
- getAncestryTreeId() {
924
- const path = "HEAD.SOUR._TREE.RIN.value";
925
- return get(this, path) || get(this.getGedcom(), path);
895
+ toJson(tag, options) {
896
+ const json = this.toObject(tag, options);
897
+ return JSON.stringify(json);
926
898
  }
927
- getMyHeritageTreeId() {
928
- const path = "HEAD._EXPORTED_FROM_SITE_ID.value";
929
- return get(this, path) || get(this.getGedcom(), path);
899
+ toObject(tag, options) {
900
+ const json = {};
901
+ this.entries().forEach(([_, item]) => {
902
+ const validTag = getValidTag(tag);
903
+ if (json[validTag]) {
904
+ if (!Array.isArray(json[validTag])) {
905
+ json[validTag] = [json[validTag]];
906
+ }
907
+ json[validTag].push(
908
+ item.toObject(validTag, options)
909
+ );
910
+ } else {
911
+ json[validTag] = item.toObject(
912
+ validTag,
913
+ options
914
+ );
915
+ }
916
+ });
917
+ return json;
930
918
  }
931
- getTreeId() {
932
- if (this?.isAncestry()) {
933
- const id = this.getAncestryTreeId();
934
- if (id !== void 0) return id;
935
- }
936
- if (this?.isMyHeritage()) {
937
- const id = this.getMyHeritageTreeId();
938
- if (id !== void 0) return id;
939
- }
940
- if (this?.isFamilySearch()) {
941
- const id = this.getFamilySearchTreeId();
942
- if (id !== void 0) return id;
943
- }
944
- if (this?.isGNO2GED()) {
945
- const id = this.getGNO2GEDTreeId();
946
- if (id !== void 0) return id;
947
- }
948
- if (this?.isAhnenblatt()) {
949
- const id = this.getAhnenblattTreeId();
950
- if (id !== void 0) return id;
951
- }
952
- if (this?.isGeni()) {
953
- const id = this.getGeniTreeId();
954
- if (id !== void 0) return id;
955
- }
956
- if (this?.isGenoPro()) {
957
- const id = this.getGenoProTreeId();
958
- if (id !== void 0) return id;
959
- }
960
- return this.getUniversalTreeId();
919
+ toGedcom(tag, level = 0, options) {
920
+ const gedcom = this.toGedcomLines(tag, level, options);
921
+ return gedcom.join("\n");
961
922
  }
962
- getAncestryTreeName() {
963
- const path = "HEAD.SOUR._TREE.value";
964
- return get(this, path) || get(this.getGedcom(), path);
923
+ toGedcomLines(tag, level = 0, options) {
924
+ const gedcom = [];
925
+ this.entries().forEach(([_, item]) => {
926
+ const validTag = getValidTag(tag);
927
+ const xref = item.id;
928
+ const payload = item.exportValue?.();
929
+ if (xref) {
930
+ gedcom.push(
931
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
932
+ );
933
+ } else {
934
+ gedcom.push(
935
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
936
+ );
937
+ }
938
+ gedcom.push(
939
+ ...item.toGedcomLines(
940
+ validTag,
941
+ level + 1,
942
+ options
943
+ )
944
+ );
945
+ });
946
+ return gedcom;
965
947
  }
966
- getMyHeritageTreeName() {
967
- const path = "HEAD.FILE.value";
968
- const treeDetails = get(this, path) || get(this.getGedcom(), path);
969
- return treeDetails?.match(
970
- /Exported by MyHeritage.com from (?<tree>.+) in.+$/
971
- )?.groups?.tree;
948
+ toValue() {
949
+ const propList = new _List();
950
+ this.entries().forEach(([key, item]) => {
951
+ propList.item(key, item?.toValue());
952
+ });
953
+ return propList;
972
954
  }
973
- getFamilySearchTreeId() {
974
- const rin = get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
975
- if (rin) {
976
- return rin;
977
- }
978
- return "familysearch";
955
+ // @deprecated use get instead
956
+ toProp(tag) {
957
+ return this.get(tag);
979
958
  }
980
- getFamilySearchTreeName() {
981
- const treeName = get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
982
- if (treeName) {
983
- return treeName;
959
+ toList() {
960
+ return new _List().concat(this.items);
961
+ }
962
+ toValueList() {
963
+ const newList = new _List();
964
+ this.values().forEach((value) => {
965
+ value.value && newList.item(value.value, value);
966
+ });
967
+ return newList;
968
+ }
969
+ };
970
+
971
+ // src/classes/common.ts
972
+ var Common = class _Common {
973
+ constructor(gedcom, id, main, parent) {
974
+ this.isListable = true;
975
+ this._gedcom = gedcom;
976
+ this._main = main;
977
+ this._parent = parent;
978
+ this.initUniqueId();
979
+ if (id) {
980
+ this._id = id;
981
+ } else {
982
+ delete this._id;
984
983
  }
985
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
986
- return fileName || "FamilySearch Import";
987
984
  }
988
- getAhnenblattTreeId() {
989
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
990
- if (fileName) {
991
- const idMatch = fileName.match(/_(\d+)/);
992
- if (idMatch) {
993
- return idMatch[1];
994
- }
985
+ set type(type) {
986
+ if (type) {
987
+ this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
995
988
  }
996
- return void 0;
989
+ this._type = type;
997
990
  }
998
- getAhnenblattTreeName() {
999
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1000
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
991
+ get type() {
992
+ return this._type;
1001
993
  }
1002
- getGeniTreeId() {
1003
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1004
- if (fileName) {
1005
- const idMatch = fileName.match(/_(\d+)/);
1006
- if (idMatch) {
1007
- return idMatch[1];
1008
- }
994
+ initUniqueId() {
995
+ if (this._gedcom) {
996
+ this.uniqueId = `~Common:${this._gedcom.refcount++}`;
1009
997
  }
1010
- return void 0;
1011
- }
1012
- getGeniTreeName() {
1013
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1014
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1015
998
  }
1016
- getGenoProTreeId() {
1017
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1018
- if (fileName) {
1019
- const idMatch = fileName.match(/_(\d+)/);
1020
- if (idMatch) {
1021
- return idMatch[1];
999
+ set uniqueId(id) {
1000
+ const currentId = this._uniqueId;
1001
+ if (this._gedcom?.reflist && id) {
1002
+ this._uniqueId = id;
1003
+ if (currentId && this._gedcom.reflist[currentId]) {
1004
+ delete this._gedcom.reflist[currentId];
1022
1005
  }
1006
+ this._gedcom.reflist[id] = this;
1023
1007
  }
1024
- return void 0;
1025
1008
  }
1026
- getGenoProTreeName() {
1027
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1028
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1009
+ get uniqueId() {
1010
+ return this._uniqueId;
1029
1011
  }
1030
- getGNO2GEDTreeId() {
1031
- const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
1032
- if (rin) {
1033
- return rin;
1034
- }
1035
- return `gno_${this._gedcom?.refcount || "unknown"}`;
1012
+ set id(id) {
1013
+ this._id = idGetter(id);
1036
1014
  }
1037
- getGNO2GEDTreeName() {
1038
- const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
1039
- if (treeName) {
1040
- return treeName;
1041
- }
1042
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1043
- return fileName || "GNO2GED Export";
1015
+ get id() {
1016
+ return idGetter(this._id);
1044
1017
  }
1045
- /**
1046
- * Universal tree ID getter for unknown/unrecognized GEDCOM sources
1047
- * Tries to extract an ID from various common locations
1048
- */
1049
- getUniversalTreeId() {
1050
- const sourceType = this.getSourceType();
1051
- const prefix = sourceType ? sourceType.toLowerCase() : "tree";
1052
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1053
- if (fileName) {
1054
- const idMatch = fileName.match(/_(\d+)/);
1055
- if (idMatch) {
1056
- return `${prefix}_${idMatch[1]}`;
1057
- }
1058
- }
1059
- return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
1018
+ set value(value) {
1019
+ this._value = value;
1020
+ }
1021
+ get value() {
1022
+ return this._value;
1060
1023
  }
1061
1024
  /**
1062
- * Universal tree name getter for unknown/unrecognized GEDCOM sources
1063
- * Tries to extract a name from various common locations
1025
+ * Set the gedcom reference for this object
1026
+ * @param gedcom - The GedComType to set as the gedcom reference
1064
1027
  */
1065
- getUniversalTreeName() {
1066
- const sourceType = this.getSourceType();
1067
- const prefix = sourceType ? `${sourceType}-` : "";
1068
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1069
- if (fileName) {
1070
- const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1071
- return `${prefix}${cleanName}`;
1072
- }
1073
- const sourName = get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1074
- if (sourName) {
1075
- return `${prefix}${sourName}`;
1076
- }
1077
- const sourValue = get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1078
- if (sourValue) {
1079
- return `${prefix}${sourValue}`;
1080
- }
1081
- return `${prefix}Unknown Tree`;
1082
- }
1083
- getTreeName() {
1084
- if (this?.isAncestry()) {
1085
- const name = this.getAncestryTreeName();
1086
- if (name) return name;
1087
- }
1088
- if (this?.isMyHeritage()) {
1089
- const name = this.getMyHeritageTreeName();
1090
- if (name) return name;
1091
- }
1092
- if (this?.isFamilySearch()) {
1093
- const name = this.getFamilySearchTreeName();
1094
- if (name) return name;
1095
- }
1096
- if (this?.isGNO2GED()) {
1097
- const name = this.getGNO2GEDTreeName();
1098
- if (name) return name;
1099
- }
1100
- if (this?.isAhnenblatt()) {
1101
- const name = this.getAhnenblattTreeName();
1102
- if (name) return name;
1103
- }
1104
- if (this?.isGeni()) {
1105
- const name = this.getGeniTreeName();
1106
- if (name) return name;
1107
- }
1108
- if (this?.isGenoPro()) {
1109
- const name = this.getGenoProTreeName();
1110
- if (name) return name;
1111
- }
1112
- return this.getUniversalTreeName();
1113
- }
1114
- };
1115
- var createProxy = (target) => {
1116
- return new Proxy(target, {
1117
- get(t, prop, receiver) {
1118
- if (prop === "unwrapped") {
1119
- return target;
1120
- }
1121
- if (prop in t) {
1122
- return Reflect.get(t, prop, receiver);
1123
- }
1124
- if (!isOnlyMainProp(prop)) {
1125
- const ref = t.ref;
1126
- if (ref && prop in ref) {
1127
- return Reflect.get(ref, prop, receiver);
1128
- }
1129
- }
1130
- }
1131
- });
1132
- };
1133
- var createCommon = (gedcom, id, main, parent) => {
1134
- return createProxy(
1135
- new Common(gedcom, id, main, parent)
1136
- );
1137
- };
1138
- var isOnlyMainProp = (key) => {
1139
- return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
1140
- };
1141
- var isValidKey = (common, key) => {
1142
- const prop = get(common, key);
1143
- return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
1144
- };
1145
- var getValidKeys = (common) => {
1146
- return Object.keys(common).filter((key) => {
1147
- return isValidKey(common, key);
1148
- });
1149
- };
1150
- var getValidTag = (tag) => {
1151
- return tag.replace(/^@@/, "");
1152
- };
1153
- var getListTag = (tag) => {
1154
- return `@@${tag}`;
1155
- };
1156
- var isId = (string) => {
1157
- return ID_REG.test(string);
1158
- };
1159
- var idGetter = (id) => {
1160
- if (id === void 0) {
1161
- return void 0;
1162
- }
1163
- let newId = `${id}`;
1164
- const parts = newId?.match(ID_GETTER_REG)?.groups;
1165
- if (!parts?.at && !parts?.letter) {
1166
- newId = `@U${newId}`;
1167
- } else if (!parts.at) {
1168
- newId = `@${newId}`;
1169
- } else if (!parts.letter) {
1170
- newId = newId.replace(/^@/, "@U");
1028
+ setGedcom(gedcom) {
1029
+ this._gedcom = gedcom;
1171
1030
  }
1172
- if (!newId.endsWith("@")) {
1173
- newId = `${newId}@`;
1031
+ // avoid to override
1032
+ get originalValue() {
1033
+ return this._value;
1174
1034
  }
1175
- return newId;
1176
- };
1177
-
1178
- // src/classes/list.ts
1179
- var List = class _List {
1180
- // length = 0;
1181
- constructor(items) {
1182
- this.items = {};
1183
- this.isListable = true;
1184
- this.marker = void 0;
1185
- if (items) {
1186
- this.concat(items);
1035
+ get ref() {
1036
+ if (typeof this.value === "string" && isId(this.value) && this.refType) {
1037
+ return this._gedcom?.[getListTag(this.refType)]?.item(
1038
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1039
+ this.value
1040
+ );
1187
1041
  }
1042
+ return this;
1188
1043
  }
1189
- get length() {
1190
- return this.values().length;
1191
- }
1192
- has(indi) {
1193
- return !!this.item(
1194
- typeof indi === "string" ? indi : indi?.id ?? ""
1195
- );
1044
+ get main() {
1045
+ return this._main;
1196
1046
  }
1197
- get(name) {
1198
- const propList = new _List();
1199
- this.entries().forEach(([key, item]) => {
1200
- propList.item(key, item.get(name));
1201
- });
1202
- return propList;
1047
+ get parent() {
1048
+ return this._parent;
1203
1049
  }
1204
- getIf(name, condition, name2) {
1205
- const propList = new _List();
1206
- this.entries().forEach(([key, item]) => {
1207
- const passedItem = item.getIf(name, condition, name2);
1208
- if (passedItem) {
1209
- propList.item(key, passedItem);
1050
+ addRef(refNode) {
1051
+ if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
1052
+ if (!this._refs) {
1053
+ this._refs = new List();
1210
1054
  }
1211
- });
1212
- return propList;
1213
- }
1214
- keys() {
1215
- return Object.keys(this.items);
1216
- }
1217
- values() {
1218
- return Object.values(this.items);
1219
- }
1220
- entries() {
1221
- return Object.entries(this.items);
1222
- }
1223
- setLength(_type) {
1055
+ this._refs.item(refNode.value, refNode);
1056
+ }
1224
1057
  }
1225
- first() {
1226
- return this.index(0);
1058
+ getRefs() {
1059
+ return this._refs;
1227
1060
  }
1228
- last() {
1229
- return this.index(this.keys().length - 1);
1061
+ exportValue() {
1062
+ return this.value;
1230
1063
  }
1231
- index(index) {
1232
- const keys = this.keys();
1233
- const key = keys[index];
1234
- return key && this.items[key];
1064
+ removeValue() {
1065
+ delete this._value;
1235
1066
  }
1236
- item(name, value) {
1237
- if (arguments.length > 1) {
1238
- set(this.items, name, value);
1239
- this.setLength();
1067
+ set(name, value) {
1068
+ if (isGedcomTrailerTag(name)) {
1069
+ return void 0;
1240
1070
  }
1241
- return get(this.items, name);
1242
- }
1243
- removeItem(name) {
1244
- unset(this.items, name);
1245
- this.setLength();
1246
- }
1247
- exclude(excludedList) {
1248
- excludedList.keys().forEach((itemId) => {
1249
- delete this.items[itemId];
1250
- });
1251
- this.setLength();
1252
- return this;
1253
- }
1254
- concat(items) {
1255
- if (Array.isArray(items)) {
1256
- items.forEach((item) => {
1257
- this.append(item);
1258
- });
1071
+ if (typeof value === "string") {
1072
+ const usedValue = createCommon(this._gedcom, void 0, this.main);
1073
+ usedValue.value = value;
1074
+ set(this, name, usedValue);
1259
1075
  } else {
1260
- Object.assign(this.items, items ?? {});
1076
+ set(this, name, value);
1261
1077
  }
1262
- this.setLength();
1263
- return this;
1078
+ return get(this, name);
1264
1079
  }
1265
- merge(mergedList) {
1266
- this.concat(mergedList?.items);
1267
- return this;
1268
- }
1269
- intersection(mergedList) {
1270
- const inter = intersectionBy(
1271
- this.values(),
1272
- mergedList?.values() ?? [],
1273
- "id"
1274
- );
1275
- return new _List(inter);
1276
- }
1277
- difference(mergedList) {
1278
- const inter = differenceBy(
1279
- this.values(),
1280
- mergedList?.values() ?? [],
1281
- "id"
1282
- );
1283
- return new _List(inter);
1284
- }
1285
- prepend(item) {
1286
- if (item.id) {
1287
- let increase = true;
1288
- if (this.items[item.id]) {
1289
- delete this.items[item.id];
1290
- increase = false;
1291
- }
1292
- this.items = Object.assign(
1293
- {
1294
- [item.id]: item
1295
- },
1296
- this.items
1297
- );
1298
- if (increase) {
1299
- this.setLength("increase");
1080
+ assign(name, value, unique = false) {
1081
+ if (isGedcomTrailerTag(name)) {
1082
+ return void 0;
1083
+ }
1084
+ let curValue = this.get(name);
1085
+ if (curValue?.isListable) {
1086
+ if (curValue instanceof _Common) {
1087
+ curValue = new List().concat({ ...[curValue] });
1088
+ this.set(name, curValue);
1300
1089
  }
1090
+ const values = [
1091
+ ...curValue.values(),
1092
+ ...value instanceof List ? value.values() : [value]
1093
+ ];
1094
+ curValue.concat({
1095
+ ...!unique ? values : uniqBy(
1096
+ values,
1097
+ (value2) => value2 instanceof _Common ? value2?.value : void 0
1098
+ )
1099
+ });
1100
+ } else {
1101
+ this.set(name, value);
1301
1102
  }
1302
- return this;
1103
+ return get(this, name);
1303
1104
  }
1304
- append(item) {
1305
- if (item.id) {
1306
- let increase = true;
1307
- if (this.items[item.id]) {
1308
- delete this.items[item.id];
1309
- increase = false;
1105
+ get(name) {
1106
+ if (!name.includes(".")) {
1107
+ return get(this, name);
1108
+ }
1109
+ const nameParts = name.split(".");
1110
+ let last = this;
1111
+ for (const part of nameParts) {
1112
+ const indexPart = Number(part);
1113
+ const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
1114
+ const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
1115
+ const usedPart = itemId || itemIndex;
1116
+ if (last instanceof List) {
1117
+ if (!isNaN(indexPart)) {
1118
+ last = last.index(indexPart);
1119
+ } else {
1120
+ if (usedPart) {
1121
+ last = last.item(usedPart);
1122
+ } else {
1123
+ last = last?.index(0)?.get(part);
1124
+ }
1125
+ }
1126
+ } else {
1127
+ const usedIndex = Number(itemIndex ?? indexPart);
1128
+ if (!isNaN(usedIndex)) {
1129
+ last = last?.toList().index(usedIndex);
1130
+ } else {
1131
+ last = last?.get(itemId ?? part);
1132
+ }
1310
1133
  }
1311
- this.items[item.id] = item;
1312
- if (increase) {
1313
- this.setLength("increase");
1134
+ if (!last) {
1135
+ break;
1314
1136
  }
1315
1137
  }
1316
- return this;
1138
+ return last;
1317
1139
  }
1318
- delete(item) {
1319
- if (item.id && this.items[item.id]) {
1320
- delete this.items[item.id];
1321
- this.setLength("decrease");
1140
+ remove(name) {
1141
+ unset(this, name);
1142
+ }
1143
+ getGedcom() {
1144
+ return this._gedcom;
1145
+ }
1146
+ getIf(name, condition, name2) {
1147
+ const nameParts = name.split(".");
1148
+ const name2Parts = name2.split(".");
1149
+ if (nameParts.length !== name2Parts.length) {
1150
+ throw new Error("Tag pathes must be of the same depth");
1322
1151
  }
1323
- return this;
1152
+ if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
1153
+ throw new Error("Final tags must be siblings");
1154
+ }
1155
+ const valueCommon = this.get(name);
1156
+ if (valueCommon instanceof _Common) {
1157
+ return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
1158
+ }
1159
+ const valueCommon2 = this.get(name2)?.toList();
1160
+ const ifList = new List();
1161
+ valueCommon?.forEach((item, key) => {
1162
+ const pair = valueCommon2?.item(key);
1163
+ ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
1164
+ });
1165
+ return ifList;
1324
1166
  }
1325
- copy(ClassName = _List) {
1326
- const newList = new ClassName();
1327
- Object.assign(newList.items, this.items);
1328
- newList.setLength();
1329
- return newList;
1167
+ toString() {
1168
+ return this.value?.toString() || "";
1330
1169
  }
1331
- except(item, ClassName = _List) {
1332
- return this.copy(ClassName).delete(item);
1170
+ toValue() {
1171
+ return this.value;
1333
1172
  }
1334
- forEach(iterate) {
1335
- this.entries().forEach(([key, item], index) => {
1336
- iterate(item, key, index);
1337
- });
1173
+ toProp(tag) {
1174
+ const prop = this.get(tag);
1175
+ return prop;
1338
1176
  }
1339
- map(iterate) {
1340
- return this.entries().map(([key, item], index) => {
1341
- return iterate(item, key, index);
1342
- });
1177
+ toList() {
1178
+ return new List().concat(
1179
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1180
+ this.id ? { [this.id]: this } : { ...[this] }
1181
+ );
1343
1182
  }
1344
- reduce(iterate, initialValue) {
1345
- return this.entries().reduce((acc, [key, item], index) => {
1346
- return iterate(acc, item, key, index);
1347
- }, initialValue);
1183
+ index(i) {
1184
+ return this.toList()?.index(i);
1348
1185
  }
1349
- filter(filters, ClassName = _List) {
1350
- const newList = new ClassName();
1351
- const isIterator = typeof filters === "function";
1352
- this.entries().forEach(([itemId, item], index) => {
1353
- if (isIterator) {
1354
- filters(item, itemId, index) && newList.item(itemId, item);
1355
- return;
1356
- }
1357
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1358
- let itemValue;
1359
- if (key === "id") {
1360
- itemValue = item.toProp("id");
1361
- } else {
1362
- itemValue = item.toProp(key)?.toValue();
1363
- }
1364
- if (Array.isArray(value)) {
1365
- return value.includes(itemValue);
1186
+ toValueList() {
1187
+ return new List().concat(
1188
+ this.value ? { [this.value]: this } : (
1189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1190
+ { ...[this] }
1191
+ )
1192
+ );
1193
+ }
1194
+ standardizeObject(tag, options) {
1195
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
1196
+ const standardize = this.standardizeMedia;
1197
+ return standardize.call(
1198
+ this,
1199
+ options.obje.namespace,
1200
+ options.obje.override,
1201
+ void 0,
1202
+ options.gedcomVersion
1203
+ );
1204
+ }
1205
+ }
1206
+ toJson(tag, options) {
1207
+ const json = this.toObject(tag, options);
1208
+ return JSON.stringify(json);
1209
+ }
1210
+ toObject(tag, options) {
1211
+ this.standardizeObject(tag, options);
1212
+ const validKeys = getValidKeys(this);
1213
+ let json = {};
1214
+ validKeys.forEach((key) => {
1215
+ if (key === "id" && this.id !== void 0) {
1216
+ json.id = this.id;
1217
+ } else if (key === "_id" && this._id !== void 0) {
1218
+ json.id = this._id;
1219
+ } else if (key === "value" && this.value !== void 0) {
1220
+ json.value = this.value;
1221
+ } else if (key === "_value" && this._value !== void 0) {
1222
+ json.value = this._value;
1223
+ } else {
1224
+ const validKey = key;
1225
+ const prop = this.get(validKey);
1226
+ if (typeof prop?.toObject === "function") {
1227
+ if (prop instanceof _Common) {
1228
+ json[validKey] = prop.toObject(validKey, options);
1229
+ } else if (prop instanceof List) {
1230
+ json = {
1231
+ ...json,
1232
+ ...prop.toObject(validKey, options)
1233
+ };
1234
+ }
1366
1235
  }
1367
- return itemValue === value;
1368
- })) {
1369
- newList.item(itemId, item);
1370
1236
  }
1371
1237
  });
1372
- return newList;
1238
+ return json;
1373
1239
  }
1374
- find(filters, _ClassName = _List) {
1375
- let foundItem;
1376
- const isIterator = typeof filters === "function";
1377
- this.entries().find(([itemId, item], index) => {
1378
- if (isIterator) {
1379
- if (filters(item, itemId, index)) {
1380
- foundItem = item;
1381
- return true;
1382
- }
1383
- return false;
1240
+ merge(other, override = false, avoidKeys = []) {
1241
+ if (!other) {
1242
+ return this;
1243
+ }
1244
+ const validKeys = getValidKeys(other);
1245
+ validKeys.forEach((validKey) => {
1246
+ if (avoidKeys.includes(validKey)) {
1247
+ return;
1384
1248
  }
1385
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1386
- let itemValue;
1387
- if (key === "id") {
1388
- itemValue = item.toProp("id");
1389
- } else {
1390
- itemValue = item.toProp(key)?.toValue();
1249
+ const current = this.get(validKey);
1250
+ const mergeValue = other.get(validKey);
1251
+ if (mergeValue) {
1252
+ if (current instanceof _Common || current instanceof List) {
1253
+ this.assign(validKey, mergeValue, true);
1254
+ } else if (!current || override && validKey !== "id" && validKey !== "_id") {
1255
+ this.set(validKey, mergeValue);
1391
1256
  }
1392
- if (Array.isArray(value)) {
1393
- return value.includes(itemValue);
1394
- }
1395
- return itemValue === value;
1396
- })) {
1397
- foundItem = item;
1398
- return true;
1399
1257
  }
1400
- return false;
1401
1258
  });
1402
- return foundItem;
1259
+ return this;
1403
1260
  }
1404
- orderBy(orders, ClassName = _List) {
1405
- const newList = new ClassName();
1406
- const isIterator = typeof orders === "function";
1407
- if (!isIterator && (!orders || !Object.keys(orders).length)) {
1408
- return newList.merge(this);
1261
+ clone(newId = false, avoidKeys = []) {
1262
+ const ctor = this.constructor;
1263
+ const instance = new ctor(
1264
+ this._gedcom,
1265
+ this._id,
1266
+ this._main,
1267
+ this._parent
1268
+ );
1269
+ if (newId && this._id) {
1270
+ instance.id = `IFT${this._id.replace(/[^0-9]/g, "")}`;
1409
1271
  }
1410
- const sortedItems = this.entries().toSorted(
1411
- ([itemAId, itemA], [itemBId, itemB]) => {
1412
- if (isIterator) {
1413
- return orders(itemA, itemAId, itemB, itemBId);
1414
- }
1415
- const [key, { direction, getter }] = Object.entries(
1416
- typeof orders === "string" ? { [orders]: { direction: "ASC" } } : orders
1417
- )[0];
1418
- const rawA = itemA.get(key);
1419
- const rawB = itemB.get(key);
1420
- let valueA = rawA?.toValue();
1421
- let valueB = rawB?.toValue();
1422
- if (getter && typeof getter === "function") {
1423
- valueA = getter(valueA, rawA);
1424
- valueB = getter(valueB, rawB);
1425
- }
1426
- let sortValue2 = 0;
1427
- if (!valueA && valueA !== 0 && !valueB && valueB !== 0) {
1428
- sortValue2 = 0;
1429
- } else if (!valueB && valueB !== 0) {
1430
- sortValue2 = 1;
1431
- } else if (!valueA && valueA !== 0) {
1432
- sortValue2 = -1;
1433
- } else if (valueA < valueB) {
1434
- sortValue2 = -1;
1435
- } else if (valueA > valueB) {
1436
- sortValue2 = 1;
1272
+ const cloned = createProxy(instance);
1273
+ cloned.merge(this, true, avoidKeys);
1274
+ return cloned;
1275
+ }
1276
+ // fromGedcom<T = typeof this>(value: string) {
1277
+ fromGedcom(_value) {
1278
+ }
1279
+ toGedcom(tag, level = 0, options) {
1280
+ const gedcom = this.toGedcomLines(tag, level, options);
1281
+ return gedcom.join("\n");
1282
+ }
1283
+ toGedcomLines(tag, level = 0, options) {
1284
+ this.standardizeObject(tag, options);
1285
+ const validKeys = getValidKeys(this);
1286
+ const gedcom = [];
1287
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
1288
+ validKeys.forEach((key) => {
1289
+ const validKey = key;
1290
+ if (isGedcom7Export && validKey === "CONC") {
1291
+ return;
1292
+ }
1293
+ const prop = this.get(validKey);
1294
+ if (typeof prop?.toGedcomLines === "function") {
1295
+ if (prop instanceof _Common) {
1296
+ const value = prop.exportValue();
1297
+ gedcom.push(
1298
+ `${level} ${validKey}${value ? ` ${value}` : ""}`
1299
+ );
1300
+ gedcom.push(
1301
+ ...prop.toGedcomLines(validKey, level + 1, options)
1302
+ );
1303
+ } else if (prop instanceof List) {
1304
+ gedcom.push(
1305
+ ...prop.toGedcomLines(validKey, level, options)
1306
+ );
1437
1307
  }
1438
- return sortValue2 * (direction === "DESC" ? -1 : 1);
1439
1308
  }
1440
- );
1441
- sortedItems.forEach(([key, value]) => {
1442
- newList.item(key, value);
1443
1309
  });
1444
- return newList;
1310
+ return gedcom;
1445
1311
  }
1446
- groupBy(groups, sort, minOccurance = -1, lessOccuranceLabel = "Appearing less than 5 times", ClassName = _List) {
1447
- const groupped = {};
1448
- const isIterator = typeof groups === "function";
1449
- if (!isIterator && (!groups || !Object.keys(groups).length)) {
1450
- groupped.Unknown = new ClassName().merge(this);
1451
- groupped.Unknown.marker = "Unknown";
1452
- return groupped;
1312
+ getHeadRecord() {
1313
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1314
+ }
1315
+ getOriginalHeadRecord() {
1316
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
1317
+ }
1318
+ getSourceHeads() {
1319
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
1320
+ return heads.filter((head, index) => {
1321
+ return Boolean(head && heads.indexOf(head) === index);
1322
+ });
1323
+ }
1324
+ getFromSourceHeads(path) {
1325
+ for (const head of this.getSourceHeads()) {
1326
+ const value = get(head, path);
1327
+ if (value !== void 0 && value !== null && value !== "") {
1328
+ return value;
1329
+ }
1453
1330
  }
1454
- this.forEach((item, itemId) => {
1455
- const group = [];
1456
- if (isIterator) {
1457
- const newGroups = groups(item, itemId);
1458
- group.push(
1459
- ...Array.isArray(newGroups) ? newGroups : [newGroups]
1460
- );
1461
- } else {
1462
- const [key, { getter }] = Object.entries(
1463
- typeof groups === "string" ? { [groups]: {} } : groups
1464
- )[0];
1465
- const raw = item.get(key)?.index(0);
1466
- let value = raw?.toValue();
1467
- if (getter && typeof getter === "function") {
1468
- value = getter(value, raw);
1469
- }
1470
- if (value) {
1471
- group.push(value);
1472
- }
1331
+ return void 0;
1332
+ }
1333
+ sourStartsWith(prefix) {
1334
+ return this.getSourceHeads().some((head) => {
1335
+ const sour = get(head, "SOUR.value");
1336
+ return !!sour?.toLowerCase()?.startsWith(prefix);
1337
+ });
1338
+ }
1339
+ isGenoPro() {
1340
+ return this.sourStartsWith("genopro");
1341
+ }
1342
+ isAhnenblatt() {
1343
+ return this.sourStartsWith("ahnenblatt");
1344
+ }
1345
+ isGeni() {
1346
+ return this.sourStartsWith("geni");
1347
+ }
1348
+ isAncestry() {
1349
+ return this.sourStartsWith("ancestry");
1350
+ }
1351
+ isMyHeritage() {
1352
+ return this.sourStartsWith("myheritage");
1353
+ }
1354
+ /**
1355
+ * Get the source type as a string (for prefixing tree IDs and names)
1356
+ * Returns the detected source type or undefined if unknown
1357
+ */
1358
+ getSourceType() {
1359
+ if (this.isAncestry()) return "Ancestry";
1360
+ if (this.isMyHeritage()) return "MyHeritage";
1361
+ if (this.isFamilySearch()) return "FamilySearch";
1362
+ if (this.isGNO2GED()) return "GNO2GED";
1363
+ if (this.isGenoPro()) return "GenoPro";
1364
+ if (this.isAhnenblatt()) return "Ahnenblatt";
1365
+ if (this.isGeni()) return "Geni";
1366
+ return void 0;
1367
+ }
1368
+ isFamilySearch() {
1369
+ return this.getSourceHeads().some((head) => {
1370
+ const sourName = get(head, "SOUR.NAME.value");
1371
+ return sourName === "FamilySearch API";
1372
+ });
1373
+ }
1374
+ isGNO2GED() {
1375
+ return this.getSourceHeads().some((head) => {
1376
+ const sour = get(head, "SOUR.value");
1377
+ return sour === "GNO2GED";
1378
+ });
1379
+ }
1380
+ getAncestryTreeId() {
1381
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
1382
+ }
1383
+ getMyHeritageTreeId() {
1384
+ return this.getFromSourceHeads("_EXPORTED_FROM_SITE_ID.value") || (get(this, "HEAD._EXPORTED_FROM_SITE_ID.value") || get(this.getGedcom(), "HEAD._EXPORTED_FROM_SITE_ID.value"));
1385
+ }
1386
+ getTreeId() {
1387
+ if (this?.isAncestry()) {
1388
+ const id = this.getAncestryTreeId();
1389
+ if (id !== void 0) return id;
1390
+ }
1391
+ if (this?.isMyHeritage()) {
1392
+ const id = this.getMyHeritageTreeId();
1393
+ if (id !== void 0) return id;
1394
+ }
1395
+ if (this?.isFamilySearch()) {
1396
+ const id = this.getFamilySearchTreeId();
1397
+ if (id !== void 0) return id;
1398
+ }
1399
+ if (this?.isGNO2GED()) {
1400
+ const id = this.getGNO2GEDTreeId();
1401
+ if (id !== void 0) return id;
1402
+ }
1403
+ if (this?.isAhnenblatt()) {
1404
+ const id = this.getAhnenblattTreeId();
1405
+ if (id !== void 0) return id;
1406
+ }
1407
+ if (this?.isGeni()) {
1408
+ const id = this.getGeniTreeId();
1409
+ if (id !== void 0) return id;
1410
+ }
1411
+ if (this?.isGenoPro()) {
1412
+ const id = this.getGenoProTreeId();
1413
+ if (id !== void 0) return id;
1414
+ }
1415
+ return this.getUniversalTreeId();
1416
+ }
1417
+ getAncestryTreeName() {
1418
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
1419
+ }
1420
+ getMyHeritageTreeName() {
1421
+ const path = "HEAD.FILE.value";
1422
+ const treeDetails = get(this, path) || get(this.getGedcom(), path);
1423
+ return treeDetails?.match(
1424
+ /Exported by MyHeritage.com from (?<tree>.+) in.+$/
1425
+ )?.groups?.tree;
1426
+ }
1427
+ getFamilySearchTreeId() {
1428
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1429
+ if (rin) {
1430
+ return rin;
1431
+ }
1432
+ return "familysearch";
1433
+ }
1434
+ getFamilySearchTreeName() {
1435
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1436
+ if (treeName) {
1437
+ return treeName;
1438
+ }
1439
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1440
+ return fileName || "FamilySearch Import";
1441
+ }
1442
+ getAhnenblattTreeId() {
1443
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1444
+ if (fileName) {
1445
+ const idMatch = fileName.match(/_(\d+)/);
1446
+ if (idMatch) {
1447
+ return idMatch[1];
1473
1448
  }
1474
- if (!group.length) {
1475
- group.push("Unknown");
1449
+ }
1450
+ return void 0;
1451
+ }
1452
+ getAhnenblattTreeName() {
1453
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1454
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1455
+ }
1456
+ getGeniTreeId() {
1457
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1458
+ if (fileName) {
1459
+ const idMatch = fileName.match(/_(\d+)/);
1460
+ if (idMatch) {
1461
+ return idMatch[1];
1476
1462
  }
1477
- group.forEach((g) => {
1478
- const newGroup = typeof g === "string" ? { group: g } : g;
1479
- if (!groupped[newGroup.group]) {
1480
- groupped[newGroup.group] = new ClassName();
1481
- groupped[newGroup.group].marker = newGroup.marker ?? newGroup.group;
1482
- }
1483
- groupped[newGroup.group]?.append(item);
1484
- });
1485
- });
1486
- if (sort) {
1487
- return Object.entries(groupped).toSorted(([ak, a], [bk, b]) => {
1488
- if (typeof sort === "function") {
1489
- return sort(a, ak, b, bk);
1490
- }
1491
- if (ak === "Unknown") {
1492
- return 1;
1493
- }
1494
- if (bk === "Unknown") {
1495
- return -1;
1496
- }
1497
- if (sort === "group") {
1498
- return ak.localeCompare(bk);
1499
- }
1500
- return (b?.length ?? 0) - (a?.length ?? 0);
1501
- }).reduce((acc, [key, value]) => {
1502
- if (minOccurance > -1 && (value?.length ?? 0) < minOccurance) {
1503
- const otherKey = lessOccuranceLabel;
1504
- acc[otherKey] = acc[otherKey] && value ? acc[otherKey].merge(value) : value;
1505
- } else {
1506
- acc[key] = value;
1463
+ }
1464
+ return void 0;
1465
+ }
1466
+ getGeniTreeName() {
1467
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1468
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1469
+ }
1470
+ getGenoProTreeId() {
1471
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1472
+ if (fileName) {
1473
+ const idMatch = fileName.match(/_(\d+)/);
1474
+ if (idMatch) {
1475
+ return idMatch[1];
1476
+ }
1477
+ }
1478
+ return void 0;
1479
+ }
1480
+ getGenoProTreeName() {
1481
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1482
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1483
+ }
1484
+ getGNO2GEDTreeId() {
1485
+ const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
1486
+ if (rin) {
1487
+ return rin;
1488
+ }
1489
+ return `gno_${this._gedcom?.refcount || "unknown"}`;
1490
+ }
1491
+ getGNO2GEDTreeName() {
1492
+ const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
1493
+ if (treeName) {
1494
+ return treeName;
1495
+ }
1496
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1497
+ return fileName || "GNO2GED Export";
1498
+ }
1499
+ /**
1500
+ * Universal tree ID getter for unknown/unrecognized GEDCOM sources
1501
+ * Tries to extract an ID from various common locations
1502
+ */
1503
+ getUniversalTreeId() {
1504
+ const sourceType = this.getSourceType();
1505
+ const prefix = sourceType ? sourceType.toLowerCase() : "tree";
1506
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1507
+ if (fileName) {
1508
+ const idMatch = fileName.match(/_(\d+)/);
1509
+ if (idMatch) {
1510
+ return `${prefix}_${idMatch[1]}`;
1511
+ }
1512
+ }
1513
+ return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
1514
+ }
1515
+ /**
1516
+ * Universal tree name getter for unknown/unrecognized GEDCOM sources
1517
+ * Tries to extract a name from various common locations
1518
+ */
1519
+ getUniversalTreeName() {
1520
+ const sourceType = this.getSourceType();
1521
+ const prefix = sourceType ? `${sourceType}-` : "";
1522
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1523
+ if (fileName) {
1524
+ const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1525
+ return `${prefix}${cleanName}`;
1526
+ }
1527
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1528
+ if (sourName) {
1529
+ return `${prefix}${sourName}`;
1530
+ }
1531
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1532
+ if (sourValue) {
1533
+ return `${prefix}${sourValue}`;
1534
+ }
1535
+ return `${prefix}Unknown Tree`;
1536
+ }
1537
+ getTreeName() {
1538
+ if (this?.isAncestry()) {
1539
+ const name = this.getAncestryTreeName();
1540
+ if (name) return name;
1541
+ }
1542
+ if (this?.isMyHeritage()) {
1543
+ const name = this.getMyHeritageTreeName();
1544
+ if (name) return name;
1545
+ }
1546
+ if (this?.isFamilySearch()) {
1547
+ const name = this.getFamilySearchTreeName();
1548
+ if (name) return name;
1549
+ }
1550
+ if (this?.isGNO2GED()) {
1551
+ const name = this.getGNO2GEDTreeName();
1552
+ if (name) return name;
1553
+ }
1554
+ if (this?.isAhnenblatt()) {
1555
+ const name = this.getAhnenblattTreeName();
1556
+ if (name) return name;
1557
+ }
1558
+ if (this?.isGeni()) {
1559
+ const name = this.getGeniTreeName();
1560
+ if (name) return name;
1561
+ }
1562
+ if (this?.isGenoPro()) {
1563
+ const name = this.getGenoProTreeName();
1564
+ if (name) return name;
1565
+ }
1566
+ return this.getUniversalTreeName();
1567
+ }
1568
+ };
1569
+ var createProxy = (target) => {
1570
+ return new Proxy(target, {
1571
+ get(t, prop, receiver) {
1572
+ if (prop === "unwrapped") {
1573
+ return target;
1574
+ }
1575
+ if (prop in t) {
1576
+ return Reflect.get(t, prop, receiver);
1577
+ }
1578
+ if (!isOnlyMainProp(prop)) {
1579
+ const ref = t.ref;
1580
+ if (ref && prop in ref) {
1581
+ return Reflect.get(ref, prop, receiver);
1507
1582
  }
1508
- return acc;
1509
- }, {});
1583
+ }
1510
1584
  }
1511
- return groupped;
1585
+ });
1586
+ };
1587
+ var createCommon = (gedcom, id, main, parent) => {
1588
+ return createProxy(
1589
+ new Common(gedcom, id, main, parent)
1590
+ );
1591
+ };
1592
+ var isOnlyMainProp = (key) => {
1593
+ return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
1594
+ };
1595
+ var isValidKey = (common, key) => {
1596
+ const prop = get(common, key);
1597
+ return key !== "_gedcom" && key !== "_main" && key !== "_parent" && key !== "_uniqueId" && key !== "_refs" && key !== "_type" && !(typeof key === "string" && isGedcomTrailerTag(key)) && (key === "id" || key === "_id" || key === "value" || key === "_value" || prop instanceof Common || prop instanceof List);
1598
+ };
1599
+ var getValidKeys = (common) => {
1600
+ return Object.keys(common).filter((key) => {
1601
+ return isValidKey(common, key);
1602
+ });
1603
+ };
1604
+ var getValidTag = (tag) => {
1605
+ return tag.replace(/^@@/, "");
1606
+ };
1607
+ var getListTag = (tag) => {
1608
+ return `@@${tag}`;
1609
+ };
1610
+ var isId = (string) => {
1611
+ return ID_REG.test(string);
1612
+ };
1613
+ var idGetter = (id) => {
1614
+ if (id === void 0) {
1615
+ return void 0;
1616
+ }
1617
+ let newId = `${id}`;
1618
+ const parts = newId?.match(ID_GETTER_REG)?.groups;
1619
+ if (!parts?.at && !parts?.letter) {
1620
+ newId = `@U${newId}`;
1621
+ } else if (!parts.at) {
1622
+ newId = `@${newId}`;
1623
+ } else if (!parts.letter) {
1624
+ newId = newId.replace(/^@/, "@U");
1625
+ }
1626
+ if (!newId.endsWith("@")) {
1627
+ newId = `${newId}@`;
1628
+ }
1629
+ return newId;
1630
+ };
1631
+
1632
+ // src/utils/fact-edit.ts
1633
+ var SINGLETON_TAGS = /* @__PURE__ */ new Set(["BIRT", "DEAT", "SSN", "SEX", "DSCR"]);
1634
+ var firstRecord = (node) => {
1635
+ if (!node) {
1636
+ return void 0;
1637
+ }
1638
+ if (node instanceof List) {
1639
+ return node.index(0);
1640
+ }
1641
+ return node.index(0) ?? node;
1642
+ };
1643
+ var setDateOn = (gedcom, owner, parent, date) => {
1644
+ const trimmed = date.trim();
1645
+ if (!trimmed) {
1646
+ return;
1647
+ }
1648
+ let dateNode = parent.get("DATE");
1649
+ if (!dateNode || dateNode instanceof List) {
1650
+ dateNode = createCommonDate(gedcom, void 0, owner, parent);
1651
+ dateNode.type = "DATE";
1652
+ parent.set("DATE", dateNode);
1653
+ }
1654
+ dateNode.value = trimmed;
1655
+ };
1656
+ var fillFact = (gedcom, owner, event, input) => {
1657
+ if (input.value?.trim()) {
1658
+ event.value = input.value.trim();
1659
+ }
1660
+ if (input.type?.trim()) {
1661
+ event.set("TYPE", input.type.trim());
1662
+ }
1663
+ if (input.date) {
1664
+ setDateOn(gedcom, owner, event, input.date);
1665
+ }
1666
+ if (input.place?.trim()) {
1667
+ event.set("PLAC", input.place.trim());
1668
+ }
1669
+ if (input.note?.trim()) {
1670
+ event.set("NOTE", input.note.trim());
1671
+ }
1672
+ };
1673
+ var addIndividualFact = (indi, input) => {
1674
+ const gedcom = indi.getGedcom();
1675
+ if (!gedcom) {
1676
+ return void 0;
1677
+ }
1678
+ const tag = input.tag;
1679
+ let event = SINGLETON_TAGS.has(tag) ? firstRecord(indi.get(tag)) : void 0;
1680
+ if (!event) {
1681
+ event = createCommon(gedcom, void 0, indi);
1682
+ event.type = tag;
1683
+ if (SINGLETON_TAGS.has(tag)) {
1684
+ indi.set(tag, event);
1685
+ } else {
1686
+ indi.assign(tag, event);
1687
+ }
1688
+ }
1689
+ fillFact(gedcom, indi, event, input);
1690
+ return event;
1691
+ };
1692
+ var addFamilyFact = (fam, input) => {
1693
+ const gedcom = fam.getGedcom();
1694
+ if (!gedcom) {
1695
+ return void 0;
1696
+ }
1697
+ const event = createCommon(gedcom, void 0, fam);
1698
+ event.type = input.tag;
1699
+ fam.assign(input.tag, event);
1700
+ fillFact(gedcom, fam, event, input);
1701
+ return event;
1702
+ };
1703
+ var addNonEvent = (owner, input) => {
1704
+ const gedcom = owner.getGedcom();
1705
+ const eventType = input.event?.trim().toUpperCase();
1706
+ if (!gedcom || !eventType) {
1707
+ return void 0;
1708
+ }
1709
+ const no = createCommon(gedcom, void 0, owner);
1710
+ no.type = "NO";
1711
+ no.value = eventType;
1712
+ if (input.date?.trim()) {
1713
+ setDateOn(gedcom, owner, no, input.date);
1714
+ }
1715
+ if (input.note?.trim()) {
1716
+ no.set("NOTE", input.note.trim());
1717
+ }
1718
+ owner.assign("NO", no);
1719
+ return no;
1720
+ };
1721
+
1722
+ // src/utils/family-edit.ts
1723
+ var PEDIGREE_VALUES = [
1724
+ "birth" /* BIRTH */,
1725
+ "adopted" /* ADOPTED */,
1726
+ "foster" /* FOSTER */,
1727
+ "step" /* STEP */,
1728
+ "sealing" /* SEALING */
1729
+ ];
1730
+ var toGedcomPedigree = (value) => {
1731
+ const normalized = String(value || "birth" /* BIRTH */).toLowerCase();
1732
+ if (normalized === "biological" /* BIOLOGICAL */ || normalized === "birth" /* BIRTH */) {
1733
+ return "birth" /* BIRTH */;
1734
+ }
1735
+ const match = PEDIGREE_VALUES.find((item) => item === normalized);
1736
+ return match ?? "birth" /* BIRTH */;
1737
+ };
1738
+ var pointerIds = (tag) => {
1739
+ if (!tag) {
1740
+ return [];
1512
1741
  }
1513
- findIndex(item) {
1514
- if (!item?.id) {
1515
- return -1;
1516
- }
1517
- return this.keys().findIndex((key) => key === item.id);
1742
+ if (tag instanceof List) {
1743
+ return tag.values().map((item) => item?.toValue?.()).filter((value2) => !!value2);
1518
1744
  }
1519
- getItems() {
1520
- return this.items;
1745
+ const value = tag.toValue?.();
1746
+ return value ? [String(value)] : [];
1747
+ };
1748
+ var firstPointerId = (tag) => pointerIds(tag)[0];
1749
+ var makePointer = (gedcom, parent, value, refType) => {
1750
+ const pointer = createCommon(gedcom, void 0, parent);
1751
+ pointer.value = value;
1752
+ pointer.refType = refType;
1753
+ return pointer;
1754
+ };
1755
+ var familyHasParent = (fam, indiId) => pointerIds(fam.get("HUSB")).includes(indiId) || pointerIds(fam.get("WIFE")).includes(indiId);
1756
+ var familyHasChild = (fam, indiId) => pointerIds(fam.get("CHIL")).includes(indiId);
1757
+ var spouseTagFor = (indi, fam) => {
1758
+ const husbId = firstPointerId(fam.get("HUSB"));
1759
+ const wifeId = firstPointerId(fam.get("WIFE"));
1760
+ if (husbId === indi.id) {
1761
+ return "HUSB";
1521
1762
  }
1522
- toJson(tag, options) {
1523
- const json = this.toObject(tag, options);
1524
- return JSON.stringify(json);
1763
+ if (wifeId === indi.id) {
1764
+ return "WIFE";
1525
1765
  }
1526
- toObject(tag, options) {
1527
- const json = {};
1528
- this.entries().forEach(([_, item]) => {
1529
- const validTag = getValidTag(tag);
1530
- if (json[validTag]) {
1531
- if (!Array.isArray(json[validTag])) {
1532
- json[validTag] = [json[validTag]];
1533
- }
1534
- json[validTag].push(
1535
- item.toObject(validTag, options)
1536
- );
1537
- } else {
1538
- json[validTag] = item.toObject(
1539
- validTag,
1540
- options
1541
- );
1542
- }
1543
- });
1544
- return json;
1766
+ if (indi.isFemale()) {
1767
+ return wifeId && wifeId !== indi.id ? "HUSB" : "WIFE";
1545
1768
  }
1546
- toGedcom(tag, level = 0, options) {
1547
- const gedcom = this.toGedcomLines(tag, level, options);
1548
- return gedcom.join("\n");
1769
+ if (indi.isMale()) {
1770
+ return husbId && husbId !== indi.id ? "WIFE" : "HUSB";
1549
1771
  }
1550
- toGedcomLines(tag, level = 0, options) {
1551
- const gedcom = [];
1552
- this.entries().forEach(([_, item]) => {
1553
- const validTag = getValidTag(tag);
1554
- const validKey = item.id;
1555
- const validValue = validKey ? validTag : item.exportValue();
1556
- gedcom.push(
1557
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
1558
- );
1559
- gedcom.push(
1560
- ...item.toGedcomLines(
1561
- validTag,
1562
- level + 1,
1563
- options
1564
- )
1565
- );
1566
- });
1567
- return gedcom;
1772
+ return husbId ? "WIFE" : "HUSB";
1773
+ };
1774
+ var canAttachAsSpouse = (fam, indi) => {
1775
+ if (!indi.id) {
1776
+ return false;
1568
1777
  }
1569
- toValue() {
1570
- const propList = new _List();
1571
- this.entries().forEach(([key, item]) => {
1572
- propList.item(key, item?.toValue());
1573
- });
1574
- return propList;
1778
+ if (familyHasParent(fam, indi.id)) {
1779
+ return true;
1575
1780
  }
1576
- // @deprecated use get instead
1577
- toProp(tag) {
1578
- return this.get(tag);
1781
+ const tag = spouseTagFor(indi, fam);
1782
+ const occupied = firstPointerId(fam.get(tag));
1783
+ return !occupied || occupied === indi.id;
1784
+ };
1785
+ var pointerByValue = (tag, id) => {
1786
+ if (!tag) {
1787
+ return void 0;
1579
1788
  }
1580
- toList() {
1581
- return new _List().concat(this.items);
1789
+ const fromValues = tag.toValueList?.().item(id);
1790
+ if (fromValues) {
1791
+ return fromValues;
1582
1792
  }
1583
- toValueList() {
1584
- const newList = new _List();
1585
- this.values().forEach((value) => {
1586
- value.value && newList.item(value.value, value);
1587
- });
1588
- return newList;
1793
+ return tag.toList()?.values().find((item) => item?.toValue?.() === id);
1794
+ };
1795
+ var setChildPedigree = (fam, child, pedigree, parent) => {
1796
+ if (!fam.id || !child.id) {
1797
+ return;
1798
+ }
1799
+ const pedi = toGedcomPedigree(pedigree);
1800
+ const famc = pointerByValue(child.get("FAMC"), fam.id);
1801
+ if (famc) {
1802
+ famc.set("PEDI", pedi);
1803
+ }
1804
+ const chil = pointerByValue(fam.get("CHIL"), child.id);
1805
+ if (!chil) {
1806
+ return;
1807
+ }
1808
+ const setFather = !parent || parent.isMale() || parent.isUnknownSex();
1809
+ const setMother = !parent || parent.isFemale() || parent.isUnknownSex();
1810
+ if (setFather) {
1811
+ chil.set("_FREL", pedi);
1812
+ }
1813
+ if (setMother) {
1814
+ chil.set("_MREL", pedi);
1815
+ }
1816
+ };
1817
+ var attachSpouseToFamily = (fam, indi) => {
1818
+ const gedcom = fam.getGedcom() ?? indi.getGedcom();
1819
+ if (!indi.id || !fam.id || !gedcom) {
1820
+ return false;
1821
+ }
1822
+ if (!canAttachAsSpouse(fam, indi)) {
1823
+ return false;
1824
+ }
1825
+ const tag = spouseTagFor(indi, fam);
1826
+ if (firstPointerId(fam.get(tag)) !== indi.id) {
1827
+ fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
1828
+ }
1829
+ indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
1830
+ return true;
1831
+ };
1832
+ var attachChildToFamily = (fam, child, pedigree, parent) => {
1833
+ const gedcom = fam.getGedcom() ?? child.getGedcom();
1834
+ if (!child.id || !fam.id || !gedcom) {
1835
+ return false;
1836
+ }
1837
+ if (!familyHasChild(fam, child.id)) {
1838
+ fam.assign("CHIL", makePointer(gedcom, fam, child.id, "INDI"), true);
1839
+ }
1840
+ const existingFamc = pointerByValue(child.get("FAMC"), fam.id);
1841
+ if (!existingFamc) {
1842
+ child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
1843
+ }
1844
+ setChildPedigree(fam, child, pedigree, parent);
1845
+ return true;
1846
+ };
1847
+ var findReusableParentChildFamily = (parent, child) => {
1848
+ if (!parent.id || !child.id) {
1849
+ return void 0;
1850
+ }
1851
+ let reusableFamc;
1852
+ let already = false;
1853
+ child.getFamilies("FAMC")?.forEach((fam) => {
1854
+ if (!fam) {
1855
+ return;
1856
+ }
1857
+ if (familyHasParent(fam, parent.id) && familyHasChild(fam, child.id)) {
1858
+ already = true;
1859
+ return;
1860
+ }
1861
+ if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
1862
+ reusableFamc = fam;
1863
+ }
1864
+ });
1865
+ if (already) {
1866
+ return "already";
1867
+ }
1868
+ if (reusableFamc) {
1869
+ return reusableFamc;
1870
+ }
1871
+ const parentFams = [];
1872
+ parent.getFamilies("FAMS")?.forEach((fam) => {
1873
+ if (fam) {
1874
+ parentFams.push(fam);
1875
+ }
1876
+ });
1877
+ if (parentFams.length === 1) {
1878
+ return parentFams[0];
1589
1879
  }
1880
+ return void 0;
1881
+ };
1882
+
1883
+ // src/utils/get-family-with.ts
1884
+ var getFamilyWith = (person1, person2, famType = "FAMS") => {
1885
+ return person1?.get(famType)?.toValueList().find((fam) => {
1886
+ const family = fam;
1887
+ return famType === "FAMC" || family?.HUSB?.value === person2 || family?.WIFE?.value === person2;
1888
+ });
1590
1889
  };
1591
1890
 
1592
1891
  // src/utils/get-places.ts
@@ -1653,16 +1952,58 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
1653
1952
  });
1654
1953
  return places;
1655
1954
  };
1955
+
1956
+ // src/utils/local-media.ts
1957
+ var localMediaResolver;
1958
+ var isRemoteOrEmbeddedMediaUrl = (path) => {
1959
+ if (!path) {
1960
+ return false;
1961
+ }
1962
+ return path.startsWith("data:") || path.startsWith("blob:") || /^https?:\/\//i.test(path);
1963
+ };
1964
+ var resolveLocalMediaUrl = async (path) => {
1965
+ if (!path) {
1966
+ return void 0;
1967
+ }
1968
+ if (isRemoteOrEmbeddedMediaUrl(path)) {
1969
+ return path;
1970
+ }
1971
+ const resolved = await localMediaResolver?.(path);
1972
+ return resolved;
1973
+ };
1656
1974
  var implemented = (type, ...args) => {
1657
1975
  };
1658
1976
 
1659
1977
  // src/utils/media-utils.ts
1660
1978
  var getFileExtension = (filename) => {
1661
- const match = filename.match(/\.([^.]+)$/);
1662
- return match ? match[1] : "";
1979
+ if (!filename) {
1980
+ return "";
1981
+ }
1982
+ if (filename.startsWith("data:")) {
1983
+ const mime = filename.slice(5).split(";")[0] || "";
1984
+ const subtype = (mime.split("/")[1] || "").toLowerCase();
1985
+ if (subtype === "jpeg") {
1986
+ return "jpg";
1987
+ }
1988
+ return subtype.split("+")[0] || "";
1989
+ }
1990
+ const withoutQuery = filename.split(/[?#]/)[0] || filename;
1991
+ const match = withoutQuery.match(/\.([^.]+)$/);
1992
+ return match ? match[1].toLowerCase() : "";
1663
1993
  };
1664
1994
  var isImageFormat = (format2) => {
1665
- if (!format2) return false;
1995
+ if (!format2) {
1996
+ return false;
1997
+ }
1998
+ let normalized = format2.toLowerCase().trim();
1999
+ if (normalized.startsWith("data:")) {
2000
+ normalized = getFileExtension(normalized);
2001
+ } else if (normalized.includes("/")) {
2002
+ normalized = normalized.split("/")[1]?.split("+")[0] || "";
2003
+ if (normalized === "jpeg") {
2004
+ normalized = "jpg";
2005
+ }
2006
+ }
1666
2007
  const imageFormats = [
1667
2008
  "jpg",
1668
2009
  "jpeg",
@@ -1674,7 +2015,11 @@ var isImageFormat = (format2) => {
1674
2015
  "tiff",
1675
2016
  "tif"
1676
2017
  ];
1677
- return imageFormats.includes(format2.toLowerCase());
2018
+ return imageFormats.includes(normalized);
2019
+ };
2020
+ var resolveObjeForm = (obje) => {
2021
+ const value = obje?.get?.("FILE.FORM")?.toValue?.() ?? obje?.get?.("FORM")?.toValue?.();
2022
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
1678
2023
  };
1679
2024
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
1680
2025
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -2219,6 +2564,34 @@ var Fam = class extends Common {
2219
2564
  getParents() {
2220
2565
  return this.getHusband().copy().merge(this.getWife());
2221
2566
  }
2567
+ hasParent(indi) {
2568
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2569
+ return !!indiId && familyHasParent(this, indiId);
2570
+ }
2571
+ hasChild(indi) {
2572
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2573
+ return !!indiId && familyHasChild(this, indiId);
2574
+ }
2575
+ canAddSpouse(indi) {
2576
+ return canAttachAsSpouse(this, indi);
2577
+ }
2578
+ addSpouse(indi) {
2579
+ return attachSpouseToFamily(this, indi);
2580
+ }
2581
+ addChild(child, pedigree, parent) {
2582
+ return attachChildToFamily(
2583
+ this,
2584
+ child,
2585
+ pedigree,
2586
+ parent
2587
+ );
2588
+ }
2589
+ addFact(input) {
2590
+ return addFamilyFact(this, input);
2591
+ }
2592
+ addNonEvent(input) {
2593
+ return addNonEvent(this, input);
2594
+ }
2222
2595
  toList() {
2223
2596
  return new Families().concat(
2224
2597
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2319,19 +2692,51 @@ var ALLOWED_FACTS = [
2319
2692
  "OCCU",
2320
2693
  "OCCUPATIONS",
2321
2694
  "RESI",
2695
+ "BIRT",
2696
+ "DEAT",
2322
2697
  "BAPT",
2698
+ "BAPM",
2323
2699
  "CHRI",
2324
2700
  "CHR",
2701
+ "CHRA",
2325
2702
  "BURI",
2326
- "EDUC",
2703
+ "CREM",
2704
+ "ADOP",
2705
+ "BARM",
2706
+ "BASM",
2707
+ "BLES",
2708
+ "CONF",
2709
+ "FCOM",
2710
+ "ORDN",
2711
+ "NATU",
2712
+ "EMIG",
2713
+ "IMMI",
2714
+ "CENS",
2715
+ "PROB",
2716
+ "WILL",
2327
2717
  "GRAD",
2718
+ "RETI",
2719
+ "EDUC",
2328
2720
  "DIV",
2329
- "IMMI",
2330
2721
  "RELI",
2722
+ "CAST",
2723
+ "DSCR",
2724
+ "NATI",
2725
+ "PROP",
2726
+ "SSN",
2727
+ "TITL",
2728
+ "ADDR",
2729
+ "BAPL",
2730
+ "CONL",
2731
+ "ENDL",
2732
+ "SLGC",
2331
2733
  "_MILT",
2332
2734
  "_MILTID",
2333
2735
  "FACT",
2334
- "_ORIG"
2736
+ "_ORIG",
2737
+ "_WLNK",
2738
+ "WWW",
2739
+ "URL"
2335
2740
  ];
2336
2741
  var DISALLOWED_CUSTOM_FACTS = ["DNA Test", "Newspaper"];
2337
2742
  var CustomFactRenderers = {
@@ -2875,7 +3280,7 @@ var Indi = class extends Common {
2875
3280
  return result;
2876
3281
  }
2877
3282
  ancestryLink() {
2878
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3283
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
2879
3284
  const tree = this.getAncestryTreeId();
2880
3285
  if (this.id) {
2881
3286
  return `https://${www}/family-tree/person/tree/${tree}/person/${this.id.replace(
@@ -2887,7 +3292,7 @@ var Indi = class extends Common {
2887
3292
  async ancestryMedia(namespace) {
2888
3293
  const list = {};
2889
3294
  const objeList = this.get("OBJE")?.toList().copy();
2890
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3295
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
2891
3296
  const tree = this.getAncestryTreeId();
2892
3297
  if (objeList) {
2893
3298
  await Promise.all(
@@ -2905,10 +3310,14 @@ var Indi = class extends Common {
2905
3310
  const clone = obje?.get("_CLON._OID")?.toValue();
2906
3311
  const mser = obje?.get("_MSER._LKID")?.toValue();
2907
3312
  let url = obje?.get("FILE")?.toValue();
2908
- const title = obje?.get("TITL")?.toValue() ?? "";
2909
- const type = obje?.get("FORM")?.toValue() ?? "raw";
2910
- const imgId = clone || mser;
2911
- if (!www || !tree || !this.id) {
3313
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3314
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3315
+ let imgId = clone || mser;
3316
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3317
+ if (!hasEmbeddedOrLocalFile && (!www || !tree || !this.id)) {
3318
+ return;
3319
+ }
3320
+ if (!this.id) {
2912
3321
  return;
2913
3322
  }
2914
3323
  if (!namespace && !url) {
@@ -2926,13 +3335,16 @@ var Indi = class extends Common {
2926
3335
  }
2927
3336
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
2928
3337
  }
3338
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
3339
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
3340
+ }
2929
3341
  if (url && imgId) {
2930
- const id = `${tree}-${this.id}-${imgId}`;
3342
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
2931
3343
  list[id] = {
2932
3344
  key,
2933
3345
  isPrimary,
2934
3346
  id,
2935
- tree,
3347
+ tree: tree || "gedzip",
2936
3348
  imgId,
2937
3349
  person: this.id,
2938
3350
  title,
@@ -2947,7 +3359,7 @@ var Indi = class extends Common {
2947
3359
  return list;
2948
3360
  }
2949
3361
  myheritageLink(poolId = 0) {
2950
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.value?.toLowerCase().replace(/^www\./gi, "");
3362
+ const www = (this.getFromSourceHeads("SOUR.CORP.value") || this._gedcom?.HEAD?.SOUR?.CORP?.value)?.toLowerCase().replace(/^www\./gi, "");
2951
3363
  const site = this.getMyHeritageTreeId();
2952
3364
  const file = this._gedcom?.HEAD?.get("FILE")?.toValue()?.match(/Exported by MyHeritage.com from .+ in (?<site>.+) on .+$/)?.groups?.site;
2953
3365
  const normalizedFile = file?.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
@@ -2978,9 +3390,11 @@ var Indi = class extends Common {
2978
3390
  obje.standardizeMedia();
2979
3391
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
2980
3392
  const url = obje?.get("FILE")?.toValue();
2981
- const title = obje?.get("NOTE")?.toValue() ?? "";
2982
- const type = obje?.get("FORM")?.toValue() ?? "raw";
2983
- const imgId = obje?.get("_PHOTO_RIN")?.toValue();
3393
+ const title = obje?.get("FILE.NOTE")?.toValue() ?? obje?.get("NOTE")?.toValue() ?? "";
3394
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3395
+ const photoRin = obje?.get("_PHOTO_RIN")?.toValue();
3396
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
3397
+ const imgId = photoRin || (hasEmbeddedOrLocalFile && url ? `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}` : void 0);
2984
3398
  if (url && imgId) {
2985
3399
  const id = `${tree}-${this.id}-${imgId}`;
2986
3400
  list[id] = {
@@ -3103,10 +3517,10 @@ var Indi = class extends Common {
3103
3517
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3104
3518
  const url = obje?.get("FILE")?.toValue();
3105
3519
  const title = obje?.get("TITL")?.toValue() ?? "";
3106
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3520
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3107
3521
  if (url) {
3108
3522
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
3109
- const imgId = urlMatch?.[1] || `img-${index}-${Date.now().toString(36)}`;
3523
+ const imgId = urlMatch?.[1] || (url.startsWith("data:") ? `data-${index}-${getFileExtension(url) || "bin"}` : `img-${index}-${Date.now().toString(36)}`);
3110
3524
  const id = `geni-${geniId}-${imgId}`;
3111
3525
  list[id] = {
3112
3526
  isPrimary,
@@ -3143,10 +3557,10 @@ var Indi = class extends Common {
3143
3557
  obje.standardizeMedia();
3144
3558
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
3145
3559
  const url = obje?.get("FILE")?.toValue();
3146
- const title = obje?.get("TITL")?.toValue() ?? "";
3147
- const type = obje?.get("FORM")?.toValue() ?? "raw";
3560
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
3561
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
3148
3562
  if (url) {
3149
- const imgId = `media-${index}-${url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3563
+ const imgId = `media-${index}-${url.startsWith("data:") ? getFileExtension(url) || "bin" : url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
3150
3564
  const id = `${treeId}-${this.id}-${imgId}`;
3151
3565
  list[id] = {
3152
3566
  isPrimary,
@@ -3186,7 +3600,20 @@ var Indi = class extends Common {
3186
3600
  cacheKey
3187
3601
  );
3188
3602
  if (cached !== void 0) {
3189
- return cached;
3603
+ if (cached?.file && !isRemoteOrEmbeddedMediaUrl(cached.file)) {
3604
+ const resolvedCached = await resolveLocalMediaUrl(cached.file);
3605
+ if (resolvedCached && isRemoteOrEmbeddedMediaUrl(resolvedCached)) {
3606
+ const refreshed = { ...cached, file: resolvedCached };
3607
+ profilePictureCache(
3608
+ this._gedcom,
3609
+ cacheKey,
3610
+ refreshed
3611
+ );
3612
+ return refreshed;
3613
+ }
3614
+ } else {
3615
+ return cached;
3616
+ }
3190
3617
  }
3191
3618
  const mediaList = await this.multimedia(namespace);
3192
3619
  if (!mediaList) {
@@ -3198,39 +3625,40 @@ var Indi = class extends Common {
3198
3625
  return void 0;
3199
3626
  }
3200
3627
  const mediaArray = Object.values(mediaList);
3201
- const primaryMedia = mediaArray.find(
3202
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
3203
- );
3204
- if (primaryMedia) {
3205
- const result = {
3206
- file: primaryMedia.url,
3207
- form: primaryMedia.contentType,
3208
- title: primaryMedia.title,
3209
- isPrimary: true
3628
+ const toDisplayableProfile = async (media, isPrimary) => {
3629
+ const resolvedFile = await resolveLocalMediaUrl(media.url);
3630
+ if (!resolvedFile || !isRemoteOrEmbeddedMediaUrl(resolvedFile)) {
3631
+ return void 0;
3632
+ }
3633
+ return {
3634
+ file: resolvedFile,
3635
+ form: media.contentType,
3636
+ title: media.title,
3637
+ isPrimary
3210
3638
  };
3211
- profilePictureCache(this._gedcom, cacheKey, result);
3212
- return result;
3213
- }
3214
- if (onlyPrimary) {
3215
- profilePictureCache(
3216
- this._gedcom,
3217
- cacheKey,
3218
- void 0
3219
- );
3220
- return void 0;
3221
- }
3222
- const secondaryMedia = mediaArray.find(
3223
- (media) => isImageFormat(media.contentType || getFileExtension(media.url))
3639
+ };
3640
+ const candidates = mediaArray.filter(
3641
+ (media) => isImageFormat(
3642
+ media.contentType || media.url || getFileExtension(media.url)
3643
+ )
3224
3644
  );
3225
- if (secondaryMedia) {
3226
- const result = {
3227
- file: secondaryMedia.url,
3228
- form: secondaryMedia.contentType,
3229
- title: secondaryMedia.title,
3230
- isPrimary: false
3231
- };
3232
- profilePictureCache(this._gedcom, cacheKey, result);
3233
- return result;
3645
+ const ordered = [
3646
+ ...candidates.filter((media) => media.isPrimary),
3647
+ ...candidates.filter((media) => !media.isPrimary)
3648
+ ];
3649
+ for (const media of ordered) {
3650
+ if (onlyPrimary && !media.isPrimary) {
3651
+ break;
3652
+ }
3653
+ const result = await toDisplayableProfile(media, !!media.isPrimary);
3654
+ if (result) {
3655
+ profilePictureCache(
3656
+ this._gedcom,
3657
+ cacheKey,
3658
+ result
3659
+ );
3660
+ return result;
3661
+ }
3234
3662
  }
3235
3663
  profilePictureCache(
3236
3664
  this._gedcom,
@@ -3468,6 +3896,156 @@ var Indi = class extends Common {
3468
3896
  isUnknownSex() {
3469
3897
  return !this.isMale() && !this.isFemale();
3470
3898
  }
3899
+ addSpouse(other) {
3900
+ if (!this.id || !other?.id || this.id === other.id || !this._gedcom) {
3901
+ return void 0;
3902
+ }
3903
+ const existing = getFamilyWith(this, other.id);
3904
+ if (existing) {
3905
+ return existing;
3906
+ }
3907
+ const fam = this._gedcom.createFamily();
3908
+ if (!fam.addSpouse(this) || !fam.addSpouse(other)) {
3909
+ return void 0;
3910
+ }
3911
+ return fam;
3912
+ }
3913
+ addChild(child, pedigree) {
3914
+ if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
3915
+ return void 0;
3916
+ }
3917
+ const existing = findReusableParentChildFamily(
3918
+ this,
3919
+ child
3920
+ );
3921
+ if (existing === "already") {
3922
+ const fam2 = child.getFamilies("FAMC")?.find(
3923
+ (item) => !!item && item.hasParent(this.id) && item.hasChild(child.id)
3924
+ );
3925
+ if (fam2) {
3926
+ setChildPedigree(
3927
+ fam2,
3928
+ child,
3929
+ pedigree,
3930
+ this
3931
+ );
3932
+ }
3933
+ return fam2;
3934
+ }
3935
+ const fam = existing ?? this._gedcom.createFamily();
3936
+ if (!fam.addSpouse(this)) {
3937
+ return void 0;
3938
+ }
3939
+ if (!fam.addChild(child, pedigree, this)) {
3940
+ return void 0;
3941
+ }
3942
+ return fam;
3943
+ }
3944
+ addParent(parent, pedigree) {
3945
+ return parent.addChild(this, pedigree);
3946
+ }
3947
+ /**
3948
+ * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
3949
+ */
3950
+ attachMultimedia(objeOrKey, options) {
3951
+ if (!this._gedcom || !this.id) {
3952
+ return void 0;
3953
+ }
3954
+ const obje = typeof objeOrKey === "string" ? this._gedcom.obje(objeOrKey) : objeOrKey;
3955
+ if (!obje?.id) {
3956
+ return void 0;
3957
+ }
3958
+ const already = this.get("OBJE")?.toList()?.values()?.some((item) => item?.toValue?.() === obje.id);
3959
+ if (!already) {
3960
+ const pointer = makePointer(
3961
+ this._gedcom,
3962
+ this,
3963
+ obje.id,
3964
+ "OBJE"
3965
+ );
3966
+ this.assign("OBJE", pointer);
3967
+ }
3968
+ if (options?.primary) {
3969
+ this.get("OBJE")?.toList()?.forEach((item) => {
3970
+ const target = item?.refType === "OBJE" ? this._gedcom?.obje(
3971
+ String(item.toValue())
3972
+ ) : item;
3973
+ if (!target) {
3974
+ return;
3975
+ }
3976
+ if (target.id === obje.id) {
3977
+ target.set("_PRIM", "Y");
3978
+ } else if (target.get("_PRIM")?.toValue() === "Y") {
3979
+ target.set("_PRIM", "N");
3980
+ }
3981
+ });
3982
+ obje.set("_PRIM", "Y");
3983
+ }
3984
+ return obje;
3985
+ }
3986
+ detachMultimedia(objeKey) {
3987
+ const list = this.get("OBJE")?.toList();
3988
+ if (!list) {
3989
+ return;
3990
+ }
3991
+ const toRemove = list.values().filter(
3992
+ (item) => item?.toValue?.() === objeKey || item?.id === objeKey
3993
+ );
3994
+ toRemove.forEach((item) => {
3995
+ if (item) {
3996
+ list.delete(item);
3997
+ }
3998
+ });
3999
+ if (list.length === 0) {
4000
+ this.remove("OBJE");
4001
+ }
4002
+ }
4003
+ /**
4004
+ * Create a multimedia record and attach it to this person.
4005
+ */
4006
+ attachMediaFromUrl(url, options) {
4007
+ if (!this._gedcom || !url) {
4008
+ return void 0;
4009
+ }
4010
+ const obje = this._gedcom.createMultimediaRecord({
4011
+ file: url,
4012
+ form: options?.form,
4013
+ title: options?.title,
4014
+ mediType: options?.mediType,
4015
+ primary: options?.primary,
4016
+ gedcomVersion: options?.gedcomVersion
4017
+ });
4018
+ return this.attachMultimedia(obje, { primary: options?.primary });
4019
+ }
4020
+ /**
4021
+ * Link a shared SNOTE record via `1 NOTE @Nn@` (GEDCOM 7).
4022
+ */
4023
+ attachSharedNote(noteOrKey) {
4024
+ if (!this._gedcom || !this.id) {
4025
+ return void 0;
4026
+ }
4027
+ const note = typeof noteOrKey === "string" ? this._gedcom.snote(noteOrKey) : noteOrKey;
4028
+ if (!note?.id) {
4029
+ return void 0;
4030
+ }
4031
+ const already = this.get("NOTE")?.toList()?.values()?.some((item) => item?.toValue?.() === note.id);
4032
+ if (!already) {
4033
+ const pointer = makePointer(
4034
+ this._gedcom,
4035
+ this,
4036
+ note.id,
4037
+ "SNOTE"
4038
+ );
4039
+ this.assign("NOTE", pointer);
4040
+ }
4041
+ return note;
4042
+ }
4043
+ addFact(input) {
4044
+ return addIndividualFact(this, input);
4045
+ }
4046
+ addNonEvent(input) {
4047
+ return addNonEvent(this, input);
4048
+ }
3471
4049
  getParentType(id) {
3472
4050
  let indi;
3473
4051
  if (typeof id === "string" || typeof id === "number") {
@@ -3508,7 +4086,7 @@ var Indi = class extends Common {
3508
4086
  if (!famChild) {
3509
4087
  const childIndi = this._gedcom?.indi(child.id);
3510
4088
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
3511
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4089
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
3512
4090
  } else {
3513
4091
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
3514
4092
  }
@@ -3668,7 +4246,9 @@ var Indi = class extends Common {
3668
4246
  }
3669
4247
  }
3670
4248
  if (kinship !== "child" || !breakAfterSpouse) {
3671
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
4249
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(
4250
+ indi.getFathers().copy().merge(indi.getMothers())
4251
+ ).forEach((relative) => {
3672
4252
  if (!visited.has(relative)) {
3673
4253
  const currentRelation = indi.getParentType(relative);
3674
4254
  const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
@@ -4067,7 +4647,7 @@ var Indi = class extends Common {
4067
4647
  return;
4068
4648
  }
4069
4649
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4070
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4650
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4071
4651
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4072
4652
  children.append(child);
4073
4653
  }
@@ -4175,7 +4755,7 @@ var Indi = class extends Common {
4175
4755
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
4176
4756
  if (!child) {
4177
4757
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
4178
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
4758
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
4179
4759
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
4180
4760
  parents.merge(family.getParents());
4181
4761
  }
@@ -6374,7 +6954,11 @@ var STANDARD_QUALIFIERS = {
6374
6954
  BEF: "Bef.",
6375
6955
  BEFORE: "Bef.",
6376
6956
  AFT: "Aft.",
6377
- AFTER: "Aft."
6957
+ AFTER: "Aft.",
6958
+ CAL: "Abt.",
6959
+ // calculated ≈ about for display
6960
+ EST: "Abt."
6961
+ // estimated ≈ about for display
6378
6962
  };
6379
6963
  var EXPORT_QUALIFIERS = {
6380
6964
  "Abt.": "ABT",
@@ -6382,6 +6966,11 @@ var EXPORT_QUALIFIERS = {
6382
6966
  "Aft.": "AFT"
6383
6967
  };
6384
6968
  var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
6969
+ var CALENDAR_PREFIX = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
6970
+ var isGedcomDateValueForm = (value) => (
6971
+ // Require space/end after the token so display forms like "Abt." are not opaque.
6972
+ /^(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)(\s|$)/i.test(value) || /\bAND\b/i.test(value)
6973
+ );
6385
6974
  var NOTE_MARKER = "####";
6386
6975
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
6387
6976
  const hasNote = baseFormat.includes("NOTE");
@@ -6406,29 +6995,45 @@ var CommonDate = class extends Common {
6406
6995
  super(gedcom, id, main, parent);
6407
6996
  delete this.id;
6408
6997
  }
6998
+ get calendar() {
6999
+ return this._calendar;
7000
+ }
6409
7001
  set value(value) {
6410
7002
  if (value) {
6411
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
6412
- const noteMatch = value.match(noteRegExp)?.groups;
6413
7003
  let validValue = value;
7004
+ const calendarMatch = validValue.match(CALENDAR_PREFIX);
7005
+ if (calendarMatch?.groups?.calendar) {
7006
+ this._calendar = calendarMatch.groups.calendar.toUpperCase();
7007
+ validValue = validValue.slice(calendarMatch[0].length);
7008
+ } else {
7009
+ this._calendar = void 0;
7010
+ }
7011
+ const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
7012
+ const noteMatch = validValue.match(noteRegExp)?.groups;
6414
7013
  if (noteMatch?.note) {
6415
7014
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6416
7015
  this.NOTE.value = noteMatch?.note;
6417
- validValue = value.replace(noteRegExp, "");
7016
+ validValue = validValue.replace(noteRegExp, "");
6418
7017
  } else {
6419
- const qualifierMatch = value.match(
6420
- /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
7018
+ const qualifierMatch = validValue.match(
7019
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER|CAL|EST)\b\s*/i
6421
7020
  );
6422
7021
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
6423
7022
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
6424
7023
  if (note) {
6425
7024
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6426
7025
  this.NOTE.value = note;
6427
- validValue = value.slice(qualifierMatch[0].length);
7026
+ validValue = validValue.slice(qualifierMatch[0].length);
6428
7027
  }
6429
7028
  }
6430
7029
  const acceptedDate = this.isValidDateFormat(validValue);
6431
- if (acceptedDate) {
7030
+ if (isGedcomDateValueForm(validValue.trim())) {
7031
+ delete this.DAY;
7032
+ delete this.MONTH;
7033
+ delete this.YEAR;
7034
+ this._date = void 0;
7035
+ this._value = value;
7036
+ } else if (acceptedDate) {
6432
7037
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
6433
7038
  this.DAY.value = format(acceptedDate, "dd");
6434
7039
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -6531,6 +7136,15 @@ var CommonDate = class extends Common {
6531
7136
  );
6532
7137
  }
6533
7138
  exportValue() {
7139
+ if (typeof this._value === "string") {
7140
+ const withoutCalendar = this._value.replace(CALENDAR_PREFIX, "");
7141
+ if (isGedcomDateValueForm(withoutCalendar.trim())) {
7142
+ return this._value.replace(
7143
+ /^((?:GREGORIAN|JULIAN|FRENCH_R|HEBREW)\s+)?(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)\b/i,
7144
+ (_m, cal, q) => `${cal || ""}${q.toUpperCase()}`
7145
+ );
7146
+ }
7147
+ }
6534
7148
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
6535
7149
  if (!formatted) {
6536
7150
  return formatted;
@@ -6538,10 +7152,14 @@ var CommonDate = class extends Common {
6538
7152
  const note = this.NOTE?.value?.trim();
6539
7153
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
6540
7154
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
6541
- return standardized.replace(
7155
+ const withMonths = standardized.replace(
6542
7156
  GEDCOM_MONTHS,
6543
7157
  (month) => month.toUpperCase()
6544
7158
  );
7159
+ if (this._calendar && this._calendar !== "GREGORIAN") {
7160
+ return `${this._calendar} ${withMonths}`;
7161
+ }
7162
+ return withMonths;
6545
7163
  }
6546
7164
  toGedcomLines(tag, level = 0, options) {
6547
7165
  const internal = new RegExp(
@@ -6562,14 +7180,6 @@ var isCommonDate = (value) => {
6562
7180
  return !!value && value !== null && typeof value === "object" && "_date" in value;
6563
7181
  };
6564
7182
 
6565
- // src/utils/get-family-with.ts
6566
- var getFamilyWith = (person1, person2, famType = "FAMS") => {
6567
- return person1?.get(famType)?.toValueList().find((fam) => {
6568
- const family = fam;
6569
- return famType === "FAMC" || family?.HUSB?.value === person2 || family?.WIFE?.value === person2;
6570
- });
6571
- };
6572
-
6573
7183
  // src/constants/orders.ts
6574
7184
  var DEFAULT = {};
6575
7185
  var BIRTH_ASC = {
@@ -6809,4 +7419,4 @@ var getMarriageAscAndBirth = (person) => (itemA, keyA, itemB, keyB) => {
6809
7419
  return getBirthAsc(itemA, keyA, itemB);
6810
7420
  };
6811
7421
 
6812
- export { ADOPTED, BIOLOGICAL, BIRTH, BIRTH_ASC, BIRTH_DESC, DATE_ASC, DATE_DESC, DEATH_ASC, DEATH_DESC, DEFAULT, EVERY, FEMALE, FOSTER, FRIEND, ID_GETTER_REG, ID_REG, ID_SPLIT_REG, LINE_REG, MALE, MAX_FILE_SIZE_TO_SYNC, OTHER, PARTNER, REF_LINE_REG, SEALING, SINGLE, SPOUSE, STEP, UNKOWN, getBirthAsc, getMarriageAsc, getMarriageAscAndBirth, getMarriageAscAndChildBirth, getNameAsc, getNameAscAndBirth, getNameDesc };
7422
+ export { ADOPTED, APP_BRANDING, BIOLOGICAL, BIRTH, BIRTH_ASC, BIRTH_DESC, DATE_ASC, DATE_DESC, DEATH_ASC, DEATH_DESC, DEFAULT, EVERY, FEMALE, FOSTER, FRIEND, ID_GETTER_REG, ID_REG, ID_SPLIT_REG, LINE_REG, MALE, MAX_FILE_SIZE_TO_SYNC, ORIG_HEAD_TAG, OTHER, PARTNER, REF_LINE_REG, SEALING, SINGLE, SPOUSE, STEP, UNKOWN, getBirthAsc, getMarriageAsc, getMarriageAscAndBirth, getMarriageAscAndChildBirth, getNameAsc, getNameAscAndBirth, getNameDesc };