@treeviz/gedcom-parser 2.0.9 → 2.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/classes/common.d.ts +6 -1
  2. package/dist/classes/common.d.ts.map +1 -1
  3. package/dist/classes/fam.d.ts +9 -1
  4. package/dist/classes/fam.d.ts.map +1 -1
  5. package/dist/classes/gedcom.d.ts +6 -2
  6. package/dist/classes/gedcom.d.ts.map +1 -1
  7. package/dist/classes/index.js +740 -51
  8. package/dist/classes/indi.d.ts +6 -0
  9. package/dist/classes/indi.d.ts.map +1 -1
  10. package/dist/cli/index.js +3194 -2499
  11. package/dist/constants/constants.d.ts +7 -0
  12. package/dist/constants/constants.d.ts.map +1 -1
  13. package/dist/constants/index.js +1531 -916
  14. package/dist/factories/index.js +654 -40
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +5201 -4407
  18. package/dist/interfaces/fam.d.ts +7 -1
  19. package/dist/interfaces/fam.d.ts.map +1 -1
  20. package/dist/interfaces/gedcom.d.ts +4 -0
  21. package/dist/interfaces/gedcom.d.ts.map +1 -1
  22. package/dist/interfaces/indi.d.ts +5 -1
  23. package/dist/interfaces/indi.d.ts.map +1 -1
  24. package/dist/kinship-translator/index.d.ts +1 -0
  25. package/dist/kinship-translator/index.d.ts.map +1 -1
  26. package/dist/kinship-translator/index.js +655 -41
  27. package/dist/kinship-translator/kinship-translator.ru.d.ts +18 -0
  28. package/dist/kinship-translator/kinship-translator.ru.d.ts.map +1 -0
  29. package/dist/kinship-translator/patterns.ru.d.ts +2 -0
  30. package/dist/kinship-translator/patterns.ru.d.ts.map +1 -0
  31. package/dist/kinship-translator/translators.d.ts +1 -0
  32. package/dist/kinship-translator/translators.d.ts.map +1 -1
  33. package/dist/kinship-translator/types.d.ts +1 -1
  34. package/dist/kinship-translator/types.d.ts.map +1 -1
  35. package/dist/structures/gedcom.d.ts +1 -0
  36. package/dist/structures/gedcom.d.ts.map +1 -1
  37. package/dist/types/types.d.ts +10 -0
  38. package/dist/types/types.d.ts.map +1 -1
  39. package/dist/utils/fact-edit.d.ts +22 -0
  40. package/dist/utils/fact-edit.d.ts.map +1 -0
  41. package/dist/utils/family-edit.d.ts +23 -0
  42. package/dist/utils/family-edit.d.ts.map +1 -0
  43. package/dist/utils/gedcom-trailer.d.ts +6 -0
  44. package/dist/utils/gedcom-trailer.d.ts.map +1 -0
  45. package/dist/utils/index.d.ts +4 -1
  46. package/dist/utils/index.d.ts.map +1 -1
  47. package/dist/utils/index.js +850 -56
  48. package/dist/utils/parser.d.ts +3 -0
  49. package/dist/utils/parser.d.ts.map +1 -1
  50. package/package.json +1 -1
@@ -8,11 +8,13 @@ var __export = (target, all) => {
8
8
  };
9
9
 
10
10
  // src/constants/constants.ts
11
+ var APP_BRANDING = "TreeViz - The Family Tree Visualiser & Editor";
11
12
  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
