@treeviz/gedcom-parser 1.0.2 → 1.0.4

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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # @treeviz/gedcom-parser
2
2
 
3
+ > Part of the [@treeviz](https://www.npmjs.com/org/treeviz) organization - A collection of tools for genealogy data processing and visualization.
4
+
3
5
  A lightweight, pluggable GEDCOM parser library for JavaScript/TypeScript applications. Originally part of [TreeViz](https://treeviz.com), extracted as a standalone package for reusability.
4
6
 
5
7
  ## Features
@@ -27,6 +27,11 @@ export declare class Common<T = string, I extends IdType = IdType> implements IC
27
27
  get id(): I | undefined;
28
28
  set value(value: T | undefined);
29
29
  get value(): T | undefined;
30
+ /**
31
+ * Set the gedcom reference for this object
32
+ * @param gedcom - The GedComType to set as the gedcom reference
33
+ */
34
+ setGedcom(gedcom: GedComType): void;
30
35
  get originalValue(): T;
31
36
  get ref(): Common<T, I> | undefined;
32
37
  get main(): Common<string, IdType>;
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/classes/common.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/classes/common.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,13 @@ export class Common {
58
58
  get value() {
59
59
  return this._value;
60
60
  }
61
+ /**
62
+ * Set the gedcom reference for this object
63
+ * @param gedcom - The GedComType to set as the gedcom reference
64
+ */
65
+ setGedcom(gedcom) {
66
+ this._gedcom = gedcom;
67
+ }
61
68
  // avoid to override
62
69
  get originalValue() {
63
70
  return this._value;
@@ -115,4 +115,25 @@ export declare const validateGedcomContent: (content?: string) => {
115
115
  valid: boolean;
116
116
  error?: string;
117
117
  };
118
+ /**
119
+ * Merge two GEDCOM objects into a single result using a configurable matching strategy
120
+ * @param targetGedcom - The base GEDCOM (kept as the primary source)
121
+ * @param sourceGedcom - The GEDCOM to be merged into the target
122
+ * @param strategy - Matching strategy: "id" (default) to match by individual ID, or any MultiTag (e.g., "NAME", "BIRT.DATE") to match by that field's value
123
+ * @returns The merged GedComType with all individuals and families combined
124
+ *
125
+ * @example
126
+ * // Merge by ID (individuals with same ID are considered the same person)
127
+ * const merged = await mergeGedcoms(target, source, "id");
128
+ *
129
+ * @example
130
+ * // Merge by NAME (individuals with same name are considered the same person)
131
+ * const merged = await mergeGedcoms(target, source, "NAME");
132
+ *
133
+ * @remarks
134
+ * - Source individuals are always assigned new unique IDs to avoid conflicts
135
+ * - When individuals match by strategy, they are merged (data and relationships combined)
136
+ * - All family relationships (FAMS/FAMC) are preserved with updated ID references
137
+ */
138
+ export declare const mergeGedcoms: (targetGedcom: GedComType, sourceGedcom: GedComType, strategy?: MultiTag | "id") => GedComType;
118
139
  //# sourceMappingURL=gedcom.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gedcom.d.ts","sourceRoot":"","sources":["../../src/classes/gedcom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,OAAO,MAAM,sBAAsB,CAAC;AAChD,OAAO,KAAK,gBAAgB,MAAM,sBAAsB,CAAC;AAEzD,OAAO,EACN,KAAK,MAAM,EACX,KAAK,OAAO,EACZ,KAAK,MAAM,EACX,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,MAAM,EAAgB,MAAM,UAAU,CAAC;AAChD,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,OAAO,CAAC;AACrC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,qBAAa,MAAO,SAAQ,MAAO,YAAW,OAAO;IACpD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,CAAC,CAAM;IACrE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACrC,QAAQ,SAAK;;IAUb,OAAO,CAAC,OAAO;IAwCf,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,IAAI,EAAE,IAAI,EAAE,QAAQ,GAAG,CAAC,GAAG,SAAS;IAI7D,KAAK;IAIL,UAAU,CACT,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,EAC3B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,EAC3B,SAAS,GAAE,QAAQ,EAAO,EAC1B,sBAAsB,UAAO;IA+D9B,UAAU,CACT,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,EAC3B,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,EAC3B,sBAAsB,UAAO;IAqD9B,IAAI;IAIJ,KAAK;IAIL,KAAK;IAIL,KAAK;IAIL,KAAK;IAIL,IAAI;IAIJ,UAAU;IAMV,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI1B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5B,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI5B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAI3B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM;IAIpB,SAAS,CAAC,IAAI,CAAC,EAAE,MAAM;IAQvB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIjC,OAAO,CAAC,mBAAmB;IA8C3B,OAAO,CAAC,iBAAiB;IAuBzB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE;IAgB3B,MAAM,CACL,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,EAC1B,OAAO,CAAC,EACL,CAAC,cAAc,GAAG;QAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;KACjB,CAAC,GACF,SAAS,GACV,MAAM;IAiBT,QAAQ,CACP,GAAG,CAAC,EAAE,QAAQ,GAAG,SAAS,EAC1B,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EACL,CAAC,cAAc,GAAG;QAClB,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;KACjB,CAAC,GACF,SAAS,GACV,MAAM;IAwBT,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM;IAY5B,kBAAkB;IAIlB,gBAAgB;IAIhB,mBAAmB;IAInB,mBAAmB;IAInB,iBAAiB;IAIjB,cAAc;IAId;;;OAGG;IACH,YAAY;IAkBZ;;;;OAIG;IACH,qBAAqB,CAAC,SAAS,GAAE,OAAO,EAAO;IA0B/C;;;;OAIG;IACH,KAAK,CAAC,WAAW,CAAC,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;CAwM/B;AAED,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,gBAAgB,CAAC;AACnD,eAAO,MAAM,YAAY,QAAO,UAE/B,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,eAAe,MAAM,YASnD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GACjC,UAAU,MAAM,KACd;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAqDlC,CAAC"}
1
+ {"version":3,"file":"gedcom.d.ts","sourceRoot":"","sources":["../../src/classes/gedcom.ts"],"names":[],"mappings":"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"}
@@ -595,3 +595,295 @@ export const validateGedcomContent = (content) => {
595
595
  // }
596
596
  return { valid: true };
597
597
  };
598
+ // Constants for merge operation
599
+ const MERGE_ID_INCREMENT_MULTIPLIER = 1000;
600
+ /**
601
+ * Helper function to convert a Common or List value to a string for comparison
602
+ */
603
+ const valueToString = (value) => {
604
+ if (!value)
605
+ return "";
606
+ return value.toString?.() || String(value?.toValue?.() || "");
607
+ };
608
+ /**
609
+ * Merge two GEDCOM objects into a single result using a configurable matching strategy
610
+ * @param targetGedcom - The base GEDCOM (kept as the primary source)
611
+ * @param sourceGedcom - The GEDCOM to be merged into the target
612
+ * @param strategy - Matching strategy: "id" (default) to match by individual ID, or any MultiTag (e.g., "NAME", "BIRT.DATE") to match by that field's value
613
+ * @returns The merged GedComType with all individuals and families combined
614
+ *
615
+ * @example
616
+ * // Merge by ID (individuals with same ID are considered the same person)
617
+ * const merged = await mergeGedcoms(target, source, "id");
618
+ *
619
+ * @example
620
+ * // Merge by NAME (individuals with same name are considered the same person)
621
+ * const merged = await mergeGedcoms(target, source, "NAME");
622
+ *
623
+ * @remarks
624
+ * - Source individuals are always assigned new unique IDs to avoid conflicts
625
+ * - When individuals match by strategy, they are merged (data and relationships combined)
626
+ * - All family relationships (FAMS/FAMC) are preserved with updated ID references
627
+ */
628
+ export const mergeGedcoms = (targetGedcom, sourceGedcom, strategy = "id") => {
629
+ // Work directly with the target GEDCOM (no serialization needed)
630
+ const mergedGedcom = targetGedcom;
631
+ // Track ID mapping: source ID -> new ID in merged GEDCOM
632
+ const idMap = new Map();
633
+ // Track matching: sourceIndiId -> targetIndiId (for individuals that match by strategy)
634
+ const matchMap = new Map();
635
+ // Get source individuals and families
636
+ const sourceIndis = sourceGedcom.indis();
637
+ const sourceFams = sourceGedcom.fams();
638
+ const targetIndis = mergedGedcom.indis();
639
+ const targetFams = mergedGedcom.fams();
640
+ // Step 1: Identify matches and create ID mappings for individuals
641
+ sourceIndis?.forEach((sourceIndi) => {
642
+ if (!sourceIndi.id)
643
+ return;
644
+ let matchedTargetIndi;
645
+ if (strategy === "id") {
646
+ // Match by ID directly - check if target has this exact ID
647
+ matchedTargetIndi = targetIndis?.item(sourceIndi.id);
648
+ }
649
+ else {
650
+ // Match by specified MultiTag value
651
+ const sourceValueRaw = sourceIndi.get(strategy);
652
+ const sourceValue = valueToString(sourceValueRaw);
653
+ if (sourceValue) {
654
+ // Find target individual with same value
655
+ matchedTargetIndi = targetIndis?.find((targetIndi) => {
656
+ const targetValueRaw = targetIndi.get(strategy);
657
+ const targetValue = valueToString(targetValueRaw);
658
+ return targetValue && targetValue === sourceValue;
659
+ });
660
+ }
661
+ }
662
+ if (matchedTargetIndi && matchedTargetIndi.id) {
663
+ // Found a match - map source ID to existing target ID
664
+ // Note: This means source individual will merge into target individual
665
+ matchMap.set(sourceIndi.id, matchedTargetIndi.id);
666
+ idMap.set(sourceIndi.id, matchedTargetIndi.id);
667
+ }
668
+ else {
669
+ // No match - need to create a new unique ID
670
+ const baseId = sourceIndi.id;
671
+ let newId = baseId;
672
+ let counter = 1;
673
+ // Generate unique ID that doesn't conflict with target
674
+ while (targetIndis?.item(newId) || idMap.has(newId)) {
675
+ // Extract number from ID like @I123@ and increment
676
+ const numMatch = baseId.match(/\d+/);
677
+ const prefix = baseId.match(/^@[A-Z]+/)?.[0] || "@I";
678
+ const baseNum = numMatch ? parseInt(numMatch[0]) : 1;
679
+ newId = `${prefix}${baseNum + counter * MERGE_ID_INCREMENT_MULTIPLIER}@`;
680
+ counter++;
681
+ }
682
+ idMap.set(sourceIndi.id, newId);
683
+ }
684
+ });
685
+ // Step 2: Map family IDs and match families with same members
686
+ const famMatchMap = new Map(); // source family ID -> target family ID
687
+ sourceFams?.forEach((sourceFam) => {
688
+ if (!sourceFam.id)
689
+ return;
690
+ // Get the member IDs for this source family
691
+ const sourceHusbId = sourceFam.HUSB?.value;
692
+ const sourceWifeId = sourceFam.WIFE?.value;
693
+ const sourceChildIds = sourceFam.CHIL?.toList()?.map(c => c.value).filter(Boolean) || [];
694
+ // Map to their final IDs (after individual matching by strategy)
695
+ const finalHusbId = sourceHusbId ? idMap.get(sourceHusbId) : undefined;
696
+ const finalWifeId = sourceWifeId ? idMap.get(sourceWifeId) : undefined;
697
+ const finalChildIds = sourceChildIds.map(id => idMap.get(id)).filter(Boolean);
698
+ // Try to find a matching family in target that has the same members (after remapping)
699
+ let matchedTargetFam;
700
+ targetFams?.forEach((targetFam) => {
701
+ if (matchedTargetFam)
702
+ return; // Already found a match
703
+ const targetHusbId = targetFam.HUSB?.value;
704
+ const targetWifeId = targetFam.WIFE?.value;
705
+ const targetChildIds = targetFam.CHIL?.toList()?.map(c => c.value).filter(Boolean) || [];
706
+ // Check if husband matches (either both undefined or same final ID)
707
+ const husbMatch = (!finalHusbId && !targetHusbId) || (finalHusbId === targetHusbId);
708
+ // Check if wife matches (either both undefined or same final ID)
709
+ const wifeMatch = (!finalWifeId && !targetWifeId) || (finalWifeId === targetWifeId);
710
+ // For a family to match, both husband and wife must match
711
+ // (if one is undefined in both, that's also a match)
712
+ if (husbMatch && wifeMatch) {
713
+ // Also check children overlap
714
+ const childOverlap = finalChildIds.filter(id => targetChildIds.includes(id)).length;
715
+ const totalUniqueChildren = new Set([...finalChildIds, ...targetChildIds]).size;
716
+ // Match if:
717
+ // 1. Both have no children, OR
718
+ // 2. At least 50% of children overlap
719
+ if (totalUniqueChildren === 0 || (childOverlap / totalUniqueChildren) >= 0.5) {
720
+ // Additionally require at least one spouse to be present (not both empty)
721
+ if (finalHusbId || finalWifeId || targetHusbId || targetWifeId) {
722
+ matchedTargetFam = targetFam;
723
+ }
724
+ }
725
+ }
726
+ });
727
+ if (matchedTargetFam && matchedTargetFam.id) {
728
+ // Found a matching family - use the target family ID
729
+ famMatchMap.set(sourceFam.id, matchedTargetFam.id);
730
+ idMap.set(sourceFam.id, matchedTargetFam.id);
731
+ }
732
+ else {
733
+ // No match - generate a new unique family ID
734
+ const baseId = sourceFam.id;
735
+ let newId = baseId;
736
+ let counter = 1;
737
+ // Generate unique family ID
738
+ while (targetFams?.item(newId) || idMap.has(newId)) {
739
+ const numMatch = baseId.match(/\d+/);
740
+ const prefix = baseId.match(/^@[A-Z]+/)?.[0] || "@F";
741
+ const baseNum = numMatch ? parseInt(numMatch[0]) : 1;
742
+ newId = `${prefix}${baseNum + counter * MERGE_ID_INCREMENT_MULTIPLIER}@`;
743
+ counter++;
744
+ }
745
+ idMap.set(sourceFam.id, newId);
746
+ }
747
+ });
748
+ // Step 3: Clone and remap individuals from source
749
+ const clonedIndis = new Map();
750
+ sourceIndis?.forEach((sourceIndi) => {
751
+ if (!sourceIndi.id)
752
+ return;
753
+ const newId = idMap.get(sourceIndi.id);
754
+ if (!newId)
755
+ return;
756
+ // Clone the individual
757
+ const cloned = sourceIndi.clone(false);
758
+ cloned.id = newId;
759
+ cloned.setGedcom(mergedGedcom);
760
+ clonedIndis.set(sourceIndi.id, cloned);
761
+ });
762
+ // Step 4: Clone and remap families from source
763
+ const clonedFams = new Map();
764
+ sourceFams?.forEach((sourceFam) => {
765
+ if (!sourceFam.id)
766
+ return;
767
+ const newFamId = idMap.get(sourceFam.id);
768
+ if (!newFamId)
769
+ return;
770
+ // Clone the family
771
+ const clonedFam = sourceFam.clone(false);
772
+ clonedFam.id = newFamId;
773
+ clonedFam.setGedcom(mergedGedcom);
774
+ clonedFams.set(sourceFam.id, clonedFam);
775
+ });
776
+ // Step 5: Update FAMS and FAMC references in cloned individuals
777
+ clonedIndis.forEach((clonedIndi, originalId) => {
778
+ const sourceIndi = sourceIndis?.item(originalId);
779
+ if (!sourceIndi)
780
+ return;
781
+ // Clear and rebuild FAMS references with remapped IDs
782
+ clonedIndi.set("FAMS", undefined);
783
+ const sourceFAMS = sourceIndi.FAMS?.toList();
784
+ sourceFAMS?.forEach((famRef) => {
785
+ const oldFamId = famRef.value;
786
+ if (oldFamId) {
787
+ const newFamId = idMap.get(oldFamId);
788
+ if (newFamId) {
789
+ const newFamRef = createCommon(mergedGedcom, undefined, clonedIndi);
790
+ newFamRef.value = newFamId;
791
+ clonedIndi.assign("FAMS", newFamRef, true);
792
+ }
793
+ }
794
+ });
795
+ // Clear and rebuild FAMC references with remapped IDs
796
+ clonedIndi.set("FAMC", undefined);
797
+ const sourceFAMC = sourceIndi.FAMC?.toList();
798
+ sourceFAMC?.forEach((famRef) => {
799
+ const oldFamId = famRef.value;
800
+ if (oldFamId) {
801
+ const newFamId = idMap.get(oldFamId);
802
+ if (newFamId) {
803
+ const newFamRef = createCommon(mergedGedcom, undefined, clonedIndi);
804
+ newFamRef.value = newFamId;
805
+ clonedIndi.assign("FAMC", newFamRef, true);
806
+ }
807
+ }
808
+ });
809
+ });
810
+ // Step 6: Update HUSB, WIFE, and CHIL references in cloned families
811
+ clonedFams.forEach((clonedFam, originalFamId) => {
812
+ const sourceFam = sourceFams?.item(originalFamId);
813
+ if (!sourceFam)
814
+ return;
815
+ // Update HUSB reference
816
+ const sourceHusb = sourceFam.HUSB?.value;
817
+ if (sourceHusb) {
818
+ const newHusbId = idMap.get(sourceHusb);
819
+ if (newHusbId) {
820
+ const newHusbRef = createCommon(mergedGedcom, undefined, clonedFam);
821
+ newHusbRef.value = newHusbId;
822
+ clonedFam.set("HUSB", newHusbRef);
823
+ }
824
+ }
825
+ // Update WIFE reference
826
+ const sourceWife = sourceFam.WIFE?.value;
827
+ if (sourceWife) {
828
+ const newWifeId = idMap.get(sourceWife);
829
+ if (newWifeId) {
830
+ const newWifeRef = createCommon(mergedGedcom, undefined, clonedFam);
831
+ newWifeRef.value = newWifeId;
832
+ clonedFam.set("WIFE", newWifeRef);
833
+ }
834
+ }
835
+ // Update CHIL references
836
+ clonedFam.set("CHIL", undefined);
837
+ const sourceChildren = sourceFam.CHIL?.toList();
838
+ sourceChildren?.forEach((childRef) => {
839
+ const oldChildId = childRef.value;
840
+ if (oldChildId) {
841
+ const newChildId = idMap.get(oldChildId);
842
+ if (newChildId) {
843
+ const newChildRef = createCommon(mergedGedcom, undefined, clonedFam);
844
+ newChildRef.value = newChildId;
845
+ clonedFam.assign("CHIL", newChildRef, true);
846
+ }
847
+ }
848
+ });
849
+ });
850
+ // Step 7: Add or merge individuals into target GEDCOM
851
+ clonedIndis.forEach((clonedIndi, originalId) => {
852
+ const matchedTargetId = matchMap.get(originalId);
853
+ if (matchedTargetId) {
854
+ // This individual matches an existing one - merge data and relationships
855
+ const targetIndi = mergedGedcom.indis()?.item(matchedTargetId);
856
+ if (targetIndi) {
857
+ // Merge without overriding existing data
858
+ targetIndi.merge(clonedIndi, false);
859
+ }
860
+ }
861
+ else {
862
+ // This is a new individual - add it to merged GEDCOM
863
+ if (clonedIndi.id) {
864
+ mergedGedcom.indis()?.item(clonedIndi.id, clonedIndi);
865
+ }
866
+ }
867
+ });
868
+ // Step 8: Add or merge families into target GEDCOM
869
+ clonedFams.forEach((clonedFam, originalFamId) => {
870
+ if (!clonedFam.id)
871
+ return;
872
+ const matchedTargetFamId = famMatchMap.get(originalFamId);
873
+ if (matchedTargetFamId) {
874
+ // This family matches an existing one - merge children and data
875
+ const targetFam = mergedGedcom.fams()?.item(matchedTargetFamId);
876
+ if (targetFam) {
877
+ // Merge family data without overriding
878
+ targetFam.merge(clonedFam, false);
879
+ // Note: Children from clonedFam are already added via merge above
880
+ // The CHIL references in clonedFam point to the correct remapped IDs
881
+ }
882
+ }
883
+ else {
884
+ // This is a new family - add it to merged GEDCOM
885
+ mergedGedcom.fams()?.item(clonedFam.id, clonedFam);
886
+ }
887
+ });
888
+ return mergedGedcom;
889
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgH3D"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/merge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoE3D"}
@@ -1,90 +1,58 @@
1
1
  import { writeFileSync } from 'fs';
2
2
  import GedcomTree from '../../utils/parser.js';
3
+ import { mergeGedcoms } from '../../classes/gedcom.js';
3
4
  import { formatSuccess } from '../utils/formatters.js';
4
5
  import { readGedcomFile, handleError } from '../utils/helpers.js';
6
+ /**
7
+ * Helper to get and validate the merge strategy
8
+ */
9
+ function getMergeStrategy(options) {
10
+ if (options.dedupe) {
11
+ console.warn('Warning: --dedupe option is deprecated. Use --strategy NAME instead.');
12
+ return 'NAME';
13
+ }
14
+ return (options.strategy || 'id');
15
+ }
5
16
  export function registerMergeCommand(program) {
6
17
  program
7
18
  .command('merge <files...>')
8
19
  .description('Merge multiple GEDCOM files')
9
20
  .requiredOption('-o, --output <file>', 'Output file path (required)')
10
- .option('--dedupe', 'Attempt to detect and merge duplicates (basic implementation)')
21
+ .option('--dedupe', 'Attempt to detect and merge duplicates (deprecated, use --strategy NAME)')
22
+ .option('--strategy <strategy>', 'Matching strategy: "id" (match by ID) or a tag like "NAME" (match by name). Default: "id"', 'id')
11
23
  .action((files, options) => {
12
24
  try {
13
25
  if (files.length < 2) {
14
26
  console.error('At least 2 files are required for merging');
15
27
  process.exit(1);
16
28
  }
17
- const allIndividuals = [];
18
- const allFamilies = [];
19
- const seenIds = new Set();
20
- let idCounter = 1;
21
- // Parse all files
22
- files.forEach(file => {
23
- const content = readGedcomFile(file);
24
- const { gedcom: tree } = GedcomTree.parse(content);
25
- const individuals = tree.indis();
26
- const families = tree.fams();
27
- // Add individuals with unique IDs
28
- individuals.forEach(indi => {
29
- let id = indi.id;
30
- // If dedupe is enabled, check for duplicates (basic check by name and birth date)
31
- if (options.dedupe) {
32
- const name = indi.NAME?.toValue();
33
- const birthDate = indi.BIRT?.DATE?.toValue();
34
- const duplicate = allIndividuals.find(existing => {
35
- return existing.NAME?.toValue() === name &&
36
- existing.BIRT?.DATE?.toValue() === birthDate;
37
- });
38
- if (duplicate) {
39
- // Skip duplicate
40
- return;
41
- }
42
- }
43
- // Ensure unique ID
44
- while (seenIds.has(id)) {
45
- id = `@I${idCounter++}@`;
46
- }
47
- seenIds.add(id);
48
- // Store with original object (we'll need to update ID in output)
49
- allIndividuals.push({ ...indi, newId: id });
50
- });
51
- // Add families with unique IDs
52
- families.forEach(fam => {
53
- let id = fam.id;
54
- while (seenIds.has(id)) {
55
- id = `@F${idCounter++}@`;
56
- }
57
- seenIds.add(id);
58
- allFamilies.push({ ...fam, newId: id });
59
- });
60
- });
61
- // Create merged GEDCOM
62
- const lines = [];
63
- lines.push('0 HEAD');
64
- lines.push('1 SOUR gedcom-parser CLI');
65
- lines.push('1 GEDC');
66
- lines.push('2 VERS 5.5.1');
67
- lines.push('1 CHAR UTF-8');
68
- // Add all individuals
69
- allIndividuals.forEach(indi => {
70
- const raw = indi.raw?.() || '';
71
- if (raw) {
72
- // Replace old ID with new ID if needed
73
- const updatedRaw = raw.replace(new RegExp(`^0 ${indi.id} INDI`, 'm'), `0 ${indi.newId} INDI`);
74
- lines.push(...updatedRaw.split('\n').filter(line => line.trim()));
75
- }
76
- });
77
- // Add all families
78
- allFamilies.forEach(fam => {
79
- const raw = fam.raw?.() || '';
80
- if (raw) {
81
- const updatedRaw = raw.replace(new RegExp(`^0 ${fam.id} FAM`, 'm'), `0 ${fam.newId} FAM`);
82
- lines.push(...updatedRaw.split('\n').filter(line => line.trim()));
83
- }
84
- });
85
- lines.push('0 TRLR');
86
- writeFileSync(options.output, lines.join('\n'), 'utf-8');
87
- console.log(formatSuccess(`Merged ${files.length} files (${allIndividuals.length} individuals, ${allFamilies.length} families) into ${options.output}`));
29
+ // For 2 files, use the new mergeGedcoms function
30
+ if (files.length === 2) {
31
+ const targetContent = readGedcomFile(files[0]);
32
+ const sourceContent = readGedcomFile(files[1]);
33
+ const { gedcom: targetGedcom } = GedcomTree.parse(targetContent);
34
+ const { gedcom: sourceGedcom } = GedcomTree.parse(sourceContent);
35
+ const strategy = getMergeStrategy(options);
36
+ const merged = mergeGedcoms(targetGedcom, sourceGedcom, strategy);
37
+ const mergedContent = merged.toGedcom();
38
+ writeFileSync(options.output, mergedContent, 'utf-8');
39
+ console.log(formatSuccess(`Merged 2 files using strategy "${strategy}" (${merged.indis()?.length} individuals, ${merged.fams()?.length} families) into ${options.output}`));
40
+ return;
41
+ }
42
+ // For more than 2 files, use iterative merging
43
+ console.log(`Merging ${files.length} files iteratively...`);
44
+ let targetContent = readGedcomFile(files[0]);
45
+ let { gedcom: targetGedcom } = GedcomTree.parse(targetContent);
46
+ const strategy = getMergeStrategy(options);
47
+ for (let i = 1; i < files.length; i++) {
48
+ const sourceContent = readGedcomFile(files[i]);
49
+ const { gedcom: sourceGedcom } = GedcomTree.parse(sourceContent);
50
+ targetGedcom = mergeGedcoms(targetGedcom, sourceGedcom, strategy);
51
+ console.log(` Merged file ${i + 1}/${files.length}: ${files[i]}`);
52
+ }
53
+ const mergedContent = targetGedcom.toGedcom();
54
+ writeFileSync(options.output, mergedContent, 'utf-8');
55
+ console.log(formatSuccess(`Merged ${files.length} files (${targetGedcom.indis()?.length} individuals, ${targetGedcom.fams()?.length} families) into ${options.output}`));
88
56
  }
89
57
  catch (error) {
90
58
  handleError(error, 'Failed to merge GEDCOM files');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeviz/gedcom-parser",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Lightweight, pluggable GEDCOM parser for JavaScript/TypeScript with optional caching and place matching. Zero browser dependencies.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",