@treeviz/gedcom-parser 2.0.12 → 2.1.0

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 (67) hide show
  1. package/README.md +236 -989
  2. package/dist/classes/common.d.ts +4 -4
  3. package/dist/classes/common.d.ts.map +1 -1
  4. package/dist/classes/date.d.ts +2 -0
  5. package/dist/classes/date.d.ts.map +1 -1
  6. package/dist/classes/fam.d.ts +2 -1
  7. package/dist/classes/fam.d.ts.map +1 -1
  8. package/dist/classes/fams.d.ts.map +1 -1
  9. package/dist/classes/gedcom.d.ts +39 -2
  10. package/dist/classes/gedcom.d.ts.map +1 -1
  11. package/dist/classes/index.js +1364 -318
  12. package/dist/classes/indi.d.ts +19 -2
  13. package/dist/classes/indi.d.ts.map +1 -1
  14. package/dist/classes/list.d.ts.map +1 -1
  15. package/dist/classes/note.d.ts.map +1 -1
  16. package/dist/classes/obje.d.ts +2 -1
  17. package/dist/classes/obje.d.ts.map +1 -1
  18. package/dist/cli/index.js +1368 -313
  19. package/dist/constants/constants.d.ts.map +1 -1
  20. package/dist/constants/index.js +312 -73
  21. package/dist/factories/index.js +310 -71
  22. package/dist/index.d.ts +10 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1547 -288
  25. package/dist/interfaces/common.d.ts +6 -4
  26. package/dist/interfaces/common.d.ts.map +1 -1
  27. package/dist/interfaces/fam.d.ts +3 -0
  28. package/dist/interfaces/fam.d.ts.map +1 -1
  29. package/dist/interfaces/gedcom.d.ts +21 -1
  30. package/dist/interfaces/gedcom.d.ts.map +1 -1
  31. package/dist/interfaces/indi.d.ts +11 -1
  32. package/dist/interfaces/indi.d.ts.map +1 -1
  33. package/dist/kinship-translator/index.js +310 -71
  34. package/dist/structures/gedcom.d.ts +1 -0
  35. package/dist/structures/gedcom.d.ts.map +1 -1
  36. package/dist/types/types.d.ts +22 -4
  37. package/dist/types/types.d.ts.map +1 -1
  38. package/dist/utils/common-creator.d.ts.map +1 -1
  39. package/dist/utils/fact-edit.d.ts +12 -0
  40. package/dist/utils/fact-edit.d.ts.map +1 -1
  41. package/dist/utils/family-edit.d.ts +2 -2
  42. package/dist/utils/family-edit.d.ts.map +1 -1
  43. package/dist/utils/gedcom-version.d.ts +6 -0
  44. package/dist/utils/gedcom-version.d.ts.map +1 -0
  45. package/dist/utils/gedcom7-dates.d.ts +20 -0
  46. package/dist/utils/gedcom7-dates.d.ts.map +1 -0
  47. package/dist/utils/gedcom7-enumerations.d.ts +26 -0
  48. package/dist/utils/gedcom7-enumerations.d.ts.map +1 -0
  49. package/dist/utils/gedcom7-metadata.d.ts +19 -0
  50. package/dist/utils/gedcom7-metadata.d.ts.map +1 -0
  51. package/dist/utils/gedcom7-schema.d.ts +27 -0
  52. package/dist/utils/gedcom7-schema.d.ts.map +1 -0
  53. package/dist/utils/gedzip.d.ts +81 -0
  54. package/dist/utils/gedzip.d.ts.map +1 -0
  55. package/dist/utils/index.d.ts +9 -0
  56. package/dist/utils/index.d.ts.map +1 -1
  57. package/dist/utils/index.js +1547 -288
  58. package/dist/utils/local-media.d.ts +30 -0
  59. package/dist/utils/local-media.d.ts.map +1 -0
  60. package/dist/utils/media-utils.d.ts +10 -6
  61. package/dist/utils/media-utils.d.ts.map +1 -1
  62. package/dist/utils/multimedia.d.ts +38 -0
  63. package/dist/utils/multimedia.d.ts.map +1 -0
  64. package/dist/utils/parser.d.ts.map +1 -1
  65. package/dist/utils/uid.d.ts +6 -0
  66. package/dist/utils/uid.d.ts.map +1 -0
  67. package/package.json +2 -1
