diff --git a/__tests__/charges/createCharge.test.ts b/__tests__/charges/createCharge.test.ts new file mode 100644 index 0000000..c7896ce --- /dev/null +++ b/__tests__/charges/createCharge.test.ts @@ -0,0 +1,46 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + ChargeDataResponseType, + ChargeOptionsType, + isChargeResponseType, +} from "../../src/types/charges"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("createCharge", () => { + it("should successfully create a charge", async () => { + const payload: ChargeOptionsType = { + expiresIn: 300, + amount: "10000", + description: "My Charge Test Zapier", + internalId: "internalId", + callbackUrl: "https://my-website.com/zbd-callback", + }; + + const response = await ZBD.createCharge(payload); + + expect(isChargeResponseType(response)).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.message).toBe("Charge created."); + expect(response.data.amount).toBe(payload.amount); + expect(response.data.description).toBe(payload.description); + }); + + describe("createCharge error scenarios", () => { + it("should throw an error given an erroneous payload (amount = 0)", async () => { + const erroneousPayload: ChargeOptionsType = { + expiresIn: 100, + amount: "0", + description: "My Charge Test Zapier", + internalId: "internalId", + callbackUrl: "https://my-website.com/zbd-callback", + }; + + await expect(ZBD.createCharge(erroneousPayload)).rejects.toMatchObject({ + message: "Request has missing or mismatch params.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/charges/getCharge.test.ts b/__tests__/charges/getCharge.test.ts new file mode 100644 index 0000000..0f01475 --- /dev/null +++ b/__tests__/charges/getCharge.test.ts @@ -0,0 +1,32 @@ +import { zbd } from "@zbd/node"; +import { TEST_API_KEY } from "../../src/constants"; +import { + ChargeDataResponseType, + isChargeResponseType, +} from "../../src/types/charges"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("getCharge", () => { + const VALID_CHARGE_ID = "4f0fa38f-efbe-485f-9293-95e697f6fbd4"; + const INVALID_CHARGE_ID = "invalid"; + const INVALID_API_KEY = "INVALID_API_KEY"; + + // Success + it("should fetch charge details for a valid charge ID", async () => { + const data = await ZBD.getCharge(VALID_CHARGE_ID); + expect(data.success).toBe(true); + expect(data.message).toBe("Fetched Charge."); + + // Data Validation + expect(isChargeResponseType(data)).toBeTruthy(); + }); + + // Charge not found + it("should return a 404 for a non-existent charge ID", async () => { + await expect(ZBD.getCharge(INVALID_CHARGE_ID)).rejects.toMatchObject({ + message: "No Charge records found with this ID.", + status: 404, + }); + }); +}); diff --git a/__tests__/create-charge.test.ts b/__tests__/create-charge.test.ts deleted file mode 100644 index e86a5a9..0000000 --- a/__tests__/create-charge.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TEST_API_KEY } from '../src/constants'; -import { zbd } from '../src/zbd'; - -const ZBD = new zbd(TEST_API_KEY); - -describe('createCharge', () => { - it('should create a charge successfully', async () => { - const chargeData = { - amount: '10000', - expiresIn: 300, - description: 'My Charge Description', - callbackUrl: 'https://your-website.com/callback', - internalId: '11af01d092444a317cb33faa6b8304b8', - }; - - const response = await ZBD.createCharge(chargeData); - - expect(response).toBeDefined(); - expect(response.success).toBe(true); - expect(response.message).toBe('Charge created.'); - - const { data } = response; - expect(data.unit).toBe('msats'); - expect(data.amount).toBe(chargeData.amount); - expect(data.description).toBe(chargeData.description); - expect(data.internalId).toBe(chargeData.internalId); - expect(data.callbackUrl).toBe(chargeData.callbackUrl); - - expect(data).toHaveProperty('status'); - expect(data).toHaveProperty('createdAt'); - expect(data).toHaveProperty('expiresAt'); - expect(data).toHaveProperty('id'); - expect(data).toHaveProperty('invoice'); - }); -}); diff --git a/__tests__/fetch-access-token.test.ts b/__tests__/fetch-access-token.test.ts new file mode 100644 index 0000000..18b54a5 --- /dev/null +++ b/__tests__/fetch-access-token.test.ts @@ -0,0 +1,99 @@ +import { zbd } from "../src/zbd"; +import { API, API_URL, TEST_API_KEY } from "../src/constants"; +import { FetchAccessTokenRes, FetchTokenParam } from "../src/types"; + +const postData = jest.fn(); +describe("fetchAccessToken", () => { + let ZBD: zbd; + + beforeEach(() => { + ZBD = new zbd(TEST_API_KEY); + ZBD.setClientId("client_id"); + ZBD.setClientSecret("secret123"); + }); + + it("should fetch access token successfully", async () => { + await ZBD.generatePKCECodes(); + const code_verifier = ZBD.getCodeChallenge(); + + console.log("Code verifier: ", code_verifier); + // Arrange + const param: FetchTokenParam = { + code: "xxx11xx1-xxxx-xxxx-xxx1-1xx11xx111xx", + redirect_uri: "http://redirect-uri.com", + }; + + const mockAccessTokenResponse: FetchAccessTokenRes = { + access_token: "mockAccessToken", + token_type: "Bearer", + expires_in: 3600, + refresh_token: "mockRefreshToken", + refresh_token_expires_in: 7200, + scope: "read write", + }; + + postData.mockResolvedValue(mockAccessTokenResponse); + + // Act + const result = await ZBD.fetchAccessToken(param); + console.log("Client id: ", ZBD.clientId); + + // Assert + // expect(result).toEqual(mockAccessTokenResponse); + // expect(postData).toHaveBeenCalledWith({ + // url: `${API_URL}${API.FETCH_ACCESS_TOKEN_ENDPOINT}`, + // headers: { ...ZBD.apiCoreHeaders }, + // body: { + // client_id: ZBD.clientId, + // client_secret: ZBD.clientSecret, + // grant_type: "authorization_code", + // code: param.code, + // redirect_uri: param.redirect_uri, + // code_verifier: ZBD.codeVerifier, + // }, + // }); + }); + + it("should throw an error if authorization code is missing", async () => { + // Arrange + const param: FetchTokenParam = { + code: "", + redirect_uri: "http://redirect-uri.com", + }; + + // Act and Assert + await expect(ZBD.fetchAccessToken(param)).rejects.toThrow( + "Authorization code is required" + ); + }); + + it("should throw an error if redirect URI is missing", async () => { + // Arrange + const param: FetchTokenParam = { + code: "authorization_code", + redirect_uri: "", + }; + + // Act and Assert + await expect(ZBD.fetchAccessToken(param)).rejects.toThrow( + "Redirect URI is required" + ); + }); + + // Add more test cases for other validation checks + + it("should throw an error if fetching access token fails", async () => { + // Arrange + const param: FetchTokenParam = { + code: "authorization_code", + redirect_uri: "http://redirect-uri.com", + }; + + postData.mockRejectedValue(new Error("Network error")); + + // Act and Assert + await expect(ZBD.fetchAccessToken(param)).rejects.toThrow( + "A code challenge is required. Generate one using .generatePKCE()." + ); + }); +}); diff --git a/__tests__/gamertag/fetchChargeFromGamertag.test.ts b/__tests__/gamertag/fetchChargeFromGamertag.test.ts new file mode 100644 index 0000000..c2b6d72 --- /dev/null +++ b/__tests__/gamertag/fetchChargeFromGamertag.test.ts @@ -0,0 +1,46 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + FetchChargeFromGamertagDataResponseType, + FetchChargeFromGamertagOptionsType, + isFetchChargeFromGamertagDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Fetch Charge from Gamertag", () => { + const requestBody: FetchChargeFromGamertagOptionsType = { + amount: "1000", + gamertag: "andre", + description: "Requesting Charge for Gamertag", + callbackUrl: "https://your-website.com/zbd-callback", + internalId: "test-internal-id", + }; + + it("should successfully create a charge for a gamertag", async () => { + const response = await ZBD.createGamertagCharge(requestBody); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isFetchChargeFromGamertagDataResponseType(response)).toBeTruthy(); + }); + + describe("fetchGamertagByUserID error scenarios", () => { + it("should throw an error given a non-existent user ID", async () => { + const errorRequestBody: FetchChargeFromGamertagOptionsType = { + amount: "1000", + gamertag: "fakeGamerTagĆ", + description: "Requesting Charge for fake Gamertag", + callbackUrl: "https://your-website.com/zbd-callback", + internalId: "test-internal-id", + }; + + await expect( + ZBD.createGamertagCharge(errorRequestBody) + ).rejects.toMatchObject({ + message: "API request failed", + status: 500, + }); + }); + }); +}); diff --git a/__tests__/gamertag/fetchGamertagByUserID.test.ts b/__tests__/gamertag/fetchGamertagByUserID.test.ts new file mode 100644 index 0000000..2351469 --- /dev/null +++ b/__tests__/gamertag/fetchGamertagByUserID.test.ts @@ -0,0 +1,33 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + FetchGamertagByUserIdDataResponseType, + isFetchGamertagByUserIDDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Fetch Gamertag By User ID", () => { + const testUserID = "ec9b38d5-b126-4307-9d1e-8aa0dfab5d7e"; + const fakeUserID = "202020"; + + it("should successfully fetch a gamertag by user ID", async () => { + const response = await ZBD.getGamertagByUserId(testUserID); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(response.message).toBe("Fetched gamertag from uuid"); + + // Data Validation + expect(isFetchGamertagByUserIDDataResponseType(response)).toBeTruthy(); + }); + + describe("fetchGamertagByUserID error scenarios", () => { + it("should throw an error given a non-existent user ID", async () => { + await expect(ZBD.getGamertagByUserId(fakeUserID)).rejects.toMatchObject({ + message: "No gamertag found with this uuid", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/gamertag/fetchGamertagTransactionDetailsByID.test.ts b/__tests__/gamertag/fetchGamertagTransactionDetailsByID.test.ts new file mode 100644 index 0000000..9ff4792 --- /dev/null +++ b/__tests__/gamertag/fetchGamertagTransactionDetailsByID.test.ts @@ -0,0 +1,34 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + GamertagTransactionDataResponseType, + isGamertagTransactionDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Fetch Gamertag Transaction Details By ID", () => { + const TEST_TRANSACTION_ID = "3418e871-05f6-4745-b12b-ccd53da9c4d1"; + const NON_EXISTENT_TRANSACTION_ID = "903883f2-67d9-4707-a21b-ddff004fe041"; + + it("should successfully fetch transaction details by ID", async () => { + const response = await ZBD.getGamertagTransaction(TEST_TRANSACTION_ID); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + + expect(isGamertagTransactionDataResponseType(response)).toBeTruthy(); + }); + + describe("fetchGamertagTransactionDetailsByID error scenarios", () => { + it("should fail to fetch transaction details given false ID", async () => { + const response = await ZBD.getGamertagTransaction( + NON_EXISTENT_TRANSACTION_ID + ); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(response.message).toBe("Transaction not found"); + }); + }); +}); diff --git a/__tests__/gamertag/fetchUserIDFromGamertag.test.ts b/__tests__/gamertag/fetchUserIDFromGamertag.test.ts new file mode 100644 index 0000000..ea6000c --- /dev/null +++ b/__tests__/gamertag/fetchUserIDFromGamertag.test.ts @@ -0,0 +1,31 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + FetchUserIdByGamertagDataResponseType, + isFetchUserIdByGamertagDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Fetch User ID By Gamertag", () => { + const testGamertag = "foxp2"; + + it("should successfully fetch user ID for a valid gamertag", async () => { + const response = await ZBD.getUserIdByGamertag(testGamertag); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isFetchUserIdByGamertagDataResponseType(response)).toBeTruthy(); + }); + + it("should return an error for an invalid gamertag", async () => { + const invalidGamertag = "nonExistentTagč"; + + await expect( + ZBD.getUserIdByGamertag(invalidGamertag) + ).rejects.toMatchObject({ + message: "No user found with this gamertag", + status: 400, + }); + }); +}); diff --git a/__tests__/gamertag/sendPaymentToGamertag.test.ts b/__tests__/gamertag/sendPaymentToGamertag.test.ts new file mode 100644 index 0000000..3fba55f --- /dev/null +++ b/__tests__/gamertag/sendPaymentToGamertag.test.ts @@ -0,0 +1,43 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + SendGamertagPaymentOptionsType, + isSendGamertagPaymentDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Send Payment to Gamertag", () => { + const mockRequestBody: SendGamertagPaymentOptionsType = { + gamertag: "foxp2", + amount: "1000", + description: "Sending to ZBD Gamertag", + }; + + it("should successfully send payment to a gamertag", async () => { + const response = await ZBD.sendGamertagPayment(mockRequestBody); // Assuming ZBD has a method called sendPaymentToGamertag + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(response.message).toBe("Payment done."); + + expect(isSendGamertagPaymentDataResponseType(response)).toBeTruthy(); + }); + + describe("sendPaymentToGamertag error scenarios", () => { + it("should throw an error given a non-existent gamertag", async () => { + const errorRequestBody: SendGamertagPaymentOptionsType = { + gamertag: "fakeGamertagĆ", + amount: "1000", + description: "Sending to non-existent Gamertag", + }; + + await expect( + ZBD.sendGamertagPayment(errorRequestBody) + ).rejects.toMatchObject({ + message: "You cannot pay to this user, missing destination", + status: 500, + }); + }); + }); +}); diff --git a/__tests__/get-authentication-url.test.ts b/__tests__/get-authentication-url.test.ts new file mode 100644 index 0000000..0ea1f8b --- /dev/null +++ b/__tests__/get-authentication-url.test.ts @@ -0,0 +1,40 @@ +import { zbd } from "../src/zbd"; +import { TEST_API_KEY } from "../src/constants"; +describe("getAuthenticationUrl", () => { + let ZBD: zbd; + + beforeEach(() => { + ZBD = new zbd(TEST_API_KEY); + ZBD.setClientId("client_id"); + ZBD.setClientSecret("secret123"); + }); + + it("should generate auth url with correct params", async () => { + await ZBD.generatePKCECodes(); + + const params = { + redirect_uri: "https://example.com/callback", + state: "random_state", + }; + + const authUrl = await ZBD.getAuthenticationUrl(params); + + expect(authUrl).toContain("client_id=client_id"); + expect(authUrl).toContain("redirect_uri=https://example.com/callback"); + expect(authUrl).toContain("scope=user"); + expect(authUrl).toContain("state=random_state"); + expect(authUrl).toContain(`code_challenge=${await ZBD.getCodeChallenge()}`); + expect(authUrl).toContain("code_challenge_method=S256"); + }); + + it("should throw an error if client id is not set", async () => { + const params = { + redirect_uri: "https://example.com/callback", + state: "random_state", + }; + + await expect(ZBD.getAuthenticationUrl(params)).rejects.toThrow( + "A code challenge is required. Generate one using .generatePKCE()." + ); + }); +}); diff --git a/__tests__/get-btcusd.test.ts b/__tests__/get-btcusd.test.ts deleted file mode 100644 index a5a091a..0000000 --- a/__tests__/get-btcusd.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TEST_API_KEY } from '../src/constants'; -import { zbd } from '../src/zbd'; - -const ZBD = new zbd(TEST_API_KEY); - -describe('getBTCUSD', () => { - it('should fetch BTC to USD exchange rate data successfully', async () => { - const response = await ZBD.getBtcUsdExchangeRate(); - - expect(response).toBeDefined(); - expect(response.success).toBe(true); - expect(response.data).toHaveProperty('btcUsdPrice'); - expect(response.data).toHaveProperty('btcUsdTimestamp'); - expect(response.message).toBe('Successfully retrieved BTC USD price ticker information.'); - }); -}); diff --git a/__tests__/get-payment.test.ts b/__tests__/get-payment.test.ts deleted file mode 100644 index 201ce4a..0000000 --- a/__tests__/get-payment.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TEST_API_KEY } from '../src/constants'; -import { zbd } from '../src/zbd'; - -const ZBD = new zbd(TEST_API_KEY); - -describe('getPayment', () => { - // it('should fetch payment transaction details successfully', async () => { - // const paymentId = ''; // provide valid paymentId - - // const data = await ZBD.getPayment(paymentId); - // console.log(data) - - // expect(data).toBeDefined(); - // expect(data.paymentId).toBe(paymentId); - // }); - - it('should return an error when an invalid payment ID is provided', async () => { - const invalidPaymentId = 'invalid-id'; - - try { - await ZBD.getPayment(invalidPaymentId); - } catch (error) { - if (error instanceof Error) { - console.log(error) - expect(error.message).toBe('No Payment records found with this ID.') - } - - } - }); -}); \ No newline at end of file diff --git a/__tests__/get-wallet.test.ts b/__tests__/get-wallet.test.ts deleted file mode 100644 index a5a6bfe..0000000 --- a/__tests__/get-wallet.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TEST_API_KEY } from '../src/constants'; -import { zbd } from '../src/zbd'; - -const ZBD = new zbd(TEST_API_KEY); - -describe('getWallet', () => { - it('should fetch wallet data successfully', async () => { - const response = await ZBD.getWallet(); - - expect(response).toBeDefined(); - expect(response.data).toHaveProperty('unit'); - expect(response.data).toHaveProperty('balance'); - expect(response.message).toBe('Successfully retrieved Wallet.'); - }); - -}); diff --git a/__tests__/internal-transfer/internalTransfer.test.ts b/__tests__/internal-transfer/internalTransfer.test.ts new file mode 100644 index 0000000..b9d354a --- /dev/null +++ b/__tests__/internal-transfer/internalTransfer.test.ts @@ -0,0 +1,51 @@ +import { INTERNAL_TRANSFER_API_KEY } from "../../src/constants"; +import { + InternalTransferOptionsType, + isInternalTransferDataResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(INTERNAL_TRANSFER_API_KEY); + +describe("Initiate Internal Transfer", () => { + it("should initiate an internal transfer successfully", async () => { + const requestBody: InternalTransferOptionsType = { + amount: "1000", + receiverWalletId: "a96de0a1-6e7f-4247-b755-02ec541449ea", + }; + + const response = await ZBD.internalTransfer(requestBody); + + expect(response.success).toBe(true); + expect(response.message).toBe("Internal Transfer done."); + + // Data Validation + expect(isInternalTransferDataResponseType(response)).toBeTruthy(); + }); + + describe("internalTransfer error scenarios", () => { + it("should throw an error given an invalid receiver wallet ID", async () => { + const errorBody: InternalTransferOptionsType = { + amount: "100000", + receiverWalletId: "b804ee02-ec0b-4fd4-b99f-1f2d3d0001a6", + }; + + await expect(ZBD.internalTransfer(errorBody)).rejects.toMatchObject({ + message: "Error processing transfer.", + status: 400, + }); + }); + + it("should throw an error given excess of balance", async () => { + const errorBody: InternalTransferOptionsType = { + amount: "5000000", + receiverWalletId: "a96de0a1-6e7f-4247-b755-02ec541449ea", + }; + + await expect(ZBD.internalTransfer(errorBody)).rejects.toMatchObject({ + message: "Error processing transfer.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/keysend/sendKeysendPayment.test.ts b/__tests__/keysend/sendKeysendPayment.test.ts new file mode 100644 index 0000000..437d8cc --- /dev/null +++ b/__tests__/keysend/sendKeysendPayment.test.ts @@ -0,0 +1,41 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { KeysendOptionsType, isKeysendDataResponseType } from "../../src/types"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Send Keysend Payment", () => { + it("should send a keysend payment successfully", async () => { + const requestBody: KeysendOptionsType = { + amount: "1000", + pubkey: + "0332d57355d673e217238ce3e4be8491aa6b2a13f95494133ee243e57df1653ace", + tlvRecords: [], + metadata: {}, + callbackUrl: "{{callbackURL}}", + }; + + const response = await ZBD.sendKeysendPayment(requestBody); + + // Data Validation + expect(response.success).toBe(true); + expect(isKeysendDataResponseType(response)).toBeTruthy(); + }); + + describe("Send Keysend error scenarios", () => { + it("should fail keysend payment given an invalid pubkey", async () => { + const errorBody: KeysendOptionsType = { + amount: "1000", + pubkey: "23", + tlvRecords: [], + metadata: {}, + callbackUrl: "{{callbackURL}}", + }; + + await expect(ZBD.sendKeysendPayment(errorBody)).rejects.toMatchObject({ + message: "Keysend payment failed.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/lightning/fetchChargeFromLightningAddress.test.ts b/__tests__/lightning/fetchChargeFromLightningAddress.test.ts new file mode 100644 index 0000000..8ebb11f --- /dev/null +++ b/__tests__/lightning/fetchChargeFromLightningAddress.test.ts @@ -0,0 +1,46 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + FetchChargeFromLightningAddressDataResponseType, + CreateChargeFromLightningAddressOptionsType, + isFetchChargeFromLightningAddressResponseType, +} from "../../src/types/lightning"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("fetchChargeFromLightningAddress", () => { + const payload: CreateChargeFromLightningAddressOptionsType = { + lnAddress: "andre@zbd.gg", + lnaddress: "andre@zbd.gg", + amount: "10000", + description: "Sending to a Lightning Address", + }; + + it("should successfully fetch a charge from Lightning Address", async () => { + const response = await ZBD.createChargeFromLightningAddress(payload); + + expect( + isFetchChargeFromLightningAddressResponseType(response) + ).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.data.amount).toBe(payload.amount); + }); + + describe("fetchChargeFromLightningAddress error scenarios", () => { + const errorPayload: CreateChargeFromLightningAddressOptionsType = { + lnAddress: "aol@lol.xqz", + lnaddress: "aol@lol.xqz", + amount: "10000", + description: "Sending to a Lightning Address", + }; + + it("should throw an error when given invalid Lightning Address", async () => { + await expect( + ZBD.createChargeFromLightningAddress(errorPayload) + ).rejects.toMatchObject({ + message: "Could not get lighning address info.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/lightning/sendPaymentToLightningAddress.test.ts b/__tests__/lightning/sendPaymentToLightningAddress.test.ts new file mode 100644 index 0000000..733a03f --- /dev/null +++ b/__tests__/lightning/sendPaymentToLightningAddress.test.ts @@ -0,0 +1,48 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + SendLightningAddressPaymentDataResponseType, + SendLightningAddressPaymentOptionsType, + isSendLightningAddressPaymentDataResponseType, +} from "../../src/types/lightning"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("sendToLightning", () => { + it("should successfully send a payment to a Lightning Address", async () => { + const payload: SendLightningAddressPaymentOptionsType = { + lnAddress: "andre@zbd.gg", + amount: "10000", + comment: "Sending to a Lightning Address", + callbackUrl: "https://your-domain.com/zbd-callback", + internalId: "uniqueIdFromYourSystem", + }; + + const response = await ZBD.sendLightningAddressPayment(payload); + + expect( + isSendLightningAddressPaymentDataResponseType(response) + ).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.data.amount).toBe(payload.amount); + }); + + describe("sendToLightning error scenarios", () => { + const errorPayload: SendLightningAddressPaymentOptionsType = { + lnAddress: "aol@lol.xqz", + amount: "10000", + comment: "Sending to a Lightning Address", + callbackUrl: "https://your-domain.com/zbd-callback", + internalId: "uniqueIdFromYourSystem", + }; + + it("should throw an error when given invalid Lightning Address", async () => { + await expect( + ZBD.sendLightningAddressPayment(errorPayload) + ).rejects.toMatchObject({ + message: "Could not get lighning address info.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/lightning/validateLightningAddress.test.ts b/__tests__/lightning/validateLightningAddress.test.ts new file mode 100644 index 0000000..821b62b --- /dev/null +++ b/__tests__/lightning/validateLightningAddress.test.ts @@ -0,0 +1,33 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + ValidateLightningAddressDataResponseType, + isValidateLightningAddressDataResponseType, +} from "../../src/types/lightning"; // Your provided interface + +const ZBD = new zbd(TEST_API_KEY); + +describe("validateLightningAddress", () => { + const mockLightningAddress = "andre@zbd.gg"; + + it("should successfully validate a Lightning Address", async () => { + const response = await ZBD.validateLightningAddress(mockLightningAddress); + + expect(response.success).toBe(true); + + // Data Validation + expect(isValidateLightningAddressDataResponseType(response)).toBeTruthy(); + }); + + describe("validateLightningAddress error scenarios", () => { + const errorLightningAddress = "aol@lol.xqz"; + + it("should say valid is false given invalid Lightning Address", async () => { + const response = await ZBD.validateLightningAddress( + errorLightningAddress + ); + expect(response.data.valid).toBe(false); + expect(response.success).toBe(true); + }); + }); +}); diff --git a/__tests__/misc/getAPIProductionIPs.test.ts b/__tests__/misc/getAPIProductionIPs.test.ts new file mode 100644 index 0000000..52c1941 --- /dev/null +++ b/__tests__/misc/getAPIProductionIPs.test.ts @@ -0,0 +1,19 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + ProdIPSDataResponseType, + isApiProductionIPsResponseType, +} from "../../src/types/misc"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("API Production IPs", () => { + it("should successfully fetch the list of production IPs", async () => { + const response = await ZBD.getZBDProdIps(); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isApiProductionIPsResponseType(response)).toBeTruthy(); + expect(Array.isArray(response.data.ips)).toBe(true); + }); +}); diff --git a/__tests__/misc/getBTCtoUSDExchangeRate.test.ts b/__tests__/misc/getBTCtoUSDExchangeRate.test.ts new file mode 100644 index 0000000..3ad96d0 --- /dev/null +++ b/__tests__/misc/getBTCtoUSDExchangeRate.test.ts @@ -0,0 +1,21 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + BTCUSDDataResponseType, + isBtcUsdExchangeRateResponseType, +} from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("BTC to USD Exchange Rate", () => { + it("should successfully fetch the latest BTC to USD exchange rate", async () => { + const response = await ZBD.getBtcUsdExchangeRate(); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isBtcUsdExchangeRateResponseType(response)).toBeTruthy(); + expect(response.message).toBe( + "Successfully retrieved BTC USD price ticker information." + ); + }); +}); diff --git a/__tests__/misc/isSupportedRegion.test.ts b/__tests__/misc/isSupportedRegion.test.ts new file mode 100644 index 0000000..cd4baaf --- /dev/null +++ b/__tests__/misc/isSupportedRegion.test.ts @@ -0,0 +1,45 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { + SupportedRegionDataResponseType, + isSupportedRegionResponseType, +} from "../../src/types/misc"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("Is Supported Region", () => { + const ipAddress = "66.109.221.0"; + const unsupportedIpAddress = "176.57.67.255"; + const errorIpAddress = "11111.11111"; + + it("should successfully check that given IP address belongs to a supported region", async () => { + const response = await ZBD.isSupportedRegion(ipAddress); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isSupportedRegionResponseType(response)).toBeTruthy(); + expect(response.data.ipAddress).toBe(ipAddress); + expect(response.data.isSupported).toBe(true); + }); + + it("should successfully check that given IP address does not belong to a supported region", async () => { + const response = await ZBD.isSupportedRegion(unsupportedIpAddress); + + expect(response).toBeDefined(); + expect(response.success).toBe(true); + expect(isSupportedRegionResponseType(response)).toBeTruthy(); + expect(response.data.ipAddress).toBe(unsupportedIpAddress); + expect(response.data.isSupported).toBe(false); + }); + + describe("checkSupportedRegion error scenarios", () => { + it("should throw error given invalid IP format", async () => { + await expect(ZBD.isSupportedRegion(errorIpAddress)).rejects.toMatchObject( + { + message: "Ip Address is not valid.", + status: 400, + } + ); + }); + }); +}); diff --git a/__tests__/payments/getPaymentDetails.test.ts b/__tests__/payments/getPaymentDetails.test.ts new file mode 100644 index 0000000..11612b8 --- /dev/null +++ b/__tests__/payments/getPaymentDetails.test.ts @@ -0,0 +1,30 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { isInvoicePaymentDataResponseType } from "../../src/types/payments"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("getPaymentDetails", () => { + const mockPaymentId = "30cba220-e8b0-4df7-81a8-88cbae76b929"; + + it("should successfully fetch a payment detail", async () => { + const response = await ZBD.getPayment(mockPaymentId); + + expect(isInvoicePaymentDataResponseType(response)).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.message).toBe("Fetched Payment."); + expect(response.data.id).toBe(mockPaymentId); + expect(response.data.description).toBe("Custom Payment Description"); + }); + + describe("getPaymentDetails error scenarios", () => { + it("should throw an error given incorrect payment id", async () => { + const errorPaymentId = "error"; + + await expect(ZBD.getPayment(errorPaymentId)).rejects.toMatchObject({ + message: "No Payment records found with this ID.", + status: 404, + }); + }); + }); +}); diff --git a/__tests__/payments/payInvoice.test.ts b/__tests__/payments/payInvoice.test.ts new file mode 100644 index 0000000..ff539ca --- /dev/null +++ b/__tests__/payments/payInvoice.test.ts @@ -0,0 +1,139 @@ +import { TEST_API_KEY, TEST_RECEIVER_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + InvoicePaymentDataResponseType, + SendPaymentOptionsType, + isInvoicePaymentDataResponseType, +} from "../../src/types/payments"; +import { + ChargeOptionsType, + isChargeResponseType, + isWalletDataResponseType, +} from "../../src/types"; + +const payerZBD = new zbd(TEST_API_KEY); +const receiverZBD = new zbd(TEST_RECEIVER_API_KEY); + +describe("payInvoice", () => { + it("should successfully pay an invoice", async () => { + // first, create charge with receiver + const chargePayload: ChargeOptionsType = { + expiresIn: 300, + amount: "10000", + description: "My Charge Test", + internalId: "internalId", + callbackUrl: "https://my-website.com/zbd-callback", + }; + + // verify charge + const chargeResponse = await receiverZBD.createCharge(chargePayload); + + expect(isChargeResponseType(chargeResponse)).toBeTruthy(); + expect(chargeResponse.success).toBe(true); + expect(chargeResponse.message).toBe("Charge created."); + expect(chargeResponse.data.amount).toBe(chargePayload.amount); + expect(chargeResponse.data.description).toBe(chargePayload.description); + + // create payment and pay it + const paymentPayload: SendPaymentOptionsType = { + description: "Custom Payment Description", + internalId: "internalId", + invoice: chargeResponse.data.invoice.request, + callbackUrl: "https://my-website.com/callback/zbd", + amount: chargeResponse.data.amount, + }; + + const response = await payerZBD.sendPayment(paymentPayload); + + expect(isInvoicePaymentDataResponseType(response)).toBeTruthy(); + expect(response.success).toBe(true); + expect(response.message).toBe("Payment done."); + expect(response.data.amount).toBe(String(paymentPayload.amount)); + expect(response.data.description).toBe(paymentPayload.description); + }); + + describe("payInvoice error scenarios", () => { + it("should throw an error when attempting to pay users own invoice", async () => { + // create charge with user + const chargePayload: ChargeOptionsType = { + expiresIn: 300, + amount: "10000", + description: "My Charge Test", + internalId: "internalId", + callbackUrl: "https://my-website.com/zbd-callback", + }; + + const chargeResponse = await payerZBD.createCharge(chargePayload); + + expect(isChargeResponseType(chargeResponse)).toBeTruthy(); + expect(chargeResponse.success).toBe(true); + expect(chargeResponse.message).toBe("Charge created."); + expect(chargeResponse.data.amount).toBe(chargePayload.amount); + expect(chargeResponse.data.description).toBe(chargePayload.description); + + const errorPaymentPayload: SendPaymentOptionsType = { + description: "Custom Payment Description", + internalId: "internalId", + invoice: chargeResponse.data.invoice.request, + callbackUrl: "https://my-website.com/callback/zbd", + amount: chargeResponse.data.amount, + }; + + await expect( + payerZBD.sendPayment(errorPaymentPayload) + ).rejects.toMatchObject({ + message: "You cannot Pay your own Charge.", + status: 400, + }); + }); + + it("should throw an error when given non-existent invoice", async () => { + const errorPayload: SendPaymentOptionsType = { + description: "Custom Payment Description", + internalId: "11af01d092444a317cb33faa6b8304b8", + invoice: "adsa", + callbackUrl: "https://my-website.com/callback/zbd", + amount: "10000", + }; + + await expect(payerZBD.sendPayment(errorPayload)).rejects.toMatchObject({ + message: "Could not decode the provided invoice.", + status: 400, + }); + }); + + // Integration test: + it("should throw an error when attempting to pay more than balance allows", async () => { + const chargePayload: ChargeOptionsType = { + expiresIn: 300, + amount: "500000000", + description: "My Charge Test", + internalId: "internalId", + callbackUrl: "https://my-website.com/zbd-callback", + }; + + // verify charge + const chargeResponse = await receiverZBD.createCharge(chargePayload); + + expect(isChargeResponseType(chargeResponse)).toBeTruthy(); + expect(chargeResponse.success).toBe(true); + expect(chargeResponse.message).toBe("Charge created."); + expect(chargeResponse.data.amount).toBe(chargePayload.amount); + expect(chargeResponse.data.description).toBe(chargePayload.description); + + // create payment and attempt to pay it + const paymentPayload: SendPaymentOptionsType = { + description: "Custom Payment Description", + internalId: "internalId", + invoice: chargeResponse.data.invoice.request, + callbackUrl: "https://my-website.com/callback/zbd", + amount: chargeResponse.data.amount, + }; + + await expect(payerZBD.sendPayment(paymentPayload)).rejects.toMatchObject({ + message: "You do not have enough funds for this transaction and fees.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/static-charges/createStaticCharge.test.ts b/__tests__/static-charges/createStaticCharge.test.ts new file mode 100644 index 0000000..d4a8f78 --- /dev/null +++ b/__tests__/static-charges/createStaticCharge.test.ts @@ -0,0 +1,52 @@ +import { zbd } from "@zbd/node"; +import { TEST_API_KEY } from "../../src/constants"; +import { + StaticChargeDataResponseType, + StaticChargeOptionsType, + isStaticChargeDataResponseType, +} from "../../src/types/static-charges"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("createStaticCharge", () => { + // Data for creating static charge + const validStaticChargeData: StaticChargeOptionsType = { + allowedSlots: 0, + minAmount: "10000", + maxAmount: "20000", + description: "This is my static charge", + internalId: "myInternalId1", + callbackUrl: "https://my-website.com/zbd-callback", + successMessage: "Congratulations your payment was successful!", + }; + + // Success + it("should successfully create a static charge", async () => { + const response = await ZBD.createStaticCharge(validStaticChargeData); + expect(response.message).toBe("Successfully created Static Charge."); + + // Data Validation + expect(isStaticChargeDataResponseType(response)).toBeTruthy(); + }); + + describe("createStaticCharge error scenarios", () => { + it("should throw an error given a faulty payload (no min max amount)", async () => { + const invalidStaticChargeData: StaticChargeOptionsType = { + allowedSlots: null, + minAmount: "0", + maxAmount: "0", + description: "This is my static charge", + internalId: "myInternalId1", + callbackUrl: "https://my-website.com/zbd-callback", + successMessage: "Congratulations your payment was successful!", + }; + + await expect( + ZBD.createStaticCharge(invalidStaticChargeData) + ).rejects.toMatchObject({ + message: "Error creating Static Charge.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/static-charges/getStaticCharge.test.ts b/__tests__/static-charges/getStaticCharge.test.ts new file mode 100644 index 0000000..bb8b2fb --- /dev/null +++ b/__tests__/static-charges/getStaticCharge.test.ts @@ -0,0 +1,35 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + StaticChargeDataResponseType, + isStaticChargeDataResponseType, +} from "../../src/types/static-charges"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("getStaticCharge", () => { + const STATIC_CHARGE_ID = "45c225b1-022b-4a37-98d6-5a5568f78d11"; + + it("should successfully fetch Static Charge details", async () => { + const responseData = await ZBD.getStaticCharge(STATIC_CHARGE_ID); + + expect(responseData.message).toBe( + "Successfully retrieved Static Charge data." + ); + expect(responseData.data.id).toBe(STATIC_CHARGE_ID); + + // Data Validation + expect(isStaticChargeDataResponseType(responseData)).toBeTruthy(); + }); + + // Static Charge not found + it("should return a 404 for a non-existent charge ID", async () => { + const NON_EXISTENT_STATIC_CHARGE_ID = "invalid"; + await expect( + ZBD.getCharge(NON_EXISTENT_STATIC_CHARGE_ID) + ).rejects.toMatchObject({ + message: "No Charge records found with this ID.", + status: 404, + }); + }); +}); diff --git a/__tests__/static-charges/updateStaticCharge.test.ts b/__tests__/static-charges/updateStaticCharge.test.ts new file mode 100644 index 0000000..d2c6184 --- /dev/null +++ b/__tests__/static-charges/updateStaticCharge.test.ts @@ -0,0 +1,61 @@ +import { zbd } from "@zbd/node"; +import { TEST_API_KEY } from "../../src/constants"; +import { + StaticChargeDataResponseType, + StaticChargeOptionsType, + isStaticChargeDataResponseType, +} from "../../src/types/static-charges"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("PATCH Update Static Charge", () => { + const STATIC_CHARGE_ID = "11311e05-c0da-4382-abb1-385a4659908b"; + it("should successfully update a Static Charge", async () => { + const updateOptions: StaticChargeOptionsType = { + allowedSlots: 0, + minAmount: "10000", + maxAmount: "5000000", + description: "This is my editted static charge", + internalId: "myInternalId1", + callbackUrl: "https://my-website.com/zbd-callback", + successMessage: "Congratulations your payment success msg was editted!", + }; + + const responseData = await ZBD.updateStaticCharge( + STATIC_CHARGE_ID, + updateOptions + ); + + expect(responseData.message).toBe("Successfully updated Static Charge."); + expect(responseData.data.id).toBe(STATIC_CHARGE_ID); + expect(responseData.data.description).toBe(updateOptions.description); + // Data Validation + expect(isStaticChargeDataResponseType(responseData)).toBeTruthy(); + }); + + describe("updateStaticCharge error scenarios", () => { + it("should throw an error given a non-existent static Charge ID", async () => { + const NON_EXISTENT_STATIC_CHARGE_ID = "10101"; + + const invalidUpdateOptions: StaticChargeOptionsType = { + allowedSlots: 0, + minAmount: "10000", + maxAmount: "5000000", + description: "This is my editted static charge", + internalId: "myInternalId1", + callbackUrl: "https://my-website.com/zbd-callback", + successMessage: "Congratulations your payment success msg was editted!", + }; + + await expect( + ZBD.updateStaticCharge( + NON_EXISTENT_STATIC_CHARGE_ID, + invalidUpdateOptions + ) + ).rejects.toMatchObject({ + message: "Error updating Static Charge data.", + status: 400, + }); + }); + }); +}); diff --git a/__tests__/wallet/getWallet.test.ts b/__tests__/wallet/getWallet.test.ts new file mode 100644 index 0000000..52f809d --- /dev/null +++ b/__tests__/wallet/getWallet.test.ts @@ -0,0 +1,18 @@ +import { TEST_API_KEY, TEST_RECEIVER_API_KEY } from "../../src/constants"; +import { isWalletDataResponseType } from "../../src/types"; +import { zbd } from "../../src/zbd"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("getWallet", () => { + it("should fetch wallet details successfully", async () => { + const response = await ZBD.getWallet(); + expect(response).toBeDefined(); + + // Data Validation + expect(isWalletDataResponseType(response)).toBeTruthy(); + + // Additional check for the balance to ensure it's a valid number string + expect(Number(response.data.balance)).not.toBeNaN(); + }); +}); diff --git a/__tests__/withdrawal/createWithdrawalRequest.test.ts b/__tests__/withdrawal/createWithdrawalRequest.test.ts new file mode 100644 index 0000000..5279336 --- /dev/null +++ b/__tests__/withdrawal/createWithdrawalRequest.test.ts @@ -0,0 +1,61 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + CreateWithdrawalRequestDataResponseType, + WithdrawalRequestOptionsType, + isCreateWithdrawalRequestDataResponseType, + isGetWithdrawalRequestDataResponseType, +} from "../../src/types"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("createWithdrawalRequest", () => { + const requestBody: WithdrawalRequestOptionsType = { + expiresIn: 300, + amount: "10000", + description: "My Withdrawal Description", + internalId: "internalId", + callbackUrl: "https://your-website.com/callback", + }; + + it("should successfully create a withdrawal request", async () => { + const responseData: CreateWithdrawalRequestDataResponseType = + await ZBD.createWithdrawalRequest(requestBody); + + expect(responseData.success).toBe(true); + expect(responseData.message).toBe( + "Successfully created Withdrawal Request." + ); + + // Check if returned data matches request body data + expect(responseData.data.amount).toBe(requestBody.amount); + expect(responseData.data.description).toBe(requestBody.description); + expect(responseData.data.internalId).toBe(requestBody.internalId); + expect(responseData.data.callbackUrl).toBe(requestBody.callbackUrl); + + // Data Validation + expect( + isCreateWithdrawalRequestDataResponseType(responseData) + ).toBeTruthy(); + }); + + describe("createWithdrawalRequest error scenarios", () => { + it("should throw an error given a faulty payload (amount invalid)", async () => { + const faultyRequestBody: WithdrawalRequestOptionsType = { + expiresIn: 300, + amount: "0", + description: "My Withdrawal Description", + internalId: "internalId", + callbackUrl: "https://your-website.com/callback", + }; + + await expect( + ZBD.createWithdrawalRequest(faultyRequestBody) + ).rejects.toMatchObject({ + message: + 'The "amount" property (in millisatoshis) must be greater than 1 sat.', + status: 400, + }); + }); + }); +}); diff --git a/__tests__/withdrawal/getWithdrawalRequest.test.ts b/__tests__/withdrawal/getWithdrawalRequest.test.ts new file mode 100644 index 0000000..4b5119c --- /dev/null +++ b/__tests__/withdrawal/getWithdrawalRequest.test.ts @@ -0,0 +1,32 @@ +import { TEST_API_KEY } from "../../src/constants"; +import { zbd } from "../../src/zbd"; +import { + GetWithdrawalRequestDataResponseType, + isGetWithdrawalRequestDataResponseType, +} from "../../src/types/withdrawal"; + +const ZBD = new zbd(TEST_API_KEY); + +describe("getWithdrawalRequest", () => { + const WITHDRAWAL_ID = "e3c7321a-cc06-48da-90a8-589bd175ace9"; + + it("should successfully fetch Withdrawal Request details", async () => { + const responseData = await ZBD.getWithdrawalRequest(WITHDRAWAL_ID); + + expect(responseData.data.id).toBe(WITHDRAWAL_ID); + + // Data Validation + expect(isGetWithdrawalRequestDataResponseType(responseData)).toBeTruthy(); + }); + + describe("createWithdrawalRequest error scenarios", () => { + it("should return an error for a non-existent withdrawal ID", async () => { + const NON_EXISTENT_WITHDRAWAL_ID = "invalid-id"; + await expect( + ZBD.getWithdrawalRequest(NON_EXISTENT_WITHDRAWAL_ID) + ).resolves.toMatchObject({ + message: "Failed to fetch withdrawal request.", + }); + }); + }); +}); diff --git a/package-lock.json b/package-lock.json index e0f58bb..10d7749 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@zbd/node", - "version": "0.6.2", + "version": "0.6.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@zbd/node", - "version": "0.6.2", + "version": "0.6.3", "license": "MIT", "dependencies": { - "@zbd/node": "^0.6.2", + "@zbd/node": "^0.6.3", "node-fetch": "^3.3.1" }, "devDependencies": { @@ -2680,9 +2680,9 @@ "dev": true }, "node_modules/@zbd/node": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@zbd/node/-/node-0.6.2.tgz", - "integrity": "sha512-66PesqbnByJFcvNUo9KaIJ3/ISENfQxaKj0fRfUA8c3Z5QEFk7Oqtfquhc4ocs940i5fm8IuoqvmHcnWQuUlmg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@zbd/node/-/node-0.6.3.tgz", + "integrity": "sha512-pKt8f6cJZ7HlbBHtJRXgMOfmp9M1WNIhZtSxuV80zEhZOf+VNVlI3w+cvi0JsHpfK/7lCJEejENs9GjU/SN/9Q==", "engines": { "node": ">=14" } diff --git a/package.json b/package.json index 59e0489..a91e43e 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "node": ">=14" }, "dependencies": { - "@zbd/node": "^0.6.2", + "@zbd/node": "^0.6.3", "node-fetch": "^3.3.1" } } diff --git a/src/constants.ts b/src/constants.ts index 2d92300..8152b6c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,45 +1,60 @@ // BaseUrl -export const API_URL = 'https://api.zebedee.io'; +export const API_URL = "https://api.zebedee.io"; // Test API Key -export const TEST_API_KEY = "8qJxln3JVJsQM0IOJhfnJopO0JSiLdlK" +export const TEST_API_KEY: string = "8qJxln3JVJsQM0IOJhfnJopO0JSiLdlK"; + +// Test API Key 2 (Receiver) +export const TEST_RECEIVER_API_KEY = "dESXgwgy7GziAbVA7AyN56uATloaburz" + +// Test API Key 3 (Internal Transfer) +export const INTERNAL_TRANSFER_API_KEY = "xqIdOA5pZAb9Ir64Qs4BlDlZZ6WrfWGW" + + // Endpoints export const API = { // Wallet - WALLET_ENDPOINT: '/v0/wallet', + WALLET_ENDPOINT: "/v0/wallet", // Charges (Pay Ins) - CHARGES_ENDPOINT: '/v0/charges', - STATIC_CHARGES_ENDPOINT: '/v0/static-charges', + CHARGES_ENDPOINT: "/v0/charges", + STATIC_CHARGES_ENDPOINT: "/v0/static-charges", // Payments (Pay Outs) - PAYMENTS_ENDPOINT: '/v0/payments', + PAYMENTS_ENDPOINT: "/v0/payments", // Lightning Address - SEND_LN_ADDRESS_PAYMENT_ENDPOINT: '/v0/ln-address/send-payment', - VALIDATE_LN_ADDRESS_ENDPOINT: '/v0/ln-address/validate', - CREATE_CHARGE_FROM_LN_ADDRESS_ENDPOINT: '/v0/ln-address/fetch-charge', + SEND_LN_ADDRESS_PAYMENT_ENDPOINT: "/v0/ln-address/send-payment", + VALIDATE_LN_ADDRESS_ENDPOINT: "/v0/ln-address/validate", + CREATE_CHARGE_FROM_LN_ADDRESS_ENDPOINT: "/v0/ln-address/fetch-charge", // ZBD Gamertags - SEND_GAMERTAG_PAYMENT_ENDPOINT: '/v0/gamertag/send-payment', - GET_GAMERTAG_PAYMENT_ENDPOINT: '/v0/gamertag/transaction', - GET_USERID_FROM_GAMERTAG_ENDPOINT: '/v0/user-id/gamertag', - GET_GAMERTAG_FROM_USERID_ENDPOINT: '/v0/gamertag/user-id', - CREATE_CHARGE_FROM_GAMERTAG_ENDPOINT: '/v0/gamertag/charges', + SEND_GAMERTAG_PAYMENT_ENDPOINT: "/v0/gamertag/send-payment", + GET_GAMERTAG_PAYMENT_ENDPOINT: "/v0/gamertag/transaction", + GET_USERID_FROM_GAMERTAG_ENDPOINT: "/v0/user-id/gamertag", + GET_GAMERTAG_FROM_USERID_ENDPOINT: "/v0/gamertag/user-id", + CREATE_CHARGE_FROM_GAMERTAG_ENDPOINT: "/v0/gamertag/charges", + + // ZBD Login (OAuth2) + GET_AUTHORIZATION_ENDPOINT: "/v1/oauth2/authorize", + FETCH_ACCESS_TOKEN_ENDPOINT: "/v1/oauth2/token", + REFRESH_TOKEN_ENDPOINT: "/v1/oauth2/token", + GET_USER_ENDPOINT: "/v1/oauth2/user", + GET_USER_WALLET_DATA_ENDPOINT: "/v1/oauth2/wallet", // Withdrawal Requests - WITHDRAWAL_REQUESTS_ENDPOINT: '/v0/withdrawal-requests', + WITHDRAWAL_REQUESTS_ENDPOINT: "/v0/withdrawal-requests", // Keysend - KEYSEND_PAYMENT_ENDPOINT: '/v0/keysend-payment', + KEYSEND_PAYMENT_ENDPOINT: "/v0/keysend-payment", // Internal Transfers - INTERNAL_TRANSFER_ENDPOINT: '/v0/internal-transfer', + INTERNAL_TRANSFER_ENDPOINT: "/v0/internal-transfer", // Utilities - IS_SUPPORTED_REGION_ENDPOINT: '/v0/is-supported-region', - DECODE_INVOICE_ENDPOINT: '/v0/decode-invoice', - FETCH_ZBD_PROD_IPS_ENDPOINT: '/v0/prod-ips', - BTCUSD_PRICE_TICKER_ENDPOINT: '/v0/btcusd', -} + IS_SUPPORTED_REGION_ENDPOINT: "/v0/is-supported-region", + DECODE_INVOICE_ENDPOINT: "/v0/decode-invoice", + FETCH_ZBD_PROD_IPS_ENDPOINT: "/v0/prod-ips", + BTCUSD_PRICE_TICKER_ENDPOINT: "/v0/btcusd", +}; diff --git a/src/types/charges.ts b/src/types/charges.ts index 37237e5..7712307 100644 --- a/src/types/charges.ts +++ b/src/types/charges.ts @@ -6,6 +6,7 @@ export interface ChargeOptionsType { callbackUrl: string; } + export interface DecodeChargeOptionsType { invoice: string; } @@ -45,4 +46,27 @@ export interface DecodeChargeResponseType { invoiceDescriptionHash: string | null; } success: boolean; +} + + +export function isChargeResponseType(obj: any): obj is ChargeDataResponseType { + return ( + obj && + typeof obj.success === "boolean" && + typeof obj.message === "string" && + obj.data && + typeof obj.data.id === "string" && + typeof obj.data.unit === "string" && + typeof obj.data.amount === "string" && + typeof obj.data.createdAt === "string" && + typeof obj.data.callbackUrl === "string" && + typeof obj.data.internalId === "string" && + typeof obj.data.description === "string" && + typeof obj.data.expiresAt === "string" && + typeof obj.data.confirmedAt === "string" || obj.data.confirmedAt === null && + typeof obj.data.status === "string" && + obj.data.invoice && + typeof obj.data.invoice.request === "string" && + typeof obj.data.invoice.uri === "string" + ); } \ No newline at end of file diff --git a/src/types/gamertag.ts b/src/types/gamertag.ts index 9d9e552..eeaaf89 100644 --- a/src/types/gamertag.ts +++ b/src/types/gamertag.ts @@ -16,7 +16,7 @@ export interface FetchGamertagByUserIdDataResponseType { export interface GamertagTransactionDataResponseType { data: { id: string; - receivedId: string; + receiverId: string; amount: string; fee: string; unit: string; @@ -25,7 +25,7 @@ export interface GamertagTransactionDataResponseType { confirmedAt: string; processedAt: string; } - message: string; + message: string | undefined; success: boolean; } @@ -71,4 +71,84 @@ export interface FetchChargeFromGamertagOptionsType { internalId: string; } - \ No newline at end of file + + +export function isSendGamertagPaymentDataResponseType(obj: any): obj is SendGamertagPaymentDataResponseType { + return ( + typeof obj === 'object' && + obj !== null && + typeof obj.success === 'boolean' && + typeof obj.message === 'string' && + typeof obj.data === 'object' && + typeof obj.data.receiverId === 'string' && + typeof obj.data.transactionId === 'string' && + typeof obj.data.amount === 'string' && + typeof obj.data.comment === 'string' + ); +} + +export function isGamertagTransactionDataResponseType(object: any): object is GamertagTransactionDataResponseType { + return ( + typeof object === 'object' && + object !== null && + (typeof object.message === 'string' || object.message === undefined) && + typeof object.success === 'boolean' && + typeof object.data === 'object' && + object.data !== null && + typeof object.data.id === 'string' && + typeof object.data.receiverId === 'string' && + typeof object.data.amount === 'string' && + typeof object.data.fee === 'string' && + typeof object.data.unit === 'string' && + typeof object.data.comment === 'string' && + typeof object.data.status === 'string' && + typeof object.data.confirmedAt === 'string' && + typeof object.data.processedAt === 'string' + ); +} + + +export function isFetchUserIdByGamertagDataResponseType(data: any): data is FetchUserIdByGamertagDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.id === 'string' + ); +} + + +export function isFetchGamertagByUserIDDataResponseType(data: any): data is FetchGamertagByUserIdDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.gamertag === 'string' && + typeof data.message === 'string' + ); +} + + +export function isFetchChargeFromGamertagDataResponseType(data: any): data is FetchChargeFromGamertagDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.unit === 'string' && + typeof data.data.status === 'string' && + typeof data.data.amount === 'string' && + typeof data.data.createdAt === 'string' && + typeof data.data.internalId === 'string' && + typeof data.data.callbackUrl === 'string' && + typeof data.data.description === 'string' && + typeof data.data.invoiceRequest === 'string' && + typeof data.data.invoiceExpiresAt === 'string' && + (typeof data.data.invoiceDescriptionHash === 'string' || data.data.invoiceDescriptionHash === null) + ); +} \ No newline at end of file diff --git a/src/types/index.ts b/src/types/index.ts index 7d89258..bbe150d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,13 +1,13 @@ -export * from './charges'; -export * from './gamertag'; -export * from './index'; -export * from './internal-transfers'; -export * from './keysend'; -export * from './lightning'; -export * from './misc'; -export * from './payments'; -export * from './static-charges'; -export * from './wallet'; -export * from './withdrawal'; -export * from './zbd-type'; - +export * from "./charges"; +export * from "./gamertag"; +export * from "./index"; +export * from "./internal-transfers"; +export * from "./keysend"; +export * from "./lightning"; +export * from "./misc"; +export * from "./payments"; +export * from "./static-charges"; +export * from "./wallet"; +export * from "./withdrawal"; +export * from "./zbd-type"; +export * from "./zbd-login"; diff --git a/src/types/internal-transfers.ts b/src/types/internal-transfers.ts index 87b56f6..0a9d520 100644 --- a/src/types/internal-transfers.ts +++ b/src/types/internal-transfers.ts @@ -18,4 +18,21 @@ export interface InternalTransferDataResponseType { export interface InternalTransferOptionsType { amount: string; receiverWalletId: string; -} \ No newline at end of file +} + +export function isInternalTransferDataResponseType(obj: any): obj is InternalTransferDataResponseType { + return obj && + typeof obj.success === 'boolean' && + typeof obj.message === 'string' && + obj.data && + typeof obj.data.id === 'string' && + typeof obj.data.senderWalletId === 'string' && + typeof obj.data.receiverWalletId === 'string' && + typeof obj.data.userId === 'string' && + typeof obj.data.sendTxId === 'string' && + typeof obj.data.receiveTxId === 'string' && + typeof obj.data.status === 'string' && + typeof obj.data.amount === 'string' && + typeof obj.data.createdAt === 'string' && + typeof obj.data.updatedAt === 'string'; +} diff --git a/src/types/keysend.ts b/src/types/keysend.ts index 9ee8af7..fff87cb 100644 --- a/src/types/keysend.ts +++ b/src/types/keysend.ts @@ -21,7 +21,25 @@ export interface KeysendDataResponseType { export interface KeysendOptionsType { amount: string; pubkey: string; - tlvRecords: string; - metadata: string; + tlvRecords: any; + metadata: any; callbackUrl: string; -} \ No newline at end of file +} + +export function isKeysendDataResponseType(obj: any): obj is KeysendDataResponseType { + return obj && + typeof obj.success === 'boolean' && + obj.data && + typeof obj.data.keysendId === 'string' && + typeof obj.data.paymentId === 'string' && + obj.data.transaction && + typeof obj.data.transaction.id === 'string' && + typeof obj.data.transaction.walletId === 'string' && + (typeof obj.data.transaction.type === 'string' || obj.data.transaction.type === undefined) && + typeof obj.data.transaction.totalAmount === 'string' && + typeof obj.data.transaction.fee === 'string' && + typeof obj.data.transaction.amount === 'string' && + (typeof obj.data.transaction.description === 'string' || obj.data.transaction.description === undefined) && + typeof obj.data.transaction.status === 'string' && + (obj.data.transaction.confirmedAt === undefined || typeof obj.data.transaction.confirmedAt === 'string'); +} diff --git a/src/types/lightning.ts b/src/types/lightning.ts index cd6dba3..d3e87dd 100644 --- a/src/types/lightning.ts +++ b/src/types/lightning.ts @@ -69,3 +69,64 @@ export interface CreateChargeFromLightningAddressOptionsType { lnAddress?: string description: string } + + +export function isSendLightningAddressPaymentDataResponseType(obj: any): obj is SendLightningAddressPaymentDataResponseType { + return ( + obj !== null && + typeof obj === 'object' && + typeof obj.success === 'boolean' && + typeof obj.message === 'string' && + typeof obj.data === 'object' && + + typeof obj.data.id === 'string' && + typeof obj.data.fee === 'string' && + typeof obj.data.unit === 'string' && + typeof obj.data.amount === 'string' && + typeof obj.data.invoice === 'string' && + (obj.data.preimage === null || typeof obj.data.preimage === 'string') && + typeof obj.data.walletId === 'string' && + typeof obj.data.transactionId === 'string' && + typeof obj.data.callbackUrl === 'string' && + typeof obj.data.internalId === 'string' && + typeof obj.data.comment === 'string' && + typeof obj.data.processedAt === 'string' && + typeof obj.data.createdAt === 'string' && + typeof obj.data.status === 'string' + ); +} + +export function isFetchChargeFromLightningAddressResponseType(obj: any): obj is FetchChargeFromLightningAddressDataResponseType { + return ( + obj && + typeof obj.success === 'boolean' && + obj.data && + typeof obj.data.lnaddress === 'string' && + typeof obj.data.amount === 'string' && + obj.data.invoice && + typeof obj.data.invoice.uri === 'string' && + typeof obj.data.invoice.request === 'string' + ); +} + +export function isValidateLightningAddressDataResponseType(obj: any): obj is ValidateLightningAddressDataResponseType { + return ( + obj && + typeof obj.success === 'boolean' && + obj.data && + typeof obj.data.valid === 'boolean' && + obj.data.metadata && + typeof obj.data.metadata.minSendable === 'number' && + typeof obj.data.metadata.maxSendable === 'number' && + typeof obj.data.metadata.commentAllowed === 'number' && + typeof obj.data.metadata.tag === 'string' && + typeof obj.data.metadata.metadata === 'string' && + typeof obj.data.metadata.callback === 'string' && + obj.data.metadata.payerData && + obj.data.metadata.payerData.name && + typeof obj.data.metadata.payerData.name.mandatory === 'boolean' && + obj.data.metadata.payerData.identifier && + typeof obj.data.metadata.payerData.identifier.mandatory === 'boolean' && + typeof obj.data.metadata.disposable === 'boolean' + ); +} diff --git a/src/types/misc.ts b/src/types/misc.ts index f99e92d..fe54fe6 100644 --- a/src/types/misc.ts +++ b/src/types/misc.ts @@ -22,4 +22,45 @@ export interface ProdIPSDataResponseType { ips: [string]; } success: boolean; +} + + +export function isSupportedRegionResponseType(data: any): data is SupportedRegionDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.ipAddress === 'string' && + typeof data.data.isSupported === 'boolean' && + typeof data.data.ipCountry === 'string' && + typeof data.data.ipRegion === 'string' + ); +} + +export function isApiProductionIPsResponseType(data: any): data is ProdIPSDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.data === 'object' && + data.data !== null && + Array.isArray(data.data.ips) && + data.data.ips.every((ip: any) => typeof ip === 'string') + ); +} + + +export function isBtcUsdExchangeRateResponseType(data: any): data is BTCUSDDataResponseType { + return ( + typeof data === 'object' && + data !== null && + typeof data.success === 'boolean' && + typeof data.message === 'string' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.btcUsdPrice === 'string' && + typeof data.data.btcUsdTimestamp === 'string' + ); } \ No newline at end of file diff --git a/src/types/payments.ts b/src/types/payments.ts index 64bc454..caa6e7c 100644 --- a/src/types/payments.ts +++ b/src/types/payments.ts @@ -23,4 +23,26 @@ export interface SendPaymentOptionsType { callbackUrl: string; amount: string; } - \ No newline at end of file + + +export function isInvoicePaymentDataResponseType(obj: any): obj is InvoicePaymentDataResponseType { + return ( + typeof obj === 'object' && + obj !== null && + typeof obj.success === 'boolean' && + typeof obj.message === 'string' && + typeof obj.data === 'object' && + obj.data !== null && + typeof obj.data.id === 'string' && + typeof obj.data.fee === 'string' && + typeof obj.data.unit === 'string' && + typeof obj.data.amount === 'string' && + typeof obj.data.invoice === 'string' && + (typeof obj.data.preimage === 'string' || obj.data.preimage === null) && + typeof obj.data.internalId === 'string' && + typeof obj.data.processedAt === 'string' && + typeof obj.data.confirmedAt === 'string' && + typeof obj.data.description === 'string' && + typeof obj.data.status === 'string' + ); +} diff --git a/src/types/static-charges.ts b/src/types/static-charges.ts index 42feed4..4d8869a 100644 --- a/src/types/static-charges.ts +++ b/src/types/static-charges.ts @@ -1,10 +1,10 @@ export interface StaticChargeOptionsType { - allowedSlots: string | null; + allowedSlots: number | null; minAmount: string; maxAmount: string; description: string; - internalId: string; + internalId: string | null; callbackUrl: string; successMessage: string; } @@ -13,19 +13,18 @@ export interface StaticChargeDataResponseType { data: { id: string; unit: string; - slots: string; + slots: number; minAmount: string; maxAmount: string; createdAt: string; callbackUrl: string; - internalId: string; + internalId: string | null; + identifier: string | null; description: string; expiresAt: string; - confirmedAt: string; successMessage: string; - allowedSlots: string | null; + allowedSlots: number | null; status: string; - fee: string; invoice: { request: string; uri: string; @@ -33,4 +32,28 @@ export interface StaticChargeDataResponseType { } message: string; } - \ No newline at end of file +export function isStaticChargeDataResponseType(obj: any): obj is StaticChargeDataResponseType { + return ( + typeof obj === 'object' && + obj !== null && + typeof obj.message === 'string' && + typeof obj.data === 'object' && + typeof obj.data.id === 'string' && + typeof obj.data.unit === 'string' && + (typeof obj.data.slots === 'number' || obj.data.slots === null) && + typeof obj.data.minAmount === 'string' && + typeof obj.data.maxAmount === 'string' && + typeof obj.data.createdAt === 'string' && + typeof obj.data.callbackUrl === 'string' && + (typeof obj.data.internalId === 'string' || obj.data.internalId === null) && + typeof obj.data.description === 'string' && + (typeof obj.data.expiresAt === 'string' || obj.data.expiresAt === null) && + (typeof obj.data.identifier === 'string' || obj.data.identifier === null) && // Added identifier check + typeof obj.data.successMessage === 'string' && + (typeof obj.data.allowedSlots === 'number' || obj.data.allowedSlots === null) && + typeof obj.data.status === 'string' && + typeof obj.data.invoice === 'object' && + typeof obj.data.invoice.request === 'string' && + typeof obj.data.invoice.uri === 'string' + ); +} diff --git a/src/types/wallet.ts b/src/types/wallet.ts index 328d91f..fe345cd 100644 --- a/src/types/wallet.ts +++ b/src/types/wallet.ts @@ -5,3 +5,13 @@ export interface WalletDataResponseType { } message: string; } + +export function isWalletDataResponseType(data: any): data is WalletDataResponseType { + return typeof data === 'object' && + data !== null && + typeof data.message === 'string' && + typeof data.data === 'object' && + data.data !== null && + typeof data.data.unit === 'string' && + typeof data.data.balance === 'string'; +} \ No newline at end of file diff --git a/src/types/withdrawal.ts b/src/types/withdrawal.ts index 52b4157..397e5ac 100644 --- a/src/types/withdrawal.ts +++ b/src/types/withdrawal.ts @@ -16,7 +16,6 @@ export interface GetWithdrawalRequestDataResponseType { internalId: string; description: string; expiresAt: string; - confirmedAt: string; status: string; invoice: { request: string; @@ -25,7 +24,7 @@ export interface GetWithdrawalRequestDataResponseType { fastUri: string; } } - message: string; + message: string | null | undefined; success: boolean; } @@ -40,7 +39,6 @@ export interface CreateWithdrawalRequestDataResponseType { internalId: string; description: string; expiresAt: string; - confirmedAt: string; status: string; fee: string; invoice: { @@ -53,3 +51,55 @@ export interface CreateWithdrawalRequestDataResponseType { message: string; success: boolean; } + +export function isGetWithdrawalRequestDataResponseType(obj: any): obj is GetWithdrawalRequestDataResponseType { + return ( + obj && + typeof obj.success === "boolean" && + (!obj.message || typeof obj.message === "string") && // Adjusted this check + obj.data && + typeof obj.data.id === "string" && + typeof obj.data.unit === "string" && + typeof obj.data.amount === "string" && + typeof obj.data.createdAt === "string" && + typeof obj.data.expiresAt === "string" && + typeof obj.data.internalId === "string" && + typeof obj.data.description === "string" && + typeof obj.data.callbackUrl === "string" && + typeof obj.data.status === "string" && + (obj.data.fee === null || typeof obj.data.fee === "number") && + obj.data.invoice && + typeof obj.data.invoice.request === "string" && + typeof obj.data.invoice.uri === "string" && + typeof obj.data.invoice.fastRequest === "string" && + typeof obj.data.invoice.fastUri === "string" + ); +} + + +export function isCreateWithdrawalRequestDataResponseType(obj: any): obj is CreateWithdrawalRequestDataResponseType { + return ( + typeof obj === 'object' && + obj !== null && + typeof obj.success === 'boolean' && + typeof obj.message === 'string' && + typeof obj.data === 'object' && + obj.data !== null && + typeof obj.data.id === 'string' && + typeof obj.data.unit === 'string' && + typeof obj.data.amount === 'string' && + typeof obj.data.createdAt === 'string' && + typeof obj.data.expiresAt === 'string' && + typeof obj.data.callbackUrl === 'string' && + typeof obj.data.internalId === 'string' && + typeof obj.data.description === 'string' && + typeof obj.data.status === 'string' && + (typeof obj.data.fee === 'string' || obj.data.fee === null) && + typeof obj.data.invoice === 'object' && + obj.data.invoice !== null && + typeof obj.data.invoice.request === 'string' && + typeof obj.data.invoice.fastRequest === 'string' && + typeof obj.data.invoice.uri === 'string' && + typeof obj.data.invoice.fastUri === 'string' + ); +} diff --git a/src/types/zbd-login.ts b/src/types/zbd-login.ts new file mode 100644 index 0000000..6adb234 --- /dev/null +++ b/src/types/zbd-login.ts @@ -0,0 +1,81 @@ +export interface OAuth2AuthorizationRequestType { + redirect_uri: string; + state: string; +} + +export interface FetchTokenParam { + code: string; + redirect_uri: string; +} + +export interface FetchTokenBody { + client_id: string; + client_secret: string; + code: string; + code_verifier: string; + grant_type: string; + redirect_uri: string; +} + +export interface FetchAccessTokenRes { + access_token: string; + token_type: string; + expires_in: number; + refresh_token: string; + refresh_token_expires_in: number; + scope: string; +} + +export interface RefreshTokenParam { + code: string; + redirect_uri: string; +} + +export interface RefreshTokenBody { + client_id: string; + client_secret: string; + code: string; + code_verifier: string; + grant_type: string; + redirect_uri: string; +} + +export interface RefreshTokenRes { + access_token: string; + token_type: string; + expires_in: number; + refresh_token: string; + refresh_token_expires_in: number; + scope: string; +} + +export interface FetchUserDataParam { + access_token: string; +} + +export interface ZBDUserData { + id: string; + email: string; + gamertag: string; + image?: string | null; + is_verified: boolean; + lightning_address: string; + public_bio: string; + public_static_charge: string; +} + +export interface FetchUserWalletDataParam { + access_token: string; +} + +interface ZBDUserWalletDataLimits { + daily: string; + maxCredit: string; + monthly: string; + weekly: string; +} + +export interface ZBDUserWalletData { + balance: string; + remaining_amount_limits: ZBDUserWalletDataLimits; +} diff --git a/src/utils.ts b/src/utils.ts index 58914f8..c61ef12 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -9,7 +9,7 @@ export const cleanup = (obj: any) => { } return newObj; -} +}; export async function postData({ url, @@ -23,7 +23,7 @@ export async function postData({ const response = await fetch(url, { method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", ...headers, }, body: JSON.stringify(cleanup(body)), @@ -33,9 +33,8 @@ export async function postData({ const errorBody = await response.json(); const error = { status: response.status, - message: errorBody.message || 'API request failed', + message: errorBody.message || "API request failed", }; - throw error; } @@ -56,18 +55,17 @@ export async function patchData({ const response = await fetch(url, { method: "PATCH", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", ...headers, }, body: JSON.stringify(cleanup(body)), }); if (!response.ok) { - const errorBody = await response.json(); const error = { status: response.status, - message: errorBody.message || 'API request failed', + message: errorBody.message || "API request failed", }; throw error; @@ -87,22 +85,20 @@ export async function getData({ const response = await fetch(url, { method: "GET", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", ...headers, }, }); if (!response.ok) { - const errorBody = await response.json(); const error = { status: response.status, - message: errorBody.message || 'API request failed', + message: errorBody.message || "API request failed", }; - throw error; } const result = await response.json(); return result; -} \ No newline at end of file +} diff --git a/src/zbd.ts b/src/zbd.ts index 5cf344b..1b2c522 100644 --- a/src/zbd.ts +++ b/src/zbd.ts @@ -1,5 +1,5 @@ -import { API_URL, API } from './constants'; -import { postData, getData, patchData } from './utils'; +import { API_URL, API } from "./constants"; +import { postData, getData, patchData } from "./utils"; import { ChargeOptionsType, KeysendOptionsType, @@ -32,11 +32,30 @@ import { CreateChargeFromLightningAddressOptionsType, SendGamertagPaymentDataResponseType, FetchChargeFromLightningAddressDataResponseType, -} from './types/index'; + OAuth2AuthorizationRequestType, + FetchTokenBody, + FetchTokenParam, + FetchAccessTokenRes, + RefreshTokenParam, + RefreshTokenBody, + RefreshTokenRes, + FetchUserDataParam, + ZBDUserData, + ZBDUserWalletData, + FetchUserWalletDataParam, +} from "./types/index"; +import * as crypto from "crypto"; + +let Encoder: { new (): any; new (): TextEncoder; prototype?: TextEncoder }; +const PKCELength: number = 128; class zbd { apiBaseUrl: string; - apiCoreHeaders: {apikey: string }; + apiCoreHeaders: { apikey: string }; + codeVerifier!: string; + codeChallenge!: string; + clientSecret!: string; + clientId!: string; constructor(apiKey: string) { this.apiBaseUrl = API_URL; @@ -44,15 +63,9 @@ class zbd { } async createCharge(options: ChargeOptionsType) { - const { - amount, - expiresIn, - internalId, - description, - callbackUrl, - } = options; + const { amount, expiresIn, internalId, description, callbackUrl } = options; - const response : ChargeDataResponseType = await postData({ + const response: ChargeDataResponseType = await postData({ url: `${API_URL}${API.CHARGES_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, body: { @@ -72,7 +85,7 @@ class zbd { url: `${API_URL}${API.CHARGES_ENDPOINT}/${chargeId}`, headers: { ...this.apiCoreHeaders }, }); - + return response; } @@ -88,16 +101,10 @@ class zbd { return response; } - async createWithdrawalRequest(options: WithdrawalRequestOptionsType) { - const { - amount, - expiresIn, - internalId, - callbackUrl, - description, - } = options; + async createWithdrawalRequest(options: WithdrawalRequestOptionsType) { + const { amount, expiresIn, internalId, callbackUrl, description } = options; - const response : CreateWithdrawalRequestDataResponseType = await postData({ + const response: CreateWithdrawalRequestDataResponseType = await postData({ url: `${API_URL}${API.WITHDRAWAL_REQUESTS_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, body: { @@ -113,16 +120,16 @@ class zbd { } async getWithdrawalRequest(withdrawalRequestId: string) { - const response : GetWithdrawalRequestDataResponseType = await getData({ + const response: GetWithdrawalRequestDataResponseType = await getData({ url: `${API_URL}${API.WITHDRAWAL_REQUESTS_ENDPOINT}/${withdrawalRequestId}`, headers: { ...this.apiCoreHeaders }, }); - + return response; } - async validateLightningAddress(lightningAddress: string) { - const response : ValidateLightningAddressDataResponseType = await getData({ + async validateLightningAddress(lightningAddress: string) { + const response: ValidateLightningAddressDataResponseType = await getData({ url: `${API_URL}${API.VALIDATE_LN_ADDRESS_ENDPOINT}/${lightningAddress}`, headers: { ...this.apiCoreHeaders }, }); @@ -130,75 +137,69 @@ class zbd { return response; } - async sendLightningAddressPayment(options: SendLightningAddressPaymentOptionsType) { - const { - amount, - comment, - lnAddress, - internalId, - callbackUrl, - } = options; - - const response : SendLightningAddressPaymentDataResponseType = await postData({ - url: `${API_URL}${API.SEND_LN_ADDRESS_PAYMENT_ENDPOINT}`, - headers: { ...this.apiCoreHeaders }, - body: { - amount, - comment, - lnAddress, - internalId, - callbackUrl, - }, - }); + async sendLightningAddressPayment( + options: SendLightningAddressPaymentOptionsType + ) { + const { amount, comment, lnAddress, internalId, callbackUrl } = options; + + const response: SendLightningAddressPaymentDataResponseType = + await postData({ + url: `${API_URL}${API.SEND_LN_ADDRESS_PAYMENT_ENDPOINT}`, + headers: { ...this.apiCoreHeaders }, + body: { + amount, + comment, + lnAddress, + internalId, + callbackUrl, + }, + }); return response; } - async createChargeFromLightningAddress(options: CreateChargeFromLightningAddressOptionsType) { - const { - amount, - lnaddress, - lnAddress, - description, - } = options; - + async createChargeFromLightningAddress( + options: CreateChargeFromLightningAddressOptionsType + ) { + const { amount, lnaddress, lnAddress, description } = options; // Addressing issue on ZBD API where it accepts `lnaddress` property // instead of `lnAddress` property as is standardized let lightningAddress = lnaddress || lnAddress; - const response: FetchChargeFromLightningAddressDataResponseType = await postData({ - url: `${API_URL}${API.CREATE_CHARGE_FROM_LN_ADDRESS_ENDPOINT}`, - headers: { ...this.apiCoreHeaders }, - body: { - amount, - description, - lnaddress: lightningAddress, - }, - }); + const response: FetchChargeFromLightningAddressDataResponseType = + await postData({ + url: `${API_URL}${API.CREATE_CHARGE_FROM_LN_ADDRESS_ENDPOINT}`, + headers: { ...this.apiCoreHeaders }, + body: { + amount, + description, + lnaddress: lightningAddress, + }, + }); return response; } - + async getWallet() { - const response : WalletDataResponseType = await getData({ + const response: WalletDataResponseType = await getData({ url: `${API_URL}${API.WALLET_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, }); - + return response; } - async isSupportedRegion(ipAddress: string) { - const response : SupportedRegionDataResponseType = await getData({ + async isSupportedRegion(ipAddress: string) { + const response: SupportedRegionDataResponseType = await getData({ url: `${API_URL}${API.IS_SUPPORTED_REGION_ENDPOINT}/${ipAddress}`, headers: { ...this.apiCoreHeaders }, }); - + return response; } - async getZBDProdIps() { + async getZBDProdIps() { const response: ProdIPSDataResponseType = await getData({ url: `${API_URL}${API.FETCH_ZBD_PROD_IPS_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, @@ -207,7 +208,7 @@ class zbd { return response; } - async getBtcUsdExchangeRate() { + async getBtcUsdExchangeRate() { const response: BTCUSDDataResponseType = await getData({ url: `${API_URL}${API.BTCUSD_PRICE_TICKER_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, @@ -216,7 +217,7 @@ class zbd { return response; } - async internalTransfer(options: InternalTransferOptionsType) { + async internalTransfer(options: InternalTransferOptionsType) { const { amount, receiverWalletId } = options; const response: InternalTransferDataResponseType = await postData({ @@ -231,14 +232,8 @@ class zbd { return response; } - async sendKeysendPayment(options: KeysendOptionsType) { - const { - amount, - pubkey, - metadata, - tlvRecords, - callbackUrl, - } = options; + async sendKeysendPayment(options: KeysendOptionsType) { + const { amount, pubkey, metadata, tlvRecords, callbackUrl } = options; const response: KeysendDataResponseType = await postData({ url: `${API_URL}${API.KEYSEND_PAYMENT_ENDPOINT}`, @@ -255,16 +250,10 @@ class zbd { return response; } - async sendPayment(options: SendPaymentOptionsType) { - const { - amount, - invoice, - internalId, - description, - callbackUrl, - } = options; + async sendPayment(options: SendPaymentOptionsType) { + const { amount, invoice, internalId, description, callbackUrl } = options; - const response : InvoicePaymentDataResponseType = await postData({ + const response: InvoicePaymentDataResponseType = await postData({ url: `${API_URL}${API.PAYMENTS_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, body: { @@ -276,10 +265,10 @@ class zbd { }, }); - return response; + return response; } - async getPayment(paymentId: string) { + async getPayment(paymentId: string) { const response = await getData({ url: `${API_URL}${API.PAYMENTS_ENDPOINT}/${paymentId}`, headers: { ...this.apiCoreHeaders }, @@ -288,7 +277,7 @@ class zbd { return response; } - async sendGamertagPayment(options: SendGamertagPaymentOptionsType) { + async sendGamertagPayment(options: SendGamertagPaymentOptionsType) { const { amount, gamertag, description } = options; const response: SendGamertagPaymentDataResponseType = await postData({ @@ -304,7 +293,7 @@ class zbd { return response; } - async getGamertagTransaction(transactionId: string) { + async getGamertagTransaction(transactionId: string) { const response: GamertagTransactionDataResponseType = await getData({ url: `${API_URL}${API.GET_GAMERTAG_PAYMENT_ENDPOINT}/${transactionId}`, headers: { ...this.apiCoreHeaders }, @@ -313,7 +302,7 @@ class zbd { return response; } - async getUserIdByGamertag(gamertag: string) { + async getUserIdByGamertag(gamertag: string) { const response: FetchUserIdByGamertagDataResponseType = await getData({ url: `${API_URL}${API.GET_USERID_FROM_GAMERTAG_ENDPOINT}/${gamertag}`, headers: { ...this.apiCoreHeaders }, @@ -322,7 +311,7 @@ class zbd { return response; } - async getGamertagByUserId(userId: string) { + async getGamertagByUserId(userId: string) { const response: FetchGamertagByUserIdDataResponseType = await getData({ url: `${API_URL}${API.GET_GAMERTAG_FROM_USERID_ENDPOINT}/${userId}`, headers: { ...this.apiCoreHeaders }, @@ -331,16 +320,10 @@ class zbd { return response; } - async createGamertagCharge(options: FetchChargeFromGamertagOptionsType) { - const { - amount, - gamertag, - internalId, - description, - callbackUrl, - } = options; + async createGamertagCharge(options: FetchChargeFromGamertagOptionsType) { + const { amount, gamertag, internalId, description, callbackUrl } = options; - const response : FetchChargeFromGamertagDataResponseType = await postData({ + const response: FetchChargeFromGamertagDataResponseType = await postData({ url: `${API_URL}${API.CREATE_CHARGE_FROM_GAMERTAG_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, body: { @@ -355,7 +338,7 @@ class zbd { return response; } - async createStaticCharge(options: StaticChargeOptionsType) { + async createStaticCharge(options: StaticChargeOptionsType) { const { minAmount, maxAmount, @@ -366,7 +349,7 @@ class zbd { successMessage, } = options; - const response : StaticChargeDataResponseType = await postData({ + const response: StaticChargeDataResponseType = await postData({ url: `${API_URL}${API.STATIC_CHARGES_ENDPOINT}`, headers: { ...this.apiCoreHeaders }, body: { @@ -383,7 +366,10 @@ class zbd { return response; } - async updateStaticCharge(staticChargeId: string, updates: StaticChargeOptionsType) { + async updateStaticCharge( + staticChargeId: string, + updates: StaticChargeOptionsType + ) { const response = await patchData({ url: `${API_URL}${API.STATIC_CHARGES_ENDPOINT}/${staticChargeId}`, headers: { ...this.apiCoreHeaders }, @@ -393,7 +379,7 @@ class zbd { return response; } - async getStaticCharge(staticChargeId: string) { + async getStaticCharge(staticChargeId: string) { const response = await getData({ url: `${API_URL}${API.STATIC_CHARGES_ENDPOINT}/${staticChargeId}`, headers: { ...this.apiCoreHeaders }, @@ -401,6 +387,322 @@ class zbd { return response; } + + // Authorization + + OAuth2AuthorizationUrl = (domain = API_URL) => { + return `${domain}`; + }; + + /** + * Generates an authentication URL for initiating the OAuth2 authorization flow. + * + * @param {OAuth2AuthorizationRequestType} param - An object containing authorization request parameters. + * @returns {Promise} A promise that resolves with the generated authentication URL. + * + * @throws {Error} If any required parameters are missing or if errors occur during URL generation. + * + * @example + * const params = { + * redirect_uri: "http://localhost:3000/callback", + * state: "random_state", + * }; + * const authUrl = await ZBD.getAuthenticationUrl(params); + * console.log(authUrl); // Print the generated authentication URL + */ + async getAuthenticationUrl(param: OAuth2AuthorizationRequestType) { + const { redirect_uri, state } = param; + let scope: string = "user"; + let response_type: string = "code"; + const baseUrl = this.OAuth2AuthorizationUrl(); + const client_id = this.getClientId(); + const client_secret = this.getClientSecret(); + if (!client_id) { + throw new Error( + "A client id is required. You can set the client id using .setClientId()." + ); + } + + if (!redirect_uri) { + throw new Error("A redirect uri is required."); + } + + if (!client_secret) { + throw new Error( + "A client secret is required. You can set the client secret using .setClientSecret()." + ); + } + if (!state) { + throw new Error("A state parameter is required."); + } + + if (!this.codeChallenge) { + throw new Error( + "A code challenge is required. Generate one using .generatePKCE()." + ); + } + + const code_challenge_method = "S256"; + + let url = `${baseUrl}${API.GET_AUTHORIZATION_ENDPOINT}`; + url += `?response_type=${response_type}`; + url += `&client_id=${client_id}`; + url += `&redirect_uri=${redirect_uri}`; + url += `&scope=${scope}`; + url += `&state=${state}`; + url += `&code_challenge=${this.codeChallenge}`; + url += `&code_challenge_method=${code_challenge_method}`; + + return url; + } + + createBrowserSafeString(toBeConverted: any) { + const convertedString = toBeConverted + .toString("base64") + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=/g, ""); + return convertedString; + } + + /** + * Sets the client Secret + * @param {String} secret - The client secret + */ + setClientSecret(secret: string) { + this.clientSecret = secret; + } + + /** + * Get the client secret + * @returns {String} Client id + */ + getClientSecret() { + return this.clientSecret; + } + + /** + * Get client ID + * @returns {String} Client id + */ + getClientId() { + return this.clientId; + } + + /** + * Sets client ID + * @param {String} clientId - The client ID + */ + setClientId(clientId: string) { + this.clientId = clientId; + } + + /** + * Sets the code verifier for PKCE flow + * @param {String} codeVerifier - new code verifier + */ + setCodeVerifier(codeVerifier: string) { + this.codeVerifier = codeVerifier; + } + + /** + * Gets the code verifier for PKCE flow + * @returns {String} - code verifier for PKCE + */ + getCodeChallenge() { + return this.codeChallenge; + } + /** + * Generates a random code verifier and code challenge for PKCE + * @returns {Promise} resolves when code challenge is generated + * @throws {Error} if code challenge generation fails + */ + async generateCodeChallenge() { + const encoder = new TextEncoder(); + const codeData = encoder.encode(this.codeVerifier); + let codeChallenge; + if (this.codeVerifier) { + return crypto.subtle.digest("SHA-256", codeData).then((digestedHash) => { + const base64String = btoa( + String.fromCharCode(...new Uint8Array(digestedHash)) + ); + codeChallenge = this.createBrowserSafeString(base64String).substr( + 0, + 128 + ); + this.codeChallenge = codeChallenge; + }); + } + + return Promise.resolve(); + } + + /** + * Generates a random code verifier and corresponding code challenge for PKCE (Proof Key for Code Exchange). + * @returns {Promise} Resolves when the code challenge is generated successfully. + * @throws {Error} If code challenge generation or other steps fail. + */ + async generatePKCECodes() { + let codeVerifier; + + const randomBytes = crypto.randomBytes(PKCELength); + codeVerifier = this.createBrowserSafeString(randomBytes).substr(0, 128); + + this.codeVerifier = codeVerifier; + + return await this.generateCodeChallenge(); + } + + /** + * Fetches an access token using the provided authorization code and parameters. + * @param param - An object containing parameters for fetching the access token. + * @returns A promise that resolves with the access token response or rejects with an error. + * @throws Error if required parameters are missing or not set. + */ + async fetchAccessToken(param: FetchTokenParam) { + if (!param.code) { + throw new Error("Authorization code is required"); + } + + if (!param.redirect_uri) { + throw new Error("Redirect URI is required"); + } + + if (!this.codeChallenge) { + throw new Error( + "A code challenge is required. Generate one using .generatePKCE()." + ); + } + + if (!this.clientId) { + throw new Error("Client ID is required. Set it using .setClientId()"); + } + + if (!this.clientSecret) { + throw new Error( + "Client Secret is required. Set it using .setClientSecret()" + ); + } + + const body: FetchTokenBody = { + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: "authorization_code", + code: param.code, + redirect_uri: param.redirect_uri, + code_verifier: this.codeVerifier, + }; + + console.log( + "URL: ", + `${API_URL}${API.FETCH_ACCESS_TOKEN_ENDPOINT}?Content-Type=application/json` + ); + + const response: FetchAccessTokenRes = await postData({ + url: `${API_URL}${API.FETCH_ACCESS_TOKEN_ENDPOINT}?Content-Type=application/json`, + headers: { ...this.apiCoreHeaders }, + body: body, + }); + + return response; + } + + /** + * Refreshes an access token using the provided refresh token and other required parameters. + * @param param - The RefreshTokenParam object containing the necessary parameters. + * @throws {Error} - Throws an error if any required parameter is missing. + * @returns {Promise} - Returns a promise that resolves with the refreshed token response. + */ + async refreshToken(param: RefreshTokenParam) { + if (!param.code) { + throw new Error("Authorization code is required"); + } + + if (!param.redirect_uri) { + throw new Error("Redirect URI is required"); + } + + if (!this.codeChallenge) { + throw new Error( + "A code challenge is required. Generate one using .generatePKCE()." + ); + } + + if (!this.clientId) { + throw new Error("Client ID is required. Set it using .setClientId()"); + } + + if (!this.clientSecret) { + throw new Error( + "Client Secret is required. Set it using .setClientSecret()" + ); + } + + const body: RefreshTokenBody = { + client_id: this.clientId, + client_secret: this.clientSecret, + grant_type: "refresh_token", + code: param.code, + redirect_uri: param.redirect_uri, + code_verifier: this.codeVerifier, + }; + + const response: RefreshTokenRes = await postData({ + url: `${API_URL}${API.REFRESH_TOKEN_ENDPOINT}?Content-Type=application/json`, + headers: { ...this.apiCoreHeaders }, + body: body, + }); + + return response; + } + + /** + * Fetches user data using the provided access token. + * @param param - The FetchUserDataParam object containing the required parameters. + * @throws {Error} - Throws an error if the access token is missing. + * @returns {Promise} - Returns a promise that resolves with the fetched user data. + */ + async fetchUserData(param: FetchUserDataParam) { + if (!param.access_token) { + throw new Error("Access token is required"); + } + + const headers = { + ...this.apiCoreHeaders, + usertoken: `Bearer ${param.access_token}`, + }; + + const response: ZBDUserData = await getData({ + url: `${API_URL}${API.GET_USER_ENDPOINT}`, + headers: headers, + }); + + return response; + } + + /** + * Fetches user wallet data using the provided access token. + * @param param - The FetchUserWalletDataParam object containing the required parameters. + * @throws {Error} - Throws an error if the access token is missing. + * @returns {Promise} - Returns a promise that resolves with the fetched user wallet data. + */ + async fetchUserWalletData(param: FetchUserWalletDataParam) { + if (!param.access_token) { + throw new Error("Access token is required"); + } + + const headers = { + ...this.apiCoreHeaders, + usertoken: `Bearer ${param.access_token}`, + }; + + const response: ZBDUserWalletData = await getData({ + url: `${API_URL}${API.GET_USER_WALLET_DATA_ENDPOINT}`, + headers: headers, + }); + + return response; + } } export { zbd }; diff --git a/yarn.lock b/yarn.lock index d0441ce..026df26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22,7 +22,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz" integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== -"@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.8.0", "@babel/core@>=7.0.0-beta.0 <8": version "7.22.9" resolved "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz" integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== @@ -1249,7 +1249,7 @@ slash "^3.0.0" write-file-atomic "^4.0.2" -"@jest/types@^29.6.1": +"@jest/types@^29.0.0", "@jest/types@^29.6.1": version "29.6.1" resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz" integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== @@ -1270,7 +1270,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@3.1.0": version "3.1.0" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== @@ -1288,23 +1288,15 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.15" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" @@ -1314,6 +1306,14 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@rollup/plugin-alias@^3.1.1": version "3.1.9" resolved "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-3.1.9.tgz" @@ -1432,7 +1432,7 @@ resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== -"@types/babel__core@^7.1.14": +"@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9": version "7.20.1" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz" integrity sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw== @@ -1543,10 +1543,10 @@ dependencies: "@types/yargs-parser" "*" -"@zbd/node@^0.6.2": - version "0.6.2" - resolved "https://registry.npmjs.org/@zbd/node/-/node-0.6.2.tgz" - integrity sha512-66PesqbnByJFcvNUo9KaIJ3/ISENfQxaKj0fRfUA8c3Z5QEFk7Oqtfquhc4ocs940i5fm8IuoqvmHcnWQuUlmg== +"@zbd/node@^0.6.3": + version "0.6.3" + resolved "https://registry.npmjs.org/@zbd/node/-/node-0.6.3.tgz" + integrity sha512-pKt8f6cJZ7HlbBHtJRXgMOfmp9M1WNIhZtSxuV80zEhZOf+VNVlI3w+cvi0JsHpfK/7lCJEejENs9GjU/SN/9Q== acorn-walk@^8.1.1: version "8.2.0" @@ -1679,7 +1679,7 @@ axios-mock-adapter@^1.21.5: fast-deep-equal "^3.1.3" is-buffer "^2.0.5" -axios@^1.4.0: +axios@^1.4.0, "axios@>= 0.17.0": version "1.4.0" resolved "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz" integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA== @@ -1688,7 +1688,7 @@ axios@^1.4.0: form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-jest@^29.6.1: +babel-jest@^29.0.0, babel-jest@^29.6.1: version "29.6.1" resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.1.tgz" integrity sha512-qu+3bdPEQC6KZSPz+4Fyjbga5OODNcp49j6GKzG1EKbkfyJBxEYGVUmVGpwCSeGouG52R4EgYMLb6p9YeEEQ4A== @@ -1832,7 +1832,7 @@ brotli-size@^4.0.0: dependencies: duplexer "0.1.1" -browserslist@^4.0.0, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4.21.9: +browserslist@^4.0.0, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4.21.9, "browserslist@>= 4.21.0": version "4.21.9" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz" integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== @@ -1904,7 +1904,18 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001503: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz" integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== -chalk@^1.0.0, chalk@^1.1.3: +chalk@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" + integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== @@ -1980,16 +1991,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + colord@^2.9.1: version "2.9.3" resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" @@ -2029,7 +2040,12 @@ concat-with-sourcemaps@^1.1.0: dependencies: source-map "^0.6.1" -convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^1.7.0: version "1.9.0" resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== @@ -2246,7 +2262,7 @@ domutils@^2.8.0: domelementtype "^2.2.0" domhandler "^4.2.0" -duplexer@0.1.1, duplexer@^0.1.1: +duplexer@^0.1.1, duplexer@0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz" integrity sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q== @@ -2358,7 +2374,12 @@ escalade@^3.1.1: resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -2440,7 +2461,7 @@ fast-deep-equal@^3.1.3: resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.1.0: +fast-json-stable-stringify@^2.1.0, fast-json-stable-stringify@2.x: version "2.1.0" resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== @@ -2551,11 +2572,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" @@ -3229,7 +3245,7 @@ jest-resolve-dependencies@^29.6.1: jest-regex-util "^29.4.3" jest-snapshot "^29.6.1" -jest-resolve@^29.6.1: +jest-resolve@*, jest-resolve@^29.6.1: version "29.6.1" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.1.tgz" integrity sha512-AeRkyS8g37UyJiP9w3mmI/VXU/q8l/IH52vj/cDAyScDcemRbSBhfX/NMYIGilQgSVwsjxrCHf3XJu4f+lxCMg== @@ -3383,7 +3399,7 @@ jest-worker@^29.6.1: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^29.6.1: +jest@^29.0.0, jest@^29.6.1: version "29.6.1" resolved "https://registry.npmjs.org/jest/-/jest-29.6.1.tgz" integrity sha512-Nirw5B4nn69rVUZtemCQhwxOBhm0nsp3hmtF4rzCeWD7BkjAXRIji7xWQfnTNbz9g0aVsBX6aZK3n+23LM6uDw== @@ -3482,7 +3498,7 @@ lodash.debounce@^4.0.8: resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.memoize@4.x, lodash.memoize@^4.1.2: +lodash.memoize@^4.1.2, lodash.memoize@4.x: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -3525,7 +3541,7 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" -make-error@1.x, make-error@^1.1.1: +make-error@^1.1.1, make-error@1.x: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -4145,7 +4161,7 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.2.1: +postcss@^8.0.0, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.2.1, postcss@^8.2.15, postcss@^8.2.2, postcss@>=8.0.9, postcss@8.x: version "8.4.27" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.27.tgz" integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== @@ -4363,7 +4379,7 @@ rollup-pluginutils@^2.8.2: dependencies: estree-walker "^0.6.1" -rollup@^2.35.1: +"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.30.0, rollup@^2.35.1, rollup@>=1.26.3, "rollup@2.x || 3.x": version "2.79.1" resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== @@ -4466,14 +4482,6 @@ source-map-js@^1.0.2: resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" @@ -4482,6 +4490,14 @@ source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" @@ -4721,7 +4737,7 @@ ts-jest@^29.1.1: semver "^7.5.3" yargs-parser "^21.0.1" -ts-node@^10.9.1: +ts-node@^10.9.1, ts-node@>=9.0.0: version "10.9.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== @@ -4794,7 +4810,7 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" -typescript@^4.1.3, typescript@^4.8.4: +typescript@^4.1.3, typescript@^4.8.4, typescript@>=2.4.0, typescript@>=2.7, "typescript@>=4.3 <6": version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==