Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use actual error objects in NWC Client #209

Merged
merged 7 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getalby/sdk",
"version": "3.3.1",
"version": "3.4.0",
"description": "The SDK to integrate with Nostr Wallet Connect and the Alby API",
"repository": "https://github.com/getAlby/js-sdk.git",
"bugs": "https://github.com/getAlby/js-sdk/issues",
Expand Down
196 changes: 145 additions & 51 deletions src/NWCClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,32 @@ export interface NWCOptions {
secret?: string;
}

export class Nip47Error extends Error {
/**
* @deprecated please use message. Deprecated since v3.3.2. Will be removed in v4.0.0.
*/
error: string;
code: string;
constructor(message: string, code: string) {
super(message);
this.error = message;
this.code = code;
}
}

/**
* A NIP-47 response was received, but with an error code (see https://github.com/nostr-protocol/nips/blob/master/47.md#error-codes)
*/
export class Nip47WalletError extends Nip47Error {}

export class Nip47TimeoutError extends Nip47Error {}
export class Nip47PublishTimeoutError extends Nip47TimeoutError {}
export class Nip47ReplyTimeoutError extends Nip47TimeoutError {}
export class Nip47PublishError extends Nip47Error {}
export class Nip47ResponseDecodingError extends Nip47Error {}
export class Nip47ResponseValidationError extends Nip47Error {}
export class Nip47UnexpectedResponseError extends Nip47Error {}

