@treeviz/gedcom-parser 2.1.3 → 2.1.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.
@@ -176,6 +176,10 @@ var storeCache = {
176
176
  }
177
177
  }, 100)
178
178
  };
179
+ var resetRelativesCache = () => {
180
+ caches.relativesOnDegreeCache = {};
181
+ caches.relativesOnLevelCache = {};
182
+ };
179
183
  var relativesCache = (cacheKey) => (gedcom, key, subKey, value) => {
180
184
  const gedcomId = getGedcomId(gedcom);
181
185
  const fullKey = `${gedcomId}:${key}`;
@@ -1720,6 +1724,28 @@ var addNonEvent = (owner, input) => {
1720
1724
  };
1721
1725
 
1722
1726
  // src/utils/family-edit.ts
1727
+ var refTypeForTag = (tag) => {
1728
+ if (tag === "FAMS" || tag === "FAMC") return "FAM";
1729
+ return "INDI";
1730
+ };
1731
+ var normalizeSinglePointer = (owner, tag) => {
1732
+ const node = owner.get(tag);
1733
+ if (!node) return;
1734
+ if (node instanceof List && node.length === 1) {
1735
+ const only = node.index(0);
1736
+ if (only) {
1737
+ owner.set(tag, only);
1738
+ return;
1739
+ }
1740
+ const raw = node.values()[0];
1741
+ if (typeof raw === "string") {
1742
+ const gedcom = owner.getGedcom?.();
1743
+ if (gedcom) {
1744
+ owner.set(tag, makePointer(gedcom, owner, raw, refTypeForTag(tag)));
1745
+ }
1746
+ }
1747
+ }
1748
+ };
1723
1749
  var PEDIGREE_VALUES = [
1724
1750
  "birth" /* BIRTH */,
1725
1751
  "adopted" /* ADOPTED */,
@@ -1827,6 +1853,9 @@ var attachSpouseToFamily = (fam, indi) => {
1827
1853
  fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
1828
1854
  }
1829
1855
  indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
1856
+ normalizeSinglePointer(indi, "FAMS");
1857
+ normalizeSinglePointer(fam, spouseTagFor(indi, fam));
1858
+ resetRelativesCache();
1830
1859
  return true;
1831
1860
  };
1832
1861
  var attachChildToFamily = (fam, child, pedigree, parent) => {
@@ -1842,14 +1871,122 @@ var attachChildToFamily = (fam, child, pedigree, parent) => {
1842
1871
  child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
1843
1872
  }
1844
1873
  setChildPedigree(fam, child, pedigree, parent);
1874
+ normalizeSinglePointer(child, "FAMC");
1875
+ normalizeSinglePointer(fam, "CHIL");
1876
+ resetRelativesCache();
1845
1877
  return true;
1846
1878
  };
1847
- var findReusableParentChildFamily = (parent, child) => {
1879
+ var removePointerByValue = (owner, tag, targetId) => {
1880
+ const node = owner.get(tag);
1881
+ if (!node) {
1882
+ return false;
1883
+ }
1884
+ if (node instanceof List) {
1885
+ const keysToRemove = [];
1886
+ node.entries().forEach(([key, item]) => {
1887
+ if (item?.toValue?.() === targetId || item?.id === targetId || String(key) === targetId) {
1888
+ keysToRemove.push(key);
1889
+ }
1890
+ });
1891
+ keysToRemove.forEach((key) => {
1892
+ node.removeItem(key);
1893
+ });
1894
+ if (node.length === 0) {
1895
+ owner.remove(tag);
1896
+ }
1897
+ return keysToRemove.length > 0;
1898
+ }
1899
+ if (node.toValue?.() === targetId) {
1900
+ owner.remove(tag);
1901
+ return true;
1902
+ }
1903
+ const asList = node.toList?.();
1904
+ if (asList && asList.length > 0) {
1905
+ const keysToRemove = [];
1906
+ asList.entries().forEach(([key, item]) => {
1907
+ if (item?.toValue?.() === targetId || item?.id === targetId) {
1908
+ keysToRemove.push(key);
1909
+ }
1910
+ });
1911
+ if (keysToRemove.length === asList.length) {
1912
+ owner.remove(tag);
1913
+ return true;
1914
+ }
1915
+ if (keysToRemove.length > 0) {
1916
+ const remaining = asList.values().filter((item) => item?.toValue?.() !== targetId);
1917
+ if (remaining.length === 1 && remaining[0]) {
1918
+ owner.set(tag, remaining[0]);
1919
+ } else {
1920
+ owner.set(tag, new List(remaining));
1921
+ }
1922
+ return true;
1923
+ }
1924
+ }
1925
+ return false;
1926
+ };
1927
+ var familyMemberCount = (fam) => {
1928
+ let count = 0;
1929
+ if (firstPointerId(fam.get("HUSB"))) {
1930
+ count += 1;
1931
+ }
1932
+ if (firstPointerId(fam.get("WIFE"))) {
1933
+ count += 1;
1934
+ }
1935
+ count += pointerIds(fam.get("CHIL")).length;
1936
+ return count;
1937
+ };
1938
+ var isDegenerateFamily = (fam) => familyMemberCount(fam) <= 1;
1939
+ var pruneEmptyFamilies = (gedcom) => {
1940
+ const emptyIds = [];
1941
+ gedcom.fams()?.forEach((fam, id) => {
1942
+ if (fam && isDegenerateFamily(fam) && id) {
1943
+ emptyIds.push(id);
1944
+ }
1945
+ });
1946
+ if (emptyIds.length === 0) {
1947
+ return [];
1948
+ }
1949
+ gedcom.indis()?.forEach((indi) => {
1950
+ if (!indi) {
1951
+ return;
1952
+ }
1953
+ emptyIds.forEach((famId) => {
1954
+ removePointerByValue(indi, "FAMS", famId);
1955
+ removePointerByValue(indi, "FAMC", famId);
1956
+ });
1957
+ });
1958
+ emptyIds.forEach((famId) => {
1959
+ gedcom.fams()?.removeItem(famId);
1960
+ });
1961
+ return emptyIds;
1962
+ };
1963
+ var findSharedSpouseFamily = (parent, coParentId) => {
1964
+ let found;
1965
+ parent.getFamilies("FAMS")?.forEach((fam) => {
1966
+ if (!found && fam && familyHasParent(fam, coParentId)) {
1967
+ found = fam;
1968
+ }
1969
+ });
1970
+ return found;
1971
+ };
1972
+ var findReusableParentChildFamily = (parent, child, options) => {
1848
1973
  if (!parent.id || !child.id) {
1849
1974
  return void 0;
1850
1975
  }
1976
+ const reuseParentFamilies = options?.reuseParentFamilies !== false;
1977
+ const gedcom = parent.getGedcom() ?? child.getGedcom();
1851
1978
  let reusableFamc;
1979
+ let coupleFam;
1852
1980
  let already = false;
1981
+ const noteCoupleWith = (coParentId) => {
1982
+ if (!coParentId || coParentId === parent.id || coupleFam || !parent.id) {
1983
+ return;
1984
+ }
1985
+ const shared = findSharedSpouseFamily(parent, coParentId);
1986
+ if (shared) {
1987
+ coupleFam = shared;
1988
+ }
1989
+ };
1853
1990
  child.getFamilies("FAMC")?.forEach((fam) => {
1854
1991
  if (!fam) {
1855
1992
  return;
@@ -1858,6 +1995,8 @@ var findReusableParentChildFamily = (parent, child) => {
1858
1995
  already = true;
1859
1996
  return;
1860
1997
  }
1998
+ pointerIds(fam.get("HUSB")).forEach(noteCoupleWith);
1999
+ pointerIds(fam.get("WIFE")).forEach(noteCoupleWith);
1861
2000
  if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
1862
2001
  reusableFamc = fam;
1863
2002
  }
@@ -1865,9 +2004,21 @@ var findReusableParentChildFamily = (parent, child) => {
1865
2004
  if (already) {
1866
2005
  return "already";
1867
2006
  }
2007
+ if (coupleFam) {
2008
+ return coupleFam;
2009
+ }
1868
2010
  if (reusableFamc) {
1869
2011
  return reusableFamc;
1870
2012
  }
2013
+ if (options?.targetFamilyId && gedcom) {
2014
+ const target = gedcom.fam(options.targetFamilyId);
2015
+ if (target && (familyHasParent(target, parent.id) || canAttachAsSpouse(target, parent))) {
2016
+ return target;
2017
+ }
2018
+ }
2019
+ if (!reuseParentFamilies) {
2020
+ return void 0;
2021
+ }
1871
2022
  const parentFams = [];
1872
2023
  parent.getFamilies("FAMS")?.forEach((fam) => {
1873
2024
  if (fam) {
@@ -1879,6 +2030,41 @@ var findReusableParentChildFamily = (parent, child) => {
1879
2030
  }
1880
2031
  return void 0;
1881
2032
  };
2033
+ var consolidateChildIntoFamily = (child, keep) => {
2034
+ if (!child.id || !keep.id) {
2035
+ return [];
2036
+ }
2037
+ const keepParentIds = /* @__PURE__ */ new Set([
2038
+ ...pointerIds(keep.get("HUSB")),
2039
+ ...pointerIds(keep.get("WIFE"))
2040
+ ]);
2041
+ const removed = [];
2042
+ child.getFamilies("FAMC")?.forEach((fam) => {
2043
+ if (!fam?.id || fam.id === keep.id) {
2044
+ return;
2045
+ }
2046
+ const famParentIds = [
2047
+ ...pointerIds(fam.get("HUSB")),
2048
+ ...pointerIds(fam.get("WIFE"))
2049
+ ];
2050
+ if (famParentIds.length === 0 || !famParentIds.every((id) => keepParentIds.has(id))) {
2051
+ return;
2052
+ }
2053
+ removePointerByValue(fam, "CHIL", child.id);
2054
+ normalizeSinglePointer(fam, "CHIL");
2055
+ removePointerByValue(child, "FAMC", fam.id);
2056
+ normalizeSinglePointer(child, "FAMC");
2057
+ removed.push(fam.id);
2058
+ });
2059
+ const gedcom = child.getGedcom() ?? keep.getGedcom();
2060
+ if (gedcom && removed.length > 0) {
2061
+ pruneEmptyFamilies(gedcom);
2062
+ }
2063
+ if (removed.length > 0) {
2064
+ resetRelativesCache();
2065
+ }
2066
+ return removed;
2067
+ };
1882
2068
 
1883
2069
  // src/utils/get-family-with.ts
1884
2070
  var getFamilyWith = (person1, person2, famType = "FAMS") => {
@@ -3910,13 +4096,17 @@ var Indi = class extends Common {
3910
4096
  }
3911
4097
  return fam;
3912
4098
  }
3913
- addChild(child, pedigree) {
4099
+ addChild(child, pedigree, options) {
3914
4100
  if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
3915
4101
  return void 0;
3916
4102
  }
3917
4103
  const existing = findReusableParentChildFamily(
3918
4104
  this,
3919
- child
4105
+ child,
4106
+ {
4107
+ reuseParentFamilies: options?.reuseParentFamilies,
4108
+ targetFamilyId: options?.targetFamilyId
4109
+ }
3920
4110
  );
3921
4111
  if (existing === "already") {
3922
4112
  const fam2 = child.getFamilies("FAMC")?.find(
@@ -3939,10 +4129,16 @@ var Indi = class extends Common {
3939
4129
  if (!fam.addChild(child, pedigree, this)) {
3940
4130
  return void 0;
3941
4131
  }
4132
+ if (existing) {
4133
+ consolidateChildIntoFamily(child, fam);
4134
+ }
3942
4135
  return fam;
3943
4136
  }
3944
- addParent(parent, pedigree) {
3945
- return parent.addChild(this, pedigree);
4137
+ addParent(parent, pedigree, options) {
4138
+ return parent.addChild(this, pedigree, {
4139
+ reuseParentFamilies: false,
4140
+ targetFamilyId: options?.targetFamilyId
4141
+ });
3946
4142
  }
3947
4143
  /**
3948
4144
  * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
@@ -1932,6 +1932,10 @@ var storeCache = {
1932
1932
  }
1933
1933
  }, 100)
1934
1934
  };
1935
+ var resetRelativesCache = () => {
1936
+ caches.relativesOnDegreeCache = {};
1937
+ caches.relativesOnLevelCache = {};
1938
+ };
1935
1939
  var relativesCache = (cacheKey) => (gedcom, key, subKey, value) => {
1936
1940
  const gedcomId = getGedcomId(gedcom);
1937
1941
  const fullKey = `${gedcomId}:${key}`;
@@ -2197,6 +2201,28 @@ var addNonEvent = (owner, input) => {
2197
2201
  };
2198
2202
 
2199
2203
  // src/utils/family-edit.ts
2204
+ var refTypeForTag = (tag) => {
2205
+ if (tag === "FAMS" || tag === "FAMC") return "FAM";
2206
+ return "INDI";
2207
+ };
2208
+ var normalizeSinglePointer = (owner, tag) => {
2209
+ const node = owner.get(tag);
2210
+ if (!node) return;
2211
+ if (node instanceof List && node.length === 1) {
2212
+ const only = node.index(0);
2213
+ if (only) {
2214
+ owner.set(tag, only);
2215
+ return;
2216
+ }
2217
+ const raw = node.values()[0];
2218
+ if (typeof raw === "string") {
2219
+ const gedcom = owner.getGedcom?.();
2220
+ if (gedcom) {
2221
+ owner.set(tag, makePointer(gedcom, owner, raw, refTypeForTag(tag)));
2222
+ }
2223
+ }
2224
+ }
2225
+ };
2200
2226
  var PEDIGREE_VALUES = [
2201
2227
  "birth" /* BIRTH */,
2202
2228
  "adopted" /* ADOPTED */,
@@ -2304,6 +2330,9 @@ var attachSpouseToFamily = (fam, indi) => {
2304
2330
  fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
2305
2331
  }
2306
2332
  indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
2333
+ normalizeSinglePointer(indi, "FAMS");
2334
+ normalizeSinglePointer(fam, spouseTagFor(indi, fam));
2335
+ resetRelativesCache();
2307
2336
  return true;
2308
2337
  };
2309
2338
  var attachChildToFamily = (fam, child, pedigree, parent) => {
@@ -2319,14 +2348,122 @@ var attachChildToFamily = (fam, child, pedigree, parent) => {
2319
2348
  child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
2320
2349
  }
2321
2350
  setChildPedigree(fam, child, pedigree, parent);
2351
+ normalizeSinglePointer(child, "FAMC");
2352
+ normalizeSinglePointer(fam, "CHIL");
2353
+ resetRelativesCache();
2322
2354
  return true;
2323
2355
  };
2324
- var findReusableParentChildFamily = (parent, child) => {
2356
+ var removePointerByValue = (owner, tag, targetId) => {
2357
+ const node = owner.get(tag);
2358
+ if (!node) {
2359
+ return false;
2360
+ }
2361
+ if (node instanceof List) {
2362
+ const keysToRemove = [];
2363
+ node.entries().forEach(([key, item]) => {
2364
+ if (item?.toValue?.() === targetId || item?.id === targetId || String(key) === targetId) {
2365
+ keysToRemove.push(key);
2366
+ }
2367
+ });
2368
+ keysToRemove.forEach((key) => {
2369
+ node.removeItem(key);
2370
+ });
2371
+ if (node.length === 0) {
2372
+ owner.remove(tag);
2373
+ }
2374
+ return keysToRemove.length > 0;
2375
+ }
2376
+ if (node.toValue?.() === targetId) {
2377
+ owner.remove(tag);
2378
+ return true;
2379
+ }
2380
+ const asList = node.toList?.();
2381
+ if (asList && asList.length > 0) {
2382
+ const keysToRemove = [];
2383
+ asList.entries().forEach(([key, item]) => {
2384
+ if (item?.toValue?.() === targetId || item?.id === targetId) {
2385
+ keysToRemove.push(key);
2386
+ }
2387
+ });
2388
+ if (keysToRemove.length === asList.length) {
2389
+ owner.remove(tag);
2390
+ return true;
2391
+ }
2392
+ if (keysToRemove.length > 0) {
2393
+ const remaining = asList.values().filter((item) => item?.toValue?.() !== targetId);
2394
+ if (remaining.length === 1 && remaining[0]) {
2395
+ owner.set(tag, remaining[0]);
2396
+ } else {
2397
+ owner.set(tag, new List(remaining));
2398
+ }
2399
+ return true;
2400
+ }
2401
+ }
2402
+ return false;
2403
+ };
2404
+ var familyMemberCount = (fam) => {
2405
+ let count = 0;
2406
+ if (firstPointerId(fam.get("HUSB"))) {
2407
+ count += 1;
2408
+ }
2409
+ if (firstPointerId(fam.get("WIFE"))) {
2410
+ count += 1;
2411
+ }
2412
+ count += pointerIds(fam.get("CHIL")).length;
2413
+ return count;
2414
+ };
2415
+ var isDegenerateFamily = (fam) => familyMemberCount(fam) <= 1;
2416
+ var pruneEmptyFamilies = (gedcom) => {
2417
+ const emptyIds = [];
2418
+ gedcom.fams()?.forEach((fam, id) => {
2419
+ if (fam && isDegenerateFamily(fam) && id) {
2420
+ emptyIds.push(id);
2421
+ }
2422
+ });
2423
+ if (emptyIds.length === 0) {
2424
+ return [];
2425
+ }
2426
+ gedcom.indis()?.forEach((indi) => {
2427
+ if (!indi) {
2428
+ return;
2429
+ }
2430
+ emptyIds.forEach((famId) => {
2431
+ removePointerByValue(indi, "FAMS", famId);
2432
+ removePointerByValue(indi, "FAMC", famId);
2433
+ });
2434
+ });
2435
+ emptyIds.forEach((famId) => {
2436
+ gedcom.fams()?.removeItem(famId);
2437
+ });
2438
+ return emptyIds;
2439
+ };
2440
+ var findSharedSpouseFamily = (parent, coParentId) => {
2441
+ let found;
2442
+ parent.getFamilies("FAMS")?.forEach((fam) => {
2443
+ if (!found && fam && familyHasParent(fam, coParentId)) {
2444
+ found = fam;
2445
+ }
2446
+ });
2447
+ return found;
2448
+ };
2449
+ var findReusableParentChildFamily = (parent, child, options) => {
2325
2450
  if (!parent.id || !child.id) {
2326
2451
  return void 0;
2327
2452
  }
2453
+ const reuseParentFamilies = options?.reuseParentFamilies !== false;
2454
+ const gedcom = parent.getGedcom() ?? child.getGedcom();
2328
2455
  let reusableFamc;
2456
+ let coupleFam;
2329
2457
  let already = false;
2458
+ const noteCoupleWith = (coParentId) => {
2459
+ if (!coParentId || coParentId === parent.id || coupleFam || !parent.id) {
2460
+ return;
2461
+ }
2462
+ const shared = findSharedSpouseFamily(parent, coParentId);
2463
+ if (shared) {
2464
+ coupleFam = shared;
2465
+ }
2466
+ };
2330
2467
  child.getFamilies("FAMC")?.forEach((fam) => {
2331
2468
  if (!fam) {
2332
2469
  return;
@@ -2335,6 +2472,8 @@ var findReusableParentChildFamily = (parent, child) => {
2335
2472
  already = true;
2336
2473
  return;
2337
2474
  }
2475
+ pointerIds(fam.get("HUSB")).forEach(noteCoupleWith);
2476
+ pointerIds(fam.get("WIFE")).forEach(noteCoupleWith);
2338
2477
  if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
2339
2478
  reusableFamc = fam;
2340
2479
  }
@@ -2342,9 +2481,21 @@ var findReusableParentChildFamily = (parent, child) => {
2342
2481
  if (already) {
2343
2482
  return "already";
2344
2483
  }
2484
+ if (coupleFam) {
2485
+ return coupleFam;
2486
+ }
2345
2487
  if (reusableFamc) {
2346
2488
  return reusableFamc;
2347
2489
  }
2490
+ if (options?.targetFamilyId && gedcom) {
2491
+ const target = gedcom.fam(options.targetFamilyId);
2492
+ if (target && (familyHasParent(target, parent.id) || canAttachAsSpouse(target, parent))) {
2493
+ return target;
2494
+ }
2495
+ }
2496
+ if (!reuseParentFamilies) {
2497
+ return void 0;
2498
+ }
2348
2499
  const parentFams = [];
2349
2500
  parent.getFamilies("FAMS")?.forEach((fam) => {
2350
2501
  if (fam) {
@@ -2356,6 +2507,41 @@ var findReusableParentChildFamily = (parent, child) => {
2356
2507
  }
2357
2508
  return void 0;
2358
2509
  };
2510
+ var consolidateChildIntoFamily = (child, keep) => {
2511
+ if (!child.id || !keep.id) {
2512
+ return [];
2513
+ }
2514
+ const keepParentIds = /* @__PURE__ */ new Set([
2515
+ ...pointerIds(keep.get("HUSB")),
2516
+ ...pointerIds(keep.get("WIFE"))
2517
+ ]);
2518
+ const removed = [];
2519
+ child.getFamilies("FAMC")?.forEach((fam) => {
2520
+ if (!fam?.id || fam.id === keep.id) {
2521
+ return;
2522
+ }
2523
+ const famParentIds = [
2524
+ ...pointerIds(fam.get("HUSB")),
2525
+ ...pointerIds(fam.get("WIFE"))
2526
+ ];
2527
+ if (famParentIds.length === 0 || !famParentIds.every((id) => keepParentIds.has(id))) {
2528
+ return;
2529
+ }
2530
+ removePointerByValue(fam, "CHIL", child.id);
2531
+ normalizeSinglePointer(fam, "CHIL");
2532
+ removePointerByValue(child, "FAMC", fam.id);
2533
+ normalizeSinglePointer(child, "FAMC");
2534
+ removed.push(fam.id);
2535
+ });
2536
+ const gedcom = child.getGedcom() ?? keep.getGedcom();
2537
+ if (gedcom && removed.length > 0) {
2538
+ pruneEmptyFamilies(gedcom);
2539
+ }
2540
+ if (removed.length > 0) {
2541
+ resetRelativesCache();
2542
+ }
2543
+ return removed;
2544
+ };
2359
2545
 
2360
2546
  // src/utils/get-places.ts
2361
2547
  var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, mainKey) => {
@@ -4407,13 +4593,17 @@ var Indi = class extends Common {
4407
4593
  }
4408
4594
  return fam;
4409
4595
  }
4410
- addChild(child, pedigree) {
4596
+ addChild(child, pedigree, options) {
4411
4597
  if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
4412
4598
  return void 0;
4413
4599
  }
4414
4600
  const existing = findReusableParentChildFamily(
4415
4601
  this,
4416
- child
4602
+ child,
4603
+ {
4604
+ reuseParentFamilies: options?.reuseParentFamilies,
4605
+ targetFamilyId: options?.targetFamilyId
4606
+ }
4417
4607
  );
4418
4608
  if (existing === "already") {
4419
4609
  const fam2 = child.getFamilies("FAMC")?.find(
@@ -4436,10 +4626,16 @@ var Indi = class extends Common {
4436
4626
  if (!fam.addChild(child, pedigree, this)) {
4437
4627
  return void 0;
4438
4628
  }
4629
+ if (existing) {
4630
+ consolidateChildIntoFamily(child, fam);
4631
+ }
4439
4632
  return fam;
4440
4633
  }
4441
- addParent(parent, pedigree) {
4442
- return parent.addChild(this, pedigree);
4634
+ addParent(parent, pedigree, options) {
4635
+ return parent.addChild(this, pedigree, {
4636
+ reuseParentFamilies: false,
4637
+ targetFamilyId: options?.targetFamilyId
4638
+ });
4443
4639
  }
4444
4640
  /**
4445
4641
  * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).