@treeviz/familysearch-sdk 2.0.6 → 2.0.7
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.
- package/dist/index.cjs +135 -50
- package/dist/index.js +135 -50
- package/dist/utils/index.cjs +135 -50
- package/dist/utils/index.js +135 -50
- package/package.json +1 -1
package/dist/utils/index.cjs
CHANGED
|
@@ -44,6 +44,64 @@ function transformSourceUrl(url) {
|
|
|
44
44
|
}
|
|
45
45
|
return `${baseUrl}/${arkId}`;
|
|
46
46
|
}
|
|
47
|
+
function sanitizeGedcomValue(value) {
|
|
48
|
+
return value.replace(/[\r\n\t]+/g, " ").trim();
|
|
49
|
+
}
|
|
50
|
+
var GEDCOM_MONTHS = [
|
|
51
|
+
"JAN",
|
|
52
|
+
"FEB",
|
|
53
|
+
"MAR",
|
|
54
|
+
"APR",
|
|
55
|
+
"MAY",
|
|
56
|
+
"JUN",
|
|
57
|
+
"JUL",
|
|
58
|
+
"AUG",
|
|
59
|
+
"SEP",
|
|
60
|
+
"OCT",
|
|
61
|
+
"NOV",
|
|
62
|
+
"DEC"
|
|
63
|
+
];
|
|
64
|
+
function formatSimpleFormalDate(value) {
|
|
65
|
+
const match = value.match(/^([+-])(\d{1,6})(?:-(\d{2}))?(?:-(\d{2}))?/);
|
|
66
|
+
if (!match) return void 0;
|
|
67
|
+
const [, sign, yearPart, monthPart, dayPart] = match;
|
|
68
|
+
if (sign === "-") return void 0;
|
|
69
|
+
const year = String(parseInt(yearPart, 10));
|
|
70
|
+
if (!monthPart) return year;
|
|
71
|
+
const month = GEDCOM_MONTHS[parseInt(monthPart, 10) - 1];
|
|
72
|
+
if (!month) return void 0;
|
|
73
|
+
if (!dayPart) return `${month} ${year}`;
|
|
74
|
+
return `${parseInt(dayPart, 10)} ${month} ${year}`;
|
|
75
|
+
}
|
|
76
|
+
function convertFormalDateToGedcom(formal) {
|
|
77
|
+
if (!formal) return void 0;
|
|
78
|
+
let value = formal.trim();
|
|
79
|
+
let approximate = false;
|
|
80
|
+
if (value.startsWith("A")) {
|
|
81
|
+
approximate = true;
|
|
82
|
+
value = value.slice(1);
|
|
83
|
+
}
|
|
84
|
+
if (value.includes("/")) {
|
|
85
|
+
const slashIndex = value.indexOf("/");
|
|
86
|
+
const start = value.slice(0, slashIndex);
|
|
87
|
+
const end = value.slice(slashIndex + 1);
|
|
88
|
+
const startDate = start ? formatSimpleFormalDate(start) : void 0;
|
|
89
|
+
const endDate = end ? formatSimpleFormalDate(end) : void 0;
|
|
90
|
+
if (start && !startDate) return void 0;
|
|
91
|
+
if (end && !endDate) return void 0;
|
|
92
|
+
if (startDate && endDate) return `BET ${startDate} AND ${endDate}`;
|
|
93
|
+
if (startDate) return `FROM ${startDate}`;
|
|
94
|
+
if (endDate) return `TO ${endDate}`;
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
const simple = formatSimpleFormalDate(value);
|
|
98
|
+
if (!simple) return void 0;
|
|
99
|
+
return approximate ? `ABT ${simple}` : simple;
|
|
100
|
+
}
|
|
101
|
+
function resolveFactDate(date) {
|
|
102
|
+
if (!date) return void 0;
|
|
103
|
+
return convertFormalDateToGedcom(date.formal) || date.original;
|
|
104
|
+
}
|
|
47
105
|
function extractName(person) {
|
|
48
106
|
if (!person) return null;
|
|
49
107
|
if (person.names?.[0]?.nameForms?.[0]) {
|
|
@@ -51,8 +109,14 @@ function extractName(person) {
|
|
|
51
109
|
const parts = nameForm.parts || [];
|
|
52
110
|
const given = parts.find((p) => p.type?.includes("Given"))?.value || "";
|
|
53
111
|
const surname = parts.find((p) => p.type?.includes("Surname"))?.value || "";
|
|
54
|
-
if (given
|
|
55
|
-
return `${given} /${surname}
|
|
112
|
+
if (given && surname) {
|
|
113
|
+
return `${given} /${surname}/`;
|
|
114
|
+
}
|
|
115
|
+
if (surname) {
|
|
116
|
+
return `/${surname}/`;
|
|
117
|
+
}
|
|
118
|
+
if (given) {
|
|
119
|
+
return given;
|
|
56
120
|
}
|
|
57
121
|
if (nameForm.fullText) {
|
|
58
122
|
return nameForm.fullText;
|
|
@@ -82,11 +146,9 @@ function extractFact(person, factType) {
|
|
|
82
146
|
const factTypeUrl = `http://gedcomx.org/${factType === "BIRTH" ? "Birth" : "Death"}`;
|
|
83
147
|
const fact = person.facts?.find((f) => f.type === factTypeUrl);
|
|
84
148
|
if (fact) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
""
|
|
89
|
-
);
|
|
149
|
+
const resolvedDate = resolveFactDate(fact.date);
|
|
150
|
+
if (resolvedDate) {
|
|
151
|
+
result.date = resolvedDate;
|
|
90
152
|
}
|
|
91
153
|
if (fact.place?.original) {
|
|
92
154
|
result.place = fact.place.original;
|
|
@@ -139,25 +201,30 @@ function convertFactToGedcom(fact) {
|
|
|
139
201
|
};
|
|
140
202
|
const gedcomTag = typeMap[fact.type] || "EVEN";
|
|
141
203
|
if (gedcomTag === "OCCU" && fact.value) {
|
|
142
|
-
lines.push(`1 OCCU ${fact.value}`);
|
|
204
|
+
lines.push(`1 OCCU ${sanitizeGedcomValue(fact.value)}`);
|
|
143
205
|
} else {
|
|
144
206
|
lines.push(`1 ${gedcomTag}`);
|
|
145
207
|
}
|
|
208
|
+
if (gedcomTag === "EVEN") {
|
|
209
|
+
const typeName = fact.type.split("/").pop();
|
|
210
|
+
if (typeName) {
|
|
211
|
+
lines.push(`2 TYPE ${sanitizeGedcomValue(typeName)}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
146
214
|
if (fact.links?.conclusion?.href) {
|
|
147
215
|
const webUrl = transformFamilySearchUrl(fact.links.conclusion.href);
|
|
148
216
|
lines.push(`2 WWW ${webUrl}`);
|
|
149
217
|
lines.push(`3 _IS_FS Y`);
|
|
150
218
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
);
|
|
219
|
+
const resolvedDate = resolveFactDate(fact.date);
|
|
220
|
+
if (resolvedDate) {
|
|
221
|
+
lines.push(`2 DATE ${sanitizeGedcomValue(resolvedDate)}`);
|
|
155
222
|
}
|
|
156
223
|
if (fact.place?.original) {
|
|
157
|
-
lines.push(`2 PLAC ${fact.place.original}`);
|
|
224
|
+
lines.push(`2 PLAC ${sanitizeGedcomValue(fact.place.original)}`);
|
|
158
225
|
}
|
|
159
226
|
if (fact.value && gedcomTag !== "OCCU") {
|
|
160
|
-
lines.push(`2 NOTE ${fact.value}`);
|
|
227
|
+
lines.push(`2 NOTE ${sanitizeGedcomValue(fact.value)}`);
|
|
161
228
|
}
|
|
162
229
|
return lines;
|
|
163
230
|
}
|
|
@@ -177,14 +244,14 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
177
244
|
lines.push("2 VERS 1.0");
|
|
178
245
|
lines.push("2 NAME FamilySearch API");
|
|
179
246
|
if (treeName) {
|
|
180
|
-
lines.push(`2 _TREE ${treeName}`);
|
|
247
|
+
lines.push(`2 _TREE ${sanitizeGedcomValue(treeName)}`);
|
|
181
248
|
const randomRIN = Math.floor(Math.random() * 1e8);
|
|
182
249
|
lines.push(`3 RIN ${randomRIN}`);
|
|
183
250
|
}
|
|
184
251
|
lines.push("1 DEST ANY");
|
|
185
252
|
lines.push("1 DATE " + formatDateForGedcom(/* @__PURE__ */ new Date()));
|
|
186
253
|
lines.push("1 SUBM @SUBM1@");
|
|
187
|
-
lines.push("1 FILE " + treeName);
|
|
254
|
+
lines.push("1 FILE " + sanitizeGedcomValue(treeName));
|
|
188
255
|
lines.push("1 GEDC");
|
|
189
256
|
lines.push("2 VERS 5.5");
|
|
190
257
|
lines.push("2 FORM LINEAGE-LINKED");
|
|
@@ -206,20 +273,22 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
206
273
|
const sourceId = `@S${sourceCounter++}@`;
|
|
207
274
|
lines.push(`0 ${sourceId} SOUR`);
|
|
208
275
|
const title = source.titles?.[0]?.value || "FamilySearch Source";
|
|
209
|
-
lines.push(`1 TITL ${title}`);
|
|
276
|
+
lines.push(`1 TITL ${sanitizeGedcomValue(title)}`);
|
|
210
277
|
if (source.citations?.[0]?.value) {
|
|
211
|
-
lines.push(`1 TEXT ${source.citations[0].value}`);
|
|
278
|
+
lines.push(`1 TEXT ${sanitizeGedcomValue(source.citations[0].value)}`);
|
|
212
279
|
}
|
|
213
280
|
if (source.about) {
|
|
214
281
|
const webUrl = transformSourceUrl(source.about);
|
|
215
282
|
lines.push(`1 WWW ${webUrl}`);
|
|
216
283
|
}
|
|
217
284
|
if (source.resourceType) {
|
|
218
|
-
lines.push(
|
|
285
|
+
lines.push(
|
|
286
|
+
`1 NOTE Resource Type: ${sanitizeGedcomValue(source.resourceType)}`
|
|
287
|
+
);
|
|
219
288
|
}
|
|
220
289
|
});
|
|
221
290
|
const fsSourMap = /* @__PURE__ */ new Map();
|
|
222
|
-
let fsSourCounter =
|
|
291
|
+
let fsSourCounter = sourceCounter;
|
|
223
292
|
pedigreeData.persons.forEach((person) => {
|
|
224
293
|
if (person.sources?.persons?.[0]?.sources) {
|
|
225
294
|
const sourceDescMap = /* @__PURE__ */ new Map();
|
|
@@ -269,20 +338,22 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
269
338
|
lines.push(`1 _IS_FS Y`);
|
|
270
339
|
lines.push(`1 _FS_ID ${id}`);
|
|
271
340
|
if (source.titles?.[0]?.value) {
|
|
272
|
-
lines.push(`1 TITL ${source.titles[0].value}`);
|
|
341
|
+
lines.push(`1 TITL ${sanitizeGedcomValue(source.titles[0].value)}`);
|
|
273
342
|
}
|
|
274
343
|
if (source.citations?.[0]?.value) {
|
|
275
|
-
lines.push(`1 TEXT ${source.citations[0].value}`);
|
|
344
|
+
lines.push(`1 TEXT ${sanitizeGedcomValue(source.citations[0].value)}`);
|
|
276
345
|
}
|
|
277
346
|
if (source.about) {
|
|
278
347
|
const webUrl = transformSourceUrl(source.about);
|
|
279
348
|
lines.push(`1 WWW ${webUrl}`);
|
|
280
349
|
}
|
|
281
350
|
if (source.resourceType) {
|
|
282
|
-
lines.push(
|
|
351
|
+
lines.push(
|
|
352
|
+
`1 NOTE Resource Type: ${sanitizeGedcomValue(source.resourceType)}`
|
|
353
|
+
);
|
|
283
354
|
}
|
|
284
355
|
notes.forEach((note) => {
|
|
285
|
-
lines.push(`1 NOTE ${note}`);
|
|
356
|
+
lines.push(`1 NOTE ${sanitizeGedcomValue(note)}`);
|
|
286
357
|
});
|
|
287
358
|
});
|
|
288
359
|
const fsMatchMap = /* @__PURE__ */ new Map();
|
|
@@ -302,7 +373,7 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
302
373
|
lines.push(`1 _IS_FS Y`);
|
|
303
374
|
lines.push(`1 _FS_ID ${id}`);
|
|
304
375
|
if (entry.title) {
|
|
305
|
-
lines.push(`1 TITL ${entry.title}`);
|
|
376
|
+
lines.push(`1 TITL ${sanitizeGedcomValue(entry.title)}`);
|
|
306
377
|
}
|
|
307
378
|
if (entry.content?.score !== void 0) {
|
|
308
379
|
lines.push(`1 SCORE ${entry.content.score}`);
|
|
@@ -323,7 +394,7 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
323
394
|
}
|
|
324
395
|
if (matchInfo.status) {
|
|
325
396
|
const statusName = matchInfo.status.includes("/") ? matchInfo.status.split("/").pop() : matchInfo.status;
|
|
326
|
-
lines.push(`1 NOTE Status: ${statusName}`);
|
|
397
|
+
lines.push(`1 NOTE Status: ${sanitizeGedcomValue(statusName || "")}`);
|
|
327
398
|
}
|
|
328
399
|
}
|
|
329
400
|
lines.push(`1 TYPE ${collectionType}`);
|
|
@@ -335,42 +406,52 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
335
406
|
if (entry.content?.gedcomx?.persons && entry.content.gedcomx.persons.length > 0) {
|
|
336
407
|
const matchedPerson = entry.content.gedcomx.persons[0];
|
|
337
408
|
if (matchedPerson.display?.name) {
|
|
338
|
-
lines.push(`1 TEXT ${matchedPerson.display.name}`);
|
|
409
|
+
lines.push(`1 TEXT ${sanitizeGedcomValue(matchedPerson.display.name)}`);
|
|
339
410
|
}
|
|
340
411
|
if (matchedPerson.display?.lifespan) {
|
|
341
412
|
lines.push(
|
|
342
|
-
`1 NOTE Lifespan: ${matchedPerson.display.lifespan}`
|
|
413
|
+
`1 NOTE Lifespan: ${sanitizeGedcomValue(matchedPerson.display.lifespan)}`
|
|
343
414
|
);
|
|
344
415
|
}
|
|
345
416
|
if (matchedPerson.display?.birthDate || matchedPerson.display?.birthPlace) {
|
|
346
|
-
const birthInfo =
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
417
|
+
const birthInfo = sanitizeGedcomValue(
|
|
418
|
+
[
|
|
419
|
+
matchedPerson.display.birthDate,
|
|
420
|
+
matchedPerson.display.birthPlace
|
|
421
|
+
].filter(Boolean).join(", ")
|
|
422
|
+
);
|
|
350
423
|
if (birthInfo) {
|
|
351
424
|
lines.push(`1 NOTE Birth: ${birthInfo}`);
|
|
352
425
|
}
|
|
353
426
|
}
|
|
354
427
|
if (matchedPerson.display?.deathDate || matchedPerson.display?.deathPlace) {
|
|
355
|
-
const deathInfo =
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
428
|
+
const deathInfo = sanitizeGedcomValue(
|
|
429
|
+
[
|
|
430
|
+
matchedPerson.display.deathDate,
|
|
431
|
+
matchedPerson.display.deathPlace
|
|
432
|
+
].filter(Boolean).join(", ")
|
|
433
|
+
);
|
|
359
434
|
if (deathInfo) {
|
|
360
435
|
lines.push(`1 NOTE Death: ${deathInfo}`);
|
|
361
436
|
}
|
|
362
437
|
}
|
|
363
438
|
if (matchedPerson.display?.gender) {
|
|
364
|
-
lines.push(
|
|
439
|
+
lines.push(
|
|
440
|
+
`1 NOTE Gender: ${sanitizeGedcomValue(matchedPerson.display.gender)}`
|
|
441
|
+
);
|
|
365
442
|
}
|
|
366
443
|
}
|
|
367
444
|
if (entry.content?.sourceDescription) {
|
|
368
445
|
const sourceDesc = entry.content.sourceDescription;
|
|
369
446
|
if (sourceDesc.citations?.[0]?.value) {
|
|
370
|
-
lines.push(
|
|
447
|
+
lines.push(
|
|
448
|
+
`1 NOTE Citation: ${sanitizeGedcomValue(sourceDesc.citations[0].value)}`
|
|
449
|
+
);
|
|
371
450
|
}
|
|
372
451
|
if (sourceDesc.resourceType) {
|
|
373
|
-
lines.push(
|
|
452
|
+
lines.push(
|
|
453
|
+
`1 NOTE Resource Type: ${sanitizeGedcomValue(sourceDesc.resourceType)}`
|
|
454
|
+
);
|
|
374
455
|
}
|
|
375
456
|
if (sourceDesc.about) {
|
|
376
457
|
const webUrl = transformSourceUrl(sourceDesc.about);
|
|
@@ -613,7 +694,7 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
613
694
|
const personData = person.fullDetails?.persons?.[0] || person;
|
|
614
695
|
const name = extractName(personData);
|
|
615
696
|
if (name) {
|
|
616
|
-
lines.push(`1 NAME ${name}`);
|
|
697
|
+
lines.push(`1 NAME ${sanitizeGedcomValue(name)}`);
|
|
617
698
|
}
|
|
618
699
|
const gender = extractGender(personData);
|
|
619
700
|
if (gender) {
|
|
@@ -623,20 +704,20 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
623
704
|
if (birth) {
|
|
624
705
|
lines.push("1 BIRT");
|
|
625
706
|
if (birth.date) {
|
|
626
|
-
lines.push(`2 DATE ${birth.date}`);
|
|
707
|
+
lines.push(`2 DATE ${sanitizeGedcomValue(birth.date)}`);
|
|
627
708
|
}
|
|
628
709
|
if (birth.place) {
|
|
629
|
-
lines.push(`2 PLAC ${birth.place}`);
|
|
710
|
+
lines.push(`2 PLAC ${sanitizeGedcomValue(birth.place)}`);
|
|
630
711
|
}
|
|
631
712
|
}
|
|
632
713
|
const death = extractFact(personData, "DEATH");
|
|
633
714
|
if (death) {
|
|
634
715
|
lines.push("1 DEAT");
|
|
635
716
|
if (death.date) {
|
|
636
|
-
lines.push(`2 DATE ${death.date}`);
|
|
717
|
+
lines.push(`2 DATE ${sanitizeGedcomValue(death.date)}`);
|
|
637
718
|
}
|
|
638
719
|
if (death.place) {
|
|
639
|
-
lines.push(`2 PLAC ${death.place}`);
|
|
720
|
+
lines.push(`2 PLAC ${sanitizeGedcomValue(death.place)}`);
|
|
640
721
|
}
|
|
641
722
|
}
|
|
642
723
|
personData.facts?.forEach((fact) => {
|
|
@@ -648,8 +729,7 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
648
729
|
if (includeNotes && person.notes?.persons?.[0]?.notes) {
|
|
649
730
|
person.notes.persons[0].notes.forEach((note) => {
|
|
650
731
|
if (note.text) {
|
|
651
|
-
|
|
652
|
-
lines.push(`1 NOTE ${noteText}`);
|
|
732
|
+
lines.push(`1 NOTE ${sanitizeGedcomValue(note.text)}`);
|
|
653
733
|
}
|
|
654
734
|
});
|
|
655
735
|
}
|
|
@@ -667,7 +747,7 @@ function convertToGedcom(pedigreeData, options = {}) {
|
|
|
667
747
|
sourceRef.qualifiers.forEach((qualifier) => {
|
|
668
748
|
if (qualifier.name && qualifier.value) {
|
|
669
749
|
lines.push(
|
|
670
|
-
`2 NOTE ${qualifier.name}: ${qualifier.value}`
|
|
750
|
+
`2 NOTE ${sanitizeGedcomValue(`${qualifier.name}: ${qualifier.value}`)}`
|
|
671
751
|
);
|
|
672
752
|
}
|
|
673
753
|
});
|
|
@@ -821,31 +901,36 @@ function addMarriageFacts(lines, relationships, person1, person2) {
|
|
|
821
901
|
return [a, b].sort().join("-") === relKey;
|
|
822
902
|
});
|
|
823
903
|
if (!rel) return;
|
|
904
|
+
const isMarriageEvent = (type) => !!type && (type.includes("Marriage") || type.includes("Divorce"));
|
|
824
905
|
const marriageFacts = [];
|
|
825
906
|
if (rel.facts && Array.isArray(rel.facts)) {
|
|
826
907
|
rel.facts.forEach((f) => {
|
|
827
|
-
if (f.type
|
|
908
|
+
if (isMarriageEvent(f.type)) {
|
|
828
909
|
marriageFacts.push(f);
|
|
829
910
|
}
|
|
830
911
|
});
|
|
831
912
|
}
|
|
832
913
|
if (rel.details?.facts && Array.isArray(rel.details.facts)) {
|
|
833
914
|
marriageFacts.push(
|
|
834
|
-
...rel.details.facts.filter((f) => f.type
|
|
915
|
+
...rel.details.facts.filter((f) => isMarriageEvent(f.type))
|
|
835
916
|
);
|
|
836
917
|
}
|
|
837
918
|
if (rel.details?.persons && Array.isArray(rel.details.persons)) {
|
|
838
919
|
rel.details.persons.forEach((p) => {
|
|
839
920
|
if (p.facts && Array.isArray(p.facts)) {
|
|
840
921
|
p.facts.forEach((f) => {
|
|
841
|
-
if (f.type
|
|
922
|
+
if (isMarriageEvent(f.type)) {
|
|
842
923
|
marriageFacts.push(f);
|
|
843
924
|
}
|
|
844
925
|
});
|
|
845
926
|
}
|
|
846
927
|
});
|
|
847
928
|
}
|
|
929
|
+
const seenFacts = /* @__PURE__ */ new Set();
|
|
848
930
|
marriageFacts.forEach((mf) => {
|
|
931
|
+
const key = `${mf.type}|${resolveFactDate(mf.date) || ""}|${mf.place?.original || ""}`;
|
|
932
|
+
if (seenFacts.has(key)) return;
|
|
933
|
+
seenFacts.add(key);
|
|
849
934
|
const factLines = convertFactToGedcom(mf);
|
|
850
935
|
factLines.forEach((line) => lines.push(line));
|
|
851
936
|
});
|