@treeviz/gedcom-parser 2.1.3 → 2.1.5
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/classes/index.js +202 -6
- package/dist/classes/indi.d.ts +7 -2
- package/dist/classes/indi.d.ts.map +1 -1
- package/dist/cli/index.js +466 -268
- package/dist/constants/index.js +201 -5
- package/dist/factories/index.js +201 -5
- package/dist/index.js +809 -386
- package/dist/interfaces/indi.d.ts +8 -3
- package/dist/interfaces/indi.d.ts.map +1 -1
- package/dist/kinship-translator/index.js +201 -5
- package/dist/utils/family-edit.d.ts +81 -2
- package/dist/utils/family-edit.d.ts.map +1 -1
- package/dist/utils/index.js +428 -7
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1121,7 +1121,299 @@ var idGetter = (id) => {
|
|
|
1121
1121
|
return newId;
|
|
1122
1122
|
};
|
|
1123
1123
|
|
|
1124
|
+
// src/factories/cache-factory.ts
|
|
1125
|
+
var defaultCacheFactory = () => {
|
|
1126
|
+
let cache = null;
|
|
1127
|
+
return {
|
|
1128
|
+
getItem: async () => cache,
|
|
1129
|
+
setItem: async (value) => {
|
|
1130
|
+
cache = value;
|
|
1131
|
+
}
|
|
1132
|
+
};
|
|
1133
|
+
};
|
|
1134
|
+
var cacheFactory = defaultCacheFactory;
|
|
1135
|
+
var getCacheManagerFactory = () => {
|
|
1136
|
+
return cacheFactory;
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
// src/utils/cache.ts
|
|
1140
|
+
var getGedcomId = (gedcom) => {
|
|
1141
|
+
if (!gedcom) {
|
|
1142
|
+
return "unknown";
|
|
1143
|
+
}
|
|
1144
|
+
const treeId = gedcom.getTreeId?.() || "";
|
|
1145
|
+
const treeName = gedcom.getTreeName?.() || "";
|
|
1146
|
+
const sanitizedName = treeName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1147
|
+
if (treeId && sanitizedName) {
|
|
1148
|
+
return `${treeId}_${sanitizedName}`;
|
|
1149
|
+
} else if (treeId) {
|
|
1150
|
+
return treeId;
|
|
1151
|
+
} else if (sanitizedName) {
|
|
1152
|
+
return sanitizedName;
|
|
1153
|
+
}
|
|
1154
|
+
return `gedcom_${gedcom.refcount}`;
|
|
1155
|
+
};
|
|
1156
|
+
var caches = {
|
|
1157
|
+
pathCache: {},
|
|
1158
|
+
relativesOnDegreeCache: {},
|
|
1159
|
+
relativesOnLevelCache: {},
|
|
1160
|
+
profilePictureCache: {}
|
|
1161
|
+
};
|
|
1162
|
+
var cacheDbs;
|
|
1163
|
+
var getCacheDbs = () => {
|
|
1164
|
+
if (!cacheDbs) {
|
|
1165
|
+
const getInstance = getCacheManagerFactory();
|
|
1166
|
+
cacheDbs = {
|
|
1167
|
+
pathCache: getInstance(
|
|
1168
|
+
"ftv",
|
|
1169
|
+
"Main",
|
|
1170
|
+
"path",
|
|
1171
|
+
true
|
|
1172
|
+
),
|
|
1173
|
+
relativesOnDegreeCache: getInstance("ftv", "Main", "path", true),
|
|
1174
|
+
relativesOnLevelCache: getInstance(
|
|
1175
|
+
"ftv",
|
|
1176
|
+
"Main",
|
|
1177
|
+
"path",
|
|
1178
|
+
true
|
|
1179
|
+
),
|
|
1180
|
+
profilePictureCache: getInstance(
|
|
1181
|
+
"ftv",
|
|
1182
|
+
"Main",
|
|
1183
|
+
"images",
|
|
1184
|
+
false
|
|
1185
|
+
)
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
return cacheDbs;
|
|
1189
|
+
};
|
|
1190
|
+
var storeCache = {
|
|
1191
|
+
// NOTE: pathCache, relativesOnLevelCache, and relativesOnDegreeCache are intentionally
|
|
1192
|
+
// kept in memory only. These debounced functions exist to satisfy the type system
|
|
1193
|
+
// but are never called.
|
|
1194
|
+
pathCache: debounce((value) => {
|
|
1195
|
+
if (value) {
|
|
1196
|
+
getCacheDbs().pathCache.setItem(value);
|
|
1197
|
+
}
|
|
1198
|
+
}, 50),
|
|
1199
|
+
relativesOnLevelCache: debounce((value) => {
|
|
1200
|
+
if (value) {
|
|
1201
|
+
getCacheDbs().relativesOnLevelCache.setItem(value);
|
|
1202
|
+
}
|
|
1203
|
+
}, 50),
|
|
1204
|
+
relativesOnDegreeCache: debounce((value) => {
|
|
1205
|
+
if (value) {
|
|
1206
|
+
getCacheDbs().relativesOnDegreeCache.setItem(value);
|
|
1207
|
+
}
|
|
1208
|
+
}, 50),
|
|
1209
|
+
// profilePictureCache IS persisted to IndexedDB
|
|
1210
|
+
profilePictureCache: debounce((value) => {
|
|
1211
|
+
if (value) {
|
|
1212
|
+
getCacheDbs().profilePictureCache.setItem(value);
|
|
1213
|
+
}
|
|
1214
|
+
}, 100)
|
|
1215
|
+
};
|
|
1216
|
+
var resetRelativesCache = () => {
|
|
1217
|
+
caches.relativesOnDegreeCache = {};
|
|
1218
|
+
caches.relativesOnLevelCache = {};
|
|
1219
|
+
};
|
|
1220
|
+
var relativesCache = (cacheKey) => (gedcom, key, subKey, value) => {
|
|
1221
|
+
const gedcomId = getGedcomId(gedcom);
|
|
1222
|
+
const fullKey = `${gedcomId}:${key}`;
|
|
1223
|
+
const cache = caches[cacheKey];
|
|
1224
|
+
if (!cache) {
|
|
1225
|
+
caches[cacheKey] = {};
|
|
1226
|
+
}
|
|
1227
|
+
if (value) {
|
|
1228
|
+
const typedCache2 = caches[cacheKey];
|
|
1229
|
+
if (!typedCache2[fullKey]) {
|
|
1230
|
+
typedCache2[fullKey] = {};
|
|
1231
|
+
}
|
|
1232
|
+
typedCache2[fullKey][subKey] = value;
|
|
1233
|
+
return typedCache2[fullKey][subKey];
|
|
1234
|
+
}
|
|
1235
|
+
const typedCache = caches[cacheKey];
|
|
1236
|
+
return typedCache?.[fullKey]?.[subKey];
|
|
1237
|
+
};
|
|
1238
|
+
var pathCache = (gedcom, key, value) => {
|
|
1239
|
+
const gedcomId = getGedcomId(gedcom);
|
|
1240
|
+
const fullKey = `${gedcomId}:${key}`;
|
|
1241
|
+
if (!caches.pathCache) {
|
|
1242
|
+
caches.pathCache = {};
|
|
1243
|
+
}
|
|
1244
|
+
if (value && caches.pathCache) {
|
|
1245
|
+
caches.pathCache[fullKey] = value;
|
|
1246
|
+
return caches.pathCache[fullKey];
|
|
1247
|
+
}
|
|
1248
|
+
return caches.pathCache?.[fullKey];
|
|
1249
|
+
};
|
|
1250
|
+
var profilePictureCache = (gedcom, key, value) => {
|
|
1251
|
+
const gedcomId = getGedcomId(gedcom);
|
|
1252
|
+
const fullKey = `${gedcomId}:${key}`;
|
|
1253
|
+
if (!caches.profilePictureCache) {
|
|
1254
|
+
caches.profilePictureCache = {};
|
|
1255
|
+
}
|
|
1256
|
+
if (value && caches.profilePictureCache) {
|
|
1257
|
+
caches.profilePictureCache[fullKey] = value;
|
|
1258
|
+
storeCache.profilePictureCache(caches.profilePictureCache);
|
|
1259
|
+
return caches.profilePictureCache[fullKey];
|
|
1260
|
+
}
|
|
1261
|
+
const cached = caches.profilePictureCache?.[fullKey];
|
|
1262
|
+
return cached;
|
|
1263
|
+
};
|
|
1264
|
+
var isNonBiologicalPath = (path) => path.some(
|
|
1265
|
+
(item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
|
|
1266
|
+
);
|
|
1267
|
+
var invertHop = (kinship) => {
|
|
1268
|
+
if (kinship === "parent") {
|
|
1269
|
+
return "child";
|
|
1270
|
+
}
|
|
1271
|
+
if (kinship === "child") {
|
|
1272
|
+
return "parent";
|
|
1273
|
+
}
|
|
1274
|
+
return kinship;
|
|
1275
|
+
};
|
|
1276
|
+
var hopRelation = (from, toIndi, kinship, inherited) => {
|
|
1277
|
+
if (kinship === "parent" || kinship === "child") {
|
|
1278
|
+
const parent = kinship === "parent" ? toIndi : from.indi;
|
|
1279
|
+
const child = kinship === "parent" ? from.indi : toIndi;
|
|
1280
|
+
const currentRelation = child.getParentType(parent);
|
|
1281
|
+
if (currentRelation && currentRelation !== "biological" /* BIOLOGICAL */) {
|
|
1282
|
+
return currentRelation;
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
if (inherited && inherited !== "biological" /* BIOLOGICAL */) {
|
|
1286
|
+
return inherited;
|
|
1287
|
+
}
|
|
1288
|
+
return void 0;
|
|
1289
|
+
};
|
|
1290
|
+
var rebuildPathFromHops = (hops, skipRelation = false) => {
|
|
1291
|
+
if (hops.length === 0) {
|
|
1292
|
+
return [];
|
|
1293
|
+
}
|
|
1294
|
+
const path = [
|
|
1295
|
+
{
|
|
1296
|
+
indi: hops[0].indi,
|
|
1297
|
+
kinship: "self",
|
|
1298
|
+
level: 0,
|
|
1299
|
+
levelUp: 0,
|
|
1300
|
+
levelDown: 0,
|
|
1301
|
+
degree: 0
|
|
1302
|
+
}
|
|
1303
|
+
];
|
|
1304
|
+
for (let i = 1; i < hops.length; i++) {
|
|
1305
|
+
const prev = path[i - 1];
|
|
1306
|
+
const { indi, kinship } = hops[i];
|
|
1307
|
+
const relation = skipRelation ? void 0 : hopRelation(prev, indi, kinship, prev.relation);
|
|
1308
|
+
if (kinship === "parent") {
|
|
1309
|
+
path.push({
|
|
1310
|
+
indi,
|
|
1311
|
+
kinship,
|
|
1312
|
+
relation,
|
|
1313
|
+
level: prev.level + 1,
|
|
1314
|
+
levelUp: prev.levelUp + 1,
|
|
1315
|
+
levelDown: prev.levelDown,
|
|
1316
|
+
degree: prev.degree
|
|
1317
|
+
});
|
|
1318
|
+
continue;
|
|
1319
|
+
}
|
|
1320
|
+
if (kinship === "child") {
|
|
1321
|
+
path.push({
|
|
1322
|
+
indi,
|
|
1323
|
+
kinship,
|
|
1324
|
+
relation,
|
|
1325
|
+
level: prev.level - 1,
|
|
1326
|
+
levelUp: prev.levelUp,
|
|
1327
|
+
levelDown: prev.levelDown + 1,
|
|
1328
|
+
degree: prev.levelUp ? prev.level > 0 ? prev.levelUp - prev.level + 1 : prev.levelDown - Math.abs(prev.level) : 0
|
|
1329
|
+
});
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
path.push({
|
|
1333
|
+
indi,
|
|
1334
|
+
kinship,
|
|
1335
|
+
relation,
|
|
1336
|
+
level: prev.level,
|
|
1337
|
+
levelUp: prev.levelUp,
|
|
1338
|
+
levelDown: prev.levelDown,
|
|
1339
|
+
degree: prev.degree
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
return path;
|
|
1343
|
+
};
|
|
1344
|
+
var storePathIfBetter = (gedcom, fromId, toId, path) => {
|
|
1345
|
+
const key = `${fromId}|${toId}`;
|
|
1346
|
+
const existing = pathCache(gedcom, key);
|
|
1347
|
+
if (existing && (!isNonBiologicalPath(existing) || isNonBiologicalPath(path))) {
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
pathCache(gedcom, key, path);
|
|
1351
|
+
};
|
|
1352
|
+
var cacheDiscoveredPath = (gedcom, path) => {
|
|
1353
|
+
const n = path.length;
|
|
1354
|
+
if (n < 2) {
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
for (let i = 0; i < n; i++) {
|
|
1358
|
+
const fromId = path[i].indi.id;
|
|
1359
|
+
if (!fromId) {
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
for (let j = i + 1; j < n; j++) {
|
|
1363
|
+
const toId = path[j].indi.id;
|
|
1364
|
+
if (!toId) {
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
const isOriginalPair = i === 0 && j === n - 1;
|
|
1368
|
+
const isDirectHop = j === i + 1;
|
|
1369
|
+
const slice = path.slice(i, j + 1);
|
|
1370
|
+
const sliceMixed = isNonBiologicalPath(slice);
|
|
1371
|
+
if (!isOriginalPair && !isDirectHop && sliceMixed) {
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
const forward = i === 0 ? slice : rebuildPathFromHops(slice, !sliceMixed);
|
|
1375
|
+
const reverseHops = [
|
|
1376
|
+
{
|
|
1377
|
+
indi: slice[slice.length - 1].indi,
|
|
1378
|
+
kinship: "self"
|
|
1379
|
+
}
|
|
1380
|
+
];
|
|
1381
|
+
for (let k = slice.length - 2; k >= 0; k--) {
|
|
1382
|
+
reverseHops.push({
|
|
1383
|
+
indi: slice[k].indi,
|
|
1384
|
+
kinship: invertHop(slice[k + 1].kinship)
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
const reverse = rebuildPathFromHops(reverseHops, !sliceMixed);
|
|
1388
|
+
storePathIfBetter(gedcom, fromId, toId, forward);
|
|
1389
|
+
storePathIfBetter(gedcom, toId, fromId, reverse);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1124
1394
|
// src/utils/family-edit.ts
|
|
1395
|
+
var refTypeForTag = (tag) => {
|
|
1396
|
+
if (tag === "FAMS" || tag === "FAMC") return "FAM";
|
|
1397
|
+
return "INDI";
|
|
1398
|
+
};
|
|
1399
|
+
var normalizeSinglePointer = (owner, tag) => {
|
|
1400
|
+
const node = owner.get(tag);
|
|
1401
|
+
if (!node) return;
|
|
1402
|
+
if (node instanceof List && node.length === 1) {
|
|
1403
|
+
const only = node.index(0);
|
|
1404
|
+
if (only) {
|
|
1405
|
+
owner.set(tag, only);
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
const raw = node.values()[0];
|
|
1409
|
+
if (typeof raw === "string") {
|
|
1410
|
+
const gedcom = owner.getGedcom?.();
|
|
1411
|
+
if (gedcom) {
|
|
1412
|
+
owner.set(tag, makePointer(gedcom, owner, raw, refTypeForTag(tag)));
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
};
|
|
1125
1417
|
var PEDIGREE_VALUES = [
|
|
1126
1418
|
"birth" /* BIRTH */,
|
|
1127
1419
|
"adopted" /* ADOPTED */,
|
|
@@ -1240,6 +1532,9 @@ var attachSpouseToFamily = (fam, indi) => {
|
|
|
1240
1532
|
fam.set(tag, makePointer(gedcom, fam, indi.id, "INDI"));
|
|
1241
1533
|
}
|
|
1242
1534
|
indi.assign("FAMS", makePointer(gedcom, indi, fam.id, "FAM"), true);
|
|
1535
|
+
normalizeSinglePointer(indi, "FAMS");
|
|
1536
|
+
normalizeSinglePointer(fam, spouseTagFor(indi, fam));
|
|
1537
|
+
resetRelativesCache();
|
|
1243
1538
|
return true;
|
|
1244
1539
|
};
|
|
1245
1540
|
var attachChildToFamily = (fam, child, pedigree, parent) => {
|
|
@@ -1255,14 +1550,122 @@ var attachChildToFamily = (fam, child, pedigree, parent) => {
|
|
|
1255
1550
|
child.assign("FAMC", makePointer(gedcom, child, fam.id, "FAM"), true);
|
|
1256
1551
|
}
|
|
1257
1552
|
setChildPedigree(fam, child, pedigree, parent);
|
|
1553
|
+
normalizeSinglePointer(child, "FAMC");
|
|
1554
|
+
normalizeSinglePointer(fam, "CHIL");
|
|
1555
|
+
resetRelativesCache();
|
|
1258
1556
|
return true;
|
|
1259
1557
|
};
|
|
1260
|
-
var
|
|
1558
|
+
var removePointerByValue = (owner, tag, targetId) => {
|
|
1559
|
+
const node = owner.get(tag);
|
|
1560
|
+
if (!node) {
|
|
1561
|
+
return false;
|
|
1562
|
+
}
|
|
1563
|
+
if (node instanceof List) {
|
|
1564
|
+
const keysToRemove = [];
|
|
1565
|
+
node.entries().forEach(([key, item]) => {
|
|
1566
|
+
if (item?.toValue?.() === targetId || item?.id === targetId || String(key) === targetId) {
|
|
1567
|
+
keysToRemove.push(key);
|
|
1568
|
+
}
|
|
1569
|
+
});
|
|
1570
|
+
keysToRemove.forEach((key) => {
|
|
1571
|
+
node.removeItem(key);
|
|
1572
|
+
});
|
|
1573
|
+
if (node.length === 0) {
|
|
1574
|
+
owner.remove(tag);
|
|
1575
|
+
}
|
|
1576
|
+
return keysToRemove.length > 0;
|
|
1577
|
+
}
|
|
1578
|
+
if (node.toValue?.() === targetId) {
|
|
1579
|
+
owner.remove(tag);
|
|
1580
|
+
return true;
|
|
1581
|
+
}
|
|
1582
|
+
const asList = node.toList?.();
|
|
1583
|
+
if (asList && asList.length > 0) {
|
|
1584
|
+
const keysToRemove = [];
|
|
1585
|
+
asList.entries().forEach(([key, item]) => {
|
|
1586
|
+
if (item?.toValue?.() === targetId || item?.id === targetId) {
|
|
1587
|
+
keysToRemove.push(key);
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
if (keysToRemove.length === asList.length) {
|
|
1591
|
+
owner.remove(tag);
|
|
1592
|
+
return true;
|
|
1593
|
+
}
|
|
1594
|
+
if (keysToRemove.length > 0) {
|
|
1595
|
+
const remaining = asList.values().filter((item) => item?.toValue?.() !== targetId);
|
|
1596
|
+
if (remaining.length === 1 && remaining[0]) {
|
|
1597
|
+
owner.set(tag, remaining[0]);
|
|
1598
|
+
} else {
|
|
1599
|
+
owner.set(tag, new List(remaining));
|
|
1600
|
+
}
|
|
1601
|
+
return true;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return false;
|
|
1605
|
+
};
|
|
1606
|
+
var familyMemberCount = (fam) => {
|
|
1607
|
+
let count = 0;
|
|
1608
|
+
if (firstPointerId(fam.get("HUSB"))) {
|
|
1609
|
+
count += 1;
|
|
1610
|
+
}
|
|
1611
|
+
if (firstPointerId(fam.get("WIFE"))) {
|
|
1612
|
+
count += 1;
|
|
1613
|
+
}
|
|
1614
|
+
count += pointerIds(fam.get("CHIL")).length;
|
|
1615
|
+
return count;
|
|
1616
|
+
};
|
|
1617
|
+
var isDegenerateFamily = (fam) => familyMemberCount(fam) <= 1;
|
|
1618
|
+
var pruneEmptyFamilies = (gedcom) => {
|
|
1619
|
+
const emptyIds = [];
|
|
1620
|
+
gedcom.fams()?.forEach((fam, id) => {
|
|
1621
|
+
if (fam && isDegenerateFamily(fam) && id) {
|
|
1622
|
+
emptyIds.push(id);
|
|
1623
|
+
}
|
|
1624
|
+
});
|
|
1625
|
+
if (emptyIds.length === 0) {
|
|
1626
|
+
return [];
|
|
1627
|
+
}
|
|
1628
|
+
gedcom.indis()?.forEach((indi) => {
|
|
1629
|
+
if (!indi) {
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
emptyIds.forEach((famId) => {
|
|
1633
|
+
removePointerByValue(indi, "FAMS", famId);
|
|
1634
|
+
removePointerByValue(indi, "FAMC", famId);
|
|
1635
|
+
});
|
|
1636
|
+
});
|
|
1637
|
+
emptyIds.forEach((famId) => {
|
|
1638
|
+
gedcom.fams()?.removeItem(famId);
|
|
1639
|
+
});
|
|
1640
|
+
return emptyIds;
|
|
1641
|
+
};
|
|
1642
|
+
var findSharedSpouseFamily = (parent, coParentId) => {
|
|
1643
|
+
let found;
|
|
1644
|
+
parent.getFamilies("FAMS")?.forEach((fam) => {
|
|
1645
|
+
if (!found && fam && familyHasParent(fam, coParentId)) {
|
|
1646
|
+
found = fam;
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
return found;
|
|
1650
|
+
};
|
|
1651
|
+
var findReusableParentChildFamily = (parent, child, options) => {
|
|
1261
1652
|
if (!parent.id || !child.id) {
|
|
1262
1653
|
return void 0;
|
|
1263
1654
|
}
|
|
1655
|
+
const reuseParentFamilies = options?.reuseParentFamilies !== false;
|
|
1656
|
+
const gedcom = parent.getGedcom() ?? child.getGedcom();
|
|
1264
1657
|
let reusableFamc;
|
|
1658
|
+
let coupleFam;
|
|
1265
1659
|
let already = false;
|
|
1660
|
+
const noteCoupleWith = (coParentId) => {
|
|
1661
|
+
if (!coParentId || coParentId === parent.id || coupleFam || !parent.id) {
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
const shared = findSharedSpouseFamily(parent, coParentId);
|
|
1665
|
+
if (shared) {
|
|
1666
|
+
coupleFam = shared;
|
|
1667
|
+
}
|
|
1668
|
+
};
|
|
1266
1669
|
child.getFamilies("FAMC")?.forEach((fam) => {
|
|
1267
1670
|
if (!fam) {
|
|
1268
1671
|
return;
|
|
@@ -1271,6 +1674,8 @@ var findReusableParentChildFamily = (parent, child) => {
|
|
|
1271
1674
|
already = true;
|
|
1272
1675
|
return;
|
|
1273
1676
|
}
|
|
1677
|
+
pointerIds(fam.get("HUSB")).forEach(noteCoupleWith);
|
|
1678
|
+
pointerIds(fam.get("WIFE")).forEach(noteCoupleWith);
|
|
1274
1679
|
if (!reusableFamc && canAttachAsSpouse(fam, parent)) {
|
|
1275
1680
|
reusableFamc = fam;
|
|
1276
1681
|
}
|
|
@@ -1278,9 +1683,21 @@ var findReusableParentChildFamily = (parent, child) => {
|
|
|
1278
1683
|
if (already) {
|
|
1279
1684
|
return "already";
|
|
1280
1685
|
}
|
|
1686
|
+
if (coupleFam) {
|
|
1687
|
+
return coupleFam;
|
|
1688
|
+
}
|
|
1281
1689
|
if (reusableFamc) {
|
|
1282
1690
|
return reusableFamc;
|
|
1283
1691
|
}
|
|
1692
|
+
if (options?.targetFamilyId && gedcom) {
|
|
1693
|
+
const target = gedcom.fam(options.targetFamilyId);
|
|
1694
|
+
if (target && (familyHasParent(target, parent.id) || canAttachAsSpouse(target, parent))) {
|
|
1695
|
+
return target;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
if (!reuseParentFamilies) {
|
|
1699
|
+
return void 0;
|
|
1700
|
+
}
|
|
1284
1701
|
const parentFams = [];
|
|
1285
1702
|
parent.getFamilies("FAMS")?.forEach((fam) => {
|
|
1286
1703
|
if (fam) {
|
|
@@ -1292,20 +1709,40 @@ var findReusableParentChildFamily = (parent, child) => {
|
|
|
1292
1709
|
}
|
|
1293
1710
|
return void 0;
|
|
1294
1711
|
};
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1712
|
+
var consolidateChildIntoFamily = (child, keep) => {
|
|
1713
|
+
if (!child.id || !keep.id) {
|
|
1714
|
+
return [];
|
|
1715
|
+
}
|
|
1716
|
+
const keepParentIds = /* @__PURE__ */ new Set([
|
|
1717
|
+
...pointerIds(keep.get("HUSB")),
|
|
1718
|
+
...pointerIds(keep.get("WIFE"))
|
|
1719
|
+
]);
|
|
1720
|
+
const removed = [];
|
|
1721
|
+
child.getFamilies("FAMC")?.forEach((fam) => {
|
|
1722
|
+
if (!fam?.id || fam.id === keep.id) {
|
|
1723
|
+
return;
|
|
1303
1724
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1725
|
+
const famParentIds = [
|
|
1726
|
+
...pointerIds(fam.get("HUSB")),
|
|
1727
|
+
...pointerIds(fam.get("WIFE"))
|
|
1728
|
+
];
|
|
1729
|
+
if (famParentIds.length === 0 || !famParentIds.every((id) => keepParentIds.has(id))) {
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
removePointerByValue(fam, "CHIL", child.id);
|
|
1733
|
+
normalizeSinglePointer(fam, "CHIL");
|
|
1734
|
+
removePointerByValue(child, "FAMC", fam.id);
|
|
1735
|
+
normalizeSinglePointer(child, "FAMC");
|
|
1736
|
+
removed.push(fam.id);
|
|
1737
|
+
});
|
|
1738
|
+
const gedcom = child.getGedcom() ?? keep.getGedcom();
|
|
1739
|
+
if (gedcom && removed.length > 0) {
|
|
1740
|
+
pruneEmptyFamilies(gedcom);
|
|
1741
|
+
}
|
|
1742
|
+
if (removed.length > 0) {
|
|
1743
|
+
resetRelativesCache();
|
|
1744
|
+
}
|
|
1745
|
+
return removed;
|
|
1309
1746
|
};
|
|
1310
1747
|
|
|
1311
1748
|
// src/factories/date-locale-factory.ts
|
|
@@ -1557,255 +1994,6 @@ var getMarriageAscAndChildBirth = (person) => (itemA, keyA, itemB, keyB) => {
|
|
|
1557
1994
|
const childB = familyB?.getChildren().orderBy(BIRTH_ASC).index(0);
|
|
1558
1995
|
return getBirthAsc(childA, keyA, childB);
|
|
1559
1996
|
};
|
|
1560
|
-
var getGedcomId = (gedcom) => {
|
|
1561
|
-
if (!gedcom) {
|
|
1562
|
-
return "unknown";
|
|
1563
|
-
}
|
|
1564
|
-
const treeId = gedcom.getTreeId?.() || "";
|
|
1565
|
-
const treeName = gedcom.getTreeName?.() || "";
|
|
1566
|
-
const sanitizedName = treeName.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1567
|
-
if (treeId && sanitizedName) {
|
|
1568
|
-
return `${treeId}_${sanitizedName}`;
|
|
1569
|
-
} else if (treeId) {
|
|
1570
|
-
return treeId;
|
|
1571
|
-
} else if (sanitizedName) {
|
|
1572
|
-
return sanitizedName;
|
|
1573
|
-
}
|
|
1574
|
-
return `gedcom_${gedcom.refcount}`;
|
|
1575
|
-
};
|
|
1576
|
-
var caches = {
|
|
1577
|
-
pathCache: {},
|
|
1578
|
-
relativesOnDegreeCache: {},
|
|
1579
|
-
relativesOnLevelCache: {},
|
|
1580
|
-
profilePictureCache: {}
|
|
1581
|
-
};
|
|
1582
|
-
var cacheDbs;
|
|
1583
|
-
var getCacheDbs = () => {
|
|
1584
|
-
if (!cacheDbs) {
|
|
1585
|
-
const getInstance = getCacheManagerFactory();
|
|
1586
|
-
cacheDbs = {
|
|
1587
|
-
pathCache: getInstance(
|
|
1588
|
-
"ftv",
|
|
1589
|
-
"Main",
|
|
1590
|
-
"path",
|
|
1591
|
-
true
|
|
1592
|
-
),
|
|
1593
|
-
relativesOnDegreeCache: getInstance("ftv", "Main", "path", true),
|
|
1594
|
-
relativesOnLevelCache: getInstance(
|
|
1595
|
-
"ftv",
|
|
1596
|
-
"Main",
|
|
1597
|
-
"path",
|
|
1598
|
-
true
|
|
1599
|
-
),
|
|
1600
|
-
profilePictureCache: getInstance(
|
|
1601
|
-
"ftv",
|
|
1602
|
-
"Main",
|
|
1603
|
-
"images",
|
|
1604
|
-
false
|
|
1605
|
-
)
|
|
1606
|
-
};
|
|
1607
|
-
}
|
|
1608
|
-
return cacheDbs;
|
|
1609
|
-
};
|
|
1610
|
-
var storeCache = {
|
|
1611
|
-
// NOTE: pathCache, relativesOnLevelCache, and relativesOnDegreeCache are intentionally
|
|
1612
|
-
// kept in memory only. These debounced functions exist to satisfy the type system
|
|
1613
|
-
// but are never called.
|
|
1614
|
-
pathCache: debounce((value) => {
|
|
1615
|
-
if (value) {
|
|
1616
|
-
getCacheDbs().pathCache.setItem(value);
|
|
1617
|
-
}
|
|
1618
|
-
}, 50),
|
|
1619
|
-
relativesOnLevelCache: debounce((value) => {
|
|
1620
|
-
if (value) {
|
|
1621
|
-
getCacheDbs().relativesOnLevelCache.setItem(value);
|
|
1622
|
-
}
|
|
1623
|
-
}, 50),
|
|
1624
|
-
relativesOnDegreeCache: debounce((value) => {
|
|
1625
|
-
if (value) {
|
|
1626
|
-
getCacheDbs().relativesOnDegreeCache.setItem(value);
|
|
1627
|
-
}
|
|
1628
|
-
}, 50),
|
|
1629
|
-
// profilePictureCache IS persisted to IndexedDB
|
|
1630
|
-
profilePictureCache: debounce((value) => {
|
|
1631
|
-
if (value) {
|
|
1632
|
-
getCacheDbs().profilePictureCache.setItem(value);
|
|
1633
|
-
}
|
|
1634
|
-
}, 100)
|
|
1635
|
-
};
|
|
1636
|
-
var relativesCache = (cacheKey) => (gedcom, key, subKey, value) => {
|
|
1637
|
-
const gedcomId = getGedcomId(gedcom);
|
|
1638
|
-
const fullKey = `${gedcomId}:${key}`;
|
|
1639
|
-
const cache = caches[cacheKey];
|
|
1640
|
-
if (!cache) {
|
|
1641
|
-
caches[cacheKey] = {};
|
|
1642
|
-
}
|
|
1643
|
-
if (value) {
|
|
1644
|
-
const typedCache2 = caches[cacheKey];
|
|
1645
|
-
if (!typedCache2[fullKey]) {
|
|
1646
|
-
typedCache2[fullKey] = {};
|
|
1647
|
-
}
|
|
1648
|
-
typedCache2[fullKey][subKey] = value;
|
|
1649
|
-
return typedCache2[fullKey][subKey];
|
|
1650
|
-
}
|
|
1651
|
-
const typedCache = caches[cacheKey];
|
|
1652
|
-
return typedCache?.[fullKey]?.[subKey];
|
|
1653
|
-
};
|
|
1654
|
-
var pathCache = (gedcom, key, value) => {
|
|
1655
|
-
const gedcomId = getGedcomId(gedcom);
|
|
1656
|
-
const fullKey = `${gedcomId}:${key}`;
|
|
1657
|
-
if (!caches.pathCache) {
|
|
1658
|
-
caches.pathCache = {};
|
|
1659
|
-
}
|
|
1660
|
-
if (value && caches.pathCache) {
|
|
1661
|
-
caches.pathCache[fullKey] = value;
|
|
1662
|
-
return caches.pathCache[fullKey];
|
|
1663
|
-
}
|
|
1664
|
-
return caches.pathCache?.[fullKey];
|
|
1665
|
-
};
|
|
1666
|
-
var profilePictureCache = (gedcom, key, value) => {
|
|
1667
|
-
const gedcomId = getGedcomId(gedcom);
|
|
1668
|
-
const fullKey = `${gedcomId}:${key}`;
|
|
1669
|
-
if (!caches.profilePictureCache) {
|
|
1670
|
-
caches.profilePictureCache = {};
|
|
1671
|
-
}
|
|
1672
|
-
if (value && caches.profilePictureCache) {
|
|
1673
|
-
caches.profilePictureCache[fullKey] = value;
|
|
1674
|
-
storeCache.profilePictureCache(caches.profilePictureCache);
|
|
1675
|
-
return caches.profilePictureCache[fullKey];
|
|
1676
|
-
}
|
|
1677
|
-
const cached = caches.profilePictureCache?.[fullKey];
|
|
1678
|
-
return cached;
|
|
1679
|
-
};
|
|
1680
|
-
var isNonBiologicalPath = (path) => path.some(
|
|
1681
|
-
(item) => item.relation && item.relation !== "biological" /* BIOLOGICAL */
|
|
1682
|
-
);
|
|
1683
|
-
var invertHop = (kinship) => {
|
|
1684
|
-
if (kinship === "parent") {
|
|
1685
|
-
return "child";
|
|
1686
|
-
}
|
|
1687
|
-
if (kinship === "child") {
|
|
1688
|
-
return "parent";
|
|
1689
|
-
}
|
|
1690
|
-
return kinship;
|
|
1691
|
-
};
|
|
1692
|
-
var hopRelation = (from, toIndi, kinship, inherited) => {
|
|
1693
|
-
if (kinship === "parent" || kinship === "child") {
|
|
1694
|
-
const parent = kinship === "parent" ? toIndi : from.indi;
|
|
1695
|
-
const child = kinship === "parent" ? from.indi : toIndi;
|
|
1696
|
-
const currentRelation = child.getParentType(parent);
|
|
1697
|
-
if (currentRelation && currentRelation !== "biological" /* BIOLOGICAL */) {
|
|
1698
|
-
return currentRelation;
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
if (inherited && inherited !== "biological" /* BIOLOGICAL */) {
|
|
1702
|
-
return inherited;
|
|
1703
|
-
}
|
|
1704
|
-
return void 0;
|
|
1705
|
-
};
|
|
1706
|
-
var rebuildPathFromHops = (hops, skipRelation = false) => {
|
|
1707
|
-
if (hops.length === 0) {
|
|
1708
|
-
return [];
|
|
1709
|
-
}
|
|
1710
|
-
const path = [
|
|
1711
|
-
{
|
|
1712
|
-
indi: hops[0].indi,
|
|
1713
|
-
kinship: "self",
|
|
1714
|
-
level: 0,
|
|
1715
|
-
levelUp: 0,
|
|
1716
|
-
levelDown: 0,
|
|
1717
|
-
degree: 0
|
|
1718
|
-
}
|
|
1719
|
-
];
|
|
1720
|
-
for (let i = 1; i < hops.length; i++) {
|
|
1721
|
-
const prev = path[i - 1];
|
|
1722
|
-
const { indi, kinship } = hops[i];
|
|
1723
|
-
const relation = skipRelation ? void 0 : hopRelation(prev, indi, kinship, prev.relation);
|
|
1724
|
-
if (kinship === "parent") {
|
|
1725
|
-
path.push({
|
|
1726
|
-
indi,
|
|
1727
|
-
kinship,
|
|
1728
|
-
relation,
|
|
1729
|
-
level: prev.level + 1,
|
|
1730
|
-
levelUp: prev.levelUp + 1,
|
|
1731
|
-
levelDown: prev.levelDown,
|
|
1732
|
-
degree: prev.degree
|
|
1733
|
-
});
|
|
1734
|
-
continue;
|
|
1735
|
-
}
|
|
1736
|
-
if (kinship === "child") {
|
|
1737
|
-
path.push({
|
|
1738
|
-
indi,
|
|
1739
|
-
kinship,
|
|
1740
|
-
relation,
|
|
1741
|
-
level: prev.level - 1,
|
|
1742
|
-
levelUp: prev.levelUp,
|
|
1743
|
-
levelDown: prev.levelDown + 1,
|
|
1744
|
-
degree: prev.levelUp ? prev.level > 0 ? prev.levelUp - prev.level + 1 : prev.levelDown - Math.abs(prev.level) : 0
|
|
1745
|
-
});
|
|
1746
|
-
continue;
|
|
1747
|
-
}
|
|
1748
|
-
path.push({
|
|
1749
|
-
indi,
|
|
1750
|
-
kinship,
|
|
1751
|
-
relation,
|
|
1752
|
-
level: prev.level,
|
|
1753
|
-
levelUp: prev.levelUp,
|
|
1754
|
-
levelDown: prev.levelDown,
|
|
1755
|
-
degree: prev.degree
|
|
1756
|
-
});
|
|
1757
|
-
}
|
|
1758
|
-
return path;
|
|
1759
|
-
};
|
|
1760
|
-
var storePathIfBetter = (gedcom, fromId, toId, path) => {
|
|
1761
|
-
const key = `${fromId}|${toId}`;
|
|
1762
|
-
const existing = pathCache(gedcom, key);
|
|
1763
|
-
if (existing && (!isNonBiologicalPath(existing) || isNonBiologicalPath(path))) {
|
|
1764
|
-
return;
|
|
1765
|
-
}
|
|
1766
|
-
pathCache(gedcom, key, path);
|
|
1767
|
-
};
|
|
1768
|
-
var cacheDiscoveredPath = (gedcom, path) => {
|
|
1769
|
-
const n = path.length;
|
|
1770
|
-
if (n < 2) {
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
for (let i = 0; i < n; i++) {
|
|
1774
|
-
const fromId = path[i].indi.id;
|
|
1775
|
-
if (!fromId) {
|
|
1776
|
-
continue;
|
|
1777
|
-
}
|
|
1778
|
-
for (let j = i + 1; j < n; j++) {
|
|
1779
|
-
const toId = path[j].indi.id;
|
|
1780
|
-
if (!toId) {
|
|
1781
|
-
continue;
|
|
1782
|
-
}
|
|
1783
|
-
const isOriginalPair = i === 0 && j === n - 1;
|
|
1784
|
-
const isDirectHop = j === i + 1;
|
|
1785
|
-
const slice = path.slice(i, j + 1);
|
|
1786
|
-
const sliceMixed = isNonBiologicalPath(slice);
|
|
1787
|
-
if (!isOriginalPair && !isDirectHop && sliceMixed) {
|
|
1788
|
-
continue;
|
|
1789
|
-
}
|
|
1790
|
-
const forward = i === 0 ? slice : rebuildPathFromHops(slice, !sliceMixed);
|
|
1791
|
-
const reverseHops = [
|
|
1792
|
-
{
|
|
1793
|
-
indi: slice[slice.length - 1].indi,
|
|
1794
|
-
kinship: "self"
|
|
1795
|
-
}
|
|
1796
|
-
];
|
|
1797
|
-
for (let k = slice.length - 2; k >= 0; k--) {
|
|
1798
|
-
reverseHops.push({
|
|
1799
|
-
indi: slice[k].indi,
|
|
1800
|
-
kinship: invertHop(slice[k + 1].kinship)
|
|
1801
|
-
});
|
|
1802
|
-
}
|
|
1803
|
-
const reverse = rebuildPathFromHops(reverseHops, !sliceMixed);
|
|
1804
|
-
storePathIfBetter(gedcom, fromId, toId, forward);
|
|
1805
|
-
storePathIfBetter(gedcom, toId, fromId, reverse);
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
};
|
|
1809
1997
|
|
|
1810
1998
|
// src/utils/get-all-prop.ts
|
|
1811
1999
|
var getAllProp = (indi, prop) => {
|
|
@@ -3924,13 +4112,17 @@ var Indi = class extends Common {
|
|
|
3924
4112
|
}
|
|
3925
4113
|
return fam;
|
|
3926
4114
|
}
|
|
3927
|
-
addChild(child, pedigree) {
|
|
4115
|
+
addChild(child, pedigree, options) {
|
|
3928
4116
|
if (!this.id || !child?.id || this.id === child.id || !this._gedcom) {
|
|
3929
4117
|
return void 0;
|
|
3930
4118
|
}
|
|
3931
4119
|
const existing = findReusableParentChildFamily(
|
|
3932
4120
|
this,
|
|
3933
|
-
child
|
|
4121
|
+
child,
|
|
4122
|
+
{
|
|
4123
|
+
reuseParentFamilies: options?.reuseParentFamilies,
|
|
4124
|
+
targetFamilyId: options?.targetFamilyId
|
|
4125
|
+
}
|
|
3934
4126
|
);
|
|
3935
4127
|
if (existing === "already") {
|
|
3936
4128
|
const fam2 = child.getFamilies("FAMC")?.find(
|
|
@@ -3953,10 +4145,16 @@ var Indi = class extends Common {
|
|
|
3953
4145
|
if (!fam.addChild(child, pedigree, this)) {
|
|
3954
4146
|
return void 0;
|
|
3955
4147
|
}
|
|
4148
|
+
if (existing) {
|
|
4149
|
+
consolidateChildIntoFamily(child, fam);
|
|
4150
|
+
}
|
|
3956
4151
|
return fam;
|
|
3957
4152
|
}
|
|
3958
|
-
addParent(parent, pedigree) {
|
|
3959
|
-
return parent.addChild(this, pedigree
|
|
4153
|
+
addParent(parent, pedigree, options) {
|
|
4154
|
+
return parent.addChild(this, pedigree, {
|
|
4155
|
+
reuseParentFamilies: false,
|
|
4156
|
+
targetFamilyId: options?.targetFamilyId
|
|
4157
|
+
});
|
|
3960
4158
|
}
|
|
3961
4159
|
/**
|
|
3962
4160
|
* Link an existing top-level OBJE record to this individual (`1 OBJE @On@`).
|
|
@@ -7951,7 +8149,7 @@ var ensureExtensionSchema = (gedcom, extraTags) => {
|
|
|
7951
8149
|
|
|
7952
8150
|
// package.json
|
|
7953
8151
|
var package_default = {
|
|
7954
|
-
version: "2.1.
|
|
8152
|
+
version: "2.1.5"};
|
|
7955
8153
|
|
|
7956
8154
|
// src/utils/get-product-details.ts
|
|
7957
8155
|
var isDevelopment = () => {
|