@bananalink-test/client 0.2.0 → 0.5.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 +208 -25
- package/dist/index.d.cts +80 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +80 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +205 -27
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -79,6 +79,14 @@ var InvalidConfigError = class extends BananalinkError {
|
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/errors/InvalidJwtError.ts
|
|
84
|
+
var InvalidJwtError = class extends BananalinkError {
|
|
85
|
+
constructor() {
|
|
86
|
+
super("Invalid Bananalink dapp jwt");
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
82
90
|
//#endregion
|
|
83
91
|
//#region src/jwt/createBananalinkJwks.ts
|
|
84
92
|
function createBananalinkJwks(apiUrl) {
|
|
@@ -149,49 +157,195 @@ var InvalidConnectionStateError = class extends BananalinkError {
|
|
|
149
157
|
}
|
|
150
158
|
};
|
|
151
159
|
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/errors/PendingSessionAbortedError.ts
|
|
162
|
+
var PendingSessionAbortedError = class extends BananalinkError {
|
|
163
|
+
constructor() {
|
|
164
|
+
super("Pending session was intentionally aborted");
|
|
165
|
+
this.name = "PendingSessionAbortedError";
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/api/createDappMessage.ts
|
|
171
|
+
function isCreateDappMessageResponse(body) {
|
|
172
|
+
return typeof body === "object" && body !== null && "messageId" in body && typeof body.messageId === "string";
|
|
173
|
+
}
|
|
174
|
+
async function createDappMessage(apiUrl, accessToken, params) {
|
|
175
|
+
const response = await fetch(`${apiUrl}/dapps/messages`, {
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: {
|
|
178
|
+
Authorization: `Bearer ${accessToken}`,
|
|
179
|
+
"Content-Type": "application/json"
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify(params)
|
|
182
|
+
});
|
|
183
|
+
if (response.ok) {
|
|
184
|
+
const body = await response.json().catch(() => {
|
|
185
|
+
throw new DappApiError("Invalid JSON response from bananalink API", response.status);
|
|
186
|
+
});
|
|
187
|
+
if (!isCreateDappMessageResponse(body)) throw new DappApiError("Unexpected response shape from bananalink API", response.status);
|
|
188
|
+
return body;
|
|
189
|
+
}
|
|
190
|
+
throw new DappApiError("Failed to create dapp message", response.status);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
//#endregion
|
|
194
|
+
//#region src/errors/RequestRejectedError.ts
|
|
195
|
+
var RequestRejectedError = class extends BananalinkError {
|
|
196
|
+
constructor(messageId) {
|
|
197
|
+
super(`Request rejected by wallet (messageId: ${messageId})`);
|
|
198
|
+
this.messageId = messageId;
|
|
199
|
+
this.name = "RequestRejectedError";
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/errors/RequestTimeoutError.ts
|
|
205
|
+
var RequestTimeoutError = class extends BananalinkError {
|
|
206
|
+
constructor(method) {
|
|
207
|
+
super(`Timeout reached for request ${method}`);
|
|
208
|
+
this.method = method;
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/errors/SessionClosedError.ts
|
|
214
|
+
var SessionClosedError = class extends BananalinkError {
|
|
215
|
+
constructor() {
|
|
216
|
+
super("Session is closed");
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
152
220
|
//#endregion
|
|
153
221
|
//#region src/core/BananalinkSession.ts
|
|
154
|
-
var BananalinkSession = class {
|
|
155
|
-
|
|
222
|
+
var BananalinkSession = class BananalinkSession {
|
|
223
|
+
static REQUEST_TIMEOUT_MS = 600 * 1e3;
|
|
224
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
225
|
+
stopListening;
|
|
226
|
+
stopListeningOnClose;
|
|
227
|
+
closed;
|
|
228
|
+
static RESULT_HANDLERS = { eth_sendTransaction: (result) => {
|
|
229
|
+
if (typeof result === "object" && result !== null && "txHash" in result && typeof result.txHash === "string") return result.txHash;
|
|
230
|
+
throw new BananalinkError(`Unexpected eth_sendTransaction result: ${result}`);
|
|
231
|
+
} };
|
|
232
|
+
constructor(sessionClaims, ws, apiUrl) {
|
|
156
233
|
this.sessionClaims = sessionClaims;
|
|
157
234
|
this.ws = ws;
|
|
235
|
+
this.apiUrl = apiUrl;
|
|
236
|
+
this.stopListening = this.ws.onMessage((payload) => {
|
|
237
|
+
if ((0, _bananalink_test_sdk_core.isMessageResponseMessage)(payload)) this.handleMessageResponse(payload);
|
|
238
|
+
});
|
|
239
|
+
this.stopListeningOnClose = this.ws.onClose(() => {
|
|
240
|
+
this.cleanup();
|
|
241
|
+
this.closed = true;
|
|
242
|
+
});
|
|
243
|
+
this.closed = false;
|
|
244
|
+
}
|
|
245
|
+
async request({ method, params, timeoutMs = BananalinkSession.REQUEST_TIMEOUT_MS }) {
|
|
246
|
+
if (this.closed) throw new SessionClosedError();
|
|
247
|
+
const { messageId } = await createDappMessage(this.apiUrl, this.sessionClaims.accessToken, {
|
|
248
|
+
method,
|
|
249
|
+
payload: params
|
|
250
|
+
});
|
|
251
|
+
if (this.closed) throw new SessionClosedError();
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
let timeout;
|
|
254
|
+
if (timeoutMs > 0) timeout = setTimeout(() => {
|
|
255
|
+
this.pendingRequests.delete(messageId);
|
|
256
|
+
reject(new RequestTimeoutError(method));
|
|
257
|
+
}, timeoutMs);
|
|
258
|
+
this.pendingRequests.set(messageId, {
|
|
259
|
+
timeout,
|
|
260
|
+
handle: (messageResponse) => {
|
|
261
|
+
if (messageResponse.status === "rejected") {
|
|
262
|
+
reject(new RequestRejectedError(messageId));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const resultHandler = BananalinkSession.RESULT_HANDLERS[method];
|
|
266
|
+
try {
|
|
267
|
+
resolve(resultHandler(messageResponse.payloadResponse));
|
|
268
|
+
} catch (error) {
|
|
269
|
+
reject(new BananalinkError(`Failed resolving request response, ${error}`));
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
reject
|
|
273
|
+
});
|
|
274
|
+
});
|
|
158
275
|
}
|
|
159
276
|
close() {
|
|
160
|
-
this.
|
|
277
|
+
this.cleanup();
|
|
278
|
+
if (!this.ws.closed) this.ws.close();
|
|
279
|
+
this.closed = true;
|
|
280
|
+
}
|
|
281
|
+
handleMessageResponse(messageResponseMessage) {
|
|
282
|
+
const pending = this.pendingRequests.get(messageResponseMessage.msgId);
|
|
283
|
+
if (!pending) return;
|
|
284
|
+
this.pendingRequests.delete(messageResponseMessage.msgId);
|
|
285
|
+
clearTimeout(pending.timeout);
|
|
286
|
+
pending.handle(messageResponseMessage);
|
|
287
|
+
}
|
|
288
|
+
cleanup() {
|
|
289
|
+
this.rejectAllPendingRequests();
|
|
290
|
+
this.stopListening();
|
|
291
|
+
this.stopListeningOnClose();
|
|
292
|
+
}
|
|
293
|
+
rejectAllPendingRequests() {
|
|
294
|
+
for (const [, pending] of this.pendingRequests) {
|
|
295
|
+
clearTimeout(pending.timeout);
|
|
296
|
+
pending.reject(/* @__PURE__ */ new Error("Pending requests rejected"));
|
|
297
|
+
}
|
|
298
|
+
this.pendingRequests.clear();
|
|
161
299
|
}
|
|
162
300
|
};
|
|
163
301
|
|
|
164
302
|
//#endregion
|
|
165
303
|
//#region src/core/BananalinkConnection.ts
|
|
166
304
|
var BananalinkConnection = class BananalinkConnection {
|
|
167
|
-
static AUTH_TIMEOUT_MILLIS =
|
|
305
|
+
static AUTH_TIMEOUT_MILLIS = 600 * 1e3;
|
|
306
|
+
apiUrl;
|
|
168
307
|
wsUrl;
|
|
169
308
|
jwt;
|
|
170
309
|
dappId;
|
|
171
310
|
dappInstanceId;
|
|
172
311
|
nonce;
|
|
312
|
+
aborting = false;
|
|
313
|
+
abortPendingSessionRequest = null;
|
|
314
|
+
pendingSessionPromise = null;
|
|
173
315
|
constructor(opts) {
|
|
316
|
+
this.apiUrl = opts.apiUrl;
|
|
174
317
|
this.wsUrl = opts.wsUrl;
|
|
175
318
|
this.jwt = opts.jwt;
|
|
176
319
|
this.dappId = opts.dappId;
|
|
177
320
|
this.dappInstanceId = opts.dappInstanceId;
|
|
178
321
|
this.nonce = opts.nonce;
|
|
322
|
+
if (!this.dappInstanceId && !this.jwt) throw new InvalidConnectionStateError("Cannot get session without authorizing a dapp instance or providing a valid JWT");
|
|
179
323
|
}
|
|
180
324
|
async getSession() {
|
|
181
|
-
if (this.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
325
|
+
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);
|
|
193
341
|
}
|
|
194
|
-
|
|
342
|
+
const ws = await this.getTransportHandle(this.dappInstanceId);
|
|
343
|
+
if (this.aborting) {
|
|
344
|
+
ws.close();
|
|
345
|
+
this.aborting = false;
|
|
346
|
+
throw new PendingSessionAbortedError();
|
|
347
|
+
}
|
|
348
|
+
this.pendingSessionPromise = new Promise((resolve, reject) => {
|
|
195
349
|
let settled = false;
|
|
196
350
|
const authTimeout = setTimeout(() => {
|
|
197
351
|
settleReject(new ConnectionTimeoutError());
|
|
@@ -204,7 +358,7 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
204
358
|
message: payload.message,
|
|
205
359
|
signature: payload.signature,
|
|
206
360
|
accessToken: payload.accessToken
|
|
207
|
-
}, ws));
|
|
361
|
+
}, ws, this.apiUrl));
|
|
208
362
|
return;
|
|
209
363
|
}
|
|
210
364
|
if ((0, _bananalink_test_sdk_core.isRejectedMessage)(payload)) settleReject(new ConnectionRejectedError());
|
|
@@ -212,11 +366,17 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
212
366
|
const stopListeningToClose = ws.onClose(() => {
|
|
213
367
|
settleReject(new BananalinkError("Connection closed before authorization completed"));
|
|
214
368
|
});
|
|
215
|
-
|
|
369
|
+
this.abortPendingSessionRequest = () => {
|
|
370
|
+
settleReject(new PendingSessionAbortedError());
|
|
371
|
+
};
|
|
372
|
+
const cleanup = () => {
|
|
373
|
+
this.abortPendingSessionRequest = null;
|
|
216
374
|
clearTimeout(authTimeout);
|
|
217
375
|
stopListeningToMessages();
|
|
218
376
|
stopListeningToClose();
|
|
219
|
-
|
|
377
|
+
this.aborting = false;
|
|
378
|
+
this.pendingSessionPromise = null;
|
|
379
|
+
};
|
|
220
380
|
function settleResolve(session) {
|
|
221
381
|
if (settled) return;
|
|
222
382
|
settled = true;
|
|
@@ -231,6 +391,24 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
231
391
|
reject(error);
|
|
232
392
|
}
|
|
233
393
|
});
|
|
394
|
+
return await this.pendingSessionPromise;
|
|
395
|
+
}
|
|
396
|
+
abortPendingSession() {
|
|
397
|
+
if (this.aborting) return;
|
|
398
|
+
this.aborting = true;
|
|
399
|
+
this.abortPendingSessionRequest?.();
|
|
400
|
+
}
|
|
401
|
+
async getTransportHandle(dappInstanceId) {
|
|
402
|
+
const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ""}`;
|
|
403
|
+
try {
|
|
404
|
+
return await (0, _bananalink_test_sdk_core.connectWebSocket)({
|
|
405
|
+
url,
|
|
406
|
+
pingIntervalMs: 3e4,
|
|
407
|
+
pongTimeoutMs: 5e3
|
|
408
|
+
});
|
|
409
|
+
} catch (error) {
|
|
410
|
+
throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
|
|
411
|
+
}
|
|
234
412
|
}
|
|
235
413
|
get connectionUrl() {
|
|
236
414
|
if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
|
|
@@ -238,9 +416,6 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
238
416
|
const nonce = this.nonce;
|
|
239
417
|
return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
|
|
240
418
|
}
|
|
241
|
-
get authorized() {
|
|
242
|
-
return this.jwt !== void 0;
|
|
243
|
-
}
|
|
244
419
|
static bind(ws, jwt) {
|
|
245
420
|
const bindMessage = {
|
|
246
421
|
type: "bind",
|
|
@@ -269,17 +444,19 @@ var BananalinkClient = class BananalinkClient {
|
|
|
269
444
|
}
|
|
270
445
|
async connect(opts) {
|
|
271
446
|
const rawJwt = opts?.jwt;
|
|
272
|
-
|
|
273
|
-
if (!jwtPayload || !rawJwt) {
|
|
447
|
+
if (!rawJwt) {
|
|
274
448
|
const { dappInstanceId, nonce } = await this.connectDappInstance();
|
|
275
449
|
return new BananalinkConnection({
|
|
276
450
|
jwt: void 0,
|
|
277
451
|
dappId: this.dapp.dappId,
|
|
278
452
|
dappInstanceId,
|
|
279
453
|
nonce,
|
|
454
|
+
apiUrl: this.apiUrl,
|
|
280
455
|
wsUrl: this.wsUrl
|
|
281
456
|
});
|
|
282
457
|
}
|
|
458
|
+
const jwtPayload = await this.getJwtPayload(rawJwt);
|
|
459
|
+
if (!jwtPayload) throw new InvalidJwtError();
|
|
283
460
|
return new BananalinkConnection({
|
|
284
461
|
jwt: {
|
|
285
462
|
jwtPayload,
|
|
@@ -288,6 +465,7 @@ var BananalinkClient = class BananalinkClient {
|
|
|
288
465
|
dappId: this.dapp.dappId,
|
|
289
466
|
dappInstanceId: null,
|
|
290
467
|
nonce: null,
|
|
468
|
+
apiUrl: this.apiUrl,
|
|
291
469
|
wsUrl: this.wsUrl
|
|
292
470
|
});
|
|
293
471
|
}
|
|
@@ -325,4 +503,9 @@ exports.ConnectionTimeoutError = ConnectionTimeoutError;
|
|
|
325
503
|
exports.DappApiError = DappApiError;
|
|
326
504
|
exports.InvalidConfigError = InvalidConfigError;
|
|
327
505
|
exports.InvalidConnectionStateError = InvalidConnectionStateError;
|
|
506
|
+
exports.InvalidJwtError = InvalidJwtError;
|
|
507
|
+
exports.PendingSessionAbortedError = PendingSessionAbortedError;
|
|
508
|
+
exports.RequestRejectedError = RequestRejectedError;
|
|
509
|
+
exports.RequestTimeoutError = RequestTimeoutError;
|
|
510
|
+
exports.SessionClosedError = SessionClosedError;
|
|
328
511
|
exports.displayBananalinkQR = displayBananalinkQR;
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,31 @@ type JwtPayload = {
|
|
|
9
9
|
jwt: string;
|
|
10
10
|
};
|
|
11
11
|
//#endregion
|
|
12
|
+
//#region src/types/TransactionRequest.d.ts
|
|
13
|
+
type TransactionRequest = {
|
|
14
|
+
to: string;
|
|
15
|
+
value?: string;
|
|
16
|
+
data?: string;
|
|
17
|
+
chainId?: number;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/types/RequestMap.d.ts
|
|
21
|
+
type RequestMap = {
|
|
22
|
+
eth_sendTransaction: {
|
|
23
|
+
params: [TransactionRequest];
|
|
24
|
+
result: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/types/Request.d.ts
|
|
29
|
+
type Request = keyof RequestMap;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/types/RequestParams.d.ts
|
|
32
|
+
type RequestParams<T extends keyof RequestMap> = RequestMap[T]['params'];
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/types/RequestResult.d.ts
|
|
35
|
+
type RequestResult<T extends keyof RequestMap> = RequestMap[T]['result'];
|
|
36
|
+
//#endregion
|
|
12
37
|
//#region src/types/SessionClaims.d.ts
|
|
13
38
|
type SessionClaims = {
|
|
14
39
|
address: string;
|
|
@@ -21,8 +46,27 @@ type SessionClaims = {
|
|
|
21
46
|
declare class BananalinkSession {
|
|
22
47
|
readonly sessionClaims: SessionClaims;
|
|
23
48
|
private readonly ws;
|
|
24
|
-
|
|
49
|
+
private readonly apiUrl;
|
|
50
|
+
private static readonly REQUEST_TIMEOUT_MS;
|
|
51
|
+
private readonly pendingRequests;
|
|
52
|
+
private readonly stopListening;
|
|
53
|
+
private readonly stopListeningOnClose;
|
|
54
|
+
private closed;
|
|
55
|
+
private static readonly RESULT_HANDLERS;
|
|
56
|
+
constructor(sessionClaims: SessionClaims, ws: TransportHandle, apiUrl: string);
|
|
57
|
+
request<T extends Request>({
|
|
58
|
+
method,
|
|
59
|
+
params,
|
|
60
|
+
timeoutMs
|
|
61
|
+
}: {
|
|
62
|
+
method: T;
|
|
63
|
+
params?: RequestParams<T>;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}): Promise<RequestResult<T>>;
|
|
25
66
|
close(): void;
|
|
67
|
+
private handleMessageResponse;
|
|
68
|
+
private cleanup;
|
|
69
|
+
private rejectAllPendingRequests;
|
|
26
70
|
}
|
|
27
71
|
//#endregion
|
|
28
72
|
//#region src/core/BananalinkConnection.d.ts
|
|
@@ -32,12 +76,17 @@ type BananalinkDappJwt = {
|
|
|
32
76
|
} | undefined;
|
|
33
77
|
declare class BananalinkConnection {
|
|
34
78
|
private static readonly AUTH_TIMEOUT_MILLIS;
|
|
79
|
+
private readonly apiUrl;
|
|
35
80
|
private readonly wsUrl;
|
|
36
81
|
private readonly jwt;
|
|
37
82
|
private readonly dappId;
|
|
38
83
|
private readonly dappInstanceId;
|
|
39
84
|
private readonly nonce;
|
|
85
|
+
private aborting;
|
|
86
|
+
private abortPendingSessionRequest;
|
|
87
|
+
private pendingSessionPromise;
|
|
40
88
|
constructor(opts: {
|
|
89
|
+
apiUrl: string;
|
|
41
90
|
wsUrl: string;
|
|
42
91
|
jwt: BananalinkDappJwt;
|
|
43
92
|
dappId: string;
|
|
@@ -45,8 +94,9 @@ declare class BananalinkConnection {
|
|
|
45
94
|
nonce: string | null;
|
|
46
95
|
});
|
|
47
96
|
getSession(): Promise<BananalinkSession>;
|
|
97
|
+
abortPendingSession(): void;
|
|
98
|
+
private getTransportHandle;
|
|
48
99
|
get connectionUrl(): string;
|
|
49
|
-
get authorized(): boolean;
|
|
50
100
|
private static bind;
|
|
51
101
|
}
|
|
52
102
|
//#endregion
|
|
@@ -101,8 +151,35 @@ declare class InvalidConnectionStateError extends BananalinkError {
|
|
|
101
151
|
constructor(message: string);
|
|
102
152
|
}
|
|
103
153
|
//#endregion
|
|
154
|
+
//#region src/errors/InvalidJwtError.d.ts
|
|
155
|
+
declare class InvalidJwtError extends BananalinkError {
|
|
156
|
+
constructor();
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/errors/PendingSessionAbortedError.d.ts
|
|
160
|
+
declare class PendingSessionAbortedError extends BananalinkError {
|
|
161
|
+
constructor();
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/errors/RequestRejectedError.d.ts
|
|
165
|
+
declare class RequestRejectedError extends BananalinkError {
|
|
166
|
+
readonly messageId: string;
|
|
167
|
+
constructor(messageId: string);
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/errors/RequestTimeoutError.d.ts
|
|
171
|
+
declare class RequestTimeoutError extends BananalinkError {
|
|
172
|
+
readonly method: Request;
|
|
173
|
+
constructor(method: Request);
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/errors/SessionClosedError.d.ts
|
|
177
|
+
declare class SessionClosedError extends BananalinkError {
|
|
178
|
+
constructor();
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
104
181
|
//#region src/utils/displayBananalinkQR.d.ts
|
|
105
182
|
declare function displayBananalinkQR(url: string): Promise<string>;
|
|
106
183
|
//#endregion
|
|
107
|
-
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
184
|
+
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, type TransactionRequest, displayBananalinkQR };
|
|
108
185
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/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;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/TransactionRequest.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
|
@@ -9,6 +9,31 @@ type JwtPayload = {
|
|
|
9
9
|
jwt: string;
|
|
10
10
|
};
|
|
11
11
|
//#endregion
|
|
12
|
+
//#region src/types/TransactionRequest.d.ts
|
|
13
|
+
type TransactionRequest = {
|
|
14
|
+
to: string;
|
|
15
|
+
value?: string;
|
|
16
|
+
data?: string;
|
|
17
|
+
chainId?: number;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/types/RequestMap.d.ts
|
|
21
|
+
type RequestMap = {
|
|
22
|
+
eth_sendTransaction: {
|
|
23
|
+
params: [TransactionRequest];
|
|
24
|
+
result: string;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/types/Request.d.ts
|
|
29
|
+
type Request = keyof RequestMap;
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/types/RequestParams.d.ts
|
|
32
|
+
type RequestParams<T extends keyof RequestMap> = RequestMap[T]['params'];
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/types/RequestResult.d.ts
|
|
35
|
+
type RequestResult<T extends keyof RequestMap> = RequestMap[T]['result'];
|
|
36
|
+
//#endregion
|
|
12
37
|
//#region src/types/SessionClaims.d.ts
|
|
13
38
|
type SessionClaims = {
|
|
14
39
|
address: string;
|
|
@@ -21,8 +46,27 @@ type SessionClaims = {
|
|
|
21
46
|
declare class BananalinkSession {
|
|
22
47
|
readonly sessionClaims: SessionClaims;
|
|
23
48
|
private readonly ws;
|
|
24
|
-
|
|
49
|
+
private readonly apiUrl;
|
|
50
|
+
private static readonly REQUEST_TIMEOUT_MS;
|
|
51
|
+
private readonly pendingRequests;
|
|
52
|
+
private readonly stopListening;
|
|
53
|
+
private readonly stopListeningOnClose;
|
|
54
|
+
private closed;
|
|
55
|
+
private static readonly RESULT_HANDLERS;
|
|
56
|
+
constructor(sessionClaims: SessionClaims, ws: TransportHandle, apiUrl: string);
|
|
57
|
+
request<T extends Request>({
|
|
58
|
+
method,
|
|
59
|
+
params,
|
|
60
|
+
timeoutMs
|
|
61
|
+
}: {
|
|
62
|
+
method: T;
|
|
63
|
+
params?: RequestParams<T>;
|
|
64
|
+
timeoutMs?: number;
|
|
65
|
+
}): Promise<RequestResult<T>>;
|
|
25
66
|
close(): void;
|
|
67
|
+
private handleMessageResponse;
|
|
68
|
+
private cleanup;
|
|
69
|
+
private rejectAllPendingRequests;
|
|
26
70
|
}
|
|
27
71
|
//#endregion
|
|
28
72
|
//#region src/core/BananalinkConnection.d.ts
|
|
@@ -32,12 +76,17 @@ type BananalinkDappJwt = {
|
|
|
32
76
|
} | undefined;
|
|
33
77
|
declare class BananalinkConnection {
|
|
34
78
|
private static readonly AUTH_TIMEOUT_MILLIS;
|
|
79
|
+
private readonly apiUrl;
|
|
35
80
|
private readonly wsUrl;
|
|
36
81
|
private readonly jwt;
|
|
37
82
|
private readonly dappId;
|
|
38
83
|
private readonly dappInstanceId;
|
|
39
84
|
private readonly nonce;
|
|
85
|
+
private aborting;
|
|
86
|
+
private abortPendingSessionRequest;
|
|
87
|
+
private pendingSessionPromise;
|
|
40
88
|
constructor(opts: {
|
|
89
|
+
apiUrl: string;
|
|
41
90
|
wsUrl: string;
|
|
42
91
|
jwt: BananalinkDappJwt;
|
|
43
92
|
dappId: string;
|
|
@@ -45,8 +94,9 @@ declare class BananalinkConnection {
|
|
|
45
94
|
nonce: string | null;
|
|
46
95
|
});
|
|
47
96
|
getSession(): Promise<BananalinkSession>;
|
|
97
|
+
abortPendingSession(): void;
|
|
98
|
+
private getTransportHandle;
|
|
48
99
|
get connectionUrl(): string;
|
|
49
|
-
get authorized(): boolean;
|
|
50
100
|
private static bind;
|
|
51
101
|
}
|
|
52
102
|
//#endregion
|
|
@@ -101,8 +151,35 @@ declare class InvalidConnectionStateError extends BananalinkError {
|
|
|
101
151
|
constructor(message: string);
|
|
102
152
|
}
|
|
103
153
|
//#endregion
|
|
154
|
+
//#region src/errors/InvalidJwtError.d.ts
|
|
155
|
+
declare class InvalidJwtError extends BananalinkError {
|
|
156
|
+
constructor();
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/errors/PendingSessionAbortedError.d.ts
|
|
160
|
+
declare class PendingSessionAbortedError extends BananalinkError {
|
|
161
|
+
constructor();
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/errors/RequestRejectedError.d.ts
|
|
165
|
+
declare class RequestRejectedError extends BananalinkError {
|
|
166
|
+
readonly messageId: string;
|
|
167
|
+
constructor(messageId: string);
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/errors/RequestTimeoutError.d.ts
|
|
171
|
+
declare class RequestTimeoutError extends BananalinkError {
|
|
172
|
+
readonly method: Request;
|
|
173
|
+
constructor(method: Request);
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/errors/SessionClosedError.d.ts
|
|
177
|
+
declare class SessionClosedError extends BananalinkError {
|
|
178
|
+
constructor();
|
|
179
|
+
}
|
|
180
|
+
//#endregion
|
|
104
181
|
//#region src/utils/displayBananalinkQR.d.ts
|
|
105
182
|
declare function displayBananalinkQR(url: string): Promise<string>;
|
|
106
183
|
//#endregion
|
|
107
|
-
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, type Dapp, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
184
|
+
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, type TransactionRequest, displayBananalinkQR };
|
|
108
185
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/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;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types/JwtPayload.ts","../src/types/TransactionRequest.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, isRejectedMessage } from "@bananalink-test/sdk-core";
|
|
2
|
+
import { connectWebSocket, isAuthorizedMessage, isMessageResponseMessage, isRejectedMessage } from "@bananalink-test/sdk-core";
|
|
3
3
|
import QRCode from "qrcode";
|
|
4
4
|
|
|
5
5
|
//#region src/errors/BananalinkError.ts
|
|
@@ -50,6 +50,14 @@ var InvalidConfigError = class extends BananalinkError {
|
|
|
50
50
|
}
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/errors/InvalidJwtError.ts
|
|
55
|
+
var InvalidJwtError = class extends BananalinkError {
|
|
56
|
+
constructor() {
|
|
57
|
+
super("Invalid Bananalink dapp jwt");
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
53
61
|
//#endregion
|
|
54
62
|
//#region src/jwt/createBananalinkJwks.ts
|
|
55
63
|
function createBananalinkJwks(apiUrl) {
|
|
@@ -120,49 +128,195 @@ var InvalidConnectionStateError = class extends BananalinkError {
|
|
|
120
128
|
}
|
|
121
129
|
};
|
|
122
130
|
|
|
131
|
+
//#endregion
|
|
132
|
+
//#region src/errors/PendingSessionAbortedError.ts
|
|
133
|
+
var PendingSessionAbortedError = class extends BananalinkError {
|
|
134
|
+
constructor() {
|
|
135
|
+
super("Pending session was intentionally aborted");
|
|
136
|
+
this.name = "PendingSessionAbortedError";
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/api/createDappMessage.ts
|
|
142
|
+
function isCreateDappMessageResponse(body) {
|
|
143
|
+
return typeof body === "object" && body !== null && "messageId" in body && typeof body.messageId === "string";
|
|
144
|
+
}
|
|
145
|
+
async function createDappMessage(apiUrl, accessToken, params) {
|
|
146
|
+
const response = await fetch(`${apiUrl}/dapps/messages`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${accessToken}`,
|
|
150
|
+
"Content-Type": "application/json"
|
|
151
|
+
},
|
|
152
|
+
body: JSON.stringify(params)
|
|
153
|
+
});
|
|
154
|
+
if (response.ok) {
|
|
155
|
+
const body = await response.json().catch(() => {
|
|
156
|
+
throw new DappApiError("Invalid JSON response from bananalink API", response.status);
|
|
157
|
+
});
|
|
158
|
+
if (!isCreateDappMessageResponse(body)) throw new DappApiError("Unexpected response shape from bananalink API", response.status);
|
|
159
|
+
return body;
|
|
160
|
+
}
|
|
161
|
+
throw new DappApiError("Failed to create dapp message", response.status);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/errors/RequestRejectedError.ts
|
|
166
|
+
var RequestRejectedError = class extends BananalinkError {
|
|
167
|
+
constructor(messageId) {
|
|
168
|
+
super(`Request rejected by wallet (messageId: ${messageId})`);
|
|
169
|
+
this.messageId = messageId;
|
|
170
|
+
this.name = "RequestRejectedError";
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/errors/RequestTimeoutError.ts
|
|
176
|
+
var RequestTimeoutError = class extends BananalinkError {
|
|
177
|
+
constructor(method) {
|
|
178
|
+
super(`Timeout reached for request ${method}`);
|
|
179
|
+
this.method = method;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/errors/SessionClosedError.ts
|
|
185
|
+
var SessionClosedError = class extends BananalinkError {
|
|
186
|
+
constructor() {
|
|
187
|
+
super("Session is closed");
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
123
191
|
//#endregion
|
|
124
192
|
//#region src/core/BananalinkSession.ts
|
|
125
|
-
var BananalinkSession = class {
|
|
126
|
-
|
|
193
|
+
var BananalinkSession = class BananalinkSession {
|
|
194
|
+
static REQUEST_TIMEOUT_MS = 600 * 1e3;
|
|
195
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
196
|
+
stopListening;
|
|
197
|
+
stopListeningOnClose;
|
|
198
|
+
closed;
|
|
199
|
+
static RESULT_HANDLERS = { eth_sendTransaction: (result) => {
|
|
200
|
+
if (typeof result === "object" && result !== null && "txHash" in result && typeof result.txHash === "string") return result.txHash;
|
|
201
|
+
throw new BananalinkError(`Unexpected eth_sendTransaction result: ${result}`);
|
|
202
|
+
} };
|
|
203
|
+
constructor(sessionClaims, ws, apiUrl) {
|
|
127
204
|
this.sessionClaims = sessionClaims;
|
|
128
205
|
this.ws = ws;
|
|
206
|
+
this.apiUrl = apiUrl;
|
|
207
|
+
this.stopListening = this.ws.onMessage((payload) => {
|
|
208
|
+
if (isMessageResponseMessage(payload)) this.handleMessageResponse(payload);
|
|
209
|
+
});
|
|
210
|
+
this.stopListeningOnClose = this.ws.onClose(() => {
|
|
211
|
+
this.cleanup();
|
|
212
|
+
this.closed = true;
|
|
213
|
+
});
|
|
214
|
+
this.closed = false;
|
|
215
|
+
}
|
|
216
|
+
async request({ method, params, timeoutMs = BananalinkSession.REQUEST_TIMEOUT_MS }) {
|
|
217
|
+
if (this.closed) throw new SessionClosedError();
|
|
218
|
+
const { messageId } = await createDappMessage(this.apiUrl, this.sessionClaims.accessToken, {
|
|
219
|
+
method,
|
|
220
|
+
payload: params
|
|
221
|
+
});
|
|
222
|
+
if (this.closed) throw new SessionClosedError();
|
|
223
|
+
return new Promise((resolve, reject) => {
|
|
224
|
+
let timeout;
|
|
225
|
+
if (timeoutMs > 0) timeout = setTimeout(() => {
|
|
226
|
+
this.pendingRequests.delete(messageId);
|
|
227
|
+
reject(new RequestTimeoutError(method));
|
|
228
|
+
}, timeoutMs);
|
|
229
|
+
this.pendingRequests.set(messageId, {
|
|
230
|
+
timeout,
|
|
231
|
+
handle: (messageResponse) => {
|
|
232
|
+
if (messageResponse.status === "rejected") {
|
|
233
|
+
reject(new RequestRejectedError(messageId));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const resultHandler = BananalinkSession.RESULT_HANDLERS[method];
|
|
237
|
+
try {
|
|
238
|
+
resolve(resultHandler(messageResponse.payloadResponse));
|
|
239
|
+
} catch (error) {
|
|
240
|
+
reject(new BananalinkError(`Failed resolving request response, ${error}`));
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
reject
|
|
244
|
+
});
|
|
245
|
+
});
|
|
129
246
|
}
|
|
130
247
|
close() {
|
|
131
|
-
this.
|
|
248
|
+
this.cleanup();
|
|
249
|
+
if (!this.ws.closed) this.ws.close();
|
|
250
|
+
this.closed = true;
|
|
251
|
+
}
|
|
252
|
+
handleMessageResponse(messageResponseMessage) {
|
|
253
|
+
const pending = this.pendingRequests.get(messageResponseMessage.msgId);
|
|
254
|
+
if (!pending) return;
|
|
255
|
+
this.pendingRequests.delete(messageResponseMessage.msgId);
|
|
256
|
+
clearTimeout(pending.timeout);
|
|
257
|
+
pending.handle(messageResponseMessage);
|
|
258
|
+
}
|
|
259
|
+
cleanup() {
|
|
260
|
+
this.rejectAllPendingRequests();
|
|
261
|
+
this.stopListening();
|
|
262
|
+
this.stopListeningOnClose();
|
|
263
|
+
}
|
|
264
|
+
rejectAllPendingRequests() {
|
|
265
|
+
for (const [, pending] of this.pendingRequests) {
|
|
266
|
+
clearTimeout(pending.timeout);
|
|
267
|
+
pending.reject(/* @__PURE__ */ new Error("Pending requests rejected"));
|
|
268
|
+
}
|
|
269
|
+
this.pendingRequests.clear();
|
|
132
270
|
}
|
|
133
271
|
};
|
|
134
272
|
|
|
135
273
|
//#endregion
|
|
136
274
|
//#region src/core/BananalinkConnection.ts
|
|
137
275
|
var BananalinkConnection = class BananalinkConnection {
|
|
138
|
-
static AUTH_TIMEOUT_MILLIS =
|
|
276
|
+
static AUTH_TIMEOUT_MILLIS = 600 * 1e3;
|
|
277
|
+
apiUrl;
|
|
139
278
|
wsUrl;
|
|
140
279
|
jwt;
|
|
141
280
|
dappId;
|
|
142
281
|
dappInstanceId;
|
|
143
282
|
nonce;
|
|
283
|
+
aborting = false;
|
|
284
|
+
abortPendingSessionRequest = null;
|
|
285
|
+
pendingSessionPromise = null;
|
|
144
286
|
constructor(opts) {
|
|
287
|
+
this.apiUrl = opts.apiUrl;
|
|
145
288
|
this.wsUrl = opts.wsUrl;
|
|
146
289
|
this.jwt = opts.jwt;
|
|
147
290
|
this.dappId = opts.dappId;
|
|
148
291
|
this.dappInstanceId = opts.dappInstanceId;
|
|
149
292
|
this.nonce = opts.nonce;
|
|
293
|
+
if (!this.dappInstanceId && !this.jwt) throw new InvalidConnectionStateError("Cannot get session without authorizing a dapp instance or providing a valid JWT");
|
|
150
294
|
}
|
|
151
295
|
async getSession() {
|
|
152
|
-
if (this.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
296
|
+
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);
|
|
164
312
|
}
|
|
165
|
-
|
|
313
|
+
const ws = await this.getTransportHandle(this.dappInstanceId);
|
|
314
|
+
if (this.aborting) {
|
|
315
|
+
ws.close();
|
|
316
|
+
this.aborting = false;
|
|
317
|
+
throw new PendingSessionAbortedError();
|
|
318
|
+
}
|
|
319
|
+
this.pendingSessionPromise = new Promise((resolve, reject) => {
|
|
166
320
|
let settled = false;
|
|
167
321
|
const authTimeout = setTimeout(() => {
|
|
168
322
|
settleReject(new ConnectionTimeoutError());
|
|
@@ -175,7 +329,7 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
175
329
|
message: payload.message,
|
|
176
330
|
signature: payload.signature,
|
|
177
331
|
accessToken: payload.accessToken
|
|
178
|
-
}, ws));
|
|
332
|
+
}, ws, this.apiUrl));
|
|
179
333
|
return;
|
|
180
334
|
}
|
|
181
335
|
if (isRejectedMessage(payload)) settleReject(new ConnectionRejectedError());
|
|
@@ -183,11 +337,17 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
183
337
|
const stopListeningToClose = ws.onClose(() => {
|
|
184
338
|
settleReject(new BananalinkError("Connection closed before authorization completed"));
|
|
185
339
|
});
|
|
186
|
-
|
|
340
|
+
this.abortPendingSessionRequest = () => {
|
|
341
|
+
settleReject(new PendingSessionAbortedError());
|
|
342
|
+
};
|
|
343
|
+
const cleanup = () => {
|
|
344
|
+
this.abortPendingSessionRequest = null;
|
|
187
345
|
clearTimeout(authTimeout);
|
|
188
346
|
stopListeningToMessages();
|
|
189
347
|
stopListeningToClose();
|
|
190
|
-
|
|
348
|
+
this.aborting = false;
|
|
349
|
+
this.pendingSessionPromise = null;
|
|
350
|
+
};
|
|
191
351
|
function settleResolve(session) {
|
|
192
352
|
if (settled) return;
|
|
193
353
|
settled = true;
|
|
@@ -202,6 +362,24 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
202
362
|
reject(error);
|
|
203
363
|
}
|
|
204
364
|
});
|
|
365
|
+
return await this.pendingSessionPromise;
|
|
366
|
+
}
|
|
367
|
+
abortPendingSession() {
|
|
368
|
+
if (this.aborting) return;
|
|
369
|
+
this.aborting = true;
|
|
370
|
+
this.abortPendingSessionRequest?.();
|
|
371
|
+
}
|
|
372
|
+
async getTransportHandle(dappInstanceId) {
|
|
373
|
+
const url = `${this.wsUrl}${dappInstanceId != null ? `?dappInstanceId=${encodeURIComponent(dappInstanceId)}` : ""}`;
|
|
374
|
+
try {
|
|
375
|
+
return await connectWebSocket({
|
|
376
|
+
url,
|
|
377
|
+
pingIntervalMs: 3e4,
|
|
378
|
+
pongTimeoutMs: 5e3
|
|
379
|
+
});
|
|
380
|
+
} catch (error) {
|
|
381
|
+
throw new BananalinkError("Unable to establish dapp websocket connection", { cause: error });
|
|
382
|
+
}
|
|
205
383
|
}
|
|
206
384
|
get connectionUrl() {
|
|
207
385
|
if (!this.dappInstanceId || !this.nonce) throw new InvalidConnectionStateError("Cannot get connection URL without a dapp instance");
|
|
@@ -209,9 +387,6 @@ var BananalinkConnection = class BananalinkConnection {
|
|
|
209
387
|
const nonce = this.nonce;
|
|
210
388
|
return `bananalink://?dappId=${encodeURIComponent(this.dappId)}&dappInstanceId=${encodeURIComponent(dappInstanceId)}&nonce=${encodeURIComponent(nonce)}`;
|
|
211
389
|
}
|
|
212
|
-
get authorized() {
|
|
213
|
-
return this.jwt !== void 0;
|
|
214
|
-
}
|
|
215
390
|
static bind(ws, jwt) {
|
|
216
391
|
const bindMessage = {
|
|
217
392
|
type: "bind",
|
|
@@ -240,17 +415,19 @@ var BananalinkClient = class BananalinkClient {
|
|
|
240
415
|
}
|
|
241
416
|
async connect(opts) {
|
|
242
417
|
const rawJwt = opts?.jwt;
|
|
243
|
-
|
|
244
|
-
if (!jwtPayload || !rawJwt) {
|
|
418
|
+
if (!rawJwt) {
|
|
245
419
|
const { dappInstanceId, nonce } = await this.connectDappInstance();
|
|
246
420
|
return new BananalinkConnection({
|
|
247
421
|
jwt: void 0,
|
|
248
422
|
dappId: this.dapp.dappId,
|
|
249
423
|
dappInstanceId,
|
|
250
424
|
nonce,
|
|
425
|
+
apiUrl: this.apiUrl,
|
|
251
426
|
wsUrl: this.wsUrl
|
|
252
427
|
});
|
|
253
428
|
}
|
|
429
|
+
const jwtPayload = await this.getJwtPayload(rawJwt);
|
|
430
|
+
if (!jwtPayload) throw new InvalidJwtError();
|
|
254
431
|
return new BananalinkConnection({
|
|
255
432
|
jwt: {
|
|
256
433
|
jwtPayload,
|
|
@@ -259,6 +436,7 @@ var BananalinkClient = class BananalinkClient {
|
|
|
259
436
|
dappId: this.dapp.dappId,
|
|
260
437
|
dappInstanceId: null,
|
|
261
438
|
nonce: null,
|
|
439
|
+
apiUrl: this.apiUrl,
|
|
262
440
|
wsUrl: this.wsUrl
|
|
263
441
|
});
|
|
264
442
|
}
|
|
@@ -287,5 +465,5 @@ async function displayBananalinkQR(url) {
|
|
|
287
465
|
}
|
|
288
466
|
|
|
289
467
|
//#endregion
|
|
290
|
-
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, DappApiError, InvalidConfigError, InvalidConnectionStateError, displayBananalinkQR };
|
|
468
|
+
export { BananalinkClient, BananalinkConnection, BananalinkError, BananalinkSession, ConnectionRejectedError, ConnectionTimeoutError, DappApiError, InvalidConfigError, InvalidConnectionStateError, InvalidJwtError, PendingSessionAbortedError, RequestRejectedError, RequestTimeoutError, SessionClosedError, displayBananalinkQR };
|
|
291
469
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/errors/BananalinkError.ts","../src/errors/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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"}
|
|
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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bananalink-test/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.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.
|
|
37
|
+
"@bananalink-test/sdk-core": "0.3.1"
|
|
38
38
|
},
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "tsdown",
|