@bananalink-test/client 0.6.1 → 0.7.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
@@ -303,15 +303,15 @@ var BananalinkSession = class BananalinkSession {
303
303
  //#region src/core/BananalinkConnection.ts
304
304
  var BananalinkConnection = class BananalinkConnection {
305
305
  static AUTH_TIMEOUT_MILLIS = 600 * 1e3;
306
+ static BIND_RESPONSE_TIMEOUT_MILLIS = 20 * 1e3;
306
307
  apiUrl;
307
308
  wsUrl;
308
309
  jwt;
309
310
  dappId;
310
311
  dappInstanceId;
311
312
  nonce;
312
- aborting = false;
313
- abortPendingSessionRequest = null;
314
313
  pendingSessionPromise = null;
314
+ pendingSessionAbortController = null;
315
315
  constructor(opts) {
316
316
  this.apiUrl = opts.apiUrl;
317
317
  this.wsUrl = opts.wsUrl;
@@ -321,82 +321,54 @@ var BananalinkConnection = class BananalinkConnection {
321
321
  this.nonce = opts.nonce;
322
322
  if (!this.dappInstanceId && !this.jwt) throw new InvalidConnectionStateError("Cannot get session without authorizing a dapp instance or providing a valid JWT");
323
323
  }
324
+ get connectionUrl() {
325
+ if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
326
+ const dappInstanceId = this.dappInstanceId;
327
+ const nonce = this.nonce;
328
+ return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
329
+ }
330
+ abortPendingSession() {
331
+ this.pendingSessionAbortController?.abort();
332
+ }
324
333
  async getSession() {
325
334
  if (this.pendingSessionPromise) return this.pendingSessionPromise;
326
- if (this.jwt) {
327
- const { jwtPayload, rawJwt } = this.jwt;
328
- const ws = await this.getTransportHandle();
329
- if (this.aborting) {
330
- ws.close();
331
- this.aborting = false;
332
- throw new PendingSessionAbortedError();
333
- }
334
- BananalinkConnection.bind(ws, rawJwt);
335
- return new BananalinkSession({
336
- address: jwtPayload.address,
337
- message: jwtPayload.message,
338
- signature: jwtPayload.signature,
339
- accessToken: rawJwt
340
- }, ws, this.apiUrl);
341
- }
342
- const ws = await this.getTransportHandle(this.dappInstanceId);
343
- if (this.aborting) {
344
- ws.close();
345
- this.aborting = false;
335
+ this.pendingSessionAbortController = new AbortController();
336
+ this.pendingSessionPromise = (this.jwt ? this.resumeSession(this.jwt, this.pendingSessionAbortController.signal) : this.waitForAuthorization(this.pendingSessionAbortController.signal)).finally(() => {
337
+ this.pendingSessionPromise = null;
338
+ this.pendingSessionAbortController = null;
339
+ });
340
+ return this.pendingSessionPromise;
341
+ }
342
+ async openSession(getSessionClaims, abortSignal) {
343
+ const transportHandle = await this.getTransportHandle(this.dappInstanceId);
344
+ const onAbort = () => transportHandle.close();
345
+ abortSignal.addEventListener("abort", onAbort, { once: true });
346
+ if (abortSignal.aborted) {
347
+ transportHandle.close();
346
348
  throw new PendingSessionAbortedError();
347
349
  }
348
- this.pendingSessionPromise = new Promise((resolve, reject) => {
349
- let settled = false;
350
- const authTimeout = setTimeout(() => {
351
- settleReject(new ConnectionTimeoutError());
352
- }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
353
- const stopListeningToMessages = ws.onMessage((payload) => {
354
- if ((0, _bananalink_test_sdk_core.isAuthorizedMessage)(payload)) {
355
- BananalinkConnection.bind(ws, payload.accessToken);
356
- settleResolve(new BananalinkSession({
357
- address: payload.address,
358
- message: payload.message,
359
- signature: payload.signature,
360
- accessToken: payload.accessToken
361
- }, ws, this.apiUrl));
362
- return;
363
- }
364
- if ((0, _bananalink_test_sdk_core.isRejectedMessage)(payload)) settleReject(new ConnectionRejectedError());
365
- });
366
- const stopListeningToClose = ws.onClose(() => {
367
- settleReject(new BananalinkError("Connection closed before authorization completed"));
368
- });
369
- this.abortPendingSessionRequest = () => {
370
- settleReject(new PendingSessionAbortedError());
371
- };
372
- const cleanup = () => {
373
- this.abortPendingSessionRequest = null;
374
- clearTimeout(authTimeout);
375
- stopListeningToMessages();
376
- stopListeningToClose();
377
- this.aborting = false;
378
- this.pendingSessionPromise = null;
379
- };
380
- function settleResolve(session) {
381
- if (settled) return;
382
- settled = true;
383
- cleanup();
384
- resolve(session);
385
- }
386
- function settleReject(error) {
387
- if (settled) return;
388
- settled = true;
389
- cleanup();
390
- ws.close();
391
- reject(error);
392
- }
393
- });
394
- return await this.pendingSessionPromise;
350
+ try {
351
+ const sessionClaims = await getSessionClaims(transportHandle);
352
+ await this.bind(transportHandle, sessionClaims.accessToken, abortSignal);
353
+ return new BananalinkSession(sessionClaims, transportHandle, this.apiUrl);
354
+ } catch (error) {
355
+ transportHandle.close();
356
+ throw error;
357
+ } finally {
358
+ abortSignal.removeEventListener("abort", onAbort);
359
+ }
395
360
  }
396
- abortPendingSession() {
397
- if (this.aborting) return;
398
- this.aborting = true;
399
- this.abortPendingSessionRequest?.();
361
+ async resumeSession(jwt, abortSignal) {
362
+ return await this.openSession(async () => ({
363
+ ...jwt.jwtPayload,
364
+ accessToken: jwt.rawJwt
365
+ }), abortSignal);
366
+ }
367
+ async waitForAuthorization(abortSignal) {
368
+ return await this.openSession(async (transportHandle) => await this.raceEvent(transportHandle, (payload, resolve, reject) => {
369
+ if ((0, _bananalink_test_sdk_core.isAuthorizedMessage)(payload)) resolve(payload);
370
+ if ((0, _bananalink_test_sdk_core.isRejectedMessage)(payload)) reject(new ConnectionRejectedError());
371
+ }, BananalinkConnection.AUTH_TIMEOUT_MILLIS, abortSignal), abortSignal);
400
372
  }
401
373
  async getTransportHandle(dappInstanceId) {
402
374
  const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ""}`;
@@ -410,18 +382,45 @@ var BananalinkConnection = class BananalinkConnection {
410
382
  throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
411
383
  }
412
384
  }
413
- get connectionUrl() {
414
- if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
415
- const dappInstanceId = this.dappInstanceId;
416
- const nonce = this.nonce;
417
- return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
418
- }
419
- static bind(ws, jwt) {
420
- const bindMessage = {
385
+ async bind(ws, jwt, abortSignal) {
386
+ const bindPromise = this.raceEvent(ws, (payload, resolve, reject) => {
387
+ if ((0, _bananalink_test_sdk_core.isBindResponseMessage)(payload)) payload.type === "bind_success" ? resolve() : reject(new InvalidJwtError());
388
+ }, BananalinkConnection.BIND_RESPONSE_TIMEOUT_MILLIS, abortSignal);
389
+ ws.send({
421
390
  type: "bind",
422
391
  jwt
392
+ });
393
+ await bindPromise;
394
+ }
395
+ async raceEvent(transportHandle, eventHandler, timeoutMs, abortSignal) {
396
+ const disposers = [];
397
+ const cleanup = () => {
398
+ for (const disposer of disposers) disposer();
423
399
  };
424
- ws.send(bindMessage);
400
+ const messagePromise = new Promise((resolve, reject) => {
401
+ const stopListeningMessages = transportHandle.onMessage((payload) => eventHandler(payload, resolve, reject));
402
+ const stopListeningClose = transportHandle.onClose(() => reject(new BananalinkError("Connection closed unexpectedly")));
403
+ disposers.push(stopListeningMessages, stopListeningClose);
404
+ });
405
+ const timeoutPromise = new Promise((_resolve, reject) => {
406
+ const timer = setTimeout(() => reject(new ConnectionTimeoutError()), timeoutMs);
407
+ disposers.push(() => clearTimeout(timer));
408
+ });
409
+ const abortPromise = new Promise((_resolve, reject) => {
410
+ if (abortSignal.aborted) reject(new PendingSessionAbortedError());
411
+ const onAbort = () => reject(new PendingSessionAbortedError());
412
+ abortSignal.addEventListener("abort", onAbort, { once: true });
413
+ disposers.push(() => abortSignal.removeEventListener("abort", onAbort));
414
+ });
415
+ try {
416
+ return await Promise.race([
417
+ messagePromise,
418
+ timeoutPromise,
419
+ abortPromise
420
+ ]);
421
+ } finally {
422
+ cleanup();
423
+ }
425
424
  }
426
425
  };
427
426
 
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dapp, Dapp as Dapp$1, EthSendTransactionParams, TransportHandle } from "@bananalink-test/sdk-core";
1
+ import { Dapp, Dapp as Dapp$1, EthSendTransactionParams, EthSendTransactionParams as EthSendTransactionParams$1, EthSendTransactionResult, TransportHandle } from "@bananalink-test/sdk-core";
2
2
 
3
3
  //#region src/types/JwtPayload.d.ts
4
4
  type JwtPayload = {
@@ -12,7 +12,7 @@ type JwtPayload = {
12
12
  //#region src/types/RequestMap.d.ts
13
13
  type RequestMap = {
14
14
  eth_sendTransaction: {
15
- params: [EthSendTransactionParams];
15
+ params: [EthSendTransactionParams$1];
16
16
  result: string;
17
17
  };
18
18
  };
@@ -68,15 +68,15 @@ type BananalinkDappJwt = {
68
68
  } | undefined;
69
69
  declare class BananalinkConnection {
70
70
  private static readonly AUTH_TIMEOUT_MILLIS;
71
+ private static readonly BIND_RESPONSE_TIMEOUT_MILLIS;
71
72
  private readonly apiUrl;
72
73
  private readonly wsUrl;
73
74
  private readonly jwt;
74
75
  private readonly dappId;
75
76
  private readonly dappInstanceId;
76
77
  private readonly nonce;
77
- private aborting;
78
- private abortPendingSessionRequest;
79
78
  private pendingSessionPromise;
79
+ private pendingSessionAbortController;
80
80
  constructor(opts: {
81
81
  apiUrl: string;
82
82
  wsUrl: string;
@@ -85,11 +85,15 @@ declare class BananalinkConnection {
85
85
  dappInstanceId: string | null;
86
86
  nonce: string | null;
87
87
  });
88
- getSession(): Promise<BananalinkSession>;
88
+ get connectionUrl(): string;
89
89
  abortPendingSession(): void;
90
+ getSession(): Promise<BananalinkSession>;
91
+ private openSession;
92
+ private resumeSession;
93
+ private waitForAuthorization;
90
94
  private getTransportHandle;
91
- get connectionUrl(): string;
92
- private static bind;
95
+ private bind;
96
+ private raceEvent;
93
97
  }
94
98
  //#endregion
95
99
  //#region src/core/BananalinkClient.d.ts
@@ -173,5 +177,5 @@ declare class SessionClosedError extends BananalinkError {
173
177
  //#region src/utils/displayBananalinkQR.d.ts
174
178
  declare function displayBananalinkQR(url: string): Promise<string>;
175
179
  //#endregion
176
- export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, InvalidJwtError, PendingSessionAbortedError, type Request, type RequestMap, type RequestParams, RequestRejectedError, type RequestResult, RequestTimeoutError, SessionClosedError, displayBananalinkQR };
180
+ export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, type EthSendTransactionParams, type EthSendTransactionResult, InvalidConfigError, InvalidConnectionStateError, InvalidJwtError, PendingSessionAbortedError, type Request, type RequestMap, type RequestParams, RequestRejectedError, type RequestResult, RequestTimeoutError, SessionClosedError, displayBananalinkQR };
177
181
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/RequestMap.ts","../src/types/Request.ts","../src/types/RequestParams.ts","../src/types/RequestResult.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/errors/InvalidJwtError.ts","../src/errors/PendingSessionAbortedError.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/RequestMap.ts","../src/types/Request.ts","../src/types/RequestParams.ts","../src/types/RequestResult.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/errors/InvalidJwtError.ts","../src/errors/PendingSessionAbortedError.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Dapp, Dapp as Dapp$1, EthSendTransactionParams, TransportHandle } from "@bananalink-test/sdk-core";
1
+ import { Dapp, Dapp as Dapp$1, EthSendTransactionParams, EthSendTransactionParams as EthSendTransactionParams$1, EthSendTransactionResult, TransportHandle } from "@bananalink-test/sdk-core";
2
2
 
3
3
  //#region src/types/JwtPayload.d.ts
4
4
  type JwtPayload = {
@@ -12,7 +12,7 @@ type JwtPayload = {
12
12
  //#region src/types/RequestMap.d.ts
13
13
  type RequestMap = {
14
14
  eth_sendTransaction: {
15
- params: [EthSendTransactionParams];
15
+ params: [EthSendTransactionParams$1];
16
16
  result: string;
17
17
  };
18
18
  };
@@ -68,15 +68,15 @@ type BananalinkDappJwt = {
68
68
  } | undefined;
69
69
  declare class BananalinkConnection {
70
70
  private static readonly AUTH_TIMEOUT_MILLIS;
71
+ private static readonly BIND_RESPONSE_TIMEOUT_MILLIS;
71
72
  private readonly apiUrl;
72
73
  private readonly wsUrl;
73
74
  private readonly jwt;
74
75
  private readonly dappId;
75
76
  private readonly dappInstanceId;
76
77
  private readonly nonce;
77
- private aborting;
78
- private abortPendingSessionRequest;
79
78
  private pendingSessionPromise;
79
+ private pendingSessionAbortController;
80
80
  constructor(opts: {
81
81
  apiUrl: string;
82
82
  wsUrl: string;
@@ -85,11 +85,15 @@ declare class BananalinkConnection {
85
85
  dappInstanceId: string | null;
86
86
  nonce: string | null;
87
87
  });
88
- getSession(): Promise<BananalinkSession>;
88
+ get connectionUrl(): string;
89
89
  abortPendingSession(): void;
90
+ getSession(): Promise<BananalinkSession>;
91
+ private openSession;
92
+ private resumeSession;
93
+ private waitForAuthorization;
90
94
  private getTransportHandle;
91
- get connectionUrl(): string;
92
- private static bind;
95
+ private bind;
96
+ private raceEvent;
93
97
  }
94
98
  //#endregion
95
99
  //#region src/core/BananalinkClient.d.ts
@@ -173,5 +177,5 @@ declare class SessionClosedError extends BananalinkError {
173
177
  //#region src/utils/displayBananalinkQR.d.ts
174
178
  declare function displayBananalinkQR(url: string): Promise<string>;
175
179
  //#endregion
176
- export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, InvalidJwtError, PendingSessionAbortedError, type Request, type RequestMap, type RequestParams, RequestRejectedError, type RequestResult, RequestTimeoutError, SessionClosedError, displayBananalinkQR };
180
+ export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, type EthSendTransactionParams, type EthSendTransactionResult, InvalidConfigError, InvalidConnectionStateError, InvalidJwtError, PendingSessionAbortedError, type Request, type RequestMap, type RequestParams, RequestRejectedError, type RequestResult, RequestTimeoutError, SessionClosedError, displayBananalinkQR };
177
181
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/RequestMap.ts","../src/types/Request.ts","../src/types/RequestParams.ts","../src/types/RequestResult.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/errors/InvalidJwtError.ts","../src/errors/PendingSessionAbortedError.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":";;;KAAY,UAAA;EAAe,OAAA;EAAiB,MAAA;EAAgB,OAAA;EAAiB,SAAA;EAAmB,GAAA;AAAA;;;KCEpF,UAAA;EACV,mBAAA;IAAuB,MAAA,GAAS,wBAAA;IAA2B,MAAA;EAAA;AAAA;;;KCDjD,OAAA,SAAgB,UAAA;;;KCAhB,aAAA,iBAA8B,UAAA,IAAc,UAAA,CAAW,CAAA;;;KCAvD,aAAA,iBAA8B,UAAA,IAAc,UAAA,CAAW,CAAA;;;KCFvD,aAAA;EAAkB,OAAA;EAAiB,OAAA;EAAiB,SAAA;EAAmB,WAAA;AAAA;;;cCatE,iBAAA;EAAA,SAiBO,aAAA,EAAe,aAAA;EAAA,iBACd,EAAA;EAAA,iBACA,MAAA;EAAA,wBAlBK,kBAAA;EAAA,iBACP,eAAA;EAAA,iBACA,aAAA;EAAA,iBACA,oBAAA;EAAA,QACT,MAAA;EAAA,wBAEgB,eAAA;cAUN,aAAA,EAAe,aAAA,EACd,EAAA,EAAI,eAAA,EACJ,MAAA;EAcN,OAAA,WAAkB,OAAA,CAAA,CAAA;IAC7B,MAAA;IACA,MAAA;IACA;EAAA;IAEA,MAAA,EAAQ,CAAA;IACR,MAAA,GAAS,aAAA,CAAc,CAAA;IACvB,SAAA;EAAA,IACE,OAAA,CAAQ,aAAA,CAAc,CAAA;EAyCnB,KAAA,CAAA;EAAA,QAQC,qBAAA;EAAA,QASA,OAAA;EAAA,QAMA,wBAAA;AAAA;;;KCvGL,iBAAA;EAAsB,UAAA,EAAY,UAAA;EAAY,MAAA;AAAA;AAAA,cAEtC,oBAAA;EAAA,wBACa,mBAAA;EAAA,iBACP,MAAA;EAAA,iBACA,KAAA;EAAA,iBACA,GAAA;EAAA,iBACA,MAAA;EAAA,iBACA,cAAA;EAAA,iBACA,KAAA;EAAA,QACT,QAAA;EAAA,QACA,0BAAA;EAAA,QACA,qBAAA;cAEI,IAAA;IACV,MAAA;IACA,KAAA;IACA,GAAA,EAAK,iBAAA;IACL,MAAA;IACA,cAAA;IACA,KAAA;EAAA;EAeW,UAAA,CAAA,GAAc,OAAA,CAAQ,iBAAA;EAuG5B,mBAAA,CAAA;EAAA,QAQO,kBAAA;EAAA,IAaH,aAAA,CAAA;EAAA,eASI,IAAA;AAAA;;;cC7KJ,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,QA6BxC,aAAA;EAAA,QAIA,mBAAA;AAAA;;;cCpEH,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;;;cCDD,eAAA,SAAwB,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCAxB,0BAAA,SAAmC,eAAA;EAAA,WAAA,CAAA;AAAA;;;cCAnC,oBAAA,SAA6B,eAAA;EAAA,SACZ,SAAA;cAAA,SAAA;AAAA;;;cCAjB,mBAAA,SAA4B,eAAA;EAAA,SACX,MAAA,EAAQ,OAAA;cAAR,MAAA,EAAQ,OAAA;AAAA;;;cCFzB,kBAAA,SAA2B,eAAA;EAAA,WAAA,CAAA;AAAA;;;iBCElB,mBAAA,CAAoB,GAAA,WAAc,OAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/RequestMap.ts","../src/types/Request.ts","../src/types/RequestParams.ts","../src/types/RequestResult.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/errors/InvalidJwtError.ts","../src/errors/PendingSessionAbortedError.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.ts","../src/utils/displayBananalinkQR.ts"],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRemoteJWKSet, jwtVerify } from "jose";
2
- import { connectWebSocket, isAuthorizedMessage, isMessageResponseMessage, isRejectedMessage } from "@bananalink-test/sdk-core";
2
+ import { connectWebSocket, isAuthorizedMessage, isBindResponseMessage, isMessageResponseMessage, isRejectedMessage } from "@bananalink-test/sdk-core";
3
3
  import QRCode from "qrcode";
4
4
 
5
5
  //#region src/errors/BananalinkError.ts
@@ -274,15 +274,15 @@ var BananalinkSession = class BananalinkSession {
274
274
  //#region src/core/BananalinkConnection.ts
275
275
  var BananalinkConnection = class BananalinkConnection {
276
276
  static AUTH_TIMEOUT_MILLIS = 600 * 1e3;
277
+ static BIND_RESPONSE_TIMEOUT_MILLIS = 20 * 1e3;
277
278
  apiUrl;
278
279
  wsUrl;
279
280
  jwt;
280
281
  dappId;
281
282
  dappInstanceId;
282
283
  nonce;
283
- aborting = false;
284
- abortPendingSessionRequest = null;
285
284
  pendingSessionPromise = null;
285
+ pendingSessionAbortController = null;
286
286
  constructor(opts) {
287
287
  this.apiUrl = opts.apiUrl;
288
288
  this.wsUrl = opts.wsUrl;
@@ -292,82 +292,54 @@ var BananalinkConnection = class BananalinkConnection {
292
292
  this.nonce = opts.nonce;
293
293
  if (!this.dappInstanceId && !this.jwt) throw new InvalidConnectionStateError("Cannot get session without authorizing a dapp instance or providing a valid JWT");
294
294
  }
295
+ get connectionUrl() {
296
+ if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
297
+ const dappInstanceId = this.dappInstanceId;
298
+ const nonce = this.nonce;
299
+ return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
300
+ }
301
+ abortPendingSession() {
302
+ this.pendingSessionAbortController?.abort();
303
+ }
295
304
  async getSession() {
296
305
  if (this.pendingSessionPromise) return this.pendingSessionPromise;
297
- if (this.jwt) {
298
- const { jwtPayload, rawJwt } = this.jwt;
299
- const ws = await this.getTransportHandle();
300
- if (this.aborting) {
301
- ws.close();
302
- this.aborting = false;
303
- throw new PendingSessionAbortedError();
304
- }
305
- BananalinkConnection.bind(ws, rawJwt);
306
- return new BananalinkSession({
307
- address: jwtPayload.address,
308
- message: jwtPayload.message,
309
- signature: jwtPayload.signature,
310
- accessToken: rawJwt
311
- }, ws, this.apiUrl);
312
- }
313
- const ws = await this.getTransportHandle(this.dappInstanceId);
314
- if (this.aborting) {
315
- ws.close();
316
- this.aborting = false;
306
+ this.pendingSessionAbortController = new AbortController();
307
+ this.pendingSessionPromise = (this.jwt ? this.resumeSession(this.jwt, this.pendingSessionAbortController.signal) : this.waitForAuthorization(this.pendingSessionAbortController.signal)).finally(() => {
308
+ this.pendingSessionPromise = null;
309
+ this.pendingSessionAbortController = null;
310
+ });
311
+ return this.pendingSessionPromise;
312
+ }
313
+ async openSession(getSessionClaims, abortSignal) {
314
+ const transportHandle = await this.getTransportHandle(this.dappInstanceId);
315
+ const onAbort = () => transportHandle.close();
316
+ abortSignal.addEventListener("abort", onAbort, { once: true });
317
+ if (abortSignal.aborted) {
318
+ transportHandle.close();
317
319
  throw new PendingSessionAbortedError();
318
320
  }
319
- this.pendingSessionPromise = new Promise((resolve, reject) => {
320
- let settled = false;
321
- const authTimeout = setTimeout(() => {
322
- settleReject(new ConnectionTimeoutError());
323
- }, BananalinkConnection.AUTH_TIMEOUT_MILLIS);
324
- const stopListeningToMessages = ws.onMessage((payload) => {
325
- if (isAuthorizedMessage(payload)) {
326
- BananalinkConnection.bind(ws, payload.accessToken);
327
- settleResolve(new BananalinkSession({
328
- address: payload.address,
329
- message: payload.message,
330
- signature: payload.signature,
331
- accessToken: payload.accessToken
332
- }, ws, this.apiUrl));
333
- return;
334
- }
335
- if (isRejectedMessage(payload)) settleReject(new ConnectionRejectedError());
336
- });
337
- const stopListeningToClose = ws.onClose(() => {
338
- settleReject(new BananalinkError("Connection closed before authorization completed"));
339
- });
340
- this.abortPendingSessionRequest = () => {
341
- settleReject(new PendingSessionAbortedError());
342
- };
343
- const cleanup = () => {
344
- this.abortPendingSessionRequest = null;
345
- clearTimeout(authTimeout);
346
- stopListeningToMessages();
347
- stopListeningToClose();
348
- this.aborting = false;
349
- this.pendingSessionPromise = null;
350
- };
351
- function settleResolve(session) {
352
- if (settled) return;
353
- settled = true;
354
- cleanup();
355
- resolve(session);
356
- }
357
- function settleReject(error) {
358
- if (settled) return;
359
- settled = true;
360
- cleanup();
361
- ws.close();
362
- reject(error);
363
- }
364
- });
365
- return await this.pendingSessionPromise;
321
+ try {
322
+ const sessionClaims = await getSessionClaims(transportHandle);
323
+ await this.bind(transportHandle, sessionClaims.accessToken, abortSignal);
324
+ return new BananalinkSession(sessionClaims, transportHandle, this.apiUrl);
325
+ } catch (error) {
326
+ transportHandle.close();
327
+ throw error;
328
+ } finally {
329
+ abortSignal.removeEventListener("abort", onAbort);
330
+ }
366
331
  }
367
- abortPendingSession() {
368
- if (this.aborting) return;
369
- this.aborting = true;
370
- this.abortPendingSessionRequest?.();
332
+ async resumeSession(jwt, abortSignal) {
333
+ return await this.openSession(async () => ({
334
+ ...jwt.jwtPayload,
335
+ accessToken: jwt.rawJwt
336
+ }), abortSignal);
337
+ }
338
+ async waitForAuthorization(abortSignal) {
339
+ return await this.openSession(async (transportHandle) => await this.raceEvent(transportHandle, (payload, resolve, reject) => {
340
+ if (isAuthorizedMessage(payload)) resolve(payload);
341
+ if (isRejectedMessage(payload)) reject(new ConnectionRejectedError());
342
+ }, BananalinkConnection.AUTH_TIMEOUT_MILLIS, abortSignal), abortSignal);
371
343
  }
372
344
  async getTransportHandle(dappInstanceId) {
373
345
  const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ""}`;
@@ -381,18 +353,45 @@ var BananalinkConnection = class BananalinkConnection {
381
353
  throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
382
354
  }
383
355
  }
384
- get connectionUrl() {
385
- if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
386
- const dappInstanceId = this.dappInstanceId;
387
- const nonce = this.nonce;
388
- return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
389
- }
390
- static bind(ws, jwt) {
391
- const bindMessage = {
356
+ async bind(ws, jwt, abortSignal) {
357
+ const bindPromise = this.raceEvent(ws, (payload, resolve, reject) => {
358
+ if (isBindResponseMessage(payload)) payload.type === "bind_success" ? resolve() : reject(new InvalidJwtError());
359
+ }, BananalinkConnection.BIND_RESPONSE_TIMEOUT_MILLIS, abortSignal);
360
+ ws.send({
392
361
  type: "bind",
393
362
  jwt
363
+ });
364
+ await bindPromise;
365
+ }
366
+ async raceEvent(transportHandle, eventHandler, timeoutMs, abortSignal) {
367
+ const disposers = [];
368
+ const cleanup = () => {
369
+ for (const disposer of disposers) disposer();
394
370
  };
395
- ws.send(bindMessage);
371
+ const messagePromise = new Promise((resolve, reject) => {
372
+ const stopListeningMessages = transportHandle.onMessage((payload) => eventHandler(payload, resolve, reject));
373
+ const stopListeningClose = transportHandle.onClose(() => reject(new BananalinkError("Connection closed unexpectedly")));
374
+ disposers.push(stopListeningMessages, stopListeningClose);
375
+ });
376
+ const timeoutPromise = new Promise((_resolve, reject) => {
377
+ const timer = setTimeout(() => reject(new ConnectionTimeoutError()), timeoutMs);
378
+ disposers.push(() => clearTimeout(timer));
379
+ });
380
+ const abortPromise = new Promise((_resolve, reject) => {
381
+ if (abortSignal.aborted) reject(new PendingSessionAbortedError());
382
+ const onAbort = () => reject(new PendingSessionAbortedError());
383
+ abortSignal.addEventListener("abort", onAbort, { once: true });
384
+ disposers.push(() => abortSignal.removeEventListener("abort", onAbort));
385
+ });
386
+ try {
387
+ return await Promise.race([
388
+ messagePromise,
389
+ timeoutPromise,
390
+ abortPromise
391
+ ]);
392
+ } finally {
393
+ cleanup();
394
+ }
396
395
  }
397
396
  };
398
397
 
@@ -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/errors/InvalidJwtError.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/errors/PendingSessionAbortedError.ts","../src/api/createDappMessage.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.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 { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidJwtError extends BananalinkError {\n constructor() {\n super('Invalid Bananalink dapp jwt');\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 { BananalinkError } from './BananalinkError.js';\n\nexport class PendingSessionAbortedError extends BananalinkError {\n constructor() {\n super('Pending session was intentionally aborted');\n this.name = 'PendingSessionAbortedError';\n }\n}\n","import { DappApiError } from '../errors/DappApiError.js';\n\nfunction isCreateDappMessageResponse(body: unknown): body is { messageId: string } {\n return typeof body === 'object' && body !== null && 'messageId' in body && typeof body.messageId === 'string';\n}\n\nexport async function createDappMessage(\n apiUrl: string,\n accessToken: string,\n params: {\n method: string;\n payload: unknown;\n },\n): Promise<{ messageId: string }> {\n const response = await fetch(`${apiUrl}/dapps/messages`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\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 (!isCreateDappMessageResponse(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 message', response.status);\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class RequestRejectedError extends BananalinkError {\n constructor(public readonly messageId: string) {\n super(`Request rejected by wallet (messageId: ${messageId})`);\n this.name = 'RequestRejectedError';\n }\n}\n","import type { Request } from '../types/Request.js';\nimport { BananalinkError } from './BananalinkError.js';\n\nexport class RequestTimeoutError extends BananalinkError {\n constructor(public readonly method: Request) {\n super(`Timeout reached for request ${method}`);\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class SessionClosedError extends BananalinkError {\n constructor() {\n super('Session is closed');\n }\n}\n","import { isMessageResponseMessage, type MessageResponseMessage, type TransportHandle } from '@bananalink-test/sdk-core';\nimport { createDappMessage } from '../api/createDappMessage.js';\nimport { BananalinkError } from '../errors/BananalinkError.js';\nimport { RequestRejectedError } from '../errors/RequestRejectedError.js';\nimport { RequestTimeoutError } from '../errors/RequestTimeoutError.js';\nimport { SessionClosedError } from '../errors/SessionClosedError.js';\nimport type { PendingMessageRequest } from '../types/PendingMessageRequest.js';\nimport type { Request } from '../types/Request.js';\nimport type { RequestParams } from '../types/RequestParams.js';\nimport type { RequestResult } from '../types/RequestResult.js';\nimport type { RequestResultHandler } from '../types/RequestResultHandler.js';\nimport type { SessionClaims } from '../types/SessionClaims.js';\n\nexport class BananalinkSession {\n private static readonly REQUEST_TIMEOUT_MS = 10 * 60 * 1000;\n private readonly pendingRequests = new Map<string, PendingMessageRequest>();\n private readonly stopListening: () => void;\n private readonly stopListeningOnClose: () => void;\n private closed: boolean;\n\n private static readonly RESULT_HANDLERS: { [T in Request]: RequestResultHandler<T> } = {\n eth_sendTransaction: (result) => {\n if (typeof result === 'object' && result !== null && 'txHash' in result && typeof result.txHash === 'string') {\n return result.txHash;\n }\n throw new BananalinkError(`Unexpected eth_sendTransaction result: ${result}`);\n },\n };\n\n constructor(\n public readonly sessionClaims: SessionClaims,\n private readonly ws: TransportHandle,\n private readonly apiUrl: string,\n ) {\n this.stopListening = this.ws.onMessage((payload) => {\n if (isMessageResponseMessage(payload)) {\n this.handleMessageResponse(payload);\n }\n });\n this.stopListeningOnClose = this.ws.onClose(() => {\n this.cleanup();\n this.closed = true;\n });\n this.closed = false;\n }\n\n public async request<T extends Request>({\n method,\n params,\n timeoutMs = BananalinkSession.REQUEST_TIMEOUT_MS,\n }: {\n method: T;\n params?: RequestParams<T>;\n timeoutMs?: number;\n }): Promise<RequestResult<T>> {\n if (this.closed) {\n throw new SessionClosedError();\n }\n const { messageId } = await createDappMessage(this.apiUrl, this.sessionClaims.accessToken, {\n method,\n payload: params,\n });\n\n if (this.closed) {\n throw new SessionClosedError();\n }\n\n return new Promise<RequestResult<T>>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n if (timeoutMs > 0) {\n timeout = setTimeout(() => {\n this.pendingRequests.delete(messageId);\n reject(new RequestTimeoutError(method));\n }, timeoutMs);\n }\n\n this.pendingRequests.set(messageId, {\n timeout,\n handle: (messageResponse: MessageResponseMessage) => {\n if (messageResponse.status === 'rejected') {\n reject(new RequestRejectedError(messageId));\n return;\n }\n const resultHandler = BananalinkSession.RESULT_HANDLERS[method];\n try {\n resolve(resultHandler(messageResponse.payloadResponse));\n } catch (error: unknown) {\n reject(new BananalinkError(`Failed resolving request response, ${error}`));\n }\n },\n reject,\n });\n });\n }\n\n public close(): void {\n this.cleanup();\n if (!this.ws.closed) {\n this.ws.close();\n }\n this.closed = true;\n }\n\n private handleMessageResponse(messageResponseMessage: MessageResponseMessage): void {\n const pending = this.pendingRequests.get(messageResponseMessage.msgId);\n if (!pending) return;\n\n this.pendingRequests.delete(messageResponseMessage.msgId);\n clearTimeout(pending.timeout);\n pending.handle(messageResponseMessage);\n }\n\n private cleanup(): void {\n this.rejectAllPendingRequests();\n this.stopListening();\n this.stopListeningOnClose();\n }\n\n private rejectAllPendingRequests(): void {\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timeout);\n // TODO: propagate error or throw generic one\n pending.reject(new Error('Pending requests rejected'));\n }\n this.pendingRequests.clear();\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 { PendingSessionAbortedError } from '../errors/PendingSessionAbortedError.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 = 10 * 60 * 1000;\n private readonly apiUrl: string;\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 private aborting: boolean = false;\n private abortPendingSessionRequest: (() => void) | null = null;\n private pendingSessionPromise: Promise<BananalinkSession> | null = null;\n\n constructor(opts: {\n apiUrl: string;\n wsUrl: string;\n jwt: BananalinkDappJwt;\n dappId: string;\n dappInstanceId: string | null;\n nonce: string | null;\n }) {\n this.apiUrl = opts.apiUrl;\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 if (!this.dappInstanceId && !this.jwt) {\n throw new InvalidConnectionStateError(\n 'Cannot get session without authorizing a dapp instance or providing a valid JWT',\n );\n }\n }\n\n public async getSession(): Promise<BananalinkSession> {\n if (this.pendingSessionPromise) {\n return this.pendingSessionPromise;\n }\n if (this.jwt) {\n const { jwtPayload, rawJwt } = this.jwt;\n const ws = await this.getTransportHandle();\n if (this.aborting) {\n ws.close();\n this.aborting = false;\n throw new PendingSessionAbortedError();\n }\n BananalinkConnection.bind(ws, rawJwt);\n return new BananalinkSession(\n {\n address: jwtPayload.address,\n message: jwtPayload.message,\n signature: jwtPayload.signature,\n accessToken: rawJwt,\n },\n ws,\n this.apiUrl,\n );\n }\n\n const ws = await this.getTransportHandle(this.dappInstanceId);\n if (this.aborting) {\n ws.close();\n this.aborting = false;\n throw new PendingSessionAbortedError();\n }\n\n this.pendingSessionPromise = 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 this.apiUrl,\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 this.abortPendingSessionRequest = () => {\n settleReject(new PendingSessionAbortedError());\n };\n\n const cleanup = (): void => {\n this.abortPendingSessionRequest = null;\n clearTimeout(authTimeout);\n stopListeningToMessages();\n stopListeningToClose();\n this.aborting = false;\n this.pendingSessionPromise = null;\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 return await this.pendingSessionPromise;\n }\n\n public abortPendingSession(): void {\n if (this.aborting) {\n return;\n }\n this.aborting = true;\n this.abortPendingSessionRequest?.();\n }\n\n private async getTransportHandle(dappInstanceId?: string | null): Promise<TransportHandle> {\n const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ''}`;\n try {\n return await connectWebSocket({\n url,\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\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 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 { InvalidJwtError } from '../errors/InvalidJwtError.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 if (!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 apiUrl: this.apiUrl,\n wsUrl: this.wsUrl,\n });\n }\n\n const jwtPayload = await this.getJwtPayload(rawJwt);\n if (!jwtPayload) {\n throw new InvalidJwtError();\n }\n\n return new BananalinkConnection({\n jwt: { jwtPayload, rawJwt },\n dappId: this.dapp.dappId,\n dappInstanceId: null,\n nonce: null,\n apiUrl: this.apiUrl,\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":";;;;;AAAA,IAAa,kBAAb,cAAqC,MAAM;CACzC,YAAY,SAAiB,SAAwB;AACnD,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO;;;;;;ACDhB,IAAa,eAAb,cAAkC,gBAAgB;CAChD,YACE,SACA,AAAgB,YAChB;AACA,QAAM,QAAQ;EAFE;AAGhB,OAAK,OAAO;;;;;;ACLhB,SAAS,6BAA6B,MAAmD;AACvF,QACE,OAAO,SAAS,YAChB,SAAS,QACT,oBAAoB,QACpB,OAAO,KAAK,mBAAmB,YAC/B,WAAW,QACX,OAAO,KAAK,UAAU;;AAI1B,eAAsB,mBAAmB,QAAgB,QAAmD;CAC1G,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS;EAC9C,SAAS,EACP,gBAAgB,oBACjB;EACD,QAAQ;EACR,MAAM,KAAK,UAAU,OAAO;EAC7B,CAAC;AACF,KAAI,SAAS,IAAI;EACf,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY;AAC7C,SAAM,IAAI,aAAa,6CAA6C,SAAS,OAAO;IACpF;AACF,MAAI,CAAC,6BAA6B,KAAK,CACrC,OAAM,IAAI,aAAa,iDAAiD,SAAS,OAAO;AAE1F,SAAO;;AAET,OAAM,IAAI,aAAa,kCAAkC,SAAS,OAAO;;;;;AC7B3E,IAAa,qBAAb,cAAwC,gBAAgB;CACtD,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;ACHhB,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,cAAc;AACZ,QAAM,8BAA8B;;;;;;ACFxC,SAAgB,qBAAqB,QAAiC;AAEpE,QAAO,mBADK,IAAI,IAAI,GAAG,OAAO,wBAAwB,CACxB;;;;;ACDhC,eAAsB,UAAU,KAAa,MAAmD;AAC9F,KAAI;EACF,MAAM,EAAE,YAAY,MAAM,UAAkD,KAAK,MAAM;GACrF,YAAY,CAAC,QAAQ;GACrB,QAAQ;GACR,gBAAgB;IAAC;IAAO;IAAO;IAAW;IAAY;GACvD,CAAC;AACF,SAAO;GACL,SAAS,QAAQ;GACjB,QAAQ,MAAM,QAAQ,QAAQ,IAAI,GAAG,QAAQ,IAAI,KAAM,QAAQ;GAC/D,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB;GACD;SACK;AACN,SAAO;;;;;;AClBX,SAAgB,WAAW,KAAa,WAA8B;AACpE,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,SAAO,UAAU,SAAS,OAAO,SAAS,QAAQ,KAAK,GAAG,CAAC;SACrD;AACN,SAAO;;;;;;ACHX,IAAa,0BAAb,cAA6C,gBAAgB;CAC3D,cAAc;AACZ,QAAM,qDAAqD;AAC3D,OAAK,OAAO;;;;;;ACHhB,IAAa,yBAAb,cAA4C,gBAAgB;CAC1D,cAAc;AACZ,QAAM,oCAAoC;AAC1C,OAAK,OAAO;;;;;;ACHhB,IAAa,8BAAb,cAAiD,gBAAgB;CAC/D,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;ACHhB,IAAa,6BAAb,cAAgD,gBAAgB;CAC9D,cAAc;AACZ,QAAM,4CAA4C;AAClD,OAAK,OAAO;;;;;;ACHhB,SAAS,4BAA4B,MAA8C;AACjF,QAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,eAAe,QAAQ,OAAO,KAAK,cAAc;;AAGvG,eAAsB,kBACpB,QACA,aACA,QAIgC;CAChC,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,kBAAkB;EACvD,QAAQ;EACR,SAAS;GACP,eAAe,UAAU;GACzB,gBAAgB;GACjB;EACD,MAAM,KAAK,UAAU,OAAO;EAC7B,CAAC;AACF,KAAI,SAAS,IAAI;EACf,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY;AAC7C,SAAM,IAAI,aAAa,6CAA6C,SAAS,OAAO;IACpF;AACF,MAAI,CAAC,4BAA4B,KAAK,CACpC,OAAM,IAAI,aAAa,iDAAiD,SAAS,OAAO;AAE1F,SAAO;;AAET,OAAM,IAAI,aAAa,iCAAiC,SAAS,OAAO;;;;;AC7B1E,IAAa,uBAAb,cAA0C,gBAAgB;CACxD,YAAY,AAAgB,WAAmB;AAC7C,QAAM,0CAA0C,UAAU,GAAG;EADnC;AAE1B,OAAK,OAAO;;;;;;ACFhB,IAAa,sBAAb,cAAyC,gBAAgB;CACvD,YAAY,AAAgB,QAAiB;AAC3C,QAAM,+BAA+B,SAAS;EADpB;;;;;;ACF9B,IAAa,qBAAb,cAAwC,gBAAgB;CACtD,cAAc;AACZ,QAAM,oBAAoB;;;;;;ACS9B,IAAa,oBAAb,MAAa,kBAAkB;CAC7B,OAAwB,qBAAqB,MAAU;CACvD,AAAiB,kCAAkB,IAAI,KAAoC;CAC3E,AAAiB;CACjB,AAAiB;CACjB,AAAQ;CAER,OAAwB,kBAA+D,EACrF,sBAAsB,WAAW;AAC/B,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,YAAY,UAAU,OAAO,OAAO,WAAW,SAClG,QAAO,OAAO;AAEhB,QAAM,IAAI,gBAAgB,0CAA0C,SAAS;IAEhF;CAED,YACE,AAAgB,eAChB,AAAiB,IACjB,AAAiB,QACjB;EAHgB;EACC;EACA;AAEjB,OAAK,gBAAgB,KAAK,GAAG,WAAW,YAAY;AAClD,OAAI,yBAAyB,QAAQ,CACnC,MAAK,sBAAsB,QAAQ;IAErC;AACF,OAAK,uBAAuB,KAAK,GAAG,cAAc;AAChD,QAAK,SAAS;AACd,QAAK,SAAS;IACd;AACF,OAAK,SAAS;;CAGhB,MAAa,QAA2B,EACtC,QACA,QACA,YAAY,kBAAkB,sBAKF;AAC5B,MAAI,KAAK,OACP,OAAM,IAAI,oBAAoB;EAEhC,MAAM,EAAE,cAAc,MAAM,kBAAkB,KAAK,QAAQ,KAAK,cAAc,aAAa;GACzF;GACA,SAAS;GACV,CAAC;AAEF,MAAI,KAAK,OACP,OAAM,IAAI,oBAAoB;AAGhC,SAAO,IAAI,SAA2B,SAAS,WAAW;GACxD,IAAI;AACJ,OAAI,YAAY,EACd,WAAU,iBAAiB;AACzB,SAAK,gBAAgB,OAAO,UAAU;AACtC,WAAO,IAAI,oBAAoB,OAAO,CAAC;MACtC,UAAU;AAGf,QAAK,gBAAgB,IAAI,WAAW;IAClC;IACA,SAAS,oBAA4C;AACnD,SAAI,gBAAgB,WAAW,YAAY;AACzC,aAAO,IAAI,qBAAqB,UAAU,CAAC;AAC3C;;KAEF,MAAM,gBAAgB,kBAAkB,gBAAgB;AACxD,SAAI;AACF,cAAQ,cAAc,gBAAgB,gBAAgB,CAAC;cAChD,OAAgB;AACvB,aAAO,IAAI,gBAAgB,sCAAsC,QAAQ,CAAC;;;IAG9E;IACD,CAAC;IACF;;CAGJ,AAAO,QAAc;AACnB,OAAK,SAAS;AACd,MAAI,CAAC,KAAK,GAAG,OACX,MAAK,GAAG,OAAO;AAEjB,OAAK,SAAS;;CAGhB,AAAQ,sBAAsB,wBAAsD;EAClF,MAAM,UAAU,KAAK,gBAAgB,IAAI,uBAAuB,MAAM;AACtE,MAAI,CAAC,QAAS;AAEd,OAAK,gBAAgB,OAAO,uBAAuB,MAAM;AACzD,eAAa,QAAQ,QAAQ;AAC7B,UAAQ,OAAO,uBAAuB;;CAGxC,AAAQ,UAAgB;AACtB,OAAK,0BAA0B;AAC/B,OAAK,eAAe;AACpB,OAAK,sBAAsB;;CAG7B,AAAQ,2BAAiC;AACvC,OAAK,MAAM,GAAG,YAAY,KAAK,iBAAiB;AAC9C,gBAAa,QAAQ,QAAQ;AAE7B,WAAQ,uBAAO,IAAI,MAAM,4BAA4B,CAAC;;AAExD,OAAK,gBAAgB,OAAO;;;;;;AC3GhC,IAAa,uBAAb,MAAa,qBAAqB;CAChC,OAAwB,sBAAsB,MAAU;CACxD,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,WAAoB;CAC5B,AAAQ,6BAAkD;CAC1D,AAAQ,wBAA2D;CAEnE,YAAY,MAOT;AACD,OAAK,SAAS,KAAK;AACnB,OAAK,QAAQ,KAAK;AAClB,OAAK,MAAM,KAAK;AAChB,OAAK,SAAS,KAAK;AACnB,OAAK,iBAAiB,KAAK;AAC3B,OAAK,QAAQ,KAAK;AAClB,MAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,IAChC,OAAM,IAAI,4BACR,kFACD;;CAIL,MAAa,aAAyC;AACpD,MAAI,KAAK,sBACP,QAAO,KAAK;AAEd,MAAI,KAAK,KAAK;GACZ,MAAM,EAAE,YAAY,WAAW,KAAK;GACpC,MAAM,KAAK,MAAM,KAAK,oBAAoB;AAC1C,OAAI,KAAK,UAAU;AACjB,OAAG,OAAO;AACV,SAAK,WAAW;AAChB,UAAM,IAAI,4BAA4B;;AAExC,wBAAqB,KAAK,IAAI,OAAO;AACrC,UAAO,IAAI,kBACT;IACE,SAAS,WAAW;IACpB,SAAS,WAAW;IACpB,WAAW,WAAW;IACtB,aAAa;IACd,EACD,IACA,KAAK,OACN;;EAGH,MAAM,KAAK,MAAM,KAAK,mBAAmB,KAAK,eAAe;AAC7D,MAAI,KAAK,UAAU;AACjB,MAAG,OAAO;AACV,QAAK,WAAW;AAChB,SAAM,IAAI,4BAA4B;;AAGxC,OAAK,wBAAwB,IAAI,SAAS,SAAS,WAAW;GAC5D,IAAI,UAAU;GAEd,MAAM,cAAc,iBAAiB;AACnC,iBAAa,IAAI,wBAAwB,CAAC;MACzC,qBAAqB,oBAAoB;GAE5C,MAAM,0BAA0B,GAAG,WAAW,YAAqB;AACjE,QAAI,oBAAoB,QAAQ,EAAE;AAChC,0BAAqB,KAAK,IAAI,QAAQ,YAAY;AAClD,mBACE,IAAI,kBACF;MACE,SAAS,QAAQ;MACjB,SAAS,QAAQ;MACjB,WAAW,QAAQ;MACnB,aAAa,QAAQ;MACtB,EACD,IACA,KAAK,OACN,CACF;AACD;;AAGF,QAAI,kBAAkB,QAAQ,CAC5B,cAAa,IAAI,yBAAyB,CAAC;KAE7C;GAEF,MAAM,uBAAuB,GAAG,cAAc;AAC5C,iBAAa,IAAI,gBAAgB,mDAAmD,CAAC;KACrF;AAEF,QAAK,mCAAmC;AACtC,iBAAa,IAAI,4BAA4B,CAAC;;GAGhD,MAAM,gBAAsB;AAC1B,SAAK,6BAA6B;AAClC,iBAAa,YAAY;AACzB,6BAAyB;AACzB,0BAAsB;AACtB,SAAK,WAAW;AAChB,SAAK,wBAAwB;;GAG/B,SAAS,cAAc,SAAkC;AACvD,QAAI,QACF;AAGF,cAAU;AACV,aAAS;AACT,YAAQ,QAAQ;;GAGlB,SAAS,aAAa,OAAoB;AACxC,QAAI,QACF;AAGF,cAAU;AACV,aAAS;AACT,OAAG,OAAO;AACV,WAAO,MAAM;;IAEf;AACF,SAAO,MAAM,KAAK;;CAGpB,AAAO,sBAA4B;AACjC,MAAI,KAAK,SACP;AAEF,OAAK,WAAW;AAChB,OAAK,8BAA8B;;CAGrC,MAAc,mBAAmB,gBAA0D;EACzF,MAAM,MAAM,GAAG,KAAK,QAAQ,kBAAkB,OAAO,mBAAmB,mBAAmB,eAAe,KAAK;AAC/G,MAAI;AACF,UAAO,MAAM,iBAAiB;IAC5B;IACA,gBAAgB;IAChB,eAAe;IAChB,CAAC;WACK,OAAO;AACd,SAAM,IAAI,gBAAgB,iDAAiD,EAAE,OAAO,OAAO,CAAC;;;CAIhG,IAAW,gBAAwB;AACjC,MAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,MAChC,OAAM,IAAI,4BAA4B,oDAAoD;EAE5F,MAAM,iBAAiB,KAAK;EAC5B,MAAM,QAAQ,KAAK;AACnB,SAAO,wBAAwB,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,mBAAmB,eAAe,CAAC,SAAS,mBAAmB,MAAM;;CAGxJ,OAAe,KAAK,IAAqB,KAAmB;EAC1D,MAAM,cAA2B;GAAE,MAAM;GAAQ;GAAK;AACtD,KAAG,KAAK,YAAY;;;;;;AC/KxB,IAAa,mBAAb,MAAa,iBAAiB;CAC5B,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,OAAwB,6BAA6B;CACrD,OAAwB,4BAA4B;CAEpD,YAAY,QAIT;AACD,OAAK,SAAS,OAAO,UAAU,iBAAiB;AAChD,OAAK,QAAQ,OAAO,SAAS,iBAAiB;AAC9C,MAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,QAAQ,QAAQ,CAAC,CAC7C,OAAM,IAAI,mBAAmB,oBAAoB,KAAK,OAAO,4BAA4B;AAE3F,MAAI,CAAC,WAAW,KAAK,OAAO,CAAC,MAAM,MAAM,CAAC,CACxC,OAAM,IAAI,mBAAmB,mBAAmB,KAAK,MAAM,wBAAwB;AAErF,OAAK,OAAO,OAAO;AACnB,OAAK,OAAO,qBAAqB,KAAK,OAAO;;CAG/C,MAAa,QAAQ,MAAuD;EAC1E,MAAM,SAAS,MAAM;AACrB,MAAI,CAAC,QAAQ;GACX,MAAM,EAAE,gBAAgB,UAAU,MAAM,KAAK,qBAAqB;AAClE,UAAO,IAAI,qBAAqB;IAC9B,KAAK;IACL,QAAQ,KAAK,KAAK;IAClB;IACA;IACA,QAAQ,KAAK;IACb,OAAO,KAAK;IACb,CAAC;;EAGJ,MAAM,aAAa,MAAM,KAAK,cAAc,OAAO;AACnD,MAAI,CAAC,WACH,OAAM,IAAI,iBAAiB;AAG7B,SAAO,IAAI,qBAAqB;GAC9B,KAAK;IAAE;IAAY;IAAQ;GAC3B,QAAQ,KAAK,KAAK;GAClB,gBAAgB;GAChB,OAAO;GACP,QAAQ,KAAK;GACb,OAAO,KAAK;GACb,CAAC;;CAGJ,MAAc,cAAc,QAA4C;AACtE,SAAO,MAAM,UAAU,QAAQ,KAAK,KAAK;;CAG3C,MAAc,sBAA2D;AACvE,SAAO,MAAM,mBAAmB,KAAK,QAAQ,KAAK,KAAK;;;;;;ACnE3D,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,YAAY,AAAgB,KAAa;AACvC,QAAM,GAAG,IAAI,qBAAqB;EADR;;;;;;ACC9B,eAAsB,oBAAoB,KAA8B;AACtE,KAAI,CAAC,WAAW,KAAK,CAAC,aAAa,CAAC,CAClC,OAAM,IAAI,gBAAgB,IAAI;AAEhC,QAAO,OAAO,UAAU,IAAI"}
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/errors/InvalidJwtError.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/errors/PendingSessionAbortedError.ts","../src/api/createDappMessage.ts","../src/errors/RequestRejectedError.ts","../src/errors/RequestTimeoutError.ts","../src/errors/SessionClosedError.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 { BananalinkError } from './BananalinkError.js';\n\nexport class InvalidJwtError extends BananalinkError {\n constructor() {\n super('Invalid Bananalink dapp jwt');\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 { BananalinkError } from './BananalinkError.js';\n\nexport class PendingSessionAbortedError extends BananalinkError {\n constructor() {\n super('Pending session was intentionally aborted');\n this.name = 'PendingSessionAbortedError';\n }\n}\n","import { DappApiError } from '../errors/DappApiError.js';\n\nfunction isCreateDappMessageResponse(body: unknown): body is { messageId: string } {\n return typeof body === 'object' && body !== null && 'messageId' in body && typeof body.messageId === 'string';\n}\n\nexport async function createDappMessage(\n apiUrl: string,\n accessToken: string,\n params: {\n method: string;\n payload: unknown;\n },\n): Promise<{ messageId: string }> {\n const response = await fetch(`${apiUrl}/dapps/messages`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\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 (!isCreateDappMessageResponse(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 message', response.status);\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class RequestRejectedError extends BananalinkError {\n constructor(public readonly messageId: string) {\n super(`Request rejected by wallet (messageId: ${messageId})`);\n this.name = 'RequestRejectedError';\n }\n}\n","import type { Request } from '../types/Request.js';\nimport { BananalinkError } from './BananalinkError.js';\n\nexport class RequestTimeoutError extends BananalinkError {\n constructor(public readonly method: Request) {\n super(`Timeout reached for request ${method}`);\n }\n}\n","import { BananalinkError } from './BananalinkError.js';\n\nexport class SessionClosedError extends BananalinkError {\n constructor() {\n super('Session is closed');\n }\n}\n","import { isMessageResponseMessage, type MessageResponseMessage, type TransportHandle } from '@bananalink-test/sdk-core';\nimport { createDappMessage } from '../api/createDappMessage.js';\nimport { BananalinkError } from '../errors/BananalinkError.js';\nimport { RequestRejectedError } from '../errors/RequestRejectedError.js';\nimport { RequestTimeoutError } from '../errors/RequestTimeoutError.js';\nimport { SessionClosedError } from '../errors/SessionClosedError.js';\nimport type { PendingMessageRequest } from '../types/PendingMessageRequest.js';\nimport type { Request } from '../types/Request.js';\nimport type { RequestParams } from '../types/RequestParams.js';\nimport type { RequestResult } from '../types/RequestResult.js';\nimport type { RequestResultHandler } from '../types/RequestResultHandler.js';\nimport type { SessionClaims } from '../types/SessionClaims.js';\n\nexport class BananalinkSession {\n private static readonly REQUEST_TIMEOUT_MS = 10 * 60 * 1000;\n private readonly pendingRequests = new Map<string, PendingMessageRequest>();\n private readonly stopListening: () => void;\n private readonly stopListeningOnClose: () => void;\n private closed: boolean;\n\n private static readonly RESULT_HANDLERS: { [T in Request]: RequestResultHandler<T> } = {\n eth_sendTransaction: (result) => {\n if (typeof result === 'object' && result !== null && 'txHash' in result && typeof result.txHash === 'string') {\n return result.txHash;\n }\n throw new BananalinkError(`Unexpected eth_sendTransaction result: ${result}`);\n },\n };\n\n constructor(\n public readonly sessionClaims: SessionClaims,\n private readonly ws: TransportHandle,\n private readonly apiUrl: string,\n ) {\n this.stopListening = this.ws.onMessage((payload) => {\n if (isMessageResponseMessage(payload)) {\n this.handleMessageResponse(payload);\n }\n });\n this.stopListeningOnClose = this.ws.onClose(() => {\n this.cleanup();\n this.closed = true;\n });\n this.closed = false;\n }\n\n public async request<T extends Request>({\n method,\n params,\n timeoutMs = BananalinkSession.REQUEST_TIMEOUT_MS,\n }: {\n method: T;\n params?: RequestParams<T>;\n timeoutMs?: number;\n }): Promise<RequestResult<T>> {\n if (this.closed) {\n throw new SessionClosedError();\n }\n const { messageId } = await createDappMessage(this.apiUrl, this.sessionClaims.accessToken, {\n method,\n payload: params,\n });\n\n if (this.closed) {\n throw new SessionClosedError();\n }\n\n return new Promise<RequestResult<T>>((resolve, reject) => {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n if (timeoutMs > 0) {\n timeout = setTimeout(() => {\n this.pendingRequests.delete(messageId);\n reject(new RequestTimeoutError(method));\n }, timeoutMs);\n }\n\n this.pendingRequests.set(messageId, {\n timeout,\n handle: (messageResponse: MessageResponseMessage) => {\n if (messageResponse.status === 'rejected') {\n reject(new RequestRejectedError(messageId));\n return;\n }\n const resultHandler = BananalinkSession.RESULT_HANDLERS[method];\n try {\n resolve(resultHandler(messageResponse.payloadResponse));\n } catch (error: unknown) {\n reject(new BananalinkError(`Failed resolving request response, ${error}`));\n }\n },\n reject,\n });\n });\n }\n\n public close(): void {\n this.cleanup();\n if (!this.ws.closed) {\n this.ws.close();\n }\n this.closed = true;\n }\n\n private handleMessageResponse(messageResponseMessage: MessageResponseMessage): void {\n const pending = this.pendingRequests.get(messageResponseMessage.msgId);\n if (!pending) return;\n\n this.pendingRequests.delete(messageResponseMessage.msgId);\n clearTimeout(pending.timeout);\n pending.handle(messageResponseMessage);\n }\n\n private cleanup(): void {\n this.rejectAllPendingRequests();\n this.stopListening();\n this.stopListeningOnClose();\n }\n\n private rejectAllPendingRequests(): void {\n for (const [, pending] of this.pendingRequests) {\n clearTimeout(pending.timeout);\n // TODO: propagate error or throw generic one\n pending.reject(new Error('Pending requests rejected'));\n }\n this.pendingRequests.clear();\n }\n}\n","import {\n type AuthorizedMessage,\n type BindMessage,\n connectWebSocket,\n isAuthorizedMessage,\n isBindResponseMessage,\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 { InvalidJwtError } from '../errors/InvalidJwtError.js';\nimport { PendingSessionAbortedError } from '../errors/PendingSessionAbortedError.js';\nimport type { JwtPayload } from '../types/JwtPayload.js';\nimport type { SessionClaims } from '../types/SessionClaims.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 = 10 * 60 * 1000;\n private static readonly BIND_RESPONSE_TIMEOUT_MILLIS = 20 * 1000;\n private readonly apiUrl: string;\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 private pendingSessionPromise: Promise<BananalinkSession> | null = null;\n private pendingSessionAbortController: AbortController | null = null;\n\n constructor(opts: {\n apiUrl: string;\n wsUrl: string;\n jwt: BananalinkDappJwt;\n dappId: string;\n dappInstanceId: string | null;\n nonce: string | null;\n }) {\n this.apiUrl = opts.apiUrl;\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 if (!this.dappInstanceId && !this.jwt) {\n throw new InvalidConnectionStateError(\n 'Cannot get session without authorizing a dapp instance or providing a valid JWT',\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 abortPendingSession(): void {\n this.pendingSessionAbortController?.abort();\n }\n\n public async getSession(): Promise<BananalinkSession> {\n if (this.pendingSessionPromise) {\n return this.pendingSessionPromise;\n }\n this.pendingSessionAbortController = new AbortController();\n this.pendingSessionPromise = (\n this.jwt\n ? this.resumeSession(this.jwt, this.pendingSessionAbortController.signal)\n : this.waitForAuthorization(this.pendingSessionAbortController.signal)\n ).finally(() => {\n this.pendingSessionPromise = null;\n this.pendingSessionAbortController = null;\n });\n return this.pendingSessionPromise;\n }\n\n private async openSession(\n getSessionClaims: (transportHandle: TransportHandle) => Promise<SessionClaims>,\n abortSignal: AbortSignal,\n ) {\n const transportHandle = await this.getTransportHandle(this.dappInstanceId);\n const onAbort = (): void => transportHandle.close();\n abortSignal.addEventListener('abort', onAbort, { once: true });\n if (abortSignal.aborted) {\n transportHandle.close();\n throw new PendingSessionAbortedError();\n }\n try {\n const sessionClaims = await getSessionClaims(transportHandle);\n await this.bind(transportHandle, sessionClaims.accessToken, abortSignal);\n return new BananalinkSession(sessionClaims, transportHandle, this.apiUrl);\n } catch (error) {\n transportHandle.close();\n throw error;\n } finally {\n abortSignal.removeEventListener('abort', onAbort);\n }\n }\n\n private async resumeSession(\n jwt: NonNullable<BananalinkDappJwt>,\n abortSignal: AbortSignal,\n ): Promise<BananalinkSession> {\n return await this.openSession(async () => ({ ...jwt.jwtPayload, accessToken: jwt.rawJwt }), abortSignal);\n }\n\n private async waitForAuthorization(abortSignal: AbortSignal): Promise<BananalinkSession> {\n return await this.openSession(\n async (transportHandle: TransportHandle) =>\n await this.raceEvent<AuthorizedMessage>(\n transportHandle,\n (payload, resolve, reject) => {\n if (isAuthorizedMessage(payload)) resolve(payload);\n if (isRejectedMessage(payload)) reject(new ConnectionRejectedError());\n },\n BananalinkConnection.AUTH_TIMEOUT_MILLIS,\n abortSignal,\n ),\n abortSignal,\n );\n }\n\n private async getTransportHandle(dappInstanceId?: string | null): Promise<TransportHandle> {\n const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ''}`;\n try {\n return await connectWebSocket({\n url,\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\n private async bind(ws: TransportHandle, jwt: string, abortSignal: AbortSignal): Promise<void> {\n const bindPromise = this.raceEvent<void>(\n ws,\n (payload, resolve, reject) => {\n if (isBindResponseMessage(payload)) {\n // TODO: handle different error cases for either invalid or expired jwt\n payload.type === 'bind_success' ? resolve() : reject(new InvalidJwtError());\n }\n },\n BananalinkConnection.BIND_RESPONSE_TIMEOUT_MILLIS,\n abortSignal,\n );\n ws.send({ type: 'bind', jwt } as BindMessage);\n await bindPromise;\n }\n\n private async raceEvent<T>(\n transportHandle: TransportHandle,\n eventHandler: (\n payload: unknown,\n resolve: (t: T | PromiseLike<T>) => void,\n reject: (error: BananalinkError) => void,\n ) => void,\n timeoutMs: number,\n abortSignal: AbortSignal,\n ): Promise<T> {\n const disposers: (() => void)[] = [];\n\n const cleanup = (): void => {\n for (const disposer of disposers) {\n disposer();\n }\n };\n\n const messagePromise = new Promise<T>((resolve, reject) => {\n const stopListeningMessages = transportHandle.onMessage((payload) => eventHandler(payload, resolve, reject));\n const stopListeningClose = transportHandle.onClose(() =>\n reject(new BananalinkError('Connection closed unexpectedly')),\n );\n disposers.push(stopListeningMessages, stopListeningClose);\n });\n\n const timeoutPromise = new Promise<T>((_resolve, reject) => {\n const timer = setTimeout(() => reject(new ConnectionTimeoutError()), timeoutMs);\n disposers.push(() => clearTimeout(timer));\n });\n\n const abortPromise = new Promise<T>((_resolve, reject) => {\n if (abortSignal.aborted) reject(new PendingSessionAbortedError());\n const onAbort = (): void => reject(new PendingSessionAbortedError());\n abortSignal.addEventListener('abort', onAbort, { once: true });\n disposers.push(() => abortSignal.removeEventListener('abort', onAbort));\n });\n\n try {\n return await Promise.race([messagePromise, timeoutPromise, abortPromise]);\n } finally {\n cleanup();\n }\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 { InvalidJwtError } from '../errors/InvalidJwtError.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 if (!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 apiUrl: this.apiUrl,\n wsUrl: this.wsUrl,\n });\n }\n\n const jwtPayload = await this.getJwtPayload(rawJwt);\n if (!jwtPayload) {\n throw new InvalidJwtError();\n }\n\n return new BananalinkConnection({\n jwt: { jwtPayload, rawJwt },\n dappId: this.dapp.dappId,\n dappInstanceId: null,\n nonce: null,\n apiUrl: this.apiUrl,\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":";;;;;AAAA,IAAa,kBAAb,cAAqC,MAAM;CACzC,YAAY,SAAiB,SAAwB;AACnD,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO;;;;;;ACDhB,IAAa,eAAb,cAAkC,gBAAgB;CAChD,YACE,SACA,AAAgB,YAChB;AACA,QAAM,QAAQ;EAFE;AAGhB,OAAK,OAAO;;;;;;ACLhB,SAAS,6BAA6B,MAAmD;AACvF,QACE,OAAO,SAAS,YAChB,SAAS,QACT,oBAAoB,QACpB,OAAO,KAAK,mBAAmB,YAC/B,WAAW,QACX,OAAO,KAAK,UAAU;;AAI1B,eAAsB,mBAAmB,QAAgB,QAAmD;CAC1G,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,SAAS;EAC9C,SAAS,EACP,gBAAgB,oBACjB;EACD,QAAQ;EACR,MAAM,KAAK,UAAU,OAAO;EAC7B,CAAC;AACF,KAAI,SAAS,IAAI;EACf,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY;AAC7C,SAAM,IAAI,aAAa,6CAA6C,SAAS,OAAO;IACpF;AACF,MAAI,CAAC,6BAA6B,KAAK,CACrC,OAAM,IAAI,aAAa,iDAAiD,SAAS,OAAO;AAE1F,SAAO;;AAET,OAAM,IAAI,aAAa,kCAAkC,SAAS,OAAO;;;;;AC7B3E,IAAa,qBAAb,cAAwC,gBAAgB;CACtD,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;ACHhB,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,cAAc;AACZ,QAAM,8BAA8B;;;;;;ACFxC,SAAgB,qBAAqB,QAAiC;AAEpE,QAAO,mBADK,IAAI,IAAI,GAAG,OAAO,wBAAwB,CACxB;;;;;ACDhC,eAAsB,UAAU,KAAa,MAAmD;AAC9F,KAAI;EACF,MAAM,EAAE,YAAY,MAAM,UAAkD,KAAK,MAAM;GACrF,YAAY,CAAC,QAAQ;GACrB,QAAQ;GACR,gBAAgB;IAAC;IAAO;IAAO;IAAW;IAAY;GACvD,CAAC;AACF,SAAO;GACL,SAAS,QAAQ;GACjB,QAAQ,MAAM,QAAQ,QAAQ,IAAI,GAAG,QAAQ,IAAI,KAAM,QAAQ;GAC/D,SAAS,QAAQ;GACjB,WAAW,QAAQ;GACnB;GACD;SACK;AACN,SAAO;;;;;;AClBX,SAAgB,WAAW,KAAa,WAA8B;AACpE,KAAI;EACF,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,SAAO,UAAU,SAAS,OAAO,SAAS,QAAQ,KAAK,GAAG,CAAC;SACrD;AACN,SAAO;;;;;;ACHX,IAAa,0BAAb,cAA6C,gBAAgB;CAC3D,cAAc;AACZ,QAAM,qDAAqD;AAC3D,OAAK,OAAO;;;;;;ACHhB,IAAa,yBAAb,cAA4C,gBAAgB;CAC1D,cAAc;AACZ,QAAM,oCAAoC;AAC1C,OAAK,OAAO;;;;;;ACHhB,IAAa,8BAAb,cAAiD,gBAAgB;CAC/D,YAAY,SAAiB;AAC3B,QAAM,QAAQ;AACd,OAAK,OAAO;;;;;;ACHhB,IAAa,6BAAb,cAAgD,gBAAgB;CAC9D,cAAc;AACZ,QAAM,4CAA4C;AAClD,OAAK,OAAO;;;;;;ACHhB,SAAS,4BAA4B,MAA8C;AACjF,QAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,eAAe,QAAQ,OAAO,KAAK,cAAc;;AAGvG,eAAsB,kBACpB,QACA,aACA,QAIgC;CAChC,MAAM,WAAW,MAAM,MAAM,GAAG,OAAO,kBAAkB;EACvD,QAAQ;EACR,SAAS;GACP,eAAe,UAAU;GACzB,gBAAgB;GACjB;EACD,MAAM,KAAK,UAAU,OAAO;EAC7B,CAAC;AACF,KAAI,SAAS,IAAI;EACf,MAAM,OAAO,MAAM,SAAS,MAAM,CAAC,YAAY;AAC7C,SAAM,IAAI,aAAa,6CAA6C,SAAS,OAAO;IACpF;AACF,MAAI,CAAC,4BAA4B,KAAK,CACpC,OAAM,IAAI,aAAa,iDAAiD,SAAS,OAAO;AAE1F,SAAO;;AAET,OAAM,IAAI,aAAa,iCAAiC,SAAS,OAAO;;;;;AC7B1E,IAAa,uBAAb,cAA0C,gBAAgB;CACxD,YAAY,AAAgB,WAAmB;AAC7C,QAAM,0CAA0C,UAAU,GAAG;EADnC;AAE1B,OAAK,OAAO;;;;;;ACFhB,IAAa,sBAAb,cAAyC,gBAAgB;CACvD,YAAY,AAAgB,QAAiB;AAC3C,QAAM,+BAA+B,SAAS;EADpB;;;;;;ACF9B,IAAa,qBAAb,cAAwC,gBAAgB;CACtD,cAAc;AACZ,QAAM,oBAAoB;;;;;;ACS9B,IAAa,oBAAb,MAAa,kBAAkB;CAC7B,OAAwB,qBAAqB,MAAU;CACvD,AAAiB,kCAAkB,IAAI,KAAoC;CAC3E,AAAiB;CACjB,AAAiB;CACjB,AAAQ;CAER,OAAwB,kBAA+D,EACrF,sBAAsB,WAAW;AAC/B,MAAI,OAAO,WAAW,YAAY,WAAW,QAAQ,YAAY,UAAU,OAAO,OAAO,WAAW,SAClG,QAAO,OAAO;AAEhB,QAAM,IAAI,gBAAgB,0CAA0C,SAAS;IAEhF;CAED,YACE,AAAgB,eAChB,AAAiB,IACjB,AAAiB,QACjB;EAHgB;EACC;EACA;AAEjB,OAAK,gBAAgB,KAAK,GAAG,WAAW,YAAY;AAClD,OAAI,yBAAyB,QAAQ,CACnC,MAAK,sBAAsB,QAAQ;IAErC;AACF,OAAK,uBAAuB,KAAK,GAAG,cAAc;AAChD,QAAK,SAAS;AACd,QAAK,SAAS;IACd;AACF,OAAK,SAAS;;CAGhB,MAAa,QAA2B,EACtC,QACA,QACA,YAAY,kBAAkB,sBAKF;AAC5B,MAAI,KAAK,OACP,OAAM,IAAI,oBAAoB;EAEhC,MAAM,EAAE,cAAc,MAAM,kBAAkB,KAAK,QAAQ,KAAK,cAAc,aAAa;GACzF;GACA,SAAS;GACV,CAAC;AAEF,MAAI,KAAK,OACP,OAAM,IAAI,oBAAoB;AAGhC,SAAO,IAAI,SAA2B,SAAS,WAAW;GACxD,IAAI;AACJ,OAAI,YAAY,EACd,WAAU,iBAAiB;AACzB,SAAK,gBAAgB,OAAO,UAAU;AACtC,WAAO,IAAI,oBAAoB,OAAO,CAAC;MACtC,UAAU;AAGf,QAAK,gBAAgB,IAAI,WAAW;IAClC;IACA,SAAS,oBAA4C;AACnD,SAAI,gBAAgB,WAAW,YAAY;AACzC,aAAO,IAAI,qBAAqB,UAAU,CAAC;AAC3C;;KAEF,MAAM,gBAAgB,kBAAkB,gBAAgB;AACxD,SAAI;AACF,cAAQ,cAAc,gBAAgB,gBAAgB,CAAC;cAChD,OAAgB;AACvB,aAAO,IAAI,gBAAgB,sCAAsC,QAAQ,CAAC;;;IAG9E;IACD,CAAC;IACF;;CAGJ,AAAO,QAAc;AACnB,OAAK,SAAS;AACd,MAAI,CAAC,KAAK,GAAG,OACX,MAAK,GAAG,OAAO;AAEjB,OAAK,SAAS;;CAGhB,AAAQ,sBAAsB,wBAAsD;EAClF,MAAM,UAAU,KAAK,gBAAgB,IAAI,uBAAuB,MAAM;AACtE,MAAI,CAAC,QAAS;AAEd,OAAK,gBAAgB,OAAO,uBAAuB,MAAM;AACzD,eAAa,QAAQ,QAAQ;AAC7B,UAAQ,OAAO,uBAAuB;;CAGxC,AAAQ,UAAgB;AACtB,OAAK,0BAA0B;AAC/B,OAAK,eAAe;AACpB,OAAK,sBAAsB;;CAG7B,AAAQ,2BAAiC;AACvC,OAAK,MAAM,GAAG,YAAY,KAAK,iBAAiB;AAC9C,gBAAa,QAAQ,QAAQ;AAE7B,WAAQ,uBAAO,IAAI,MAAM,4BAA4B,CAAC;;AAExD,OAAK,gBAAgB,OAAO;;;;;;ACvGhC,IAAa,uBAAb,MAAa,qBAAqB;CAChC,OAAwB,sBAAsB,MAAU;CACxD,OAAwB,+BAA+B,KAAK;CAC5D,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAQ,wBAA2D;CACnE,AAAQ,gCAAwD;CAEhE,YAAY,MAOT;AACD,OAAK,SAAS,KAAK;AACnB,OAAK,QAAQ,KAAK;AAClB,OAAK,MAAM,KAAK;AAChB,OAAK,SAAS,KAAK;AACnB,OAAK,iBAAiB,KAAK;AAC3B,OAAK,QAAQ,KAAK;AAClB,MAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,IAChC,OAAM,IAAI,4BACR,kFACD;;CAIL,IAAW,gBAAwB;AACjC,MAAI,CAAC,KAAK,kBAAkB,CAAC,KAAK,MAChC,OAAM,IAAI,4BAA4B,oDAAoD;EAE5F,MAAM,iBAAiB,KAAK;EAC5B,MAAM,QAAQ,KAAK;AACnB,SAAO,wBAAwB,mBAAmB,KAAK,OAAO,CAAC,kBAAkB,mBAAmB,eAAe,CAAC,SAAS,mBAAmB,MAAM;;CAGxJ,AAAO,sBAA4B;AACjC,OAAK,+BAA+B,OAAO;;CAG7C,MAAa,aAAyC;AACpD,MAAI,KAAK,sBACP,QAAO,KAAK;AAEd,OAAK,gCAAgC,IAAI,iBAAiB;AAC1D,OAAK,yBACH,KAAK,MACD,KAAK,cAAc,KAAK,KAAK,KAAK,8BAA8B,OAAO,GACvE,KAAK,qBAAqB,KAAK,8BAA8B,OAAO,EACxE,cAAc;AACd,QAAK,wBAAwB;AAC7B,QAAK,gCAAgC;IACrC;AACF,SAAO,KAAK;;CAGd,MAAc,YACZ,kBACA,aACA;EACA,MAAM,kBAAkB,MAAM,KAAK,mBAAmB,KAAK,eAAe;EAC1E,MAAM,gBAAsB,gBAAgB,OAAO;AACnD,cAAY,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAC9D,MAAI,YAAY,SAAS;AACvB,mBAAgB,OAAO;AACvB,SAAM,IAAI,4BAA4B;;AAExC,MAAI;GACF,MAAM,gBAAgB,MAAM,iBAAiB,gBAAgB;AAC7D,SAAM,KAAK,KAAK,iBAAiB,cAAc,aAAa,YAAY;AACxE,UAAO,IAAI,kBAAkB,eAAe,iBAAiB,KAAK,OAAO;WAClE,OAAO;AACd,mBAAgB,OAAO;AACvB,SAAM;YACE;AACR,eAAY,oBAAoB,SAAS,QAAQ;;;CAIrD,MAAc,cACZ,KACA,aAC4B;AAC5B,SAAO,MAAM,KAAK,YAAY,aAAa;GAAE,GAAG,IAAI;GAAY,aAAa,IAAI;GAAQ,GAAG,YAAY;;CAG1G,MAAc,qBAAqB,aAAsD;AACvF,SAAO,MAAM,KAAK,YAChB,OAAO,oBACL,MAAM,KAAK,UACT,kBACC,SAAS,SAAS,WAAW;AAC5B,OAAI,oBAAoB,QAAQ,CAAE,SAAQ,QAAQ;AAClD,OAAI,kBAAkB,QAAQ,CAAE,QAAO,IAAI,yBAAyB,CAAC;KAEvE,qBAAqB,qBACrB,YACD,EACH,YACD;;CAGH,MAAc,mBAAmB,gBAA0D;EACzF,MAAM,MAAM,GAAG,KAAK,QAAQ,kBAAkB,OAAO,mBAAmB,mBAAmB,eAAe,KAAK;AAC/G,MAAI;AACF,UAAO,MAAM,iBAAiB;IAC5B;IACA,gBAAgB;IAChB,eAAe;IAChB,CAAC;WACK,OAAO;AACd,SAAM,IAAI,gBAAgB,iDAAiD,EAAE,OAAO,OAAO,CAAC;;;CAIhG,MAAc,KAAK,IAAqB,KAAa,aAAyC;EAC5F,MAAM,cAAc,KAAK,UACvB,KACC,SAAS,SAAS,WAAW;AAC5B,OAAI,sBAAsB,QAAQ,CAEhC,SAAQ,SAAS,iBAAiB,SAAS,GAAG,OAAO,IAAI,iBAAiB,CAAC;KAG/E,qBAAqB,8BACrB,YACD;AACD,KAAG,KAAK;GAAE,MAAM;GAAQ;GAAK,CAAgB;AAC7C,QAAM;;CAGR,MAAc,UACZ,iBACA,cAKA,WACA,aACY;EACZ,MAAM,YAA4B,EAAE;EAEpC,MAAM,gBAAsB;AAC1B,QAAK,MAAM,YAAY,UACrB,WAAU;;EAId,MAAM,iBAAiB,IAAI,SAAY,SAAS,WAAW;GACzD,MAAM,wBAAwB,gBAAgB,WAAW,YAAY,aAAa,SAAS,SAAS,OAAO,CAAC;GAC5G,MAAM,qBAAqB,gBAAgB,cACzC,OAAO,IAAI,gBAAgB,iCAAiC,CAAC,CAC9D;AACD,aAAU,KAAK,uBAAuB,mBAAmB;IACzD;EAEF,MAAM,iBAAiB,IAAI,SAAY,UAAU,WAAW;GAC1D,MAAM,QAAQ,iBAAiB,OAAO,IAAI,wBAAwB,CAAC,EAAE,UAAU;AAC/E,aAAU,WAAW,aAAa,MAAM,CAAC;IACzC;EAEF,MAAM,eAAe,IAAI,SAAY,UAAU,WAAW;AACxD,OAAI,YAAY,QAAS,QAAO,IAAI,4BAA4B,CAAC;GACjE,MAAM,gBAAsB,OAAO,IAAI,4BAA4B,CAAC;AACpE,eAAY,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;AAC9D,aAAU,WAAW,YAAY,oBAAoB,SAAS,QAAQ,CAAC;IACvE;AAEF,MAAI;AACF,UAAO,MAAM,QAAQ,KAAK;IAAC;IAAgB;IAAgB;IAAa,CAAC;YACjE;AACR,YAAS;;;;;;;AC7Lf,IAAa,mBAAb,MAAa,iBAAiB;CAC5B,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,OAAwB,6BAA6B;CACrD,OAAwB,4BAA4B;CAEpD,YAAY,QAIT;AACD,OAAK,SAAS,OAAO,UAAU,iBAAiB;AAChD,OAAK,QAAQ,OAAO,SAAS,iBAAiB;AAC9C,MAAI,CAAC,WAAW,KAAK,QAAQ,CAAC,QAAQ,QAAQ,CAAC,CAC7C,OAAM,IAAI,mBAAmB,oBAAoB,KAAK,OAAO,4BAA4B;AAE3F,MAAI,CAAC,WAAW,KAAK,OAAO,CAAC,MAAM,MAAM,CAAC,CACxC,OAAM,IAAI,mBAAmB,mBAAmB,KAAK,MAAM,wBAAwB;AAErF,OAAK,OAAO,OAAO;AACnB,OAAK,OAAO,qBAAqB,KAAK,OAAO;;CAG/C,MAAa,QAAQ,MAAuD;EAC1E,MAAM,SAAS,MAAM;AACrB,MAAI,CAAC,QAAQ;GACX,MAAM,EAAE,gBAAgB,UAAU,MAAM,KAAK,qBAAqB;AAClE,UAAO,IAAI,qBAAqB;IAC9B,KAAK;IACL,QAAQ,KAAK,KAAK;IAClB;IACA;IACA,QAAQ,KAAK;IACb,OAAO,KAAK;IACb,CAAC;;EAGJ,MAAM,aAAa,MAAM,KAAK,cAAc,OAAO;AACnD,MAAI,CAAC,WACH,OAAM,IAAI,iBAAiB;AAG7B,SAAO,IAAI,qBAAqB;GAC9B,KAAK;IAAE;IAAY;IAAQ;GAC3B,QAAQ,KAAK,KAAK;GAClB,gBAAgB;GAChB,OAAO;GACP,QAAQ,KAAK;GACb,OAAO,KAAK;GACb,CAAC;;CAGJ,MAAc,cAAc,QAA4C;AACtE,SAAO,MAAM,UAAU,QAAQ,KAAK,KAAK;;CAG3C,MAAc,sBAA2D;AACvE,SAAO,MAAM,mBAAmB,KAAK,QAAQ,KAAK,KAAK;;;;;;ACnE3D,IAAa,kBAAb,cAAqC,gBAAgB;CACnD,YAAY,AAAgB,KAAa;AACvC,QAAM,GAAG,IAAI,qBAAqB;EADR;;;;;;ACC9B,eAAsB,oBAAoB,KAA8B;AACtE,KAAI,CAAC,WAAW,KAAK,CAAC,aAAa,CAAC,CAClC,OAAM,IAAI,gBAAgB,IAAI;AAEhC,QAAO,OAAO,UAAU,IAAI"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bananalink-test/client",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "jose": "^6.1.3",
36
36
  "qrcode": "^1.5.4",
37
- "@bananalink-test/sdk-core": "0.4.0"
37
+ "@bananalink-test/sdk-core": "0.5.0"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsdown",