Skip to content

Commit 5e77025

Browse files
authored
Merge pull request #209 from getAlby/fix/nip47-errors
fix: use actual error objects in NWC Client
2 parents f697e10 + 1161162 commit 5e77025

File tree

2 files changed

+146
-52
lines changed

2 files changed

+146
-52
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@getalby/sdk",
3-
"version": "3.3.1",
3+
"version": "3.4.0",
44
"description": "The SDK to integrate with Nostr Wallet Connect and the Alby API",
55
"repository": "https://github.com/getAlby/js-sdk.git",
66
"bugs": "https://github.com/getAlby/js-sdk/issues",

src/NWCClient.ts

+145-51
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,32 @@ export interface NWCOptions {
129129
secret?: string;
130130
}
131131

132+
export class Nip47Error extends Error {
133+
/**
134+
* @deprecated please use message. Deprecated since v3.3.2. Will be removed in v4.0.0.
135+
*/
136+
error: string;
137+
code: string;
138+
constructor(message: string, code: string) {
139+
super(message);
140+
this.error = message;
141+
this.code = code;
142+
}
143+
}
144+
145+
/**
146+
* A NIP-47 response was received, but with an error code (see https://github.com/nostr-protocol/nips/blob/master/47.md#error-codes)
147+
*/
148+
export class Nip47WalletError extends Nip47Error {}
149+
150+
export class Nip47TimeoutError extends Nip47Error {}
151+
export class Nip47PublishTimeoutError extends Nip47TimeoutError {}
152+
export class Nip47ReplyTimeoutError extends Nip47TimeoutError {}
153+
export class Nip47PublishError extends Nip47Error {}
154+
export class Nip47ResponseDecodingError extends Nip47Error {}
155+
export class Nip47ResponseValidationError extends Nip47Error {}
156+
export class Nip47UnexpectedResponseError extends Nip47Error {}
157+
132158
export const NWCs: Record<string, NWCOptions> = {
133159
alby: {
134160
authorizationUrl: "https://nwc.getalby.com/apps/new",
@@ -331,15 +357,15 @@ export class NWCClient {
331357
`height=${height},width=${width},top=${top},left=${left}`,
332358
);
333359
if (!popup) {
334-
reject();
360+
reject(new Error("failed to execute window.open"));
335361
return;
336-
} // only for TS?
362+
}
337363

338364
const checkForPopup = () => {
339365
if (popup && popup.closed) {
340-
reject();
341366
clearInterval(popupChecker);
342367
window.removeEventListener("message", onMessage);
368+
reject(new Error("Popup closed"));
343369
}
344370
};
345371

@@ -466,7 +492,7 @@ export class NWCClient {
466492
errors: [],
467493
};
468494
} catch (error) {
469-
console.error("Failed to request multi_pay_keysend", error);
495+
console.error("Failed to request multi_pay_invoice", error);
470496
throw error;
471497
}
472498
}
@@ -590,10 +616,12 @@ export class NWCClient {
590616
function replyTimeout() {
591617
sub.unsub();
592618
//console.error(`Reply timeout: event ${event.id} `);
593-
reject({
594-
error: `reply timeout: event ${event.id}`,
595-
code: "INTERNAL",
596-
});
619+
reject(
620+
new Nip47ReplyTimeoutError(
621+
`reply timeout: event ${event.id}`,
622+
"INTERNAL",
623+
),
624+
);
597625
}
598626

599627
const replyTimeoutCheck = setTimeout(replyTimeout, 60000);
@@ -611,32 +639,64 @@ export class NWCClient {
611639
try {
612640
response = JSON.parse(decryptedContent);
613641
} catch (e) {
614-
reject({ error: "invalid response", code: "INTERNAL" });
642+
clearTimeout(replyTimeoutCheck);
643+
sub.unsub();
644+
reject(
645+
new Nip47ResponseDecodingError(
646+
"failed to deserialize response",
647+
"INTERNAL",
648+
),
649+
);
615650
return;
616651
}
617-
if (event.kind == 23195 && response.result) {
618-
// console.info("NIP-47 result", response.result);
619-
if (resultValidator(response.result)) {
620-
resolve(response.result);
652+
if (event.kind == 23195) {
653+
if (response.result) {
654+
// console.info("NIP-47 result", response.result);
655+
if (resultValidator(response.result)) {
656+
resolve(response.result);
657+
} else {
658+
clearTimeout(replyTimeoutCheck);
659+
sub.unsub();
660+
reject(
661+
new Nip47ResponseValidationError(
662+
"response from NWC failed validation: " +
663+
JSON.stringify(response.result),
664+
"INTERNAL",
665+
),
666+
);
667+
}
621668
} else {
622-
reject({
623-
error:
624-
"Response from NWC failed validation: " +
625-
JSON.stringify(response.result),
626-
code: "INTERNAL",
627-
});
669+
clearTimeout(replyTimeoutCheck);
670+
sub.unsub();
671+
// console.error("Wallet error", response.error);
672+
reject(
673+
new Nip47WalletError(
674+
response.error?.message || "unknown Error",
675+
response.error?.code || "INTERNAL",
676+
),
677+
);
628678
}
629679
} else {
630-
reject({
631-
error: response.error?.message,
632-
code: response.error?.code,
633-
});
680+
clearTimeout(replyTimeoutCheck);
681+
sub.unsub();
682+
reject(
683+
new Nip47UnexpectedResponseError(
684+
response.error?.message || "unknown Error",
685+
response.error?.code || "INTERNAL",
686+
),
687+
);
634688
}
635689
});
636690

637691
function publishTimeout() {
692+
sub.unsub();
638693
//console.error(`Publish timeout: event ${event.id}`);
639-
reject({ error: `Publish timeout: event ${event.id}` });
694+
reject(
695+
new Nip47PublishTimeoutError(
696+
`publish timeout: ${event.id}`,
697+
"INTERNAL",
698+
),
699+
);
640700
}
641701
const publishTimeoutCheck = setTimeout(publishTimeout, 5000);
642702

@@ -647,7 +707,9 @@ export class NWCClient {
647707
} catch (error) {
648708
//console.error(`Failed to publish to ${this.relay.url}`, error);
649709
clearTimeout(publishTimeoutCheck);
650-
reject({ error: `Failed to publish request: ${error}` });
710+
reject(
711+
new Nip47PublishError(`failed to publish: ${error}`, "INTERNAL"),
712+
);
651713
}
652714
})();
653715
});
@@ -696,10 +758,12 @@ export class NWCClient {
696758
function replyTimeout() {
697759
sub.unsub();
698760
//console.error(`Reply timeout: event ${event.id} `);
699-
reject({
700-
error: `reply timeout: event ${event.id}`,
701-
code: "INTERNAL",
702-
});
761+
reject(
762+
new Nip47ReplyTimeoutError(
763+
`reply timeout: event ${event.id}`,
764+
"INTERNAL",
765+
),
766+
);
703767
}
704768

705769
const replyTimeoutCheck = setTimeout(replyTimeout, 60000);
@@ -716,24 +780,42 @@ export class NWCClient {
716780
try {
717781
response = JSON.parse(decryptedContent);
718782
} catch (e) {
719-
console.error(e);
783+
// console.error(e);
720784
clearTimeout(replyTimeoutCheck);
721785
sub.unsub();
722-
reject({ error: "invalid response", code: "INTERNAL" });
723-
return;
786+
reject(
787+
new Nip47ResponseDecodingError(
788+
"failed to deserialize response",
789+
"INTERNAL",
790+
),
791+
);
724792
}
725-
if (event.kind == 23195 && response.result) {
726-
// console.info("NIP-47 result", response.result);
727-
try {
793+
if (event.kind == 23195) {
794+
if (response.result) {
795+
// console.info("NIP-47 result", response.result);
728796
if (!resultValidator(response.result)) {
729-
throw new Error(
730-
"Response from NWC failed validation: " +
731-
JSON.stringify(response.result),
797+
clearTimeout(replyTimeoutCheck);
798+
sub.unsub();
799+
reject(
800+
new Nip47ResponseValidationError(
801+
"Response from NWC failed validation: " +
802+
JSON.stringify(response.result),
803+
"INTERNAL",
804+
),
732805
);
806+
return;
733807
}
734808
const dTag = event.tags.find((tag) => tag[0] === "d")?.[1];
735809
if (dTag === undefined) {
736-
throw new Error("No d tag found in response event");
810+
clearTimeout(replyTimeoutCheck);
811+
sub.unsub();
812+
reject(
813+
new Nip47ResponseValidationError(
814+
"No d tag found in response event",
815+
"INTERNAL",
816+
),
817+
);
818+
return;
737819
}
738820
results.push({
739821
...response.result,
@@ -745,28 +827,38 @@ export class NWCClient {
745827
//console.log("Received results", results);
746828
resolve(results);
747829
}
748-
} catch (error) {
749-
console.error(error);
830+
} else {
750831
clearTimeout(replyTimeoutCheck);
751832
sub.unsub();
752-
reject({
753-
error: (error as Error).message,
754-
code: "INTERNAL",
755-
});
833+
// console.error("Wallet error", response.error);
834+
reject(
835+
new Nip47WalletError(
836+
response.error?.message,
837+
response.error?.code,
838+
),
839+
);
756840
}
757841
} else {
758842
clearTimeout(replyTimeoutCheck);
759843
sub.unsub();
760-
reject({
761-
error: response.error?.message,
762-
code: response.error?.code,
763-
});
844+
reject(
845+
new Nip47UnexpectedResponseError(
846+
response.error?.message,
847+
response.error?.code,
848+
),
849+
);
764850
}
765851
});
766852

767853
function publishTimeout() {
854+
sub.unsub();
768855
//console.error(`Publish timeout: event ${event.id}`);
769-
reject({ error: `Publish timeout: event ${event.id}` });
856+
reject(
857+
new Nip47PublishTimeoutError(
858+
`Publish timeout: ${event.id}`,
859+
"INTERNAL",
860+
),
861+
);
770862
}
771863
const publishTimeoutCheck = setTimeout(publishTimeout, 5000);
772864

@@ -777,7 +869,9 @@ export class NWCClient {
777869
} catch (error) {
778870
//console.error(`Failed to publish to ${this.relay.url}`, error);
779871
clearTimeout(publishTimeoutCheck);
780-
reject({ error: `Failed to publish request: ${error}` });
872+
reject(
873+
new Nip47PublishError(`Failed to publish: ${error}`, "INTERNAL"),
874+
);
781875
}
782876
})();
783877
});

0 commit comments

Comments
 (0)