@treeviz/familysearch-sdk 1.0.10

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.
@@ -0,0 +1,663 @@
1
+ 'use strict';
2
+
3
+ // src/utils/gedcom-converter.ts
4
+ function transformFamilySearchUrl(url) {
5
+ if (!url.includes("/platform/tree/persons/")) {
6
+ return url;
7
+ }
8
+ const personId = url.match(/persons\/([^/?]+)/)?.[1];
9
+ if (!personId) {
10
+ return url;
11
+ }
12
+ let baseUrl = "https://www.familysearch.org";
13
+ if (url.includes("integration.familysearch.org") || url.includes("api-integ.familysearch.org")) {
14
+ baseUrl = "https://integration.familysearch.org";
15
+ } else if (url.includes("beta.familysearch.org") || url.includes("apibeta.familysearch.org")) {
16
+ baseUrl = "https://beta.familysearch.org";
17
+ }
18
+ return `${baseUrl}/tree/person/${personId}`;
19
+ }
20
+ function transformSourceUrl(url) {
21
+ if (!url) return url;
22
+ if (!url.includes("/platform/") && url.includes("ark:")) {
23
+ return url;
24
+ }
25
+ const arkMatch = url.match(/ark:\/[^/?]+\/[^/?]+/);
26
+ if (!arkMatch) {
27
+ return url;
28
+ }
29
+ const arkId = arkMatch[0];
30
+ let baseUrl = "https://www.familysearch.org";
31
+ if (url.includes("integration.familysearch.org") || url.includes("api-integ.familysearch.org")) {
32
+ baseUrl = "https://integration.familysearch.org";
33
+ } else if (url.includes("beta.familysearch.org") || url.includes("apibeta.familysearch.org")) {
34
+ baseUrl = "https://beta.familysearch.org";
35
+ }
36
+ return `${baseUrl}/${arkId}`;
37
+ }
38
+ function extractName(person) {
39
+ if (!person) return null;
40
+ if (person.names?.[0]?.nameForms?.[0]) {
41
+ const nameForm = person.names[0].nameForms[0];
42
+ const parts = nameForm.parts || [];
43
+ const given = parts.find((p) => p.type?.includes("Given"))?.value || "";
44
+ const surname = parts.find((p) => p.type?.includes("Surname"))?.value || "";
45
+ if (given || surname) {
46
+ return `${given} /${surname}/`.trim();
47
+ }
48
+ if (nameForm.fullText) {
49
+ return nameForm.fullText;
50
+ }
51
+ }
52
+ if (person.display?.name) {
53
+ return person.display.name;
54
+ }
55
+ return null;
56
+ }
57
+ function extractGender(person) {
58
+ if (!person) return null;
59
+ if (person.gender?.type?.includes("Male")) {
60
+ return "M";
61
+ } else if (person.gender?.type?.includes("Female")) {
62
+ return "F";
63
+ } else if (person.display?.gender === "Male") {
64
+ return "M";
65
+ } else if (person.display?.gender === "Female") {
66
+ return "F";
67
+ }
68
+ return null;
69
+ }
70
+ function extractFact(person, factType) {
71
+ if (!person) return null;
72
+ const result = {};
73
+ const factTypeUrl = `http://gedcomx.org/${factType === "BIRTH" ? "Birth" : "Death"}`;
74
+ const fact = person.facts?.find((f) => f.type === factTypeUrl);
75
+ if (fact) {
76
+ if (fact.date?.formal || fact.date?.original) {
77
+ result.date = (fact.date?.formal || fact.date?.original)?.replace(
78
+ /^(-|\+)/,
79
+ ""
80
+ );
81
+ }
82
+ if (fact.place?.original) {
83
+ result.place = fact.place.original;
84
+ }
85
+ }
86
+ if (factType === "BIRTH") {
87
+ result.date = result.date || person.display?.birthDate;
88
+ result.place = result.place || person.display?.birthPlace;
89
+ } else if (factType === "DEATH") {
90
+ result.date = result.date || person.display?.deathDate;
91
+ result.place = result.place || person.display?.deathPlace;
92
+ }
93
+ return result.date || result.place ? result : null;
94
+ }
95
+ function formatDateForGedcom(date) {
96
+ const months = [
97
+ "JAN",
98
+ "FEB",
99
+ "MAR",
100
+ "APR",
101
+ "MAY",
102
+ "JUN",
103
+ "JUL",
104
+ "AUG",
105
+ "SEP",
106
+ "OCT",
107
+ "NOV",
108
+ "DEC"
109
+ ];
110
+ const day = date.getDate();
111
+ const month = months[date.getMonth()];
112
+ const year = date.getFullYear();
113
+ return `${day} ${month} ${year}`;
114
+ }
115
+ function convertFactToGedcom(fact) {
116
+ const lines = [];
117
+ if (!fact.type) return lines;
118
+ const typeMap = {
119
+ "http://gedcomx.org/Burial": "BURI",
120
+ "http://gedcomx.org/Christening": "CHR",
121
+ "http://gedcomx.org/Baptism": "BAPM",
122
+ "http://gedcomx.org/Marriage": "MARR",
123
+ "http://gedcomx.org/Divorce": "DIV",
124
+ "http://gedcomx.org/Residence": "RESI",
125
+ "http://gedcomx.org/Occupation": "OCCU",
126
+ "http://gedcomx.org/Immigration": "IMMI",
127
+ "http://gedcomx.org/Emigration": "EMIG",
128
+ "http://gedcomx.org/Naturalization": "NATU",
129
+ "http://gedcomx.org/Census": "CENS"
130
+ };
131
+ const gedcomTag = typeMap[fact.type] || "EVEN";
132
+ if (gedcomTag === "OCCU" && fact.value) {
133
+ lines.push(`1 OCCU ${fact.value}`);
134
+ } else {
135
+ lines.push(`1 ${gedcomTag}`);
136
+ }
137
+ if (fact.links?.conclusion?.href) {
138
+ const webUrl = transformFamilySearchUrl(fact.links.conclusion.href);
139
+ lines.push(`2 _FS_LINK ${webUrl}`);
140
+ }
141
+ if (fact.date?.formal || fact.date?.original) {
142
+ lines.push(
143
+ `2 DATE ${(fact.date?.formal || fact.date?.original)?.replace(/^(-|\+)/, "")}`
144
+ );
145
+ }
146
+ if (fact.place?.original) {
147
+ lines.push(`2 PLAC ${fact.place.original}`);
148
+ }
149
+ if (fact.value && gedcomTag !== "OCCU") {
150
+ lines.push(`2 NOTE ${fact.value}`);
151
+ }
152
+ return lines;
153
+ }
154
+ function convertToGedcom(pedigreeData, options = {}) {
155
+ const {
156
+ treeName = "FamilySearch Import",
157
+ includeLinks = true,
158
+ includeNotes = true,
159
+ environment = "production"
160
+ } = options;
161
+ if (!pedigreeData || !pedigreeData.persons) {
162
+ throw new Error("Invalid FamilySearch data: no persons found");
163
+ }
164
+ const lines = [];
165
+ lines.push("0 HEAD");
166
+ lines.push("1 SOUR FamilySearch");
167
+ lines.push("2 VERS 1.0");
168
+ lines.push("2 NAME FamilySearch API");
169
+ lines.push("1 DEST ANY");
170
+ lines.push("1 DATE " + formatDateForGedcom(/* @__PURE__ */ new Date()));
171
+ lines.push("1 SUBM @SUBM1@");
172
+ lines.push("1 FILE " + treeName);
173
+ lines.push("1 GEDC");
174
+ lines.push("2 VERS 5.5");
175
+ lines.push("2 FORM LINEAGE-LINKED");
176
+ lines.push("1 CHAR UTF-8");
177
+ lines.push("0 @SUBM1@ SUBM");
178
+ const sourceMap = /* @__PURE__ */ new Map();
179
+ let sourceCounter = 1;
180
+ pedigreeData.persons.forEach((person) => {
181
+ const sourceDescriptions = person.fullDetails?.sourceDescriptions;
182
+ if (sourceDescriptions && Array.isArray(sourceDescriptions)) {
183
+ sourceDescriptions.forEach((source) => {
184
+ if (source.id && !sourceMap.has(source.id)) {
185
+ sourceMap.set(source.id, source);
186
+ }
187
+ });
188
+ }
189
+ });
190
+ sourceMap.forEach((source) => {
191
+ const sourceId = `@S${sourceCounter++}@`;
192
+ lines.push(`0 ${sourceId} SOUR`);
193
+ const title = source.titles?.[0]?.value || "FamilySearch Source";
194
+ lines.push(`1 TITL ${title}`);
195
+ if (source.citations?.[0]?.value) {
196
+ lines.push(`1 TEXT ${source.citations[0].value}`);
197
+ }
198
+ if (source.about) {
199
+ const webUrl = transformSourceUrl(source.about);
200
+ lines.push(`1 WWW ${webUrl}`);
201
+ }
202
+ if (source.resourceType) {
203
+ lines.push(`1 NOTE Resource Type: ${source.resourceType}`);
204
+ }
205
+ });
206
+ const personIdMap = /* @__PURE__ */ new Map();
207
+ pedigreeData.persons.forEach((person, index) => {
208
+ const gedcomId = `@I${index + 1}@`;
209
+ personIdMap.set(person.id, gedcomId);
210
+ });
211
+ const families = /* @__PURE__ */ new Map();
212
+ const childToParents = /* @__PURE__ */ new Map();
213
+ const allRelationships = [
214
+ ...pedigreeData.relationships || []
215
+ ];
216
+ pedigreeData.persons.forEach((person) => {
217
+ const personRelationships = person.fullDetails?.relationships;
218
+ if (personRelationships && Array.isArray(personRelationships)) {
219
+ personRelationships.forEach((rel) => {
220
+ const exists = allRelationships.some((r) => r.id === rel.id);
221
+ if (!exists) {
222
+ allRelationships.push(rel);
223
+ }
224
+ });
225
+ }
226
+ const childAndParentsRels = person.fullDetails?.childAndParentsRelationships;
227
+ if (childAndParentsRels && Array.isArray(childAndParentsRels)) {
228
+ childAndParentsRels.forEach((capRel) => {
229
+ if (capRel.parent1 && capRel.child) {
230
+ const rel = {
231
+ id: `${capRel.id}-p1`,
232
+ type: "http://gedcomx.org/ParentChild",
233
+ person1: { resourceId: capRel.parent1.resourceId },
234
+ person2: { resourceId: capRel.child.resourceId },
235
+ parent2: capRel.parent2 ? { resourceId: capRel.parent2.resourceId } : void 0
236
+ };
237
+ const exists = allRelationships.some(
238
+ (r) => r.id === rel.id
239
+ );
240
+ if (!exists) {
241
+ allRelationships.push(rel);
242
+ }
243
+ }
244
+ if (capRel.parent2 && capRel.child) {
245
+ const rel = {
246
+ id: `${capRel.id}-p2`,
247
+ type: "http://gedcomx.org/ParentChild",
248
+ person1: { resourceId: capRel.parent2.resourceId },
249
+ person2: { resourceId: capRel.child.resourceId },
250
+ parent2: capRel.parent1 ? { resourceId: capRel.parent1.resourceId } : void 0
251
+ };
252
+ const exists = allRelationships.some(
253
+ (r) => r.id === rel.id
254
+ );
255
+ if (!exists) {
256
+ allRelationships.push(rel);
257
+ }
258
+ }
259
+ });
260
+ }
261
+ });
262
+ const validPersonIds = new Set(pedigreeData.persons.map((p) => p.id));
263
+ allRelationships.forEach((rel) => {
264
+ if (rel.type?.includes("ParentChild")) {
265
+ const parentId = rel.person1?.resourceId;
266
+ const childId = rel.person2?.resourceId;
267
+ const parent2Id = rel.parent2?.resourceId;
268
+ if (!parentId || !childId) {
269
+ return;
270
+ }
271
+ if (!validPersonIds.has(childId)) {
272
+ return;
273
+ }
274
+ const validParents = [];
275
+ if (validPersonIds.has(parentId)) {
276
+ validParents.push(parentId);
277
+ }
278
+ if (parent2Id && validPersonIds.has(parent2Id)) {
279
+ validParents.push(parent2Id);
280
+ }
281
+ if (validParents.length > 0) {
282
+ if (!childToParents.has(childId)) {
283
+ childToParents.set(childId, []);
284
+ }
285
+ const parents = childToParents.get(childId);
286
+ validParents.forEach((parentId2) => {
287
+ if (!parents.includes(parentId2)) {
288
+ parents.push(parentId2);
289
+ }
290
+ });
291
+ }
292
+ } else if (rel.type?.includes("Couple")) {
293
+ const person1 = rel.person1?.resourceId;
294
+ const person2 = rel.person2?.resourceId;
295
+ if (!person1 || !person2) {
296
+ return;
297
+ }
298
+ if (validPersonIds.has(person1) && validPersonIds.has(person2)) {
299
+ const famKey = [person1, person2].sort().join("-");
300
+ if (!families.has(famKey)) {
301
+ families.set(famKey, {
302
+ spouses: /* @__PURE__ */ new Set([person1, person2]),
303
+ children: []
304
+ });
305
+ }
306
+ }
307
+ }
308
+ });
309
+ childToParents.forEach((parents, childId) => {
310
+ if (parents.length >= 2) {
311
+ const famKey = parents.slice(0, 2).sort().join("-");
312
+ if (!families.has(famKey)) {
313
+ families.set(famKey, {
314
+ spouses: new Set(parents.slice(0, 2)),
315
+ children: []
316
+ });
317
+ }
318
+ families.get(famKey).children.push(childId);
319
+ } else if (parents.length === 1) {
320
+ const famKey = `single-${parents[0]}`;
321
+ if (!families.has(famKey)) {
322
+ families.set(famKey, {
323
+ spouses: /* @__PURE__ */ new Set([parents[0]]),
324
+ children: []
325
+ });
326
+ }
327
+ families.get(famKey).children.push(childId);
328
+ }
329
+ });
330
+ const connectablePersons = /* @__PURE__ */ new Set();
331
+ if (options.ancestryPersonIds && options.ancestryPersonIds.size > 0) {
332
+ options.ancestryPersonIds.forEach((personId) => {
333
+ connectablePersons.add(personId);
334
+ });
335
+ families.forEach((family) => {
336
+ const spouses = Array.from(family.spouses);
337
+ if (spouses.some((spouseId) => connectablePersons.has(spouseId))) {
338
+ spouses.forEach((spouseId) => connectablePersons.add(spouseId));
339
+ }
340
+ });
341
+ let addedNewPersons = true;
342
+ while (addedNewPersons) {
343
+ addedNewPersons = false;
344
+ const beforeSize = connectablePersons.size;
345
+ childToParents.forEach((parents, childId) => {
346
+ if (parents.some((parentId) => connectablePersons.has(parentId))) {
347
+ if (!connectablePersons.has(childId)) {
348
+ connectablePersons.add(childId);
349
+ addedNewPersons = true;
350
+ }
351
+ }
352
+ });
353
+ families.forEach((family) => {
354
+ const spouses = Array.from(family.spouses);
355
+ const hasConnectableSpouse = spouses.some(
356
+ (spouseId) => connectablePersons.has(spouseId)
357
+ );
358
+ const hasConnectableChild = family.children.some(
359
+ (childId) => connectablePersons.has(childId)
360
+ );
361
+ if (hasConnectableSpouse || hasConnectableChild) {
362
+ spouses.forEach((spouseId) => {
363
+ if (!connectablePersons.has(spouseId)) {
364
+ connectablePersons.add(spouseId);
365
+ addedNewPersons = true;
366
+ }
367
+ });
368
+ }
369
+ });
370
+ const afterSize = connectablePersons.size;
371
+ if (afterSize > beforeSize) {
372
+ addedNewPersons = afterSize !== beforeSize;
373
+ }
374
+ }
375
+ } else {
376
+ childToParents.forEach((parents, childId) => {
377
+ connectablePersons.add(childId);
378
+ parents.forEach((parentId) => connectablePersons.add(parentId));
379
+ });
380
+ }
381
+ const allowOrphanFamilies = options.allowOrphanFamilies === true;
382
+ const orphanFamKeys = /* @__PURE__ */ new Set();
383
+ const personToFamKeys = /* @__PURE__ */ new Map();
384
+ families.forEach((family, key) => {
385
+ const spouseArray = Array.from(family.spouses);
386
+ const anyMemberConnectable = spouseArray.some((spouseId) => connectablePersons.has(spouseId)) || family.children.some((childId) => connectablePersons.has(childId));
387
+ const isOrphanFamily = !anyMemberConnectable;
388
+ if (isOrphanFamily) {
389
+ orphanFamKeys.add(key);
390
+ }
391
+ spouseArray.forEach((spouseId) => {
392
+ if (!personToFamKeys.has(spouseId))
393
+ personToFamKeys.set(spouseId, /* @__PURE__ */ new Set());
394
+ personToFamKeys.get(spouseId).add(key);
395
+ });
396
+ family.children.forEach((childId) => {
397
+ if (!personToFamKeys.has(childId))
398
+ personToFamKeys.set(childId, /* @__PURE__ */ new Set());
399
+ personToFamKeys.get(childId).add(key);
400
+ });
401
+ });
402
+ pedigreeData.persons.forEach((person) => {
403
+ const gedcomId = personIdMap.get(person.id);
404
+ if (!gedcomId) {
405
+ console.warn(
406
+ `[GEDCOM] Person ${person.id} not found in personIdMap`
407
+ );
408
+ return;
409
+ }
410
+ if (!allowOrphanFamilies) {
411
+ const famKeys = personToFamKeys.get(person.id);
412
+ if (famKeys && famKeys.size > 0 && Array.from(famKeys).every((key) => orphanFamKeys.has(key))) {
413
+ return;
414
+ }
415
+ }
416
+ lines.push(`0 ${gedcomId} INDI`);
417
+ if (person.id) {
418
+ lines.push(`1 _FS_ID ${person.id}`);
419
+ }
420
+ if (includeLinks) {
421
+ const personLink = person.links?.person?.href || person.identifiers?.["http://gedcomx.org/Persistent"]?.[0];
422
+ if (personLink) {
423
+ const webUrl = transformFamilySearchUrl(personLink);
424
+ lines.push(`1 _FS_LINK ${webUrl}`);
425
+ } else if (person.id) {
426
+ let baseUrl = "https://www.familysearch.org";
427
+ if (environment === "beta") {
428
+ baseUrl = "https://beta.familysearch.org";
429
+ } else if (environment === "integration") {
430
+ baseUrl = "https://integration.familysearch.org";
431
+ }
432
+ const webUrl = `${baseUrl}/tree/person/${person.id}`;
433
+ lines.push(`1 _FS_LINK ${webUrl}`);
434
+ }
435
+ }
436
+ const personData = person.fullDetails?.persons?.[0] || person;
437
+ const name = extractName(personData);
438
+ if (name) {
439
+ lines.push(`1 NAME ${name}`);
440
+ }
441
+ const gender = extractGender(personData);
442
+ if (gender) {
443
+ lines.push(`1 SEX ${gender}`);
444
+ }
445
+ const birth = extractFact(personData, "BIRTH");
446
+ if (birth) {
447
+ lines.push("1 BIRT");
448
+ if (birth.date) {
449
+ lines.push(`2 DATE ${birth.date}`);
450
+ }
451
+ if (birth.place) {
452
+ lines.push(`2 PLAC ${birth.place}`);
453
+ }
454
+ }
455
+ const death = extractFact(personData, "DEATH");
456
+ if (death) {
457
+ lines.push("1 DEAT");
458
+ if (death.date) {
459
+ lines.push(`2 DATE ${death.date}`);
460
+ }
461
+ if (death.place) {
462
+ lines.push(`2 PLAC ${death.place}`);
463
+ }
464
+ }
465
+ personData.facts?.forEach((fact) => {
466
+ if (fact.type && fact.type !== "http://gedcomx.org/Birth" && fact.type !== "http://gedcomx.org/Death") {
467
+ const factLines = convertFactToGedcom(fact);
468
+ factLines.forEach((line) => lines.push(line));
469
+ }
470
+ });
471
+ if (includeNotes && person.notes?.persons?.[0]?.notes) {
472
+ person.notes.persons[0].notes.forEach((note) => {
473
+ if (note.text) {
474
+ const noteText = note.text.replace(/\n/g, " ");
475
+ lines.push(`1 NOTE ${noteText}`);
476
+ }
477
+ });
478
+ }
479
+ const personSources = personData?.sources;
480
+ if (personSources && Array.isArray(personSources)) {
481
+ personSources.forEach((sourceRef) => {
482
+ const sourceId = sourceRef.descriptionId;
483
+ if (!sourceId) return;
484
+ const sourceIds = Array.from(sourceMap.keys());
485
+ const sourceIndex = sourceIds.indexOf(sourceId);
486
+ if (sourceIndex === -1) return;
487
+ const gedcomSourceId = `@S${sourceIndex + 1}@`;
488
+ lines.push(`1 SOUR ${gedcomSourceId}`);
489
+ if (sourceRef.qualifiers && Array.isArray(sourceRef.qualifiers)) {
490
+ sourceRef.qualifiers.forEach((qualifier) => {
491
+ if (qualifier.name && qualifier.value) {
492
+ lines.push(
493
+ `2 NOTE ${qualifier.name}: ${qualifier.value}`
494
+ );
495
+ }
496
+ });
497
+ }
498
+ });
499
+ }
500
+ });
501
+ let famIndex = 1;
502
+ const familyIdMap = /* @__PURE__ */ new Map();
503
+ families.forEach((family, key) => {
504
+ const spouseArray = Array.from(family.spouses);
505
+ const parentsInMap = spouseArray.filter((id) => personIdMap.has(id));
506
+ const hasNoParents = parentsInMap.length === 0;
507
+ const hasSingleParentNoChildren = parentsInMap.length === 1 && family.children.length === 0;
508
+ if (hasNoParents) {
509
+ if (family.children.length > 0) {
510
+ console.warn(
511
+ `[FS SDK GEDCOM] Skipping FAM for orphaned child(ren): ${family.children.length} child(ren) with 0 parents`
512
+ );
513
+ }
514
+ return;
515
+ }
516
+ if (hasSingleParentNoChildren) {
517
+ return;
518
+ }
519
+ const anyMemberConnectable = spouseArray.some((spouseId) => connectablePersons.has(spouseId)) || family.children.some((childId) => connectablePersons.has(childId));
520
+ const isOrphanFamily = !anyMemberConnectable;
521
+ if (isOrphanFamily && !allowOrphanFamilies) {
522
+ return;
523
+ }
524
+ const famId = `@F${famIndex++}@`;
525
+ familyIdMap.set(key, famId);
526
+ lines.push(`0 ${famId} FAM`);
527
+ if (isOrphanFamily && allowOrphanFamilies) {
528
+ lines.push(`1 _IS_ORPHAN_FAMILY Y`);
529
+ }
530
+ if (spouseArray.length === 2) {
531
+ const [person1, person2] = spouseArray;
532
+ const person1Data = pedigreeData.persons?.find(
533
+ (p) => p.id === person1
534
+ );
535
+ const person2Data = pedigreeData.persons?.find(
536
+ (p) => p.id === person2
537
+ );
538
+ const gender1 = extractGender(
539
+ person1Data?.fullDetails?.persons?.[0] || person1Data
540
+ );
541
+ const gender2 = extractGender(
542
+ person2Data?.fullDetails?.persons?.[0] || person2Data
543
+ );
544
+ const p1 = personIdMap.get(person1);
545
+ const p2 = personIdMap.get(person2);
546
+ if (gender1 === "M" || gender2 === "F") {
547
+ if (p1) lines.push(`1 HUSB ${p1}`);
548
+ if (p2) lines.push(`1 WIFE ${p2}`);
549
+ } else {
550
+ if (p2) lines.push(`1 HUSB ${p2}`);
551
+ if (p1) lines.push(`1 WIFE ${p1}`);
552
+ }
553
+ addMarriageFacts(lines, allRelationships, person1, person2);
554
+ } else if (spouseArray.length === 1) {
555
+ const parentData = pedigreeData.persons?.find(
556
+ (p) => p.id === spouseArray[0]
557
+ );
558
+ const gender = extractGender(
559
+ parentData?.fullDetails?.persons?.[0] || parentData
560
+ );
561
+ if (gender === "M") {
562
+ lines.push(`1 HUSB ${personIdMap.get(spouseArray[0])}`);
563
+ } else {
564
+ lines.push(`1 WIFE ${personIdMap.get(spouseArray[0])}`);
565
+ }
566
+ }
567
+ family.children.forEach((childId) => {
568
+ const childGedcomId = personIdMap.get(childId);
569
+ if (childGedcomId) {
570
+ lines.push(`1 CHIL ${childGedcomId}`);
571
+ }
572
+ });
573
+ });
574
+ childToParents.forEach((parents, childId) => {
575
+ const childGedcomId = personIdMap.get(childId);
576
+ if (!childGedcomId) return;
577
+ let famId;
578
+ if (parents.length >= 2) {
579
+ const famKey = parents.slice(0, 2).sort().join("-");
580
+ famId = familyIdMap.get(famKey);
581
+ } else if (parents.length === 1) {
582
+ const famKey = `single-${parents[0]}`;
583
+ famId = familyIdMap.get(famKey);
584
+ }
585
+ if (famId) {
586
+ const indiRecordIndex = lines.findIndex(
587
+ (line) => line === `0 ${childGedcomId} INDI`
588
+ );
589
+ if (indiRecordIndex !== -1) {
590
+ lines.splice(indiRecordIndex + 1, 0, `1 FAMC ${famId}`);
591
+ }
592
+ }
593
+ });
594
+ families.forEach((family, key) => {
595
+ const famId = familyIdMap.get(key);
596
+ if (!famId) return;
597
+ family.spouses.forEach((spouseId) => {
598
+ const spouseGedcomId = personIdMap.get(spouseId);
599
+ if (!spouseGedcomId) return;
600
+ const indiRecordIndex = lines.findIndex(
601
+ (line) => line === `0 ${spouseGedcomId} INDI`
602
+ );
603
+ if (indiRecordIndex !== -1) {
604
+ let insertIndex = indiRecordIndex + 1;
605
+ while (insertIndex < lines.length && lines[insertIndex].startsWith("1 FAMC")) {
606
+ insertIndex++;
607
+ }
608
+ lines.splice(insertIndex, 0, `1 FAMS ${famId}`);
609
+ }
610
+ });
611
+ });
612
+ const personsInFamilies = /* @__PURE__ */ new Set();
613
+ families.forEach((family) => {
614
+ family.spouses.forEach((id) => personsInFamilies.add(id));
615
+ family.children.forEach((id) => personsInFamilies.add(id));
616
+ });
617
+ lines.push("0 TRLR");
618
+ return lines.join("\n");
619
+ }
620
+ function addMarriageFacts(lines, relationships, person1, person2) {
621
+ const relKey = [person1, person2].sort().join("-");
622
+ const rel = relationships.find((r) => {
623
+ const a = r.person1?.resourceId || r.person1?.resource?.resourceId;
624
+ const b = r.person2?.resourceId || r.person2?.resource?.resourceId;
625
+ if (!a || !b) return false;
626
+ return [a, b].sort().join("-") === relKey;
627
+ });
628
+ if (!rel) return;
629
+ const marriageFacts = [];
630
+ if (rel.facts && Array.isArray(rel.facts)) {
631
+ rel.facts.forEach((f) => {
632
+ if (f.type?.includes("Marriage")) {
633
+ marriageFacts.push(f);
634
+ }
635
+ });
636
+ }
637
+ if (rel.details?.facts && Array.isArray(rel.details.facts)) {
638
+ marriageFacts.push(
639
+ ...rel.details.facts.filter((f) => f.type?.includes("Marriage"))
640
+ );
641
+ }
642
+ if (rel.details?.persons && Array.isArray(rel.details.persons)) {
643
+ rel.details.persons.forEach((p) => {
644
+ if (p.facts && Array.isArray(p.facts)) {
645
+ p.facts.forEach((f) => {
646
+ if (f.type?.includes("Marriage")) {
647
+ marriageFacts.push(f);
648
+ }
649
+ });
650
+ }
651
+ });
652
+ }
653
+ marriageFacts.forEach((mf) => {
654
+ const factLines = convertFactToGedcom(mf);
655
+ factLines.forEach((line) => lines.push(line));
656
+ });
657
+ }
658
+ var convertFamilySearchToGedcom = convertToGedcom;
659
+
660
+ exports.convertFamilySearchToGedcom = convertFamilySearchToGedcom;
661
+ exports.convertToGedcom = convertToGedcom;
662
+ //# sourceMappingURL=index.cjs.map
663
+ //# sourceMappingURL=index.cjs.map