@byoky/sdk 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 byoky contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.cjs CHANGED
@@ -69,8 +69,9 @@ function createProxyFetch(providerId, sessionKey) {
69
69
  cleanup();
70
70
  reject(new Error("Proxy request timed out"));
71
71
  }, 12e4);
72
- function handleEvent(event) {
73
- const data = event.detail ?? event.data;
72
+ const channel = new MessageChannel();
73
+ channel.port1.onmessage = (event) => {
74
+ const data = event.data;
74
75
  if (data?.requestId !== requestId) return;
75
76
  switch (data.type) {
76
77
  case "BYOKY_PROXY_RESPONSE_META":
@@ -114,12 +115,11 @@ function createProxyFetch(providerId, sessionKey) {
114
115
  break;
115
116
  }
116
117
  }
117
- }
118
+ };
118
119
  function cleanup() {
119
120
  clearTimeout(timeout);
120
- document.removeEventListener("byoky-message", handleEvent);
121
+ channel.port1.close();
121
122
  }
122
- document.addEventListener("byoky-message", handleEvent);
123
123
  window.postMessage(
124
124
  {
125
125
  type: "BYOKY_PROXY_REQUEST",
@@ -131,7 +131,8 @@ function createProxyFetch(providerId, sessionKey) {
131
131
  headers,
132
132
  body
133
133
  },
134
- "*"
134
+ window.location.origin,
135
+ [channel.port2]
135
136
  );
136
137
  });
137
138
  };
@@ -161,6 +162,115 @@ async function readBody(body) {
161
162
  return void 0;
162
163
  }
163
164
 
165
+ // src/relay-fetch.ts
166
+ function createRelayFetch(ws, providerId) {
167
+ return async (input, init) => {
168
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
169
+ const method = init?.method ?? "GET";
170
+ const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
171
+ const body = init?.body ? await readBody2(init.body) : void 0;
172
+ const requestId = crypto.randomUUID();
173
+ return new Promise((resolve, reject) => {
174
+ const { readable, writable } = new TransformStream();
175
+ const writer = writable.getWriter();
176
+ const encoder = new TextEncoder();
177
+ let resolved = false;
178
+ const timeout = setTimeout(() => {
179
+ cleanup();
180
+ reject(new Error("Relay proxy request timed out"));
181
+ }, 12e4);
182
+ function handleMessage(event) {
183
+ let data;
184
+ try {
185
+ data = JSON.parse(event.data);
186
+ } catch {
187
+ return;
188
+ }
189
+ if (data.requestId !== requestId) return;
190
+ switch (data.type) {
191
+ case "relay:response:meta":
192
+ if (!resolved) {
193
+ resolved = true;
194
+ clearTimeout(timeout);
195
+ resolve(
196
+ new Response(readable, {
197
+ status: data.status,
198
+ statusText: data.statusText ?? "",
199
+ headers: new Headers(data.headers)
200
+ })
201
+ );
202
+ }
203
+ break;
204
+ case "relay:response:chunk":
205
+ writer.write(encoder.encode(data.chunk)).catch(() => {
206
+ });
207
+ break;
208
+ case "relay:response:done":
209
+ writer.close().catch(() => {
210
+ });
211
+ cleanup();
212
+ break;
213
+ case "relay:response:error": {
214
+ const err = data.error;
215
+ const message = typeof err === "string" ? err : err?.message ?? "Relay proxy error";
216
+ const errResponse = new Response(
217
+ JSON.stringify({ error: { message, code: typeof err === "object" ? err?.code : "RELAY_ERROR" } }),
218
+ { status: 500, headers: { "content-type": "application/json" } }
219
+ );
220
+ if (!resolved) {
221
+ resolved = true;
222
+ clearTimeout(timeout);
223
+ resolve(errResponse);
224
+ }
225
+ writer.close().catch(() => {
226
+ });
227
+ cleanup();
228
+ break;
229
+ }
230
+ }
231
+ }
232
+ function cleanup() {
233
+ clearTimeout(timeout);
234
+ ws.removeEventListener("message", handleMessage);
235
+ }
236
+ ws.addEventListener("message", handleMessage);
237
+ ws.send(JSON.stringify({
238
+ type: "relay:request",
239
+ requestId,
240
+ providerId,
241
+ url,
242
+ method,
243
+ headers,
244
+ body
245
+ }));
246
+ });
247
+ };
248
+ }
249
+ async function readBody2(body) {
250
+ if (typeof body === "string") return body;
251
+ if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
252
+ if (body instanceof Blob) return body.text();
253
+ if (body instanceof URLSearchParams) return body.toString();
254
+ if (body instanceof ReadableStream) {
255
+ const reader = body.getReader();
256
+ const chunks = [];
257
+ for (; ; ) {
258
+ const { done, value } = await reader.read();
259
+ if (done) break;
260
+ chunks.push(value);
261
+ }
262
+ const total = chunks.reduce((acc, c) => acc + c.length, 0);
263
+ const combined = new Uint8Array(total);
264
+ let offset = 0;
265
+ for (const chunk of chunks) {
266
+ combined.set(chunk, offset);
267
+ offset += chunk.length;
268
+ }
269
+ return new TextDecoder().decode(combined);
270
+ }
271
+ return void 0;
272
+ }
273
+
164
274
  // src/relay-client.ts
165
275
  var import_core2 = require("@byoky/core");
