@treeviz/gedcom-parser 2.0.7 → 2.0.9

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 (29) hide show
  1. package/dist/classes/date.d.ts +2 -0
  2. package/dist/classes/date.d.ts.map +1 -1
  3. package/dist/classes/index.js +424 -159
  4. package/dist/classes/indi.d.ts.map +1 -1
  5. package/dist/cli/index.js +424 -159
  6. package/dist/constants/index.js +423 -158
  7. package/dist/factories/index.js +423 -158
  8. package/dist/index.js +425 -160
  9. package/dist/kinship-translator/index.js +423 -158
  10. package/dist/kinship-translator/kinship-translator.basic.d.ts +11 -0
  11. package/dist/kinship-translator/kinship-translator.basic.d.ts.map +1 -1
  12. package/dist/kinship-translator/kinship-translator.d.ts.map +1 -1
  13. package/dist/kinship-translator/kinship-translator.de.d.ts +2 -1
  14. package/dist/kinship-translator/kinship-translator.de.d.ts.map +1 -1
  15. package/dist/kinship-translator/kinship-translator.en.d.ts +2 -1
  16. package/dist/kinship-translator/kinship-translator.en.d.ts.map +1 -1
  17. package/dist/kinship-translator/kinship-translator.es.d.ts +2 -1
  18. package/dist/kinship-translator/kinship-translator.es.d.ts.map +1 -1
  19. package/dist/kinship-translator/kinship-translator.fr.d.ts +2 -1
  20. package/dist/kinship-translator/kinship-translator.fr.d.ts.map +1 -1
  21. package/dist/kinship-translator/kinship-translator.hu.d.ts +8 -1
  22. package/dist/kinship-translator/kinship-translator.hu.d.ts.map +1 -1
  23. package/dist/kinship-translator/kinship-translator.interface.d.ts +1 -0
  24. package/dist/kinship-translator/kinship-translator.interface.d.ts.map +1 -1
  25. package/dist/kinship-translator/patterns.es.d.ts.map +1 -1
  26. package/dist/utils/cache.d.ts +14 -1
  27. package/dist/utils/cache.d.ts.map +1 -1
  28. package/dist/utils/index.js +425 -160
  29. package/package.json +1 -1
@@ -217,6 +217,135 @@ var profilePictureCache = (gedcom, key, value) => {
217
217
  const cached = caches.profilePictureCache?.[fullKey];
218
218
  return cached;
219
219
  };
