@bananalink-test/client 0.1.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 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
 
@@ -150,44 +151,14 @@ var InvalidConnectionStateError = class extends BananalinkError {
150
151
 
151
152
  //#endregion
152
153
  //#region src/core/BananalinkSession.ts
153
- var BananalinkSession = class BananalinkSession {
154
- static PING_INTERVAL_MILLIS = 30 * 1e3;
155
- static PONG_TIMEOUT_MILLIS = 5 * 1e3;
156
- pingTimer = null;
157
- pongTimer = null;
154
+ var BananalinkSession = class {
158
155
  constructor(sessionClaims, ws) {
159
156
  this.sessionClaims = sessionClaims;
160
157
  this.ws = ws;
161
- this.ws.addEventListener("message", (event) => {
162
- const { type } = JSON.parse(event.data);
163
- if (type === "pong" && this.pongTimer) {
164
- clearTimeout(this.pongTimer);
165
- this.pongTimer = null;
166
- }
167
- });
168
- this.startPing();
169
158
  }
170
159
  close() {
171
- this.stopTimers();
172
160
  this.ws.close();
173
161
  }
174
- startPing() {
175
- this.pingTimer = setInterval(() => this.ping(), BananalinkSession.PING_INTERVAL_MILLIS);
176
- }
177
- ping() {
178
- if (this.ws.readyState !== WebSocket.OPEN) {
179
- this.close();
180
- return;
181
- }
182
- this.ws.send(JSON.stringify({ type: "ping" }));
183
- this.pongTimer = setTimeout(() => this.close(), BananalinkSession.PONG_TIMEOUT_MILLIS);
184
- }
185
- stopTimers() {
186
- if (this.pingTimer !== null) clearInterval(this.pingTimer);
187
- if (this.pongTimer !== null) clearTimeout(this.pongTimer);
188
- this.pingTimer = null;
189
- this.pongTimer = null;
190
- }
191
162
  };
192
163
 
193
164
  //#endregion
@@ -210,32 +181,55 @@ var BananalinkConnection = class BananalinkConnection {
210
181
  if (this.jwt) throw new BananalinkError("Recovering a lost dapp connection is not yet implemented");
211
182
  const dappInstanceId = this.dappInstanceId;
212
183
  if (!dappInstanceId) throw new InvalidConnectionStateError("Cannot get session without authorizing the dapp instance or providing a valid JWT");
213
- return new Promise((resolve, reject) => {
214
- const ws = new WebSocket(`${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`);
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;
215
196
  const authTimeout = setTimeout(() => {
216
- ws.close();
217
- reject(new ConnectionTimeoutError());
197
+ settleReject(new ConnectionTimeoutError());
218
198
  }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
219
- const onMessage = (event) => {
220
- const msg = JSON.parse(event.data);
221
- clearTimeout(authTimeout);
222
- ws.removeEventListener("message", onMessage);
223
- if (BananalinkConnection.isAuthorizedMessage(msg)) {
224
- BananalinkConnection.bind(ws, msg.accessToken);
225
- resolve(new BananalinkSession({
226
- address: msg.address,
227
- message: msg.message,
228
- signature: msg.signature,
229
- accessToken: msg.accessToken
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
230
207
  }, ws));
231
- } else if (BananalinkConnection.isRejectedMessage(msg)) {
232
- ws.close();
233
- reject(new ConnectionRejectedError());
208
+ return;
234
209
  }
235
- };
236
- ws.addEventListener("message", onMessage);
237
- ws.addEventListener("error", () => {});
238
- ws.addEventListener("close", () => {});
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
+ }
239
233
  });
240
234
  }
241
235
  get connectionUrl() {
@@ -248,16 +242,11 @@ var BananalinkConnection = class BananalinkConnection {
248
242
  return this.jwt !== void 0;
249
243
  }
250
244
  static bind(ws, jwt) {
251
- ws.send(JSON.stringify({
245
+ const bindMessage = {
252
246
  type: "bind",
253
247
  jwt
254
- }));
255
- }
256
- static isAuthorizedMessage(payload) {
257
- return typeof payload === "object" && payload !== null && "type" in payload && payload.type === "authorized" && "accessToken" in payload && typeof payload.accessToken === "string" && "refreshToken" in payload && typeof payload.refreshToken === "string" && "message" in payload && typeof payload.message === "string" && "signature" in payload && typeof payload.signature === "string" && "address" in payload && typeof payload.address === "string";
258
- }
259
- static isRejectedMessage(payload) {
260
- return typeof payload === "object" && payload !== null && "type" in payload && payload.type === "rejected";
248
+ };
249
+ ws.send(bindMessage);
261
250
  }
262
251
  };
263
252
 
package/dist/index.d.cts CHANGED
@@ -1,15 +1,5 @@
1
- //#region src/types/Dapp.d.ts
2
- type Dapp = {
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;
@@ -31,15 +21,8 @@ type SessionClaims = {
31
21
  declare class BananalinkSession {
32
22
  readonly sessionClaims: SessionClaims;
33
23
  private readonly ws;
34
- private static readonly PING_INTERVAL_MILLIS;
35
- private static readonly PONG_TIMEOUT_MILLIS;
36
- private pingTimer;
37
- private pongTimer;
38
- constructor(sessionClaims: SessionClaims, ws: WebSocket);
24
+ constructor(sessionClaims: SessionClaims, ws: TransportHandle);
39
25
  close(): void;
40
- private startPing;
41
- private ping;
42
- private stopTimers;
43
26
  }
44
27
  //#endregion
45
28
  //#region src/core/BananalinkConnection.d.ts
@@ -65,8 +48,6 @@ declare class BananalinkConnection {
65
48
  get connectionUrl(): string;
66
49
  get authorized(): boolean;
67
50
  private static bind;
68
- private static isAuthorizedMessage;
69
- private static isRejectedMessage;
70
51
  }
71
52
  //#endregion
72
53
  //#region src/core/BananalinkClient.d.ts
@@ -80,7 +61,7 @@ declare class BananalinkClient {
80
61
  constructor(config: {
81
62
  apiUrl?: string;
82
63
  wsUrl?: string;
83
- dapp: Dapp;
64
+ dapp: Dapp$1;
84
65
  });
85
66
  connect(opts?: {
86
67
  jwt: string;
@@ -123,5 +104,5 @@ declare class InvalidConnectionStateError extends BananalinkError {
123
104
  //#region src/utils/displayBananalinkQR.d.ts
124
105
  declare function displayBananalinkQR(url: string): Promise<string>;
125
106
  //#endregion
126
- 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 };
127
108
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/Dapp.ts","../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,IAAA;EACV,MAAA;EACA,QAAA;EACA,eAAA;EACA,aAAA;EACA,MAAA;EACA,GAAA;EACA,KAAA;EACA,OAAA;AAAA;;;KCRU,UAAA;EAAe,OAAA;EAAiB,MAAA;EAAgB,OAAA;EAAiB,SAAA;EAAmB,GAAA;AAAA;;;KCApF,aAAA;EAAkB,OAAA;EAAiB,OAAA;EAAiB,SAAA;EAAmB,WAAA;AAAA;;;cCEtE,iBAAA;EAAA,SAQO,aAAA,EAAe,aAAA;EAAA,iBACd,EAAA;EAAA,wBARK,oBAAA;EAAA,wBACA,mBAAA;EAAA,QAEhB,SAAA;EAAA,QACA,SAAA;cAGU,aAAA,EAAe,aAAA,EACd,EAAA,EAAI,SAAA;EAYhB,KAAA,CAAA;EAAA,QAKC,SAAA;EAAA,QAIA,IAAA;EAAA,QASA,UAAA;AAAA;;;KChCL,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,IAqDxB,aAAA,CAAA;EAAA,IASA,UAAA,CAAA;EAAA,eAKI,IAAA;EAAA,eAIA,mBAAA;EAAA,eAmBA,iBAAA;AAAA;;;cCjHJ,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,IAAA;EAAA;EAcK,OAAA,CAAQ,IAAA;IAAS,GAAA;EAAA,IAAgB,OAAA,CAAQ,oBAAA;EAAA,QAsBxC,aAAA;EAAA,QAIA,mBAAA;AAAA;;;cC7DH,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"}
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,15 +1,5 @@
1
- //#region src/types/Dapp.d.ts
2
- type Dapp = {
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;
@@ -31,15 +21,8 @@ type SessionClaims = {
31
21
  declare class BananalinkSession {
32
22
  readonly sessionClaims: SessionClaims;
33
23
  private readonly ws;
34
- private static readonly PING_INTERVAL_MILLIS;
35
- private static readonly PONG_TIMEOUT_MILLIS;
36
- private pingTimer;
37
- private pongTimer;
38
- constructor(sessionClaims: SessionClaims, ws: WebSocket);
24
+ constructor(sessionClaims: SessionClaims, ws: TransportHandle);
39
25
  close(): void;
40
- private startPing;
41
- private ping;
42
- private stopTimers;
43
26
  }
44
27
  //#endregion
45
28
  //#region src/core/BananalinkConnection.d.ts
@@ -65,8 +48,6 @@ declare class BananalinkConnection {
65
48
  get connectionUrl(): string;
66
49
  get authorized(): boolean;
67
50
  private static bind;
68
- private static isAuthorizedMessage;
69
- private static isRejectedMessage;
70
51
  }
71
52
  //#endregion
72
53
  //#region src/core/BananalinkClient.d.ts
@@ -80,7 +61,7 @@ declare class BananalinkClient {
80
61
  constructor(config: {
81
62
  apiUrl?: string;
82
63
  wsUrl?: string;
83
- dapp: Dapp;
64
+ dapp: Dapp$1;
84
65
  });
85
66
  connect(opts?: {
86
67
  jwt: string;
@@ -123,5 +104,5 @@ declare class InvalidConnectionStateError extends BananalinkError {
123
104
  //#region src/utils/displayBananalinkQR.d.ts
124
105
  declare function displayBananalinkQR(url: string): Promise<string>;
125
106
  //#endregion
126
- 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 };
127
108
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/Dapp.ts","../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,IAAA;EACV,MAAA;EACA,QAAA;EACA,eAAA;EACA,aAAA;EACA,MAAA;EACA,GAAA;EACA,KAAA;EACA,OAAA;AAAA;;;KCRU,UAAA;EAAe,OAAA;EAAiB,MAAA;EAAgB,OAAA;EAAiB,SAAA;EAAmB,GAAA;AAAA;;;KCApF,aAAA;EAAkB,OAAA;EAAiB,OAAA;EAAiB,SAAA;EAAmB,WAAA;AAAA;;;cCEtE,iBAAA;EAAA,SAQO,aAAA,EAAe,aAAA;EAAA,iBACd,EAAA;EAAA,wBARK,oBAAA;EAAA,wBACA,mBAAA;EAAA,QAEhB,SAAA;EAAA,QACA,SAAA;cAGU,aAAA,EAAe,aAAA,EACd,EAAA,EAAI,SAAA;EAYhB,KAAA,CAAA;EAAA,QAKC,SAAA;EAAA,QAIA,IAAA;EAAA,QASA,UAAA;AAAA;;;KChCL,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,IAqDxB,aAAA,CAAA;EAAA,IASA,UAAA,CAAA;EAAA,eAKI,IAAA;EAAA,eAIA,mBAAA;EAAA,eAmBA,iBAAA;AAAA;;;cCjHJ,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,IAAA;EAAA;EAcK,OAAA,CAAQ,IAAA;IAAS,GAAA;EAAA,IAAgB,OAAA,CAAQ,oBAAA;EAAA,QAsBxC,aAAA;EAAA,QAIA,mBAAA;AAAA;;;cC7DH,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"}
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
@@ -121,44 +122,14 @@ var InvalidConnectionStateError = class extends BananalinkError {
121
122
 
122
123
  //#endregion
123
124
  //#region src/core/BananalinkSession.ts
124
- var BananalinkSession = class BananalinkSession {
125
- static PING_INTERVAL_MILLIS = 30 * 1e3;
126
- static PONG_TIMEOUT_MILLIS = 5 * 1e3;
127
- pingTimer = null;
128
- pongTimer = null;
125
+ var BananalinkSession = class {
129
126
  constructor(sessionClaims, ws) {
130
127
  this.sessionClaims = sessionClaims;
131
128
  this.ws = ws;
132
- this.ws.addEventListener("message", (event) => {
133
- const { type } = JSON.parse(event.data);
134
- if (type === "pong" && this.pongTimer) {
135
- clearTimeout(this.pongTimer);
136
- this.pongTimer = null;
137
- }
138
- });
139
- this.startPing();
140
129
  }
141
130
  close() {
142
- this.stopTimers();
143
131
  this.ws.close();
144
132
  }
145
- startPing() {
146
- this.pingTimer = setInterval(() => this.ping(), BananalinkSession.PING_INTERVAL_MILLIS);
147
- }
148
- ping() {
149
- if (this.ws.readyState !== WebSocket.OPEN) {
150
- this.close();
151
- return;
152
- }
153
- this.ws.send(JSON.stringify({ type: "ping" }));
154
- this.pongTimer = setTimeout(() => this.close(), BananalinkSession.PONG_TIMEOUT_MILLIS);
155
- }
156
- stopTimers() {
157
- if (this.pingTimer !== null) clearInterval(this.pingTimer);
158
- if (this.pongTimer !== null) clearTimeout(this.pongTimer);
159
- this.pingTimer = null;
160
- this.pongTimer = null;
161
- }
162
133
  };
163
134
 
164
135
  //#endregion
@@ -181,32 +152,55 @@ var BananalinkConnection = class BananalinkConnection {
181
152
  if (this.jwt) throw new BananalinkError("Recovering a lost dapp connection is not yet implemented");
182
153
  const dappInstanceId = this.dappInstanceId;
183
154
  if (!dappInstanceId) throw new InvalidConnectionStateError("Cannot get session without authorizing the dapp instance or providing a valid JWT");
184
- return new Promise((resolve, reject) => {
185
- const ws = new WebSocket(`${this.wsUrl}?dappInstanceId=${encodeURIComponent(dappInstanceId)}`);
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;
186
167
  const authTimeout = setTimeout(() => {
187
- ws.close();
188
- reject(new ConnectionTimeoutError());
168
+ settleReject(new ConnectionTimeoutError());
189
169
  }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
190
- const onMessage = (event) => {
191
- const msg = JSON.parse(event.data);
192
- clearTimeout(authTimeout);
193
- ws.removeEventListener("message", onMessage);
194
- if (BananalinkConnection.isAuthorizedMessage(msg)) {
195
- BananalinkConnection.bind(ws, msg.accessToken);
196
- resolve(new BananalinkSession({
197
- address: msg.address,
198
- message: msg.message,
199
- signature: msg.signature,
200
- accessToken: msg.accessToken
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
201
178
  }, ws));
202
- } else if (BananalinkConnection.isRejectedMessage(msg)) {
203
- ws.close();
204
- reject(new ConnectionRejectedError());
179
+ return;
205
180
  }
206
- };
207
- ws.addEventListener("message", onMessage);
208
- ws.addEventListener("error", () => {});
209
- ws.addEventListener("close", () => {});
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
+ }
210
204
  });
211
205
  }
212
206
  get connectionUrl() {
@@ -219,16 +213,11 @@ var BananalinkConnection = class BananalinkConnection {
219
213
  return this.jwt !== void 0;
220
214
  }
221
215
  static bind(ws, jwt) {
222
- ws.send(JSON.stringify({
216
+ const bindMessage = {
223
217
  type: "bind",
224
218
  jwt
225
- }));
226
- }
227
- static isAuthorizedMessage(payload) {
228
- return typeof payload === "object" && payload !== null && "type" in payload && payload.type === "authorized" && "accessToken" in payload && typeof payload.accessToken === "string" && "refreshToken" in payload && typeof payload.refreshToken === "string" && "message" in payload && typeof payload.message === "string" && "signature" in payload && typeof payload.signature === "string" && "address" in payload && typeof payload.address === "string";
229
- }
230
- static isRejectedMessage(payload) {
231
- return typeof payload === "object" && payload !== null && "type" in payload && payload.type === "rejected";
219
+ };
220
+ ws.send(bindMessage);
232
221
  }
233
222
  };
234
223
 
@@ -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<{ 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 { SessionClaims } from '../types/SessionClaims.js';\n\nexport 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(\n public readonly sessionClaims: SessionClaims,\n private readonly ws: WebSocket,\n ) {\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 { AuthorizedMessage } from '../types/AuthorizedMessage.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport type { RejectedMessage } from '../types/RejectedMessage.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 (BananalinkConnection.isAuthorizedMessage(msg)) {\n BananalinkConnection.bind(ws, msg.accessToken);\n resolve(\n new BananalinkSession(\n {\n address: msg.address,\n message: msg.message,\n signature: msg.signature,\n accessToken: msg.accessToken,\n },\n ws,\n ),\n );\n } else if (BananalinkConnection.isRejectedMessage(msg)) {\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 private static isAuthorizedMessage(payload: unknown): payload is AuthorizedMessage {\n return (\n typeof payload === 'object' &&\n payload !== null &&\n 'type' in payload &&\n payload.type === 'authorized' &&\n 'accessToken' in payload &&\n typeof payload.accessToken === 'string' &&\n 'refreshToken' in payload &&\n typeof payload.refreshToken === 'string' &&\n 'message' in payload &&\n typeof payload.message === 'string' &&\n 'signature' in payload &&\n typeof payload.signature === 'string' &&\n 'address' in payload &&\n typeof payload.address === 'string'\n );\n }\n\n private static isRejectedMessage(payload: unknown): payload is RejectedMessage {\n return typeof payload === 'object' && payload !== null && 'type' in payload && payload.type === 'rejected';\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.1.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",