export const NWCs: Record<string, NWCOptions> = {
alby: {
authorizationUrl: "https://nwc.getalby.com/apps/new",
Expand Down Expand Up @@ -331,15 +357,15 @@ export class NWCClient {
`height=${height},width=${width},top=${top},left=${left}`,
);
if (!popup) {
reject();
reject(new Error("failed to execute window.open"));
return;
} // only for TS?
}

const checkForPopup = () => {
if (popup && popup.closed) {
reject();
clearInterval(popupChecker);
window.removeEventListener("message", onMessage);
reject(new Error("Popup closed"));
}
};

Expand Down Expand Up @@ -466,7 +492,7 @@ export class NWCClient {
errors: [],
};
} catch (error) {
console.error("Failed to request multi_pay_keysend", error);
console.error("Failed to request multi_pay_invoice", error);
throw error;
}
}
Expand Down Expand Up @@ -590,10 +616,12 @@ export class NWCClient {
function replyTimeout() {
sub.unsub();
//console.error(`Reply timeout: event ${event.id} `);
reject({
error: `reply timeout: event ${event.id}`,
code: "INTERNAL",
});
reject(
new Nip47ReplyTimeoutError(
`reply timeout: event ${event.id}`,
"INTERNAL",
),
);
}

const replyTimeoutCheck = setTimeout(replyTimeout, 60000);
Expand All @@ -611,32 +639,64 @@ export class NWCClient {
try {
response = JSON.parse(decryptedContent);
} catch (e) {
reject({ error: "invalid response", code: "INTERNAL" });
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject(
new Nip47ResponseDecodingError(
"failed to deserialize response",
"INTERNAL",
),
);
return;
}
if (event.kind == 23195 && response.result) {
// console.info("NIP-47 result", response.result);
if (resultValidator(response.result)) {
resolve(response.result);
if (event.kind == 23195) {
if (response.result) {
// console.info("NIP-47 result", response.result);
if (resultValidator(response.result)) {
resolve(response.result);
} else {
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject(
new Nip47ResponseValidationError(
"response from NWC failed validation: " +
JSON.stringify(response.result),
"INTERNAL",
),
);
}
} else {
reject({
error:
"Response from NWC failed validation: " +
JSON.stringify(response.result),
code: "INTERNAL",
});
clearTimeout(replyTimeoutCheck);
sub.unsub();
// console.error("Wallet error", response.error);
reject(
new Nip47WalletError(
response.error?.message || "unknown Error",
response.error?.code || "INTERNAL",
),
);
}
} else {
reject({
error: response.error?.message,
code: response.error?.code,
});
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject(
new Nip47UnexpectedResponseError(
response.error?.message || "unknown Error",
response.error?.code || "INTERNAL",
),
);
}
});

function publishTimeout() {
sub.unsub();
//console.error(`Publish timeout: event ${event.id}`);
reject({ error: `Publish timeout: event ${event.id}` });
reject(
new Nip47PublishTimeoutError(
`publish timeout: ${event.id}`,
"INTERNAL",
),
);
}
const publishTimeoutCheck = setTimeout(publishTimeout, 5000);

Expand All @@ -647,7 +707,9 @@ export class NWCClient {
} catch (error) {
//console.error(`Failed to publish to ${this.relay.url}`, error);
clearTimeout(publishTimeoutCheck);
reject({ error: `Failed to publish request: ${error}` });
reject(
new Nip47PublishError(`failed to publish: ${error}`, "INTERNAL"),
);
}
})();
});
Expand Down Expand Up @@ -696,10 +758,12 @@ export class NWCClient {
function replyTimeout() {
sub.unsub();
//console.error(`Reply timeout: event ${event.id} `);
reject({
error: `reply timeout: event ${event.id}`,
code: "INTERNAL",
});
reject(
new Nip47ReplyTimeoutError(
`reply timeout: event ${event.id}`,
"INTERNAL",
),
);
}

const replyTimeoutCheck = setTimeout(replyTimeout, 60000);
Expand All @@ -716,24 +780,42 @@ export class NWCClient {
try {
response = JSON.parse(decryptedContent);
} catch (e) {
console.error(e);
// console.error(e);
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject({ error: "invalid response", code: "INTERNAL" });
return;
reject(
new Nip47ResponseDecodingError(
"failed to deserialize response",
"INTERNAL",
),
);
}
if (event.kind == 23195 && response.result) {
// console.info("NIP-47 result", response.result);
try {
if (event.kind == 23195) {
if (response.result) {
// console.info("NIP-47 result", response.result);
if (!resultValidator(response.result)) {
throw new Error(
"Response from NWC failed validation: " +
JSON.stringify(response.result),
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject(
new Nip47ResponseValidationError(
"Response from NWC failed validation: " +
JSON.stringify(response.result),
"INTERNAL",
),
);
return;
}
const dTag = event.tags.find((tag) => tag[0] === "d")?.[1];
if (dTag === undefined) {
throw new Error("No d tag found in response event");
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject(
new Nip47ResponseValidationError(
"No d tag found in response event",
"INTERNAL",
),
);
return;
}
results.push({
...response.result,
Expand All @@ -745,28 +827,38 @@ export class NWCClient {
//console.log("Received results", results);
resolve(results);
}
} catch (error) {
console.error(error);
} else {
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject({
error: (error as Error).message,
code: "INTERNAL",
});
// console.error("Wallet error", response.error);
reject(
new Nip47WalletError(
response.error?.message,
response.error?.code,
),
);
}
} else {
clearTimeout(replyTimeoutCheck);
sub.unsub();
reject({
error: response.error?.message,
code: response.error?.code,
});
reject(
new Nip47UnexpectedResponseError(
response.error?.message,
response.error?.code,
),
);
}
});

function publishTimeout() {
sub.unsub();
//console.error(`Publish timeout: event ${event.id}`);
reject({ error: `Publish timeout: event ${event.id}` });
reject(
new Nip47PublishTimeoutError(
`Publish timeout: ${event.id}`,
"INTERNAL",
),
);
}
const publishTimeoutCheck = setTimeout(publishTimeout, 5000);

Expand All @@ -777,7 +869,9 @@ export class NWCClient {
} catch (error) {
//console.error(`Failed to publish to ${this.relay.url}`, error);
clearTimeout(publishTimeoutCheck);
reject({ error: `Failed to publish request: ${error}` });
reject(
new Nip47PublishError(`Failed to publish: ${error}`, "INTERNAL"),
);
}
})();
});
Expand Down
Loading