220
+ var isNonBiologicalPath = (path) => path.some(
221
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
222
+ );
223
+ var invertHop = (kinship) => {
224
+ if (kinship === "parent") {
225
+ return "child";
226
+ }
227
+ if (kinship === "child") {
228
+ return "parent";
229
+ }
230
+ return kinship;
231
+ };
232
+ var hopRelation = (from, toIndi, kinship, inherited) => {
233
+ if (kinship === "parent" || kinship === "child") {
234
+ const parent = kinship === "parent" ? toIndi : from.indi;
235
+ const child = kinship === "parent" ? from.indi : toIndi;
236
+ const currentRelation = child.getParentType(parent);
237
+ if (currentRelation && currentRelation !== "biological" /* BIOLOGICAL */) {
238
+ return currentRelation;
239
+ }
240
+ }
241
+ if (inherited && inherited !== "biological" /* BIOLOGICAL */) {
242
+ return inherited;
243
+ }
244
+ return void 0;
245
+ };
246
+ var rebuildPathFromHops = (hops, skipRelation = false) => {
247
+ if (hops.length === 0) {
248
+ return [];
249
+ }
250
+ const path = [
251
+ {
252
+ indi: hops[0].indi,
253
+ kinship: "self",
254
+ level: 0,
255
+ levelUp: 0,
256
+ levelDown: 0,
257
+ degree: 0
258
+ }
259
+ ];
260
+ for (let i = 1; i < hops.length; i++) {
261
+ const prev = path[i - 1];
262
+ const { indi, kinship } = hops[i];
263
+ const relation = skipRelation ? void 0 : hopRelation(prev, indi, kinship, prev.relation);
264
+ if (kinship === "parent") {
265
+ path.push({
266
+ indi,
267
+ kinship,
268
+ relation,
269
+ level: prev.level + 1,
270
+ levelUp: prev.levelUp + 1,
271
+ levelDown: prev.levelDown,
272
+ degree: prev.degree
273
+ });
274
+ continue;
275
+ }
276
+ if (kinship === "child") {
277
+ path.push({
278
+ indi,
279
+ kinship,
280
+ relation,
281
+ level: prev.level - 1,
282
+ levelUp: prev.levelUp,
283
+ levelDown: prev.levelDown + 1,
284
+ degree: prev.levelUp ? prev.level > 0 ? prev.levelUp - prev.level + 1 : prev.levelDown - Math.abs(prev.level) : 0
285
+ });
286
+ continue;
287
+ }
288
+ path.push({
289
+ indi,
290
+ kinship,
291
+ relation,
292
+ level: prev.level,
293
+ levelUp: prev.levelUp,
294
+ levelDown: prev.levelDown,
295
+ degree: prev.degree
296
+ });
297
+ }
298
+ return path;
299
+ };
300
+ var storePathIfBetter = (gedcom, fromId, toId, path) => {
301
+ const key = `${fromId}|${toId}`;
302
+ const existing = pathCache(gedcom, key);
303
+ if (existing && (!isNonBiologicalPath(existing) || isNonBiologicalPath(path))) {
304
+ return;
305
+ }
306
+ pathCache(gedcom, key, path);
307
+ };
308
+ var cacheDiscoveredPath = (gedcom, path) => {
309
+ const n = path.length;
310
+ if (n < 2) {
311
+ return;
312
+ }
313
+ for (let i = 0; i < n; i++) {
314
+ const fromId = path[i].indi.id;
315
+ if (!fromId) {
316
+ continue;
317
+ }
318
+ for (let j = i + 1; j < n; j++) {
319
+ const toId = path[j].indi.id;
320
+ if (!toId) {
321
+ continue;
322
+ }
323
+ const isOriginalPair = i === 0 && j === n - 1;
324
+ const isDirectHop = j === i + 1;
325
+ const slice = path.slice(i, j + 1);
326
+ const sliceMixed = isNonBiologicalPath(slice);
327
+ if (!isOriginalPair && !isDirectHop && sliceMixed) {
328
+ continue;
329
+ }
330
+ const forward = i === 0 ? slice : rebuildPathFromHops(slice, !sliceMixed);
331
+ const reverseHops = [
332
+ {
333
+ indi: slice[slice.length - 1].indi,
334
+ kinship: "self"
335
+ }
336
+ ];
337
+ for (let k = slice.length - 2; k >= 0; k--) {
338
+ reverseHops.push({
339
+ indi: slice[k].indi,
340
+ kinship: invertHop(slice[k + 1].kinship)
341
+ });
342
+ }
343
+ const reverse = rebuildPathFromHops(reverseHops, !sliceMixed);
344
+ storePathIfBetter(gedcom, fromId, toId, forward);
345
+ storePathIfBetter(gedcom, toId, fromId, reverse);
346
+ }
347
+ }
348
+ };
220
349
 
221
350
  // src/utils/get-all-prop.ts
