chore: upgrade fastify and fastify vite (#7558)

This commit is contained in:
Laurin Quast 2026-01-26 18:18:09 +01:00 committed by GitHub
parent caac40a495
commit dba29f592c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 613 additions and 481 deletions

View file

@ -163,7 +163,6 @@
"@graphiql/react": "patches/@graphiql__react.patch",
"countup.js": "patches/countup.js.patch",
"@oclif/core@4.0.6": "patches/@oclif__core@4.0.6.patch",
"@fastify/vite": "patches/@fastify__vite.patch",
"p-cancelable@4.0.1": "patches/p-cancelable@4.0.1.patch",
"bentocache": "patches/bentocache.patch",
"nextra": "patches/nextra.patch",

View file

@ -63,7 +63,7 @@
"@apollo/composition": "2.10.4",
"@types/node": "22.10.5",
"esbuild": "0.25.9",
"fastify": "4.29.1",
"fastify": "5.7.1",
"graphql": "16.9.0"
},
"publishConfig": {

View file

@ -101,7 +101,7 @@ export class PersistedDocumentIngester {
async processBatch(data: BatchProcessEvent['data']) {
this.logger.debug(
'Processing batch. (targetId=%s, appDeploymentId=%s, operationCount=%n)',
'Processing batch. (targetId=%s, appDeploymentId=%s, operationCount=%d)',
data.targetId,
data.appDeployment.id,
data.documents.length,
@ -118,7 +118,7 @@ export class PersistedDocumentIngester {
if (hashValidation.success === false || bodyValidation.success === false) {
this.logger.debug(
'Invalid operation provided. Processing failed. (targetId=%s, appDeploymentId=%s, operationIndex=%n)',
'Invalid operation provided. Processing failed. (targetId=%s, appDeploymentId=%s, operationIndex=%d)',
data.targetId,
data.appDeployment.id,
index,
@ -146,7 +146,7 @@ export class PersistedDocumentIngester {
if (err instanceof GraphQLError) {
console.error(err);
this.logger.debug(
'Failed parsing GraphQL operation. (targetId=%s, appDeploymentId=%s, operationIndex=%n)',
'Failed parsing GraphQL operation. (targetId=%s, appDeploymentId=%s, operationIndex=%d)',
data.targetId,
data.appDeployment.id,
index,
@ -171,7 +171,7 @@ export class PersistedDocumentIngester {
if (errors.length > 0) {
this.logger.debug(
'GraphQL operation did not pass validation against latest valid schema version. (targetId=%s, appDeploymentId=%s, operationIndex=%n)',
'GraphQL operation did not pass validation against latest valid schema version. (targetId=%s, appDeploymentId=%s, operationIndex=%d)',
data.targetId,
data.appDeployment.id,
index,
@ -234,7 +234,7 @@ export class PersistedDocumentIngester {
if (documents.length) {
this.logger.debug(
'inserting documents into clickhouse and s3. (targetId=%s, appDeployment=%s, documentCount=%n)',
'inserting documents into clickhouse and s3. (targetId=%s, appDeployment=%s, documentCount=%d)',
data.targetId,
data.appDeployment.id,
documents.length,
@ -261,7 +261,7 @@ export class PersistedDocumentIngester {
}) {
// 1. Insert into ClickHouse
this.logger.debug(
'Inserting documents into ClickHouse. (targetId=%s, appDeployment=%s, documentCount=%n)',
'Inserting documents into ClickHouse. (targetId=%s, appDeployment=%s, documentCount=%d)',
args.targetId,
args.appDeployment.id,
args.documents.length,
@ -291,7 +291,7 @@ export class PersistedDocumentIngester {
});
this.logger.debug(
'Inserting documents into ClickHouse finished. (targetId=%s, appDeployment=%s, documentCount=%n)',
'Inserting documents into ClickHouse finished. (targetId=%s, appDeployment=%s, documentCount=%d)',
args.targetId,
args.appDeployment.id,
args.documents.length,
@ -308,7 +308,7 @@ export class PersistedDocumentIngester {
documents: Array<DocumentRecord>;
}) {
this.logger.debug(
'Inserting documents into S3. (targetId=%s, appDeployment=%s, documentCount=%n)',
'Inserting documents into S3. (targetId=%s, appDeployment=%s, documentCount=%d)',
args.targetId,
args.appDeployment.id,
args.documents.length,
@ -353,7 +353,7 @@ export class PersistedDocumentIngester {
await Promise.all(tasks);
this.logger.debug(
'Inserting documents into S3 finished. (targetId=%s, appDeployment=%s, documentCount=%n)',
'Inserting documents into S3 finished. (targetId=%s, appDeployment=%s, documentCount=%d)',
args.targetId,
args.appDeployment.id,
args.documents.length,
@ -370,7 +370,7 @@ export class PersistedDocumentIngester {
try {
await this.onDocumentsPersisted(docsForCache);
this.logger.debug(
'Cache prefill callback completed. (targetId=%s, appDeployment=%s, documentCount=%n)',
'Cache prefill callback completed. (targetId=%s, appDeployment=%s, documentCount=%d)',
args.targetId,
args.appDeployment.id,
docsForCache.length,

View file

@ -23,7 +23,7 @@
"bcryptjs": "2.4.3",
"dotenv": "16.4.7",
"esbuild": "0.25.9",
"fastify": "4.29.1",
"fastify": "5.7.1",
"graphql": "16.9.0",
"itty-router": "4.2.2",
"itty-router-extras": "0.4.6",

View file

@ -18,7 +18,7 @@
"@trpc/server": "10.45.3",
"date-fns": "4.1.0",
"dotenv": "16.4.7",
"fastify": "4.29.1",
"fastify": "5.7.1",
"pino-pretty": "11.3.0",
"reflect-metadata": "0.2.2",
"stripe": "17.5.0",

View file

@ -18,7 +18,7 @@
"ajv": "8.17.1",
"dotenv": "16.4.7",
"eslint": "8.57.1",
"fastify": "4.29.1",
"fastify": "5.7.1",
"graphql": "16.9.0",
"pino-pretty": "11.3.0",
"zod": "3.25.76",

View file

@ -21,7 +21,7 @@
"@types/ioredis-mock": "8.2.5",
"dotenv": "16.4.7",
"fast-json-stable-stringify": "2.1.0",
"fastify": "4.29.1",
"fastify": "5.7.1",
"fastq": "1.19.1",
"got": "14.4.7",
"graphql": "16.9.0",

View file

@ -479,7 +479,7 @@ async function callExternalService(
}
}
logger?.error('encountered an unexpected error, throwing. error=%o', error);
logger?.error('encountered an unexpected error, throwing. error=%s', error);
throw error;
} finally {

View file

@ -19,8 +19,8 @@
"@escape.tech/graphql-armor-max-depth": "2.4.2",
"@escape.tech/graphql-armor-max-directives": "2.3.1",
"@escape.tech/graphql-armor-max-tokens": "2.5.1",
"@fastify/cors": "9.0.1",
"@fastify/formbody": "7.4.0",
"@fastify/cors": "11.2.0",
"@fastify/formbody": "8.0.2",
"@graphql-hive/plugin-opentelemetry": "1.3.0",
"@graphql-hive/yoga": "workspace:*",
"@graphql-tools/merge": "9.1.1",
@ -40,7 +40,7 @@
"@trpc/server": "10.45.3",
"@whatwg-node/server": "0.10.17",
"dotenv": "16.4.7",
"fastify": "4.29.1",
"fastify": "5.7.1",
"got": "14.4.7",
"graphql": "16.9.0",
"graphql-yoga": "5.13.3",

View file

@ -156,7 +156,10 @@ export async function main() {
return res.status(403).send(err.message);
}
return supertokensErrorHandler()(err, req, res);
// We can not upgrade Supertokens Node as it removed some APIs we rely on for
// our SSO flow. This the as `any` cast here.
// The code is still compatible and purely a type error.
return supertokensErrorHandler()(err, req, res as any);
});
await server.register(cors, (_: unknown): FastifyCorsOptionsDelegateCallback => {
return (req, callback) => {

View file

@ -9,7 +9,7 @@
"@trpc/server": "10.45.3"
},
"devDependencies": {
"@fastify/cors": "9.0.1",
"@fastify/cors": "11.2.0",
"@graphql-hive/logger": "1.0.9",
"@graphql-hive/plugin-opentelemetry": "1.3.0",
"@opentelemetry/api": "1.9.0",
@ -25,8 +25,8 @@
"@sentry/node": "7.120.2",
"@sentry/types": "7.120.2",
"@sentry/utils": "7.120.2",
"fastify": "4.29.1",
"fastify-plugin": "4.5.1",
"fastify": "5.7.1",
"fastify-plugin": "5.1.0",
"opentelemetry-instrumentation-fetch-node": "1.2.3",
"p-retry": "6.2.1",
"prom-client": "15.1.3",

View file

@ -5,9 +5,9 @@ export function createErrorHandler(server: FastifyInstance) {
return function errorHandler(message: string, error: Error, logger?: FastifyBaseLogger) {
Sentry.captureException(error);
if (logger) {
logger.error(message + ' (error=%s)', error);
logger.error(`${message} (error=%s)`, error);
} else {
server.log.error(message + ' (error=%s)', error);
server.log.error(`${message} (error=%s)`, error);
}
};
}

View file

@ -54,12 +54,7 @@ declare module 'fastify' {
function defaultFormatSpanName(request: FastifyRequest) {
const { method } = request;
let path;
if (request.routeOptions) {
path = request.routeOptions.url;
} else {
path = request.routerPath;
}
let path = request.routeOptions.url;
return path ? `${method} ${path}` : method;
}

View file

@ -56,13 +56,16 @@ export async function createServer(options: {
const server = fastify({
disableRequestLogging: true,
bodyLimit: options.bodyLimit ?? 30e6, // 30mb by default
logger:
options.log instanceof Logger
? bridgeHiveLoggerToFastifyLogger(options.log)
: {
...(options.log instanceof Logger
? {
loggerInstance: bridgeHiveLoggerToFastifyLogger(options.log),
}
: {
logger: {
level: options.log.level,
redact: ['request.options', 'options', 'request.headers.authorization'],
},
}),
maxParamLength: 5000,
requestIdHeader: 'x-request-id',
trustProxy: true,

View file

@ -1,4 +1,4 @@
import type { FastifyInstance, FastifyPluginAsync } from 'fastify';
import { type FastifyError, type FastifyInstance, type FastifyPluginAsync } from 'fastify';
import fp from 'fastify-plugin';
import * as Sentry from '@sentry/node';
import { cleanRequestId, maskToken } from './helpers';
@ -9,7 +9,7 @@ const plugin: FastifyPluginAsync<{ isSentryEnabled: boolean }> = async (server,
}
server.setErrorHandler((err, req, reply) => {
if (err.statusCode && err.statusCode < 500) {
if (isFastifyError(err) && err.statusCode && err.statusCode < 500) {
req.log.warn(err.message);
void reply.status(err.statusCode).send({
error: err.statusCode,
@ -67,3 +67,7 @@ export async function useHTTPErrorHandler(server: FastifyInstance, isSentryEnabl
isSentryEnabled,
});
}
function isFastifyError(error: unknown): error is FastifyError {
return typeof error === 'object' && !!error && 'statusCode' in error && 'code' in error;
}

View file

@ -15,7 +15,7 @@
"@trpc/server": "10.45.3",
"@types/ms": "0.7.34",
"dotenv": "16.4.7",
"fastify": "4.29.1",
"fastify": "5.7.1",
"ioredis": "5.8.2",
"lru-cache": "11.0.2",
"ms": "2.1.3",

View file

@ -56,7 +56,7 @@ export async function createStorage(
try {
await db.touchTokens({ tokens });
} catch (error) {
serverLogger.error('Failed to touch tokens', error);
serverLogger.error('Failed to touch tokens (error=%s)', error);
}
});
const cache = new LRUCache<
@ -379,7 +379,7 @@ function handleStorageError(params: {
tier: 'redis' | 'redis-stale' | 'db';
action: 'fetch' | 'set';
}) {
params.logger.error(params.logMsg, params.error);
params.logger.error(`${params.logMsg} (error=%s)`, params.error);
captureException(params.error, {
tags: {
storageTier: params.tier,

View file

@ -59,7 +59,7 @@ export function createTokens(config: { endpoint: string; logger: ServiceLogger }
}
return TokenStatus.NotFound;
} catch (error) {
config.logger.error('Failed to fetch fresh token', error);
config.logger.error('Failed to fetch fresh token (error=%s)', error);
return TokenStatus.NotFound;
}
},

View file

@ -171,7 +171,7 @@ export function registerUsageCollectionLegacyRoute(args: {
activeSpan?.addEvent('rate-limited');
droppedReports.labels({ targetId: tokenInfo.target, orgId: tokenInfo.organization }).inc();
authenticatedRequestLogger.debug(
'Rate limited',
'Rate limited (token=%s, target=%s, organization=%s)',
maskedToken,
tokenInfo.target,
tokenInfo.organization,

View file

@ -150,7 +150,12 @@ export function registerUsageCollectionRoute(args: {
if (isRateLimited) {
activeSpan?.addEvent('rate-limited');
droppedReports.labels({ targetId: target.id, orgId: target.orgId }).inc();
authenticatedRequestLogger.debug('Rate limited', maskedToken, target.id, target.orgId);
authenticatedRequestLogger.debug(
'Rate limited (token=%s, target=%s, organization=%s)',
maskedToken,
target.id,
target.orgId,
);
await reply.status(429).send();
return;
}

View file

@ -123,7 +123,7 @@ export const usageProcessorV1 = traceInlineSync(
logger.warn(
`Detected invalid operation (target=%s): %o`,
token.target,
validationResult.errors,
validationResult.errors ?? [],
);
invalidRawOperations
.labels({

View file

@ -3,7 +3,7 @@
"type": "module",
"private": true,
"scripts": {
"build": "tsx ../../../scripts/runify.ts src/server/index.ts && vite build --outDir dist/client",
"build": "tsx ../../../scripts/runify.ts src/server/index.ts && vite build",
"build-storybook": "storybook build",
"dev": "tsx watch --clear-screen=false --exclude \"./**/*.mjs\" src/server/dev.ts",
"generate-changelog": "node ../../../scripts/generate-changelog.js",
@ -17,9 +17,9 @@
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@fastify/cors": "9.0.1",
"@fastify/static": "7.0.4",
"@fastify/vite": "6.0.7",
"@fastify/cors": "11.2.0",
"@fastify/static": "9.0.0",
"@fastify/vite": "8.4.1",
"@graphiql/plugin-explorer": "4.0.0-alpha.2",
"@graphiql/react": "1.0.0-alpha.4",
"@graphiql/toolkit": "0.9.1",
@ -69,7 +69,7 @@
"@storybook/react-vite": "8.6.15",
"@stripe/react-stripe-js": "3.1.1",
"@stripe/stripe-js": "5.5.0",
"@tailwindcss/vite": "^4.1.18",
"@tailwindcss/vite": "4.1.18",
"@tanstack/react-form": "^1.27.0",
"@tanstack/react-query": "5.63.0",
"@tanstack/react-router": "1.34.9",
@ -92,7 +92,7 @@
"@urql/core": "5.0.3",
"@urql/exchange-auth": "2.2.0",
"@urql/exchange-graphcache": "7.1.0",
"@vitejs/plugin-react": "4.3.4",
"@vitejs/plugin-react": "5.1.2",
"@xyflow/react": "12.4.4",
"class-variance-authority": "0.7.1",
"clsx": "2.1.1",
@ -104,7 +104,7 @@
"dotenv": "16.4.7",
"echarts": "5.6.0",
"echarts-for-react": "3.0.2",
"fastify": "4.29.1",
"fastify": "5.7.1",
"formik": "2.4.6",
"framer-motion": "11.18.2",
"graphiql": "4.0.0-alpha.5",
@ -154,8 +154,8 @@
"urql": "4.1.0",
"use-debounce": "10.0.4",
"valtio": "1.13.2",
"vite": "7.1.11",
"vite-plugin-monaco-editor": "^1.1.0",
"vite": "7.3.1",
"vite-plugin-monaco-editor": "1.1.0",
"vite-tsconfig-paths": "5.1.4",
"wonka": "6.3.4",
"yup": "1.6.1",

View file

@ -52,6 +52,7 @@ const BaseSchema = zod.object({
PORT: emptyString(NumberFromString().optional()),
APP_BASE_URL: zod.string().url(),
GRAPHQL_PUBLIC_ENDPOINT: zod.string().url(),
GRAPHQL_PUBLIC_SUBSCRIPTION_ENDPOINT: zod.string().url(),
GRAPHQL_PUBLIC_ORIGIN: zod.string().url(),
INTEGRATION_GITHUB_APP_NAME: emptyString(zod.string().optional()),
GA_TRACKING_ID: emptyString(zod.string().optional()),

View file

@ -2,6 +2,7 @@ import { resolve } from 'node:path';
import type { Plugin, UserConfig } from 'vite';
import monacoEditor from 'vite-plugin-monaco-editor';
import tsconfigPaths from 'vite-tsconfig-paths';
import viteFastify from '@fastify/vite/plugin';
import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
@ -26,6 +27,7 @@ export default {
root: __dirname,
plugins: [
tsconfigPaths(),
viteFastify({ spa: true, useRelativePaths: true }),
react(),
tailwindcss(),
reactScanPlugin,
@ -41,6 +43,7 @@ export default {
}),
],
build: {
outDir: 'dist',
rollupOptions: {
input: {
index: resolve(__dirname, 'index.html'),

View file

@ -1,43 +0,0 @@
diff --git a/html.js b/html.js
index 479125b6e98296863245c3f226ce3edf57af5f8b..b1e2b63f33d290e63ec65e425dbad03c1aa384af 100644
--- a/html.js
+++ b/html.js
@@ -35,10 +35,10 @@ function createHtmlTemplateFunction(source) {
// biome-ignore lint/style/noCommaOperator: indirect call to eval() to ensure global scope
const compiledTemplatingFunction = (0, eval)(
// biome-ignore lint/style/useTemplate: needed for compiling
- `(asReadable) => (function ({ ${[
- ...new Set(params.map((s) => s.split('.')[0])),
- ].join(', ')} }) {` +
- `return asReadable\`${interpolated.map((s) => serialize(s)).join('')}\`` +
+ `(asReadable) => (function ({ ${[...new Set(params.map(s => s.split('.')[0]))].join(
+ ', ',
+ )} }) {` +
+ `return asReadable\`${interpolated.map(s => serialize(s)).join('')}\`` +
'})',
)(asReadable)
@@ -75,5 +75,5 @@ function serialize(frag) {
if (typeof frag === 'object') {
return `$\{${frag.param}}`
}
- return frag
+ return frag.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
}
diff --git a/mode/development.js b/mode/development.js
index af9de9d75a3689cd4f4b5d2876f2e38bd2674ae4..94ecb29a8e0d2615b1ecd0114dba7f3979dc2b11 100644
--- a/mode/development.js
+++ b/mode/development.js
@@ -79,7 +79,11 @@ async function setup(config) {
}
}
}
- const indexHtmlPath = join(config.vite.root, 'index.html')
+
+ // Request is decorated with viteHtmlFile in: packages/web/app/src/server/index.ts
+ // It is used to render more than one html file
+ const htmlFileName = req.viteHtmlFile ?? 'index.html';
+ const indexHtmlPath = join(config.vite.root,htmlFileName)
const indexHtml = await read(indexHtmlPath, 'utf8')
const transformedHtml = await this.devServer.transformIndexHtml(
req.url,

File diff suppressed because it is too large Load diff