166
276
  function createRelayClient(wsUrl, sessionKey, providers) {
@@ -172,7 +282,7 @@ function createRelayClient(wsUrl, sessionKey, providers) {
172
282
  try {
173
283
  const parsed = new URL(wsUrl);
174
284
  const isSecure = parsed.protocol === "wss:";
175
- const isLocalWs = parsed.protocol === "ws:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1");
285
+ const isLocalWs = parsed.protocol === "ws:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]");
176
286
  if (!isSecure && !isLocalWs) {
177
287
  status = "disconnected";
178
288
  return {
@@ -343,9 +453,16 @@ function createRelayClient(wsUrl, sessionKey, providers) {
343
453
  // src/byoky.ts
344
454
  var Byoky = class {
345
455
  timeout;
456
+ relayUrl;
346
457
  constructor(options = {}) {
347
458
  this.timeout = options.timeout ?? 6e4;
459
+ this.relayUrl = options.relayUrl ?? "wss://relay.byoky.com";
348
460
  }
461
+ /**
462
+ * Connect to a Byoky wallet. Tries the browser extension first.
463
+ * If no extension is found and `onPairingReady` is provided,
464
+ * falls back to relay mode — pairing with a mobile wallet app.
465
+ */
349
466
  async connect(request = {}) {
350
467
  if (typeof window === "undefined") {
351
468
  throw new import_core3.ByokyError(
@@ -353,15 +470,22 @@ var Byoky = class {
353
470
  "Byoky SDK requires a browser environment. On the server, use your API key directly."
354
471
  );
355
472
  }
356
- if (!isExtensionInstalled()) {
357
- const storeUrl = getStoreUrl();
358
- if (storeUrl) {
359
- window.open(storeUrl, "_blank");
360
- }
361
- throw import_core3.ByokyError.walletNotInstalled();
473
+ const { onPairingReady, useRelay, ...connectRequest } = request;
474
+ if (useRelay && onPairingReady) {
475
+ return this.connectViaRelay(connectRequest, onPairingReady);
476
+ }
477
+ if (isExtensionInstalled()) {
478
+ const response = await this.sendConnectRequest(connectRequest);
479
+ return this.buildSession(response);
480
+ }
481
+ if (onPairingReady) {
482
+ return this.connectViaRelay(connectRequest, onPairingReady);
362
483
  }
363
- const response = await this.sendConnectRequest(request);
364
- return this.buildSession(response);
484
+ const storeUrl = getStoreUrl();
485
+ if (storeUrl) {
486
+ window.open(storeUrl, "_blank");
487
+ }
488
+ throw import_core3.ByokyError.walletNotInstalled();
365
489
  }
366
490
  /**
367
491
  * Reconnect to an existing session using previously stored response data.
@@ -374,24 +498,139 @@ var Byoky = class {
374
498
  if (!connected) return null;
375
499
  return this.buildSession(savedResponse);
376
500
  }
501
+ // --- Relay pairing ---
502
+ connectViaRelay(request, onPairingReady) {
503
+ return new Promise((resolve, reject) => {
504
+ const roomId = crypto.randomUUID();
505
+ const authToken = Array.from(crypto.getRandomValues(new Uint8Array(32))).map((b) => b.toString(16).padStart(2, "0")).join("");
506
+ const pairingCode = (0, import_core3.encodePairPayload)({
507
+ v: 1,
508
+ r: this.relayUrl,
509
+ id: roomId,
510
+ t: authToken,
511
+ o: window.location.origin
512
+ });
513
+ const ws = new WebSocket(this.relayUrl);
514
+ const timeout = setTimeout(() => {
515
+ ws.close();
516
+ reject(new import_core3.ByokyError(import_core3.ByokyErrorCode.UNKNOWN, "Pairing timed out. No wallet connected."));
517
+ }, this.timeout);
518
+ ws.onopen = () => {
519
+ ws.send(JSON.stringify({
520
+ type: "relay:auth",
521
+ roomId,
522
+ authToken,
523
+ role: "recipient"
524
+ }));
525
+ };
526
+ ws.onmessage = (event) => {
527
+ let msg;
528
+ try {
529
+ msg = JSON.parse(event.data);
530
+ } catch {
531
+ return;
532
+ }
533
+ switch (msg.type) {
534
+ case "relay:auth:result":
535
+ if (msg.success) {
536
+ onPairingReady(pairingCode);
537
+ } else {
538
+ clearTimeout(timeout);
539
+ ws.close();
540
+ reject(new import_core3.ByokyError(import_core3.ByokyErrorCode.UNKNOWN, `Relay auth failed: ${msg.error}`));
541
+ }
542
+ break;
543
+ case "relay:pair:hello": {
544
+ clearTimeout(timeout);
545
+ const providers = msg.providers;
546
+ ws.send(JSON.stringify({ type: "relay:pair:ack" }));
547
+ resolve(this.buildRelaySession(ws, roomId, providers));
548
+ break;
549
+ }
550
+ case "relay:peer:status":
551
+ break;
552
+ }
553
+ };
554
+ ws.onerror = () => {
555
+ clearTimeout(timeout);
556
+ reject(new import_core3.ByokyError(import_core3.ByokyErrorCode.UNKNOWN, "Failed to connect to relay server"));
557
+ };
558
+ ws.onclose = () => {
559
+ clearTimeout(timeout);
560
+ };
561
+ });
562
+ }
563
+ buildRelaySession(ws, roomId, providers) {
564
+ const sessionKey = `relay_${roomId}`;
565
+ const disconnectCallbacks = /* @__PURE__ */ new Set();
566
+ const pingInterval = setInterval(() => {
567
+ if (ws.readyState === WebSocket.OPEN) {
568
+ ws.send(JSON.stringify({ type: "relay:ping", ts: Date.now() }));
569
+ }
570
+ }, 3e4);
571
+ ws.addEventListener("close", () => {
572
+ clearInterval(pingInterval);
573
+ for (const cb of disconnectCallbacks) cb();
574
+ disconnectCallbacks.clear();
575
+ });
576
+ ws.addEventListener("message", (event) => {
577
+ try {
578
+ const msg = JSON.parse(event.data);
579
+ if (msg.type === "relay:peer:status" && msg.online === false) {
580
+ for (const cb of disconnectCallbacks) cb();
581
+ disconnectCallbacks.clear();
582
+ clearInterval(pingInterval);
583
+ ws.close(1e3, "Phone disconnected");
584
+ }
585
+ } catch {
586
+ }
587
+ });
588
+ return {
589
+ sessionKey,
590
+ proxyUrl: "",
591
+ providers,
592
+ createFetch: (providerId) => createRelayFetch(ws, providerId),
593
+ createRelay: () => {
594
+ throw new Error("Relay-in-relay not supported");
595
+ },
596
+ disconnect: () => {
597
+ clearInterval(pingInterval);
598
+ ws.close(1e3, "Client disconnected");
599
+ },
600
+ isConnected: async () => ws.readyState === WebSocket.OPEN,
601
+ getUsage: async () => ({ requests: 0, inputTokens: 0, outputTokens: 0, byProvider: {} }),
602
+ onDisconnect: (callback) => {
603
+ disconnectCallbacks.add(callback);
604
+ return () => {
605
+ disconnectCallbacks.delete(callback);
606
+ };
607
+ }
608
+ };
609
+ }
610
+ // --- Extension-based session ---
377
611
  buildSession(response) {
378
612
  const sessionKey = response.sessionKey;
379
613
  const disconnectCallbacks = /* @__PURE__ */ new Set();
380
- function handleRevocation(event) {
381
- const msg = event.detail;
614
+ const notifyChannel = new MessageChannel();
615
+ notifyChannel.port1.onmessage = (event) => {
616
+ const msg = event.data;
382
617
  if (msg?.type === "BYOKY_SESSION_REVOKED" && msg.payload?.sessionKey === sessionKey) {
383
618
  for (const cb of disconnectCallbacks) cb();
384
619
  disconnectCallbacks.clear();
385
- document.removeEventListener("byoky-message", handleRevocation);
620
+ notifyChannel.port1.close();
386
621
  }
387
- }
388
- document.addEventListener("byoky-message", handleRevocation);
622
+ };
623
+ window.postMessage(
624
+ { type: "BYOKY_REGISTER_NOTIFY" },
625
+ window.location.origin,
626
+ [notifyChannel.port2]
627
+ );
389
628
  return {
390
629
  ...response,
391
630
  createFetch: (providerId) => createProxyFetch(providerId, sessionKey),
392
631
  createRelay: (wsUrl) => createRelayClient(wsUrl, sessionKey, response.providers),
393
632
  disconnect: () => {
394
- document.removeEventListener("byoky-message", handleRevocation);
633
+ notifyChannel.port1.close();
395
634
  this.sendDisconnect(sessionKey);
396
635
  },
397
636
  isConnected: () => this.querySessionStatus(sessionKey),
@@ -413,8 +652,9 @@ var Byoky = class {
413
652
  new import_core3.ByokyError(import_core3.ByokyErrorCode.UNKNOWN, "Connection request timed out")
414
653
  );
415
654
  }, this.timeout);
416
- function handleEvent(event) {
417
- const msg = event.detail;
655
+ const channel = new MessageChannel();
656
+ channel.port1.onmessage = (event) => {
657
+ const msg = event.data;
418
658
  if (typeof msg?.type !== "string" || !msg.type.startsWith("BYOKY_")) return;
419
659
  if (msg.requestId !== requestId) return;
420
660
  cleanup();
@@ -424,12 +664,11 @@ var Byoky = class {
424
664
  const { code, message } = msg.payload;
425
665
  reject(new import_core3.ByokyError(code, message));
426
666
  }
427
- }
667
+ };
428
668
  function cleanup() {
429
669
  clearTimeout(timeoutId);
430
- document.removeEventListener("byoky-message", handleEvent);
670
+ channel.port1.close();
431
671
  }
432
- document.addEventListener("byoky-message", handleEvent);
433
672
  window.postMessage(
434
673
  {
435
674
  type: "BYOKY_CONNECT_REQUEST",
@@ -437,14 +676,15 @@ var Byoky = class {
437
676
  requestId,
438
677
  payload: request
439
678
  },
440
- "*"
679
+ window.location.origin,
680
+ [channel.port2]
441
681
  );
442
682
  });
443
683
  }
444
684
  sendDisconnect(sessionKey) {
445
685
  window.postMessage(
446
686
  { type: "BYOKY_DISCONNECT", payload: { sessionKey } },
447
- "*"
687
+ window.location.origin
448
688
  );
449
689
  }
450
690
  querySessionStatus(sessionKey) {
@@ -454,24 +694,24 @@ var Byoky = class {
454
694
  cleanup();
455
695
  resolve(false);
456
696
  }, 5e3);
457
- function handleEvent(event) {
458
- const msg = event.detail;
697
+ const channel = new MessageChannel();
698
+ channel.port1.onmessage = (event) => {
699
+ const msg = event.data;
459
700
  if (msg?.requestId !== requestId) return;
460
701
  if (msg.type === "BYOKY_SESSION_STATUS_RESPONSE") {
461
702
  cleanup();
462
703
  resolve(!!msg.payload?.connected);
463
704
  }
464
- }
705
+ };
465
706
  function cleanup() {
466
707
  clearTimeout(timeout);
467
- document.removeEventListener("byoky-message", handleEvent);
708
+ channel.port1.close();
468
709
  }
469
- document.addEventListener("byoky-message", handleEvent);
470
710
  window.postMessage({
471
711
  type: "BYOKY_SESSION_STATUS",
472
712
  requestId,
473
713
  payload: { sessionKey }
474
- }, "*");
714
+ }, window.location.origin, [channel.port2]);
475
715
  });
