···2424 "license": "ISC",
2525 "dependencies": {
2626 "@apollo/server": "^5.5.0",
2727- "@as-integrations/express4": "^1.1.2",
2727+ "@as-integrations/express5": "^1.1.2",
2828 "@aws-sdk/client-s3": "^3.1017.0",
2929 "@aws-sdk/client-secrets-manager": "^3.1017.0",
3030 "@aws-sdk/client-ses": "^3.1017.0",
···4747 "@types/connect-pg-simple": "^7.0.0",
4848 "@types/cookie-parser": "^1.4.2",
4949 "@types/debug": "^4.1.5",
5050- "@types/express": "^4.17.17",
5050+ "@types/express": "^5.0.3",
5151 "@types/express-session": "^1.17.4",
5252 "@types/jsonwebtoken": "^9.0.10",
5353 "@types/lodash": "^4.14.179",
···6868 "dataloader": "^2.1.0",
6969 "date-fns": "^2.30.0",
7070 "eslint-plugin-switch-statement": "^0.0.11",
7171- "express": "^4.17.1",
7171+ "express": "^5.1.0",
7272 "express-session": "^1.17.2",
7373 "fast-check": "^4.6.0",
7474 "form-data": "^4.0.0",
···118118 "@faker-js/faker": "^7.5.0",
119119 "@types/cls-hooked": "^4.3.3",
120120 "@types/cors": "^2.8.19",
121121+ "@types/express-serve-static-core": "^5.1.1",
121122 "@types/graphql-depth-limit": "^1.1.6",
122123 "@types/jest": "^29.2.4",
123124 "@types/js-yaml": "^4.0.5",
···156157 "classNameTemplate": "{classname}",
157158 "titleTemplate": "{title}"
158159 },
159159- "//": "Use an override to remove the @types/restify dependency, b/c it conflicts w/ pino-http. And a fork of retry-axios that has fixed type defs for TS's new module resolution algorithm.",
160160+ "//": "Use an override to remove the @types/restify dependency, b/c it conflicts w/ pino-http. And a fork of retry-axios that has fixed type defs for TS's new module resolution algorithm. Also pin @types/express to v5 across the dep tree so transitive deps (e.g. @node-saml/passport-saml) don't drag in v4 types alongside our v5 types.",
160161 "overrides": {
161162 "@types/restify": "npm:pino@8.6.0",
163163+ "@types/express": "$@types/express",
162164 "@googlemaps/google-maps-services-js@^3.3.16": {
163165 "retry-axios": "npm:@ethanresnick/retry-axios@2.6.1"
164166 }
+12-10
server/routes/index.test.ts
···4545 next(new Error('error after send.'));
4646 });
47474848- // We have to move this new route to be before the catch all 404 route,
4949- // which makes it tricky. So we put it right after the three handlers that
5050- // express adds automatically (to parse query params, create the request
5151- // object, and (iiuc) normalize trailing path slashes), but before all our
5252- // handlers.
5353- const stack = server._router.stack as unknown[];
4848+ // Move the new route before the `/api/v1` sub-app mount, otherwise
4949+ // requests to `/api/v1/error` fall through to the sub-app's 404 handler.
5050+ type Layer = { name: string };
5151+ const stack = server.router.stack as Layer[];
5452 const newlyAddedErrorRoute = stack.at(-1);
5553 if (newlyAddedErrorRoute === undefined) {
5654 throw new Error('expected route on stack');
5755 }
5656+ const apiMountIdx = stack.findIndex((l) => l.name === 'mounted_app');
5757+ if (apiMountIdx === -1) {
5858+ throw new Error('expected /api/v1 sub-app mount on stack');
5959+ }
5860 const withoutLast = stack.slice(0, -1);
5961 // eslint-disable-next-line functional/immutable-data -- express test-only router reordering
6060- server._router.stack = [
6161- ...withoutLast.slice(0, 3),
6262+ server.router.stack = [
6363+ ...withoutLast.slice(0, apiMountIdx),
6264 newlyAddedErrorRoute,
6363- ...withoutLast.slice(3),
6464- ] as typeof server._router.stack;
6565+ ...withoutLast.slice(apiMountIdx),
6666+ ] as typeof server.router.stack;
65676668 try {
6769 const resp = await request.get('/api/v1/error');