tablinum 0.6.0 → 0.6.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.
- package/dist/index.js +106 -71
- package/dist/svelte/index.svelte.js +106 -71
- package/dist/sync/gift-wrap.d.ts +1 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -422,7 +422,7 @@ function buildStructSchema(def, options = {}) {
|
|
|
422
422
|
}
|
|
423
423
|
for (const [name, fieldDef] of Object.entries(def.fields)) {
|
|
424
424
|
const fieldSchema = fieldDefToSchema(fieldDef);
|
|
425
|
-
schemaFields[name] = options.allOptional ? Schema3.optionalKey(fieldSchema) : fieldSchema;
|
|
425
|
+
schemaFields[name] = options.allOptional || fieldDef.isOptional ? Schema3.optionalKey(fieldSchema) : fieldSchema;
|
|
426
426
|
}
|
|
427
427
|
return Schema3.Struct(schemaFields);
|
|
428
428
|
}
|
|
@@ -1418,7 +1418,7 @@ function reconcileWithRelay(storage, relay, relayUrl, publicKeys) {
|
|
|
1418
1418
|
const allHaveIds = [];
|
|
1419
1419
|
const allNeedIds = [];
|
|
1420
1420
|
const subId = `neg-${Date.now()}`;
|
|
1421
|
-
const initialMsg = yield* Effect7.
|
|
1421
|
+
const initialMsg = yield* Effect7.tryPromise({
|
|
1422
1422
|
try: () => neg.initiate(),
|
|
1423
1423
|
catch: (e) => new SyncError({
|
|
1424
1424
|
message: `Negentropy initiate failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -1430,7 +1430,7 @@ function reconcileWithRelay(storage, relay, relayUrl, publicKeys) {
|
|
|
1430
1430
|
while (currentMsg !== null) {
|
|
1431
1431
|
const response = yield* relay.sendNegMsg(relayUrl, subId, filter, currentMsg);
|
|
1432
1432
|
if (response.msgHex === null) break;
|
|
1433
|
-
const reconcileResult = yield* Effect7.
|
|
1433
|
+
const reconcileResult = yield* Effect7.tryPromise({
|
|
1434
1434
|
try: () => neg.reconcile(response.msgHex),
|
|
1435
1435
|
catch: (e) => new SyncError({
|
|
1436
1436
|
message: `Negentropy reconcile failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -1583,30 +1583,44 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1583
1583
|
if (!memberRecord) return false;
|
|
1584
1584
|
return !!memberRecord.removedAt;
|
|
1585
1585
|
});
|
|
1586
|
-
const
|
|
1586
|
+
const storeGiftWrapShell = (gw) => storage.putGiftWrap({ id: gw.id, event: gw, createdAt: gw.created_at });
|
|
1587
|
+
const unwrapGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1587
1588
|
const existing = yield* storage.getGiftWrap(remoteGw.id);
|
|
1588
1589
|
if (existing) return null;
|
|
1589
|
-
const
|
|
1590
|
-
|
|
1591
|
-
|
|
1590
|
+
const rumor = yield* giftWrapHandle.unwrap(remoteGw).pipe(
|
|
1591
|
+
Effect8.orElseSucceed(() => null)
|
|
1592
|
+
);
|
|
1593
|
+
if (!rumor) {
|
|
1594
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1592
1595
|
return null;
|
|
1593
1596
|
}
|
|
1594
|
-
const rumor = unwrapResult.success;
|
|
1595
1597
|
const dTag = rumor.tags.find((t) => t[0] === "d")?.[1];
|
|
1596
1598
|
if (!dTag) {
|
|
1597
|
-
yield*
|
|
1599
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1598
1600
|
return null;
|
|
1599
1601
|
}
|
|
1600
1602
|
const colonIdx = dTag.indexOf(":");
|
|
1601
1603
|
if (colonIdx === -1) {
|
|
1602
|
-
yield*
|
|
1604
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1603
1605
|
return null;
|
|
1604
1606
|
}
|
|
1605
|
-
const
|
|
1606
|
-
|
|
1607
|
+
const collection2 = dTag.substring(0, colonIdx);
|
|
1608
|
+
if (!knownCollections.has(collection2)) {
|
|
1609
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1610
|
+
return null;
|
|
1611
|
+
}
|
|
1612
|
+
return {
|
|
1613
|
+
giftWrap: remoteGw,
|
|
1614
|
+
rumor,
|
|
1615
|
+
collection: collection2,
|
|
1616
|
+
recordId: dTag.substring(colonIdx + 1)
|
|
1617
|
+
};
|
|
1618
|
+
});
|
|
1619
|
+
const applyUnwrappedEvent = (uw) => Effect8.gen(function* () {
|
|
1620
|
+
const { giftWrap: remoteGw, rumor, collection: collectionName, recordId } = uw;
|
|
1607
1621
|
const retention = knownCollections.get(collectionName);
|
|
1608
1622
|
if (retention === void 0) {
|
|
1609
|
-
yield*
|
|
1623
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1610
1624
|
return null;
|
|
1611
1625
|
}
|
|
1612
1626
|
if (rumor.pubkey) {
|
|
@@ -1615,20 +1629,20 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1615
1629
|
yield* Effect8.logWarning("Rejected write from removed member", {
|
|
1616
1630
|
author: rumor.pubkey.slice(0, 12)
|
|
1617
1631
|
});
|
|
1618
|
-
yield*
|
|
1632
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1619
1633
|
return null;
|
|
1620
1634
|
}
|
|
1621
1635
|
}
|
|
1622
|
-
let data = null;
|
|
1623
|
-
let kind = "u";
|
|
1624
1636
|
const parsed = yield* Effect8.try({
|
|
1625
1637
|
try: () => JSON.parse(rumor.content),
|
|
1626
1638
|
catch: () => void 0
|
|
1627
1639
|
}).pipe(Effect8.orElseSucceed(() => void 0));
|
|
1628
1640
|
if (parsed === void 0) {
|
|
1629
|
-
yield*
|
|
1641
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1630
1642
|
return null;
|
|
1631
1643
|
}
|
|
1644
|
+
let data = null;
|
|
1645
|
+
let kind = "u";
|
|
1632
1646
|
if (parsed === null || parsed._deleted) {
|
|
1633
1647
|
kind = "d";
|
|
1634
1648
|
} else {
|
|
@@ -1665,6 +1679,78 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1665
1679
|
}
|
|
1666
1680
|
return collectionName;
|
|
1667
1681
|
});
|
|
1682
|
+
const reconcileRelay = (url, pubKeys) => Effect8.gen(function* () {
|
|
1683
|
+
yield* Effect8.logDebug("Syncing relay", { relay: url });
|
|
1684
|
+
const { haveIds, needIds } = yield* reconcileWithRelay(
|
|
1685
|
+
storage,
|
|
1686
|
+
relay,
|
|
1687
|
+
url,
|
|
1688
|
+
Array.from(pubKeys)
|
|
1689
|
+
).pipe(
|
|
1690
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1691
|
+
Effect8.orElseSucceed(() => ({ haveIds: [], needIds: [] }))
|
|
1692
|
+
);
|
|
1693
|
+
yield* Effect8.logDebug("Relay reconciliation result", {
|
|
1694
|
+
relay: url,
|
|
1695
|
+
need: needIds.length,
|
|
1696
|
+
have: haveIds.length
|
|
1697
|
+
});
|
|
1698
|
+
const events = needIds.length > 0 ? yield* relay.fetchEvents(needIds, url).pipe(
|
|
1699
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1700
|
+
Effect8.orElseSucceed(() => [])
|
|
1701
|
+
) : [];
|
|
1702
|
+
return {
|
|
1703
|
+
events,
|
|
1704
|
+
haveIds: haveIds.map((id) => ({ id, url }))
|
|
1705
|
+
};
|
|
1706
|
+
}).pipe(Effect8.withLogSpan("tablinum.reconcileRelay"));
|
|
1707
|
+
const syncAllRelays = (pubKeys, changedCollections) => Effect8.gen(function* () {
|
|
1708
|
+
const results = yield* Effect8.forEach(
|
|
1709
|
+
relayUrls,
|
|
1710
|
+
(url) => reconcileRelay(url, pubKeys),
|
|
1711
|
+
{ concurrency: "unbounded" }
|
|
1712
|
+
);
|
|
1713
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1714
|
+
const allGiftWraps = [];
|
|
1715
|
+
for (const { events } of results) {
|
|
1716
|
+
for (const ev of events) {
|
|
1717
|
+
if (!seen.has(ev.id)) {
|
|
1718
|
+
seen.add(ev.id);
|
|
1719
|
+
allGiftWraps.push(ev);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
const unwrapped = [];
|
|
1724
|
+
for (const gw of allGiftWraps) {
|
|
1725
|
+
const result = yield* unwrapGiftWrap(gw).pipe(Effect8.orElseSucceed(() => null));
|
|
1726
|
+
if (result) unwrapped.push(result);
|
|
1727
|
+
}
|
|
1728
|
+
unwrapped.sort((a, b) => a.rumor.created_at - b.rumor.created_at || (a.rumor.id < b.rumor.id ? -1 : 1));
|
|
1729
|
+
for (const event of unwrapped) {
|
|
1730
|
+
const collection2 = yield* applyUnwrappedEvent(event).pipe(
|
|
1731
|
+
Effect8.orElseSucceed(() => null)
|
|
1732
|
+
);
|
|
1733
|
+
if (collection2) changedCollections.add(collection2);
|
|
1734
|
+
}
|
|
1735
|
+
const allHaveIds = results.flatMap((r) => r.haveIds);
|
|
1736
|
+
yield* Effect8.forEach(
|
|
1737
|
+
allHaveIds,
|
|
1738
|
+
({ id, url }) => Effect8.gen(function* () {
|
|
1739
|
+
const gw = yield* storage.getGiftWrap(id);
|
|
1740
|
+
if (!gw?.event) return;
|
|
1741
|
+
yield* relay.publish(gw.event, [url]).pipe(
|
|
1742
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1743
|
+
Effect8.ignore
|
|
1744
|
+
);
|
|
1745
|
+
}),
|
|
1746
|
+
{ concurrency: "unbounded", discard: true }
|
|
1747
|
+
);
|
|
1748
|
+
}).pipe(Effect8.withLogSpan("tablinum.syncAllRelays"));
|
|
1749
|
+
const processGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1750
|
+
const uw = yield* unwrapGiftWrap(remoteGw);
|
|
1751
|
+
if (!uw) return null;
|
|
1752
|
+
return yield* applyUnwrappedEvent(uw);
|
|
1753
|
+
});
|
|
1668
1754
|
const processRealtimeGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1669
1755
|
const collection2 = yield* processGiftWrap(remoteGw).pipe(Effect8.orElseSucceed(() => null));
|
|
1670
1756
|
if (collection2) {
|
|
@@ -1733,55 +1819,8 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1733
1819
|
Effect8.ignore
|
|
1734
1820
|
);
|
|
1735
1821
|
}),
|
|
1736
|
-
{ discard: true }
|
|
1822
|
+
{ concurrency: "unbounded", discard: true }
|
|
1737
1823
|
);
|
|
1738
|
-
const syncRelay = (url, pubKeys, changedCollections) => Effect8.gen(function* () {
|
|
1739
|
-
yield* Effect8.logDebug("Syncing relay", { relay: url });
|
|
1740
|
-
const reconcileResult = yield* Effect8.result(
|
|
1741
|
-
reconcileWithRelay(storage, relay, url, Array.from(pubKeys))
|
|
1742
|
-
);
|
|
1743
|
-
if (reconcileResult._tag === "Failure") {
|
|
1744
|
-
onSyncError?.(reconcileResult.failure);
|
|
1745
|
-
return;
|
|
1746
|
-
}
|
|
1747
|
-
const { haveIds, needIds } = reconcileResult.success;
|
|
1748
|
-
yield* Effect8.logDebug("Relay reconciliation result", {
|
|
1749
|
-
relay: url,
|
|
1750
|
-
need: needIds.length,
|
|
1751
|
-
have: haveIds.length
|
|
1752
|
-
});
|
|
1753
|
-
if (needIds.length > 0) {
|
|
1754
|
-
const fetched = yield* relay.fetchEvents(needIds, url).pipe(
|
|
1755
|
-
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1756
|
-
Effect8.orElseSucceed(() => [])
|
|
1757
|
-
);
|
|
1758
|
-
const sorted = [...fetched].sort((a, b) => a.created_at - b.created_at);
|
|
1759
|
-
yield* Effect8.forEach(
|
|
1760
|
-
sorted,
|
|
1761
|
-
(remoteGw) => Effect8.gen(function* () {
|
|
1762
|
-
const collection2 = yield* processGiftWrap(remoteGw).pipe(
|
|
1763
|
-
Effect8.orElseSucceed(() => null)
|
|
1764
|
-
);
|
|
1765
|
-
if (collection2) changedCollections.add(collection2);
|
|
1766
|
-
}),
|
|
1767
|
-
{ discard: true }
|
|
1768
|
-
);
|
|
1769
|
-
}
|
|
1770
|
-
if (haveIds.length > 0) {
|
|
1771
|
-
yield* Effect8.forEach(
|
|
1772
|
-
haveIds,
|
|
1773
|
-
(id) => Effect8.gen(function* () {
|
|
1774
|
-
const gw = yield* storage.getGiftWrap(id);
|
|
1775
|
-
if (!gw?.event) return;
|
|
1776
|
-
yield* relay.publish(gw.event, [url]).pipe(
|
|
1777
|
-
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1778
|
-
Effect8.ignore
|
|
1779
|
-
);
|
|
1780
|
-
}),
|
|
1781
|
-
{ discard: true }
|
|
1782
|
-
);
|
|
1783
|
-
}
|
|
1784
|
-
}).pipe(Effect8.withLogSpan("tablinum.syncRelay"));
|
|
1785
1824
|
let healingActive = false;
|
|
1786
1825
|
const healingEffect = Effect8.gen(function* () {
|
|
1787
1826
|
if (!healingActive) return;
|
|
@@ -1791,9 +1830,7 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1791
1830
|
yield* Effect8.gen(function* () {
|
|
1792
1831
|
const pubKeys = getSubscriptionPubKeys();
|
|
1793
1832
|
const changedCollections = /* @__PURE__ */ new Set();
|
|
1794
|
-
yield*
|
|
1795
|
-
discard: true
|
|
1796
|
-
});
|
|
1833
|
+
yield* syncAllRelays(pubKeys, changedCollections);
|
|
1797
1834
|
if (changedCollections.size > 0) {
|
|
1798
1835
|
yield* notifyReplayComplete(watchCtx, [...changedCollections]);
|
|
1799
1836
|
}
|
|
@@ -1807,9 +1844,7 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1807
1844
|
const changedCollections = /* @__PURE__ */ new Set();
|
|
1808
1845
|
yield* Effect8.gen(function* () {
|
|
1809
1846
|
const pubKeys = getSubscriptionPubKeys();
|
|
1810
|
-
yield*
|
|
1811
|
-
discard: true
|
|
1812
|
-
});
|
|
1847
|
+
yield* syncAllRelays(pubKeys, changedCollections);
|
|
1813
1848
|
yield* publishQueue.flush(relayUrls).pipe(Effect8.ignore);
|
|
1814
1849
|
}).pipe(
|
|
1815
1850
|
Effect8.ensuring(
|
|
@@ -459,7 +459,7 @@ function buildStructSchema(def, options = {}) {
|
|
|
459
459
|
}
|
|
460
460
|
for (const [name, fieldDef] of Object.entries(def.fields)) {
|
|
461
461
|
const fieldSchema = fieldDefToSchema(fieldDef);
|
|
462
|
-
schemaFields[name] = options.allOptional ? Schema4.optionalKey(fieldSchema) : fieldSchema;
|
|
462
|
+
schemaFields[name] = options.allOptional || fieldDef.isOptional ? Schema4.optionalKey(fieldSchema) : fieldSchema;
|
|
463
463
|
}
|
|
464
464
|
return Schema4.Struct(schemaFields);
|
|
465
465
|
}
|
|
@@ -1455,7 +1455,7 @@ function reconcileWithRelay(storage, relay, relayUrl, publicKeys) {
|
|
|
1455
1455
|
const allHaveIds = [];
|
|
1456
1456
|
const allNeedIds = [];
|
|
1457
1457
|
const subId = `neg-${Date.now()}`;
|
|
1458
|
-
const initialMsg = yield* Effect7.
|
|
1458
|
+
const initialMsg = yield* Effect7.tryPromise({
|
|
1459
1459
|
try: () => neg.initiate(),
|
|
1460
1460
|
catch: (e) => new SyncError({
|
|
1461
1461
|
message: `Negentropy initiate failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -1467,7 +1467,7 @@ function reconcileWithRelay(storage, relay, relayUrl, publicKeys) {
|
|
|
1467
1467
|
while (currentMsg !== null) {
|
|
1468
1468
|
const response = yield* relay.sendNegMsg(relayUrl, subId, filter, currentMsg);
|
|
1469
1469
|
if (response.msgHex === null) break;
|
|
1470
|
-
const reconcileResult = yield* Effect7.
|
|
1470
|
+
const reconcileResult = yield* Effect7.tryPromise({
|
|
1471
1471
|
try: () => neg.reconcile(response.msgHex),
|
|
1472
1472
|
catch: (e) => new SyncError({
|
|
1473
1473
|
message: `Negentropy reconcile failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
@@ -1620,30 +1620,44 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1620
1620
|
if (!memberRecord) return false;
|
|
1621
1621
|
return !!memberRecord.removedAt;
|
|
1622
1622
|
});
|
|
1623
|
-
const
|
|
1623
|
+
const storeGiftWrapShell = (gw) => storage.putGiftWrap({ id: gw.id, event: gw, createdAt: gw.created_at });
|
|
1624
|
+
const unwrapGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1624
1625
|
const existing = yield* storage.getGiftWrap(remoteGw.id);
|
|
1625
1626
|
if (existing) return null;
|
|
1626
|
-
const
|
|
1627
|
-
|
|
1628
|
-
|
|
1627
|
+
const rumor = yield* giftWrapHandle.unwrap(remoteGw).pipe(
|
|
1628
|
+
Effect8.orElseSucceed(() => null)
|
|
1629
|
+
);
|
|
1630
|
+
if (!rumor) {
|
|
1631
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1629
1632
|
return null;
|
|
1630
1633
|
}
|
|
1631
|
-
const rumor = unwrapResult.success;
|
|
1632
1634
|
const dTag = rumor.tags.find((t) => t[0] === "d")?.[1];
|
|
1633
1635
|
if (!dTag) {
|
|
1634
|
-
yield*
|
|
1636
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1635
1637
|
return null;
|
|
1636
1638
|
}
|
|
1637
1639
|
const colonIdx = dTag.indexOf(":");
|
|
1638
1640
|
if (colonIdx === -1) {
|
|
1639
|
-
yield*
|
|
1641
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1640
1642
|
return null;
|
|
1641
1643
|
}
|
|
1642
|
-
const
|
|
1643
|
-
|
|
1644
|
+
const collection2 = dTag.substring(0, colonIdx);
|
|
1645
|
+
if (!knownCollections.has(collection2)) {
|
|
1646
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
return {
|
|
1650
|
+
giftWrap: remoteGw,
|
|
1651
|
+
rumor,
|
|
1652
|
+
collection: collection2,
|
|
1653
|
+
recordId: dTag.substring(colonIdx + 1)
|
|
1654
|
+
};
|
|
1655
|
+
});
|
|
1656
|
+
const applyUnwrappedEvent = (uw) => Effect8.gen(function* () {
|
|
1657
|
+
const { giftWrap: remoteGw, rumor, collection: collectionName, recordId } = uw;
|
|
1644
1658
|
const retention = knownCollections.get(collectionName);
|
|
1645
1659
|
if (retention === void 0) {
|
|
1646
|
-
yield*
|
|
1660
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1647
1661
|
return null;
|
|
1648
1662
|
}
|
|
1649
1663
|
if (rumor.pubkey) {
|
|
@@ -1652,20 +1666,20 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1652
1666
|
yield* Effect8.logWarning("Rejected write from removed member", {
|
|
1653
1667
|
author: rumor.pubkey.slice(0, 12)
|
|
1654
1668
|
});
|
|
1655
|
-
yield*
|
|
1669
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1656
1670
|
return null;
|
|
1657
1671
|
}
|
|
1658
1672
|
}
|
|
1659
|
-
let data = null;
|
|
1660
|
-
let kind = "u";
|
|
1661
1673
|
const parsed = yield* Effect8.try({
|
|
1662
1674
|
try: () => JSON.parse(rumor.content),
|
|
1663
1675
|
catch: () => void 0
|
|
1664
1676
|
}).pipe(Effect8.orElseSucceed(() => void 0));
|
|
1665
1677
|
if (parsed === void 0) {
|
|
1666
|
-
yield*
|
|
1678
|
+
yield* storeGiftWrapShell(remoteGw);
|
|
1667
1679
|
return null;
|
|
1668
1680
|
}
|
|
1681
|
+
let data = null;
|
|
1682
|
+
let kind = "u";
|
|
1669
1683
|
if (parsed === null || parsed._deleted) {
|
|
1670
1684
|
kind = "d";
|
|
1671
1685
|
} else {
|
|
@@ -1702,6 +1716,78 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1702
1716
|
}
|
|
1703
1717
|
return collectionName;
|
|
1704
1718
|
});
|
|
1719
|
+
const reconcileRelay = (url, pubKeys) => Effect8.gen(function* () {
|
|
1720
|
+
yield* Effect8.logDebug("Syncing relay", { relay: url });
|
|
1721
|
+
const { haveIds, needIds } = yield* reconcileWithRelay(
|
|
1722
|
+
storage,
|
|
1723
|
+
relay,
|
|
1724
|
+
url,
|
|
1725
|
+
Array.from(pubKeys)
|
|
1726
|
+
).pipe(
|
|
1727
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1728
|
+
Effect8.orElseSucceed(() => ({ haveIds: [], needIds: [] }))
|
|
1729
|
+
);
|
|
1730
|
+
yield* Effect8.logDebug("Relay reconciliation result", {
|
|
1731
|
+
relay: url,
|
|
1732
|
+
need: needIds.length,
|
|
1733
|
+
have: haveIds.length
|
|
1734
|
+
});
|
|
1735
|
+
const events = needIds.length > 0 ? yield* relay.fetchEvents(needIds, url).pipe(
|
|
1736
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1737
|
+
Effect8.orElseSucceed(() => [])
|
|
1738
|
+
) : [];
|
|
1739
|
+
return {
|
|
1740
|
+
events,
|
|
1741
|
+
haveIds: haveIds.map((id) => ({ id, url }))
|
|
1742
|
+
};
|
|
1743
|
+
}).pipe(Effect8.withLogSpan("tablinum.reconcileRelay"));
|
|
1744
|
+
const syncAllRelays = (pubKeys, changedCollections) => Effect8.gen(function* () {
|
|
1745
|
+
const results = yield* Effect8.forEach(
|
|
1746
|
+
relayUrls,
|
|
1747
|
+
(url) => reconcileRelay(url, pubKeys),
|
|
1748
|
+
{ concurrency: "unbounded" }
|
|
1749
|
+
);
|
|
1750
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1751
|
+
const allGiftWraps = [];
|
|
1752
|
+
for (const { events } of results) {
|
|
1753
|
+
for (const ev of events) {
|
|
1754
|
+
if (!seen.has(ev.id)) {
|
|
1755
|
+
seen.add(ev.id);
|
|
1756
|
+
allGiftWraps.push(ev);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const unwrapped = [];
|
|
1761
|
+
for (const gw of allGiftWraps) {
|
|
1762
|
+
const result = yield* unwrapGiftWrap(gw).pipe(Effect8.orElseSucceed(() => null));
|
|
1763
|
+
if (result) unwrapped.push(result);
|
|
1764
|
+
}
|
|
1765
|
+
unwrapped.sort((a, b) => a.rumor.created_at - b.rumor.created_at || (a.rumor.id < b.rumor.id ? -1 : 1));
|
|
1766
|
+
for (const event of unwrapped) {
|
|
1767
|
+
const collection2 = yield* applyUnwrappedEvent(event).pipe(
|
|
1768
|
+
Effect8.orElseSucceed(() => null)
|
|
1769
|
+
);
|
|
1770
|
+
if (collection2) changedCollections.add(collection2);
|
|
1771
|
+
}
|
|
1772
|
+
const allHaveIds = results.flatMap((r) => r.haveIds);
|
|
1773
|
+
yield* Effect8.forEach(
|
|
1774
|
+
allHaveIds,
|
|
1775
|
+
({ id, url }) => Effect8.gen(function* () {
|
|
1776
|
+
const gw = yield* storage.getGiftWrap(id);
|
|
1777
|
+
if (!gw?.event) return;
|
|
1778
|
+
yield* relay.publish(gw.event, [url]).pipe(
|
|
1779
|
+
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1780
|
+
Effect8.ignore
|
|
1781
|
+
);
|
|
1782
|
+
}),
|
|
1783
|
+
{ concurrency: "unbounded", discard: true }
|
|
1784
|
+
);
|
|
1785
|
+
}).pipe(Effect8.withLogSpan("tablinum.syncAllRelays"));
|
|
1786
|
+
const processGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1787
|
+
const uw = yield* unwrapGiftWrap(remoteGw);
|
|
1788
|
+
if (!uw) return null;
|
|
1789
|
+
return yield* applyUnwrappedEvent(uw);
|
|
1790
|
+
});
|
|
1705
1791
|
const processRealtimeGiftWrap = (remoteGw) => Effect8.gen(function* () {
|
|
1706
1792
|
const collection2 = yield* processGiftWrap(remoteGw).pipe(Effect8.orElseSucceed(() => null));
|
|
1707
1793
|
if (collection2) {
|
|
@@ -1770,55 +1856,8 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1770
1856
|
Effect8.ignore
|
|
1771
1857
|
);
|
|
1772
1858
|
}),
|
|
1773
|
-
{ discard: true }
|
|
1859
|
+
{ concurrency: "unbounded", discard: true }
|
|
1774
1860
|
);
|
|
1775
|
-
const syncRelay = (url, pubKeys, changedCollections) => Effect8.gen(function* () {
|
|
1776
|
-
yield* Effect8.logDebug("Syncing relay", { relay: url });
|
|
1777
|
-
const reconcileResult = yield* Effect8.result(
|
|
1778
|
-
reconcileWithRelay(storage, relay, url, Array.from(pubKeys))
|
|
1779
|
-
);
|
|
1780
|
-
if (reconcileResult._tag === "Failure") {
|
|
1781
|
-
onSyncError?.(reconcileResult.failure);
|
|
1782
|
-
return;
|
|
1783
|
-
}
|
|
1784
|
-
const { haveIds, needIds } = reconcileResult.success;
|
|
1785
|
-
yield* Effect8.logDebug("Relay reconciliation result", {
|
|
1786
|
-
relay: url,
|
|
1787
|
-
need: needIds.length,
|
|
1788
|
-
have: haveIds.length
|
|
1789
|
-
});
|
|
1790
|
-
if (needIds.length > 0) {
|
|
1791
|
-
const fetched = yield* relay.fetchEvents(needIds, url).pipe(
|
|
1792
|
-
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1793
|
-
Effect8.orElseSucceed(() => [])
|
|
1794
|
-
);
|
|
1795
|
-
const sorted = [...fetched].sort((a, b) => a.created_at - b.created_at);
|
|
1796
|
-
yield* Effect8.forEach(
|
|
1797
|
-
sorted,
|
|
1798
|
-
(remoteGw) => Effect8.gen(function* () {
|
|
1799
|
-
const collection2 = yield* processGiftWrap(remoteGw).pipe(
|
|
1800
|
-
Effect8.orElseSucceed(() => null)
|
|
1801
|
-
);
|
|
1802
|
-
if (collection2) changedCollections.add(collection2);
|
|
1803
|
-
}),
|
|
1804
|
-
{ discard: true }
|
|
1805
|
-
);
|
|
1806
|
-
}
|
|
1807
|
-
if (haveIds.length > 0) {
|
|
1808
|
-
yield* Effect8.forEach(
|
|
1809
|
-
haveIds,
|
|
1810
|
-
(id) => Effect8.gen(function* () {
|
|
1811
|
-
const gw = yield* storage.getGiftWrap(id);
|
|
1812
|
-
if (!gw?.event) return;
|
|
1813
|
-
yield* relay.publish(gw.event, [url]).pipe(
|
|
1814
|
-
Effect8.tapError((err) => Effect8.sync(() => onSyncError?.(err))),
|
|
1815
|
-
Effect8.ignore
|
|
1816
|
-
);
|
|
1817
|
-
}),
|
|
1818
|
-
{ discard: true }
|
|
1819
|
-
);
|
|
1820
|
-
}
|
|
1821
|
-
}).pipe(Effect8.withLogSpan("tablinum.syncRelay"));
|
|
1822
1861
|
let healingActive = false;
|
|
1823
1862
|
const healingEffect = Effect8.gen(function* () {
|
|
1824
1863
|
if (!healingActive) return;
|
|
@@ -1828,9 +1867,7 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1828
1867
|
yield* Effect8.gen(function* () {
|
|
1829
1868
|
const pubKeys = getSubscriptionPubKeys();
|
|
1830
1869
|
const changedCollections = /* @__PURE__ */ new Set();
|
|
1831
|
-
yield*
|
|
1832
|
-
discard: true
|
|
1833
|
-
});
|
|
1870
|
+
yield* syncAllRelays(pubKeys, changedCollections);
|
|
1834
1871
|
if (changedCollections.size > 0) {
|
|
1835
1872
|
yield* notifyReplayComplete(watchCtx, [...changedCollections]);
|
|
1836
1873
|
}
|
|
@@ -1844,9 +1881,7 @@ function createSyncHandle(storage, giftWrapHandle, relay, publishQueue, syncStat
|
|
|
1844
1881
|
const changedCollections = /* @__PURE__ */ new Set();
|
|
1845
1882
|
yield* Effect8.gen(function* () {
|
|
1846
1883
|
const pubKeys = getSubscriptionPubKeys();
|
|
1847
|
-
yield*
|
|
1848
|
-
discard: true
|
|
1849
|
-
});
|
|
1884
|
+
yield* syncAllRelays(pubKeys, changedCollections);
|
|
1850
1885
|
yield* publishQueue.flush(relayUrls).pipe(Effect8.ignore);
|
|
1851
1886
|
}).pipe(
|
|
1852
1887
|
Effect8.ensuring(
|
package/dist/sync/gift-wrap.d.ts
CHANGED
|
@@ -3,11 +3,10 @@ import { unwrapEvent } from "nostr-tools/nip59";
|
|
|
3
3
|
import type { NostrEvent, UnsignedEvent } from "nostr-tools/pure";
|
|
4
4
|
import { CryptoError } from "../errors.ts";
|
|
5
5
|
import type { EpochStore } from "../db/epoch.ts";
|
|
6
|
-
type Rumor = ReturnType<typeof unwrapEvent>;
|
|
6
|
+
export type Rumor = ReturnType<typeof unwrapEvent>;
|
|
7
7
|
export interface GiftWrapHandle {
|
|
8
8
|
readonly wrap: (rumor: Partial<UnsignedEvent>) => Effect.Effect<NostrEvent, CryptoError>;
|
|
9
9
|
readonly unwrap: (giftWrap: NostrEvent) => Effect.Effect<Rumor, CryptoError>;
|
|
10
10
|
}
|
|
11
11
|
export declare function createGiftWrapHandle(senderPrivateKey: Uint8Array, recipientPublicKey: string, decryptionPrivateKey: Uint8Array): GiftWrapHandle;
|
|
12
12
|
export declare function createEpochGiftWrapHandle(senderPrivateKey: Uint8Array, epochStore: EpochStore): GiftWrapHandle;
|
|
13
|
-
export {};
|