@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
package/dist/cli/index.js CHANGED
@@ -1258,6 +1258,135 @@ var profilePictureCache = (gedcom, key, value) => {
1258
1258
  const cached = caches.profilePictureCache?.[fullKey];
1259
1259
  return cached;
1260
1260
  };
1261
+ var isNonBiologicalPath = (path) => path.some(
1262
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
1263
+ );
1264
+ var invertHop = (kinship) => {
1265
+ if (kinship === "parent") {
1266
+ return "child";
1267
+ }
1268
+ if (kinship === "child") {
1269
+ return "parent";
1270
+ }
1271
+ return kinship;
1272
+ };
1273
+ var hopRelation = (from, toIndi, kinship, inherited) => {
1274
+ if (kinship === "parent" || kinship === "child") {
1275
+ const parent = kinship === "parent" ? toIndi : from.indi;
1276
+ const child = kinship === "parent" ? from.indi : toIndi;
1277
+ const currentRelation = child.getParentType(parent);
1278
+ if (currentRelation && currentRelation !== "biological" /* BIOLOGICAL */) {
1279
+ return currentRelation;
1280
+ }
1281
+ }
1282
+ if (inherited && inherited !== "biological" /* BIOLOGICAL */) {
1283
+ return inherited;
1284
+ }
1285
+ return void 0;
1286
+ };
1287
+ var rebuildPathFromHops = (hops, skipRelation = false) => {
1288
+ if (hops.length === 0) {
1289
+ return [];
1290
+ }
1291
+ const path = [
1292
+ {
1293
+ indi: hops[0].indi,
1294
+ kinship: "self",
1295
+ level: 0,
1296
+ levelUp: 0,
1297
+ levelDown: 0,
1298
+ degree: 0
1299
+ }
1300
+ ];
1301
+ for (let i = 1; i < hops.length; i++) {
1302
+ const prev = path[i - 1];
1303
+ const { indi, kinship } = hops[i];
1304
+ const relation = skipRelation ? void 0 : hopRelation(prev, indi, kinship, prev.relation);
1305
+ if (kinship === "parent") {
1306
+ path.push({
1307
+ indi,
1308
+ kinship,
1309
+ relation,
1310
+ level: prev.level + 1,
1311
+ levelUp: prev.levelUp + 1,
1312
+ levelDown: prev.levelDown,
1313
+ degree: prev.degree
1314
+ });
1315
+ continue;
1316
+ }
1317
+ if (kinship === "child") {
1318
+ path.push({
1319
+ indi,
1320
+ kinship,
1321
+ relation,
1322
+ level: prev.level - 1,
1323
+ levelUp: prev.levelUp,
1324
+ levelDown: prev.levelDown + 1,
1325
+ degree: prev.levelUp ? prev.level > 0 ? prev.levelUp - prev.level + 1 : prev.levelDown - Math.abs(prev.level) : 0
1326
+ });
1327
+ continue;
1328
+ }
1329
+ path.push({
1330
+ indi,
1331
+ kinship,
1332
+ relation,
1333
+ level: prev.level,
1334
+ levelUp: prev.levelUp,
1335
+ levelDown: prev.levelDown,
1336
+ degree: prev.degree
1337
+ });
1338
+ }
1339
+ return path;
1340
+ };
1341
+ var storePathIfBetter = (gedcom, fromId, toId, path) => {
1342
+ const key = `${fromId}|${toId}`;
1343
+ const existing = pathCache(gedcom, key);
1344
+ if (existing && (!isNonBiologicalPath(existing) || isNonBiologicalPath(path))) {
1345
+ return;
1346
+ }
1347
+ pathCache(gedcom, key, path);
1348
+ };
1349
+ var cacheDiscoveredPath = (gedcom, path) => {
1350
+ const n = path.length;
1351
+ if (n < 2) {
1352
+ return;
1353
+ }
1354
+ for (let i = 0; i < n; i++) {
1355
+ const fromId = path[i].indi.id;
1356
+ if (!fromId) {
1357
+ continue;
1358
+ }
1359
+ for (let j = i + 1; j < n; j++) {
1360
+ const toId = path[j].indi.id;
1361
+ if (!toId) {
1362
+ continue;
1363
+ }
1364
+ const isOriginalPair = i === 0 && j === n - 1;
1365
+ const isDirectHop = j === i + 1;
1366
+ const slice = path.slice(i, j + 1);
1367
+ const sliceMixed = isNonBiologicalPath(slice);
1368
+ if (!isOriginalPair && !isDirectHop && sliceMixed) {
1369
+ continue;
1370
+ }
1371
+ const forward = i === 0 ? slice : rebuildPathFromHops(slice, !sliceMixed);
1372
+ const reverseHops = [
1373
+ {
1374
+ indi: slice[slice.length - 1].indi,
1375
+ kinship: "self"
1376
+ }
1377
+ ];
1378
+ for (let k = slice.length - 2; k >= 0; k--) {
1379
+ reverseHops.push({
1380
+ indi: slice[k].indi,
1381
+ kinship: invertHop(slice[k + 1].kinship)
1382
+ });
1383
+ }
1384
+ const reverse = rebuildPathFromHops(reverseHops, !sliceMixed);
1385
+ storePathIfBetter(gedcom, fromId, toId, forward);
1386
+ storePathIfBetter(gedcom, toId, fromId, reverse);
1387
+ }
1388
+ }
1389
+ };
1261
1390
 
