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