@unicitylabs/sphere-sdk 0.5.3 → 0.5.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/README.md +2 -0
- package/dist/connect/index.cjs +145 -23
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.d.cts +15 -2
- package/dist/connect/index.d.ts +15 -2
- package/dist/connect/index.js +145 -23
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +670 -473
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +123 -2
- package/dist/core/index.d.ts +123 -2
- package/dist/core/index.js +667 -473
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +119 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.d.cts +53 -1
- package/dist/impl/browser/connect/index.d.ts +53 -1
- package/dist/impl/browser/connect/index.js +119 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/browser/index.cjs +306 -193
- package/dist/impl/browser/index.cjs.map +1 -1
- package/dist/impl/browser/index.js +306 -193
- package/dist/impl/browser/index.js.map +1 -1
- package/dist/impl/browser/ipfs.cjs +134 -19
- package/dist/impl/browser/ipfs.cjs.map +1 -1
- package/dist/impl/browser/ipfs.js +134 -19
- package/dist/impl/browser/ipfs.js.map +1 -1
- package/dist/impl/nodejs/connect/index.cjs +101 -6
- package/dist/impl/nodejs/connect/index.cjs.map +1 -1
- package/dist/impl/nodejs/connect/index.d.cts +2 -0
- package/dist/impl/nodejs/connect/index.d.ts +2 -0
- package/dist/impl/nodejs/connect/index.js +101 -6
- package/dist/impl/nodejs/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.cjs +267 -152
- package/dist/impl/nodejs/index.cjs.map +1 -1
- package/dist/impl/nodejs/index.d.cts +2 -1
- package/dist/impl/nodejs/index.d.ts +2 -1
- package/dist/impl/nodejs/index.js +267 -152
- package/dist/impl/nodejs/index.js.map +1 -1
- package/dist/index.cjs +682 -493
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -8
- package/dist/index.d.ts +124 -8
- package/dist/index.js +680 -493
- package/dist/index.js.map +1 -1
- package/dist/l1/index.cjs +139 -32
- package/dist/l1/index.cjs.map +1 -1
- package/dist/l1/index.js +139 -32
- package/dist/l1/index.js.map +1 -1
- package/package.json +1 -16
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ A modular TypeScript SDK for Unicity wallet operations supporting both Layer 1 (
|
|
|
16
16
|
- **TXF Serialization** - Token eXchange Format for storage and transfer
|
|
17
17
|
- **Token Validation** - Aggregator-based token verification
|
|
18
18
|
- **Core Utilities** - Crypto, currency, bech32, base58 functions
|
|
19
|
+
- **Connect Protocol** - dApp ↔ wallet communication via `ConnectClient` / `ConnectHost` (browser extension + popup)
|
|
19
20
|
|
|
20
21
|
## Installation
|
|
21
22
|
|
|
@@ -32,6 +33,7 @@ Choose your platform:
|
|
|
32
33
|
| **Browser** | [QUICKSTART-BROWSER.md](docs/QUICKSTART-BROWSER.md) | SDK only | IPFS sync (built-in) |
|
|
33
34
|
| **Node.js** | [QUICKSTART-NODEJS.md](docs/QUICKSTART-NODEJS.md) | SDK + `ws` | IPFS sync (built-in) |
|
|
34
35
|
| **CLI** | See below | SDK + `tsx` | - |
|
|
36
|
+
| **dApp integration** | [CONNECT.md](docs/CONNECT.md) | SDK only | Sphere extension |
|
|
35
37
|
|
|
36
38
|
## CLI (Command Line Interface)
|
|
37
39
|
|
package/dist/connect/index.cjs
CHANGED
|
@@ -42,6 +42,110 @@ __export(connect_exports, {
|
|
|
42
42
|
});
|
|
43
43
|
module.exports = __toCommonJS(connect_exports);
|
|
44
44
|
|
|
45
|
+
// core/logger.ts
|
|
46
|
+
var LOGGER_KEY = "__sphere_sdk_logger__";
|
|
47
|
+
function getState() {
|
|
48
|
+
const g = globalThis;
|
|
49
|
+
if (!g[LOGGER_KEY]) {
|
|
50
|
+
g[LOGGER_KEY] = { debug: false, tags: {}, handler: null };
|
|
51
|
+
}
|
|
52
|
+
return g[LOGGER_KEY];
|
|
53
|
+
}
|
|
54
|
+
function isEnabled(tag) {
|
|
55
|
+
const state = getState();
|
|
56
|
+
if (tag in state.tags) return state.tags[tag];
|
|
57
|
+
return state.debug;
|
|
58
|
+
}
|
|
59
|
+
var logger = {
|
|
60
|
+
/**
|
|
61
|
+
* Configure the logger. Can be called multiple times (last write wins).
|
|
62
|
+
* Typically called by createBrowserProviders(), createNodeProviders(), or Sphere.init().
|
|
63
|
+
*/
|
|
64
|
+
configure(config) {
|
|
65
|
+
const state = getState();
|
|
66
|
+
if (config.debug !== void 0) state.debug = config.debug;
|
|
67
|
+
if (config.handler !== void 0) state.handler = config.handler;
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* Enable/disable debug logging for a specific tag.
|
|
71
|
+
* Per-tag setting overrides the global debug flag.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* logger.setTagDebug('Nostr', true); // enable only Nostr logs
|
|
76
|
+
* logger.setTagDebug('Nostr', false); // disable Nostr logs even if global debug=true
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
setTagDebug(tag, enabled) {
|
|
80
|
+
getState().tags[tag] = enabled;
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* Clear per-tag override, falling back to global debug flag.
|
|
84
|
+
*/
|
|
85
|
+
clearTagDebug(tag) {
|
|
86
|
+
delete getState().tags[tag];
|
|
87
|
+
},
|
|
88
|
+
/** Returns true if debug mode is enabled for the given tag (or globally). */
|
|
89
|
+
isDebugEnabled(tag) {
|
|
90
|
+
if (tag) return isEnabled(tag);
|
|
91
|
+
return getState().debug;
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* Debug-level log. Only shown when debug is enabled (globally or for this tag).
|
|
95
|
+
* Use for detailed operational information.
|
|
96
|
+
*/
|
|
97
|
+
debug(tag, message, ...args) {
|
|
98
|
+
if (!isEnabled(tag)) return;
|
|
99
|
+
const state = getState();
|
|
100
|
+
if (state.handler) {
|
|
101
|
+
state.handler("debug", tag, message, ...args);
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`[${tag}]`, message, ...args);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
/**
|
|
107
|
+
* Warning-level log. ALWAYS shown regardless of debug flag.
|
|
108
|
+
* Use for important but non-critical issues (timeouts, retries, degraded state).
|
|
109
|
+
*/
|
|
110
|
+
warn(tag, message, ...args) {
|
|
111
|
+
const state = getState();
|
|
112
|
+
if (state.handler) {
|
|
113
|
+
state.handler("warn", tag, message, ...args);
|
|
114
|
+
} else {
|
|
115
|
+
console.warn(`[${tag}]`, message, ...args);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* Error-level log. ALWAYS shown regardless of debug flag.
|
|
120
|
+
* Use for critical failures that should never be silenced.
|
|
121
|
+
*/
|
|
122
|
+
error(tag, message, ...args) {
|
|
123
|
+
const state = getState();
|
|
124
|
+
if (state.handler) {
|
|
125
|
+
state.handler("error", tag, message, ...args);
|
|
126
|
+
} else {
|
|
127
|
+
console.error(`[${tag}]`, message, ...args);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
/** Reset all logger state (debug flag, tags, handler). Primarily for tests. */
|
|
131
|
+
reset() {
|
|
132
|
+
const g = globalThis;
|
|
133
|
+
delete g[LOGGER_KEY];
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// core/errors.ts
|
|
138
|
+
var SphereError = class extends Error {
|
|
139
|
+
code;
|
|
140
|
+
cause;
|
|
141
|
+
constructor(message, code, cause) {
|
|
142
|
+
super(message);
|
|
143
|
+
this.name = "SphereError";
|
|
144
|
+
this.code = code;
|
|
145
|
+
this.cause = cause;
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
45
149
|
// constants.ts
|
|
46
150
|
var STORAGE_KEYS_GLOBAL = {
|
|
47
151
|
/** Encrypted BIP39 mnemonic */
|
|
@@ -306,7 +410,7 @@ var ConnectHost = class {
|
|
|
306
410
|
return;
|
|
307
411
|
}
|
|
308
412
|
} catch (error) {
|
|
309
|
-
|
|
413
|
+
logger.warn("ConnectHost", "Error handling message:", error);
|
|
310
414
|
}
|
|
311
415
|
}
|
|
312
416
|
// ===========================================================================
|
|
@@ -318,10 +422,16 @@ var ConnectHost = class {
|
|
|
318
422
|
this.sendHandshakeResponse([], void 0, void 0);
|
|
319
423
|
return;
|
|
320
424
|
}
|
|
425
|
+
if (msg.sessionId && this.session?.active && this.session.id === msg.sessionId) {
|
|
426
|
+
const identity2 = this.getPublicIdentity();
|
|
427
|
+
this.sendHandshakeResponse([...this.grantedPermissions], this.session.id, identity2);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
321
430
|
const requestedPermissions = msg.permissions;
|
|
322
431
|
const { approved, grantedPermissions } = await this.config.onConnectionRequest(
|
|
323
432
|
dapp,
|
|
324
|
-
requestedPermissions
|
|
433
|
+
requestedPermissions,
|
|
434
|
+
msg.silent
|
|
325
435
|
);
|
|
326
436
|
if (!approved) {
|
|
327
437
|
this.sendHandshakeResponse([], void 0, void 0);
|
|
@@ -371,8 +481,12 @@ var ConnectHost = class {
|
|
|
371
481
|
return;
|
|
372
482
|
}
|
|
373
483
|
if (msg.method === RPC_METHODS.DISCONNECT) {
|
|
484
|
+
const disconnectedSession = this.session;
|
|
374
485
|
this.revokeSession();
|
|
375
486
|
this.sendResult(msg.id, { disconnected: true });
|
|
487
|
+
if (disconnectedSession && this.config.onDisconnect) {
|
|
488
|
+
Promise.resolve(this.config.onDisconnect(disconnectedSession)).catch((err) => logger.warn("Connect", "onDisconnect handler error", err));
|
|
489
|
+
}
|
|
376
490
|
return;
|
|
377
491
|
}
|
|
378
492
|
if (!hasMethodPermission(this.grantedPermissions, msg.method)) {
|
|
@@ -443,17 +557,17 @@ var ConnectHost = class {
|
|
|
443
557
|
return this.sphere.payments.getHistory();
|
|
444
558
|
case RPC_METHODS.L1_GET_BALANCE:
|
|
445
559
|
if (!this.sphere.payments.l1) {
|
|
446
|
-
throw new
|
|
560
|
+
throw new SphereError("L1 module not available", "MODULE_NOT_AVAILABLE");
|
|
447
561
|
}
|
|
448
562
|
return this.sphere.payments.l1.getBalance();
|
|
449
563
|
case RPC_METHODS.L1_GET_HISTORY:
|
|
450
564
|
if (!this.sphere.payments.l1) {
|
|
451
|
-
throw new
|
|
565
|
+
throw new SphereError("L1 module not available", "MODULE_NOT_AVAILABLE");
|
|
452
566
|
}
|
|
453
567
|
return this.sphere.payments.l1.getHistory(params.limit);
|
|
454
568
|
case RPC_METHODS.RESOLVE:
|
|
455
569
|
if (!params.identifier) {
|
|
456
|
-
throw new
|
|
570
|
+
throw new SphereError("Missing required parameter: identifier", "VALIDATION_ERROR");
|
|
457
571
|
}
|
|
458
572
|
return this.sphere.resolve(params.identifier);
|
|
459
573
|
case RPC_METHODS.SUBSCRIBE:
|
|
@@ -461,7 +575,7 @@ var ConnectHost = class {
|
|
|
461
575
|
case RPC_METHODS.UNSUBSCRIBE:
|
|
462
576
|
return this.handleUnsubscribe(params.event);
|
|
463
577
|
case RPC_METHODS.GET_CONVERSATIONS: {
|
|
464
|
-
if (!this.sphere.communications) throw new
|
|
578
|
+
if (!this.sphere.communications) throw new SphereError("Communications module not available", "MODULE_NOT_AVAILABLE");
|
|
465
579
|
const convos = this.sphere.communications.getConversations();
|
|
466
580
|
const result = [];
|
|
467
581
|
const needsResolve = [];
|
|
@@ -484,7 +598,10 @@ var ConnectHost = class {
|
|
|
484
598
|
if (needsResolve.length > 0) {
|
|
485
599
|
const resolved = await Promise.all(
|
|
486
600
|
needsResolve.map(
|
|
487
|
-
({ peerPubkey }) => this.sphere.communications.resolvePeerNametag(peerPubkey).catch(() =>
|
|
601
|
+
({ peerPubkey }) => this.sphere.communications.resolvePeerNametag(peerPubkey).catch((err) => {
|
|
602
|
+
logger.debug("Connect", "Peer nametag resolution failed", err);
|
|
603
|
+
return void 0;
|
|
604
|
+
})
|
|
488
605
|
)
|
|
489
606
|
);
|
|
490
607
|
for (let i = 0; i < needsResolve.length; i++) {
|
|
@@ -497,8 +614,8 @@ var ConnectHost = class {
|
|
|
497
614
|
return result;
|
|
498
615
|
}
|
|
499
616
|
case RPC_METHODS.GET_MESSAGES: {
|
|
500
|
-
if (!this.sphere.communications) throw new
|
|
501
|
-
if (!params.peerPubkey) throw new
|
|
617
|
+
if (!this.sphere.communications) throw new SphereError("Communications module not available", "MODULE_NOT_AVAILABLE");
|
|
618
|
+
if (!params.peerPubkey) throw new SphereError("Missing required parameter: peerPubkey", "VALIDATION_ERROR");
|
|
502
619
|
return this.sphere.communications.getConversationPage(
|
|
503
620
|
params.peerPubkey,
|
|
504
621
|
{
|
|
@@ -508,7 +625,7 @@ var ConnectHost = class {
|
|
|
508
625
|
);
|
|
509
626
|
}
|
|
510
627
|
case RPC_METHODS.GET_DM_UNREAD_COUNT: {
|
|
511
|
-
if (!this.sphere.communications) throw new
|
|
628
|
+
if (!this.sphere.communications) throw new SphereError("Communications module not available", "MODULE_NOT_AVAILABLE");
|
|
512
629
|
return {
|
|
513
630
|
unreadCount: this.sphere.communications.getUnreadCount(
|
|
514
631
|
params.peerPubkey
|
|
@@ -516,22 +633,22 @@ var ConnectHost = class {
|
|
|
516
633
|
};
|
|
517
634
|
}
|
|
518
635
|
case RPC_METHODS.MARK_AS_READ: {
|
|
519
|
-
if (!this.sphere.communications) throw new
|
|
636
|
+
if (!this.sphere.communications) throw new SphereError("Communications module not available", "MODULE_NOT_AVAILABLE");
|
|
520
637
|
if (!params.messageIds || !Array.isArray(params.messageIds)) {
|
|
521
|
-
throw new
|
|
638
|
+
throw new SphereError("Missing required parameter: messageIds (string[])", "VALIDATION_ERROR");
|
|
522
639
|
}
|
|
523
640
|
await this.sphere.communications.markAsRead(params.messageIds);
|
|
524
641
|
return { marked: true, count: params.messageIds.length };
|
|
525
642
|
}
|
|
526
643
|
default:
|
|
527
|
-
throw new
|
|
644
|
+
throw new SphereError(`Unknown method: ${method}`, "VALIDATION_ERROR");
|
|
528
645
|
}
|
|
529
646
|
}
|
|
530
647
|
// ===========================================================================
|
|
531
648
|
// Event Subscriptions
|
|
532
649
|
// ===========================================================================
|
|
533
650
|
handleSubscribe(eventName) {
|
|
534
|
-
if (!eventName) throw new
|
|
651
|
+
if (!eventName) throw new SphereError("Missing required parameter: event", "VALIDATION_ERROR");
|
|
535
652
|
if (this.eventSubscriptions.has(eventName)) {
|
|
536
653
|
return { subscribed: true, event: eventName };
|
|
537
654
|
}
|
|
@@ -548,7 +665,7 @@ var ConnectHost = class {
|
|
|
548
665
|
return { subscribed: true, event: eventName };
|
|
549
666
|
}
|
|
550
667
|
handleUnsubscribe(eventName) {
|
|
551
|
-
if (!eventName) throw new
|
|
668
|
+
if (!eventName) throw new SphereError("Missing required parameter: event", "VALIDATION_ERROR");
|
|
552
669
|
const unsub = this.eventSubscriptions.get(eventName);
|
|
553
670
|
if (unsub) {
|
|
554
671
|
unsub();
|
|
@@ -639,6 +756,8 @@ var ConnectClient = class {
|
|
|
639
756
|
requestedPermissions;
|
|
640
757
|
timeout;
|
|
641
758
|
intentTimeout;
|
|
759
|
+
resumeSessionId;
|
|
760
|
+
silent;
|
|
642
761
|
sessionId = null;
|
|
643
762
|
grantedPermissions = [];
|
|
644
763
|
identity = null;
|
|
@@ -654,6 +773,8 @@ var ConnectClient = class {
|
|
|
654
773
|
this.requestedPermissions = config.permissions ?? [...ALL_PERMISSIONS];
|
|
655
774
|
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
656
775
|
this.intentTimeout = config.intentTimeout ?? DEFAULT_INTENT_TIMEOUT;
|
|
776
|
+
this.resumeSessionId = config.resumeSessionId ?? null;
|
|
777
|
+
this.silent = config.silent ?? false;
|
|
657
778
|
}
|
|
658
779
|
// ===========================================================================
|
|
659
780
|
// Connection
|
|
@@ -673,7 +794,9 @@ var ConnectClient = class {
|
|
|
673
794
|
type: "handshake",
|
|
674
795
|
direction: "request",
|
|
675
796
|
permissions: this.requestedPermissions,
|
|
676
|
-
dapp: this.dapp
|
|
797
|
+
dapp: this.dapp,
|
|
798
|
+
...this.resumeSessionId ? { sessionId: this.resumeSessionId } : {},
|
|
799
|
+
...this.silent ? { silent: true } : {}
|
|
677
800
|
});
|
|
678
801
|
});
|
|
679
802
|
}
|
|
@@ -708,7 +831,7 @@ var ConnectClient = class {
|
|
|
708
831
|
// ===========================================================================
|
|
709
832
|
/** Send a query request and return the result */
|
|
710
833
|
async query(method, params) {
|
|
711
|
-
if (!this.connected) throw new
|
|
834
|
+
if (!this.connected) throw new SphereError("Not connected", "NOT_INITIALIZED");
|
|
712
835
|
const id = createRequestId();
|
|
713
836
|
return new Promise((resolve, reject) => {
|
|
714
837
|
const timer = setTimeout(() => {
|
|
@@ -735,7 +858,7 @@ var ConnectClient = class {
|
|
|
735
858
|
// ===========================================================================
|
|
736
859
|
/** Send an intent request. The wallet will open its UI for user confirmation. */
|
|
737
860
|
async intent(action, params) {
|
|
738
|
-
if (!this.connected) throw new
|
|
861
|
+
if (!this.connected) throw new SphereError("Not connected", "NOT_INITIALIZED");
|
|
739
862
|
const id = createRequestId();
|
|
740
863
|
return new Promise((resolve, reject) => {
|
|
741
864
|
const timer = setTimeout(() => {
|
|
@@ -765,8 +888,7 @@ var ConnectClient = class {
|
|
|
765
888
|
if (!this.eventHandlers.has(event)) {
|
|
766
889
|
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
767
890
|
if (this.connected) {
|
|
768
|
-
this.query(RPC_METHODS.SUBSCRIBE, { event }).catch(() =>
|
|
769
|
-
});
|
|
891
|
+
this.query(RPC_METHODS.SUBSCRIBE, { event }).catch((err) => logger.debug("Connect", "Event subscription failed", err));
|
|
770
892
|
}
|
|
771
893
|
}
|
|
772
894
|
this.eventHandlers.get(event).add(handler);
|
|
@@ -777,8 +899,7 @@ var ConnectClient = class {
|
|
|
777
899
|
if (handlers.size === 0) {
|
|
778
900
|
this.eventHandlers.delete(event);
|
|
779
901
|
if (this.connected) {
|
|
780
|
-
this.query(RPC_METHODS.UNSUBSCRIBE, { event }).catch(() =>
|
|
781
|
-
});
|
|
902
|
+
this.query(RPC_METHODS.UNSUBSCRIBE, { event }).catch((err) => logger.debug("Connect", "Event unsubscription failed", err));
|
|
782
903
|
}
|
|
783
904
|
}
|
|
784
905
|
}
|
|
@@ -806,7 +927,8 @@ var ConnectClient = class {
|
|
|
806
927
|
for (const handler of handlers) {
|
|
807
928
|
try {
|
|
808
929
|
handler(msg.data);
|
|
809
|
-
} catch {
|
|
930
|
+
} catch (err) {
|
|
931
|
+
logger.debug("Connect", "Event handler error", err);
|
|
810
932
|
}
|
|
811
933
|
}
|
|
812
934
|
}
|