@bananalink-test/wallet 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +20 -14
- package/dist/index.d.cts +1 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +21 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -39,10 +39,6 @@ function isDappInfo(value) {
|
|
|
39
39
|
return typeof record.dappId === "string" && typeof record.dappName === "string" && (typeof record.dappDescription === "string" || typeof record.dappDescription === "undefined") && typeof record.dappStatement === "string" && typeof record.domain === "string" && typeof record.uri === "string" && Array.isArray(record.icons) && record.icons.every((icon) => typeof icon === "string") && typeof record.chainId === "number" && Number.isFinite(record.chainId);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/constants/RevocationStatement.ts
|
|
44
|
-
const REVOCATION_STATEMENT = "Revoke dapp access";
|
|
45
|
-
|
|
46
42
|
//#endregion
|
|
47
43
|
//#region src/api/deleteDappTokens.ts
|
|
48
44
|
async function deleteDappTokens(apiUrl, dappId, body) {
|
|
@@ -66,12 +62,23 @@ async function fetchRevocationNonce(apiUrl) {
|
|
|
66
62
|
throw new ApiError(response.status, body);
|
|
67
63
|
}
|
|
68
64
|
const body = await response.json();
|
|
69
|
-
if (typeof body !== "object" || body === null || !("nonce" in body)) throw new ApiError(response.status, body);
|
|
70
|
-
const { nonce } = body;
|
|
71
|
-
if (typeof nonce !== "string" || nonce.length === 0) throw new ApiError(response.status, body);
|
|
72
|
-
return
|
|
65
|
+
if (typeof body !== "object" || body === null || !("nonce" in body) || !("domain" in body)) throw new ApiError(response.status, body);
|
|
66
|
+
const { nonce, domain } = body;
|
|
67
|
+
if (typeof nonce !== "string" || nonce.length === 0 || typeof domain !== "string" || domain.length === 0) throw new ApiError(response.status, body);
|
|
68
|
+
return {
|
|
69
|
+
nonce,
|
|
70
|
+
domain
|
|
71
|
+
};
|
|
73
72
|
}
|
|
74
73
|
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/constants/RevocationStatement.ts
|
|
76
|
+
const REVOCATION_SIWE = {
|
|
77
|
+
statement: "Revoke dapp access",
|
|
78
|
+
uri: "https://bananalink",
|
|
79
|
+
chainId: 1
|
|
80
|
+
};
|
|
81
|
+
|
|
75
82
|
//#endregion
|
|
76
83
|
//#region src/errors/NotConnectedError.ts
|
|
77
84
|
var NotConnectedError = class extends BananalinkError {
|
|
@@ -282,14 +289,14 @@ var BananalinkWallet = class BananalinkWallet {
|
|
|
282
289
|
async revoke(params) {
|
|
283
290
|
if (!params.dappId.trim()) throw new BananalinkError("dappId is required");
|
|
284
291
|
if (!params.signer.address.trim()) throw new BananalinkError("signer address is required");
|
|
285
|
-
const
|
|
292
|
+
const { nonce, domain } = await fetchRevocationNonce(this.apiUrl);
|
|
286
293
|
const message = buildSiweMessage({
|
|
287
|
-
domain
|
|
294
|
+
domain,
|
|
288
295
|
address: params.signer.address,
|
|
289
|
-
uri:
|
|
290
|
-
statement:
|
|
296
|
+
uri: REVOCATION_SIWE.uri,
|
|
297
|
+
statement: REVOCATION_SIWE.statement,
|
|
291
298
|
nonce,
|
|
292
|
-
chainId:
|
|
299
|
+
chainId: REVOCATION_SIWE.chainId
|
|
293
300
|
});
|
|
294
301
|
const signature = await params.signer.signMessage(message);
|
|
295
302
|
await deleteDappTokens(this.apiUrl, params.dappId, {
|
|
@@ -342,7 +349,6 @@ exports.BananalinkError = BananalinkError;
|
|
|
342
349
|
exports.BananalinkWallet = BananalinkWallet;
|
|
343
350
|
exports.ConnectionError = ConnectionError;
|
|
344
351
|
exports.NotConnectedError = NotConnectedError;
|
|
345
|
-
exports.REVOCATION_STATEMENT = REVOCATION_STATEMENT;
|
|
346
352
|
exports.buildSiweMessage = buildSiweMessage;
|
|
347
353
|
exports.fetchDapp = fetchDapp;
|
|
348
354
|
exports.generateWalletId = generateWalletId;
|
package/dist/index.d.cts
CHANGED
|
@@ -13,9 +13,6 @@ interface DappInfo {
|
|
|
13
13
|
//#region src/api/fetchDapp.d.ts
|
|
14
14
|
declare function fetchDapp(apiUrl: string, dappId: string): Promise<DappInfo>;
|
|
15
15
|
//#endregion
|
|
16
|
-
//#region src/constants/RevocationStatement.d.ts
|
|
17
|
-
declare const REVOCATION_STATEMENT = "Revoke dapp access";
|
|
18
|
-
//#endregion
|
|
19
16
|
//#region src/types/ConnectionRequestParams.d.ts
|
|
20
17
|
interface ConnectionRequestParams {
|
|
21
18
|
dappInstanceId: string;
|
|
@@ -113,5 +110,5 @@ declare function buildSiweMessage(params: BuildSiweMessageParams): string;
|
|
|
113
110
|
//#region src/utils/parseDeepLink.d.ts
|
|
114
111
|
declare function parseDeepLink(deepLink: string): DeepLinkParams;
|
|
115
112
|
//#endregion
|
|
116
|
-
export { ApiError, BananalinkError, BananalinkWallet, type BananalinkWalletConfig, type BuildSiweMessageParams, ConnectionError, type ConnectionRequest, type ConnectionRequestParams, type DappInfo, type DeepLinkParams, NotConnectedError,
|
|
113
|
+
export { ApiError, BananalinkError, BananalinkWallet, type BananalinkWalletConfig, type BuildSiweMessageParams, ConnectionError, type ConnectionRequest, type ConnectionRequestParams, type DappInfo, type DeepLinkParams, NotConnectedError, type RevokeParams, type Signer, buildSiweMessage, fetchDapp, generateWalletId, parseDeepLink };
|
|
117
114
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/DappInfo.ts","../src/api/fetchDapp.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/DappInfo.ts","../src/api/fetchDapp.ts","../src/types/ConnectionRequestParams.ts","../src/types/Signer.ts","../src/types/RevokeParams.ts","../src/ws/ConnectionRequest.ts","../src/core/BananalinkWalletConfig.ts","../src/core/BananalinkWallet.ts","../src/core/generateWalletId.ts","../src/errors/BananalinkError.ts","../src/errors/ApiError.ts","../src/errors/ConnectionError.ts","../src/errors/NotConnectedError.ts","../src/types/BuildSiweMessageParams.ts","../src/types/DeepLinkParams.ts","../src/utils/buildSiweMessage.ts","../src/utils/parseDeepLink.ts"],"mappings":";UAAiB,QAAA;EACf,MAAA;EACA,QAAA;EACA,eAAA;EACA,aAAA;EACA,MAAA;EACA,GAAA;EACA,KAAA;EACA,OAAA;AAAA;;;iBCLoB,SAAA,CAAU,MAAA,UAAgB,MAAA,WAAiB,OAAA,CAAQ,QAAA;;;UCHxD,uBAAA;EACf,cAAA;EACA,MAAA;EACA,KAAA;AAAA;;;UCHe,MAAA;EACf,OAAA;EACA,WAAA,GAAc,OAAA,aAAoB,OAAA;AAAA;;;UCAnB,YAAA;EACf,MAAA;EACA,MAAA,EAAQ,MAAA;AAAA;;;UCDO,iBAAA;EAAA,SACN,cAAA;EAAA,SACA,KAAA;EAAA,SACA,IAAA,EAAM,QAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,MAAA,GAAS,OAAA;EACzB,MAAA,IAAU,OAAA;AAAA;;;UCRK,sBAAA;EACf,MAAA;EACA,KAAA;AAAA;;;cCaW,gBAAA;EAAA,wBACa,eAAA;EAAA,wBACA,cAAA;EAAA,iBAEP,MAAA;EAAA,iBACA,KAAA;EAAA,QACT,EAAA;cAEI,MAAA,GAAS,sBAAA;EAKf,OAAA,CAAQ,QAAA,WAAmB,OAAA;EAW3B,oBAAA,CAAqB,MAAA,EAAQ,uBAAA,GAA0B,OAAA,CAAQ,iBAAA;EA2B/D,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA;EAyBpC,UAAA,CAAA;AAAA;;;iBC3Fc,gBAAA,CAAA;;;cCAH,eAAA,SAAwB,KAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCC5B,QAAA,SAAiB,eAAA;EAAA,SACZ,MAAA;EAAA,SACA,IAAA;cAEJ,MAAA,UAAgB,IAAA;AAAA;;;cCJjB,eAAA,SAAwB,eAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCD5B,iBAAA,SAA0B,eAAA;EAAA,WAAA,CAAA;AAAA;;;UCFtB,sBAAA;EACf,MAAA;EACA,OAAA;EACA,GAAA;EACA,SAAA;EACA,KAAA;EACA,OAAA;EACA,QAAA;AAAA;;;UCPe,cAAA;EACf,MAAA;EACA,cAAA;EACA,KAAA;AAAA;;;iBCDc,gBAAA,CAAiB,MAAA,EAAQ,sBAAA;;;iBCCzB,aAAA,CAAc,QAAA,WAAmB,cAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -13,9 +13,6 @@ interface DappInfo {
|
|
|
13
13
|
//#region src/api/fetchDapp.d.ts
|
|
14
14
|
declare function fetchDapp(apiUrl: string, dappId: string): Promise<DappInfo>;
|
|
15
15
|
//#endregion
|
|
16
|
-
//#region src/constants/RevocationStatement.d.ts
|
|
17
|
-
declare const REVOCATION_STATEMENT = "Revoke dapp access";
|
|
18
|
-
//#endregion
|
|
19
16
|
//#region src/types/ConnectionRequestParams.d.ts
|
|
20
17
|
interface ConnectionRequestParams {
|
|
21
18
|
dappInstanceId: string;
|
|
@@ -113,5 +110,5 @@ declare function buildSiweMessage(params: BuildSiweMessageParams): string;
|
|
|
113
110
|
//#region src/utils/parseDeepLink.d.ts
|
|
114
111
|
declare function parseDeepLink(deepLink: string): DeepLinkParams;
|
|
115
112
|
//#endregion
|
|
116
|
-
export { ApiError, BananalinkError, BananalinkWallet, type BananalinkWalletConfig, type BuildSiweMessageParams, ConnectionError, type ConnectionRequest, type ConnectionRequestParams, type DappInfo, type DeepLinkParams, NotConnectedError,
|
|
113
|
+
export { ApiError, BananalinkError, BananalinkWallet, type BananalinkWalletConfig, type BuildSiweMessageParams, ConnectionError, type ConnectionRequest, type ConnectionRequestParams, type DappInfo, type DeepLinkParams, NotConnectedError, type RevokeParams, type Signer, buildSiweMessage, fetchDapp, generateWalletId, parseDeepLink };
|
|
117
114
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/DappInfo.ts","../src/api/fetchDapp.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/DappInfo.ts","../src/api/fetchDapp.ts","../src/types/ConnectionRequestParams.ts","../src/types/Signer.ts","../src/types/RevokeParams.ts","../src/ws/ConnectionRequest.ts","../src/core/BananalinkWalletConfig.ts","../src/core/BananalinkWallet.ts","../src/core/generateWalletId.ts","../src/errors/BananalinkError.ts","../src/errors/ApiError.ts","../src/errors/ConnectionError.ts","../src/errors/NotConnectedError.ts","../src/types/BuildSiweMessageParams.ts","../src/types/DeepLinkParams.ts","../src/utils/buildSiweMessage.ts","../src/utils/parseDeepLink.ts"],"mappings":";UAAiB,QAAA;EACf,MAAA;EACA,QAAA;EACA,eAAA;EACA,aAAA;EACA,MAAA;EACA,GAAA;EACA,KAAA;EACA,OAAA;AAAA;;;iBCLoB,SAAA,CAAU,MAAA,UAAgB,MAAA,WAAiB,OAAA,CAAQ,QAAA;;;UCHxD,uBAAA;EACf,cAAA;EACA,MAAA;EACA,KAAA;AAAA;;;UCHe,MAAA;EACf,OAAA;EACA,WAAA,GAAc,OAAA,aAAoB,OAAA;AAAA;;;UCAnB,YAAA;EACf,MAAA;EACA,MAAA,EAAQ,MAAA;AAAA;;;UCDO,iBAAA;EAAA,SACN,cAAA;EAAA,SACA,KAAA;EAAA,SACA,IAAA,EAAM,QAAA;EACf,OAAA,CAAQ,MAAA,EAAQ,MAAA,GAAS,OAAA;EACzB,MAAA,IAAU,OAAA;AAAA;;;UCRK,sBAAA;EACf,MAAA;EACA,KAAA;AAAA;;;cCaW,gBAAA;EAAA,wBACa,eAAA;EAAA,wBACA,cAAA;EAAA,iBAEP,MAAA;EAAA,iBACA,KAAA;EAAA,QACT,EAAA;cAEI,MAAA,GAAS,sBAAA;EAKf,OAAA,CAAQ,QAAA,WAAmB,OAAA;EAW3B,oBAAA,CAAqB,MAAA,EAAQ,uBAAA,GAA0B,OAAA,CAAQ,iBAAA;EA2B/D,MAAA,CAAO,MAAA,EAAQ,YAAA,GAAe,OAAA;EAyBpC,UAAA,CAAA;AAAA;;;iBC3Fc,gBAAA,CAAA;;;cCAH,eAAA,SAAwB,KAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCC5B,QAAA,SAAiB,eAAA;EAAA,SACZ,MAAA;EAAA,SACA,IAAA;cAEJ,MAAA,UAAgB,IAAA;AAAA;;;cCJjB,eAAA,SAAwB,eAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCD5B,iBAAA,SAA0B,eAAA;EAAA,WAAA,CAAA;AAAA;;;UCFtB,sBAAA;EACf,MAAA;EACA,OAAA;EACA,GAAA;EACA,SAAA;EACA,KAAA;EACA,OAAA;EACA,QAAA;AAAA;;;UCPe,cAAA;EACf,MAAA;EACA,cAAA;EACA,KAAA;AAAA;;;iBCDc,gBAAA,CAAiB,MAAA,EAAQ,sBAAA;;;iBCCzB,aAAA,CAAc,QAAA,WAAmB,cAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -37,10 +37,6 @@ function isDappInfo(value) {
|
|
|
37
37
|
return typeof record.dappId === "string" && typeof record.dappName === "string" && (typeof record.dappDescription === "string" || typeof record.dappDescription === "undefined") && typeof record.dappStatement === "string" && typeof record.domain === "string" && typeof record.uri === "string" && Array.isArray(record.icons) && record.icons.every((icon) => typeof icon === "string") && typeof record.chainId === "number" && Number.isFinite(record.chainId);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/constants/RevocationStatement.ts
|
|
42
|
-
const REVOCATION_STATEMENT = "Revoke dapp access";
|
|
43
|
-
|
|
44
40
|
//#endregion
|
|
45
41
|
//#region src/api/deleteDappTokens.ts
|
|
46
42
|
async function deleteDappTokens(apiUrl, dappId, body) {
|
|
@@ -64,12 +60,23 @@ async function fetchRevocationNonce(apiUrl) {
|
|
|
64
60
|
throw new ApiError(response.status, body);
|
|
65
61
|
}
|
|
66
62
|
const body = await response.json();
|
|
67
|
-
if (typeof body !== "object" || body === null || !("nonce" in body)) throw new ApiError(response.status, body);
|
|
68
|
-
const { nonce } = body;
|
|
69
|
-
if (typeof nonce !== "string" || nonce.length === 0) throw new ApiError(response.status, body);
|
|
70
|
-
return
|
|
63
|
+
if (typeof body !== "object" || body === null || !("nonce" in body) || !("domain" in body)) throw new ApiError(response.status, body);
|
|
64
|
+
const { nonce, domain } = body;
|
|
65
|
+
if (typeof nonce !== "string" || nonce.length === 0 || typeof domain !== "string" || domain.length === 0) throw new ApiError(response.status, body);
|
|
66
|
+
return {
|
|
67
|
+
nonce,
|
|
68
|
+
domain
|
|
69
|
+
};
|
|
71
70
|
}
|
|
72
71
|
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/constants/RevocationStatement.ts
|
|
74
|
+
const REVOCATION_SIWE = {
|
|
75
|
+
statement: "Revoke dapp access",
|
|
76
|
+
uri: "https://bananalink",
|
|
77
|
+
chainId: 1
|
|
78
|
+
};
|
|
79
|
+
|
|
73
80
|
//#endregion
|
|
74
81
|
//#region src/errors/NotConnectedError.ts
|
|
75
82
|
var NotConnectedError = class extends BananalinkError {
|
|
@@ -280,14 +287,14 @@ var BananalinkWallet = class BananalinkWallet {
|
|
|
280
287
|
async revoke(params) {
|
|
281
288
|
if (!params.dappId.trim()) throw new BananalinkError("dappId is required");
|
|
282
289
|
if (!params.signer.address.trim()) throw new BananalinkError("signer address is required");
|
|
283
|
-
const
|
|
290
|
+
const { nonce, domain } = await fetchRevocationNonce(this.apiUrl);
|
|
284
291
|
const message = buildSiweMessage({
|
|
285
|
-
domain
|
|
292
|
+
domain,
|
|
286
293
|
address: params.signer.address,
|
|
287
|
-
uri:
|
|
288
|
-
statement:
|
|
294
|
+
uri: REVOCATION_SIWE.uri,
|
|
295
|
+
statement: REVOCATION_SIWE.statement,
|
|
289
296
|
nonce,
|
|
290
|
-
chainId:
|
|
297
|
+
chainId: REVOCATION_SIWE.chainId
|
|
291
298
|
});
|
|
292
299
|
const signature = await params.signer.signMessage(message);
|
|
293
300
|
await deleteDappTokens(this.apiUrl, params.dappId, {
|
|
@@ -335,5 +342,5 @@ function parseDeepLink(deepLink) {
|
|
|
335
342
|
}
|
|
336
343
|
|
|
337
344
|
//#endregion
|
|
338
|
-
export { ApiError, BananalinkError, BananalinkWallet, ConnectionError, NotConnectedError,
|
|
345
|
+
export { ApiError, BananalinkError, BananalinkWallet, ConnectionError, NotConnectedError, buildSiweMessage, fetchDapp, generateWalletId, parseDeepLink };
|
|
339
346
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/BananalinkError.ts","../src/errors/ApiError.ts","../src/api/fetchDapp.ts","../src/constants/RevocationStatement.ts","../src/api/deleteDappTokens.ts","../src/api/fetchRevocationNonce.ts","../src/errors/NotConnectedError.ts","../src/utils/buildSiweMessage.ts","../src/errors/ConnectionError.ts","../src/ws/connectWebSocket.ts","../src/ws/createConnectionRequest.ts","../src/core/BananalinkWallet.ts","../src/core/generateWalletId.ts","../src/utils/parseDeepLink.ts"],"sourcesContent":["export class BananalinkError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'BananalinkError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ApiError extends BananalinkError {\n public readonly status: number;\n public readonly body: unknown;\n\n constructor(status: number, body: unknown) {\n super(`API request failed with status ${status}`);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n","import { ApiError } from '../errors/ApiError.js';\nimport type { DappInfo } from '../types/DappInfo.js';\n\nexport async function fetchDapp(apiUrl: string, dappId: string): Promise<DappInfo> {\n const response = await fetch(`${apiUrl}/dapps/${encodeURIComponent(dappId)}`);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n throw new ApiError(response.status, body);\n }\n\n const body = await response.json();\n\n if (!isDappInfo(body)) {\n throw new ApiError(response.status, body);\n }\n\n return body;\n}\n\nfunction isDappInfo(value: unknown): value is DappInfo {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n typeof record.dappId === 'string' &&\n typeof record.dappName === 'string' &&\n (typeof record.dappDescription === 'string' || typeof record.dappDescription === 'undefined') &&\n typeof record.dappStatement === 'string' &&\n typeof record.domain === 'string' &&\n typeof record.uri === 'string' &&\n Array.isArray(record.icons) &&\n record.icons.every((icon) => typeof icon === 'string') &&\n typeof record.chainId === 'number' &&\n Number.isFinite(record.chainId)\n );\n}\n","export const REVOCATION_STATEMENT = 'Revoke dapp access';\n","import { ApiError } from '../errors/ApiError.js';\n\nexport async function deleteDappTokens(\n apiUrl: string,\n dappId: string,\n body: { message: string; signature: string },\n): Promise<void> {\n const response = await fetch(`${apiUrl}/wallet/dapps/${encodeURIComponent(dappId)}/tokens`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.json().catch(() => null);\n throw new ApiError(response.status, responseBody);\n }\n}\n","import { ApiError } from '../errors/ApiError.js';\n\nexport async function fetchRevocationNonce(apiUrl: string): Promise<string> {\n const response = await fetch(`${apiUrl}/wallet/revocations/nonce`);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n throw new ApiError(response.status, body);\n }\n\n const body: unknown = await response.json();\n\n if (typeof body !== 'object' || body === null || !('nonce' in body)) {\n throw new ApiError(response.status, body);\n }\n\n const { nonce } = body as { nonce: unknown };\n\n if (typeof nonce !== 'string' || nonce.length === 0) {\n throw new ApiError(response.status, body);\n }\n\n return nonce;\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class NotConnectedError extends BananalinkError {\n constructor() {\n super('Wallet is not connected. Call connect() first.');\n this.name = 'NotConnectedError';\n }\n}\n","import type { BuildSiweMessageParams } from '../types/BuildSiweMessageParams.js';\n\nexport function buildSiweMessage(params: BuildSiweMessageParams): string {\n const issuedAt = params.issuedAt ?? new Date().toISOString();\n\n return (\n `${params.domain} wants you to sign in with your Ethereum account:\\n` +\n `${params.address}\\n` +\n `\\n` +\n `${params.statement}\\n` +\n `\\n` +\n `URI: ${params.uri}\\n` +\n `Version: 1\\n` +\n `Chain ID: ${params.chainId}\\n` +\n `Nonce: ${params.nonce}\\n` +\n `Issued At: ${issuedAt}`\n );\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionError extends BananalinkError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConnectionError';\n }\n}\n","import { ConnectionError } from '../errors/ConnectionError.js';\nimport type { ConnectWebSocketOptions } from './ConnectWebSocketOptions.js';\nimport type { WsHandle } from './WsHandle.js';\n\nconst HEARTBEAT_PING = 'ping' as const;\nconst HEARTBEAT_PONG = 'pong' as const;\n\nexport function connectWebSocket(options: ConnectWebSocketOptions): Promise<WsHandle> {\n const {\n url,\n maxReconnectAttempts = 5,\n baseReconnectDelayMs = 1000,\n pingIntervalMs = 300_000,\n pongTimeoutMs = 10_000,\n } = options;\n\n if (pingIntervalMs > 0 && pongTimeoutMs >= pingIntervalMs) {\n throw new ConnectionError(`pongTimeoutMs (${pongTimeoutMs}) must be less than pingIntervalMs (${pingIntervalMs})`);\n }\n\n return new Promise((resolveHandle, rejectHandle) => {\n let ws: WebSocket;\n let reconnectAttempts = 0;\n let intentionallyClosed = false;\n let connected = false;\n let permanentlyClosed = false;\n let settled = false;\n let lastConnectError: unknown;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let pongTimer: ReturnType<typeof setTimeout> | null = null;\n\n function resolveOnce(): void {\n if (settled) {\n return;\n }\n\n settled = true;\n resolveHandle(handle);\n }\n\n function rejectOnce(error: ConnectionError): void {\n if (settled) {\n return;\n }\n\n settled = true;\n rejectHandle(error);\n }\n\n function clearPongTimeout(): void {\n if (pongTimer === null) {\n return;\n }\n\n clearTimeout(pongTimer);\n pongTimer = null;\n }\n\n function stopHeartbeat(): void {\n if (pingTimer !== null) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n\n clearPongTimeout();\n }\n\n function startHeartbeat(): void {\n stopHeartbeat();\n\n if (pingIntervalMs <= 0) {\n return;\n }\n\n pingTimer = setInterval(() => {\n if (intentionallyClosed || permanentlyClosed || ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n try {\n ws.send(JSON.stringify({ type: HEARTBEAT_PING }));\n } catch {\n ws.close();\n return;\n }\n\n clearPongTimeout();\n pongTimer = setTimeout(() => {\n if (!intentionallyClosed && !permanentlyClosed && ws.readyState === WebSocket.OPEN) {\n ws.close();\n }\n }, pongTimeoutMs);\n }, pingIntervalMs);\n }\n\n function connect() {\n ws = new WebSocket(url);\n\n ws.addEventListener('open', () => {\n reconnectAttempts = 0;\n permanentlyClosed = false;\n startHeartbeat();\n\n if (!connected) {\n connected = true;\n resolveOnce();\n }\n });\n\n ws.addEventListener('message', (event) => {\n if (typeof event.data === 'string' && isPongMessage(event.data)) {\n clearPongTimeout();\n }\n });\n\n ws.addEventListener('close', () => {\n stopHeartbeat();\n\n if (intentionallyClosed) return;\n attemptReconnect();\n });\n\n ws.addEventListener('error', (event) => {\n if (!connected) {\n lastConnectError = 'message' in event ? (event as ErrorEvent).message : `WebSocket error on ${url}`;\n }\n });\n }\n\n function attemptReconnect() {\n if (reconnectAttempts >= maxReconnectAttempts) {\n permanentlyClosed = true;\n\n if (!connected) {\n rejectOnce(\n new ConnectionError('Failed to connect after maximum reconnect attempts', {\n cause: lastConnectError,\n }),\n );\n }\n\n return;\n }\n\n const jitter = 0.5 + Math.random() * 0.5;\n const delay = baseReconnectDelayMs * 2 ** reconnectAttempts * jitter;\n reconnectAttempts++;\n\n setTimeout(() => {\n if (!intentionallyClosed) connect();\n }, delay);\n }\n\n const handle: WsHandle = {\n get closed() {\n return intentionallyClosed || permanentlyClosed;\n },\n\n send(data: unknown) {\n if (intentionallyClosed || permanentlyClosed) {\n throw new ConnectionError('WebSocket is closed');\n }\n\n if (ws.readyState !== WebSocket.OPEN) {\n throw new ConnectionError('WebSocket is not connected');\n }\n\n ws.send(JSON.stringify(data));\n },\n\n close() {\n intentionallyClosed = true;\n stopHeartbeat();\n\n if (typeof ws !== 'undefined') {\n ws.close();\n }\n },\n };\n\n connect();\n });\n}\n\nfunction isPongMessage(data: string): boolean {\n try {\n const parsed: unknown = JSON.parse(data);\n return (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'type' in parsed &&\n (parsed as { type: unknown }).type === HEARTBEAT_PONG\n );\n } catch {\n return false;\n }\n}\n","import type { DappInfo } from '../types/DappInfo.js';\nimport type { Signer } from '../types/Signer.js';\nimport { buildSiweMessage } from '../utils/buildSiweMessage.js';\nimport type { ConnectionRequest } from './ConnectionRequest.js';\nimport type { WsHandle } from './WsHandle.js';\n\nexport function createConnectionRequest(params: {\n dappInstanceId: string;\n nonce: string;\n dapp: DappInfo;\n ws: WsHandle;\n}): ConnectionRequest {\n return {\n dappInstanceId: params.dappInstanceId,\n nonce: params.nonce,\n dapp: params.dapp,\n\n async approve(signer: Signer): Promise<void> {\n const message = buildSiweMessage({\n domain: params.dapp.domain,\n address: signer.address,\n uri: params.dapp.uri,\n statement: params.dapp.dappStatement,\n nonce: params.nonce,\n chainId: params.dapp.chainId,\n });\n\n const signature = await signer.signMessage(message);\n\n params.ws.send({\n type: 'authorize',\n dappInstanceId: params.dappInstanceId,\n message,\n signature,\n });\n },\n\n async reject(): Promise<void> {\n params.ws.send({\n type: 'reject',\n dappInstanceId: params.dappInstanceId,\n });\n },\n };\n}\n","import { deleteDappTokens } from '../api/deleteDappTokens.js';\nimport { fetchDapp } from '../api/fetchDapp.js';\nimport { fetchRevocationNonce } from '../api/fetchRevocationNonce.js';\nimport { REVOCATION_STATEMENT } from '../constants/RevocationStatement.js';\nimport { BananalinkError } from '../errors/BananalinkError.js';\nimport { NotConnectedError } from '../errors/NotConnectedError.js';\nimport type { ConnectionRequestParams } from '../types/ConnectionRequestParams.js';\nimport type { RevokeParams } from '../types/RevokeParams.js';\nimport { buildSiweMessage } from '../utils/buildSiweMessage.js';\nimport type { ConnectionRequest } from '../ws/ConnectionRequest.js';\nimport { connectWebSocket } from '../ws/connectWebSocket.js';\nimport { createConnectionRequest } from '../ws/createConnectionRequest.js';\nimport type { WsHandle } from '../ws/WsHandle.js';\nimport type { BananalinkWalletConfig } from './BananalinkWalletConfig.js';\n\nexport class BananalinkWallet {\n private static readonly DEFAULT_API_URL = 'https://tfr9p2mn4i.execute-api.us-east-2.amazonaws.com';\n private static readonly DEFAULT_WS_URL = 'wss://yb47qomkt3.execute-api.us-east-2.amazonaws.com/v1';\n\n private readonly apiUrl: string;\n private readonly wsUrl: string;\n private ws: WsHandle | null = null;\n\n constructor(config?: BananalinkWalletConfig) {\n this.apiUrl = stripTrailingSlash(config?.apiUrl ?? BananalinkWallet.DEFAULT_API_URL);\n this.wsUrl = stripTrailingSlash(config?.wsUrl ?? BananalinkWallet.DEFAULT_WS_URL);\n }\n\n async connect(walletId: string): Promise<void> {\n if (!walletId.trim()) {\n throw new BananalinkError('walletId is required');\n }\n\n this.disconnect();\n\n const url = `${this.wsUrl}?walletId=${encodeURIComponent(walletId)}`;\n this.ws = await connectWebSocket({ url });\n }\n\n async getConnectionRequest(params: ConnectionRequestParams): Promise<ConnectionRequest> {\n if (!this.ws) {\n throw new NotConnectedError();\n }\n\n if (!params.dappInstanceId.trim()) {\n throw new BananalinkError('dappInstanceId is required');\n }\n\n if (!params.dappId.trim()) {\n throw new BananalinkError('dappId is required');\n }\n\n if (!params.nonce.trim()) {\n throw new BananalinkError('nonce is required');\n }\n\n const dapp = await fetchDapp(this.apiUrl, params.dappId);\n\n return createConnectionRequest({\n dappInstanceId: params.dappInstanceId,\n nonce: params.nonce,\n dapp,\n ws: this.ws,\n });\n }\n\n async revoke(params: RevokeParams): Promise<void> {\n if (!params.dappId.trim()) {\n throw new BananalinkError('dappId is required');\n }\n\n if (!params.signer.address.trim()) {\n throw new BananalinkError('signer address is required');\n }\n\n const [dapp, nonce] = await Promise.all([fetchDapp(this.apiUrl, params.dappId), fetchRevocationNonce(this.apiUrl)]);\n\n const message = buildSiweMessage({\n domain: dapp.domain,\n address: params.signer.address,\n uri: dapp.uri,\n statement: REVOCATION_STATEMENT,\n nonce,\n chainId: dapp.chainId,\n });\n\n const signature = await params.signer.signMessage(message);\n\n await deleteDappTokens(this.apiUrl, params.dappId, { message, signature });\n }\n\n disconnect(): void {\n this.ws?.close();\n this.ws = null;\n }\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '');\n}\n","export function generateWalletId(): string {\n return crypto.randomUUID();\n}\n","import { BananalinkError } from '../errors/BananalinkError.js';\nimport type { DeepLinkParams } from '../types/DeepLinkParams.js';\n\nexport function parseDeepLink(deepLink: string): DeepLinkParams {\n let url: URL;\n\n try {\n url = new URL(deepLink);\n } catch (error) {\n throw new BananalinkError('Invalid deep link URL', { cause: error });\n }\n\n const dappId = url.searchParams.get('dappId');\n const dappInstanceId = url.searchParams.get('dappInstanceId');\n const nonce = url.searchParams.get('nonce');\n\n if (!dappId || !dappInstanceId || !nonce) {\n const missing = [!dappId && 'dappId', !dappInstanceId && 'dappInstanceId', !nonce && 'nonce'].filter(Boolean);\n throw new BananalinkError(`Invalid deep link: missing ${missing.join(', ')}`);\n }\n\n return { dappId, dappInstanceId, nonce };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/BananalinkError.ts","../src/errors/ApiError.ts","../src/api/fetchDapp.ts","../src/api/deleteDappTokens.ts","../src/api/fetchRevocationNonce.ts","../src/constants/RevocationStatement.ts","../src/errors/NotConnectedError.ts","../src/utils/buildSiweMessage.ts","../src/errors/ConnectionError.ts","../src/ws/connectWebSocket.ts","../src/ws/createConnectionRequest.ts","../src/core/BananalinkWallet.ts","../src/core/generateWalletId.ts","../src/utils/parseDeepLink.ts"],"sourcesContent":["export class BananalinkError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'BananalinkError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ApiError extends BananalinkError {\n public readonly status: number;\n public readonly body: unknown;\n\n constructor(status: number, body: unknown) {\n super(`API request failed with status ${status}`);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n","import { ApiError } from '../errors/ApiError.js';\nimport type { DappInfo } from '../types/DappInfo.js';\n\nexport async function fetchDapp(apiUrl: string, dappId: string): Promise<DappInfo> {\n const response = await fetch(`${apiUrl}/dapps/${encodeURIComponent(dappId)}`);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n throw new ApiError(response.status, body);\n }\n\n const body = await response.json();\n\n if (!isDappInfo(body)) {\n throw new ApiError(response.status, body);\n }\n\n return body;\n}\n\nfunction isDappInfo(value: unknown): value is DappInfo {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const record = value as Record<string, unknown>;\n\n return (\n typeof record.dappId === 'string' &&\n typeof record.dappName === 'string' &&\n (typeof record.dappDescription === 'string' || typeof record.dappDescription === 'undefined') &&\n typeof record.dappStatement === 'string' &&\n typeof record.domain === 'string' &&\n typeof record.uri === 'string' &&\n Array.isArray(record.icons) &&\n record.icons.every((icon) => typeof icon === 'string') &&\n typeof record.chainId === 'number' &&\n Number.isFinite(record.chainId)\n );\n}\n","import { ApiError } from '../errors/ApiError.js';\n\nexport async function deleteDappTokens(\n apiUrl: string,\n dappId: string,\n body: { message: string; signature: string },\n): Promise<void> {\n const response = await fetch(`${apiUrl}/wallet/dapps/${encodeURIComponent(dappId)}/tokens`, {\n method: 'DELETE',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.json().catch(() => null);\n throw new ApiError(response.status, responseBody);\n }\n}\n","import { ApiError } from '../errors/ApiError.js';\n\nexport interface RevocationNonceResponse {\n nonce: string;\n domain: string;\n}\n\nexport async function fetchRevocationNonce(apiUrl: string): Promise<RevocationNonceResponse> {\n const response = await fetch(`${apiUrl}/wallet/revocations/nonce`);\n\n if (!response.ok) {\n const body = await response.json().catch(() => null);\n throw new ApiError(response.status, body);\n }\n\n const body: unknown = await response.json();\n\n if (typeof body !== 'object' || body === null || !('nonce' in body) || !('domain' in body)) {\n throw new ApiError(response.status, body);\n }\n\n const { nonce, domain } = body as { nonce: unknown; domain: unknown };\n\n if (typeof nonce !== 'string' || nonce.length === 0 || typeof domain !== 'string' || domain.length === 0) {\n throw new ApiError(response.status, body);\n }\n\n return { nonce, domain };\n}\n","export const REVOCATION_SIWE = {\n statement: 'Revoke dapp access',\n uri: 'https://bananalink',\n chainId: 1,\n} as const;\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class NotConnectedError extends BananalinkError {\n constructor() {\n super('Wallet is not connected. Call connect() first.');\n this.name = 'NotConnectedError';\n }\n}\n","import type { BuildSiweMessageParams } from '../types/BuildSiweMessageParams.js';\n\nexport function buildSiweMessage(params: BuildSiweMessageParams): string {\n const issuedAt = params.issuedAt ?? new Date().toISOString();\n\n return (\n `${params.domain} wants you to sign in with your Ethereum account:\\n` +\n `${params.address}\\n` +\n `\\n` +\n `${params.statement}\\n` +\n `\\n` +\n `URI: ${params.uri}\\n` +\n `Version: 1\\n` +\n `Chain ID: ${params.chainId}\\n` +\n `Nonce: ${params.nonce}\\n` +\n `Issued At: ${issuedAt}`\n );\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionError extends BananalinkError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConnectionError';\n }\n}\n","import { ConnectionError } from '../errors/ConnectionError.js';\nimport type { ConnectWebSocketOptions } from './ConnectWebSocketOptions.js';\nimport type { WsHandle } from './WsHandle.js';\n\nconst HEARTBEAT_PING = 'ping' as const;\nconst HEARTBEAT_PONG = 'pong' as const;\n\nexport function connectWebSocket(options: ConnectWebSocketOptions): Promise<WsHandle> {\n const {\n url,\n maxReconnectAttempts = 5,\n baseReconnectDelayMs = 1000,\n pingIntervalMs = 300_000,\n pongTimeoutMs = 10_000,\n } = options;\n\n if (pingIntervalMs > 0 && pongTimeoutMs >= pingIntervalMs) {\n throw new ConnectionError(`pongTimeoutMs (${pongTimeoutMs}) must be less than pingIntervalMs (${pingIntervalMs})`);\n }\n\n return new Promise((resolveHandle, rejectHandle) => {\n let ws: WebSocket;\n let reconnectAttempts = 0;\n let intentionallyClosed = false;\n let connected = false;\n let permanentlyClosed = false;\n let settled = false;\n let lastConnectError: unknown;\n let pingTimer: ReturnType<typeof setInterval> | null = null;\n let pongTimer: ReturnType<typeof setTimeout> | null = null;\n\n function resolveOnce(): void {\n if (settled) {\n return;\n }\n\n settled = true;\n resolveHandle(handle);\n }\n\n function rejectOnce(error: ConnectionError): void {\n if (settled) {\n return;\n }\n\n settled = true;\n rejectHandle(error);\n }\n\n function clearPongTimeout(): void {\n if (pongTimer === null) {\n return;\n }\n\n clearTimeout(pongTimer);\n pongTimer = null;\n }\n\n function stopHeartbeat(): void {\n if (pingTimer !== null) {\n clearInterval(pingTimer);\n pingTimer = null;\n }\n\n clearPongTimeout();\n }\n\n function startHeartbeat(): void {\n stopHeartbeat();\n\n if (pingIntervalMs <= 0) {\n return;\n }\n\n pingTimer = setInterval(() => {\n if (intentionallyClosed || permanentlyClosed || ws.readyState !== WebSocket.OPEN) {\n return;\n }\n\n try {\n ws.send(JSON.stringify({ type: HEARTBEAT_PING }));\n } catch {\n ws.close();\n return;\n }\n\n clearPongTimeout();\n pongTimer = setTimeout(() => {\n if (!intentionallyClosed && !permanentlyClosed && ws.readyState === WebSocket.OPEN) {\n ws.close();\n }\n }, pongTimeoutMs);\n }, pingIntervalMs);\n }\n\n function connect() {\n ws = new WebSocket(url);\n\n ws.addEventListener('open', () => {\n reconnectAttempts = 0;\n permanentlyClosed = false;\n startHeartbeat();\n\n if (!connected) {\n connected = true;\n resolveOnce();\n }\n });\n\n ws.addEventListener('message', (event) => {\n if (typeof event.data === 'string' && isPongMessage(event.data)) {\n clearPongTimeout();\n }\n });\n\n ws.addEventListener('close', () => {\n stopHeartbeat();\n\n if (intentionallyClosed) return;\n attemptReconnect();\n });\n\n ws.addEventListener('error', (event) => {\n if (!connected) {\n lastConnectError = 'message' in event ? (event as ErrorEvent).message : `WebSocket error on ${url}`;\n }\n });\n }\n\n function attemptReconnect() {\n if (reconnectAttempts >= maxReconnectAttempts) {\n permanentlyClosed = true;\n\n if (!connected) {\n rejectOnce(\n new ConnectionError('Failed to connect after maximum reconnect attempts', {\n cause: lastConnectError,\n }),\n );\n }\n\n return;\n }\n\n const jitter = 0.5 + Math.random() * 0.5;\n const delay = baseReconnectDelayMs * 2 ** reconnectAttempts * jitter;\n reconnectAttempts++;\n\n setTimeout(() => {\n if (!intentionallyClosed) connect();\n }, delay);\n }\n\n const handle: WsHandle = {\n get closed() {\n return intentionallyClosed || permanentlyClosed;\n },\n\n send(data: unknown) {\n if (intentionallyClosed || permanentlyClosed) {\n throw new ConnectionError('WebSocket is closed');\n }\n\n if (ws.readyState !== WebSocket.OPEN) {\n throw new ConnectionError('WebSocket is not connected');\n }\n\n ws.send(JSON.stringify(data));\n },\n\n close() {\n intentionallyClosed = true;\n stopHeartbeat();\n\n if (typeof ws !== 'undefined') {\n ws.close();\n }\n },\n };\n\n connect();\n });\n}\n\nfunction isPongMessage(data: string): boolean {\n try {\n const parsed: unknown = JSON.parse(data);\n return (\n typeof parsed === 'object' &&\n parsed !== null &&\n 'type' in parsed &&\n (parsed as { type: unknown }).type === HEARTBEAT_PONG\n );\n } catch {\n return false;\n }\n}\n","import type { DappInfo } from '../types/DappInfo.js';\nimport type { Signer } from '../types/Signer.js';\nimport { buildSiweMessage } from '../utils/buildSiweMessage.js';\nimport type { ConnectionRequest } from './ConnectionRequest.js';\nimport type { WsHandle } from './WsHandle.js';\n\nexport function createConnectionRequest(params: {\n dappInstanceId: string;\n nonce: string;\n dapp: DappInfo;\n ws: WsHandle;\n}): ConnectionRequest {\n return {\n dappInstanceId: params.dappInstanceId,\n nonce: params.nonce,\n dapp: params.dapp,\n\n async approve(signer: Signer): Promise<void> {\n const message = buildSiweMessage({\n domain: params.dapp.domain,\n address: signer.address,\n uri: params.dapp.uri,\n statement: params.dapp.dappStatement,\n nonce: params.nonce,\n chainId: params.dapp.chainId,\n });\n\n const signature = await signer.signMessage(message);\n\n params.ws.send({\n type: 'authorize',\n dappInstanceId: params.dappInstanceId,\n message,\n signature,\n });\n },\n\n async reject(): Promise<void> {\n params.ws.send({\n type: 'reject',\n dappInstanceId: params.dappInstanceId,\n });\n },\n };\n}\n","import { deleteDappTokens } from '../api/deleteDappTokens.js';\nimport { fetchDapp } from '../api/fetchDapp.js';\nimport { fetchRevocationNonce } from '../api/fetchRevocationNonce.js';\nimport { REVOCATION_SIWE } from '../constants/RevocationStatement.js';\nimport { BananalinkError } from '../errors/BananalinkError.js';\nimport { NotConnectedError } from '../errors/NotConnectedError.js';\nimport type { ConnectionRequestParams } from '../types/ConnectionRequestParams.js';\nimport type { RevokeParams } from '../types/RevokeParams.js';\nimport { buildSiweMessage } from '../utils/buildSiweMessage.js';\nimport type { ConnectionRequest } from '../ws/ConnectionRequest.js';\nimport { connectWebSocket } from '../ws/connectWebSocket.js';\nimport { createConnectionRequest } from '../ws/createConnectionRequest.js';\nimport type { WsHandle } from '../ws/WsHandle.js';\nimport type { BananalinkWalletConfig } from './BananalinkWalletConfig.js';\n\nexport class BananalinkWallet {\n private static readonly DEFAULT_API_URL = 'https://tfr9p2mn4i.execute-api.us-east-2.amazonaws.com';\n private static readonly DEFAULT_WS_URL = 'wss://yb47qomkt3.execute-api.us-east-2.amazonaws.com/v1';\n\n private readonly apiUrl: string;\n private readonly wsUrl: string;\n private ws: WsHandle | null = null;\n\n constructor(config?: BananalinkWalletConfig) {\n this.apiUrl = stripTrailingSlash(config?.apiUrl ?? BananalinkWallet.DEFAULT_API_URL);\n this.wsUrl = stripTrailingSlash(config?.wsUrl ?? BananalinkWallet.DEFAULT_WS_URL);\n }\n\n async connect(walletId: string): Promise<void> {\n if (!walletId.trim()) {\n throw new BananalinkError('walletId is required');\n }\n\n this.disconnect();\n\n const url = `${this.wsUrl}?walletId=${encodeURIComponent(walletId)}`;\n this.ws = await connectWebSocket({ url });\n }\n\n async getConnectionRequest(params: ConnectionRequestParams): Promise<ConnectionRequest> {\n if (!this.ws) {\n throw new NotConnectedError();\n }\n\n if (!params.dappInstanceId.trim()) {\n throw new BananalinkError('dappInstanceId is required');\n }\n\n if (!params.dappId.trim()) {\n throw new BananalinkError('dappId is required');\n }\n\n if (!params.nonce.trim()) {\n throw new BananalinkError('nonce is required');\n }\n\n const dapp = await fetchDapp(this.apiUrl, params.dappId);\n\n return createConnectionRequest({\n dappInstanceId: params.dappInstanceId,\n nonce: params.nonce,\n dapp,\n ws: this.ws,\n });\n }\n\n async revoke(params: RevokeParams): Promise<void> {\n if (!params.dappId.trim()) {\n throw new BananalinkError('dappId is required');\n }\n\n if (!params.signer.address.trim()) {\n throw new BananalinkError('signer address is required');\n }\n\n const { nonce, domain } = await fetchRevocationNonce(this.apiUrl);\n\n const message = buildSiweMessage({\n domain,\n address: params.signer.address,\n uri: REVOCATION_SIWE.uri,\n statement: REVOCATION_SIWE.statement,\n nonce,\n chainId: REVOCATION_SIWE.chainId,\n });\n\n const signature = await params.signer.signMessage(message);\n\n await deleteDappTokens(this.apiUrl, params.dappId, { message, signature });\n }\n\n disconnect(): void {\n this.ws?.close();\n this.ws = null;\n }\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, '');\n}\n","export function generateWalletId(): string {\n return crypto.randomUUID();\n}\n","import { BananalinkError } from '../errors/BananalinkError.js';\nimport type { DeepLinkParams } from '../types/DeepLinkParams.js';\n\nexport function parseDeepLink(deepLink: string): DeepLinkParams {\n let url: URL;\n\n try {\n url = new URL(deepLink);\n } catch (error) {\n throw new BananalinkError('Invalid deep link URL', { cause: error });\n }\n\n const dappId = url.searchParams.get('dappId');\n const dappInstanceId = url.searchParams.get('dappInstanceId');\n const nonce = url.searchParams.get('nonce');\n\n if (!dappId || !dappInstanceId || !nonce) {\n const missing = [!dappId && 'dappId', !dappInstanceId && 'dappInstanceId', !nonce && 'nonce'].filter(Boolean);\n throw new BananalinkError(`Invalid deep link: missing ${missing.join(', ')}`);\n }\n\n return { dappId, dappInstanceId, nonce };\n}\n"],"mappings":"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"}
|