222
351
  var getAllProp = (indi, prop) => {
@@ -3478,7 +3607,6 @@ var Indi = class extends Common {
3478
3607
  if (cache) {
3479
3608
  return cache;
3480
3609
  }
3481
- const visited = new Individuals().append(this);
3482
3610
  const mainItem = {
3483
3611
  indi: this,
3484
3612
  level: 0,
@@ -3487,129 +3615,155 @@ var Indi = class extends Common {
3487
3615
  degree: 0,
3488
3616
  kinship: "self"
3489
3617
  };
3490
- const path = [mainItem];
3618
+ const mainPath = [mainItem];
3491
3619
  if (this.id === usedIndi.id) {
3492
- return path;
3620
+ return mainPath;
3493
3621
  }
3494
- const queue = [
3495
- {
3496
- ...mainItem,
3497
- path
3498
- }
3499
- ];
3500
- let helper = 0;
3501
- while (queue.length > 0) {
3502
- if (helper++ > 1e6) {
3503
- break;
3504
- }
3505
- const {
3506
- indi,
3507
- path: path2,
3508
- kinship,
3509
- relation,
3510
- level,
3511
- levelUp,
3512
- levelDown,
3513
- degree,
3514
- breakOnNext,
3515
- breakAfterNext,
3516
- inLaw
3517
- } = queue.shift();
3518
- if (usedIndi.id === indi.id) {
3519
- if (breakOnNext) {
3520
- return void 0;
3622
+ const bfs = (biologicalOnly) => {
3623
+ const visited = new Individuals().append(this);
3624
+ const queue = [
3625
+ {
3626
+ ...mainItem,
3627
+ path: mainPath
3521
3628
  }
3522
- pathCache(this._gedcom, cacheKey, path2);
3523
- return path2;
3524
- }
3525
- visited.append(indi);
3526
- const additional = {};
3527
- if (breakOnNext || breakAfterNext) {
3528
- additional.breakOnNext = breakOnNext || breakAfterNext;
3529
- }
3530
- if (inLaw) {
3531
- additional.inLaw = inLaw;
3532
- }
3533
- if (kinship === "spouse" && breakAfterSpouse) {
3534
- if (path2.length <= 2) {
3535
- additional.inLaw = true;
3536
- } else {
3537
- additional.breakOnNext = true;
3629
+ ];
3630
+ let helper = 0;
3631
+ while (queue.length > 0) {
3632
+ if (helper++ > 1e6) {
3633
+ break;
3538
3634
  }
3539
- }
3540
- if (kinship !== "child" || !breakAfterSpouse) {
3541
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3635
+ const {
3636
+ indi,
3637
+ path,
3638
+ kinship,
3639
+ relation,
3640
+ level,
3641
+ levelUp,
3642
+ levelDown,
3643
+ degree,
3644
+ breakOnNext,
3645
+ breakAfterNext,
3646
+ inLaw
3647
+ } = queue.shift();
3648
+ if (usedIndi.id === indi.id) {
3649
+ if (breakOnNext) {
3650
+ return void 0;
3651
+ }
3652
+ return path;
3653
+ }
3654
+ visited.append(indi);
3655
+ const additional = {};
3656
+ if (breakOnNext || breakAfterNext) {
3657
+ additional.breakOnNext = breakOnNext || breakAfterNext;
3658
+ }
3659
+ if (inLaw) {
3660
+ additional.inLaw = inLaw;
3661
+ }
3662
+ if (!biologicalOnly && kinship === "spouse" && breakAfterSpouse) {
3663
+ if (path.length <= 2) {
3664
+ additional.inLaw = true;
3665
+ } else {
3666
+ additional.breakOnNext = true;
3667
+ }
3668
+ }
3669
+ if (kinship !== "child" || !breakAfterSpouse) {
3670
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3671
+ if (!visited.has(relative)) {
3672
+ const currentRelation = indi.getParentType(relative);
3673
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3674
+ if (biologicalOnly && isNonBiological) {
3675
+ return;
3676
+ }
3677
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
3678
+ additional.breakAfterNext = true;
3679
+ }
3680
+ const newItem = {
3681
+ indi: relative,
3682
+ kinship: "parent",
3683
+ relation: isNonBiological ? currentRelation : relation,
3684
+ level: level + 1,
3685
+ levelUp: levelUp + 1,
3686
+ levelDown,
3687
+ degree,
3688
+ ...additional
3689
+ };
3690
+ queue.push({
3691
+ ...newItem,
3692
+ path: [...path, newItem]
3693
+ });
3694
+ }
3695
+ });
3696
+ }
3697
+ indi.getChildren().forEach((relative) => {
3542
3698
  if (!visited.has(relative)) {
3543
- const currentRelation = indi.getParentType(relative);
3544
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
3699
+ const currentRelation = relative.getParentType(indi);
3700
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3701
+ if (biologicalOnly && isNonBiological) {
3702
+ return;
3703
+ }
3704
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
3545
3705
  additional.breakAfterNext = true;
3546
3706
  }
3547
3707
  const newItem = {
3548
3708
  indi: relative,
3549
- kinship: "parent",
3550
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
3551
- level: level + 1,
3552
- levelUp: levelUp + 1,
3553
- levelDown,
3554
- degree,
3709
+ kinship: "child",
3710
+ relation: isNonBiological ? currentRelation : relation,
3711
+ level: level - 1,
3712
+ levelUp,
3713
+ levelDown: levelDown + 1,
3714
+ degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
3555
3715
  ...additional
3556
3716
  };
3557
3717
  queue.push({
3558
3718
  ...newItem,
3559
- path: [...path2, newItem]
3719
+ path: [...path, newItem]
3560
3720
  });
3561
3721
  }
3562
3722
  });
3563
- }
3564
- indi.getChildren().forEach((relative) => {
3565
- if (!visited.has(relative)) {
3566
- const currentRelation = relative.getParentType(indi);
3567
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
3568
- additional.breakAfterNext = true;
3569
- }
3570
- const newItem = {
3571
- indi: relative,
3572
- kinship: "child",
3573
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
3574
- level: level - 1,
3575
- levelUp,
3576
- levelDown: levelDown + 1,
3577
- degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
3578
- ...additional
3579
- };
3580
- queue.push({
3581
- ...newItem,
3582
- path: [...path2, newItem]
3583
- });
3723
+ if (biologicalOnly) {
3724
+ continue;
3584
3725
  }
3585
- });
3586
- indi.getCoParents().forEach((relative) => {
3587
- if (!visited.has(relative)) {
3588
- const currentAddition = {};
3589
- if (relation && relation !== "biological" /* BIOLOGICAL */) {
3590
- currentAddition.relation = relation;
3591
- }
3592
- if (inLaw) {
3593
- currentAddition.breakOnNext = true;
3726
+ indi.getCoParents().forEach((relative) => {
3727
+ if (!visited.has(relative)) {
3728
+ const currentAddition = {};
3729
+ if (relation && relation !== "biological" /* BIOLOGICAL */) {
3730
+ currentAddition.relation = relation;
3731
+ }
3732
+ if (inLaw) {
3733
+ currentAddition.breakOnNext = true;
3734
+ }
3735
+ const newItem = {
3736
+ indi: relative,
3737
+ kinship: "spouse",
3738
+ level,
3739
+ levelUp,
3740
+ levelDown,
3741
+ degree,
3742
+ ...additional,
3743
+ ...currentAddition
3744
+ };
3745
+ queue.push({
3746
+ ...newItem,
3747
+ path: [...path, newItem]
3748
+ });
3594
3749
  }
3595
- const newItem = {
3596
- indi: relative,
3597
- kinship: "spouse",
3598
- level,
3599
- levelUp,
3600
- levelDown,
3601
- degree,
3602
- ...additional,
3603
- ...currentAddition
3604
- };
3605
- queue.push({
3606
- ...newItem,
3607
- path: [...path2, newItem]
3608
- });
3609
- }
3610
- });
3750
+ });
3751
+ }
3752
+ return void 0;
3753
+ };
3754
+ const foundPath = bfs(false);
3755
+ const lastItem = foundPath?.[foundPath.length - 1];
3756
+ if (lastItem?.relation && lastItem.relation !== "biological" /* BIOLOGICAL */) {
3757
+ const biologicalPath = bfs(true);
3758
+ if (biologicalPath) {
3759
+ cacheDiscoveredPath(this._gedcom, biologicalPath);
3760
+ return biologicalPath;
3761
+ }
3611
3762
  }
3612
- return void 0;
3763
+ if (foundPath) {
3764
+ cacheDiscoveredPath(this._gedcom, foundPath);
3765
+ }
3766
+ return foundPath;
3613
3767
  }
3614
3768
  kinship(other, showMainPerson, lang = "en", entirePath, displayName = "givenname") {
3615
3769
  const KinshipTranslatorClass2 = getKinshipTranslatorClass();
@@ -4581,6 +4735,44 @@ var KinshipTranslatorBasic = class {
4581
4735
  }
4582
4736
  return this.pathN?.indi;
4583
4737
  }
4738
+ /**
4739
+ * True when the path goes through a common ancestor whose two children
4740
+ * (the person we came from and the person we go to) share fewer than two
4741
+ * biological parents — i.e. they are half-siblings, so every collateral
4742
+ * relative on this path is a half-aunt/half-cousin/... too.
4743
+ *
4744
+ * Direct ancestors/descendants and paths that already use a
4745
+ * step/adopted/foster edge are never half-blood.
4746
+ */
4747
+ get isHalfBlood() {
4748
+ const path = this.path;
4749
+ if (!path || path.length < 3) {
4750
+ return false;
4751
+ }
4752
+ if (path.some(
4753
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
4754
+ )) {
4755
+ return false;
4756
+ }
4757
+ let peakIndex = -1;
4758
+ for (let i = 1; i < path.length; i++) {
4759
+ if (path[i].kinship === "parent") {
4760
+ peakIndex = i;
4761
+ }
4762
+ }
4763
+ if (peakIndex <= 0 || peakIndex >= path.length - 1) {
4764
+ return false;
4765
+ }
4766
+ if (path[peakIndex + 1].kinship !== "child") {
4767
+ return false;
4768
+ }
4769
+ const siblingA = path[peakIndex - 1].indi;
4770
+ const siblingB = path[peakIndex + 1].indi;
4771
+ const parentsA = siblingA.getBiologicalParents();
4772
+ const parentsB = siblingB.getBiologicalParents();
4773
+ const inter = parentsA.intersection(parentsB);
4774
+ return inter.length === 1 && (parentsA.length > 1 || parentsB.length > 1);
4775
+ }
4584
4776
  indirect() {
4585
4777
  return "";
4586
4778
  }
@@ -4608,6 +4800,9 @@ var KinshipTranslatorBasic = class {
4608
4800
  relationType(relation) {
4609
4801
  return relation ?? "";
4610
4802
  }
4803
+ halfBlood(relation) {
4804
+ return relation ?? "";
4805
+ }
4611
4806
  of(relation) {
4612
4807
  return relation ?? "";
4613
4808
  }
@@ -4846,14 +5041,34 @@ var KinshipTranslatorHU = class extends KinshipTranslatorBasic {
4846
5041
  return `${prefix.replace(/nagy$/, "")}unoka`;
4847
5042
  }
4848
5043
  sibling() {
4849
- const parents1 = this.person1?.getBiologicalParents();
4850
- const parentsN = this.personN?.getBiologicalParents();
4851
- const inter = parents1?.intersection(parentsN);
4852
- if (!inter || inter.length < 2) {
4853
- return "f\xE9ltestv\xE9r";
4854
- }
4855
5044
  return "testv\xE9r";
4856
5045
  }
5046
+ halfBlood(relation) {
5047
+ if (!relation || !this.isHalfBlood) {
5048
+ return relation ?? "";
5049
+ }
5050
+ const [head, ...rest] = relation.split(" ");
5051
+ if (!head) {
5052
+ return relation;
5053
+ }
5054
+ const prefixed = this.prefixHalf(head);
5055
+ return [prefixed, ...rest].join(" ");
5056
+ }
5057
+ /**
5058
+ * AkH. 139: compounds of more than two members stay closed up to 6
5059
+ * syllables; longer ones are hyphenated at the main boundary. A head
5060
+ * that is already hyphenated also takes a hyphenated half- prefix.
5061
+ */
5062
+ prefixHalf(head) {
5063
+ if (head.includes("-") || !/[aáeéiíoóöőuúüű]/i.test(head)) {
5064
+ return `f\xE9l-${head}`;
5065
+ }
5066
+ const syllables = (head.match(/[aáeéiíoóöőuúüű]/gi) ?? []).length + 1;
5067
+ if (syllables > 6) {
5068
+ return `f\xE9l-${head}`;
5069
+ }
5070
+ return `f\xE9l${head}`;
5071
+ }
4857
5072
  spouse() {
4858
5073
  if (this.personN?.isMale()) {
4859
5074
  return "f\xE9rj";
@@ -5052,20 +5267,22 @@ var KinshipTranslatorEn = class extends KinshipTranslatorBasic {
5052
5267
  return `${prefix}child`;
5053
5268
  }
5054
5269
  sibling() {
5055
- const parents1 = this.person1?.getBiologicalParents();
5056
- const parentsN = this.personN?.getBiologicalParents();
5057
- const inter = parents1?.intersection(parentsN);
5058
- let prefix = "";
5059
- if (!inter || inter.length < 2) {
5060
- prefix = "half-";
5061
- }
5062
5270
  if (this.personN?.isMale()) {
5063
- return `${prefix}brother`;
5271
+ return "brother";
5064
5272
  }
5065
5273
  if (this.personN?.isFemale()) {
5066
- return `${prefix}sister`;
5274
+ return "sister";
5275
+ }
5276
+ return "sibling";
5277
+ }
5278
+ halfBlood(relation) {
5279
+ if (!relation || !this.isHalfBlood) {
5280
+ return relation ?? "";
5281
+ }
5282
+ if (relation.includes(" ")) {
5283
+ return `half ${relation}`;
5067
5284
  }
5068
- return `${prefix}sibling`;
5285
+ return `half-${relation}`;
5069
5286
  }
5070
5287
  spouse() {
5071
5288
  if (this.personN?.isMale()) {
@@ -5247,20 +5464,22 @@ var KinshipTranslatorDe = class extends KinshipTranslatorBasic {
5247
5464
  return `${prefix}kind`;
5248
5465
  }
5249
5466
  sibling() {
5250
- const parents1 = this.person1?.getBiologicalParents();
5251
- const parentsN = this.personN?.getBiologicalParents();
5252
- const inter = parents1?.intersection(parentsN);
5253
- let prefix = "";
5254
- if (!inter || inter.length < 2) {
5255
- prefix = "Halb";
5256
- }
5257
5467
  if (this.personN?.isMale()) {
5258
- return `${prefix}bruder`;
5468
+ return "bruder";
5259
5469
  }
5260
5470
  if (this.personN?.isFemale()) {
5261
- return `${prefix}schwester`;
5471
+ return "schwester";
5472
+ }
5473
+ return "geschwister";
5474
+ }
5475
+ halfBlood(relation) {
5476
+ if (!relation || !this.isHalfBlood) {
5477
+ return relation ?? "";
5478
+ }
5479
+ if (/^[A-ZÄÖÜ]/.test(relation) || /[-\s]/.test(relation)) {
5480
+ return `Halb-${relation}`;
5262
5481
  }
5263
- return `${prefix}geschwister`;
5482
+ return `Halb${relation}`;
5264
5483
  }
5265
5484
  spouse() {
5266
5485
  if (this.personN?.isMale()) {
@@ -5442,20 +5661,19 @@ var KinshipTranslatorFr = class extends KinshipTranslatorBasic {
5442
5661
  return `${prefix}enfant`;
5443
5662
  }
5444
5663
  sibling() {
5445
- const parents1 = this.person1?.getBiologicalParents();
5446
- const parentsN = this.personN?.getBiologicalParents();
5447
- const inter = parents1?.intersection(parentsN);
5448
- let prefix = "";
5449
- if (!inter || inter.length < 2) {
5450
- prefix = "demi-";
5451
- }
5452
5664
  if (this.personN?.isMale()) {
5453
- return `${prefix}fr\xE8re`;
5665
+ return "fr\xE8re";
5454
5666
  }
5455
5667
  if (this.personN?.isFemale()) {
5456
- return `${prefix}s\u0153ur`;
5668
+ return "s\u0153ur";
5669
+ }
5670
+ return "fr\xE8re/s\u0153ur";
5671
+ }
5672
+ halfBlood(relation) {
5673
+ if (!relation || !this.isHalfBlood) {
5674
+ return relation ?? "";
5457
5675
  }
5458
- return `${prefix}fr\xE8re/s\u0153ur`;
5676
+ return `demi-${relation}`;
5459
5677
  }
5460
5678
  spouse() {
5461
5679
  if (this.personN?.isMale()) {
@@ -5552,12 +5770,12 @@ var InLawsEs = {
5552
5770
  "madre del (c\xF3nyuge|esposa|esposo)": "suegra",
5553
5771
  "padre del (c\xF3nyuge|esposa|esposo)": "suegro",
5554
5772
  "padre/madre del (c\xF3nyuge|esposa|esposo)": "suegro/suegra",
5555
- "esposa de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
5556
- "esposo de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
5557
- "c\xF3nyuge de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
5558
- "(medio )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
5559
- "(medio )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
5560
- "(medio )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
5773
+ "esposa de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
5774
+ "esposo de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
5775
+ "c\xF3nyuge de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
5776
+ "(medio |media |medio/a )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
5777
+ "(medio |media |medio/a )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
5778
+ "(medio |media |medio/a )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
5561
5779
  };
5562
5780
 
5563
5781
  // src/kinship-translator/kinship-translator.es.ts
@@ -5637,20 +5855,25 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
5637
5855
  return `${prefix}hijo/hija`;
5638
5856
  }
5639
5857
  sibling() {
5640
- const parents1 = this.person1?.getBiologicalParents();
5641
- const parentsN = this.personN?.getBiologicalParents();
5642
- const inter = parents1?.intersection(parentsN);
5643
- let prefix = "";
5644
- if (!inter || inter.length < 2) {
5645
- prefix = "medio ";
5646
- }
5647
5858
  if (this.personN?.isMale()) {
5648
- return `${prefix}hermano`;
5859
+ return "hermano";
5860
+ }
5861
+ if (this.personN?.isFemale()) {
5862
+ return "hermana";
5863
+ }
5864
+ return "hermano/hermana";
5865
+ }
5866
+ halfBlood(relation) {
5867
+ if (!relation || !this.isHalfBlood) {
5868
+ return relation ?? "";
5649
5869
  }
5650
5870
  if (this.personN?.isFemale()) {
5651
- return `${prefix}hermana`;
5871
+ return `media ${relation}`;
5652
5872
  }
5653
- return `${prefix}hermano/hermana`;
5873
+ if (this.personN?.isMale()) {
5874
+ return `medio ${relation}`;
5875
+ }
5876
+ return `medio/a ${relation}`;
5654
5877
  }
5655
5878
  spouse() {
5656
5879
  if (this.personN?.isMale()) {
@@ -5832,6 +6055,7 @@ var KinshipTranslator = class {
5832
6055
  } else if (personN.level !== 0) {
5833
6056
  relation = this.removal();
5834
6057
  }
6058
+ relation = this.translator?.halfBlood(relation);
5835
6059
  relation = this.translator?.relationType(relation);
5836
6060
  if (this.translator?.isOfSpouse) {
5837
6061
  relation = this.translator?.ofSpouse(relation);
@@ -5900,6 +6124,20 @@ var LONG_NOTES = {
5900
6124
  "Bef.": "Before",
5901
6125
  "Aft.": "After"
5902
6126
  };
6127
+ var STANDARD_QUALIFIERS = {
6128
+ ABT: "Abt.",
6129
+ ABOUT: "Abt.",
6130
+ BEF: "Bef.",
6131
+ BEFORE: "Bef.",
6132
+ AFT: "Aft.",
6133
+ AFTER: "Aft."
6134
+ };
6135
+ var EXPORT_QUALIFIERS = {
6136
+ "Abt.": "ABT",
6137
+ "Bef.": "BEF",
6138
+ "Aft.": "AFT"
6139
+ };
6140
+ var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
5903
6141
  var NOTE_MARKER = "####";
5904
6142
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
5905
6143
  const hasNote = baseFormat.includes("NOTE");
@@ -5933,6 +6171,17 @@ var CommonDate = class extends Common {
5933
6171
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
5934
6172
  this.NOTE.value = noteMatch?.note;
5935
6173
  validValue = value.replace(noteRegExp, "");
6174
+ } else {
6175
+ const qualifierMatch = value.match(
6176
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
6177
+ );
6178
+ const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
6179
+ const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
6180
+ if (note) {
6181
+ this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
6182
+ this.NOTE.value = note;
6183
+ validValue = value.slice(qualifierMatch[0].length);
6184
+ }
5936
6185
  }
5937
6186
  const acceptedDate = this.isValidDateFormat(validValue);
5938
6187
  if (acceptedDate) {
@@ -6038,7 +6287,23 @@ var CommonDate = class extends Common {
6038
6287
  );
6039
6288
  }
6040
6289
  exportValue() {
6041
- return this.toValue("NOTE dd MMM yyyy", null);
6290
+ const formatted = this.toValue("NOTE dd MMM yyyy", null);
6291
+ if (!formatted) {
6292
+ return formatted;
6293
+ }
6294
+ const note = this.NOTE?.value?.trim();
6295
+ const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
6296
+ const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
6297
+ return standardized.replace(
6298
+ GEDCOM_MONTHS,
6299
+ (month) => month.toUpperCase()
6300
+ );
6301
+ }
6302
+ toGedcomLines(tag, level = 0, options) {
6303
+ const internal = new RegExp(
6304
+ `^${level} (DAY|MONTH|YEAR)( |$)|^${level} NOTE (Abt\\.|Bef\\.|Aft\\.)$`
6305
+ );
6306
+ return super.toGedcomLines(tag, level, options).filter((line) => !internal.test(line));
6042
6307
  }
6043
6308
  inRange(range, trueIfNoYear = false) {
6044
6309
  return inRange(this.YEAR?.value, range, trueIfNoYear);