1262
1391
  // src/utils/get-all-prop.ts
1263
1392
  var getAllProp = (indi, prop) => {
@@ -2884,7 +3013,6 @@ var Indi = class extends Common {
2884
3013
  if (cache) {
2885
3014
  return cache;
2886
3015
  }
2887
- const visited = new Individuals().append(this);
2888
3016
  const mainItem = {
2889
3017
  indi: this,
2890
3018
  level: 0,
@@ -2893,129 +3021,155 @@ var Indi = class extends Common {
2893
3021
  degree: 0,
2894
3022
  kinship: "self"
2895
3023
  };
2896
- const path = [mainItem];
3024
+ const mainPath = [mainItem];
2897
3025
  if (this.id === usedIndi.id) {
2898
- return path;
3026
+ return mainPath;
2899
3027
  }
2900
- const queue = [
2901
- {
2902
- ...mainItem,
2903
- path
2904
- }
2905
- ];
2906
- let helper = 0;
2907
- while (queue.length > 0) {
2908
- if (helper++ > 1e6) {
2909
- break;
2910
- }
2911
- const {
2912
- indi,
2913
- path: path2,
2914
- kinship,
2915
- relation,
2916
- level,
2917
- levelUp,
2918
- levelDown,
2919
- degree,
2920
- breakOnNext,
2921
- breakAfterNext,
2922
- inLaw
2923
- } = queue.shift();
2924
- if (usedIndi.id === indi.id) {
2925
- if (breakOnNext) {
2926
- return void 0;
3028
+ const bfs = (biologicalOnly) => {
3029
+ const visited = new Individuals().append(this);
3030
+ const queue = [
3031
+ {
3032
+ ...mainItem,
3033
+ path: mainPath
2927
3034
  }
2928
- pathCache(this._gedcom, cacheKey, path2);
2929
- return path2;
2930
- }
2931
- visited.append(indi);
2932
- const additional = {};
2933
- if (breakOnNext || breakAfterNext) {
2934
- additional.breakOnNext = breakOnNext || breakAfterNext;
2935
- }
2936
- if (inLaw) {
2937
- additional.inLaw = inLaw;
2938
- }
2939
- if (kinship === "spouse" && breakAfterSpouse) {
2940
- if (path2.length <= 2) {
2941
- additional.inLaw = true;
2942
- } else {
2943
- additional.breakOnNext = true;
3035
+ ];
3036
+ let helper = 0;
3037
+ while (queue.length > 0) {
3038
+ if (helper++ > 1e6) {
3039
+ break;
2944
3040
  }
2945
- }
2946
- if (kinship !== "child" || !breakAfterSpouse) {
2947
- indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3041
+ const {
3042
+ indi,
3043
+ path,
3044
+ kinship,
3045
+ relation,
3046
+ level,
3047
+ levelUp,
3048
+ levelDown,
3049
+ degree,
3050
+ breakOnNext,
3051
+ breakAfterNext,
3052
+ inLaw
3053
+ } = queue.shift();
3054
+ if (usedIndi.id === indi.id) {
3055
+ if (breakOnNext) {
3056
+ return void 0;
3057
+ }
3058
+ return path;
3059
+ }
3060
+ visited.append(indi);
3061
+ const additional = {};
3062
+ if (breakOnNext || breakAfterNext) {
3063
+ additional.breakOnNext = breakOnNext || breakAfterNext;
3064
+ }
3065
+ if (inLaw) {
3066
+ additional.inLaw = inLaw;
3067
+ }
3068
+ if (!biologicalOnly && kinship === "spouse" && breakAfterSpouse) {
3069
+ if (path.length <= 2) {
3070
+ additional.inLaw = true;
3071
+ } else {
3072
+ additional.breakOnNext = true;
3073
+ }
3074
+ }
3075
+ if (kinship !== "child" || !breakAfterSpouse) {
3076
+ indi.getBiologicalFathers().copy().merge(indi.getBiologicalMothers()).merge(indi.getFathers().copy().merge(indi.getMothers())).forEach((relative) => {
3077
+ if (!visited.has(relative)) {
3078
+ const currentRelation = indi.getParentType(relative);
3079
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3080
+ if (biologicalOnly && isNonBiological) {
3081
+ return;
3082
+ }
3083
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
3084
+ additional.breakAfterNext = true;
3085
+ }
3086
+ const newItem = {
3087
+ indi: relative,
3088
+ kinship: "parent",
3089
+ relation: isNonBiological ? currentRelation : relation,
3090
+ level: level + 1,
3091
+ levelUp: levelUp + 1,
3092
+ levelDown,
3093
+ degree,
3094
+ ...additional
3095
+ };
3096
+ queue.push({
3097
+ ...newItem,
3098
+ path: [...path, newItem]
3099
+ });
3100
+ }
3101
+ });
3102
+ }
3103
+ indi.getChildren().forEach((relative) => {
2948
3104
  if (!visited.has(relative)) {
2949
- const currentRelation = indi.getParentType(relative);
2950
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
3105
+ const currentRelation = relative.getParentType(indi);
3106
+ const isNonBiological = !!currentRelation && currentRelation !== "biological" /* BIOLOGICAL */;
3107
+ if (biologicalOnly && isNonBiological) {
3108
+ return;
3109
+ }
3110
+ if (!biologicalOnly && breakAfterNonBiological && isNonBiological) {
2951
3111
  additional.breakAfterNext = true;
2952
3112
  }
2953
3113
  const newItem = {
2954
3114
  indi: relative,
2955
- kinship: "parent",
2956
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
2957
- level: level + 1,
2958
- levelUp: levelUp + 1,
2959
- levelDown,
2960
- degree,
3115
+ kinship: "child",
3116
+ relation: isNonBiological ? currentRelation : relation,
3117
+ level: level - 1,
3118
+ levelUp,
3119
+ levelDown: levelDown + 1,
3120
+ degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
2961
3121
  ...additional
2962
3122
  };
2963
3123
  queue.push({
2964
3124
  ...newItem,
2965
- path: [...path2, newItem]
3125
+ path: [...path, newItem]
2966
3126
  });
2967
3127
  }
2968
3128
  });
2969
- }
2970
- indi.getChildren().forEach((relative) => {
2971
- if (!visited.has(relative)) {
2972
- const currentRelation = relative.getParentType(indi);
2973
- if (breakAfterNonBiological && currentRelation !== "biological" /* BIOLOGICAL */) {
2974
- additional.breakAfterNext = true;
2975
- }
2976
- const newItem = {
2977
- indi: relative,
2978
- kinship: "child",
2979
- relation: currentRelation && currentRelation !== "biological" /* BIOLOGICAL */ ? currentRelation : relation,
2980
- level: level - 1,
2981
- levelUp,
2982
- levelDown: levelDown + 1,
2983
- degree: levelUp ? level > 0 ? levelUp - level + 1 : levelDown - Math.abs(level) : 0,
2984
- ...additional
2985
- };
2986
- queue.push({
2987
- ...newItem,
2988
- path: [...path2, newItem]
2989
- });
3129
+ if (biologicalOnly) {
3130
+ continue;
2990
3131
  }
2991
- });
2992
- indi.getCoParents().forEach((relative) => {
2993
- if (!visited.has(relative)) {
2994
- const currentAddition = {};
2995
- if (relation && relation !== "biological" /* BIOLOGICAL */) {
2996
- currentAddition.relation = relation;
2997
- }
2998
- if (inLaw) {
2999
- currentAddition.breakOnNext = true;
3132
+ indi.getCoParents().forEach((relative) => {
3133
+ if (!visited.has(relative)) {
3134
+ const currentAddition = {};
3135
+ if (relation && relation !== "biological" /* BIOLOGICAL */) {
3136
+ currentAddition.relation = relation;
3137
+ }
3138
+ if (inLaw) {
3139
+ currentAddition.breakOnNext = true;
3140
+ }
3141
+ const newItem = {
3142
+ indi: relative,
3143
+ kinship: "spouse",
3144
+ level,
3145
+ levelUp,
3146
+ levelDown,
3147
+ degree,
3148
+ ...additional,
3149
+ ...currentAddition
3150
+ };
3151
+ queue.push({
3152
+ ...newItem,
3153
+ path: [...path, newItem]
3154
+ });
3000
3155
  }
3001
- const newItem = {
3002
- indi: relative,
3003
- kinship: "spouse",
3004
- level,
3005
- levelUp,
3006
- levelDown,
3007
- degree,
3008
- ...additional,
3009
- ...currentAddition
3010
- };
3011
- queue.push({
3012
- ...newItem,
3013
- path: [...path2, newItem]
3014
- });
3015
- }
3016
- });
3156
+ });
3157
+ }
3158
+ return void 0;
3159
+ };
3160
+ const foundPath = bfs(false);
3161
+ const lastItem = foundPath?.[foundPath.length - 1];
3162
+ if (lastItem?.relation && lastItem.relation !== "biological" /* BIOLOGICAL */) {
3163
+ const biologicalPath = bfs(true);
3164
+ if (biologicalPath) {
3165
+ cacheDiscoveredPath(this._gedcom, biologicalPath);
3166
+ return biologicalPath;
3167
+ }
3017
3168
  }
3018
- return void 0;
3169
+ if (foundPath) {
3170
+ cacheDiscoveredPath(this._gedcom, foundPath);
3171
+ }
3172
+ return foundPath;
3019
3173
  }
3020
3174
  kinship(other, showMainPerson, lang = "en", entirePath, displayName = "givenname") {
3021
3175
  const KinshipTranslatorClass2 = getKinshipTranslatorClass();
@@ -3987,6 +4141,44 @@ var KinshipTranslatorBasic = class {
3987
4141
  }
3988
4142
  return this.pathN?.indi;
3989
4143
  }
4144
+ /**
4145
+ * True when the path goes through a common ancestor whose two children
4146
+ * (the person we came from and the person we go to) share fewer than two
4147
+ * biological parents — i.e. they are half-siblings, so every collateral
4148
+ * relative on this path is a half-aunt/half-cousin/... too.
4149
+ *
4150
+ * Direct ancestors/descendants and paths that already use a
4151
+ * step/adopted/foster edge are never half-blood.
4152
+ */
4153
+ get isHalfBlood() {
4154
+ const path = this.path;
4155
+ if (!path || path.length < 3) {
4156
+ return false;
4157
+ }
4158
+ if (path.some(
4159
+ (item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
4160
+ )) {
4161
+ return false;
4162
+ }
4163
+ let peakIndex = -1;
4164
+ for (let i = 1; i < path.length; i++) {
4165
+ if (path[i].kinship === "parent") {
4166
+ peakIndex = i;
4167
+ }
4168
+ }
4169
+ if (peakIndex <= 0 || peakIndex >= path.length - 1) {
4170
+ return false;
4171
+ }
4172
+ if (path[peakIndex + 1].kinship !== "child") {
4173
+ return false;
4174
+ }
4175
+ const siblingA = path[peakIndex - 1].indi;
4176
+ const siblingB = path[peakIndex + 1].indi;
4177
+ const parentsA = siblingA.getBiologicalParents();
4178
+ const parentsB = siblingB.getBiologicalParents();
4179
+ const inter = parentsA.intersection(parentsB);
4180
+ return inter.length === 1 && (parentsA.length > 1 || parentsB.length > 1);
4181
+ }
3990
4182
  indirect() {
3991
4183
  return "";
3992
4184
  }
@@ -4014,6 +4206,9 @@ var KinshipTranslatorBasic = class {
4014
4206
  relationType(relation) {
4015
4207
  return relation ?? "";
4016
4208
  }
4209
+ halfBlood(relation) {
4210
+ return relation ?? "";
4211
+ }
4017
4212
  of(relation) {
4018
4213
  return relation ?? "";
4019
4214
  }
@@ -4252,14 +4447,34 @@ var KinshipTranslatorHU = class extends KinshipTranslatorBasic {
4252
4447
  return `${prefix.replace(/nagy$/, "")}unoka`;
4253
4448
  }
4254
4449
  sibling() {
4255
- const parents1 = this.person1?.getBiologicalParents();
4256
- const parentsN = this.personN?.getBiologicalParents();
4257
- const inter = parents1?.intersection(parentsN);
4258
- if (!inter || inter.length < 2) {
4259
- return "f\xE9ltestv\xE9r";
4260
- }
4261
4450
  return "testv\xE9r";
4262
4451
  }
4452
+ halfBlood(relation) {
4453
+ if (!relation || !this.isHalfBlood) {
4454
+ return relation ?? "";
4455
+ }
4456
+ const [head, ...rest] = relation.split(" ");
4457
+ if (!head) {
4458
+ return relation;
4459
+ }
4460
+ const prefixed = this.prefixHalf(head);
4461
+ return [prefixed, ...rest].join(" ");
4462
+ }
4463
+ /**
4464
+ * AkH. 139: compounds of more than two members stay closed up to 6
4465
+ * syllables; longer ones are hyphenated at the main boundary. A head
4466
+ * that is already hyphenated also takes a hyphenated half- prefix.
4467
+ */
4468
+ prefixHalf(head) {
4469
+ if (head.includes("-") || !/[aáeéiíoóöőuúüű]/i.test(head)) {
4470
+ return `f\xE9l-${head}`;
4471
+ }
4472
+ const syllables = (head.match(/[aáeéiíoóöőuúüű]/gi) ?? []).length + 1;
4473
+ if (syllables > 6) {
4474
+ return `f\xE9l-${head}`;
4475
+ }
4476
+ return `f\xE9l${head}`;
4477
+ }
4263
4478
  spouse() {
4264
4479
  if (this.personN?.isMale()) {
4265
4480
  return "f\xE9rj";
@@ -4458,20 +4673,22 @@ var KinshipTranslatorEn = class extends KinshipTranslatorBasic {
4458
4673
  return `${prefix}child`;
4459
4674
  }
4460
4675
  sibling() {
4461
- const parents1 = this.person1?.getBiologicalParents();
4462
- const parentsN = this.personN?.getBiologicalParents();
4463
- const inter = parents1?.intersection(parentsN);
4464
- let prefix = "";
4465
- if (!inter || inter.length < 2) {
4466
- prefix = "half-";
4467
- }
4468
4676
  if (this.personN?.isMale()) {
4469
- return `${prefix}brother`;
4677
+ return "brother";
4470
4678
  }
4471
4679
  if (this.personN?.isFemale()) {
4472
- return `${prefix}sister`;
4680
+ return "sister";
4681
+ }
4682
+ return "sibling";
4683
+ }
4684
+ halfBlood(relation) {
4685
+ if (!relation || !this.isHalfBlood) {
4686
+ return relation ?? "";
4687
+ }
4688
+ if (relation.includes(" ")) {
4689
+ return `half ${relation}`;
4473
4690
  }
4474
- return `${prefix}sibling`;
4691
+ return `half-${relation}`;
4475
4692
  }
4476
4693
  spouse() {
4477
4694
  if (this.personN?.isMale()) {
@@ -4653,20 +4870,22 @@ var KinshipTranslatorDe = class extends KinshipTranslatorBasic {
4653
4870
  return `${prefix}kind`;
4654
4871
  }
4655
4872
  sibling() {
4656
- const parents1 = this.person1?.getBiologicalParents();
4657
- const parentsN = this.personN?.getBiologicalParents();
4658
- const inter = parents1?.intersection(parentsN);
4659
- let prefix = "";
4660
- if (!inter || inter.length < 2) {
4661
- prefix = "Halb";
4662
- }
4663
4873
  if (this.personN?.isMale()) {
4664
- return `${prefix}bruder`;
4874
+ return "bruder";
4665
4875
  }
4666
4876
  if (this.personN?.isFemale()) {
4667
- return `${prefix}schwester`;
4877
+ return "schwester";
4878
+ }
4879
+ return "geschwister";
4880
+ }
4881
+ halfBlood(relation) {
4882
+ if (!relation || !this.isHalfBlood) {
4883
+ return relation ?? "";
4884
+ }
4885
+ if (/^[A-ZÄÖÜ]/.test(relation) || /[-\s]/.test(relation)) {
4886
+ return `Halb-${relation}`;
4668
4887
  }
4669
- return `${prefix}geschwister`;
4888
+ return `Halb${relation}`;
4670
4889
  }
4671
4890
  spouse() {
4672
4891
  if (this.personN?.isMale()) {
@@ -4848,20 +5067,19 @@ var KinshipTranslatorFr = class extends KinshipTranslatorBasic {
4848
5067
  return `${prefix}enfant`;
4849
5068
  }
4850
5069
  sibling() {
4851
- const parents1 = this.person1?.getBiologicalParents();
4852
- const parentsN = this.personN?.getBiologicalParents();
4853
- const inter = parents1?.intersection(parentsN);
4854
- let prefix = "";
4855
- if (!inter || inter.length < 2) {
4856
- prefix = "demi-";
4857
- }
4858
5070
  if (this.personN?.isMale()) {
4859
- return `${prefix}fr\xE8re`;
5071
+ return "fr\xE8re";
4860
5072
  }
4861
5073
  if (this.personN?.isFemale()) {
4862
- return `${prefix}s\u0153ur`;
5074
+ return "s\u0153ur";
5075
+ }
5076
+ return "fr\xE8re/s\u0153ur";
5077
+ }
5078
+ halfBlood(relation) {
5079
+ if (!relation || !this.isHalfBlood) {
5080
+ return relation ?? "";
4863
5081
  }
4864
- return `${prefix}fr\xE8re/s\u0153ur`;
5082
+ return `demi-${relation}`;
4865
5083
  }
4866
5084
  spouse() {
4867
5085
  if (this.personN?.isMale()) {
@@ -4958,12 +5176,12 @@ var InLawsEs = {
4958
5176
  "madre del (c\xF3nyuge|esposa|esposo)": "suegra",
4959
5177
  "padre del (c\xF3nyuge|esposa|esposo)": "suegro",
4960
5178
  "padre/madre del (c\xF3nyuge|esposa|esposo)": "suegro/suegra",
4961
- "esposa de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
4962
- "esposo de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
4963
- "c\xF3nyuge de (medio )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
4964
- "(medio )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
4965
- "(medio )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
4966
- "(medio )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
5179
+ "esposa de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ada",
5180
+ "esposo de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado",
5181
+ "c\xF3nyuge de (medio |media |medio/a )?(hermano/hermana|hermana|hermano)": "cu\xF1ado/cu\xF1ada",
5182
+ "(medio |media |medio/a )?hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ada",
5183
+ "(medio |media |medio/a )?hermano del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado",
5184
+ "(medio |media |medio/a )?hermano/hermana del (c\xF3nyuge|esposa|esposo)": "cu\xF1ado/cu\xF1ada"
4967
5185
  };
4968
5186
 
4969
5187
  // src/kinship-translator/kinship-translator.es.ts
@@ -5043,20 +5261,25 @@ var KinshipTranslatorEs = class extends KinshipTranslatorBasic {
5043
5261
  return `${prefix}hijo/hija`;
5044
5262
  }
5045
5263
  sibling() {
5046
- const parents1 = this.person1?.getBiologicalParents();
5047
- const parentsN = this.personN?.getBiologicalParents();
5048
- const inter = parents1?.intersection(parentsN);
5049
- let prefix = "";
5050
- if (!inter || inter.length < 2) {
5051
- prefix = "medio ";
5052
- }
5053
5264
  if (this.personN?.isMale()) {
5054
- return `${prefix}hermano`;
5265
+ return "hermano";
5266
+ }
5267
+ if (this.personN?.isFemale()) {
5268
+ return "hermana";
5269
+ }
5270
+ return "hermano/hermana";
5271
+ }
5272
+ halfBlood(relation) {
5273
+ if (!relation || !this.isHalfBlood) {
5274
+ return relation ?? "";
5055
5275
  }
5056
5276
  if (this.personN?.isFemale()) {
5057
- return `${prefix}hermana`;
5277
+ return `media ${relation}`;
5058
5278
  }
5059
- return `${prefix}hermano/hermana`;
5279
+ if (this.personN?.isMale()) {
5280
+ return `medio ${relation}`;
5281
+ }
5282
+ return `medio/a ${relation}`;
5060
5283
  }
5061
5284
  spouse() {
5062
5285
  if (this.personN?.isMale()) {
@@ -5238,6 +5461,7 @@ var KinshipTranslator = class {
5238
5461
  } else if (personN.level !== 0) {
5239
5462
  relation = this.removal();
5240
5463
  }
5464
+ relation = this.translator?.halfBlood(relation);
5241
5465
  relation = this.translator?.relationType(relation);
5242
5466
  if (this.translator?.isOfSpouse) {
5243
5467
  relation = this.translator?.ofSpouse(relation);
@@ -5306,6 +5530,20 @@ var LONG_NOTES = {
5306
5530
  "Bef.": "Before",
5307
5531
  "Aft.": "After"
5308
5532
  };
5533
+ var STANDARD_QUALIFIERS = {
5534
+ ABT: "Abt.",
5535
+ ABOUT: "Abt.",
5536
+ BEF: "Bef.",
5537
+ BEFORE: "Bef.",
5538
+ AFT: "Aft.",
5539
+ AFTER: "Aft."
5540
+ };
5541
+ var EXPORT_QUALIFIERS = {
5542
+ "Abt.": "ABT",
5543
+ "Bef.": "BEF",
5544
+ "Aft.": "AFT"
5545
+ };
5546
+ var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
5309
5547
  var NOTE_MARKER = "####";
5310
5548
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
5311
5549
  const hasNote = baseFormat.includes("NOTE");
@@ -5339,6 +5577,17 @@ var CommonDate = class extends Common {
5339
5577
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
5340
5578
  this.NOTE.value = noteMatch?.note;
5341
5579
  validValue = value.replace(noteRegExp, "");
5580
+ } else {
5581
+ const qualifierMatch = value.match(
5582
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
5583
+ );
5584
+ const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
5585
+ const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
5586
+ if (note) {
5587
+ this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
5588
+ this.NOTE.value = note;
5589
+ validValue = value.slice(qualifierMatch[0].length);
5590
+ }
5342
5591
  }
5343
5592
  const acceptedDate = this.isValidDateFormat(validValue);
5344
5593
  if (acceptedDate) {
@@ -5444,7 +5693,23 @@ var CommonDate = class extends Common {
5444
5693
  );
5445
5694
  }
5446
5695
  exportValue() {
5447
- return this.toValue("NOTE dd MMM yyyy", null);
5696
+ const formatted = this.toValue("NOTE dd MMM yyyy", null);
5697
+ if (!formatted) {
5698
+ return formatted;
5699
+ }
5700
+ const note = this.NOTE?.value?.trim();
5701
+ const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
5702
+ const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
5703
+ return standardized.replace(
5704
+ GEDCOM_MONTHS,
5705
+ (month) => month.toUpperCase()
5706
+ );
5707
+ }
5708
+ toGedcomLines(tag, level = 0, options) {
5709
+ const internal = new RegExp(
5710
+ `^${level} (DAY|MONTH|YEAR)( |$)|^${level} NOTE (Abt\\.|Bef\\.|Aft\\.)$`
5711
+ );
5712
+ return super.toGedcomLines(tag, level, options).filter((line) => !internal.test(line));
5448
5713
  }
5449
5714
  inRange(range, trueIfNoYear = false) {
5450
5715
  return inRange(this.YEAR?.value, range, trueIfNoYear);
@@ -6247,7 +6512,7 @@ var Families = class _Families extends List {
6247
6512
 
6248
6513
  // package.json
6249
6514
  var package_default = {
6250
- version: "2.0.7"};
6515
+ version: "2.0.9"};
6251
6516
 
6252
6517
  // src/utils/get-product-details.ts
6253
6518
  var isDevelopment = () => {