| 153 | * @returns {Promise<string>} The CSRF token |
| 154 | */ |
| 155 | export async function getCsrfToken() { |
| 156 | if (csrfToken) return csrfToken; |
| 157 | if (csrfTokenPromise) return await csrfTokenPromise; |
| 158 | |
| 159 | csrfTokenPromise = (async () => { |
| 160 | const startedAt = Date.now(); |
| 161 | const controller = |
| 162 | typeof AbortController !== "undefined" ? new AbortController() : null; |
| 163 | let timeoutId = null; |
| 164 | let timeoutPromise = null; |
| 165 | let response; |
| 166 | |
| 167 | try { |
| 168 | if (controller) { |
| 169 | timeoutId = setTimeout(() => controller.abort(), CSRF_TIMEOUT_MS); |
| 170 | } else { |
| 171 | timeoutPromise = new Promise((_, reject) => { |
| 172 | timeoutId = setTimeout(() => { |
| 173 | reject(new Error("CSRF token request timed out")); |
| 174 | }, CSRF_TIMEOUT_MS); |
| 175 | }); |
| 176 | } |
| 177 | |
| 178 | /** @type {RequestInit} */ |
| 179 | const fetchOptions = { credentials: "same-origin" }; |
| 180 | if (controller) { |
| 181 | fetchOptions.signal = controller.signal; |
| 182 | } |
| 183 | |
| 184 | const fetchPromise = fetch("/api/csrf_token", fetchOptions); |
| 185 | response = timeoutPromise |
| 186 | ? await Promise.race([fetchPromise, timeoutPromise]) |
| 187 | : await fetchPromise; |
| 188 | } catch (error) { |
| 189 | if (error && error["name"] === "AbortError") { |
| 190 | throw new Error("CSRF token request timed out"); |
| 191 | } |
| 192 | throw error; |
| 193 | } finally { |
| 194 | if (timeoutId) { |
| 195 | clearTimeout(timeoutId); |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | if (redirect(response)) return; |
| 200 | |
| 201 | const json = await response.json(); |
| 202 | if (json.ok) { |
| 203 | const runtimeId = |
| 204 | typeof json.runtime_id === "string" && json.runtime_id.length > 0 |
| 205 | ? json.runtime_id |
| 206 | : null; |
| 207 | |
| 208 | csrfToken = json.token; |
| 209 | if (runtimeId) { |
| 210 | runtimeIdCache = runtimeId; |
| 211 | } |
| 212 | const injectedRuntimeId = |