@@ -2226,11 +2226,17 @@ var List = class _List {
2226
2226
  const gedcom = [];
2227
2227
  this.entries().forEach(([_, item]) => {
2228
2228
  const validTag = getValidTag(tag);
2229
- const validKey = item.id;
2230
- const validValue = validKey ? validTag : item.exportValue();
2231
- gedcom.push(
2232
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
2233
- );
2229
+ const xref = item.id;
2230
+ const payload = item.exportValue?.();
2231
+ if (xref) {
2232
+ gedcom.push(
2233
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
2234
+ );
2235
+ } else {
2236
+ gedcom.push(
2237
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
2238
+ );
2239
+ }
2234
2240
  gedcom.push(
2235
2241
  ...item.toGedcomLines(
2236
2242
  validTag,
@@ -2488,12 +2494,14 @@ var Common = class _Common {
2488
2494
  );
2489
2495
  }
2490
2496
  standardizeObject(tag, options) {
2491
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
2497
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
2492
2498
  const standardize = this.standardizeMedia;
2493
2499
  return standardize.call(
2494
2500
  this,
2495
2501
  options.obje.namespace,
2496
- options.obje.override
2502
+ options.obje.override,
2503
+ void 0,
2504
+ options.gedcomVersion
2497
2505
  );
2498
2506
  }
2499
2507
  }
@@ -2520,7 +2528,7 @@ var Common = class _Common {
2520
2528
  if (typeof prop?.toObject === "function") {
2521
2529
  if (prop instanceof _Common) {
2522
2530
  json[validKey] = prop.toObject(validKey, options);
2523
- } else {
2531
+ } else if (prop instanceof List) {
2524
2532
  json = {
2525
2533
  ...json,
2526
2534
  ...prop.toObject(validKey, options)
@@ -2578,8 +2586,12 @@ var Common = class _Common {
2578
2586
  this.standardizeObject(tag, options);
2579
2587
  const validKeys = getValidKeys(this);
2580
2588
  const gedcom = [];
2589
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
2581
2590
  validKeys.forEach((key) => {
2582
2591
  const validKey = key;
2592
+ if (isGedcom7Export && validKey === "CONC") {
2593
+ return;
2594
+ }
2583
2595
  const prop = this.get(validKey);
2584
2596
  if (typeof prop?.toGedcomLines === "function") {
2585
2597
  if (prop instanceof _Common) {
@@ -2590,7 +2602,7 @@ var Common = class _Common {
2590
2602
  gedcom.push(
2591
2603
  ...prop.toGedcomLines(validKey, level + 1, options)
2592
2604
  );
2593
- } else {
2605
+ } else if (prop instanceof List) {
2594
2606
  gedcom.push(
2595
2607
  ...prop.toGedcomLines(validKey, level, options)
2596
2608
  );
@@ -2931,7 +2943,11 @@ var STANDARD_QUALIFIERS = {
2931
2943
  BEF: "Bef.",
2932
2944
  BEFORE: "Bef.",
2933
2945
  AFT: "Aft.",
2934
- AFTER: "Aft."
2946
+ AFTER: "Aft.",
2947
+ CAL: "Abt.",
2948
+ // calculated ≈ about for display
2949
+ EST: "Abt."
2950
+ // estimated ≈ about for display
2935
2951
  };
2936
2952
  var EXPORT_QUALIFIERS = {
2937
2953
  "Abt.": "ABT",
@@ -2939,6 +2955,11 @@ var EXPORT_QUALIFIERS = {
2939
2955
  "Aft.": "AFT"
2940
2956
  };
2941
2957
  var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
2958
+ var CALENDAR_PREFIX = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
2959
+ var isGedcomDateValueForm = (value) => (
2960
+ // Require space/end after the token so display forms like "Abt." are not opaque.
2961
+ /^(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)(\s|$)/i.test(value) || /\bAND\b/i.test(value)
2962
+ );
2942
2963
  var NOTE_MARKER = "####";
2943
2964
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
2944
2965
  const hasNote = baseFormat.includes("NOTE");
@@ -2963,29 +2984,45 @@ var CommonDate = class extends Common {
2963
2984
  super(gedcom, id, main, parent);
2964
2985
  delete this.id;
2965
2986
  }
2987
+ get calendar() {
2988
+ return this._calendar;
2989
+ }
2966
2990
  set value(value) {
2967
2991
  if (value) {
2968
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
2969
- const noteMatch = value.match(noteRegExp)?.groups;
2970
2992
  let validValue = value;
2993
+ const calendarMatch = validValue.match(CALENDAR_PREFIX);
2994
+ if (calendarMatch?.groups?.calendar) {
2995
+ this._calendar = calendarMatch.groups.calendar.toUpperCase();
2996
+ validValue = validValue.slice(calendarMatch[0].length);
2997
+ } else {
2998
+ this._calendar = void 0;
2999
+ }
3000
+ const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
3001
+ const noteMatch = validValue.match(noteRegExp)?.groups;
2971
3002
  if (noteMatch?.note) {
2972
3003
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
2973
3004
  this.NOTE.value = noteMatch?.note;
2974
- validValue = value.replace(noteRegExp, "");
3005
+ validValue = validValue.replace(noteRegExp, "");
2975
3006
  } else {
2976
- const qualifierMatch = value.match(
2977
- /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
3007
+ const qualifierMatch = validValue.match(
3008
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER|CAL|EST)\b\s*/i
2978
3009
  );
2979
3010
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
2980
3011
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
2981
3012
  if (note) {
2982
3013
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
2983
3014
  this.NOTE.value = note;
2984
- validValue = value.slice(qualifierMatch[0].length);
3015
+ validValue = validValue.slice(qualifierMatch[0].length);
2985
3016
  }
2986
3017
  }
2987
3018
  const acceptedDate = this.isValidDateFormat(validValue);
2988
- if (acceptedDate) {
3019
+ if (isGedcomDateValueForm(validValue.trim())) {
3020
+ delete this.DAY;
3021
+ delete this.MONTH;
3022
+ delete this.YEAR;
3023
+ this._date = void 0;
3024
+ this._value = value;
3025
+ } else if (acceptedDate) {
2989
3026
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
2990
3027
  this.DAY.value = format(acceptedDate, "dd");
2991
3028
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -3088,6 +3125,15 @@ var CommonDate = class extends Common {
3088
3125
  );
3089
3126
  }
3090
3127
  exportValue() {
3128
+ if (typeof this._value === "string") {
3129
+ const withoutCalendar = this._value.replace(CALENDAR_PREFIX, "");
3130
+ if (isGedcomDateValueForm(withoutCalendar.trim())) {
3131
+ return this._value.replace(
3132
+ /^((?:GREGORIAN|JULIAN|FRENCH_R|HEBREW)\s+)?(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)\b/i,
3133
+ (_m, cal, q) => `${cal || ""}${q.toUpperCase()}`
3134
+ );
3135
+ }
3136
+ }
3091
3137
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
3092
3138
  if (!formatted) {
3093
3139
  return formatted;
@@ -3095,10 +3141,14 @@ var CommonDate = class extends Common {
3095
3141
  const note = this.NOTE?.value?.trim();
3096
3142
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
3097
3143
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
3098
- return standardized.replace(
3144
+ const withMonths = standardized.replace(
3099
3145
  GEDCOM_MONTHS,
3100
3146
  (month) => month.toUpperCase()
3101
3147
  );
3148
+ if (this._calendar && this._calendar !== "GREGORIAN") {
3149
+ return `${this._calendar} ${withMonths}`;
3150
+ }
3151
+ return withMonths;
3102
3152
  }
3103
3153
  toGedcomLines(tag, level = 0, options) {
3104
3154
  const internal = new RegExp(
@@ -3630,6 +3680,24 @@ var addFamilyFact = (fam, input) => {
3630
3680
  fillFact(gedcom, fam, event, input);
3631
3681
  return event;
3632
3682
  };
3683
+ var addNonEvent = (owner, input) => {
3684
+ const gedcom = owner.getGedcom();
3685
+ const eventType = input.event?.trim().toUpperCase();
3686
+ if (!gedcom || !eventType) {
3687
+ return void 0;
3688
+ }
3689
+ const no = createCommon(gedcom, void 0, owner);
3690
+ no.type = "NO";
3691
+ no.value = eventType;
3692
+ if (input.date?.trim()) {
3693
+ setDateOn(gedcom, owner, no, input.date);
3694
+ }
3695
+ if (input.note?.trim()) {
3696
+ no.set("NOTE", input.note.trim());
3697
+ }
3698
+ owner.assign("NO", no);
3699
+ return no;
3700
+ };
3633
3701
 
3634
3702
  // src/utils/family-edit.ts
3635
3703
  var PEDIGREE_VALUES = [
@@ -3856,16 +3924,58 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
3856
3924
  });
3857
3925
  return places;
3858
3926
  };
3927
+
3928
+ // src/utils/local-media.ts
3929
+ var localMediaResolver;
3930
+ var isRemoteOrEmbeddedMediaUrl = (path) => {
3931
+ if (!path) {
3932
+ return false;
3933
+ }
3934
+ return path.startsWith("data:") || path.startsWith("blob:") || /^https?:\/\//i.test(path);
3935
+ };
3936
+ var resolveLocalMediaUrl = async (path) => {
3937
+ if (!path) {
3938
+ return void 0;
3939
+ }
3940
+ if (isRemoteOrEmbeddedMediaUrl(path)) {
3941
+ return path;
3942
+ }
3943
+ const resolved = await localMediaResolver?.(path);
3944
+ return resolved;
3945
+ };
3859
3946
  var implemented = (type, ...args) => {
3860
3947
  };
3861
3948
 
3862
3949
  // src/utils/media-utils.ts
3863
3950
  var getFileExtension = (filename) => {
3864
- const match = filename.match(/\.([^.]+)$/);
3865
- return match ? match[1] : "";
3951
+ if (!filename) {
3952
+ return "";
3953
+ }
3954
+ if (filename.startsWith("data:")) {
3955
+ const mime = filename.slice(5).split(";")[0] || "";
3956
+ const subtype = (mime.split("/")[1] || "").toLowerCase();
3957
+ if (subtype === "jpeg") {
3958
+ return "jpg";
3959
+ }
3960
+ return subtype.split("+")[0] || "";
3961
+ }
3962
+ const withoutQuery = filename.split(/[?#]/)[0] || filename;
3963
+ const match = withoutQuery.match(/\.([^.]+)$/);
3964
+ return match ? match[1].toLowerCase() : "";
3866
3965
  };
3867
3966
  var isImageFormat = (format2) => {
3868
- if (!format2) return false;
3967
+ if (!format2) {
3968
+ return false;
3969
+ }
3970
+ let normalized = format2.toLowerCase().trim();
3971
+ if (normalized.startsWith("data:")) {
3972
+ normalized = getFileExtension(normalized);
3973
+ } else if (normalized.includes("/")) {
3974
+ normalized = normalized.split("/")[1]?.split("+")[0] || "";
3975
+ if (normalized === "jpeg") {
3976
+ normalized = "jpg";
3977
+ }
3978
+ }
3869
3979
  const imageFormats = [
3870
3980
  "jpg",
3871
3981
  "jpeg",
@@ -3877,7 +3987,11 @@ var isImageFormat = (format2) => {
3877
3987
  "tiff",
3878
3988
  "tif"
3879
3989
  ];
3880
- return imageFormats.includes(format2.toLowerCase());
3990
+ return imageFormats.includes(normalized);
3991
+ };
3992
+ var resolveObjeForm = (obje) => {
3993
+ const value = obje?.get?.("FILE.FORM")?.toValue?.() ?? obje?.get?.("FORM")?.toValue?.();
3994
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
3881
3995
  };
3882
3996
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
3883
3997
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -4447,6 +4561,9 @@ var Fam = class extends Common {
4447
4561
  addFact(input) {
4448
4562
  return addFamilyFact(this, input);
4449
4563
  }
4564
+ addNonEvent(input) {
4565
+ return addNonEvent(this, input);
4566
+ }
4450
4567
  toList() {
4451
4568
  return new Families().concat(
4452
4569
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -5165,10 +5282,14 @@ var Indi = class extends Common {
5165
5282
  const clone = obje?.get("_CLON._OID")?.toValue();
5166
5283
  const mser = obje?.get("_MSER._LKID")?.toValue();
5167
5284
  let url = obje?.get("FILE")?.toValue();
5168
- const title = obje?.get("TITL")?.toValue() ?? "";
5169
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5170
- const imgId = clone || mser;
5171
- if (!www || !tree || !this.id) {
5285
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
5286
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5287
+ let imgId = clone || mser;
5288
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
5289
+ if (!hasEmbeddedOrLocalFile && (!www || !tree || !this.id)) {
5290
+ return;
5291
+ }
5292
+ if (!this.id) {
5172
5293
  return;
5173
5294
  }
5174
5295
  if (!namespace && !url) {
@@ -5186,13 +5307,16 @@ var Indi = class extends Common {
5186
5307
  }
5187
5308
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
5188
5309
  }
5310
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
5311
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
5312
+ }
5189
5313
  if (url && imgId) {
5190
- const id = `${tree}-${this.id}-${imgId}`;
5314
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
5191
5315
  list[id] = {
5192
5316
  key,
5193
5317
  isPrimary,
5194
5318
  id,
5195
- tree,
5319
+ tree: tree || "gedzip",
5196
5320
  imgId,
5197
5321
  person: this.id,
5198
5322
  title,
@@ -5238,9 +5362,11 @@ var Indi = class extends Common {
5238
5362
  obje.standardizeMedia();
5239
5363
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5240
5364
  const url = obje?.get("FILE")?.toValue();
5241
- const title = obje?.get("NOTE")?.toValue() ?? "";
5242
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5243
- const imgId = obje?.get("_PHOTO_RIN")?.toValue();
5365
+ const title = obje?.get("FILE.NOTE")?.toValue() ?? obje?.get("NOTE")?.toValue() ?? "";
5366
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5367
+ const photoRin = obje?.get("_PHOTO_RIN")?.toValue();
5368
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
5369
+ const imgId = photoRin || (hasEmbeddedOrLocalFile && url ? `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}` : void 0);
5244
5370
  if (url && imgId) {
5245
5371
  const id = `${tree}-${this.id}-${imgId}`;
5246
5372
  list[id] = {
@@ -5363,10 +5489,10 @@ var Indi = class extends Common {
5363
5489
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5364
5490
  const url = obje?.get("FILE")?.toValue();
5365
5491
  const title = obje?.get("TITL")?.toValue() ?? "";
5366
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5492
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5367
5493
  if (url) {
5368
5494
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
5369
- const imgId = urlMatch?.[1] || `img-${index}-${Date.now().toString(36)}`;
5495
+ const imgId = urlMatch?.[1] || (url.startsWith("data:") ? `data-${index}-${getFileExtension(url) || "bin"}` : `img-${index}-${Date.now().toString(36)}`);
5370
5496
  const id = `geni-${geniId}-${imgId}`;
5371
5497
  list[id] = {
5372
5498
  isPrimary,
@@ -5403,10 +5529,10 @@ var Indi = class extends Common {
5403
5529
  obje.standardizeMedia();
5404
5530
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5405
5531
  const url = obje?.get("FILE")?.toValue();
5406
- const title = obje?.get("TITL")?.toValue() ?? "";
5407
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5532
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
5533
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5408
5534
  if (url) {
5409
- const imgId = `media-${index}-${url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
5535
+ const imgId = `media-${index}-${url.startsWith("data:") ? getFileExtension(url) || "bin" : url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
5410
5536
  const id = `${treeId}-${this.id}-${imgId}`;
5411
5537
  list[id] = {
5412
5538
  isPrimary,
@@ -5446,7 +5572,20 @@ var Indi = class extends Common {
5446
5572
  cacheKey
5447
5573
  );
5448
5574
  if (cached !== void 0) {
5449
- return cached;
5575
+ if (cached?.file && !isRemoteOrEmbeddedMediaUrl(cached.file)) {
5576
+ const resolvedCached = await resolveLocalMediaUrl(cached.file);
5577
+ if (resolvedCached && isRemoteOrEmbeddedMediaUrl(resolvedCached)) {
5578
+ const refreshed = { ...cached, file: resolvedCached };
5579
+ profilePictureCache(
5580
+ this._gedcom,
5581
+ cacheKey,
5582
+ refreshed
5583
+ );
5584
+ return refreshed;
5585
+ }
5586
+ } else {
5587
+ return cached;
5588
+ }
5450
5589
  }
5451
5590
  const mediaList = await this.multimedia(namespace);
5452
5591
  if (!mediaList) {
@@ -5458,39 +5597,40 @@ var Indi = class extends Common {
5458
5597
  return void 0;
5459
5598
  }
5460
5599
  const mediaArray = Object.values(mediaList);
5461
- const primaryMedia = mediaArray.find(
5462
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
5463
- );
5464
- if (primaryMedia) {
5465
- const result = {
5466
- file: primaryMedia.url,
5467
- form: primaryMedia.contentType,
5468
- title: primaryMedia.title,
5469
- isPrimary: true
5600
+ const toDisplayableProfile = async (media, isPrimary) => {
5601
+ const resolvedFile = await resolveLocalMediaUrl(media.url);
5602
+ if (!resolvedFile || !isRemoteOrEmbeddedMediaUrl(resolvedFile)) {
5603
+ return void 0;
5604
+ }
5605
+ return {
5606
+ file: resolvedFile,
5607
+ form: media.contentType,
5608
+ title: media.title,
5609
+ isPrimary
5470
5610
  };
5471
- profilePictureCache(this._gedcom, cacheKey, result);
5472
- return result;
5473
- }
5474
- if (onlyPrimary) {
5475
- profilePictureCache(
5476
- this._gedcom,
5477
- cacheKey,
5478
- void 0
5479
- );
5480
- return void 0;
5481
- }
5482
- const secondaryMedia = mediaArray.find(
5483
- (media) => isImageFormat(media.contentType || getFileExtension(media.url))
5611
+ };
5612
+ const candidates = mediaArray.filter(
5613
+ (media) => isImageFormat(
5614
+ media.contentType || media.url || getFileExtension(media.url)
5615
+ )
5484
5616
  );
5485
- if (secondaryMedia) {
5486
- const result = {
5487
- file: secondaryMedia.url,
5488
- form: secondaryMedia.contentType,
5489
- title: secondaryMedia.title,
5490
- isPrimary: false
5491
- };
5492
- profilePictureCache(this._gedcom, cacheKey, result);
5493
- return result;
5617
+ const ordered = [
5618
+ ...candidates.filter((media) => media.isPrimary),
5619
+ ...candidates.filter((media) => !media.isPrimary)
5620
+ ];
5621
+ for (const media of ordered) {
5622
+ if (onlyPrimary && !media.isPrimary) {
5623
+ break;
5624
+ }
5625
+ const result = await toDisplayableProfile(media, !!media.isPrimary);
5626
+ if (result) {
5627
+ profilePictureCache(
5628
+ this._gedcom,
5629
+ cacheKey,
5630
+ result
5631
+ );
5632
+ return result;
5633
+ }
5494
5634
  }
5495
5635
  profilePictureCache(
5496
5636
  this._gedcom,
@@ -5776,9 +5916,108 @@ var Indi = class extends Common {
5776
5916
  addParent(parent, pedigree) {
5777
5917
  return parent.addChild(this, pedigree);
5778
5918
  }
5919
+ /**
5920
+ * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
5921
+ */
5922
+ attachMultimedia(objeOrKey, options) {
5923
+ if (!this._gedcom || !this.id) {
5924
+ return void 0;
5925
+ }
5926
+ const obje = typeof objeOrKey === "string" ? this._gedcom.obje(objeOrKey) : objeOrKey;
5927
+ if (!obje?.id) {
5928
+ return void 0;
5929
+ }
5930
+ const already = this.get("OBJE")?.toList()?.values()?.some((item) => item?.toValue?.() === obje.id);
5931
+ if (!already) {
5932
+ const pointer = makePointer(
5933
+ this._gedcom,
5934
+ this,
5935
+ obje.id,
5936
+ "OBJE"
5937
+ );
5938
+ this.assign("OBJE", pointer);
5939
+ }
5940
+ if (options?.primary) {
5941
+ this.get("OBJE")?.toList()?.forEach((item) => {
5942
+ const target = item?.refType === "OBJE" ? this._gedcom?.obje(
5943
+ String(item.toValue())
5944
+ ) : item;
5945
+ if (!target) {
5946
+ return;
5947
+ }
5948
+ if (target.id === obje.id) {
5949
+ target.set("_PRIM", "Y");
5950
+ } else if (target.get("_PRIM")?.toValue() === "Y") {
5951
+ target.set("_PRIM", "N");
5952
+ }
5953
+ });
5954
+ obje.set("_PRIM", "Y");
5955
+ }
5956
+ return obje;
5957
+ }
5958
+ detachMultimedia(objeKey) {
5959
+ const list = this.get("OBJE")?.toList();
5960
+ if (!list) {
5961
+ return;
5962
+ }
5963
+ const toRemove = list.values().filter(
5964
+ (item) => item?.toValue?.() === objeKey || item?.id === objeKey
5965
+ );
5966
+ toRemove.forEach((item) => {
5967
+ if (item) {
5968
+ list.delete(item);
5969
+ }
5970
+ });
5971
+ if (list.length === 0) {
5972
+ this.remove("OBJE");
5973
+ }
5974
+ }
5975
+ /**
5976
+ * Create a multimedia record and attach it to this person.
5977
+ */
5978
+ attachMediaFromUrl(url, options) {
5979
+ if (!this._gedcom || !url) {
5980
+ return void 0;
5981
+ }
5982
+ const obje = this._gedcom.createMultimediaRecord({
5983
+ file: url,
5984
+ form: options?.form,
5985
+ title: options?.title,
5986
+ mediType: options?.mediType,
5987
+ primary: options?.primary,
5988
+ gedcomVersion: options?.gedcomVersion
5989
+ });
5990
+ return this.attachMultimedia(obje, { primary: options?.primary });
5991
+ }
5992
+ /**
5993
+ * Link a shared SNOTE record via `1 NOTE @Nn@` (GEDCOM 7).
5994
+ */
5995
+ attachSharedNote(noteOrKey) {
5996
+ if (!this._gedcom || !this.id) {
5997
+ return void 0;
5998
+ }
5999
+ const note = typeof noteOrKey === "string" ? this._gedcom.snote(noteOrKey) : noteOrKey;
6000
+ if (!note?.id) {
6001
+ return void 0;
6002
+ }
6003
+ const already = this.get("NOTE")?.toList()?.values()?.some((item) => item?.toValue?.() === note.id);
6004
+ if (!already) {
6005
+ const pointer = makePointer(
6006
+ this._gedcom,
6007
+ this,
6008
+ note.id,
6009
+ "SNOTE"
6010
+ );
6011
+ this.assign("NOTE", pointer);
6012
+ }
6013
+ return note;
6014
+ }
5779
6015
  addFact(input) {
5780
6016
  return addIndividualFact(this, input);
5781
6017
  }
6018
+ addNonEvent(input) {
6019
+ return addNonEvent(this, input);
6020
+ }
5782
6021
  getParentType(id) {
5783
6022
  let indi;
5784
6023
  if (typeof id === "string" || typeof id === "number") {
@@ -5819,7 +6058,7 @@ var Indi = class extends Common {
5819
6058
  if (!famChild) {
5820
6059
  const childIndi = this._gedcom?.indi(child.id);
5821
6060
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
5822
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
6061
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
5823
6062
  } else {
5824
6063
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
5825
6064
  }
@@ -6380,7 +6619,7 @@ var Indi = class extends Common {
6380
6619
  return;
6381
6620
  }
6382
6621
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
6383
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
6622
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
6384
6623
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
6385
6624
  children.append(child);
6386
6625
  }
@@ -6488,7 +6727,7 @@ var Indi = class extends Common {
6488
6727
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
6489
6728
  if (!child) {
6490
6729
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
6491
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
6730
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
6492
6731
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
6493
6732
  parents.merge(family.getParents());
6494
6733
  }
@@ -17,6 +17,7 @@ interface IGedComStructure extends Common, Omit<NonStandard, "id" | "value">, Pa
17
17
  "@@REPO"?: Repositories;
18
18
  "@@SOUR"?: Sources;
19
19
  "@@SUBM"?: Submitters;
20
+ "@@SNOTE"?: List;
20
21
  _ORIGHEAD?: Common;
21
22
  HEAD?: Common & {
22
23
  GEDC?: Common & {
@@ -1 +1 @@
1
- {"version":3,"file":"gedcom.d.ts","sourceRoot":"","sources":["../../src/structures/gedcom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,KAAK,iBAAiB,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,cAAc,MAAM,QAAQ,CAAC;AAEzC,UAAU,gBACT,SACC,MAAM,EACN,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,EACjC,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG;QACf,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,GAAG;YAChB,GAAG,CAAC,EAAE,MAAM,CAAC;SACb,CAAC;QACF,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,IAAI,CAAC,EAAE,MAAM,GAAG;gBACf,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,KAAK,CAAC,EAAE,MAAM,CAAC;gBACf,GAAG,CAAC,EAAE,MAAM,CAAC;gBACb,GAAG,CAAC,EAAE,MAAM,CAAC;aACb,GAAG,iBAAiB,CAAC;YACtB,IAAI,CAAC,EAAE,MAAM,GAAG;gBACf,IAAI,CAAC,EAAE,MAAM,GAAG;oBACf,IAAI,CAAC,EAAE,MAAM,CAAC;iBACd,CAAC;gBACF,IAAI,CAAC,EAAE,MAAM,CAAC;aACd,CAAC;YACF,KAAK,CAAC,EAAE,MAAM,GAAG;gBAChB,GAAG,CAAC,EAAE,MAAM,CAAC;aACb,CAAC;SACF,CAAC;QAEF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;KACF,GAAG,cAAc,CAAC;CACnB;AACD,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"gedcom.d.ts","sourceRoot":"","sources":["../../src/structures/gedcom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAE3D,OAAO,KAAK,iBAAiB,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,cAAc,MAAM,QAAQ,CAAC;AAEzC,UAAU,gBACT,SACC,MAAM,EACN,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,EACjC,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,EAAE,EAAE,IAAI,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,UAAU,CAAC;IACtB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG;QACf,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,KAAK,CAAC,EAAE,MAAM,GAAG;YAChB,GAAG,CAAC,EAAE,MAAM,CAAC;SACb,CAAC;QACF,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,IAAI,CAAC,EAAE,MAAM,GAAG;gBACf,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,KAAK,CAAC,EAAE,MAAM,CAAC;gBACf,GAAG,CAAC,EAAE,MAAM,CAAC;gBACb,GAAG,CAAC,EAAE,MAAM,CAAC;aACb,GAAG,iBAAiB,CAAC;YACtB,IAAI,CAAC,EAAE,MAAM,GAAG;gBACf,IAAI,CAAC,EAAE,MAAM,GAAG;oBACf,IAAI,CAAC,EAAE,MAAM,CAAC;iBACd,CAAC;gBACF,IAAI,CAAC,EAAE,MAAM,CAAC;aACd,CAAC;YACF,KAAK,CAAC,EAAE,MAAM,GAAG;gBAChB,GAAG,CAAC,EAAE,MAAM,CAAC;aACb,CAAC;SACF,CAAC;QAEF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;QACF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,GAAG;YACf,IAAI,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;KACF,GAAG,cAAc,CAAC;CACnB;AACD,eAAe,gBAAgB,CAAC"}