Rest/Fn/Worker/
bootstrap.rs1use super::{WorkerConfig, WorkerType};
6
7pub struct WorkerBootstrap {
9 config:WorkerConfig,
10}
11
12impl WorkerBootstrap {
13 pub fn new(config:WorkerConfig) -> Self { Self { config } }
14
15 pub fn generate_module_worker(&self, entry_point:&str) -> String {
17 let mut code = String::new();
18
19 code.push_str("// Module worker bootstrap\n");
21
22 code.push_str(&self.generate_polyfills());
24
25 for script in &self.config.bootstrap_scripts {
27 code.push_str(&format!("import '{}';\n", script));
28 }
29
30 code.push_str(&format!("import '{}';\n", entry_point));
32
33 code
34 }
35
36 pub fn generate_classic_worker(&self, entry_point:&str) -> String {
38 let mut code = String::new();
39
40 code.push_str("// Classic worker bootstrap\n");
42
43 code.push_str(&self.generate_classic_polyfills());
45
46 for script in &self.config.bootstrap_scripts {
48 code.push_str(&format!("importScripts('{}');\n", script));
49 }
50
51 code.push_str(&format!("importScripts('{}');\n", entry_point));
53
54 code
55 }
56
57 pub fn generate_shared_worker(&self, entry_point:&str) -> String {
59 let mut code = String::new();
60
61 code.push_str("// Shared worker bootstrap\n");
62
63 code.push_str(
65 r#"
66self.onconnect = function(event) {
67 const port = event.ports[0];
68 port.onmessage = function(event) {
69 // Handle messages from the main thread
70 self.dispatchEvent(new MessageEvent('message', event));
71 };
72 port.start();
73};
74
75"#,
76 );
77
78 code.push_str(&self.generate_classic_polyfills());
80
81 code.push_str(&format!("importScripts('{}');\n", entry_point));
83
84 code
85 }
86
87 fn generate_polyfills(&self) -> String {
89 r#"
90// Polyfills for worker environment
91(function() {
92 // Ensure globalThis is available
93 if (typeof globalThis === 'undefined') {
94 self.globalThis = self;
95 }
96
97 // Ensure MessageChannel is available
98 if (typeof MessageChannel === 'undefined') {
99 self.MessageChannel = class MessageChannel {
100 constructor() {
101 this.port1 = new MessagePort();
102 this.port2 = new MessagePort();
103 }
104 };
105 }
106
107 // Ensure MessagePort is available
108 if (typeof MessagePort === 'undefined') {
109 self.MessagePort = class MessagePort {
110 constructor() {
111 this.onmessage = null;
112 this.onmessageerror = null;
113 }
114 postMessage(data) {}
115 start() {}
116 close() {}
117 };
118 }
119})();
120
121"#
122 .to_string()
123 }
124
125 fn generate_classic_polyfills(&self) -> String {
127 r#"
128// Classic worker polyfills
129(function() {
130 // Minimal polyfills for classic workers
131 if (typeof globalThis === 'undefined') {
132 self.globalThis = self;
133 }
134})();
135
136"#
137 .to_string()
138 }
139
140 pub fn generate_worker_loader(&self, worker_name:&str, module_url:&str) -> String {
142 format!(
143 r#"
144(function() {{
145 const workerCode = `
146 {loader_code}
147 `;
148
149 const blob = new Blob([workerCode], {{ type: 'application/javascript' }});
150 const url = URL.createObjectURL(blob);
151
152 self["{worker_name}"] = new Worker(url, {{ type: 'module' }});
153
154 // Clean up blob URL after worker is created
155 URL.revokeObjectURL(url);
156}})();
157"#,
158 loader_code = self
159 .generate_module_worker(module_url)
160 .replace("`", "\\`")
161 .replace("${", "\\${")
162 )
163 }
164}
165
166pub fn generate_inline_worker(code:&str, worker_type:WorkerType) -> String {
168 match worker_type {
169 WorkerType::Module => {
170 format!(
171 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})), {{ type: \
172 'module' }})",
173 code.replace("`", "\\`").replace("${", "\\${")
174 )
175 },
176 WorkerType::Classic => {
177 format!(
178 "new Worker(URL.createObjectURL(new Blob([`{}`], {{ type: 'application/javascript' }})))",
179 code.replace("`", "\\`").replace("${", "\\${")
180 )
181 },
182 }
183}
184
185pub fn generate_worker_declaration(worker_name:&str) -> String {
187 format!(
188 r#"declare const {worker_name}: Worker;
189export {{ {worker_name} }};
190"#
191 )
192}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[test]
199 fn test_module_worker_bootstrap() {
200 let config = WorkerConfig::new();
201 let bootstrap = WorkerBootstrap::new(config);
202
203 let code = bootstrap.generate_module_worker("./entry.js");
204 assert!(code.contains("Module worker bootstrap"));
205 assert!(code.contains("./entry.js"));
206 }
207
208 #[test]
209 fn test_classic_worker_bootstrap() {
210 let config = WorkerConfig::new();
211 let bootstrap = WorkerBootstrap::new(config);
212
213 let code = bootstrap.generate_classic_worker("./entry.js");
214 assert!(code.contains("Classic worker bootstrap"));
215 assert!(code.contains("./entry.js"));
216 }
217
218 #[test]
219 fn test_shared_worker_bootstrap() {
220 let config = WorkerConfig::new();
221 let bootstrap = WorkerBootstrap::new(config);
222
223 let code = bootstrap.generate_shared_worker("./entry.js");
224 assert!(code.contains("Shared worker bootstrap"));
225 assert!(code.contains("onconnect"));
226 }
227}