@mercuryworkshop/scramjet-controller 0.0.1 → 0.0.3
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/controller.api.js +469 -0
- package/dist/controller.api.js.map +1 -0
- package/dist/controller.inject.js +308 -0
- package/dist/controller.inject.js.map +1 -0
- package/dist/controller.sw.js +276 -0
- package/dist/controller.sw.js.map +1 -0
- package/package.json +10 -9
- package/src/index.ts +416 -0
- package/src/inject.ts +246 -0
- package/src/sw.ts +160 -0
- package/src/types.d.ts +95 -0
- package/tsconfig.json +25 -0
package/src/index.ts
CHANGED
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { type MethodsDefinition, RpcHelper } from "@mercuryworkshop/rpc";
|
|
2
|
+
import type * as ScramjetGlobal from "@mercuryworkshop/scramjet";
|
|
3
|
+
|
|
4
|
+
declare const $scramjet: typeof ScramjetGlobal;
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type TransportToController,
|
|
8
|
+
type Controllerbound,
|
|
9
|
+
type ControllerToTransport,
|
|
10
|
+
type SWbound,
|
|
11
|
+
type WebSocketMessage,
|
|
12
|
+
} from "./types";
|
|
13
|
+
import {
|
|
14
|
+
BareCompatibleClient,
|
|
15
|
+
type BareResponse,
|
|
16
|
+
type ProxyTransport,
|
|
17
|
+
} from "@mercuryworkshop/proxy-transports";
|
|
18
|
+
|
|
19
|
+
const cookieJar = new $scramjet.CookieJar();
|
|
20
|
+
|
|
21
|
+
type Config = {
|
|
22
|
+
wasmPath: string;
|
|
23
|
+
injectPath: string;
|
|
24
|
+
scramjetPath: string;
|
|
25
|
+
virtualWasmPath: string;
|
|
26
|
+
prefix: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
fetch("/scramjet/scramjet.wasm.wasm").then(async (resp) => {
|
|
30
|
+
$scramjet.setWasm(await resp.arrayBuffer());
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const config: Config = {
|
|
34
|
+
prefix: "/~/sj/",
|
|
35
|
+
virtualWasmPath: "scramjet.wasm.js",
|
|
36
|
+
injectPath: "/controller/controller.inject.js",
|
|
37
|
+
scramjetPath: "/scramjet/scramjet.js",
|
|
38
|
+
wasmPath: "/scramjet/scramjet.wasm.wasm",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const cfg = {
|
|
42
|
+
flags: {
|
|
43
|
+
...$scramjet.defaultConfig.flags,
|
|
44
|
+
allowFailedIntercepts: true,
|
|
45
|
+
},
|
|
46
|
+
maskedfiles: ["inject.js", "scramjet.wasm.js"],
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const frames: Record<string, Frame> = {};
|
|
50
|
+
|
|
51
|
+
let wasmPayload: string | null = null;
|
|
52
|
+
|
|
53
|
+
function makeId(): string {
|
|
54
|
+
return Math.random().toString(36).substring(2, 10);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const codecEncode = (url: string) => {
|
|
58
|
+
if (!url) return url;
|
|
59
|
+
|
|
60
|
+
return encodeURIComponent(url);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const codecDecode = (url: string) => {
|
|
64
|
+
if (!url) return url;
|
|
65
|
+
|
|
66
|
+
return decodeURIComponent(url);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type ControllerInit = {
|
|
70
|
+
serviceworker: ServiceWorker;
|
|
71
|
+
transport: ProxyTransport;
|
|
72
|
+
};
|
|
73
|
+
export class Controller {
|
|
74
|
+
id: string;
|
|
75
|
+
prefix: string;
|
|
76
|
+
frames: Frame[] = [];
|
|
77
|
+
cookieJar = new $scramjet.CookieJar();
|
|
78
|
+
|
|
79
|
+
rpc: RpcHelper<Controllerbound, SWbound>;
|
|
80
|
+
private ready: Promise<void>;
|
|
81
|
+
private readyResolve!: () => void;
|
|
82
|
+
|
|
83
|
+
transport: ProxyTransport;
|
|
84
|
+
|
|
85
|
+
private methods: MethodsDefinition<Controllerbound> = {
|
|
86
|
+
ready: async () => {
|
|
87
|
+
this.readyResolve();
|
|
88
|
+
},
|
|
89
|
+
request: async (data) => {
|
|
90
|
+
try {
|
|
91
|
+
let path = new URL(data.rawUrl).pathname;
|
|
92
|
+
const frame = this.frames.find((f) => path.startsWith(f.prefix));
|
|
93
|
+
if (!frame) throw new Error("No frame found for request");
|
|
94
|
+
|
|
95
|
+
if (path === frame.prefix + config.virtualWasmPath) {
|
|
96
|
+
if (!wasmPayload) {
|
|
97
|
+
const resp = await fetch(config.wasmPath);
|
|
98
|
+
const buf = await resp.arrayBuffer();
|
|
99
|
+
const b64 = btoa(
|
|
100
|
+
new Uint8Array(buf)
|
|
101
|
+
.reduce(
|
|
102
|
+
(data, byte) => (data.push(String.fromCharCode(byte)), data),
|
|
103
|
+
[] as any
|
|
104
|
+
)
|
|
105
|
+
.join("")
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
let payload = "";
|
|
109
|
+
payload +=
|
|
110
|
+
"console.warn('WTF'); if ('document' in self && document.currentScript) { document.currentScript.remove(); }\n";
|
|
111
|
+
payload += `self.WASM = '${b64}';`;
|
|
112
|
+
wasmPayload = payload;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return [
|
|
116
|
+
{
|
|
117
|
+
body: wasmPayload,
|
|
118
|
+
status: 200,
|
|
119
|
+
statusText: "OK",
|
|
120
|
+
headers: {
|
|
121
|
+
"Content-Type": ["application/javascript"],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
[],
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let sjheaders = $scramjet.ScramjetHeaders.fromRawHeaders(
|
|
129
|
+
data.initialHeaders
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const fetchresponse = await frame.fetchHandler.handleFetch({
|
|
133
|
+
initialHeaders: sjheaders,
|
|
134
|
+
rawClientUrl: data.rawClientUrl
|
|
135
|
+
? new URL(data.rawClientUrl)
|
|
136
|
+
: undefined,
|
|
137
|
+
rawUrl: new URL(data.rawUrl),
|
|
138
|
+
destination: data.destination,
|
|
139
|
+
method: data.method,
|
|
140
|
+
mode: data.mode,
|
|
141
|
+
referrer: data.referrer,
|
|
142
|
+
body: data.body,
|
|
143
|
+
cache: data.cache,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
return [
|
|
147
|
+
{
|
|
148
|
+
body: fetchresponse.body,
|
|
149
|
+
status: fetchresponse.status,
|
|
150
|
+
statusText: fetchresponse.statusText,
|
|
151
|
+
headers: fetchresponse.headers.toRawHeaders(),
|
|
152
|
+
},
|
|
153
|
+
fetchresponse.body instanceof ReadableStream ||
|
|
154
|
+
fetchresponse.body instanceof ArrayBuffer
|
|
155
|
+
? [fetchresponse.body]
|
|
156
|
+
: [],
|
|
157
|
+
];
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.error("Error in controller request handler:", e);
|
|
160
|
+
throw e;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
initRemoteTransport: async (port) => {
|
|
164
|
+
const rpc = new RpcHelper<TransportToController, ControllerToTransport>(
|
|
165
|
+
{
|
|
166
|
+
request: async ({ remote, method, body, headers }) => {
|
|
167
|
+
let response = await this.transport.request(
|
|
168
|
+
new URL(remote),
|
|
169
|
+
method,
|
|
170
|
+
body,
|
|
171
|
+
headers,
|
|
172
|
+
undefined
|
|
173
|
+
);
|
|
174
|
+
return [response, [response.body]];
|
|
175
|
+
},
|
|
176
|
+
connect: async ({ url, protocols, requestHeaders, port }) => {
|
|
177
|
+
let resolve: (arg: TransportToController["connect"][1]) => void;
|
|
178
|
+
let promise = new Promise<TransportToController["connect"][1]>(
|
|
179
|
+
(res) => (resolve = res)
|
|
180
|
+
);
|
|
181
|
+
const [send, close] = this.transport.connect(
|
|
182
|
+
new URL(url),
|
|
183
|
+
protocols,
|
|
184
|
+
requestHeaders,
|
|
185
|
+
(protocol) => {
|
|
186
|
+
resolve({
|
|
187
|
+
result: "success",
|
|
188
|
+
protocol: protocol,
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
(data) => {
|
|
192
|
+
port.postMessage(
|
|
193
|
+
{
|
|
194
|
+
type: "data",
|
|
195
|
+
data: data,
|
|
196
|
+
} as WebSocketMessage,
|
|
197
|
+
data instanceof ArrayBuffer ? [data] : []
|
|
198
|
+
);
|
|
199
|
+
},
|
|
200
|
+
(close, reason) => {
|
|
201
|
+
port.postMessage({
|
|
202
|
+
type: "close",
|
|
203
|
+
code: close,
|
|
204
|
+
reason: reason,
|
|
205
|
+
} as WebSocketMessage);
|
|
206
|
+
},
|
|
207
|
+
(error) => {
|
|
208
|
+
resolve({
|
|
209
|
+
result: "failure",
|
|
210
|
+
error: error,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
port.onmessageerror = (ev) => {
|
|
215
|
+
console.error(
|
|
216
|
+
"Transport port messageerror (this should never happen!)",
|
|
217
|
+
ev
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
port.onmessage = ({ data }: { data: WebSocketMessage }) => {
|
|
221
|
+
if (data.type === "data") {
|
|
222
|
+
send(data.data);
|
|
223
|
+
} else if (data.type === "close") {
|
|
224
|
+
close(data.code, data.reason);
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
return [await promise, []];
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
"transport",
|
|
232
|
+
(data, transfer) => port.postMessage(data, transfer)
|
|
233
|
+
);
|
|
234
|
+
port.onmessageerror = (ev) => {
|
|
235
|
+
console.error(
|
|
236
|
+
"Transport port messageerror (this should never happen!)",
|
|
237
|
+
ev
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
port.onmessage = (e) => {
|
|
241
|
+
rpc.recieve(e.data);
|
|
242
|
+
};
|
|
243
|
+
rpc.call("ready", undefined, []);
|
|
244
|
+
},
|
|
245
|
+
sendSetCookie: async ({ url, cookie }) => {},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
constructor(public init: ControllerInit) {
|
|
249
|
+
this.transport = init.transport;
|
|
250
|
+
this.id = makeId();
|
|
251
|
+
this.prefix = config.prefix + this.id + "/";
|
|
252
|
+
|
|
253
|
+
this.ready = new Promise<void>((resolve) => {
|
|
254
|
+
this.readyResolve = resolve;
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
let channel = new MessageChannel();
|
|
258
|
+
this.rpc = new RpcHelper<Controllerbound, SWbound>(
|
|
259
|
+
this.methods,
|
|
260
|
+
"tabchannel-" + this.id,
|
|
261
|
+
(data, transfer) => {
|
|
262
|
+
channel.port1.postMessage(data, transfer);
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
channel.port1.addEventListener("message", (e) => {
|
|
266
|
+
this.rpc.recieve(e.data);
|
|
267
|
+
});
|
|
268
|
+
channel.port1.start();
|
|
269
|
+
|
|
270
|
+
init.serviceworker.postMessage(
|
|
271
|
+
{
|
|
272
|
+
$controller$init: {
|
|
273
|
+
prefix: config.prefix + this.id,
|
|
274
|
+
id: this.id,
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
[channel.port2]
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
createFrame(element?: HTMLIFrameElement): Frame {
|
|
282
|
+
element ??= document.createElement("iframe");
|
|
283
|
+
const frame = new Frame(this, element);
|
|
284
|
+
this.frames.push(frame);
|
|
285
|
+
return frame;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
wait(): Promise<void> {
|
|
289
|
+
return this.ready;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function yieldGetInjectScripts(
|
|
294
|
+
cookieJar: ScramjetGlobal.CookieJar,
|
|
295
|
+
config: Config,
|
|
296
|
+
sjconfig: ScramjetGlobal.ScramjetConfig,
|
|
297
|
+
prefix: URL
|
|
298
|
+
) {
|
|
299
|
+
return function getInjectScripts(meta, handler, script) {
|
|
300
|
+
return [
|
|
301
|
+
script(config.scramjetPath),
|
|
302
|
+
script(config.injectPath),
|
|
303
|
+
script(prefix.href + config.virtualWasmPath),
|
|
304
|
+
script(
|
|
305
|
+
"data:text/javascript;base64," +
|
|
306
|
+
btoa(`
|
|
307
|
+
document.currentScript.remove();
|
|
308
|
+
$scramjetController.load({
|
|
309
|
+
config: ${JSON.stringify(config)},
|
|
310
|
+
sjconfig: ${JSON.stringify(sjconfig)},
|
|
311
|
+
cookies: ${cookieJar.dump()},
|
|
312
|
+
prefix: new URL("${prefix.href}"),
|
|
313
|
+
yieldGetInjectScripts: ${yieldGetInjectScripts.toString()},
|
|
314
|
+
codecEncode: ${codecEncode.toString()},
|
|
315
|
+
codecDecode: ${codecDecode.toString()},
|
|
316
|
+
})
|
|
317
|
+
`)
|
|
318
|
+
),
|
|
319
|
+
];
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
class Frame {
|
|
324
|
+
fetchHandler: ScramjetGlobal.ScramjetFetchHandler;
|
|
325
|
+
id: string;
|
|
326
|
+
prefix: string;
|
|
327
|
+
|
|
328
|
+
get context() {
|
|
329
|
+
let sjcfg = {
|
|
330
|
+
...$scramjet.defaultConfig,
|
|
331
|
+
...cfg,
|
|
332
|
+
};
|
|
333
|
+
return {
|
|
334
|
+
cookieJar,
|
|
335
|
+
prefix: new URL(this.prefix, location.href),
|
|
336
|
+
config: sjcfg,
|
|
337
|
+
interface: {
|
|
338
|
+
getInjectScripts: yieldGetInjectScripts(
|
|
339
|
+
this.controller.cookieJar,
|
|
340
|
+
config,
|
|
341
|
+
{ ...$scramjet.defaultConfig, ...cfg },
|
|
342
|
+
new URL(this.prefix, location.href)
|
|
343
|
+
),
|
|
344
|
+
getWorkerInjectScripts: (meta, type, script) => {
|
|
345
|
+
let str = "";
|
|
346
|
+
|
|
347
|
+
str += script(config.scramjetPath);
|
|
348
|
+
str += script(this.prefix + config.virtualWasmPath);
|
|
349
|
+
str += `
|
|
350
|
+
(()=>{
|
|
351
|
+
const { ScramjetClient, CookieJar, setWasm } = $scramjet;
|
|
352
|
+
|
|
353
|
+
setWasm(Uint8Array.from(atob(self.WASM), (c) => c.charCodeAt(0)));
|
|
354
|
+
delete self.WASM;
|
|
355
|
+
|
|
356
|
+
const sjconfig = ${JSON.stringify(sjcfg)};
|
|
357
|
+
const prefix = new URL("${this.prefix}", location.href);
|
|
358
|
+
|
|
359
|
+
const context = {
|
|
360
|
+
interface: {
|
|
361
|
+
codecEncode: ${codecEncode.toString()},
|
|
362
|
+
codecDecode: ${codecDecode.toString()},
|
|
363
|
+
},
|
|
364
|
+
prefix,
|
|
365
|
+
config: sjconfig
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const client = new ScramjetClient(globalThis, {
|
|
369
|
+
context,
|
|
370
|
+
transport: null,
|
|
371
|
+
shouldPassthroughWebsocket: (url) => {
|
|
372
|
+
return url === "wss://anura.pro/";
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
client.hook();
|
|
377
|
+
})();
|
|
378
|
+
`;
|
|
379
|
+
|
|
380
|
+
return str;
|
|
381
|
+
},
|
|
382
|
+
codecEncode,
|
|
383
|
+
codecDecode,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
constructor(
|
|
389
|
+
public controller: Controller,
|
|
390
|
+
public element: HTMLIFrameElement
|
|
391
|
+
) {
|
|
392
|
+
this.id = makeId();
|
|
393
|
+
this.prefix = this.controller.prefix + this.id + "/";
|
|
394
|
+
|
|
395
|
+
this.fetchHandler = new $scramjet.ScramjetFetchHandler({
|
|
396
|
+
crossOriginIsolated: self.crossOriginIsolated,
|
|
397
|
+
context: this.context,
|
|
398
|
+
transport: controller.transport,
|
|
399
|
+
async sendSetCookie(url, cookie) {},
|
|
400
|
+
async fetchBlobUrl(url) {
|
|
401
|
+
return (await fetch(url)) as BareResponseFetch;
|
|
402
|
+
},
|
|
403
|
+
async fetchDataUrl(url) {
|
|
404
|
+
return (await fetch(url)) as BareResponseFetch;
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
go(url: string) {
|
|
410
|
+
const encoded = $scramjet.rewriteUrl(url, this.context, {
|
|
411
|
+
origin: new URL(location.href),
|
|
412
|
+
base: new URL(location.href),
|
|
413
|
+
});
|
|
414
|
+
this.element.src = encoded;
|
|
415
|
+
}
|
|
416
|
+
}
|
package/src/inject.ts
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import type { CookieJar, ScramjetConfig } from "@mercuryworkshop/scramjet";
|
|
2
|
+
import type * as ScramjetGlobal from "@mercuryworkshop/scramjet";
|
|
3
|
+
declare const $scramjet: typeof ScramjetGlobal;
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
RawHeaders,
|
|
7
|
+
ProxyTransport,
|
|
8
|
+
TransferrableResponse,
|
|
9
|
+
} from "@mercuryworkshop/proxy-transports";
|
|
10
|
+
|
|
11
|
+
import { RpcHelper } from "@mercuryworkshop/rpc";
|
|
12
|
+
import type {
|
|
13
|
+
ControllerToTransport,
|
|
14
|
+
TransportToController,
|
|
15
|
+
WebSocketData,
|
|
16
|
+
WebSocketMessage,
|
|
17
|
+
} from "./types";
|
|
18
|
+
|
|
19
|
+
const MessagePort_postMessage = MessagePort.prototype.postMessage;
|
|
20
|
+
const postMessage = (
|
|
21
|
+
port: MessagePort,
|
|
22
|
+
data: any,
|
|
23
|
+
transfer?: Transferable[]
|
|
24
|
+
) => {
|
|
25
|
+
MessagePort_postMessage.call(port, data, transfer as any);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
class RemoteTransport implements ProxyTransport {
|
|
29
|
+
private readyResolve!: () => void;
|
|
30
|
+
private readyPromise: Promise<void> = new Promise((resolve) => {
|
|
31
|
+
this.readyResolve = resolve;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
public ready = false;
|
|
35
|
+
async init() {
|
|
36
|
+
await this.readyPromise;
|
|
37
|
+
this.ready = true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private rpc: RpcHelper<ControllerToTransport, TransportToController>;
|
|
41
|
+
constructor(public port: MessagePort) {
|
|
42
|
+
this.rpc = new RpcHelper<ControllerToTransport, TransportToController>(
|
|
43
|
+
{
|
|
44
|
+
ready: async () => {
|
|
45
|
+
this.readyResolve();
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
"transport",
|
|
49
|
+
(data, transfer) => {
|
|
50
|
+
postMessage(port, data, transfer);
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
port.onmessageerror = (ev) => {
|
|
54
|
+
console.error("onmessageerror (this should never happen!)", ev);
|
|
55
|
+
};
|
|
56
|
+
port.onmessage = (ev) => {
|
|
57
|
+
this.rpc.recieve(ev.data);
|
|
58
|
+
};
|
|
59
|
+
port.start();
|
|
60
|
+
}
|
|
61
|
+
connect(
|
|
62
|
+
url: URL,
|
|
63
|
+
protocols: string[],
|
|
64
|
+
requestHeaders: RawHeaders,
|
|
65
|
+
onopen: (protocol: string, extensions: string) => void,
|
|
66
|
+
onmessage: (data: Blob | ArrayBuffer | string) => void,
|
|
67
|
+
onclose: (code: number, reason: string) => void,
|
|
68
|
+
onerror: (error: string) => void
|
|
69
|
+
): [
|
|
70
|
+
(data: Blob | ArrayBuffer | string) => void,
|
|
71
|
+
(code: number, reason: string) => void,
|
|
72
|
+
] {
|
|
73
|
+
const channel = new MessageChannel();
|
|
74
|
+
let port = channel.port1;
|
|
75
|
+
console.warn("connecting");
|
|
76
|
+
this.rpc
|
|
77
|
+
.call(
|
|
78
|
+
"connect",
|
|
79
|
+
{
|
|
80
|
+
url: url.href,
|
|
81
|
+
protocols,
|
|
82
|
+
requestHeaders,
|
|
83
|
+
port: channel.port2,
|
|
84
|
+
},
|
|
85
|
+
[channel.port2]
|
|
86
|
+
)
|
|
87
|
+
.then((response) => {
|
|
88
|
+
console.log(response);
|
|
89
|
+
if (response.result === "success") {
|
|
90
|
+
onopen(response.protocol, response.extensions);
|
|
91
|
+
} else {
|
|
92
|
+
onerror(response.error);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
port.onmessage = (ev) => {
|
|
96
|
+
const message = ev.data as WebSocketMessage;
|
|
97
|
+
if (message.type === "data") {
|
|
98
|
+
onmessage(message.data);
|
|
99
|
+
} else if (message.type === "close") {
|
|
100
|
+
onclose(message.code, message.reason);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
port.onmessageerror = (ev) => {
|
|
104
|
+
console.error("onmessageerror (this should never happen!)", ev);
|
|
105
|
+
onerror("Message error in transport port");
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
return [
|
|
109
|
+
(data) => {
|
|
110
|
+
postMessage(
|
|
111
|
+
port,
|
|
112
|
+
{
|
|
113
|
+
type: "data",
|
|
114
|
+
data: data,
|
|
115
|
+
},
|
|
116
|
+
data instanceof ArrayBuffer ? [data] : []
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
(code) => {
|
|
120
|
+
postMessage(port, {
|
|
121
|
+
type: "close",
|
|
122
|
+
code: code,
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async request(
|
|
129
|
+
remote: URL,
|
|
130
|
+
method: string,
|
|
131
|
+
body: BodyInit | null,
|
|
132
|
+
headers: RawHeaders,
|
|
133
|
+
signal: AbortSignal | undefined
|
|
134
|
+
): Promise<TransferrableResponse> {
|
|
135
|
+
return await this.rpc.call("request", {
|
|
136
|
+
remote: remote.href,
|
|
137
|
+
method,
|
|
138
|
+
body,
|
|
139
|
+
headers,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
meta() {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const sw = navigator.serviceWorker.controller;
|
|
149
|
+
const { SCRAMJETCLIENT, ScramjetClient, CookieJar, setWasm } = $scramjet;
|
|
150
|
+
|
|
151
|
+
type Config = any;
|
|
152
|
+
type Init = {
|
|
153
|
+
config: Config;
|
|
154
|
+
sjconfig: ScramjetConfig;
|
|
155
|
+
cookies: string;
|
|
156
|
+
prefix: URL;
|
|
157
|
+
yieldGetInjectScripts: (
|
|
158
|
+
cookieJar: CookieJar,
|
|
159
|
+
config: Config,
|
|
160
|
+
sjconfig: ScramjetConfig,
|
|
161
|
+
prefix: URL
|
|
162
|
+
) => any;
|
|
163
|
+
codecEncode: (input: string) => string;
|
|
164
|
+
codecDecode: (input: string) => string;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export function load({
|
|
168
|
+
config,
|
|
169
|
+
sjconfig,
|
|
170
|
+
cookies,
|
|
171
|
+
prefix,
|
|
172
|
+
yieldGetInjectScripts,
|
|
173
|
+
codecEncode,
|
|
174
|
+
codecDecode,
|
|
175
|
+
}: Init) {
|
|
176
|
+
let client;
|
|
177
|
+
if (SCRAMJETCLIENT in globalThis) {
|
|
178
|
+
client = globalThis[SCRAMJETCLIENT];
|
|
179
|
+
} else {
|
|
180
|
+
setWasm(Uint8Array.from(atob(self.WASM), (c) => c.charCodeAt(0)));
|
|
181
|
+
delete self.WASM;
|
|
182
|
+
|
|
183
|
+
const channel = new MessageChannel();
|
|
184
|
+
const transport = new RemoteTransport(channel.port1);
|
|
185
|
+
sw?.postMessage(
|
|
186
|
+
{
|
|
187
|
+
$sw$initRemoteTransport: {
|
|
188
|
+
port: channel.port2,
|
|
189
|
+
prefix: prefix.href,
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
[channel.port2]
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const cookieJar = new CookieJar();
|
|
196
|
+
cookieJar.load(cookies);
|
|
197
|
+
|
|
198
|
+
const context = {
|
|
199
|
+
interface: {
|
|
200
|
+
getInjectScripts: yieldGetInjectScripts(
|
|
201
|
+
cookieJar,
|
|
202
|
+
config,
|
|
203
|
+
sjconfig,
|
|
204
|
+
prefix
|
|
205
|
+
),
|
|
206
|
+
codecEncode,
|
|
207
|
+
codecDecode,
|
|
208
|
+
},
|
|
209
|
+
prefix,
|
|
210
|
+
cookieJar,
|
|
211
|
+
config: sjconfig,
|
|
212
|
+
};
|
|
213
|
+
function createFrameId() {
|
|
214
|
+
return `${Array(8)
|
|
215
|
+
.fill(0)
|
|
216
|
+
.map(() => Math.floor(Math.random() * 36).toString(36))
|
|
217
|
+
.join("")}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const frame = globalThis.frameElement as HTMLIFrameElement | null;
|
|
221
|
+
if (frame && !frame.name) {
|
|
222
|
+
frame.name = createFrameId();
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
client = new ScramjetClient(globalThis, {
|
|
226
|
+
context,
|
|
227
|
+
transport,
|
|
228
|
+
sendSetCookie: async (url, cookie) => {
|
|
229
|
+
// sw.postMessage({
|
|
230
|
+
// $controller$setCookie: {
|
|
231
|
+
// url,
|
|
232
|
+
// cookie
|
|
233
|
+
// }
|
|
234
|
+
// });
|
|
235
|
+
},
|
|
236
|
+
shouldPassthroughWebsocket: (url) => {
|
|
237
|
+
return url === "wss://anura.pro/";
|
|
238
|
+
},
|
|
239
|
+
shouldBlockMessageEvent(i) {
|
|
240
|
+
return false;
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
client.hook();
|
|
245
|
+
}
|
|
246
|
+
}
|