@opendatalabs/personal-server-ts-lite 0.0.1-canary.01ca694

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.
Files changed (60) hide show
  1. package/dist/bridge.d.ts +23 -0
  2. package/dist/bridge.d.ts.map +1 -0
  3. package/dist/bridge.js +53 -0
  4. package/dist/bridge.js.map +1 -0
  5. package/dist/browser-runtime.d.ts +31 -0
  6. package/dist/browser-runtime.d.ts.map +1 -0
  7. package/dist/browser-runtime.js +109 -0
  8. package/dist/browser-runtime.js.map +1 -0
  9. package/dist/browser-tls-rustls/browser_tls_rustls.d.ts +49 -0
  10. package/dist/browser-tls-rustls/browser_tls_rustls.js +401 -0
  11. package/dist/browser-tls-rustls/browser_tls_rustls_bg.wasm +0 -0
  12. package/dist/client.d.ts +22 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +376 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/diagnostics.d.ts +166 -0
  17. package/dist/diagnostics.d.ts.map +1 -0
  18. package/dist/diagnostics.js +265 -0
  19. package/dist/diagnostics.js.map +1 -0
  20. package/dist/index.d.ts +15 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +14 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mcp-store.d.ts +35 -0
  25. package/dist/mcp-store.d.ts.map +1 -0
  26. package/dist/mcp-store.js +177 -0
  27. package/dist/mcp-store.js.map +1 -0
  28. package/dist/owner-binding.d.ts +7 -0
  29. package/dist/owner-binding.d.ts.map +1 -0
  30. package/dist/owner-binding.js +18 -0
  31. package/dist/owner-binding.js.map +1 -0
  32. package/dist/relay-tls.d.ts +19 -0
  33. package/dist/relay-tls.d.ts.map +1 -0
  34. package/dist/relay-tls.js +226 -0
  35. package/dist/relay-tls.js.map +1 -0
  36. package/dist/relay.d.ts +70 -0
  37. package/dist/relay.d.ts.map +1 -0
  38. package/dist/relay.js +368 -0
  39. package/dist/relay.js.map +1 -0
  40. package/dist/runtime.d.ts +95 -0
  41. package/dist/runtime.d.ts.map +1 -0
  42. package/dist/runtime.js +991 -0
  43. package/dist/runtime.js.map +1 -0
  44. package/dist/state.d.ts +62 -0
  45. package/dist/state.d.ts.map +1 -0
  46. package/dist/state.js +296 -0
  47. package/dist/state.js.map +1 -0
  48. package/dist/storage-utils.d.ts +8 -0
  49. package/dist/storage-utils.d.ts.map +1 -0
  50. package/dist/storage-utils.js +67 -0
  51. package/dist/storage-utils.js.map +1 -0
  52. package/dist/storage.d.ts +57 -0
  53. package/dist/storage.d.ts.map +1 -0
  54. package/dist/storage.js +575 -0
  55. package/dist/storage.js.map +1 -0
  56. package/dist/sync.d.ts +28 -0
  57. package/dist/sync.d.ts.map +1 -0
  58. package/dist/sync.js +158 -0
  59. package/dist/sync.js.map +1 -0
  60. package/package.json +40 -0