476
716
  }
477
717
  querySessionUsage(sessionKey) {
@@ -481,8 +721,9 @@ var Byoky = class {
481
721
  cleanup();
482
722
  reject(new Error("Usage query timed out"));
483
723
  }, 5e3);
484
- function handleEvent(event) {
485
- const msg = event.detail;
724
+ const channel = new MessageChannel();
725
+ channel.port1.onmessage = (event) => {
726
+ const msg = event.data;
486
727
  if (msg?.requestId !== requestId) return;
487
728
  if (msg.type === "BYOKY_SESSION_USAGE_RESPONSE") {
488
729
  cleanup();
@@ -492,17 +733,16 @@ var Byoky = class {
492
733
  reject(new Error("Session not found"));
493
734
  }
494
735
  }
495
- }
736
+ };
496
737
  function cleanup() {
497
738
  clearTimeout(timeout);
498
- document.removeEventListener("byoky-message", handleEvent);
739
+ channel.port1.close();
499
740
  }
500
- document.addEventListener("byoky-message", handleEvent);
501
741
  window.postMessage({
502
742
  type: "BYOKY_SESSION_USAGE",
503
743
  requestId,
504
744
  payload: { sessionKey }
505
- }, "*");
745
+ }, window.location.origin, [channel.port2]);
506
746
  });
507
747
  }
