@unicitylabs/sphere-sdk 0.6.10-dev.4 → 0.6.10-dev.6
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/core/index.cjs +102 -81
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +102 -81
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +2 -2
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +2 -2
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +2 -2
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.js +2 -2
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +102 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +102 -81
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +32 -0
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.js +32 -0
- package/dist/l1/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -559,6 +559,33 @@ function waitForConnection() {
|
|
|
559
559
|
connectionCallbacks.push(callback);
|
|
560
560
|
});
|
|
561
561
|
}
|
|
562
|
+
function startPingTimer() {
|
|
563
|
+
stopPingTimer();
|
|
564
|
+
pingTimer = setInterval(() => {
|
|
565
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) return;
|
|
566
|
+
try {
|
|
567
|
+
const id = ++requestId;
|
|
568
|
+
ws.send(JSON.stringify({ jsonrpc: "2.0", id, method: "server.ping", params: [] }));
|
|
569
|
+
pending[id] = {
|
|
570
|
+
resolve: () => {
|
|
571
|
+
},
|
|
572
|
+
reject: () => {
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
const timeoutId = setTimeout(() => {
|
|
576
|
+
delete pending[id];
|
|
577
|
+
}, 1e4);
|
|
578
|
+
pending[id].timeoutId = timeoutId;
|
|
579
|
+
} catch {
|
|
580
|
+
}
|
|
581
|
+
}, PING_INTERVAL);
|
|
582
|
+
}
|
|
583
|
+
function stopPingTimer() {
|
|
584
|
+
if (pingTimer) {
|
|
585
|
+
clearInterval(pingTimer);
|
|
586
|
+
pingTimer = null;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
562
589
|
function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
563
590
|
if (isConnected) {
|
|
564
591
|
return Promise.resolve();
|
|
@@ -581,6 +608,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
581
608
|
isConnected = true;
|
|
582
609
|
isConnecting = false;
|
|
583
610
|
reconnectAttempts = 0;
|
|
611
|
+
startPingTimer();
|
|
584
612
|
hasResolved = true;
|
|
585
613
|
resolve();
|
|
586
614
|
connectionCallbacks.forEach((cb) => {
|
|
@@ -592,6 +620,7 @@ function connect(endpoint = DEFAULT_ENDPOINT) {
|
|
|
592
620
|
ws.onclose = () => {
|
|
593
621
|
isConnected = false;
|
|
594
622
|
isBlockSubscribed = false;
|
|
623
|
+
stopPingTimer();
|
|
595
624
|
Object.values(pending).forEach((req) => {
|
|
596
625
|
if (req.timeoutId) clearTimeout(req.timeoutId);
|
|
597
626
|
req.reject(new Error("WebSocket connection closed"));
|
|
@@ -774,6 +803,7 @@ async function getCurrentBlockHeight() {
|
|
|
774
803
|
}
|
|
775
804
|
}
|
|
776
805
|
function disconnect() {
|
|
806
|
+
stopPingTimer();
|
|
777
807
|
if (ws) {
|
|
778
808
|
intentionalClose = true;
|
|
779
809
|
ws.close();
|
|
@@ -794,7 +824,7 @@ function disconnect() {
|
|
|
794
824
|
blockSubscribers.length = 0;
|
|
795
825
|
lastBlockHeader = null;
|
|
796
826
|
}
|
|
797
|
-
var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT;
|
|
827
|
+
var DEFAULT_ENDPOINT, ws, isConnected, isConnecting, requestId, intentionalClose, reconnectAttempts, isBlockSubscribed, lastBlockHeader, pingTimer, pending, blockSubscribers, connectionCallbacks, MAX_RECONNECT_ATTEMPTS, BASE_DELAY, MAX_DELAY, RPC_TIMEOUT, CONNECTION_TIMEOUT, PING_INTERVAL;
|
|
798
828
|
var init_network = __esm({
|
|
799
829
|
"l1/network.ts"() {
|
|
800
830
|
"use strict";
|
|
@@ -810,6 +840,7 @@ var init_network = __esm({
|
|
|
810
840
|
reconnectAttempts = 0;
|
|
811
841
|
isBlockSubscribed = false;
|
|
812
842
|
lastBlockHeader = null;
|
|
843
|
+
pingTimer = null;
|
|
813
844
|
pending = {};
|
|
814
845
|
blockSubscribers = [];
|
|
815
846
|
connectionCallbacks = [];
|
|
@@ -818,6 +849,7 @@ var init_network = __esm({
|
|
|
818
849
|
MAX_DELAY = 6e4;
|
|
819
850
|
RPC_TIMEOUT = 3e4;
|
|
820
851
|
CONNECTION_TIMEOUT = 3e4;
|
|
852
|
+
PING_INTERVAL = 3e4;
|
|
821
853
|
}
|
|
822
854
|
});
|
|
823
855
|
|
|
@@ -2612,9 +2644,9 @@ var NostrTransportProvider = class _NostrTransportProvider {
|
|
|
2612
2644
|
if (subId) {
|
|
2613
2645
|
this.nostrClient?.unsubscribe(subId);
|
|
2614
2646
|
}
|
|
2615
|
-
logger.warn("Nostr", `queryEvents timed out after
|
|
2647
|
+
logger.warn("Nostr", `queryEvents timed out after 15s, returning ${events.length} event(s)`, { kinds: filterObj.kinds, limit: filterObj.limit });
|
|
2616
2648
|
resolve(events);
|
|
2617
|
-
},
|
|
2649
|
+
}, 15e3);
|
|
2618
2650
|
const subId = this.nostrClient.subscribe(filter, {
|
|
2619
2651
|
onEvent: (event) => {
|
|
2620
2652
|
events.push({
|
|
@@ -12530,6 +12562,15 @@ var GroupChatModule = class {
|
|
|
12530
12562
|
}
|
|
12531
12563
|
destroy() {
|
|
12532
12564
|
this.destroyConnection();
|
|
12565
|
+
if (this.persistTimer) {
|
|
12566
|
+
clearTimeout(this.persistTimer);
|
|
12567
|
+
this.persistTimer = null;
|
|
12568
|
+
if (this.deps) {
|
|
12569
|
+
this.doPersistAll().catch(
|
|
12570
|
+
(err) => logger.debug("GroupChat", "Persist on destroy failed", err)
|
|
12571
|
+
);
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12533
12574
|
this.groups.clear();
|
|
12534
12575
|
this.messages.clear();
|
|
12535
12576
|
this.members.clear();
|
|
@@ -12538,10 +12579,7 @@ var GroupChatModule = class {
|
|
|
12538
12579
|
this.messageHandlers.clear();
|
|
12539
12580
|
this.relayAdminPubkeys = null;
|
|
12540
12581
|
this.relayAdminFetchPromise = null;
|
|
12541
|
-
|
|
12542
|
-
clearTimeout(this.persistTimer);
|
|
12543
|
-
this.persistTimer = null;
|
|
12544
|
-
}
|
|
12582
|
+
this.persistPromise = null;
|
|
12545
12583
|
this.deps = null;
|
|
12546
12584
|
}
|
|
12547
12585
|
destroyConnection() {
|
|
@@ -12666,12 +12704,12 @@ var GroupChatModule = class {
|
|
|
12666
12704
|
if (!this.client) return;
|
|
12667
12705
|
const groupIds = Array.from(this.groups.keys());
|
|
12668
12706
|
if (groupIds.length === 0) return;
|
|
12669
|
-
const
|
|
12707
|
+
const sinceTimestamp = this.getLatestKnownTimestamp(groupIds);
|
|
12670
12708
|
this.trackSubscription(
|
|
12671
12709
|
createNip29Filter({
|
|
12672
12710
|
kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
|
|
12673
12711
|
"#h": groupIds,
|
|
12674
|
-
...
|
|
12712
|
+
...sinceTimestamp ? { since: sinceTimestamp } : {}
|
|
12675
12713
|
}),
|
|
12676
12714
|
{ onEvent: (event) => this.handleGroupEvent(event) }
|
|
12677
12715
|
);
|
|
@@ -12692,12 +12730,12 @@ var GroupChatModule = class {
|
|
|
12692
12730
|
}
|
|
12693
12731
|
subscribeToGroup(groupId) {
|
|
12694
12732
|
if (!this.client) return;
|
|
12695
|
-
const
|
|
12733
|
+
const sinceTimestamp = this.getLatestKnownTimestamp([groupId]);
|
|
12696
12734
|
this.trackSubscription(
|
|
12697
12735
|
createNip29Filter({
|
|
12698
12736
|
kinds: [NIP29_KINDS.CHAT_MESSAGE, NIP29_KINDS.THREAD_ROOT, NIP29_KINDS.THREAD_REPLY],
|
|
12699
12737
|
"#h": [groupId],
|
|
12700
|
-
...
|
|
12738
|
+
...sinceTimestamp ? { since: sinceTimestamp } : {}
|
|
12701
12739
|
}),
|
|
12702
12740
|
{ onEvent: (event) => this.handleGroupEvent(event) }
|
|
12703
12741
|
);
|
|
@@ -12771,7 +12809,7 @@ var GroupChatModule = class {
|
|
|
12771
12809
|
}
|
|
12772
12810
|
group.updatedAt = event.created_at * 1e3;
|
|
12773
12811
|
this.groups.set(groupId, group);
|
|
12774
|
-
this.
|
|
12812
|
+
this.schedulePersist();
|
|
12775
12813
|
} else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {
|
|
12776
12814
|
this.updateMembersFromEvent(groupId, event);
|
|
12777
12815
|
} else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {
|
|
@@ -12792,7 +12830,7 @@ var GroupChatModule = class {
|
|
|
12792
12830
|
}
|
|
12793
12831
|
}
|
|
12794
12832
|
this.deps.emitEvent("groupchat:updated", {});
|
|
12795
|
-
this.
|
|
12833
|
+
this.schedulePersist();
|
|
12796
12834
|
} else if (event.kind === NIP29_KINDS.REMOVE_USER) {
|
|
12797
12835
|
if (this.processedEventIds.has(event.id)) return;
|
|
12798
12836
|
const eventTimestampMs = event.created_at * 1e3;
|
|
@@ -12853,7 +12891,7 @@ var GroupChatModule = class {
|
|
|
12853
12891
|
};
|
|
12854
12892
|
this.saveMemberToMemory(member);
|
|
12855
12893
|
}
|
|
12856
|
-
this.
|
|
12894
|
+
this.schedulePersist();
|
|
12857
12895
|
}
|
|
12858
12896
|
updateAdminsFromEvent(groupId, event) {
|
|
12859
12897
|
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
@@ -12873,7 +12911,7 @@ var GroupChatModule = class {
|
|
|
12873
12911
|
});
|
|
12874
12912
|
}
|
|
12875
12913
|
}
|
|
12876
|
-
this.
|
|
12914
|
+
this.schedulePersist();
|
|
12877
12915
|
}
|
|
12878
12916
|
// ===========================================================================
|
|
12879
12917
|
// Group Membership Restoration
|
|
@@ -12884,13 +12922,11 @@ var GroupChatModule = class {
|
|
|
12884
12922
|
if (!myPubkey) return [];
|
|
12885
12923
|
const groupIdsWithMembership = /* @__PURE__ */ new Set();
|
|
12886
12924
|
await this.oneshotSubscription(
|
|
12887
|
-
|
|
12925
|
+
createNip29Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS], "#p": [myPubkey] }),
|
|
12888
12926
|
{
|
|
12889
12927
|
onEvent: (event) => {
|
|
12890
12928
|
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
12891
|
-
if (
|
|
12892
|
-
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
12893
|
-
if (pTags.some((tag) => tag[1] === myPubkey)) {
|
|
12929
|
+
if (groupId) {
|
|
12894
12930
|
groupIdsWithMembership.add(groupId);
|
|
12895
12931
|
}
|
|
12896
12932
|
},
|
|
@@ -12902,22 +12938,24 @@ var GroupChatModule = class {
|
|
|
12902
12938
|
);
|
|
12903
12939
|
if (groupIdsWithMembership.size === 0) return [];
|
|
12904
12940
|
const restoredGroups = [];
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
|
|
12915
|
-
|
|
12941
|
+
await Promise.all(
|
|
12942
|
+
Array.from(groupIdsWithMembership).map(async (groupId) => {
|
|
12943
|
+
if (this.groups.has(groupId)) return;
|
|
12944
|
+
try {
|
|
12945
|
+
const group = await this.fetchGroupMetadataInternal(groupId);
|
|
12946
|
+
if (group) {
|
|
12947
|
+
this.groups.set(groupId, group);
|
|
12948
|
+
restoredGroups.push(group);
|
|
12949
|
+
await Promise.all([
|
|
12950
|
+
this.fetchAndSaveMembers(groupId),
|
|
12951
|
+
this.fetchMessages(groupId)
|
|
12952
|
+
]);
|
|
12953
|
+
}
|
|
12954
|
+
} catch (error) {
|
|
12955
|
+
logger.warn("GroupChat", "Failed to restore group", groupId, error);
|
|
12916
12956
|
}
|
|
12917
|
-
}
|
|
12918
|
-
|
|
12919
|
-
}
|
|
12920
|
-
}
|
|
12957
|
+
})
|
|
12958
|
+
);
|
|
12921
12959
|
if (restoredGroups.length > 0) {
|
|
12922
12960
|
await this.subscribeToJoinedGroups();
|
|
12923
12961
|
this.deps.emitEvent("groupchat:updated", {});
|
|
@@ -12932,47 +12970,24 @@ var GroupChatModule = class {
|
|
|
12932
12970
|
await this.ensureConnected();
|
|
12933
12971
|
if (!this.client) return [];
|
|
12934
12972
|
const groupsMap = /* @__PURE__ */ new Map();
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
const
|
|
12942
|
-
if (
|
|
12943
|
-
|
|
12944
|
-
if (!existing || group.createdAt > existing.createdAt) {
|
|
12945
|
-
groupsMap.set(group.id, group);
|
|
12946
|
-
}
|
|
12947
|
-
}
|
|
12948
|
-
},
|
|
12949
|
-
onComplete: () => {
|
|
12950
|
-
},
|
|
12951
|
-
timeoutMs: 1e4,
|
|
12952
|
-
timeoutLabel: "fetchAvailableGroups(metadata)"
|
|
12953
|
-
}
|
|
12954
|
-
),
|
|
12955
|
-
this.oneshotSubscription(
|
|
12956
|
-
new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_MEMBERS] }),
|
|
12957
|
-
{
|
|
12958
|
-
onEvent: (event) => {
|
|
12959
|
-
const groupId = this.getGroupIdFromMetadataEvent(event);
|
|
12960
|
-
if (groupId) {
|
|
12961
|
-
const pTags = event.tags.filter((t) => t[0] === "p");
|
|
12962
|
-
memberCountsMap.set(groupId, pTags.length);
|
|
12973
|
+
await this.oneshotSubscription(
|
|
12974
|
+
new import_nostr_js_sdk4.Filter({ kinds: [NIP29_KINDS.GROUP_METADATA] }),
|
|
12975
|
+
{
|
|
12976
|
+
onEvent: (event) => {
|
|
12977
|
+
const group = this.parseGroupMetadata(event);
|
|
12978
|
+
if (group && group.visibility === GroupVisibility.PUBLIC) {
|
|
12979
|
+
const existing = groupsMap.get(group.id);
|
|
12980
|
+
if (!existing || group.createdAt > existing.createdAt) {
|
|
12981
|
+
groupsMap.set(group.id, group);
|
|
12963
12982
|
}
|
|
12964
|
-
}
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
for (const [groupId, count] of memberCountsMap) {
|
|
12973
|
-
const group = groupsMap.get(groupId);
|
|
12974
|
-
if (group) group.memberCount = count;
|
|
12975
|
-
}
|
|
12983
|
+
}
|
|
12984
|
+
},
|
|
12985
|
+
onComplete: () => {
|
|
12986
|
+
},
|
|
12987
|
+
timeoutMs: 1e4,
|
|
12988
|
+
timeoutLabel: "fetchAvailableGroups(metadata)"
|
|
12989
|
+
}
|
|
12990
|
+
);
|
|
12976
12991
|
return Array.from(groupsMap.values());
|
|
12977
12992
|
}
|
|
12978
12993
|
async joinGroup(groupId, inviteCode) {
|
|
@@ -13303,7 +13318,7 @@ var GroupChatModule = class {
|
|
|
13303
13318
|
if (group && (group.unreadCount || 0) > 0) {
|
|
13304
13319
|
group.unreadCount = 0;
|
|
13305
13320
|
this.groups.set(groupId, group);
|
|
13306
|
-
this.
|
|
13321
|
+
this.schedulePersist();
|
|
13307
13322
|
}
|
|
13308
13323
|
}
|
|
13309
13324
|
// ===========================================================================
|
|
@@ -13325,7 +13340,7 @@ var GroupChatModule = class {
|
|
|
13325
13340
|
if (eventId) {
|
|
13326
13341
|
this.removeMemberFromMemory(groupId, userPubkey);
|
|
13327
13342
|
this.deps.emitEvent("groupchat:updated", {});
|
|
13328
|
-
this.
|
|
13343
|
+
this.schedulePersist();
|
|
13329
13344
|
return true;
|
|
13330
13345
|
}
|
|
13331
13346
|
return false;
|
|
@@ -13348,7 +13363,7 @@ var GroupChatModule = class {
|
|
|
13348
13363
|
if (eventId) {
|
|
13349
13364
|
this.deleteMessageFromMemory(groupId, messageId);
|
|
13350
13365
|
this.deps.emitEvent("groupchat:updated", {});
|
|
13351
|
-
this.
|
|
13366
|
+
this.schedulePersist();
|
|
13352
13367
|
return true;
|
|
13353
13368
|
}
|
|
13354
13369
|
return false;
|
|
@@ -13428,7 +13443,7 @@ var GroupChatModule = class {
|
|
|
13428
13443
|
* or 0 if no messages exist. Used to set `since` on subscriptions so the relay
|
|
13429
13444
|
* only sends events we don't already have.
|
|
13430
13445
|
*/
|
|
13431
|
-
|
|
13446
|
+
getLatestKnownTimestamp(groupIds) {
|
|
13432
13447
|
let latest = 0;
|
|
13433
13448
|
for (const gid of groupIds) {
|
|
13434
13449
|
const msgs = this.messages.get(gid);
|
|
@@ -13509,7 +13524,7 @@ var GroupChatModule = class {
|
|
|
13509
13524
|
});
|
|
13510
13525
|
}
|
|
13511
13526
|
}
|
|
13512
|
-
this.
|
|
13527
|
+
this.schedulePersist();
|
|
13513
13528
|
}
|
|
13514
13529
|
async fetchGroupMembersInternal(groupId) {
|
|
13515
13530
|
if (!this.client) return [];
|
|
@@ -13636,8 +13651,11 @@ var GroupChatModule = class {
|
|
|
13636
13651
|
addProcessedEventId(eventId) {
|
|
13637
13652
|
this.processedEventIds.add(eventId);
|
|
13638
13653
|
if (this.processedEventIds.size > 1e4) {
|
|
13639
|
-
|
|
13640
|
-
|
|
13654
|
+
let toDelete = 5e3;
|
|
13655
|
+
for (const id of this.processedEventIds) {
|
|
13656
|
+
if (toDelete-- <= 0) break;
|
|
13657
|
+
this.processedEventIds.delete(id);
|
|
13658
|
+
}
|
|
13641
13659
|
}
|
|
13642
13660
|
}
|
|
13643
13661
|
// ===========================================================================
|
|
@@ -13774,6 +13792,7 @@ var GroupChatModule = class {
|
|
|
13774
13792
|
let name = "Unnamed Group";
|
|
13775
13793
|
let description;
|
|
13776
13794
|
let picture;
|
|
13795
|
+
let memberCount;
|
|
13777
13796
|
let isPrivate = false;
|
|
13778
13797
|
let writeRestricted = false;
|
|
13779
13798
|
if (event.content && event.content.trim()) {
|
|
@@ -13794,6 +13813,7 @@ var GroupChatModule = class {
|
|
|
13794
13813
|
if (tag[0] === "private") isPrivate = true;
|
|
13795
13814
|
if (tag[0] === "public" && tag[1] === "false") isPrivate = true;
|
|
13796
13815
|
if (tag[0] === "write-restricted") writeRestricted = true;
|
|
13816
|
+
if (tag[0] === "member_count" && tag[1]) memberCount = parseInt(tag[1], 10) || void 0;
|
|
13797
13817
|
}
|
|
13798
13818
|
return {
|
|
13799
13819
|
id: groupId,
|
|
@@ -13801,6 +13821,7 @@ var GroupChatModule = class {
|
|
|
13801
13821
|
name,
|
|
13802
13822
|
description,
|
|
13803
13823
|
picture,
|
|
13824
|
+
memberCount,
|
|
13804
13825
|
visibility: isPrivate ? GroupVisibility.PRIVATE : GroupVisibility.PUBLIC,
|
|
13805
13826
|
writeRestricted: writeRestricted || void 0,
|
|
13806
13827
|
createdAt: event.created_at * 1e3
|