From b4b3628026dd4e84071088b3c79b46f5f500b227 Mon Sep 17 00:00:00 2001 From: Ankita Sahu <71656941+SAHU-01@users.noreply.github.com> Date: Sun, 19 Oct 2025 03:12:02 +0530 Subject: [PATCH 1/2] feat: fixing error inconsistency --- src/lib/error-pages.ts | 395 +++++++++++++++++++++++++++++++++++++++++ src/lib/error-types.ts | 173 ++++++++++++++++++ src/sw.ts | 76 +++++++- 3 files changed, 636 insertions(+), 8 deletions(-) create mode 100644 src/lib/error-pages.ts create mode 100644 src/lib/error-types.ts diff --git a/src/lib/error-pages.ts b/src/lib/error-pages.ts new file mode 100644 index 00000000..91ca20a2 --- /dev/null +++ b/src/lib/error-pages.ts @@ -0,0 +1,395 @@ +/** + * Error page HTML generation for IPFS Service Worker Gateway + * + * This module generates user-friendly error pages with detailed information, + * actionable suggestions, and debugging tools. + * + * @module error-pages + */ + +import type { ErrorInfo, ErrorType } from './error-types.js' + +/** + * Configuration for error page generation + */ +export interface ErrorPageConfig { + /** HTTP status code */ + status: number + /** HTTP status text */ + statusText: string + /** Full request URL that caused the error */ + url: string + /** Extracted CID from the URL (if applicable) */ + cid: string | null + /** Error type classification */ + errorType: ErrorType | string + /** User-friendly error message */ + errorMessage: string + /** List of actionable suggestions */ + suggestions: string[] + /** Optional stack trace for debugging */ + stack?: string +} + +/** + * Generate complete HTML for error page + * + * @param config - Error page configuration + * @returns Complete HTML document as string + */ +export function generateErrorPageHTML(config: ErrorPageConfig): string { + const { + status, + statusText, + url, + cid, + errorType, + errorMessage, + suggestions, + stack + } = config + + return ` + + + + + + ${status} ${statusText} - IPFS Service Worker Gateway + ${generateStyles()} + + +
+ ${generateHeader(status, statusText, errorType)} +
+ ${generateCIDSection(cid)} + ${generateErrorSection(errorMessage)} + ${generateSuggestionsSection(suggestions)} + ${generateActionsSection(cid)} + ${generateTechnicalDetails(config, stack)} +
+
+ +` +} + +/** + * Generate CSS styles for error page + */ +function generateStyles(): string { + return `` +} + +/** + * Generate header section with status and error type + */ +function generateHeader(status: number, statusText: string, errorType: string): string { + return `
+

${status} ${statusText}

+
${errorType}
+
` +} + +/** + * Generate CID information section (if CID is present) + */ +function generateCIDSection(cid: string | null): string { + if (!cid) { + return '' + } + + return `
+

🔗 Requested Content

+
+

CID:

+
${escapeHtml(cid)}
+
+
` +} + +/** + * Generate error explanation section + */ +function generateErrorSection(errorMessage: string): string { + return `
+

⚠️ What Went Wrong

+
+

${escapeHtml(errorMessage)}

+
+
` +} + +/** + * Generate suggestions section with actionable items + */ +function generateSuggestionsSection(suggestions: string[]): string { + if (!suggestions || suggestions.length === 0) { + return '' + } + + const suggestionsList = suggestions + .map(s => `
  • ${escapeHtml(s)}
  • `) + .join('') + + return `
    +

    💡 Possible Solutions

    +
    + +
    +
    ` +} + +/** + * Generate action buttons section + */ +function generateActionsSection(cid: string | null): string { + const publicGatewayButton = cid + ? ` + 🌐 Try Public Gateway + ` + : '' + + return `
    + + + ${publicGatewayButton} +
    ` +} + +/** + * Generate technical details section (collapsible) + */ +function generateTechnicalDetails(config: ErrorPageConfig, stack?: string): string { + const technicalInfo = { + status: config.status, + statusText: config.statusText, + errorType: config.errorType, + url: config.url, + cid: config.cid, + timestamp: new Date().toISOString(), + userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown' + } + + const jsonString = JSON.stringify(technicalInfo, null, 2) + const stackTrace = stack ? `\n\nStack Trace:\n${stack}` : '' + + return `
    + 🔧 Technical Details (for debugging) +
    ${escapeHtml(jsonString + stackTrace)}
    +
    ` +} + +/** + * Escape HTML special characters to prevent XSS + */ +function escapeHtml(unsafe: string): string { + return unsafe + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') +} + +/** + * Generate error page from ErrorInfo object + * + * @param errorInfo - Error information from detectErrorType + * @param url - Request URL + * @param cid - Extracted CID (optional) + * @param stack - Stack trace (optional) + * @returns Complete HTML error page + */ +export function generateErrorPageFromInfo( + errorInfo: ErrorInfo, + url: string, + cid: string | null, + stack?: string +): string { + return generateErrorPageHTML({ + status: errorInfo.statusCode, + statusText: getStatusText(errorInfo.statusCode), + url, + cid, + errorType: errorInfo.errorType, + errorMessage: errorInfo.errorMessage, + suggestions: errorInfo.suggestions, + stack + }) +} + +/** + * Get HTTP status text for status code + */ +function getStatusText(status: number): string { + const statusTexts: Record = { + 400: 'Bad Request', + 404: 'Not Found', + 415: 'Unsupported Media Type', + 500: 'Internal Server Error', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout' + } + return statusTexts[status] || 'Error' +} \ No newline at end of file diff --git a/src/lib/error-types.ts b/src/lib/error-types.ts new file mode 100644 index 00000000..16a22d78 --- /dev/null +++ b/src/lib/error-types.ts @@ -0,0 +1,173 @@ +/** + * Error type detection and categorization for IPFS Service Worker Gateway + * + * This module analyzes errors and provides user-friendly explanations with + * actionable suggestions for resolution. + * + * @module error-types + */ + +/** + * Structured error information with user-facing details + */ +export interface ErrorInfo { + /** Machine-readable error category */ + errorType: ErrorType + /** User-friendly error explanation */ + errorMessage: string + /** Actionable suggestions to resolve the issue */ + suggestions: string[] + /** HTTP status code to return */ + statusCode: number +} + +/** + * Supported error categories + */ +export enum ErrorType { + HASH_VERIFICATION_FAILED = 'Hash Verification Failed', + NO_PROVIDERS = 'No Providers Found', + TIMEOUT = 'Request Timeout', + NETWORK_ERROR = 'Network Error', + INVALID_CID = 'Invalid CID', + UNSUPPORTED_FORMAT = 'Unsupported Content Type', + UNKNOWN = 'Unknown Error' +} + +/** + * Analyze an error and return categorized information + * + * @param error - Error object or error message string + * @returns Structured error information with suggestions + * + */ +export function detectErrorType(error: Error | string): ErrorInfo { + const errorMsg = typeof error === 'string' ? error : error.message + const errorMsgLower = errorMsg.toLowerCase() + + // Hash verification error - critical data integrity issue + if (errorMsgLower.includes('hash') && (errorMsgLower.includes('match') || errorMsgLower.includes('verif'))) { + return { + errorType: ErrorType.HASH_VERIFICATION_FAILED, + errorMessage: 'The downloaded block\'s hash did not match the requested CID. This indicates data corruption or a security issue.', + suggestions: [ + 'The content may have been corrupted during transmission', + 'Try accessing the content from a different IPFS gateway', + 'Clear your browser cache and retry', + 'If you control this content, verify its integrity and re-pin to IPFS' + ], + statusCode: 502 // Bad Gateway - upstream returned invalid data + } + } + + // No providers error - content unavailable on network + if (errorMsgLower.includes('no provider') || errorMsgLower.includes('no peers') || errorMsgLower.includes('could not find')) { + return { + errorType: ErrorType.NO_PROVIDERS, + errorMessage: 'No nodes on the IPFS network are currently hosting this content.', + suggestions: [ + 'The content may have been unpinned from all hosting nodes', + 'Wait a few minutes and try again as nodes may come online', + 'Verify the CID is correct and the content was properly published', + 'Check if the content is available on public gateways like ipfs.io' + ], + statusCode: 404 // Not Found - content doesn't exist on network + } + } + + // Timeout error - slow retrieval + if (errorMsgLower.includes('timeout') || errorMsgLower.includes('aborted')) { + return { + errorType: ErrorType.TIMEOUT, + errorMessage: 'The request took too long to complete and was aborted.', + suggestions: [ + 'The content may be hosted on slow or geographically distant nodes', + 'Try again in a few moments', + 'Check your internet connection', + 'Try accessing via a public IPFS gateway', + 'Consider increasing the timeout in the config page' + ], + statusCode: 504 // Gateway Timeout + } + } + + // Network error - connectivity issues + if (errorMsgLower.includes('network') || errorMsgLower.includes('fetch failed') || errorMsgLower.includes('connection')) { + return { + errorType: ErrorType.NETWORK_ERROR, + errorMessage: 'A network error occurred while fetching the content.', + suggestions: [ + 'Check your internet connection', + 'Try refreshing the page', + 'The IPFS network may be experiencing temporary issues', + 'Try again in a few moments' + ], + statusCode: 503 // Service Unavailable + } + } + + // Invalid CID - malformed identifier + if (errorMsgLower.includes('invalid') && errorMsgLower.includes('cid')) { + return { + errorType: ErrorType.INVALID_CID, + errorMessage: 'The provided CID is not valid or properly formatted.', + suggestions: [ + 'Verify the CID is correctly formatted (starts with "bafy" or "Qm")', + 'Ensure you copied the complete CID without truncation', + 'Try accessing different content to verify the gateway is working', + 'Learn about CID formats at https://cid.ipfs.tech' + ], + statusCode: 400 // Bad Request + } + } + + // Unsupported format + if (errorMsgLower.includes('unsupported') || errorMsgLower.includes('codec')) { + return { + errorType: ErrorType.UNSUPPORTED_FORMAT, + errorMessage: 'The content uses a format or codec not supported by this gateway.', + suggestions: [ + 'The content may use newer IPFS features not yet supported', + 'Try accessing it from a different IPFS implementation', + 'Check the IPFS documentation for supported content types' + ], + statusCode: 415 // Unsupported Media Type + } + } + + // Default/unknown error + return { + errorType: ErrorType.UNKNOWN, + errorMessage: errorMsg, + suggestions: [ + 'Try refreshing the page', + 'Check the browser console for technical details', + 'Try accessing the content from a public IPFS gateway', + 'Report this issue at https://github.com/ipfs/service-worker-gateway/issues' + ], + statusCode: 500 // Internal Server Error + } +} + +/** + * Extract CID from URL in subdomain or path format + * + * @param url - URL object to parse + * @returns Extracted CID or null if not found + * + */ +export function extractCIDFromURL(url: URL): string | null { + // Subdomain format: .ipfs.domain.com or .ipns.domain.com + const subdomainMatch = url.hostname.match(/^(.+?)\.(ipfs|ipns)\./) + if (subdomainMatch) { + return subdomainMatch[1] + } + + // Path format: /ipfs/ or /ipns/ + const pathMatch = url.pathname.match(/^\/(ipfs|ipns)\/([^/?#]+)/) + if (pathMatch) { + return pathMatch[2] + } + + return null +} \ No newline at end of file diff --git a/src/sw.ts b/src/sw.ts index 496b9e0a..7095204f 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -11,6 +11,8 @@ import { findOriginIsolationRedirect, isPathGatewayRequest, isSubdomainGatewayRe import { isUnregisterRequest } from './lib/unregister-request.js' import type { ConfigDb } from './lib/config-db.js' import type { VerifiedFetch } from '@helia/verified-fetch' +import { detectErrorType, extractCIDFromURL } from './lib/error-types.ts' +import { generateErrorPageHTML, generateErrorPageFromInfo } from './lib/error-pages.ts' /** ****************************************************** @@ -614,29 +616,66 @@ async function fetchHandler ({ path, request, event }: FetchHandlerArg): Promise } catch (err: unknown) { log.trace('fetchHandler: error: ', err) const errorMessages: string[] = [] + let firstError: Error | null = null if (isAggregateError(err)) { log.error('fetchHandler aggregate error: ', err.message) for (const e of err.errors) { errorMessages.push(e.message) log.error('fetchHandler errors: ', e) + if (firstError == null) { + firstError = e + } } } else { - errorMessages.push(err instanceof Error ? err.message : JSON.stringify(err)) + const error = err instanceof Error ? err : new Error(JSON.stringify(err)) + errorMessages.push(error.message) log.error('fetchHandler error: ', err) + firstError = error } const errorMessage = errorMessages.join('\n') if (errorMessage.includes('aborted') || signal.aborted) { return await get504Response(event) } - const response = new Response('Service Worker IPFS Gateway error: ' + errorMessage, { status: 500 }) - response.headers.set('ipfs-sw', 'true') - return response + + const errorInfo = detectErrorType(firstError ?? errorMessage) + const url = new URL(event.request.url) + const cid = extractCIDFromURL(url) + + const errorHtml = generateErrorPageFromInfo( + errorInfo, + url.href, + cid, + firstError?.stack + ) + + return new Response(errorHtml, { + status: errorInfo.statusCode, + statusText: getStatusText(errorInfo.statusCode), + headers: { + 'Content-Type': 'text/html; charset=utf-8', + 'ipfs-sw': 'true', + 'Cache-Control': 'no-cache, no-store, must-revalidate' + } + }) } finally { clearTimeout(signalAbortTimeout) } } +function getStatusText(status: number): string { + const statusTexts: Record = { + 400: 'Bad Request', + 404: 'Not Found', + 415: 'Unsupported Media Type', + 500: 'Internal Server Error', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + 504: 'Gateway Timeout' + } + return statusTexts[status] || 'Error' +} + /** * TODO: better styling * TODO: more error details from @helia/verified-fetch @@ -677,6 +716,8 @@ async function errorPageResponse (fetchResponse: Response): Promise { const mergedHeaders = new Headers(fetchResponse.headers) mergedHeaders.set('Content-Type', 'text/html') mergedHeaders.set('ipfs-sw', 'true') + mergedHeaders.set('Cache-Control', 'no-cache, no-store, must-revalidate') + return new Response(` @@ -716,13 +757,32 @@ async function errorPageResponse (fetchResponse: Response): Promise { } async function get504Response (event: FetchEvent): Promise { - const response504 = await fetch(new URL('/ipfs-sw-504.html', event.request.url)) + const url = new URL(event.request.url) + const cid = extractCIDFromURL(url) + + const errorHtml = generateErrorPageHTML({ + status: 504, + statusText: 'Gateway Timeout', + url: url.href, + cid, + errorType: 'Timeout', + errorMessage: 'The gateway timed out while trying to fetch your content from the IPFS network.', + suggestions: [ + 'Wait a few moments and click the Retry button', + 'The content may be hosted on slow or distant nodes', + 'Try accessing the content from a public IPFS gateway', + 'Check if the content is still available on the IPFS network', + 'Consider increasing the timeout in the config page' + ] + }) - return new Response(response504.body, { + return new Response(errorHtml, { status: 504, + statusText: 'Gateway Timeout', headers: { - 'Content-Type': 'text/html', - 'ipfs-sw': 'true' + 'Content-Type': 'text/html; charset=utf-8', + 'ipfs-sw': 'true', + 'Cache-Control': 'no-cache, no-store, must-revalidate' } }) } From 283be3474a1fce4cb4a574db223d886999ecda0f Mon Sep 17 00:00:00 2001 From: Ankita Sahu <71656941+SAHU-01@users.noreply.github.com> Date: Sun, 19 Oct 2025 06:13:03 +0530 Subject: [PATCH 2/2] fix: resolve error page UX inconsistencies (#817, #816) --- src/lib/error-pages.ts | 470 +++++++++++++---------------------------- src/lib/error-types.ts | 16 +- src/sw.ts | 13 +- 3 files changed, 157 insertions(+), 342 deletions(-) diff --git a/src/lib/error-pages.ts b/src/lib/error-pages.ts index 91ca20a2..bfb3c030 100644 --- a/src/lib/error-pages.ts +++ b/src/lib/error-pages.ts @@ -1,10 +1,6 @@ /** * Error page HTML generation for IPFS Service Worker Gateway - * - * This module generates user-friendly error pages with detailed information, - * actionable suggestions, and debugging tools. - * - * @module error-pages + * Generates inline error pages matching the original 504.html styling */ import type { ErrorInfo, ErrorType } from './error-types.js' @@ -13,336 +9,20 @@ import type { ErrorInfo, ErrorType } from './error-types.js' * Configuration for error page generation */ export interface ErrorPageConfig { - /** HTTP status code */ status: number - /** HTTP status text */ statusText: string - /** Full request URL that caused the error */ url: string - /** Extracted CID from the URL (if applicable) */ cid: string | null - /** Error type classification */ errorType: ErrorType | string - /** User-friendly error message */ errorMessage: string - /** List of actionable suggestions */ suggestions: string[] - /** Optional stack trace for debugging */ stack?: string } -/** - * Generate complete HTML for error page - * - * @param config - Error page configuration - * @returns Complete HTML document as string - */ -export function generateErrorPageHTML(config: ErrorPageConfig): string { - const { - status, - statusText, - url, - cid, - errorType, - errorMessage, - suggestions, - stack - } = config - - return ` - - - - - - ${status} ${statusText} - IPFS Service Worker Gateway - ${generateStyles()} - - -
    - ${generateHeader(status, statusText, errorType)} -
    - ${generateCIDSection(cid)} - ${generateErrorSection(errorMessage)} - ${generateSuggestionsSection(suggestions)} - ${generateActionsSection(cid)} - ${generateTechnicalDetails(config, stack)} -
    -
    - -` -} - -/** - * Generate CSS styles for error page - */ -function generateStyles(): string { - return `` -} - -/** - * Generate header section with status and error type - */ -function generateHeader(status: number, statusText: string, errorType: string): string { - return `
    -

    ${status} ${statusText}

    -
    ${errorType}
    -
    ` -} - -/** - * Generate CID information section (if CID is present) - */ -function generateCIDSection(cid: string | null): string { - if (!cid) { - return '' - } - - return `
    -

    🔗 Requested Content

    -
    -

    CID:

    -
    ${escapeHtml(cid)}
    -
    -
    ` -} - -/** - * Generate error explanation section - */ -function generateErrorSection(errorMessage: string): string { - return `
    -

    ⚠️ What Went Wrong

    -
    -

    ${escapeHtml(errorMessage)}

    -
    -
    ` -} - -/** - * Generate suggestions section with actionable items - */ -function generateSuggestionsSection(suggestions: string[]): string { - if (!suggestions || suggestions.length === 0) { - return '' - } - - const suggestionsList = suggestions - .map(s => `
  • ${escapeHtml(s)}
  • `) - .join('') - - return `
    -

    💡 Possible Solutions

    -
    -
      ${suggestionsList}
    -
    -
    ` -} - -/** - * Generate action buttons section - */ -function generateActionsSection(cid: string | null): string { - const publicGatewayButton = cid - ? ` - 🌐 Try Public Gateway - ` - : '' - - return `
    - - - ${publicGatewayButton} -
    ` -} - -/** - * Generate technical details section (collapsible) - */ -function generateTechnicalDetails(config: ErrorPageConfig, stack?: string): string { - const technicalInfo = { - status: config.status, - statusText: config.statusText, - errorType: config.errorType, - url: config.url, - cid: config.cid, - timestamp: new Date().toISOString(), - userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown' - } - - const jsonString = JSON.stringify(technicalInfo, null, 2) - const stackTrace = stack ? `\n\nStack Trace:\n${stack}` : '' - - return `
    - 🔧 Technical Details (for debugging) -
    ${escapeHtml(jsonString + stackTrace)}
    -
    ` -} - /** * Escape HTML special characters to prevent XSS */ -function escapeHtml(unsafe: string): string { +function escapeHtml (unsafe: string): string { return unsafe .replace(/&/g, '&') .replace(/ `
  • ${escapeHtml(s)}
  • `).join('') + + return ` + + + + ${status} ${statusText} + + + +
    +
    + + + +
    +
    +

    Service Worker Gateway (beta)

    +
    +
    +
    +
    + ${status} ${escapeHtml(statusText)} +
    +
    + ${cid +? ` +

    Requested CID:

    +

    ${escapeHtml(cid)}

    + ` +: ''} + + +

    What went wrong:

    +

    ${escapeHtml(errorMessage)}

    + + + ${suggestions.length > 0 +? ` +

    How you can proceed:

    +
      + ${suggestionsList} +
    + ` +: ''} + +

    Additional resources:

    + + + ${cid ? `Debug retrievability of CID` : ''} +
    +
    + + +` +} + /** * Generate error page from ErrorInfo object - * + * * @param errorInfo - Error information from detectErrorType * @param url - Request URL * @param cid - Extracted CID (optional) * @param stack - Stack trace (optional) * @returns Complete HTML error page */ -export function generateErrorPageFromInfo( +export function generateErrorPageFromInfo ( errorInfo: ErrorInfo, url: string, cid: string | null, @@ -381,7 +199,7 @@ export function generateErrorPageFromInfo( /** * Get HTTP status text for status code */ -function getStatusText(status: number): string { +function getStatusText (status: number): string { const statusTexts: Record = { 400: 'Bad Request', 404: 'Not Found', @@ -392,4 +210,4 @@ function getStatusText(status: number): string { 504: 'Gateway Timeout' } return statusTexts[status] || 'Error' -} \ No newline at end of file +} diff --git a/src/lib/error-types.ts b/src/lib/error-types.ts index 16a22d78..b9ed0fff 100644 --- a/src/lib/error-types.ts +++ b/src/lib/error-types.ts @@ -1,9 +1,9 @@ /** * Error type detection and categorization for IPFS Service Worker Gateway - * + * * This module analyzes errors and provides user-friendly explanations with * actionable suggestions for resolution. - * + * * @module error-types */ @@ -36,12 +36,11 @@ export enum ErrorType { /** * Analyze an error and return categorized information - * + * * @param error - Error object or error message string * @returns Structured error information with suggestions - * */ -export function detectErrorType(error: Error | string): ErrorInfo { +export function detectErrorType (error: Error | string): ErrorInfo { const errorMsg = typeof error === 'string' ? error : error.message const errorMsgLower = errorMsg.toLowerCase() @@ -151,12 +150,11 @@ export function detectErrorType(error: Error | string): ErrorInfo { /** * Extract CID from URL in subdomain or path format - * + * * @param url - URL object to parse * @returns Extracted CID or null if not found - * */ -export function extractCIDFromURL(url: URL): string | null { +export function extractCIDFromURL (url: URL): string | null { // Subdomain format: .ipfs.domain.com or .ipns.domain.com const subdomainMatch = url.hostname.match(/^(.+?)\.(ipfs|ipns)\./) if (subdomainMatch) { @@ -170,4 +168,4 @@ export function extractCIDFromURL(url: URL): string | null { } return null -} \ No newline at end of file +} diff --git a/src/sw.ts b/src/sw.ts index 7095204f..6a846fa7 100644 --- a/src/sw.ts +++ b/src/sw.ts @@ -1,5 +1,7 @@ import { getConfig } from './lib/config-db.js' import { HASH_FRAGMENTS, QUERY_PARAMS } from './lib/constants.js' +import { generateErrorPageHTML, generateErrorPageFromInfo } from './lib/error-pages.ts' +import { detectErrorType, extractCIDFromURL } from './lib/error-types.ts' import { getHeliaSwRedirectUrl } from './lib/first-hit-helpers.js' import { GenericIDB } from './lib/generic-db.js' import { getSubdomainParts } from './lib/get-subdomain-parts.js' @@ -11,8 +13,6 @@ import { findOriginIsolationRedirect, isPathGatewayRequest, isSubdomainGatewayRe import { isUnregisterRequest } from './lib/unregister-request.js' import type { ConfigDb } from './lib/config-db.js' import type { VerifiedFetch } from '@helia/verified-fetch' -import { detectErrorType, extractCIDFromURL } from './lib/error-types.ts' -import { generateErrorPageHTML, generateErrorPageFromInfo } from './lib/error-pages.ts' /** ****************************************************** @@ -642,13 +642,13 @@ async function fetchHandler ({ path, request, event }: FetchHandlerArg): Promise const url = new URL(event.request.url) const cid = extractCIDFromURL(url) - const errorHtml = generateErrorPageFromInfo( + const errorHtml = generateErrorPageFromInfo( errorInfo, url.href, cid, firstError?.stack ) - + return new Response(errorHtml, { status: errorInfo.statusCode, statusText: getStatusText(errorInfo.statusCode), @@ -663,7 +663,7 @@ async function fetchHandler ({ path, request, event }: FetchHandlerArg): Promise } } -function getStatusText(status: number): string { +function getStatusText (status: number): string { const statusTexts: Record = { 400: 'Bad Request', 404: 'Not Found', @@ -718,7 +718,6 @@ async function errorPageResponse (fetchResponse: Response): Promise { mergedHeaders.set('ipfs-sw', 'true') mergedHeaders.set('Cache-Control', 'no-cache, no-store, must-revalidate') - return new Response(` @@ -759,7 +758,7 @@ async function errorPageResponse (fetchResponse: Response): Promise { async function get504Response (event: FetchEvent): Promise { const url = new URL(event.request.url) const cid = extractCIDFromURL(url) - + const errorHtml = generateErrorPageHTML({ status: 504, statusText: 'Gateway Timeout',