Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
NebulaServices
GitHub Repository: NebulaServices/Nebula
Path: blob/main/src/utils/serviceWorker.ts
976 views
1
import { BareMuxConnection } from "@mercuryworkshop/bare-mux";
2
import { defaultStore } from "./storage";
3
import { SettingsVals, WispServers } from "./values";
4
import { log } from "./index";
5
6
/**
7
* Creates a script element and returns it for usage or more modification.
8
*
9
* @example
10
* const script = createScript("/scram/scramjet.controller.js", true);
11
* document.body.appendChild(script);
12
*/
13
const createScript = (src: string, defer?: boolean): HTMLScriptElement => {
14
const script = document.createElement('script') as HTMLScriptElement;
15
script.src = src;
16
if (defer) script.defer = defer;
17
return script;
18
};
19
20
/**
21
* A generator function to create and load our proxy scripts. Allows us to pause and continue when needed.
22
*
23
* @example
24
* const proxyScripts = createProxyScripts();
25
* if (proxyScripts.next().value) document.body.appendChild(proxyScripts.next().value)
26
* // We can now check to see if that script is there or not and then continue after.
27
*/
28
function* createProxyScripts() {
29
const uv = createScript("/uv/uv.bundle.js", true);
30
yield uv;
31
const uvConfig = createScript("/uv/uv.config.js", true);
32
yield uvConfig;
33
const sj = createScript("/scram/scramjet.all.js", true);
34
yield sj;
35
};
36
37
/**
38
* Function that resolves ONLY when uv and Scramjet are not undefined. This prevents us from using these values before they are added and executed.
39
*
40
* @example
41
* await checkProxyScripts();
42
* @example
43
* checkProxyScripts().then(() => { // Do something });
44
*/
45
const checkProxyScripts = (): Promise<void> => {
46
return new Promise((resolve) => {
47
const checkScript = setInterval(() => {
48
if (typeof __uv$config !== "undefined" && typeof $scramjetLoadController !== "undefined") {
49
clearInterval(checkScript);
50
resolve();
51
}
52
}, 100);
53
});
54
};
55
56
/**
57
* Creates a bareMux connection an returns it the instantiated instance as a promise.
58
*
59
* @example
60
* const conn = createBareMuxConn("/baremux/worker.js");
61
*/
62
const createBareMuxConn = (worker: string): Promise<BareMuxConnection> => {
63
return new Promise<BareMuxConnection>((resolve) => {
64
const conn = new BareMuxConnection(worker);
65
resolve(conn);
66
});
67
};
68
69
/**
70
* Sets a transport via an already active BareMux connection. The options are libcurl or epoxy and returns a void promise.
71
*
72
*
73
* @example
74
* const conn = createBareMuxConn("/baremux/worker.js");
75
* setTransport(conn, "libcurl");
76
*/
77
const setTransport = (conn: BareMuxConnection, transport?: "libcurl" | "epoxy"): Promise<void> => {
78
const server = defaultStore.getVal(SettingsVals.proxy.wispServer);
79
return new Promise((resolve) => {
80
log({ type: 'info', bg: false, prefix: false }, `Set transport: ${transport ? transport : "libcurl"}`);
81
log({ type: 'info', bg: false, prefix: false }, `Set wisp server at: ${server ? WispServers[server]: WispServers.default }`);
82
if (transport === "epoxy") return resolve(conn.setTransport("/epoxy/index.mjs", [ { wisp: server ? WispServers[server] : WispServers.default }]));
83
if (transport === "libcurl") return resolve(conn.setTransport("/libcurl/index.mjs", [ { wisp: server ? WispServers[server] : WispServers.default }]));
84
});
85
};
86
87
type SWInit = {
88
serviceWorker: ServiceWorkerRegistration;
89
90
// TODO: Fix types
91
//@ts-ignore
92
sj: ScramjetController;
93
bareMuxConn: BareMuxConnection;
94
}
95
96
/**
97
* This class automatically sets up and registers our service worker.
98
*
99
* @example
100
* const sw = new SW();
101
* sw.getSWInfo() // Returns an object with the service worker, scramjet controller instance and the baremux connection all in one method
102
* sw.setSWInfo() // Allows one to override the info returned from getSWInfo() should be used sparingly or never.
103
*/
104
class SW {
105
#init!: SWInit;
106
#ready: boolean = false;
107
static #instances = new Set();
108
constructor(conn: BareMuxConnection) {
109
SW.#instances.add(this);
110
111
// TODO: Fix types
112
//@ts-ignore
113
const sj = (): ScramjetController => {
114
const { ScramjetController } = $scramjetLoadController();
115
const sj = new ScramjetController({
116
prefix: '/~/scramjet',
117
files: {
118
wasm: "/scram/scramjet.wasm.wasm",
119
all: "/scram/scramjet.all.js",
120
sync: "/scram/scramjet.sync.js"
121
},
122
flags: {
123
rewriterLogs: false
124
}
125
});
126
return sj;
127
}
128
if ("serviceWorker" in navigator) {
129
(async () => { await navigator.serviceWorker.getRegistrations() })();
130
const scram = sj();
131
(async () => await scram.init())();
132
navigator.serviceWorker.ready.then(async (reg) => {
133
log({ type: 'info', prefix: true, bg: false }, 'ServiceWorker ready and active!');
134
this.#init = { serviceWorker: reg, sj: scram, bareMuxConn: conn };
135
this.#ready = true;
136
});
137
navigator.serviceWorker.register("/sw.js", { scope: '/' });
138
}
139
else {
140
throw new Error('Your browser is not supported! This website uses Service Workers heavily.');
141
}
142
}
143
144
/**
145
* Static method to get an already existing SW class
146
*
147
*
148
* @example
149
* SW.getInstances.next().value // Get the first instance.
150
*
151
* @example
152
* // Loop through every instance
153
* for (const sw of SW.getInstances()) {
154
* console.log(sw) // DO some real work
155
* }
156
*/
157
static *getInstances() {
158
for (const value of SW.#instances.keys()) {
159
yield value as SW;
160
}
161
}
162
/**
163
* Allows you to overrid the items set. Should be used sparingly or never.
164
*/
165
setSWInfo(items: SWInit): void {
166
this.#init = { serviceWorker: items.serviceWorker, sj: items.sj, bareMuxConn: items.bareMuxConn };
167
this.#ready = true;
168
}
169
170
/**
171
* Returns a promise that resolves to the serviceWorker, scramjet controller and bareMux Connection ONLY when these values are ready.
172
*
173
* @example
174
* const sw = new SW(conn); // "conn" must be a baremux connection that you created.
175
* const swInfo = await sw.getSWInfo();
176
*
177
* @example
178
* const sw = new SW(conn); // "conn" must be a baremux connection that you created
179
* sw.getInfo().then((info) => { // Do something with said info }
180
*/
181
getSWInfo(): Promise<SWInit> {
182
return new Promise((resolve) => {
183
const checkState = setInterval(() => {
184
if (this.#ready) {
185
clearInterval(checkState);
186
resolve(this.#init);
187
}
188
}, 100);
189
});
190
}
191
}
192
193
export { createScript, createProxyScripts, checkProxyScripts, createBareMuxConn, setTransport, SW };
194
195