@bananalink-test/client 0.0.0 → 0.2.0
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 +63 -54
- package/dist/index.d.cts +17 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +17 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +63 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
29
|
let jose = require("jose");
|
|
30
|
+
let _bananalink_test_sdk_core = require("@bananalink-test/sdk-core");
|
|
30
31
|
let qrcode = require("qrcode");
|
|
31
32
|
qrcode = __toESM(qrcode);
|
|
32
33
|
|
|
@@ -91,11 +92,19 @@ async function verifyJwt(jwt, jwks) {
|
|
|
91
92
|
const { payload } = await (0, jose.jwtVerify)(jwt, jwks, {
|
|
92
93
|
algorithms: ["ES256"],
|
|
93
94
|
issuer: "bananalink",
|
|
94
|
-
requiredClaims: [
|
|
95
|
+
requiredClaims: [
|
|
96
|
+
"sub",
|
|
97
|
+
"aud",
|
|
98
|
+
"message",
|
|
99
|
+
"signature"
|
|
100
|
+
]
|
|
95
101
|
});
|
|
96
102
|
return {
|
|
97
103
|
address: payload.sub,
|
|
98
|
-
dappId: Array.isArray(payload.aud) ? payload.aud[0] : payload.aud
|
|
104
|
+
dappId: Array.isArray(payload.aud) ? payload.aud[0] : payload.aud,
|
|
105
|
+
message: payload.message,
|
|
106
|
+
signature: payload.signature,
|
|
107
|
+
jwt
|
|
99
108
|
};
|
|
100
109
|
} catch {
|
|
101
110
|
return null;
|
|
@@ -142,43 +151,14 @@ var InvalidConnectionStateError = class extends BananalinkError {
|
|
|
142
151
|
|
|
143
152
|
//#endregion
|
|
144
153
|
//#region src/core/BananalinkSession.ts
|
|
145
|
-
var BananalinkSession = class
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
pingTimer = null;
|
|
149
|
-
pongTimer = null;
|
|
150
|
-
constructor(ws) {
|
|
154
|
+
var BananalinkSession = class {
|
|
155
|
+
constructor(sessionClaims, ws) {
|
|
156
|
+
this.sessionClaims = sessionClaims;
|
|
151
157
|
this.ws = ws;
|
|
152
|
-
this.ws.addEventListener("message", (event) => {
|
|
153
|
-
const { type } = JSON.parse(event.data);
|
|
154
|
-
if (type === "pong" && this.pongTimer) {
|
|
155
|
-
clearTimeout(this.pongTimer);
|
|
156
|
-
this.pongTimer = null;
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
this.startPing();
|
|
160
158
|
}
|
|
161
159
|
close() {
|
|
162
|
-
this.stopTimers();
|
|
163
160
|
this.ws.close();
|
|
164
161
|
}
|
|
165
|
-
startPing() {
|
|
166
|
-
this.pingTimer = setInterval(() => this.ping(), BananalinkSession.PING_INTERVAL_MILLIS);
|
|
167
|
-
}
|
|
168
|
-
ping() {
|
|
169
|
-
if (this.ws.readyState !== WebSocket.OPEN) {
|
|
170
|
-
this.close();
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
this.ws.send(JSON.stringify({ type: "ping" }));
|
|
174
|
-
this.pongTimer = setTimeout(() => this.close(), BananalinkSession.PONG_TIMEOUT_MILLIS);
|
|
175
|
-
}
|
|
176
|
-
stopTimers() {
|
|
177
|
-
if (this.pingTimer !== null) clearInterval(this.pingTimer);
|
|
178
|
-
if (this.pongTimer !== null) clearTimeout(this.pongTimer);
|
|
179
|
-
this.pingTimer = null;
|
|
180
|
-
this.pongTimer = null;
|
|
181
|
-
}
|
|
182
162
|
};
|
|
183
163
|
|
|
184
164
|
//#endregion
|
|
@@ -201,27 +181,55 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
201
181
|
if (this.jwt) throw new BananalinkError("Recovering a lost dapp connection is not yet implemented");
|
|
202
182
|
const dappInstanceId = this.dappInstanceId;
|
|
203
183
|
if (!dappInstanceId) throw new InvalidConnectionStateError("Cannot get session without authorizing the dapp instance or providing a valid JWT");
|
|
204
|
-
|
|
205
|
-
|
|
184
|
+
let ws;
|
|
185
|
+
try {
|
|
186
|
+
ws = await (0, _bananalink_test_sdk_core.connectWebSocket)({
|
|
187
|
+
url: `${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`,
|
|
188
|
+
pingIntervalMs: 3e4,
|
|
189
|
+
pongTimeoutMs: 5e3
|
|
190
|
+
});
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
|
|
193
|
+
}
|
|
194
|
+
return await new Promise((resolve, reject) => {
|
|
195
|
+
let settled = false;
|
|
206
196
|
const authTimeout = setTimeout(() => {
|
|
207
|
-
|
|
208
|
-
reject(new ConnectionTimeoutError());
|
|
197
|
+
settleReject(new ConnectionTimeoutError());
|
|
209
198
|
}, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
ws
|
|
219
|
-
|
|
199
|
+
const stopListeningToMessages = ws.onMessage((payload) => {
|
|
200
|
+
if ((0, _bananalink_test_sdk_core.isAuthorizedMessage)(payload)) {
|
|
201
|
+
BananalinkConnection.bind(ws, payload.accessToken);
|
|
202
|
+
settleResolve(new BananalinkSession({
|
|
203
|
+
address: payload.address,
|
|
204
|
+
message: payload.message,
|
|
205
|
+
signature: payload.signature,
|
|
206
|
+
accessToken: payload.accessToken
|
|
207
|
+
}, ws));
|
|
208
|
+
return;
|
|
220
209
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
ws.
|
|
224
|
-
|
|
210
|
+
if ((0, _bananalink_test_sdk_core.isRejectedMessage)(payload)) settleReject(new ConnectionRejectedError());
|
|
211
|
+
});
|
|
212
|
+
const stopListeningToClose = ws.onClose(() => {
|
|
213
|
+
settleReject(new BananalinkError("Connection closed before authorization completed"));
|
|
214
|
+
});
|
|
215
|
+
function cleanup() {
|
|
216
|
+
clearTimeout(authTimeout);
|
|
217
|
+
stopListeningToMessages();
|
|
218
|
+
stopListeningToClose();
|
|
219
|
+
}
|
|
220
|
+
function settleResolve(session) {
|
|
221
|
+
if (settled) return;
|
|
222
|
+
settled = true;
|
|
223
|
+
cleanup();
|
|
224
|
+
resolve(session);
|
|
225
|
+
}
|
|
226
|
+
function settleReject(error) {
|
|
227
|
+
if (settled) return;
|
|
228
|
+
settled = true;
|
|
229
|
+
cleanup();
|
|
230
|
+
ws.close();
|
|
231
|
+
reject(error);
|
|
232
|
+
}
|
|
225
233
|
});
|
|
226
234
|
}
|
|
227
235
|
get connectionUrl() {
|
|
@@ -234,10 +242,11 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
234
242
|
return this.jwt !== void 0;
|
|
235
243
|
}
|
|
236
244
|
static bind(ws, jwt) {
|
|
237
|
-
|
|
245
|
+
const bindMessage = {
|
|
238
246
|
type: "bind",
|
|
239
247
|
jwt
|
|
240
|
-
}
|
|
248
|
+
};
|
|
249
|
+
ws.send(bindMessage);
|
|
241
250
|
}
|
|
242
251
|
};
|
|
243
252
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,33 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
dappId: string;
|
|
4
|
-
dappName: string;
|
|
5
|
-
dappDescription?: string;
|
|
6
|
-
dappStatement: string;
|
|
7
|
-
domain: string;
|
|
8
|
-
uri: string;
|
|
9
|
-
icons?: string[];
|
|
10
|
-
chainId?: number;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
1
|
+
import { Dapp, Dapp as Dapp$1, TransportHandle } from "@bananalink-test/sdk-core";
|
|
2
|
+
|
|
13
3
|
//#region src/types/JwtPayload.d.ts
|
|
14
4
|
type JwtPayload = {
|
|
15
5
|
address: string;
|
|
16
6
|
dappId: string;
|
|
7
|
+
message: string;
|
|
8
|
+
signature: string;
|
|
9
|
+
jwt: string;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/types/SessionClaims.d.ts
|
|
13
|
+
type SessionClaims = {
|
|
14
|
+
address: string;
|
|
15
|
+
message: string;
|
|
16
|
+
signature: string;
|
|
17
|
+
accessToken: string;
|
|
17
18
|
};
|
|
18
19
|
//#endregion
|
|
19
20
|
//#region src/core/BananalinkSession.d.ts
|
|
20
21
|
declare class BananalinkSession {
|
|
22
|
+
readonly sessionClaims: SessionClaims;
|
|
21
23
|
private readonly ws;
|
|
22
|
-
|
|
23
|
-
private static readonly PONG_TIMEOUT_MILLIS;
|
|
24
|
-
private pingTimer;
|
|
25
|
-
private pongTimer;
|
|
26
|
-
constructor(ws: WebSocket);
|
|
24
|
+
constructor(sessionClaims: SessionClaims, ws: TransportHandle);
|
|
27
25
|
close(): void;
|
|
28
|
-
private startPing;
|
|
29
|
-
private ping;
|
|
30
|
-
private stopTimers;
|
|
31
26
|
}
|
|
32
27
|
//#endregion
|
|
33
28
|
//#region src/core/BananalinkConnection.d.ts
|
|
@@ -66,7 +61,7 @@ declare class BananalinkClient {
|
|
|
66
61
|
constructor(config: {
|
|
67
62
|
apiUrl?: string;
|
|
68
63
|
wsUrl?: string;
|
|
69
|
-
dapp: Dapp;
|
|
64
|
+
dapp: Dapp$1;
|
|
70
65
|
});
|
|
71
66
|
connect(opts?: {
|
|
72
67
|
jwt: string;
|
|
@@ -109,5 +104,5 @@ declare class InvalidConnectionStateError extends BananalinkError {
|
|
|
109
104
|
//#region src/utils/displayBananalinkQR.d.ts
|
|
110
105
|
declare function displayBananalinkQR(url: string): Promise<string>;
|
|
111
106
|
//#endregion
|
|
112
|
-
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
107
|
+
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
113
108
|
//# 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/
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/SessionClaims.ts","../src/core/BananalinkSession.ts","../src/core/BananalinkConnection.ts","../src/core/BananalinkClient.ts","../src/errors/BananalinkError.ts","../src/errors/ConnectionRejectedError.ts","../src/errors/ConnectionTimeoutError.ts","../src/errors/DappApiError.ts","../src/errors/InvalidConfigError.ts","../src/errors/InvalidConnectionStateError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":";;;KAAY,UAAA;EAAe,OAAA;EAAiB,MAAA;EAAgB,OAAA;EAAiB,SAAA;EAAmB,GAAA;AAAA;;;KCApF,aAAA;EAAkB,OAAA;EAAiB,OAAA;EAAiB,SAAA;EAAmB,WAAA;AAAA;;;cCGtE,iBAAA;EAAA,SAEO,aAAA,EAAe,aAAA;EAAA,iBACd,EAAA;cADD,aAAA,EAAe,aAAA,EACd,EAAA,EAAI,eAAA;EAGhB,KAAA,CAAA;AAAA;;;KCKJ,iBAAA;EAAsB,UAAA,EAAY,UAAA;EAAY,MAAA;AAAA;AAAA,cAEtC,oBAAA;EAAA,wBACa,mBAAA;EAAA,iBACP,KAAA;EAAA,iBACA,GAAA;EAAA,iBACA,MAAA;EAAA,iBACA,cAAA;EAAA,iBACA,KAAA;cAEL,IAAA;IACV,KAAA;IACA,GAAA,EAAK,iBAAA;IACL,MAAA;IACA,cAAA;IACA,KAAA;EAAA;EASW,UAAA,CAAA,GAAc,OAAA,CAAQ,iBAAA;EAAA,IAqFxB,aAAA,CAAA;EAAA,IASA,UAAA,CAAA;EAAA,eAKI,IAAA;AAAA;;;cChIJ,gBAAA;EAAA,iBACM,MAAA;EAAA,iBACA,KAAA;EAAA,iBACA,IAAA;EAAA,iBACA,IAAA;EAAA,wBACO,0BAAA;EAAA,wBACA,yBAAA;cAEZ,MAAA;IACV,MAAA;IACA,KAAA;IACA,IAAA,EAAM,MAAA;EAAA;EAcK,OAAA,CAAQ,IAAA;IAAS,GAAA;EAAA,IAAgB,OAAA,CAAQ,oBAAA;EAAA,QAsBxC,aAAA;EAAA,QAIA,mBAAA;AAAA;;;cC5DH,eAAA,SAAwB,KAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCC5B,uBAAA,SAAgC,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCAhC,sBAAA,SAA+B,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCA/B,YAAA,SAAqB,eAAA;EAAA,SAGd,UAAA;cADhB,OAAA,UACgB,UAAA;AAAA;;;cCHP,kBAAA,SAA2B,eAAA;cAC1B,OAAA;AAAA;;;cCDD,2BAAA,SAAoC,eAAA;cACnC,OAAA;AAAA;;;iBCCQ,mBAAA,CAAoB,GAAA,WAAc,OAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,33 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
dappId: string;
|
|
4
|
-
dappName: string;
|
|
5
|
-
dappDescription?: string;
|
|
6
|
-
dappStatement: string;
|
|
7
|
-
domain: string;
|
|
8
|
-
uri: string;
|
|
9
|
-
icons?: string[];
|
|
10
|
-
chainId?: number;
|
|
11
|
-
};
|
|
12
|
-
//#endregion
|
|
1
|
+
import { Dapp, Dapp as Dapp$1, TransportHandle } from "@bananalink-test/sdk-core";
|
|
2
|
+
|
|
13
3
|
//#region src/types/JwtPayload.d.ts
|
|
14
4
|
type JwtPayload = {
|
|
15
5
|
address: string;
|
|
16
6
|
dappId: string;
|
|
7
|
+
message: string;
|
|
8
|
+
signature: string;
|
|
9
|
+
jwt: string;
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/types/SessionClaims.d.ts
|
|
13
|
+
type SessionClaims = {
|
|
14
|
+
address: string;
|
|
15
|
+
message: string;
|
|
16
|
+
signature: string;
|
|
17
|
+
accessToken: string;
|
|
17
18
|
};
|
|
18
19
|
//#endregion
|
|
19
20
|
//#region src/core/BananalinkSession.d.ts
|
|
20
21
|
declare class BananalinkSession {
|
|
22
|
+
readonly sessionClaims: SessionClaims;
|
|
21
23
|
private readonly ws;
|
|
22
|
-
|
|
23
|
-
private static readonly PONG_TIMEOUT_MILLIS;
|
|
24
|
-
private pingTimer;
|
|
25
|
-
private pongTimer;
|
|
26
|
-
constructor(ws: WebSocket);
|
|
24
|
+
constructor(sessionClaims: SessionClaims, ws: TransportHandle);
|
|
27
25
|
close(): void;
|
|
28
|
-
private startPing;
|
|
29
|
-
private ping;
|
|
30
|
-
private stopTimers;
|
|
31
26
|
}
|
|
32
27
|
//#endregion
|
|
33
28
|
//#region src/core/BananalinkConnection.d.ts
|
|
@@ -66,7 +61,7 @@ declare class BananalinkClient {
|
|
|
66
61
|
constructor(config: {
|
|
67
62
|
apiUrl?: string;
|
|
68
63
|
wsUrl?: string;
|
|
69
|
-
dapp: Dapp;
|
|
64
|
+
dapp: Dapp$1;
|
|
70
65
|
});
|
|
71
66
|
connect(opts?: {
|
|
72
67
|
jwt: string;
|
|
@@ -109,5 +104,5 @@ declare class InvalidConnectionStateError extends BananalinkError {
|
|
|
109
104
|
//#region src/utils/displayBananalinkQR.d.ts
|
|
110
105
|
declare function displayBananalinkQR(url: string): Promise<string>;
|
|
111
106
|
//#endregion
|
|
112
|
-
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
107
|
+
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
113
108
|
//# 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/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/SessionClaims.ts","../src/core/BananalinkSession.ts","../src/core/BananalinkConnection.ts","../src/core/BananalinkClient.ts","../src/errors/BananalinkError.ts","../src/errors/ConnectionRejectedError.ts","../src/errors/ConnectionTimeoutError.ts","../src/errors/DappApiError.ts","../src/errors/InvalidConfigError.ts","../src/errors/InvalidConnectionStateError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":";;;KAAY,UAAA;EAAe,OAAA;EAAiB,MAAA;EAAgB,OAAA;EAAiB,SAAA;EAAmB,GAAA;AAAA;;;KCApF,aAAA;EAAkB,OAAA;EAAiB,OAAA;EAAiB,SAAA;EAAmB,WAAA;AAAA;;;cCGtE,iBAAA;EAAA,SAEO,aAAA,EAAe,aAAA;EAAA,iBACd,EAAA;cADD,aAAA,EAAe,aAAA,EACd,EAAA,EAAI,eAAA;EAGhB,KAAA,CAAA;AAAA;;;KCKJ,iBAAA;EAAsB,UAAA,EAAY,UAAA;EAAY,MAAA;AAAA;AAAA,cAEtC,oBAAA;EAAA,wBACa,mBAAA;EAAA,iBACP,KAAA;EAAA,iBACA,GAAA;EAAA,iBACA,MAAA;EAAA,iBACA,cAAA;EAAA,iBACA,KAAA;cAEL,IAAA;IACV,KAAA;IACA,GAAA,EAAK,iBAAA;IACL,MAAA;IACA,cAAA;IACA,KAAA;EAAA;EASW,UAAA,CAAA,GAAc,OAAA,CAAQ,iBAAA;EAAA,IAqFxB,aAAA,CAAA;EAAA,IASA,UAAA,CAAA;EAAA,eAKI,IAAA;AAAA;;;cChIJ,gBAAA;EAAA,iBACM,MAAA;EAAA,iBACA,KAAA;EAAA,iBACA,IAAA;EAAA,iBACA,IAAA;EAAA,wBACO,0BAAA;EAAA,wBACA,yBAAA;cAEZ,MAAA;IACV,MAAA;IACA,KAAA;IACA,IAAA,EAAM,MAAA;EAAA;EAcK,OAAA,CAAQ,IAAA;IAAS,GAAA;EAAA,IAAgB,OAAA,CAAQ,oBAAA;EAAA,QAsBxC,aAAA;EAAA,QAIA,mBAAA;AAAA;;;cC5DH,eAAA,SAAwB,KAAA;cACvB,OAAA,UAAiB,OAAA,GAAU,YAAA;AAAA;;;cCC5B,uBAAA,SAAgC,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCAhC,sBAAA,SAA+B,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCA/B,YAAA,SAAqB,eAAA;EAAA,SAGd,UAAA;cADhB,OAAA,UACgB,UAAA;AAAA;;;cCHP,kBAAA,SAA2B,eAAA;cAC1B,OAAA;AAAA;;;cCDD,2BAAA,SAAoC,eAAA;cACnC,OAAA;AAAA;;;iBCCQ,mBAAA,CAAoB,GAAA,WAAc,OAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
2
|
+
import { connectWebSocket, isAuthorizedMessage, isRejectedMessage } from "@bananalink-test/sdk-core";
|
|
2
3
|
import QRCode from "qrcode";
|
|
3
4
|
|
|
4
5
|
//#region src/errors/BananalinkError.ts
|
|
@@ -62,11 +63,19 @@ async function verifyJwt(jwt, jwks) {
|
|
|
62
63
|
const { payload } = await jwtVerify(jwt, jwks, {
|
|
63
64
|
algorithms: ["ES256"],
|
|
64
65
|
issuer: "bananalink",
|
|
65
|
-
requiredClaims: [
|
|
66
|
+
requiredClaims: [
|
|
67
|
+
"sub",
|
|
68
|
+
"aud",
|
|
69
|
+
"message",
|
|
70
|
+
"signature"
|
|
71
|
+
]
|
|
66
72
|
});
|
|
67
73
|
return {
|
|
68
74
|
address: payload.sub,
|
|
69
|
-
dappId: Array.isArray(payload.aud) ? payload.aud[0] : payload.aud
|
|
75
|
+
dappId: Array.isArray(payload.aud) ? payload.aud[0] : payload.aud,
|
|
76
|
+
message: payload.message,
|
|
77
|
+
signature: payload.signature,
|
|
78
|
+
jwt
|
|
70
79
|
};
|
|
71
80
|
} catch {
|
|
72
81
|
return null;
|
|
@@ -113,43 +122,14 @@ var InvalidConnectionStateError = class extends BananalinkError {
|
|
|
113
122
|
|
|
114
123
|
//#endregion
|
|
115
124
|
//#region src/core/BananalinkSession.ts
|
|
116
|
-
var BananalinkSession = class
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
pingTimer = null;
|
|
120
|
-
pongTimer = null;
|
|
121
|
-
constructor(ws) {
|
|
125
|
+
var BananalinkSession = class {
|
|
126
|
+
constructor(sessionClaims, ws) {
|
|
127
|
+
this.sessionClaims = sessionClaims;
|
|
122
128
|
this.ws = ws;
|
|
123
|
-
this.ws.addEventListener("message", (event) => {
|
|
124
|
-
const { type } = JSON.parse(event.data);
|
|
125
|
-
if (type === "pong" && this.pongTimer) {
|
|
126
|
-
clearTimeout(this.pongTimer);
|
|
127
|
-
this.pongTimer = null;
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
this.startPing();
|
|
131
129
|
}
|
|
132
130
|
close() {
|
|
133
|
-
this.stopTimers();
|
|
134
131
|
this.ws.close();
|
|
135
132
|
}
|
|
136
|
-
startPing() {
|
|
137
|
-
this.pingTimer = setInterval(() => this.ping(), BananalinkSession.PING_INTERVAL_MILLIS);
|
|
138
|
-
}
|
|
139
|
-
ping() {
|
|
140
|
-
if (this.ws.readyState !== WebSocket.OPEN) {
|
|
141
|
-
this.close();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
this.ws.send(JSON.stringify({ type: "ping" }));
|
|
145
|
-
this.pongTimer = setTimeout(() => this.close(), BananalinkSession.PONG_TIMEOUT_MILLIS);
|
|
146
|
-
}
|
|
147
|
-
stopTimers() {
|
|
148
|
-
if (this.pingTimer !== null) clearInterval(this.pingTimer);
|
|
149
|
-
if (this.pongTimer !== null) clearTimeout(this.pongTimer);
|
|
150
|
-
this.pingTimer = null;
|
|
151
|
-
this.pongTimer = null;
|
|
152
|
-
}
|
|
153
133
|
};
|
|
154
134
|
|
|
155
135
|
//#endregion
|
|
@@ -172,27 +152,55 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
172
152
|
if (this.jwt) throw new BananalinkError("Recovering a lost dapp connection is not yet implemented");
|
|
173
153
|
const dappInstanceId = this.dappInstanceId;
|
|
174
154
|
if (!dappInstanceId) throw new InvalidConnectionStateError("Cannot get session without authorizing the dapp instance or providing a valid JWT");
|
|
175
|
-
|
|
176
|
-
|
|
155
|
+
let ws;
|
|
156
|
+
try {
|
|
157
|
+
ws = await connectWebSocket({
|
|
158
|
+
url: `${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`,
|
|
159
|
+
pingIntervalMs: 3e4,
|
|
160
|
+
pongTimeoutMs: 5e3
|
|
161
|
+
});
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
|
|
164
|
+
}
|
|
165
|
+
return await new Promise((resolve, reject) => {
|
|
166
|
+
let settled = false;
|
|
177
167
|
const authTimeout = setTimeout(() => {
|
|
178
|
-
|
|
179
|
-
reject(new ConnectionTimeoutError());
|
|
168
|
+
settleReject(new ConnectionTimeoutError());
|
|
180
169
|
}, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
ws
|
|
190
|
-
|
|
170
|
+
const stopListeningToMessages = ws.onMessage((payload) => {
|
|
171
|
+
if (isAuthorizedMessage(payload)) {
|
|
172
|
+
BananalinkConnection.bind(ws, payload.accessToken);
|
|
173
|
+
settleResolve(new BananalinkSession({
|
|
174
|
+
address: payload.address,
|
|
175
|
+
message: payload.message,
|
|
176
|
+
signature: payload.signature,
|
|
177
|
+
accessToken: payload.accessToken
|
|
178
|
+
}, ws));
|
|
179
|
+
return;
|
|
191
180
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
ws.
|
|
195
|
-
|
|
181
|
+
if (isRejectedMessage(payload)) settleReject(new ConnectionRejectedError());
|
|
182
|
+
});
|
|
183
|
+
const stopListeningToClose = ws.onClose(() => {
|
|
184
|
+
settleReject(new BananalinkError("Connection closed before authorization completed"));
|
|
185
|
+
});
|
|
186
|
+
function cleanup() {
|
|
187
|
+
clearTimeout(authTimeout);
|
|
188
|
+
stopListeningToMessages();
|
|
189
|
+
stopListeningToClose();
|
|
190
|
+
}
|
|
191
|
+
function settleResolve(session) {
|
|
192
|
+
if (settled) return;
|
|
193
|
+
settled = true;
|
|
194
|
+
cleanup();
|
|
195
|
+
resolve(session);
|
|
196
|
+
}
|
|
197
|
+
function settleReject(error) {
|
|
198
|
+
if (settled) return;
|
|
199
|
+
settled = true;
|
|
200
|
+
cleanup();
|
|
201
|
+
ws.close();
|
|
202
|
+
reject(error);
|
|
203
|
+
}
|
|
196
204
|
});
|
|
197
205
|
}
|
|
198
206
|
get connectionUrl() {
|
|
@@ -205,10 +213,11 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
205
213
|
return this.jwt !== void 0;
|
|
206
214
|
}
|
|
207
215
|
static bind(ws, jwt) {
|
|
208
|
-
|
|
216
|
+
const bindMessage = {
|
|
209
217
|
type: "bind",
|
|
210
218
|
jwt
|
|
211
|
-
}
|
|
219
|
+
};
|
|
220
|
+
ws.send(bindMessage);
|
|
212
221
|
}
|
|
213
222
|
};
|
|
214
223
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/BananalinkError.ts","../src/errors/DappApiError.ts","../src/api/createDappInstance.ts","../src/errors/InvalidConfigError.ts","../src/jwt/createBananalinkJwks.ts","../src/jwt/verifyJwt.ts","../src/utils/isValidUrl.ts","../src/errors/ConnectionRejectedError.ts","../src/errors/ConnectionTimeoutError.ts","../src/errors/InvalidConnectionStateError.ts","../src/core/BananalinkSession.ts","../src/core/BananalinkConnection.ts","../src/core/BananalinkClient.ts","../src/errors/InvalidUrlError.ts","../src/utils/displayBananalinkQR.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 DappApiError extends BananalinkError {\n constructor(\n message: string,\n public readonly statusCode: number,\n ) {\n super(message);\n this.name = 'DappApiError';\n }\n}\n","import { DappApiError } from '../errors/DappApiError.js';\nimport type { CreateDappInstanceResponse } from '../types/CreateDappInstanceResponse.js';\nimport type { Dapp } from '../types/Dapp.js';\n\nfunction isCreateDappInstanceResponse(body: unknown): body is CreateDappInstanceResponse {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'dappInstanceId' in body &&\n typeof body.dappInstanceId === 'string' &&\n 'nonce' in body &&\n typeof body.nonce === 'string'\n );\n}\n\nexport async function createDappInstance(apiUrl: string, params: Dapp): Promise<CreateDappInstanceResponse> {\n const response = await fetch(`${apiUrl}/dapps`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify(params),\n });\n if (response.ok) {\n const body = await response.json().catch(() => {\n throw new DappApiError('Invalid JSON response from bananalink API', response.status);\n });\n if (!isCreateDappInstanceResponse(body)) {\n throw new DappApiError('Unexpected response shape from bananalink API', response.status);\n }\n return body;\n }\n throw new DappApiError('Failed to create dapp instance', response.status);\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidConfigError extends BananalinkError {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidConfigError';\n }\n}\n","import { createRemoteJWKSet, type JWTVerifyGetKey } from 'jose';\n\nexport function createBananalinkJwks(apiUrl: string): JWTVerifyGetKey {\n const url = new URL(`${apiUrl}/.well-known/jwks.json`);\n return createRemoteJWKSet(url);\n}\n","import { type JWTVerifyGetKey, jwtVerify } from 'jose';\nimport type { JwtPayload } from '../types/JwtPayload.js';\n\nexport async function verifyJwt(jwt: string, jwks: JWTVerifyGetKey): Promise<JwtPayload | null> {\n try {\n const { payload } = await jwtVerify(jwt, jwks, {\n algorithms: ['ES256'],\n issuer: 'bananalink',\n requiredClaims: ['sub', 'aud'],\n });\n return {\n address: payload.sub as string,\n dappId: Array.isArray(payload.aud) ? payload.aud[0] : (payload.aud as string),\n };\n } catch {\n return null;\n }\n}\n","export function isValidUrl(url: string, protocols: string[]): boolean {\n try {\n const parsed = new URL(url);\n return protocols.includes(parsed.protocol.replace(':', ''));\n } catch {\n return false;\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionRejectedError extends BananalinkError {\n constructor() {\n super('Dapp connection attempt was rejected by the wallet');\n this.name = 'ConnectionRejectedError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionTimeoutError extends BananalinkError {\n constructor() {\n super('Dapp connection attempt timed out');\n this.name = 'ConnectionTimeoutError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidConnectionStateError extends BananalinkError {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidConnectionStateError';\n }\n}\n","export class BananalinkSession {\n private static readonly PING_INTERVAL_MILLIS = 30 * 1000;\n private static readonly PONG_TIMEOUT_MILLIS = 5 * 1000;\n\n private pingTimer: ReturnType<typeof setInterval> | null = null;\n private pongTimer: ReturnType<typeof setTimeout> | null = null;\n\n constructor(private readonly ws: WebSocket) {\n this.ws.addEventListener('message', (event) => {\n const { type } = JSON.parse(event.data);\n if (type === 'pong' && this.pongTimer) {\n clearTimeout(this.pongTimer);\n this.pongTimer = null;\n }\n });\n this.startPing();\n }\n\n public close(): void {\n this.stopTimers();\n this.ws.close();\n }\n\n private startPing(): void {\n this.pingTimer = setInterval(() => this.ping(), BananalinkSession.PING_INTERVAL_MILLIS);\n }\n\n private ping(): void {\n if (this.ws.readyState !== WebSocket.OPEN) {\n this.close();\n return;\n }\n this.ws.send(JSON.stringify({ type: 'ping' }));\n this.pongTimer = setTimeout(() => this.close(), BananalinkSession.PONG_TIMEOUT_MILLIS);\n }\n\n private stopTimers(): void {\n if (this.pingTimer !== null) {\n clearInterval(this.pingTimer);\n }\n if (this.pongTimer !== null) {\n clearTimeout(this.pongTimer);\n }\n this.pingTimer = null;\n this.pongTimer = null;\n }\n}\n","import { BananalinkError } from '../errors/BananalinkError.js';\nimport { ConnectionRejectedError } from '../errors/ConnectionRejectedError.js';\nimport { ConnectionTimeoutError } from '../errors/ConnectionTimeoutError.js';\nimport { InvalidConnectionStateError } from '../errors/InvalidConnectionStateError.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport { BananalinkSession } from './BananalinkSession.js';\n\ntype BananalinkDappJwt = { jwtPayload: JwtPayload; rawJwt: string } | undefined;\n\nexport class BananalinkConnection {\n private static readonly AUTH_TIMEOUT_MILLIS = 60 * 1000;\n private readonly wsUrl: string;\n private readonly jwt: BananalinkDappJwt;\n private readonly dappId: string;\n private readonly dappInstanceId: string | null;\n private readonly nonce: string | null;\n\n constructor(opts: {\n wsUrl: string;\n jwt: BananalinkDappJwt;\n dappId: string;\n dappInstanceId: string | null;\n nonce: string | null;\n }) {\n this.wsUrl = opts.wsUrl;\n this.jwt = opts.jwt;\n this.dappId = opts.dappId;\n this.dappInstanceId = opts.dappInstanceId;\n this.nonce = opts.nonce;\n }\n\n public async getSession(): Promise<BananalinkSession> {\n if (this.jwt) {\n throw new BananalinkError('Recovering a lost dapp connection is not yet implemented');\n }\n\n const dappInstanceId = this.dappInstanceId;\n if (!dappInstanceId) {\n throw new InvalidConnectionStateError(\n 'Cannot get session without authorizing the dapp instance or providing a valid JWT',\n );\n }\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(`${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`);\n\n const authTimeout = setTimeout(() => {\n ws.close();\n reject(new ConnectionTimeoutError());\n }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);\n\n const onMessage = (event: MessageEvent): void => {\n const msg = JSON.parse(event.data);\n\n // cheating, but we don't expect any other type of message at this point\n clearTimeout(authTimeout);\n ws.removeEventListener('message', onMessage);\n\n if (msg.type === 'authorized') {\n BananalinkConnection.bind(ws, msg.accessToken);\n resolve(new BananalinkSession(ws));\n } else if (msg.type === 'rejected') {\n ws.close();\n reject(new ConnectionRejectedError());\n }\n };\n\n // TODO: abstract in a transport interface\n ws.addEventListener('message', onMessage);\n ws.addEventListener('error', () => {});\n ws.addEventListener('close', () => {});\n });\n }\n\n public get connectionUrl(): string {\n if (!this.dappInstanceId || !this.nonce) {\n throw new InvalidConnectionStateError('Cannot get connection URL without a dapp instance');\n }\n const dappInstanceId = this.dappInstanceId;\n const nonce = this.nonce;\n return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;\n }\n\n public get authorized(): boolean {\n // TODO: handle revocation\n return this.jwt !== undefined;\n }\n\n private static bind(ws: WebSocket, jwt: string) {\n ws.send(JSON.stringify({ type: 'bind', jwt: jwt }));\n }\n}\n","import { createDappInstance } from '../api/createDappInstance.js';\nimport { InvalidConfigError } from '../errors/InvalidConfigError.js';\nimport { createBananalinkJwks } from '../jwt/createBananalinkJwks.js';\nimport { verifyJwt } from '../jwt/verifyJwt.js';\nimport type { CreateDappInstanceResponse } from '../types/CreateDappInstanceResponse.js';\nimport type { Dapp } from '../types/Dapp.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport { isValidUrl } from '../utils/isValidUrl.js';\nimport { BananalinkConnection } from './BananalinkConnection.js';\n\nexport class BananalinkClient {\n private readonly apiUrl: string;\n private readonly wsUrl: string;\n private readonly jwks: ReturnType<typeof createBananalinkJwks>;\n private readonly dapp: Dapp;\n private static readonly DEFAULT_BANANALINK_API_URL = 'https://tfr9p2mn4i.execute-api.us-east-2.amazonaws.com';\n private static readonly DEFAULT_BANANALINK_WS_URL = 'wss://yb47qomkt3.execute-api.us-east-2.amazonaws.com/v1';\n\n constructor(config: {\n apiUrl?: string;\n wsUrl?: string;\n dapp: Dapp;\n }) {\n this.apiUrl = config.apiUrl ?? BananalinkClient.DEFAULT_BANANALINK_API_URL;\n this.wsUrl = config.wsUrl ?? BananalinkClient.DEFAULT_BANANALINK_WS_URL;\n if (!isValidUrl(this.apiUrl, ['http', 'https'])) {\n throw new InvalidConfigError(`Invalid apiUrl: \"${this.apiUrl}\". Must use http or https.`);\n }\n if (!isValidUrl(this.wsUrl, ['ws', 'wss'])) {\n throw new InvalidConfigError(`Invalid wsUrl: \"${this.wsUrl}\". Must use ws or wss.`);\n }\n this.dapp = config.dapp;\n this.jwks = createBananalinkJwks(this.apiUrl);\n }\n\n public async connect(opts?: { jwt: string }): Promise<BananalinkConnection> {\n const rawJwt = opts?.jwt;\n const jwtPayload = await (rawJwt ? this.getJwtPayload(rawJwt) : Promise.resolve(null));\n if (!jwtPayload || !rawJwt) {\n const { dappInstanceId, nonce } = await this.connectDappInstance();\n return new BananalinkConnection({\n jwt: undefined,\n dappId: this.dapp.dappId,\n dappInstanceId,\n nonce,\n wsUrl: this.wsUrl,\n });\n }\n return new BananalinkConnection({\n jwt: { jwtPayload, rawJwt },\n dappId: this.dapp.dappId,\n dappInstanceId: null,\n nonce: null,\n wsUrl: this.wsUrl,\n });\n }\n\n private async getJwtPayload(rawJwt: string): Promise<JwtPayload | null> {\n return await verifyJwt(rawJwt, this.jwks);\n }\n\n private async connectDappInstance(): Promise<CreateDappInstanceResponse> {\n return await createDappInstance(this.apiUrl, this.dapp);\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidUrlError extends BananalinkError {\n constructor(public readonly url: string) {\n super(`${url} is not a valid url`);\n }\n}\n","import QRCode from 'qrcode';\nimport { InvalidUrlError } from '../errors/InvalidUrlError.js';\nimport { isValidUrl } from './isValidUrl.js';\n\nexport async function displayBananalinkQR(url: string): Promise<string> {\n if (!isValidUrl(url, ['bananalink'])) {\n throw new InvalidUrlError(url);\n }\n return QRCode.toDataURL(url);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/BananalinkError.ts","../src/errors/DappApiError.ts","../src/api/createDappInstance.ts","../src/errors/InvalidConfigError.ts","../src/jwt/createBananalinkJwks.ts","../src/jwt/verifyJwt.ts","../src/utils/isValidUrl.ts","../src/errors/ConnectionRejectedError.ts","../src/errors/ConnectionTimeoutError.ts","../src/errors/InvalidConnectionStateError.ts","../src/core/BananalinkSession.ts","../src/core/BananalinkConnection.ts","../src/core/BananalinkClient.ts","../src/errors/InvalidUrlError.ts","../src/utils/displayBananalinkQR.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 DappApiError extends BananalinkError {\n constructor(\n message: string,\n public readonly statusCode: number,\n ) {\n super(message);\n this.name = 'DappApiError';\n }\n}\n","import type { CreateDappInstanceResponse, Dapp } from '@bananalink-test/sdk-core';\nimport { DappApiError } from '../errors/DappApiError.js';\n\nfunction isCreateDappInstanceResponse(body: unknown): body is CreateDappInstanceResponse {\n return (\n typeof body === 'object' &&\n body !== null &&\n 'dappInstanceId' in body &&\n typeof body.dappInstanceId === 'string' &&\n 'nonce' in body &&\n typeof body.nonce === 'string'\n );\n}\n\nexport async function createDappInstance(apiUrl: string, params: Dapp): Promise<CreateDappInstanceResponse> {\n const response = await fetch(`${apiUrl}/dapps`, {\n headers: {\n 'Content-Type': 'application/json',\n },\n method: 'POST',\n body: JSON.stringify(params),\n });\n if (response.ok) {\n const body = await response.json().catch(() => {\n throw new DappApiError('Invalid JSON response from bananalink API', response.status);\n });\n if (!isCreateDappInstanceResponse(body)) {\n throw new DappApiError('Unexpected response shape from bananalink API', response.status);\n }\n return body;\n }\n throw new DappApiError('Failed to create dapp instance', response.status);\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidConfigError extends BananalinkError {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidConfigError';\n }\n}\n","import { createRemoteJWKSet, type JWTVerifyGetKey } from 'jose';\n\nexport function createBananalinkJwks(apiUrl: string): JWTVerifyGetKey {\n const url = new URL(`${apiUrl}/.well-known/jwks.json`);\n return createRemoteJWKSet(url);\n}\n","import { type JWTVerifyGetKey, jwtVerify } from 'jose';\nimport type { JwtPayload } from '../types/JwtPayload.js';\n\nexport async function verifyJwt(jwt: string, jwks: JWTVerifyGetKey): Promise<JwtPayload | null> {\n try {\n const { payload } = await jwtVerify<{ message: string; signature: string }>(jwt, jwks, {\n algorithms: ['ES256'],\n issuer: 'bananalink',\n requiredClaims: ['sub', 'aud', 'message', 'signature'],\n });\n return {\n address: payload.sub as string,\n dappId: Array.isArray(payload.aud) ? payload.aud[0] : (payload.aud as string),\n message: payload.message,\n signature: payload.signature,\n jwt,\n };\n } catch {\n return null;\n }\n}\n","export function isValidUrl(url: string, protocols: string[]): boolean {\n try {\n const parsed = new URL(url);\n return protocols.includes(parsed.protocol.replace(':', ''));\n } catch {\n return false;\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionRejectedError extends BananalinkError {\n constructor() {\n super('Dapp connection attempt was rejected by the wallet');\n this.name = 'ConnectionRejectedError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class ConnectionTimeoutError extends BananalinkError {\n constructor() {\n super('Dapp connection attempt timed out');\n this.name = 'ConnectionTimeoutError';\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidConnectionStateError extends BananalinkError {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidConnectionStateError';\n }\n}\n","import type { TransportHandle } from '@bananalink-test/sdk-core';\nimport type { SessionClaims } from '../types/SessionClaims.js';\n\nexport class BananalinkSession {\n constructor(\n public readonly sessionClaims: SessionClaims,\n private readonly ws: TransportHandle,\n ) {}\n\n public close(): void {\n this.ws.close();\n }\n}\n","import {\n type BindMessage,\n connectWebSocket,\n isAuthorizedMessage,\n isRejectedMessage,\n type TransportHandle,\n} from '@bananalink-test/sdk-core';\nimport { BananalinkError } from '../errors/BananalinkError.js';\nimport { ConnectionRejectedError } from '../errors/ConnectionRejectedError.js';\nimport { ConnectionTimeoutError } from '../errors/ConnectionTimeoutError.js';\nimport { InvalidConnectionStateError } from '../errors/InvalidConnectionStateError.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport { BananalinkSession } from './BananalinkSession.js';\n\ntype BananalinkDappJwt = { jwtPayload: JwtPayload; rawJwt: string } | undefined;\n\nexport class BananalinkConnection {\n private static readonly AUTH_TIMEOUT_MILLIS = 60 * 1000;\n private readonly wsUrl: string;\n private readonly jwt: BananalinkDappJwt;\n private readonly dappId: string;\n private readonly dappInstanceId: string | null;\n private readonly nonce: string | null;\n\n constructor(opts: {\n wsUrl: string;\n jwt: BananalinkDappJwt;\n dappId: string;\n dappInstanceId: string | null;\n nonce: string | null;\n }) {\n this.wsUrl = opts.wsUrl;\n this.jwt = opts.jwt;\n this.dappId = opts.dappId;\n this.dappInstanceId = opts.dappInstanceId;\n this.nonce = opts.nonce;\n }\n\n public async getSession(): Promise<BananalinkSession> {\n if (this.jwt) {\n throw new BananalinkError('Recovering a lost dapp connection is not yet implemented');\n }\n\n const dappInstanceId = this.dappInstanceId;\n if (!dappInstanceId) {\n throw new InvalidConnectionStateError(\n 'Cannot get session without authorizing the dapp instance or providing a valid JWT',\n );\n }\n\n let ws: TransportHandle;\n try {\n ws = await connectWebSocket({\n url: `${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`,\n pingIntervalMs: 30_000,\n pongTimeoutMs: 5_000,\n });\n } catch (error) {\n throw new BananalinkError('Unable to establish dapp websocket connection', { cause: error });\n }\n\n return await new Promise((resolve, reject) => {\n let settled = false;\n\n const authTimeout = setTimeout(() => {\n settleReject(new ConnectionTimeoutError());\n }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);\n\n const stopListeningToMessages = ws.onMessage((payload: unknown) => {\n if (isAuthorizedMessage(payload)) {\n BananalinkConnection.bind(ws, payload.accessToken);\n settleResolve(\n new BananalinkSession(\n {\n address: payload.address,\n message: payload.message,\n signature: payload.signature,\n accessToken: payload.accessToken,\n },\n ws,\n ),\n );\n return;\n }\n\n if (isRejectedMessage(payload)) {\n settleReject(new ConnectionRejectedError());\n }\n });\n\n const stopListeningToClose = ws.onClose(() => {\n settleReject(new BananalinkError('Connection closed before authorization completed'));\n });\n\n function cleanup(): void {\n clearTimeout(authTimeout);\n stopListeningToMessages();\n stopListeningToClose();\n }\n\n function settleResolve(session: BananalinkSession): void {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n resolve(session);\n }\n\n function settleReject(error: Error): void {\n if (settled) {\n return;\n }\n\n settled = true;\n cleanup();\n ws.close();\n reject(error);\n }\n });\n }\n\n public get connectionUrl(): string {\n if (!this.dappInstanceId || !this.nonce) {\n throw new InvalidConnectionStateError('Cannot get connection URL without a dapp instance');\n }\n const dappInstanceId = this.dappInstanceId;\n const nonce = this.nonce;\n return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;\n }\n\n public get authorized(): boolean {\n // TODO: handle revocation\n return this.jwt !== undefined;\n }\n\n private static bind(ws: TransportHandle, jwt: string): void {\n const bindMessage: BindMessage = { type: 'bind', jwt };\n ws.send(bindMessage);\n }\n}\n","import type { CreateDappInstanceResponse, Dapp } from '@bananalink-test/sdk-core';\nimport { createDappInstance } from '../api/createDappInstance.js';\nimport { InvalidConfigError } from '../errors/InvalidConfigError.js';\nimport { createBananalinkJwks } from '../jwt/createBananalinkJwks.js';\nimport { verifyJwt } from '../jwt/verifyJwt.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport { isValidUrl } from '../utils/isValidUrl.js';\nimport { BananalinkConnection } from './BananalinkConnection.js';\n\nexport class BananalinkClient {\n private readonly apiUrl: string;\n private readonly wsUrl: string;\n private readonly jwks: ReturnType<typeof createBananalinkJwks>;\n private readonly dapp: Dapp;\n private static readonly DEFAULT_BANANALINK_API_URL = 'https://tfr9p2mn4i.execute-api.us-east-2.amazonaws.com';\n private static readonly DEFAULT_BANANALINK_WS_URL = 'wss://yb47qomkt3.execute-api.us-east-2.amazonaws.com/v1';\n\n constructor(config: {\n apiUrl?: string;\n wsUrl?: string;\n dapp: Dapp;\n }) {\n this.apiUrl = config.apiUrl ?? BananalinkClient.DEFAULT_BANANALINK_API_URL;\n this.wsUrl = config.wsUrl ?? BananalinkClient.DEFAULT_BANANALINK_WS_URL;\n if (!isValidUrl(this.apiUrl, ['http', 'https'])) {\n throw new InvalidConfigError(`Invalid apiUrl: \"${this.apiUrl}\". Must use http or https.`);\n }\n if (!isValidUrl(this.wsUrl, ['ws', 'wss'])) {\n throw new InvalidConfigError(`Invalid wsUrl: \"${this.wsUrl}\". Must use ws or wss.`);\n }\n this.dapp = config.dapp;\n this.jwks = createBananalinkJwks(this.apiUrl);\n }\n\n public async connect(opts?: { jwt: string }): Promise<BananalinkConnection> {\n const rawJwt = opts?.jwt;\n const jwtPayload = await (rawJwt ? this.getJwtPayload(rawJwt) : Promise.resolve(null));\n if (!jwtPayload || !rawJwt) {\n const { dappInstanceId, nonce } = await this.connectDappInstance();\n return new BananalinkConnection({\n jwt: undefined,\n dappId: this.dapp.dappId,\n dappInstanceId,\n nonce,\n wsUrl: this.wsUrl,\n });\n }\n return new BananalinkConnection({\n jwt: { jwtPayload, rawJwt },\n dappId: this.dapp.dappId,\n dappInstanceId: null,\n nonce: null,\n wsUrl: this.wsUrl,\n });\n }\n\n private async getJwtPayload(rawJwt: string): Promise<JwtPayload | null> {\n return await verifyJwt(rawJwt, this.jwks);\n }\n\n private async connectDappInstance(): Promise<CreateDappInstanceResponse> {\n return await createDappInstance(this.apiUrl, this.dapp);\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidUrlError extends BananalinkError {\n constructor(public readonly url: string) {\n super(`${url} is not a valid url`);\n }\n}\n","import QRCode from 'qrcode';\nimport { InvalidUrlError } from '../errors/InvalidUrlError.js';\nimport { isValidUrl } from './isValidUrl.js';\n\nexport async function displayBananalinkQR(url: string): Promise<string> {\n if (!isValidUrl(url, ['bananalink'])) {\n throw new InvalidUrlError(url);\n }\n return QRCode.toDataURL(url);\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananalink-test/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"jose": "^6.1.3",
|
|
36
|
-
"qrcode": "^1.5.4"
|
|
36
|
+
"qrcode": "^1.5.4",
|
|
37
|
+
"@bananalink-test/sdk-core": "0.2.0"
|
|
37
38
|
},
|
|
38
39
|
"scripts": {
|
|
39
40
|
"build": "tsdown",
|