@unicitylabs/sphere-sdk 0.6.0 → 0.6.2
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/README.md +8 -0
- package/dist/core/index.cjs +53 -11
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +37 -1
- package/dist/core/index.d.ts +37 -1
- package/dist/core/index.js +53 -11
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +28 -0
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +28 -0
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +28 -0
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +11 -0
- package/dist/impl/nodejs/index.d.ts +11 -0
- package/dist/impl/nodejs/index.js +28 -0
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +53 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +53 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/dist/l1/index.d.cts +0 -717
- package/dist/l1/index.d.ts +0 -717
package/dist/index.d.cts
CHANGED
|
@@ -30,6 +30,8 @@ interface GroupData {
|
|
|
30
30
|
unreadCount?: number;
|
|
31
31
|
lastMessageTime?: number;
|
|
32
32
|
lastMessageText?: string;
|
|
33
|
+
/** Only admins and moderators can post; other members are read-only (NIP-29 "write-restricted" tag) */
|
|
34
|
+
writeRestricted?: boolean;
|
|
33
35
|
/** When the current user joined this group locally (used to filter old events) */
|
|
34
36
|
localJoinedAt?: number;
|
|
35
37
|
}
|
|
@@ -62,11 +64,24 @@ interface GroupChatModuleConfig {
|
|
|
62
64
|
/** Max reconnect attempts (default: 5) */
|
|
63
65
|
maxReconnectAttempts?: number;
|
|
64
66
|
}
|
|
67
|
+
interface GroupMessagesPage {
|
|
68
|
+
messages: GroupMessageData[];
|
|
69
|
+
hasMore: boolean;
|
|
70
|
+
oldestTimestamp: number | null;
|
|
71
|
+
}
|
|
72
|
+
interface GetGroupMessagesPageOptions {
|
|
73
|
+
/** Max messages to return (default: 20) */
|
|
74
|
+
limit?: number;
|
|
75
|
+
/** Return messages older than this timestamp */
|
|
76
|
+
before?: number;
|
|
77
|
+
}
|
|
65
78
|
interface CreateGroupOptions {
|
|
66
79
|
name: string;
|
|
67
80
|
description?: string;
|
|
68
81
|
picture?: string;
|
|
69
82
|
visibility?: GroupVisibility;
|
|
83
|
+
/** Only admins and moderators can post; other members are read-only */
|
|
84
|
+
writeRestricted?: boolean;
|
|
70
85
|
}
|
|
71
86
|
|
|
72
87
|
/**
|
|
@@ -1299,7 +1314,7 @@ interface TrackedAddress extends TrackedAddressEntry {
|
|
|
1299
1314
|
/** Primary nametag (from nametag cache, without @ prefix) */
|
|
1300
1315
|
readonly nametag?: string;
|
|
1301
1316
|
}
|
|
1302
|
-
type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'history:updated';
|
|
1317
|
+
type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'groupchat:ready' | 'communications:ready' | 'history:updated';
|
|
1303
1318
|
interface SphereEventMap {
|
|
1304
1319
|
'transfer:incoming': IncomingTransfer;
|
|
1305
1320
|
'transfer:confirmed': TransferResult;
|
|
@@ -1399,6 +1414,12 @@ interface SphereEventMap {
|
|
|
1399
1414
|
'groupchat:connection': {
|
|
1400
1415
|
connected: boolean;
|
|
1401
1416
|
};
|
|
1417
|
+
'groupchat:ready': {
|
|
1418
|
+
groupCount: number;
|
|
1419
|
+
};
|
|
1420
|
+
'communications:ready': {
|
|
1421
|
+
conversationCount: number;
|
|
1422
|
+
};
|
|
1402
1423
|
'history:updated': TransactionHistoryEntry;
|
|
1403
1424
|
}
|
|
1404
1425
|
type SphereEventHandler<T extends SphereEventType> = (data: SphereEventMap[T]) => void;
|
|
@@ -1963,6 +1984,14 @@ interface TransportProvider extends BaseProvider {
|
|
|
1963
1984
|
* and resolves after EOSE (End Of Stored Events).
|
|
1964
1985
|
*/
|
|
1965
1986
|
fetchPendingEvents?(): Promise<void>;
|
|
1987
|
+
/**
|
|
1988
|
+
* Register a handler to be called when the chat subscription receives EOSE
|
|
1989
|
+
* (End Of Stored Events), indicating that historical DMs have been delivered.
|
|
1990
|
+
* The handler fires at most once per subscription lifecycle.
|
|
1991
|
+
*
|
|
1992
|
+
* @returns Unsubscribe function
|
|
1993
|
+
*/
|
|
1994
|
+
onChatReady?(handler: () => void): () => void;
|
|
1966
1995
|
}
|
|
1967
1996
|
/**
|
|
1968
1997
|
* Payload for sending instant split bundles
|
|
@@ -3652,6 +3681,7 @@ declare class GroupChatModule {
|
|
|
3652
3681
|
getGroups(): GroupData[];
|
|
3653
3682
|
getGroup(groupId: string): GroupData | null;
|
|
3654
3683
|
getMessages(groupId: string): GroupMessageData[];
|
|
3684
|
+
getMessagesPage(groupId: string, options?: GetGroupMessagesPageOptions): GroupMessagesPage;
|
|
3655
3685
|
getMembers(groupId: string): GroupMemberData[];
|
|
3656
3686
|
getMember(groupId: string, pubkey: string): GroupMemberData | null;
|
|
3657
3687
|
getTotalUnreadCount(): number;
|
|
@@ -3667,6 +3697,12 @@ declare class GroupChatModule {
|
|
|
3667
3697
|
*/
|
|
3668
3698
|
canModerateGroup(groupId: string): Promise<boolean>;
|
|
3669
3699
|
isCurrentUserRelayAdmin(): Promise<boolean>;
|
|
3700
|
+
/**
|
|
3701
|
+
* Check if current user can write messages to a group.
|
|
3702
|
+
* For write-restricted groups, only admins/moderators can post.
|
|
3703
|
+
* For normal groups, any member can post.
|
|
3704
|
+
*/
|
|
3705
|
+
canWriteToGroup(groupId: string): boolean;
|
|
3670
3706
|
getCurrentUserRole(groupId: string): GroupRole | null;
|
|
3671
3707
|
onMessage(handler: (message: GroupMessageData) => void): () => void;
|
|
3672
3708
|
getRelayUrls(): string[];
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ interface GroupData {
|
|
|
30
30
|
unreadCount?: number;
|
|
31
31
|
lastMessageTime?: number;
|
|
32
32
|
lastMessageText?: string;
|
|
33
|
+
/** Only admins and moderators can post; other members are read-only (NIP-29 "write-restricted" tag) */
|
|
34
|
+
writeRestricted?: boolean;
|
|
33
35
|
/** When the current user joined this group locally (used to filter old events) */
|
|
34
36
|
localJoinedAt?: number;
|
|
35
37
|
}
|
|
@@ -62,11 +64,24 @@ interface GroupChatModuleConfig {
|
|
|
62
64
|
/** Max reconnect attempts (default: 5) */
|
|
63
65
|
maxReconnectAttempts?: number;
|
|
64
66
|
}
|
|
67
|
+
interface GroupMessagesPage {
|
|
68
|
+
messages: GroupMessageData[];
|
|
69
|
+
hasMore: boolean;
|
|
70
|
+
oldestTimestamp: number | null;
|
|
71
|
+
}
|
|
72
|
+
interface GetGroupMessagesPageOptions {
|
|
73
|
+
/** Max messages to return (default: 20) */
|
|
74
|
+
limit?: number;
|
|
75
|
+
/** Return messages older than this timestamp */
|
|
76
|
+
before?: number;
|
|
77
|
+
}
|
|
65
78
|
interface CreateGroupOptions {
|
|
66
79
|
name: string;
|
|
67
80
|
description?: string;
|
|
68
81
|
picture?: string;
|
|
69
82
|
visibility?: GroupVisibility;
|
|
83
|
+
/** Only admins and moderators can post; other members are read-only */
|
|
84
|
+
writeRestricted?: boolean;
|
|
70
85
|
}
|
|
71
86
|
|
|
72
87
|
/**
|
|
@@ -1299,7 +1314,7 @@ interface TrackedAddress extends TrackedAddressEntry {
|
|
|
1299
1314
|
/** Primary nametag (from nametag cache, without @ prefix) */
|
|
1300
1315
|
readonly nametag?: string;
|
|
1301
1316
|
}
|
|
1302
|
-
type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'history:updated';
|
|
1317
|
+
type SphereEventType = 'transfer:incoming' | 'transfer:confirmed' | 'transfer:failed' | 'payment_request:incoming' | 'payment_request:accepted' | 'payment_request:rejected' | 'payment_request:paid' | 'payment_request:response' | 'message:dm' | 'message:read' | 'message:typing' | 'composing:started' | 'message:broadcast' | 'sync:started' | 'sync:completed' | 'sync:provider' | 'sync:error' | 'connection:changed' | 'nametag:registered' | 'nametag:recovered' | 'identity:changed' | 'address:activated' | 'address:hidden' | 'address:unhidden' | 'sync:remote-update' | 'groupchat:message' | 'groupchat:joined' | 'groupchat:left' | 'groupchat:kicked' | 'groupchat:group_deleted' | 'groupchat:updated' | 'groupchat:connection' | 'groupchat:ready' | 'communications:ready' | 'history:updated';
|
|
1303
1318
|
interface SphereEventMap {
|
|
1304
1319
|
'transfer:incoming': IncomingTransfer;
|
|
1305
1320
|
'transfer:confirmed': TransferResult;
|
|
@@ -1399,6 +1414,12 @@ interface SphereEventMap {
|
|
|
1399
1414
|
'groupchat:connection': {
|
|
1400
1415
|
connected: boolean;
|
|
1401
1416
|
};
|
|
1417
|
+
'groupchat:ready': {
|
|
1418
|
+
groupCount: number;
|
|
1419
|
+
};
|
|
1420
|
+
'communications:ready': {
|
|
1421
|
+
conversationCount: number;
|
|
1422
|
+
};
|
|
1402
1423
|
'history:updated': TransactionHistoryEntry;
|
|
1403
1424
|
}
|
|
1404
1425
|
type SphereEventHandler<T extends SphereEventType> = (data: SphereEventMap[T]) => void;
|
|
@@ -1963,6 +1984,14 @@ interface TransportProvider extends BaseProvider {
|
|
|
1963
1984
|
* and resolves after EOSE (End Of Stored Events).
|
|
1964
1985
|
*/
|
|
1965
1986
|
fetchPendingEvents?(): Promise<void>;
|
|
1987
|
+
/**
|
|
1988
|
+
* Register a handler to be called when the chat subscription receives EOSE
|
|
1989
|
+
* (End Of Stored Events), indicating that historical DMs have been delivered.
|
|
1990
|
+
* The handler fires at most once per subscription lifecycle.
|
|
1991
|
+
*
|
|
1992
|
+
* @returns Unsubscribe function
|
|
1993
|
+
*/
|
|
1994
|
+
onChatReady?(handler: () => void): () => void;
|
|
1966
1995
|
}
|
|
1967
1996
|
/**
|
|
1968
1997
|
* Payload for sending instant split bundles
|
|
@@ -3652,6 +3681,7 @@ declare class GroupChatModule {
|
|
|
3652
3681
|
getGroups(): GroupData[];
|
|
3653
3682
|
getGroup(groupId: string): GroupData | null;
|
|
3654
3683
|
getMessages(groupId: string): GroupMessageData[];
|
|
3684
|
+
getMessagesPage(groupId: string, options?: GetGroupMessagesPageOptions): GroupMessagesPage;
|
|
3655
3685
|
getMembers(groupId: string): GroupMemberData[];
|
|
3656
3686
|
getMember(groupId: string, pubkey: string): GroupMemberData | null;
|
|
3657
3687
|
getTotalUnreadCount(): number;
|
|
@@ -3667,6 +3697,12 @@ declare class GroupChatModule {
|
|
|
3667
3697
|
*/
|
|
3668
3698
|
canModerateGroup(groupId: string): Promise<boolean>;
|
|
3669
3699
|
isCurrentUserRelayAdmin(): Promise<boolean>;
|
|
3700
|
+
/**
|
|
3701
|
+
* Check if current user can write messages to a group.
|
|
3702
|
+
* For write-restricted groups, only admins/moderators can post.
|
|
3703
|
+
* For normal groups, any member can post.
|
|
3704
|
+
*/
|
|
3705
|
+
canWriteToGroup(groupId: string): boolean;
|
|
3670
3706
|
getCurrentUserRole(groupId: string): GroupRole | null;
|
|
3671
3707
|
onMessage(handler: (message: GroupMessageData) => void): () => void;
|
|
3672
3708
|
getRelayUrls(): string[];
|
package/dist/index.js
CHANGED
|
@@ -8594,6 +8594,12 @@ var CommunicationsModule = class {
|
|
|
8594
8594
|
this.unsubscribeComposing = deps.transport.onComposing?.((indicator) => {
|
|
8595
8595
|
this.handleComposingIndicator(indicator);
|
|
8596
8596
|
}) ?? null;
|
|
8597
|
+
if (deps.transport.onChatReady) {
|
|
8598
|
+
deps.transport.onChatReady(() => {
|
|
8599
|
+
const conversations = this.getConversations();
|
|
8600
|
+
deps.emitEvent("communications:ready", { conversationCount: conversations.size });
|
|
8601
|
+
});
|
|
8602
|
+
}
|
|
8597
8603
|
}
|
|
8598
8604
|
/**
|
|
8599
8605
|
* Load messages from storage.
|
|
@@ -9240,6 +9246,7 @@ var GroupChatModule = class {
|
|
|
9240
9246
|
await this.subscribeToJoinedGroups();
|
|
9241
9247
|
}
|
|
9242
9248
|
this.deps.emitEvent("groupchat:connection", { connected: true });
|
|
9249
|
+
this.deps.emitEvent("groupchat:ready", { groupCount: this.groups.size });
|
|
9243
9250
|
} catch (error) {
|
|
9244
9251
|
logger.error("GroupChat", "Failed to connect to relays", error);
|
|
9245
9252
|
this.deps.emitEvent("groupchat:connection", { connected: false });
|
|
@@ -9358,17 +9365,23 @@ var GroupChatModule = class {
|
|
|
9358
9365
|
const group = this.groups.get(groupId);
|
|
9359
9366
|
if (!group) return;
|
|
9360
9367
|
if (event.kind === NIP29_KINDS.GROUP_METADATA) {
|
|
9361
|
-
if (
|
|
9362
|
-
|
|
9363
|
-
|
|
9364
|
-
|
|
9365
|
-
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9368
|
+
if (event.content && event.content.trim()) {
|
|
9369
|
+
try {
|
|
9370
|
+
const metadata = JSON.parse(event.content);
|
|
9371
|
+
group.name = metadata.name || group.name;
|
|
9372
|
+
group.description = metadata.about || group.description;
|
|
9373
|
+
group.picture = metadata.picture || group.picture;
|
|
9374
|
+
if (metadata["write-restricted"] === true) group.writeRestricted = true;
|
|
9375
|
+
else group.writeRestricted = void 0;
|
|
9376
|
+
} catch {
|
|
9377
|
+
}
|
|
9371
9378
|
}
|
|
9379
|
+
for (const tag of event.tags) {
|
|
9380
|
+
if (tag[0] === "write-restricted") group.writeRestricted = true;
|
|
9381
|
+
}
|
|
9382
|
+
group.updatedAt = event.created_at * 1e3;
|
|
9383
|
+
this.groups.set(groupId, group);
|
|
9384
|
+
this.persistGroups();
|
|
9372
9385
|
} else if (event.kind === NIP29_KINDS.GROUP_MEMBERS) {
|
|
9373
9386
|
this.updateMembersFromEvent(groupId, event);
|
|
9374
9387
|
} else if (event.kind === NIP29_KINDS.GROUP_ADMINS) {
|
|
@@ -9672,7 +9685,8 @@ var GroupChatModule = class {
|
|
|
9672
9685
|
picture: options.picture,
|
|
9673
9686
|
closed: true,
|
|
9674
9687
|
private: isPrivate,
|
|
9675
|
-
hidden: isPrivate
|
|
9688
|
+
hidden: isPrivate,
|
|
9689
|
+
...options.writeRestricted ? { "write-restricted": true } : {}
|
|
9676
9690
|
})
|
|
9677
9691
|
});
|
|
9678
9692
|
if (!eventId) return null;
|
|
@@ -9867,6 +9881,19 @@ var GroupChatModule = class {
|
|
|
9867
9881
|
getMessages(groupId) {
|
|
9868
9882
|
return (this.messages.get(groupId) || []).sort((a, b) => a.timestamp - b.timestamp);
|
|
9869
9883
|
}
|
|
9884
|
+
getMessagesPage(groupId, options) {
|
|
9885
|
+
const limit = options?.limit ?? 20;
|
|
9886
|
+
const before = options?.before ?? Infinity;
|
|
9887
|
+
const groupMessages = this.messages.get(groupId) ?? [];
|
|
9888
|
+
const filtered = groupMessages.filter((m) => m.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
|
|
9889
|
+
const page = filtered.slice(0, limit);
|
|
9890
|
+
return {
|
|
9891
|
+
messages: page.reverse(),
|
|
9892
|
+
// chronological order
|
|
9893
|
+
hasMore: filtered.length > limit,
|
|
9894
|
+
oldestTimestamp: page.length > 0 ? page[0].timestamp : null
|
|
9895
|
+
};
|
|
9896
|
+
}
|
|
9870
9897
|
getMembers(groupId) {
|
|
9871
9898
|
return (this.members.get(groupId) || []).sort((a, b) => a.joinedAt - b.joinedAt);
|
|
9872
9899
|
}
|
|
@@ -9973,6 +10000,17 @@ var GroupChatModule = class {
|
|
|
9973
10000
|
const admins = await this.fetchRelayAdmins();
|
|
9974
10001
|
return admins.has(myPubkey);
|
|
9975
10002
|
}
|
|
10003
|
+
/**
|
|
10004
|
+
* Check if current user can write messages to a group.
|
|
10005
|
+
* For write-restricted groups, only admins/moderators can post.
|
|
10006
|
+
* For normal groups, any member can post.
|
|
10007
|
+
*/
|
|
10008
|
+
canWriteToGroup(groupId) {
|
|
10009
|
+
const group = this.groups.get(groupId);
|
|
10010
|
+
if (!group) return false;
|
|
10011
|
+
if (!group.writeRestricted) return true;
|
|
10012
|
+
return this.isCurrentUserModerator(groupId);
|
|
10013
|
+
}
|
|
9976
10014
|
getCurrentUserRole(groupId) {
|
|
9977
10015
|
const myPubkey = this.getMyPublicKey();
|
|
9978
10016
|
if (!myPubkey) return null;
|
|
@@ -10347,6 +10385,7 @@ var GroupChatModule = class {
|
|
|
10347
10385
|
let description;
|
|
10348
10386
|
let picture;
|
|
10349
10387
|
let isPrivate = false;
|
|
10388
|
+
let writeRestricted = false;
|
|
10350
10389
|
if (event.content && event.content.trim()) {
|
|
10351
10390
|
try {
|
|
10352
10391
|
const metadata = JSON.parse(event.content);
|
|
@@ -10354,6 +10393,7 @@ var GroupChatModule = class {
|
|
|
10354
10393
|
description = metadata.about || metadata.description;
|
|
10355
10394
|
picture = metadata.picture;
|
|
10356
10395
|
isPrivate = metadata.private === true;
|
|
10396
|
+
if (metadata["write-restricted"] === true) writeRestricted = true;
|
|
10357
10397
|
} catch {
|
|
10358
10398
|
}
|
|
10359
10399
|
}
|
|
@@ -10363,6 +10403,7 @@ var GroupChatModule = class {
|
|
|
10363
10403
|
if (tag[0] === "picture" && tag[1]) picture = tag[1];
|
|
10364
10404
|
if (tag[0] === "private") isPrivate = true;
|
|
10365
10405
|
if (tag[0] === "public" && tag[1] === "false") isPrivate = true;
|
|
10406
|
+
if (tag[0] === "write-restricted") writeRestricted = true;
|
|
10366
10407
|
}
|
|
10367
10408
|
return {
|
|
10368
10409
|
id: groupId,
|
|
@@ -10371,6 +10412,7 @@ var GroupChatModule = class {
|
|
|
10371
10412
|
description,
|
|
10372
10413
|
picture,
|
|
10373
10414
|
visibility: isPrivate ? GroupVisibility.PRIVATE : GroupVisibility.PUBLIC,
|
|
10415
|
+
writeRestricted: writeRestricted || void 0,
|
|
10374
10416
|
createdAt: event.created_at * 1e3
|
|
10375
10417
|
};
|
|
10376
10418
|
} catch {
|