508
748
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts","../src/relay-client.ts"],"sourcesContent":["export { Byoky } from './byoky.js';\nexport type { ByokySession, ByokyOptions } from './byoky.js';\nexport { isExtensionInstalled, getStoreUrl } from './detect.js';\nexport { createProxyFetch } from './proxy-fetch.js';\nexport type { RelayConnection } from './relay-client.js';\nexport {\n type ConnectRequest,\n type ConnectResponse,\n type ProviderRequirement,\n type SessionUsage,\n ByokyError,\n ByokyErrorCode,\n} from '@byoky/core';\n","import type { ConnectRequest, ConnectResponse, SessionUsage } from '@byoky/core';\nimport { ByokyError, ByokyErrorCode, isByokyMessage } from '@byoky/core';\nimport { isExtensionInstalled, getStoreUrl } from './detect.js';\nimport { createProxyFetch } from './proxy-fetch.js';\nimport { createRelayClient, type RelayConnection } from './relay-client.js';\n\nexport interface ByokySession extends ConnectResponse {\n /** Create a fetch function that proxies requests through the wallet for the given provider. */\n createFetch(providerId: string): typeof fetch;\n /** Open a relay channel so a backend server can make LLM calls through this session. */\n createRelay(wsUrl: string): RelayConnection;\n /** Disconnect this session from the wallet. */\n disconnect(): void;\n /** Check if this session is still connected and valid. */\n isConnected(): Promise<boolean>;\n /** Get token usage stats for this session only. */\n getUsage(): Promise<SessionUsage>;\n /** Register a callback for when the wallet revokes this session. */\n onDisconnect(callback: () => void): () => void;\n}\n\nexport interface ByokyOptions {\n timeout?: number;\n}\n\nexport class Byoky {\n private timeout: number;\n\n constructor(options: ByokyOptions = {}) {\n this.timeout = options.timeout ?? 60_000;\n }\n\n async connect(request: ConnectRequest = {}): Promise<ByokySession> {\n if (typeof window === 'undefined') {\n throw new ByokyError(\n ByokyErrorCode.UNKNOWN,\n 'Byoky SDK requires a browser environment. On the server, use your API key directly.',\n );\n }\n\n if (!isExtensionInstalled()) {\n const storeUrl = getStoreUrl();\n if (storeUrl) {\n window.open(storeUrl, '_blank');\n }\n throw ByokyError.walletNotInstalled();\n }\n\n const response = await this.sendConnectRequest(request);\n return this.buildSession(response);\n }\n\n /**\n * Reconnect to an existing session using previously stored response data.\n * Returns null if the session is no longer valid.\n */\n async reconnect(savedResponse: ConnectResponse): Promise<ByokySession | null> {\n if (typeof window === 'undefined') return null;\n if (!isExtensionInstalled()) return null;\n\n const connected = await this.querySessionStatus(savedResponse.sessionKey);\n if (!connected) return null;\n\n return this.buildSession(savedResponse);\n }\n\n private buildSession(response: ConnectResponse): ByokySession {\n const sessionKey = response.sessionKey;\n const disconnectCallbacks = new Set<() => void>();\n\n function handleRevocation(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.type === 'BYOKY_SESSION_REVOKED' && msg.payload?.sessionKey === sessionKey) {\n for (const cb of disconnectCallbacks) cb();\n disconnectCallbacks.clear();\n document.removeEventListener('byoky-message', handleRevocation);\n }\n }\n document.addEventListener('byoky-message', handleRevocation);\n\n return {\n ...response,\n createFetch: (providerId: string) =>\n createProxyFetch(providerId, sessionKey),\n createRelay: (wsUrl: string) =>\n createRelayClient(wsUrl, sessionKey, response.providers),\n disconnect: () => {\n document.removeEventListener('byoky-message', handleRevocation);\n this.sendDisconnect(sessionKey);\n },\n isConnected: () => this.querySessionStatus(sessionKey),\n getUsage: () => this.querySessionUsage(sessionKey),\n onDisconnect: (callback: () => void) => {\n disconnectCallbacks.add(callback);\n return () => { disconnectCallbacks.delete(callback); };\n },\n };\n }\n\n private sendConnectRequest(\n request: ConnectRequest,\n ): Promise<ConnectResponse> {\n return new Promise<ConnectResponse>((resolve, reject) => {\n const requestId = crypto.randomUUID();\n\n const timeoutId = setTimeout(() => {\n cleanup();\n reject(\n new ByokyError(ByokyErrorCode.UNKNOWN, 'Connection request timed out'),\n );\n }, this.timeout);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (typeof msg?.type !== 'string' || !msg.type.startsWith('BYOKY_')) return;\n if (msg.requestId !== requestId) return;\n\n cleanup();\n\n if (msg.type === 'BYOKY_CONNECT_RESPONSE') {\n resolve(msg.payload as ConnectResponse);\n } else if (msg.type === 'BYOKY_ERROR') {\n const { code, message } = msg.payload as {\n code: string;\n message: string;\n };\n reject(new ByokyError(code as ByokyErrorCode, message));\n }\n }\n\n function cleanup() {\n clearTimeout(timeoutId);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_CONNECT_REQUEST',\n id: requestId,\n requestId,\n payload: request,\n },\n '*',\n );\n });\n }\n\n private sendDisconnect(sessionKey: string): void {\n window.postMessage(\n { type: 'BYOKY_DISCONNECT', payload: { sessionKey } },\n '*',\n );\n }\n\n private querySessionStatus(sessionKey: string): Promise<boolean> {\n return new Promise((resolve) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => { cleanup(); resolve(false); }, 5000);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_STATUS_RESPONSE') {\n cleanup();\n resolve(!!msg.payload?.connected);\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n window.postMessage({\n type: 'BYOKY_SESSION_STATUS',\n requestId,\n payload: { sessionKey },\n }, '*');\n });\n }\n\n private querySessionUsage(sessionKey: string): Promise<SessionUsage> {\n return new Promise((resolve, reject) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Usage query timed out'));\n }, 5000);\n\n function handleEvent(event: Event) {\n const msg = (event as CustomEvent).detail;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_USAGE_RESPONSE') {\n cleanup();\n if (msg.payload) {\n resolve(msg.payload as SessionUsage);\n } else {\n reject(new Error('Session not found'));\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n window.postMessage({\n type: 'BYOKY_SESSION_USAGE',\n requestId,\n payload: { sessionKey },\n }, '*');\n });\n }\n}\n","import { BYOKY_PROVIDER_KEY } from '@byoky/core';\n\nexport function isExtensionInstalled(): boolean {\n return typeof window !== 'undefined' && BYOKY_PROVIDER_KEY in window;\n}\n\nexport function getStoreUrl(): string | null {\n if (typeof navigator === 'undefined') return null;\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('chrome') && !ua.includes('edg')) {\n return 'https://chrome.google.com/webstore/detail/byoky/TODO_EXTENSION_ID';\n }\n if (ua.includes('firefox')) {\n return 'https://addons.mozilla.org/en-US/firefox/addon/byoky/';\n }\n if (ua.includes('safari') && !ua.includes('chrome')) {\n return 'https://apps.apple.com/app/byoky/TODO_APP_ID';\n }\n return null;\n}\n","export function createProxyFetch(\n providerId: string,\n sessionKey: string,\n): typeof fetch {\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(new Headers(init.headers).entries())\n : {};\n const body = init?.body ? await readBody(init.body) : undefined;\n\n const requestId = crypto.randomUUID();\n\n return new Promise<Response>((resolve, reject) => {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let resolved = false;\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Proxy request timed out'));\n }, 120_000);\n\n function handleEvent(event: Event) {\n const data = (event as CustomEvent).detail ?? (event as MessageEvent).data;\n if (data?.requestId !== requestId) return;\n\n switch (data.type) {\n case 'BYOKY_PROXY_RESPONSE_META':\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(\n new Response(readable, {\n status: data.status,\n statusText: data.statusText,\n headers: new Headers(data.headers),\n }),\n );\n }\n break;\n\n case 'BYOKY_PROXY_RESPONSE_CHUNK':\n writer.write(encoder.encode(data.chunk)).catch(() => {});\n break;\n\n case 'BYOKY_PROXY_RESPONSE_DONE':\n writer.close().catch(() => {});\n cleanup();\n break;\n\n case 'BYOKY_PROXY_RESPONSE_ERROR': {\n const errResponse = new Response(\n JSON.stringify({ error: data.error }),\n {\n status: data.status || 500,\n headers: { 'content-type': 'application/json' },\n },\n );\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(errResponse);\n }\n writer.close().catch(() => {});\n cleanup();\n break;\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n document.removeEventListener('byoky-message', handleEvent);\n }\n\n document.addEventListener('byoky-message', handleEvent);\n\n window.postMessage(\n {\n type: 'BYOKY_PROXY_REQUEST',\n requestId,\n sessionKey,\n providerId,\n url,\n method,\n headers,\n body,\n },\n '*',\n );\n });\n };\n}\n\nasync function readBody(body: BodyInit): Promise<string | undefined> {\n if (typeof body === 'string') return body;\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);\n if (body instanceof Blob) return body.text();\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n const total = chunks.reduce((acc, c) => acc + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return new TextDecoder().decode(combined);\n }\n return undefined;\n}\n","import type {\n AuthMethod,\n RelayMessage,\n RelayRequest,\n} from '@byoky/core';\nimport {\n ByokyError,\n parseRelayMessage,\n WS_READY_STATE,\n} from '@byoky/core';\nimport { createProxyFetch } from './proxy-fetch.js';\n\nexport interface RelayConnection {\n readonly status: 'connecting' | 'connected' | 'disconnected';\n close(): void;\n onClose(callback: (reason?: string) => void): () => void;\n}\n\nexport function createRelayClient(\n wsUrl: string,\n sessionKey: string,\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>,\n): RelayConnection {\n let status: 'connecting' | 'connected' | 'disconnected' = 'connecting';\n const closeCallbacks = new Set<(reason?: string) => void>();\n const inFlight = new Map<string, AbortController>();\n\n let ws: WebSocket;\n let pingInterval: ReturnType<typeof setInterval> | undefined;\n\n try {\n const parsed = new URL(wsUrl);\n const isSecure = parsed.protocol === 'wss:';\n const isLocalWs = parsed.protocol === 'ws:' &&\n (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1');\n if (!isSecure && !isLocalWs) {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Insecure WebSocket URL rejected — use wss:// for non-localhost connections'); return () => {}; },\n };\n }\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Invalid WebSocket URL'); return () => {}; },\n };\n }\n\n try {\n ws = new WebSocket(wsUrl);\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Failed to create WebSocket'); return () => {}; },\n };\n }\n\n ws.onopen = () => {\n status = 'connected';\n\n // Send hello with a relay-specific ID (never expose the real session key to the relay server)\n const relayId = `relay_${crypto.randomUUID().replace(/-/g, '')}`;\n ws.send(JSON.stringify({\n type: 'relay:hello',\n sessionId: relayId,\n providers,\n }));\n\n // Keepalive ping every 30s\n pingInterval = setInterval(() => {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify({ type: 'relay:ping', ts: Date.now() }));\n }\n }, 30_000);\n };\n\n ws.onmessage = (event) => {\n const msg = parseRelayMessage(event.data);\n if (!msg) return;\n\n switch (msg.type) {\n case 'relay:request':\n handleRequest(msg);\n break;\n case 'relay:ping':\n ws.send(JSON.stringify({ type: 'relay:pong', ts: msg.ts }));\n break;\n case 'relay:pong':\n // Keepalive response, nothing to do\n break;\n }\n };\n\n ws.onclose = (event) => {\n cleanup(event.reason || 'WebSocket closed');\n };\n\n ws.onerror = () => {\n cleanup('WebSocket error');\n };\n\n async function handleRequest(req: RelayRequest) {\n const { requestId, providerId, url, method, headers, body } = req;\n const controller = new AbortController();\n inFlight.set(requestId, controller);\n\n try {\n const proxyFetch = createProxyFetch(providerId, sessionKey);\n const response = await proxyFetch(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n // Send response metadata\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n send({\n type: 'relay:response:meta',\n requestId,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n\n // Stream the body\n if (response.body) {\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (controller.signal.aborted) break;\n\n send({\n type: 'relay:response:chunk',\n requestId,\n chunk: decoder.decode(value, { stream: true }),\n });\n }\n }\n\n send({ type: 'relay:response:done', requestId });\n } catch (err) {\n send({\n type: 'relay:response:error',\n requestId,\n error: {\n code: err instanceof ByokyError ? err.code : 'PROXY_ERROR',\n message: err instanceof Error ? err.message : 'Unknown error',\n },\n });\n } finally {\n inFlight.delete(requestId);\n }\n }\n\n function send(msg: RelayMessage) {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n }\n\n function cleanup(reason?: string) {\n if (status === 'disconnected') return;\n status = 'disconnected';\n\n if (pingInterval) clearInterval(pingInterval);\n\n // Abort all in-flight requests\n for (const controller of inFlight.values()) {\n controller.abort();\n }\n inFlight.clear();\n\n for (const cb of closeCallbacks) cb(reason);\n closeCallbacks.clear();\n }\n\n return {\n get status() { return status; },\n close() {\n if (ws.readyState === WS_READY_STATE.OPEN || ws.readyState === WS_READY_STATE.CONNECTING) {\n ws.close(1000, 'Client closed');\n }\n cleanup('Client closed');\n },\n onClose(callback: (reason?: string) => void) {\n closeCallbacks.add(callback);\n return () => { closeCallbacks.delete(callback); };\n },\n };\n}\n"],"mappings":"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","names":["import_core","import_core","import_core"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts","../src/relay-fetch.ts","../src/relay-client.ts"],"sourcesContent":["export { Byoky } from './byoky.js';\nexport type { ByokySession, ByokyOptions } from './byoky.js';\nexport { isExtensionInstalled, getStoreUrl } from './detect.js';\nexport { createProxyFetch } from './proxy-fetch.js';\nexport type { RelayConnection } from './relay-client.js';\nexport {\n type ConnectRequest,\n type ConnectResponse,\n type ProviderRequirement,\n type SessionUsage,\n ByokyError,\n ByokyErrorCode,\n} from '@byoky/core';\n","import type { AuthMethod, ConnectRequest, ConnectResponse, SessionUsage } from '@byoky/core';\nimport { ByokyError, ByokyErrorCode, isByokyMessage, encodePairPayload } from '@byoky/core';\nimport { isExtensionInstalled, getStoreUrl } from './detect.js';\nimport { createProxyFetch } from './proxy-fetch.js';\nimport { createRelayFetch } from './relay-fetch.js';\nimport { createRelayClient, type RelayConnection } from './relay-client.js';\n\nexport interface ByokySession extends ConnectResponse {\n /** Create a fetch function that proxies requests through the wallet for the given provider. */\n createFetch(providerId: string): typeof fetch;\n /** Open a relay channel so a backend server can make LLM calls through this session. */\n createRelay(wsUrl: string): RelayConnection;\n /** Disconnect this session from the wallet. */\n disconnect(): void;\n /** Check if this session is still connected and valid. */\n isConnected(): Promise<boolean>;\n /** Get token usage stats for this session only. */\n getUsage(): Promise<SessionUsage>;\n /** Register a callback for when the wallet revokes this session. */\n onDisconnect(callback: () => void): () => void;\n}\n\nexport interface ByokyOptions {\n timeout?: number;\n /** Relay server URL for mobile wallet pairing. Default: wss://relay.byoky.com */\n relayUrl?: string;\n}\n\nexport class Byoky {\n private timeout: number;\n private relayUrl: string;\n\n constructor(options: ByokyOptions = {}) {\n this.timeout = options.timeout ?? 60_000;\n this.relayUrl = options.relayUrl ?? 'wss://relay.byoky.com';\n }\n\n /**\n * Connect to a Byoky wallet. Tries the browser extension first.\n * If no extension is found and `onPairingReady` is provided,\n * falls back to relay mode — pairing with a mobile wallet app.\n */\n async connect(request: ConnectRequest & {\n /** Called with a pairing code when no extension is detected. Show as QR or text. */\n onPairingReady?: (pairingCode: string) => void;\n /** Skip extension detection and go directly to relay pairing. */\n useRelay?: boolean;\n } = {}): Promise<ByokySession> {\n if (typeof window === 'undefined') {\n throw new ByokyError(\n ByokyErrorCode.UNKNOWN,\n 'Byoky SDK requires a browser environment. On the server, use your API key directly.',\n );\n }\n\n const { onPairingReady, useRelay, ...connectRequest } = request;\n\n // Go directly to relay if explicitly requested\n if (useRelay && onPairingReady) {\n return this.connectViaRelay(connectRequest, onPairingReady);\n }\n\n // Try extension first\n if (isExtensionInstalled()) {\n const response = await this.sendConnectRequest(connectRequest);\n return this.buildSession(response);\n }\n\n // Fall back to relay pairing if callback provided\n if (onPairingReady) {\n return this.connectViaRelay(connectRequest, onPairingReady);\n }\n\n // No extension, no relay callback — throw\n const storeUrl = getStoreUrl();\n if (storeUrl) {\n window.open(storeUrl, '_blank');\n }\n throw ByokyError.walletNotInstalled();\n }\n\n /**\n * Reconnect to an existing session using previously stored response data.\n * Returns null if the session is no longer valid.\n */\n async reconnect(savedResponse: ConnectResponse): Promise<ByokySession | null> {\n if (typeof window === 'undefined') return null;\n if (!isExtensionInstalled()) return null;\n\n const connected = await this.querySessionStatus(savedResponse.sessionKey);\n if (!connected) return null;\n\n return this.buildSession(savedResponse);\n }\n\n // --- Relay pairing ---\n\n private connectViaRelay(\n request: ConnectRequest,\n onPairingReady: (code: string) => void,\n ): Promise<ByokySession> {\n return new Promise<ByokySession>((resolve, reject) => {\n const roomId = crypto.randomUUID();\n const authToken = Array.from(crypto.getRandomValues(new Uint8Array(32)))\n .map(b => b.toString(16).padStart(2, '0')).join('');\n\n const pairingCode = encodePairPayload({\n v: 1,\n r: this.relayUrl,\n id: roomId,\n t: authToken,\n o: window.location.origin,\n });\n\n const ws = new WebSocket(this.relayUrl);\n\n const timeout = setTimeout(() => {\n ws.close();\n reject(new ByokyError(ByokyErrorCode.UNKNOWN, 'Pairing timed out. No wallet connected.'));\n }, this.timeout);\n\n ws.onopen = () => {\n // Authenticate as recipient (the web app receives proxied responses)\n ws.send(JSON.stringify({\n type: 'relay:auth',\n roomId,\n authToken,\n role: 'recipient',\n }));\n };\n\n ws.onmessage = (event) => {\n let msg: { type: string; [k: string]: unknown };\n try {\n msg = JSON.parse(event.data);\n } catch {\n return;\n }\n\n switch (msg.type) {\n case 'relay:auth:result':\n if (msg.success) {\n // Authenticated — show the pairing code\n onPairingReady(pairingCode);\n } else {\n clearTimeout(timeout);\n ws.close();\n reject(new ByokyError(ByokyErrorCode.UNKNOWN, `Relay auth failed: ${msg.error}`));\n }\n break;\n\n case 'relay:pair:hello': {\n // Phone wallet connected and sent its providers\n clearTimeout(timeout);\n const providers = msg.providers as Record<string, { available: boolean; authMethod: AuthMethod }>;\n\n // Acknowledge the pairing\n ws.send(JSON.stringify({ type: 'relay:pair:ack' }));\n\n resolve(this.buildRelaySession(ws, roomId, providers));\n break;\n }\n\n case 'relay:peer:status':\n // Phone came online but hasn't sent pair:hello yet — wait\n break;\n }\n };\n\n ws.onerror = () => {\n clearTimeout(timeout);\n reject(new ByokyError(ByokyErrorCode.UNKNOWN, 'Failed to connect to relay server'));\n };\n\n ws.onclose = () => {\n clearTimeout(timeout);\n };\n });\n }\n\n private buildRelaySession(\n ws: WebSocket,\n roomId: string,\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>,\n ): ByokySession {\n const sessionKey = `relay_${roomId}`;\n const disconnectCallbacks = new Set<() => void>();\n\n // Keepalive\n const pingInterval = setInterval(() => {\n if (ws.readyState === WebSocket.OPEN) {\n ws.send(JSON.stringify({ type: 'relay:ping', ts: Date.now() }));\n }\n }, 30_000);\n\n ws.addEventListener('close', () => {\n clearInterval(pingInterval);\n for (const cb of disconnectCallbacks) cb();\n disconnectCallbacks.clear();\n });\n\n // Listen for phone going offline (app backgrounded/closed)\n ws.addEventListener('message', (event) => {\n try {\n const msg = JSON.parse(event.data);\n if (msg.type === 'relay:peer:status' && msg.online === false) {\n for (const cb of disconnectCallbacks) cb();\n disconnectCallbacks.clear();\n clearInterval(pingInterval);\n ws.close(1000, 'Phone disconnected');\n }\n } catch {}\n });\n\n return {\n sessionKey,\n proxyUrl: '',\n providers,\n createFetch: (providerId: string) => createRelayFetch(ws, providerId),\n createRelay: () => { throw new Error('Relay-in-relay not supported'); },\n disconnect: () => {\n clearInterval(pingInterval);\n ws.close(1000, 'Client disconnected');\n },\n isConnected: async () => ws.readyState === WebSocket.OPEN,\n getUsage: async () => ({ requests: 0, inputTokens: 0, outputTokens: 0, byProvider: {} }),\n onDisconnect: (callback: () => void) => {\n disconnectCallbacks.add(callback);\n return () => { disconnectCallbacks.delete(callback); };\n },\n };\n }\n\n // --- Extension-based session ---\n\n private buildSession(response: ConnectResponse): ByokySession {\n const sessionKey = response.sessionKey;\n const disconnectCallbacks = new Set<() => void>();\n\n // Register a secure notification channel via MessageChannel so\n // revocation events cannot be spoofed by page scripts.\n const notifyChannel = new MessageChannel();\n notifyChannel.port1.onmessage = (event: MessageEvent) => {\n const msg = event.data;\n if (msg?.type === 'BYOKY_SESSION_REVOKED' && msg.payload?.sessionKey === sessionKey) {\n for (const cb of disconnectCallbacks) cb();\n disconnectCallbacks.clear();\n notifyChannel.port1.close();\n }\n };\n window.postMessage(\n { type: 'BYOKY_REGISTER_NOTIFY' },\n window.location.origin,\n [notifyChannel.port2],\n );\n\n return {\n ...response,\n createFetch: (providerId: string) =>\n createProxyFetch(providerId, sessionKey),\n createRelay: (wsUrl: string) =>\n createRelayClient(wsUrl, sessionKey, response.providers),\n disconnect: () => {\n notifyChannel.port1.close();\n this.sendDisconnect(sessionKey);\n },\n isConnected: () => this.querySessionStatus(sessionKey),\n getUsage: () => this.querySessionUsage(sessionKey),\n onDisconnect: (callback: () => void) => {\n disconnectCallbacks.add(callback);\n return () => { disconnectCallbacks.delete(callback); };\n },\n };\n }\n\n private sendConnectRequest(\n request: ConnectRequest,\n ): Promise<ConnectResponse> {\n return new Promise<ConnectResponse>((resolve, reject) => {\n const requestId = crypto.randomUUID();\n\n const timeoutId = setTimeout(() => {\n cleanup();\n reject(\n new ByokyError(ByokyErrorCode.UNKNOWN, 'Connection request timed out'),\n );\n }, this.timeout);\n\n const channel = new MessageChannel();\n\n channel.port1.onmessage = (event: MessageEvent) => {\n const msg = event.data;\n if (typeof msg?.type !== 'string' || !msg.type.startsWith('BYOKY_')) return;\n if (msg.requestId !== requestId) return;\n\n cleanup();\n\n if (msg.type === 'BYOKY_CONNECT_RESPONSE') {\n resolve(msg.payload as ConnectResponse);\n } else if (msg.type === 'BYOKY_ERROR') {\n const { code, message } = msg.payload as {\n code: string;\n message: string;\n };\n reject(new ByokyError(code as ByokyErrorCode, message));\n }\n };\n\n function cleanup() {\n clearTimeout(timeoutId);\n channel.port1.close();\n }\n\n window.postMessage(\n {\n type: 'BYOKY_CONNECT_REQUEST',\n id: requestId,\n requestId,\n payload: request,\n },\n window.location.origin,\n [channel.port2],\n );\n });\n }\n\n private sendDisconnect(sessionKey: string): void {\n window.postMessage(\n { type: 'BYOKY_DISCONNECT', payload: { sessionKey } },\n window.location.origin,\n );\n }\n\n private querySessionStatus(sessionKey: string): Promise<boolean> {\n return new Promise((resolve) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => { cleanup(); resolve(false); }, 5000);\n\n const channel = new MessageChannel();\n\n channel.port1.onmessage = (event: MessageEvent) => {\n const msg = event.data;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_STATUS_RESPONSE') {\n cleanup();\n resolve(!!msg.payload?.connected);\n }\n };\n\n function cleanup() {\n clearTimeout(timeout);\n channel.port1.close();\n }\n\n window.postMessage({\n type: 'BYOKY_SESSION_STATUS',\n requestId,\n payload: { sessionKey },\n }, window.location.origin, [channel.port2]);\n });\n }\n\n private querySessionUsage(sessionKey: string): Promise<SessionUsage> {\n return new Promise((resolve, reject) => {\n const requestId = crypto.randomUUID();\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Usage query timed out'));\n }, 5000);\n\n const channel = new MessageChannel();\n\n channel.port1.onmessage = (event: MessageEvent) => {\n const msg = event.data;\n if (msg?.requestId !== requestId) return;\n if (msg.type === 'BYOKY_SESSION_USAGE_RESPONSE') {\n cleanup();\n if (msg.payload) {\n resolve(msg.payload as SessionUsage);\n } else {\n reject(new Error('Session not found'));\n }\n }\n };\n\n function cleanup() {\n clearTimeout(timeout);\n channel.port1.close();\n }\n\n window.postMessage({\n type: 'BYOKY_SESSION_USAGE',\n requestId,\n payload: { sessionKey },\n }, window.location.origin, [channel.port2]);\n });\n }\n}\n","import { BYOKY_PROVIDER_KEY } from '@byoky/core';\n\nexport function isExtensionInstalled(): boolean {\n return typeof window !== 'undefined' && BYOKY_PROVIDER_KEY in window;\n}\n\nexport function getStoreUrl(): string | null {\n if (typeof navigator === 'undefined') return null;\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('chrome') && !ua.includes('edg')) {\n return 'https://chrome.google.com/webstore/detail/byoky/TODO_EXTENSION_ID';\n }\n if (ua.includes('firefox')) {\n return 'https://addons.mozilla.org/en-US/firefox/addon/byoky/';\n }\n if (ua.includes('safari') && !ua.includes('chrome')) {\n return 'https://apps.apple.com/app/byoky/TODO_APP_ID';\n }\n return null;\n}\n","export function createProxyFetch(\n providerId: string,\n sessionKey: string,\n): typeof fetch {\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(new Headers(init.headers).entries())\n : {};\n const body = init?.body ? await readBody(init.body) : undefined;\n\n const requestId = crypto.randomUUID();\n\n return new Promise<Response>((resolve, reject) => {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let resolved = false;\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Proxy request timed out'));\n }, 120_000);\n\n // Use MessageChannel for secure response delivery — page scripts\n // cannot intercept or spoof messages on a transferred port.\n const channel = new MessageChannel();\n\n channel.port1.onmessage = (event: MessageEvent) => {\n const data = event.data;\n if (data?.requestId !== requestId) return;\n\n switch (data.type) {\n case 'BYOKY_PROXY_RESPONSE_META':\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(\n new Response(readable, {\n status: data.status,\n statusText: data.statusText,\n headers: new Headers(data.headers),\n }),\n );\n }\n break;\n\n case 'BYOKY_PROXY_RESPONSE_CHUNK':\n writer.write(encoder.encode(data.chunk)).catch(() => {});\n break;\n\n case 'BYOKY_PROXY_RESPONSE_DONE':\n writer.close().catch(() => {});\n cleanup();\n break;\n\n case 'BYOKY_PROXY_RESPONSE_ERROR': {\n const errResponse = new Response(\n JSON.stringify({ error: data.error }),\n {\n status: data.status || 500,\n headers: { 'content-type': 'application/json' },\n },\n );\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(errResponse);\n }\n writer.close().catch(() => {});\n cleanup();\n break;\n }\n }\n };\n\n function cleanup() {\n clearTimeout(timeout);\n channel.port1.close();\n }\n\n window.postMessage(\n {\n type: 'BYOKY_PROXY_REQUEST',\n requestId,\n sessionKey,\n providerId,\n url,\n method,\n headers,\n body,\n },\n window.location.origin,\n [channel.port2],\n );\n });\n };\n}\n\nasync function readBody(body: BodyInit): Promise<string | undefined> {\n if (typeof body === 'string') return body;\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);\n if (body instanceof Blob) return body.text();\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n const total = chunks.reduce((acc, c) => acc + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return new TextDecoder().decode(combined);\n }\n return undefined;\n}\n","/**\n * Creates a fetch-like function that routes requests through a WebSocket relay\n * to a mobile wallet app, instead of through a browser extension.\n */\nexport function createRelayFetch(\n ws: WebSocket,\n providerId: string,\n): typeof fetch {\n return async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const url =\n typeof input === 'string'\n ? input\n : input instanceof URL\n ? input.toString()\n : input.url;\n\n const method = init?.method ?? 'GET';\n const headers = init?.headers\n ? Object.fromEntries(new Headers(init.headers).entries())\n : {};\n const body = init?.body ? await readBody(init.body) : undefined;\n\n const requestId = crypto.randomUUID();\n\n return new Promise<Response>((resolve, reject) => {\n const { readable, writable } = new TransformStream<Uint8Array>();\n const writer = writable.getWriter();\n const encoder = new TextEncoder();\n let resolved = false;\n\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error('Relay proxy request timed out'));\n }, 120_000);\n\n function handleMessage(event: MessageEvent) {\n let data: { type: string; requestId?: string; [k: string]: unknown };\n try {\n data = JSON.parse(event.data);\n } catch {\n return;\n }\n if (data.requestId !== requestId) return;\n\n switch (data.type) {\n case 'relay:response:meta':\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(\n new Response(readable, {\n status: data.status as number,\n statusText: (data.statusText as string) ?? '',\n headers: new Headers(data.headers as Record<string, string>),\n }),\n );\n }\n break;\n\n case 'relay:response:chunk':\n writer.write(encoder.encode(data.chunk as string)).catch(() => {});\n break;\n\n case 'relay:response:done':\n writer.close().catch(() => {});\n cleanup();\n break;\n\n case 'relay:response:error': {\n const err = data.error as { code: string; message: string } | string | undefined;\n const message = typeof err === 'string' ? err : err?.message ?? 'Relay proxy error';\n const errResponse = new Response(\n JSON.stringify({ error: { message, code: typeof err === 'object' ? err?.code : 'RELAY_ERROR' } }),\n { status: 500, headers: { 'content-type': 'application/json' } },\n );\n if (!resolved) {\n resolved = true;\n clearTimeout(timeout);\n resolve(errResponse);\n }\n writer.close().catch(() => {});\n cleanup();\n break;\n }\n }\n }\n\n function cleanup() {\n clearTimeout(timeout);\n ws.removeEventListener('message', handleMessage);\n }\n\n ws.addEventListener('message', handleMessage);\n\n ws.send(JSON.stringify({\n type: 'relay:request',\n requestId,\n providerId,\n url,\n method,\n headers,\n body,\n }));\n });\n };\n}\n\nasync function readBody(body: BodyInit): Promise<string | undefined> {\n if (typeof body === 'string') return body;\n if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);\n if (body instanceof Blob) return body.text();\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof ReadableStream) {\n const reader = body.getReader();\n const chunks: Uint8Array[] = [];\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n }\n const total = chunks.reduce((acc, c) => acc + c.length, 0);\n const combined = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.length;\n }\n return new TextDecoder().decode(combined);\n }\n return undefined;\n}\n","import type {\n AuthMethod,\n RelayMessage,\n RelayRequest,\n} from '@byoky/core';\nimport {\n ByokyError,\n parseRelayMessage,\n WS_READY_STATE,\n} from '@byoky/core';\nimport { createProxyFetch } from './proxy-fetch.js';\n\nexport interface RelayConnection {\n readonly status: 'connecting' | 'connected' | 'disconnected';\n close(): void;\n onClose(callback: (reason?: string) => void): () => void;\n}\n\nexport function createRelayClient(\n wsUrl: string,\n sessionKey: string,\n providers: Record<string, { available: boolean; authMethod: AuthMethod }>,\n): RelayConnection {\n let status: 'connecting' | 'connected' | 'disconnected' = 'connecting';\n const closeCallbacks = new Set<(reason?: string) => void>();\n const inFlight = new Map<string, AbortController>();\n\n let ws: WebSocket;\n let pingInterval: ReturnType<typeof setInterval> | undefined;\n\n try {\n const parsed = new URL(wsUrl);\n const isSecure = parsed.protocol === 'wss:';\n const isLocalWs = parsed.protocol === 'ws:' &&\n (parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1' || parsed.hostname === '[::1]');\n if (!isSecure && !isLocalWs) {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Insecure WebSocket URL rejected — use wss:// for non-localhost connections'); return () => {}; },\n };\n }\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Invalid WebSocket URL'); return () => {}; },\n };\n }\n\n try {\n ws = new WebSocket(wsUrl);\n } catch {\n status = 'disconnected';\n return {\n get status() { return status; },\n close() {},\n onClose(cb) { cb('Failed to create WebSocket'); return () => {}; },\n };\n }\n\n ws.onopen = () => {\n status = 'connected';\n\n // Send hello with a relay-specific ID (never expose the real session key to the relay server)\n const relayId = `relay_${crypto.randomUUID().replace(/-/g, '')}`;\n ws.send(JSON.stringify({\n type: 'relay:hello',\n sessionId: relayId,\n providers,\n }));\n\n // Keepalive ping every 30s\n pingInterval = setInterval(() => {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify({ type: 'relay:ping', ts: Date.now() }));\n }\n }, 30_000);\n };\n\n ws.onmessage = (event) => {\n const msg = parseRelayMessage(event.data);\n if (!msg) return;\n\n switch (msg.type) {\n case 'relay:request':\n handleRequest(msg);\n break;\n case 'relay:ping':\n ws.send(JSON.stringify({ type: 'relay:pong', ts: msg.ts }));\n break;\n case 'relay:pong':\n // Keepalive response, nothing to do\n break;\n }\n };\n\n ws.onclose = (event) => {\n cleanup(event.reason || 'WebSocket closed');\n };\n\n ws.onerror = () => {\n cleanup('WebSocket error');\n };\n\n async function handleRequest(req: RelayRequest) {\n const { requestId, providerId, url, method, headers, body } = req;\n const controller = new AbortController();\n inFlight.set(requestId, controller);\n\n try {\n const proxyFetch = createProxyFetch(providerId, sessionKey);\n const response = await proxyFetch(url, {\n method,\n headers,\n body,\n signal: controller.signal,\n });\n\n // Send response metadata\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n send({\n type: 'relay:response:meta',\n requestId,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n });\n\n // Stream the body\n if (response.body) {\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (controller.signal.aborted) break;\n\n send({\n type: 'relay:response:chunk',\n requestId,\n chunk: decoder.decode(value, { stream: true }),\n });\n }\n }\n\n send({ type: 'relay:response:done', requestId });\n } catch (err) {\n send({\n type: 'relay:response:error',\n requestId,\n error: {\n code: err instanceof ByokyError ? err.code : 'PROXY_ERROR',\n message: err instanceof Error ? err.message : 'Unknown error',\n },\n });\n } finally {\n inFlight.delete(requestId);\n }\n }\n\n function send(msg: RelayMessage) {\n if (ws.readyState === WS_READY_STATE.OPEN) {\n ws.send(JSON.stringify(msg));\n }\n }\n\n function cleanup(reason?: string) {\n if (status === 'disconnected') return;\n status = 'disconnected';\n\n if (pingInterval) clearInterval(pingInterval);\n\n // Abort all in-flight requests\n for (const controller of inFlight.values()) {\n controller.abort();\n }\n inFlight.clear();\n\n for (const cb of closeCallbacks) cb(reason);\n closeCallbacks.clear();\n }\n\n return {\n get status() { return status; },\n close() {\n if (ws.readyState === WS_READY_STATE.OPEN || ws.readyState === WS_READY_STATE.CONNECTING) {\n ws.close(1000, 'Client closed');\n }\n cleanup('Client closed');\n },\n onClose(callback: (reason?: string) => void) {\n closeCallbacks.add(callback);\n return () => { closeCallbacks.delete(callback); };\n },\n };\n}\n"],"mappings":"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","names":["import_core","readBody","import_core","import_core"]}