13
  var LINE_REG = /(?<indent>^[0-9]) (?:(@[_a-zA-Z0-9]*@) )?(?<type>_[_A-Z][_a-zA-Z]{2,}|_?[A-Z][a-zA-Z]{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
@@ -95,7 +97,8 @@ __export(translators_exports, {
95
97
  en: () => KinshipTranslatorEn,
96
98
  es: () => KinshipTranslatorEs,
97
99
  fr: () => KinshipTranslatorFr,
98
- hu: () => KinshipTranslatorHU
100
+ hu: () => KinshipTranslatorHU,
101
+ ru: () => KinshipTranslatorRu
99
102
  });
100
103
  var getGedcomId = (gedcom) => {
101
104
  if (!gedcom) {
@@ -542,1050 +545,1317 @@ var dateFormatter = (indi, showMarriages = false, showDays = false, showPlaces =
542
545
  } : {}
543
546
  };
544
547
  };
545
- var Common = class _Common {
546
- 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 = {};
547
556
  this.isListable = true;
548
- this._gedcom = gedcom;
549
- this._main = main;
550
- this._parent = parent;
551
- this.initUniqueId();
552
- if (id) {
553
- this._id = id;
554
- } else {
555
- delete this._id;
557
+ this.marker = void 0;
558
+ if (items) {
559
+ this.concat(items);
556
560
  }
557
561
  }
558
- set type(type) {
559
- if (type) {
560
- this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
561
- }
562
- this._type = type;
562
+ get length() {
563
+ return this.values().length;
563
564
  }
564
- get type() {
565
- return this._type;
565
+ has(indi) {
566
+ return !!this.item(
567
+ typeof indi === "string" ? indi : indi?.id ?? ""
568
+ );
566
569
  }
567
- initUniqueId() {
568
- if (this._gedcom) {
569
- this.uniqueId = `~Common:${this._gedcom.refcount++}`;
570
- }
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;
571
576
  }
572
- set uniqueId(id) {
573
- const currentId = this._uniqueId;
574
- if (this._gedcom?.reflist && id) {
575
- this._uniqueId = id;
576
- if (currentId && this._gedcom.reflist[currentId]) {
577
- 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);
578
583
  }
579
- this._gedcom.reflist[id] = this;
580
- }
584
+ });
585
+ return propList;
581
586
  }
582
- get uniqueId() {
583
- return this._uniqueId;
587
+ keys() {
588
+ return Object.keys(this.items);
584
589
  }
585
- set id(id) {
586
- this._id = idGetter(id);
590
+ values() {
591
+ return Object.values(this.items);
587
592
  }
588
- get id() {
589
- return idGetter(this._id);
593
+ entries() {
594
+ return Object.entries(this.items);
590
595
  }
591
- set value(value) {
592
- this._value = value;
596
+ setLength(_type) {
593
597
  }
594
- get value() {
595
- return this._value;
598
+ first() {
599
+ return this.index(0);
596
600
  }
597
- /**
598
- * Set the gedcom reference for this object
599
- * @param gedcom - The GedComType to set as the gedcom reference
600
- */
601
- setGedcom(gedcom) {
602
- this._gedcom = gedcom;
601
+ last() {
602
+ return this.index(this.keys().length - 1);
603
603
  }
604
- // avoid to override
605
- get originalValue() {
606
- return this._value;
604
+ index(index) {
605
+ const keys = this.keys();
606
+ const key = keys[index];
607
+ return key && this.items[key];
607
608
  }
608
- get ref() {
609
- if (typeof this.value === "string" && isId(this.value) && this.refType) {
610
- return this._gedcom?.[getListTag(this.refType)]?.item(
611
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
612
- this.value
613
- );
609
+ item(name, value) {
610
+ if (arguments.length > 1) {
611
+ set(this.items, name, value);
612
+ this.setLength();
614
613
  }
615
- return this;
614
+ return get(this.items, name);
616
615
  }
617
- get main() {
618
- return this._main;
616
+ removeItem(name) {
617
+ unset(this.items, name);
618
+ this.setLength();
619
619
  }
620
- get parent() {
621
- return this._parent;
620
+ exclude(excludedList) {
621
+ excludedList.keys().forEach((itemId) => {
622
+ delete this.items[itemId];
623
+ });
624
+ this.setLength();
625
+ return this;
622
626
  }
623
- addRef(refNode) {
624
- if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
625
- if (!this._refs) {
626
- this._refs = new List();
627
- }
628
- 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 ?? {});
629
634
  }
635
+ this.setLength();
636
+ return this;
630
637
  }
631
- getRefs() {
632
- return this._refs;
633
- }
634
- exportValue() {
635
- return this.value;
638
+ merge(mergedList) {
639
+ this.concat(mergedList?.items);
640
+ return this;
636
641
  }
637
- removeValue() {
638
- delete this._value;
642
+ intersection(mergedList) {
643
+ const inter = intersectionBy(
644
+ this.values(),
645
+ mergedList?.values() ?? [],
646
+ "id"
647
+ );
648
+ return new _List(inter);
639
649
  }
640
- set(name, value) {
641
- if (typeof value === "string") {
642
- const usedValue = createCommon(this._gedcom, void 0, this.main);
643
- usedValue.value = value;
644
- set(this, name, usedValue);
645
- } else {
646
- set(this, name, value);
647
- }
648
- 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);
649
657
  }
650
- assign(name, value, unique = false) {
651
- let curValue = this.get(name);
652
- if (curValue?.isListable) {
653
- if (curValue instanceof _Common) {
654
- curValue = new List().concat({ ...[curValue] });
655
- 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");
656
673
  }
657
- const values = [
658
- ...curValue.values(),
659
- ...value instanceof List ? value.values() : [value]
660
- ];
661
- curValue.concat({
662
- ...!unique ? values : uniqBy(
663
- values,
664
- (value2) => value2 instanceof _Common ? value2?.value : void 0
665
- )
666
- });
667
- } else {
668
- this.set(name, value);
669
674
  }
670
- return get(this, name);
675
+ return this;
671
676
  }
672
- get(name) {
673
- if (!name.includes(".")) {
674
- return get(this, name);
675
- }
676
- const nameParts = name.split(".");
677
- let last = this;
678
- for (const part of nameParts) {
679
- const indexPart = Number(part);
680
- const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
681
- const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
682
- const usedPart = itemId || itemIndex;
683
- if (last instanceof List) {
684
- if (!isNaN(indexPart)) {
685
- last = last.index(indexPart);
686
- } else {
687
- if (usedPart) {
688
- last = last.item(usedPart);
689
- } else {
690
- last = last?.index(0)?.get(part);
691
- }
692
- }
693
- } else {
694
- const usedIndex = Number(itemIndex ?? indexPart);
695
- if (!isNaN(usedIndex)) {
696
- last = last?.toList().index(usedIndex);
697
- } else {
698
- last = last?.get(itemId ?? part);
699
- }
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;
700
683
  }
701
- if (!last) {
702
- break;
684
+ this.items[item.id] = item;
685
+ if (increase) {
686
+ this.setLength("increase");
703
687
  }
704
688
  }
705
- return last;
689
+ return this;
706
690
  }
707
- remove(name) {
708
- 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;
709
697
  }
710
- getGedcom() {
711
- 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;
712
703
  }
713
- getIf(name, condition, name2) {
714
- const nameParts = name.split(".");
715
- const name2Parts = name2.split(".");
716
- if (nameParts.length !== name2Parts.length) {
717
- throw new Error("Tag pathes must be of the same depth");
718
- }
719
- if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
720
- throw new Error("Final tags must be siblings");
721
- }
722
- const valueCommon = this.get(name);
723
- if (valueCommon instanceof _Common) {
724
- return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
725
- }
726
- const valueCommon2 = this.get(name2)?.toList();
727
- const ifList = new List();
728
- valueCommon?.forEach((item, key) => {
729
- const pair = valueCommon2?.item(key);
730
- ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
731
- });
732
- return ifList;
733
- }
734
- toString() {
735
- return this.value?.toString() || "";
736
- }
737
- toValue() {
738
- return this.value;
739
- }
740
- toProp(tag) {
741
- const prop = this.get(tag);
742
- return prop;
743
- }
744
- toList() {
745
- return new List().concat(
746
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
747
- this.id ? { [this.id]: this } : { ...[this] }
748
- );
749
- }
750
- index(i) {
751
- return this.toList()?.index(i);
704
+ except(item, ClassName = _List) {
705
+ return this.copy(ClassName).delete(item);
752
706
  }
753
- toValueList() {
754
- return new List().concat(
755
- this.value ? { [this.value]: this } : (
756
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
757
- { ...[this] }
758
- )
759
- );
707
+ forEach(iterate) {
708
+ this.entries().forEach(([key, item], index) => {
709
+ iterate(item, key, index);
710
+ });
760
711
  }
761
- standardizeObject(tag, options) {
762
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
763
- const standardize = this.standardizeMedia;
764
- return standardize.call(
765
- this,
766
- options.obje.namespace,
767
- options.obje.override
768
- );
769
- }
712
+ map(iterate) {
713
+ return this.entries().map(([key, item], index) => {
714
+ return iterate(item, key, index);
715
+ });
770
716
  }
771
- toJson(tag, options) {
772
- const json = this.toObject(tag, options);
773
- 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);
774
721
  }
775
- toObject(tag, options) {
776
- this.standardizeObject(tag, options);
777
- const validKeys = getValidKeys(this);
778
- let json = {};
779
- validKeys.forEach((key) => {
780
- if (key === "id" && this.id !== void 0) {
781
- json.id = this.id;
782
- } else if (key === "_id" && this._id !== void 0) {
783
- json.id = this._id;
784
- } else if (key === "value" && this.value !== void 0) {
785
- json.value = this.value;
786
- } else if (key === "_value" && this._value !== void 0) {
787
- json.value = this._value;
788
- } else {
789
- const validKey = key;
790
- const prop = this.get(validKey);
791
- if (typeof prop?.toObject === "function") {
792
- if (prop instanceof _Common) {
793
- json[validKey] = prop.toObject(validKey, options);
794
- } else {
795
- json = {
796
- ...json,
797
- ...prop.toObject(validKey, options)
798
- };
799
- }
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();
736
+ }
737
+ if (Array.isArray(value)) {
738
+ return value.includes(itemValue);
800
739
  }
740
+ return itemValue === value;
741
+ })) {
742
+ newList.item(itemId, item);
801
743
  }
802
744
  });
803
- return json;
745
+ return newList;
804
746
  }
805
- merge(other, override = false, avoidKeys = []) {
806
- if (!other) {
807
- return this;
808
- }
809
- const validKeys = getValidKeys(other);
810
- validKeys.forEach((validKey) => {
811
- if (avoidKeys.includes(validKey)) {
812
- 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;
813
757
  }
814
- const current = this.get(validKey);
815
- const mergeValue = other.get(validKey);
816
- if (mergeValue) {
817
- if (current instanceof _Common || current instanceof List) {
818
- this.assign(validKey, mergeValue, true);
819
- } else if (!current || override && validKey !== "id" && validKey !== "_id") {
820
- 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);
821
767
  }
768
+ return itemValue === value;
769
+ })) {
770
+ foundItem = item;
771
+ return true;
822
772
  }
773
+ return false;
823
774
  });
824
- return this;
775
+ return foundItem;
825
776
  }
826
- clone(newId = false, avoidKeys = []) {
827
- const ctor = this.constructor;
828
- const instance = new ctor(
829
- this._gedcom,
830
- this._id,
831
- this._main,
832
- this._parent
833
- );
834
- if (newId && this._id) {
835
- 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);
836
782
  }
837
- const cloned = createProxy(instance);
838
- cloned.merge(this, true, avoidKeys);
839
- return cloned;
840
- }
841
- // fromGedcom<T = typeof this>(value: string) {
842
- fromGedcom(_value) {
843
- }
844
- toGedcom(tag, level = 0, options) {
845
- const gedcom = this.toGedcomLines(tag, level, options);
846
- return gedcom.join("\n");
847
- }
848
- toGedcomLines(tag, level = 0, options) {
849
- this.standardizeObject(tag, options);
850
- const validKeys = getValidKeys(this);
851
- const gedcom = [];
852
- validKeys.forEach((key) => {
853
- const validKey = key;
854
- const prop = this.get(validKey);
855
- if (typeof prop?.toGedcomLines === "function") {
856
- if (prop instanceof _Common) {
857
- const value = prop.exportValue();
858
- gedcom.push(
859
- `${level} ${validKey}${value ? ` ${value}` : ""}`
860
- );
861
- gedcom.push(
862
- ...prop.toGedcomLines(validKey, level + 1, options)
863
- );
864
- } else {
865
- gedcom.push(
866
- ...prop.toGedcomLines(validKey, level, options)
867
- );
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;
868
810
  }
811
+ return sortValue2 * (direction === "DESC" ? -1 : 1);
869
812
  }
813
+ );
814
+ sortedItems.forEach(([key, value]) => {
815
+ newList.item(key, value);
870
816
  });
871
- return gedcom;
872
- }
873
- isGenoPro() {
874
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
875
- const sour = get(head, "SOUR.value");
876
- return !!sour?.toLowerCase()?.startsWith("genopro");
877
- }
878
- isAhnenblatt() {
879
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
880
- const sour = get(head, "SOUR.value");
881
- return !!sour?.toLowerCase()?.startsWith("ahnenblatt");
882
- }
883
- isGeni() {
884
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
885
- const sour = get(head, "SOUR.value");
886
- return !!sour?.toLowerCase()?.startsWith("geni");
887
- }
888
- isAncestry() {
889
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
890
- const sour = get(head, "SOUR.value");
891
- return !!sour?.toLowerCase()?.startsWith("ancestry");
892
- }
893
- isMyHeritage() {
894
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
895
- const sour = get(head, "SOUR.value");
896
- return !!sour?.toLowerCase()?.startsWith("myheritage");
817
+ return newList;
897
818
  }
898
- /**
899
- * Get the source type as a string (for prefixing tree IDs and names)
900
- * Returns the detected source type or undefined if unknown
901
- */
902
- getSourceType() {
903
- if (this.isAncestry()) return "Ancestry";
904
- if (this.isMyHeritage()) return "MyHeritage";
905
- if (this.isFamilySearch()) return "FamilySearch";
906
- if (this.isGNO2GED()) return "GNO2GED";
907
- if (this.isGenoPro()) return "GenoPro";
908
- if (this.isAhnenblatt()) return "Ahnenblatt";
909
- if (this.isGeni()) return "Geni";
910
- 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;
911
885
  }
912
- isFamilySearch() {
913
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
914
- const sourName = get(head, "SOUR.NAME.value");
915
- return sourName === "FamilySearch API";
886
+ findIndex(item) {
887
+ if (!item?.id) {
888
+ return -1;
889
+ }
890
+ return this.keys().findIndex((key) => key === item.id);
916
891
  }
917
- isGNO2GED() {
918
- const head = get(this, "HEAD") || get(this.getGedcom(), "HEAD");
919
- const sour = get(head, "SOUR.value");
920
- return sour === "GNO2GED";
892
+ getItems() {
893
+ return this.items;
921
894
  }
922
- getAncestryTreeId() {
923
- const path = "HEAD.SOUR._TREE.RIN.value";
924
- return get(this, path) || get(this.getGedcom(), path);
895
+ toJson(tag, options) {
896
+ const json = this.toObject(tag, options);
897
+ return JSON.stringify(json);
925
898
  }
926
- getMyHeritageTreeId() {
927
- const path = "HEAD._EXPORTED_FROM_SITE_ID.value";
928
- 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;
929
918
  }
930
- getTreeId() {
931
- if (this?.isAncestry()) {
932
- const id = this.getAncestryTreeId();
933
- if (id !== void 0) return id;
934
- }
935
- if (this?.isMyHeritage()) {
936
- const id = this.getMyHeritageTreeId();
937
- if (id !== void 0) return id;
938
- }
939
- if (this?.isFamilySearch()) {
940
- const id = this.getFamilySearchTreeId();
941
- if (id !== void 0) return id;
942
- }
943
- if (this?.isGNO2GED()) {
944
- const id = this.getGNO2GEDTreeId();
945
- if (id !== void 0) return id;
946
- }
947
- if (this?.isAhnenblatt()) {
948
- const id = this.getAhnenblattTreeId();
949
- if (id !== void 0) return id;
950
- }
951
- if (this?.isGeni()) {
952
- const id = this.getGeniTreeId();
953
- if (id !== void 0) return id;
954
- }
955
- if (this?.isGenoPro()) {
956
- const id = this.getGenoProTreeId();
957
- if (id !== void 0) return id;
958
- }
959
- return this.getUniversalTreeId();
919
+ toGedcom(tag, level = 0, options) {
920
+ const gedcom = this.toGedcomLines(tag, level, options);
921
+ return gedcom.join("\n");
960
922
  }
961
- getAncestryTreeName() {
962
- const path = "HEAD.SOUR._TREE.value";
963
- 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 validKey = item.id;
928
+ const validValue = validKey ? validTag : item.exportValue();
929
+ gedcom.push(
930
+ `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
931
+ );
932
+ gedcom.push(
933
+ ...item.toGedcomLines(
934
+ validTag,
935
+ level + 1,
936
+ options
937
+ )
938
+ );
939
+ });
940
+ return gedcom;
964
941
  }
965
- getMyHeritageTreeName() {
966
- const path = "HEAD.FILE.value";
967
- const treeDetails = get(this, path) || get(this.getGedcom(), path);
968
- return treeDetails?.match(
969
- /Exported by MyHeritage.com from (?<tree>.+) in.+$/
970
- )?.groups?.tree;
942
+ toValue() {
943
+ const propList = new _List();
944
+ this.entries().forEach(([key, item]) => {
945
+ propList.item(key, item?.toValue());
946
+ });
947
+ return propList;
971
948
  }
972
- getFamilySearchTreeId() {
973
- const rin = get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
974
- if (rin) {
975
- return rin;
976
- }
977
- return "familysearch";
949
+ // @deprecated use get instead
950
+ toProp(tag) {
951
+ return this.get(tag);
978
952
  }
979
- getFamilySearchTreeName() {
980
- const treeName = get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
981
- if (treeName) {
982
- return treeName;
953
+ toList() {
954
+ return new _List().concat(this.items);
955
+ }
956
+ toValueList() {
957
+ const newList = new _List();
958
+ this.values().forEach((value) => {
959
+ value.value && newList.item(value.value, value);
960
+ });
961
+ return newList;
962
+ }
963
+ };
964
+
965
+ // src/classes/common.ts
966
+ var Common = class _Common {
967
+ constructor(gedcom, id, main, parent) {
968
+ this.isListable = true;
969
+ this._gedcom = gedcom;
970
+ this._main = main;
971
+ this._parent = parent;
972
+ this.initUniqueId();
973
+ if (id) {
974
+ this._id = id;
975
+ } else {
976
+ delete this._id;
983
977
  }
984
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
985
- return fileName || "FamilySearch Import";
986
978
  }
987
- getAhnenblattTreeId() {
988
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
989
- if (fileName) {
990
- const idMatch = fileName.match(/_(\d+)/);
991
- if (idMatch) {
992
- return idMatch[1];
993
- }
979
+ set type(type) {
980
+ if (type) {
981
+ this.uniqueId = this.uniqueId?.replace(/^[^:]+/, `~${type}`);
994
982
  }
995
- return void 0;
983
+ this._type = type;
996
984
  }
997
- getAhnenblattTreeName() {
998
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
999
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
985
+ get type() {
986
+ return this._type;
1000
987
  }
1001
- getGeniTreeId() {
1002
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1003
- if (fileName) {
1004
- const idMatch = fileName.match(/_(\d+)/);
1005
- if (idMatch) {
1006
- return idMatch[1];
1007
- }
988
+ initUniqueId() {
989
+ if (this._gedcom) {
990
+ this.uniqueId = `~Common:${this._gedcom.refcount++}`;
1008
991
  }
1009
- return void 0;
1010
992
  }
1011
- getGeniTreeName() {
1012
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1013
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1014
- }
1015
- getGenoProTreeId() {
1016
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1017
- if (fileName) {
1018
- const idMatch = fileName.match(/_(\d+)/);
1019
- if (idMatch) {
1020
- return idMatch[1];
993
+ set uniqueId(id) {
994
+ const currentId = this._uniqueId;
995
+ if (this._gedcom?.reflist && id) {
996
+ this._uniqueId = id;
997
+ if (currentId && this._gedcom.reflist[currentId]) {
998
+ delete this._gedcom.reflist[currentId];
1021
999
  }
1000
+ this._gedcom.reflist[id] = this;
1022
1001
  }
1023
- return void 0;
1024
1002
  }
1025
- getGenoProTreeName() {
1026
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1027
- return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1003
+ get uniqueId() {
1004
+ return this._uniqueId;
1028
1005
  }
1029
- getGNO2GEDTreeId() {
1030
- const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
1031
- if (rin) {
1032
- return rin;
1033
- }
1034
- return `gno_${this._gedcom?.refcount || "unknown"}`;
1006
+ set id(id) {
1007
+ this._id = idGetter(id);
1035
1008
  }
1036
- getGNO2GEDTreeName() {
1037
- const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
1038
- if (treeName) {
1039
- return treeName;
1040
- }
1041
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1042
- return fileName || "GNO2GED Export";
1009
+ get id() {
1010
+ return idGetter(this._id);
1043
1011
  }
1044
- /**
1045
- * Universal tree ID getter for unknown/unrecognized GEDCOM sources
1046
- * Tries to extract an ID from various common locations
1047
- */
1048
- getUniversalTreeId() {
1049
- const sourceType = this.getSourceType();
1050
- const prefix = sourceType ? sourceType.toLowerCase() : "tree";
1051
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1052
- if (fileName) {
1053
- const idMatch = fileName.match(/_(\d+)/);
1054
- if (idMatch) {
1055
- return `${prefix}_${idMatch[1]}`;
1056
- }
1057
- }
1058
- return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
1012
+ set value(value) {
1013
+ this._value = value;
1014
+ }
1015
+ get value() {
1016
+ return this._value;
1059
1017
  }
1060
1018
  /**
1061
- * Universal tree name getter for unknown/unrecognized GEDCOM sources
1062
- * Tries to extract a name from various common locations
1019
+ * Set the gedcom reference for this object
1020
+ * @param gedcom - The GedComType to set as the gedcom reference
1063
1021
  */
1064
- getUniversalTreeName() {
1065
- const sourceType = this.getSourceType();
1066
- const prefix = sourceType ? `${sourceType}-` : "";
1067
- const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1068
- if (fileName) {
1069
- const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1070
- return `${prefix}${cleanName}`;
1071
- }
1072
- const sourName = get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1073
- if (sourName) {
1074
- return `${prefix}${sourName}`;
1075
- }
1076
- const sourValue = get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1077
- if (sourValue) {
1078
- return `${prefix}${sourValue}`;
1079
- }
1080
- return `${prefix}Unknown Tree`;
1022
+ setGedcom(gedcom) {
1023
+ this._gedcom = gedcom;
1081
1024
  }
1082
- getTreeName() {
1083
- if (this?.isAncestry()) {
1084
- const name = this.getAncestryTreeName();
1085
- if (name) return name;
1086
- }
1087
- if (this?.isMyHeritage()) {
1088
- const name = this.getMyHeritageTreeName();
1089
- if (name) return name;
1025
+ // avoid to override
1026
+ get originalValue() {
1027
+ return this._value;
1028
+ }
1029
+ get ref() {
1030
+ if (typeof this.value === "string" && isId(this.value) && this.refType) {
1031
+ return this._gedcom?.[getListTag(this.refType)]?.item(
1032
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1033
+ this.value
1034
+ );
1090
1035
  }
1091
- if (this?.isFamilySearch()) {
1092
- const name = this.getFamilySearchTreeName();
1093
- if (name) return name;
1036
+ return this;
1037
+ }
1038
+ get main() {
1039
+ return this._main;
1040
+ }
1041
+ get parent() {
1042
+ return this._parent;
1043
+ }
1044
+ addRef(refNode) {
1045
+ if (typeof refNode.value === "string" && isId(refNode.value) && refNode.refType) {
1046
+ if (!this._refs) {
1047
+ this._refs = new List();
1048
+ }
1049
+ this._refs.item(refNode.value, refNode);
1094
1050
  }
1095
- if (this?.isGNO2GED()) {
1096
- const name = this.getGNO2GEDTreeName();
1097
- if (name) return name;
1051
+ }
1052
+ getRefs() {
1053
+ return this._refs;
1054
+ }
1055
+ exportValue() {
1056
+ return this.value;
1057
+ }
1058
+ removeValue() {
1059
+ delete this._value;
1060
+ }
1061
+ set(name, value) {
1062
+ if (isGedcomTrailerTag(name)) {
1063
+ return void 0;
1098
1064
  }
1099
- if (this?.isAhnenblatt()) {
1100
- const name = this.getAhnenblattTreeName();
1101
- if (name) return name;
1065
+ if (typeof value === "string") {
1066
+ const usedValue = createCommon(this._gedcom, void 0, this.main);
1067
+ usedValue.value = value;
1068
+ set(this, name, usedValue);
1069
+ } else {
1070
+ set(this, name, value);
1102
1071
  }
1103
- if (this?.isGeni()) {
1104
- const name = this.getGeniTreeName();
1105
- if (name) return name;
1072
+ return get(this, name);
1073
+ }
1074
+ assign(name, value, unique = false) {
1075
+ if (isGedcomTrailerTag(name)) {
1076
+ return void 0;
1106
1077
  }
1107
- if (this?.isGenoPro()) {
1108
- const name = this.getGenoProTreeName();
1109
- if (name) return name;
1078
+ let curValue = this.get(name);
1079
+ if (curValue?.isListable) {
1080
+ if (curValue instanceof _Common) {
1081
+ curValue = new List().concat({ ...[curValue] });
1082
+ this.set(name, curValue);
1083
+ }
1084
+ const values = [
1085
+ ...curValue.values(),
1086
+ ...value instanceof List ? value.values() : [value]
1087
+ ];
1088
+ curValue.concat({
1089
+ ...!unique ? values : uniqBy(
1090
+ values,
1091
+ (value2) => value2 instanceof _Common ? value2?.value : void 0
1092
+ )
1093
+ });
1094
+ } else {
1095
+ this.set(name, value);
1110
1096
  }
1111
- return this.getUniversalTreeName();
1097
+ return get(this, name);
1112
1098
  }
1113
- };
1114
- var createProxy = (target) => {
1115
- return new Proxy(target, {
1116
- get(t, prop, receiver) {
1117
- if (prop === "unwrapped") {
1118
- return target;
1119
- }
1120
- if (prop in t) {
1121
- return Reflect.get(t, prop, receiver);
1122
- }
1123
- if (!isOnlyMainProp(prop)) {
1124
- const ref = t.ref;
1125
- if (ref && prop in ref) {
1126
- return Reflect.get(ref, prop, receiver);
1099
+ get(name) {
1100
+ if (!name.includes(".")) {
1101
+ return get(this, name);
1102
+ }
1103
+ const nameParts = name.split(".");
1104
+ let last = this;
1105
+ for (const part of nameParts) {
1106
+ const indexPart = Number(part);
1107
+ const itemIndex = part.match(/items\[(?<itemIndex>\d+)\]/)?.groups?.itemIndex;
1108
+ const itemId = part.match(/items\[(?<itemId>@[A-Z]+\d+@)\]/)?.groups?.itemId;
1109
+ const usedPart = itemId || itemIndex;
1110
+ if (last instanceof List) {
1111
+ if (!isNaN(indexPart)) {
1112
+ last = last.index(indexPart);
1113
+ } else {
1114
+ if (usedPart) {
1115
+ last = last.item(usedPart);
1116
+ } else {
1117
+ last = last?.index(0)?.get(part);
1118
+ }
1119
+ }
1120
+ } else {
1121
+ const usedIndex = Number(itemIndex ?? indexPart);
1122
+ if (!isNaN(usedIndex)) {
1123
+ last = last?.toList().index(usedIndex);
1124
+ } else {
1125
+ last = last?.get(itemId ?? part);
1127
1126
  }
1128
1127
  }
1128
+ if (!last) {
1129
+ break;
1130
+ }
1129
1131
  }
1130
- });
1131
- };
1132
- var createCommon = (gedcom, id, main, parent) => {
1133
- return createProxy(
1134
- new Common(gedcom, id, main, parent)
1135
- );
1136
- };
1137
- var isOnlyMainProp = (key) => {
1138
- return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
1139
- };
1140
- var isValidKey = (common, key) => {
1141
- const prop = get(common, key);
1142
- 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);
1143
- };
1144
- var getValidKeys = (common) => {
1145
- return Object.keys(common).filter((key) => {
1146
- return isValidKey(common, key);
1147
- });
1148
- };
1149
- var getValidTag = (tag) => {
1150
- return tag.replace(/^@@/, "");
1151
- };
1152
- var getListTag = (tag) => {
1153
- return `@@${tag}`;
1154
- };
1155
- var isId = (string) => {
1156
- return ID_REG.test(string);
1157
- };
1158
- var idGetter = (id) => {
1159
- if (id === void 0) {
1160
- return void 0;
1132
+ return last;
1161
1133
  }
1162
- let newId = `${id}`;
1163
- const parts = newId?.match(ID_GETTER_REG)?.groups;
1164
- if (!parts?.at && !parts?.letter) {
1165
- newId = `@U${newId}`;
1166
- } else if (!parts.at) {
1167
- newId = `@${newId}`;
1168
- } else if (!parts.letter) {
1169
- newId = newId.replace(/^@/, "@U");
1134
+ remove(name) {
1135
+ unset(this, name);
1170
1136
  }
1171
- if (!newId.endsWith("@")) {
1172
- newId = `${newId}@`;
1137
+ getGedcom() {
1138
+ return this._gedcom;
1173
1139
  }
1174
- return newId;
1175
- };
1176
-
1177
- // src/classes/list.ts
1178
- var List = class _List {
1179
- // length = 0;
1180
- constructor(items) {
1181
- this.items = {};
1182
- this.isListable = true;
1183
- this.marker = void 0;
1184
- if (items) {
1185
- this.concat(items);
1140
+ getIf(name, condition, name2) {
1141
+ const nameParts = name.split(".");
1142
+ const name2Parts = name2.split(".");
1143
+ if (nameParts.length !== name2Parts.length) {
1144
+ throw new Error("Tag pathes must be of the same depth");
1186
1145
  }
1187
- }
1188
- get length() {
1189
- return this.values().length;
1190
- }
1191
- has(indi) {
1192
- return !!this.item(
1193
- typeof indi === "string" ? indi : indi?.id ?? ""
1194
- );
1195
- }
1196
- get(name) {
1197
- const propList = new _List();
1198
- this.entries().forEach(([key, item]) => {
1199
- propList.item(key, item.get(name));
1200
- });
1201
- return propList;
1202
- }
1203
- getIf(name, condition, name2) {
1204
- const propList = new _List();
1205
- this.entries().forEach(([key, item]) => {
1206
- const passedItem = item.getIf(name, condition, name2);
1207
- if (passedItem) {
1208
- propList.item(key, passedItem);
1209
- }
1146
+ if (nameParts.length > 1 && nameParts[0] !== name2Parts[0]) {
1147
+ throw new Error("Final tags must be siblings");
1148
+ }
1149
+ const valueCommon = this.get(name);
1150
+ if (valueCommon instanceof _Common) {
1151
+ return valueCommon?.toValue() !== condition ? void 0 : this.get(name2);
1152
+ }
1153
+ const valueCommon2 = this.get(name2)?.toList();
1154
+ const ifList = new List();
1155
+ valueCommon?.forEach((item, key) => {
1156
+ const pair = valueCommon2?.item(key);
1157
+ ifList.item(key, item?.toValue() !== condition ? void 0 : pair);
1210
1158
  });
1211
- return propList;
1212
- }
1213
- keys() {
1214
- return Object.keys(this.items);
1159
+ return ifList;
1215
1160
  }
1216
- values() {
1217
- return Object.values(this.items);
1161
+ toString() {
1162
+ return this.value?.toString() || "";
1218
1163
  }
1219
- entries() {
1220
- return Object.entries(this.items);
1164
+ toValue() {
1165
+ return this.value;
1221
1166
  }
1222
- setLength(_type) {
1167
+ toProp(tag) {
1168
+ const prop = this.get(tag);
1169
+ return prop;
1223
1170
  }
1224
- first() {
1225
- return this.index(0);
1171
+ toList() {
1172
+ return new List().concat(
1173
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1174
+ this.id ? { [this.id]: this } : { ...[this] }
1175
+ );
1226
1176
  }
1227
- last() {
1228
- return this.index(this.keys().length - 1);
1177
+ index(i) {
1178
+ return this.toList()?.index(i);
1229
1179
  }
1230
- index(index) {
1231
- const keys = this.keys();
1232
- const key = keys[index];
1233
- return key && this.items[key];
1180
+ toValueList() {
1181
+ return new List().concat(
1182
+ this.value ? { [this.value]: this } : (
1183
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1184
+ { ...[this] }
1185
+ )
1186
+ );
1234
1187
  }
1235
- item(name, value) {
1236
- if (arguments.length > 1) {
1237
- set(this.items, name, value);
1238
- this.setLength();
1188
+ standardizeObject(tag, options) {
1189
+ if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
1190
+ const standardize = this.standardizeMedia;
1191
+ return standardize.call(
1192
+ this,
1193
+ options.obje.namespace,
1194
+ options.obje.override
1195
+ );
1239
1196
  }
1240
- return get(this.items, name);
1241
1197
  }
1242
- removeItem(name) {
1243
- unset(this.items, name);
1244
- this.setLength();
1198
+ toJson(tag, options) {
1199
+ const json = this.toObject(tag, options);
1200
+ return JSON.stringify(json);
1245
1201
  }
1246
- exclude(excludedList) {
1247
- excludedList.keys().forEach((itemId) => {
1248
- delete this.items[itemId];
1202
+ toObject(tag, options) {
1203
+ this.standardizeObject(tag, options);
1204
+ const validKeys = getValidKeys(this);
1205
+ let json = {};
1206
+ validKeys.forEach((key) => {
1207
+ if (key === "id" && this.id !== void 0) {
1208
+ json.id = this.id;
1209
+ } else if (key === "_id" && this._id !== void 0) {
1210
+ json.id = this._id;
1211
+ } else if (key === "value" && this.value !== void 0) {
1212
+ json.value = this.value;
1213
+ } else if (key === "_value" && this._value !== void 0) {
1214
+ json.value = this._value;
1215
+ } else {
1216
+ const validKey = key;
1217
+ const prop = this.get(validKey);
1218
+ if (typeof prop?.toObject === "function") {
1219
+ if (prop instanceof _Common) {
1220
+ json[validKey] = prop.toObject(validKey, options);
1221
+ } else {
1222
+ json = {
1223
+ ...json,
1224
+ ...prop.toObject(validKey, options)
1225
+ };
1226
+ }
1227
+ }
1228
+ }
1249
1229
  });
1250
- this.setLength();
1251
- return this;
1230
+ return json;
1252
1231
  }
1253
- concat(items) {
1254
- if (Array.isArray(items)) {
1255
- items.forEach((item) => {
1256
- this.append(item);
1257
- });
1258
- } else {
1259
- Object.assign(this.items, items ?? {});
1232
+ merge(other, override = false, avoidKeys = []) {
1233
+ if (!other) {
1234
+ return this;
1260
1235
  }
1261
- this.setLength();
1262
- return this;
1263
- }
1264
- merge(mergedList) {
1265
- this.concat(mergedList?.items);
1236
+ const validKeys = getValidKeys(other);
1237
+ validKeys.forEach((validKey) => {
1238
+ if (avoidKeys.includes(validKey)) {
1239
+ return;
1240
+ }
1241
+ const current = this.get(validKey);
1242
+ const mergeValue = other.get(validKey);
1243
+ if (mergeValue) {
1244
+ if (current instanceof _Common || current instanceof List) {
1245
+ this.assign(validKey, mergeValue, true);
1246
+ } else if (!current || override && validKey !== "id" && validKey !== "_id") {
1247
+ this.set(validKey, mergeValue);
1248
+ }
1249
+ }
1250
+ });
1266
1251
  return this;
1267
1252
  }
1268
- intersection(mergedList) {
1269
- const inter = intersectionBy(
1270
- this.values(),
1271
- mergedList?.values() ?? [],
1272
- "id"
1253
+ clone(newId = false, avoidKeys = []) {
1254
+ const ctor = this.constructor;
1255
+ const instance = new ctor(
1256
+ this._gedcom,
1257
+ this._id,
1258
+ this._main,
1259
+ this._parent
1273
1260
  );
1274
- return new _List(inter);
1261
+ if (newId && this._id) {
1262
+ instance.id = `IFT${this._id.replace(/[^0-9]/g, "")}`;
1263
+ }
1264
+ const cloned = createProxy(instance);
1265
+ cloned.merge(this, true, avoidKeys);
1266
+ return cloned;
1275
1267
  }
1276
- difference(mergedList) {
1277
- const inter = differenceBy(
1278
- this.values(),
1279
- mergedList?.values() ?? [],
1280
- "id"
1281
- );
1282
- return new _List(inter);
1268
+ // fromGedcom<T = typeof this>(value: string) {
1269
+ fromGedcom(_value) {
1283
1270
  }
1284
- prepend(item) {
1285
- if (item.id) {
1286
- let increase = true;
1287
- if (this.items[item.id]) {
1288
- delete this.items[item.id];
1289
- increase = false;
1290
- }
1291
- this.items = Object.assign(
1292
- {
1293
- [item.id]: item
1294
- },
1295
- this.items
1296
- );
1297
- if (increase) {
1298
- this.setLength("increase");
1299
- }
1300
- }
1301
- return this;
1271
+ toGedcom(tag, level = 0, options) {
1272
+ const gedcom = this.toGedcomLines(tag, level, options);
1273
+ return gedcom.join("\n");
1302
1274
  }
1303
- append(item) {
1304
- if (item.id) {
1305
- let increase = true;
1306
- if (this.items[item.id]) {
1307
- delete this.items[item.id];
1308
- increase = false;
1309
- }
1310
- this.items[item.id] = item;
1311
- if (increase) {
1312
- this.setLength("increase");
1275
+ toGedcomLines(tag, level = 0, options) {
1276
+ this.standardizeObject(tag, options);
1277
+ const validKeys = getValidKeys(this);
1278
+ const gedcom = [];
1279
+ validKeys.forEach((key) => {
1280
+ const validKey = key;
1281
+ const prop = this.get(validKey);
1282
+ if (typeof prop?.toGedcomLines === "function") {
1283
+ if (prop instanceof _Common) {
1284
+ const value = prop.exportValue();
1285
+ gedcom.push(
1286
+ `${level} ${validKey}${value ? ` ${value}` : ""}`
1287
+ );
1288
+ gedcom.push(
1289
+ ...prop.toGedcomLines(validKey, level + 1, options)
1290
+ );
1291
+ } else {
1292
+ gedcom.push(
1293
+ ...prop.toGedcomLines(validKey, level, options)
1294
+ );
1295
+ }
1313
1296
  }
1314
- }
1315
- return this;
1297
+ });
1298
+ return gedcom;
1316
1299
  }
1317
- delete(item) {
1318
- if (item.id && this.items[item.id]) {
1319
- delete this.items[item.id];
1320
- this.setLength("decrease");
1321
- }
1322
- return this;
1300
+ getHeadRecord() {
1301
+ return get(this, "HEAD") || get(this.getGedcom(), "HEAD");
1323
1302
  }
1324
- copy(ClassName = _List) {
1325
- const newList = new ClassName();
1326
- Object.assign(newList.items, this.items);
1327
- newList.setLength();
1328
- return newList;
1303
+ getOriginalHeadRecord() {
1304
+ return get(this, ORIG_HEAD_TAG) || get(this.getGedcom(), ORIG_HEAD_TAG) || get(this.getHeadRecord(), ORIG_HEAD_TAG);
1329
1305
  }
1330
- except(item, ClassName = _List) {
1331
- return this.copy(ClassName).delete(item);
1306
+ getSourceHeads() {
1307
+ const heads = [this.getOriginalHeadRecord(), this.getHeadRecord()];
1308
+ return heads.filter((head, index) => {
1309
+ return Boolean(head && heads.indexOf(head) === index);
1310
+ });
1332
1311
  }
1333
- forEach(iterate) {
1334
- this.entries().forEach(([key, item], index) => {
1335
- iterate(item, key, index);
1312
+ getFromSourceHeads(path) {
1313
+ for (const head of this.getSourceHeads()) {
1314
+ const value = get(head, path);
1315
+ if (value !== void 0 && value !== null && value !== "") {
1316
+ return value;
1317
+ }
1318
+ }
1319
+ return void 0;
1320
+ }
1321
+ sourStartsWith(prefix) {
1322
+ return this.getSourceHeads().some((head) => {
1323
+ const sour = get(head, "SOUR.value");
1324
+ return !!sour?.toLowerCase()?.startsWith(prefix);
1336
1325
  });
1337
1326
  }
1338
- map(iterate) {
1339
- return this.entries().map(([key, item], index) => {
1340
- return iterate(item, key, index);
1327
+ isGenoPro() {
1328
+ return this.sourStartsWith("genopro");
1329
+ }
1330
+ isAhnenblatt() {
1331
+ return this.sourStartsWith("ahnenblatt");
1332
+ }
1333
+ isGeni() {
1334
+ return this.sourStartsWith("geni");
1335
+ }
1336
+ isAncestry() {
1337
+ return this.sourStartsWith("ancestry");
1338
+ }
1339
+ isMyHeritage() {
1340
+ return this.sourStartsWith("myheritage");
1341
+ }
1342
+ /**
1343
+ * Get the source type as a string (for prefixing tree IDs and names)
1344
+ * Returns the detected source type or undefined if unknown
1345
+ */
1346
+ getSourceType() {
1347
+ if (this.isAncestry()) return "Ancestry";
1348
+ if (this.isMyHeritage()) return "MyHeritage";
1349
+ if (this.isFamilySearch()) return "FamilySearch";
1350
+ if (this.isGNO2GED()) return "GNO2GED";
1351
+ if (this.isGenoPro()) return "GenoPro";
1352
+ if (this.isAhnenblatt()) return "Ahnenblatt";
1353
+ if (this.isGeni()) return "Geni";
1354
+ return void 0;
1355
+ }
1356
+ isFamilySearch() {
1357
+ return this.getSourceHeads().some((head) => {
1358
+ const sourName = get(head, "SOUR.NAME.value");
1359
+ return sourName === "FamilySearch API";
1341
1360
  });
1342
1361
  }
1343
- reduce(iterate, initialValue) {
1344
- return this.entries().reduce((acc, [key, item], index) => {
1345
- return iterate(acc, item, key, index);
1346
- }, initialValue);
1362
+ isGNO2GED() {
1363
+ return this.getSourceHeads().some((head) => {
1364
+ const sour = get(head, "SOUR.value");
1365
+ return sour === "GNO2GED";
1366
+ });
1347
1367
  }
1348
- filter(filters, ClassName = _List) {
1349
- const newList = new ClassName();
1350
- const isIterator = typeof filters === "function";
1351
- this.entries().forEach(([itemId, item], index) => {
1352
- if (isIterator) {
1353
- filters(item, itemId, index) && newList.item(itemId, item);
1354
- return;
1355
- }
1356
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1357
- let itemValue;
1358
- if (key === "id") {
1359
- itemValue = item.toProp("id");
1360
- } else {
1361
- itemValue = item.toProp(key)?.toValue();
1362
- }
1363
- if (Array.isArray(value)) {
1364
- return value.includes(itemValue);
1365
- }
1366
- return itemValue === value;
1367
- })) {
1368
- newList.item(itemId, item);
1368
+ getAncestryTreeId() {
1369
+ return this.getFromSourceHeads("SOUR._TREE.RIN.value") || (get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value"));
1370
+ }
1371
+ getMyHeritageTreeId() {
1372
+ 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"));
1373
+ }
1374
+ getTreeId() {
1375
+ if (this?.isAncestry()) {
1376
+ const id = this.getAncestryTreeId();
1377
+ if (id !== void 0) return id;
1378
+ }
1379
+ if (this?.isMyHeritage()) {
1380
+ const id = this.getMyHeritageTreeId();
1381
+ if (id !== void 0) return id;
1382
+ }
1383
+ if (this?.isFamilySearch()) {
1384
+ const id = this.getFamilySearchTreeId();
1385
+ if (id !== void 0) return id;
1386
+ }
1387
+ if (this?.isGNO2GED()) {
1388
+ const id = this.getGNO2GEDTreeId();
1389
+ if (id !== void 0) return id;
1390
+ }
1391
+ if (this?.isAhnenblatt()) {
1392
+ const id = this.getAhnenblattTreeId();
1393
+ if (id !== void 0) return id;
1394
+ }
1395
+ if (this?.isGeni()) {
1396
+ const id = this.getGeniTreeId();
1397
+ if (id !== void 0) return id;
1398
+ }
1399
+ if (this?.isGenoPro()) {
1400
+ const id = this.getGenoProTreeId();
1401
+ if (id !== void 0) return id;
1402
+ }
1403
+ return this.getUniversalTreeId();
1404
+ }
1405
+ getAncestryTreeName() {
1406
+ return this.getFromSourceHeads("SOUR._TREE.value") || (get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value"));
1407
+ }
1408
+ getMyHeritageTreeName() {
1409
+ const path = "HEAD.FILE.value";
1410
+ const treeDetails = get(this, path) || get(this.getGedcom(), path);
1411
+ return treeDetails?.match(
1412
+ /Exported by MyHeritage.com from (?<tree>.+) in.+$/
1413
+ )?.groups?.tree;
1414
+ }
1415
+ getFamilySearchTreeId() {
1416
+ const rin = this.getFromSourceHeads("SOUR._TREE.RIN.value") || get(this, "HEAD.SOUR._TREE.RIN.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.RIN.value");
1417
+ if (rin) {
1418
+ return rin;
1419
+ }
1420
+ return "familysearch";
1421
+ }
1422
+ getFamilySearchTreeName() {
1423
+ const treeName = this.getFromSourceHeads("SOUR._TREE.value") || get(this, "HEAD.SOUR._TREE.value") || get(this.getGedcom(), "HEAD.SOUR._TREE.value");
1424
+ if (treeName) {
1425
+ return treeName;
1426
+ }
1427
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1428
+ return fileName || "FamilySearch Import";
1429
+ }
1430
+ getAhnenblattTreeId() {
1431
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1432
+ if (fileName) {
1433
+ const idMatch = fileName.match(/_(\d+)/);
1434
+ if (idMatch) {
1435
+ return idMatch[1];
1369
1436
  }
1370
- });
1371
- return newList;
1437
+ }
1438
+ return void 0;
1372
1439
  }
1373
- find(filters, _ClassName = _List) {
1374
- let foundItem;
1375
- const isIterator = typeof filters === "function";
1376
- this.entries().find(([itemId, item], index) => {
1377
- if (isIterator) {
1378
- if (filters(item, itemId, index)) {
1379
- foundItem = item;
1380
- return true;
1381
- }
1382
- return false;
1440
+ getAhnenblattTreeName() {
1441
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1442
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1443
+ }
1444
+ getGeniTreeId() {
1445
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1446
+ if (fileName) {
1447
+ const idMatch = fileName.match(/_(\d+)/);
1448
+ if (idMatch) {
1449
+ return idMatch[1];
1383
1450
  }
1384
- if (!Object.keys(filters).length || Object.entries(filters).every(([key, value]) => {
1385
- let itemValue;
1386
- if (key === "id") {
1387
- itemValue = item.toProp("id");
1388
- } else {
1389
- itemValue = item.toProp(key)?.toValue();
1390
- }
1391
- if (Array.isArray(value)) {
1392
- return value.includes(itemValue);
1393
- }
1394
- return itemValue === value;
1395
- })) {
1396
- foundItem = item;
1397
- return true;
1451
+ }
1452
+ return void 0;
1453
+ }
1454
+ getGeniTreeName() {
1455
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1456
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1457
+ }
1458
+ getGenoProTreeId() {
1459
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1460
+ if (fileName) {
1461
+ const idMatch = fileName.match(/_(\d+)/);
1462
+ if (idMatch) {
1463
+ return idMatch[1];
1398
1464
  }
1399
- return false;
1400
- });
1401
- return foundItem;
1465
+ }
1466
+ return void 0;
1402
1467
  }
1403
- orderBy(orders, ClassName = _List) {
1404
- const newList = new ClassName();
1405
- const isIterator = typeof orders === "function";
1406
- if (!isIterator && (!orders || !Object.keys(orders).length)) {
1407
- return newList.merge(this);
1468
+ getGenoProTreeName() {
1469
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1470
+ return fileName?.replace(/\.ged$/i, "").replace(/_/g, " ");
1471
+ }
1472
+ getGNO2GEDTreeId() {
1473
+ const rin = get(this, "HEAD._TREE.RIN.value") || get(this.getGedcom(), "HEAD._TREE.RIN.value");
1474
+ if (rin) {
1475
+ return rin;
1408
1476
  }
1409
- const sortedItems = this.entries().toSorted(
1410
- ([itemAId, itemA], [itemBId, itemB]) => {
1411
- if (isIterator) {
1412
- return orders(itemA, itemAId, itemB, itemBId);
1413
- }
1414
- const [key, { direction, getter }] = Object.entries(
1415
- typeof orders === "string" ? { [orders]: { direction: "ASC" } } : orders
1416
- )[0];
1417
- const rawA = itemA.get(key);
1418
- const rawB = itemB.get(key);
1419
- let valueA = rawA?.toValue();
1420
- let valueB = rawB?.toValue();
1421
- if (getter && typeof getter === "function") {
1422
- valueA = getter(valueA, rawA);
1423
- valueB = getter(valueB, rawB);
1424
- }
1425
- let sortValue2 = 0;
1426
- if (!valueA && valueA !== 0 && !valueB && valueB !== 0) {
1427
- sortValue2 = 0;
1428
- } else if (!valueB && valueB !== 0) {
1429
- sortValue2 = 1;
1430
- } else if (!valueA && valueA !== 0) {
1431
- sortValue2 = -1;
1432
- } else if (valueA < valueB) {
1433
- sortValue2 = -1;
1434
- } else if (valueA > valueB) {
1435
- sortValue2 = 1;
1436
- }
1437
- return sortValue2 * (direction === "DESC" ? -1 : 1);
1477
+ return `gno_${this._gedcom?.refcount || "unknown"}`;
1478
+ }
1479
+ getGNO2GEDTreeName() {
1480
+ const treeName = get(this, "HEAD._TREE.value") || get(this.getGedcom(), "HEAD._TREE.value");
1481
+ if (treeName) {
1482
+ return treeName;
1483
+ }
1484
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1485
+ return fileName || "GNO2GED Export";
1486
+ }
1487
+ /**
1488
+ * Universal tree ID getter for unknown/unrecognized GEDCOM sources
1489
+ * Tries to extract an ID from various common locations
1490
+ */
1491
+ getUniversalTreeId() {
1492
+ const sourceType = this.getSourceType();
1493
+ const prefix = sourceType ? sourceType.toLowerCase() : "tree";
1494
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1495
+ if (fileName) {
1496
+ const idMatch = fileName.match(/_(\d+)/);
1497
+ if (idMatch) {
1498
+ return `${prefix}_${idMatch[1]}`;
1438
1499
  }
1439
- );
1440
- sortedItems.forEach(([key, value]) => {
1441
- newList.item(key, value);
1442
- });
1443
- return newList;
1500
+ }
1501
+ return `${prefix}_${this._gedcom?.refcount || "unknown"}`;
1444
1502
  }
1445
- groupBy(groups, sort, minOccurance = -1, lessOccuranceLabel = "Appearing less than 5 times", ClassName = _List) {
1446
- const groupped = {};
1447
- const isIterator = typeof groups === "function";
1448
- if (!isIterator && (!groups || !Object.keys(groups).length)) {
1449
- groupped.Unknown = new ClassName().merge(this);
1450
- groupped.Unknown.marker = "Unknown";
1451
- return groupped;
1503
+ /**
1504
+ * Universal tree name getter for unknown/unrecognized GEDCOM sources
1505
+ * Tries to extract a name from various common locations
1506
+ */
1507
+ getUniversalTreeName() {
1508
+ const sourceType = this.getSourceType();
1509
+ const prefix = sourceType ? `${sourceType}-` : "";
1510
+ const fileName = get(this, "HEAD.FILE.value") || get(this.getGedcom(), "HEAD.FILE.value");
1511
+ if (fileName) {
1512
+ const cleanName = fileName.replace(/\.ged$/i, "").replace(/_/g, " ");
1513
+ return `${prefix}${cleanName}`;
1452
1514
  }
1453
- this.forEach((item, itemId) => {
1454
- const group = [];
1455
- if (isIterator) {
1456
- const newGroups = groups(item, itemId);
1457
- group.push(
1458
- ...Array.isArray(newGroups) ? newGroups : [newGroups]
1459
- );
1460
- } else {
1461
- const [key, { getter }] = Object.entries(
1462
- typeof groups === "string" ? { [groups]: {} } : groups
1463
- )[0];
1464
- const raw = item.get(key)?.index(0);
1465
- let value = raw?.toValue();
1466
- if (getter && typeof getter === "function") {
1467
- value = getter(value, raw);
1468
- }
1469
- if (value) {
1470
- group.push(value);
1471
- }
1515
+ const sourName = this.getFromSourceHeads("SOUR.NAME.value") || get(this, "HEAD.SOUR.NAME.value") || get(this.getGedcom(), "HEAD.SOUR.NAME.value");
1516
+ if (sourName) {
1517
+ return `${prefix}${sourName}`;
1518
+ }
1519
+ const sourValue = this.getFromSourceHeads("SOUR.value") || get(this, "HEAD.SOUR.value") || get(this.getGedcom(), "HEAD.SOUR.value");
1520
+ if (sourValue) {
1521
+ return `${prefix}${sourValue}`;
1522
+ }
1523
+ return `${prefix}Unknown Tree`;
1524
+ }
1525
+ getTreeName() {
1526
+ if (this?.isAncestry()) {
1527
+ const name = this.getAncestryTreeName();
1528
+ if (name) return name;
1529
+ }
1530
+ if (this?.isMyHeritage()) {
1531
+ const name = this.getMyHeritageTreeName();
1532
+ if (name) return name;
1533
+ }
1534
+ if (this?.isFamilySearch()) {
1535
+ const name = this.getFamilySearchTreeName();
1536
+ if (name) return name;
1537
+ }
1538
+ if (this?.isGNO2GED()) {
1539
+ const name = this.getGNO2GEDTreeName();
1540
+ if (name) return name;
1541
+ }
1542
+ if (this?.isAhnenblatt()) {
1543
+ const name = this.getAhnenblattTreeName();
1544
+ if (name) return name;
1545
+ }
1546
+ if (this?.isGeni()) {
1547
+ const name = this.getGeniTreeName();
1548
+ if (name) return name;
1549
+ }
1550
+ if (this?.isGenoPro()) {
1551
+ const name = this.getGenoProTreeName();
1552
+ if (name) return name;
1553
+ }
1554
+ return this.getUniversalTreeName();
1555
+ }
1556
+ };
1557
+ var createProxy = (target) => {
1558
+ return new Proxy(target, {
1559
+ get(t, prop, receiver) {
1560
+ if (prop === "unwrapped") {
1561
+ return target;
1472
1562
  }
1473
- if (!group.length) {
1474
- group.push("Unknown");
1563
+ if (prop in t) {
1564
+ return Reflect.get(t, prop, receiver);
1475
1565
  }
1476
- group.forEach((g) => {
1477
- const newGroup = typeof g === "string" ? { group: g } : g;
1478
- if (!groupped[newGroup.group]) {
1479
- groupped[newGroup.group] = new ClassName();
1480
- groupped[newGroup.group].marker = newGroup.marker ?? newGroup.group;
1481
- }
1482
- groupped[newGroup.group]?.append(item);
1483
- });
1484
- });
1485
- if (sort) {
1486
- return Object.entries(groupped).toSorted(([ak, a], [bk, b]) => {
1487
- if (typeof sort === "function") {
1488
- return sort(a, ak, b, bk);
1489
- }
1490
- if (ak === "Unknown") {
1491
- return 1;
1492
- }
1493
- if (bk === "Unknown") {
1494
- return -1;
1495
- }
1496
- if (sort === "group") {
1497
- return ak.localeCompare(bk);
1498
- }
1499
- return (b?.length ?? 0) - (a?.length ?? 0);
1500
- }).reduce((acc, [key, value]) => {
1501
- if (minOccurance > -1 && (value?.length ?? 0) < minOccurance) {
1502
- const otherKey = lessOccuranceLabel;
1503
- acc[otherKey] = acc[otherKey] && value ? acc[otherKey].merge(value) : value;
1504
- } else {
1505
- acc[key] = value;
1566
+ if (!isOnlyMainProp(prop)) {
1567
+ const ref = t.ref;
1568
+ if (ref && prop in ref) {
1569
+ return Reflect.get(ref, prop, receiver);
1506
1570
  }
1507
- return acc;
1508
- }, {});
1571
+ }
1509
1572
  }
1510
- return groupped;
1573
+ });
1574
+ };
1575
+ var createCommon = (gedcom, id, main, parent) => {
1576
+ return createProxy(
1577
+ new Common(gedcom, id, main, parent)
1578
+ );
1579
+ };
1580
+ var isOnlyMainProp = (key) => {
1581
+ return key === "_gedcom" || key === "_main" || key === "_parent" || key === "_uniqueId" || key === "_type" || key === "_refs" || key === "id" || key === "_id";
1582
+ };
1583
+ var isValidKey = (common, key) => {
1584
+ const prop = get(common, key);
1585
+ 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);
1586
+ };
1587
+ var getValidKeys = (common) => {
1588
+ return Object.keys(common).filter((key) => {
1589
+ return isValidKey(common, key);
1590
+ });
1591
+ };
1592
+ var getValidTag = (tag) => {
1593
+ return tag.replace(/^@@/, "");
1594
+ };
1595
+ var getListTag = (tag) => {
1596
+ return `@@${tag}`;
1597
+ };
1598
+ var isId = (string) => {
1599
+ return ID_REG.test(string);
1600
+ };
1601
+ var idGetter = (id) => {
1602
+ if (id === void 0) {
1603
+ return void 0;
1604
+ }
1605
+ let newId = `${id}`;
1606
+ const parts = newId?.match(ID_GETTER_REG)?.groups;
1607
+ if (!parts?.at && !parts?.letter) {
1608
+ newId = `@U${newId}`;
1609
+ } else if (!parts.at) {
1610
+ newId = `@${newId}`;
1611
+ } else if (!parts.letter) {
1612
+ newId = newId.replace(/^@/, "@U");
1613
+ }
1614
+ if (!newId.endsWith("@")) {
1615
+ newId = `${newId}@`;
1616
+ }
1617
+ return newId;
1618
+ };
1619
+
1620
+ // src/utils/fact-edit.ts
1621
+ var SINGLETON_TAGS = /* @__PURE__ */ new Set(["BIRT", "DEAT", "SSN", "SEX", "DSCR"]);
1622
+ var firstRecord = (node) => {
1623
+ if (!node) {
1624
+ return void 0;
1625
+ }
1626
+ if (node instanceof List) {
1627
+ return node.index(0);
1628
+ }
1629
+ return node.index(0) ?? node;
1630
+ };
1631
+ var setDateOn = (gedcom, owner, parent, date) => {
1632
+ const trimmed = date.trim();
1633
+ if (!trimmed) {
1634
+ return;
1635
+ }
1636
+ let dateNode = parent.get("DATE");
1637
+ if (!dateNode || dateNode instanceof List) {
1638
+ dateNode = createCommonDate(gedcom, void 0, owner, parent);
1639
+ dateNode.type = "DATE";
1640
+ parent.set("DATE", dateNode);
1641
+ }
1642
+ dateNode.value = trimmed;
1643
+ };
1644
+ var fillFact = (gedcom, owner, event, input) => {
1645
+ if (input.value?.trim()) {
1646
+ event.value = input.value.trim();
1647
+ }
1648
+ if (input.type?.trim()) {
1649
+ event.set("TYPE", input.type.trim());
1650
+ }
1651
+ if (input.date) {
1652
+ setDateOn(gedcom, owner, event, input.date);
1653
+ }
1654
+ if (input.place?.trim()) {
1655
+ event.set("PLAC", input.place.trim());
1656
+ }
1657
+ if (input.note?.trim()) {
1658
+ event.set("NOTE", input.note.trim());
1659
+ }
1660
+ };
1661
+ var addIndividualFact = (indi, input) => {
1662
+ const gedcom = indi.getGedcom();
1663
+ if (!gedcom) {
1664
+ return void 0;
1665
+ }
1666
+ const tag = input.tag;
1667
+ let event = SINGLETON_TAGS.has(tag) ? firstRecord(indi.get(tag)) : void 0;
1668
+ if (!event) {
1669
+ event = createCommon(gedcom, void 0, indi);
1670
+ event.type = tag;
1671
+ if (SINGLETON_TAGS.has(tag)) {
1672
+ indi.set(tag, event);
1673
+ } else {
1674
+ indi.assign(tag, event);
1675
+ }
1676
+ }
1677
+ fillFact(gedcom, indi, event, input);
1678
+ return event;
1679
+ };
1680
+ var addFamilyFact = (fam, input) => {
1681
+ const gedcom = fam.getGedcom();
1682
+ if (!gedcom) {
1683
+ return void 0;
1684
+ }
1685
+ const event = createCommon(gedcom, void 0, fam);
1686
+ event.type = input.tag;
1687
+ fam.assign(input.tag, event);
1688
+ fillFact(gedcom, fam, event, input);
1689
+ return event;
1690
+ };
1691
+
1692
+ // src/utils/family-edit.ts
1693
+ var PEDIGREE_VALUES = [
1694
+ "birth" /* BIRTH */,
1695
+ "adopted" /* ADOPTED */,
1696
+ "foster" /* FOSTER */,
1697
+ "step" /* STEP */,
1698
+ "sealing" /* SEALING */
1699
+ ];
1700
+ var toGedcomPedigree = (value) => {
1701
+ const normalized = String(value || "birth" /* BIRTH */).toLowerCase();
1702
+ if (normalized === "biological" /* BIOLOGICAL */ || normalized === "birth" /* BIRTH */) {
1703
+ return "birth" /* BIRTH */;
1704
+ }
1705
+ const match = PEDIGREE_VALUES.find((item) => item === normalized);
1706
+ return match ?? "birth" /* BIRTH */;
1707
+ };
1708
+ var pointerIds = (tag) => {
1709
+ if (!tag) {
1710
+ return [];
1711
+ }
1712
+ if (tag instanceof List) {
1713
+ return tag.values().map((item) => item?.toValue?.()).filter((value2) => !!value2);
1714
+ }
1715
+ const value = tag.toValue?.();
1716
+ return value ? [String(value)] : [];
1717
+ };
1718
+ var firstPointerId = (tag) => pointerIds(tag)[0];
1719
+ var makePointer = (gedcom, parent, value, refType) => {
1720
+ const pointer = createCommon(gedcom, void 0, parent);
1721
+ pointer.value = value;
1722
+ pointer.refType = refType;
1723
+ return pointer;
1724
+ };
1725
+ var familyHasParent = (fam, indiId) => pointerIds(fam.get("HUSB")).includes(indiId) || pointerIds(fam.get("WIFE")).includes(indiId);
1726
+ var familyHasChild = (fam, indiId) => pointerIds(fam.get("CHIL")).includes(indiId);
1727
+ var spouseTagFor = (indi, fam) => {
1728
+ const husbId = firstPointerId(fam.get("HUSB"));
1729
+ const wifeId = firstPointerId(fam.get("WIFE"));
1730
+ if (husbId === indi.id) {
1731
+ return "HUSB";
1732
+ }
1733
+ if (wifeId === indi.id) {
1734
+ return "WIFE";
1735
+ }
1736
+ if (indi.isFemale()) {
1737
+ return wifeId && wifeId !== indi.id ? "HUSB" : "WIFE";
1738
+ }
1739
+ if (indi.isMale()) {
1740
+ return husbId && husbId !== indi.id ? "WIFE" : "HUSB";
1741
+ }
1742
+ return husbId ? "WIFE" : "HUSB";
1743
+ };
1744
+ var canAttachAsSpouse = (fam, indi) => {
1745
+ if (!indi.id) {
1746
+ return false;
1747
+ }
1748
+ if (familyHasParent(fam, indi.id)) {
1749
+ return true;
1750
+ }
1751
+ const tag = spouseTagFor(indi, fam);
1752
+ const occupied = firstPointerId(fam.get(tag));
1753
+ return !occupied || occupied === indi.id;
1754
+ };
1755
+ var pointerByValue = (tag, id) => {
1756
+ if (!tag) {
1757
+ return void 0;
1758
+ }
1759
+ const fromValues = tag.toValueList?.().item(id);
1760
+ if (fromValues) {
1761
+ return fromValues;
1762
+ }
1763
+ return tag.toList()?.values().find((item) => item?.toValue?.() === id);
1764
+ };
1765
+ var setChildPedigree = (fam, child, pedigree, parent) => {
1766
+ if (!fam.id || !child.id) {
1767
+ return;
1768
+ }
1769
+ const pedi = toGedcomPedigree(pedigree);
1770
+ const famc = pointerByValue(child.get("FAMC"), fam.id);
1771
+ if (famc) {
1772
+ famc.set("PEDI", pedi);
1773
+ }
1774
+ const chil = pointerByValue(fam.get("CHIL"), child.id);
1775
+ if (!chil) {
1776
+ return;
1777
+ }
1778
+ const setFather = !parent || parent.isMale() || parent.isUnknownSex();
1779
+ const setMother = !parent || parent.isFemale() || parent.isUnknownSex();
1780
+ if (setFather) {
1781
+ chil.set("_FREL", pedi);
1782
+ }
1783
+ if (setMother) {
1784
+ chil.set("_MREL", pedi);
1511
1785
  }
1512
- findIndex(item) {
1513
- if (!item?.id) {
1514
- return -1;
1515
- }
1516
- return this.keys().findIndex((key) => key === item.id);
1786
+ };
1787
+ var attachSpouseToFamily = (fam, indi) => {
1788
+ const gedcom = fam.getGedcom() ?? indi.getGedcom();
1789
+ if (!indi.id || !fam.id || !gedcom) {
1790
+ return false;
1517
1791
  }
1518
- getItems() {
1519
- return this.items;
1792
+ if (!canAttachAsSpouse(fam, indi)) {
1793
+ return false;
1520
1794
  }
1521
- toJson(tag, options) {
1522
- const json = this.toObject(tag, options);
1523
- return JSON.stringify(json);
1795
+ const tag = spouseTagFor(indi, fam);
1796
+ if (firstPointerId(fam.get(tag)) !== indi.id) {
1797
+ fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
1524
1798
  }
1525
- toObject(tag, options) {
1526
- const json = {};
1527
- this.entries().forEach(([_, item]) => {
1528
- const validTag = getValidTag(tag);
1529
- if (json[validTag]) {
1530
- if (!Array.isArray(json[validTag])) {
1531
- json[validTag] = [json[validTag]];
1532
- }
1533
- json[validTag].push(
1534
- item.toObject(validTag, options)
1535
- );
1536
- } else {
1537
- json[validTag] = item.toObject(
1538
- validTag,
1539
- options
1540
- );
1541
- }
1542
- });
1543
- return json;
1799
+ indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
1800
+ return true;
1801
+ };
1802
+ var attachChildToFamily = (fam, child, pedigree, parent) => {
1803
+ const gedcom = fam.getGedcom() ?? child.getGedcom();
1804
+ if (!child.id || !fam.id || !gedcom) {
1805
+ return false;
1544
1806
  }
1545
- toGedcom(tag, level = 0, options) {
1546
- const gedcom = this.toGedcomLines(tag, level, options);
1547
- return gedcom.join("\n");
1807
+ if (!familyHasChild(fam, child.id)) {
1808
+ fam.assign("CHIL", makePointer(gedcom, fam, child.id, "INDI"), true);
1548
1809
  }
1549
- toGedcomLines(tag, level = 0, options) {
1550
- const gedcom = [];
1551
- this.entries().forEach(([_, item]) => {
1552
- const validTag = getValidTag(tag);
1553
- const validKey = item.id;
1554
- const validValue = validKey ? validTag : item.exportValue();
1555
- gedcom.push(
1556
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
1557
- );
1558
- gedcom.push(
1559
- ...item.toGedcomLines(
1560
- validTag,
1561
- level + 1,
1562
- options
1563
- )
1564
- );
1565
- });
1566
- return gedcom;
1810
+ const existingFamc = pointerByValue(child.get("FAMC"), fam.id);
1811
+ if (!existingFamc) {
1812
+ child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
1567
1813
  }
1568
- toValue() {
1569
- const propList = new _List();
1570
- this.entries().forEach(([key, item]) => {
1571
- propList.item(key, item?.toValue());
1572
- });
1573
- return propList;
1814
+ setChildPedigree(fam, child, pedigree, parent);
1815
+ return true;
1816
+ };
1817
+ var findReusableParentChildFamily = (parent, child) => {
1818
+ if (!parent.id || !child.id) {
1819
+ return void 0;
1574
1820
  }
1575
- // @deprecated use get instead
1576
- toProp(tag) {
1577
- return this.get(tag);
1821
+ let reusableFamc;
1822
+ let already = false;
1823
+ child.getFamilies("FAMC")?.forEach((fam) => {
1824
+ if (!fam) {
1825
+ return;
1826
+ }
1827
+ if (familyHasParent(fam, parent.id) && familyHasChild(fam, child.id)) {
1828
+ already = true;
1829
+ return;
1830
+ }
1831
+ if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
1832
+ reusableFamc = fam;
1833
+ }
1834
+ });
1835
+ if (already) {
1836
+ return "already";
1578
1837
  }
1579
- toList() {
1580
- return new _List().concat(this.items);
1838
+ if (reusableFamc) {
1839
+ return reusableFamc;
1581
1840
  }
1582
- toValueList() {
1583
- const newList = new _List();
1584
- this.values().forEach((value) => {
1585
- value.value && newList.item(value.value, value);
1586
- });
1587
- return newList;
1841
+ const parentFams = [];
1842
+ parent.getFamilies("FAMS")?.forEach((fam) => {
1843
+ if (fam) {
1844
+ parentFams.push(fam);
1845
+ }
1846
+ });
1847
+ if (parentFams.length === 1) {
1848
+ return parentFams[0];
1588
1849
  }
1850
+ return void 0;
1851
+ };
1852
+
1853
+ // src/utils/get-family-with.ts
1854
+ var getFamilyWith = (person1, person2, famType = "FAMS") => {
1855
+ return person1?.get(famType)?.toValueList().find((fam) => {
1856
+ const family = fam;
1857
+ return famType === "FAMC" || family?.HUSB?.value === person2 || family?.WIFE?.value === person2;
1858
+ });
1589
1859
  };
1590
1860
 
1591
1861
  // src/utils/get-places.ts
@@ -2218,6 +2488,31 @@ var Fam = class extends Common {
2218
2488
  getParents() {
2219
2489
  return this.getHusband().copy().merge(this.getWife());
2220
2490
  }
2491
+ hasParent(indi) {
2492
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2493
+ return !!indiId && familyHasParent(this, indiId);
2494
+ }
2495
+ hasChild(indi) {
2496
+ const indiId = typeof indi === "string" ? indi : indi?.id;
2497
+ return !!indiId && familyHasChild(this, indiId);
2498
+ }
2499
+ canAddSpouse(indi) {
2500
+ return canAttachAsSpouse(this, indi);
2501
+ }
2502
+ addSpouse(indi) {
2503
+ return attachSpouseToFamily(this, indi);
2504
+ }
2505
+ addChild(child, pedigree, parent) {
2506
+ return attachChildToFamily(
2507
+ this,
2508
+ child,
2509
+ pedigree,
2510
+ parent
2511
+ );
2512
+ }
2513
+ addFact(input) {
2514
+ return addFamilyFact(this, input);
2515
+ }
2221
2516
  toList() {
2222
2517
  return new Families().concat(
2223
2518
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2318,19 +2613,51 @@ var ALLOWED_FACTS = [
2318
2613
  "OCCU",
2319
2614
  "OCCUPATIONS",
2320
2615
  "RESI",
2616
+ "BIRT",
2617
+ "DEAT",
2321
2618
  "BAPT",
2619
+ "BAPM",
2322
2620
  "CHRI",
2323
2621
  "CHR",
2622
+ "CHRA",
2324
2623
  "BURI",
2325
- "EDUC",
2624
+ "CREM",
2625
+ "ADOP",
2626
+ "BARM",
2627
+ "BASM",
2628
+ "BLES",
2629
+ "CONF",
2630
+ "FCOM",
2631
+ "ORDN",
2632
+ "NATU",
2633
+ "EMIG",
2634
+ "IMMI",
2635
+ "CENS",
2636
+ "PROB",
2637
+ "WILL",
2326
2638
  "GRAD",
2639
+ "RETI",
2640
+ "EDUC",
2327
2641
  "DIV",
2328
- "IMMI",
2329
2642
  "RELI",
2643
+ "CAST",
2644
+ "DSCR",
2645
+ "NATI",
2646
+ "PROP",
2647
+ "SSN",
2648
+ "TITL",
2649
+ "ADDR",
2650
+ "BAPL",
2651
+ "CONL",
2652
+ "ENDL",
2653
+ "SLGC",
2330
2654
  "_MILT",
2331
2655
  "_MILTID",
2332
2656
  "FACT",
2333
- "_ORIG"
2657
+ "_ORIG",
2658
+ "_WLNK",
2659
+ "WWW",
2660
+ "URL"
2334
2661
  ];
2335
2662
  var DISALLOWED_CUSTOM_FACTS = ["DNA Test", "Newspaper"];
2336
2663
  var CustomFactRenderers = {
@@ -2874,7 +3201,7 @@ var Indi = class extends Common {
2874
3201
  return result;
2875
3202
  }
2876
3203
  ancestryLink() {
2877
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3204
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
2878
3205
  const tree = this.getAncestryTreeId();
2879
3206
  if (this.id) {
2880
3207
  return `https://${www}/family-tree/person/tree/${tree}/person/${this.id.replace(
@@ -2886,7 +3213,7 @@ var Indi = class extends Common {
2886
3213
  async ancestryMedia(namespace) {
2887
3214
  const list = {};
2888
3215
  const objeList = this.get("OBJE")?.toList().copy();
2889
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
3216
+ const www = this.getFromSourceHeads("SOUR.CORP.WWW.value") || this._gedcom?.HEAD?.SOUR?.CORP?.WWW?.value;
2890
3217
  const tree = this.getAncestryTreeId();
2891
3218
  if (objeList) {
2892
3219
  await Promise.all(
@@ -2946,7 +3273,7 @@ var Indi = class extends Common {
2946
3273
  return list;
2947
3274
  }
2948
3275
  myheritageLink(poolId = 0) {
2949
- const www = this._gedcom?.HEAD?.SOUR?.CORP?.value?.toLowerCase().replace(/^www\./gi, "");
3276
+ const www = (this.getFromSourceHeads("SOUR.CORP.value") || this._gedcom?.HEAD?.SOUR?.CORP?.value)?.toLowerCase().replace(/^www\./gi, "");
2950
3277
  const site = this.getMyHeritageTreeId();
2951
3278
  const file = this._gedcom?.HEAD?.get("FILE")?.toValue()?.match(/Exported by MyHeritage.com from .+ in (?<site>.+) on .+$/)?.groups?.site;
2952
3279
  const normalizedFile = file?.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-");
@@ -3467,6 +3794,57 @@ var Indi = class extends Common {
3467
3794
  isUnknownSex() {
3468
3795
  return !this.isMale() && !this.isFemale();
3469
3796
  }
3797
+ addSpouse(other) {
3798
+ if (!this.id || !other?.id || this.id === other.id || !this._gedcom) {
3799
+ return void 0;
3800
+ }
3801
+ const existing = getFamilyWith(this, other.id);
3802
+ if (existing) {
3803
+ return existing;
3804
+ }
3805
+ const fam = this._gedcom.createFamily();
3806
+ if (!fam.addSpouse(this) || !fam.addSpouse(other)) {
3807
+ return void 0;
3808
+ }
3809
+ return fam;
3810
+ }
3811
+ addChild(child, pedigree) {
3812
+ if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
3813
+ return void 0;
3814
+ }
3815
+ const existing = findReusableParentChildFamily(
3816
+ this,
3817
+ child
3818
+ );
3819
+ if (existing === "already") {
3820
+ const fam2 = child.getFamilies("FAMC")?.find(
3821
+ (item) => !!item && item.hasParent(this.id) && item.hasChild(child.id)
3822
+ );
3823
+ if (fam2) {
3824
+ setChildPedigree(
3825
+ fam2,
3826
+ child,
3827
+ pedigree,
3828
+ this
3829
+ );
3830
+ }
3831
+ return fam2;
3832
+ }
3833
+ const fam = existing ?? this._gedcom.createFamily();
3834
+ if (!fam.addSpouse(this)) {
3835
+ return void 0;
3836
+ }
3837
+ if (!fam.addChild(child, pedigree, this)) {
3838
+ return void 0;
3839
+ }
3840
+ return fam;
3841
+ }
3842
+ addParent(parent, pedigree) {
3843
+ return parent.addChild(this, pedigree);
3844
+ }
3845
+ addFact(input) {
3846
+ return addIndividualFact(this, input);
3847
+ }
3470
3848
  getParentType(id) {
3471
3849
  let indi;
3472
3850
  if (typeof id === "string" || typeof id === "number") {
@@ -3667,7 +4045,9 @@ var Indi = class extends Common {
3667
4045
  }
3668
4046
  }
3669
4047
  if (kinship !== "child" || !breakAfterSpouse) {
3670
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
4048
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(
4049
+ indi.getFathers().copy().merge(indi.getMothers())
4050
+ ).forEach((relative) => {
3671
4051
  if (!visited.has(relative)) {
3672
4052
  const currentRelation = indi.getParentType(relative);
3673
4053
  const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
@@ -5962,6 +6342,249 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
5962
6342
  }
5963
6343
  };
5964
6344
 
6345
+ // src/kinship-translator/patterns.ru.ts
6346
+ var InLawsRu = {
6347
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
6348
+ "\u0436\u0435\u043D\u0430 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
6349
+ "\u043C\u0443\u0436 (\u0440\u0435\u0431\u0451\u043D\u043A\u0430|\u0434\u043E\u0447\u0435\u0440\u0438|\u0441\u044B\u043D\u0430)": "\u0437\u044F\u0442\u044C",
6350
+ "\u043C\u0430\u0442\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0451\u0449\u0430",
6351
+ "\u043E\u0442\u0435\u0446 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C",
6352
+ "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0442\u0435\u0441\u0442\u044C/\u0442\u0451\u0449\u0430",
6353
+ "\u0436\u0435\u043D\u0430 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
6354
+ "\u043C\u0443\u0436 (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C",
6355
+ "\u0441\u0443\u043F\u0440\u0443\u0433(?:\u0430)? (\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?(\u0431\u0440\u0430\u0442\u0430|\u0441\u0435\u0441\u0442\u0440\u044B|\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430)": "\u0437\u044F\u0442\u044C/\u043D\u0435\u0432\u0435\u0441\u0442\u043A\u0430",
6356
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0435\u0441\u0442\u0440\u0430 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0437\u043E\u043B\u043E\u0432\u043A\u0430",
6357
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0431\u0440\u0430\u0442 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D",
6358
+ "(\u0441\u0432\u043E\u0434\u043D(?:\u044B\u0439|\u0430\u044F) )?\u0441\u0438\u0431\u043B\u0438\u043D\u0433 (\u0441\u0443\u043F\u0440\u0443\u0433\u0430|\u0436\u0435\u043D\u044B|\u043C\u0443\u0436\u0430)": "\u0448\u0443\u0440\u0438\u043D/\u0437\u043E\u043B\u043E\u0432\u043A\u0430"
6359
+ };
6360
+
6361
+ // src/kinship-translator/kinship-translator.ru.ts
6362
+ var GENITIVE = {
6363
+ \u0441\u044B\u043D: "\u0441\u044B\u043D\u0430",
6364
+ \u0434\u043E\u0447\u044C: "\u0434\u043E\u0447\u0435\u0440\u0438",
6365
+ \u0440\u0435\u0431\u0451\u043D\u043E\u043A: "\u0440\u0435\u0431\u0451\u043D\u043A\u0430",
6366
+ \u0431\u0440\u0430\u0442: "\u0431\u0440\u0430\u0442\u0430",
6367
+ \u0441\u0435\u0441\u0442\u0440\u0430: "\u0441\u0435\u0441\u0442\u0440\u044B",
6368
+ \u0441\u0438\u0431\u043B\u0438\u043D\u0433: "\u0441\u0438\u0431\u043B\u0438\u043D\u0433\u0430",
6369
+ \u043E\u0442\u0435\u0446: "\u043E\u0442\u0446\u0430",
6370
+ \u043C\u0430\u0442\u044C: "\u043C\u0430\u0442\u0435\u0440\u0438",
6371
+ \u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C: "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044F",
6372
+ \u043C\u0443\u0436: "\u043C\u0443\u0436\u0430",
6373
+ \u0436\u0435\u043D\u0430: "\u0436\u0435\u043D\u044B",
6374
+ \u0441\u0443\u043F\u0440\u0443\u0433: "\u0441\u0443\u043F\u0440\u0443\u0433\u0430",
6375
+ \u0441\u0443\u043F\u0440\u0443\u0433\u0430: "\u0441\u0443\u043F\u0440\u0443\u0433\u0438"
6376
+ };
6377
+ var toGenitive = (word) => GENITIVE[word] ?? word;
6378
+ var cousinWord = (degree, sex) => {
6379
+ const stem = degree <= 1 ? "\u0434\u0432\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 2 ? "\u0442\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : degree === 3 ? "\u0447\u0435\u0442\u0432\u0435\u0440\u043E\u044E\u0440\u043E\u0434\u043D" : `${degree + 1}-\u044E\u0440\u043E\u0434\u043D`;
6380
+ if (sex === "m") {
6381
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442`;
6382
+ }
6383
+ if (sex === "f") {
6384
+ return `${stem}\u0430\u044F \u0441\u0435\u0441\u0442\u0440\u0430`;
6385
+ }
6386
+ return `${stem}\u044B\u0439 \u0431\u0440\u0430\u0442/\u0441\u0435\u0441\u0442\u0440\u0430`;
6387
+ };
6388
+ var praRepeat = (count) => count > 0 ? "\u043F\u0440\u0430".repeat(count) : "";
6389
+ var sexOf = (person) => {
6390
+ if (person?.isMale?.()) {
6391
+ return "m";
6392
+ }
6393
+ if (person?.isFemale?.()) {
6394
+ return "f";
6395
+ }
6396
+ return "n";
6397
+ };
6398
+ var KinshipTranslatorRu = class extends KinshipTranslatorBasic {
6399
+ indirect() {
6400
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6401
+ if (degree > 0) {
6402
+ degree = degree - 1;
6403
+ }
6404
+ return cousinWord(degree, sexOf(this.personN));
6405
+ }
6406
+ removal() {
6407
+ const level = this.pathN?.level ?? 0;
6408
+ let degree = Math.abs(this.pathN?.degree ?? 0);
6409
+ if (degree > 0) {
6410
+ degree = degree - 1;
6411
+ }
6412
+ if (!degree) {
6413
+ return level < 0 ? this.nibling() : this.auncle();
6414
+ }
6415
+ const removed = Math.abs(level);
6416
+ return `${cousinWord(degree, sexOf(this.personN))} (${removed}\xD7 \u0447\u0435\u0440\u0435\u0437 \u043F\u043E\u043A\u043E\u043B\u0435\u043D\u0438\u0435)`;
6417
+ }
6418
+ auncle() {
6419
+ const level = Math.abs(this.pathN?.level ?? 0);
6420
+ const pra = praRepeat(Math.max(0, level - 2));
6421
+ const sex = sexOf(this.personN);
6422
+ if (sex === "m") {
6423
+ return `${pra}\u0434\u044F\u0434\u044F`;
6424
+ }
6425
+ if (sex === "f") {
6426
+ return `${pra}\u0442\u0451\u0442\u044F`;
6427
+ }
6428
+ return `${pra}\u0434\u044F\u0434\u044F/\u0442\u0451\u0442\u044F`;
6429
+ }
6430
+ nibling() {
6431
+ const level = Math.abs(this.pathN?.level ?? 0);
6432
+ const pra = praRepeat(Math.max(0, level - 2));
6433
+ const sex = sexOf(this.personN);
6434
+ if (sex === "m") {
6435
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A`;
6436
+ }
6437
+ if (sex === "f") {
6438
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6439
+ }
6440
+ return `${pra}\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u043A/\u043F\u043B\u0435\u043C\u044F\u043D\u043D\u0438\u0446\u0430`;
6441
+ }
6442
+ parent() {
6443
+ const level = Math.abs(this.pathN?.level ?? 0);
6444
+ const sex = sexOf(this.personN);
6445
+ if (level <= 1) {
6446
+ if (sex === "m") {
6447
+ return "\u043E\u0442\u0435\u0446";
6448
+ }
6449
+ if (sex === "f") {
6450
+ return "\u043C\u0430\u0442\u044C";
6451
+ }
6452
+ return "\u0440\u043E\u0434\u0438\u0442\u0435\u043B\u044C";
6453
+ }
6454
+ if (level === 2) {
6455
+ if (sex === "m") {
6456
+ return "\u0434\u0435\u0434";
6457
+ }
6458
+ if (sex === "f") {
6459
+ return "\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6460
+ }
6461
+ return "\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430";
6462
+ }
6463
+ const pra = praRepeat(level - 2);
6464
+ if (sex === "m") {
6465
+ return `${pra}\u0434\u0435\u0434`;
6466
+ }
6467
+ if (sex === "f") {
6468
+ return `${pra}\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6469
+ }
6470
+ return `${pra}\u0434\u0435\u0434/\u0431\u0430\u0431\u0443\u0448\u043A\u0430`;
6471
+ }
6472
+ child() {
6473
+ const level = Math.abs(this.pathN?.level ?? 0);
6474
+ const sex = sexOf(this.personN);
6475
+ if (level <= 1) {
6476
+ if (sex === "m") {
6477
+ return "\u0441\u044B\u043D";
6478
+ }
6479
+ if (sex === "f") {
6480
+ return "\u0434\u043E\u0447\u044C";
6481
+ }
6482
+ return "\u0440\u0435\u0431\u0451\u043D\u043E\u043A";
6483
+ }
6484
+ if (level === 2) {
6485
+ if (sex === "m") {
6486
+ return "\u0432\u043D\u0443\u043A";
6487
+ }
6488
+ if (sex === "f") {
6489
+ return "\u0432\u043D\u0443\u0447\u043A\u0430";
6490
+ }
6491
+ return "\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430";
6492
+ }
6493
+ const pra = praRepeat(level - 2);
6494
+ if (sex === "m") {
6495
+ return `${pra}\u0432\u043D\u0443\u043A`;
6496
+ }
6497
+ if (sex === "f") {
6498
+ return `${pra}\u0432\u043D\u0443\u0447\u043A\u0430`;
6499
+ }
6500
+ return `${pra}\u0432\u043D\u0443\u043A/\u0432\u043D\u0443\u0447\u043A\u0430`;
6501
+ }
6502
+ sibling() {
6503
+ const sex = sexOf(this.personN);
6504
+ if (sex === "m") {
6505
+ return "\u0431\u0440\u0430\u0442";
6506
+ }
6507
+ if (sex === "f") {
6508
+ return "\u0441\u0435\u0441\u0442\u0440\u0430";
6509
+ }
6510
+ return "\u0441\u0438\u0431\u043B\u0438\u043D\u0433";
6511
+ }
6512
+ halfBlood(relation) {
6513
+ if (!relation || !this.isHalfBlood) {
6514
+ return relation ?? "";
6515
+ }
6516
+ if (/сестра|дочь|мать|тётя|бабушка|жена|племянница|внучка/.test(
6517
+ relation
6518
+ )) {
6519
+ return `\u0441\u0432\u043E\u0434\u043D\u0430\u044F ${relation}`;
6520
+ }
6521
+ return `\u0441\u0432\u043E\u0434\u043D\u044B\u0439 ${relation}`;
6522
+ }
6523
+ spouse() {
6524
+ const sex = sexOf(this.personN);
6525
+ if (sex === "m") {
6526
+ return "\u043C\u0443\u0436";
6527
+ }
6528
+ if (sex === "f") {
6529
+ return "\u0436\u0435\u043D\u0430";
6530
+ }
6531
+ return "\u0441\u0443\u043F\u0440\u0443\u0433";
6532
+ }
6533
+ ofSpouse(relation) {
6534
+ if (!relation) {
6535
+ return "";
6536
+ }
6537
+ const spouse = this.path?.[1].indi;
6538
+ const sex = sexOf(spouse);
6539
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6540
+ return `${relation} ${toGenitive(spouseType)}`;
6541
+ }
6542
+ spouseOf(relation) {
6543
+ if (!relation) {
6544
+ return "";
6545
+ }
6546
+ const spouse = this.path?.[this.path.length - 1].indi;
6547
+ const sex = sexOf(spouse);
6548
+ const spouseType = sex === "m" ? "\u043C\u0443\u0436" : sex === "f" ? "\u0436\u0435\u043D\u0430" : "\u0441\u0443\u043F\u0440\u0443\u0433";
6549
+ return `${spouseType} ${toGenitive(relation)}`;
6550
+ }
6551
+ of(relation) {
6552
+ const formattedName = nameFormatter(this.path?.[0]?.indi, {
6553
+ nameOrder: "first-last"
6554
+ });
6555
+ let name = formattedName.inOrder.filter(Boolean).join(" ");
6556
+ if (this.displayName === "none") {
6557
+ name = "";
6558
+ } else if (this.displayName === "givenname") {
6559
+ name = formattedName.givenname;
6560
+ } else if (this.displayName === "surname") {
6561
+ name = `${formattedName.surname}`;
6562
+ }
6563
+ if (!name || !relation) {
6564
+ return relation ?? "";
6565
+ }
6566
+ return `${name}: ${relation}`;
6567
+ }
6568
+ inLaw(relation) {
6569
+ let inLawRelation = relation;
6570
+ Object.keys(InLawsRu).find((pattern) => {
6571
+ const regex = new RegExp(`^${pattern}$`);
6572
+ if (relation?.match(regex)) {
6573
+ inLawRelation = InLawsRu[pattern];
6574
+ return true;
6575
+ }
6576
+ return false;
6577
+ });
6578
+ return inLawRelation ?? "";
6579
+ }
6580
+ relationType(relation) {
6581
+ if (!relation || !this.pathN?.relation) {
6582
+ return relation ?? "";
6583
+ }
6584
+ return `${this.pathN.relation} ${relation}`;
6585
+ }
6586
+ };
6587
+
5965
6588
  // src/kinship-translator/kinship-translator.ts
5966
6589
  var KinshipTranslator = class {
5967
6590
  constructor(person1, person2, lang = "en", entirePath, displayName = "givenname") {
@@ -6318,14 +6941,6 @@ var isCommonDate = (value) => {
6318
6941
  return !!value && value !== null && typeof value === "object" && "_date" in value;
6319
6942
  };
6320
6943
 
6321
- // src/utils/get-family-with.ts
6322
- var getFamilyWith = (person1, person2, famType = "FAMS") => {
6323
- return person1?.get(famType)?.toValueList().find((fam) => {
6324
- const family = fam;
6325
- return famType === "FAMC" || family?.HUSB?.value === person2 || family?.WIFE?.value === person2;
6326
- });
6327
- };
6328
-
6329
6944
  // src/constants/orders.ts
6330
6945
  var DEFAULT = {};
6331
6946
  var BIRTH_ASC = {
@@ -6565,4 +7180,4 @@ var getMarriageAscAndBirth = (person) => (itemA, keyA, itemB, keyB) => {
6565
7180
  return getBirthAsc(itemA, keyA, itemB);
6566
7181
  };
6567
7182
 
6568
- 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 };
7183
+ 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 };