@unicitylabs/sphere-sdk 0.3.6 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.cjs +61 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +57 -1
- package/dist/core/index.d.ts +57 -1
- package/dist/core/index.js +61 -1
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +167 -32
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +170 -33
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +114 -4
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +49 -0
- package/dist/impl/nodejs/index.d.ts +49 -0
- package/dist/impl/nodejs/index.js +117 -5
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +61 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +61 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -533,36 +533,61 @@ var IndexedDBTokenStorageProvider = class {
|
|
|
533
533
|
return meta !== null;
|
|
534
534
|
}
|
|
535
535
|
async clear() {
|
|
536
|
-
|
|
537
|
-
this.db.close();
|
|
538
|
-
this.db = null;
|
|
539
|
-
}
|
|
540
|
-
this.status = "disconnected";
|
|
541
|
-
const CLEAR_TIMEOUT = 1500;
|
|
542
|
-
const withTimeout = (promise, ms, label) => Promise.race([
|
|
543
|
-
promise,
|
|
544
|
-
new Promise(
|
|
545
|
-
(_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms)
|
|
546
|
-
)
|
|
547
|
-
]);
|
|
548
|
-
const deleteDb = (name) => new Promise((resolve) => {
|
|
549
|
-
const req = indexedDB.deleteDatabase(name);
|
|
550
|
-
req.onsuccess = () => resolve();
|
|
551
|
-
req.onerror = () => resolve();
|
|
552
|
-
req.onblocked = () => resolve();
|
|
553
|
-
});
|
|
536
|
+
const dbNames = [this.dbName];
|
|
554
537
|
try {
|
|
538
|
+
if (this.db) {
|
|
539
|
+
await this.clearStore(STORE_TOKENS);
|
|
540
|
+
await this.clearStore(STORE_META);
|
|
541
|
+
this.db.close();
|
|
542
|
+
this.db = null;
|
|
543
|
+
}
|
|
544
|
+
this.status = "disconnected";
|
|
555
545
|
if (typeof indexedDB.databases === "function") {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
546
|
+
try {
|
|
547
|
+
const dbs = await Promise.race([
|
|
548
|
+
indexedDB.databases(),
|
|
549
|
+
new Promise(
|
|
550
|
+
(_, reject) => setTimeout(() => reject(new Error("timeout")), 1500)
|
|
551
|
+
)
|
|
552
|
+
]);
|
|
553
|
+
for (const dbInfo of dbs) {
|
|
554
|
+
if (dbInfo.name && dbInfo.name.startsWith(this.dbNamePrefix) && dbInfo.name !== this.dbName) {
|
|
555
|
+
dbNames.push(dbInfo.name);
|
|
556
|
+
try {
|
|
557
|
+
const db = await new Promise((resolve, reject) => {
|
|
558
|
+
const req = indexedDB.open(dbInfo.name, DB_VERSION);
|
|
559
|
+
req.onsuccess = () => resolve(req.result);
|
|
560
|
+
req.onerror = () => reject(req.error);
|
|
561
|
+
req.onupgradeneeded = (e) => {
|
|
562
|
+
const d = e.target.result;
|
|
563
|
+
if (!d.objectStoreNames.contains(STORE_TOKENS)) d.createObjectStore(STORE_TOKENS, { keyPath: "id" });
|
|
564
|
+
if (!d.objectStoreNames.contains(STORE_META)) d.createObjectStore(STORE_META);
|
|
565
|
+
};
|
|
566
|
+
});
|
|
567
|
+
const clearTx = db.transaction([STORE_TOKENS, STORE_META], "readwrite");
|
|
568
|
+
clearTx.objectStore(STORE_TOKENS).clear();
|
|
569
|
+
clearTx.objectStore(STORE_META).clear();
|
|
570
|
+
await new Promise((resolve) => {
|
|
571
|
+
clearTx.oncomplete = () => resolve();
|
|
572
|
+
clearTx.onerror = () => resolve();
|
|
573
|
+
});
|
|
574
|
+
db.close();
|
|
575
|
+
} catch {
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
} catch {
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
for (const name of dbNames) {
|
|
583
|
+
try {
|
|
584
|
+
const req = indexedDB.deleteDatabase(name);
|
|
585
|
+
req.onerror = () => {
|
|
586
|
+
};
|
|
587
|
+
req.onblocked = () => {
|
|
588
|
+
};
|
|
589
|
+
} catch {
|
|
590
|
+
}
|
|
566
591
|
}
|
|
567
592
|
return true;
|
|
568
593
|
} catch (err) {
|
|
@@ -1111,7 +1136,9 @@ import {
|
|
|
1111
1136
|
EventKinds,
|
|
1112
1137
|
hashNametag,
|
|
1113
1138
|
NostrClient,
|
|
1114
|
-
Filter
|
|
1139
|
+
Filter,
|
|
1140
|
+
isChatMessage,
|
|
1141
|
+
isReadReceipt
|
|
1115
1142
|
} from "@unicitylabs/nostr-js-sdk";
|
|
1116
1143
|
|
|
1117
1144
|
// core/crypto.ts
|
|
@@ -1329,6 +1356,8 @@ var NostrTransportProvider = class {
|
|
|
1329
1356
|
transferHandlers = /* @__PURE__ */ new Set();
|
|
1330
1357
|
paymentRequestHandlers = /* @__PURE__ */ new Set();
|
|
1331
1358
|
paymentRequestResponseHandlers = /* @__PURE__ */ new Set();
|
|
1359
|
+
readReceiptHandlers = /* @__PURE__ */ new Set();
|
|
1360
|
+
typingIndicatorHandlers = /* @__PURE__ */ new Set();
|
|
1332
1361
|
broadcastHandlers = /* @__PURE__ */ new Map();
|
|
1333
1362
|
eventCallbacks = /* @__PURE__ */ new Set();
|
|
1334
1363
|
constructor(config) {
|
|
@@ -1580,6 +1609,18 @@ var NostrTransportProvider = class {
|
|
|
1580
1609
|
const wrappedContent = senderNametag ? JSON.stringify({ senderNametag, text: content }) : content;
|
|
1581
1610
|
const giftWrap = NIP17.createGiftWrap(this.keyManager, nostrRecipient, wrappedContent);
|
|
1582
1611
|
await this.publishEvent(giftWrap);
|
|
1612
|
+
const selfWrapContent = JSON.stringify({
|
|
1613
|
+
selfWrap: true,
|
|
1614
|
+
originalId: giftWrap.id,
|
|
1615
|
+
recipientPubkey,
|
|
1616
|
+
senderNametag,
|
|
1617
|
+
text: content
|
|
1618
|
+
});
|
|
1619
|
+
const selfPubkey = this.keyManager.getPublicKeyHex();
|
|
1620
|
+
const selfGiftWrap = NIP17.createGiftWrap(this.keyManager, selfPubkey, selfWrapContent);
|
|
1621
|
+
this.publishEvent(selfGiftWrap).catch((err) => {
|
|
1622
|
+
this.log("Self-wrap publish failed:", err);
|
|
1623
|
+
});
|
|
1583
1624
|
this.emitEvent({
|
|
1584
1625
|
type: "message:sent",
|
|
1585
1626
|
timestamp: Date.now(),
|
|
@@ -1678,6 +1719,37 @@ var NostrTransportProvider = class {
|
|
|
1678
1719
|
this.paymentRequestResponseHandlers.add(handler);
|
|
1679
1720
|
return () => this.paymentRequestResponseHandlers.delete(handler);
|
|
1680
1721
|
}
|
|
1722
|
+
// ===========================================================================
|
|
1723
|
+
// Read Receipts
|
|
1724
|
+
// ===========================================================================
|
|
1725
|
+
async sendReadReceipt(recipientTransportPubkey, messageEventId) {
|
|
1726
|
+
if (!this.keyManager) throw new Error("Not initialized");
|
|
1727
|
+
const nostrRecipient = recipientTransportPubkey.length === 66 ? recipientTransportPubkey.slice(2) : recipientTransportPubkey;
|
|
1728
|
+
const event = NIP17.createReadReceipt(this.keyManager, nostrRecipient, messageEventId);
|
|
1729
|
+
await this.publishEvent(event);
|
|
1730
|
+
this.log("Sent read receipt for:", messageEventId, "to:", nostrRecipient.slice(0, 16));
|
|
1731
|
+
}
|
|
1732
|
+
onReadReceipt(handler) {
|
|
1733
|
+
this.readReceiptHandlers.add(handler);
|
|
1734
|
+
return () => this.readReceiptHandlers.delete(handler);
|
|
1735
|
+
}
|
|
1736
|
+
// ===========================================================================
|
|
1737
|
+
// Typing Indicators
|
|
1738
|
+
// ===========================================================================
|
|
1739
|
+
async sendTypingIndicator(recipientTransportPubkey) {
|
|
1740
|
+
if (!this.keyManager) throw new Error("Not initialized");
|
|
1741
|
+
const nostrRecipient = recipientTransportPubkey.length === 66 ? recipientTransportPubkey.slice(2) : recipientTransportPubkey;
|
|
1742
|
+
const content = JSON.stringify({
|
|
1743
|
+
type: "typing",
|
|
1744
|
+
senderNametag: this.identity?.nametag
|
|
1745
|
+
});
|
|
1746
|
+
const event = NIP17.createGiftWrap(this.keyManager, nostrRecipient, content);
|
|
1747
|
+
await this.publishEvent(event);
|
|
1748
|
+
}
|
|
1749
|
+
onTypingIndicator(handler) {
|
|
1750
|
+
this.typingIndicatorHandlers.add(handler);
|
|
1751
|
+
return () => this.typingIndicatorHandlers.delete(handler);
|
|
1752
|
+
}
|
|
1681
1753
|
/**
|
|
1682
1754
|
* Resolve any identifier to full peer information.
|
|
1683
1755
|
* Routes to the appropriate specific resolve method based on identifier format.
|
|
@@ -2131,11 +2203,74 @@ var NostrTransportProvider = class {
|
|
|
2131
2203
|
const pm = NIP17.unwrap(event, this.keyManager);
|
|
2132
2204
|
this.log("Gift wrap unwrapped, sender:", pm.senderPubkey?.slice(0, 16), "kind:", pm.kind);
|
|
2133
2205
|
if (pm.senderPubkey === this.keyManager.getPublicKeyHex()) {
|
|
2134
|
-
|
|
2206
|
+
try {
|
|
2207
|
+
const parsed = JSON.parse(pm.content);
|
|
2208
|
+
if (parsed?.selfWrap && parsed.recipientPubkey) {
|
|
2209
|
+
this.log("Self-wrap replay for recipient:", parsed.recipientPubkey?.slice(0, 16));
|
|
2210
|
+
const message2 = {
|
|
2211
|
+
id: parsed.originalId || pm.eventId,
|
|
2212
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
2213
|
+
senderNametag: parsed.senderNametag,
|
|
2214
|
+
recipientTransportPubkey: parsed.recipientPubkey,
|
|
2215
|
+
content: parsed.text ?? "",
|
|
2216
|
+
timestamp: pm.timestamp * 1e3,
|
|
2217
|
+
encrypted: true,
|
|
2218
|
+
isSelfWrap: true
|
|
2219
|
+
};
|
|
2220
|
+
for (const handler of this.messageHandlers) {
|
|
2221
|
+
try {
|
|
2222
|
+
handler(message2);
|
|
2223
|
+
} catch (e) {
|
|
2224
|
+
this.log("Self-wrap handler error:", e);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
} catch {
|
|
2230
|
+
}
|
|
2231
|
+
this.log("Skipping own non-self-wrap message");
|
|
2135
2232
|
return;
|
|
2136
2233
|
}
|
|
2137
|
-
if (pm
|
|
2138
|
-
this.log("
|
|
2234
|
+
if (isReadReceipt(pm)) {
|
|
2235
|
+
this.log("Read receipt from:", pm.senderPubkey?.slice(0, 16), "for:", pm.replyToEventId);
|
|
2236
|
+
if (pm.replyToEventId) {
|
|
2237
|
+
const receipt = {
|
|
2238
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
2239
|
+
messageEventId: pm.replyToEventId,
|
|
2240
|
+
timestamp: pm.timestamp * 1e3
|
|
2241
|
+
};
|
|
2242
|
+
for (const handler of this.readReceiptHandlers) {
|
|
2243
|
+
try {
|
|
2244
|
+
handler(receipt);
|
|
2245
|
+
} catch (e) {
|
|
2246
|
+
this.log("Read receipt handler error:", e);
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
try {
|
|
2253
|
+
const parsed = JSON.parse(pm.content);
|
|
2254
|
+
if (parsed?.type === "typing") {
|
|
2255
|
+
this.log("Typing indicator from:", pm.senderPubkey?.slice(0, 16));
|
|
2256
|
+
const indicator = {
|
|
2257
|
+
senderTransportPubkey: pm.senderPubkey,
|
|
2258
|
+
senderNametag: parsed.senderNametag,
|
|
2259
|
+
timestamp: pm.timestamp * 1e3
|
|
2260
|
+
};
|
|
2261
|
+
for (const handler of this.typingIndicatorHandlers) {
|
|
2262
|
+
try {
|
|
2263
|
+
handler(indicator);
|
|
2264
|
+
} catch (e) {
|
|
2265
|
+
this.log("Typing handler error:", e);
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
return;
|
|
2269
|
+
}
|
|
2270
|
+
} catch {
|
|
2271
|
+
}
|
|
2272
|
+
if (!isChatMessage(pm)) {
|
|
2273
|
+
this.log("Skipping unknown message kind:", pm.kind);
|
|
2139
2274
|
return;
|
|
2140
2275
|
}
|
|
2141
2276
|
let content = pm.content;
|
|
@@ -2150,7 +2285,9 @@ var NostrTransportProvider = class {
|
|
|
2150
2285
|
}
|
|
2151
2286
|
this.log("DM received from:", senderNametag || pm.senderPubkey?.slice(0, 16), "content:", content?.slice(0, 50));
|
|
2152
2287
|
const message = {
|
|
2153
|
-
id
|
|
2288
|
+
// Use outer gift wrap event.id so it matches the sender's stored giftWrap.id.
|
|
2289
|
+
// This ensures read receipts reference an ID the sender recognizes.
|
|
2290
|
+
id: event.id,
|
|
2154
2291
|
senderTransportPubkey: pm.senderPubkey,
|
|
2155
2292
|
senderNametag,
|
|
2156
2293
|
content,
|