security: upgrade @nestjs/graphql 12→13 + @ptc-org/nestjs-query 4→9 (+ @nestjs/config 4) (#21402)

## What

Upgrades the NestJS GraphQL stack to clear the High **`ws`** alert
(GHSA-3h5v-q93c-6h6q) and modernize off two heavily-patched majors.
`@nestjs/graphql@13` pulls `ws@8.20.1` (was 8.16.0).

This had to be a **coordinated** upgrade: `@ptc-org/nestjs-query@4.2.0`
doesn't support `@nestjs/graphql@13`, so all three move together.

| Package | From → To |
|---|---|
| `@nestjs/config` | 3.3.0 → ^4.0.4 |
| `@nestjs/graphql` | 12.1.1 → ^13.4.2 |
| `@ptc-org/nestjs-query-{core,graphql,typeorm}` | 4.x → ^9.4.0 |

## The tricky bits

- **Re-ported the custom `@nestjs/graphql` patch onto v13.** v13 rewrote
the schema builder and added its *own* native multi-schema support
(`includeModules`, native `clear()`). Twenty's patch
(`resolverSchemaScope` + `computeReachableTypes` — the
core/metadata/admin split) is re-merged into v13's new
`generate(options, includeModules, reachableTypes)` flow, with a
link-preserving `storage.clear()` so cross-schema `resolveType` closures
keep working.
- **Re-ported the `@ptc-org` patch onto 9.4.0**: removes the
`@shareable` federation directive from built-in connection/response
types, **and** adds a `.js` extension to its extensionless deep import
of `@nestjs/graphql` internals — which v13's new `"exports"` map
otherwise rejects at runtime (this was the boot blocker).
- **`AppTokenService`**: nestjs-query 9 requires custom services to
inject their repo and `super(repo)` it (added an `@InjectRepository`
constructor).
- **`gridPosition` input fields**: dropped the `deprecationReason` (a
*required* input field can't be `@deprecated` under the upgraded
graphql) — fields keep their original nullability, so the **schema is
unchanged**.
- **Service specs**: nestjs-query 9's `TypeOrmQueryService` reads the
repo's driver/metadata at construction, so the mocked repos now include
`manager`/`metadata`.

## Verification

- `nx typecheck twenty-server`: **0 errors**; lint clean
- Server boots; **all 3 GraphQL schemas** (`/graphql`, `/metadata`,
`/admin-panel`) generate and respond `200`
- `graphql:generate` for all 3 schemas is **byte-identical** to before
the upgrade (the reachable-types re-port is faithful)
- **108 service unit tests pass** (incl. all 6 `TypeOrmQueryService`
services)
- `ws@8.16.0` gone (now 8.17.1 + 8.18.0); `yarn install --immutable`
clean

## Note on lodash
`lodash@4.17.21` still remains via `zapier-platform-core` (runtime) and
`@stoplight/spectral`, so the lodash alert is **reduced but not fully
cleared** by this PR — it needs those separate sources addressed (or a
resolution).
This commit is contained in:
Charles Bochet
2026-06-10 15:55:15 +02:00
committed by GitHub
parent f1c7aecadb
commit 7258722754
12 changed files with 288 additions and 371 deletions
@@ -13,7 +13,12 @@ describe('AppTokenService', () => {
AppTokenService,
{
provide: getRepositoryToken(AppTokenEntity),
useValue: {},
useValue: {
manager: {
connection: { driver: { options: { type: 'postgres' } } },
},
metadata: { columns: [] },
},
},
],
}).compile();
@@ -1,5 +1,15 @@
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmQueryService } from '@ptc-org/nestjs-query-typeorm';
import { Repository } from 'typeorm';
import { type AppTokenEntity } from 'src/engine/core-modules/app-token/app-token.entity';
import { AppTokenEntity } from 'src/engine/core-modules/app-token/app-token.entity';
export class AppTokenService extends TypeOrmQueryService<AppTokenEntity> {}
export class AppTokenService extends TypeOrmQueryService<AppTokenEntity> {
constructor(
@InjectRepository(AppTokenEntity)
private readonly appTokenRepository: Repository<AppTokenEntity>,
) {
super(appTokenRepository);
}
}
@@ -52,6 +52,10 @@ describe('UserWorkspaceService', () => {
exists: jest.fn(),
findOne: jest.fn(),
findOneOrFail: jest.fn(),
manager: {
connection: { driver: { options: { type: 'postgres' } } },
},
metadata: { columns: [] },
},
},
{
@@ -52,6 +52,10 @@ describe('UserService', () => {
save: jest.fn(),
softDelete: jest.fn(),
update: jest.fn(),
manager: {
connection: { driver: { options: { type: 'postgres' } } },
},
metadata: { columns: [] },
},
},
{
@@ -57,6 +57,10 @@ describe('WorkspaceService', () => {
findOne: jest.fn(),
softDelete: jest.fn(),
delete: jest.fn(),
manager: {
connection: { driver: { options: { type: 'postgres' } } },
},
metadata: { columns: [] },
},
},
{