@byoky/sdk 0.2.0 → 0.4.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.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/byoky.ts
2
- import { ByokyError, ByokyErrorCode } from "@byoky/core";
2
+ import { ByokyError as ByokyError2, ByokyErrorCode } from "@byoky/core";
3
3
 
4
4
  // src/detect.ts
5
5
  import { BYOKY_PROVIDER_KEY } from "@byoky/core";
@@ -130,6 +130,189 @@ async function readBody(body) {
130
130
  return void 0;
131
131
  }
132
132
 
133
+ // src/relay-client.ts
134
+ import {
135
+ ByokyError,
136
+ parseRelayMessage,
137
+ WS_READY_STATE
138
+ } from "@byoky/core";
139
+ function createRelayClient(wsUrl, sessionKey, providers) {
140
+ let status = "connecting";
141
+ const closeCallbacks = /* @__PURE__ */ new Set();
142
+ const inFlight = /* @__PURE__ */ new Map();
143
+ let ws;
144
+ let pingInterval;
145
+ try {
146
+ const parsed = new URL(wsUrl);
147
+ const isSecure = parsed.protocol === "wss:";
148
+ const isLocalWs = parsed.protocol === "ws:" && (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1");
149
+ if (!isSecure && !isLocalWs) {
150
+ status = "disconnected";
151
+ return {
152
+ get status() {
153
+ return status;
154
+ },
155
+ close() {
156
+ },
157
+ onClose(cb) {
158
+ cb("Insecure WebSocket URL rejected \u2014 use wss:// for non-localhost connections");
159
+ return () => {
160
+ };
161
+ }
162
+ };
163
+ }
164
+ } catch {
165
+ status = "disconnected";
166
+ return {
167
+ get status() {
168
+ return status;
169
+ },
170
+ close() {
171
+ },
172
+ onClose(cb) {
173
+ cb("Invalid WebSocket URL");
174
+ return () => {
175
+ };
176
+ }
177
+ };
178
+ }
179
+ try {
180
+ ws = new WebSocket(wsUrl);
181
+ } catch {
182
+ status = "disconnected";
183
+ return {
184
+ get status() {
185
+ return status;
186
+ },
187
+ close() {
188
+ },
189
+ onClose(cb) {
190
+ cb("Failed to create WebSocket");
191
+ return () => {
192
+ };
193
+ }
194
+ };
195
+ }
196
+ ws.onopen = () => {
197
+ status = "connected";
198
+ const relayId = `relay_${crypto.randomUUID().replace(/-/g, "")}`;
199
+ ws.send(JSON.stringify({
200
+ type: "relay:hello",
201
+ sessionId: relayId,
202
+ providers
203
+ }));
204
+ pingInterval = setInterval(() => {
205
+ if (ws.readyState === WS_READY_STATE.OPEN) {
206
+ ws.send(JSON.stringify({ type: "relay:ping", ts: Date.now() }));
207
+ }
208
+ }, 3e4);
209
+ };
210
+ ws.onmessage = (event) => {
211
+ const msg = parseRelayMessage(event.data);
212
+ if (!msg) return;
213
+ switch (msg.type) {
214
+ case "relay:request":
215
+ handleRequest(msg);
216
+ break;
217
+ case "relay:ping":
218
+ ws.send(JSON.stringify({ type: "relay:pong", ts: msg.ts }));
219
+ break;
220
+ case "relay:pong":
221
+ break;
222
+ }
223
+ };
224
+ ws.onclose = (event) => {
225
+ cleanup(event.reason || "WebSocket closed");
226
+ };
227
+ ws.onerror = () => {
228
+ cleanup("WebSocket error");
229
+ };
230
+ async function handleRequest(req) {
231
+ const { requestId, providerId, url, method, headers, body } = req;
232
+ const controller = new AbortController();
233
+ inFlight.set(requestId, controller);
234
+ try {
235
+ const proxyFetch = createProxyFetch(providerId, sessionKey);
236
+ const response = await proxyFetch(url, {
237
+ method,
238
+ headers,
239
+ body,
240
+ signal: controller.signal
241
+ });
242
+ const responseHeaders = {};
243
+ response.headers.forEach((value, key) => {
244
+ responseHeaders[key] = value;
245
+ });
246
+ send({
247
+ type: "relay:response:meta",
248
+ requestId,
249
+ status: response.status,
250
+ statusText: response.statusText,
251
+ headers: responseHeaders
252
+ });
253
+ if (response.body) {
254
+ const reader = response.body.getReader();
255
+ const decoder = new TextDecoder();
256
+ while (true) {
257
+ const { done, value } = await reader.read();
258
+ if (done) break;
259
+ if (controller.signal.aborted) break;
260
+ send({
261
+ type: "relay:response:chunk",
262
+ requestId,
263
+ chunk: decoder.decode(value, { stream: true })
264
+ });
265
+ }
266
+ }
267
+ send({ type: "relay:response:done", requestId });
268
+ } catch (err) {
269
+ send({
270
+ type: "relay:response:error",
271
+ requestId,
272
+ error: {
273
+ code: err instanceof ByokyError ? err.code : "PROXY_ERROR",
274
+ message: err instanceof Error ? err.message : "Unknown error"
275
+ }
276
+ });
277
+ } finally {
278
+ inFlight.delete(requestId);
279
+ }
280
+ }
281
+ function send(msg) {
282
+ if (ws.readyState === WS_READY_STATE.OPEN) {
283
+ ws.send(JSON.stringify(msg));
284
+ }
285
+ }
286
+ function cleanup(reason) {
287
+ if (status === "disconnected") return;
288
+ status = "disconnected";
289
+ if (pingInterval) clearInterval(pingInterval);
290
+ for (const controller of inFlight.values()) {
291
+ controller.abort();
292
+ }
293
+ inFlight.clear();
294
+ for (const cb of closeCallbacks) cb(reason);
295
+ closeCallbacks.clear();
296
+ }
297
+ return {
298
+ get status() {
299
+ return status;
300
+ },
301
+ close() {
302
+ if (ws.readyState === WS_READY_STATE.OPEN || ws.readyState === WS_READY_STATE.CONNECTING) {
303
+ ws.close(1e3, "Client closed");
304
+ }
305
+ cleanup("Client closed");
306
+ },
307
+ onClose(callback) {
308
+ closeCallbacks.add(callback);
309
+ return () => {
310
+ closeCallbacks.delete(callback);
311
+ };
312
+ }
313
+ };
314
+ }
315
+
133
316
  // src/byoky.ts
134
317
  var Byoky = class {
135
318
  timeout;
@@ -137,18 +320,61 @@ var Byoky = class {
137
320
  this.timeout = options.timeout ?? 6e4;
138
321
  }
139
322
  async connect(request = {}) {
323
+ if (typeof window === "undefined") {
324
+ throw new ByokyError2(
325
+ ByokyErrorCode.UNKNOWN,
326
+ "Byoky SDK requires a browser environment. On the server, use your API key directly."
327
+ );
328
+ }
140
329
  if (!isExtensionInstalled()) {
141
330
  const storeUrl = getStoreUrl();
142
331
  if (storeUrl) {
143
332
  window.open(storeUrl, "_blank");
144
333
  }
145
- throw ByokyError.walletNotInstalled();
334
+ throw ByokyError2.walletNotInstalled();
146
335
  }
147
336
  const response = await this.sendConnectRequest(request);
337
+ return this.buildSession(response);
338
+ }
339
+ /**
340
+ * Reconnect to an existing session using previously stored response data.
341
+ * Returns null if the session is no longer valid.
342
+ */
343
+ async reconnect(savedResponse) {
344
+ if (typeof window === "undefined") return null;
345
+ if (!isExtensionInstalled()) return null;
346
+ const connected = await this.querySessionStatus(savedResponse.sessionKey);
347
+ if (!connected) return null;
348
+ return this.buildSession(savedResponse);
349
+ }
350
+ buildSession(response) {
351
+ const sessionKey = response.sessionKey;
352
+ const disconnectCallbacks = /* @__PURE__ */ new Set();
353
+ function handleRevocation(event) {
354
+ const msg = event.detail;
355
+ if (msg?.type === "BYOKY_SESSION_REVOKED" && msg.payload?.sessionKey === sessionKey) {
356
+ for (const cb of disconnectCallbacks) cb();
357
+ disconnectCallbacks.clear();
358
+ document.removeEventListener("byoky-message", handleRevocation);
359
+ }
360
+ }
361
+ document.addEventListener("byoky-message", handleRevocation);
148
362
  return {
149
363
  ...response,
150
- createFetch: (providerId) => createProxyFetch(providerId, response.sessionKey),
151
- disconnect: () => this.disconnect(response.sessionKey)
364
+ createFetch: (providerId) => createProxyFetch(providerId, sessionKey),
365
+ createRelay: (wsUrl) => createRelayClient(wsUrl, sessionKey, response.providers),
366
+ disconnect: () => {
367
+ document.removeEventListener("byoky-message", handleRevocation);
368
+ this.sendDisconnect(sessionKey);
369
+ },
370
+ isConnected: () => this.querySessionStatus(sessionKey),
371
+ getUsage: () => this.querySessionUsage(sessionKey),
372
+ onDisconnect: (callback) => {
373
+ disconnectCallbacks.add(callback);
374
+ return () => {
375
+ disconnectCallbacks.delete(callback);
376
+ };
377
+ }
152
378
  };
153
379
  }
154
380
  sendConnectRequest(request) {
@@ -157,7 +383,7 @@ var Byoky = class {
157
383
  const timeoutId = setTimeout(() => {
158
384
  cleanup();
159
385
  reject(
160
- new ByokyError(ByokyErrorCode.UNKNOWN, "Connection request timed out")
386
+ new ByokyError2(ByokyErrorCode.UNKNOWN, "Connection request timed out")
161
387
  );
162
388
  }, this.timeout);
163
389
  function handleEvent(event) {
@@ -169,7 +395,7 @@ var Byoky = class {
169
395
  resolve(msg.payload);
170
396
  } else if (msg.type === "BYOKY_ERROR") {
171
397
  const { code, message } = msg.payload;
172
- reject(new ByokyError(code, message));
398
+ reject(new ByokyError2(code, message));
173
399
  }
174
400
  }
175
401
  function cleanup() {
@@ -188,22 +414,80 @@ var Byoky = class {
188
414
  );
189
415
  });
190
416
  }
191
- disconnect(sessionKey) {
417
+ sendDisconnect(sessionKey) {
192
418
  window.postMessage(
193
419
  { type: "BYOKY_DISCONNECT", payload: { sessionKey } },
194
420
  "*"
195
421
  );
196
422
  }
423
+ querySessionStatus(sessionKey) {
424
+ return new Promise((resolve) => {
425
+ const requestId = crypto.randomUUID();
426
+ const timeout = setTimeout(() => {
427
+ cleanup();
428
+ resolve(false);
429
+ }, 5e3);
430
+ function handleEvent(event) {
431
+ const msg = event.detail;
432
+ if (msg?.requestId !== requestId) return;
433
+ if (msg.type === "BYOKY_SESSION_STATUS_RESPONSE") {
434
+ cleanup();
435
+ resolve(!!msg.payload?.connected);
436
+ }
437
+ }
438
+ function cleanup() {
439
+ clearTimeout(timeout);
440
+ document.removeEventListener("byoky-message", handleEvent);
441
+ }
442
+ document.addEventListener("byoky-message", handleEvent);
443
+ window.postMessage({
444
+ type: "BYOKY_SESSION_STATUS",
445
+ requestId,
446
+ payload: { sessionKey }
447
+ }, "*");
448
+ });
449
+ }
450
+ querySessionUsage(sessionKey) {
451
+ return new Promise((resolve, reject) => {
452
+ const requestId = crypto.randomUUID();
453
+ const timeout = setTimeout(() => {
454
+ cleanup();
455
+ reject(new Error("Usage query timed out"));
456
+ }, 5e3);
457
+ function handleEvent(event) {
458
+ const msg = event.detail;
459
+ if (msg?.requestId !== requestId) return;
460
+ if (msg.type === "BYOKY_SESSION_USAGE_RESPONSE") {
461
+ cleanup();
462
+ if (msg.payload) {
463
+ resolve(msg.payload);
464
+ } else {
465
+ reject(new Error("Session not found"));
466
+ }
467
+ }
468
+ }
469
+ function cleanup() {
470
+ clearTimeout(timeout);
471
+ document.removeEventListener("byoky-message", handleEvent);
472
+ }
473
+ document.addEventListener("byoky-message", handleEvent);
474
+ window.postMessage({
475
+ type: "BYOKY_SESSION_USAGE",
476
+ requestId,
477
+ payload: { sessionKey }
478
+ }, "*");
479
+ });
480
+ }
197
481
  };
198
482
 
199
483
  // src/index.ts
200
484
  import {
201
- ByokyError as ByokyError2,
485
+ ByokyError as ByokyError3,
202
486
  ByokyErrorCode as ByokyErrorCode2
203
487
  } from "@byoky/core";
204
488
  export {
205
489
  Byoky,
206
- ByokyError2 as ByokyError,
490
+ ByokyError3 as ByokyError,
207
491
  ByokyErrorCode2 as ByokyErrorCode,
208
492
  createProxyFetch,
209
493
  getStoreUrl,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts","../src/index.ts"],"sourcesContent":["import type { ConnectRequest, ConnectResponse } from '@byoky/core';\nimport { ByokyError, ByokyErrorCode, isByokyMessage } from '@byoky/core';\nimport { isExtensionInstalled, getStoreUrl } from './detect.js';\nimport { createProxyFetch } from './proxy-fetch.js';\n\nexport interface ByokySession extends ConnectResponse {\n createFetch(providerId: string): typeof fetch;\n disconnect(): 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 (!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\n return {\n ...response,\n createFetch: (providerId: string) =>\n createProxyFetch(providerId, response.sessionKey),\n disconnect: () => this.disconnect(response.sessionKey),\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 disconnect(sessionKey: string): void {\n window.postMessage(\n { type: 'BYOKY_DISCONNECT', 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","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 {\n type ConnectRequest,\n type ConnectResponse,\n type ProviderRequirement,\n ByokyError,\n ByokyErrorCode,\n} from '@byoky/core';\n"],"mappings":"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","names":["ByokyError","ByokyErrorCode"]}
1
+ {"version":3,"sources":["../src/byoky.ts","../src/detect.ts","../src/proxy-fetch.ts","../src/relay-client.ts","../src/index.ts"],"sourcesContent":["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","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"],"mappings":"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","names":["ByokyError","ByokyError","ByokyError","ByokyErrorCode"]}
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ ByokyServer: () => ByokyServer
24
+ });
25
+ module.exports = __toCommonJS(server_exports);
26
+ var import_core = require("@byoky/core");
27
+ var ByokyServer = class {
28
+ pingInterval;
29
+ helloTimeout;
30
+ constructor(options = {}) {
31
+ this.pingInterval = options.pingInterval ?? 3e4;
32
+ this.helloTimeout = options.helloTimeout ?? 1e4;
33
+ }
34
+ handleConnection(ws) {
35
+ const self = this;
36
+ return new Promise((resolve, reject) => {
37
+ let sessionId = "";
38
+ let providers = {};
39
+ let connected = false;
40
+ const closeCallbacks = /* @__PURE__ */ new Set();
41
+ const pendingRequests = /* @__PURE__ */ new Map();
42
+ let pingTimer;
43
+ let requestCounter = 0;
44
+ const helloTimer = setTimeout(() => {
45
+ reject(import_core.ByokyError.relayConnectionFailed("Timed out waiting for relay:hello"));
46
+ ws.close(4e3, "Hello timeout");
47
+ }, this.helloTimeout);
48
+ ws.onmessage = (event) => {
49
+ const msg = (0, import_core.parseRelayMessage)(event.data);
50
+ if (!msg) return;
51
+ switch (msg.type) {
52
+ case "relay:hello":
53
+ handleHello(msg);
54
+ break;
55
+ case "relay:response:meta": {
56
+ const pending = pendingRequests.get(msg.requestId);
57
+ if (pending) {
58
+ pending.resolveMeta({
59
+ status: msg.status,
60
+ statusText: msg.statusText,
61
+ headers: msg.headers
62
+ });
63
+ }
64
+ break;
65
+ }
66
+ case "relay:response:chunk": {
67
+ const pending = pendingRequests.get(msg.requestId);
68
+ if (pending) pending.pushChunk(msg.chunk);
69
+ break;
70
+ }
71
+ case "relay:response:done": {
72
+ const pending = pendingRequests.get(msg.requestId);
73
+ if (pending) {
74
+ pending.done();
75
+ pendingRequests.delete(msg.requestId);
76
+ }
77
+ break;
78
+ }
79
+ case "relay:response:error": {
80
+ const pending = pendingRequests.get(msg.requestId);
81
+ if (pending) {
82
+ pending.error(new Error(msg.error.message));
83
+ pendingRequests.delete(msg.requestId);
84
+ }
85
+ break;
86
+ }
87
+ case "relay:ping":
88
+ (0, import_core.sendRelayMessage)(ws, { type: "relay:pong", ts: msg.ts });
89
+ break;
90
+ case "relay:pong":
91
+ break;
92
+ }
93
+ };
94
+ ws.onclose = () => {
95
+ cleanup();
96
+ };
97
+ ws.onerror = () => {
98
+ cleanup();
99
+ };
100
+ function handleHello(msg) {
101
+ clearTimeout(helloTimer);
102
+ sessionId = msg.sessionId;
103
+ providers = msg.providers;
104
+ connected = true;
105
+ if (self.pingInterval > 0) {
106
+ pingTimer = setInterval(() => {
107
+ (0, import_core.sendRelayMessage)(ws, { type: "relay:ping", ts: Date.now() });
108
+ }, self.pingInterval);
109
+ }
110
+ resolve(client);
111
+ }
112
+ function cleanup() {
113
+ if (!connected) return;
114
+ connected = false;
115
+ clearTimeout(helloTimer);
116
+ if (pingTimer) clearInterval(pingTimer);
117
+ for (const pending of pendingRequests.values()) {
118
+ pending.error(import_core.ByokyError.relayDisconnected());
119
+ }
120
+ pendingRequests.clear();
121
+ for (const cb of closeCallbacks) cb();
122
+ closeCallbacks.clear();
123
+ }
124
+ function createClientFetch(providerId) {
125
+ return async (input, init) => {
126
+ if (!connected) throw import_core.ByokyError.relayDisconnected();
127
+ const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
128
+ const method = init?.method ?? "GET";
129
+ const headers = init?.headers ? Object.fromEntries(new Headers(init.headers).entries()) : {};
130
+ const body = init?.body ? await readBody(init.body) : void 0;
131
+ const requestId = `relay-${++requestCounter}-${Date.now()}`;
132
+ return new Promise((resolveFetch, rejectFetch) => {
133
+ let metaResolved = false;
134
+ let controller;
135
+ const encoder = new TextEncoder();
136
+ const stream = new ReadableStream({
137
+ start(c) {
138
+ controller = c;
139
+ }
140
+ });
141
+ pendingRequests.set(requestId, {
142
+ resolveMeta: (meta) => {
143
+ if (metaResolved) return;
144
+ metaResolved = true;
145
+ resolveFetch(new Response(stream, {
146
+ status: meta.status,
147
+ statusText: meta.statusText,
148
+ headers: new Headers(meta.headers)
149
+ }));
150
+ },
151
+ pushChunk: (chunk) => {
152
+ try {
153
+ controller.enqueue(encoder.encode(chunk));
154
+ } catch {
155
+ }
156
+ },
157
+ done: () => {
158
+ try {
159
+ controller.close();
160
+ } catch {
161
+ }
162
+ },
163
+ error: (err) => {
164
+ try {
165
+ controller.error(err);
166
+ } catch {
167
+ }
168
+ if (!metaResolved) {
169
+ metaResolved = true;
170
+ rejectFetch(err);
171
+ }
172
+ }
173
+ });
174
+ (0, import_core.sendRelayMessage)(ws, {
175
+ type: "relay:request",
176
+ requestId,
177
+ providerId,
178
+ url,
179
+ method,
180
+ headers,
181
+ body
182
+ });
183
+ });
184
+ };
185
+ }
186
+ const client = {
187
+ get sessionId() {
188
+ return sessionId;
189
+ },
190
+ get providers() {
191
+ return providers;
192
+ },
193
+ get connected() {
194
+ return connected;
195
+ },
196
+ createFetch: (providerId) => createClientFetch(providerId),
197
+ close() {
198
+ ws.close(1e3, "Server closed");
199
+ cleanup();
200
+ },
201
+ onClose(callback) {
202
+ closeCallbacks.add(callback);
203
+ return () => {
204
+ closeCallbacks.delete(callback);
205
+ };
206
+ }
207
+ };
208
+ });
209
+ }
210
+ };
211
+ async function readBody(body) {
212
+ if (typeof body === "string") return body;
213
+ if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
214
+ if (body instanceof Uint8Array) return new TextDecoder().decode(body);
215
+ if (typeof Blob !== "undefined" && body instanceof Blob) return body.text();
216
+ if (typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams) return body.toString();
217
+ return void 0;
218
+ }
219
+ // Annotate the CommonJS export names for ESM import in node:
220
+ 0 && (module.exports = {
221
+ ByokyServer
222
+ });
223
+ //# sourceMappingURL=server.cjs.map