@@ -0,0 +1,226 @@
1
+ import forge from "node-forge";
2
+ import initRustls, { TlsServer as RustlsServer, } from "./browser-tls-rustls/browser_tls_rustls.js";
3
+ const TLS_IDENTITY_CACHE_KEY = "personal-server-lite-tls-identity-v1";
4
+ const DEFAULT_PUBLIC_SUFFIX = "34.16.49.200.sslip.io";
5
+ let rustlsInitPromise;
6
+ export function createRustlsPsLiteRelayTlsFactory(options) {
7
+ let identityPromise;
8
+ return {
9
+ async prepare(input) {
10
+ identityPromise ??= createTlsIdentity(input, options);
11
+ await identityPromise;
12
+ },
13
+ async createStream(input) {
14
+ await ensureRustls();
15
+ identityPromise ??= createTlsIdentity(input, options);
16
+ const identity = await identityPromise;
17
+ const tls = new RustlsServer(identity.certPem, identity.keyPem);
18
+ return createRustlsStream(tls);
19
+ },
20
+ };
21
+ }
22
+ export function psLiteRelayPublicHost(sessionId, publicSuffix = DEFAULT_PUBLIC_SUFFIX) {
23
+ return `${sessionId}.${publicSuffix.replace(/^\./, "")}`;
24
+ }
25
+ export function psLiteRelayPublicUrl(sessionId, publicSuffix = DEFAULT_PUBLIC_SUFFIX) {
26
+ return `https://${psLiteRelayPublicHost(sessionId, publicSuffix)}`;
27
+ }
28
+ function createRustlsStream(tls) {
29
+ return {
30
+ processTls(payload) {
31
+ return normalizeTlsStep(tls.process_tls(payload));
32
+ },
33
+ writePlaintext(payload, endStream) {
34
+ return normalizeTlsStep(tls.write_plaintext(payload, endStream));
35
+ },
36
+ close() {
37
+ tls.free();
38
+ },
39
+ };
40
+ }
41
+ function ensureRustls() {
42
+ rustlsInitPromise ??= initRustls();
43
+ return rustlsInitPromise;
44
+ }
45
+ function normalizeTlsStep(value) {
46
+ const step = value;
47
+ return {
48
+ plaintext: step.plaintext instanceof Uint8Array ? step.plaintext : new Uint8Array(),
49
+ tls: step.tls instanceof Uint8Array ? step.tls : new Uint8Array(),
50
+ handshaking: Boolean(step.handshaking),
51
+ };
52
+ }
53
+ async function createTlsIdentity(input, options) {
54
+ const hostname = psLiteRelayPublicHost(input.sessionId, options.publicSuffix);
55
+ const cached = readCachedTlsIdentity(hostname, options.storage);
56
+ if (cached) {
57
+ return cached;
58
+ }
59
+ const keys = await generateKeyPair();
60
+ const keyPem = forge.pki.privateKeyToPem(keys.privateKey);
61
+ const csrPem = createCsrPem(hostname, keys);
62
+ const issued = await requestAcmeCertificate({
63
+ issuerUrl: resolveCertIssuerUrl(options),
64
+ sessionId: input.sessionId,
65
+ csrPem,
66
+ keyPem,
67
+ issueToken: input.issueToken ?? "",
68
+ hostname,
69
+ storage: options.storage,
70
+ logger: options.logger,
71
+ });
72
+ if (issued) {
73
+ return issued;
74
+ }
75
+ return createSelfSignedIdentity(hostname, keyPem, keys);
76
+ }
77
+ function generateKeyPair() {
78
+ return new Promise((resolve, reject) => {
79
+ forge.pki.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (error, keyPair) => {
80
+ if (error || !keyPair) {
81
+ reject(error ?? new Error("failed to generate keypair"));
82
+ return;
83
+ }
84
+ resolve(keyPair);
85
+ });
86
+ });
87
+ }
88
+ function createCsrPem(hostname, keys) {
89
+ const csr = forge.pki.createCertificationRequest();
90
+ csr.publicKey = keys.publicKey;
91
+ csr.setSubject([{ name: "commonName", value: hostname }]);
92
+ csr.setAttributes([
93
+ {
94
+ name: "extensionRequest",
95
+ extensions: [
96
+ {
97
+ name: "subjectAltName",
98
+ altNames: [{ type: 2, value: hostname }],
99
+ },
100
+ ],
101
+ },
102
+ ]);
103
+ csr.sign(keys.privateKey, forge.md.sha256.create());
104
+ return forge.pki.certificationRequestToPem(csr);
105
+ }
106
+ function createSelfSignedIdentity(hostname, keyPem, keys) {
107
+ const cert = forge.pki.createCertificate();
108
+ cert.publicKey = keys.publicKey;
109
+ cert.serialNumber = Array.from(crypto.getRandomValues(new Uint8Array(12)), (byte) => byte.toString(16).padStart(2, "0")).join("");
110
+ cert.validity.notBefore = new Date(Date.now() - 60_000);
111
+ cert.validity.notAfter = new Date(Date.now() + 24 * 60 * 60 * 1000);
112
+ const attrs = [{ name: "commonName", value: hostname }];
113
+ cert.setSubject(attrs);
114
+ cert.setIssuer(attrs);
115
+ cert.setExtensions([
116
+ { name: "basicConstraints", cA: false },
117
+ { name: "keyUsage", digitalSignature: true, keyEncipherment: true },
118
+ { name: "extKeyUsage", serverAuth: true },
119
+ {
120
+ name: "subjectAltName",
121
+ altNames: [{ type: 2, value: hostname }],
122
+ },
123
+ ]);
124
+ cert.sign(keys.privateKey, forge.md.sha256.create());
125
+ return {
126
+ certPem: forge.pki.certificateToPem(cert),
127
+ keyPem,
128
+ hostname,
129
+ source: "self-signed",
130
+ trusted: false,
131
+ };
132
+ }
133
+ async function requestAcmeCertificate(input) {
134
+ if (!input.issueToken) {
135
+ input.logger?.("ACME issuer token unavailable; using self-signed certificate");
136
+ return undefined;
137
+ }
138
+ try {
139
+ const response = await fetch(input.issuerUrl, {
140
+ method: "POST",
141
+ headers: { "content-type": "application/json" },
142
+ body: JSON.stringify({
143
+ sessionId: input.sessionId,
144
+ csrPem: input.csrPem,
145
+ issueToken: input.issueToken,
146
+ }),
147
+ });
148
+ if (!response.ok) {
149
+ const detail = await response.text();
150
+ input.logger?.(`ACME issuer unavailable (${response.status}); using self-signed certificate`);
151
+ input.logger?.(detail.slice(0, 240));
152
+ return undefined;
153
+ }
154
+ const payload = (await response.json());
155
+ if (!payload.certPem) {
156
+ input.logger?.("ACME issuer returned no certificate; using self-signed certificate");
157
+ return undefined;
158
+ }
159
+ const identity = {
160
+ certPem: payload.certPem,
161
+ keyPem: input.keyPem,
162
+ hostname: input.hostname,
163
+ source: "acme",
164
+ trusted: true,
165
+ };
166
+ cacheTlsIdentity(identity, input.storage);
167
+ return identity;
168
+ }
169
+ catch (error) {
170
+ input.logger?.(error instanceof Error ? error.message : String(error));
171
+ input.logger?.("ACME certificate request failed; using self-signed certificate");
172
+ return undefined;
173
+ }
174
+ }
175
+ function resolveCertIssuerUrl(options) {
176
+ if (options.certIssuerUrl) {
177
+ return options.certIssuerUrl.replace(/\/+$/, "");
178
+ }
179
+ const url = new URL(options.controlUrl);
180
+ url.protocol = url.protocol === "wss:" ? "https:" : "http:";
181
+ url.pathname = "/issue-cert";
182
+ url.search = "";
183
+ url.hash = "";
184
+ return url.toString();
185
+ }
186
+ function readCachedTlsIdentity(hostname, storage = globalThis.localStorage) {
187
+ try {
188
+ const raw = storage.getItem(`${TLS_IDENTITY_CACHE_KEY}:${hostname}`);
189
+ if (!raw) {
190
+ return undefined;
191
+ }
192
+ const identity = JSON.parse(raw);
193
+ if (identity.hostname !== hostname ||
194
+ !identity.certPem ||
195
+ !identity.keyPem ||
196
+ identity.source !== "acme") {
197
+ return undefined;
198
+ }
199
+ const notAfter = firstCertificateNotAfter(identity.certPem);
200
+ if (notAfter.getTime() - Date.now() < 24 * 60 * 60 * 1000) {
201
+ return undefined;
202
+ }
203
+ return {
204
+ ...identity,
205
+ source: "cached-acme",
206
+ trusted: true,
207
+ };
208
+ }
209
+ catch {
210
+ return undefined;
211
+ }
212
+ }
213
+ function cacheTlsIdentity(identity, storage = globalThis.localStorage) {
214
+ if (identity.source !== "acme") {
215
+ return;
216
+ }
217
+ storage.setItem(`${TLS_IDENTITY_CACHE_KEY}:${identity.hostname}`, JSON.stringify(identity));
218
+ }
219
+ function firstCertificateNotAfter(certPem) {
220
+ const match = certPem.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/);
221
+ if (!match) {
222
+ return new Date(0);
223
+ }
224
+ return forge.pki.certificateFromPem(match[0]).validity.notAfter;
225
+ }
226
+ //# sourceMappingURL=relay-tls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-tls.js","sourceRoot":"","sources":["../src/relay-tls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { type PsLiteBridgeResponse } from "./bridge.js";
2
+ import type { PsLiteRuntime } from "./runtime.js";
3
+ export interface PsLiteRelayClientOptions {
4
+ sessionId: string;
5
+ runtime: PsLiteRuntime;
6
+ controlUrl?: string;
7
+ publicSuffix?: string;
8
+ certIssuerUrl?: string;
9
+ origin?: string;
10
+ webSocketFactory?: PsLiteRelayWebSocketFactory;
11
+ tls?: PsLiteRelayTlsFactory | false;
12
+ logger?: (line: string) => void;
13
+ onStatus?: (status: PsLiteRelayStatus) => void;
14
+ }
15
+ export type PsLiteRelayStatus = "connecting" | "connected" | "disconnected" | "closed" | "error";
16
+ export interface PsLiteRelayClient {
17
+ readonly sessionId: string;
18
+ readonly url: string;
19
+ close(reason?: string): void;
20
+ }
21
+ export interface PsLiteRelayWebSocketFactory {
22
+ (url: string): PsLiteRelayWebSocket;
23
+ }
24
+ export interface PsLiteRelayWebSocket {
25
+ binaryType: string;
26
+ readonly readyState: number;
27
+ readonly OPEN: number;
28
+ readonly CONNECTING: number;
29
+ onopen: (() => void) | null;
30
+ onmessage: ((event: {
31
+ data: string | ArrayBuffer | Uint8Array;
32
+ }) => void) | null;
33
+ onclose: (() => void) | null;
34
+ onerror: (() => void) | null;
35
+ send(data: string | Uint8Array): void;
36
+ close(code?: number, reason?: string): void;
37
+ }
38
+ export interface PsLiteRelayTlsFactory {
39
+ prepare?(input: PsLiteRelayTlsPrepareInput): Promise<void>;
40
+ createStream(input: PsLiteRelayTlsStreamInput): Promise<PsLiteRelayTlsStream>;
41
+ }
42
+ export interface PsLiteRelayTlsPrepareInput {
43
+ sessionId: string;
44
+ issueToken?: string;
45
+ }
46
+ export interface PsLiteRelayTlsStreamInput {
47
+ sessionId: string;
48
+ streamId: number;
49
+ issueToken?: string;
50
+ }
51
+ export interface PsLiteRelayTlsStream {
52
+ processTls(payload: Uint8Array): PsLiteRelayTlsStep;
53
+ writePlaintext(payload: Uint8Array, endStream: boolean): PsLiteRelayTlsStep;
54
+ close?(): void;
55
+ }
56
+ export interface PsLiteRelayTlsStep {
57
+ plaintext: Uint8Array;
58
+ tls: Uint8Array;
59
+ handshaking?: boolean;
60
+ }
61
+ export declare function psLiteRelayControlUrl(sessionId: string, controlUrl?: string): string;
62
+ export declare function psLiteRelayPublicUrl(sessionId: string, publicSuffix?: string): string;
63
+ export declare function startPsLiteRelayClient(options: PsLiteRelayClientOptions): PsLiteRelayClient;
64
+ export declare function buildHttpResponse(response: PsLiteBridgeResponse): string;
65
+ export declare function encodeDataFrame(streamId: number, payload: Uint8Array): Uint8Array;
66
+ export declare function decodeDataFrame(data: string | ArrayBuffer | Uint8Array): {
67
+ streamId: number;
68
+ payload: Uint8Array;
69
+ } | null;
70
+ //# sourceMappingURL=relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../src/relay.ts"],"names":[],"mappings":"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"}
package/dist/relay.js ADDED
@@ -0,0 +1,368 @@
1
+ import { handlePsLiteBridgeRequest, } from "./bridge.js";
2
+ import { createRustlsPsLiteRelayTlsFactory } from "./relay-tls.js";
3
+ const DATA_FRAME_TYPE = 1;
4
+ const HEADER_BYTES = 5;
5
+ const DEFAULT_CONTROL_URL = "wss://control.34.16.49.200.sslip.io:8443";
6
+ const DEFAULT_PUBLIC_SUFFIX = "34.16.49.200.sslip.io";
7
+ const DEFAULT_ORIGIN = "https://ps-lite.local";
8
+ export function psLiteRelayControlUrl(sessionId, controlUrl = DEFAULT_CONTROL_URL) {
9
+ return `${controlUrl.replace(/\/+$/, "")}/browser/${sessionId}`;
10
+ }
11
+ export function psLiteRelayPublicUrl(sessionId, publicSuffix = DEFAULT_PUBLIC_SUFFIX) {
12
+ return `https://${sessionId}.${publicSuffix.replace(/^\./, "")}`;
13
+ }
14
+ export function startPsLiteRelayClient(options) {
15
+ const baseControlUrl = options.controlUrl ?? DEFAULT_CONTROL_URL;
16
+ const controlUrl = psLiteRelayControlUrl(options.sessionId, baseControlUrl);
17
+ const socket = createSocket(options.webSocketFactory, controlUrl);
18
+ const tls = options.tls === undefined
19
+ ? createRustlsPsLiteRelayTlsFactory({
20
+ controlUrl: baseControlUrl,
21
+ publicSuffix: options.publicSuffix,
22
+ certIssuerUrl: options.certIssuerUrl,
23
+ logger: options.logger,
24
+ })
25
+ : options.tls;
26
+ const streams = new Map();
27
+ const pendingStreamData = new Map();
28
+ const origin = options.origin ?? DEFAULT_ORIGIN;
29
+ let issueToken;
30
+ let closed = false;
31
+ socket.binaryType = "arraybuffer";
32
+ options.onStatus?.("connecting");
33
+ const log = (line) => options.logger?.(line);
34
+ const sendText = (message) => {
35
+ if (socket.readyState === socket.OPEN) {
36
+ socket.send(JSON.stringify(message));
37
+ }
38
+ };
39
+ const sendData = (streamId, payload) => {
40
+ if (socket.readyState === socket.OPEN) {
41
+ socket.send(encodeDataFrame(streamId, payload));
42
+ }
43
+ };
44
+ const closeStream = (streamId, reason) => {
45
+ const stream = streams.get(streamId);
46
+ stream?.tls?.close?.();
47
+ streams.delete(streamId);
48
+ pendingStreamData.delete(streamId);
49
+ sendText({ type: "stream.close", streamId, reason });
50
+ };
51
+ const writePlaintextResponse = (stream, responseBytes) => {
52
+ if (!stream.tls) {
53
+ sendData(stream.streamId, responseBytes);
54
+ return;
55
+ }
56
+ const step = stream.tls.writePlaintext(responseBytes, true);
57
+ if (step.tls.length > 0) {
58
+ sendData(stream.streamId, step.tls);
59
+ }
60
+ };
61
+ const drainHttpRequests = async (stream) => {
62
+ while (true) {
63
+ const parsed = parseHttpRequest(stream.plaintext);
64
+ if (!parsed) {
65
+ return;
66
+ }
67
+ stream.plaintext = stream.plaintext.slice(parsed.consumedBytes);
68
+ const response = await handleRelayHttpRequest(options.runtime, parsed.request, origin);
69
+ // `response` is a binary (latin1) string: buildHttpResponse appends the
70
+ // body via bytesToBinary, so each char code 0x00–0xFF is one body byte.
71
+ // It must be turned back into bytes the same way (binaryToBytes), NOT
72
+ // UTF-8 re-encoded — textToBytes would expand every byte >= 0x80 into a
73
+ // 2-byte sequence, corrupting binary payloads (e.g. OpenPGP-encrypted
74
+ // files fail downstream with "not a valid OpenPGP message"). ASCII/JSON
75
+ // bodies survive UTF-8, which is why only encrypted binary downloads broke.
76
+ writePlaintextResponse(stream, binaryToBytes(response));
77
+ closeStream(stream.streamId, "http_response_sent");
78
+ return;
79
+ }
80
+ };
81
+ const handlePlaintext = (stream, plaintext) => {
82
+ if (plaintext.length === 0) {
83
+ return;
84
+ }
85
+ stream.plaintext += bytesToBinary(plaintext);
86
+ void drainHttpRequests(stream).catch((error) => {
87
+ log(error instanceof Error ? error.message : String(error));
88
+ closeStream(stream.streamId, "handler_error");
89
+ });
90
+ };
91
+ const processPayload = (stream, payload) => {
92
+ if (!stream.tls) {
93
+ handlePlaintext(stream, payload);
94
+ return;
95
+ }
96
+ try {
97
+ const step = stream.tls.processTls(payload);
98
+ if (step.tls.length > 0) {
99
+ sendData(stream.streamId, step.tls);
100
+ }
101
+ handlePlaintext(stream, step.plaintext);
102
+ }
103
+ catch (error) {
104
+ log(error instanceof Error ? error.message : String(error));
105
+ closeStream(stream.streamId, "tls_error");
106
+ }
107
+ };
108
+ const openStream = async (streamId) => {
109
+ const stream = {
110
+ streamId,
111
+ plaintext: "",
112
+ };
113
+ if (tls) {
114
+ stream.tls = await tls.createStream({
115
+ sessionId: options.sessionId,
116
+ streamId,
117
+ issueToken,
118
+ });
119
+ }
120
+ streams.set(streamId, stream);
121
+ const buffered = pendingStreamData.get(streamId) ?? [];
122
+ pendingStreamData.delete(streamId);
123
+ for (const payload of buffered) {
124
+ processPayload(stream, payload);
125
+ }
126
+ };
127
+ const handleControl = (message) => {
128
+ if (message.type === "session.ready") {
129
+ issueToken = message.issueToken;
130
+ options.onStatus?.("connected");
131
+ if (tls && tls.prepare) {
132
+ void tls
133
+ .prepare({
134
+ sessionId: options.sessionId,
135
+ issueToken,
136
+ })
137
+ .catch((error) => {
138
+ log(error instanceof Error ? error.message : String(error));
139
+ });
140
+ }
141
+ return;
142
+ }
143
+ if (message.type === "pong") {
144
+ return;
145
+ }
146
+ if (message.type === "stream.open" &&
147
+ typeof message.streamId === "number") {
148
+ void openStream(message.streamId).catch((error) => {
149
+ log(error instanceof Error ? error.message : String(error));
150
+ if (typeof message.streamId === "number") {
151
+ closeStream(message.streamId, "stream_open_error");
152
+ }
153
+ });
154
+ return;
155
+ }
156
+ if (message.type === "stream.close" &&
157
+ typeof message.streamId === "number") {
158
+ streams.get(message.streamId)?.tls?.close?.();
159
+ streams.delete(message.streamId);
160
+ pendingStreamData.delete(message.streamId);
161
+ }
162
+ };
163
+ const handleDataFrame = (data) => {
164
+ const frame = decodeDataFrame(data);
165
+ if (!frame) {
166
+ return;
167
+ }
168
+ const stream = streams.get(frame.streamId);
169
+ if (!stream) {
170
+ const buffered = pendingStreamData.get(frame.streamId) ?? [];
171
+ buffered.push(frame.payload);
172
+ pendingStreamData.set(frame.streamId, buffered);
173
+ return;
174
+ }
175
+ processPayload(stream, frame.payload);
176
+ };
177
+ socket.onopen = () => options.onStatus?.("connected");
178
+ socket.onmessage = (event) => {
179
+ if (typeof event.data === "string") {
180
+ handleControl(JSON.parse(event.data));
181
+ return;
182
+ }
183
+ handleDataFrame(event.data);
184
+ };
185
+ socket.onclose = () => {
186
+ streams.forEach((stream) => stream.tls?.close?.());
187
+ streams.clear();
188
+ pendingStreamData.clear();
189
+ options.onStatus?.(closed ? "closed" : "disconnected");
190
+ };
191
+ socket.onerror = () => options.onStatus?.("error");
192
+ return {
193
+ sessionId: options.sessionId,
194
+ url: controlUrl,
195
+ close(reason = "closed") {
196
+ closed = true;
197
+ streams.forEach((stream) => stream.tls?.close?.());
198
+ streams.clear();
199
+ pendingStreamData.clear();
200
+ socket.close(1000, reason);
201
+ },
202
+ };
203
+ }
204
+ async function handleRelayHttpRequest(runtime, request, origin) {
205
+ const bridgeRequest = {
206
+ requestId: crypto.randomUUID(),
207
+ method: request.method,
208
+ path: request.path,
209
+ query: request.query,
210
+ headers: request.headers,
211
+ body: encodeBase64(request.body),
212
+ };
213
+ const bridgeResponse = await handlePsLiteBridgeRequest(runtime, bridgeRequest, {
214
+ origin,
215
+ });
216
+ return buildHttpResponse(bridgeResponse);
217
+ }
218
+ export function buildHttpResponse(response) {
219
+ const responseHeaders = {
220
+ "cache-control": "no-store",
221
+ connection: "close",
222
+ ...response.headers,
223
+ };
224
+ const bodyBytes = decodeBase64(response.body);
225
+ const head = [
226
+ `HTTP/1.1 ${response.status} ${httpStatusText(response.status)}`,
227
+ ...Object.entries(responseHeaders).map(([key, value]) => `${key}: ${value}`),
228
+ `content-length: ${bodyBytes.length}`,
229
+ "",
230
+ "",
231
+ ].join("\r\n");
232
+ return head + bytesToBinary(bodyBytes);
233
+ }
234
+ function createSocket(factory, url) {
235
+ if (factory) {
236
+ return factory(url);
237
+ }
238
+ if (typeof WebSocket === "undefined") {
239
+ throw new Error("WebSocket is required to start PS Lite relay client");
240
+ }
241
+ return new WebSocket(url);
242
+ }
243
+ export function encodeDataFrame(streamId, payload) {
244
+ const frame = new Uint8Array(HEADER_BYTES + payload.length);
245
+ frame[0] = DATA_FRAME_TYPE;
246
+ writeUint32(frame, 1, streamId);
247
+ frame.set(payload, HEADER_BYTES);
248
+ return frame;
249
+ }
250
+ export function decodeDataFrame(data) {
251
+ const frame = typeof data === "string"
252
+ ? textToBytes(data)
253
+ : data instanceof Uint8Array
254
+ ? data
255
+ : new Uint8Array(data);
256
+ if (frame.length < HEADER_BYTES || frame[0] !== DATA_FRAME_TYPE) {
257
+ return null;
258
+ }
259
+ return {
260
+ streamId: readUint32(frame, 1),
261
+ payload: frame.slice(HEADER_BYTES),
262
+ };
263
+ }
264
+ function parseHttpRequest(buffer) {
265
+ const headerEnd = buffer.indexOf("\r\n\r\n");
266
+ if (headerEnd === -1) {
267
+ return undefined;
268
+ }
269
+ const headerBlock = buffer.slice(0, headerEnd);
270
+ const lines = headerBlock.split("\r\n");
271
+ const [method, target] = lines[0]?.split(" ") ?? [];
272
+ if (!method || !target) {
273
+ return undefined;
274
+ }
275
+ const headers = {};
276
+ for (const line of lines.slice(1)) {
277
+ const separator = line.indexOf(":");
278
+ if (separator === -1) {
279
+ continue;
280
+ }
281
+ headers[line.slice(0, separator).trim().toLowerCase()] = line
282
+ .slice(separator + 1)
283
+ .trim();
284
+ }
285
+ const bodyStart = headerEnd + 4;
286
+ const contentLength = Number(headers["content-length"] ?? "0");
287
+ const requestEnd = bodyStart + contentLength;
288
+ if (buffer.length < requestEnd) {
289
+ return undefined;
290
+ }
291
+ const url = new URL(target, DEFAULT_ORIGIN);
292
+ return {
293
+ consumedBytes: requestEnd,
294
+ request: {
295
+ method,
296
+ path: url.pathname,
297
+ query: url.searchParams.toString(),
298
+ headers,
299
+ body: binaryToBytes(buffer.slice(bodyStart, requestEnd)),
300
+ },
301
+ };
302
+ }
303
+ function encodeBase64(bytes) {
304
+ let binary = "";
305
+ for (const byte of bytes) {
306
+ binary += String.fromCharCode(byte);
307
+ }
308
+ return typeof globalThis.btoa === "function"
309
+ ? globalThis.btoa(binary)
310
+ : Buffer.from(binary, "binary").toString("base64");
311
+ }
312
+ function decodeBase64(input) {
313
+ if (!input)
314
+ return new Uint8Array();
315
+ const binary = typeof globalThis.atob === "function"
316
+ ? globalThis.atob(input)
317
+ : Buffer.from(input, "base64").toString("binary");
318
+ return binaryToBytes(binary);
319
+ }
320
+ function textToBytes(value) {
321
+ return new TextEncoder().encode(value);
322
+ }
323
+ function binaryToBytes(value) {
324
+ return Uint8Array.from(value, (char) => char.charCodeAt(0));
325
+ }
326
+ function bytesToBinary(bytes) {
327
+ let result = "";
328
+ for (const byte of bytes) {
329
+ result += String.fromCharCode(byte);
330
+ }
331
+ return result;
332
+ }
333
+ function writeUint32(bytes, offset, value) {
334
+ bytes[offset] = (value >>> 24) & 0xff;
335
+ bytes[offset + 1] = (value >>> 16) & 0xff;
336
+ bytes[offset + 2] = (value >>> 8) & 0xff;
337
+ bytes[offset + 3] = value & 0xff;
338
+ }
339
+ function readUint32(bytes, offset) {
340
+ return (bytes[offset] * 0x1000000 +
341
+ ((bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]));
342
+ }
343
+ function httpStatusText(status) {
344
+ if (status === 200)
345
+ return "OK";
346
+ if (status === 201)
347
+ return "Created";
348
+ if (status === 204)
349
+ return "No Content";
350
+ if (status === 400)
351
+ return "Bad Request";
352
+ if (status === 401)
353
+ return "Unauthorized";
354
+ if (status === 403)
355
+ return "Forbidden";
356
+ if (status === 404)
357
+ return "Not Found";
358
+ if (status === 405)
359
+ return "Method Not Allowed";
360
+ if (status === 413)
361
+ return "Content Too Large";
362
+ if (status === 500)
363
+ return "Internal Server Error";
364
+ if (status === 503)
365
+ return "Service Unavailable";
366
+ return "Error";
367
+ }
368
+ //# sourceMappingURL=relay.js.map