@treeviz/gedcom-parser 2.0.12 → 2.1.1

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
@@ -1,5 +1,6 @@
1
1
  import { format, isValid, parse } from 'date-fns';
2
2
  import { debounce, set, get, uniqBy, unset, difference, intersectionBy, differenceBy } from 'lodash-es';
3
+ import JSZip from 'jszip';
3
4
 
4
5
  var __defProp = Object.defineProperty;
5
6
  var __export = (target, all) => {
@@ -479,11 +480,17 @@ var List = class _List {
479
480
  const gedcom = [];
480
481
  this.entries().forEach(([_, item]) => {
481
482
  const validTag = getValidTag(tag);
482
- const validKey = item.id;
483
- const validValue = validKey ? validTag : item.exportValue();
484
- gedcom.push(
485
- `${level} ${validKey || validTag}${validValue ? ` ${validValue}` : ""}`
486
- );
483
+ const xref = item.id;
484
+ const payload = item.exportValue?.();
485
+ if (xref) {
486
+ gedcom.push(
487
+ `${level} ${xref} ${validTag}${payload ? ` ${payload}` : ""}`
488
+ );
489
+ } else {
490
+ gedcom.push(
491
+ `${level} ${validTag}${payload ? ` ${payload}` : ""}`
492
+ );
493
+ }
487
494
  gedcom.push(
488
495
  ...item.toGedcomLines(
489
496
  validTag,
@@ -741,12 +748,14 @@ var Common = class _Common {
741
748
  );
742
749
  }
743
750
  standardizeObject(tag, options) {
744
- if (tag === "OBJE" && !options?.original && options?.obje?.standardize && options?.obje?.namespace && "standardizeMedia" in this) {
751
+ if (tag === "OBJE" && options?.obje?.standardize && "standardizeMedia" in this) {
745
752
  const standardize = this.standardizeMedia;
746
753
  return standardize.call(
747
754
  this,
748
755
  options.obje.namespace,
749
- options.obje.override
756
+ options.obje.override,
757
+ void 0,
758
+ options.gedcomVersion
750
759
  );
751
760
  }
752
761
  }
@@ -773,7 +782,7 @@ var Common = class _Common {
773
782
  if (typeof prop?.toObject === "function") {
774
783
  if (prop instanceof _Common) {
775
784
  json[validKey] = prop.toObject(validKey, options);
776
- } else {
785
+ } else if (prop instanceof List) {
777
786
  json = {
778
787
  ...json,
779
788
  ...prop.toObject(validKey, options)
@@ -831,8 +840,12 @@ var Common = class _Common {
831
840
  this.standardizeObject(tag, options);
832
841
  const validKeys = getValidKeys(this);
833
842
  const gedcom = [];
843
+ const isGedcom7Export = options?.gedcomVersion === "7.0";
834
844
  validKeys.forEach((key) => {
835
845
  const validKey = key;
846
+ if (isGedcom7Export && validKey === "CONC") {
847
+ return;
848
+ }
836
849
  const prop = this.get(validKey);
837
850
  if (typeof prop?.toGedcomLines === "function") {
838
851
  if (prop instanceof _Common) {
@@ -843,7 +856,7 @@ var Common = class _Common {
843
856
  gedcom.push(
844
857
  ...prop.toGedcomLines(validKey, level + 1, options)
845
858
  );
846
- } else {
859
+ } else if (prop instanceof List) {
847
860
  gedcom.push(
848
861
  ...prop.toGedcomLines(validKey, level, options)
849
862
  );
@@ -3224,7 +3237,11 @@ var STANDARD_QUALIFIERS = {
3224
3237
  BEF: "Bef.",
3225
3238
  BEFORE: "Bef.",
3226
3239
  AFT: "Aft.",
3227
- AFTER: "Aft."
3240
+ AFTER: "Aft.",
3241
+ CAL: "Abt.",
3242
+ // calculated ≈ about for display
3243
+ EST: "Abt."
3244
+ // estimated ≈ about for display
3228
3245
  };
3229
3246
  var EXPORT_QUALIFIERS = {
3230
3247
  "Abt.": "ABT",
@@ -3232,6 +3249,11 @@ var EXPORT_QUALIFIERS = {
3232
3249
  "Aft.": "AFT"
3233
3250
  };
3234
3251
  var GEDCOM_MONTHS = /\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\b/g;
3252
+ var CALENDAR_PREFIX = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
3253
+ var isGedcomDateValueForm = (value) => (
3254
+ // Require space/end after the token so display forms like "Abt." are not opaque.
3255
+ /^(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)(\s|$)/i.test(value) || /\bAND\b/i.test(value)
3256
+ );
3235
3257
  var NOTE_MARKER = "####";
3236
3258
  var formatDateWithComponents = (date, hasDay, hasMonth, hasYear, noteValue, baseFormat = "dd MMM yyyy", locale) => {
3237
3259
  const hasNote = baseFormat.includes("NOTE");
@@ -3256,29 +3278,45 @@ var CommonDate = class extends Common {
3256
3278
  super(gedcom, id, main, parent);
3257
3279
  delete this.id;
3258
3280
  }
3281
+ get calendar() {
3282
+ return this._calendar;
3283
+ }
3259
3284
  set value(value) {
3260
3285
  if (value) {
3261
- const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
3262
- const noteMatch = value.match(noteRegExp)?.groups;
3263
3286
  let validValue = value;
3287
+ const calendarMatch = validValue.match(CALENDAR_PREFIX);
3288
+ if (calendarMatch?.groups?.calendar) {
3289
+ this._calendar = calendarMatch.groups.calendar.toUpperCase();
3290
+ validValue = validValue.slice(calendarMatch[0].length);
3291
+ } else {
3292
+ this._calendar = void 0;
3293
+ }
3294
+ const noteRegExp = /^(?<note>[a-zA-Z]+\.)/;
3295
+ const noteMatch = validValue.match(noteRegExp)?.groups;
3264
3296
  if (noteMatch?.note) {
3265
3297
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
3266
3298
  this.NOTE.value = noteMatch?.note;
3267
- validValue = value.replace(noteRegExp, "");
3299
+ validValue = validValue.replace(noteRegExp, "");
3268
3300
  } else {
3269
- const qualifierMatch = value.match(
3270
- /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER)\b\s*/i
3301
+ const qualifierMatch = validValue.match(
3302
+ /^(?<qualifier>ABT|ABOUT|BEF|BEFORE|AFT|AFTER|CAL|EST)\b\s*/i
3271
3303
  );
3272
3304
  const qualifier = qualifierMatch?.groups?.qualifier?.toUpperCase();
3273
3305
  const note = qualifier ? STANDARD_QUALIFIERS[qualifier] : void 0;
3274
3306
  if (note) {
3275
3307
  this.NOTE = this.NOTE || createCommon(this._gedcom, void 0, this.main);
3276
3308
  this.NOTE.value = note;
3277
- validValue = value.slice(qualifierMatch[0].length);
3309
+ validValue = validValue.slice(qualifierMatch[0].length);
3278
3310
  }
3279
3311
  }
3280
3312
  const acceptedDate = this.isValidDateFormat(validValue);
3281
- if (acceptedDate) {
3313
+ if (isGedcomDateValueForm(validValue.trim())) {
3314
+ delete this.DAY;
3315
+ delete this.MONTH;
3316
+ delete this.YEAR;
3317
+ this._date = void 0;
3318
+ this._value = value;
3319
+ } else if (acceptedDate) {
3282
3320
  this.DAY = this.DAY || createCommon(this._gedcom, void 0, this.main);
3283
3321
  this.DAY.value = format(acceptedDate, "dd");
3284
3322
  this.MONTH = this.MONTH || createCommon(this._gedcom, void 0, this.main);
@@ -3381,6 +3419,15 @@ var CommonDate = class extends Common {
3381
3419
  );
3382
3420
  }
3383
3421
  exportValue() {
3422
+ if (typeof this._value === "string") {
3423
+ const withoutCalendar = this._value.replace(CALENDAR_PREFIX, "");
3424
+ if (isGedcomDateValueForm(withoutCalendar.trim())) {
3425
+ return this._value.replace(
3426
+ /^((?:GREGORIAN|JULIAN|FRENCH_R|HEBREW)\s+)?(FROM|TO|BET|AFT|BEF|ABT|CAL|EST|INT)\b/i,
3427
+ (_m, cal, q) => `${cal || ""}${q.toUpperCase()}`
3428
+ );
3429
+ }
3430
+ }
3384
3431
  const formatted = this.toValue("NOTE dd MMM yyyy", null);
3385
3432
  if (!formatted) {
3386
3433
  return formatted;
@@ -3388,10 +3435,14 @@ var CommonDate = class extends Common {
3388
3435
  const note = this.NOTE?.value?.trim();
3389
3436
  const qualifier = note ? EXPORT_QUALIFIERS[note] : void 0;
3390
3437
  const standardized = qualifier ? formatted.replace(note, qualifier) : formatted;
3391
- return standardized.replace(
3438
+ const withMonths = standardized.replace(
3392
3439
  GEDCOM_MONTHS,
3393
3440
  (month) => month.toUpperCase()
3394
3441
  );
3442
+ if (this._calendar && this._calendar !== "GREGORIAN") {
3443
+ return `${this._calendar} ${withMonths}`;
3444
+ }
3445
+ return withMonths;
3395
3446
  }
3396
3447
  toGedcomLines(tag, level = 0, options) {
3397
3448
  const internal = new RegExp(
@@ -3923,6 +3974,24 @@ var addFamilyFact = (fam, input) => {
3923
3974
  fillFact(gedcom, fam, event, input);
3924
3975
  return event;
3925
3976
  };
3977
+ var addNonEvent = (owner, input) => {
3978
+ const gedcom = owner.getGedcom();
3979
+ const eventType = input.event?.trim().toUpperCase();
3980
+ if (!gedcom || !eventType) {
3981
+ return void 0;
3982
+ }
3983
+ const no = createCommon(gedcom, void 0, owner);
3984
+ no.type = "NO";
3985
+ no.value = eventType;
3986
+ if (input.date?.trim()) {
3987
+ setDateOn(gedcom, owner, no, input.date);
3988
+ }
3989
+ if (input.note?.trim()) {
3990
+ no.set("NOTE", input.note.trim());
3991
+ }
3992
+ owner.assign("NO", no);
3993
+ return no;
3994
+ };
3926
3995
 
3927
3996
  // src/utils/family-edit.ts
3928
3997
  var PEDIGREE_VALUES = [
@@ -4160,16 +4229,58 @@ var getPlaces = (common, type = ["ALL" /* All */], maxLevel = 1, level = 0, main
4160
4229
  });
4161
4230
  return places;
4162
4231
  };
4232
+
4233
+ // src/utils/local-media.ts
4234
+ var localMediaResolver;
4235
+ var isRemoteOrEmbeddedMediaUrl = (path) => {
4236
+ if (!path) {
4237
+ return false;
4238
+ }
4239
+ return path.startsWith("data:") || path.startsWith("blob:") || /^https?:\/\//i.test(path);
4240
+ };
4241
+ var resolveLocalMediaUrl = async (path) => {
4242
+ if (!path) {
4243
+ return void 0;
4244
+ }
4245
+ if (isRemoteOrEmbeddedMediaUrl(path)) {
4246
+ return path;
4247
+ }
4248
+ const resolved = await localMediaResolver?.(path);
4249
+ return resolved;
4250
+ };
4163
4251
  var implemented = (type, ...args) => {
4164
4252
  };
4165
4253
 
4166
4254
  // src/utils/media-utils.ts
4167
4255
  var getFileExtension = (filename) => {
4168
- const match = filename.match(/\.([^.]+)$/);
4169
- return match ? match[1] : "";
4256
+ if (!filename) {
4257
+ return "";
4258
+ }
4259
+ if (filename.startsWith("data:")) {
4260
+ const mime = filename.slice(5).split(";")[0] || "";
4261
+ const subtype = (mime.split("/")[1] || "").toLowerCase();
4262
+ if (subtype === "jpeg") {
4263
+ return "jpg";
4264
+ }
4265
+ return subtype.split("+")[0] || "";
4266
+ }
4267
+ const withoutQuery = filename.split(/[?#]/)[0] || filename;
4268
+ const match = withoutQuery.match(/\.([^.]+)$/);
4269
+ return match ? match[1].toLowerCase() : "";
4170
4270
  };
4171
4271
  var isImageFormat = (format2) => {
4172
- if (!format2) return false;
4272
+ if (!format2) {
4273
+ return false;
4274
+ }
4275
+ let normalized = format2.toLowerCase().trim();
4276
+ if (normalized.startsWith("data:")) {
4277
+ normalized = getFileExtension(normalized);
4278
+ } else if (normalized.includes("/")) {
4279
+ normalized = normalized.split("/")[1]?.split("+")[0] || "";
4280
+ if (normalized === "jpeg") {
4281
+ normalized = "jpg";
4282
+ }
4283
+ }
4173
4284
  const imageFormats = [
4174
4285
  "jpg",
4175
4286
  "jpeg",
@@ -4181,7 +4292,11 @@ var isImageFormat = (format2) => {
4181
4292
  "tiff",
4182
4293
  "tif"
4183
4294
  ];
4184
- return imageFormats.includes(format2.toLowerCase());
4295
+ return imageFormats.includes(normalized);
4296
+ };
4297
+ var resolveObjeForm = (obje) => {
4298
+ const value = obje?.get?.("FILE.FORM")?.toValue?.() ?? obje?.get?.("FORM")?.toValue?.();
4299
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
4185
4300
  };
4186
4301
  var uniqueItemsCache = /* @__PURE__ */ new WeakMap();
4187
4302
  var setNestedGroup = (obj, key, value, uniqueCounting = true) => {
@@ -4751,6 +4866,9 @@ var Fam = class extends Common {
4751
4866
  addFact(input) {
4752
4867
  return addFamilyFact(this, input);
4753
4868
  }
4869
+ addNonEvent(input) {
4870
+ return addNonEvent(this, input);
4871
+ }
4754
4872
  toList() {
4755
4873
  return new Families().concat(
4756
4874
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -5441,10 +5559,14 @@ var Indi = class extends Common {
5441
5559
  const clone = obje?.get("_CLON._OID")?.toValue();
5442
5560
  const mser = obje?.get("_MSER._LKID")?.toValue();
5443
5561
  let url = obje?.get("FILE")?.toValue();
5444
- const title = obje?.get("TITL")?.toValue() ?? "";
5445
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5446
- const imgId = clone || mser;
5447
- if (!www || !tree || !this.id) {
5562
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
5563
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5564
+ let imgId = clone || mser;
5565
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
5566
+ if (!hasEmbeddedOrLocalFile && (!www || !tree || !this.id)) {
5567
+ return;
5568
+ }
5569
+ if (!this.id) {
5448
5570
  return;
5449
5571
  }
5450
5572
  if (!namespace && !url) {
@@ -5462,13 +5584,16 @@ var Indi = class extends Common {
5462
5584
  }
5463
5585
  url = url || `https://${www}/mediaui-viewer/tree/${tree}/media/${media}`;
5464
5586
  }
5587
+ if (!imgId && hasEmbeddedOrLocalFile && url) {
5588
+ imgId = typeof media === "string" && media || `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}`;
5589
+ }
5465
5590
  if (url && imgId) {
5466
- const id = `${tree}-${this.id}-${imgId}`;
5591
+ const id = `${tree || "gedzip"}-${this.id}-${imgId}`;
5467
5592
  list[id] = {
5468
5593
  key,
5469
5594
  isPrimary,
5470
5595
  id,
5471
- tree,
5596
+ tree: tree || "gedzip",
5472
5597
  imgId,
5473
5598
  person: this.id,
5474
5599
  title,
@@ -5514,9 +5639,11 @@ var Indi = class extends Common {
5514
5639
  obje.standardizeMedia();
5515
5640
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5516
5641
  const url = obje?.get("FILE")?.toValue();
5517
- const title = obje?.get("NOTE")?.toValue() ?? "";
5518
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5519
- const imgId = obje?.get("_PHOTO_RIN")?.toValue();
5642
+ const title = obje?.get("FILE.NOTE")?.toValue() ?? obje?.get("NOTE")?.toValue() ?? "";
5643
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5644
+ const photoRin = obje?.get("_PHOTO_RIN")?.toValue();
5645
+ const hasEmbeddedOrLocalFile = !!url && (url.startsWith("data:") || url.startsWith("blob:") || !/^https?:\/\//i.test(url));
5646
+ const imgId = photoRin || (hasEmbeddedOrLocalFile && url ? `local-${String(key).replace(/[@]/g, "")}-${getFileExtension(url) || "bin"}` : void 0);
5520
5647
  if (url && imgId) {
5521
5648
  const id = `${tree}-${this.id}-${imgId}`;
5522
5649
  list[id] = {
@@ -5639,10 +5766,10 @@ var Indi = class extends Common {
5639
5766
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5640
5767
  const url = obje?.get("FILE")?.toValue();
5641
5768
  const title = obje?.get("TITL")?.toValue() ?? "";
5642
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5769
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5643
5770
  if (url) {
5644
5771
  const urlMatch = url.match(/\/([^/]+)\?hash=/);
5645
- const imgId = urlMatch?.[1] || `img-${index}-${Date.now().toString(36)}`;
5772
+ const imgId = urlMatch?.[1] || (url.startsWith("data:") ? `data-${index}-${getFileExtension(url) || "bin"}` : `img-${index}-${Date.now().toString(36)}`);
5646
5773
  const id = `geni-${geniId}-${imgId}`;
5647
5774
  list[id] = {
5648
5775
  isPrimary,
@@ -5679,10 +5806,10 @@ var Indi = class extends Common {
5679
5806
  obje.standardizeMedia();
5680
5807
  const isPrimary = obje?.get("_PRIM")?.toValue() === "Y";
5681
5808
  const url = obje?.get("FILE")?.toValue();
5682
- const title = obje?.get("TITL")?.toValue() ?? "";
5683
- const type = obje?.get("FORM")?.toValue() ?? "raw";
5809
+ const title = obje?.get("FILE.TITL")?.toValue() ?? obje?.get("TITL")?.toValue() ?? "";
5810
+ const type = resolveObjeForm(obje) ?? (url ? getFileExtension(url) : void 0) ?? "raw";
5684
5811
  if (url) {
5685
- const imgId = `media-${index}-${url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
5812
+ const imgId = `media-${index}-${url.startsWith("data:") ? getFileExtension(url) || "bin" : url.split("/").pop()?.split("?")[0]?.substring(0, 20) || Date.now().toString(36)}`;
5686
5813
  const id = `${treeId}-${this.id}-${imgId}`;
5687
5814
  list[id] = {
5688
5815
  isPrimary,
@@ -5722,7 +5849,20 @@ var Indi = class extends Common {
5722
5849
  cacheKey
5723
5850
  );
5724
5851
  if (cached !== void 0) {
5725
- return cached;
5852
+ if (cached?.file && !isRemoteOrEmbeddedMediaUrl(cached.file)) {
5853
+ const resolvedCached = await resolveLocalMediaUrl(cached.file);
5854
+ if (resolvedCached && isRemoteOrEmbeddedMediaUrl(resolvedCached)) {
5855
+ const refreshed = { ...cached, file: resolvedCached };
5856
+ profilePictureCache(
5857
+ this._gedcom,
5858
+ cacheKey,
5859
+ refreshed
5860
+ );
5861
+ return refreshed;
5862
+ }
5863
+ } else {
5864
+ return cached;
5865
+ }
5726
5866
  }
5727
5867
  const mediaList = await this.multimedia(namespace);
5728
5868
  if (!mediaList) {
@@ -5734,39 +5874,40 @@ var Indi = class extends Common {
5734
5874
  return void 0;
5735
5875
  }
5736
5876
  const mediaArray = Object.values(mediaList);
5737
- const primaryMedia = mediaArray.find(
5738
- (media) => media.isPrimary && isImageFormat(media.contentType || getFileExtension(media.url))
5739
- );
5740
- if (primaryMedia) {
5741
- const result = {
5742
- file: primaryMedia.url,
5743
- form: primaryMedia.contentType,
5744
- title: primaryMedia.title,
5745
- isPrimary: true
5877
+ const toDisplayableProfile = async (media, isPrimary) => {
5878
+ const resolvedFile = await resolveLocalMediaUrl(media.url);
5879
+ if (!resolvedFile || !isRemoteOrEmbeddedMediaUrl(resolvedFile)) {
5880
+ return void 0;
5881
+ }
5882
+ return {
5883
+ file: resolvedFile,
5884
+ form: media.contentType,
5885
+ title: media.title,
5886
+ isPrimary
5746
5887
  };
5747
- profilePictureCache(this._gedcom, cacheKey, result);
5748
- return result;
5749
- }
5750
- if (onlyPrimary) {
5751
- profilePictureCache(
5752
- this._gedcom,
5753
- cacheKey,
5754
- void 0
5755
- );
5756
- return void 0;
5757
- }
5758
- const secondaryMedia = mediaArray.find(
5759
- (media) => isImageFormat(media.contentType || getFileExtension(media.url))
5888
+ };
5889
+ const candidates = mediaArray.filter(
5890
+ (media) => isImageFormat(
5891
+ media.contentType || media.url || getFileExtension(media.url)
5892
+ )
5760
5893
  );
5761
- if (secondaryMedia) {
5762
- const result = {
5763
- file: secondaryMedia.url,
5764
- form: secondaryMedia.contentType,
5765
- title: secondaryMedia.title,
5766
- isPrimary: false
5767
- };
5768
- profilePictureCache(this._gedcom, cacheKey, result);
5769
- return result;
5894
+ const ordered = [
5895
+ ...candidates.filter((media) => media.isPrimary),
5896
+ ...candidates.filter((media) => !media.isPrimary)
5897
+ ];
5898
+ for (const media of ordered) {
5899
+ if (onlyPrimary && !media.isPrimary) {
5900
+ break;
5901
+ }
5902
+ const result = await toDisplayableProfile(media, !!media.isPrimary);
5903
+ if (result) {
5904
+ profilePictureCache(
5905
+ this._gedcom,
5906
+ cacheKey,
5907
+ result
5908
+ );
5909
+ return result;
5910
+ }
5770
5911
  }
5771
5912
  profilePictureCache(
5772
5913
  this._gedcom,
@@ -6052,9 +6193,108 @@ var Indi = class extends Common {
6052
6193
  addParent(parent, pedigree) {
6053
6194
  return parent.addChild(this, pedigree);
6054
6195
  }
6196
+ /**
6197
+ * Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
6198
+ */
6199
+ attachMultimedia(objeOrKey, options) {
6200
+ if (!this._gedcom || !this.id) {
6201
+ return void 0;
6202
+ }
6203
+ const obje = typeof objeOrKey === "string" ? this._gedcom.obje(objeOrKey) : objeOrKey;
6204
+ if (!obje?.id) {
6205
+ return void 0;
6206
+ }
6207
+ const already = this.get("OBJE")?.toList()?.values()?.some((item) => item?.toValue?.() === obje.id);
6208
+ if (!already) {
6209
+ const pointer = makePointer(
6210
+ this._gedcom,
6211
+ this,
6212
+ obje.id,
6213
+ "OBJE"
6214
+ );
6215
+ this.assign("OBJE", pointer);
6216
+ }
6217
+ if (options?.primary) {
6218
+ this.get("OBJE")?.toList()?.forEach((item) => {
6219
+ const target = item?.refType === "OBJE" ? this._gedcom?.obje(
6220
+ String(item.toValue())
6221
+ ) : item;
6222
+ if (!target) {
6223
+ return;
6224
+ }
6225
+ if (target.id === obje.id) {
6226
+ target.set("_PRIM", "Y");
6227
+ } else if (target.get("_PRIM")?.toValue() === "Y") {
6228
+ target.set("_PRIM", "N");
6229
+ }
6230
+ });
6231
+ obje.set("_PRIM", "Y");
6232
+ }
6233
+ return obje;
6234
+ }
6235
+ detachMultimedia(objeKey) {
6236
+ const list = this.get("OBJE")?.toList();
6237
+ if (!list) {
6238
+ return;
6239
+ }
6240
+ const toRemove = list.values().filter(
6241
+ (item) => item?.toValue?.() === objeKey || item?.id === objeKey
6242
+ );
6243
+ toRemove.forEach((item) => {
6244
+ if (item) {
6245
+ list.delete(item);
6246
+ }
6247
+ });
6248
+ if (list.length === 0) {
6249
+ this.remove("OBJE");
6250
+ }
6251
+ }
6252
+ /**
6253
+ * Create a multimedia record and attach it to this person.
6254
+ */
6255
+ attachMediaFromUrl(url, options) {
6256
+ if (!this._gedcom || !url) {
6257
+ return void 0;
6258
+ }
6259
+ const obje = this._gedcom.createMultimediaRecord({
6260
+ file: url,
6261
+ form: options?.form,
6262
+ title: options?.title,
6263
+ mediType: options?.mediType,
6264
+ primary: options?.primary,
6265
+ gedcomVersion: options?.gedcomVersion
6266
+ });
6267
+ return this.attachMultimedia(obje, { primary: options?.primary });
6268
+ }
6269
+ /**
6270
+ * Link a shared SNOTE record via `1 NOTE @Nn@` (GEDCOM 7).
6271
+ */
6272
+ attachSharedNote(noteOrKey) {
6273
+ if (!this._gedcom || !this.id) {
6274
+ return void 0;
6275
+ }
6276
+ const note = typeof noteOrKey === "string" ? this._gedcom.snote(noteOrKey) : noteOrKey;
6277
+ if (!note?.id) {
6278
+ return void 0;
6279
+ }
6280
+ const already = this.get("NOTE")?.toList()?.values()?.some((item) => item?.toValue?.() === note.id);
6281
+ if (!already) {
6282
+ const pointer = makePointer(
6283
+ this._gedcom,
6284
+ this,
6285
+ note.id,
6286
+ "SNOTE"
6287
+ );
6288
+ this.assign("NOTE", pointer);
6289
+ }
6290
+ return note;
6291
+ }
6055
6292
  addFact(input) {
6056
6293
  return addIndividualFact(this, input);
6057
6294
  }
6295
+ addNonEvent(input) {
6296
+ return addNonEvent(this, input);
6297
+ }
6058
6298
  getParentType(id) {
6059
6299
  let indi;
6060
6300
  if (typeof id === "string" || typeof id === "number") {
@@ -6095,7 +6335,7 @@ var Indi = class extends Common {
6095
6335
  if (!famChild) {
6096
6336
  const childIndi = this._gedcom?.indi(child.id);
6097
6337
  const childState = childIndi?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
6098
- childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
6338
+ childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
6099
6339
  } else {
6100
6340
  childType = famChild.get(usedRel)?.toValue() || "biological" /* BIOLOGICAL */;
6101
6341
  }
@@ -6656,7 +6896,7 @@ var Indi = class extends Common {
6656
6896
  return;
6657
6897
  }
6658
6898
  const childState = child?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
6659
- const childType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
6899
+ const childType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
6660
6900
  if (childType?.toLowerCase() === filter.PEDI.toLowerCase()) {
6661
6901
  children.append(child);
6662
6902
  }
@@ -6764,7 +7004,7 @@ var Indi = class extends Common {
6764
7004
  const child = this.id && family.CHIL?.toValueList()?.item(this.id);
6765
7005
  if (!child) {
6766
7006
  const childState = this?.get("FAMC")?.toList()?.filter((item) => item.value === family.id).index(0);
6767
- const parentType = childState?.get("PEDI")?.toValue() || "biological" /* BIOLOGICAL */;
7007
+ const parentType = (childState?.get("PEDI") ?? childState?.get("PEDT"))?.toValue() || "biological" /* BIOLOGICAL */;
6768
7008
  if (parentType?.toLowerCase() === filter.PEDI.toLowerCase()) {
6769
7009
  parents.merge(family.getParents());
6770
7010
  }
@@ -7156,104 +7396,851 @@ var createIndi = (gedcom, id, main, parent) => {
7156
7396
  );
7157
7397
  };
7158
7398
 
7159
- // package.json
7160
- var package_default = {
7161
- version: "2.0.12"};
7162
-
7163
- // src/utils/get-product-details.ts
7164
- var getVersion = () => package_default.version;
7165
-
7166
- // src/classes/gedcom.ts
7167
- var isObjectPatch = (value) => {
7168
- return !!value && typeof value === "object" && !Array.isArray(value);
7169
- };
7170
- var GedCom = class extends Common {
7171
- constructor() {
7172
- super();
7173
- this.tagMembers = {};
7174
- this.reflist = {};
7175
- this.refcount = 0;
7176
- delete this._gedcom;
7177
- delete this.id;
7178
- this.removeValue();
7179
- }
7180
- getMain(type, index) {
7181
- const list = !type || type instanceof List ? type : this.getList(type);
7182
- if (!list) {
7183
- return void 0;
7399
+ // src/utils/gedcom-version.ts
7400
+ var normalizeGedcomVersion = (value) => {
7401
+ const trimmed = value?.trim() ?? "";
7402
+ if (trimmed.startsWith("7")) {
7403
+ return "7.0";
7404
+ }
7405
+ return "5.5.1";
7406
+ };
7407
+ var GEDZIP_GEDCOM_ENTRY = "gedcom.ged";
7408
+ var sanitizePathSegment = (name) => name.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^\.+/, "") || "file";
7409
+ var extensionFor = (contentType) => {
7410
+ const raw = (contentType || "bin").replace(/^\./, "").toLowerCase();
7411
+ if (raw.includes("/")) {
7412
+ return raw.split("/").pop() || "bin";
7413
+ }
7414
+ return raw || "bin";
7415
+ };
7416
+ var rewriteGedcomFilePaths = (gedcomText, urlToPath) => {
7417
+ const entries = urlToPath instanceof Map ? urlToPath : new Map(Object.entries(urlToPath));
7418
+ let text = gedcomText;
7419
+ entries.forEach((path, url) => {
7420
+ const escaped = url.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7421
+ text = text.replace(
7422
+ new RegExp(`(^|\\r?\\n)(\\d+ FILE )${escaped}`, "g"),
7423
+ `$1$2${path}`
7424
+ );
7425
+ });
7426
+ return text;
7427
+ };
7428
+ var assignMediaPaths = (mediaFiles) => {
7429
+ const urlToPath = /* @__PURE__ */ new Map();
7430
+ const usedPaths = /* @__PURE__ */ new Set([GEDZIP_GEDCOM_ENTRY]);
7431
+ const files = [];
7432
+ for (const file of mediaFiles) {
7433
+ if (!file.content || !file.url) {
7434
+ continue;
7184
7435
  }
7185
- if (typeof index === "string") {
7186
- return list.item(index);
7436
+ const ext = extensionFor(file.contentType);
7437
+ const base = sanitizePathSegment(
7438
+ file.imgId || file.title || file.id || "media"
7439
+ );
7440
+ let path = `media/${base}.${ext}`;
7441
+ let suffix = 1;
7442
+ while (usedPaths.has(path)) {
7443
+ path = `media/${base}-${suffix++}.${ext}`;
7444
+ }
7445
+ usedPaths.add(path);
7446
+ urlToPath.set(file.url, path);
7447
+ const marker = "base64,";
7448
+ const idx = file.content.indexOf(marker);
7449
+ if (idx >= 0) {
7450
+ files.push({
7451
+ path,
7452
+ url: file.url,
7453
+ content: file.content.substring(idx + marker.length),
7454
+ base64: true
7455
+ });
7456
+ } else {
7457
+ files.push({
7458
+ path,
7459
+ url: file.url,
7460
+ content: file.content,
7461
+ base64: false
7462
+ });
7187
7463
  }
7188
- const keys = list.keys();
7189
- return list.item(keys[index]);
7190
7464
  }
7191
- getList(type) {
7192
- return this.get(type);
7465
+ return { urlToPath, files };
7466
+ };
7467
+ var buildGedzip = async (gedcomText, mediaFiles = []) => {
7468
+ const zip = new JSZip();
7469
+ const { urlToPath, files } = assignMediaPaths(mediaFiles);
7470
+ for (const file of files) {
7471
+ zip.file(file.path, file.content, { base64: file.base64 });
7193
7472
  }
7194
- createNodeFromPatchValue(value, main) {
7195
- if (value === void 0 || value === null) {
7196
- return void 0;
7473
+ zip.file(GEDZIP_GEDCOM_ENTRY, rewriteGedcomFilePaths(gedcomText, urlToPath));
7474
+ return zip.generateAsync({ type: "uint8array" });
7475
+ };
7476
+
7477
+ // src/utils/gedcom7-enumerations.ts
7478
+ var GEDCOM7_SEX_VALUES = ["M", "F", "U", "X", "N"];
7479
+ var GEDCOM7_PEDI_VALUES = [
7480
+ "adopted",
7481
+ "birth",
7482
+ "foster",
7483
+ "sealing"
7484
+ ];
7485
+ var GEDCOM7_ROLE_VALUES = [
7486
+ "CHIL",
7487
+ "CLERGY",
7488
+ "FATH",
7489
+ "FRIEND",
7490
+ "GODP",
7491
+ "HUSB",
7492
+ "MOTH",
7493
+ "MULTIPLE",
7494
+ "NGHBR",
7495
+ "OFFICIATOR",
7496
+ "OTHER",
7497
+ "PARENT",
7498
+ "SPOU",
7499
+ "WIFE",
7500
+ "WITN"
7501
+ ];
7502
+ var GEDCOM7_RESN_VALUES = [
7503
+ "CONFIDENTIAL",
7504
+ "LOCKED",
7505
+ "PRIVACY"
7506
+ ];
7507
+ var isAllowed = (value, allowed) => allowed.includes(value);
7508
+ var moveToPhrase = (node, phrase) => {
7509
+ const previousValue = node.value;
7510
+ const previousPhrase = node.get("PHRASE");
7511
+ const previousPhraseValue = previousPhrase?.toValue();
7512
+ node.removeValue();
7513
+ if (previousPhrase) {
7514
+ previousPhrase.value = phrase;
7515
+ } else {
7516
+ const phraseNode = createCommon(node.getGedcom(), void 0, node.main);
7517
+ phraseNode.value = phrase;
7518
+ node.set("PHRASE", phraseNode);
7519
+ }
7520
+ return () => {
7521
+ if (previousValue === void 0) {
7522
+ node.removeValue();
7523
+ } else {
7524
+ node.value = previousValue;
7197
7525
  }
7198
- const node = createCommon(this, void 0, main);
7199
- if (Array.isArray(value)) {
7200
- value.forEach((item) => {
7201
- const childNode = this.createNodeFromPatchValue(item, node);
7202
- if (childNode) {
7203
- node.assign("DATA", childNode);
7204
- }
7526
+ if (previousPhrase) {
7527
+ previousPhrase.value = previousPhraseValue;
7528
+ } else {
7529
+ node.remove("PHRASE");
7530
+ }
7531
+ };
7532
+ };
7533
+ var normalizeEnum = (node, allowed, mode, restores) => {
7534
+ const value = node?.toValue();
7535
+ if (!node || typeof value !== "string" || !value.trim()) {
7536
+ return;
7537
+ }
7538
+ const normalized = mode === "upper" ? value.trim().toUpperCase() : value.trim().toLowerCase();
7539
+ if (isAllowed(normalized, allowed)) {
7540
+ if (value !== normalized) {
7541
+ const previous = node.value;
7542
+ node.value = normalized;
7543
+ restores.push(() => {
7544
+ node.value = previous;
7205
7545
  });
7206
- return node;
7207
7546
  }
7208
- if (isObjectPatch(value)) {
7209
- this.applyObjectToCommon(node, value);
7210
- return node;
7547
+ return;
7548
+ }
7549
+ restores.push(moveToPhrase(node, value.trim()));
7550
+ };
7551
+ var applyGedcom7Enumerations = (gedcom) => {
7552
+ const restores = [];
7553
+ gedcom.indis()?.forEach((indi) => {
7554
+ if (!indi) return;
7555
+ normalizeEnum(indi.get("SEX"), GEDCOM7_SEX_VALUES, "upper", restores);
7556
+ normalizeEnum(indi.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
7557
+ indi.get("FAMC")?.toList()?.forEach((famc) => {
7558
+ normalizeEnum(
7559
+ famc?.get("PEDI") ?? famc?.get("PEDT"),
7560
+ GEDCOM7_PEDI_VALUES,
7561
+ "lower",
7562
+ restores
7563
+ );
7564
+ });
7565
+ indi.get("ASSO")?.toList()?.forEach((asso) => {
7566
+ normalizeEnum(
7567
+ asso?.get("ROLE"),
7568
+ GEDCOM7_ROLE_VALUES,
7569
+ "upper",
7570
+ restores
7571
+ );
7572
+ });
7573
+ });
7574
+ gedcom.fams()?.forEach((fam) => {
7575
+ if (!fam) return;
7576
+ normalizeEnum(fam.get("RESN"), GEDCOM7_RESN_VALUES, "upper", restores);
7577
+ fam.get("ASSO")?.toList()?.forEach((asso) => {
7578
+ normalizeEnum(
7579
+ asso?.get("ROLE"),
7580
+ GEDCOM7_ROLE_VALUES,
7581
+ "upper",
7582
+ restores
7583
+ );
7584
+ });
7585
+ });
7586
+ return () => {
7587
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7588
+ restores[i]?.();
7211
7589
  }
7212
- node.value = String(value);
7213
- return node;
7590
+ };
7591
+ };
7592
+
7593
+ // src/utils/gedcom7-dates.ts
7594
+ var GEDCOM7_CALENDARS = [
7595
+ "GREGORIAN",
7596
+ "JULIAN",
7597
+ "FRENCH_R",
7598
+ "HEBREW"
7599
+ ];
7600
+ var CALENDAR_PREFIX2 = /^(?<calendar>GREGORIAN|JULIAN|FRENCH_R|HEBREW)\b\s+/i;
7601
+ var parseGedcom7CalendarPrefix = (value) => {
7602
+ const match = value.match(CALENDAR_PREFIX2);
7603
+ if (!match?.groups?.calendar) {
7604
+ return { calendar: void 0, rest: value };
7214
7605
  }
7215
- applyObjectToCommon(target, patch) {
7216
- Object.entries(patch).forEach(([rawTag, value]) => {
7217
- if (rawTag === "value") {
7218
- if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
7219
- return;
7220
- }
7221
- target.value = String(value);
7222
- return;
7223
- }
7224
- if (rawTag === "id") {
7225
- if (typeof value === "string") {
7226
- target.id = value;
7606
+ return {
7607
+ calendar: match.groups.calendar.toUpperCase(),
7608
+ rest: value.slice(match[0].length)
7609
+ };
7610
+ };
7611
+ var withGedcom7Calendar = (value, calendar) => {
7612
+ if (!calendar || calendar.toUpperCase() === "GREGORIAN") {
7613
+ return value;
7614
+ }
7615
+ const upper = calendar.toUpperCase();
7616
+ if (GEDCOM7_CALENDARS.includes(upper)) {
7617
+ const { rest } = parseGedcom7CalendarPrefix(value);
7618
+ return `${upper} ${rest}`.trim();
7619
+ }
7620
+ return value;
7621
+ };
7622
+ var applyGedcom7DatePhrases = (gedcom) => {
7623
+ const restores = [];
7624
+ const visitDate = (dateNode) => {
7625
+ if (!dateNode || typeof dateNode.toValue !== "function") {
7626
+ return;
7627
+ }
7628
+ const asDate = dateNode;
7629
+ const hasComponents = !!(asDate.DAY || asDate.MONTH || asDate.YEAR);
7630
+ const raw = dateNode.exportValue?.() ?? dateNode.toValue();
7631
+ if (typeof raw !== "string" || !raw.trim()) {
7632
+ return;
7633
+ }
7634
+ const { calendar, rest } = parseGedcom7CalendarPrefix(raw.trim());
7635
+ const looksStructured = hasComponents || /^(ABT|CAL|EST|BEF|AFT|FROM|TO|BET|INT)(\s|$)/i.test(rest) || /\bAND\b/i.test(rest) || /^\d{1,2}\s+[A-Z]{3}\s+\d{1,4}/i.test(rest) || /^[A-Z]{3}\s+\d{1,4}/i.test(rest) || /^\d{1,4}$/.test(rest);
7636
+ if (looksStructured) {
7637
+ if (calendar && calendar !== "GREGORIAN") {
7638
+ const previous = dateNode.value;
7639
+ const next = withGedcom7Calendar(rest, calendar);
7640
+ if (previous !== next) {
7641
+ dateNode.value = next;
7642
+ restores.push(() => {
7643
+ dateNode.value = previous;
7644
+ });
7227
7645
  }
7228
- return;
7229
- }
7230
- const tag = rawTag;
7231
- if (value === void 0) {
7232
- return;
7233
- }
7234
- if (value === null) {
7235
- target.remove(tag);
7236
- return;
7237
7646
  }
7238
- if (Array.isArray(value)) {
7239
- target.remove(tag);
7240
- value.forEach((item) => {
7241
- const listNode = this.createNodeFromPatchValue(
7242
- item,
7243
- target.main ?? this
7244
- );
7245
- if (listNode) {
7246
- target.assign(tag, listNode);
7247
- }
7248
- });
7249
- return;
7647
+ return;
7648
+ }
7649
+ const previousValue = dateNode.value;
7650
+ const previousPhrase = dateNode.get("PHRASE");
7651
+ const previousPhraseValue = previousPhrase?.toValue();
7652
+ dateNode.removeValue();
7653
+ if (previousPhrase) {
7654
+ previousPhrase.value = raw.trim();
7655
+ } else {
7656
+ const phrase = createCommon(
7657
+ dateNode.getGedcom(),
7658
+ void 0,
7659
+ dateNode.main
7660
+ );
7661
+ phrase.value = raw.trim();
7662
+ dateNode.set("PHRASE", phrase);
7663
+ }
7664
+ restores.push(() => {
7665
+ if (previousValue === void 0) {
7666
+ dateNode.removeValue();
7667
+ } else {
7668
+ dateNode.value = previousValue;
7250
7669
  }
7251
- if (isObjectPatch(value)) {
7252
- const existing = target.get(tag);
7253
- const objectNode = existing ?? createCommon(this, void 0, target.main ?? this);
7254
- this.applyObjectToCommon(objectNode, value);
7255
- target.set(tag, objectNode);
7256
- return;
7670
+ if (previousPhrase) {
7671
+ previousPhrase.value = previousPhraseValue;
7672
+ } else {
7673
+ dateNode.remove("PHRASE");
7674
+ }
7675
+ });
7676
+ };
7677
+ const walk = (node) => {
7678
+ if (!node) return;
7679
+ node.get("DATE")?.toList()?.forEach((item) => visitDate(item));
7680
+ node.get("SDATE")?.toList()?.forEach((item) => visitDate(item));
7681
+ };
7682
+ gedcom.indis()?.forEach((indi) => {
7683
+ if (!indi) return;
7684
+ walk(indi);
7685
+ [
7686
+ "BIRT",
7687
+ "DEAT",
7688
+ "CHR",
7689
+ "BAPM",
7690
+ "BURI",
7691
+ "CREM",
7692
+ "ADOP",
7693
+ "EVEN",
7694
+ "RESI",
7695
+ "OCCU",
7696
+ "MARR"
7697
+ ].forEach((tag) => {
7698
+ indi.get(tag)?.toList()?.forEach((event) => walk(event));
7699
+ });
7700
+ });
7701
+ gedcom.fams()?.forEach((fam) => {
7702
+ if (!fam) return;
7703
+ ["MARR", "DIV", "ENGA", "ANUL", "EVEN"].forEach((tag) => {
7704
+ fam.get(tag)?.toList()?.forEach((event) => walk(event));
7705
+ });
7706
+ });
7707
+ return () => {
7708
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7709
+ restores[i]?.();
7710
+ }
7711
+ };
7712
+ };
7713
+
7714
+ // src/utils/uid.ts
7715
+ var createGedcomUid = () => {
7716
+ const cryptoObj = typeof globalThis !== "undefined" ? globalThis.crypto : void 0;
7717
+ if (cryptoObj && typeof cryptoObj.randomUUID === "function") {
7718
+ return cryptoObj.randomUUID();
7719
+ }
7720
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
7721
+ const rand = Math.random() * 16 | 0;
7722
+ const value = char === "x" ? rand : rand & 3 | 8;
7723
+ return value.toString(16);
7724
+ });
7725
+ };
7726
+
7727
+ // src/utils/gedcom7-metadata.ts
7728
+ var GEDCOM_MONTHS2 = [
7729
+ "JAN",
7730
+ "FEB",
7731
+ "MAR",
7732
+ "APR",
7733
+ "MAY",
7734
+ "JUN",
7735
+ "JUL",
7736
+ "AUG",
7737
+ "SEP",
7738
+ "OCT",
7739
+ "NOV",
7740
+ "DEC"
7741
+ ];
7742
+ var formatGedcom7Timestamp = (date = /* @__PURE__ */ new Date()) => {
7743
+ const day = String(date.getUTCDate());
7744
+ const month = GEDCOM_MONTHS2[date.getUTCMonth()];
7745
+ const year = String(date.getUTCFullYear());
7746
+ const hh = String(date.getUTCHours()).padStart(2, "0");
7747
+ const mm = String(date.getUTCMinutes()).padStart(2, "0");
7748
+ const ss = String(date.getUTCSeconds()).padStart(2, "0");
7749
+ return {
7750
+ date: `${day} ${month} ${year}`,
7751
+ time: `${hh}:${mm}:${ss}Z`
7752
+ };
7753
+ };
7754
+ var ensureMetaDate = (gedcom, record, tag, when, restores) => {
7755
+ if (record.get(tag)) {
7756
+ return;
7757
+ }
7758
+ const { date, time } = formatGedcom7Timestamp(when);
7759
+ const meta = createCommon(gedcom, void 0, record);
7760
+ meta.type = tag;
7761
+ const dateNode = createCommonDate(gedcom, void 0, record, meta);
7762
+ dateNode.type = "DATE";
7763
+ dateNode.value = date;
7764
+ dateNode.set("TIME", time);
7765
+ meta.set("DATE", dateNode);
7766
+ record.set(tag, meta);
7767
+ restores.push(() => {
7768
+ record.remove(tag);
7769
+ });
7770
+ };
7771
+ var ensureUid = (record, restores) => {
7772
+ const existing = record.get("UID")?.toValue();
7773
+ if (typeof existing === "string" && existing.trim()) {
7774
+ return;
7775
+ }
7776
+ const uid = createGedcomUid();
7777
+ record.set("UID", uid);
7778
+ restores.push(() => {
7779
+ record.remove("UID");
7780
+ });
7781
+ };
7782
+ var applyGedcom7RecordMetadata = (gedcom, options) => {
7783
+ const restores = [];
7784
+ const now = /* @__PURE__ */ new Date();
7785
+ const visit = (record) => {
7786
+ if (!record?.id) {
7787
+ return;
7788
+ }
7789
+ ensureUid(record, restores);
7790
+ ensureMetaDate(gedcom, record, "CREA", now, restores);
7791
+ ensureMetaDate(gedcom, record, "CHAN", now, restores);
7792
+ };
7793
+ gedcom.indis()?.forEach((indi) => visit(indi));
7794
+ gedcom.fams()?.forEach((fam) => visit(fam));
7795
+ gedcom.objes()?.forEach((obje) => visit(obje));
7796
+ gedcom.sours()?.forEach((sour) => visit(sour));
7797
+ gedcom.repos()?.forEach((repo) => visit(repo));
7798
+ gedcom.subms()?.forEach((subm) => visit(subm));
7799
+ gedcom.snotes()?.forEach((note) => visit(note));
7800
+ return () => {
7801
+ for (let i = restores.length - 1; i >= 0; i -= 1) {
7802
+ restores[i]?.();
7803
+ }
7804
+ };
7805
+ };
7806
+
7807
+ // src/utils/gedcom7-schema.ts
7808
+ var TREEVIZ_EXTENSION_BASE_URI = "https://treeviz.com/gedcom";
7809
+ var TREEVIZ_EXTENSION_URIS = {
7810
+ _ORIGHEAD: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIGHEAD`,
7811
+ _TREE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREE`,
7812
+ _TID: `${TREEVIZ_EXTENSION_BASE_URI}#_TID`,
7813
+ _PRIM: `${TREEVIZ_EXTENSION_BASE_URI}#_PRIM`,
7814
+ _FREL: `${TREEVIZ_EXTENSION_BASE_URI}#_FREL`,
7815
+ _MREL: `${TREEVIZ_EXTENSION_BASE_URI}#_MREL`,
7816
+ _SREL: `${TREEVIZ_EXTENSION_BASE_URI}#_SREL`,
7817
+ _UID: `${TREEVIZ_EXTENSION_BASE_URI}#_UID`,
7818
+ _WLNK: `${TREEVIZ_EXTENSION_BASE_URI}#_WLNK`,
7819
+ _MTTAG: `${TREEVIZ_EXTENSION_BASE_URI}#_MTTAG`,
7820
+ _MTCAT: `${TREEVIZ_EXTENSION_BASE_URI}#_MTCAT`,
7821
+ _CLON: `${TREEVIZ_EXTENSION_BASE_URI}#_CLON`,
7822
+ _MSER: `${TREEVIZ_EXTENSION_BASE_URI}#_MSER`,
7823
+ _OID: `${TREEVIZ_EXTENSION_BASE_URI}#_OID`,
7824
+ _LKID: `${TREEVIZ_EXTENSION_BASE_URI}#_LKID`,
7825
+ _PHOTO_RIN: `${TREEVIZ_EXTENSION_BASE_URI}#_PHOTO_RIN`,
7826
+ _EXPORTED_FROM_SITE_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_EXPORTED_FROM_SITE_ID`,
7827
+ _FS_ID: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_ID`,
7828
+ _FS_LINK: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_LINK`,
7829
+ _FS_MATCH: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_MATCH`,
7830
+ _FS_SOUR: `${TREEVIZ_EXTENSION_BASE_URI}#_FS_SOUR`,
7831
+ _IS_FS: `${TREEVIZ_EXTENSION_BASE_URI}#_IS_FS`,
7832
+ _LABEL: `${TREEVIZ_EXTENSION_BASE_URI}#_LABEL`,
7833
+ _MILT: `${TREEVIZ_EXTENSION_BASE_URI}#_MILT`,
7834
+ _MILTID: `${TREEVIZ_EXTENSION_BASE_URI}#_MILTID`,
7835
+ _ORIG: `${TREEVIZ_EXTENSION_BASE_URI}#_ORIG`,
7836
+ _ENV: `${TREEVIZ_EXTENSION_BASE_URI}#_ENV`,
7837
+ _TREEVIZ_SETTINGS: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_SETTINGS`,
7838
+ _TREEVIZ_VIEW: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_VIEW`,
7839
+ _TREEVIZ_DETECTIVE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE`,
7840
+ _TREEVIZ_DETECTIVE_STATE: `${TREEVIZ_EXTENSION_BASE_URI}#_TREEVIZ_DETECTIVE_STATE`
7841
+ };
7842
+ var parseTagDefinition = (payload) => {
7843
+ const trimmed = payload.trim();
7844
+ const space = trimmed.indexOf(" ");
7845
+ if (space <= 0) {
7846
+ return void 0;
7847
+ }
7848
+ const tag = trimmed.slice(0, space).trim();
7849
+ const uri = trimmed.slice(space + 1).trim();
7850
+ if (!tag || !uri) {
7851
+ return void 0;
7852
+ }
7853
+ return { tag, uri };
7854
+ };
7855
+ var getExtensionSchema = (gedcom) => {
7856
+ const map = /* @__PURE__ */ new Map();
7857
+ const schma = gedcom.get("HEAD")?.get("SCHMA");
7858
+ if (!schma) {
7859
+ return map;
7860
+ }
7861
+ schma.get("TAG")?.toList()?.forEach((tagNode) => {
7862
+ const payload = tagNode?.toValue();
7863
+ if (typeof payload !== "string") {
7864
+ return;
7865
+ }
7866
+ const parsed = parseTagDefinition(payload);
7867
+ if (parsed) {
7868
+ map.set(parsed.tag.toUpperCase(), parsed.uri);
7869
+ }
7870
+ });
7871
+ return map;
7872
+ };
7873
+ var ensureSchemaNode = (gedcom) => {
7874
+ const head = gedcom.get("HEAD");
7875
+ if (!head) {
7876
+ return void 0;
7877
+ }
7878
+ let schma = head.get("SCHMA");
7879
+ if (!schma) {
7880
+ schma = createCommon(gedcom, void 0, head);
7881
+ head.set("SCHMA", schma);
7882
+ }
7883
+ return schma;
7884
+ };
7885
+ var registerExtensionTag = (gedcom, tag, uri) => {
7886
+ const normalizedTag = tag.trim().toUpperCase();
7887
+ const normalizedUri = uri.trim();
7888
+ if (!normalizedTag.startsWith("_") || !normalizedUri) {
7889
+ return void 0;
7890
+ }
7891
+ const schma = ensureSchemaNode(gedcom);
7892
+ if (!schma) {
7893
+ return void 0;
7894
+ }
7895
+ const payload = `${normalizedTag} ${normalizedUri}`;
7896
+ const existingList = schma.get("TAG")?.toList();
7897
+ const existing = existingList?.values().find((node) => {
7898
+ const value = node?.toValue();
7899
+ return typeof value === "string" && value.trim().toUpperCase().startsWith(`${normalizedTag} `);
7900
+ });
7901
+ if (existing) {
7902
+ existing.value = payload;
7903
+ return existing;
7904
+ }
7905
+ const tagNode = createCommon(gedcom, void 0, schma);
7906
+ tagNode.value = payload;
7907
+ schma.assign("TAG", tagNode);
7908
+ return tagNode;
7909
+ };
7910
+ var ensureExtensionSchema = (gedcom, extraTags) => {
7911
+ const head = gedcom.get("HEAD");
7912
+ const hadSchma = !!head?.get("SCHMA");
7913
+ const previousPayloads = head?.get("SCHMA")?.get("TAG")?.toList()?.values().map((node) => ({
7914
+ node,
7915
+ value: node?.toValue()
7916
+ })) ?? [];
7917
+ const tags = /* @__PURE__ */ new Set([
7918
+ ...Object.keys(TREEVIZ_EXTENSION_URIS),
7919
+ ...(extraTags ?? []).map((tag) => tag.toUpperCase())
7920
+ ]);
7921
+ tags.forEach((tag) => {
7922
+ const uri = TREEVIZ_EXTENSION_URIS[tag] ?? `${TREEVIZ_EXTENSION_BASE_URI}#${tag}`;
7923
+ if (tag.startsWith("_")) {
7924
+ registerExtensionTag(gedcom, tag, uri);
7925
+ }
7926
+ });
7927
+ return () => {
7928
+ if (!head) {
7929
+ return;
7930
+ }
7931
+ if (!hadSchma) {
7932
+ head.remove("SCHMA");
7933
+ return;
7934
+ }
7935
+ const schma = head.get("SCHMA");
7936
+ const list = schma?.get("TAG")?.toList();
7937
+ const keep = new Set(
7938
+ previousPayloads.map(
7939
+ (item) => typeof item.value === "string" ? item.value : void 0
7940
+ ).filter((value) => !!value)
7941
+ );
7942
+ list?.values().forEach((node) => {
7943
+ const value = node?.toValue();
7944
+ if (typeof value === "string" && !keep.has(value) && node) {
7945
+ list.delete(node);
7946
+ }
7947
+ });
7948
+ previousPayloads.forEach(({ node, value }) => {
7949
+ if (node && value !== void 0) {
7950
+ node.value = value;
7951
+ }
7952
+ });
7953
+ };
7954
+ };
7955
+
7956
+ // package.json
7957
+ var package_default = {
7958
+ version: "2.1.1"};
7959
+
7960
+ // src/utils/get-product-details.ts
7961
+ var getVersion = () => package_default.version;
7962
+
7963
+ // src/utils/multimedia.ts
7964
+ var inferMediaForm = (file, fallback = "bin") => {
7965
+ const withoutQuery = file.split("?")[0] || file;
7966
+ const ext = getFileExtension(withoutQuery);
7967
+ if (ext) {
7968
+ return ext.toLowerCase() === "jpeg" ? "jpg" : ext.toLowerCase();
7969
+ }
7970
+ return fallback;
7971
+ };
7972
+ var mergeMediaLists = (...lists) => {
7973
+ const merged = {};
7974
+ for (const list of lists) {
7975
+ if (!list) continue;
7976
+ Object.assign(merged, list);
7977
+ }
7978
+ return merged;
7979
+ };
7980
+
7981
+ // src/classes/note.ts
7982
+ var CommonNote = class extends Common {
7983
+ constructor(gedcom, id, main, parent) {
7984
+ super(gedcom, id, main, parent);
7985
+ if (!id) {
7986
+ delete this.id;
7987
+ }
7988
+ }
7989
+ set value(value) {
7990
+ const [note, ...contents] = value?.split(/\r?\n/) ?? [];
7991
+ this._value = note;
7992
+ if (contents.length) {
7993
+ const newContents = new List();
7994
+ contents.forEach((c, i) => {
7995
+ const newContent = createCommon(
7996
+ this._gedcom,
7997
+ void 0,
7998
+ this.main
7999
+ );
8000
+ newContent.id = `@U${i}@`;
8001
+ newContent.value = c;
8002
+ newContents.append(newContent);
8003
+ });
8004
+ const firstItem = newContents.index(0);
8005
+ if (firstItem) {
8006
+ if (newContents.length === 1) {
8007
+ this.set("CONT", firstItem);
8008
+ } else {
8009
+ this.set("CONT", newContents);
8010
+ }
8011
+ }
8012
+ }
8013
+ }
8014
+ get value() {
8015
+ const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8016
+ if (contents) {
8017
+ return `${this._value}
8018
+ ${contents}`;
8019
+ }
8020
+ return this._value;
8021
+ }
8022
+ exportValue() {
8023
+ return this._value;
8024
+ }
8025
+ };
8026
+ var createCommonNote = (gedcom, id, main, parent) => {
8027
+ return createProxy(
8028
+ new CommonNote(gedcom, id, main, parent)
8029
+ );
8030
+ };
8031
+
8032
+ // src/classes/obje.ts
8033
+ var Obje = class extends Common {
8034
+ standardizeMedia(namespace, override = true, urlGetter, gedcomVersion) {
8035
+ if (!this._gedcom) {
8036
+ return this;
8037
+ }
8038
+ const rin = this?.get("RIN")?.toValue();
8039
+ const clone = this?.get("_CLON._OID")?.toValue();
8040
+ const mser = this?.get("_MSER._LKID")?.toValue();
8041
+ const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8042
+ const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8043
+ const formRaw = this?.get("FILE.FORM")?.toValue() ?? this?.get("FORM")?.toValue();
8044
+ const mediType = this?.get("FILE.FORM.TYPE")?.toValue() ?? this?.get("FORM.TYPE")?.toValue() ?? this?.get("MEDI")?.toValue();
8045
+ const file = this?.get("FILE")?.toValue();
8046
+ const imgId = rin || clone || mser;
8047
+ const url = file || namespace && urlGetter?.(namespace, imgId);
8048
+ if (!url) {
8049
+ return this;
8050
+ }
8051
+ const version = normalizeGedcomVersion(
8052
+ gedcomVersion ?? this._gedcom.getGedcomVersion?.()
8053
+ );
8054
+ const form = formRaw || inferMediaForm(String(url));
8055
+ const newObject = createObje(this._gedcom, this.id, this.main);
8056
+ if (!override) {
8057
+ Object.assign(newObject, this);
8058
+ }
8059
+ const fileNode = createCommon(this._gedcom, void 0, this.main);
8060
+ fileNode.value = url;
8061
+ if (version === "7.0") {
8062
+ const formNode = createCommon(this._gedcom, void 0, fileNode);
8063
+ formNode.value = form;
8064
+ if (mediType) {
8065
+ formNode.set("TYPE", String(mediType));
8066
+ }
8067
+ fileNode.set("FORM", formNode);
8068
+ if (title) {
8069
+ fileNode.set("TITL", String(title));
8070
+ }
8071
+ if (note) {
8072
+ fileNode.set("NOTE", String(note));
8073
+ }
8074
+ newObject.set("FILE", fileNode);
8075
+ } else {
8076
+ newObject.set("FILE", fileNode);
8077
+ newObject.set("FORM", form);
8078
+ if (mediType) {
8079
+ newObject.set("MEDI", String(mediType));
8080
+ }
8081
+ if (title) {
8082
+ newObject.set("TITL", String(title));
8083
+ if (!note) {
8084
+ newObject.set("NOTE", String(title));
8085
+ }
8086
+ }
8087
+ if (note) {
8088
+ newObject.set("NOTE", String(note));
8089
+ if (!title) {
8090
+ newObject.set("TITL", String(note));
8091
+ }
8092
+ }
8093
+ }
8094
+ const prim = this.get("_PRIM")?.toValue();
8095
+ if (prim === "Y" || prim === "N") {
8096
+ newObject.set("_PRIM", prim);
8097
+ }
8098
+ const photoRin = this.get("_PHOTO_RIN")?.toValue();
8099
+ if (rin) {
8100
+ newObject.set("RIN", String(rin));
8101
+ }
8102
+ if (photoRin) {
8103
+ newObject.set("_PHOTO_RIN", String(photoRin));
8104
+ }
8105
+ const clonNode = this.get("_CLON");
8106
+ if (clonNode) {
8107
+ newObject.set("_CLON", clonNode);
8108
+ }
8109
+ const mserNode = this.get("_MSER");
8110
+ if (mserNode) {
8111
+ newObject.set("_MSER", mserNode);
8112
+ }
8113
+ const oidNode = this.get("_OID");
8114
+ if (oidNode && !newObject.get("_OID")) {
8115
+ newObject.set("_OID", oidNode);
8116
+ }
8117
+ const lkidNode = this.get("_LKID");
8118
+ if (lkidNode && !newObject.get("_LKID")) {
8119
+ newObject.set("_LKID", lkidNode);
8120
+ }
8121
+ if (override) {
8122
+ this.remove("FILE");
8123
+ this.remove("FORM");
8124
+ this.remove("MEDI");
8125
+ this.remove("TITL");
8126
+ this.remove("NOTE");
8127
+ Object.assign(this, newObject);
8128
+ return this;
8129
+ }
8130
+ return newObject;
8131
+ }
8132
+ };
8133
+ var createObje = (gedcom, id, main, parent) => {
8134
+ return createProxy(
8135
+ new Obje(gedcom, id, main, parent)
8136
+ );
8137
+ };
8138
+
8139
+ // src/classes/objes.ts
8140
+ var Objects = class _Objects extends List {
8141
+ copy() {
8142
+ const newList = new _Objects();
8143
+ this.entries().forEach(([key, value]) => {
8144
+ newList.item(key, value);
8145
+ });
8146
+ return newList;
8147
+ }
8148
+ except(item) {
8149
+ return this.copy().delete(item);
8150
+ }
8151
+ };
8152
+
8153
+ // src/classes/gedcom.ts
8154
+ var isObjectPatch = (value) => {
8155
+ return !!value && typeof value === "object" && !Array.isArray(value);
8156
+ };
8157
+ var GedCom = class extends Common {
8158
+ constructor() {
8159
+ super();
8160
+ this.tagMembers = {};
8161
+ this.reflist = {};
8162
+ this.refcount = 0;
8163
+ delete this._gedcom;
8164
+ delete this.id;
8165
+ this.removeValue();
8166
+ }
8167
+ getMain(type, index) {
8168
+ const list = !type || type instanceof List ? type : this.getList(type);
8169
+ if (!list) {
8170
+ return void 0;
8171
+ }
8172
+ if (typeof index === "string") {
8173
+ return list.item(index);
8174
+ }
8175
+ const keys = list.keys();
8176
+ return list.item(keys[index]);
8177
+ }
8178
+ getList(type) {
8179
+ return this.get(type);
8180
+ }
8181
+ createNodeFromPatchValue(value, main) {
8182
+ if (value === void 0 || value === null) {
8183
+ return void 0;
8184
+ }
8185
+ const node = createCommon(this, void 0, main);
8186
+ if (Array.isArray(value)) {
8187
+ value.forEach((item) => {
8188
+ const childNode = this.createNodeFromPatchValue(item, node);
8189
+ if (childNode) {
8190
+ node.assign("DATA", childNode);
8191
+ }
8192
+ });
8193
+ return node;
8194
+ }
8195
+ if (isObjectPatch(value)) {
8196
+ this.applyObjectToCommon(node, value);
8197
+ return node;
8198
+ }
8199
+ node.value = String(value);
8200
+ return node;
8201
+ }
8202
+ applyObjectToCommon(target, patch) {
8203
+ Object.entries(patch).forEach(([rawTag, value]) => {
8204
+ if (rawTag === "value") {
8205
+ if (value === null || value === void 0 || Array.isArray(value) || isObjectPatch(value)) {
8206
+ return;
8207
+ }
8208
+ target.value = String(value);
8209
+ return;
8210
+ }
8211
+ if (rawTag === "id") {
8212
+ if (typeof value === "string") {
8213
+ target.id = value;
8214
+ }
8215
+ return;
8216
+ }
8217
+ const tag = rawTag;
8218
+ if (value === void 0) {
8219
+ return;
8220
+ }
8221
+ if (value === null) {
8222
+ target.remove(tag);
8223
+ return;
8224
+ }
8225
+ if (Array.isArray(value)) {
8226
+ target.remove(tag);
8227
+ value.forEach((item) => {
8228
+ const listNode = this.createNodeFromPatchValue(
8229
+ item,
8230
+ target.main ?? this
8231
+ );
8232
+ if (listNode) {
8233
+ target.assign(tag, listNode);
8234
+ }
8235
+ });
8236
+ return;
8237
+ }
8238
+ if (isObjectPatch(value)) {
8239
+ const existing = target.get(tag);
8240
+ const objectNode = existing ?? createCommon(this, void 0, target.main ?? this);
8241
+ this.applyObjectToCommon(objectNode, value);
8242
+ target.set(tag, objectNode);
8243
+ return;
7257
8244
  }
7258
8245
  target.set(tag, String(value));
7259
8246
  });
@@ -7381,6 +8368,9 @@ var GedCom = class extends Common {
7381
8368
  const id = this.nextIndiKey();
7382
8369
  const indi = createIndi(this, id);
7383
8370
  indi.type = "INDI";
8371
+ if (this.getGedcomVersion() === "7.0") {
8372
+ indi.set("UID", createGedcomUid());
8373
+ }
7384
8374
  indis.item(id, indi);
7385
8375
  return indi;
7386
8376
  }
@@ -7393,9 +8383,151 @@ var GedCom = class extends Common {
7393
8383
  const id = this.nextFamKey();
7394
8384
  const fam = createFam(this, id);
7395
8385
  fam.type = "FAM";
8386
+ if (this.getGedcomVersion() === "7.0") {
8387
+ fam.set("UID", createGedcomUid());
8388
+ }
7396
8389
  fams.item(id, fam);
7397
8390
  return fam;
7398
8391
  }
8392
+ nextObjeKey() {
8393
+ const ids = [];
8394
+ this.objes()?.forEach((_obje, id) => {
8395
+ ids.push(String(id));
8396
+ });
8397
+ return nextRecordId(ids, "O");
8398
+ }
8399
+ /**
8400
+ * Create a top-level multimedia record (`0 @On@ OBJE`) with FILE/FORM/TITL.
8401
+ * GEDCOM 7 uses nested `FILE → FORM [→ TYPE]`; 5.5.1 uses sibling FORM/TITL.
8402
+ */
8403
+ createMultimediaRecord(input) {
8404
+ const existing = this.objes();
8405
+ const objes = existing ?? new Objects();
8406
+ if (!existing) {
8407
+ this.set("@@OBJE", objes);
8408
+ }
8409
+ const id = this.nextObjeKey();
8410
+ const obje = createObje(this, id);
8411
+ obje.type = "OBJE";
8412
+ objes.item(id, obje);
8413
+ const version = normalizeGedcomVersion(
8414
+ input.gedcomVersion ?? this.getGedcomVersion()
8415
+ );
8416
+ const form = input.form || inferMediaForm(input.file);
8417
+ const fileNode = createCommon(this, void 0, obje);
8418
+ fileNode.value = input.file;
8419
+ if (version === "7.0") {
8420
+ const formNode = createCommon(
8421
+ this,
8422
+ void 0,
8423
+ fileNode
8424
+ );
8425
+ formNode.value = form;
8426
+ if (input.mediType) {
8427
+ formNode.set("TYPE", input.mediType);
8428
+ }
8429
+ fileNode.set("FORM", formNode);
8430
+ if (input.title) {
8431
+ fileNode.set("TITL", input.title);
8432
+ }
8433
+ obje.set("FILE", fileNode);
8434
+ } else {
8435
+ obje.set("FILE", fileNode);
8436
+ obje.set("FORM", form);
8437
+ if (input.mediType) {
8438
+ obje.set("MEDI", input.mediType);
8439
+ }
8440
+ if (input.title) {
8441
+ obje.set("TITL", input.title);
8442
+ }
8443
+ }
8444
+ if (input.primary) {
8445
+ obje.set("_PRIM", "Y");
8446
+ }
8447
+ if (normalizeGedcomVersion(
8448
+ input.gedcomVersion ?? this.getGedcomVersion()
8449
+ ) === "7.0") {
8450
+ obje.set("UID", createGedcomUid());
8451
+ }
8452
+ return obje;
8453
+ }
8454
+ nextSnoteKey() {
8455
+ const ids = [];
8456
+ this.snotes()?.forEach((_note, id) => {
8457
+ ids.push(String(id));
8458
+ });
8459
+ return nextRecordId(ids, "N");
8460
+ }
8461
+ snotes() {
8462
+ return this.getList("@@SNOTE");
8463
+ }
8464
+ snote(index) {
8465
+ return this.getMain(this.snotes(), index);
8466
+ }
8467
+ /**
8468
+ * Create a top-level shared note record (`0 @Nn@ SNOTE` for GEDCOM 7).
8469
+ */
8470
+ createSharedNote(text) {
8471
+ const existing = this.snotes();
8472
+ const notes = existing ?? new List();
8473
+ if (!existing) {
8474
+ this.set("@@SNOTE", notes);
8475
+ }
8476
+ const id = this.nextSnoteKey();
8477
+ const note = createCommonNote(this, id);
8478
+ note.type = "SNOTE";
8479
+ note.value = text;
8480
+ if (this.getGedcomVersion() === "7.0") {
8481
+ note.set("UID", createGedcomUid());
8482
+ }
8483
+ notes.item(id, note);
8484
+ return note;
8485
+ }
8486
+ registerExtensionTag(tag, uri) {
8487
+ return registerExtensionTag(this, tag, uri);
8488
+ }
8489
+ getExtensionSchema() {
8490
+ return getExtensionSchema(this);
8491
+ }
8492
+ ensureExtensionSchema(extraTags) {
8493
+ ensureExtensionSchema(this, extraTags);
8494
+ return getExtensionSchema(this);
8495
+ }
8496
+ /**
8497
+ * Collect multimedia descriptors via each individual's `multimedia()`.
8498
+ */
8499
+ async collectMultimedia(options) {
8500
+ const people = [];
8501
+ if (options?.indiIds?.length) {
8502
+ options.indiIds.forEach((id) => {
8503
+ const indi = this.indi(id);
8504
+ if (indi) {
8505
+ people.push(indi);
8506
+ }
8507
+ });
8508
+ } else {
8509
+ this.indis()?.forEach((indi) => {
8510
+ if (indi) {
8511
+ people.push(indi);
8512
+ }
8513
+ });
8514
+ }
8515
+ const lists = await Promise.all(
8516
+ people.map((indi) => indi.multimedia(options?.namespace))
8517
+ );
8518
+ return mergeMediaLists(...lists);
8519
+ }
8520
+ /**
8521
+ * Serialize as GEDCOM 7 and pack a FamilySearch GEDZIP (`.gdz`).
8522
+ * Media bytes must be supplied by the caller (cache/fetch outside the parser).
8523
+ */
8524
+ async toGedzip(options) {
8525
+ const text = this.toGedcom(void 0, 0, {
8526
+ ...options,
8527
+ gedcomVersion: "7.0"
8528
+ });
8529
+ return buildGedzip(text, options?.media ?? []);
8530
+ }
7399
8531
  objes() {
7400
8532
  return this.getList("@@OBJE");
7401
8533
  }
@@ -7480,9 +8612,52 @@ var GedCom = class extends Common {
7480
8612
  });
7481
8613
  return lists;
7482
8614
  }
7483
- getDownloadHeader() {
8615
+ getGedcomVersion() {
8616
+ const raw = this.get("HEAD")?.get("GEDC")?.get("VERS")?.toValue();
8617
+ return normalizeGedcomVersion(
8618
+ typeof raw === "string" ? raw : void 0
8619
+ );
8620
+ }
8621
+ /** Rewrite HEAD.GEDC (and CHAR/FORM) for the requested export version. */
8622
+ applyExportVersion(version) {
8623
+ const head = this.get("HEAD");
8624
+ if (!head) {
8625
+ return;
8626
+ }
8627
+ this.applyGedcomSpecToHead(head, normalizeGedcomVersion(version));
8628
+ }
8629
+ applyGedcomSpecToHead(head, version) {
8630
+ const previousGedc = head.get("GEDC");
8631
+ const previousForm = previousGedc?.get("FORM")?.toValue();
8632
+ const previousChar = head.get("CHAR")?.toValue();
8633
+ head.remove("GEDC");
8634
+ const gedc = head.set("GEDC", "");
8635
+ if (!gedc) {
8636
+ return;
8637
+ }
8638
+ gedc.set("VERS", version);
8639
+ if (version === "7.0") {
8640
+ head.remove("CHAR");
8641
+ return;
8642
+ }
8643
+ gedc.set(
8644
+ "FORM",
8645
+ typeof previousForm === "string" && previousForm ? previousForm : "LINEAGE-LINKED"
8646
+ );
8647
+ if (!head.get("CHAR")?.toValue()) {
8648
+ head.set(
8649
+ "CHAR",
8650
+ typeof previousChar === "string" && previousChar ? previousChar : "UTF-8"
8651
+ );
8652
+ }
8653
+ }
8654
+ getDownloadHeader(gedcomVersion) {
7484
8655
  const newHead = createCommon();
7485
8656
  Object.assign(newHead, this.get("HEAD") ?? {});
8657
+ this.applyGedcomSpecToHead(
8658
+ newHead,
8659
+ gedcomVersion ?? this.getGedcomVersion()
8660
+ );
7486
8661
  const existingSour = this.get("HEAD")?.get("SOUR");
7487
8662
  const existingTreeName = existingSour?.get("_TREE")?.toValue();
7488
8663
  const existingTreeUniqueId = existingSour?.get("_TID")?.toValue();
@@ -7529,22 +8704,45 @@ var GedCom = class extends Common {
7529
8704
  }
7530
8705
  toGedcom(tag, level, options) {
7531
8706
  if (options?.super) {
7532
- return appendGedcomTrailer(
7533
- super.toGedcom(tag, level, options)
7534
- );
8707
+ return appendGedcomTrailer(super.toGedcom(tag, level, options));
7535
8708
  }
8709
+ const requestedVersion = options?.gedcomVersion;
8710
+ const exportVersion = requestedVersion ? normalizeGedcomVersion(requestedVersion) : this.getGedcomVersion();
8711
+ const exportOptions = {
8712
+ ...options,
8713
+ obje: {
8714
+ standardize: options?.obje?.standardize ?? (requestedVersion !== void 0 || !options?.original),
8715
+ override: options?.obje?.override,
8716
+ namespace: options?.obje?.namespace
8717
+ }
8718
+ };
7536
8719
  const newGedcom = createGedCom();
7537
8720
  Object.assign(newGedcom, this);
7538
- if (!options?.original) {
8721
+ if (!exportOptions.original) {
7539
8722
  Object.assign(newGedcom, {
7540
- HEAD: this.getDownloadHeader()
8723
+ HEAD: this.getDownloadHeader(exportVersion)
7541
8724
  });
7542
8725
  }
7543
- if (options?.indis?.length) {
7544
- const newContent = this.getIndiRelatedLists(options.indis);
8726
+ if (exportOptions.indis?.length) {
8727
+ const newContent = this.getIndiRelatedLists(exportOptions.indis);
7545
8728
  Object.assign(newGedcom, newContent);
7546
8729
  }
7547
- return newGedcom.toGedcom(tag, level, { ...options, super: true });
8730
+ const restoreEnums = exportVersion === "7.0" ? applyGedcom7Enumerations(newGedcom) : void 0;
8731
+ const restoreDates = exportVersion === "7.0" ? applyGedcom7DatePhrases(newGedcom) : void 0;
8732
+ const restoreMetadata = exportVersion === "7.0" ? applyGedcom7RecordMetadata(newGedcom) : void 0;
8733
+ const restoreSchema = exportVersion === "7.0" ? ensureExtensionSchema(newGedcom) : void 0;
8734
+ try {
8735
+ return newGedcom.toGedcom(tag, level, {
8736
+ ...exportOptions,
8737
+ gedcomVersion: exportVersion,
8738
+ super: true
8739
+ });
8740
+ } finally {
8741
+ restoreSchema?.();
8742
+ restoreMetadata?.();
8743
+ restoreDates?.();
8744
+ restoreEnums?.();
8745
+ }
7548
8746
  }
7549
8747
  hasTag(tag) {
7550
8748
  const tagName = typeof tag === "string" ? tag : tag?.get("NAME")?.toValue();
@@ -8023,158 +9221,6 @@ var mergeGedcoms = (targetGedcom, sourceGedcom, strategy = "id") => {
8023
9221
  return mergedGedcom;
8024
9222
  };
8025
9223
 
8026
- // src/classes/note.ts
8027
- var CommonNote = class extends Common {
8028
- constructor(gedcom, id, main, parent) {
8029
- super(gedcom, id, main, parent);
8030
- delete this.id;
8031
- }
8032
- set value(value) {
8033
- const [note, ...contents] = value?.split(/\r?\n/) ?? [];
8034
- this._value = note;
8035
- if (contents.length) {
8036
- const newContents = new List();
8037
- contents.forEach((c, i) => {
8038
- const newContent = createCommon(
8039
- this._gedcom,
8040
- void 0,
8041
- this.main
8042
- );
8043
- newContent.id = `@U${i}@`;
8044
- newContent.value = c;
8045
- newContents.append(newContent);
8046
- });
8047
- const firstItem = newContents.index(0);
8048
- if (firstItem) {
8049
- if (newContents.length === 1) {
8050
- this.set("CONT", firstItem);
8051
- } else {
8052
- this.set("CONT", newContents);
8053
- }
8054
- }
8055
- }
8056
- }
8057
- get value() {
8058
- const contents = this.get("CONT")?.toList().map((content) => content.value).join("\n");
8059
- if (contents) {
8060
- return `${this._value}
8061
- ${contents}`;
8062
- }
8063
- return this._value;
8064
- }
8065
- exportValue() {
8066
- return this._value;
8067
- }
8068
- };
8069
- var createCommonNote = (gedcom, id, main, parent) => {
8070
- return createProxy(
8071
- new CommonNote(gedcom, id, main, parent)
8072
- );
8073
- };
8074
-
8075
- // src/classes/obje.ts
8076
- var Obje = class extends Common {
8077
- standardizeMedia(namespace, override = true, urlGetter) {
8078
- if (!this._gedcom) {
8079
- return this;
8080
- }
8081
- const rin = this?.get("RIN")?.toValue();
8082
- const clone = this?.get("_CLON._OID")?.toValue();
8083
- const mser = this?.get("_MSER._LKID")?.toValue();
8084
- const title = this?.get("FILE.TITL")?.toValue() ?? this?.get("TITL")?.toValue() ?? "";
8085
- const note = this?.get("FILE.NOTE")?.toValue() ?? this?.get("NOTE")?.toValue() ?? "";
8086
- const form = this?.get("FILE.FORM")?.toValue();
8087
- const file = this?.get("FILE")?.toValue();
8088
- const imgId = rin || clone || mser;
8089
- const url = file || namespace && urlGetter?.(namespace, imgId);
8090
- if (!url) {
8091
- return this;
8092
- }
8093
- const newObject = createObje(this._gedcom, this.id, this.main);
8094
- if (!override) {
8095
- Object.assign(newObject, this);
8096
- }
8097
- if (form) {
8098
- if (typeof form === "string") {
8099
- const newForm = createCommon(
8100
- this._gedcom,
8101
- void 0,
8102
- this.main
8103
- );
8104
- newForm.value = form;
8105
- newObject.set("FORM", newForm);
8106
- } else {
8107
- newObject.set("FORM", form);
8108
- }
8109
- }
8110
- if (title) {
8111
- if (typeof title === "string") {
8112
- const newTitle = createCommon(
8113
- this._gedcom,
8114
- void 0,
8115
- this.main
8116
- );
8117
- newTitle.value = title;
8118
- newObject.set("TITL", newTitle);
8119
- if (!note) {
8120
- newObject.set("NOTE", newTitle);
8121
- }
8122
- } else {
8123
- newObject.set("TITL", title);
8124
- if (!note) {
8125
- newObject.set("NOTE", title);
8126
- }
8127
- }
8128
- }
8129
- if (note) {
8130
- if (typeof note === "string") {
8131
- const newNote = createCommon(
8132
- this._gedcom,
8133
- void 0,
8134
- this.main
8135
- );
8136
- newNote.value = note;
8137
- newObject.set("NOTE", newNote);
8138
- if (!title) {
8139
- newObject.set("TITL", newNote);
8140
- }
8141
- } else {
8142
- newObject.set("NOTE", note);
8143
- if (!title) {
8144
- newObject.set("TITL", note);
8145
- }
8146
- }
8147
- }
8148
- const newUrl = createCommon(this._gedcom, void 0, this.main);
8149
- newUrl.value = url;
8150
- newObject.set("FILE", newUrl);
8151
- if (override) {
8152
- Object.assign(this, newObject);
8153
- return this;
8154
- }
8155
- return newObject;
8156
- }
8157
- };
8158
- var createObje = (gedcom, id, main, parent) => {
8159
- return createProxy(
8160
- new Obje(gedcom, id, main, parent)
8161
- );
8162
- };
8163
-
8164
- // src/classes/objes.ts
8165
- var Objects = class _Objects extends List {
8166
- copy() {
8167
- const newList = new _Objects();
8168
- this.entries().forEach(([key, value]) => {
8169
- newList.item(key, value);
8170
- });
8171
- return newList;
8172
- }
8173
- except(item) {
8174
- return this.copy().delete(item);
8175
- }
8176
- };
8177
-
8178
9224
  // src/classes/repo.ts
8179
9225
  var Repo = class extends Common {
8180
9226
  };