diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index 6536139833..644d1c6944 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -1,6 +1,6 @@ -import { dirname } from 'path' -import { fileURLToPath } from 'url' -import path from 'path' +import path, { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + import js from '@eslint/js' import tseslint from 'typescript-eslint' diff --git a/frontend/graphql-codegen.ts b/frontend/graphql-codegen.ts index 41bd209fb7..a7aeb2a1c9 100644 --- a/frontend/graphql-codegen.ts +++ b/frontend/graphql-codegen.ts @@ -1,24 +1,50 @@ import { CodegenConfig } from '@graphql-codegen/cli' const PUBLIC_API_URL = process.env.PUBLIC_API_URL || 'http://localhost:8000' +type CsrfResponse = { + csrftoken?: string +} -export default (async (): Promise => { - let response + +export default async function graphqlCodegenConfig(): Promise { + let response: Response try { response = await fetch(`${PUBLIC_API_URL}/csrf/`, { method: 'GET', }) - } catch { - /* eslint-disable no-console */ - console.log('Failed to fetch CSRF token: make sure the backend is running.') - return + } catch (err) { + // eslint-disable-next-line no-console + console.error('Failed to fetch CSRF token: make sure the backend is running.', err) + throw new Error('Failed to fetch CSRF token', { cause: err }) } if (!response.ok) { throw new Error(`Failed to fetch CSRF token: ${response.status} ${response.statusText}`) } - const csrfToken = (await response.json()).csrftoken + + let body: unknown + + try { + body = await response.json() + } catch (err) { + throw new Error( + `Failed to parse CSRF token response: ${response.status} ${response.statusText}`, + { cause: err } + ) + } + + const data = body as CsrfResponse + + const csrfToken = + typeof data.csrftoken === 'string' && data.csrftoken.trim() !== '' + ? data.csrftoken + : null + + + if (!csrfToken) { + throw new Error("Failed to fetch CSRF token: missing or invalid 'csrftoken' in response body") + } return { documents: ['src/**/*.{ts,tsx}', '!src/types/__generated__/**'], @@ -26,26 +52,17 @@ export default (async (): Promise => { './src/': { config: { avoidOptionals: { - // Use `null` for nullable fields instead of optionals field: true, - // Allow nullable input fields to remain unspecified inputValue: false, }, defaultScalarType: 'any', - // Apollo Client always includes `__typename` fields nonOptionalTypename: true, - // Apollo Client doesn't add the `__typename` field to root types so - // don't generate a type for the `__typename` for root operation types. skipTypeNameForRoot: true, }, - // Order of plugins matter plugins: ['typescript-operations', 'typed-document-node'], preset: 'near-operation-file', presetConfig: { - // This should be the file generated by the "typescript" plugin above, - // relative to the directory specified for this configuration baseTypesPath: './types/__generated__/graphql.ts', - // Relative to the source files folder: '../../types/__generated__', }, }, @@ -62,7 +79,6 @@ export default (async (): Promise => { plugins: ['typescript'], }, }, - // Don't exit with non-zero status when there are no documents ignoreNoDocuments: true, overwrite: true, schema: { @@ -74,4 +90,4 @@ export default (async (): Promise => { }, }, } -})() +}