From ecc607b850d12a493991b0440886714112429d1b Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Fri, 6 Feb 2026 01:59:57 +0700 Subject: [PATCH 01/23] refactor: use ORM mode as core for RQ mode --- CLAUDE.md | 144 +- .../codegen/examples/example.schema.graphql | 593 ++++++ .../codegen/examples/multi-target.config.ts | 15 + graphql/codegen/jest.config.js | 6 +- .../client-generator.test.ts.snap | 27 +- .../model-generator.test.ts.snap | 136 +- .../react-query-hooks.test.ts.snap | 1820 +++++++---------- .../codegen/client-generator.test.ts | 26 +- .../__tests__/codegen/format-output.test.ts | 3 +- .../codegen/input-types-generator.test.ts | 140 +- .../__tests__/codegen/model-generator.test.ts | 32 +- .../__tests__/codegen/query-builder.test.ts | 56 +- .../codegen/query-keys-factory.test.ts | 120 +- .../codegen/react-query-hooks.test.ts | 148 +- .../codegen/react-query-optional.test.ts | 46 +- .../src/__tests__/codegen/scalars.test.ts | 6 +- .../codegen/schema-types-generator.test.ts | 46 +- .../src/__tests__/codegen/utils.test.ts | 40 +- .../__tests__/config/resolve-config.test.ts | 26 +- .../__tests__/introspect/infer-tables.test.ts | 250 +-- graphql/codegen/src/cli/index.ts | 35 +- graphql/codegen/src/cli/shared.ts | 29 +- graphql/codegen/src/client/error.ts | 70 +- graphql/codegen/src/client/execute.ts | 16 +- graphql/codegen/src/client/index.ts | 20 +- graphql/codegen/src/client/typed-document.ts | 2 +- graphql/codegen/src/core/ast.ts | 279 ++- graphql/codegen/src/core/codegen/babel-ast.ts | 6 +- graphql/codegen/src/core/codegen/barrel.ts | 55 +- graphql/codegen/src/core/codegen/client.ts | 96 +- .../src/core/codegen/custom-mutations.ts | 334 ++- .../src/core/codegen/custom-queries.ts | 706 +++---- graphql/codegen/src/core/codegen/gql-ast.ts | 405 ---- graphql/codegen/src/core/codegen/index.ts | 212 +- .../codegen/src/core/codegen/invalidation.ts | 21 +- .../codegen/src/core/codegen/mutation-keys.ts | 27 +- graphql/codegen/src/core/codegen/mutations.ts | 1040 +++------- .../codegen/src/core/codegen/orm/barrel.ts | 9 +- .../src/core/codegen/orm/client-generator.ts | 31 +- .../codegen/src/core/codegen/orm/client.ts | 12 +- .../core/codegen/orm/custom-ops-generator.ts | 263 ++- graphql/codegen/src/core/codegen/orm/index.ts | 42 +- .../core/codegen/orm/input-types-generator.ts | 144 +- .../src/core/codegen/orm/model-generator.ts | 278 ++- graphql/codegen/src/core/codegen/queries.ts | 1511 +++----------- .../codegen/src/core/codegen/query-keys.ts | 14 +- graphql/codegen/src/core/codegen/scalars.ts | 6 +- .../src/core/codegen/schema-gql-ast.ts | 518 ----- .../core/codegen/schema-types-generator.ts | 15 +- .../src/core/codegen/select-helpers.ts | 90 + .../codegen/src/core/codegen/shared/index.ts | 25 +- .../core/codegen/templates/client.browser.ts | 271 --- .../src/core/codegen/templates/client.node.ts | 337 --- .../src/core/codegen/templates/orm-client.ts | 16 +- .../core/codegen/templates/query-builder.ts | 281 ++- .../core/codegen/templates/select-types.ts | 11 +- .../codegen/src/core/codegen/type-resolver.ts | 71 +- graphql/codegen/src/core/codegen/types.ts | 25 +- graphql/codegen/src/core/codegen/utils.ts | 45 +- graphql/codegen/src/core/config/index.ts | 7 +- graphql/codegen/src/core/config/loader.ts | 11 +- graphql/codegen/src/core/config/resolver.ts | 18 +- graphql/codegen/src/core/custom-ast.ts | 48 +- graphql/codegen/src/core/database/index.ts | 5 +- graphql/codegen/src/core/generate.ts | 48 +- graphql/codegen/src/core/index.ts | 6 +- .../src/core/introspect/fetch-schema.ts | 31 +- graphql/codegen/src/core/introspect/index.ts | 20 +- .../src/core/introspect/infer-tables.ts | 63 +- .../src/core/introspect/source/api-schemas.ts | 10 +- .../src/core/introspect/source/database.ts | 7 +- .../src/core/introspect/source/endpoint.ts | 6 +- .../src/core/introspect/source/file.ts | 4 +- .../src/core/introspect/source/index.ts | 108 +- .../src/core/introspect/source/pgpm-module.ts | 12 +- .../src/core/introspect/transform-schema.ts | 38 +- .../codegen/src/core/meta-object/convert.ts | 12 +- .../codegen/src/core/meta-object/validate.ts | 4 +- graphql/codegen/src/core/output/index.ts | 4 +- graphql/codegen/src/core/output/writer.ts | 8 +- graphql/codegen/src/core/pipeline/index.ts | 24 +- graphql/codegen/src/core/query-builder.ts | 56 +- graphql/codegen/src/core/watch/cache.ts | 1 + graphql/codegen/src/core/watch/index.ts | 18 +- .../codegen/src/core/watch/orchestrator.ts | 50 +- graphql/codegen/src/core/watch/poller.ts | 15 +- .../codegen/src/generators/field-selector.ts | 94 +- graphql/codegen/src/generators/index.ts | 16 +- graphql/codegen/src/generators/mutations.ts | 97 +- graphql/codegen/src/generators/select.ts | 187 +- graphql/codegen/src/index.ts | 14 +- graphql/codegen/src/types/config.ts | 18 +- graphql/codegen/src/types/index.ts | 39 +- 93 files changed, 4891 insertions(+), 7256 deletions(-) create mode 100644 graphql/codegen/examples/example.schema.graphql create mode 100644 graphql/codegen/examples/multi-target.config.ts delete mode 100644 graphql/codegen/src/core/codegen/gql-ast.ts delete mode 100644 graphql/codegen/src/core/codegen/schema-gql-ast.ts create mode 100644 graphql/codegen/src/core/codegen/select-helpers.ts delete mode 100644 graphql/codegen/src/core/codegen/templates/client.browser.ts delete mode 100644 graphql/codegen/src/core/codegen/templates/client.node.ts diff --git a/CLAUDE.md b/CLAUDE.md index 907fe7cd1..776c240c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,33 +5,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Build & Development Commands ```bash -# Install dependencies -pnpm install - -# Build all packages -pnpm build - -# Build all packages (dev mode - faster, no optimizations) -pnpm build:dev - -# Lint all packages (auto-fix enabled) -pnpm lint - -# Clean build artifacts -pnpm clean - -# Update dependencies interactively -pnpm deps +pnpm install # Install dependencies +pnpm build # Build all packages +pnpm build:dev # Build all packages (dev mode - faster, no optimizations) +pnpm lint # Lint all packages (auto-fix enabled) +pnpm clean # Clean build artifacts +pnpm deps # Update dependencies interactively (pnpm up -r -i -L) ``` ### Per-Package Commands -Navigate to any package directory (e.g., `cd pgpm/cli`) and run: +From any package directory (e.g., `cd pgpm/cli`): ```bash -pnpm build # Build the package +pnpm build # Build the package (uses makage) pnpm lint # Lint with auto-fix -pnpm test # Run tests +pnpm test # Run tests (Jest) pnpm test:watch # Run tests in watch mode pnpm dev # Run in development mode (where available) ``` @@ -41,54 +30,74 @@ pnpm dev # Run in development mode (where available) ```bash cd packages/cli pnpm test -- path/to/test.test.ts -# or with pattern matching: pnpm test -- --testNamePattern="test name pattern" ``` +### Publishing + +Lerna with independent versioning and conventional commits. Publishing only from `main` branch: + +```bash +npx lerna version # Bump versions +npx lerna publish # Publish to npm +``` + ## Project Architecture -This is a **pnpm monorepo** using Lerna for versioning/publishing. The workspace is organized into domain-specific directories: +A **pnpm monorepo** with Lerna for versioning. PostgreSQL-first framework: design your database schema, manage it with pgpm, and get a production-ready GraphQL API automatically via PostGraphile. + +### Data Flow + +``` +PostgreSQL (schema + RLS policies, managed by pgpm migrations) + ↓ +PostGraphile (graphql/server + graphile/* plugins) + ↓ +GraphQL Schema (auto-generated from database) + ↓ +graphql/codegen (--react-query mode OR --orm mode) + ↓ +React Query Hooks or Prisma-like ORM Client +``` -### Core Package Groups +### Workspace Groups | Directory | Purpose | |-----------|---------| -| `pgpm/` | PostgreSQL Package Manager - CLI, core engine, types | -| `graphql/` | GraphQL layer - server, codegen, React hooks, testing | -| `graphile/` | PostGraphile plugins - filters, i18n, meta-schema, PostGIS | -| `postgres/` | PostgreSQL utilities - introspection, testing, seeding, AST | -| `packages/` | Shared utilities - CLI, ORM, query builder | -| `uploads/` | File streaming - S3, ETags, content-type detection | -| `jobs/` | Job scheduling and worker infrastructure | +| `pgpm/` | PostgreSQL Package Manager - CLI (`pgpm`), core engine, types, env, logger | +| `graphql/` | GraphQL layer - server, codegen, query builder, explorer, AST utilities | +| `graphile/` | PostGraphile plugins - filters, i18n, meta-schema, PostGIS, search, uploads, settings | +| `postgres/` | PostgreSQL utilities - introspection, testing (pgsql-test), seeding, AST, query context | +| `packages/` | Shared utilities - CLI (`cnc`), ORM base, query builder, server utils, client | +| `uploads/` | File streaming - S3/MinIO, ETags, content-type detection, UUID hashing | +| `jobs/` | Knative job scheduling - worker, scheduler, service, functions | +| `functions/` | Knative cloud functions (e.g., send-email-link) | -### Key Packages +### Key Packages & CLIs -**pgpm (PostgreSQL Package Manager)** -- `pgpm/cli` - Main CLI tool (`pgpm` command) -- `pgpm/core` - Migration engine, dependency resolution, deployment +**`pgpm` CLI** (`pgpm/cli`) - PostgreSQL Package Manager. Commands: `init`, `add`, `deploy`, `revert`, `verify`, `plan`, `install`, `export`, `docker`, `dump`, `tag`. Manages SQL migrations in Sqitch-compatible format with dependency resolution. -**GraphQL Stack** -- `graphql/server` - Express + PostGraphile API server -- `graphql/codegen` - SDK generator (React Query hooks or Prisma-like ORM) -- `graphql/query` - Fluent GraphQL query builder +**`cnc` CLI** (`packages/cli`, binary: `cnc` or `constructive`) - Full dev toolkit. Commands: `server` (start PostGraphile), `explorer` (GraphiQL UI), `codegen` (generate SDK), `get-graphql-schema`, `jobs`, `context`, `auth`, `execute`. -**Testing Infrastructure** -- `postgres/pgsql-test` - Isolated PostgreSQL test environments with transaction rollback -- `graphile/graphile-test` - GraphQL testing utilities +**`graphql/codegen`** - Generates type-safe clients from GraphQL schema or endpoint: +- `--react-query` mode: TanStack Query v5 hooks with query key factories +- `--orm` mode: Prisma-like fluent API with `InferSelectResult` type inference, discriminated union error handling (`.unwrap()`, `.unwrapOr()`) +- Sources: GraphQL endpoint URL, .graphql schema file, or direct database introspection -### Testing Pattern +**`graphql/server`** - Express + PostGraphile. Supports multi-endpoint routing via subdomain/host detection (schema builder with app-public/auth/admin sub-endpoints). Uses `LISTEN/NOTIFY` for schema cache invalidation. -Tests use `pgsql-test` for database testing with per-test transaction rollback: +**`graphile/graphile-settings`** - Centralizes all PostGraphile plugin config: connection filters, full-text search, PostGIS, i18n, meta-schema, many-to-many, search, upload plugin. Single `getGraphileSettings(opts)` entry point. -```typescript -import { getConnections } from 'pgsql-test'; +**`packages/query-builder`** - Fluent SQL query builder for SELECT/INSERT/UPDATE/DELETE with JOINs, WHERE, GROUP BY. Schema-qualified tables. -let db, teardown; +### Testing Infrastructure -beforeAll(async () => { - ({ db, teardown } = await getConnections()); -}); +**`postgres/pgsql-test`** - Isolated PostgreSQL test environments with per-test transaction rollback: +```typescript +import { getConnections } from 'pgsql-test'; +let db, teardown; +beforeAll(async () => ({ db, teardown } = await getConnections())); beforeEach(() => db.beforeEach()); afterEach(() => db.afterEach()); afterAll(() => teardown()); @@ -100,26 +109,35 @@ test('example', async () => { }); ``` +**`graphile/graphile-test`** - GraphQL testing with PostGraphile snapshot support. + +### Job System + +Background jobs use Knative: jobs are added to `app_jobs.jobs` table → `knative-job-worker` polls and picks up → POSTs to Knative function URL → function executes (e.g., send email) → returns status. + ### Database Configuration -Tests require PostgreSQL. Standard PG environment variables: -- `PGHOST` (default: localhost) -- `PGPORT` (default: 5432) -- `PGUSER` (default: postgres) -- `PGPASSWORD` (default: password) +Tests require PostgreSQL. Standard PG env vars: +- `PGHOST` (default: localhost), `PGPORT` (default: 5432) +- `PGUSER` (default: postgres), `PGPASSWORD` (default: password) For S3/MinIO tests: `MINIO_ENDPOINT`, `AWS_ACCESS_KEY`, `AWS_SECRET_KEY`, `AWS_REGION` -### Build System +## Build System -- Uses `makage` for TypeScript compilation (handles both CJS and ESM output) -- Jest with ts-jest for testing -- ESLint with TypeScript support -- Each package has its own `tsconfig.json` extending root config +- **makage** compiles TypeScript to both CJS and ESM, outputs to `dist/` +- `makage build --dev` for faster dev builds +- Some packages use `copyfiles` for non-TS assets (SQL files, templates) +- Jest with `ts-jest` preset per-package (`jest.config.js` in each package) -### Code Conventions +## Code Conventions -- TypeScript with `strict: true` (but `strictNullChecks: false`) -- Target: ES2022, Module: CommonJS -- Packages publish to npm from `dist/` directory +- TypeScript with `strict: true` but `strictNullChecks: false` +- Target: ES2022, Module: CommonJS, ModuleResolution: node +- 2-space indent, single quotes, semicolons required, no trailing commas +- Imports auto-sorted by `simple-import-sort`, unused imports auto-removed +- `@typescript-eslint/no-explicit-any`: allowed (turned off) +- Unused var pattern: prefix with `_` (e.g., `_unused`) - Workspace dependencies use `workspace:^` protocol +- Packages publish from `dist/` directory +- GraphQL pinned to `15.10.1` via overrides diff --git a/graphql/codegen/examples/example.schema.graphql b/graphql/codegen/examples/example.schema.graphql new file mode 100644 index 000000000..1bba02d5c --- /dev/null +++ b/graphql/codegen/examples/example.schema.graphql @@ -0,0 +1,593 @@ +"""A universally unique identifier as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122).""" +scalar UUID + +""" +A point in time as described by the [ISO +8601](https://en.wikipedia.org/wiki/ISO_8601) standard. May or may not include a timezone. +""" +scalar Datetime + +"""A location as described by the [GeoJSON](https://geojson.org/) format.""" +scalar JSON + +"""A string representing a cursor for pagination.""" +scalar Cursor + +"""The root query type.""" +type Query { + """Reads and enables pagination through a set of `User`.""" + users( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + orderBy: [UsersOrderBy!] = [PRIMARY_KEY_ASC] + filter: UserFilter + condition: UserCondition + ): UsersConnection + + """Reads a single `User` using its globally unique `ID`.""" + user(id: UUID!): User + + """Reads and enables pagination through a set of `Post`.""" + posts( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + orderBy: [PostsOrderBy!] = [PRIMARY_KEY_ASC] + filter: PostFilter + condition: PostCondition + ): PostsConnection + + """Reads a single `Post` using its globally unique `ID`.""" + post(id: UUID!): Post + + """Reads and enables pagination through a set of `Comment`.""" + comments( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + orderBy: [CommentsOrderBy!] = [PRIMARY_KEY_ASC] + filter: CommentFilter + ): CommentsConnection + + """Reads a single `Comment` using its globally unique `ID`.""" + comment(id: UUID!): Comment + + """Reads and enables pagination through a set of `Tag`.""" + tags( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + orderBy: [TagsOrderBy!] = [PRIMARY_KEY_ASC] + ): TagsConnection + + """The currently authenticated user.""" + currentUser: User + + """Search users by name or email.""" + searchUsers(query: String!): [User!] +} + +"""The root mutation type.""" +type Mutation { + """Creates a single `User`.""" + createUser(input: CreateUserInput!): CreateUserPayload + + """Updates a single `User` using its globally unique `ID`.""" + updateUser(input: UpdateUserInput!): UpdateUserPayload + + """Deletes a single `User` using its globally unique `ID`.""" + deleteUser(input: DeleteUserInput!): DeleteUserPayload + + """Creates a single `Post`.""" + createPost(input: CreatePostInput!): CreatePostPayload + + """Updates a single `Post` using its globally unique `ID`.""" + updatePost(input: UpdatePostInput!): UpdatePostPayload + + """Deletes a single `Post` using its globally unique `ID`.""" + deletePost(input: DeletePostInput!): DeletePostPayload + + """Creates a single `Comment`.""" + createComment(input: CreateCommentInput!): CreateCommentPayload + + """Updates a single `Comment` using its globally unique `ID`.""" + updateComment(input: UpdateCommentInput!): UpdateCommentPayload + + """Deletes a single `Comment` using its globally unique `ID`.""" + deleteComment(input: DeleteCommentInput!): DeleteCommentPayload + + """Creates a single `Tag`.""" + createTag(input: CreateTagInput!): CreateTagPayload + + """Authenticate a user and return a JWT token.""" + login(email: String!, password: String!): LoginPayload + + """Register a new user account.""" + register(username: String!, email: String!, password: String!): RegisterPayload +} + +# ============================================================================ +# Entity Types +# ============================================================================ + +type User { + id: UUID! + username: String! + email: String! + displayName: String + bio: String + role: UserRole! + isActive: Boolean! + createdAt: Datetime! + updatedAt: Datetime + + """Reads and enables pagination through a set of `Post`.""" + posts( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + orderBy: [PostsOrderBy!] = [PRIMARY_KEY_ASC] + filter: PostFilter + ): PostsConnection! + + """Reads and enables pagination through a set of `Comment`.""" + comments( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + ): CommentsConnection! +} + +type Post { + id: UUID! + title: String! + content: String + slug: String! + status: PostStatus! + authorId: UUID! + publishedAt: Datetime + createdAt: Datetime! + updatedAt: Datetime + + """Reads the `User` that authored this post.""" + author: User + + """Reads and enables pagination through a set of `Comment`.""" + comments( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + ): CommentsConnection! + + """Reads and enables pagination through a set of `Tag`.""" + tags( + first: Int + last: Int + offset: Int + before: Cursor + after: Cursor + ): TagsConnection! +} + +type Comment { + id: UUID! + body: String! + postId: UUID! + authorId: UUID! + createdAt: Datetime! + updatedAt: Datetime + + """The post this comment belongs to.""" + post: Post + + """The user who authored this comment.""" + author: User +} + +type Tag { + id: UUID! + name: String! + slug: String! + createdAt: Datetime! +} + +# ============================================================================ +# Enums +# ============================================================================ + +enum UserRole { + ADMIN + EDITOR + USER + GUEST +} + +enum PostStatus { + DRAFT + PUBLISHED + ARCHIVED +} + +enum UsersOrderBy { + NATURAL + PRIMARY_KEY_ASC + PRIMARY_KEY_DESC + ID_ASC + ID_DESC + USERNAME_ASC + USERNAME_DESC + EMAIL_ASC + EMAIL_DESC + CREATED_AT_ASC + CREATED_AT_DESC +} + +enum PostsOrderBy { + NATURAL + PRIMARY_KEY_ASC + PRIMARY_KEY_DESC + ID_ASC + ID_DESC + TITLE_ASC + TITLE_DESC + CREATED_AT_ASC + CREATED_AT_DESC + PUBLISHED_AT_ASC + PUBLISHED_AT_DESC +} + +enum CommentsOrderBy { + NATURAL + PRIMARY_KEY_ASC + PRIMARY_KEY_DESC + ID_ASC + ID_DESC + CREATED_AT_ASC + CREATED_AT_DESC +} + +enum TagsOrderBy { + NATURAL + PRIMARY_KEY_ASC + PRIMARY_KEY_DESC + ID_ASC + ID_DESC + NAME_ASC + NAME_DESC +} + +# ============================================================================ +# Connection Types +# ============================================================================ + +type UsersConnection { + nodes: [User!]! + edges: [UsersEdge!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type UsersEdge { + node: User! + cursor: Cursor! +} + +type PostsConnection { + nodes: [Post!]! + edges: [PostsEdge!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type PostsEdge { + node: Post! + cursor: Cursor! +} + +type CommentsConnection { + nodes: [Comment!]! + edges: [CommentsEdge!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type CommentsEdge { + node: Comment! + cursor: Cursor! +} + +type TagsConnection { + nodes: [Tag!]! + edges: [TagsEdge!]! + pageInfo: PageInfo! + totalCount: Int! +} + +type TagsEdge { + node: Tag! + cursor: Cursor! +} + +type PageInfo { + hasNextPage: Boolean! + hasPreviousPage: Boolean! + startCursor: Cursor + endCursor: Cursor +} + +# ============================================================================ +# Filter Types +# ============================================================================ + +input UserFilter { + id: UUIDFilter + username: StringFilter + email: StringFilter + role: UserRoleFilter + isActive: BooleanFilter + and: [UserFilter!] + or: [UserFilter!] + not: UserFilter +} + +input PostFilter { + id: UUIDFilter + title: StringFilter + status: PostStatusFilter + authorId: UUIDFilter + and: [PostFilter!] + or: [PostFilter!] + not: PostFilter +} + +input CommentFilter { + id: UUIDFilter + postId: UUIDFilter + authorId: UUIDFilter + and: [CommentFilter!] + or: [CommentFilter!] + not: CommentFilter +} + +input UUIDFilter { + equalTo: UUID + notEqualTo: UUID + in: [UUID!] + notIn: [UUID!] + isNull: Boolean +} + +input StringFilter { + equalTo: String + notEqualTo: String + in: [String!] + notIn: [String!] + includes: String + startsWith: String + endsWith: String + isNull: Boolean +} + +input BooleanFilter { + equalTo: Boolean + notEqualTo: Boolean + isNull: Boolean +} + +input UserRoleFilter { + equalTo: UserRole + notEqualTo: UserRole + in: [UserRole!] + notIn: [UserRole!] +} + +input PostStatusFilter { + equalTo: PostStatus + notEqualTo: PostStatus + in: [PostStatus!] + notIn: [PostStatus!] +} + +# ============================================================================ +# Condition Types +# ============================================================================ + +input UserCondition { + id: UUID + username: String + email: String +} + +input PostCondition { + id: UUID + title: String + authorId: UUID +} + +# ============================================================================ +# Mutation Input Types +# ============================================================================ + +input CreateUserInput { + clientMutationId: String + user: UserInput! +} + +input UserInput { + username: String! + email: String! + displayName: String + bio: String + role: UserRole +} + +input UpdateUserInput { + clientMutationId: String + id: UUID! + patch: UserPatch! +} + +input UserPatch { + username: String + email: String + displayName: String + bio: String + role: UserRole + isActive: Boolean +} + +input DeleteUserInput { + clientMutationId: String + id: UUID! +} + +input CreatePostInput { + clientMutationId: String + post: PostInput! +} + +input PostInput { + title: String! + content: String + slug: String! + status: PostStatus + authorId: UUID! +} + +input UpdatePostInput { + clientMutationId: String + id: UUID! + patch: PostPatch! +} + +input PostPatch { + title: String + content: String + slug: String + status: PostStatus + publishedAt: Datetime +} + +input DeletePostInput { + clientMutationId: String + id: UUID! +} + +input CreateCommentInput { + clientMutationId: String + comment: CommentInput! +} + +input CommentInput { + body: String! + postId: UUID! + authorId: UUID! +} + +input UpdateCommentInput { + clientMutationId: String + id: UUID! + patch: CommentPatch! +} + +input CommentPatch { + body: String +} + +input DeleteCommentInput { + clientMutationId: String + id: UUID! +} + +input CreateTagInput { + clientMutationId: String + tag: TagInput! +} + +input TagInput { + name: String! + slug: String! +} + +# ============================================================================ +# Mutation Payload Types +# ============================================================================ + +type CreateUserPayload { + clientMutationId: String + user: User +} + +type UpdateUserPayload { + clientMutationId: String + user: User +} + +type DeleteUserPayload { + clientMutationId: String + user: User +} + +type CreatePostPayload { + clientMutationId: String + post: Post +} + +type UpdatePostPayload { + clientMutationId: String + post: Post +} + +type DeletePostPayload { + clientMutationId: String + post: Post +} + +type CreateCommentPayload { + clientMutationId: String + comment: Comment +} + +type UpdateCommentPayload { + clientMutationId: String + comment: Comment +} + +type DeleteCommentPayload { + clientMutationId: String + comment: Comment +} + +type CreateTagPayload { + clientMutationId: String + tag: Tag +} + +# ============================================================================ +# Custom Operation Payload Types +# ============================================================================ + +type LoginPayload { + token: String! + user: User! +} + +type RegisterPayload { + token: String! + user: User! +} diff --git a/graphql/codegen/examples/multi-target.config.ts b/graphql/codegen/examples/multi-target.config.ts new file mode 100644 index 000000000..75f3628c0 --- /dev/null +++ b/graphql/codegen/examples/multi-target.config.ts @@ -0,0 +1,15 @@ +import type { GraphQLSDKConfigTarget } from '../src/types/config'; + +/** + * Multi-target example config for graphql-codegen + * + * Usage with CLI flags to select mode: + * tsx src/cli/index.ts --config examples/multi-target.config.ts --react-query + * tsx src/cli/index.ts --config examples/multi-target.config.ts --orm + */ +const config: GraphQLSDKConfigTarget = { + endpoint: 'http://api.localhost:3000/graphql', + output: './examples/output/generated-sdk-public', +}; + +export default config; diff --git a/graphql/codegen/jest.config.js b/graphql/codegen/jest.config.js index 86f911c5d..475aa4db7 100644 --- a/graphql/codegen/jest.config.js +++ b/graphql/codegen/jest.config.js @@ -7,9 +7,9 @@ module.exports = { 'ts-jest', { babelConfig: false, - tsconfig: 'tsconfig.json', - }, - ], + tsconfig: 'tsconfig.json' + } + ] }, transformIgnorePatterns: [`/node_modules/*`], testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$', diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap index 11e9fad8f..31e4f0863 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap @@ -107,13 +107,13 @@ exports[`client-generator generateOrmClientFile generates OrmClient class with e import type { GraphQLAdapter, GraphQLError, - QueryResult, + QueryResult } from '@constructive-io/graphql-types'; export type { GraphQLAdapter, GraphQLError, - QueryResult, + QueryResult } from '@constructive-io/graphql-types'; /** @@ -139,19 +139,19 @@ export class FetchAdapter implements GraphQLAdapter { headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers, + ...this.headers }, body: JSON.stringify({ query: document, - variables: variables ?? {}, - }), + variables: variables ?? {} + }) }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: \`HTTP \${response.status}: \${response.statusText}\` }], + errors: [{ message: \`HTTP \${response.status}: \${response.statusText}\` }] }; } @@ -164,14 +164,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors, + errors: json.errors }; } return { ok: true, data: json.data as T, - errors: undefined, + errors: undefined }; } @@ -301,8 +301,17 @@ export interface UpdateArgs { select?: TSelect; } -export interface DeleteArgs { +export type FindOneArgs< + TSelect, + TIdName extends string = 'id', + TId = string +> = { + select?: TSelect; +} & Record; + +export interface DeleteArgs { where: TWhere; + select?: TSelect; } /** diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap index dc51d712f..fafb3a3d1 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap @@ -7,18 +7,21 @@ exports[`model-generator generates model with all CRUD methods 1`] = ` * DO NOT EDIT - changes will be overwritten */ import { OrmClient } from "../client"; -import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildCreateDocument, buildUpdateDocument, buildDeleteDocument } from "../query-builder"; +import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; import type { User, UserWithRelations, UserSelect, UserFilter, UsersOrderBy, CreateUserInput, UpdateUserInput, UserPatch } from "../input-types"; +const defaultSelect = { + id: true +} as const; export class UserModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, UserFilter, UsersOrderBy>): QueryBuilder<{ + findMany(args?: FindManyArgs, UserFilter, UsersOrderBy>): QueryBuilder<{ users: ConnectionResult>; }> { const { document, variables - } = buildFindManyDocument("User", "users", args?.select, { + } = buildFindManyDocument("User", "users", args?.select ?? defaultSelect, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -36,7 +39,7 @@ export class UserModel { variables }); } - findFirst(args?: FindFirstArgs, UserFilter>): QueryBuilder<{ + findFirst(args?: FindFirstArgs, UserFilter>): QueryBuilder<{ users: { nodes: InferSelectResult[]; }; @@ -44,7 +47,7 @@ export class UserModel { const { document, variables - } = buildFindFirstDocument("User", "users", args?.select, { + } = buildFindFirstDocument("User", "users", args?.select ?? defaultSelect, { where: args?.where }, "UserFilter"); return new QueryBuilder({ @@ -56,7 +59,26 @@ export class UserModel { variables }); } - create(args: CreateArgs, CreateUserInput["user"]>): QueryBuilder<{ + findOne(args: { + id: string; + select?: DeepExact; + }): QueryBuilder<{ + user: InferSelectResult | null; + }> { + const { + document, + variables + } = buildFindOneDocument("User", "user", args.id, args.select ?? defaultSelect, "id", "UUID!"); + return new QueryBuilder({ + client: this.client, + operation: "query", + operationName: "User", + fieldName: "user", + document, + variables + }); + } + create(args: CreateArgs, CreateUserInput["user"]>): QueryBuilder<{ createUser: { user: InferSelectResult; }; @@ -64,7 +86,7 @@ export class UserModel { const { document, variables - } = buildCreateDocument("User", "createUser", "user", args.select, args.data, "CreateUserInput"); + } = buildCreateDocument("User", "createUser", "user", args.select ?? defaultSelect, args.data, "CreateUserInput"); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -74,7 +96,7 @@ export class UserModel { variables }); } - update(args: UpdateArgs, { + update(args: UpdateArgs, { id: string; }, UserPatch>): QueryBuilder<{ updateUser: { @@ -84,7 +106,7 @@ export class UserModel { const { document, variables - } = buildUpdateDocument("User", "updateUser", "user", args.select, args.where, args.data, "UpdateUserInput"); + } = buildUpdateByPkDocument("User", "updateUser", "user", args.select ?? defaultSelect, args.where.id, args.data, "UpdateUserInput", "id"); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -94,19 +116,17 @@ export class UserModel { variables }); } - delete(args: DeleteArgs<{ + delete(args: DeleteArgs<{ id: string; - }>): QueryBuilder<{ + }, DeepExact>): QueryBuilder<{ deleteUser: { - user: { - id: string; - }; + user: InferSelectResult; }; }> { const { document, variables - } = buildDeleteDocument("User", "deleteUser", "user", args.where, "DeleteUserInput"); + } = buildDeleteByPkDocument("User", "deleteUser", "user", args.where.id, "DeleteUserInput", "id", args.select ?? defaultSelect); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -126,18 +146,21 @@ exports[`model-generator generates model without update/delete when not availabl * DO NOT EDIT - changes will be overwritten */ import { OrmClient } from "../client"; -import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildCreateDocument, buildUpdateDocument, buildDeleteDocument } from "../query-builder"; +import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, AuditLogsOrderBy, CreateAuditLogInput, UpdateAuditLogInput, AuditLogPatch } from "../input-types"; +const defaultSelect = { + id: true +} as const; export class AuditLogModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, AuditLogFilter, AuditLogsOrderBy>): QueryBuilder<{ + findMany(args?: FindManyArgs, AuditLogFilter, AuditLogsOrderBy>): QueryBuilder<{ auditLogs: ConnectionResult>; }> { const { document, variables - } = buildFindManyDocument("AuditLog", "auditLogs", args?.select, { + } = buildFindManyDocument("AuditLog", "auditLogs", args?.select ?? defaultSelect, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -155,7 +178,7 @@ export class AuditLogModel { variables }); } - findFirst(args?: FindFirstArgs, AuditLogFilter>): QueryBuilder<{ + findFirst(args?: FindFirstArgs, AuditLogFilter>): QueryBuilder<{ auditLogs: { nodes: InferSelectResult[]; }; @@ -163,7 +186,7 @@ export class AuditLogModel { const { document, variables - } = buildFindFirstDocument("AuditLog", "auditLogs", args?.select, { + } = buildFindFirstDocument("AuditLog", "auditLogs", args?.select ?? defaultSelect, { where: args?.where }, "AuditLogFilter"); return new QueryBuilder({ @@ -175,7 +198,26 @@ export class AuditLogModel { variables }); } - create(args: CreateArgs, CreateAuditLogInput["auditLog"]>): QueryBuilder<{ + findOne(args: { + id: string; + select?: DeepExact; + }): QueryBuilder<{ + auditLog: InferSelectResult | null; + }> { + const { + document, + variables + } = buildFindOneDocument("AuditLog", "auditLog", args.id, args.select ?? defaultSelect, "id", "UUID!"); + return new QueryBuilder({ + client: this.client, + operation: "query", + operationName: "AuditLog", + fieldName: "auditLog", + document, + variables + }); + } + create(args: CreateArgs, CreateAuditLogInput["auditLog"]>): QueryBuilder<{ createAuditLog: { auditLog: InferSelectResult; }; @@ -183,7 +225,7 @@ export class AuditLogModel { const { document, variables - } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select, args.data, "CreateAuditLogInput"); + } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select ?? defaultSelect, args.data, "CreateAuditLogInput"); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -203,18 +245,21 @@ exports[`model-generator handles custom query/mutation names 1`] = ` * DO NOT EDIT - changes will be overwritten */ import { OrmClient } from "../client"; -import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildCreateDocument, buildUpdateDocument, buildDeleteDocument } from "../query-builder"; +import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; import type { Organization, OrganizationWithRelations, OrganizationSelect, OrganizationFilter, OrganizationsOrderBy, CreateOrganizationInput, UpdateOrganizationInput, OrganizationPatch } from "../input-types"; +const defaultSelect = { + id: true +} as const; export class OrganizationModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, OrganizationFilter, OrganizationsOrderBy>): QueryBuilder<{ + findMany(args?: FindManyArgs, OrganizationFilter, OrganizationsOrderBy>): QueryBuilder<{ allOrganizations: ConnectionResult>; }> { const { document, variables - } = buildFindManyDocument("Organization", "allOrganizations", args?.select, { + } = buildFindManyDocument("Organization", "allOrganizations", args?.select ?? defaultSelect, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -232,7 +277,7 @@ export class OrganizationModel { variables }); } - findFirst(args?: FindFirstArgs, OrganizationFilter>): QueryBuilder<{ + findFirst(args?: FindFirstArgs, OrganizationFilter>): QueryBuilder<{ allOrganizations: { nodes: InferSelectResult[]; }; @@ -240,7 +285,7 @@ export class OrganizationModel { const { document, variables - } = buildFindFirstDocument("Organization", "allOrganizations", args?.select, { + } = buildFindFirstDocument("Organization", "allOrganizations", args?.select ?? defaultSelect, { where: args?.where }, "OrganizationFilter"); return new QueryBuilder({ @@ -252,7 +297,26 @@ export class OrganizationModel { variables }); } - create(args: CreateArgs, CreateOrganizationInput["organization"]>): QueryBuilder<{ + findOne(args: { + id: string; + select?: DeepExact; + }): QueryBuilder<{ + organizationById: InferSelectResult | null; + }> { + const { + document, + variables + } = buildFindOneDocument("Organization", "organizationById", args.id, args.select ?? defaultSelect, "id", "UUID!"); + return new QueryBuilder({ + client: this.client, + operation: "query", + operationName: "Organization", + fieldName: "organizationById", + document, + variables + }); + } + create(args: CreateArgs, CreateOrganizationInput["organization"]>): QueryBuilder<{ registerOrganization: { organization: InferSelectResult; }; @@ -260,7 +324,7 @@ export class OrganizationModel { const { document, variables - } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select, args.data, "CreateOrganizationInput"); + } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select ?? defaultSelect, args.data, "CreateOrganizationInput"); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -270,7 +334,7 @@ export class OrganizationModel { variables }); } - update(args: UpdateArgs, { + update(args: UpdateArgs, { id: string; }, OrganizationPatch>): QueryBuilder<{ modifyOrganization: { @@ -280,7 +344,7 @@ export class OrganizationModel { const { document, variables - } = buildUpdateDocument("Organization", "modifyOrganization", "organization", args.select, args.where, args.data, "UpdateOrganizationInput"); + } = buildUpdateByPkDocument("Organization", "modifyOrganization", "organization", args.select ?? defaultSelect, args.where.id, args.data, "UpdateOrganizationInput", "id"); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -290,19 +354,17 @@ export class OrganizationModel { variables }); } - delete(args: DeleteArgs<{ + delete(args: DeleteArgs<{ id: string; - }>): QueryBuilder<{ + }, DeepExact>): QueryBuilder<{ removeOrganization: { - organization: { - id: string; - }; + organization: InferSelectResult; }; }> { const { document, variables - } = buildDeleteDocument("Organization", "removeOrganization", "organization", args.where, "DeleteOrganizationInput"); + } = buildDeleteByPkDocument("Organization", "removeOrganization", "organization", args.where.id, "DeleteOrganizationInput", "id", args.select ?? defaultSelect); return new QueryBuilder({ client: this.client, operation: "mutation", diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index efafdb00e..1e1d1710d 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -57,8 +57,6 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel with al * \`\`\` */ export * from "./client"; -export * from "./types"; -export * from "./schema-types"; export * from "./query-keys"; export * from "./mutation-keys"; export * from "./invalidation"; @@ -97,7 +95,6 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel without * \`\`\` */ export * from "./client"; -export * from "./types"; export * from "./queries"; export * from "./mutations";" `; @@ -133,8 +130,6 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel without * \`\`\` */ export * from "./client"; -export * from "./types"; -export * from "./schema-types"; export * from "./queries";" `; @@ -192,33 +187,30 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { LoginPayload } from "../schema-types"; -import { customMutationKeys } from "../mutation-keys"; -/** GraphQL mutation document */ -export const loginMutationDocument = \` -mutation LoginMutation($email: String!, $password: String!) { - login(email: $email, password: $password) { - token - } -} -\`; -export interface LoginMutationVariables { - email: string; - password: string; -} -export interface LoginMutationResult { - login: LoginPayload; -} -export function useLoginMutation(options?: Omit, 'mutationFn'>) { +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { customMutationKeys } from '../mutation-keys'; +import type { LoginVariables } from '../../orm/mutation'; +import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { LoginVariables } from '../../orm/mutation'; +export type { LoginPayloadSelect } from '../../orm/input-types'; + +const defaultSelect = { token: true } as const; + +export function useLoginMutation( + args?: { select?: DeepExact }, + options?: Omit }, Error, LoginVariables>, 'mutationFn'> +) { return useMutation({ mutationKey: customMutationKeys.login(), - mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables), - ...options + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); -}" +} +" `; exports[`Custom Mutation Hook Generators generateCustomMutationHook generates custom mutation hook with input object argument 1`] = ` @@ -228,32 +220,30 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { RegisterInput, RegisterPayload } from "../schema-types"; -import { customMutationKeys } from "../mutation-keys"; -/** GraphQL mutation document */ -export const registerMutationDocument = \` -mutation RegisterMutation($input: RegisterInput!) { - register(input: $input) { - token - } -} -\`; -export interface RegisterMutationVariables { - input: RegisterInput; -} -export interface RegisterMutationResult { - register: RegisterPayload; -} -export function useRegisterMutation(options?: Omit, 'mutationFn'>) { +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { customMutationKeys } from '../mutation-keys'; +import type { RegisterVariables } from '../../orm/mutation'; +import type { RegisterPayloadSelect, RegisterPayload } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { RegisterVariables } from '../../orm/mutation'; +export type { RegisterPayloadSelect } from '../../orm/input-types'; + +const defaultSelect = { token: true } as const; + +export function useRegisterMutation( + args?: { select?: DeepExact }, + options?: Omit }, Error, RegisterVariables>, 'mutationFn'> +) { return useMutation({ mutationKey: customMutationKeys.register(), - mutationFn: (variables: RegisterMutationVariables) => execute(registerMutationDocument, variables), - ...options + mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); -}" +} +" `; exports[`Custom Mutation Hook Generators generateCustomMutationHook generates custom mutation hook without arguments 1`] = ` @@ -263,29 +253,28 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { LogoutPayload } from "../schema-types"; -import { customMutationKeys } from "../mutation-keys"; -/** GraphQL mutation document */ -export const logoutMutationDocument = \` -mutation LogoutMutation { - logout { - success - } -} -\`; -export interface LogoutMutationResult { - logout: LogoutPayload; -} -export function useLogoutMutation(options?: Omit, 'mutationFn'>) { +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { customMutationKeys } from '../mutation-keys'; +import type { LogoutPayloadSelect, LogoutPayload } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { LogoutPayloadSelect } from '../../orm/input-types'; + +const defaultSelect = { success: true } as const; + +export function useLogoutMutation( + args?: { select?: DeepExact }, + options?: Omit }, Error, void>, 'mutationFn'> +) { return useMutation({ mutationKey: customMutationKeys.logout(), - mutationFn: () => execute(logoutMutationDocument), - ...options + mutationFn: () => getClient().mutation.logout({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); -}" +} +" `; exports[`Custom Mutation Hook Generators generateCustomMutationHook generates custom mutation hook without centralized keys 1`] = ` @@ -295,31 +284,28 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { LoginPayload } from "../schema-types"; -/** GraphQL mutation document */ -export const loginMutationDocument = \` -mutation LoginMutation($email: String!, $password: String!) { - login(email: $email, password: $password) { - token - } -} -\`; -export interface LoginMutationVariables { - email: string; - password: string; -} -export interface LoginMutationResult { - login: LoginPayload; -} -export function useLoginMutation(options?: Omit, 'mutationFn'>) { +import { useMutation } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { LoginVariables } from '../../orm/mutation'; +import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { LoginVariables } from '../../orm/mutation'; +export type { LoginPayloadSelect } from '../../orm/input-types'; + +const defaultSelect = { token: true } as const; + +export function useLoginMutation( + args?: { select?: DeepExact }, + options?: Omit }, Error, LoginVariables>, 'mutationFn'> +) { return useMutation({ - mutationFn: (variables: LoginMutationVariables) => execute(loginMutationDocument, variables), - ...options + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); -}" +} +" `; exports[`Custom Query Hook Generators generateCustomQueryHook generates custom query hook with arguments 1`] = ` @@ -329,72 +315,81 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User } from "../schema-types"; -import { customQueryKeys } from "../query-keys"; -/** GraphQL query document */ -export const searchUsersQueryDocument = \` -query SearchUsersQuery($query: String!, $limit: Int) { - searchUsers(query: $query, limit: $limit) -} -\`; -export interface SearchUsersQueryVariables { - query: string; - limit?: number; -} -export interface SearchUsersQueryResult { - searchUsers: User[]; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { customQueryKeys } from '../query-keys'; +import type { SearchUsersVariables } from '../../orm/query'; +import type { UserSelect, User } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { SearchUsersVariables } from '../../orm/query'; +export type { UserSelect } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const searchUsersQueryKey = customQueryKeys.searchUsers; + /** * Search users by name or email - * + * * @example * \`\`\`tsx * const { data, isLoading } = useSearchUsersQuery({ query, limit }); - * + * * if (data?.searchUsers) { * console.log(data.searchUsers); * } * \`\`\` */ -export function useSearchUsersQuery(variables: SearchUsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) { +export function useSearchUsersQuery( + variables: SearchUsersVariables, + args?: { select?: DeepExact }, + options?: Omit[] }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => execute(searchUsersQueryDocument, variables), + queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), enabled: !!variables && options?.enabled !== false, - ...options + ...options, }); } + /** * Fetch searchUsers without React hooks - * + * * @example * \`\`\`ts * const data = await fetchSearchUsersQuery({ query, limit }); * \`\`\` */ -export async function fetchSearchUsersQuery(variables: SearchUsersQueryVariables, options?: ExecuteOptions): Promise { - return execute(searchUsersQueryDocument, variables, options); +export async function fetchSearchUsersQuery( + variables: SearchUsersVariables, + args?: { select?: DeepExact } +) { + return getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); } + /** * Prefetch searchUsers for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchSearchUsersQuery(queryClient, { query, limit }); * \`\`\` */ -export async function prefetchSearchUsersQuery(queryClient: QueryClient, variables: SearchUsersQueryVariables, options?: ExecuteOptions): Promise { +export async function prefetchSearchUsersQuery( + queryClient: QueryClient, + variables: SearchUsersVariables, + args?: { select?: DeepExact } +): Promise { await queryClient.prefetchQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => execute(searchUsersQueryDocument, variables, options) + queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), }); -}" +} +" `; exports[`Custom Query Hook Generators generateCustomQueryHook generates custom query hook without arguments 1`] = ` @@ -404,67 +399,75 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User } from "../schema-types"; -import { customQueryKeys } from "../query-keys"; -/** GraphQL query document */ -export const currentUserQueryDocument = \` -query CurrentUserQuery { - currentUser -} -\`; -export interface CurrentUserQueryResult { - currentUser: User; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { customQueryKeys } from '../query-keys'; +import type { UserSelect, User } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { UserSelect } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const currentUserQueryKey = customQueryKeys.currentUser; + /** * Get the current authenticated user - * + * * @example * \`\`\`tsx * const { data, isLoading } = useCurrentUserQuery(); - * + * * if (data?.currentUser) { * console.log(data.currentUser); * } * \`\`\` */ -export function useCurrentUserQuery(options?: Omit, 'queryKey' | 'queryFn'>) { +export function useCurrentUserQuery( + args?: { select?: DeepExact }, + options?: Omit }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => execute(currentUserQueryDocument), - ...options + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); } + /** * Fetch currentUser without React hooks - * + * * @example * \`\`\`ts * const data = await fetchCurrentUserQuery(); * \`\`\` */ -export async function fetchCurrentUserQuery(options?: ExecuteOptions): Promise { - return execute(currentUserQueryDocument, undefined, options); +export async function fetchCurrentUserQuery( + args?: { select?: DeepExact } +) { + return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); } + /** * Prefetch currentUser for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchCurrentUserQuery(queryClient); * \`\`\` */ -export async function prefetchCurrentUserQuery(queryClient: QueryClient, options?: ExecuteOptions): Promise { +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + args?: { select?: DeepExact } +): Promise { await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => execute(currentUserQueryDocument, undefined, options) + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), }); -}" +} +" `; exports[`Custom Query Hook Generators generateCustomQueryHook generates custom query hook without centralized keys 1`] = ` @@ -474,66 +477,74 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User } from "../schema-types"; -/** GraphQL query document */ -export const currentUserQueryDocument = \` -query CurrentUserQuery { - currentUser -} -\`; -export interface CurrentUserQueryResult { - currentUser: User; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { UserSelect, User } from '../../orm/input-types'; +import type { DeepExact, InferSelectResult } from '../../orm/select-types'; + +export type { UserSelect } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory for caching */ -export const currentUserQueryKey = () => ["currentUser"] as const; +export const currentUserQueryKey = () => ['currentUser'] as const; + /** * Get the current authenticated user - * + * * @example * \`\`\`tsx * const { data, isLoading } = useCurrentUserQuery(); - * + * * if (data?.currentUser) { * console.log(data.currentUser); * } * \`\`\` */ -export function useCurrentUserQuery(options?: Omit, 'queryKey' | 'queryFn'>) { +export function useCurrentUserQuery( + args?: { select?: DeepExact }, + options?: Omit }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => execute(currentUserQueryDocument), - ...options + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + ...options, }); } + /** * Fetch currentUser without React hooks - * + * * @example * \`\`\`ts * const data = await fetchCurrentUserQuery(); * \`\`\` */ -export async function fetchCurrentUserQuery(options?: ExecuteOptions): Promise { - return execute(currentUserQueryDocument, undefined, options); +export async function fetchCurrentUserQuery( + args?: { select?: DeepExact } +) { + return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); } + /** * Prefetch currentUser for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchCurrentUserQuery(queryClient); * \`\`\` */ -export async function prefetchCurrentUserQuery(queryClient: QueryClient, options?: ExecuteOptions): Promise { +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + args?: { select?: DeepExact } +): Promise { await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => execute(currentUserQueryDocument, undefined, options) + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), }); -}" +} +" `; exports[`Mutation Hook Generators generateCreateMutationHook generates create mutation hook for simple table 1`] = ` @@ -543,69 +554,52 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { User } from "../types"; -import { userKeys } from "../query-keys"; -import { userMutationKeys } from "../mutation-keys"; -export type { User } from "../types"; -export const createUserMutationDocument = \` -mutation CreateUserMutation($input: CreateUserInput!) { - createUser(input: $input) { - user { - id - email - name - createdAt - } - } -} -\`; -/** Input type for creating a User */ -interface UserCreateInput { - email?: string | null; - name?: string | null; -} -export interface CreateUserMutationVariables { - input: { - user: UserCreateInput; - }; -} -export interface CreateUserMutationResult { - createUser: { - user: User; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { userKeys } from '../query-keys'; +import { userMutationKeys } from '../mutation-keys'; +import type { + UserSelect, + UserWithRelations, + CreateUserInput, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for creating a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useCreateUserMutation(); - * - * mutate({ - * input: { - * user: { - * // ... fields - * }, - * }, + * const { mutate, isPending } = useCreateUserMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation(options?: Omit, 'mutationFn'>) { +export function useCreateUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.create(), - mutationFn: (variables: CreateUserMutationVariables) => execute(createUserMutationDocument, variables), + mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: userKeys.lists() - }); + queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateCreateMutationHook generates create mutation hook for table with relationships 1`] = ` @@ -615,74 +609,52 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { Post } from "../types"; -import { postKeys } from "../query-keys"; -import type { PostScope } from "../query-keys"; -import { postMutationKeys } from "../mutation-keys"; -export type { Post } from "../types"; -export const createPostMutationDocument = \` -mutation CreatePostMutation($input: CreatePostInput!) { - createPost(input: $input) { - post { - id - title - content - authorId - published - createdAt - } - } -} -\`; -/** Input type for creating a Post */ -interface PostCreateInput { - title?: string | null; - content?: string | null; - authorId?: string | null; - published?: boolean | null; -} -export interface CreatePostMutationVariables { - input: { - post: PostCreateInput; - }; -} -export interface CreatePostMutationResult { - createPost: { - post: Post; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { postKeys } from '../query-keys'; +import { postMutationKeys } from '../mutation-keys'; +import type { + PostSelect, + PostWithRelations, + CreatePostInput, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { PostSelect, PostWithRelations, CreatePostInput } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for creating a Post - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useCreatePostMutation(); - * - * mutate({ - * input: { - * post: { - * // ... fields - * }, - * }, + * const { mutate, isPending } = useCreatePostMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreatePostMutation(options?: Omit, 'mutationFn'>) { +export function useCreatePostMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.create(), - mutationFn: (variables: CreatePostMutationVariables) => execute(createPostMutationDocument, variables), + mutationFn: (data: CreatePostInput['post']) => getClient().post.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: postKeys.lists() - }); + queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateCreateMutationHook generates create mutation hook without centralized keys 1`] = ` @@ -692,66 +664,49 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { User } from "../types"; -export type { User } from "../types"; -export const createUserMutationDocument = \` -mutation CreateUserMutation($input: CreateUserInput!) { - createUser(input: $input) { - user { - id - email - name - createdAt - } - } -} -\`; -/** Input type for creating a User */ -interface UserCreateInput { - email?: string | null; - name?: string | null; -} -export interface CreateUserMutationVariables { - input: { - user: UserCreateInput; - }; -} -export interface CreateUserMutationResult { - createUser: { - user: User; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { + UserSelect, + UserWithRelations, + CreateUserInput, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for creating a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useCreateUserMutation(); - * - * mutate({ - * input: { - * user: { - * // ... fields - * }, - * }, + * const { mutate, isPending } = useCreateUserMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation(options?: Omit, 'mutationFn'>) { +export function useCreateUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (variables: CreateUserMutationVariables) => execute(createUserMutationDocument, variables), + mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: ["user", "list"] - }); + queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mutation hook for simple table 1`] = ` @@ -761,58 +716,52 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import { userKeys } from "../query-keys"; -import { userMutationKeys } from "../mutation-keys"; -export const deleteUserMutationDocument = \` -mutation DeleteUserMutation($input: DeleteUserInput!) { - deleteUser(input: $input) { - clientMutationId - } -} -\`; -export interface DeleteUserMutationVariables { - input: { - id: string; - }; -} -export interface DeleteUserMutationResult { - deleteUser: { - clientMutationId: string | null; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { userKeys } from '../query-keys'; +import { userMutationKeys } from '../mutation-keys'; +import type { + UserSelect, + UserWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for deleting a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useDeleteUserMutation(); - * - * mutate({ - * input: { - * id: 'value-to-delete', - * }, + * const { mutate, isPending } = useDeleteUserMutation({ + * select: { id: true }, * }); + * + * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation(options?: Omit, 'mutationFn'>) { +export function useDeleteUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: (variables: DeleteUserMutationVariables) => execute(deleteUserMutationDocument, variables), + mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ - queryKey: userKeys.detail(variables.input.id) - }); - queryClient.invalidateQueries({ - queryKey: userKeys.lists() - }); + queryClient.removeQueries({ queryKey: userKeys.detail(variables.id) }); + queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mutation hook for table with relationships 1`] = ` @@ -822,59 +771,52 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import { postKeys } from "../query-keys"; -import type { PostScope } from "../query-keys"; -import { postMutationKeys } from "../mutation-keys"; -export const deletePostMutationDocument = \` -mutation DeletePostMutation($input: DeletePostInput!) { - deletePost(input: $input) { - clientMutationId - } -} -\`; -export interface DeletePostMutationVariables { - input: { - id: string; - }; -} -export interface DeletePostMutationResult { - deletePost: { - clientMutationId: string | null; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { postKeys } from '../query-keys'; +import { postMutationKeys } from '../mutation-keys'; +import type { + PostSelect, + PostWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { PostSelect, PostWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for deleting a Post - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useDeletePostMutation(); - * - * mutate({ - * input: { - * id: 'value-to-delete', - * }, + * const { mutate, isPending } = useDeletePostMutation({ + * select: { id: true }, * }); + * + * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeletePostMutation(options?: Omit, 'mutationFn'>) { +export function useDeletePostMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: (variables: DeletePostMutationVariables) => execute(deletePostMutationDocument, variables), + mutationFn: ({ id }: { id: string }) => getClient().post.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ - queryKey: postKeys.detail(variables.input.id) - }); - queryClient.invalidateQueries({ - queryKey: postKeys.lists() - }); + queryClient.removeQueries({ queryKey: postKeys.detail(variables.id) }); + queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mutation hook without centralized keys 1`] = ` @@ -884,55 +826,49 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -export const deleteUserMutationDocument = \` -mutation DeleteUserMutation($input: DeleteUserInput!) { - deleteUser(input: $input) { - clientMutationId - } -} -\`; -export interface DeleteUserMutationVariables { - input: { - id: string; - }; -} -export interface DeleteUserMutationResult { - deleteUser: { - clientMutationId: string | null; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { + UserSelect, + UserWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for deleting a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useDeleteUserMutation(); - * - * mutate({ - * input: { - * id: 'value-to-delete', - * }, + * const { mutate, isPending } = useDeleteUserMutation({ + * select: { id: true }, * }); + * + * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation(options?: Omit, 'mutationFn'>) { +export function useDeleteUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (variables: DeleteUserMutationVariables) => execute(deleteUserMutationDocument, variables), + mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ - queryKey: ["user", "detail", variables.input.id] - }); - queryClient.invalidateQueries({ - queryKey: ["user", "list"] - }); + queryClient.removeQueries({ queryKey: ['user', 'detail', variables.id] }); + queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateUpdateMutationHook generates update mutation hook for simple table 1`] = ` @@ -942,75 +878,53 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { User } from "../types"; -import { userKeys } from "../query-keys"; -import { userMutationKeys } from "../mutation-keys"; -export type { User } from "../types"; -export const updateUserMutationDocument = \` -mutation UpdateUserMutation($input: UpdateUserInput!) { - updateUser(input: $input) { - user { - id - email - name - createdAt - } - } -} -\`; -/** Patch type for updating a User - all fields optional */ -interface UserPatch { - email?: string | null; - name?: string | null; - createdAt?: string | null; -} -export interface UpdateUserMutationVariables { - input: { - id: string; - patch: UserPatch; - }; -} -export interface UpdateUserMutationResult { - updateUser: { - user: User; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { userKeys } from '../query-keys'; +import { userMutationKeys } from '../mutation-keys'; +import type { + UserSelect, + UserWithRelations, + UserPatch, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for updating a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useUpdateUserMutation(); - * - * mutate({ - * input: { - * id: 'value-here', - * patch: { - * // ... fields to update - * }, - * }, + * const { mutate, isPending } = useUpdateUserMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation(options?: Omit, 'mutationFn'>) { +export function useUpdateUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: (variables: UpdateUserMutationVariables) => execute(updateUserMutationDocument, variables), + mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: userKeys.detail(variables.input.id) - }); - queryClient.invalidateQueries({ - queryKey: userKeys.lists() - }); + queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) }); + queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateUpdateMutationHook generates update mutation hook for table with relationships 1`] = ` @@ -1020,80 +934,53 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { Post } from "../types"; -import { postKeys } from "../query-keys"; -import type { PostScope } from "../query-keys"; -import { postMutationKeys } from "../mutation-keys"; -export type { Post } from "../types"; -export const updatePostMutationDocument = \` -mutation UpdatePostMutation($input: UpdatePostInput!) { - updatePost(input: $input) { - post { - id - title - content - authorId - published - createdAt - } - } -} -\`; -/** Patch type for updating a Post - all fields optional */ -interface PostPatch { - title?: string | null; - content?: string | null; - authorId?: string | null; - published?: boolean | null; - createdAt?: string | null; -} -export interface UpdatePostMutationVariables { - input: { - id: string; - patch: PostPatch; - }; -} -export interface UpdatePostMutationResult { - updatePost: { - post: Post; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { postKeys } from '../query-keys'; +import { postMutationKeys } from '../mutation-keys'; +import type { + PostSelect, + PostWithRelations, + PostPatch, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { PostSelect, PostWithRelations, PostPatch } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for updating a Post - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useUpdatePostMutation(); - * - * mutate({ - * input: { - * id: 'value-here', - * patch: { - * // ... fields to update - * }, - * }, + * const { mutate, isPending } = useUpdatePostMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdatePostMutation(options?: Omit, 'mutationFn'>) { +export function useUpdatePostMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: (variables: UpdatePostMutationVariables) => execute(updatePostMutationDocument, variables), + mutationFn: ({ id, patch }: { id: string; patch: PostPatch }) => getClient().post.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: postKeys.detail(variables.input.id) - }); - queryClient.invalidateQueries({ - queryKey: postKeys.lists() - }); + queryClient.invalidateQueries({ queryKey: postKeys.detail(variables.id) }); + queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options + ...options, }); -}" +} +" `; exports[`Mutation Hook Generators generateUpdateMutationHook generates update mutation hook without centralized keys 1`] = ` @@ -1103,72 +990,50 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import type { UseMutationOptions } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { User } from "../types"; -export type { User } from "../types"; -export const updateUserMutationDocument = \` -mutation UpdateUserMutation($input: UpdateUserInput!) { - updateUser(input: $input) { - user { - id - email - name - createdAt - } - } -} -\`; -/** Patch type for updating a User - all fields optional */ -interface UserPatch { - email?: string | null; - name?: string | null; - createdAt?: string | null; -} -export interface UpdateUserMutationVariables { - input: { - id: string; - patch: UserPatch; - }; -} -export interface UpdateUserMutationResult { - updateUser: { - user: User; - }; -} +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import type { UseMutationOptions } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { + UserSelect, + UserWithRelations, + UserPatch, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** * Mutation hook for updating a User - * + * * @example * \`\`\`tsx - * const { mutate, isPending } = useUpdateUserMutation(); - * - * mutate({ - * input: { - * id: 'value-here', - * patch: { - * // ... fields to update - * }, - * }, + * const { mutate, isPending } = useUpdateUserMutation({ + * select: { id: true, name: true }, * }); + * + * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation(options?: Omit, 'mutationFn'>) { +export function useUpdateUserMutation( + args?: { select?: DeepExact }, + options?: Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +) { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (variables: UpdateUserMutationVariables) => execute(updateUserMutationDocument, variables), + mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ - queryKey: ["user", "detail", variables.input.id] - }); - queryClient.invalidateQueries({ - queryKey: ["user", "list"] - }); + queryClient.invalidateQueries({ queryKey: ['user', 'detail', variables.id] }); + queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options + ...options, }); -}" +} +" `; exports[`Query Hook Generators generateListQueryHook generates list query hook for simple table 1`] = ` @@ -1178,132 +1043,86 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User, UUIDFilter, StringFilter, DatetimeFilter } from "../types"; -import { userKeys } from "../query-keys"; -export type { User } from "../types"; -export const usersQueryDocument = \` -query UsersQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: UserFilter, $condition: UserCondition, $orderBy: [UsersOrderBy!]) { - users( - first: $first - last: $last - offset: $offset - before: $before - after: $after - filter: $filter - condition: $condition - orderBy: $orderBy - ) { - totalCount - nodes { - id - email - name - createdAt - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -\`; -interface UserFilter { - id?: UUIDFilter; - email?: StringFilter; - name?: StringFilter; - createdAt?: DatetimeFilter; - and?: UserFilter[]; - or?: UserFilter[]; - not?: UserFilter; -} -interface UserCondition { - id?: string; - email?: string; - name?: string; - createdAt?: string; -} -type UsersOrderBy = "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC"; -export interface UsersQueryVariables { - first?: number; - last?: number; - offset?: number; - before?: string; - after?: string; - filter?: UserFilter; - condition?: UserCondition; - orderBy?: UsersOrderBy[]; -} -export interface UsersQueryResult { - users: { - totalCount: number; - nodes: User[]; - pageInfo: { - hasNextPage: boolean; - hasPreviousPage: boolean; - startCursor: string | null; - endCursor: string | null; - }; - }; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { userKeys } from '../query-keys'; +import type { + UserSelect, + UserWithRelations, + UserFilter, + UsersOrderBy, +} from '../../orm/input-types'; +import type { + FindManyArgs, + DeepExact, + InferSelectResult, + ConnectionResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const usersQueryKey = userKeys.list; + /** * Query hook for fetching User list - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUsersQuery({ + * select: { id: true, name: true }, * first: 10, - * filter: { name: { equalTo: "example" } }, + * where: { name: { equalTo: "example" } }, * orderBy: ['CREATED_AT_DESC'], * }); * \`\`\` */ -export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) { +export function useUsersQuery( + args?: FindManyArgs, UserFilter, UsersOrderBy>, + options?: Omit> }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ - queryKey: userKeys.list(variables), - queryFn: () => execute(usersQueryDocument, variables), - ...options + queryKey: userKeys.list(args), + queryFn: () => getClient().user.findMany(args).unwrap(), + ...options, }); } + /** * Fetch User list without React hooks - * + * * @example * \`\`\`ts - * // Direct fetch - * const data = await fetchUsersQuery({ first: 10 }); - * - * // With QueryClient - * const data = await queryClient.fetchQuery({ - * queryKey: usersQueryKey(variables), - * queryFn: () => fetchUsersQuery(variables), - * }); + * const data = await fetchUsersQuery({ first: 10, select: { id: true } }); * \`\`\` */ -export async function fetchUsersQuery(variables?: UsersQueryVariables, options?: ExecuteOptions): Promise { - return execute(usersQueryDocument, variables, options); +export async function fetchUsersQuery( + args?: FindManyArgs, UserFilter, UsersOrderBy>, +) { + return getClient().user.findMany(args).unwrap(); } + /** * Prefetch User list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUsersQuery(queryClient, { first: 10 }); * \`\`\` */ -export async function prefetchUsersQuery(queryClient: QueryClient, variables?: UsersQueryVariables, options?: ExecuteOptions): Promise { +export async function prefetchUsersQuery( + queryClient: QueryClient, + args?: FindManyArgs, UserFilter, UsersOrderBy>, +): Promise { await queryClient.prefetchQuery({ - queryKey: userKeys.list(variables), - queryFn: () => execute(usersQueryDocument, variables, options) + queryKey: userKeys.list(args), + queryFn: () => getClient().user.findMany(args).unwrap(), }); -}" +} +" `; exports[`Query Hook Generators generateListQueryHook generates list query hook for table with relationships 1`] = ` @@ -1313,100 +1132,44 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { Post, UUIDFilter, StringFilter, BooleanFilter, DatetimeFilter } from "../types"; -import { postKeys } from "../query-keys"; -import type { PostScope } from "../query-keys"; -export type { Post } from "../types"; -export const postsQueryDocument = \` -query PostsQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: PostFilter, $condition: PostCondition, $orderBy: [PostsOrderBy!]) { - posts( - first: $first - last: $last - offset: $offset - before: $before - after: $after - filter: $filter - condition: $condition - orderBy: $orderBy - ) { - totalCount - nodes { - id - title - content - authorId - published - createdAt - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -\`; -interface PostFilter { - id?: UUIDFilter; - title?: StringFilter; - content?: StringFilter; - authorId?: UUIDFilter; - published?: BooleanFilter; - createdAt?: DatetimeFilter; - and?: PostFilter[]; - or?: PostFilter[]; - not?: PostFilter; -} -interface PostCondition { - id?: string; - title?: string; - content?: string; - authorId?: string; - published?: boolean; - createdAt?: string; -} -type PostsOrderBy = "ID_ASC" | "ID_DESC" | "TITLE_ASC" | "TITLE_DESC" | "CONTENT_ASC" | "CONTENT_DESC" | "AUTHOR_ID_ASC" | "AUTHOR_ID_DESC" | "PUBLISHED_ASC" | "PUBLISHED_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC"; -export interface PostsQueryVariables { - first?: number; - last?: number; - offset?: number; - before?: string; - after?: string; - filter?: PostFilter; - condition?: PostCondition; - orderBy?: PostsOrderBy[]; -} -export interface PostsQueryResult { - posts: { - totalCount: number; - nodes: Post[]; - pageInfo: { - hasNextPage: boolean; - hasPreviousPage: boolean; - startCursor: string | null; - endCursor: string | null; - }; - }; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { postKeys } from '../query-keys'; +import type { PostScope } from '../query-keys'; +import type { + PostSelect, + PostWithRelations, + PostFilter, + PostsOrderBy, +} from '../../orm/input-types'; +import type { + FindManyArgs, + DeepExact, + InferSelectResult, + ConnectionResult, +} from '../../orm/select-types'; + +export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const postsQueryKey = postKeys.list; + /** * Query hook for fetching Post list - * + * * @example * \`\`\`tsx * const { data, isLoading } = usePostsQuery({ + * select: { id: true, name: true }, * first: 10, - * filter: { name: { equalTo: "example" } }, + * where: { name: { equalTo: "example" } }, * orderBy: ['CREATED_AT_DESC'], * }); * \`\`\` - * + * * @example With scope for hierarchical cache invalidation * \`\`\`tsx * const { data } = usePostsQuery( @@ -1415,49 +1178,51 @@ export const postsQueryKey = postKeys.list; * ); * \`\`\` */ -export function usePostsQuery(variables?: PostsQueryVariables, options?: Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope }) { - const { - scope, - ...queryOptions - } = options ?? {}; +export function usePostsQuery( + args?: FindManyArgs, PostFilter, PostsOrderBy>, + options?: Omit> }, Error>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +) { + const { scope, ...queryOptions } = options ?? {}; return useQuery({ - queryKey: postKeys.list(variables, scope), - queryFn: () => execute(postsQueryDocument, variables), - ...queryOptions + queryKey: postKeys.list(args, scope), + queryFn: () => getClient().post.findMany(args).unwrap(), + ...queryOptions, }); } + /** * Fetch Post list without React hooks - * + * * @example * \`\`\`ts - * // Direct fetch - * const data = await fetchPostsQuery({ first: 10 }); - * - * // With QueryClient - * const data = await queryClient.fetchQuery({ - * queryKey: postsQueryKey(variables), - * queryFn: () => fetchPostsQuery(variables), - * }); + * const data = await fetchPostsQuery({ first: 10, select: { id: true } }); * \`\`\` */ -export async function fetchPostsQuery(variables?: PostsQueryVariables, options?: ExecuteOptions): Promise { - return execute(postsQueryDocument, variables, options); +export async function fetchPostsQuery( + args?: FindManyArgs, PostFilter, PostsOrderBy>, +) { + return getClient().post.findMany(args).unwrap(); } + /** * Prefetch Post list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchPostsQuery(queryClient, { first: 10 }); * \`\`\` */ -export async function prefetchPostsQuery(queryClient: QueryClient, variables?: PostsQueryVariables, scope?: PostScope, options?: ExecuteOptions): Promise { +export async function prefetchPostsQuery( + queryClient: QueryClient, + args?: FindManyArgs, PostFilter, PostsOrderBy>, + scope?: PostScope, +): Promise { await queryClient.prefetchQuery({ - queryKey: postKeys.list(variables, scope), - queryFn: () => execute(postsQueryDocument, variables, options) + queryKey: postKeys.list(args, scope), + queryFn: () => getClient().post.findMany(args).unwrap(), }); -}" +} +" `; exports[`Query Hook Generators generateListQueryHook generates list query hook without centralized keys 1`] = ` @@ -1467,130 +1232,84 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook w * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User, UUIDFilter, StringFilter, DatetimeFilter } from "../types"; -export type { User } from "../types"; -export const usersQueryDocument = \` -query UsersQuery($first: Int, $last: Int, $offset: Int, $before: Cursor, $after: Cursor, $filter: UserFilter, $condition: UserCondition, $orderBy: [UsersOrderBy!]) { - users( - first: $first - last: $last - offset: $offset - before: $before - after: $after - filter: $filter - condition: $condition - orderBy: $orderBy - ) { - totalCount - nodes { - id - email - name - createdAt - } - pageInfo { - hasNextPage - hasPreviousPage - startCursor - endCursor - } - } -} -\`; -interface UserFilter { - id?: UUIDFilter; - email?: StringFilter; - name?: StringFilter; - createdAt?: DatetimeFilter; - and?: UserFilter[]; - or?: UserFilter[]; - not?: UserFilter; -} -interface UserCondition { - id?: string; - email?: string; - name?: string; - createdAt?: string; -} -type UsersOrderBy = "ID_ASC" | "ID_DESC" | "EMAIL_ASC" | "EMAIL_DESC" | "NAME_ASC" | "NAME_DESC" | "CREATED_AT_ASC" | "CREATED_AT_DESC" | "NATURAL" | "PRIMARY_KEY_ASC" | "PRIMARY_KEY_DESC"; -export interface UsersQueryVariables { - first?: number; - last?: number; - offset?: number; - before?: string; - after?: string; - filter?: UserFilter; - condition?: UserCondition; - orderBy?: UsersOrderBy[]; -} -export interface UsersQueryResult { - users: { - totalCount: number; - nodes: User[]; - pageInfo: { - hasNextPage: boolean; - hasPreviousPage: boolean; - startCursor: string | null; - endCursor: string | null; - }; - }; -} -export const usersQueryKey = (variables?: UsersQueryVariables) => ["user", "list", variables] as const; +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { + UserSelect, + UserWithRelations, + UserFilter, + UsersOrderBy, +} from '../../orm/input-types'; +import type { + FindManyArgs, + DeepExact, + InferSelectResult, + ConnectionResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + +export const usersQueryKey = (variables?: FindManyArgs) => ['user', 'list', variables] as const; + /** * Query hook for fetching User list - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUsersQuery({ + * select: { id: true, name: true }, * first: 10, - * filter: { name: { equalTo: "example" } }, + * where: { name: { equalTo: "example" } }, * orderBy: ['CREATED_AT_DESC'], * }); * \`\`\` */ -export function useUsersQuery(variables?: UsersQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) { +export function useUsersQuery( + args?: FindManyArgs, UserFilter, UsersOrderBy>, + options?: Omit> }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ - queryKey: usersQueryKey(variables), - queryFn: () => execute(usersQueryDocument, variables), - ...options + queryKey: usersQueryKey(args), + queryFn: () => getClient().user.findMany(args).unwrap(), + ...options, }); } + /** * Fetch User list without React hooks - * + * * @example * \`\`\`ts - * // Direct fetch - * const data = await fetchUsersQuery({ first: 10 }); - * - * // With QueryClient - * const data = await queryClient.fetchQuery({ - * queryKey: usersQueryKey(variables), - * queryFn: () => fetchUsersQuery(variables), - * }); + * const data = await fetchUsersQuery({ first: 10, select: { id: true } }); * \`\`\` */ -export async function fetchUsersQuery(variables?: UsersQueryVariables, options?: ExecuteOptions): Promise { - return execute(usersQueryDocument, variables, options); +export async function fetchUsersQuery( + args?: FindManyArgs, UserFilter, UsersOrderBy>, +) { + return getClient().user.findMany(args).unwrap(); } + /** * Prefetch User list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUsersQuery(queryClient, { first: 10 }); * \`\`\` */ -export async function prefetchUsersQuery(queryClient: QueryClient, variables?: UsersQueryVariables, options?: ExecuteOptions): Promise { +export async function prefetchUsersQuery( + queryClient: QueryClient, + args?: FindManyArgs, UserFilter, UsersOrderBy>, +): Promise { await queryClient.prefetchQuery({ - queryKey: usersQueryKey(variables), - queryFn: () => execute(usersQueryDocument, variables, options) + queryKey: usersQueryKey(args), + queryFn: () => getClient().user.findMany(args).unwrap(), }); -}" +} +" `; exports[`Query Hook Generators generateSingleQueryHook generates single query hook for simple table 1`] = ` @@ -1600,71 +1319,80 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User } from "../types"; -import { userKeys } from "../query-keys"; -export type { User } from "../types"; -export const userQueryDocument = \` -query UserQuery($id: UUID!) { - user(id: $id) { - id - email - name - createdAt - } -} -\`; -export interface UserQueryVariables { - id: string; -} -export interface UserQueryResult { - user: User | null; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { userKeys } from '../query-keys'; +import type { + UserSelect, + UserWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const userQueryKey = userKeys.detail; + /** * Query hook for fetching a single User - * + * * @example * \`\`\`tsx - * const { data, isLoading } = useUserQuery({ id: 'some-id' }); + * const { data, isLoading } = useUserQuery({ + * id: 'some-id', + * select: { id: true, name: true }, + * }); * \`\`\` */ -export function useUserQuery(variables: UserQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) { +export function useUserQuery( + args: { id: string; select?: DeepExact }, + options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ - queryKey: userKeys.detail(variables.id), - queryFn: () => execute(userQueryDocument, variables), - ...options + queryKey: userKeys.detail(args.id), + queryFn: () => getClient().user.findOne(args).unwrap(), + ...options, }); } + /** * Fetch a single User without React hooks - * + * * @example * \`\`\`ts - * const data = await fetchUserQuery({ id: 'some-id' }); + * const data = await fetchUserQuery({ id: 'some-id', select: { id: true } }); * \`\`\` */ -export async function fetchUserQuery(variables: UserQueryVariables, options?: ExecuteOptions): Promise { - return execute(userQueryDocument, variables, options); +export async function fetchUserQuery( + args: { id: string; select?: DeepExact }, +) { + return getClient().user.findOne(args).unwrap(); } + /** * Prefetch a single User for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUserQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchUserQuery(queryClient: QueryClient, variables: UserQueryVariables, options?: ExecuteOptions): Promise { +export async function prefetchUserQuery( + queryClient: QueryClient, + args: { id: string; select?: DeepExact }, +): Promise { await queryClient.prefetchQuery({ - queryKey: userKeys.detail(variables.id), - queryFn: () => execute(userQueryDocument, variables, options) + queryKey: userKeys.detail(args.id), + queryFn: () => getClient().user.findOne(args).unwrap(), }); -}" +} +" `; exports[`Query Hook Generators generateSingleQueryHook generates single query hook for table with relationships 1`] = ` @@ -1674,42 +1402,38 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { Post } from "../types"; -import { postKeys } from "../query-keys"; -import type { PostScope } from "../query-keys"; -export type { Post } from "../types"; -export const postQueryDocument = \` -query PostQuery($id: UUID!) { - post(id: $id) { - id - title - content - authorId - published - createdAt - } -} -\`; -export interface PostQueryVariables { - id: string; -} -export interface PostQueryResult { - post: Post | null; -} +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import { postKeys } from '../query-keys'; +import type { PostScope } from '../query-keys'; +import type { + PostSelect, + PostWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { PostSelect, PostWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + /** Query key factory - re-exported from query-keys.ts */ export const postQueryKey = postKeys.detail; + /** * Query hook for fetching a single Post - * + * * @example * \`\`\`tsx - * const { data, isLoading } = usePostQuery({ id: 'some-id' }); + * const { data, isLoading } = usePostQuery({ + * id: 'some-id', + * select: { id: true, name: true }, + * }); * \`\`\` - * + * * @example With scope for hierarchical cache invalidation * \`\`\`tsx * const { data } = usePostQuery( @@ -1718,42 +1442,51 @@ export const postQueryKey = postKeys.detail; * ); * \`\`\` */ -export function usePostQuery(variables: PostQueryVariables, options?: Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope }) { - const { - scope, - ...queryOptions - } = options ?? {}; +export function usePostQuery( + args: { id: string; select?: DeepExact }, + options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +) { + const { scope, ...queryOptions } = options ?? {}; return useQuery({ - queryKey: postKeys.detail(variables.id, scope), - queryFn: () => execute(postQueryDocument, variables), - ...queryOptions + queryKey: postKeys.detail(args.id, scope), + queryFn: () => getClient().post.findOne(args).unwrap(), + ...queryOptions, }); } + /** * Fetch a single Post without React hooks - * + * * @example * \`\`\`ts - * const data = await fetchPostQuery({ id: 'some-id' }); + * const data = await fetchPostQuery({ id: 'some-id', select: { id: true } }); * \`\`\` */ -export async function fetchPostQuery(variables: PostQueryVariables, options?: ExecuteOptions): Promise { - return execute(postQueryDocument, variables, options); +export async function fetchPostQuery( + args: { id: string; select?: DeepExact }, +) { + return getClient().post.findOne(args).unwrap(); } + /** * Prefetch a single Post for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchPostQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchPostQuery(queryClient: QueryClient, variables: PostQueryVariables, scope?: PostScope, options?: ExecuteOptions): Promise { +export async function prefetchPostQuery( + queryClient: QueryClient, + args: { id: string; select?: DeepExact }, + scope?: PostScope, +): Promise { await queryClient.prefetchQuery({ - queryKey: postKeys.detail(variables.id, scope), - queryFn: () => execute(postQueryDocument, variables, options) + queryKey: postKeys.detail(args.id, scope), + queryFn: () => getClient().post.findOne(args).unwrap(), }); -}" +} +" `; exports[`Query Hook Generators generateSingleQueryHook generates single query hook without centralized keys 1`] = ` @@ -1763,69 +1496,78 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from "@tanstack/react-query"; -import type { UseQueryOptions, QueryClient } from "@tanstack/react-query"; -import { execute } from "../client"; -import type { ExecuteOptions } from "../client"; -import type { User } from "../types"; -export type { User } from "../types"; -export const userQueryDocument = \` -query UserQuery($id: UUID!) { - user(id: $id) { - id - email - name - createdAt - } -} -\`; -export interface UserQueryVariables { - id: string; -} -export interface UserQueryResult { - user: User | null; -} -export const userQueryKey = (id: string) => ["user", "detail", id] as const; +import { useQuery } from '@tanstack/react-query'; +import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import { getClient } from '../client'; +import type { + UserSelect, + UserWithRelations, +} from '../../orm/input-types'; +import type { + DeepExact, + InferSelectResult, +} from '../../orm/select-types'; + +export type { UserSelect, UserWithRelations } from '../../orm/input-types'; + +const defaultSelect = { id: true } as const; + +export const userQueryKey = (id: string) => ['user', 'detail', id] as const; + /** * Query hook for fetching a single User - * + * * @example * \`\`\`tsx - * const { data, isLoading } = useUserQuery({ id: 'some-id' }); + * const { data, isLoading } = useUserQuery({ + * id: 'some-id', + * select: { id: true, name: true }, + * }); * \`\`\` */ -export function useUserQuery(variables: UserQueryVariables, options?: Omit, 'queryKey' | 'queryFn'>) { +export function useUserQuery( + args: { id: string; select?: DeepExact }, + options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> +) { return useQuery({ - queryKey: userQueryKey(variables.id), - queryFn: () => execute(userQueryDocument, variables), - ...options + queryKey: userQueryKey(args.id), + queryFn: () => getClient().user.findOne(args).unwrap(), + ...options, }); } + /** * Fetch a single User without React hooks - * + * * @example * \`\`\`ts - * const data = await fetchUserQuery({ id: 'some-id' }); + * const data = await fetchUserQuery({ id: 'some-id', select: { id: true } }); * \`\`\` */ -export async function fetchUserQuery(variables: UserQueryVariables, options?: ExecuteOptions): Promise { - return execute(userQueryDocument, variables, options); +export async function fetchUserQuery( + args: { id: string; select?: DeepExact }, +) { + return getClient().user.findOne(args).unwrap(); } + /** * Prefetch a single User for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUserQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchUserQuery(queryClient: QueryClient, variables: UserQueryVariables, options?: ExecuteOptions): Promise { +export async function prefetchUserQuery( + queryClient: QueryClient, + args: { id: string; select?: DeepExact }, +): Promise { await queryClient.prefetchQuery({ - queryKey: userQueryKey(variables.id), - queryFn: () => execute(userQueryDocument, variables, options) + queryKey: userQueryKey(args.id), + queryFn: () => getClient().user.findOne(args).unwrap(), }); -}" +} +" `; exports[`Schema Types Generator generateSchemaTypesFile generates schema types file with empty table types 1`] = ` diff --git a/graphql/codegen/src/__tests__/codegen/client-generator.test.ts b/graphql/codegen/src/__tests__/codegen/client-generator.test.ts index afcf275e4..3bd0fbc02 100644 --- a/graphql/codegen/src/__tests__/codegen/client-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/client-generator.test.ts @@ -4,12 +4,12 @@ * Tests the generated ORM client files: client.ts, query-builder.ts, select-types.ts, index.ts */ import { + generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile, - generateCreateClientFile, + generateSelectTypesFile } from '../../core/codegen/orm/client-generator'; -import type { CleanTable, CleanFieldType, CleanRelations } from '../../types/schema'; +import type { CleanFieldType, CleanRelations,CleanTable } from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -17,14 +17,14 @@ import type { CleanTable, CleanFieldType, CleanRelations } from '../../types/sch const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, - string: { gqlType: 'String', isArray: false } as CleanFieldType, + string: { gqlType: 'String', isArray: false } as CleanFieldType }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -34,7 +34,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -91,13 +91,13 @@ describe('client-generator', () => { createTable({ name: 'User', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' }, + query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' } }), createTable({ name: 'Post', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', delete: 'deletePost' }, - }), + query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', delete: 'deletePost' } + }) ]; const result = generateCreateClientFile(tables, false, false); @@ -114,8 +114,8 @@ describe('client-generator', () => { createTable({ name: 'User', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' }, - }), + query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' } + }) ]; const result = generateCreateClientFile(tables, true, true); @@ -123,8 +123,8 @@ describe('client-generator', () => { expect(result.content).toMatchSnapshot(); expect(result.content).toContain('createQueryOperations'); expect(result.content).toContain('createMutationOperations'); - expect(result.content).toContain("query: createQueryOperations(client)"); - expect(result.content).toContain("mutation: createMutationOperations(client)"); + expect(result.content).toContain('query: createQueryOperations(client)'); + expect(result.content).toContain('mutation: createMutationOperations(client)'); }); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/format-output.test.ts b/graphql/codegen/src/__tests__/codegen/format-output.test.ts index bb8b87099..35169f7fb 100644 --- a/graphql/codegen/src/__tests__/codegen/format-output.test.ts +++ b/graphql/codegen/src/__tests__/codegen/format-output.test.ts @@ -3,8 +3,9 @@ * Verifies that oxfmt formats generated code correctly */ import * as fs from 'node:fs'; -import * as path from 'node:path'; import * as os from 'node:os'; +import * as path from 'node:path'; + import { formatOutput } from '../../core/output'; describe('formatOutput', () => { diff --git a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts index d509456fa..241a7ae69 100644 --- a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts @@ -6,15 +6,15 @@ * used to validate the AST-based migration produces equivalent results. */ // Jest globals - no import needed -import { generateInputTypesFile, collectInputTypeNames, collectPayloadTypeNames } from '../../core/codegen/orm/input-types-generator'; +import { collectInputTypeNames, collectPayloadTypeNames,generateInputTypesFile } from '../../core/codegen/orm/input-types-generator'; import type { - CleanTable, + CleanArgument, CleanFieldType, CleanRelations, - TypeRegistry, - ResolvedType, - CleanArgument, + CleanTable, CleanTypeRef, + ResolvedType, + TypeRegistry } from '../../types/schema'; // ============================================================================ @@ -32,7 +32,7 @@ const fieldTypes = { json: { gqlType: 'JSON', isArray: false } as CleanFieldType, bigint: { gqlType: 'BigInt', isArray: false } as CleanFieldType, stringArray: { gqlType: 'String', isArray: true } as CleanFieldType, - intArray: { gqlType: 'Int', isArray: true } as CleanFieldType, + intArray: { gqlType: 'Int', isArray: true } as CleanFieldType }; // ============================================================================ @@ -43,7 +43,7 @@ const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -53,7 +53,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -89,15 +89,15 @@ const userTable = createTable({ { name: 'age', type: fieldTypes.int }, { name: 'isActive', type: fieldTypes.boolean }, { name: 'createdAt', type: fieldTypes.datetime }, - { name: 'metadata', type: fieldTypes.json }, + { name: 'metadata', type: fieldTypes.json } ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser', - }, + delete: 'deleteUser' + } }); /** @@ -111,7 +111,7 @@ const postTable = createTable({ { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, { name: 'publishedAt', type: fieldTypes.datetime }, - { name: 'tags', type: fieldTypes.stringArray }, + { name: 'tags', type: fieldTypes.stringArray } ], relations: { belongsTo: [ @@ -120,8 +120,8 @@ const postTable = createTable({ isUnique: false, referencesTable: 'User', type: null, - keys: [{ name: 'authorId', type: fieldTypes.uuid }], - }, + keys: [{ name: 'authorId', type: fieldTypes.uuid }] + } ], hasOne: [], hasMany: [ @@ -130,18 +130,18 @@ const postTable = createTable({ isUnique: false, referencedByTable: 'Comment', type: null, - keys: [], - }, + keys: [] + } ], - manyToMany: [], + manyToMany: [] }, query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost', - }, + delete: 'deletePost' + } }); /** @@ -154,7 +154,7 @@ const commentTable = createTable({ { name: 'body', type: fieldTypes.string }, { name: 'postId', type: fieldTypes.uuid }, { name: 'authorId', type: fieldTypes.uuid }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], relations: { belongsTo: [ @@ -163,27 +163,27 @@ const commentTable = createTable({ isUnique: false, referencesTable: 'Post', type: null, - keys: [], + keys: [] }, { fieldName: 'author', isUnique: false, referencesTable: 'User', type: null, - keys: [], - }, + keys: [] + } ], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }, query: { all: 'comments', one: 'comment', create: 'createComment', update: 'updateComment', - delete: 'deleteComment', - }, + delete: 'deleteComment' + } }); /** @@ -200,18 +200,18 @@ const userTableWithRelations = createTable({ isUnique: false, referencedByTable: 'Post', type: null, - keys: [], + keys: [] }, { fieldName: 'comments', isUnique: false, referencedByTable: 'Comment', type: null, - keys: [], - }, + keys: [] + } ], - manyToMany: [], - }, + manyToMany: [] + } }); /** @@ -222,7 +222,7 @@ const categoryTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'slug', type: fieldTypes.string }, + { name: 'slug', type: fieldTypes.string } ], relations: { belongsTo: [], @@ -233,17 +233,17 @@ const categoryTable = createTable({ fieldName: 'posts', rightTable: 'Post', junctionTable: 'PostCategory', - type: null, - }, - ], + type: null + } + ] }, query: { all: 'categories', one: 'category', create: 'createCategory', update: 'updateCategory', - delete: 'deleteCategory', - }, + delete: 'deleteCategory' + } }); /** @@ -255,7 +255,7 @@ const profileTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'bio', type: fieldTypes.string }, { name: 'userId', type: fieldTypes.uuid }, - { name: 'avatarUrl', type: fieldTypes.string }, + { name: 'avatarUrl', type: fieldTypes.string } ], relations: { belongsTo: [ @@ -264,20 +264,20 @@ const profileTable = createTable({ isUnique: true, referencesTable: 'User', type: null, - keys: [], - }, + keys: [] + } ], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }, query: { all: 'profiles', one: 'profile', create: 'createProfile', update: 'updateProfile', - delete: 'deleteProfile', - }, + delete: 'deleteProfile' + } }); // User with hasOne to profile @@ -291,8 +291,8 @@ const userTableWithProfile = createTable({ isUnique: true, referencedByTable: 'Profile', type: null, - keys: [], - }, + keys: [] + } ], hasMany: [ { @@ -300,11 +300,11 @@ const userTableWithProfile = createTable({ isUnique: false, referencedByTable: 'Post', type: null, - keys: [], - }, + keys: [] + } ], - manyToMany: [], - }, + manyToMany: [] + } }); // ============================================================================ @@ -318,8 +318,8 @@ const sampleTypeRegistry = createTypeRegistry({ inputFields: [ { name: 'email', type: createNonNull(createTypeRef('SCALAR', 'String')) }, { name: 'password', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'rememberMe', type: createTypeRef('SCALAR', 'Boolean') }, - ], + { name: 'rememberMe', type: createTypeRef('SCALAR', 'Boolean') } + ] }, RegisterInput: { kind: 'INPUT_OBJECT', @@ -327,13 +327,13 @@ const sampleTypeRegistry = createTypeRegistry({ inputFields: [ { name: 'email', type: createNonNull(createTypeRef('SCALAR', 'String')) }, { name: 'password', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'name', type: createTypeRef('SCALAR', 'String') }, - ], + { name: 'name', type: createTypeRef('SCALAR', 'String') } + ] }, UserRole: { kind: 'ENUM', name: 'UserRole', - enumValues: ['ADMIN', 'USER', 'GUEST'], + enumValues: ['ADMIN', 'USER', 'GUEST'] }, LoginPayload: { kind: 'OBJECT', @@ -341,9 +341,9 @@ const sampleTypeRegistry = createTypeRegistry({ fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, { name: 'user', type: createTypeRef('OBJECT', 'User') }, - { name: 'expiresAt', type: createTypeRef('SCALAR', 'Datetime') }, - ], - }, + { name: 'expiresAt', type: createTypeRef('SCALAR', 'Datetime') } + ] + } }); // ============================================================================ @@ -646,14 +646,14 @@ describe('collectInputTypeNames', () => { const operations = [ { args: [ - { name: 'input', type: createNonNull(createTypeRef('INPUT_OBJECT', 'LoginInput')) }, - ] as CleanArgument[], + { name: 'input', type: createNonNull(createTypeRef('INPUT_OBJECT', 'LoginInput')) } + ] as CleanArgument[] }, { args: [ - { name: 'data', type: createTypeRef('INPUT_OBJECT', 'RegisterInput') }, - ] as CleanArgument[], - }, + { name: 'data', type: createTypeRef('INPUT_OBJECT', 'RegisterInput') } + ] as CleanArgument[] + } ]; const result = collectInputTypeNames(operations); @@ -666,9 +666,9 @@ describe('collectInputTypeNames', () => { const operations = [ { args: [ - { name: 'filter', type: createTypeRef('INPUT_OBJECT', 'UserFilter') }, - ] as CleanArgument[], - }, + { name: 'filter', type: createTypeRef('INPUT_OBJECT', 'UserFilter') } + ] as CleanArgument[] + } ]; const result = collectInputTypeNames(operations); @@ -681,7 +681,7 @@ describe('collectPayloadTypeNames', () => { it('collects Payload type names from operations', () => { const operations = [ { returnType: createTypeRef('OBJECT', 'LoginPayload') }, - { returnType: createTypeRef('OBJECT', 'RegisterPayload') }, + { returnType: createTypeRef('OBJECT', 'RegisterPayload') } ]; const result = collectPayloadTypeNames(operations); @@ -690,14 +690,14 @@ describe('collectPayloadTypeNames', () => { expect(result.has('RegisterPayload')).toBe(true); }); - it('excludes Connection types', () => { + it('includes Connection types', () => { const operations = [ - { returnType: createTypeRef('OBJECT', 'UsersConnection') }, + { returnType: createTypeRef('OBJECT', 'UsersConnection') } ]; const result = collectPayloadTypeNames(operations); - expect(result.has('UsersConnection')).toBe(false); + expect(result.has('UsersConnection')).toBe(true); }); }); @@ -717,7 +717,7 @@ describe('edge cases', () => { it('handles table with only id field', () => { const minimalTable = createTable({ name: 'Minimal', - fields: [{ name: 'id', type: fieldTypes.uuid }], + fields: [{ name: 'id', type: fieldTypes.uuid }] }); const result = generateInputTypesFile(new Map(), new Set(), [minimalTable]); diff --git a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts index 1a27b9062..86de98981 100644 --- a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts @@ -4,7 +4,7 @@ * Tests the generated model classes with findMany, findFirst, create, update, delete methods. */ import { generateModelFile } from '../../core/codegen/orm/model-generator'; -import type { CleanTable, CleanFieldType, CleanRelations } from '../../types/schema'; +import type { CleanFieldType, CleanRelations,CleanTable } from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -15,14 +15,14 @@ const fieldTypes = { string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -32,7 +32,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -49,15 +49,15 @@ describe('model-generator', () => { { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, { name: 'isActive', type: fieldTypes.boolean }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser', - }, + delete: 'deleteUser' + } }); const result = generateModelFile(table, false); @@ -73,15 +73,15 @@ describe('model-generator', () => { fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'action', type: fieldTypes.string }, - { name: 'timestamp', type: fieldTypes.datetime }, + { name: 'timestamp', type: fieldTypes.datetime } ], query: { all: 'auditLogs', one: 'auditLog', create: 'createAuditLog', update: undefined, - delete: undefined, - }, + delete: undefined + } }); const result = generateModelFile(table, false); @@ -99,15 +99,15 @@ describe('model-generator', () => { name: 'Organization', fields: [ { name: 'id', type: fieldTypes.uuid }, - { name: 'name', type: fieldTypes.string }, + { name: 'name', type: fieldTypes.string } ], query: { all: 'allOrganizations', one: 'organizationById', create: 'registerOrganization', update: 'modifyOrganization', - delete: 'removeOrganization', - }, + delete: 'removeOrganization' + } }); const result = generateModelFile(table, false); @@ -125,15 +125,15 @@ describe('model-generator', () => { fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'price', type: fieldTypes.int }, + { name: 'price', type: fieldTypes.int } ], query: { all: 'products', one: 'product', create: 'createProduct', update: 'updateProduct', - delete: 'deleteProduct', - }, + delete: 'deleteProduct' + } }); const result = generateModelFile(table, false); diff --git a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts index b91c420fc..7c0d3b599 100644 --- a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts +++ b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts @@ -5,8 +5,8 @@ * Functions are re-implemented here to avoid ./client import issues. */ import * as t from 'gql-ast'; -import { parseType, print } from 'graphql'; import type { ArgumentNode, FieldNode, VariableDefinitionNode } from 'graphql'; +import { parseType, print } from 'graphql'; // ============================================================================ // Core functions from query-builder.ts (re-implemented for testing) @@ -16,7 +16,7 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { return [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections: nodeSelections }), + selectionSet: t.selectionSet({ selections: nodeSelections }) }), t.field({ name: 'totalCount' }), t.field({ @@ -26,10 +26,10 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }), - ], - }), - }), + t.field({ name: 'endCursor' }) + ] + }) + }) ]; } @@ -43,13 +43,13 @@ function addVariable( definitions.push( t.variableDefinition({ variable: t.variable({ name: spec.varName }), - type: parseType(spec.typeName), + type: parseType(spec.typeName) }) ); args.push( t.argument({ name: spec.argName ?? spec.varName, - value: t.variable({ name: spec.varName }), + value: t.variable({ name: spec.varName }) }) ); variables[spec.varName] = spec.value; @@ -66,7 +66,7 @@ function buildSelections(select: Record | undefined): FieldNode fields.push( t.field({ name: key, - selectionSet: t.selectionSet({ selections: buildSelections(nested.select) }), + selectionSet: t.selectionSet({ selections: buildSelections(nested.select) }) }) ); } @@ -104,12 +104,12 @@ function buildFindManyDocument( t.field({ name: queryField, args: queryArgs.length ? queryArgs : undefined, - selectionSet: t.selectionSet({ selections: buildConnectionSelections(selections) }), - }), - ], - }), - }), - ], + selectionSet: t.selectionSet({ selections: buildConnectionSelections(selections) }) + }) + ] + }) + }) + ] }); return { document: print(document), variables }; } @@ -130,8 +130,8 @@ function buildMutationDocument( variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'input' }), - type: parseType(inputTypeName + '!'), - }), + type: parseType(inputTypeName + '!') + }) ], selectionSet: t.selectionSet({ selections: [ @@ -142,15 +142,15 @@ function buildMutationDocument( selections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections }), - }), - ], - }), - }), - ], - }), - }), - ], + selectionSet: t.selectionSet({ selections }) + }) + ] + }) + }) + ] + }) + }) + ] }) ); } @@ -166,7 +166,7 @@ describe('query-builder', () => { id: true, name: true, ignored: false, - profile: { select: { bio: true } }, + profile: { select: { bio: true } } }); expect(result).toHaveLength(3); @@ -199,7 +199,7 @@ describe('query-builder', () => { expect(variables).toEqual({ where: { status: { equalTo: 'active' } }, first: 10, - orderBy: ['NAME_ASC'], + orderBy: ['NAME_ASC'] }); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts b/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts index abc8484b9..ad96c2697 100644 --- a/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts +++ b/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts @@ -6,24 +6,24 @@ * - Mutation keys factory (mutation-keys.ts) * - Cache invalidation helpers (invalidation.ts) */ -import { generateQueryKeysFile } from '../../core/codegen/query-keys'; -import { generateMutationKeysFile } from '../../core/codegen/mutation-keys'; import { generateInvalidationFile } from '../../core/codegen/invalidation'; -import type { CleanTable, CleanFieldType, CleanRelations, CleanOperation, CleanTypeRef } from '../../types/schema'; -import type { QueryKeyConfig, EntityRelationship } from '../../types/config'; +import { generateMutationKeysFile } from '../../core/codegen/mutation-keys'; +import { generateQueryKeysFile } from '../../core/codegen/query-keys'; +import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; +import type { CleanFieldType, CleanOperation, CleanRelations, CleanTable, CleanTypeRef } from '../../types/schema'; const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -33,7 +33,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -47,15 +47,15 @@ const simpleUserTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser', - }, + delete: 'deleteUser' + } }); const postTable = createTable({ @@ -65,15 +65,15 @@ const postTable = createTable({ { name: 'title', type: fieldTypes.string }, { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost', - }, + delete: 'deletePost' + } }); const organizationTable = createTable({ @@ -81,15 +81,15 @@ const organizationTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'slug', type: fieldTypes.string }, + { name: 'slug', type: fieldTypes.string } ], query: { all: 'organizations', one: 'organization', create: 'createOrganization', update: 'updateOrganization', - delete: 'deleteOrganization', - }, + delete: 'deleteOrganization' + } }); const databaseTable = createTable({ @@ -97,15 +97,15 @@ const databaseTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'organizationId', type: fieldTypes.uuid }, + { name: 'organizationId', type: fieldTypes.uuid } ], query: { all: 'databases', one: 'database', create: 'createDatabase', update: 'updateDatabase', - delete: 'deleteDatabase', - }, + delete: 'deleteDatabase' + } }); const tableEntityTable = createTable({ @@ -113,15 +113,15 @@ const tableEntityTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'databaseId', type: fieldTypes.uuid }, + { name: 'databaseId', type: fieldTypes.uuid } ], query: { all: 'tables', one: 'table', create: 'createTable', update: 'updateTable', - delete: 'deleteTable', - }, + delete: 'deleteTable' + } }); const fieldTable = createTable({ @@ -130,15 +130,15 @@ const fieldTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, { name: 'tableId', type: fieldTypes.uuid }, - { name: 'type', type: fieldTypes.string }, + { name: 'type', type: fieldTypes.string } ], query: { all: 'fields', one: 'field', create: 'createField', update: 'updateField', - delete: 'deleteField', - }, + delete: 'deleteField' + } }); const simpleConfig: QueryKeyConfig = { @@ -146,17 +146,17 @@ const simpleConfig: QueryKeyConfig = { relationships: {}, generateScopedKeys: true, generateCascadeHelpers: true, - generateMutationKeys: true, + generateMutationKeys: true }; const simpleRelationships: Record = { - post: { parent: 'User', foreignKey: 'authorId' }, + post: { parent: 'User', foreignKey: 'authorId' } }; const hierarchicalRelationships: Record = { database: { parent: 'Organization', foreignKey: 'organizationId' }, table: { parent: 'Database', foreignKey: 'databaseId', ancestors: ['organization'] }, - field: { parent: 'Table', foreignKey: 'tableId', ancestors: ['database', 'organization'] }, + field: { parent: 'Table', foreignKey: 'tableId', ancestors: ['database', 'organization'] } }; const sampleCustomQueries: CleanOperation[] = [ @@ -165,18 +165,18 @@ const sampleCustomQueries: CleanOperation[] = [ kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user', + description: 'Get the current authenticated user' }, { name: 'searchUsers', kind: 'query', args: [ { name: 'query', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'limit', type: createTypeRef('SCALAR', 'Int') }, + { name: 'limit', type: createTypeRef('SCALAR', 'Int') } ], returnType: createTypeRef('LIST', null, createTypeRef('OBJECT', 'User')), - description: 'Search users by name or email', - }, + description: 'Search users by name or email' + } ]; const sampleCustomMutations: CleanOperation[] = [ @@ -185,18 +185,18 @@ const sampleCustomMutations: CleanOperation[] = [ kind: 'mutation', args: [ { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, + { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user', + description: 'Authenticate user' }, { name: 'logout', kind: 'mutation', args: [], returnType: createTypeRef('OBJECT', 'LogoutPayload'), - description: 'Log out current user', - }, + description: 'Log out current user' + } ]; describe('generateQueryKeysFile', () => { @@ -204,7 +204,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable], customQueries: [], - config: simpleConfig, + config: simpleConfig }); expect(result.fileName).toBe('query-keys.ts'); expect(result.content).toMatchSnapshot(); @@ -214,7 +214,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable, postTable], customQueries: [], - config: simpleConfig, + config: simpleConfig }); expect(result.content).toMatchSnapshot(); }); @@ -225,8 +225,8 @@ describe('generateQueryKeysFile', () => { customQueries: [], config: { ...simpleConfig, - relationships: simpleRelationships, - }, + relationships: simpleRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -237,8 +237,8 @@ describe('generateQueryKeysFile', () => { customQueries: [], config: { ...simpleConfig, - relationships: hierarchicalRelationships, - }, + relationships: hierarchicalRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -247,7 +247,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable], customQueries: sampleCustomQueries, - config: simpleConfig, + config: simpleConfig }); expect(result.content).toMatchSnapshot(); }); @@ -259,8 +259,8 @@ describe('generateQueryKeysFile', () => { config: { ...simpleConfig, relationships: simpleRelationships, - generateScopedKeys: false, - }, + generateScopedKeys: false + } }); expect(result.content).toMatchSnapshot(); }); @@ -271,7 +271,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable], customMutations: [], - config: simpleConfig, + config: simpleConfig }); expect(result.fileName).toBe('mutation-keys.ts'); expect(result.content).toMatchSnapshot(); @@ -281,7 +281,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable, postTable], customMutations: [], - config: simpleConfig, + config: simpleConfig }); expect(result.content).toMatchSnapshot(); }); @@ -292,8 +292,8 @@ describe('generateMutationKeysFile', () => { customMutations: [], config: { ...simpleConfig, - relationships: simpleRelationships, - }, + relationships: simpleRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -302,7 +302,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable], customMutations: sampleCustomMutations, - config: simpleConfig, + config: simpleConfig }); expect(result.content).toMatchSnapshot(); }); @@ -313,8 +313,8 @@ describe('generateMutationKeysFile', () => { customMutations: [], config: { ...simpleConfig, - relationships: hierarchicalRelationships, - }, + relationships: hierarchicalRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -324,7 +324,7 @@ describe('generateInvalidationFile', () => { it('generates invalidation helpers for a single table without relationships', () => { const result = generateInvalidationFile({ tables: [simpleUserTable], - config: simpleConfig, + config: simpleConfig }); expect(result.fileName).toBe('invalidation.ts'); expect(result.content).toMatchSnapshot(); @@ -333,7 +333,7 @@ describe('generateInvalidationFile', () => { it('generates invalidation helpers for multiple tables', () => { const result = generateInvalidationFile({ tables: [simpleUserTable, postTable], - config: simpleConfig, + config: simpleConfig }); expect(result.content).toMatchSnapshot(); }); @@ -343,8 +343,8 @@ describe('generateInvalidationFile', () => { tables: [simpleUserTable, postTable], config: { ...simpleConfig, - relationships: simpleRelationships, - }, + relationships: simpleRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -354,8 +354,8 @@ describe('generateInvalidationFile', () => { tables: [organizationTable, databaseTable, tableEntityTable, fieldTable], config: { ...simpleConfig, - relationships: hierarchicalRelationships, - }, + relationships: hierarchicalRelationships + } }); expect(result.content).toMatchSnapshot(); }); @@ -366,8 +366,8 @@ describe('generateInvalidationFile', () => { config: { ...simpleConfig, relationships: simpleRelationships, - generateCascadeHelpers: false, - }, + generateCascadeHelpers: false + } }); expect(result.content).toMatchSnapshot(); }); diff --git a/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts b/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts index d5e273df6..a8a187295 100644 --- a/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts +++ b/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts @@ -9,26 +9,26 @@ * - Schema types * - Barrel files */ -import { generateListQueryHook, generateSingleQueryHook } from '../../core/codegen/queries'; -import { generateCreateMutationHook, generateUpdateMutationHook, generateDeleteMutationHook } from '../../core/codegen/mutations'; -import { generateCustomQueryHook } from '../../core/codegen/custom-queries'; -import { generateCustomMutationHook } from '../../core/codegen/custom-mutations'; -import { generateSchemaTypesFile } from '../../core/codegen/schema-types-generator'; import { - generateQueriesBarrel, - generateMutationsBarrel, - generateMainBarrel, - generateCustomQueriesBarrel, generateCustomMutationsBarrel, + generateCustomQueriesBarrel, + generateMainBarrel, + generateMutationsBarrel, + generateQueriesBarrel } from '../../core/codegen/barrel'; +import { generateCustomMutationHook } from '../../core/codegen/custom-mutations'; +import { generateCustomQueryHook } from '../../core/codegen/custom-queries'; +import { generateCreateMutationHook, generateDeleteMutationHook,generateUpdateMutationHook } from '../../core/codegen/mutations'; +import { generateListQueryHook, generateSingleQueryHook } from '../../core/codegen/queries'; +import { generateSchemaTypesFile } from '../../core/codegen/schema-types-generator'; import type { - CleanTable, CleanFieldType, - CleanRelations, CleanOperation, + CleanRelations, + CleanTable, CleanTypeRef, - TypeRegistry, ResolvedType, + TypeRegistry } from '../../types/schema'; const fieldTypes = { @@ -36,14 +36,14 @@ const fieldTypes = { string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, - boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType, + boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -53,7 +53,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -67,15 +67,15 @@ const simpleUserTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser', - }, + delete: 'deleteUser' + } }); const postTable = createTable({ @@ -86,15 +86,15 @@ const postTable = createTable({ { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, { name: 'published', type: fieldTypes.boolean }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost', - }, + delete: 'deletePost' + } }); const simpleCustomQueries: CleanOperation[] = [ @@ -103,18 +103,18 @@ const simpleCustomQueries: CleanOperation[] = [ kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user', + description: 'Get the current authenticated user' }, { name: 'searchUsers', kind: 'query', args: [ { name: 'query', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'limit', type: createTypeRef('SCALAR', 'Int') }, + { name: 'limit', type: createTypeRef('SCALAR', 'Int') } ], returnType: createTypeRef('LIST', null, createTypeRef('OBJECT', 'User')), - description: 'Search users by name or email', - }, + description: 'Search users by name or email' + } ]; const simpleCustomMutations: CleanOperation[] = [ @@ -123,27 +123,27 @@ const simpleCustomMutations: CleanOperation[] = [ kind: 'mutation', args: [ { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, + { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user', + description: 'Authenticate user' }, { name: 'logout', kind: 'mutation', args: [], returnType: createTypeRef('OBJECT', 'LogoutPayload'), - description: 'Log out current user', + description: 'Log out current user' }, { name: 'register', kind: 'mutation', args: [ - { name: 'input', type: createTypeRef('NON_NULL', null, createTypeRef('INPUT_OBJECT', 'RegisterInput')) }, + { name: 'input', type: createTypeRef('NON_NULL', null, createTypeRef('INPUT_OBJECT', 'RegisterInput')) } ], returnType: createTypeRef('OBJECT', 'RegisterPayload'), - description: 'Register a new user', - }, + description: 'Register a new user' + } ]; function createTypeRegistry(): TypeRegistry { @@ -154,16 +154,16 @@ function createTypeRegistry(): TypeRegistry { name: 'LoginPayload', fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, - { name: 'user', type: createTypeRef('OBJECT', 'User') }, - ], + { name: 'user', type: createTypeRef('OBJECT', 'User') } + ] } as ResolvedType); registry.set('LogoutPayload', { kind: 'OBJECT', name: 'LogoutPayload', fields: [ - { name: 'success', type: createTypeRef('SCALAR', 'Boolean') }, - ], + { name: 'success', type: createTypeRef('SCALAR', 'Boolean') } + ] } as ResolvedType); registry.set('RegisterPayload', { @@ -171,8 +171,8 @@ function createTypeRegistry(): TypeRegistry { name: 'RegisterPayload', fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, - { name: 'user', type: createTypeRef('OBJECT', 'User') }, - ], + { name: 'user', type: createTypeRef('OBJECT', 'User') } + ] } as ResolvedType); registry.set('RegisterInput', { @@ -181,22 +181,22 @@ function createTypeRegistry(): TypeRegistry { inputFields: [ { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'name', type: createTypeRef('SCALAR', 'String') }, - ], + { name: 'name', type: createTypeRef('SCALAR', 'String') } + ] } as ResolvedType); registry.set('UserRole', { kind: 'ENUM', name: 'UserRole', - enumValues: ['ADMIN', 'USER', 'GUEST'], + enumValues: ['ADMIN', 'USER', 'GUEST'] } as ResolvedType); registry.set('Query', { kind: 'OBJECT', name: 'Query', fields: [ - { name: 'currentUser', type: createTypeRef('OBJECT', 'User') }, - ], + { name: 'currentUser', type: createTypeRef('OBJECT', 'User') } + ] } as ResolvedType); registry.set('Mutation', { @@ -205,8 +205,8 @@ function createTypeRegistry(): TypeRegistry { fields: [ { name: 'login', type: createTypeRef('OBJECT', 'LoginPayload') }, { name: 'logout', type: createTypeRef('OBJECT', 'LogoutPayload') }, - { name: 'register', type: createTypeRef('OBJECT', 'RegisterPayload') }, - ], + { name: 'register', type: createTypeRef('OBJECT', 'RegisterPayload') } + ] } as ResolvedType); return registry; @@ -217,7 +217,7 @@ describe('Query Hook Generators', () => { it('generates list query hook for simple table', () => { const result = generateListQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result.fileName).toBe('useUsersQuery.ts'); @@ -227,7 +227,7 @@ describe('Query Hook Generators', () => { it('generates list query hook without centralized keys', () => { const result = generateListQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -237,7 +237,7 @@ describe('Query Hook Generators', () => { const result = generateListQueryHook(postTable, { reactQueryEnabled: true, useCentralizedKeys: true, - hasRelationships: true, + hasRelationships: true }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -248,7 +248,7 @@ describe('Query Hook Generators', () => { it('generates single query hook for simple table', () => { const result = generateSingleQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result.fileName).toBe('useUserQuery.ts'); @@ -258,7 +258,7 @@ describe('Query Hook Generators', () => { it('generates single query hook without centralized keys', () => { const result = generateSingleQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -268,7 +268,7 @@ describe('Query Hook Generators', () => { const result = generateSingleQueryHook(postTable, { reactQueryEnabled: true, useCentralizedKeys: true, - hasRelationships: true, + hasRelationships: true }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -281,7 +281,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook for simple table', () => { const result = generateCreateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useCreateUserMutation.ts'); @@ -291,7 +291,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook without centralized keys', () => { const result = generateCreateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -300,8 +300,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook for table with relationships', () => { const result = generateCreateMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true, - hasRelationships: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -312,7 +311,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook for simple table', () => { const result = generateUpdateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useUpdateUserMutation.ts'); @@ -322,7 +321,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook without centralized keys', () => { const result = generateUpdateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -331,8 +330,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook for table with relationships', () => { const result = generateUpdateMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true, - hasRelationships: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -343,7 +341,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook for simple table', () => { const result = generateDeleteMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useDeleteUserMutation.ts'); @@ -353,7 +351,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook without centralized keys', () => { const result = generateDeleteMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -362,8 +360,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook for table with relationships', () => { const result = generateDeleteMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true, - hasRelationships: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -377,7 +374,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useCurrentUserQuery.ts'); @@ -388,7 +385,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[1], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useSearchUsersQuery.ts'); @@ -399,7 +396,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -413,7 +410,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useLoginMutation.ts'); @@ -424,7 +421,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[1], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useLogoutMutation.ts'); @@ -435,7 +432,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[2], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true, + useCentralizedKeys: true }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useRegisterMutation.ts'); @@ -446,7 +443,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: false, + useCentralizedKeys: false }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -459,7 +456,7 @@ describe('Schema Types Generator', () => { it('generates schema types file with enums and input objects', () => { const result = generateSchemaTypesFile({ typeRegistry: createTypeRegistry(), - tableTypeNames: new Set(['User', 'Post']), + tableTypeNames: new Set(['User', 'Post']) }); expect(result.fileName).toBe('schema-types.ts'); expect(result.content).toMatchSnapshot(); @@ -468,7 +465,7 @@ describe('Schema Types Generator', () => { it('generates schema types file with empty table types', () => { const result = generateSchemaTypesFile({ typeRegistry: createTypeRegistry(), - tableTypeNames: new Set(), + tableTypeNames: new Set() }); expect(result.content).toMatchSnapshot(); }); @@ -503,27 +500,24 @@ describe('Barrel File Generators', () => { describe('generateMainBarrel', () => { it('generates main barrel with all options enabled', () => { const result = generateMainBarrel([simpleUserTable, postTable], { - hasSchemaTypes: true, hasMutations: true, hasQueryKeys: true, hasMutationKeys: true, - hasInvalidation: true, + hasInvalidation: true }); expect(result).toMatchSnapshot(); }); it('generates main barrel without custom operations', () => { const result = generateMainBarrel([simpleUserTable], { - hasSchemaTypes: false, - hasMutations: true, + hasMutations: true }); expect(result).toMatchSnapshot(); }); it('generates main barrel without mutations', () => { const result = generateMainBarrel([simpleUserTable, postTable], { - hasSchemaTypes: true, - hasMutations: false, + hasMutations: false }); expect(result).toMatchSnapshot(); }); diff --git a/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts b/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts index cf21047a6..93143400f 100644 --- a/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts +++ b/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts @@ -6,11 +6,11 @@ * - Mutation generators return null (since they require React Query) * - Standalone fetch functions are still generated for queries */ -import { generateListQueryHook, generateSingleQueryHook, generateAllQueryHooks } from '../../core/codegen/queries'; -import { generateCreateMutationHook, generateUpdateMutationHook, generateDeleteMutationHook, generateAllMutationHooks } from '../../core/codegen/mutations'; -import { generateCustomQueryHook, generateAllCustomQueryHooks } from '../../core/codegen/custom-queries'; -import { generateCustomMutationHook, generateAllCustomMutationHooks } from '../../core/codegen/custom-mutations'; -import type { CleanTable, CleanFieldType, CleanRelations, CleanOperation, CleanTypeRef, TypeRegistry } from '../../types/schema'; +import { generateAllCustomMutationHooks,generateCustomMutationHook } from '../../core/codegen/custom-mutations'; +import { generateAllCustomQueryHooks,generateCustomQueryHook } from '../../core/codegen/custom-queries'; +import { generateAllMutationHooks,generateCreateMutationHook, generateDeleteMutationHook, generateUpdateMutationHook } from '../../core/codegen/mutations'; +import { generateAllQueryHooks,generateListQueryHook, generateSingleQueryHook } from '../../core/codegen/queries'; +import type { CleanFieldType, CleanOperation, CleanRelations, CleanTable, CleanTypeRef, TypeRegistry } from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -20,14 +20,14 @@ const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; function createTable(partial: Partial & { name: string }): CleanTable { @@ -37,7 +37,7 @@ function createTable(partial: Partial & { name: string }): CleanTabl relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints, + constraints: partial.constraints }; } @@ -47,15 +47,15 @@ const userTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime }, + { name: 'createdAt', type: fieldTypes.datetime } ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser', - }, + delete: 'deleteUser' + } }); function createTypeRef(kind: CleanTypeRef['kind'], name: string | null, ofType?: CleanTypeRef): CleanTypeRef { @@ -67,7 +67,7 @@ const sampleQueryOperation: CleanOperation = { kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user', + description: 'Get the current authenticated user' }; const sampleMutationOperation: CleanOperation = { @@ -75,10 +75,10 @@ const sampleMutationOperation: CleanOperation = { kind: 'mutation', args: [ { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, + { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user', + description: 'Authenticate user' }; const emptyTypeRegistry: TypeRegistry = new Map(); @@ -111,9 +111,9 @@ describe('Query generators with reactQueryEnabled: false', () => { expect(result.content).toContain('export async function fetchUsersQuery'); }); - it('should still include GraphQL document when disabled', () => { + it('should still include ORM client imports when disabled', () => { const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); - expect(result.content).toContain('usersQueryDocument'); + expect(result.content).toContain('getClient'); }); it('should still include query key factory when disabled', () => { @@ -251,7 +251,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(result.content).not.toContain('@tanstack/react-query'); expect(result.content).not.toContain('useQuery'); @@ -261,7 +261,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(result.content).not.toContain('export function useCurrentUserQuery'); }); @@ -270,7 +270,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(result.content).toContain('export async function fetchCurrentUserQuery'); }); @@ -281,7 +281,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const results = generateAllCustomQueryHooks({ operations: [sampleQueryOperation], typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(results.length).toBe(1); expect(results[0].content).not.toContain('@tanstack/react-query'); @@ -299,7 +299,7 @@ describe('Custom mutation generators with reactQueryEnabled: false', () => { const result = generateCustomMutationHook({ operation: sampleMutationOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(result).toBeNull(); }); @@ -310,7 +310,7 @@ describe('Custom mutation generators with reactQueryEnabled: false', () => { const results = generateAllCustomMutationHooks({ operations: [sampleMutationOperation], typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false, + reactQueryEnabled: false }); expect(results).toEqual([]); }); @@ -326,7 +326,7 @@ describe('Custom mutation generators with reactQueryEnabled: true (default)', () it('should return mutation file by default', () => { const result = generateCustomMutationHook({ operation: sampleMutationOperation, - typeRegistry: emptyTypeRegistry, + typeRegistry: emptyTypeRegistry }); expect(result).not.toBeNull(); expect(result!.content).toContain('useMutation'); diff --git a/graphql/codegen/src/__tests__/codegen/scalars.test.ts b/graphql/codegen/src/__tests__/codegen/scalars.test.ts index 27539d3f4..7c81ae5c9 100644 --- a/graphql/codegen/src/__tests__/codegen/scalars.test.ts +++ b/graphql/codegen/src/__tests__/codegen/scalars.test.ts @@ -2,12 +2,12 @@ * Tests for scalar mappings */ import { - SCALAR_TS_MAP, + BASE_FILTER_TYPE_NAMES, SCALAR_FILTER_MAP, SCALAR_NAMES, - BASE_FILTER_TYPE_NAMES, - scalarToTsType, + SCALAR_TS_MAP, scalarToFilterType, + scalarToTsType } from '../../core/codegen/scalars'; describe('scalars', () => { diff --git a/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts b/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts index 70fc56de2..526013226 100644 --- a/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts @@ -2,7 +2,7 @@ * Snapshot tests for schema-types-generator */ import { generateSchemaTypesFile } from '../../core/codegen/schema-types-generator'; -import type { TypeRegistry, ResolvedType } from '../../types/schema'; +import type { ResolvedType,TypeRegistry } from '../../types/schema'; function createTypeRegistry(types: Array<[string, ResolvedType]>): TypeRegistry { return new Map(types); @@ -12,12 +12,12 @@ describe('schema-types-generator', () => { it('generates enum types as string unions', () => { const registry = createTypeRegistry([ ['Status', { kind: 'ENUM', name: 'Status', enumValues: ['ACTIVE', 'INACTIVE', 'PENDING'] }], - ['Priority', { kind: 'ENUM', name: 'Priority', enumValues: ['LOW', 'MEDIUM', 'HIGH'] }], + ['Priority', { kind: 'ENUM', name: 'Priority', enumValues: ['LOW', 'MEDIUM', 'HIGH'] }] ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(), + tableTypeNames: new Set() }); expect(result.content).toMatchSnapshot(); @@ -34,9 +34,9 @@ describe('schema-types-generator', () => { inputFields: [ { name: 'email', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'String' } } }, { name: 'name', type: { kind: 'SCALAR', name: 'String' } }, - { name: 'age', type: { kind: 'SCALAR', name: 'Int' } }, - ], - }, + { name: 'age', type: { kind: 'SCALAR', name: 'Int' } } + ] + } ], [ 'UpdateUserInput', @@ -45,15 +45,15 @@ describe('schema-types-generator', () => { name: 'UpdateUserInput', inputFields: [ { name: 'id', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'UUID' } } }, - { name: 'name', type: { kind: 'SCALAR', name: 'String' } }, - ], - }, - ], + { name: 'name', type: { kind: 'SCALAR', name: 'String' } } + ] + } + ] ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(), + tableTypeNames: new Set() }); expect(result.content).toMatchSnapshot(); @@ -61,12 +61,12 @@ describe('schema-types-generator', () => { it('generates union types', () => { const registry = createTypeRegistry([ - ['SearchResult', { kind: 'UNION', name: 'SearchResult', possibleTypes: ['User', 'Post', 'Comment'] }], + ['SearchResult', { kind: 'UNION', name: 'SearchResult', possibleTypes: ['User', 'Post', 'Comment'] }] ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(), + tableTypeNames: new Set() }); expect(result.content).toMatchSnapshot(); @@ -80,9 +80,9 @@ describe('schema-types-generator', () => { kind: 'OBJECT', name: 'Mutation', fields: [ - { name: 'login', type: { kind: 'OBJECT', name: 'LoginPayload' } }, - ], - }, + { name: 'login', type: { kind: 'OBJECT', name: 'LoginPayload' } } + ] + } ], [ 'LoginPayload', @@ -92,15 +92,15 @@ describe('schema-types-generator', () => { fields: [ { name: 'token', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'String' } } }, { name: 'refreshToken', type: { kind: 'SCALAR', name: 'String' } }, - { name: 'user', type: { kind: 'OBJECT', name: 'User' } }, - ], - }, - ], + { name: 'user', type: { kind: 'OBJECT', name: 'User' } } + ] + } + ] ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(['User']), + tableTypeNames: new Set(['User']) }); expect(result.content).toMatchSnapshot(); @@ -111,12 +111,12 @@ describe('schema-types-generator', () => { const registry = createTypeRegistry([ ['User', { kind: 'ENUM', name: 'User', enumValues: ['ADMIN'] }], ['String', { kind: 'ENUM', name: 'String', enumValues: ['A'] }], - ['CustomEnum', { kind: 'ENUM', name: 'CustomEnum', enumValues: ['VALUE_A', 'VALUE_B'] }], + ['CustomEnum', { kind: 'ENUM', name: 'CustomEnum', enumValues: ['VALUE_A', 'VALUE_B'] }] ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(['User']), + tableTypeNames: new Set(['User']) }); expect(result.content).toMatchSnapshot(); diff --git a/graphql/codegen/src/__tests__/codegen/utils.test.ts b/graphql/codegen/src/__tests__/codegen/utils.test.ts index 62dbf33a0..38f9f066d 100644 --- a/graphql/codegen/src/__tests__/codegen/utils.test.ts +++ b/graphql/codegen/src/__tests__/codegen/utils.test.ts @@ -2,25 +2,25 @@ * Tests for codegen utility functions */ import { + getFilterTypeName, + getGeneratedFileHeader, + getOrderByTypeName, + getPrimaryKeyInfo, + getTableNames, + gqlTypeToTs, lcFirst, - ucFirst, toCamelCase, toPascalCase, toScreamingSnake, - getTableNames, - getFilterTypeName, - getOrderByTypeName, - gqlTypeToTs, - getPrimaryKeyInfo, - getGeneratedFileHeader, + ucFirst } from '../../core/codegen/utils'; -import type { CleanTable, CleanRelations } from '../../types/schema'; +import type { CleanRelations,CleanTable } from '../../types/schema'; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [], + manyToMany: [] }; // Use any for test fixture overrides to avoid strict type requirements @@ -29,7 +29,7 @@ function createTable(name: string, overrides: Record = {}): Cle name, fields: [], relations: emptyRelations, - ...overrides, + ...overrides } as CleanTable; } @@ -78,7 +78,7 @@ describe('utils', () => { it('uses inflection overrides when provided', () => { const result = getTableNames( createTable('Person', { - inflection: { tableFieldName: 'individual', allRows: 'people' }, + inflection: { tableFieldName: 'individual', allRows: 'people' } }) ); expect(result.singularName).toBe('individual'); @@ -88,7 +88,7 @@ describe('utils', () => { it('uses query.all for plural name', () => { const result = getTableNames( createTable('Child', { - query: { all: 'children', one: 'child', create: 'createChild', update: 'updateChild', delete: 'deleteChild' }, + query: { all: 'children', one: 'child', create: 'createChild', update: 'updateChild', delete: 'deleteChild' } }) ); expect(result.pluralName).toBe('children'); @@ -138,8 +138,8 @@ describe('utils', () => { it('extracts PK from constraints', () => { const table = createTable('User', { constraints: { - primaryKey: [{ name: 'users_pkey', fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }] }], - }, + primaryKey: [{ name: 'users_pkey', fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }] }] + } }); const result = getPrimaryKeyInfo(table); expect(result).toEqual([{ name: 'id', gqlType: 'UUID', tsType: 'string' }]); @@ -147,7 +147,7 @@ describe('utils', () => { it('falls back to id field', () => { const table = createTable('User', { - fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }], + fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }] }); const result = getPrimaryKeyInfo(table); expect(result).toEqual([{ name: 'id', gqlType: 'UUID', tsType: 'string' }]); @@ -161,11 +161,11 @@ describe('utils', () => { name: 'user_roles_pkey', fields: [ { name: 'userId', type: { gqlType: 'UUID', isArray: false } }, - { name: 'roleId', type: { gqlType: 'UUID', isArray: false } }, - ], - }, - ], - }, + { name: 'roleId', type: { gqlType: 'UUID', isArray: false } } + ] + } + ] + } }); const result = getPrimaryKeyInfo(table); expect(result).toHaveLength(2); diff --git a/graphql/codegen/src/__tests__/config/resolve-config.test.ts b/graphql/codegen/src/__tests__/config/resolve-config.test.ts index e7e879345..c3d5cd162 100644 --- a/graphql/codegen/src/__tests__/config/resolve-config.test.ts +++ b/graphql/codegen/src/__tests__/config/resolve-config.test.ts @@ -1,16 +1,16 @@ import type { - GraphQLSDKConfigTarget, + GraphQLSDKConfigTarget } from '../../types/config'; import { - mergeConfig, - getConfigOptions, DEFAULT_CONFIG, + getConfigOptions, + mergeConfig } from '../../types/config'; describe('config resolution', () => { it('resolves config with defaults', () => { const config: GraphQLSDKConfigTarget = { - endpoint: 'https://api.example.com/graphql', + endpoint: 'https://api.example.com/graphql' }; const resolved = getConfigOptions(config); @@ -29,9 +29,9 @@ describe('config resolution', () => { tables: { include: ['User'] }, queryKeys: { relationships: { - database: { parent: 'organization', foreignKey: 'organizationId' }, - }, - }, + database: { parent: 'organization', foreignKey: 'organizationId' } + } + } }; const overrides: GraphQLSDKConfigTarget = { @@ -40,9 +40,9 @@ describe('config resolution', () => { tables: { exclude: ['_internal'] }, queryKeys: { relationships: { - table: { parent: 'database', foreignKey: 'databaseId' }, - }, - }, + table: { parent: 'database', foreignKey: 'databaseId' } + } + } }; const merged = mergeConfig(base, overrides); @@ -50,15 +50,15 @@ describe('config resolution', () => { expect(merged.output).toBe('./generated/custom'); expect(merged.headers).toEqual({ Authorization: 'Bearer base', - 'X-Custom': '1', + 'X-Custom': '1' }); expect(merged.tables).toEqual({ include: ['User'], - exclude: ['_internal'], + exclude: ['_internal'] }); expect(merged.queryKeys?.relationships).toEqual({ database: { parent: 'organization', foreignKey: 'organizationId' }, - table: { parent: 'database', foreignKey: 'databaseId' }, + table: { parent: 'database', foreignKey: 'databaseId' } }); }); }); diff --git a/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts b/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts index af8828802..80091abed 100644 --- a/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts +++ b/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts @@ -6,12 +6,12 @@ */ import { inferTablesFromIntrospection } from '../../core/introspect/infer-tables'; import type { - IntrospectionQueryResponse, - IntrospectionType, - IntrospectionTypeRef, + IntrospectionEnumValue, IntrospectionField, IntrospectionInputValue, - IntrospectionEnumValue, + IntrospectionQueryResponse, + IntrospectionType, + IntrospectionTypeRef } from '../../types/introspection'; // ============================================================================ @@ -99,19 +99,19 @@ function createIntrospection( name: a.name, type: a.type, description: null, - defaultValue: null, + defaultValue: null }) ), deprecationReason: null, description: null, - isDeprecated: false, + isDeprecated: false }); const makeInputField = (f: InputFieldDef): IntrospectionInputValue => ({ name: f.name, type: f.type, description: null, - defaultValue: null, + defaultValue: null }); // Add Query and Mutation types @@ -124,21 +124,21 @@ function createIntrospection( enumValues: null, interfaces: [], possibleTypes: null, - description: null, + description: null }, ...(mutationFields.length > 0 ? [ - { - name: 'Mutation', - kind: 'OBJECT' as const, - fields: mutationFields.map(makeField), - inputFields: null, - enumValues: null, - interfaces: [], - possibleTypes: null, - description: null, - }, - ] + { + name: 'Mutation', + kind: 'OBJECT' as const, + fields: mutationFields.map(makeField), + inputFields: null, + enumValues: null, + interfaces: [], + possibleTypes: null, + description: null + } + ] : []), ...types.map( (t): IntrospectionType => ({ @@ -152,19 +152,19 @@ function createIntrospection( enumValues: t.kind === 'ENUM' ? (t.enumValues ?? []).map( - (v): IntrospectionEnumValue => ({ - name: v, - deprecationReason: null, - description: null, - isDeprecated: false, - }) - ) + (v): IntrospectionEnumValue => ({ + name: v, + deprecationReason: null, + description: null, + isDeprecated: false + }) + ) : null, interfaces: [], possibleTypes: null, - description: null, + description: null }) - ), + ) ]; return { @@ -173,8 +173,8 @@ function createIntrospection( mutationType: mutationFields.length > 0 ? { name: 'Mutation' } : null, subscriptionType: null, types: allTypes, - directives: [], - }, + directives: [] + } }; } @@ -192,8 +192,8 @@ describe('Entity Detection', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'email', type: scalar('String') }, - ], + { name: 'email', type: scalar('String') } + ] }, // UsersConnection type (indicates User is an entity) { @@ -201,15 +201,15 @@ describe('Entity Detection', () => { kind: 'OBJECT', fields: [ { name: 'nodes', type: list(object('User')) }, - { name: 'pageInfo', type: nonNull(object('PageInfo')) }, - ], + { name: 'pageInfo', type: nonNull(object('PageInfo')) } + ] }, // PageInfo (builtin, should be ignored) - { name: 'PageInfo', kind: 'OBJECT', fields: [] }, + { name: 'PageInfo', kind: 'OBJECT', fields: [] } ], [ // Query for users - { name: 'users', type: object('UsersConnection') }, + { name: 'users', type: object('UsersConnection') } ] ); @@ -225,26 +225,26 @@ describe('Entity Detection', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Post', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'Comment', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'CommentsConnection', kind: 'OBJECT', fields: [] }, + { name: 'CommentsConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'users', type: object('UsersConnection') }, { name: 'posts', type: object('PostsConnection') }, - { name: 'comments', type: object('CommentsConnection') }, + { name: 'comments', type: object('CommentsConnection') } ] ); @@ -261,15 +261,15 @@ describe('Entity Detection', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, // Does not have Connection (should be ignored) { name: 'AuditLog', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], - }, + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + } ], [{ name: 'users', type: object('UsersConnection') }] ); @@ -297,10 +297,10 @@ describe('Field Extraction', () => { { name: 'email', type: scalar('String') }, { name: 'age', type: scalar('Int') }, { name: 'isActive', type: scalar('Boolean') }, - { name: 'metadata', type: scalar('JSON') }, - ], + { name: 'metadata', type: scalar('JSON') } + ] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }] ); @@ -314,7 +314,7 @@ describe('Field Extraction', () => { 'email', 'age', 'isActive', - 'metadata', + 'metadata' ]); expect(fields.find((f) => f.name === 'id')?.type.gqlType).toBe('UUID'); expect(fields.find((f) => f.name === 'email')?.type.gqlType).toBe('String'); @@ -328,10 +328,10 @@ describe('Field Extraction', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'tags', type: list(scalar('String')) }, - ], + { name: 'tags', type: list(scalar('String')) } + ] }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, + { name: 'PostsConnection', kind: 'OBJECT', fields: [] } ], [{ name: 'posts', type: object('PostsConnection') }] ); @@ -354,27 +354,27 @@ describe('Field Extraction', () => { { name: 'id', type: nonNull(scalar('UUID')) }, { name: 'title', type: scalar('String') }, { name: 'author', type: object('User') }, // belongsTo relation - { name: 'comments', type: object('CommentsConnection') }, // hasMany relation - ], + { name: 'comments', type: object('CommentsConnection') } // hasMany relation + ] }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Comment', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'CommentsConnection', kind: 'OBJECT', fields: [] }, + { name: 'CommentsConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'posts', type: object('PostsConnection') }, { name: 'users', type: object('UsersConnection') }, - { name: 'comments', type: object('CommentsConnection') }, + { name: 'comments', type: object('CommentsConnection') } ] ); @@ -402,20 +402,20 @@ describe('Relation Inference', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'author', type: object('User') }, - ], + { name: 'author', type: object('User') } + ] }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'posts', type: object('PostsConnection') }, - { name: 'users', type: object('UsersConnection') }, + { name: 'users', type: object('UsersConnection') } ] ); @@ -435,20 +435,20 @@ describe('Relation Inference', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'posts', type: object('PostsConnection') }, - ], + { name: 'posts', type: object('PostsConnection') } + ] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Post', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, + { name: 'PostsConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'users', type: object('UsersConnection') }, - { name: 'posts', type: object('PostsConnection') }, + { name: 'posts', type: object('PostsConnection') } ] ); @@ -471,21 +471,21 @@ describe('Relation Inference', () => { // ManyToMany pattern: {entities}By{JunctionTable}{Keys} { name: 'productsByProductCategoryProductIdAndCategoryId', - type: object('ProductsConnection'), - }, - ], + type: object('ProductsConnection') + } + ] }, { name: 'CategoriesConnection', kind: 'OBJECT', fields: [] }, { name: 'Product', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'ProductsConnection', kind: 'OBJECT', fields: [] }, + { name: 'ProductsConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'categories', type: object('CategoriesConnection') }, - { name: 'products', type: object('ProductsConnection') }, + { name: 'products', type: object('ProductsConnection') } ] ); @@ -511,9 +511,9 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [{ name: 'allUsers', type: object('UsersConnection') }] ); @@ -529,17 +529,17 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'users', type: object('UsersConnection') }, { name: 'user', type: object('User'), - args: [{ name: 'id', type: nonNull(scalar('UUID')) }], - }, + args: [{ name: 'id', type: nonNull(scalar('UUID')) }] + } ] ); @@ -554,12 +554,12 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [ - { name: 'users', type: object('UsersConnection') }, + { name: 'users', type: object('UsersConnection') } // No single user query ] ); @@ -579,10 +579,10 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, - { name: 'CreateUserPayload', kind: 'OBJECT', fields: [] }, + { name: 'CreateUserPayload', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }], [ @@ -590,9 +590,9 @@ describe('Mutation Operation Matching', () => { name: 'createUser', type: object('CreateUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('CreateUserInput')) }, - ], - }, + { name: 'input', type: nonNull(inputObject('CreateUserInput')) } + ] + } ] ); @@ -607,11 +607,11 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, - { name: 'DeleteUserPayload', kind: 'OBJECT', fields: [] }, + { name: 'DeleteUserPayload', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }], [ @@ -619,16 +619,16 @@ describe('Mutation Operation Matching', () => { name: 'updateUser', type: object('UpdateUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('UpdateUserInput')) }, - ], + { name: 'input', type: nonNull(inputObject('UpdateUserInput')) } + ] }, { name: 'deleteUser', type: object('DeleteUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('DeleteUserInput')) }, - ], - }, + { name: 'input', type: nonNull(inputObject('DeleteUserInput')) } + ] + } ] ); @@ -644,15 +644,15 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, - { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, + { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }], [ { name: 'updateUserById', type: object('UpdateUserPayload') }, - { name: 'updateUser', type: object('UpdateUserPayload') }, + { name: 'updateUser', type: object('UpdateUserPayload') } ] ); @@ -673,9 +673,9 @@ describe('Constraint Inference', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }] ); @@ -692,14 +692,14 @@ describe('Constraint Inference', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'userId', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'userId', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UpdateUserInput', kind: 'INPUT_OBJECT', - inputFields: [{ name: 'id', type: nonNull(scalar('UUID')) }], - }, + inputFields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + } ], [{ name: 'users', type: object('UsersConnection') }], [{ name: 'updateUser', type: object('UpdateUserPayload') }] @@ -723,12 +723,12 @@ describe('Inflection Building', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UserFilter', kind: 'INPUT_OBJECT', inputFields: [] }, { name: 'UserPatch', kind: 'INPUT_OBJECT', inputFields: [] }, - { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, + { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] } ], [{ name: 'users', type: object('UsersConnection') }] ); @@ -750,9 +750,9 @@ describe('Inflection Building', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, + { name: 'UsersConnection', kind: 'OBJECT', fields: [] } // No UserFilter, UserPatch, or UpdateUserPayload ], [{ name: 'users', type: object('UsersConnection') }] @@ -776,7 +776,7 @@ describe('Edge Cases', () => { const introspection = createIntrospection( [ // Only built-in types, no entities - { name: 'PageInfo', kind: 'OBJECT', fields: [] }, + { name: 'PageInfo', kind: 'OBJECT', fields: [] } ], [] ); @@ -793,9 +793,9 @@ describe('Edge Cases', () => { { name: 'Orphan', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'OrphansConnection', kind: 'OBJECT', fields: [] }, + { name: 'OrphansConnection', kind: 'OBJECT', fields: [] } ], [] // No query fields ); @@ -814,8 +814,8 @@ describe('Edge Cases', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'posts', type: object('PostsConnection') }, - ], + { name: 'posts', type: object('PostsConnection') } + ] }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { @@ -823,14 +823,14 @@ describe('Edge Cases', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'author', type: object('User') }, - ], + { name: 'author', type: object('User') } + ] }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, + { name: 'PostsConnection', kind: 'OBJECT', fields: [] } ], [ { name: 'users', type: object('UsersConnection') }, - { name: 'posts', type: object('PostsConnection') }, + { name: 'posts', type: object('PostsConnection') } ] ); @@ -852,9 +852,9 @@ describe('Edge Cases', () => { { name: 'Person', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, - { name: 'PeopleConnection', kind: 'OBJECT', fields: [] }, + { name: 'PeopleConnection', kind: 'OBJECT', fields: [] } ], [{ name: 'people', type: object('PeopleConnection') }] ); @@ -875,15 +875,15 @@ describe('Edge Cases', () => { fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, { name: 'street', type: scalar('String') }, - { name: 'city', type: scalar('String') }, - ], + { name: 'city', type: scalar('String') } + ] }, { name: 'AddressesConnection', kind: 'OBJECT', fields: [] }, { name: 'AddressesOrderBy', kind: 'ENUM', - enumValues: ['ID_ASC', 'ID_DESC'], - }, + enumValues: ['ID_ASC', 'ID_DESC'] + } ], [{ name: 'addresses', type: object('AddressesConnection') }] ); @@ -903,10 +903,10 @@ describe('Edge Cases', () => { { name: 'Category', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'CategoriesConnection', kind: 'OBJECT', fields: [] }, - { name: 'CategoriesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] }, + { name: 'CategoriesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] } ], [{ name: 'categories', type: object('CategoriesConnection') }] ); @@ -923,11 +923,11 @@ describe('Edge Cases', () => { { name: 'Status', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] }, { name: 'StatusesConnection', kind: 'OBJECT', fields: [] }, // Schema has the actual OrderBy enum - { name: 'StatusesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] }, + { name: 'StatusesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] } ], [{ name: 'statuses', type: object('StatusesConnection') }] ); diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index 5ef53ee8c..6698e2a38 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -5,12 +5,12 @@ * This is a thin wrapper around the core generate() function. * All business logic is in the core modules. */ -import { CLI, CLIOptions, Inquirerer, getPackageJson } from 'inquirerer'; +import { CLI, CLIOptions, getPackageJson,Inquirerer } from 'inquirerer'; -import { generate } from '../core/generate'; import { findConfigFile, loadConfigFile } from '../core/config'; +import { generate } from '../core/generate'; import type { GraphQLSDKConfigTarget } from '../types/config'; -import { camelizeArgv, codegenQuestions, printResult, type CodegenAnswers } from './shared'; +import { camelizeArgv, type CodegenAnswers,codegenQuestions, printResult } from './shared'; const usage = ` graphql-codegen - GraphQL SDK generator for Constructive databases @@ -61,6 +61,15 @@ export const commands = async ( const configPath = (argv.config || argv.c || findConfigFile()) as string | undefined; const targetName = (argv.target || argv.t) as string | undefined; + // Collect CLI flags that should override config file settings + const cliOverrides: Partial = {}; + if (argv['react-query'] === true) cliOverrides.reactQuery = true; + if (argv.orm === true) cliOverrides.orm = true; + if (argv.verbose === true || argv.v === true) cliOverrides.verbose = true; + if (argv['dry-run'] === true) cliOverrides.dryRun = true; + if (argv.output || argv.o) cliOverrides.output = (argv.output || argv.o) as string; + if (argv.authorization || argv.a) cliOverrides.authorization = (argv.authorization || argv.a) as string; + // If config file exists, load and run if (configPath) { const loaded = await loadConfigFile(configPath); @@ -87,7 +96,7 @@ export const commands = async ( let hasError = false; for (const name of names) { console.log(`\n[${name}]`); - const result = await generate(targets[name]); + const result = await generate({ ...targets[name], ...cliOverrides }); printResult(result); if (!result.success) hasError = true; } @@ -97,8 +106,8 @@ export const commands = async ( return argv; } - // Single config - const result = await generate(config as GraphQLSDKConfigTarget); + // Single config — merge CLI overrides + const result = await generate({ ...(config as GraphQLSDKConfigTarget), ...cliOverrides }); printResult(result); if (!result.success) process.exit(1); prompter.close(); @@ -114,7 +123,7 @@ export const commands = async ( // Build db config if schemas or apiNames provided const db = (camelized.schemas || camelized.apiNames) ? { schemas: camelized.schemas, - apiNames: camelized.apiNames, + apiNames: camelized.apiNames } : undefined; const result = await generate({ @@ -127,7 +136,7 @@ export const commands = async ( orm: camelized.orm, browserCompatible: camelized.browserCompatible, dryRun: camelized.dryRun, - verbose: camelized.verbose, + verbose: camelized.verbose }); printResult(result); @@ -145,17 +154,17 @@ export const options: Partial = { o: 'output', t: 'target', a: 'authorization', - v: 'verbose', + v: 'verbose' }, boolean: [ - 'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db', 'browser-compatible', + 'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db', 'browser-compatible' ], string: [ 'config', 'endpoint', 'schema-file', 'output', 'target', 'authorization', 'pgpm-module-path', 'pgpm-workspace-path', 'pgpm-module-name', - 'schemas', 'api-names', - ], - }, + 'schemas', 'api-names' + ] + } }; if (require.main === module) { diff --git a/graphql/codegen/src/cli/shared.ts b/graphql/codegen/src/cli/shared.ts index f147b9fa8..a1dee66a5 100644 --- a/graphql/codegen/src/cli/shared.ts +++ b/graphql/codegen/src/cli/shared.ts @@ -4,10 +4,11 @@ * These are exported so that packages/cli can use the same questions * and types, ensuring consistency between the two CLIs. */ -import type { Question } from 'inquirerer'; -import type { GenerateResult } from '../core/generate'; import { camelize } from 'inflekt'; import { inflektTree } from 'inflekt/transform-keys'; +import type { Question } from 'inquirerer'; + +import type { GenerateResult } from '../core/generate'; /** * Sanitize function that splits comma-separated strings into arrays @@ -43,13 +44,13 @@ export const codegenQuestions: Question[] = [ name: 'endpoint', message: 'GraphQL endpoint URL', type: 'text', - required: false, + required: false }, { name: 'schema-file', message: 'Path to GraphQL schema file', type: 'text', - required: false, + required: false }, { name: 'output', @@ -57,21 +58,21 @@ export const codegenQuestions: Question[] = [ type: 'text', required: false, default: 'codegen', - useDefault: true, + useDefault: true }, { name: 'schemas', message: 'PostgreSQL schemas (comma-separated)', type: 'text', required: false, - sanitize: splitCommas, + sanitize: splitCommas }, { name: 'api-names', message: 'API names (comma-separated)', type: 'text', required: false, - sanitize: splitCommas, + sanitize: splitCommas }, { name: 'react-query', @@ -79,7 +80,7 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true, + useDefault: true }, { name: 'orm', @@ -87,7 +88,7 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true, + useDefault: true }, { name: 'browser-compatible', @@ -95,13 +96,13 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: true, - useDefault: true, + useDefault: true }, { name: 'authorization', message: 'Authorization header value', type: 'text', - required: false, + required: false }, { name: 'dry-run', @@ -109,7 +110,7 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true, + useDefault: true }, { name: 'verbose', @@ -117,8 +118,8 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true, - }, + useDefault: true + } ]; /** diff --git a/graphql/codegen/src/client/error.ts b/graphql/codegen/src/client/error.ts index d879712de..368094f84 100644 --- a/graphql/codegen/src/client/error.ts +++ b/graphql/codegen/src/client/error.ts @@ -43,7 +43,7 @@ export const DataErrorType = { EXCLUSION_VIOLATION: 'EXCLUSION_VIOLATION', // Generic errors - UNKNOWN_ERROR: 'UNKNOWN_ERROR', + UNKNOWN_ERROR: 'UNKNOWN_ERROR' } as const; export type DataErrorType = (typeof DataErrorType)[keyof typeof DataErrorType]; @@ -91,36 +91,36 @@ export class DataError extends Error { getUserMessage(): string { switch (this.type) { - case DataErrorType.NETWORK_ERROR: - return 'Network error. Please check your connection and try again.'; - case DataErrorType.TIMEOUT_ERROR: - return 'Request timed out. Please try again.'; - case DataErrorType.UNAUTHORIZED: - return 'You are not authorized. Please log in and try again.'; - case DataErrorType.FORBIDDEN: - return 'You do not have permission to access this resource.'; - case DataErrorType.VALIDATION_FAILED: - return 'Validation failed. Please check your input and try again.'; - case DataErrorType.REQUIRED_FIELD_MISSING: - return this.fieldName - ? `The field "${this.fieldName}" is required.` - : 'A required field is missing.'; - case DataErrorType.UNIQUE_VIOLATION: - return this.fieldName - ? `A record with this ${this.fieldName} already exists.` - : 'A record with this value already exists.'; - case DataErrorType.FOREIGN_KEY_VIOLATION: - return 'This record references a record that does not exist.'; - case DataErrorType.NOT_NULL_VIOLATION: - return this.fieldName - ? `The field "${this.fieldName}" cannot be empty.` - : 'A required field cannot be empty.'; - case DataErrorType.CHECK_VIOLATION: - return this.fieldName - ? `The value for "${this.fieldName}" is not valid.` - : 'The value does not meet the required constraints.'; - default: - return this.message || 'An unexpected error occurred.'; + case DataErrorType.NETWORK_ERROR: + return 'Network error. Please check your connection and try again.'; + case DataErrorType.TIMEOUT_ERROR: + return 'Request timed out. Please try again.'; + case DataErrorType.UNAUTHORIZED: + return 'You are not authorized. Please log in and try again.'; + case DataErrorType.FORBIDDEN: + return 'You do not have permission to access this resource.'; + case DataErrorType.VALIDATION_FAILED: + return 'Validation failed. Please check your input and try again.'; + case DataErrorType.REQUIRED_FIELD_MISSING: + return this.fieldName + ? `The field "${this.fieldName}" is required.` + : 'A required field is missing.'; + case DataErrorType.UNIQUE_VIOLATION: + return this.fieldName + ? `A record with this ${this.fieldName} already exists.` + : 'A record with this value already exists.'; + case DataErrorType.FOREIGN_KEY_VIOLATION: + return 'This record references a record that does not exist.'; + case DataErrorType.NOT_NULL_VIOLATION: + return this.fieldName + ? `The field "${this.fieldName}" cannot be empty.` + : 'A required field cannot be empty.'; + case DataErrorType.CHECK_VIOLATION: + return this.fieldName + ? `The value for "${this.fieldName}" is not valid.` + : 'The value does not meet the required constraints.'; + default: + return this.message || 'An unexpected error occurred.'; } } @@ -148,7 +148,7 @@ export const PG_ERROR_CODES = { DATETIME_FIELD_OVERFLOW: '22008', UNDEFINED_TABLE: '42P01', UNDEFINED_COLUMN: '42703', - INSUFFICIENT_PRIVILEGE: '42501', + INSUFFICIENT_PRIVILEGE: '42501' } as const; // ============================================================================ @@ -187,7 +187,7 @@ export const createError = { new DataError(DataErrorType.NOT_NULL_VIOLATION, message, { fieldName, constraint, code: '23502' }), unknown: (originalError: Error) => - new DataError(DataErrorType.UNKNOWN_ERROR, originalError.message, { originalError }), + new DataError(DataErrorType.UNKNOWN_ERROR, originalError.message, { originalError }) }; // ============================================================================ @@ -293,7 +293,7 @@ export function parseGraphQLError(error: unknown): DataError { code: extCode, fieldName, constraint, - context: gqlError.extensions, + context: gqlError.extensions }); } @@ -303,7 +303,7 @@ export function parseGraphQLError(error: unknown): DataError { code: extCode, fieldName, constraint, - context: gqlError.extensions, + context: gqlError.extensions }); } diff --git a/graphql/codegen/src/client/execute.ts b/graphql/codegen/src/client/execute.ts index 6f59e7cec..1c4441acf 100644 --- a/graphql/codegen/src/client/execute.ts +++ b/graphql/codegen/src/client/execute.ts @@ -5,8 +5,8 @@ import type { DocumentNode } from 'graphql'; import { print } from 'graphql'; +import { createError, type DataError,parseGraphQLError } from './error'; import { TypedDocumentString } from './typed-document'; -import { createError, parseGraphQLError, type DataError } from './error'; // ============================================================================ // Types @@ -69,7 +69,7 @@ export async function execute( endpoint: string, document: TDocument, variables?: VariablesOf, - options: ExecuteOptions = {}, + options: ExecuteOptions = {} ): Promise> { const { headers = {}, timeout = 30000, signal } = options; @@ -88,13 +88,13 @@ export async function execute( headers: { 'Content-Type': 'application/json', Accept: 'application/graphql-response+json, application/json', - ...headers, + ...headers }, body: JSON.stringify({ query: documentToString(document), - ...(variables !== undefined && { variables }), + ...(variables !== undefined && { variables }) }), - signal: combinedSignal, + signal: combinedSignal }); clearTimeout(timeoutId); @@ -182,12 +182,12 @@ export function createGraphQLClient(options: GraphQLClientOptions) { async execute( document: TDocument, variables?: VariablesOf, - options: ExecuteOptions = {}, + options: ExecuteOptions = {} ): Promise> { return execute(endpoint, document, variables, { headers: { ...defaultHeaders, ...options.headers }, timeout: options.timeout ?? defaultTimeout, - signal: options.signal, + signal: options.signal }); }, @@ -196,7 +196,7 @@ export function createGraphQLClient(options: GraphQLClientOptions) { */ getEndpoint(): string { return endpoint; - }, + } }; } diff --git a/graphql/codegen/src/client/index.ts b/graphql/codegen/src/client/index.ts index 53b0bb352..2ec241259 100644 --- a/graphql/codegen/src/client/index.ts +++ b/graphql/codegen/src/client/index.ts @@ -2,24 +2,22 @@ * Client exports */ -export { TypedDocumentString, type DocumentTypeDecoration } from './typed-document'; - export { - DataError, - DataErrorType, createError, - parseGraphQLError, - isDataError, - PG_ERROR_CODES, + DataError, type DataErrorOptions, + DataErrorType, type GraphQLError, + isDataError, + parseGraphQLError, + PG_ERROR_CODES } from './error'; - export { - execute, createGraphQLClient, + execute, type ExecuteOptions, - type GraphQLClientOptions, type GraphQLClient, - type GraphQLResponse, + type GraphQLClientOptions, + type GraphQLResponse } from './execute'; +export { type DocumentTypeDecoration,TypedDocumentString } from './typed-document'; diff --git a/graphql/codegen/src/client/typed-document.ts b/graphql/codegen/src/client/typed-document.ts index ca3f895a4..95350ad96 100644 --- a/graphql/codegen/src/client/typed-document.ts +++ b/graphql/codegen/src/client/typed-document.ts @@ -33,7 +33,7 @@ export class TypedDocumentString this.value = value; this.__meta__ = { hash: this.generateHash(value), - ...meta, + ...meta }; } diff --git a/graphql/codegen/src/core/ast.ts b/graphql/codegen/src/core/ast.ts index d7903bd09..7c3b95db7 100644 --- a/graphql/codegen/src/core/ast.ts +++ b/graphql/codegen/src/core/ast.ts @@ -5,19 +5,18 @@ import type { FieldNode, TypeNode, ValueNode, - VariableDefinitionNode, + VariableDefinitionNode } from 'graphql'; import { camelize, singularize } from 'inflekt'; - import { getCustomAst } from './custom-ast'; import type { ASTFunctionParams, - QueryFieldSelection, MutationASTParams, NestedProperties, ObjectArrayItem, - QueryProperty, + QueryFieldSelection, + QueryProperty } from './types'; const NON_MUTABLE_PROPS = ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']; @@ -32,7 +31,7 @@ const objectToArray = ( isNotNull: obj[k].isNotNull, isArray: obj[k].isArray, isArrayNotNull: obj[k].isArrayNotNull, - properties: obj[k].properties, + properties: obj[k].properties })); interface CreateGqlMutationParams { @@ -52,32 +51,32 @@ const createGqlMutation = ({ selections, variableDefinitions, modelName, - useModel = true, + useModel = true }: CreateGqlMutationParams): DocumentNode => { const opSel: FieldNode[] = !modelName ? [ - t.field({ - name: operationName, - args: selectArgs, - selectionSet: t.selectionSet({ selections }), - }), - ] + t.field({ + name: operationName, + args: selectArgs, + selectionSet: t.selectionSet({ selections }) + }) + ] : [ - t.field({ - name: operationName, - args: selectArgs, - selectionSet: t.selectionSet({ - selections: useModel - ? [ - t.field({ - name: modelName, - selectionSet: t.selectionSet({ selections }), - }), - ] - : selections, - }), - }), - ]; + t.field({ + name: operationName, + args: selectArgs, + selectionSet: t.selectionSet({ + selections: useModel + ? [ + t.field({ + name: modelName, + selectionSet: t.selectionSet({ selections }) + }) + ] + : selections + }) + }) + ]; return t.document({ definitions: [ @@ -85,16 +84,16 @@ const createGqlMutation = ({ operation: 'mutation', name: mutationName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }), - }), - ], + selectionSet: t.selectionSet({ selections: opSel }) + }) + ] }); }; export const getAll = ({ queryName, operationName, - selection, + selection }: ASTFunctionParams): DocumentNode => { const selections = getSelections(selection); @@ -104,15 +103,15 @@ export const getAll = ({ selectionSet: t.selectionSet({ selections: [ t.field({ - name: 'totalCount', + name: 'totalCount' }), t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections }), - }), - ], - }), - }), + selectionSet: t.selectionSet({ selections }) + }) + ] + }) + }) ]; const ast = t.document({ @@ -120,9 +119,9 @@ export const getAll = ({ t.operationDefinition({ operation: 'query', name: queryName, - selectionSet: t.selectionSet({ selections: opSel }), - }), - ], + selectionSet: t.selectionSet({ selections: opSel }) + }) + ] }); return ast; @@ -131,7 +130,7 @@ export const getAll = ({ export const getCount = ({ queryName, operationName, - query, + query }: Omit): DocumentNode => { const Singular = query.model; const Filter = `${Singular}Filter`; @@ -140,17 +139,17 @@ export const getCount = ({ const variableDefinitions: VariableDefinitionNode[] = [ t.variableDefinition({ variable: t.variable({ name: 'condition' }), - type: t.namedType({ type: Condition }), + type: t.namedType({ type: Condition }) }), t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: Filter }), - }), + type: t.namedType({ type: Filter }) + }) ]; const args: ArgumentNode[] = [ t.argument({ name: 'condition', value: t.variable({ name: 'condition' }) }), - t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }), + t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }) ]; // PostGraphile supports totalCount through connections @@ -161,11 +160,11 @@ export const getCount = ({ selectionSet: t.selectionSet({ selections: [ t.field({ - name: 'totalCount', - }), - ], - }), - }), + name: 'totalCount' + }) + ] + }) + }) ]; const ast = t.document({ @@ -174,9 +173,9 @@ export const getCount = ({ operation: 'query', name: queryName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }), - }), - ], + selectionSet: t.selectionSet({ selections: opSel }) + }) + ] }); return ast; @@ -187,7 +186,7 @@ export const getMany = ({ queryName, operationName, query, - selection, + selection }: ASTFunctionParams): DocumentNode => { const Singular = query.model; const Plural = @@ -200,38 +199,38 @@ export const getMany = ({ const variableDefinitions: VariableDefinitionNode[] = [ t.variableDefinition({ variable: t.variable({ name: 'first' }), - type: t.namedType({ type: 'Int' }), + type: t.namedType({ type: 'Int' }) }), t.variableDefinition({ variable: t.variable({ name: 'last' }), - type: t.namedType({ type: 'Int' }), + type: t.namedType({ type: 'Int' }) }), t.variableDefinition({ variable: t.variable({ name: 'after' }), - type: t.namedType({ type: 'Cursor' }), + type: t.namedType({ type: 'Cursor' }) }), t.variableDefinition({ variable: t.variable({ name: 'before' }), - type: t.namedType({ type: 'Cursor' }), + type: t.namedType({ type: 'Cursor' }) }), t.variableDefinition({ variable: t.variable({ name: 'offset' }), - type: t.namedType({ type: 'Int' }), + type: t.namedType({ type: 'Int' }) }), t.variableDefinition({ variable: t.variable({ name: 'condition' }), - type: t.namedType({ type: Condition }), + type: t.namedType({ type: Condition }) }), t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: Filter }), + type: t.namedType({ type: Filter }) }), t.variableDefinition({ variable: t.variable({ name: 'orderBy' }), type: t.listType({ - type: t.nonNullType({ type: t.namedType({ type: OrderBy }) }), - }), - }), + type: t.nonNullType({ type: t.namedType({ type: OrderBy }) }) + }) + }) ]; const args: ArgumentNode[] = [ @@ -242,44 +241,44 @@ export const getMany = ({ t.argument({ name: 'before', value: t.variable({ name: 'before' }) }), t.argument({ name: 'condition', - value: t.variable({ name: 'condition' }), + value: t.variable({ name: 'condition' }) }), t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }), - t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }), + t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }) ]; const pageInfoFields: FieldNode[] = [ t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'endCursor' }), - t.field({ name: 'startCursor' }), + t.field({ name: 'startCursor' }) ]; const dataField: FieldNode = builder?._edges ? t.field({ - name: 'edges', - selectionSet: t.selectionSet({ - selections: [ - t.field({ name: 'cursor' }), - t.field({ - name: 'node', - selectionSet: t.selectionSet({ selections }), - }), - ], - }), + name: 'edges', + selectionSet: t.selectionSet({ + selections: [ + t.field({ name: 'cursor' }), + t.field({ + name: 'node', + selectionSet: t.selectionSet({ selections }) + }) + ] }) + }) : t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections }), - }); + name: 'nodes', + selectionSet: t.selectionSet({ selections }) + }); const connectionFields: FieldNode[] = [ t.field({ name: 'totalCount' }), t.field({ name: 'pageInfo', - selectionSet: t.selectionSet({ selections: pageInfoFields }), + selectionSet: t.selectionSet({ selections: pageInfoFields }) }), - dataField, + dataField ]; const ast = t.document({ @@ -293,12 +292,12 @@ export const getMany = ({ t.field({ name: operationName, args, - selectionSet: t.selectionSet({ selections: connectionFields }), - }), - ], - }), - }), - ], + selectionSet: t.selectionSet({ selections: connectionFields }) + }) + ] + }) + }) + ] }); return ast; @@ -308,7 +307,7 @@ export const getOne = ({ queryName, operationName, query, - selection, + selection }: ASTFunctionParams): DocumentNode => { const variableDefinitions: VariableDefinitionNode[] = Object.keys( query.properties @@ -321,7 +320,7 @@ export const getOne = ({ type: fieldType, isNotNull, isArray, - isArrayNotNull, + isArrayNotNull } = field; let type: TypeNode = t.namedType({ type: fieldType }); if (isNotNull) type = t.nonNullType({ type }); @@ -331,7 +330,7 @@ export const getOne = ({ } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type, + type }); }); @@ -342,7 +341,7 @@ export const getOne = ({ .map((field) => { return t.argument({ name: field.name, - value: t.variable({ name: field.name }), + value: t.variable({ name: field.name }) }); }); @@ -352,8 +351,8 @@ export const getOne = ({ t.field({ name: operationName, args: selectArgs, - selectionSet: t.selectionSet({ selections }), - }), + selectionSet: t.selectionSet({ selections }) + }) ]; const ast = t.document({ @@ -362,9 +361,9 @@ export const getOne = ({ operation: 'query', name: queryName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }), - }), - ], + selectionSet: t.selectionSet({ selections: opSel }) + }) + ] }); return ast; }; @@ -373,7 +372,7 @@ export const createOne = ({ mutationName, operationName, mutation, - selection, + selection }: MutationASTParams): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); @@ -412,14 +411,14 @@ export const createOne = ({ fields: attrs.map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }), + value: t.variable({ name: field.name }) }) - ), - }), - }), - ], - }), - }), + ) + }) + }) + ] + }) + }) ]; const selections = selection @@ -432,7 +431,7 @@ export const createOne = ({ selectArgs, selections, variableDefinitions, - modelName, + modelName }); return ast; @@ -442,7 +441,7 @@ export const patchOne = ({ mutationName, operationName, mutation, - selection, + selection }: MutationASTParams): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); @@ -479,7 +478,7 @@ export const patchOne = ({ ...patchByAttrs.map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }), + value: t.variable({ name: field.name }) }) ), t.objectField({ @@ -490,14 +489,14 @@ export const patchOne = ({ .map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }), + value: t.variable({ name: field.name }) }) - ), - }), - }), - ], - }), - }), + ) + }) + }) + ] + }) + }) ]; const selections = selection @@ -510,7 +509,7 @@ export const patchOne = ({ selectArgs, selections, variableDefinitions, - modelName, + modelName }); return ast; @@ -519,7 +518,7 @@ export const patchOne = ({ export const deleteOne = ({ mutationName, operationName, - mutation, + mutation }: Omit): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); @@ -548,7 +547,7 @@ export const deleteOne = ({ } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type, + type }); } ); @@ -560,11 +559,11 @@ export const deleteOne = ({ fields: deleteAttrs.map((f) => t.objectField({ name: f.name, - value: t.variable({ name: f.name }), + value: t.variable({ name: f.name }) }) - ), - }), - }), + ) + }) + }) ]; // so we can support column select grants plugin @@ -576,7 +575,7 @@ export const deleteOne = ({ selections, useModel: false, variableDefinitions, - modelName, + modelName }); return ast; @@ -614,7 +613,7 @@ export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[ const [argName, argValue] = variable; const argAst = t.argument({ name: argName, - value: getComplexValueAst(argValue), + value: getComplexValueAst(argValue) }); return argAst ? [...acc, argAst] : acc; }, @@ -627,19 +626,19 @@ export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[ const selectionSet = isBelongTo ? t.selectionSet({ selections: subSelections }) : t.selectionSet({ - selections: [ - t.field({ name: 'totalCount' }), - t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections: subSelections }), - }), - ], - }); + selections: [ + t.field({ name: 'totalCount' }), + t.field({ + name: 'nodes', + selectionSet: t.selectionSet({ selections: subSelections }) + }) + ] + }); return t.field({ name, args, - selectionSet, + selectionSet }); } else { return selectionAst(selectionDefn); @@ -670,7 +669,7 @@ function getComplexValueAst(value: unknown): ValueNode { // Handle arrays if (Array.isArray(value)) { return t.listValue({ - values: value.map((item) => getComplexValueAst(item)), + values: value.map((item) => getComplexValueAst(item)) }); } @@ -681,9 +680,9 @@ function getComplexValueAst(value: unknown): ValueNode { fields: Object.entries(obj).map(([key, val]) => t.objectField({ name: key, - value: getComplexValueAst(val), + value: getComplexValueAst(val) }) - ), + ) }); } @@ -699,7 +698,7 @@ function getCreateVariablesAst( type: fieldType, isNotNull, isArray, - isArrayNotNull, + isArrayNotNull } = field; let type: TypeNode = t.namedType({ type: fieldType }); if (isNotNull) type = t.nonNullType({ type }); @@ -709,7 +708,7 @@ function getCreateVariablesAst( } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type, + type }); }); } @@ -730,14 +729,14 @@ function getUpdateVariablesAst( } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type, + type }); }); const patcherVariables: VariableDefinitionNode[] = patchers.map((patcher) => { return t.variableDefinition({ variable: t.variable({ name: patcher }), - type: t.nonNullType({ type: t.namedType({ type: 'String' }) }), + type: t.nonNullType({ type: t.namedType({ type: 'String' }) }) }); }); diff --git a/graphql/codegen/src/core/codegen/babel-ast.ts b/graphql/codegen/src/core/codegen/babel-ast.ts index 9a189b2f7..0b981d4de 100644 --- a/graphql/codegen/src/core/codegen/babel-ast.ts +++ b/graphql/codegen/src/core/codegen/babel-ast.ts @@ -9,7 +9,7 @@ import generate from '@babel/generator'; import * as t from '@babel/types'; // Re-export for convenience -export { t, generate }; +export { generate,t }; /** * Generate code from an array of statements @@ -29,7 +29,7 @@ export const commentBlock = (value: string): t.CommentBlock => { value, start: null, end: null, - loc: null, + loc: null }; }; @@ -42,7 +42,7 @@ export const commentLine = (value: string): t.CommentLine => { value, start: null, end: null, - loc: null, + loc: null }; }; diff --git a/graphql/codegen/src/core/codegen/barrel.ts b/graphql/codegen/src/core/codegen/barrel.ts index 25d5a6fa6..dc11a6843 100644 --- a/graphql/codegen/src/core/codegen/barrel.ts +++ b/graphql/codegen/src/core/codegen/barrel.ts @@ -3,18 +3,19 @@ * * Using Babel AST for generating barrel (index.ts) files with re-exports. */ -import type { CleanTable } from '../../types/schema'; import * as t from '@babel/types'; -import { generateCode, addJSDocComment } from './babel-ast'; + +import type { CleanTable } from '../../types/schema'; +import { addJSDocComment,generateCode } from './babel-ast'; +import { getOperationHookName } from './type-resolver'; import { + getCreateMutationHookName, + getDeleteMutationHookName, getListQueryHookName, getSingleQueryHookName, - getCreateMutationHookName, getUpdateMutationHookName, - getDeleteMutationHookName, - hasValidPrimaryKey, + hasValidPrimaryKey } from './utils'; -import { getOperationHookName } from './type-resolver'; /** * Helper to create export * from './module' statement @@ -46,7 +47,7 @@ export function generateQueriesBarrel(tables: CleanTable[]): string { addJSDocComment(statements[0], [ 'Query hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten', + 'DO NOT EDIT - changes will be overwritten' ]); } @@ -62,16 +63,16 @@ export function generateMutationsBarrel(tables: CleanTable[]): string { // Export all mutation hooks for (const table of tables) { const createHookName = getCreateMutationHookName(table); - const updateHookName = getUpdateMutationHookName(table); - const deleteHookName = getDeleteMutationHookName(table); statements.push(exportAllFrom(`./${createHookName}`)); - // Only add update/delete if they exist - if (table.query?.update !== null) { + // Only add update/delete if they exist AND table has valid PK + if (table.query?.update !== null && hasValidPrimaryKey(table)) { + const updateHookName = getUpdateMutationHookName(table); statements.push(exportAllFrom(`./${updateHookName}`)); } - if (table.query?.delete !== null) { + if (table.query?.delete !== null && hasValidPrimaryKey(table)) { + const deleteHookName = getDeleteMutationHookName(table); statements.push(exportAllFrom(`./${deleteHookName}`)); } } @@ -81,7 +82,7 @@ export function generateMutationsBarrel(tables: CleanTable[]): string { addJSDocComment(statements[0], [ 'Mutation hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten', + 'DO NOT EDIT - changes will be overwritten' ]); } @@ -95,7 +96,6 @@ export function generateMutationsBarrel(tables: CleanTable[]): string { * @param hasSchemaTypes - Whether schema-types.ts was generated */ export interface MainBarrelOptions { - hasSchemaTypes?: boolean; hasMutations?: boolean; /** Whether query-keys.ts was generated */ hasQueryKeys?: boolean; @@ -112,27 +112,18 @@ export function generateMainBarrel( const opts: MainBarrelOptions = options; const { - hasSchemaTypes = false, hasMutations = true, hasQueryKeys = false, hasMutationKeys = false, - hasInvalidation = false, + hasInvalidation = false } = opts; const tableNames = tables.map((tbl) => tbl.name).join(', '); const statements: t.Statement[] = []; - // Client configuration + // Client configuration (ORM wrapper with configure/getClient) statements.push(exportAllFrom('./client')); - // Entity and filter types - statements.push(exportAllFrom('./types')); - - // Schema types (input, payload, enum types) - if (hasSchemaTypes) { - statements.push(exportAllFrom('./schema-types')); - } - // Centralized query keys (for cache management) if (hasQueryKeys) { statements.push(exportAllFrom('./query-keys')); @@ -185,7 +176,7 @@ export function generateMainBarrel( ' const { mutate } = useCreateCarMutation();', ' // ...', '}', - '```', + '```' ]); } @@ -224,7 +215,7 @@ export function generateRootBarrel(options: RootBarrelOptions = {}): string { if (statements.length > 0) { addJSDocComment(statements[0], [ 'Generated SDK - auto-generated, do not edit', - '@generated by @constructive-io/graphql-codegen', + '@generated by @constructive-io/graphql-codegen' ]); } @@ -277,7 +268,7 @@ export function generateCustomQueriesBarrel( addJSDocComment(statements[0], [ 'Query hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten', + 'DO NOT EDIT - changes will be overwritten' ]); } @@ -302,15 +293,15 @@ export function generateCustomMutationsBarrel( exportedHooks.add(createHookName); } - // Only add update/delete if they exist - if (table.query?.update !== null) { + // Only add update/delete if they exist AND table has valid PK + if (table.query?.update !== null && hasValidPrimaryKey(table)) { const updateHookName = getUpdateMutationHookName(table); if (!exportedHooks.has(updateHookName)) { statements.push(exportAllFrom(`./${updateHookName}`)); exportedHooks.add(updateHookName); } } - if (table.query?.delete !== null) { + if (table.query?.delete !== null && hasValidPrimaryKey(table)) { const deleteHookName = getDeleteMutationHookName(table); if (!exportedHooks.has(deleteHookName)) { statements.push(exportAllFrom(`./${deleteHookName}`)); @@ -333,7 +324,7 @@ export function generateCustomMutationsBarrel( addJSDocComment(statements[0], [ 'Mutation hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten', + 'DO NOT EDIT - changes will be overwritten' ]); } diff --git a/graphql/codegen/src/core/codegen/client.ts b/graphql/codegen/src/core/codegen/client.ts index 809c6f513..483837da4 100644 --- a/graphql/codegen/src/core/codegen/client.ts +++ b/graphql/codegen/src/core/codegen/client.ts @@ -1,74 +1,58 @@ /** - * Client generator - generates client.ts with configure() and execute() + * Client generator - generates client.ts as ORM client wrapper * - * Reads from template files in the templates/ directory for proper type checking. + * Generates a configure()/getClient() singleton pattern that wraps the ORM client. + * React Query hooks use getClient() to delegate to ORM model methods. */ -import * as fs from 'fs'; -import * as path from 'path'; import { getGeneratedFileHeader } from './utils'; -export interface GenerateClientFileOptions { - /** - * Generate browser-compatible code using native fetch - * When true (default), uses native W3C fetch API - * When false, uses undici fetch with dispatcher support for localhost DNS resolution - * @default true - */ - browserCompatible?: boolean; -} - /** - * Find a template file path. - * Templates are at ./templates/ relative to this file in both src/ and dist/. + * Generate client.ts content - ORM client wrapper with configure/getClient */ -function findTemplateFile(templateName: string): string { - const templatePath = path.join(__dirname, 'templates', templateName); +export function generateClientFile(): string { + const header = getGeneratedFileHeader('ORM client wrapper for React Query hooks'); - if (fs.existsSync(templatePath)) { - return templatePath; - } + const code = ` +import { createClient } from '../orm'; +import type { OrmClientConfig } from '../orm/client'; - throw new Error( - `Could not find template file: ${templateName}. ` + - `Searched in: ${templatePath}` - ); -} +export type { OrmClientConfig } from '../orm/client'; +export type { GraphQLAdapter, GraphQLError, QueryResult } from '../orm/client'; +export { GraphQLRequestError } from '../orm/client'; + +type OrmClientInstance = ReturnType; +let client: OrmClientInstance | null = null; /** - * Read a template file and replace the header with generated file header + * Configure the ORM client for React Query hooks + * + * @example + * \`\`\`ts + * import { configure } from './generated/hooks'; + * + * configure({ + * endpoint: 'https://api.example.com/graphql', + * headers: { Authorization: 'Bearer ' }, + * }); + * \`\`\` */ -function readTemplateFile(templateName: string, description: string): string { - const templatePath = findTemplateFile(templateName); - let content = fs.readFileSync(templatePath, 'utf-8'); - - // Replace the source file header comment with the generated file header - // Match the header pattern used in template files - const headerPattern = - /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/; - - content = content.replace( - headerPattern, - getGeneratedFileHeader(description) + '\n' - ); - - return content; +export function configure(config: OrmClientConfig): void { + client = createClient(config); } /** - * Generate client.ts content - * @param options - Generation options + * Get the configured ORM client instance + * @throws Error if configure() has not been called */ -export function generateClientFile( - options: GenerateClientFileOptions = {} -): string { - const { browserCompatible = true } = options; - - const templateName = browserCompatible - ? 'client.browser.ts' - : 'client.node.ts'; +export function getClient(): OrmClientInstance { + if (!client) { + throw new Error( + 'ORM client not configured. Call configure() before using hooks.' + ); + } + return client; +} +`; - return readTemplateFile( - templateName, - 'GraphQL client configuration and execution' - ); + return header + '\n' + code.trim() + '\n'; } diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index 2d9a10aef..ef4cc4b90 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -4,6 +4,9 @@ * Generates hooks for operations discovered via schema introspection * that are NOT table CRUD operations (e.g., login, register, etc.) * + * Delegates to ORM custom mutation operations: + * getClient().mutation.operationName(args, { select }).unwrap() + * * Output structure: * mutations/ * useLoginMutation.ts @@ -12,24 +15,21 @@ */ import type { CleanOperation, - CleanArgument, - TypeRegistry, + TypeRegistry } from '../../types/schema'; -import * as t from '@babel/types'; -import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast'; -import { buildCustomMutationString } from './schema-gql-ast'; import { - typeRefToTsType, - isTypeRequired, - getOperationHookName, - getOperationFileName, - getOperationVariablesTypeName, - getOperationResultTypeName, - getDocumentConstName, + buildDefaultSelectLiteral, + getSelectTypeName, + wrapInferSelectResult +} from './select-helpers'; +import { createTypeTracker, - type TypeTracker, + getOperationFileName, + getOperationHookName, + getTypeBaseName, + typeRefToTsType } from './type-resolver'; -import { getGeneratedFileHeader } from './utils'; +import { getGeneratedFileHeader,ucFirst } from './utils'; export interface GeneratedCustomMutationFile { fileName: string; @@ -47,25 +47,6 @@ export interface GenerateCustomMutationHookOptions { useCentralizedKeys?: boolean; } -interface VariablesProp { - name: string; - type: string; - optional: boolean; - docs?: string[]; -} - -function generateVariablesProperties( - args: CleanArgument[], - tracker?: TypeTracker -): VariablesProp[] { - return args.map((arg) => ({ - name: arg.name, - type: typeRefToTsType(arg.type, tracker), - optional: !isTypeRequired(arg.type), - docs: arg.description ? [arg.description] : undefined, - })); -} - export function generateCustomMutationHook( options: GenerateCustomMutationHookOptions ): GeneratedCustomMutationFile | null { @@ -90,210 +71,147 @@ function generateCustomMutationHookInternal( const { operation, typeRegistry, - maxDepth = 2, - skipQueryField = true, tableTypeNames, - useCentralizedKeys = true, + useCentralizedKeys = true } = options; const hookName = getOperationHookName(operation.name, 'mutation'); const fileName = getOperationFileName(operation.name, 'mutation'); - const variablesTypeName = getOperationVariablesTypeName(operation.name, 'mutation'); - const resultTypeName = getOperationResultTypeName(operation.name, 'mutation'); - const documentConstName = getDocumentConstName(operation.name, 'mutation'); + const varTypeName = `${ucFirst(operation.name)}Variables`; const tracker = createTypeTracker({ tableTypeNames }); - const mutationDocument = buildCustomMutationString({ - operation, - typeRegistry, - maxDepth, - skipQueryField, - }); - - const statements: t.Statement[] = []; - - const variablesProps = - operation.args.length > 0 - ? generateVariablesProperties(operation.args, tracker) - : []; + const hasArgs = operation.args.length > 0; + // Resolve types using tracker for import tracking const resultType = typeRefToTsType(operation.returnType, tracker); - - const schemaTypes = tracker.getImportableTypes(); - const tableTypes = tracker.getTableTypes(); - - const reactQueryImport = t.importDeclaration( - [t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation'))], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - - const reactQueryTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - - if (tableTypes.length > 0) { - const typesImport = t.importDeclaration( - tableTypes.map((tt) => t.importSpecifier(t.identifier(tt), t.identifier(tt))), - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); - } - - if (schemaTypes.length > 0) { - const schemaTypesImport = t.importDeclaration( - schemaTypes.map((st) => t.importSpecifier(t.identifier(st), t.identifier(st))), - t.stringLiteral('../schema-types') - ); - schemaTypesImport.importKind = 'type'; - statements.push(schemaTypesImport); - } - - if (useCentralizedKeys) { - const mutationKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier('customMutationKeys'), t.identifier('customMutationKeys'))], - t.stringLiteral('../mutation-keys') - ); - statements.push(mutationKeyImport); - } - - const mutationDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(documentConstName), - t.templateLiteral( - [t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], - [] - ) - ), - ]); - const mutationDocExport = t.exportNamedDeclaration(mutationDocConst); - addJSDocComment(mutationDocExport, ['GraphQL mutation document']); - statements.push(mutationDocExport); - - if (operation.args.length > 0) { - const variablesInterfaceProps = variablesProps.map((vp) => { - const prop = t.tsPropertySignature( - t.identifier(vp.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(vp.type))) - ); - prop.optional = vp.optional; - return prop; - }); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(variablesTypeName), - null, - null, - t.tsInterfaceBody(variablesInterfaceProps) - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); + for (const arg of operation.args) { + typeRefToTsType(arg.type, tracker); } - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(operation.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(resultType))) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(resultTypeName), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); + const selectTypeName = getSelectTypeName(operation.returnType); + const payloadTypeName = getTypeBaseName(operation.returnType); + const hasSelect = !!selectTypeName && !!payloadTypeName; + const defaultSelectLiteral = + hasSelect && payloadTypeName + ? buildDefaultSelectLiteral(payloadTypeName, typeRegistry) + : null; - const hasArgs = operation.args.length > 0; + const lines: string[] = []; - const hookBodyStatements: t.Statement[] = []; - const mutationOptions: (t.ObjectProperty | t.SpreadElement)[] = []; + // Imports + lines.push(`import { useMutation } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import { getClient } from '../client';`); if (useCentralizedKeys) { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationKey'), - t.callExpression( - t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), - [] - ) - ) - ); + lines.push(`import { customMutationKeys } from '../mutation-keys';`); } + // ORM type imports - variable types come from orm/mutation, entity types from orm/input-types if (hasArgs) { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationFn'), - t.arrowFunctionExpression( - [typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)))], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(resultTypeName)), - t.tsTypeReference(t.identifier(variablesTypeName)), - ] - ) - ) - ) - ); + lines.push(`import type { ${varTypeName} } from '../../orm/mutation';`); + } + + const inputTypeImports: string[] = []; + if (hasSelect) { + inputTypeImports.push(selectTypeName); + inputTypeImports.push(payloadTypeName); } else { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName)], - [t.tsTypeReference(t.identifier(resultTypeName))] - ) - ) - ) - ); + // For scalar/Connection returns, import any non-scalar type used in resultType + for (const refType of tracker.referencedTypes) { + if (!inputTypeImports.includes(refType)) { + inputTypeImports.push(refType); + } + } + } + if (inputTypeImports.length > 0) { + lines.push(`import type { ${inputTypeImports.join(', ')} } from '../../orm/input-types';`); } - mutationOptions.push(t.spreadElement(t.identifier('options'))); + if (hasSelect) { + lines.push(`import type { DeepExact, InferSelectResult } from '../../orm/select-types';`); + } - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)]) - ) - ); + lines.push(''); - const optionsType = hasArgs - ? `Omit, 'mutationFn'>` - : `Omit, 'mutationFn'>`; + // Re-export variable types for consumer convenience + if (hasArgs) { + lines.push(`export type { ${varTypeName} } from '../../orm/mutation';`); + } + if (hasSelect) { + lines.push(`export type { ${selectTypeName} } from '../../orm/input-types';`); + } + if (hasArgs || hasSelect) { + lines.push(''); + } - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsType))); + if (hasSelect && defaultSelectLiteral) { + lines.push(`const defaultSelect = ${defaultSelectLiteral} as const;`); + lines.push(''); + } - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - [optionsParam], - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - statements.push(hookExport); + // Hook + if (hasSelect) { + // With select: generic hook + const selectedResult = wrapInferSelectResult(operation.returnType, payloadTypeName); + const resultTypeStr = `{ ${operation.name}: ${selectedResult} }`; + const mutationVarType = hasArgs ? varTypeName : 'void'; + + const optionsType = `Omit, 'mutationFn'>`; + + lines.push(`export function ${hookName}(`); + lines.push(` args?: { select?: DeepExact },`); + lines.push(` options?: ${optionsType}`); + lines.push(`) {`); + lines.push(` return useMutation({`); + + if (useCentralizedKeys) { + lines.push(` mutationKey: customMutationKeys.${operation.name}(),`); + } + + if (hasArgs) { + lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + } else { + lines.push(` mutationFn: () => getClient().mutation.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + } + + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); + } else { + // Without select: simple hook (scalar return type) + const resultTypeStr = `{ ${operation.name}: ${resultType} }`; + const mutationVarType = hasArgs ? varTypeName : 'void'; + + const optionsType = `Omit, 'mutationFn'>`; + + lines.push(`export function ${hookName}(`); + lines.push(` options?: ${optionsType}`); + lines.push(`) {`); + lines.push(` return useMutation({`); + + if (useCentralizedKeys) { + lines.push(` mutationKey: customMutationKeys.${operation.name}(),`); + } + + if (hasArgs) { + lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables).unwrap(),`); + } else { + lines.push(` mutationFn: () => getClient().mutation.${operation.name}().unwrap(),`); + } + + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); + } - const code = generateCode(statements); - const content = getGeneratedFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n' + code; + const content = getGeneratedFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n' + lines.join('\n') + '\n'; return { fileName, content, - operationName: operation.name, + operationName: operation.name }; } @@ -317,7 +235,7 @@ export function generateAllCustomMutationHooks( skipQueryField = true, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true, + useCentralizedKeys = true } = options; return operations @@ -330,7 +248,7 @@ export function generateAllCustomMutationHooks( skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys, + useCentralizedKeys }) ) .filter((result): result is GeneratedCustomMutationFile => result !== null); diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index daefb0eb0..9c005c5e1 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -4,6 +4,9 @@ * Generates hooks for operations discovered via schema introspection * that are NOT table CRUD operations (e.g., currentUser, nodeById, etc.) * + * Delegates to ORM custom query operations: + * getClient().query.operationName(args, { select }).unwrap() + * * Output structure: * queries/ * useCurrentUserQuery.ts @@ -12,25 +15,23 @@ */ import type { CleanOperation, - CleanArgument, - TypeRegistry, + TypeRegistry } from '../../types/schema'; -import * as t from '@babel/types'; -import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast'; -import { buildCustomQueryString } from './schema-gql-ast'; import { - typeRefToTsType, - isTypeRequired, - getOperationHookName, + buildDefaultSelectLiteral, + getSelectTypeName, + wrapInferSelectResult +} from './select-helpers'; +import { + createTypeTracker, getOperationFileName, - getOperationVariablesTypeName, - getOperationResultTypeName, - getDocumentConstName, + getOperationHookName, getQueryKeyName, - createTypeTracker, - type TypeTracker, + getTypeBaseName, + isTypeRequired, + typeRefToTsType } from './type-resolver'; -import { ucFirst, getGeneratedFileHeader } from './utils'; +import { getGeneratedFileHeader,ucFirst } from './utils'; export interface GeneratedCustomQueryFile { fileName: string; @@ -48,497 +49,330 @@ export interface GenerateCustomQueryHookOptions { useCentralizedKeys?: boolean; } -interface VariablesProp { - name: string; - type: string; - optional: boolean; - docs?: string[]; -} - -function generateVariablesProperties( - args: CleanArgument[], - tracker?: TypeTracker -): VariablesProp[] { - return args.map((arg) => ({ - name: arg.name, - type: typeRefToTsType(arg.type, tracker), - optional: !isTypeRequired(arg.type), - docs: arg.description ? [arg.description] : undefined, - })); -} - export function generateCustomQueryHook( options: GenerateCustomQueryHookOptions ): GeneratedCustomQueryFile { const { operation, typeRegistry, - maxDepth = 2, - skipQueryField = true, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true, + useCentralizedKeys = true } = options; const hookName = getOperationHookName(operation.name, 'query'); const fileName = getOperationFileName(operation.name, 'query'); - const variablesTypeName = getOperationVariablesTypeName(operation.name, 'query'); - const resultTypeName = getOperationResultTypeName(operation.name, 'query'); - const documentConstName = getDocumentConstName(operation.name, 'query'); const queryKeyName = getQueryKeyName(operation.name); + const varTypeName = `${ucFirst(operation.name)}Variables`; const tracker = createTypeTracker({ tableTypeNames }); - const queryDocument = buildCustomQueryString({ - operation, - typeRegistry, - maxDepth, - skipQueryField, - }); - - const statements: t.Statement[] = []; - - const variablesProps = - operation.args.length > 0 - ? generateVariablesProperties(operation.args, tracker) - : []; + const hasArgs = operation.args.length > 0; + const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type)); + // Resolve types using tracker for import tracking const resultType = typeRefToTsType(operation.returnType, tracker); + for (const arg of operation.args) { + typeRefToTsType(arg.type, tracker); + } + + const selectTypeName = getSelectTypeName(operation.returnType); + const payloadTypeName = getTypeBaseName(operation.returnType); + const hasSelect = !!selectTypeName && !!payloadTypeName; + const defaultSelectLiteral = + hasSelect && payloadTypeName + ? buildDefaultSelectLiteral(payloadTypeName, typeRegistry) + : null; - const schemaTypes = tracker.getImportableTypes(); - const tableTypes = tracker.getTableTypes(); + const lines: string[] = []; + // Imports if (reactQueryEnabled) { - const reactQueryImport = t.importDeclaration( - [t.importSpecifier(t.identifier('useQuery'), t.identifier('useQuery'))], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - const reactQueryTypeImport = t.importDeclaration( - [ - t.importSpecifier(t.identifier('UseQueryOptions'), t.identifier('UseQueryOptions')), - t.importSpecifier(t.identifier('QueryClient'), t.identifier('QueryClient')), - ], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); + lines.push(`import { useQuery } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); } - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - const clientTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier('ExecuteOptions'), t.identifier('ExecuteOptions'))], - t.stringLiteral('../client') - ); - clientTypeImport.importKind = 'type'; - statements.push(clientTypeImport); - - if (tableTypes.length > 0) { - const typesImport = t.importDeclaration( - tableTypes.map((tt) => t.importSpecifier(t.identifier(tt), t.identifier(tt))), - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); + lines.push(`import { getClient } from '../client';`); + + if (useCentralizedKeys) { + lines.push(`import { customQueryKeys } from '../query-keys';`); } - if (schemaTypes.length > 0) { - const schemaTypesImport = t.importDeclaration( - schemaTypes.map((st) => t.importSpecifier(t.identifier(st), t.identifier(st))), - t.stringLiteral('../schema-types') - ); - schemaTypesImport.importKind = 'type'; - statements.push(schemaTypesImport); + // ORM type imports - variable types come from orm/query, entity types from orm/input-types + if (hasArgs) { + lines.push(`import type { ${varTypeName} } from '../../orm/query';`); } - if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier('customQueryKeys'), t.identifier('customQueryKeys'))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); + const inputTypeImports: string[] = []; + if (hasSelect) { + inputTypeImports.push(selectTypeName); + inputTypeImports.push(payloadTypeName); + } else { + // For scalar/Connection returns, import any non-scalar type used in resultType + const baseName = getTypeBaseName(operation.returnType); + if (baseName && !tracker.referencedTypes.has('__skip__')) { + // Import Connection types and other non-scalar types referenced in the result + for (const refType of tracker.referencedTypes) { + if (!inputTypeImports.includes(refType)) { + inputTypeImports.push(refType); + } + } + } + } + if (inputTypeImports.length > 0) { + lines.push(`import type { ${inputTypeImports.join(', ')} } from '../../orm/input-types';`); } - const queryDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(documentConstName), - t.templateLiteral( - [t.templateElement({ raw: '\n' + queryDocument, cooked: '\n' + queryDocument }, true)], - [] - ) - ), - ]); - const queryDocExport = t.exportNamedDeclaration(queryDocConst); - addJSDocComment(queryDocExport, ['GraphQL query document']); - statements.push(queryDocExport); - - if (operation.args.length > 0) { - const variablesInterfaceProps = variablesProps.map((vp) => { - const prop = t.tsPropertySignature( - t.identifier(vp.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(vp.type))) - ); - prop.optional = vp.optional; - return prop; - }); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(variablesTypeName), - null, - null, - t.tsInterfaceBody(variablesInterfaceProps) - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); + if (hasSelect) { + lines.push(`import type { DeepExact, InferSelectResult } from '../../orm/select-types';`); } - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(operation.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(resultType))) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(resultTypeName), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); + lines.push(''); + // Re-export variable types for consumer convenience + if (hasArgs) { + lines.push(`export type { ${varTypeName} } from '../../orm/query';`); + } + if (hasSelect) { + lines.push(`export type { ${selectTypeName} } from '../../orm/input-types';`); + } + if (hasArgs || hasSelect) { + lines.push(''); + } + + if (hasSelect && defaultSelectLiteral) { + lines.push(`const defaultSelect = ${defaultSelectLiteral} as const;`); + lines.push(''); + } + + // Query key if (useCentralizedKeys) { - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(queryKeyName), - t.memberExpression(t.identifier('customQueryKeys'), t.identifier(operation.name)) - ), - ]); - const queryKeyExport = t.exportNamedDeclaration(queryKeyConst); - addJSDocComment(queryKeyExport, ['Query key factory - re-exported from query-keys.ts']); - statements.push(queryKeyExport); - } else if (operation.args.length > 0) { - const queryKeyArrow = t.arrowFunctionExpression( - [typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), true)], - t.tsAsExpression( - t.arrayExpression([t.stringLiteral(operation.name), t.identifier('variables')]), - t.tsTypeReference(t.identifier('const')) - ) - ); - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator(t.identifier(queryKeyName), queryKeyArrow), - ]); - const queryKeyExport = t.exportNamedDeclaration(queryKeyConst); - addJSDocComment(queryKeyExport, ['Query key factory for caching']); - statements.push(queryKeyExport); + lines.push(`/** Query key factory - re-exported from query-keys.ts */`); + lines.push(`export const ${queryKeyName} = customQueryKeys.${operation.name};`); + } else if (hasArgs) { + lines.push(`/** Query key factory for caching */`); + lines.push(`export const ${queryKeyName} = (variables?: ${varTypeName}) => ['${operation.name}', variables] as const;`); } else { - const queryKeyArrow = t.arrowFunctionExpression( - [], - t.tsAsExpression( - t.arrayExpression([t.stringLiteral(operation.name)]), - t.tsTypeReference(t.identifier('const')) - ) - ); - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator(t.identifier(queryKeyName), queryKeyArrow), - ]); - const queryKeyExport = t.exportNamedDeclaration(queryKeyConst); - addJSDocComment(queryKeyExport, ['Query key factory for caching']); - statements.push(queryKeyExport); + lines.push(`/** Query key factory for caching */`); + lines.push(`export const ${queryKeyName} = () => ['${operation.name}'] as const;`); } + lines.push(''); + // Hook if (reactQueryEnabled) { - const hasArgs = operation.args.length > 0; - const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type)); + const description = operation.description || `Query hook for ${operation.name}`; + const argNames = operation.args.map((a) => a.name).join(', '); + const exampleCall = hasArgs ? `${hookName}({ ${argNames} })` : `${hookName}()`; - const hookBodyStatements: t.Statement[] = []; - const useQueryOptions: (t.ObjectProperty | t.SpreadElement)[] = []; + lines.push(`/**`); + lines.push(` * ${description}`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`tsx`); + lines.push(` * const { data, isLoading } = ${exampleCall};`); + lines.push(` *`); + lines.push(` * if (data?.${operation.name}) {`); + lines.push(` * console.log(data.${operation.name});`); + lines.push(` * }`); + lines.push(` * \`\`\``); + lines.push(` */`); + + if (hasSelect) { + // With select: generic hook + const selectedResult = wrapInferSelectResult(operation.returnType, payloadTypeName); + const resultTypeStr = `{ ${operation.name}: ${selectedResult} }`; + const paramsArr: string[] = []; + + if (hasArgs) { + paramsArr.push(` variables${hasRequiredArgs ? '' : '?'}: ${varTypeName},`); + } + paramsArr.push(` args?: { select?: DeepExact },`); + + const optionsType = `Omit, 'queryKey' | 'queryFn'>`; + paramsArr.push(` options?: ${optionsType}`); + + lines.push(`export function ${hookName}(`); + lines.push(paramsArr.join('\n')); + lines.push(`) {`); + lines.push(` return useQuery({`); + + if (hasArgs) { + lines.push(` queryKey: ${queryKeyName}(variables),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + } else { + lines.push(` queryKey: ${queryKeyName}(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + } - if (hasArgs) { - useQueryOptions.push( - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')]) - ) - ); - useQueryOptions.push( - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(resultTypeName)), - t.tsTypeReference(t.identifier(variablesTypeName)), - ] - ) - ) - ) - ); if (hasRequiredArgs) { - useQueryOptions.push( - t.objectProperty( - t.identifier('enabled'), - t.logicalExpression( - '&&', - t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), - t.binaryExpression( - '!==', - t.optionalMemberExpression( - t.identifier('options'), - t.identifier('enabled'), - false, - true - ), - t.booleanLiteral(false) - ) - ) - ) - ); + lines.push(` enabled: !!variables && options?.enabled !== false,`); } + + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); } else { - useQueryOptions.push( - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(queryKeyName), []) - ) - ); - useQueryOptions.push( - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName)], - [t.tsTypeReference(t.identifier(resultTypeName))] - ) - ) - ) - ); - } - useQueryOptions.push(t.spreadElement(t.identifier('options'))); + // Without select: simple hook (scalar return type) + const resultTypeStr = `{ ${operation.name}: ${resultType} }`; + const paramsArr: string[] = []; - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [t.objectExpression(useQueryOptions)]) - ) - ); + if (hasArgs) { + paramsArr.push(` variables${hasRequiredArgs ? '' : '?'}: ${varTypeName},`); + } - const hookParams: t.Identifier[] = []; - if (hasArgs) { - hookParams.push( - typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs) - ); + const optionsType = `Omit, 'queryKey' | 'queryFn'>`; + paramsArr.push(` options?: ${optionsType}`); + + lines.push(`export function ${hookName}(`); + lines.push(paramsArr.join('\n')); + lines.push(`) {`); + lines.push(` return useQuery({`); + + if (hasArgs) { + lines.push(` queryKey: ${queryKeyName}(variables),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap(),`); + } else { + lines.push(` queryKey: ${queryKeyName}(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}().unwrap(),`); + } + + if (hasRequiredArgs) { + lines.push(` enabled: !!variables && options?.enabled !== false,`); + } + + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); } - const optionsTypeStr = `Omit, 'queryKey' | 'queryFn'>`; - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr))); - hookParams.push(optionsParam); - - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - hookParams, - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - const description = operation.description || `Query hook for ${operation.name}`; - const argNames = operation.args.map((a) => a.name).join(', '); - const exampleCall = hasArgs ? `${hookName}({ ${argNames} })` : `${hookName}()`; - addJSDocComment(hookExport, [ - description, - '', - '@example', - '```tsx', - `const { data, isLoading } = ${exampleCall};`, - '', - `if (data?.${operation.name}) {`, - ` console.log(data.${operation.name});`, - '}', - '```', - ]); - statements.push(hookExport); + lines.push(''); } + // Fetch function (non-hook) const fetchFnName = `fetch${ucFirst(operation.name)}Query`; - const hasArgs = operation.args.length > 0; - const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type)); + const fetchArgNames = operation.args.map((a) => a.name).join(', '); + const fetchExampleCall = hasArgs ? `${fetchFnName}({ ${fetchArgNames} })` : `${fetchFnName}()`; + + lines.push(`/**`); + lines.push(` * Fetch ${operation.name} without React hooks`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * const data = await ${fetchExampleCall};`); + lines.push(` * \`\`\``); + lines.push(` */`); + + if (hasSelect) { + const fetchParamsArr: string[] = []; + if (hasArgs) { + fetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); + } + fetchParamsArr.push(`args?: { select?: DeepExact }`); - const fetchBodyStatements: t.Statement[] = []; - if (hasArgs) { - fetchBodyStatements.push( - t.returnStatement( - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(resultTypeName)), - t.tsTypeReference(t.identifier(variablesTypeName)), - ] - ) - ) - ); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` ${fetchParamsArr.join(',\n ')}`); + lines.push(`) {`); + if (hasArgs) { + lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap();`); + } else { + lines.push(` return getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap();`); + } + lines.push(`}`); } else { - fetchBodyStatements.push( - t.returnStatement( - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')], - [t.tsTypeReference(t.identifier(resultTypeName))] - ) - ) - ); - } + const fetchParamsArr: string[] = []; + if (hasArgs) { + fetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); + } - const fetchParams: t.Identifier[] = []; - if (hasArgs) { - fetchParams.push( - typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs) - ); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` ${fetchParamsArr.join(',\n ')}`); + lines.push(`) {`); + if (hasArgs) { + lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap();`); + } else { + lines.push(` return getClient().query.${operation.name}().unwrap();`); + } + lines.push(`}`); } - fetchParams.push(typedParam('options', t.tsTypeReference(t.identifier('ExecuteOptions')), true)); - - const fetchFunc = t.functionDeclaration( - t.identifier(fetchFnName), - fetchParams, - t.blockStatement(fetchBodyStatements) - ); - fetchFunc.async = true; - fetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([t.tsTypeReference(t.identifier(resultTypeName))]) - ) - ); - const fetchExport = t.exportNamedDeclaration(fetchFunc); - - const argNames = operation.args.map((a) => a.name).join(', '); - const fetchExampleCall = hasArgs ? `${fetchFnName}({ ${argNames} })` : `${fetchFnName}()`; - addJSDocComment(fetchExport, [ - `Fetch ${operation.name} without React hooks`, - '', - '@example', - '```ts', - `const data = await ${fetchExampleCall};`, - '```', - ]); - statements.push(fetchExport); + // Prefetch function if (reactQueryEnabled) { + lines.push(''); + const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`; + const prefetchArgNames = operation.args.map((a) => a.name).join(', '); + const prefetchExampleCall = hasArgs + ? `${prefetchFnName}(queryClient, { ${prefetchArgNames} })` + : `${prefetchFnName}(queryClient)`; - const prefetchBodyStatements: t.Statement[] = []; - const prefetchQueryOptions: t.ObjectProperty[] = []; + lines.push(`/**`); + lines.push(` * Prefetch ${operation.name} for SSR or cache warming`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * await ${prefetchExampleCall};`); + lines.push(` * \`\`\``); + lines.push(` */`); + + if (hasSelect) { + const prefetchParamsArr: string[] = ['queryClient: QueryClient']; + if (hasArgs) { + prefetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); + } + prefetchParamsArr.push(`args?: { select?: DeepExact }`); + + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` ${prefetchParamsArr.join(',\n ')}`); + lines.push(`): Promise {`); + + if (hasArgs) { + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryKeyName}(variables),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` });`); + } else { + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryKeyName}(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` });`); + } - if (hasArgs) { - prefetchQueryOptions.push( - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(queryKeyName), [t.identifier('variables')]) - ) - ); - prefetchQueryOptions.push( - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(resultTypeName)), - t.tsTypeReference(t.identifier(variablesTypeName)), - ] - ) - ) - ) - ); + lines.push(`}`); } else { - prefetchQueryOptions.push( - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(queryKeyName), []) - ) - ); - prefetchQueryOptions.push( - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(documentConstName), t.identifier('undefined'), t.identifier('options')], - [t.tsTypeReference(t.identifier(resultTypeName))] - ) - ) - ) - ); - } + const prefetchParamsArr: string[] = ['queryClient: QueryClient']; + if (hasArgs) { + prefetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); + } - prefetchBodyStatements.push( - t.expressionStatement( - t.awaitExpression( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression(prefetchQueryOptions)] - ) - ) - ) - ); + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` ${prefetchParamsArr.join(',\n ')}`); + lines.push(`): Promise {`); + + if (hasArgs) { + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryKeyName}(variables),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap(),`); + lines.push(` });`); + } else { + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryKeyName}(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}().unwrap(),`); + lines.push(` });`); + } - const prefetchParams: t.Identifier[] = [ - typedParam('queryClient', t.tsTypeReference(t.identifier('QueryClient'))), - ]; - if (hasArgs) { - prefetchParams.push( - typedParam('variables', t.tsTypeReference(t.identifier(variablesTypeName)), !hasRequiredArgs) - ); + lines.push(`}`); } - prefetchParams.push(typedParam('options', t.tsTypeReference(t.identifier('ExecuteOptions')), true)); - - const prefetchFunc = t.functionDeclaration( - t.identifier(prefetchFnName), - prefetchParams, - t.blockStatement(prefetchBodyStatements) - ); - prefetchFunc.async = true; - prefetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([t.tsVoidKeyword()]) - ) - ); - const prefetchExport = t.exportNamedDeclaration(prefetchFunc); - - const prefetchExampleCall = hasArgs - ? `${prefetchFnName}(queryClient, { ${argNames} })` - : `${prefetchFnName}(queryClient)`; - addJSDocComment(prefetchExport, [ - `Prefetch ${operation.name} for SSR or cache warming`, - '', - '@example', - '```ts', - `await ${prefetchExampleCall};`, - '```', - ]); - statements.push(prefetchExport); } - const code = generateCode(statements); const headerText = reactQueryEnabled ? `Custom query hook for ${operation.name}` : `Custom query functions for ${operation.name}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + code; + const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; return { fileName, content, - operationName: operation.name, + operationName: operation.name }; } @@ -562,7 +396,7 @@ export function generateAllCustomQueryHooks( skipQueryField = true, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true, + useCentralizedKeys = true } = options; return operations @@ -575,7 +409,7 @@ export function generateAllCustomQueryHooks( skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys, + useCentralizedKeys }) ); } diff --git a/graphql/codegen/src/core/codegen/gql-ast.ts b/graphql/codegen/src/core/codegen/gql-ast.ts deleted file mode 100644 index 612db24a0..000000000 --- a/graphql/codegen/src/core/codegen/gql-ast.ts +++ /dev/null @@ -1,405 +0,0 @@ -/** - * GraphQL AST builders using gql-ast - * - * Provides utilities for generating GraphQL queries and mutations via AST - * instead of string concatenation. - */ -import * as t from 'gql-ast'; -import { print } from 'graphql'; -import type { - DocumentNode, - FieldNode, - ArgumentNode, - VariableDefinitionNode, -} from 'graphql'; -import type { CleanTable, CleanField } from '../../types/schema'; -import { - getTableNames, - getAllRowsQueryName, - getSingleRowQueryName, - getCreateMutationName, - getUpdateMutationName, - getDeleteMutationName, - getFilterTypeName, - getConditionTypeName, - getOrderByTypeName, - getScalarFields, - getPrimaryKeyInfo, - ucFirst, -} from './utils'; - - - -// ============================================================================ -// Field selection builders -// ============================================================================ - -/** - * Create field selections from CleanField array - */ -function createFieldSelections(fields: CleanField[]): FieldNode[] { - return fields.map((field) => t.field({ name: field.name })); -} - -/** - * Create pageInfo selection - */ -function createPageInfoSelection(): FieldNode { - return t.field({ - name: 'pageInfo', - selectionSet: t.selectionSet({ - selections: [ - t.field({ name: 'hasNextPage' }), - t.field({ name: 'hasPreviousPage' }), - t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }), - ], - }), - }); -} - -// ============================================================================ -// List query builder -// ============================================================================ - -export interface ListQueryConfig { - table: CleanTable; -} - -/** - * Build a list query AST for a table - */ -export function buildListQueryAST(config: ListQueryConfig): DocumentNode { - const { table } = config; - const queryName = getAllRowsQueryName(table); - const filterType = getFilterTypeName(table); - const conditionType = getConditionTypeName(table); - const orderByType = getOrderByTypeName(table); - const scalarFields = getScalarFields(table); - - // Variable definitions - all pagination arguments from PostGraphile - const variableDefinitions: VariableDefinitionNode[] = [ - t.variableDefinition({ - variable: t.variable({ name: 'first' }), - type: t.namedType({ type: 'Int' }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'last' }), - type: t.namedType({ type: 'Int' }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'offset' }), - type: t.namedType({ type: 'Int' }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'before' }), - type: t.namedType({ type: 'Cursor' }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'after' }), - type: t.namedType({ type: 'Cursor' }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: filterType }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'condition' }), - type: t.namedType({ type: conditionType }), - }), - t.variableDefinition({ - variable: t.variable({ name: 'orderBy' }), - type: t.listType({ - type: t.nonNullType({ type: t.namedType({ type: orderByType }) }), - }), - }), - ]; - - // Query arguments - const args: ArgumentNode[] = [ - t.argument({ name: 'first', value: t.variable({ name: 'first' }) }), - t.argument({ name: 'last', value: t.variable({ name: 'last' }) }), - t.argument({ name: 'offset', value: t.variable({ name: 'offset' }) }), - t.argument({ name: 'before', value: t.variable({ name: 'before' }) }), - t.argument({ name: 'after', value: t.variable({ name: 'after' }) }), - t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }), - t.argument({ name: 'condition', value: t.variable({ name: 'condition' }) }), - t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }), - ]; - - // Field selections - const fieldSelections = createFieldSelections(scalarFields); - - // Connection fields - const connectionFields: FieldNode[] = [ - t.field({ name: 'totalCount' }), - t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections: fieldSelections }), - }), - createPageInfoSelection(), - ]; - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'query', - name: `${ucFirst(queryName)}Query`, - variableDefinitions, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: queryName, - args, - selectionSet: t.selectionSet({ selections: connectionFields }), - }), - ], - }), - }), - ], - }); -} - -// ============================================================================ -// Single item query builder -// ============================================================================ - -export interface SingleQueryConfig { - table: CleanTable; -} - -/** - * Build a single item query AST for a table - */ -export function buildSingleQueryAST(config: SingleQueryConfig): DocumentNode { - const { table } = config; - const queryName = getSingleRowQueryName(table); - const scalarFields = getScalarFields(table); - - // Get primary key info dynamically from table constraints - const pkFields = getPrimaryKeyInfo(table); - // For simplicity, use first PK field (most common case) - const pkField = pkFields[0]; - - // Variable definitions - use dynamic PK field name and type - const variableDefinitions: VariableDefinitionNode[] = [ - t.variableDefinition({ - variable: t.variable({ name: pkField.name }), - type: t.nonNullType({ type: t.namedType({ type: pkField.gqlType }) }), - }), - ]; - - // Query arguments - use dynamic PK field name - const args: ArgumentNode[] = [ - t.argument({ name: pkField.name, value: t.variable({ name: pkField.name }) }), - ]; - - // Field selections - const fieldSelections = createFieldSelections(scalarFields); - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'query', - name: `${ucFirst(queryName)}Query`, - variableDefinitions, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: queryName, - args, - selectionSet: t.selectionSet({ selections: fieldSelections }), - }), - ], - }), - }), - ], - }); -} - -// ============================================================================ -// Create mutation builder -// ============================================================================ - -export interface CreateMutationConfig { - table: CleanTable; -} - -/** - * Build a create mutation AST for a table - */ -export function buildCreateMutationAST(config: CreateMutationConfig): DocumentNode { - const { table } = config; - const { typeName, singularName } = getTableNames(table); - const mutationName = getCreateMutationName(table); - const inputTypeName = `Create${typeName}Input`; - const scalarFields = getScalarFields(table); - - // Variable definitions - const variableDefinitions: VariableDefinitionNode[] = [ - t.variableDefinition({ - variable: t.variable({ name: 'input' }), - type: t.nonNullType({ type: t.namedType({ type: inputTypeName }) }), - }), - ]; - - // Mutation arguments - const args: ArgumentNode[] = [ - t.argument({ name: 'input', value: t.variable({ name: 'input' }) }), - ]; - - // Field selections - const fieldSelections = createFieldSelections(scalarFields); - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'mutation', - name: `${ucFirst(mutationName)}Mutation`, - variableDefinitions, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: mutationName, - args, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: singularName, - selectionSet: t.selectionSet({ selections: fieldSelections }), - }), - ], - }), - }), - ], - }), - }), - ], - }); -} - -// ============================================================================ -// Update mutation builder -// ============================================================================ - -export interface UpdateMutationConfig { - table: CleanTable; -} - -/** - * Build an update mutation AST for a table - */ -export function buildUpdateMutationAST(config: UpdateMutationConfig): DocumentNode { - const { table } = config; - const { typeName, singularName } = getTableNames(table); - const mutationName = getUpdateMutationName(table); - const inputTypeName = `Update${typeName}Input`; - const scalarFields = getScalarFields(table); - - // Variable definitions - const variableDefinitions: VariableDefinitionNode[] = [ - t.variableDefinition({ - variable: t.variable({ name: 'input' }), - type: t.nonNullType({ type: t.namedType({ type: inputTypeName }) }), - }), - ]; - - // Mutation arguments - const args: ArgumentNode[] = [ - t.argument({ name: 'input', value: t.variable({ name: 'input' }) }), - ]; - - // Field selections - const fieldSelections = createFieldSelections(scalarFields); - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'mutation', - name: `${ucFirst(mutationName)}Mutation`, - variableDefinitions, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: mutationName, - args, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: singularName, - selectionSet: t.selectionSet({ selections: fieldSelections }), - }), - ], - }), - }), - ], - }), - }), - ], - }); -} - -// ============================================================================ -// Delete mutation builder -// ============================================================================ - -export interface DeleteMutationConfig { - table: CleanTable; -} - -/** - * Build a delete mutation AST for a table - */ -export function buildDeleteMutationAST(config: DeleteMutationConfig): DocumentNode { - const { table } = config; - const { typeName } = getTableNames(table); - const mutationName = getDeleteMutationName(table); - const inputTypeName = `Delete${typeName}Input`; - - // Variable definitions - const variableDefinitions: VariableDefinitionNode[] = [ - t.variableDefinition({ - variable: t.variable({ name: 'input' }), - type: t.nonNullType({ type: t.namedType({ type: inputTypeName }) }), - }), - ]; - - // Mutation arguments - const args: ArgumentNode[] = [ - t.argument({ name: 'input', value: t.variable({ name: 'input' }) }), - ]; - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'mutation', - name: `${ucFirst(mutationName)}Mutation`, - variableDefinitions, - selectionSet: t.selectionSet({ - selections: [ - t.field({ - name: mutationName, - args, - selectionSet: t.selectionSet({ - selections: [ - t.field({ name: 'clientMutationId' }), - ], - }), - }), - ], - }), - }), - ], - }); -} - -// ============================================================================ -// Print utilities -// ============================================================================ - -/** - * Print AST to GraphQL string - */ -export function printGraphQL(ast: DocumentNode): string { - return print(ast); -} diff --git a/graphql/codegen/src/core/codegen/index.ts b/graphql/codegen/src/core/codegen/index.ts index 407e10527..b77bad93d 100644 --- a/graphql/codegen/src/core/codegen/index.ts +++ b/graphql/codegen/src/core/codegen/index.ts @@ -2,52 +2,48 @@ * Code generation orchestrator * * Coordinates all code generators to produce the complete SDK output. + * Hooks delegate to ORM model methods - types are imported from ORM's input-types.ts. * * Output structure: * generated/ * index.ts - Main barrel export - * client.ts - GraphQL client with configure() and execute() - * types.ts - Entity interfaces and filter types + * client.ts - ORM client wrapper (configure/getClient) * queries/ * index.ts - Query hooks barrel - * useCarsQuery.ts - List query hook (table-based) - * useCarQuery.ts - Single item query hook (table-based) - * useCurrentUserQuery.ts - Custom query hook + * useCarsQuery.ts - List query hook -> ORM findMany + * useCarQuery.ts - Single item query hook -> ORM findOne + * useCurrentUserQuery.ts - Custom query hook -> ORM query.xxx * ... * mutations/ * index.ts - Mutation hooks barrel - * useCreateCarMutation.ts - Table-based CRUD - * useUpdateCarMutation.ts - * useDeleteCarMutation.ts - * useLoginMutation.ts - Custom mutation - * useRegisterMutation.ts + * useCreateCarMutation.ts - -> ORM create + * useUpdateCarMutation.ts - -> ORM update + * useDeleteCarMutation.ts - -> ORM delete + * useLoginMutation.ts - Custom mutation -> ORM mutation.xxx * ... */ +import type { GraphQLSDKConfigTarget, QueryKeyConfig } from '../../types/config'; +import { DEFAULT_QUERY_KEY_CONFIG } from '../../types/config'; import type { - CleanTable, CleanOperation, - TypeRegistry, + CleanTable, + TypeRegistry } from '../../types/schema'; -import type { GraphQLSDKConfigTarget, QueryKeyConfig } from '../../types/config'; -import { DEFAULT_QUERY_KEY_CONFIG } from '../../types/config'; - -import { generateClientFile } from './client'; -import { generateTypesFile } from './types'; -import { generateSchemaTypesFile } from './schema-types-generator'; -import { generateAllQueryHooks } from './queries'; -import { generateAllMutationHooks } from './mutations'; -import { generateAllCustomQueryHooks } from './custom-queries'; -import { generateAllCustomMutationHooks } from './custom-mutations'; -import { generateQueryKeysFile } from './query-keys'; -import { generateMutationKeysFile } from './mutation-keys'; -import { generateInvalidationFile } from './invalidation'; import { - generateQueriesBarrel, - generateMutationsBarrel, - generateMainBarrel, - generateCustomQueriesBarrel, generateCustomMutationsBarrel, + generateCustomQueriesBarrel, + generateMainBarrel, + generateMutationsBarrel, + generateQueriesBarrel } from './barrel'; +import { generateClientFile } from './client'; +import { generateAllCustomMutationHooks } from './custom-mutations'; +import { generateAllCustomQueryHooks } from './custom-queries'; +import { generateInvalidationFile } from './invalidation'; +import { generateMutationKeysFile } from './mutation-keys'; +import { generateAllMutationHooks } from './mutations'; +import { generateAllQueryHooks } from './queries'; +import { generateQueryKeysFile } from './query-keys'; import { getTableNames } from './utils'; // ============================================================================ @@ -114,7 +110,7 @@ export function generateAllFiles( * (they're expected to exist in the shared types directory). */ export function generate(options: GenerateOptions): GenerateResult { - const { tables, customOperations, config, sharedTypesPath } = options; + const { tables, customOperations, config } = options; const files: GeneratedFile[] = []; // Extract codegen options @@ -127,62 +123,18 @@ export function generate(options: GenerateOptions): GenerateResult { const useCentralizedKeys = queryKeyConfig.generateScopedKeys; const hasRelationships = Object.keys(queryKeyConfig.relationships).length > 0; - // 1. Generate client.ts + // 1. Generate client.ts (ORM client wrapper) files.push({ path: 'client.ts', - content: generateClientFile({ - browserCompatible: config.browserCompatible ?? true, - }), + content: generateClientFile() }); // Collect table type names for import path resolution const tableTypeNames = new Set(tables.map((t) => getTableNames(t).typeName)); - // When using shared types, skip generating types.ts and schema-types.ts - // They're already generated in the shared directory - let hasSchemaTypes = false; - let generatedEnumNames: string[] = []; - - if (sharedTypesPath) { - // Using shared types - check if schema-types would be generated - if (customOperations && customOperations.typeRegistry) { - const schemaTypesResult = generateSchemaTypesFile({ - typeRegistry: customOperations.typeRegistry, - tableTypeNames, - }); - if (schemaTypesResult.content.split('\n').length > 10) { - hasSchemaTypes = true; - generatedEnumNames = schemaTypesResult.generatedEnums || []; - } - } - } else { - // 2. Generate schema-types.ts for custom operations (if any) - // NOTE: This must come BEFORE types.ts so that types.ts can import enum types - if (customOperations && customOperations.typeRegistry) { - const schemaTypesResult = generateSchemaTypesFile({ - typeRegistry: customOperations.typeRegistry, - tableTypeNames, - }); - - // Only include if there's meaningful content - if (schemaTypesResult.content.split('\n').length > 10) { - files.push({ - path: 'schema-types.ts', - content: schemaTypesResult.content, - }); - hasSchemaTypes = true; - generatedEnumNames = schemaTypesResult.generatedEnums || []; - } - } - - // 3. Generate types.ts (can now import enums from schema-types) - files.push({ - path: 'types.ts', - content: generateTypesFile(tables, { - enumsFromSchemaTypes: generatedEnumNames, - }), - }); - } + // NOTE: types.ts and schema-types.ts are no longer generated here. + // Hooks now import types directly from the ORM's input-types.ts, + // which serves as the single source of truth for all types. // 3b. Generate centralized query keys (query-keys.ts) let hasQueryKeys = false; @@ -190,11 +142,11 @@ export function generate(options: GenerateOptions): GenerateResult { const queryKeysResult = generateQueryKeysFile({ tables, customQueries: customOperations?.queries ?? [], - config: queryKeyConfig, + config: queryKeyConfig }); files.push({ path: queryKeysResult.fileName, - content: queryKeysResult.content, + content: queryKeysResult.content }); hasQueryKeys = true; } @@ -205,11 +157,11 @@ export function generate(options: GenerateOptions): GenerateResult { const mutationKeysResult = generateMutationKeysFile({ tables, customMutations: customOperations?.mutations ?? [], - config: queryKeyConfig, + config: queryKeyConfig }); files.push({ path: mutationKeysResult.fileName, - content: mutationKeysResult.content, + content: mutationKeysResult.content }); hasMutationKeys = true; } @@ -219,11 +171,11 @@ export function generate(options: GenerateOptions): GenerateResult { if (useCentralizedKeys && queryKeyConfig.generateCascadeHelpers) { const invalidationResult = generateInvalidationFile({ tables, - config: queryKeyConfig, + config: queryKeyConfig }); files.push({ path: invalidationResult.fileName, - content: invalidationResult.content, + content: invalidationResult.content }); hasInvalidation = true; } @@ -232,12 +184,12 @@ export function generate(options: GenerateOptions): GenerateResult { const queryHooks = generateAllQueryHooks(tables, { reactQueryEnabled, useCentralizedKeys, - hasRelationships, + hasRelationships }); for (const hook of queryHooks) { files.push({ path: `queries/${hook.fileName}`, - content: hook.content, + content: hook.content }); } @@ -255,13 +207,13 @@ export function generate(options: GenerateOptions): GenerateResult { skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys, + useCentralizedKeys }); for (const hook of customQueryHooks) { files.push({ path: `queries/${hook.fileName}`, - content: hook.content, + content: hook.content }); } } @@ -272,24 +224,21 @@ export function generate(options: GenerateOptions): GenerateResult { content: customQueryHooks.length > 0 ? generateCustomQueriesBarrel( - tables, - customQueryHooks.map((h) => h.operationName) - ) - : generateQueriesBarrel(tables), + tables, + customQueryHooks.map((h) => h.operationName) + ) + : generateQueriesBarrel(tables) }); // 6. Generate table-based mutation hooks (mutations/*.ts) const mutationHooks = generateAllMutationHooks(tables, { reactQueryEnabled, - enumsFromSchemaTypes: generatedEnumNames, - useCentralizedKeys, - hasRelationships, - tableTypeNames, + useCentralizedKeys }); for (const hook of mutationHooks) { files.push({ path: `mutations/${hook.fileName}`, - content: hook.content, + content: hook.content }); } @@ -307,13 +256,13 @@ export function generate(options: GenerateOptions): GenerateResult { skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys, + useCentralizedKeys }); for (const hook of customMutationHooks) { files.push({ path: `mutations/${hook.fileName}`, - content: hook.content, + content: hook.content }); } } @@ -328,23 +277,23 @@ export function generate(options: GenerateOptions): GenerateResult { content: customMutationHooks.length > 0 ? generateCustomMutationsBarrel( - tables, - customMutationHooks.map((h) => h.operationName) - ) - : generateMutationsBarrel(tables), + tables, + customMutationHooks.map((h) => h.operationName) + ) + : generateMutationsBarrel(tables) }); } - // 9. Generate main index.ts barrel (with schema-types if present) + // 9. Generate main index.ts barrel + // No longer includes types.ts or schema-types.ts - hooks import from ORM directly files.push({ path: 'index.ts', content: generateMainBarrel(tables, { - hasSchemaTypes, hasMutations, hasQueryKeys, hasMutationKeys, - hasInvalidation, - }), + hasInvalidation + }) }); return { @@ -355,8 +304,8 @@ export function generate(options: GenerateOptions): GenerateResult { mutationHooks: mutationHooks.length, customQueryHooks: customQueryHooks.length, customMutationHooks: customMutationHooks.length, - totalFiles: files.length, - }, + totalFiles: files.length + } }; } @@ -364,34 +313,33 @@ export function generate(options: GenerateOptions): GenerateResult { // Re-exports for convenience // ============================================================================ +export { + generateCustomMutationsBarrel, + generateCustomQueriesBarrel, + generateMainBarrel, + generateMutationsBarrel, + generateQueriesBarrel +} from './barrel'; export { generateClientFile } from './client'; -export { generateTypesFile } from './types'; export { - generateAllQueryHooks, - generateListQueryHook, - generateSingleQueryHook, -} from './queries'; + generateAllCustomMutationHooks, + generateCustomMutationHook +} from './custom-mutations'; +export { + generateAllCustomQueryHooks, + generateCustomQueryHook +} from './custom-queries'; +export { generateInvalidationFile } from './invalidation'; +export { generateMutationKeysFile } from './mutation-keys'; export { generateAllMutationHooks, generateCreateMutationHook, - generateUpdateMutationHook, generateDeleteMutationHook, + generateUpdateMutationHook } from './mutations'; export { - generateAllCustomQueryHooks, - generateCustomQueryHook, -} from './custom-queries'; -export { - generateAllCustomMutationHooks, - generateCustomMutationHook, -} from './custom-mutations'; -export { - generateQueriesBarrel, - generateMutationsBarrel, - generateMainBarrel, - generateCustomQueriesBarrel, - generateCustomMutationsBarrel, -} from './barrel'; + generateAllQueryHooks, + generateListQueryHook, + generateSingleQueryHook +} from './queries'; export { generateQueryKeysFile } from './query-keys'; -export { generateMutationKeysFile } from './mutation-keys'; -export { generateInvalidationFile } from './invalidation'; diff --git a/graphql/codegen/src/core/codegen/invalidation.ts b/graphql/codegen/src/core/codegen/invalidation.ts index 96247303e..ef9a5bf4b 100644 --- a/graphql/codegen/src/core/codegen/invalidation.ts +++ b/graphql/codegen/src/core/codegen/invalidation.ts @@ -4,17 +4,18 @@ * Generates type-safe cache invalidation utilities with cascade support * for parent-child entity relationships. */ -import type { CleanTable } from '../../types/schema'; -import type { QueryKeyConfig, EntityRelationship } from '../../types/config'; -import { getTableNames, getGeneratedFileHeader, ucFirst, lcFirst } from './utils'; import * as t from '@babel/types'; + +import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; +import type { CleanTable } from '../../types/schema'; import { - generateCode, addJSDocComment, - asConst, - typedParam, addLineComment, + asConst, + generateCode, + typedParam } from './babel-ast'; +import { getGeneratedFileHeader, getTableNames, lcFirst,ucFirst } from './utils'; export interface InvalidationGeneratorOptions { tables: CleanTable[]; @@ -247,7 +248,7 @@ function buildEntityInvalidateProperty( const withChildrenProp = t.objectProperty(t.identifier('withChildren'), withChildrenArrowFn); addJSDocComment(withChildrenProp, [ `Invalidate ${singularName} and all child entities`, - `Cascades to: ${descendants.join(', ')}`, + `Cascades to: ${descendants.join(', ')}` ]); innerProperties.push(withChildrenProp); } @@ -397,7 +398,7 @@ export function generateInvalidationFile( 'invalidate.user.lists(queryClient);', '', '// Invalidate specific user', - 'invalidate.user.detail(queryClient, userId);', + 'invalidate.user.detail(queryClient, userId);' ]; if (generateCascadeHelpers && Object.keys(relationships).length > 0) { invalidateDocLines.push(''); @@ -426,7 +427,7 @@ export function generateInvalidationFile( 'Remove queries from cache (for delete operations)', '', 'Use these when an entity is deleted to remove it from cache', - 'instead of just invalidating (which would trigger a refetch).', + 'instead of just invalidating (which would trigger a refetch).' ]); statements.push(removeDecl); @@ -484,6 +485,6 @@ ${description} return { fileName: 'invalidation.ts', - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/mutation-keys.ts b/graphql/codegen/src/core/codegen/mutation-keys.ts index 431d84fe3..5816e3ee1 100644 --- a/graphql/codegen/src/core/codegen/mutation-keys.ts +++ b/graphql/codegen/src/core/codegen/mutation-keys.ts @@ -7,17 +7,18 @@ * - Mutation deduplication * - Tracking mutation state with useIsMutating */ -import type { CleanTable, CleanOperation } from '../../types/schema'; -import type { QueryKeyConfig, EntityRelationship } from '../../types/config'; -import { getTableNames, getGeneratedFileHeader, lcFirst } from './utils'; import * as t from '@babel/types'; + +import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; +import type { CleanOperation,CleanTable } from '../../types/schema'; import { - generateCode, addJSDocComment, asConst, constArray, - typedParam, + generateCode, + typedParam } from './babel-ast'; +import { getGeneratedFileHeader, getTableNames, lcFirst } from './utils'; export interface MutationKeyGeneratorOptions { tables: CleanTable[]; @@ -75,12 +76,12 @@ function generateEntityMutationKeysDeclaration( false, true ) - ]), + ]) ]), constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), - t.stringLiteral('create'), + t.stringLiteral('create') ]) ) ); @@ -92,7 +93,7 @@ function generateEntityMutationKeysDeclaration( constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), - t.stringLiteral('create'), + t.stringLiteral('create') ]) ); @@ -108,7 +109,7 @@ function generateEntityMutationKeysDeclaration( t.stringLiteral('mutation'), t.stringLiteral(entityKey), t.stringLiteral('update'), - t.identifier('id'), + t.identifier('id') ]) ); const updateProp = t.objectProperty(t.identifier('update'), updateArrowFn); @@ -122,7 +123,7 @@ function generateEntityMutationKeysDeclaration( t.stringLiteral('mutation'), t.stringLiteral(entityKey), t.stringLiteral('delete'), - t.identifier('id'), + t.identifier('id') ]) ); const deleteProp = t.objectProperty(t.identifier('delete'), deleteArrowFn); @@ -166,7 +167,7 @@ function generateCustomMutationKeysDeclaration( constArray([ t.stringLiteral('mutation'), t.stringLiteral(op.name), - t.identifier('identifier'), + t.identifier('identifier') ]), constArray([t.stringLiteral('mutation'), t.stringLiteral(op.name)]) ) @@ -243,7 +244,7 @@ function generateUnifiedMutationStoreDeclaration( '', '// Check if a specific user is being updated', 'const isUpdating = useIsMutating({ mutationKey: mutationKeys.user.update(userId) });', - '```', + '```' ]); return decl; @@ -335,6 +336,6 @@ ${description} return { fileName: 'mutation-keys.ts', - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 0a3a75227..8e368337e 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -1,51 +1,31 @@ /** - * Mutation hook generators using Babel AST-based code generation + * Mutation hook generators - delegates to ORM model methods * * Output structure: * mutations/ - * useCreateCarMutation.ts - * useUpdateCarMutation.ts - * useDeleteCarMutation.ts + * useCreateCarMutation.ts -> ORM create + * useUpdateCarMutation.ts -> ORM update + * useDeleteCarMutation.ts -> ORM delete */ import type { CleanTable } from '../../types/schema'; -import * as t from '@babel/types'; -import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast'; import { - buildCreateMutationAST, - buildUpdateMutationAST, - buildDeleteMutationAST, - printGraphQL, -} from './gql-ast'; -import { - getTableNames, - getCreateMutationHookName, - getUpdateMutationHookName, - getDeleteMutationHookName, getCreateMutationFileName, - getUpdateMutationFileName, - getDeleteMutationFileName, + getCreateMutationHookName, getCreateMutationName, - getUpdateMutationName, + getDefaultSelectFieldName, + getDeleteMutationFileName, + getDeleteMutationHookName, getDeleteMutationName, - getScalarFields, - getPrimaryKeyInfo, - fieldTypeToTs, - ucFirst, - lcFirst, getGeneratedFileHeader, + getPrimaryKeyInfo, + getTableNames, + getUpdateMutationFileName, + getUpdateMutationHookName, + getUpdateMutationName, + hasValidPrimaryKey, + lcFirst } from './utils'; -function isAutoGeneratedField(fieldName: string, pkFieldNames: Set): boolean { - const name = fieldName.toLowerCase(); - if (pkFieldNames.has(fieldName)) return true; - const timestampPatterns = [ - 'createdat', 'created_at', 'createddate', 'created_date', - 'updatedat', 'updated_at', 'updateddate', 'updated_date', - 'deletedat', 'deleted_at', - ]; - return timestampPatterns.includes(name); -} - export interface GeneratedMutationFile { fileName: string; content: string; @@ -53,11 +33,7 @@ export interface GeneratedMutationFile { export interface MutationGeneratorOptions { reactQueryEnabled?: boolean; - enumsFromSchemaTypes?: string[]; useCentralizedKeys?: boolean; - hasRelationships?: boolean; - /** All table type names for determining which types to import from types.ts vs schema-types.ts */ - tableTypeNames?: Set; } export function generateCreateMutationHook( @@ -66,293 +42,95 @@ export function generateCreateMutationHook( ): GeneratedMutationFile | null { const { reactQueryEnabled = true, - enumsFromSchemaTypes = [], - useCentralizedKeys = true, - hasRelationships = false, - tableTypeNames = new Set(), + useCentralizedKeys = true } = options; if (!reactQueryEnabled) { return null; } - const enumSet = new Set(enumsFromSchemaTypes); const { typeName, singularName } = getTableNames(table); const hookName = getCreateMutationHookName(table); + const mutationName = getCreateMutationName(table); const keysName = `${lcFirst(typeName)}Keys`; const mutationKeysName = `${lcFirst(typeName)}MutationKeys`; - const scopeTypeName = `${typeName}Scope`; - const mutationName = getCreateMutationName(table); - const scalarFields = getScalarFields(table); - - const pkFieldNames = new Set(getPrimaryKeyInfo(table).map((pk) => pk.name)); - - const usedEnums = new Set(); - const usedTableTypes = new Set(); - for (const field of scalarFields) { - const cleanType = field.type.gqlType.replace(/!/g, ''); - if (enumSet.has(cleanType)) { - usedEnums.add(cleanType); - } else if (tableTypeNames.has(cleanType) && cleanType !== typeName) { - // Track table types used in scalar fields (excluding the main type which is already imported) - usedTableTypes.add(cleanType); - } - } + const selectTypeName = `${typeName}Select`; + const relationTypeName = `${typeName}WithRelations`; + const createInputTypeName = `Create${typeName}Input`; + + const defaultFieldName = getDefaultSelectFieldName(table); + + const lines: string[] = []; - const mutationAST = buildCreateMutationAST({ table }); - const mutationDocument = printGraphQL(mutationAST); - - const statements: t.Statement[] = []; - - const reactQueryImport = t.importDeclaration( - [ - t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')), - t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')), - ], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - - const reactQueryTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - - // Import the main type and any other table types used in scalar fields - const allTypesToImport = [typeName, ...Array.from(usedTableTypes)].sort(); - const typesImport = t.importDeclaration( - allTypesToImport.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); - - if (usedEnums.size > 0) { - const enumImport = t.importDeclaration( - Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), - t.stringLiteral('../schema-types') - ); - enumImport.importKind = 'type'; - statements.push(enumImport); + // Imports + lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import { getClient } from '../client';`); + + if (useCentralizedKeys) { + lines.push(`import { ${keysName} } from '../query-keys';`); + lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); } + lines.push(`import type {`); + lines.push(` ${selectTypeName},`); + lines.push(` ${relationTypeName},`); + lines.push(` ${createInputTypeName},`); + lines.push(`} from '../../orm/input-types';`); + lines.push(`import type {`); + lines.push(` DeepExact,`); + lines.push(` InferSelectResult,`); + lines.push(`} from '../../orm/select-types';`); + lines.push(''); + + // Re-export types + lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${createInputTypeName} } from '../../orm/input-types';`); + lines.push(''); + + lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); + lines.push(''); + + // Hook + lines.push(`/**`); + lines.push(` * Mutation hook for creating a ${typeName}`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`tsx`); + lines.push(` * const { mutate, isPending } = ${hookName}({`); + lines.push(` * select: { id: true, name: true },`); + lines.push(` * });`); + lines.push(` *`); + lines.push(` * mutate({ name: 'New item' });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export function ${hookName}(`); + lines.push(` args?: { select?: DeepExact },`); + lines.push(` options?: Omit } }, Error, ${createInputTypeName}['${singularName}']>, 'mutationFn'>`); + lines.push(`) {`); + lines.push(` const queryClient = useQueryClient();`); + lines.push(` return useMutation({`); + if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); - if (hasRelationships) { - const scopeTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], - t.stringLiteral('../query-keys') - ); - scopeTypeImport.importKind = 'type'; - statements.push(scopeTypeImport); - } - const mutationKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], - t.stringLiteral('../mutation-keys') - ); - statements.push(mutationKeyImport); + lines.push(` mutationKey: ${mutationKeysName}.create(),`); } - const reExportDecl = t.exportNamedDeclaration( - null, - [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], - t.stringLiteral('../types') - ); - reExportDecl.exportKind = 'type'; - statements.push(reExportDecl); - - const mutationDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${mutationName}MutationDocument`), - t.templateLiteral( - [t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], - [] - ) - ), - ]); - statements.push(t.exportNamedDeclaration(mutationDocConst)); - - const inputFields = scalarFields - .filter((f) => !isAutoGeneratedField(f.name, pkFieldNames)) - .map((f) => { - const prop = t.tsPropertySignature( - t.identifier(f.name), - t.tsTypeAnnotation( - t.tsUnionType([ - t.tsTypeReference(t.identifier(fieldTypeToTs(f.type))), - t.tsNullKeyword(), - ]) - ) - ); - prop.optional = true; - return prop; - }); - - const createInputInterface = t.tsInterfaceDeclaration( - t.identifier(`${typeName}CreateInput`), - null, - null, - t.tsInterfaceBody(inputFields) - ); - addJSDocComment(createInputInterface, [`Input type for creating a ${typeName}`]); - statements.push(createInputInterface); - - const variablesInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier('input'), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier(lcFirst(typeName)), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}CreateInput`))) - ), - ]) - ) - ), - ]); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationVariables`), - null, - null, - variablesInterfaceBody - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); - - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(mutationName), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier(singularName), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName))) - ), - ]) - ) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationResult`), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); - - const hookBodyStatements: t.Statement[] = []; - hookBodyStatements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('queryClient'), - t.callExpression(t.identifier('useQueryClient'), []) - ), - ]) - ); - - const mutationOptions: (t.ObjectProperty | t.SpreadElement)[] = []; - if (useCentralizedKeys) { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationKey'), - t.callExpression( - t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), - [] - ) - ) - ); - } - mutationOptions.push( - t.objectProperty( - t.identifier('mutationFn'), - t.arrowFunctionExpression( - [typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)), - ] - ) - ) - ) - ); - - const invalidateQueryKey = useCentralizedKeys - ? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); - - mutationOptions.push( - t.objectProperty( - t.identifier('onSuccess'), - t.arrowFunctionExpression( - [], - t.blockStatement([ - t.expressionStatement( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), invalidateQueryKey)])] - ) - ), - ]) - ) - ) - ); - mutationOptions.push(t.spreadElement(t.identifier('options'))); - - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)]) - ) - ); - - const optionsTypeStr = `Omit, 'mutationFn'>`; - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr))); - - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - [optionsParam], - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - addJSDocComment(hookExport, [ - `Mutation hook for creating a ${typeName}`, - '', - '@example', - '```tsx', - `const { mutate, isPending } = ${hookName}();`, - '', - 'mutate({', - ' input: {', - ` ${lcFirst(typeName)}: {`, - ' // ... fields', - ' },', - ' },', - '});', - '```', - ]); - statements.push(hookExport); - - const code = generateCode(statements); - const content = getGeneratedFileHeader(`Create mutation hook for ${typeName}`) + '\n\n' + code; + lines.push(` mutationFn: (data: ${createInputTypeName}['${singularName}']) => getClient().${singularName}.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + + const listKey = useCentralizedKeys + ? `${keysName}.lists()` + : `['${typeName.toLowerCase()}', 'list']`; + lines.push(` onSuccess: () => {`); + lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); + lines.push(` },`); + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); + + const content = getGeneratedFileHeader(`Create mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getCreateMutationFileName(table), - content, + content }; } @@ -362,10 +140,7 @@ export function generateUpdateMutationHook( ): GeneratedMutationFile | null { const { reactQueryEnabled = true, - enumsFromSchemaTypes = [], - useCentralizedKeys = true, - hasRelationships = false, - tableTypeNames = new Set(), + useCentralizedKeys = true } = options; if (!reactQueryEnabled) { @@ -376,307 +151,98 @@ export function generateUpdateMutationHook( return null; } - const enumSet = new Set(enumsFromSchemaTypes); + if (!hasValidPrimaryKey(table)) { + return null; + } + const { typeName, singularName } = getTableNames(table); const hookName = getUpdateMutationHookName(table); const mutationName = getUpdateMutationName(table); - const scalarFields = getScalarFields(table); const keysName = `${lcFirst(typeName)}Keys`; const mutationKeysName = `${lcFirst(typeName)}MutationKeys`; - const scopeTypeName = `${typeName}Scope`; + const selectTypeName = `${typeName}Select`; + const relationTypeName = `${typeName}WithRelations`; + const patchTypeName = `${typeName}Patch`; const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pkFieldNames = new Set(pkFields.map((pk) => pk.name)); - - const usedEnums = new Set(); - const usedTableTypes = new Set(); - for (const field of scalarFields) { - const cleanType = field.type.gqlType.replace(/!/g, ''); - if (enumSet.has(cleanType)) { - usedEnums.add(cleanType); - } else if (tableTypeNames.has(cleanType) && cleanType !== typeName) { - usedTableTypes.add(cleanType); - } - } - const mutationAST = buildUpdateMutationAST({ table }); - const mutationDocument = printGraphQL(mutationAST); - - const statements: t.Statement[] = []; - - const reactQueryImport = t.importDeclaration( - [ - t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')), - t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')), - ], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - - const reactQueryTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - - // Import the main type and any other table types used in scalar fields - const allTypesToImportUpdate = [typeName, ...Array.from(usedTableTypes)].sort(); - const typesImport = t.importDeclaration( - allTypesToImportUpdate.map((t_) => t.importSpecifier(t.identifier(t_), t.identifier(t_))), - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); - - if (usedEnums.size > 0) { - const enumImport = t.importDeclaration( - Array.from(usedEnums).sort().map((e) => t.importSpecifier(t.identifier(e), t.identifier(e))), - t.stringLiteral('../schema-types') - ); - enumImport.importKind = 'type'; - statements.push(enumImport); - } + const lines: string[] = []; + + // Imports + lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import { getClient } from '../client';`); if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); - if (hasRelationships) { - const scopeTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], - t.stringLiteral('../query-keys') - ); - scopeTypeImport.importKind = 'type'; - statements.push(scopeTypeImport); - } - const mutationKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], - t.stringLiteral('../mutation-keys') - ); - statements.push(mutationKeyImport); + lines.push(`import { ${keysName} } from '../query-keys';`); + lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); } - const reExportDecl = t.exportNamedDeclaration( - null, - [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], - t.stringLiteral('../types') - ); - reExportDecl.exportKind = 'type'; - statements.push(reExportDecl); - - const mutationDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${mutationName}MutationDocument`), - t.templateLiteral( - [t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], - [] - ) - ), - ]); - statements.push(t.exportNamedDeclaration(mutationDocConst)); - - const patchFields = scalarFields - .filter((f) => !pkFieldNames.has(f.name)) - .map((f) => { - const prop = t.tsPropertySignature( - t.identifier(f.name), - t.tsTypeAnnotation( - t.tsUnionType([ - t.tsTypeReference(t.identifier(fieldTypeToTs(f.type))), - t.tsNullKeyword(), - ]) - ) - ); - prop.optional = true; - return prop; - }); - - const patchInterface = t.tsInterfaceDeclaration( - t.identifier(`${typeName}Patch`), - null, - null, - t.tsInterfaceBody(patchFields) - ); - addJSDocComment(patchInterface, [`Patch type for updating a ${typeName} - all fields optional`]); - statements.push(patchInterface); - - const pkTypeAnnotation = - pkField.tsType === 'string' - ? t.tsStringKeyword() - : pkField.tsType === 'number' - ? t.tsNumberKeyword() - : t.tsTypeReference(t.identifier(pkField.tsType)); - - const variablesInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier('input'), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)), - t.tsPropertySignature( - t.identifier('patch'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(`${typeName}Patch`))) - ), - ]) - ) - ), - ]); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationVariables`), - null, - null, - variablesInterfaceBody - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); - - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(mutationName), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier(singularName), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(typeName))) - ), - ]) - ) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationResult`), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); - - const hookBodyStatements: t.Statement[] = []; - hookBodyStatements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('queryClient'), - t.callExpression(t.identifier('useQueryClient'), []) - ), - ]) - ); - - const mutationOptions: (t.ObjectProperty | t.SpreadElement)[] = []; + lines.push(`import type {`); + lines.push(` ${selectTypeName},`); + lines.push(` ${relationTypeName},`); + lines.push(` ${patchTypeName},`); + lines.push(`} from '../../orm/input-types';`); + lines.push(`import type {`); + lines.push(` DeepExact,`); + lines.push(` InferSelectResult,`); + lines.push(`} from '../../orm/select-types';`); + lines.push(''); + + // Re-export types + lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${patchTypeName} } from '../../orm/input-types';`); + lines.push(''); + + lines.push(`const defaultSelect = { ${pkField.name}: true } as const;`); + lines.push(''); + + // Hook + lines.push(`/**`); + lines.push(` * Mutation hook for updating a ${typeName}`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`tsx`); + lines.push(` * const { mutate, isPending } = ${hookName}({`); + lines.push(` * select: { id: true, name: true },`); + lines.push(` * });`); + lines.push(` *`); + lines.push(` * mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export function ${hookName}(`); + lines.push(` args?: { select?: DeepExact },`); + lines.push(` options?: Omit } }, Error, { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }>, 'mutationFn'>`); + lines.push(`) {`); + lines.push(` const queryClient = useQueryClient();`); + lines.push(` return useMutation({`); + if (useCentralizedKeys) { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationKey'), - t.memberExpression(t.identifier(mutationKeysName), t.identifier('all')) - ) - ); + lines.push(` mutationKey: ${mutationKeysName}.all,`); } - mutationOptions.push( - t.objectProperty( - t.identifier('mutationFn'), - t.arrowFunctionExpression( - [typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)), - ] - ) - ) - ) - ); - - const detailQueryKey = useCentralizedKeys - ? t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))] - ) - : t.arrayExpression([ - t.stringLiteral(typeName.toLowerCase()), - t.stringLiteral('detail'), - t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)), - ]); - - const listQueryKey = useCentralizedKeys - ? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); - - mutationOptions.push( - t.objectProperty( - t.identifier('onSuccess'), - t.arrowFunctionExpression( - [t.identifier('_'), t.identifier('variables')], - t.blockStatement([ - t.expressionStatement( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])] - ) - ), - t.expressionStatement( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])] - ) - ), - ]) - ) - ) - ); - mutationOptions.push(t.spreadElement(t.identifier('options'))); - - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)]) - ) - ); - - const optionsTypeStr = `Omit, 'mutationFn'>`; - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr))); - - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - [optionsParam], - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - addJSDocComment(hookExport, [ - `Mutation hook for updating a ${typeName}`, - '', - '@example', - '```tsx', - `const { mutate, isPending } = ${hookName}();`, - '', - 'mutate({', - ' input: {', - ` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-here'" : '123'},`, - ' patch: {', - ' // ... fields to update', - ' },', - ' },', - '});', - '```', - ]); - statements.push(hookExport); - - const code = generateCode(statements); - const content = getGeneratedFileHeader(`Update mutation hook for ${typeName}`) + '\n\n' + code; + + lines.push(` mutationFn: ({ ${pkField.name}, patch }: { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }) => getClient().${singularName}.update({ where: { ${pkField.name} }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + + const detailKey = useCentralizedKeys + ? `${keysName}.detail(variables.${pkField.name})` + : `['${typeName.toLowerCase()}', 'detail', variables.${pkField.name}]`; + const listKey = useCentralizedKeys + ? `${keysName}.lists()` + : `['${typeName.toLowerCase()}', 'list']`; + + lines.push(` onSuccess: (_, variables) => {`); + lines.push(` queryClient.invalidateQueries({ queryKey: ${detailKey} });`); + lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); + lines.push(` },`); + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); + + const content = getGeneratedFileHeader(`Update mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getUpdateMutationFileName(table), - content, + content }; } @@ -686,8 +252,7 @@ export function generateDeleteMutationHook( ): GeneratedMutationFile | null { const { reactQueryEnabled = true, - useCentralizedKeys = true, - hasRelationships = false, + useCentralizedKeys = true } = options; if (!reactQueryEnabled) { @@ -698,233 +263,96 @@ export function generateDeleteMutationHook( return null; } - const { typeName } = getTableNames(table); + if (!hasValidPrimaryKey(table)) { + return null; + } + + const { typeName, singularName } = getTableNames(table); const hookName = getDeleteMutationHookName(table); const mutationName = getDeleteMutationName(table); const keysName = `${lcFirst(typeName)}Keys`; const mutationKeysName = `${lcFirst(typeName)}MutationKeys`; - const scopeTypeName = `${typeName}Scope`; + const selectTypeName = `${typeName}Select`; + const relationTypeName = `${typeName}WithRelations`; const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const mutationAST = buildDeleteMutationAST({ table }); - const mutationDocument = printGraphQL(mutationAST); - - const statements: t.Statement[] = []; - - const reactQueryImport = t.importDeclaration( - [ - t.importSpecifier(t.identifier('useMutation'), t.identifier('useMutation')), - t.importSpecifier(t.identifier('useQueryClient'), t.identifier('useQueryClient')), - ], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - - const reactQueryTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier('UseMutationOptions'), t.identifier('UseMutationOptions'))], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); + const lines: string[] = []; + + // Imports + lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import { getClient } from '../client';`); if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); - if (hasRelationships) { - const scopeTypeImport = t.importDeclaration( - [t.importSpecifier(t.identifier(scopeTypeName), t.identifier(scopeTypeName))], - t.stringLiteral('../query-keys') - ); - scopeTypeImport.importKind = 'type'; - statements.push(scopeTypeImport); - } - const mutationKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(mutationKeysName), t.identifier(mutationKeysName))], - t.stringLiteral('../mutation-keys') - ); - statements.push(mutationKeyImport); + lines.push(`import { ${keysName} } from '../query-keys';`); + lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); } - const mutationDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${mutationName}MutationDocument`), - t.templateLiteral( - [t.templateElement({ raw: '\n' + mutationDocument, cooked: '\n' + mutationDocument }, true)], - [] - ) - ), - ]); - statements.push(t.exportNamedDeclaration(mutationDocConst)); - - const pkTypeAnnotation = - pkField.tsType === 'string' - ? t.tsStringKeyword() - : pkField.tsType === 'number' - ? t.tsNumberKeyword() - : t.tsTypeReference(t.identifier(pkField.tsType)); - - const variablesInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier('input'), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTypeAnnotation)), - ]) - ) - ), - ]); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationVariables`), - null, - null, - variablesInterfaceBody - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); - - const clientMutationIdProp = t.tsPropertySignature( - t.identifier('clientMutationId'), - t.tsTypeAnnotation(t.tsUnionType([t.tsStringKeyword(), t.tsNullKeyword()])) - ); - - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(mutationName), - t.tsTypeAnnotation(t.tsTypeLiteral([clientMutationIdProp])) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(mutationName)}MutationResult`), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); - - const hookBodyStatements: t.Statement[] = []; - hookBodyStatements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier('queryClient'), - t.callExpression(t.identifier('useQueryClient'), []) - ), - ]) - ); - - const mutationOptions: (t.ObjectProperty | t.SpreadElement)[] = []; + lines.push(`import type {`); + lines.push(` ${selectTypeName},`); + lines.push(` ${relationTypeName},`); + lines.push(`} from '../../orm/input-types';`); + lines.push(`import type {`); + lines.push(` DeepExact,`); + lines.push(` InferSelectResult,`); + lines.push(`} from '../../orm/select-types';`); + lines.push(''); + + // Re-export types + lines.push(`export type { ${selectTypeName}, ${relationTypeName} } from '../../orm/input-types';`); + lines.push(''); + + lines.push(`const defaultSelect = { ${pkField.name}: true } as const;`); + lines.push(''); + + // Hook + lines.push(`/**`); + lines.push(` * Mutation hook for deleting a ${typeName}`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`tsx`); + lines.push(` * const { mutate, isPending } = ${hookName}({`); + lines.push(` * select: { id: true },`); + lines.push(` * });`); + lines.push(` *`); + lines.push(` * mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export function ${hookName}(`); + lines.push(` args?: { select?: DeepExact },`); + lines.push(` options?: Omit } }, Error, { ${pkField.name}: ${pkField.tsType} }>, 'mutationFn'>`); + lines.push(`) {`); + lines.push(` const queryClient = useQueryClient();`); + lines.push(` return useMutation({`); + if (useCentralizedKeys) { - mutationOptions.push( - t.objectProperty( - t.identifier('mutationKey'), - t.memberExpression(t.identifier(mutationKeysName), t.identifier('all')) - ) - ); + lines.push(` mutationKey: ${mutationKeysName}.all,`); } - mutationOptions.push( - t.objectProperty( - t.identifier('mutationFn'), - t.arrowFunctionExpression( - [typedParam('variables', t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)))], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${mutationName}MutationDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(mutationName)}MutationVariables`)), - ] - ) - ) - ) - ); - - const detailQueryKey = useCentralizedKeys - ? t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name))] - ) - : t.arrayExpression([ - t.stringLiteral(typeName.toLowerCase()), - t.stringLiteral('detail'), - t.memberExpression(t.memberExpression(t.identifier('variables'), t.identifier('input')), t.identifier(pkField.name)), - ]); - - const listQueryKey = useCentralizedKeys - ? t.callExpression(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); - - mutationOptions.push( - t.objectProperty( - t.identifier('onSuccess'), - t.arrowFunctionExpression( - [t.identifier('_'), t.identifier('variables')], - t.blockStatement([ - t.expressionStatement( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), detailQueryKey)])] - ) - ), - t.expressionStatement( - t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), listQueryKey)])] - ) - ), - ]) - ) - ) - ); - mutationOptions.push(t.spreadElement(t.identifier('options'))); - - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useMutation'), [t.objectExpression(mutationOptions)]) - ) - ); - - const optionsTypeStr = `Omit, 'mutationFn'>`; - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(optionsTypeStr))); - - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - [optionsParam], - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - addJSDocComment(hookExport, [ - `Mutation hook for deleting a ${typeName}`, - '', - '@example', - '```tsx', - `const { mutate, isPending } = ${hookName}();`, - '', - 'mutate({', - ' input: {', - ` ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'},`, - ' },', - '});', - '```', - ]); - statements.push(hookExport); - - const code = generateCode(statements); - const content = getGeneratedFileHeader(`Delete mutation hook for ${typeName}`) + '\n\n' + code; + + lines.push(` mutationFn: ({ ${pkField.name} }: { ${pkField.name}: ${pkField.tsType} }) => getClient().${singularName}.delete({ where: { ${pkField.name} }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + + const detailKey = useCentralizedKeys + ? `${keysName}.detail(variables.${pkField.name})` + : `['${typeName.toLowerCase()}', 'detail', variables.${pkField.name}]`; + const listKey = useCentralizedKeys + ? `${keysName}.lists()` + : `['${typeName.toLowerCase()}', 'list']`; + + lines.push(` onSuccess: (_, variables) => {`); + lines.push(` queryClient.removeQueries({ queryKey: ${detailKey} });`); + lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); + lines.push(` },`); + lines.push(` ...options,`); + lines.push(` });`); + lines.push(`}`); + + const content = getGeneratedFileHeader(`Delete mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getDeleteMutationFileName(table), - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/orm/barrel.ts b/graphql/codegen/src/core/codegen/orm/barrel.ts index fda3b5b15..b1a9e63a0 100644 --- a/graphql/codegen/src/core/codegen/orm/barrel.ts +++ b/graphql/codegen/src/core/codegen/orm/barrel.ts @@ -3,10 +3,11 @@ * * Generates index.ts files that re-export all models and operations. */ -import type { CleanTable } from '../../../types/schema'; import * as t from '@babel/types'; + +import type { CleanTable } from '../../../types/schema'; import { generateCode } from '../babel-ast'; -import { getTableNames, lcFirst, getGeneratedFileHeader } from '../utils'; +import { getGeneratedFileHeader,getTableNames, lcFirst } from '../utils'; export interface GeneratedBarrelFile { fileName: string; @@ -41,7 +42,7 @@ export function generateModelsBarrel(tables: CleanTable[]): GeneratedBarrelFile return { fileName: 'models/index.ts', - content: header + '\n' + code, + content: header + '\n' + code }; } @@ -62,6 +63,6 @@ export * from './input-types'; return { fileName: 'types.ts', - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/orm/client-generator.ts b/graphql/codegen/src/core/codegen/orm/client-generator.ts index 765d95807..293f0be52 100644 --- a/graphql/codegen/src/core/codegen/orm/client-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/client-generator.ts @@ -3,13 +3,14 @@ * * Generates the createClient() factory function and main client file. */ -import type { CleanTable } from '../../../types/schema'; import * as t from '@babel/types'; -import { generateCode, commentBlock } from '../babel-ast'; -import { getTableNames, lcFirst, getGeneratedFileHeader } from '../utils'; import * as fs from 'fs'; import * as path from 'path'; +import type { CleanTable } from '../../../types/schema'; +import { commentBlock,generateCode } from '../babel-ast'; +import { getGeneratedFileHeader,getTableNames, lcFirst } from '../utils'; + export interface GeneratedClientFile { fileName: string; content: string; @@ -61,7 +62,7 @@ function readTemplateFile(templateName: string, description: string): string { export function generateOrmClientFile(): GeneratedClientFile { return { fileName: 'client.ts', - content: readTemplateFile('orm-client.ts', 'ORM Client - Runtime GraphQL executor'), + content: readTemplateFile('orm-client.ts', 'ORM Client - Runtime GraphQL executor') }; } @@ -76,7 +77,7 @@ export function generateQueryBuilderFile(): GeneratedClientFile { content: readTemplateFile( 'query-builder.ts', 'Query Builder - Builds and executes GraphQL operations' - ), + ) }; } @@ -88,7 +89,7 @@ export function generateQueryBuilderFile(): GeneratedClientFile { export function generateSelectTypesFile(): GeneratedClientFile { return { fileName: 'select-types.ts', - content: readTemplateFile('select-types.ts', 'Type utilities for select inference'), + content: readTemplateFile('select-types.ts', 'Type utilities for select inference') }; } @@ -167,7 +168,7 @@ export function generateCreateClientFile( t.exportSpecifier( t.identifier('GraphQLAdapter'), t.identifier('GraphQLAdapter') - ), + ) ], t.stringLiteral('./client') ); @@ -182,7 +183,7 @@ export function generateCreateClientFile( t.exportSpecifier( t.identifier('GraphQLRequestError'), t.identifier('GraphQLRequestError') - ), + ) ], t.stringLiteral('./client') ) @@ -196,7 +197,7 @@ export function generateCreateClientFile( t.exportSpecifier( t.identifier('QueryBuilder'), t.identifier('QueryBuilder') - ), + ) ], t.stringLiteral('./query-builder') ) @@ -217,7 +218,7 @@ export function generateCreateClientFile( t.exportSpecifier( t.identifier('createQueryOperations'), t.identifier('createQueryOperations') - ), + ) ], t.stringLiteral('./query') ) @@ -231,7 +232,7 @@ export function generateCreateClientFile( t.exportSpecifier( t.identifier('createMutationOperations'), t.identifier('createMutationOperations') - ), + ) ], t.stringLiteral('./mutation') ) @@ -257,7 +258,7 @@ export function generateCreateClientFile( t.objectProperty( t.identifier('query'), t.callExpression(t.identifier('createQueryOperations'), [ - t.identifier('client'), + t.identifier('client') ]) ) ); @@ -268,7 +269,7 @@ export function generateCreateClientFile( t.objectProperty( t.identifier('mutation'), t.callExpression(t.identifier('createMutationOperations'), [ - t.identifier('client'), + t.identifier('client') ]) ) ); @@ -279,7 +280,7 @@ export function generateCreateClientFile( t.variableDeclarator( t.identifier('client'), t.newExpression(t.identifier('OrmClient'), [t.identifier('config')]) - ), + ) ]); const returnStmt = t.returnStatement(t.objectExpression(returnProperties)); @@ -329,6 +330,6 @@ export function generateCreateClientFile( return { fileName: 'index.ts', - content: header + '\n' + code, + content: header + '\n' + code }; } diff --git a/graphql/codegen/src/core/codegen/orm/client.ts b/graphql/codegen/src/core/codegen/orm/client.ts index f7e233531..d217115f0 100644 --- a/graphql/codegen/src/core/codegen/orm/client.ts +++ b/graphql/codegen/src/core/codegen/orm/client.ts @@ -54,19 +54,19 @@ export class FetchAdapter implements GraphQLAdapter { headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers, + ...this.headers }, body: JSON.stringify({ query: document, - variables: variables ?? {}, - }), + variables: variables ?? {} + }) }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }], + errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }] }; } @@ -79,14 +79,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors, + errors: json.errors }; } return { ok: true, data: json.data as T, - errors: undefined, + errors: undefined }; } diff --git a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts index 0e7a55cb0..e38554296 100644 --- a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts @@ -4,16 +4,17 @@ * Generates db.query.* and db.mutation.* namespaces for non-table operations * like login, register, currentUser, etc. */ -import type { CleanOperation, CleanArgument } from '../../../types/schema'; import * as t from '@babel/types'; -import { generateCode } from '../babel-ast'; -import { ucFirst, getGeneratedFileHeader } from '../utils'; + +import type { CleanArgument, CleanOperation, TypeRegistry } from '../../../types/schema'; +import { asConst, generateCode } from '../babel-ast'; +import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers'; import { - typeRefToTsType, - isTypeRequired, getTypeBaseName, + isTypeRequired, + typeRefToTsType } from '../type-resolver'; -import { SCALAR_NAMES } from '../scalars'; +import { getGeneratedFileHeader,ucFirst } from '../utils'; export interface GeneratedCustomOpsFile { fileName: string; @@ -45,13 +46,6 @@ function collectInputTypeNamesFromOps(operations: CleanOperation[]): string[] { return Array.from(inputTypes); } -// Types that don't need Select types -const NON_SELECT_TYPES = new Set([ - ...SCALAR_NAMES, - 'Query', - 'Mutation', -]); - /** * Collect all payload/return type names from operations (for Select types) * Filters out scalar types @@ -66,8 +60,6 @@ function collectPayloadTypeNamesFromOps( if ( baseName && !baseName.endsWith('Connection') && - baseName !== 'Query' && - baseName !== 'Mutation' && !NON_SELECT_TYPES.has(baseName) ) { payloadTypes.add(baseName); @@ -78,21 +70,26 @@ function collectPayloadTypeNamesFromOps( } /** - * Get the Select type name for a return type - * Returns null for scalar types, Connection types (no select needed) + * Collect Connection and other non-scalar return type names that need importing + * (for typing QueryBuilder results on scalar/Connection operations) */ -function getSelectTypeName(returnType: CleanArgument['type']): string | null { - const baseName = getTypeBaseName(returnType); - if ( - baseName && - !NON_SELECT_TYPES.has(baseName) && - baseName !== 'Query' && - baseName !== 'Mutation' && - !baseName.endsWith('Connection') - ) { - return `${baseName}Select`; +function collectRawReturnTypeNames( + operations: CleanOperation[] +): string[] { + const types = new Set(); + + for (const op of operations) { + const baseName = getTypeBaseName(op.returnType); + if ( + baseName && + !NON_SELECT_TYPES.has(baseName) && + baseName.endsWith('Connection') + ) { + types.add(baseName); + } } - return null; + + return Array.from(types); } function createImportDeclaration( @@ -153,15 +150,106 @@ function parseTypeAnnotation(typeStr: string): t.TSType { return t.tsTypeReference(t.identifier(typeStr)); } +function buildSelectedResultTsType( + typeRef: CleanArgument['type'], + payloadTypeName: string +): t.TSType { + if (typeRef.kind === 'NON_NULL' && typeRef.ofType) { + return buildSelectedResultTsType(typeRef.ofType as CleanArgument['type'], payloadTypeName); + } + + if (typeRef.kind === 'LIST' && typeRef.ofType) { + return t.tsArrayType( + buildSelectedResultTsType(typeRef.ofType as CleanArgument['type'], payloadTypeName) + ); + } + + return t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(payloadTypeName)), + t.tsTypeReference(t.identifier('S')) + ]) + ); +} + +function buildDefaultSelectExpression( + typeName: string, + typeRegistry: TypeRegistry, + depth: number = 0 +): t.Expression { + const resolved = typeRegistry.get(typeName); + const fields = resolved?.fields ?? []; + + if (depth > 3 || fields.length === 0) { + // Use first field if available, otherwise fallback to 'id' + const fallbackName = fields.length > 0 ? fields[0].name : 'id'; + return t.objectExpression([t.objectProperty(t.identifier(fallbackName), t.booleanLiteral(true))]); + } + + // Prefer id-like fields + const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); + if (idLike) { + return t.objectExpression([ + t.objectProperty(t.identifier(idLike.name), t.booleanLiteral(true)) + ]); + } + + // Prefer scalar/enum fields + const scalarField = fields.find((f) => { + const baseName = getTypeBaseName(f.type); + if (!baseName) return false; + if (NON_SELECT_TYPES.has(baseName)) return true; + const baseResolved = typeRegistry.get(baseName); + return baseResolved?.kind === 'ENUM'; + }); + + if (scalarField) { + return t.objectExpression([ + t.objectProperty(t.identifier(scalarField.name), t.booleanLiteral(true)) + ]); + } + + // Fallback: first field (ensure valid selection for object fields) + const first = fields[0]; + + const firstBaseName = getTypeBaseName(first.type); + if (!firstBaseName || NON_SELECT_TYPES.has(firstBaseName)) { + return t.objectExpression([ + t.objectProperty(t.identifier(first.name), t.booleanLiteral(true)) + ]); + } + + const nestedResolved = typeRegistry.get(firstBaseName); + if (nestedResolved?.kind === 'ENUM') { + return t.objectExpression([ + t.objectProperty(t.identifier(first.name), t.booleanLiteral(true)) + ]); + } + + return t.objectExpression([ + t.objectProperty( + t.identifier(first.name), + t.objectExpression([ + t.objectProperty( + t.identifier('select'), + buildDefaultSelectExpression(firstBaseName, typeRegistry, depth + 1) + ) + ]) + ) + ]); +} + function buildOperationMethod( op: CleanOperation, - operationType: 'query' | 'mutation' + operationType: 'query' | 'mutation', + defaultSelectIdent?: t.Identifier ): t.ObjectProperty { const hasArgs = op.args.length > 0; const varTypeName = `${ucFirst(op.name)}Variables`; const varDefs = op.args.map((arg) => ({ name: arg.name, - type: formatGraphQLType(arg.type), + type: formatGraphQLType(arg.type) })); const selectTypeName = getSelectTypeName(op.returnType); @@ -191,14 +279,14 @@ function buildOperationMethod( t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(selectTypeName)) ]) ) ) ); prop.optional = true; return prop; - })(), + })() ]) ); } else { @@ -216,13 +304,21 @@ function buildOperationMethod( ); prop.optional = true; return prop; - })(), + })() ]) ); } params.push(optionsParam); // Build the QueryBuilder call + const selectExpr = defaultSelectIdent + ? t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true), + defaultSelectIdent + ) + : t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true); + const queryBuilderArgs = t.objectExpression([ t.objectProperty(t.identifier('client'), t.identifier('client'), false, true), t.objectProperty(t.identifier('operation'), t.stringLiteral(operationType)), @@ -233,39 +329,45 @@ function buildOperationMethod( t.stringLiteral(operationType), t.stringLiteral(ucFirst(op.name)), t.stringLiteral(op.name), - t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true), + selectExpr, hasArgs ? t.identifier('args') : t.identifier('undefined'), t.arrayExpression( varDefs.map((v) => t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral(v.name)), - t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)), + t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)) ]) ) - ), + ) ]) - ), + ) ]); const newExpr = t.newExpression(t.identifier('QueryBuilder'), [queryBuilderArgs]); - // Add type parameter if we have a select type + // Add type parameter to QueryBuilder for typed .unwrap() results if (selectTypeName && payloadTypeName) { + // Select-based type: use InferSelectResult (newExpr as any).typeParameters = t.tsTypeParameterInstantiation([ t.tsTypeLiteral([ t.tsPropertySignature( t.identifier(op.name), t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('InferSelectResult'), - t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(payloadTypeName)), - t.tsTypeReference(t.identifier('S')), - ]) - ) + buildSelectedResultTsType(op.returnType, payloadTypeName) ) - ), - ]), + ) + ]) + ]); + } else { + // Scalar/Connection type: use raw TS type directly + const rawTsType = typeRefToTsType(op.returnType); + (newExpr as any).typeParameters = t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(op.name), + t.tsTypeAnnotation(parseTypeAnnotation(rawTsType)) + ) + ]) ]); } @@ -273,9 +375,12 @@ function buildOperationMethod( // Add type parameters to arrow function if we have a select type if (selectTypeName) { + const defaultType = defaultSelectIdent + ? t.tsTypeQuery(defaultSelectIdent) + : null; const typeParam = t.tsTypeParameter( t.tsTypeReference(t.identifier(selectTypeName)), - null, + defaultType, 'S' ); (typeParam as any).const = true; @@ -289,7 +394,8 @@ function buildOperationMethod( * Generate the query/index.ts file for custom query operations */ export function generateCustomQueryOpsFile( - operations: CleanOperation[] + operations: CleanOperation[], + typeRegistry: TypeRegistry ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -297,7 +403,8 @@ export function generateCustomQueryOpsFile( const inputTypeNames = collectInputTypeNamesFromOps(operations); const payloadTypeNames = collectPayloadTypeNamesFromOps(operations); const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`); - const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames])]; + const rawReturnTypeNames = collectRawReturnTypeNames(operations); + const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames, ...rawReturnTypeNames])]; // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); @@ -314,8 +421,29 @@ export function generateCustomQueryOpsFile( if (varInterface) statements.push(varInterface); } + // Default selects (avoid invalid documents when select is omitted) + const defaultSelectIdentsByOpName = new Map(); + for (const op of operations) { + const selectTypeName = getSelectTypeName(op.returnType); + const payloadTypeName = getTypeBaseName(op.returnType); + if (!selectTypeName || !payloadTypeName) continue; + + const ident = t.identifier(`${op.name}DefaultSelect`); + defaultSelectIdentsByOpName.set(op.name, ident); + statements.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + ident, + asConst(buildDefaultSelectExpression(payloadTypeName, typeRegistry)) + ) + ]) + ); + } + // Generate factory function - const operationProperties = operations.map((op) => buildOperationMethod(op, 'query')); + const operationProperties = operations.map((op) => + buildOperationMethod(op, 'query', defaultSelectIdentsByOpName.get(op.name)) + ); const returnObj = t.objectExpression(operationProperties); const returnStmt = t.returnStatement(returnObj); @@ -335,7 +463,7 @@ export function generateCustomQueryOpsFile( return { fileName: 'query/index.ts', - content: header + '\n' + code, + content: header + '\n' + code }; } @@ -343,7 +471,8 @@ export function generateCustomQueryOpsFile( * Generate the mutation/index.ts file for custom mutation operations */ export function generateCustomMutationOpsFile( - operations: CleanOperation[] + operations: CleanOperation[], + typeRegistry: TypeRegistry ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -351,7 +480,8 @@ export function generateCustomMutationOpsFile( const inputTypeNames = collectInputTypeNamesFromOps(operations); const payloadTypeNames = collectPayloadTypeNamesFromOps(operations); const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`); - const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames])]; + const rawReturnTypeNames = collectRawReturnTypeNames(operations); + const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames, ...rawReturnTypeNames])]; // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); @@ -368,8 +498,29 @@ export function generateCustomMutationOpsFile( if (varInterface) statements.push(varInterface); } + // Default selects (avoid invalid documents when select is omitted) + const defaultSelectIdentsByOpName = new Map(); + for (const op of operations) { + const selectTypeName = getSelectTypeName(op.returnType); + const payloadTypeName = getTypeBaseName(op.returnType); + if (!selectTypeName || !payloadTypeName) continue; + + const ident = t.identifier(`${op.name}DefaultSelect`); + defaultSelectIdentsByOpName.set(op.name, ident); + statements.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + ident, + asConst(buildDefaultSelectExpression(payloadTypeName, typeRegistry)) + ) + ]) + ); + } + // Generate factory function - const operationProperties = operations.map((op) => buildOperationMethod(op, 'mutation')); + const operationProperties = operations.map((op) => + buildOperationMethod(op, 'mutation', defaultSelectIdentsByOpName.get(op.name)) + ); const returnObj = t.objectExpression(operationProperties); const returnStmt = t.returnStatement(returnObj); @@ -389,7 +540,7 @@ export function generateCustomMutationOpsFile( return { fileName: 'mutation/index.ts', - content: header + '\n' + code, + content: header + '\n' + code }; } diff --git a/graphql/codegen/src/core/codegen/orm/index.ts b/graphql/codegen/src/core/codegen/orm/index.ts index 099826959..afeff9380 100644 --- a/graphql/codegen/src/core/codegen/orm/index.ts +++ b/graphql/codegen/src/core/codegen/orm/index.ts @@ -4,21 +4,21 @@ * Main entry point for ORM code generation. Coordinates all generators * and produces the complete ORM client output. */ -import type { CleanTable, CleanOperation, TypeRegistry } from '../../../types/schema'; import type { GraphQLSDKConfigTarget } from '../../../types/config'; +import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema'; +import { generateModelsBarrel, generateTypesBarrel } from './barrel'; import { + generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile, - generateCreateClientFile, + generateSelectTypesFile } from './client-generator'; -import { generateAllModelFiles } from './model-generator'; import { - generateCustomQueryOpsFile, generateCustomMutationOpsFile, + generateCustomQueryOpsFile } from './custom-ops-generator'; -import { generateModelsBarrel, generateTypesBarrel } from './barrel'; -import { generateInputTypesFile, collectInputTypeNames, collectPayloadTypeNames } from './input-types-generator'; +import { collectInputTypeNames, collectPayloadTypeNames,generateInputTypesFile } from './input-types-generator'; +import { generateAllModelFiles } from './model-generator'; export interface GeneratedFile { path: string; @@ -80,7 +80,7 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { for (const modelFile of modelFiles) { files.push({ path: `models/${modelFile.fileName}`, - content: modelFile.content, + content: modelFile.content }); } @@ -93,7 +93,7 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { if (tables.length > 0 || (typeRegistry && (hasCustomQueries || hasCustomMutations))) { const allOps = [ ...(customOperations?.queries ?? []), - ...(customOperations?.mutations ?? []), + ...(customOperations?.mutations ?? []) ]; const usedInputTypes = collectInputTypeNames(allOps); const usedPayloadTypes = collectPayloadTypeNames(allOps); @@ -106,7 +106,7 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { const crudPayloadTypes = [ `Create${typeName}Payload`, `Update${typeName}Payload`, - `Delete${typeName}Payload`, + `Delete${typeName}Payload` ]; for (const payloadType of crudPayloadTypes) { if (typeRegistry.has(payloadType)) { @@ -127,12 +127,18 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { // 5. Generate custom operations (if any) if (hasCustomQueries && customOperations?.queries) { - const queryOpsFile = generateCustomQueryOpsFile(customOperations.queries); + const queryOpsFile = generateCustomQueryOpsFile( + customOperations.queries, + typeRegistry ?? new Map() + ); files.push({ path: queryOpsFile.fileName, content: queryOpsFile.content }); } if (hasCustomMutations && customOperations?.mutations) { - const mutationOpsFile = generateCustomMutationOpsFile(customOperations.mutations); + const mutationOpsFile = generateCustomMutationOpsFile( + customOperations.mutations, + typeRegistry ?? new Map() + ); files.push({ path: mutationOpsFile.fileName, content: mutationOpsFile.content }); } @@ -150,17 +156,17 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { tables: tables.length, customQueries: customOperations?.queries.length ?? 0, customMutations: customOperations?.mutations.length ?? 0, - totalFiles: files.length, - }, + totalFiles: files.length + } }; } // Re-export generators for direct use +export { generateModelsBarrel, generateTypesBarrel } from './barrel'; export { generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile, + generateSelectTypesFile } from './client-generator'; -export { generateModelFile, generateAllModelFiles } from './model-generator'; -export { generateCustomQueryOpsFile, generateCustomMutationOpsFile } from './custom-ops-generator'; -export { generateModelsBarrel, generateTypesBarrel } from './barrel'; +export { generateCustomMutationOpsFile,generateCustomQueryOpsFile } from './custom-ops-generator'; +export { generateAllModelFiles,generateModelFile } from './model-generator'; diff --git a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts index ca7154c57..ee3468f4f 100644 --- a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts @@ -10,24 +10,26 @@ * * Uses Babel AST for robust code generation. */ +import * as t from '@babel/types'; +import { pluralize } from 'inflekt'; + import type { - TypeRegistry, CleanArgument, CleanTable, + TypeRegistry } from '../../../types/schema'; -import * as t from '@babel/types'; -import { generateCode, addLineComment } from '../babel-ast'; +import { addLineComment,generateCode } from '../babel-ast'; +import { scalarToFilterType,scalarToTsType } from '../scalars'; +import { getTypeBaseName } from '../type-resolver'; import { - getTableNames, - getFilterTypeName, getConditionTypeName, - getOrderByTypeName, - isRelationField, + getFilterTypeName, getGeneratedFileHeader, + getOrderByTypeName, + getPrimaryKeyInfo, + getTableNames, + isRelationField } from '../utils'; -import { pluralize } from 'inflekt'; -import { getTypeBaseName } from '../type-resolver'; -import { scalarToTsType, scalarToFilterType } from '../scalars'; export interface GeneratedInputTypesFile { fileName: string; @@ -43,7 +45,7 @@ const EXCLUDED_MUTATION_FIELDS = [ 'id', 'createdAt', 'updatedAt', - 'nodeId', + 'nodeId' ] as const; // ============================================================================ @@ -54,7 +56,7 @@ const EXCLUDED_MUTATION_FIELDS = [ * Overrides for input-type generation */ const INPUT_SCALAR_OVERRIDES: Record = { - JSON: 'Record', + JSON: 'Record' }; /** @@ -63,7 +65,7 @@ const INPUT_SCALAR_OVERRIDES: Record = { function scalarToInputTs(scalar: string): string { return scalarToTsType(scalar, { unknownScalar: 'name', - overrides: INPUT_SCALAR_OVERRIDES, + overrides: INPUT_SCALAR_OVERRIDES }); } @@ -132,18 +134,18 @@ function parseTypeString(typeStr: string): t.TSType { // Handle primitive types switch (typeStr) { - case 'string': - return t.tsStringKeyword(); - case 'number': - return t.tsNumberKeyword(); - case 'boolean': - return t.tsBooleanKeyword(); - case 'null': - return t.tsNullKeyword(); - case 'unknown': - return t.tsUnknownKeyword(); - default: - return t.tsTypeReference(t.identifier(typeStr)); + case 'string': + return t.tsStringKeyword(); + case 'number': + return t.tsNumberKeyword(); + case 'boolean': + return t.tsBooleanKeyword(); + case 'null': + return t.tsNullKeyword(); + case 'unknown': + return t.tsUnknownKeyword(); + default: + return t.tsTypeReference(t.identifier(typeStr)); } } @@ -256,72 +258,72 @@ const SCALAR_FILTER_CONFIGS: ScalarFilterConfig[] = [ { name: 'StringFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'], + operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'] }, { name: 'IntFilter', tsType: 'number', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'FloatFilter', tsType: 'number', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'BooleanFilter', tsType: 'boolean', operators: ['equality'] }, { name: 'UUIDFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray'], + operators: ['equality', 'distinct', 'inArray'] }, { name: 'DatetimeFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'DateFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'JSONFilter', tsType: 'Record', - operators: ['equality', 'distinct', 'json'], + operators: ['equality', 'distinct', 'json'] }, { name: 'BigIntFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'BigFloatFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'], + operators: ['equality', 'distinct', 'inArray', 'comparison'] }, { name: 'BitStringFilter', tsType: 'string', operators: ['equality'] }, { name: 'InternetAddressFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'], + operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'] }, { name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] }, // List filters (for array fields like string[], int[], uuid[]) { name: 'StringListFilter', tsType: 'string[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'], + operators: ['equality', 'distinct', 'comparison', 'listArray'] }, { name: 'IntListFilter', tsType: 'number[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'], + operators: ['equality', 'distinct', 'comparison', 'listArray'] }, { name: 'UUIDListFilter', tsType: 'string[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'], - }, + operators: ['equality', 'distinct', 'comparison', 'listArray'] + } ]; /** @@ -542,7 +544,7 @@ function buildEntityProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: isNullable ? `${tsType} | null` : tsType, - optional: isNullable, + optional: isNullable }); } @@ -582,13 +584,13 @@ function generateRelationHelperTypes(): t.Statement[] { const connectionResultProps: t.TSPropertySignature[] = [ createPropertySignature('nodes', 'T[]', false), createPropertySignature('totalCount', 'number', false), - createPropertySignature('pageInfo', 'PageInfo', false), + createPropertySignature('pageInfo', 'PageInfo', false) ]; const connectionResultBody = t.tsInterfaceBody(connectionResultProps); const connectionResultDecl = t.tsInterfaceDeclaration( t.identifier('ConnectionResult'), t.tsTypeParameterDeclaration([ - t.tsTypeParameter(null, null, 'T'), + t.tsTypeParameter(null, null, 'T') ]), null, connectionResultBody @@ -601,7 +603,7 @@ function generateRelationHelperTypes(): t.Statement[] { { name: 'hasNextPage', type: 'boolean', optional: false }, { name: 'hasPreviousPage', type: 'boolean', optional: false }, { name: 'startCursor', type: 'string | null', optional: true }, - { name: 'endCursor', type: 'string | null', optional: true }, + { name: 'endCursor', type: 'string | null', optional: true } ]) ); @@ -663,7 +665,7 @@ function buildEntityRelationProperties( properties.push({ name: relation.fieldName, type: `${relatedTypeName} | null`, - optional: true, + optional: true }); } @@ -676,7 +678,7 @@ function buildEntityRelationProperties( properties.push({ name: relation.fieldName, type: `${relatedTypeName} | null`, - optional: true, + optional: true }); } @@ -689,7 +691,7 @@ function buildEntityRelationProperties( properties.push({ name: relation.fieldName, type: `ConnectionResult<${relatedTypeName}>`, - optional: true, + optional: true }); } @@ -702,7 +704,7 @@ function buildEntityRelationProperties( properties.push({ name: relation.fieldName, type: `ConnectionResult<${relatedTypeName}>`, - optional: true, + optional: true }); } @@ -803,8 +805,8 @@ function buildSelectTypeLiteral( ); selectProp.optional = true; return selectProp; - })(), - ]), + })() + ]) ]) ) ); @@ -869,8 +871,8 @@ function buildSelectTypeLiteral( ); p.optional = true; return p; - })(), - ]), + })() + ]) ]) ) ); @@ -932,8 +934,8 @@ function buildSelectTypeLiteral( ); p.optional = true; return p; - })(), - ]), + })() + ]) ]) ) ); @@ -964,8 +966,8 @@ function buildSelectTypeLiteral( ); selectProp.optional = true; return selectProp; - })(), - ]), + })() + ]) ]) ) ); @@ -1077,7 +1079,7 @@ function buildTableConditionProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: `${tsType} | null`, - optional: true, + optional: true }); } @@ -1214,7 +1216,7 @@ function buildCreateInputInterface(table: CleanTable): t.ExportNamedDeclaration t.tsPropertySignature( t.identifier(singularName), t.tsTypeAnnotation(nestedObjectType) - ), + ) ]; const body = t.tsInterfaceBody(mainProps); @@ -1249,7 +1251,7 @@ function buildPatchProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: `${tsType} | null`, - optional: true, + optional: true }); } @@ -1264,6 +1266,11 @@ function generateCrudInputTypes(table: CleanTable): t.Statement[] { const { typeName } = getTableNames(table); const patchName = `${typeName}Patch`; + const pkFields = getPrimaryKeyInfo(table); + const pkField = pkFields[0]; + const pkFieldName = pkField?.name ?? 'id'; + const pkFieldTsType = pkField?.tsType ?? 'string'; + // Create input statements.push(buildCreateInputInterface(table)); @@ -1276,8 +1283,8 @@ function generateCrudInputTypes(table: CleanTable): t.Statement[] { statements.push( createExportedInterface(`Update${typeName}Input`, [ { name: 'clientMutationId', type: 'string', optional: true }, - { name: 'id', type: 'string', optional: false }, - { name: 'patch', type: patchName, optional: false }, + { name: pkFieldName, type: pkFieldTsType, optional: false }, + { name: 'patch', type: patchName, optional: false } ]) ); @@ -1285,7 +1292,7 @@ function generateCrudInputTypes(table: CleanTable): t.Statement[] { statements.push( createExportedInterface(`Delete${typeName}Input`, [ { name: 'clientMutationId', type: 'string', optional: true }, - { name: 'id', type: 'string', optional: false }, + { name: pkFieldName, type: pkFieldTsType, optional: false } ]) ); @@ -1456,10 +1463,7 @@ export function collectPayloadTypeNames( for (const op of operations) { const baseName = getTypeBaseName(op.returnType); - if ( - baseName && - (baseName.endsWith('Payload') || !baseName.endsWith('Connection')) - ) { + if (baseName) { payloadTypes.add(baseName); } } @@ -1494,7 +1498,7 @@ function generatePayloadTypes( 'BigFloat', 'Cursor', 'Query', - 'Mutation', + 'Mutation' ]); // Process all types - no artificial limit @@ -1524,7 +1528,7 @@ function generatePayloadTypes( interfaceProps.push({ name: field.name, type: isNullable ? `${tsType} | null` : tsType, - optional: isNullable, + optional: isNullable }); // Follow nested OBJECT types @@ -1563,8 +1567,8 @@ function generatePayloadTypes( ); p.optional = true; return p; - })(), - ]), + })() + ]) ]); } else { propType = t.tsBooleanKeyword(); @@ -1669,6 +1673,6 @@ export function generateInputTypesFile( return { fileName: 'input-types.ts', - content: header + '\n' + code, + content: header + '\n' + code }; } diff --git a/graphql/codegen/src/core/codegen/orm/model-generator.ts b/graphql/codegen/src/core/codegen/orm/model-generator.ts index 0498688b4..5b05c3e41 100644 --- a/graphql/codegen/src/core/codegen/orm/model-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/model-generator.ts @@ -3,15 +3,19 @@ * * Generates per-table model classes with findMany, findFirst, create, update, delete methods. */ -import type { CleanTable } from '../../../types/schema'; import * as t from '@babel/types'; -import { generateCode } from '../babel-ast'; + +import type { CleanTable } from '../../../types/schema'; +import { asConst, generateCode } from '../babel-ast'; import { - getTableNames, - getOrderByTypeName, + getDefaultSelectFieldName, getFilterTypeName, - lcFirst, getGeneratedFileHeader, + getOrderByTypeName, + getPrimaryKeyInfo, + getTableNames, + hasValidPrimaryKey, + lcFirst } from '../utils'; export interface GeneratedModelFile { @@ -45,10 +49,10 @@ function buildMethodBody( t.variableDeclarator( t.objectPattern([ t.objectProperty(t.identifier('document'), t.identifier('document'), false, true), - t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true), + t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true) ]), t.callExpression(t.identifier(builderFn), args) - ), + ) ]); const returnStmt = t.returnStatement( @@ -59,8 +63,8 @@ function buildMethodBody( t.objectProperty(t.identifier('operationName'), t.stringLiteral(typeName)), t.objectProperty(t.identifier('fieldName'), t.stringLiteral(fieldName)), t.objectProperty(t.identifier('document'), t.identifier('document'), false, true), - t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true), - ]), + t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true) + ]) ]) ); @@ -80,16 +84,26 @@ function createClassMethod( return method; } -function createConstTypeParam(constraintTypeName: string): t.TSTypeParameterDeclaration { +function createConstTypeParam( + constraintTypeName: string, + defaultType?: t.TSType +): t.TSTypeParameterDeclaration { const param = t.tsTypeParameter( t.tsTypeReference(t.identifier(constraintTypeName)), - null, + defaultType ?? null, 'S' ); (param as any).const = true; return t.tsTypeParameterDeclaration([param]); } +function tsTypeFromPrimitive(typeName: string): t.TSType { + if (typeName === 'string') return t.tsStringKeyword(); + if (typeName === 'number') return t.tsNumberKeyword(); + if (typeName === 'boolean') return t.tsBooleanKeyword(); + return t.tsTypeReference(t.identifier(typeName)); +} + export function generateModelFile( table: CleanTable, _useSharedTypes: boolean @@ -109,6 +123,12 @@ export function generateModelFile( const deleteInputTypeName = `Delete${typeName}Input`; const patchTypeName = `${typeName}Patch`; + const pkFields = getPrimaryKeyInfo(table); + const pkField = pkFields[0]; + const pkFieldTsType = tsTypeFromPrimitive(pkField.tsType); + const defaultSelectIdent = t.identifier('defaultSelect'); + const defaultSelectFieldName = getDefaultSelectFieldName(table); + const pluralQueryName = table.query?.all ?? pluralName; const createMutationName = table.query?.create ?? `create${typeName}`; const updateMutationName = table.query?.update; @@ -118,18 +138,32 @@ export function generateModelFile( statements.push(createImportDeclaration('../client', ['OrmClient'])); statements.push(createImportDeclaration('../query-builder', [ - 'QueryBuilder', 'buildFindManyDocument', 'buildFindFirstDocument', - 'buildCreateDocument', 'buildUpdateDocument', 'buildDeleteDocument', + 'QueryBuilder', 'buildFindManyDocument', 'buildFindFirstDocument', 'buildFindOneDocument', + 'buildCreateDocument', 'buildUpdateByPkDocument', 'buildDeleteByPkDocument' ])); statements.push(createImportDeclaration('../select-types', [ 'ConnectionResult', 'FindManyArgs', 'FindFirstArgs', 'CreateArgs', - 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'DeepExact', + 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'DeepExact' ], true)); statements.push(createImportDeclaration('../input-types', [ typeName, relationTypeName, selectTypeName, whereTypeName, orderByTypeName, - createInputTypeName, updateInputTypeName, patchTypeName, + createInputTypeName, updateInputTypeName, patchTypeName ], true)); + // Default select (ensures valid GraphQL selection + sound TS return types) + statements.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + defaultSelectIdent, + asConst( + t.objectExpression([ + t.objectProperty(t.identifier(defaultSelectFieldName), t.booleanLiteral(true)) + ]) + ) + ) + ]) + ); + const classBody: t.ClassBody['body'] = []; // Constructor @@ -147,10 +181,10 @@ export function generateModelFile( t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(selectTypeName)) ])), t.tsTypeReference(t.identifier(whereTypeName)), - t.tsTypeReference(t.identifier(orderByTypeName)), + t.tsTypeReference(t.identifier(orderByTypeName)) ])) ); const findManyReturnType = t.tsTypeAnnotation( @@ -160,17 +194,22 @@ export function generateModelFile( t.tsTypeReference(t.identifier('ConnectionResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')), - ])), + t.tsTypeReference(t.identifier('S')) + ])) ])) - )), - ]), + )) + ]) ])) ); + const findManySelectExpr = t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + defaultSelectIdent + ); const findManyArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + findManySelectExpr, t.objectExpression([ t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)), t.objectProperty(t.identifier('orderBy'), t.tsAsExpression( @@ -181,12 +220,12 @@ export function generateModelFile( t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)), t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)), t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)), - t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)), + t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)) ]), t.stringLiteral(whereTypeName), - t.stringLiteral(orderByTypeName), + t.stringLiteral(orderByTypeName) ]; - classBody.push(createClassMethod('findMany', createConstTypeParam(selectTypeName), [findManyParam], findManyReturnType, + classBody.push(createClassMethod('findMany', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findManyParam], findManyReturnType, buildMethodBody('buildFindManyDocument', findManyArgs, 'query', typeName, pluralQueryName))); // findFirst method @@ -196,9 +235,9 @@ export function generateModelFile( t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(selectTypeName)) ])), - t.tsTypeReference(t.identifier(whereTypeName)), + t.tsTypeReference(t.identifier(whereTypeName)) ])) ); const findFirstReturnType = t.tsTypeAnnotation( @@ -209,35 +248,103 @@ export function generateModelFile( t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation( t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier('S')) ]))) - )), + )) ]) - )), - ]), + )) + ]) ])) ); + const findFirstSelectExpr = t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + defaultSelectIdent + ); const findFirstArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + findFirstSelectExpr, t.objectExpression([ - t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)), + t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)) ]), - t.stringLiteral(whereTypeName), + t.stringLiteral(whereTypeName) ]; - classBody.push(createClassMethod('findFirst', createConstTypeParam(selectTypeName), [findFirstParam], findFirstReturnType, + classBody.push(createClassMethod('findFirst', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findFirstParam], findFirstReturnType, buildMethodBody('buildFindFirstDocument', findFirstArgs, 'query', typeName, pluralQueryName))); + // findOne method (only if table has valid PK and singular query) + const singleQueryName = table.query?.one; + if (singleQueryName && hasValidPrimaryKey(table)) { + const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!'; + + const findOneParam = t.identifier('args'); + findOneParam.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkFieldTsType) + ); + prop.optional = false; + return prop; + })(), + (() => { + const prop = t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier(selectTypeName)) + ])) + ) + ); + prop.optional = true; + return prop; + })() + ]) + ); + const findOneReturnType = t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(singleQueryName), t.tsTypeAnnotation( + t.tsUnionType([ + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + t.tsTypeReference(t.identifier('S')) + ])), + t.tsNullKeyword() + ]) + )) + ]) + ])) + ); + const findOneSelectExpr = t.logicalExpression( + '??', + t.memberExpression(t.identifier('args'), t.identifier('select')), + defaultSelectIdent + ); + const findOneArgs = [ + t.stringLiteral(typeName), + t.stringLiteral(singleQueryName), + t.memberExpression(t.identifier('args'), t.identifier(pkField.name)), + findOneSelectExpr, + t.stringLiteral(pkField.name), + t.stringLiteral(pkGqlType) + ]; + classBody.push(createClassMethod('findOne', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findOneParam], findOneReturnType, + buildMethodBody('buildFindOneDocument', findOneArgs, 'query', typeName, singleQueryName))); + } + // create method const createParam = t.identifier('args'); createParam.typeAnnotation = t.tsTypeAnnotation( t.tsTypeReference(t.identifier('CreateArgs'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(selectTypeName)) ])), - t.tsIndexedAccessType(t.tsTypeReference(t.identifier(createInputTypeName)), t.tsLiteralType(t.stringLiteral(singularName))), + t.tsIndexedAccessType(t.tsTypeReference(t.identifier(createInputTypeName)), t.tsLiteralType(t.stringLiteral(singularName))) ])) ); const createReturnType = t.tsTypeAnnotation( @@ -248,23 +355,28 @@ export function generateModelFile( t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier('S')) ])) - )), + )) ]) - )), - ]), + )) + ]) ])) ); + const createSelectExpr = t.logicalExpression( + '??', + t.memberExpression(t.identifier('args'), t.identifier('select')), + defaultSelectIdent + ); const createArgs = [ t.stringLiteral(typeName), t.stringLiteral(createMutationName), t.stringLiteral(entityLower), - t.memberExpression(t.identifier('args'), t.identifier('select')), + createSelectExpr, t.memberExpression(t.identifier('args'), t.identifier('data')), - t.stringLiteral(createInputTypeName), + t.stringLiteral(createInputTypeName) ]; - classBody.push(createClassMethod('create', createConstTypeParam(selectTypeName), [createParam], createReturnType, + classBody.push(createClassMethod('create', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [createParam], createReturnType, buildMethodBody('buildCreateDocument', createArgs, 'mutation', typeName, createMutationName))); // update method (if available) @@ -274,10 +386,16 @@ export function generateModelFile( t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)), + t.tsTypeReference(t.identifier(selectTypeName)) ])), - t.tsTypeLiteral([t.tsPropertySignature(t.identifier('id'), t.tsTypeAnnotation(t.tsStringKeyword()))]), - t.tsTypeReference(t.identifier(patchTypeName)), + t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkFieldTsType)); + prop.optional = false; + return prop; + })() + ]), + t.tsTypeReference(t.identifier(patchTypeName)) ])) ); const updateReturnType = t.tsTypeAnnotation( @@ -288,33 +406,52 @@ export function generateModelFile( t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier('S')) ])) - )), + )) ]) - )), - ]), + )) + ]) ])) ); + const updateSelectExpr = t.logicalExpression( + '??', + t.memberExpression(t.identifier('args'), t.identifier('select')), + defaultSelectIdent + ); const updateArgs = [ t.stringLiteral(typeName), t.stringLiteral(updateMutationName), t.stringLiteral(entityLower), - t.memberExpression(t.identifier('args'), t.identifier('select')), - t.memberExpression(t.identifier('args'), t.identifier('where')), + updateSelectExpr, + t.memberExpression( + t.memberExpression(t.identifier('args'), t.identifier('where')), + t.identifier(pkField.name) + ), t.memberExpression(t.identifier('args'), t.identifier('data')), t.stringLiteral(updateInputTypeName), + t.stringLiteral(pkField.name) ]; - classBody.push(createClassMethod('update', createConstTypeParam(selectTypeName), [updateParam], updateReturnType, - buildMethodBody('buildUpdateDocument', updateArgs, 'mutation', typeName, updateMutationName))); + classBody.push(createClassMethod('update', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [updateParam], updateReturnType, + buildMethodBody('buildUpdateByPkDocument', updateArgs, 'mutation', typeName, updateMutationName))); } - // delete method (if available) + // delete method (if available) - supports optional select for returning entity fields if (deleteMutationName) { const deleteParam = t.identifier('args'); deleteParam.typeAnnotation = t.tsTypeAnnotation( t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([t.tsPropertySignature(t.identifier('id'), t.tsTypeAnnotation(t.tsStringKeyword()))]), + t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkFieldTsType)); + prop.optional = false; + return prop; + })() + ]), + t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier(selectTypeName)) + ])) ])) ); const deleteReturnType = t.tsTypeAnnotation( @@ -323,22 +460,35 @@ export function generateModelFile( t.tsPropertySignature(t.identifier(deleteMutationName), t.tsTypeAnnotation( t.tsTypeLiteral([ t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeLiteral([t.tsPropertySignature(t.identifier('id'), t.tsTypeAnnotation(t.tsStringKeyword()))]) - )), + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + t.tsTypeReference(t.identifier('S')) + ])) + )) ]) - )), - ]), + )) + ]) ])) ); + const deleteSelectExpr = t.logicalExpression( + '??', + t.memberExpression(t.identifier('args'), t.identifier('select')), + defaultSelectIdent + ); const deleteArgs = [ t.stringLiteral(typeName), t.stringLiteral(deleteMutationName), t.stringLiteral(entityLower), - t.memberExpression(t.identifier('args'), t.identifier('where')), + t.memberExpression( + t.memberExpression(t.identifier('args'), t.identifier('where')), + t.identifier(pkField.name) + ), t.stringLiteral(deleteInputTypeName), + t.stringLiteral(pkField.name), + deleteSelectExpr ]; - classBody.push(createClassMethod('delete', null, [deleteParam], deleteReturnType, - buildMethodBody('buildDeleteDocument', deleteArgs, 'mutation', typeName, deleteMutationName))); + classBody.push(createClassMethod('delete', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [deleteParam], deleteReturnType, + buildMethodBody('buildDeleteByPkDocument', deleteArgs, 'mutation', typeName, deleteMutationName))); } const classDecl = t.classDeclaration(t.identifier(modelName), null, t.classBody(classBody)); diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index 55852c339..5e6d7285b 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -1,39 +1,28 @@ /** - * Query hook generators using Babel AST-based code generation + * Query hook generators - delegates to ORM model methods * * Output structure: * queries/ - * useCarsQuery.ts - List query hook - * useCarQuery.ts - Single item query hook + * useCarsQuery.ts - List query hook -> ORM findMany + * useCarQuery.ts - Single item query hook -> ORM findOne */ import type { CleanTable } from '../../types/schema'; -import * as t from '@babel/types'; -import { generateCode, addJSDocComment, typedParam, createTypedCallExpression } from './babel-ast'; import { - buildListQueryAST, - buildSingleQueryAST, - printGraphQL, -} from './gql-ast'; -import { - getTableNames, - getListQueryHookName, - getSingleQueryHookName, - getListQueryFileName, - getSingleQueryFileName, getAllRowsQueryName, - getSingleRowQueryName, + getDefaultSelectFieldName, getFilterTypeName, - getConditionTypeName, + getGeneratedFileHeader, + getListQueryFileName, + getListQueryHookName, getOrderByTypeName, - getScalarFields, - getScalarFilterType, getPrimaryKeyInfo, + getSingleQueryFileName, + getSingleQueryHookName, + getSingleRowQueryName, + getTableNames, hasValidPrimaryKey, - fieldTypeToTs, - toScreamingSnake, - ucFirst, lcFirst, - getGeneratedFileHeader, + ucFirst } from './utils'; export interface GeneratedQueryFile { @@ -47,55 +36,6 @@ export interface QueryGeneratorOptions { hasRelationships?: boolean; } -function createUnionType(values: string[]): t.TSUnionType { - return t.tsUnionType(values.map((v) => t.tsLiteralType(t.stringLiteral(v)))); -} - -function createFilterInterfaceDeclaration( - name: string, - fieldFilters: Array<{ fieldName: string; filterType: string }>, - isExported: boolean = true -): t.Statement { - const properties: t.TSPropertySignature[] = []; - for (const filter of fieldFilters) { - const prop = t.tsPropertySignature( - t.identifier(filter.fieldName), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filter.filterType))) - ); - prop.optional = true; - properties.push(prop); - } - const andProp = t.tsPropertySignature( - t.identifier('and'), - t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(name)))) - ); - andProp.optional = true; - properties.push(andProp); - const orProp = t.tsPropertySignature( - t.identifier('or'), - t.tsTypeAnnotation(t.tsArrayType(t.tsTypeReference(t.identifier(name)))) - ); - orProp.optional = true; - properties.push(orProp); - const notProp = t.tsPropertySignature( - t.identifier('not'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(name))) - ); - notProp.optional = true; - properties.push(notProp); - const body = t.tsInterfaceBody(properties); - const interfaceDecl = t.tsInterfaceDeclaration( - t.identifier(name), - null, - null, - body - ); - if (isExported) { - return t.exportNamedDeclaration(interfaceDecl); - } - return interfaceDecl; -} - export function generateListQueryHook( table: CleanTable, options: QueryGeneratorOptions = {} @@ -103,742 +43,194 @@ export function generateListQueryHook( const { reactQueryEnabled = true, useCentralizedKeys = true, - hasRelationships = false, + hasRelationships = false } = options; - const { typeName, pluralName } = getTableNames(table); + const { typeName, pluralName, singularName } = getTableNames(table); const hookName = getListQueryHookName(table); const queryName = getAllRowsQueryName(table); const filterTypeName = getFilterTypeName(table); - const conditionTypeName = getConditionTypeName(table); const orderByTypeName = getOrderByTypeName(table); - const scalarFields = getScalarFields(table); const keysName = `${lcFirst(typeName)}Keys`; const scopeTypeName = `${typeName}Scope`; + const selectTypeName = `${typeName}Select`; + const relationTypeName = `${typeName}WithRelations`; - const queryAST = buildListQueryAST({ table }); - const queryDocument = printGraphQL(queryAST); + const defaultFieldName = getDefaultSelectFieldName(table); - const statements: t.Statement[] = []; - - const filterTypesUsed = new Set(); - for (const field of scalarFields) { - const filterType = getScalarFilterType(field.type.gqlType, field.type.isArray); - if (filterType) { - filterTypesUsed.add(filterType); - } - } + const lines: string[] = []; + // Imports if (reactQueryEnabled) { - const reactQueryImport = t.importDeclaration( - [t.importSpecifier(t.identifier('useQuery'), t.identifier('useQuery'))], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - const reactQueryTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier('UseQueryOptions'), - t.identifier('UseQueryOptions') - ), - t.importSpecifier( - t.identifier('QueryClient'), - t.identifier('QueryClient') - ), - ], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); + lines.push(`import { useQuery } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); } - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - const clientTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier('ExecuteOptions'), - t.identifier('ExecuteOptions') - ), - ], - t.stringLiteral('../client') - ); - clientTypeImport.importKind = 'type'; - statements.push(clientTypeImport); - - const typesImport = t.importDeclaration( - [ - t.importSpecifier(t.identifier(typeName), t.identifier(typeName)), - ...Array.from(filterTypesUsed).map((ft) => - t.importSpecifier(t.identifier(ft), t.identifier(ft)) - ), - ], - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); + lines.push(`import { getClient } from '../client';`); if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); + lines.push(`import { ${keysName} } from '../query-keys';`); if (hasRelationships) { - const scopeTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier(scopeTypeName), - t.identifier(scopeTypeName) - ), - ], - t.stringLiteral('../query-keys') - ); - scopeTypeImport.importKind = 'type'; - statements.push(scopeTypeImport); - } - } - - const reExportDecl = t.exportNamedDeclaration( - null, - [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], - t.stringLiteral('../types') - ); - reExportDecl.exportKind = 'type'; - statements.push(reExportDecl); - - const queryDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryDocument`), - t.templateLiteral( - [ - t.templateElement( - { raw: '\n' + queryDocument, cooked: '\n' + queryDocument }, - true - ), - ], - [] - ) - ), - ]); - statements.push(t.exportNamedDeclaration(queryDocConst)); - - const fieldFilters = scalarFields - .map((field) => { - const filterType = getScalarFilterType(field.type.gqlType, field.type.isArray); - return filterType ? { fieldName: field.name, filterType } : null; - }) - .filter((f): f is { fieldName: string; filterType: string } => f !== null); - - statements.push( - createFilterInterfaceDeclaration(filterTypeName, fieldFilters, false) - ); - - // Generate Condition interface (simple equality filter with scalar types) - // Track non-primitive types (enums) that need to be imported - const enumTypesUsed = new Set(); - const conditionProperties: t.TSPropertySignature[] = scalarFields.map( - (field) => { - const tsType = fieldTypeToTs(field.type); - const isPrimitive = - tsType === 'string' || - tsType === 'number' || - tsType === 'boolean' || - tsType === 'unknown' || - tsType.endsWith('[]'); - let typeAnnotation: t.TSType; - if (field.type.isArray) { - const baseType = tsType.replace('[]', ''); - const isBasePrimitive = - baseType === 'string' || - baseType === 'number' || - baseType === 'boolean' || - baseType === 'unknown'; - if (!isBasePrimitive) { - enumTypesUsed.add(baseType); - } - typeAnnotation = t.tsArrayType( - baseType === 'string' - ? t.tsStringKeyword() - : baseType === 'number' - ? t.tsNumberKeyword() - : baseType === 'boolean' - ? t.tsBooleanKeyword() - : t.tsTypeReference(t.identifier(baseType)) - ); - } else { - if (!isPrimitive) { - enumTypesUsed.add(tsType); - } - typeAnnotation = - tsType === 'string' - ? t.tsStringKeyword() - : tsType === 'number' - ? t.tsNumberKeyword() - : tsType === 'boolean' - ? t.tsBooleanKeyword() - : t.tsTypeReference(t.identifier(tsType)); - } - const prop = t.tsPropertySignature( - t.identifier(field.name), - t.tsTypeAnnotation(typeAnnotation) - ); - prop.optional = true; - return prop; + lines.push(`import type { ${scopeTypeName} } from '../query-keys';`); } - ); - - // Add import for enum types if any are used - if (enumTypesUsed.size > 0) { - const schemaTypesImport = t.importDeclaration( - Array.from(enumTypesUsed).map((et) => - t.importSpecifier(t.identifier(et), t.identifier(et)) - ), - t.stringLiteral('../schema-types') - ); - schemaTypesImport.importKind = 'type'; - statements.push(schemaTypesImport); } - const conditionInterface = t.tsInterfaceDeclaration( - t.identifier(conditionTypeName), - null, - null, - t.tsInterfaceBody(conditionProperties) - ); - statements.push(conditionInterface); - - const orderByValues = [ - ...scalarFields.flatMap((f) => [ - `${toScreamingSnake(f.name)}_ASC`, - `${toScreamingSnake(f.name)}_DESC`, - ]), - 'NATURAL', - 'PRIMARY_KEY_ASC', - 'PRIMARY_KEY_DESC', - ]; - const orderByTypeAlias = t.tsTypeAliasDeclaration( - t.identifier(orderByTypeName), - null, - createUnionType(orderByValues) - ); - statements.push(orderByTypeAlias); - - const variablesInterfaceBody = t.tsInterfaceBody([ - (() => { - const p = t.tsPropertySignature( - t.identifier('first'), - t.tsTypeAnnotation(t.tsNumberKeyword()) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('last'), - t.tsTypeAnnotation(t.tsNumberKeyword()) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('offset'), - t.tsTypeAnnotation(t.tsNumberKeyword()) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('before'), - t.tsTypeAnnotation(t.tsStringKeyword()) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('after'), - t.tsTypeAnnotation(t.tsStringKeyword()) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('filter'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterTypeName))) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('condition'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(conditionTypeName))) - ); - p.optional = true; - return p; - })(), - (() => { - const p = t.tsPropertySignature( - t.identifier('orderBy'), - t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier(orderByTypeName))) - ) - ); - p.optional = true; - return p; - })(), - ]); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(pluralName)}QueryVariables`), - null, - null, - variablesInterfaceBody - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); - - const pageInfoType = t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier('hasNextPage'), - t.tsTypeAnnotation(t.tsBooleanKeyword()) - ), - t.tsPropertySignature( - t.identifier('hasPreviousPage'), - t.tsTypeAnnotation(t.tsBooleanKeyword()) - ), - t.tsPropertySignature( - t.identifier('startCursor'), - t.tsTypeAnnotation( - t.tsUnionType([t.tsStringKeyword(), t.tsNullKeyword()]) - ) - ), - t.tsPropertySignature( - t.identifier('endCursor'), - t.tsTypeAnnotation( - t.tsUnionType([t.tsStringKeyword(), t.tsNullKeyword()]) - ) - ), - ]); - const resultType = t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier('totalCount'), - t.tsTypeAnnotation(t.tsNumberKeyword()) - ), - t.tsPropertySignature( - t.identifier('nodes'), - t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier(typeName))) - ) - ), - t.tsPropertySignature( - t.identifier('pageInfo'), - t.tsTypeAnnotation(pageInfoType) - ), - ]); - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(queryName), - t.tsTypeAnnotation(resultType) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(pluralName)}QueryResult`), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); - + lines.push(`import type {`); + lines.push(` ${selectTypeName},`); + lines.push(` ${relationTypeName},`); + lines.push(` ${filterTypeName},`); + lines.push(` ${orderByTypeName},`); + lines.push(`} from '../../orm/input-types';`); + lines.push(`import type {`); + lines.push(` FindManyArgs,`); + lines.push(` DeepExact,`); + lines.push(` InferSelectResult,`); + lines.push(` ConnectionResult,`); + lines.push(`} from '../../orm/select-types';`); + lines.push(''); + + // Re-export types for backwards compat + lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${filterTypeName}, ${orderByTypeName} } from '../../orm/input-types';`); + lines.push(''); + + lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); + lines.push(''); + + // Query key if (useCentralizedKeys) { - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryKey`), - t.memberExpression(t.identifier(keysName), t.identifier('list')) - ), - ]); - const queryKeyExport = t.exportNamedDeclaration(queryKeyConst); - addJSDocComment(queryKeyExport, [ - 'Query key factory - re-exported from query-keys.ts', - ]); - statements.push(queryKeyExport); + lines.push(`/** Query key factory - re-exported from query-keys.ts */`); + lines.push(`export const ${queryName}QueryKey = ${keysName}.list;`); } else { - const queryKeyArrow = t.arrowFunctionExpression( - [ - typedParam( - 'variables', - t.tsTypeReference( - t.identifier(`${ucFirst(pluralName)}QueryVariables`) - ), - true - ), - ], - t.tsAsExpression( - t.arrayExpression([ - t.stringLiteral(typeName.toLowerCase()), - t.stringLiteral('list'), - t.identifier('variables'), - ]), - t.tsTypeReference(t.identifier('const')) - ) - ); - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryKey`), - queryKeyArrow - ), - ]); - statements.push(t.exportNamedDeclaration(queryKeyConst)); + lines.push(`export const ${queryName}QueryKey = (variables?: FindManyArgs) => ['${typeName.toLowerCase()}', 'list', variables] as const;`); } + lines.push(''); + // Hook if (reactQueryEnabled) { - const hookBodyStatements: t.Statement[] = []; + const docLines = [ + `/**`, + ` * Query hook for fetching ${typeName} list`, + ` *`, + ` * @example`, + ` * \`\`\`tsx`, + ` * const { data, isLoading } = ${hookName}({`, + ` * select: { id: true, name: true },`, + ` * first: 10,`, + ` * where: { name: { equalTo: "example" } },`, + ` * orderBy: ['CREATED_AT_DESC'],`, + ` * });`, + ` * \`\`\`` + ]; if (hasRelationships && useCentralizedKeys) { - hookBodyStatements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.objectPattern([ - t.objectProperty( - t.identifier('scope'), - t.identifier('scope'), - false, - true - ), - t.restElement(t.identifier('queryOptions')), - ]), - t.logicalExpression( - '??', - t.identifier('options'), - t.objectExpression([]) - ) - ), - ]) - ); - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression( - t.memberExpression( - t.identifier(keysName), - t.identifier('list') - ), - [t.identifier('variables'), t.identifier('scope')] - ) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('queryOptions')), - ]), - ]) - ) - ); - } else if (useCentralizedKeys) { - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression( - t.memberExpression( - t.identifier(keysName), - t.identifier('list') - ), - [t.identifier('variables')] - ) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('options')), - ]), - ]) - ) - ); - } else { - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(`${queryName}QueryKey`), [ - t.identifier('variables'), - ]) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('options')), - ]), - ]) - ) - ); + docLines.push(` *`); + docLines.push(` * @example With scope for hierarchical cache invalidation`); + docLines.push(` * \`\`\`tsx`); + docLines.push(` * const { data } = ${hookName}(`); + docLines.push(` * { first: 10 },`); + docLines.push(` * { scope: { parentId: 'parent-id' } }`); + docLines.push(` * );`); + docLines.push(` * \`\`\``); } + docLines.push(` */`); + lines.push(...docLines); - const hookParams: t.Identifier[] = [ - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - true - ), - ]; - let optionsTypeStr: string; + let optionsType: string; if (hasRelationships && useCentralizedKeys) { - optionsTypeStr = `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; + optionsType = `Omit> }, Error>, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; } else { - optionsTypeStr = `Omit, 'queryKey' | 'queryFn'>`; + optionsType = `Omit> }, Error>, 'queryKey' | 'queryFn'>`; } - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(optionsTypeStr)) - ); - hookParams.push(optionsParam); - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - hookParams, - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - const docLines = [ - `Query hook for fetching ${typeName} list`, - '', - '@example', - '```tsx', - `const { data, isLoading } = ${hookName}({`, - ' first: 10,', - ' filter: { name: { equalTo: "example" } },', - " orderBy: ['CREATED_AT_DESC'],", - '});', - '```', - ]; + lines.push(`export function ${hookName}(`); + lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); + lines.push(` options?: ${optionsType}`); + lines.push(`) {`); + if (hasRelationships && useCentralizedKeys) { - docLines.push(''); - docLines.push('@example With scope for hierarchical cache invalidation'); - docLines.push('```tsx'); - docLines.push(`const { data } = ${hookName}(`); - docLines.push(' { first: 10 },'); - docLines.push(" { scope: { parentId: 'parent-id' } }"); - docLines.push(');'); - docLines.push('```'); + lines.push(` const { scope, ...queryOptions } = options ?? {};`); + lines.push(` return useQuery({`); + lines.push(` queryKey: ${keysName}.list(args, scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` ...queryOptions,`); + lines.push(` });`); + } else if (useCentralizedKeys) { + lines.push(` return useQuery({`); + lines.push(` queryKey: ${keysName}.list(args),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` ...options,`); + lines.push(` });`); + } else { + lines.push(` return useQuery({`); + lines.push(` queryKey: ${queryName}QueryKey(args),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` ...options,`); + lines.push(` });`); } - addJSDocComment(hookExport, docLines); - statements.push(hookExport); - } - const fetchFuncBody = t.blockStatement([ - t.returnStatement( - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - ] - ) - ), - ]); - const fetchFunc = t.functionDeclaration( - t.identifier(`fetch${ucFirst(pluralName)}Query`), - [ - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - true - ), - typedParam( - 'options', - t.tsTypeReference(t.identifier('ExecuteOptions')), - true - ), - ], - fetchFuncBody - ); - fetchFunc.async = true; - fetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - ]) - ) - ); - const fetchExport = t.exportNamedDeclaration(fetchFunc); - addJSDocComment(fetchExport, [ - `Fetch ${typeName} list without React hooks`, - '', - '@example', - '```ts', - '// Direct fetch', - `const data = await fetch${ucFirst(pluralName)}Query({ first: 10 });`, - '', - '// With QueryClient', - 'const data = await queryClient.fetchQuery({', - ` queryKey: ${queryName}QueryKey(variables),`, - ` queryFn: () => fetch${ucFirst(pluralName)}Query(variables),`, - '});', - '```', - ]); - statements.push(fetchExport); + lines.push(`}`); + lines.push(''); + } + // Fetch function (non-hook) + lines.push(`/**`); + lines.push(` * Fetch ${typeName} list without React hooks`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * const data = await fetch${ucFirst(pluralName)}Query({ first: 10, select: { id: true } });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); + lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); + lines.push(`) {`); + lines.push(` return getClient().${singularName}.findMany(args).unwrap();`); + lines.push(`}`); + lines.push(''); + + // Prefetch function if (reactQueryEnabled) { - const prefetchParams: t.Identifier[] = [ - typedParam( - 'queryClient', - t.tsTypeReference(t.identifier('QueryClient')) - ), - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - true - ), - ]; + lines.push(`/**`); + lines.push(` * Prefetch ${typeName} list for SSR or cache warming`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * await prefetch${ucFirst(pluralName)}Query(queryClient, { first: 10 });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); if (hasRelationships && useCentralizedKeys) { - prefetchParams.push( - typedParam( - 'scope', - t.tsTypeReference(t.identifier(scopeTypeName)), - true - ) - ); + lines.push(` scope?: ${scopeTypeName},`); } - prefetchParams.push( - typedParam( - 'options', - t.tsTypeReference(t.identifier('ExecuteOptions')), - true - ) - ); + lines.push(`): Promise {`); - let prefetchQueryKeyExpr: t.Expression; if (hasRelationships && useCentralizedKeys) { - prefetchQueryKeyExpr = t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('list')), - [t.identifier('variables'), t.identifier('scope')] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${keysName}.list(args, scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` });`); } else if (useCentralizedKeys) { - prefetchQueryKeyExpr = t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('list')), - [t.identifier('variables')] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${keysName}.list(args),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` });`); } else { - prefetchQueryKeyExpr = t.callExpression( - t.identifier(`${queryName}QueryKey`), - [t.identifier('variables')] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryName}QueryKey(args),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` });`); } - const prefetchFuncBody = t.blockStatement([ - t.expressionStatement( - t.awaitExpression( - t.callExpression( - t.memberExpression( - t.identifier('queryClient'), - t.identifier('prefetchQuery') - ), - [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - prefetchQueryKeyExpr - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(pluralName)}QueryVariables`)), - ] - ) - ) - ), - ]), - ] - ) - ) - ), - ]); - - const prefetchFunc = t.functionDeclaration( - t.identifier(`prefetch${ucFirst(pluralName)}Query`), - prefetchParams, - prefetchFuncBody - ); - prefetchFunc.async = true; - prefetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([t.tsVoidKeyword()]) - ) - ); - const prefetchExport = t.exportNamedDeclaration(prefetchFunc); - addJSDocComment(prefetchExport, [ - `Prefetch ${typeName} list for SSR or cache warming`, - '', - '@example', - '```ts', - `await prefetch${ucFirst(pluralName)}Query(queryClient, { first: 10 });`, - '```', - ]); - statements.push(prefetchExport); + lines.push(`}`); } - const code = generateCode(statements); const headerText = reactQueryEnabled ? `List query hook for ${typeName}` : `List query functions for ${typeName}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + code; + const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getListQueryFileName(table), - content, + content }; } @@ -846,7 +238,6 @@ export function generateSingleQueryHook( table: CleanTable, options: QueryGeneratorOptions = {} ): GeneratedQueryFile | null { - // Skip tables with composite keys - they are handled as custom queries if (!hasValidPrimaryKey(table)) { return null; } @@ -854,536 +245,190 @@ export function generateSingleQueryHook( const { reactQueryEnabled = true, useCentralizedKeys = true, - hasRelationships = false, + hasRelationships = false } = options; const { typeName, singularName } = getTableNames(table); const hookName = getSingleQueryHookName(table); const queryName = getSingleRowQueryName(table); const keysName = `${lcFirst(typeName)}Keys`; const scopeTypeName = `${typeName}Scope`; + const selectTypeName = `${typeName}Select`; + const relationTypeName = `${typeName}WithRelations`; const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pkName = pkField.name; - const pkTsType = pkField.tsType; + const pkFieldName = pkField?.name ?? 'id'; + const pkFieldTsType = pkField?.tsType ?? 'string'; + const defaultFieldName = getDefaultSelectFieldName(table); - const queryAST = buildSingleQueryAST({ table }); - const queryDocument = printGraphQL(queryAST); - - const statements: t.Statement[] = []; + const lines: string[] = []; + // Imports if (reactQueryEnabled) { - const reactQueryImport = t.importDeclaration( - [t.importSpecifier(t.identifier('useQuery'), t.identifier('useQuery'))], - t.stringLiteral('@tanstack/react-query') - ); - statements.push(reactQueryImport); - const reactQueryTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier('UseQueryOptions'), - t.identifier('UseQueryOptions') - ), - t.importSpecifier( - t.identifier('QueryClient'), - t.identifier('QueryClient') - ), - ], - t.stringLiteral('@tanstack/react-query') - ); - reactQueryTypeImport.importKind = 'type'; - statements.push(reactQueryTypeImport); + lines.push(`import { useQuery } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); } - - const clientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('execute'), t.identifier('execute'))], - t.stringLiteral('../client') - ); - statements.push(clientImport); - const clientTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier('ExecuteOptions'), - t.identifier('ExecuteOptions') - ), - ], - t.stringLiteral('../client') - ); - clientTypeImport.importKind = 'type'; - statements.push(clientTypeImport); - - const typesImport = t.importDeclaration( - [t.importSpecifier(t.identifier(typeName), t.identifier(typeName))], - t.stringLiteral('../types') - ); - typesImport.importKind = 'type'; - statements.push(typesImport); + lines.push(`import { getClient } from '../client';`); if (useCentralizedKeys) { - const queryKeyImport = t.importDeclaration( - [t.importSpecifier(t.identifier(keysName), t.identifier(keysName))], - t.stringLiteral('../query-keys') - ); - statements.push(queryKeyImport); + lines.push(`import { ${keysName} } from '../query-keys';`); if (hasRelationships) { - const scopeTypeImport = t.importDeclaration( - [ - t.importSpecifier( - t.identifier(scopeTypeName), - t.identifier(scopeTypeName) - ), - ], - t.stringLiteral('../query-keys') - ); - scopeTypeImport.importKind = 'type'; - statements.push(scopeTypeImport); + lines.push(`import type { ${scopeTypeName} } from '../query-keys';`); } } - const reExportDecl = t.exportNamedDeclaration( - null, - [t.exportSpecifier(t.identifier(typeName), t.identifier(typeName))], - t.stringLiteral('../types') - ); - reExportDecl.exportKind = 'type'; - statements.push(reExportDecl); - - const queryDocConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryDocument`), - t.templateLiteral( - [ - t.templateElement( - { raw: '\n' + queryDocument, cooked: '\n' + queryDocument }, - true - ), - ], - [] - ) - ), - ]); - statements.push(t.exportNamedDeclaration(queryDocConst)); - - const pkTypeAnnotation = - pkTsType === 'string' - ? t.tsStringKeyword() - : pkTsType === 'number' - ? t.tsNumberKeyword() - : t.tsTypeReference(t.identifier(pkTsType)); + lines.push(`import type {`); + lines.push(` ${selectTypeName},`); + lines.push(` ${relationTypeName},`); + lines.push(`} from '../../orm/input-types';`); + lines.push(`import type {`); + lines.push(` DeepExact,`); + lines.push(` InferSelectResult,`); + lines.push(`} from '../../orm/select-types';`); + lines.push(''); - const variablesInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(pkName), - t.tsTypeAnnotation(pkTypeAnnotation) - ), - ]); - const variablesInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(singularName)}QueryVariables`), - null, - null, - variablesInterfaceBody - ); - statements.push(t.exportNamedDeclaration(variablesInterface)); + // Re-export types + lines.push(`export type { ${selectTypeName}, ${relationTypeName} } from '../../orm/input-types';`); + lines.push(''); - const resultInterfaceBody = t.tsInterfaceBody([ - t.tsPropertySignature( - t.identifier(queryName), - t.tsTypeAnnotation( - t.tsUnionType([ - t.tsTypeReference(t.identifier(typeName)), - t.tsNullKeyword(), - ]) - ) - ), - ]); - const resultInterface = t.tsInterfaceDeclaration( - t.identifier(`${ucFirst(singularName)}QueryResult`), - null, - null, - resultInterfaceBody - ); - statements.push(t.exportNamedDeclaration(resultInterface)); + lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); + lines.push(''); + // Query key if (useCentralizedKeys) { - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryKey`), - t.memberExpression(t.identifier(keysName), t.identifier('detail')) - ), - ]); - const queryKeyExport = t.exportNamedDeclaration(queryKeyConst); - addJSDocComment(queryKeyExport, [ - 'Query key factory - re-exported from query-keys.ts', - ]); - statements.push(queryKeyExport); + lines.push(`/** Query key factory - re-exported from query-keys.ts */`); + lines.push(`export const ${queryName}QueryKey = ${keysName}.detail;`); } else { - const queryKeyArrow = t.arrowFunctionExpression( - [typedParam(pkName, pkTypeAnnotation)], - t.tsAsExpression( - t.arrayExpression([ - t.stringLiteral(typeName.toLowerCase()), - t.stringLiteral('detail'), - t.identifier(pkName), - ]), - t.tsTypeReference(t.identifier('const')) - ) - ); - const queryKeyConst = t.variableDeclaration('const', [ - t.variableDeclarator( - t.identifier(`${queryName}QueryKey`), - queryKeyArrow - ), - ]); - statements.push(t.exportNamedDeclaration(queryKeyConst)); + lines.push(`export const ${queryName}QueryKey = (id: ${pkFieldTsType}) => ['${typeName.toLowerCase()}', 'detail', id] as const;`); } + lines.push(''); + // Hook if (reactQueryEnabled) { - const hookBodyStatements: t.Statement[] = []; + const docLines = [ + `/**`, + ` * Query hook for fetching a single ${typeName}`, + ` *`, + ` * @example`, + ` * \`\`\`tsx`, + ` * const { data, isLoading } = ${hookName}({`, + ` * ${pkFieldName}: 'some-id',`, + ` * select: { id: true, name: true },`, + ` * });`, + ` * \`\`\`` + ]; if (hasRelationships && useCentralizedKeys) { - hookBodyStatements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - t.objectPattern([ - t.objectProperty( - t.identifier('scope'), - t.identifier('scope'), - false, - true - ), - t.restElement(t.identifier('queryOptions')), - ]), - t.logicalExpression( - '??', - t.identifier('options'), - t.objectExpression([]) - ) - ), - ]) - ); - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression( - t.memberExpression( - t.identifier(keysName), - t.identifier('detail') - ), - [ - t.memberExpression( - t.identifier('variables'), - t.identifier(pkName) - ), - t.identifier('scope'), - ] - ) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('queryOptions')), - ]), - ]) - ) - ); - } else if (useCentralizedKeys) { - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression( - t.memberExpression( - t.identifier(keysName), - t.identifier('detail') - ), - [ - t.memberExpression( - t.identifier('variables'), - t.identifier(pkName) - ), - ] - ) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('options')), - ]), - ]) - ) - ); - } else { - hookBodyStatements.push( - t.returnStatement( - t.callExpression(t.identifier('useQuery'), [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - t.callExpression(t.identifier(`${queryName}QueryKey`), [ - t.memberExpression( - t.identifier('variables'), - t.identifier(pkName) - ), - ]) - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)), - ] - ) - ) - ), - t.spreadElement(t.identifier('options')), - ]), - ]) - ) - ); + docLines.push(` *`); + docLines.push(` * @example With scope for hierarchical cache invalidation`); + docLines.push(` * \`\`\`tsx`); + docLines.push(` * const { data } = ${hookName}(`); + docLines.push(` * { ${pkFieldName}: 'some-id' },`); + docLines.push(` * { scope: { parentId: 'parent-id' } }`); + docLines.push(` * );`); + docLines.push(` * \`\`\``); } + docLines.push(` */`); + lines.push(...docLines); - const hookParams: t.Identifier[] = [ - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)) - ), - ]; - let optionsTypeStr: string; + let optionsType: string; if (hasRelationships && useCentralizedKeys) { - optionsTypeStr = `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; + optionsType = `Omit | null }, Error>, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; } else { - optionsTypeStr = `Omit, 'queryKey' | 'queryFn'>`; + optionsType = `Omit | null }, Error>, 'queryKey' | 'queryFn'>`; } - const optionsParam = t.identifier('options'); - optionsParam.optional = true; - optionsParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(optionsTypeStr)) - ); - hookParams.push(optionsParam); - const hookFunc = t.functionDeclaration( - t.identifier(hookName), - hookParams, - t.blockStatement(hookBodyStatements) - ); - const hookExport = t.exportNamedDeclaration(hookFunc); - const docLines = [ - `Query hook for fetching a single ${typeName}`, - '', - '@example', - '```tsx', - `const { data, isLoading } = ${hookName}({ ${pkName}: 'some-id' });`, - '```', - ]; + lines.push(`export function ${hookName}(`); + lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); + lines.push(` options?: ${optionsType}`); + lines.push(`) {`); + if (hasRelationships && useCentralizedKeys) { - docLines.push(''); - docLines.push('@example With scope for hierarchical cache invalidation'); - docLines.push('```tsx'); - docLines.push(`const { data } = ${hookName}(`); - docLines.push(` { ${pkName}: 'some-id' },`); - docLines.push(" { scope: { parentId: 'parent-id' } }"); - docLines.push(');'); - docLines.push('```'); + lines.push(` const { scope, ...queryOptions } = options ?? {};`); + lines.push(` return useQuery({`); + lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}, scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` ...queryOptions,`); + lines.push(` });`); + } else if (useCentralizedKeys) { + lines.push(` return useQuery({`); + lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` ...options,`); + lines.push(` });`); + } else { + lines.push(` return useQuery({`); + lines.push(` queryKey: ${queryName}QueryKey(args.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` ...options,`); + lines.push(` });`); } - addJSDocComment(hookExport, docLines); - statements.push(hookExport); - } - const fetchFuncBody = t.blockStatement([ - t.returnStatement( - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)), - ] - ) - ), - ]); - const fetchFunc = t.functionDeclaration( - t.identifier(`fetch${ucFirst(singularName)}Query`), - [ - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)) - ), - typedParam( - 'options', - t.tsTypeReference(t.identifier('ExecuteOptions')), - true - ), - ], - fetchFuncBody - ); - fetchFunc.async = true; - fetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - ]) - ) - ); - const fetchExport = t.exportNamedDeclaration(fetchFunc); - addJSDocComment(fetchExport, [ - `Fetch a single ${typeName} without React hooks`, - '', - '@example', - '```ts', - `const data = await fetch${ucFirst(singularName)}Query({ ${pkName}: 'some-id' });`, - '```', - ]); - statements.push(fetchExport); + lines.push(`}`); + lines.push(''); + } + // Fetch function + lines.push(`/**`); + lines.push(` * Fetch a single ${typeName} without React hooks`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * const data = await fetch${ucFirst(singularName)}Query({ ${pkFieldName}: 'some-id', select: { id: true } });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export async function fetch${ucFirst(singularName)}Query(`); + lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); + lines.push(`) {`); + lines.push(` return getClient().${singularName}.findOne(args).unwrap();`); + lines.push(`}`); + lines.push(''); + + // Prefetch function if (reactQueryEnabled) { - const prefetchParams: t.Identifier[] = [ - typedParam( - 'queryClient', - t.tsTypeReference(t.identifier('QueryClient')) - ), - typedParam( - 'variables', - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)) - ), - ]; + lines.push(`/**`); + lines.push(` * Prefetch a single ${typeName} for SSR or cache warming`); + lines.push(` *`); + lines.push(` * @example`); + lines.push(` * \`\`\`ts`); + lines.push(` * await prefetch${ucFirst(singularName)}Query(queryClient, { ${pkFieldName}: 'some-id' });`); + lines.push(` * \`\`\``); + lines.push(` */`); + lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); if (hasRelationships && useCentralizedKeys) { - prefetchParams.push( - typedParam( - 'scope', - t.tsTypeReference(t.identifier(scopeTypeName)), - true - ) - ); + lines.push(` scope?: ${scopeTypeName},`); } - prefetchParams.push( - typedParam( - 'options', - t.tsTypeReference(t.identifier('ExecuteOptions')), - true - ) - ); + lines.push(`): Promise {`); - let prefetchQueryKeyExpr: t.Expression; if (hasRelationships && useCentralizedKeys) { - prefetchQueryKeyExpr = t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [ - t.memberExpression(t.identifier('variables'), t.identifier(pkName)), - t.identifier('scope'), - ] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}, scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` });`); } else if (useCentralizedKeys) { - prefetchQueryKeyExpr = t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.memberExpression(t.identifier('variables'), t.identifier(pkName))] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` });`); } else { - prefetchQueryKeyExpr = t.callExpression( - t.identifier(`${queryName}QueryKey`), - [t.memberExpression(t.identifier('variables'), t.identifier(pkName))] - ); + lines.push(` await queryClient.prefetchQuery({`); + lines.push(` queryKey: ${queryName}QueryKey(args.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` });`); } - const prefetchFuncBody = t.blockStatement([ - t.expressionStatement( - t.awaitExpression( - t.callExpression( - t.memberExpression( - t.identifier('queryClient'), - t.identifier('prefetchQuery') - ), - [ - t.objectExpression([ - t.objectProperty( - t.identifier('queryKey'), - prefetchQueryKeyExpr - ), - t.objectProperty( - t.identifier('queryFn'), - t.arrowFunctionExpression( - [], - createTypedCallExpression( - t.identifier('execute'), - [t.identifier(`${queryName}QueryDocument`), t.identifier('variables'), t.identifier('options')], - [ - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryResult`)), - t.tsTypeReference(t.identifier(`${ucFirst(singularName)}QueryVariables`)), - ] - ) - ) - ), - ]), - ] - ) - ) - ), - ]); - - const prefetchFunc = t.functionDeclaration( - t.identifier(`prefetch${ucFirst(singularName)}Query`), - prefetchParams, - prefetchFuncBody - ); - prefetchFunc.async = true; - prefetchFunc.returnType = t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('Promise'), - t.tsTypeParameterInstantiation([t.tsVoidKeyword()]) - ) - ); - const prefetchExport = t.exportNamedDeclaration(prefetchFunc); - addJSDocComment(prefetchExport, [ - `Prefetch a single ${typeName} for SSR or cache warming`, - '', - '@example', - '```ts', - `await prefetch${ucFirst(singularName)}Query(queryClient, { ${pkName}: 'some-id' });`, - '```', - ]); - statements.push(prefetchExport); + lines.push(`}`); } - const code = generateCode(statements); const headerText = reactQueryEnabled ? `Single item query hook for ${typeName}` : `Single item query functions for ${typeName}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + code; + const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getSingleQueryFileName(table), - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/query-keys.ts b/graphql/codegen/src/core/codegen/query-keys.ts index 791d4eec6..b9c09c468 100644 --- a/graphql/codegen/src/core/codegen/query-keys.ts +++ b/graphql/codegen/src/core/codegen/query-keys.ts @@ -10,17 +10,17 @@ */ import * as t from '@babel/types'; -import type { CleanTable, CleanOperation } from '../../types/schema'; -import type { QueryKeyConfig, EntityRelationship } from '../../types/config'; -import { getTableNames, getGeneratedFileHeader, ucFirst, lcFirst } from './utils'; +import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; +import type { CleanOperation,CleanTable } from '../../types/schema'; import { - generateCode, addJSDocComment, asConst, constArray, - typedParam, + generateCode, keyofTypeof, + typedParam } from './babel-ast'; +import { getGeneratedFileHeader, getTableNames, lcFirst,ucFirst } from './utils'; export interface QueryKeyGeneratorOptions { tables: CleanTable[]; @@ -558,7 +558,7 @@ function generateUnifiedStoreDeclaration( '', '// Invalidate specific user', 'queryClient.invalidateQueries({ queryKey: queryKeys.user.detail(userId) });', - '```', + '```' ]); return decl; @@ -714,6 +714,6 @@ ${description} return { fileName: 'query-keys.ts', - content, + content }; } diff --git a/graphql/codegen/src/core/codegen/scalars.ts b/graphql/codegen/src/core/codegen/scalars.ts index f659cb095..f13047967 100644 --- a/graphql/codegen/src/core/codegen/scalars.ts +++ b/graphql/codegen/src/core/codegen/scalars.ts @@ -40,7 +40,7 @@ export const SCALAR_TS_MAP: Record = { TsQuery: 'string', // File upload - Upload: 'File', + Upload: 'File' }; export const SCALAR_FILTER_MAP: Record = { @@ -59,7 +59,7 @@ export const SCALAR_FILTER_MAP: Record = { BitString: 'BitStringFilter', InternetAddress: 'InternetAddressFilter', FullText: 'FullTextFilter', - Interval: 'StringFilter', + Interval: 'StringFilter' }; export const SCALAR_NAMES = new Set(Object.keys(SCALAR_TS_MAP)); @@ -70,7 +70,7 @@ const LIST_FILTER_SCALARS = new Set(['String', 'Int', 'UUID']); /** All base filter type names - skip these in schema-types.ts to avoid duplicates */ export const BASE_FILTER_TYPE_NAMES = new Set([ ...new Set(Object.values(SCALAR_FILTER_MAP)), - ...Array.from(LIST_FILTER_SCALARS).map((s) => `${s}ListFilter`), + ...Array.from(LIST_FILTER_SCALARS).map((s) => `${s}ListFilter`) ]); export function scalarToTsType( diff --git a/graphql/codegen/src/core/codegen/schema-gql-ast.ts b/graphql/codegen/src/core/codegen/schema-gql-ast.ts deleted file mode 100644 index 1d51f33de..000000000 --- a/graphql/codegen/src/core/codegen/schema-gql-ast.ts +++ /dev/null @@ -1,518 +0,0 @@ -/** - * Dynamic GraphQL AST builders for custom operations - * - * Generates GraphQL query/mutation documents from CleanOperation data - * using gql-ast library for proper AST construction. - */ -import * as t from 'gql-ast'; -import { print } from 'graphql'; -import type { - DocumentNode, - FieldNode, - ArgumentNode, - VariableDefinitionNode, - TypeNode, -} from 'graphql'; -import type { - CleanOperation, - CleanArgument, - CleanTypeRef, - CleanObjectField, - TypeRegistry, -} from '../../types/schema'; -import { getBaseTypeKind, shouldSkipField } from './type-resolver'; - -// ============================================================================ -// Configuration -// ============================================================================ - -export interface FieldSelectionConfig { - /** Max depth for nested object selections */ - maxDepth: number; - /** Skip the 'query' field in payloads */ - skipQueryField: boolean; - /** Type registry for resolving nested types */ - typeRegistry?: TypeRegistry; -} - -// ============================================================================ -// Type Node Builders (GraphQL Type AST) -// ============================================================================ - -/** - * Build a GraphQL type node from CleanTypeRef - * Handles NON_NULL, LIST, and named types - */ -function buildTypeNode(typeRef: CleanTypeRef): TypeNode { - switch (typeRef.kind) { - case 'NON_NULL': - if (typeRef.ofType) { - const innerType = buildTypeNode(typeRef.ofType); - // Can't wrap NON_NULL in NON_NULL - if (innerType.kind === 'NonNullType') { - return innerType; - } - return t.nonNullType({ type: innerType as any }); - } - return t.namedType({ type: 'String' }); - - case 'LIST': - if (typeRef.ofType) { - return t.listType({ type: buildTypeNode(typeRef.ofType) }); - } - return t.listType({ type: t.namedType({ type: 'String' }) }); - - case 'SCALAR': - case 'ENUM': - case 'OBJECT': - case 'INPUT_OBJECT': - return t.namedType({ type: typeRef.name ?? 'String' }); - - default: - return t.namedType({ type: typeRef.name ?? 'String' }); - } -} - -// ============================================================================ -// Variable Definition Builders -// ============================================================================ - -/** - * Build variable definitions from operation arguments - */ -export function buildVariableDefinitions( - args: CleanArgument[] -): VariableDefinitionNode[] { - return args.map((arg) => - t.variableDefinition({ - variable: t.variable({ name: arg.name }), - type: buildTypeNode(arg.type), - }) - ); -} - -/** - * Build argument nodes that reference variables - */ -function buildArgumentNodes(args: CleanArgument[]): ArgumentNode[] { - return args.map((arg) => - t.argument({ - name: arg.name, - value: t.variable({ name: arg.name }), - }) - ); -} - -// ============================================================================ -// Field Selection Builders -// ============================================================================ - -/** - * Check if a type should have selections (is an object type) - */ -function typeNeedsSelections(typeRef: CleanTypeRef): boolean { - const baseKind = getBaseTypeKind(typeRef); - return baseKind === 'OBJECT'; -} - -/** - * Get the resolved fields for a type reference - * Uses type registry for deep resolution - */ -function getResolvedFields( - typeRef: CleanTypeRef, - typeRegistry?: TypeRegistry -): CleanObjectField[] | undefined { - // First check if fields are directly on the typeRef - if (typeRef.fields) { - return typeRef.fields; - } - - // For wrapper types, unwrap and check - if (typeRef.ofType) { - return getResolvedFields(typeRef.ofType, typeRegistry); - } - - // Look up in type registry - if (typeRegistry && typeRef.name) { - const resolved = typeRegistry.get(typeRef.name); - if (resolved?.fields) { - return resolved.fields; - } - } - - return undefined; -} - -/** - * Build field selections for an object type - * Recursively handles nested objects up to maxDepth - */ -export function buildFieldSelections( - typeRef: CleanTypeRef, - config: FieldSelectionConfig, - currentDepth: number = 0 -): FieldNode[] { - const { maxDepth, skipQueryField, typeRegistry } = config; - - // Stop recursion at max depth - if (currentDepth >= maxDepth) { - return []; - } - - const fields = getResolvedFields(typeRef, typeRegistry); - if (!fields || fields.length === 0) { - return []; - } - - const selections: FieldNode[] = []; - - for (const field of fields) { - // Skip internal fields - if (shouldSkipField(field.name, skipQueryField)) { - continue; - } - - const fieldKind = getBaseTypeKind(field.type); - - // For scalar and enum types, just add the field - if (fieldKind === 'SCALAR' || fieldKind === 'ENUM') { - selections.push(t.field({ name: field.name })); - continue; - } - - // For object types, recurse if within depth limit - if (fieldKind === 'OBJECT' && currentDepth < maxDepth - 1) { - const nestedSelections = buildFieldSelections( - field.type, - config, - currentDepth + 1 - ); - - if (nestedSelections.length > 0) { - selections.push( - t.field({ - name: field.name, - selectionSet: t.selectionSet({ selections: nestedSelections }), - }) - ); - } - } - } - - return selections; -} - -/** - * Build selections for a return type, handling connections and payloads - */ -function buildReturnTypeSelections( - returnType: CleanTypeRef, - config: FieldSelectionConfig -): FieldNode[] { - const fields = getResolvedFields(returnType, config.typeRegistry); - - if (!fields || fields.length === 0) { - return []; - } - - // Check if this is a connection type - const hasNodes = fields.some((f) => f.name === 'nodes'); - const hasTotalCount = fields.some((f) => f.name === 'totalCount'); - - if (hasNodes && hasTotalCount) { - return buildConnectionSelections(fields, config); - } - - // Check if this is a mutation payload (has clientMutationId) - const hasClientMutationId = fields.some( - (f) => f.name === 'clientMutationId' - ); - - if (hasClientMutationId) { - return buildPayloadSelections(fields, config); - } - - // Regular object - build normal selections - return buildFieldSelections(returnType, config); -} - -/** - * Build selections for a connection type - */ -function buildConnectionSelections( - fields: CleanObjectField[], - config: FieldSelectionConfig -): FieldNode[] { - const selections: FieldNode[] = []; - - // Add totalCount - const totalCountField = fields.find((f) => f.name === 'totalCount'); - if (totalCountField) { - selections.push(t.field({ name: 'totalCount' })); - } - - // Add nodes with nested selections - const nodesField = fields.find((f) => f.name === 'nodes'); - if (nodesField) { - const nodeSelections = buildFieldSelections(nodesField.type, config); - if (nodeSelections.length > 0) { - selections.push( - t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections: nodeSelections }), - }) - ); - } - } - - // Add pageInfo - const pageInfoField = fields.find((f) => f.name === 'pageInfo'); - if (pageInfoField) { - selections.push( - t.field({ - name: 'pageInfo', - selectionSet: t.selectionSet({ - selections: [ - t.field({ name: 'hasNextPage' }), - t.field({ name: 'hasPreviousPage' }), - t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }), - ], - }), - }) - ); - } - - return selections; -} - -/** - * Build selections for a mutation payload type - */ -function buildPayloadSelections( - fields: CleanObjectField[], - config: FieldSelectionConfig -): FieldNode[] { - const selections: FieldNode[] = []; - - for (const field of fields) { - // Skip query field - if (shouldSkipField(field.name, config.skipQueryField)) { - continue; - } - - const fieldKind = getBaseTypeKind(field.type); - - // Add scalar fields directly - if (fieldKind === 'SCALAR' || fieldKind === 'ENUM') { - selections.push(t.field({ name: field.name })); - continue; - } - - // For object fields (like the returned entity), add with selections - if (fieldKind === 'OBJECT') { - const nestedSelections = buildFieldSelections(field.type, config); - if (nestedSelections.length > 0) { - selections.push( - t.field({ - name: field.name, - selectionSet: t.selectionSet({ selections: nestedSelections }), - }) - ); - } - } - } - - return selections; -} - -// ============================================================================ -// Custom Query Builder -// ============================================================================ - -export interface CustomQueryConfig { - operation: CleanOperation; - typeRegistry?: TypeRegistry; - maxDepth?: number; - skipQueryField?: boolean; -} - -/** - * Build a custom query AST from a CleanOperation - */ -export function buildCustomQueryAST(config: CustomQueryConfig): DocumentNode { - const { - operation, - typeRegistry, - maxDepth = 2, - skipQueryField = true, - } = config; - - const operationName = `${ucFirst(operation.name)}Query`; - - // Build variable definitions - const variableDefinitions = buildVariableDefinitions(operation.args); - - // Build arguments that reference the variables - const args = buildArgumentNodes(operation.args); - - // Build return type selections - const fieldSelectionConfig: FieldSelectionConfig = { - maxDepth, - skipQueryField, - typeRegistry, - }; - - const returnTypeNeedsSelections = typeNeedsSelections(operation.returnType); - let selections: FieldNode[] = []; - - if (returnTypeNeedsSelections) { - selections = buildReturnTypeSelections( - operation.returnType, - fieldSelectionConfig - ); - } - - // Build the query field - const queryField: FieldNode = - selections.length > 0 - ? t.field({ - name: operation.name, - args: args.length > 0 ? args : undefined, - selectionSet: t.selectionSet({ selections }), - }) - : t.field({ - name: operation.name, - args: args.length > 0 ? args : undefined, - }); - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'query', - name: operationName, - variableDefinitions: - variableDefinitions.length > 0 ? variableDefinitions : undefined, - selectionSet: t.selectionSet({ - selections: [queryField], - }), - }), - ], - }); -} - -// ============================================================================ -// Custom Mutation Builder -// ============================================================================ - -export interface CustomMutationConfig { - operation: CleanOperation; - typeRegistry?: TypeRegistry; - maxDepth?: number; - skipQueryField?: boolean; -} - -/** - * Build a custom mutation AST from a CleanOperation - */ -export function buildCustomMutationAST( - config: CustomMutationConfig -): DocumentNode { - const { - operation, - typeRegistry, - maxDepth = 2, - skipQueryField = true, - } = config; - - const operationName = `${ucFirst(operation.name)}Mutation`; - - // Build variable definitions - const variableDefinitions = buildVariableDefinitions(operation.args); - - // Build arguments that reference the variables - const args = buildArgumentNodes(operation.args); - - // Build return type selections - const fieldSelectionConfig: FieldSelectionConfig = { - maxDepth, - skipQueryField, - typeRegistry, - }; - - const returnTypeNeedsSelections = typeNeedsSelections(operation.returnType); - let selections: FieldNode[] = []; - - if (returnTypeNeedsSelections) { - selections = buildReturnTypeSelections( - operation.returnType, - fieldSelectionConfig - ); - } - - // Build the mutation field - const mutationField: FieldNode = - selections.length > 0 - ? t.field({ - name: operation.name, - args: args.length > 0 ? args : undefined, - selectionSet: t.selectionSet({ selections }), - }) - : t.field({ - name: operation.name, - args: args.length > 0 ? args : undefined, - }); - - return t.document({ - definitions: [ - t.operationDefinition({ - operation: 'mutation', - name: operationName, - variableDefinitions: - variableDefinitions.length > 0 ? variableDefinitions : undefined, - selectionSet: t.selectionSet({ - selections: [mutationField], - }), - }), - ], - }); -} - -// ============================================================================ -// Print Utilities -// ============================================================================ - -/** - * Print a document AST to GraphQL string - */ -export function printGraphQL(ast: DocumentNode): string { - return print(ast); -} - -/** - * Build and print a custom query in one call - */ -export function buildCustomQueryString(config: CustomQueryConfig): string { - return printGraphQL(buildCustomQueryAST(config)); -} - -/** - * Build and print a custom mutation in one call - */ -export function buildCustomMutationString( - config: CustomMutationConfig -): string { - return printGraphQL(buildCustomMutationAST(config)); -} - -// ============================================================================ -// Helper Utilities -// ============================================================================ - -/** - * Uppercase first character - */ -function ucFirst(str: string): string { - return str.charAt(0).toUpperCase() + str.slice(1); -} diff --git a/graphql/codegen/src/core/codegen/schema-types-generator.ts b/graphql/codegen/src/core/codegen/schema-types-generator.ts index b4bc6a655..0e8e4b6a1 100644 --- a/graphql/codegen/src/core/codegen/schema-types-generator.ts +++ b/graphql/codegen/src/core/codegen/schema-types-generator.ts @@ -11,19 +11,20 @@ * * Uses Babel AST for robust code generation. */ +import * as t from '@babel/types'; + import type { - TypeRegistry, CleanArgument, ResolvedType, + TypeRegistry } from '../../types/schema'; -import * as t from '@babel/types'; import { generateCode } from './babel-ast'; -import { getTypeBaseName } from './type-resolver'; import { - scalarToTsType, - SCALAR_NAMES, BASE_FILTER_TYPE_NAMES, + SCALAR_NAMES, + scalarToTsType } from './scalars'; +import { getTypeBaseName } from './type-resolver'; import { getGeneratedFileHeader } from './utils'; export interface GeneratedSchemaTypesFile { @@ -49,7 +50,7 @@ const SKIP_TYPES = new Set([ '__InputValue', '__EnumValue', '__Directive', - ...BASE_FILTER_TYPE_NAMES, + ...BASE_FILTER_TYPE_NAMES ]); const SKIP_TYPE_PATTERNS: RegExp[] = []; @@ -390,6 +391,6 @@ export function generateSchemaTypesFile( fileName: 'schema-types.ts', content, generatedEnums: Array.from(enumResult.generatedTypes).sort(), - referencedTableTypes, + referencedTableTypes }; } diff --git a/graphql/codegen/src/core/codegen/select-helpers.ts b/graphql/codegen/src/core/codegen/select-helpers.ts new file mode 100644 index 000000000..d17a770e3 --- /dev/null +++ b/graphql/codegen/src/core/codegen/select-helpers.ts @@ -0,0 +1,90 @@ +/** + * Shared helpers for select type resolution in custom operations + * + * Used by custom-queries.ts, custom-mutations.ts, and orm/custom-ops-generator.ts + */ +import type { CleanArgument, TypeRegistry } from '../../types/schema'; +import { SCALAR_NAMES } from './scalars'; +import { getTypeBaseName } from './type-resolver'; + +/** + * Types that don't need Select types (scalars + root query/mutation types) + */ +export const NON_SELECT_TYPES = new Set([ + ...SCALAR_NAMES, + 'Query', + 'Mutation' +]); + +/** + * Get the Select type name for a return type. + * Returns null for scalar types, Connection types, and root types. + */ +export function getSelectTypeName(returnType: CleanArgument['type']): string | null { + const baseName = getTypeBaseName(returnType); + if ( + baseName && + !NON_SELECT_TYPES.has(baseName) && + !baseName.endsWith('Connection') + ) { + return `${baseName}Select`; + } + return null; +} + +/** + * Wrap a type reference in InferSelectResult, handling NON_NULL and LIST wrappers. + */ +export function wrapInferSelectResult( + typeRef: CleanArgument['type'], + payloadTypeName: string +): string { + if (typeRef.kind === 'NON_NULL' && typeRef.ofType) { + return wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName); + } + + if (typeRef.kind === 'LIST' && typeRef.ofType) { + return `${wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName)}[]`; + } + + return `InferSelectResult<${payloadTypeName}, S>`; +} + +/** + * Build a default select literal string for a given type. + * Finds an 'id' or 'nodeId' field, or falls back to first scalar field. + */ +export function buildDefaultSelectLiteral( + typeName: string, + typeRegistry: TypeRegistry, + depth: number = 0 +): string { + const resolved = typeRegistry.get(typeName); + const fields = resolved?.fields ?? []; + + if (depth > 3 || fields.length === 0) { + // Use first field if available, otherwise fallback to 'id' + return fields.length > 0 ? `{ ${fields[0].name}: true }` : `{ id: true }`; + } + + const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); + if (idLike) return `{ ${idLike.name}: true }`; + + const scalarField = fields.find((f) => { + const baseName = getTypeBaseName(f.type); + if (!baseName) return false; + if (NON_SELECT_TYPES.has(baseName)) return true; + return typeRegistry.get(baseName)?.kind === 'ENUM'; + }); + if (scalarField) return `{ ${scalarField.name}: true }`; + + const first = fields[0]; + + const firstBase = getTypeBaseName(first.type); + if (!firstBase || NON_SELECT_TYPES.has(firstBase) || typeRegistry.get(firstBase)?.kind === 'ENUM') { + return `{ ${first.name}: true }`; + } + + const nested = buildDefaultSelectLiteral(firstBase, typeRegistry, depth + 1); + return `{ ${first.name}: { select: ${nested} } }`; +} diff --git a/graphql/codegen/src/core/codegen/shared/index.ts b/graphql/codegen/src/core/codegen/shared/index.ts index ccf6819fe..ccc31ead8 100644 --- a/graphql/codegen/src/core/codegen/shared/index.ts +++ b/graphql/codegen/src/core/codegen/shared/index.ts @@ -11,12 +11,13 @@ * schema-types.ts - Enums, input types, payload types * filters.ts - Filter types (StringFilter, IntFilter, etc.) */ -import type { CleanTable, CleanOperation, TypeRegistry } from '../../../types/schema'; -import type { GraphQLSDKConfigTarget } from '../../../types/config'; import * as t from '@babel/types'; -import { generateCode, addJSDocComment } from '../babel-ast'; -import { generateTypesFile } from '../types'; + +import type { GraphQLSDKConfigTarget } from '../../../types/config'; +import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema'; +import { addJSDocComment,generateCode } from '../babel-ast'; import { generateSchemaTypesFile } from '../schema-types-generator'; +import { generateTypesFile } from '../types'; import { getTableNames } from '../utils'; /** @@ -64,14 +65,14 @@ export function generateSharedTypes(options: GenerateSharedOptions): GenerateSha if (customOperations && customOperations.typeRegistry) { const schemaTypesResult = generateSchemaTypesFile({ typeRegistry: customOperations.typeRegistry, - tableTypeNames, + tableTypeNames }); // Only include if there's meaningful content if (schemaTypesResult.content.split('\n').length > 10) { files.push({ path: 'schema-types.ts', - content: schemaTypesResult.content, + content: schemaTypesResult.content }); hasSchemaTypes = true; generatedEnumNames = schemaTypesResult.generatedEnums || []; @@ -82,21 +83,21 @@ export function generateSharedTypes(options: GenerateSharedOptions): GenerateSha files.push({ path: 'types.ts', content: generateTypesFile(tables, { - enumsFromSchemaTypes: generatedEnumNames, - }), + enumsFromSchemaTypes: generatedEnumNames + }) }); // 3. Generate barrel export (index.ts) const barrelContent = generateSharedBarrel(hasSchemaTypes); files.push({ path: 'index.ts', - content: barrelContent, + content: barrelContent }); return { files, generatedEnumNames, - hasSchemaTypes, + hasSchemaTypes }; } @@ -118,12 +119,12 @@ function generateSharedBarrel(hasSchemaTypes: boolean): string { if (statements.length > 0) { addJSDocComment(statements[0], [ 'Shared types - auto-generated, do not edit', - '@generated by @constructive-io/graphql-codegen', + '@generated by @constructive-io/graphql-codegen' ]); } return generateCode(statements); } -export { generateTypesFile } from '../types'; export { generateSchemaTypesFile } from '../schema-types-generator'; +export { generateTypesFile } from '../types'; diff --git a/graphql/codegen/src/core/codegen/templates/client.browser.ts b/graphql/codegen/src/core/codegen/templates/client.browser.ts deleted file mode 100644 index 6db2d891c..000000000 --- a/graphql/codegen/src/core/codegen/templates/client.browser.ts +++ /dev/null @@ -1,271 +0,0 @@ -/** - * GraphQL client configuration and execution (Browser-compatible) - * - * This is the RUNTIME code that gets copied to generated output. - * Uses native W3C fetch API for browser compatibility. - * - * NOTE: This file is read at codegen time and written to output. - * Any changes here will affect all generated clients. - */ - -// ============================================================================ -// Configuration -// ============================================================================ - -export interface GraphQLClientConfig { - /** GraphQL endpoint URL */ - endpoint: string; - /** Default headers to include in all requests */ - headers?: Record; - /** Dynamic headers callback called on every request */ - getHeaders?: () => Record; -} - -let globalConfig: GraphQLClientConfig | null = null; - -/** - * Configure the GraphQL client - * - * @example - * ```ts - * import { configure } from './generated'; - * - * configure({ - * endpoint: 'https://api.example.com/graphql', - * headers: { - * Authorization: 'Bearer ', - * }, - * }); - * ``` - */ -export function configure(config: GraphQLClientConfig): void { - globalConfig = config; -} - -/** - * Get the current configuration - * @throws Error if not configured - */ -export function getConfig(): GraphQLClientConfig { - if (!globalConfig) { - throw new Error( - 'GraphQL client not configured. Call configure() before making requests.' - ); - } - return globalConfig; -} - -/** - * Set a single header value - * Useful for updating Authorization after login - * - * @example - * ```ts - * setHeader('Authorization', 'Bearer '); - * ``` - */ -export function setHeader(key: string, value: string): void { - const config = getConfig(); - globalConfig = { - ...config, - headers: { ...config.headers, [key]: value }, - }; -} - -/** - * Merge multiple headers into the current configuration - * - * @example - * ```ts - * setHeaders({ Authorization: 'Bearer ', 'X-Custom': 'value' }); - * ``` - */ -export function setHeaders(headers: Record): void { - const config = getConfig(); - globalConfig = { - ...config, - headers: { ...config.headers, ...headers }, - }; -} - -// ============================================================================ -// Error handling -// ============================================================================ - -export interface GraphQLError { - message: string; - locations?: Array<{ line: number; column: number }>; - path?: Array; - extensions?: Record; -} - -export class GraphQLClientError extends Error { - constructor( - message: string, - public errors: GraphQLError[], - public response?: Response - ) { - super(message); - this.name = 'GraphQLClientError'; - } -} - -// ============================================================================ -// Execution -// ============================================================================ - -export interface ExecuteOptions { - /** Override headers for this request */ - headers?: Record; - /** AbortSignal for request cancellation */ - signal?: AbortSignal; -} - -/** - * Execute a GraphQL operation - * - * @example - * ```ts - * const result = await execute( - * carsQueryDocument, - * { first: 10 } - * ); - * ``` - */ -export async function execute< - TData = unknown, - TVariables = Record, ->( - document: string, - variables?: TVariables, - options?: ExecuteOptions -): Promise { - const config = getConfig(); - const dynamicHeaders = config.getHeaders?.() ?? {}; - - const response = await fetch(config.endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...config.headers, - ...dynamicHeaders, - ...options?.headers, - }, - body: JSON.stringify({ - query: document, - variables, - }), - signal: options?.signal, - }); - - const json = await response.json(); - - if (json.errors && json.errors.length > 0) { - throw new GraphQLClientError( - json.errors[0].message || 'GraphQL request failed', - json.errors, - response - ); - } - - return json.data as TData; -} - -/** - * Execute a GraphQL operation with full response (data + errors) - * Useful when you want to handle partial data with errors - */ -export async function executeWithErrors< - TData = unknown, - TVariables = Record, ->( - document: string, - variables?: TVariables, - options?: ExecuteOptions -): Promise<{ data: TData | null; errors: GraphQLError[] | null }> { - const config = getConfig(); - const dynamicHeaders = config.getHeaders?.() ?? {}; - - const response = await fetch(config.endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...config.headers, - ...dynamicHeaders, - ...options?.headers, - }, - body: JSON.stringify({ - query: document, - variables, - }), - signal: options?.signal, - }); - - const json = await response.json(); - - return { - data: json.data ?? null, - errors: json.errors ?? null, - }; -} - -// ============================================================================ -// QueryClient Factory -// ============================================================================ - -/** - * Default QueryClient configuration optimized for GraphQL - * - * These defaults provide a good balance between freshness and performance: - * - staleTime: 1 minute - data considered fresh, won't refetch - * - gcTime: 5 minutes - unused data kept in cache - * - refetchOnWindowFocus: false - don't refetch when tab becomes active - * - retry: 1 - retry failed requests once - */ -export const defaultQueryClientOptions = { - defaultOptions: { - queries: { - staleTime: 1000 * 60, // 1 minute - gcTime: 1000 * 60 * 5, // 5 minutes - refetchOnWindowFocus: false, - retry: 1, - }, - }, -}; - -/** - * QueryClient options type for createQueryClient - */ -export interface CreateQueryClientOptions { - defaultOptions?: { - queries?: { - staleTime?: number; - gcTime?: number; - refetchOnWindowFocus?: boolean; - retry?: number | boolean; - retryDelay?: number | ((attemptIndex: number) => number); - }; - mutations?: { - retry?: number | boolean; - retryDelay?: number | ((attemptIndex: number) => number); - }; - }; -} - -// Note: createQueryClient is available when using with @tanstack/react-query -// Import QueryClient from '@tanstack/react-query' and use these options: -// -// import { QueryClient } from '@tanstack/react-query'; -// const queryClient = new QueryClient(defaultQueryClientOptions); -// -// Or merge with your own options: -// const queryClient = new QueryClient({ -// ...defaultQueryClientOptions, -// defaultOptions: { -// ...defaultQueryClientOptions.defaultOptions, -// queries: { -// ...defaultQueryClientOptions.defaultOptions.queries, -// staleTime: 30000, // Override specific options -// }, -// }, -// }); diff --git a/graphql/codegen/src/core/codegen/templates/client.node.ts b/graphql/codegen/src/core/codegen/templates/client.node.ts deleted file mode 100644 index 156f41ceb..000000000 --- a/graphql/codegen/src/core/codegen/templates/client.node.ts +++ /dev/null @@ -1,337 +0,0 @@ -/** - * GraphQL client configuration and execution (Node.js with native http/https) - * - * This is the RUNTIME code that gets copied to generated output. - * Uses native Node.js http/https modules. - * - * NOTE: This file is read at codegen time and written to output. - * Any changes here will affect all generated clients. - */ - -import http from 'node:http'; -import https from 'node:https'; - -// ============================================================================ -// HTTP Request Helper -// ============================================================================ - -interface HttpResponse { - statusCode: number; - statusMessage: string; - data: string; -} - -/** - * Make an HTTP/HTTPS request using native Node modules - */ -function makeRequest( - url: URL, - options: http.RequestOptions, - body: string -): Promise { - return new Promise((resolve, reject) => { - const protocol = url.protocol === 'https:' ? https : http; - - const req = protocol.request(url, options, (res) => { - let data = ''; - res.setEncoding('utf8'); - res.on('data', (chunk: string) => { - data += chunk; - }); - res.on('end', () => { - resolve({ - statusCode: res.statusCode || 0, - statusMessage: res.statusMessage || '', - data, - }); - }); - }); - - req.on('error', reject); - req.write(body); - req.end(); - }); -} - -// ============================================================================ -// Configuration -// ============================================================================ - -export interface GraphQLClientConfig { - /** GraphQL endpoint URL */ - endpoint: string; - /** Default headers to include in all requests */ - headers?: Record; - /** Dynamic headers callback called on every request */ - getHeaders?: () => Record; -} - -let globalConfig: GraphQLClientConfig | null = null; - -/** - * Configure the GraphQL client - * - * @example - * ```ts - * import { configure } from './generated'; - * - * configure({ - * endpoint: 'https://api.example.com/graphql', - * headers: { - * Authorization: 'Bearer ', - * }, - * }); - * ``` - */ -export function configure(config: GraphQLClientConfig): void { - globalConfig = config; -} - -/** - * Get the current configuration - * @throws Error if not configured - */ -export function getConfig(): GraphQLClientConfig { - if (!globalConfig) { - throw new Error( - 'GraphQL client not configured. Call configure() before making requests.' - ); - } - return globalConfig; -} - -/** - * Set a single header value - * Useful for updating Authorization after login - * - * @example - * ```ts - * setHeader('Authorization', 'Bearer '); - * ``` - */ -export function setHeader(key: string, value: string): void { - const config = getConfig(); - globalConfig = { - ...config, - headers: { ...config.headers, [key]: value }, - }; -} - -/** - * Merge multiple headers into the current configuration - * - * @example - * ```ts - * setHeaders({ Authorization: 'Bearer ', 'X-Custom': 'value' }); - * ``` - */ -export function setHeaders(headers: Record): void { - const config = getConfig(); - globalConfig = { - ...config, - headers: { ...config.headers, ...headers }, - }; -} - -// ============================================================================ -// Error handling -// ============================================================================ - -export interface GraphQLError { - message: string; - locations?: Array<{ line: number; column: number }>; - path?: Array; - extensions?: Record; -} - -export class GraphQLClientError extends Error { - constructor( - message: string, - public errors: GraphQLError[], - public statusCode?: number - ) { - super(message); - this.name = 'GraphQLClientError'; - } -} - -// ============================================================================ -// Execution -// ============================================================================ - -export interface ExecuteOptions { - /** Override headers for this request */ - headers?: Record; -} - -/** - * Execute a GraphQL operation - * - * @example - * ```ts - * const result = await execute( - * carsQueryDocument, - * { first: 10 } - * ); - * ``` - */ -export async function execute< - TData = unknown, - TVariables = Record, ->( - document: string, - variables?: TVariables, - options?: ExecuteOptions -): Promise { - const config = getConfig(); - const url = new URL(config.endpoint); - const dynamicHeaders = config.getHeaders?.() ?? {}; - - const body = JSON.stringify({ - query: document, - variables, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...config.headers, - ...dynamicHeaders, - ...options?.headers, - }, - }; - - const response = await makeRequest(url, requestOptions, body); - - if (response.statusCode < 200 || response.statusCode >= 300) { - throw new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`); - } - - const json = JSON.parse(response.data) as { - data?: TData; - errors?: GraphQLError[]; - }; - - if (json.errors && json.errors.length > 0) { - throw new GraphQLClientError( - json.errors[0].message || 'GraphQL request failed', - json.errors, - response.statusCode - ); - } - - return json.data as TData; -} - -/** - * Execute a GraphQL operation with full response (data + errors) - * Useful when you want to handle partial data with errors - */ -export async function executeWithErrors< - TData = unknown, - TVariables = Record, ->( - document: string, - variables?: TVariables, - options?: ExecuteOptions -): Promise<{ data: TData | null; errors: GraphQLError[] | null }> { - const config = getConfig(); - const url = new URL(config.endpoint); - const dynamicHeaders = config.getHeaders?.() ?? {}; - - const body = JSON.stringify({ - query: document, - variables, - }); - - const requestOptions: http.RequestOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - ...config.headers, - ...dynamicHeaders, - ...options?.headers, - }, - }; - - const response = await makeRequest(url, requestOptions, body); - - if (response.statusCode < 200 || response.statusCode >= 300) { - return { - data: null, - errors: [{ message: `HTTP ${response.statusCode}: ${response.statusMessage}` }], - }; - } - - const json = JSON.parse(response.data) as { - data?: TData; - errors?: GraphQLError[]; - }; - - return { - data: json.data ?? null, - errors: json.errors ?? null, - }; -} - -// ============================================================================ -// QueryClient Factory -// ============================================================================ - -/** - * Default QueryClient configuration optimized for GraphQL - * - * These defaults provide a good balance between freshness and performance: - * - staleTime: 1 minute - data considered fresh, won't refetch - * - gcTime: 5 minutes - unused data kept in cache - * - refetchOnWindowFocus: false - don't refetch when tab becomes active - * - retry: 1 - retry failed requests once - */ -export const defaultQueryClientOptions = { - defaultOptions: { - queries: { - staleTime: 1000 * 60, // 1 minute - gcTime: 1000 * 60 * 5, // 5 minutes - refetchOnWindowFocus: false, - retry: 1, - }, - }, -}; - -/** - * QueryClient options type for createQueryClient - */ -export interface CreateQueryClientOptions { - defaultOptions?: { - queries?: { - staleTime?: number; - gcTime?: number; - refetchOnWindowFocus?: boolean; - retry?: number | boolean; - retryDelay?: number | ((attemptIndex: number) => number); - }; - mutations?: { - retry?: number | boolean; - retryDelay?: number | ((attemptIndex: number) => number); - }; - }; -} - -// Note: createQueryClient is available when using with @tanstack/react-query -// Import QueryClient from '@tanstack/react-query' and use these options: -// -// import { QueryClient } from '@tanstack/react-query'; -// const queryClient = new QueryClient(defaultQueryClientOptions); -// -// Or merge with your own options: -// const queryClient = new QueryClient({ -// ...defaultQueryClientOptions, -// defaultOptions: { -// ...defaultQueryClientOptions.defaultOptions, -// queries: { -// ...defaultQueryClientOptions.defaultOptions.queries, -// staleTime: 30000, // Override specific options -// }, -// }, -// }); diff --git a/graphql/codegen/src/core/codegen/templates/orm-client.ts b/graphql/codegen/src/core/codegen/templates/orm-client.ts index a71633d67..c6993a338 100644 --- a/graphql/codegen/src/core/codegen/templates/orm-client.ts +++ b/graphql/codegen/src/core/codegen/templates/orm-client.ts @@ -11,13 +11,13 @@ import type { GraphQLAdapter, GraphQLError, - QueryResult, + QueryResult } from '@constructive-io/graphql-types'; export type { GraphQLAdapter, GraphQLError, - QueryResult, + QueryResult } from '@constructive-io/graphql-types'; /** @@ -43,19 +43,19 @@ export class FetchAdapter implements GraphQLAdapter { headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers, + ...this.headers }, body: JSON.stringify({ query: document, - variables: variables ?? {}, - }), + variables: variables ?? {} + }) }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }], + errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }] }; } @@ -68,14 +68,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors, + errors: json.errors }; } return { ok: true, data: json.data as T, - errors: undefined, + errors: undefined }; } diff --git a/graphql/codegen/src/core/codegen/templates/query-builder.ts b/graphql/codegen/src/core/codegen/templates/query-builder.ts index 869e210e8..a678fbbd0 100644 --- a/graphql/codegen/src/core/codegen/templates/query-builder.ts +++ b/graphql/codegen/src/core/codegen/templates/query-builder.ts @@ -8,15 +8,16 @@ * Any changes here will affect all generated ORM clients. */ -import * as t from 'gql-ast'; import { parseType, print } from '@0no-co/graphql.web'; +import * as t from 'gql-ast'; import type { ArgumentNode, - FieldNode, - VariableDefinitionNode, EnumValueNode, + FieldNode, + VariableDefinitionNode } from 'graphql'; -import { OrmClient, QueryResult, GraphQLRequestError } from './client'; + +import { GraphQLRequestError,OrmClient, QueryResult } from './client'; export interface QueryBuilderConfig { client: OrmClient; @@ -133,7 +134,7 @@ export function buildSelections( nested.filter ? t.argument({ name: 'filter', value: buildValueAst(nested.filter) }) : null, - buildEnumListArg('orderBy', nested.orderBy), + buildEnumListArg('orderBy', nested.orderBy) ]); if (isConnection) { @@ -142,8 +143,8 @@ export function buildSelections( name: key, args, selectionSet: t.selectionSet({ - selections: buildConnectionSelections(nestedSelections), - }), + selections: buildConnectionSelections(nestedSelections) + }) }) ); } else { @@ -151,7 +152,7 @@ export function buildSelections( t.field({ name: key, args, - selectionSet: t.selectionSet({ selections: nestedSelections }), + selectionSet: t.selectionSet({ selections: nestedSelections }) }) ); } @@ -200,7 +201,7 @@ export function buildFindManyDocument( { varName: 'orderBy', typeName: '[' + orderByTypeName + '!]', - value: args.orderBy?.length ? args.orderBy : undefined, + value: args.orderBy?.length ? args.orderBy : undefined }, variableDefinitions, queryArgs, @@ -249,13 +250,13 @@ export function buildFindManyDocument( name: queryField, args: queryArgs.length ? queryArgs : undefined, selectionSet: t.selectionSet({ - selections: buildConnectionSelections(selections), - }), - }), - ], - }), - }), - ], + selections: buildConnectionSelections(selections) + }) + }) + ] + }) + }) + ] }); return { document: print(document), variables }; @@ -305,15 +306,15 @@ export function buildFindFirstDocument( selections: [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections }), - }), - ], - }), - }), - ], - }), - }), - ], + selectionSet: t.selectionSet({ selections }) + }) + ] + }) + }) + ] + }) + }) + ] }); return { document: print(document), variables }; @@ -339,15 +340,15 @@ export function buildCreateDocument( resultSelections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections }), - }), - ], + selectionSet: t.selectionSet({ selections }) + }) + ] }), variables: { input: { - [entityField]: data, - }, - }, + [entityField]: data + } + } }; } @@ -372,26 +373,117 @@ export function buildUpdateDocument( +export function buildUpdateByPkDocument( + operationName: string, + mutationField: string, + entityField: string, + select: TSelect, + id: string | number, + data: TData, + inputTypeName: string, + idFieldName: string +): { document: string; variables: Record } { + const selections = select + ? buildSelections(select as Record) + : [t.field({ name: 'id' })]; + + return { + document: buildInputMutationDocument({ + operationName, + mutationField, + inputTypeName, + resultSelections: [ + t.field({ + name: entityField, + selectionSet: t.selectionSet({ selections }) + }) + ] + }), + variables: { + input: { + [idFieldName]: id, + patch: data + } + } + }; +} + +export function buildFindOneDocument( + operationName: string, + queryField: string, + id: string | number, + select: TSelect, + idArgName: string, + idTypeName: string +): { document: string; variables: Record } { + const selections = select + ? buildSelections(select as Record) + : [t.field({ name: 'id' })]; + + const variableDefinitions: VariableDefinitionNode[] = [ + t.variableDefinition({ + variable: t.variable({ name: idArgName }), + type: parseType(idTypeName) + }) + ]; + + const queryArgs: ArgumentNode[] = [ + t.argument({ + name: idArgName, + value: t.variable({ name: idArgName }) + }) + ]; + + const document = t.document({ + definitions: [ + t.operationDefinition({ + operation: 'query', + name: operationName + 'Query', + variableDefinitions, + selectionSet: t.selectionSet({ + selections: [ + t.field({ + name: queryField, + args: queryArgs, + selectionSet: t.selectionSet({ selections }) + }) + ] + }) + }) + ] + }); + + return { + document: print(document), + variables: { [idArgName]: id } + }; +} + +export function buildDeleteDocument( operationName: string, mutationField: string, entityField: string, where: TWhere, - inputTypeName: string + inputTypeName: string, + select?: TSelect ): { document: string; variables: Record } { + const entitySelections = select + ? buildSelections(select as Record) + : [t.field({ name: 'id' })]; + return { document: buildInputMutationDocument({ operationName, @@ -401,16 +493,49 @@ export function buildDeleteDocument( t.field({ name: entityField, selectionSet: t.selectionSet({ - selections: [t.field({ name: 'id' })], - }), - }), - ], + selections: entitySelections + }) + }) + ] }), variables: { input: { - id: where.id, - }, - }, + id: where.id + } + } + }; +} + +export function buildDeleteByPkDocument( + operationName: string, + mutationField: string, + entityField: string, + id: string | number, + inputTypeName: string, + idFieldName: string, + select?: TSelect +): { document: string; variables: Record } { + const entitySelections = select + ? buildSelections(select as Record) + : [t.field({ name: 'id' })]; + + return { + document: buildInputMutationDocument({ + operationName, + mutationField, + inputTypeName, + resultSelections: [ + t.field({ + name: entityField, + selectionSet: t.selectionSet({ selections: entitySelections }) + }) + ] + }), + variables: { + input: { + [idFieldName]: id + } + } }; } @@ -440,13 +565,13 @@ export function buildCustomDocument( const variableDefs = variableDefinitions.map((definition) => t.variableDefinition({ variable: t.variable({ name: definition.name }), - type: parseType(definition.type), + type: parseType(definition.type) }) ); const fieldArgs = variableDefinitions.map((definition) => t.argument({ name: definition.name, - value: t.variable({ name: definition.name }), + value: t.variable({ name: definition.name }) }) ); @@ -467,17 +592,17 @@ export function buildCustomDocument( args: fieldArgs.length ? fieldArgs : undefined, selectionSet: fieldSelections.length ? t.selectionSet({ selections: fieldSelections }) - : undefined, - }), - ], - }), - }), - ], + : undefined + }) + ] + }) + }) + ] }); return { document: print(document), - variables: (args ?? {}) as Record, + variables: (args ?? {}) as Record }; } @@ -513,15 +638,15 @@ function buildEnumListArg( return t.argument({ name, value: t.listValue({ - values: values.map((value) => buildEnumValue(value)), - }), + values: values.map((value) => buildEnumValue(value)) + }) }); } function buildEnumValue(value: string): EnumValueNode { return { kind: 'EnumValue', - value, + value }; } @@ -530,7 +655,7 @@ function buildPageInfoSelections(): FieldNode[] { t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }), + t.field({ name: 'endCursor' }) ]; } @@ -538,13 +663,13 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { return [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections: nodeSelections }), + selectionSet: t.selectionSet({ selections: nodeSelections }) }), t.field({ name: 'totalCount' }), t.field({ name: 'pageInfo', - selectionSet: t.selectionSet({ selections: buildPageInfoSelections() }), - }), + selectionSet: t.selectionSet({ selections: buildPageInfoSelections() }) + }) ]; } @@ -571,8 +696,8 @@ function buildInputMutationDocument(config: InputMutationConfig): string { variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'input' }), - type: parseType(config.inputTypeName + '!'), - }), + type: parseType(config.inputTypeName + '!') + }) ], selectionSet: t.selectionSet({ selections: [ @@ -581,17 +706,17 @@ function buildInputMutationDocument(config: InputMutationConfig): string { args: [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }), - }), + value: t.variable({ name: 'input' }) + }) ], selectionSet: t.selectionSet({ - selections: config.resultSelections, - }), - }), - ], - }), - }), - ], + selections: config.resultSelections + }) + }) + ] + }) + }) + ] }); return print(document); } @@ -607,13 +732,13 @@ function addVariable( definitions.push( t.variableDefinition({ variable: t.variable({ name: spec.varName }), - type: parseType(spec.typeName), + type: parseType(spec.typeName) }) ); args.push( t.argument({ name: spec.argName ?? spec.varName, - value: t.variable({ name: spec.varName }), + value: t.variable({ name: spec.varName }) }) ); variables[spec.varName] = spec.value; @@ -650,7 +775,7 @@ function buildValueAst( if (Array.isArray(value)) { return t.listValue({ - values: value.map((item) => buildValueAst(item)), + values: value.map((item) => buildValueAst(item)) }); } @@ -660,9 +785,9 @@ function buildValueAst( fields: Object.entries(obj).map(([key, val]) => t.objectField({ name: key, - value: buildValueAst(val), + value: buildValueAst(val) }) - ), + ) }); } diff --git a/graphql/codegen/src/core/codegen/templates/select-types.ts b/graphql/codegen/src/core/codegen/templates/select-types.ts index 4f0360a77..df14aac7b 100644 --- a/graphql/codegen/src/core/codegen/templates/select-types.ts +++ b/graphql/codegen/src/core/codegen/templates/select-types.ts @@ -48,8 +48,17 @@ export interface UpdateArgs { select?: TSelect; } -export interface DeleteArgs { +export type FindOneArgs< + TSelect, + TIdName extends string = 'id', + TId = string +> = { + select?: TSelect; +} & Record; + +export interface DeleteArgs { where: TWhere; + select?: TSelect; } /** diff --git a/graphql/codegen/src/core/codegen/type-resolver.ts b/graphql/codegen/src/core/codegen/type-resolver.ts index f358cff12..9e8856aa3 100644 --- a/graphql/codegen/src/core/codegen/type-resolver.ts +++ b/graphql/codegen/src/core/codegen/type-resolver.ts @@ -5,11 +5,10 @@ * into TypeScript type strings and interface definitions. */ import type { - CleanTypeRef, - CleanArgument, CleanObjectField, + CleanTypeRef } from '../../types/schema'; -import { scalarToTsType as resolveScalarToTs, SCALAR_NAMES } from './scalars'; +import { SCALAR_NAMES,scalarToTsType as resolveScalarToTs } from './scalars'; // ============================================================================ // Type Tracker for Collecting Referenced Types @@ -31,7 +30,7 @@ const SKIP_TYPE_TRACKING = new Set([ '__EnumValue', '__Directive', // Connection types (handled separately) - 'PageInfo', + 'PageInfo' ]); /** @@ -88,7 +87,7 @@ export function createTypeTracker(options?: TypeTrackerOptions): TypeTracker { }, reset() { referencedTypes.clear(); - }, + } }; } @@ -116,42 +115,42 @@ export function scalarToTsType(scalarName: string): string { */ export function typeRefToTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): string { switch (typeRef.kind) { - case 'NON_NULL': - // Non-null wrapper - unwrap and return the inner type - if (typeRef.ofType) { - return typeRefToTsType(typeRef.ofType, tracker); - } - return 'unknown'; + case 'NON_NULL': + // Non-null wrapper - unwrap and return the inner type + if (typeRef.ofType) { + return typeRefToTsType(typeRef.ofType, tracker); + } + return 'unknown'; - case 'LIST': - // List wrapper - wrap inner type in array - if (typeRef.ofType) { - const innerType = typeRefToTsType(typeRef.ofType, tracker); - return `${innerType}[]`; - } - return 'unknown[]'; + case 'LIST': + // List wrapper - wrap inner type in array + if (typeRef.ofType) { + const innerType = typeRefToTsType(typeRef.ofType, tracker); + return `${innerType}[]`; + } + return 'unknown[]'; - case 'SCALAR': - // Scalar type - map to TS type - return scalarToTsType(typeRef.name ?? 'unknown'); + case 'SCALAR': + // Scalar type - map to TS type + return scalarToTsType(typeRef.name ?? 'unknown'); - case 'ENUM': { - // Enum type - use the GraphQL enum name and track it - const typeName = typeRef.name ?? 'string'; - tracker?.track(typeName); - return typeName; - } + case 'ENUM': { + // Enum type - use the GraphQL enum name and track it + const typeName = typeRef.name ?? 'string'; + tracker?.track(typeName); + return typeName; + } - case 'OBJECT': - case 'INPUT_OBJECT': { - // Object types - use the GraphQL type name and track it - const typeName = typeRef.name ?? 'unknown'; - tracker?.track(typeName); - return typeName; - } + case 'OBJECT': + case 'INPUT_OBJECT': { + // Object types - use the GraphQL type name and track it + const typeName = typeRef.name ?? 'unknown'; + tracker?.track(typeName); + return typeName; + } - default: - return 'unknown'; + default: + return 'unknown'; } } diff --git a/graphql/codegen/src/core/codegen/types.ts b/graphql/codegen/src/core/codegen/types.ts index 4491ed972..1fb93e70c 100644 --- a/graphql/codegen/src/core/codegen/types.ts +++ b/graphql/codegen/src/core/codegen/types.ts @@ -1,10 +1,11 @@ /** * Types generator - generates types.ts with entity interfaces using Babel AST */ -import type { CleanTable } from '../../types/schema'; import * as t from '@babel/types'; + +import type { CleanTable } from '../../types/schema'; import { generateCode } from './babel-ast'; -import { getScalarFields, fieldTypeToTs, getGeneratedFileHeader } from './utils'; +import { fieldTypeToTs, getGeneratedFileHeader,getScalarFields } from './utils'; interface InterfaceProperty { name: string; @@ -37,7 +38,7 @@ const FILTER_CONFIGS: Array<{ name: string; tsType: string; operators: FilterOps // List filters { name: 'StringListFilter', tsType: 'string[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] }, { name: 'IntListFilter', tsType: 'number[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] }, - { name: 'UUIDListFilter', tsType: 'string[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] }, + { name: 'UUIDListFilter', tsType: 'string[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] } ]; /** Build filter properties based on operator sets */ @@ -49,7 +50,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac props.push( { name: 'isNull', type: 'boolean', optional: true }, { name: 'equalTo', type: tsType, optional: true }, - { name: 'notEqualTo', type: tsType, optional: true }, + { name: 'notEqualTo', type: tsType, optional: true } ); } @@ -57,7 +58,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac if (operators.includes('distinct')) { props.push( { name: 'distinctFrom', type: tsType, optional: true }, - { name: 'notDistinctFrom', type: tsType, optional: true }, + { name: 'notDistinctFrom', type: tsType, optional: true } ); } @@ -65,7 +66,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac if (operators.includes('inArray')) { props.push( { name: 'in', type: `${tsType}[]`, optional: true }, - { name: 'notIn', type: `${tsType}[]`, optional: true }, + { name: 'notIn', type: `${tsType}[]`, optional: true } ); } @@ -75,7 +76,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'lessThan', type: tsType, optional: true }, { name: 'lessThanOrEqualTo', type: tsType, optional: true }, { name: 'greaterThan', type: tsType, optional: true }, - { name: 'greaterThanOrEqualTo', type: tsType, optional: true }, + { name: 'greaterThanOrEqualTo', type: tsType, optional: true } ); } @@ -97,7 +98,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'like', type: 'string', optional: true }, { name: 'notLike', type: 'string', optional: true }, { name: 'likeInsensitive', type: 'string', optional: true }, - { name: 'notLikeInsensitive', type: 'string', optional: true }, + { name: 'notLikeInsensitive', type: 'string', optional: true } ); } @@ -108,7 +109,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'containedBy', type: 'unknown', optional: true }, { name: 'containsKey', type: 'string', optional: true }, { name: 'containsAllKeys', type: 'string[]', optional: true }, - { name: 'containsAnyKeys', type: 'string[]', optional: true }, + { name: 'containsAnyKeys', type: 'string[]', optional: true } ); } @@ -117,7 +118,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac props.push( { name: 'contains', type: 'string', optional: true }, { name: 'containedBy', type: 'string', optional: true }, - { name: 'containsOrContainedBy', type: 'string', optional: true }, + { name: 'containsOrContainedBy', type: 'string', optional: true } ); } @@ -139,7 +140,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'anyLessThan', type: baseType, optional: true }, { name: 'anyLessThanOrEqualTo', type: baseType, optional: true }, { name: 'anyGreaterThan', type: baseType, optional: true }, - { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true }, + { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true } ); } @@ -237,7 +238,7 @@ export function generateTypesFile( const properties: InterfaceProperty[] = scalarFields.map((field) => ({ name: field.name, - type: `${fieldTypeToTs(field.type)} | null`, + type: `${fieldTypeToTs(field.type)} | null` })); statements.push(createInterfaceDeclaration(table.name, properties)); diff --git a/graphql/codegen/src/core/codegen/utils.ts b/graphql/codegen/src/core/codegen/utils.ts index dc46b8703..859a67900 100644 --- a/graphql/codegen/src/core/codegen/utils.ts +++ b/graphql/codegen/src/core/codegen/utils.ts @@ -1,13 +1,14 @@ /** * Codegen utilities - naming conventions, type mapping, and helpers */ +import { pluralize } from 'inflekt'; + import type { - CleanTable, CleanField, CleanFieldType, + CleanTable } from '../../types/schema'; -import { scalarToTsType, scalarToFilterType } from './scalars'; -import { pluralize } from 'inflekt'; +import { scalarToFilterType,scalarToTsType } from './scalars'; // ============================================================================ // String manipulation @@ -77,7 +78,7 @@ export function getTableNames(table: CleanTable): TableNames { typeName, singularName, pluralName, - pluralTypeName, + pluralTypeName }; } @@ -360,8 +361,8 @@ export function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[] { { name: idField.name, gqlType: idField.type.gqlType, - tsType: fieldTypeToTs(idField.type), - }, + tsType: fieldTypeToTs(idField.type) + } ]; } // Last resort: assume 'id' of type string (UUID) @@ -370,7 +371,7 @@ export function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[] { return pk.fields.map((f) => ({ name: f.name, gqlType: f.type.gqlType, - tsType: fieldTypeToTs(f.type), + tsType: fieldTypeToTs(f.type) })); } @@ -400,6 +401,36 @@ export function hasValidPrimaryKey(table: CleanTable): boolean { return false; } +/** + * Get the best field name for a defaultSelect literal. + * Prefers PK field if valid, then 'id'/'nodeId', then first scalar field. + * Unlike getPrimaryKeyInfo(), never returns a fictional 'id' fallback. + */ +export function getDefaultSelectFieldName(table: CleanTable): string { + // 1. Try the actual primary key + const pk = table.constraints?.primaryKey?.[0]; + if (pk && pk.fields.length >= 1) { + return pk.fields[0].name; + } + // 2. Try id / nodeId fields + const idField = table.fields.find((f) => f.name === 'id' || f.name === 'nodeId'); + if (idField) { + return idField.name; + } + // 3. First non-array scalar field + const scalarField = table.fields.find( + (f) => !f.type.isArray && scalarToTsType(f.type.gqlType) !== f.type.gqlType + ); + if (scalarField) { + return scalarField.name; + } + // 4. First field of any kind + if (table.fields.length > 0) { + return table.fields[0].name; + } + return 'id'; +} + // ============================================================================ // Query key generation // ============================================================================ diff --git a/graphql/codegen/src/core/config/index.ts b/graphql/codegen/src/core/config/index.ts index 239e5581b..99dea07ad 100644 --- a/graphql/codegen/src/core/config/index.ts +++ b/graphql/codegen/src/core/config/index.ts @@ -6,12 +6,11 @@ export { CONFIG_FILENAME, findConfigFile, loadConfigFile, - type LoadConfigFileResult, + type LoadConfigFileResult } from './loader'; - export { - loadAndResolveConfig, - loadWatchConfig, type ConfigOverrideOptions, + loadAndResolveConfig, type LoadConfigResult, + loadWatchConfig } from './resolver'; diff --git a/graphql/codegen/src/core/config/loader.ts b/graphql/codegen/src/core/config/loader.ts index 2a80524c5..668b3adc9 100644 --- a/graphql/codegen/src/core/config/loader.ts +++ b/graphql/codegen/src/core/config/loader.ts @@ -6,6 +6,7 @@ */ import * as fs from 'node:fs'; import * as path from 'node:path'; + import { createJiti } from 'jiti'; export const CONFIG_FILENAME = 'graphql-codegen.config.ts'; @@ -55,7 +56,7 @@ export async function loadConfigFile( if (!fs.existsSync(resolvedPath)) { return { success: false, - error: `Config file not found: ${resolvedPath}`, + error: `Config file not found: ${resolvedPath}` }; } @@ -64,7 +65,7 @@ export async function loadConfigFile( // jiti handles .ts, .js, .mjs, .cjs and ESM/CJS interop const jiti = createJiti(__filename, { interopDefault: true, - debug: process.env.JITI_DEBUG === '1', + debug: process.env.JITI_DEBUG === '1' }); // jiti.import() with { default: true } returns mod?.default ?? mod @@ -73,19 +74,19 @@ export async function loadConfigFile( if (!config || typeof config !== 'object') { return { success: false, - error: 'Config file must export a configuration object', + error: 'Config file must export a configuration object' }; } return { success: true, - config, + config }; } catch (err) { const message = err instanceof Error ? err.message : 'Unknown error'; return { success: false, - error: `Failed to load config file: ${message}`, + error: `Failed to load config file: ${message}` }; } } diff --git a/graphql/codegen/src/core/config/resolver.ts b/graphql/codegen/src/core/config/resolver.ts index 56c551e3b..aa71fe60b 100644 --- a/graphql/codegen/src/core/config/resolver.ts +++ b/graphql/codegen/src/core/config/resolver.ts @@ -6,9 +6,9 @@ */ import type { GraphQLSDKConfig, - GraphQLSDKConfigTarget, + GraphQLSDKConfigTarget } from '../../types/config'; -import { mergeConfig, getConfigOptions } from '../../types/config'; +import { getConfigOptions,mergeConfig } from '../../types/config'; import { findConfigFile, loadConfigFile } from './loader'; /** @@ -47,13 +47,13 @@ export async function loadAndResolveConfig( const sources = [ overrides.endpoint, overrides.schemaFile, - overrides.db, + overrides.db ].filter(Boolean); if (sources.length > 1) { return { success: false, error: - 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.', + 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.' }; } @@ -85,13 +85,13 @@ export async function loadAndResolveConfig( return { success: false, error: - 'No source specified. Use --endpoint, --schema-file, or --db, or create a config file with "graphql-codegen init".', + 'No source specified. Use --endpoint, --schema-file, or --db, or create a config file with "graphql-codegen init".' }; } return { success: true, - config: getConfigOptions(mergedConfig), + config: getConfigOptions(mergedConfig) }; } @@ -134,12 +134,12 @@ export async function loadWatchConfig(options: { const watchOverrides: GraphQLSDKConfigTarget = { watch: { ...(options.pollInterval !== undefined && { - pollInterval: options.pollInterval, + pollInterval: options.pollInterval }), ...(options.debounce !== undefined && { debounce: options.debounce }), ...(options.touch !== undefined && { touchFile: options.touch }), - ...(options.clear !== undefined && { clearScreen: options.clear }), - }, + ...(options.clear !== undefined && { clearScreen: options.clear }) + } }; let mergedConfig = mergeConfig(baseConfig, sourceOverrides); diff --git a/graphql/codegen/src/core/custom-ast.ts b/graphql/codegen/src/core/custom-ast.ts index 5d9a1428e..02ea4642b 100644 --- a/graphql/codegen/src/core/custom-ast.ts +++ b/graphql/codegen/src/core/custom-ast.ts @@ -22,7 +22,7 @@ export function getCustomAst(fieldDefn?: MetaField): FieldNode | null { } return t.field({ - name: fieldDefn.name, + name: fieldDefn.name }); } @@ -57,7 +57,7 @@ export function getCustomAstForCleanField(field: CleanField): FieldNode { // Return simple field for scalar types return t.field({ - name, + name }); } @@ -72,7 +72,7 @@ export function requiresSubfieldSelection(field: CleanField): boolean { 'GeometryPoint', 'Interval', 'GeometryGeometryCollection', - 'GeoJSON', + 'GeoJSON' ]; return complexTypes.includes(gqlType); @@ -85,8 +85,8 @@ export function geometryPointAst(name: string): FieldNode { return t.field({ name, selectionSet: t.selectionSet({ - selections: toFieldArray(['x', 'y']), - }), + selections: toFieldArray(['x', 'y']) + }) }); } @@ -101,8 +101,8 @@ export function geometryCollectionAst(name: string): FieldNode { kind: 'NamedType', name: { kind: 'Name', - value: 'GeometryPoint', - }, + value: 'GeometryPoint' + } }, selectionSet: { kind: 'SelectionSet', @@ -111,18 +111,18 @@ export function geometryCollectionAst(name: string): FieldNode { kind: 'Field', name: { kind: 'Name', - value: 'x', - }, + value: 'x' + } }, { kind: 'Field', name: { kind: 'Name', - value: 'y', - }, - }, - ], - }, + value: 'y' + } + } + ] + } }; return t.field({ @@ -133,11 +133,11 @@ export function geometryCollectionAst(name: string): FieldNode { name: 'geometries', selectionSet: t.selectionSet({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - selections: [inlineFragment as any], // gql-ast limitation with inline fragments - }), - }), - ], - }), + selections: [inlineFragment as any] // gql-ast limitation with inline fragments + }) + }) + ] + }) }); } @@ -148,8 +148,8 @@ export function geometryAst(name: string): FieldNode { return t.field({ name, selectionSet: t.selectionSet({ - selections: toFieldArray(['geojson']), - }), + selections: toFieldArray(['geojson']) + }) }); } @@ -166,9 +166,9 @@ export function intervalAst(name: string): FieldNode { 'minutes', 'months', 'seconds', - 'years', - ]), - }), + 'years' + ]) + }) }); } diff --git a/graphql/codegen/src/core/database/index.ts b/graphql/codegen/src/core/database/index.ts index 74c41cb03..0ca12ff14 100644 --- a/graphql/codegen/src/core/database/index.ts +++ b/graphql/codegen/src/core/database/index.ts @@ -6,6 +6,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; + import { buildSchemaSDL } from '@constructive-io/graphql-server'; export interface BuildSchemaFromDatabaseOptions { @@ -47,7 +48,7 @@ export async function buildSchemaFromDatabase( const sdl = await buildSchemaSDL({ database, schemas, - graphile: { pgSettings: async () => ({ role: 'administrator' }) }, + graphile: { pgSettings: async () => ({ role: 'administrator' }) } }); // Write schema to file @@ -74,6 +75,6 @@ export async function buildSchemaSDLFromDatabase(options: { return buildSchemaSDL({ database, schemas, - graphile: { pgSettings: async () => ({ role: 'administrator' }) }, + graphile: { pgSettings: async () => ({ role: 'administrator' }) } }); } diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index 51e679a2d..e73b34edc 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -6,15 +6,15 @@ */ import path from 'path'; -import { createSchemaSource, validateSourceOptions } from './introspect'; -import { runCodegenPipeline, validateTablesFound } from './pipeline'; +import type { GraphQLSDKConfigTarget } from '../types/config'; +import { getConfigOptions } from '../types/config'; import { generate as generateReactQueryFiles } from './codegen'; import { generateRootBarrel } from './codegen/barrel'; import { generateOrm as generateOrmFiles } from './codegen/orm'; import { generateSharedTypes } from './codegen/shared'; +import { createSchemaSource, validateSourceOptions } from './introspect'; import { writeGeneratedFiles } from './output'; -import type { GraphQLSDKConfigTarget } from '../types/config'; -import { getConfigOptions } from '../types/config'; +import { runCodegenPipeline, validateTablesFound } from './pipeline'; export interface GenerateOptions extends GraphQLSDKConfigTarget { authorization?: string; @@ -44,14 +44,16 @@ export async function generate(options: GenerateOptions = {}): Promise t.name), - filesWritten: allFilesWritten, + filesWritten: allFilesWritten }; } diff --git a/graphql/codegen/src/core/index.ts b/graphql/codegen/src/core/index.ts index 5dbf3eb23..86a01af13 100644 --- a/graphql/codegen/src/core/index.ts +++ b/graphql/codegen/src/core/index.ts @@ -6,8 +6,8 @@ */ // Main generate function (orchestrates the entire pipeline) -export { generate } from './generate'; export type { GenerateOptions, GenerateResult } from './generate'; +export { generate } from './generate'; // Types export * from './types'; @@ -17,10 +17,10 @@ export * from './ast'; export * from './custom-ast'; // Query builder -export { QueryBuilder, MetaObject } from './query-builder'; +export { MetaObject,QueryBuilder } from './query-builder'; // Meta object utilities -export { validateMetaObject, convertFromMetaSchema } from './meta-object'; +export { convertFromMetaSchema,validateMetaObject } from './meta-object'; // Configuration loading and resolution export * from './config'; diff --git a/graphql/codegen/src/core/introspect/fetch-schema.ts b/graphql/codegen/src/core/introspect/fetch-schema.ts index 81cd7b5bb..7cd561125 100644 --- a/graphql/codegen/src/core/introspect/fetch-schema.ts +++ b/graphql/codegen/src/core/introspect/fetch-schema.ts @@ -4,8 +4,9 @@ */ import http from 'node:http'; import https from 'node:https'; -import { SCHEMA_INTROSPECTION_QUERY } from './schema-query'; + import type { IntrospectionQueryResponse } from '../../types/introspection'; +import { SCHEMA_INTROSPECTION_QUERY } from './schema-query'; interface HttpResponse { statusCode: number; @@ -35,7 +36,7 @@ function makeRequest( resolve({ statusCode: res.statusCode || 0, statusMessage: res.statusMessage || '', - data, + data }); }); }); @@ -83,7 +84,7 @@ export async function fetchSchema( const requestHeaders: Record = { 'Content-Type': 'application/json', Accept: 'application/json', - ...headers, + ...headers }; if (authorization) { @@ -92,12 +93,12 @@ export async function fetchSchema( const body = JSON.stringify({ query: SCHEMA_INTROSPECTION_QUERY, - variables: {}, + variables: {} }); const requestOptions: http.RequestOptions = { method: 'POST', - headers: requestHeaders, + headers: requestHeaders }; try { @@ -107,7 +108,7 @@ export async function fetchSchema( return { success: false, error: `HTTP ${response.statusCode}: ${response.statusMessage}`, - statusCode: response.statusCode, + statusCode: response.statusCode }; } @@ -121,7 +122,7 @@ export async function fetchSchema( return { success: false, error: `GraphQL errors: ${errorMessages}`, - statusCode: response.statusCode, + statusCode: response.statusCode }; } @@ -130,21 +131,21 @@ export async function fetchSchema( success: false, error: 'No __schema field in response. Introspection may be disabled on this endpoint.', - statusCode: response.statusCode, + statusCode: response.statusCode }; } return { success: true, data: json.data, - statusCode: response.statusCode, + statusCode: response.statusCode }; } catch (err) { if (err instanceof Error) { if (err.message.includes('timeout')) { return { success: false, - error: `Request timeout after ${timeout}ms`, + error: `Request timeout after ${timeout}ms` }; } @@ -152,31 +153,31 @@ export async function fetchSchema( if (errorCode === 'ECONNREFUSED') { return { success: false, - error: `Connection refused - is the server running at ${endpoint}?`, + error: `Connection refused - is the server running at ${endpoint}?` }; } if (errorCode === 'ENOTFOUND') { return { success: false, - error: `DNS lookup failed for ${url.hostname} - check the endpoint URL`, + error: `DNS lookup failed for ${url.hostname} - check the endpoint URL` }; } if (errorCode === 'ECONNRESET') { return { success: false, - error: `Connection reset by server at ${endpoint}`, + error: `Connection reset by server at ${endpoint}` }; } return { success: false, - error: err.message, + error: err.message }; } return { success: false, - error: 'Unknown error occurred', + error: 'Unknown error occurred' }; } } diff --git a/graphql/codegen/src/core/introspect/index.ts b/graphql/codegen/src/core/introspect/index.ts index baaf7ebc2..5ddf28346 100644 --- a/graphql/codegen/src/core/introspect/index.ts +++ b/graphql/codegen/src/core/introspect/index.ts @@ -3,29 +3,29 @@ */ // Table inference from introspection -export { inferTablesFromIntrospection } from './infer-tables'; export type { InferTablesOptions } from './infer-tables'; +export { inferTablesFromIntrospection } from './infer-tables'; // Pluralization utilities (from inflekt) -export { singularize, pluralize } from 'inflekt'; +export { pluralize,singularize } from 'inflekt'; // Schema sources +export type { + CreateSchemaSourceOptions, + SchemaSource, + SchemaSourceResult +} from './source'; export { createSchemaSource, - validateSourceOptions, EndpointSchemaSource, FileSchemaSource, SchemaSourceError, -} from './source'; -export type { - SchemaSource, - SchemaSourceResult, - CreateSchemaSourceOptions, + validateSourceOptions } from './source'; // Schema fetching (still used by watch mode) -export { fetchSchema } from './fetch-schema'; export type { FetchSchemaOptions, FetchSchemaResult } from './fetch-schema'; +export { fetchSchema } from './fetch-schema'; // Transform utilities (only filterTables, getTableNames, findTable are still useful) -export { getTableNames, findTable, filterTables } from './transform'; +export { filterTables,findTable, getTableNames } from './transform'; diff --git a/graphql/codegen/src/core/introspect/infer-tables.ts b/graphql/codegen/src/core/introspect/infer-tables.ts index 9f5ab631a..ef53092be 100644 --- a/graphql/codegen/src/core/introspect/infer-tables.ts +++ b/graphql/codegen/src/core/introspect/infer-tables.ts @@ -12,27 +12,28 @@ * - Query operations: {pluralName} (list), {singularName} (single) * - Mutation operations: create{Name}, update{Name}, delete{Name} */ +import { lcFirst, pluralize, singularize, ucFirst } from 'inflekt'; + import type { + IntrospectionField, IntrospectionQueryResponse, IntrospectionType, - IntrospectionField, - IntrospectionTypeRef, + IntrospectionTypeRef } from '../../types/introspection'; -import { unwrapType, getBaseTypeName, isList } from '../../types/introspection'; +import { getBaseTypeName, isList,unwrapType } from '../../types/introspection'; import type { - CleanTable, + CleanBelongsToRelation, CleanField, CleanFieldType, - CleanRelations, - CleanBelongsToRelation, CleanHasManyRelation, CleanManyToManyRelation, - TableInflection, - TableQueryNames, - TableConstraints, + CleanRelations, + CleanTable, ConstraintInfo, + TableConstraints, + TableInflection, + TableQueryNames } from '../../types/schema'; -import { singularize, pluralize, lcFirst, ucFirst } from 'inflekt'; // ============================================================================ // Pattern Matching Constants @@ -63,7 +64,7 @@ const PATTERNS = { // Mutation name patterns (camelCase) createMutation: /^create([A-Z][a-zA-Z0-9]*)$/, updateMutation: /^update([A-Z][a-zA-Z0-9]*)$/, - deleteMutation: /^delete([A-Z][a-zA-Z0-9]*)$/, + deleteMutation: /^delete([A-Z][a-zA-Z0-9]*)$/ }; /** @@ -88,7 +89,7 @@ const BUILTIN_TYPES = new Set([ 'Time', 'JSON', 'BigInt', - 'BigFloat', + 'BigFloat' ]); /** @@ -244,7 +245,7 @@ function buildCleanTable( one: queryOps.one ?? lcFirst(entityName), create: mutationOps.create ?? `create${entityName}`, update: mutationOps.update, - delete: mutationOps.delete, + delete: mutationOps.delete }; return { @@ -254,9 +255,9 @@ function buildCleanTable( relations, inflection, query, - constraints, + constraints }, - hasRealOperation, + hasRealOperation }; } @@ -295,7 +296,7 @@ function extractEntityFields( // Include scalar, enum, and other non-relation fields fields.push({ name: field.name, - type: convertToCleanFieldType(field.type), + type: convertToCleanFieldType(field.type) }); } @@ -324,7 +325,7 @@ function convertToCleanFieldType( return { gqlType: baseType.name ?? 'Unknown', - isArray, + isArray // PostgreSQL-specific fields are not available from introspection // They were optional anyway and not used by generators }; @@ -371,7 +372,7 @@ function inferRelations( isUnique: false, // Can't determine from introspection alone referencesTable: baseTypeName, type: baseTypeName, - keys: [], // Would need FK info to populate + keys: [] // Would need FK info to populate }); } } @@ -425,8 +426,8 @@ function inferHasManyOrManyToMany( fieldName: field.name, rightTable: actualEntityName, junctionTable, - type: connectionTypeName, - }, + type: connectionTypeName + } }; } @@ -437,8 +438,8 @@ function inferHasManyOrManyToMany( isUnique: false, referencedByTable: relatedEntityName, type: connectionTypeName, - keys: [], - }, + keys: [] + } }; } @@ -596,9 +597,9 @@ function inferConstraints( fields: [ { name: idField.name, - type: convertToCleanFieldType(idField.type), - }, - ], + type: convertToCleanFieldType(idField.type) + } + ] }); } } @@ -617,9 +618,9 @@ function inferConstraints( fields: [ { name: idField.name, - type: convertToCleanFieldType(idField.type), - }, - ], + type: convertToCleanFieldType(idField.type) + } + ] }); } } @@ -628,7 +629,7 @@ function inferConstraints( return { primaryKey, foreignKey: [], // Would need FK info to populate - unique: [], // Would need constraint info to populate + unique: [] // Would need constraint info to populate }; } @@ -681,7 +682,7 @@ function buildInflection( tableType: entityName, typeName: entityName, updateByPrimaryKey: `update${entityName}`, - updatePayloadType: hasUpdatePayload ? `Update${entityName}Payload` : null, + updatePayloadType: hasUpdatePayload ? `Update${entityName}Payload` : null }; } @@ -710,7 +711,7 @@ function findOrderByType( const candidates = [ `${entityName}sOrderBy`, // Simple 's' plural: User -> UsersOrderBy `${entityName}esOrderBy`, // 'es' plural: Address -> AddressesOrderBy - `${entityName}OrderBy`, // No change (already plural or singular OK) + `${entityName}OrderBy` // No change (already plural or singular OK) ]; // Check each candidate diff --git a/graphql/codegen/src/core/introspect/source/api-schemas.ts b/graphql/codegen/src/core/introspect/source/api-schemas.ts index 4ff79229f..4b81f5280 100644 --- a/graphql/codegen/src/core/introspect/source/api-schemas.ts +++ b/graphql/codegen/src/core/introspect/source/api-schemas.ts @@ -39,7 +39,7 @@ export async function validateServicesSchemas( if (apisCheck.rows.length === 0) { return { valid: false, - error: 'services_public.apis table not found. The database must have the services schema deployed.', + error: 'services_public.apis table not found. The database must have the services schema deployed.' }; } @@ -52,7 +52,7 @@ export async function validateServicesSchemas( if (apiSchemasCheck.rows.length === 0) { return { valid: false, - error: 'services_public.api_schemas table not found. The database must have the services schema deployed.', + error: 'services_public.api_schemas table not found. The database must have the services schema deployed.' }; } @@ -65,7 +65,7 @@ export async function validateServicesSchemas( if (metaschemaCheck.rows.length === 0) { return { valid: false, - error: 'metaschema_public.schema table not found. The database must have the metaschema deployed.', + error: 'metaschema_public.schema table not found. The database must have the metaschema deployed.' }; } @@ -73,7 +73,7 @@ export async function validateServicesSchemas( } catch (err) { return { valid: false, - error: `Failed to validate services schemas: ${err instanceof Error ? err.message : 'Unknown error'}`, + error: `Failed to validate services schemas: ${err instanceof Error ? err.message : 'Unknown error'}` }; } } @@ -142,7 +142,7 @@ export function createDatabasePool(database: string): Pool { port: parseInt(url.port || '5432', 10), user: url.username, password: url.password, - database: dbName, + database: dbName }); } diff --git a/graphql/codegen/src/core/introspect/source/database.ts b/graphql/codegen/src/core/introspect/source/database.ts index 4b77ea12e..55dfb0355 100644 --- a/graphql/codegen/src/core/introspect/source/database.ts +++ b/graphql/codegen/src/core/introspect/source/database.ts @@ -5,11 +5,12 @@ * introspection and converts it to introspection format. */ import { buildSchema, introspectionFromSchema } from 'graphql'; -import type { SchemaSource, SchemaSourceResult } from './types'; -import { SchemaSourceError } from './types'; + import type { IntrospectionQueryResponse } from '../../../types/introspection'; import { buildSchemaSDLFromDatabase } from '../../database'; import { createDatabasePool, resolveApiSchemas, validateServicesSchemas } from './api-schemas'; +import type { SchemaSource, SchemaSourceResult } from './types'; +import { SchemaSourceError } from './types'; export interface DatabaseSchemaSourceOptions { /** @@ -77,7 +78,7 @@ export class DatabaseSchemaSource implements SchemaSource { try { sdl = await buildSchemaSDLFromDatabase({ database, - schemas, + schemas }); } catch (err) { throw new SchemaSourceError( diff --git a/graphql/codegen/src/core/introspect/source/endpoint.ts b/graphql/codegen/src/core/introspect/source/endpoint.ts index a5c50ff84..20ec8d931 100644 --- a/graphql/codegen/src/core/introspect/source/endpoint.ts +++ b/graphql/codegen/src/core/introspect/source/endpoint.ts @@ -4,9 +4,9 @@ * Fetches GraphQL schema via introspection from a live endpoint. * Wraps the existing fetchSchema() function with the SchemaSource interface. */ +import { fetchSchema } from '../fetch-schema'; import type { SchemaSource, SchemaSourceResult } from './types'; import { SchemaSourceError } from './types'; -import { fetchSchema } from '../fetch-schema'; export interface EndpointSchemaSourceOptions { /** @@ -45,7 +45,7 @@ export class EndpointSchemaSource implements SchemaSource { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: this.options.timeout, + timeout: this.options.timeout }); if (!result.success) { @@ -63,7 +63,7 @@ export class EndpointSchemaSource implements SchemaSource { } return { - introspection: result.data, + introspection: result.data }; } diff --git a/graphql/codegen/src/core/introspect/source/file.ts b/graphql/codegen/src/core/introspect/source/file.ts index 6ab828aac..58ffe6b33 100644 --- a/graphql/codegen/src/core/introspect/source/file.ts +++ b/graphql/codegen/src/core/introspect/source/file.ts @@ -6,10 +6,12 @@ */ import * as fs from 'node:fs'; import * as path from 'node:path'; + import { buildSchema, introspectionFromSchema } from 'graphql'; + +import type { IntrospectionQueryResponse } from '../../../types/introspection'; import type { SchemaSource, SchemaSourceResult } from './types'; import { SchemaSourceError } from './types'; -import type { IntrospectionQueryResponse } from '../../../types/introspection'; export interface FileSchemaSourceOptions { /** diff --git a/graphql/codegen/src/core/introspect/source/index.ts b/graphql/codegen/src/core/introspect/source/index.ts index 9642201c0..c96b498be 100644 --- a/graphql/codegen/src/core/introspect/source/index.ts +++ b/graphql/codegen/src/core/introspect/source/index.ts @@ -7,23 +7,21 @@ * - PostgreSQL databases (via PostGraphile introspection) * - PGPM modules (via ephemeral database deployment) */ -export * from './types'; +export * from './api-schemas'; +export * from './database'; export * from './endpoint'; export * from './file'; -export * from './database'; export * from './pgpm-module'; -export * from './api-schemas'; +export * from './types'; -import type { SchemaSource } from './types'; -import type { DbConfig, PgpmConfig } from '../../../types/config'; +import type { DbConfig } from '../../../types/config'; +import { DatabaseSchemaSource } from './database'; import { EndpointSchemaSource } from './endpoint'; import { FileSchemaSource } from './file'; -import { DatabaseSchemaSource } from './database'; import { - PgpmModuleSchemaSource, - isPgpmModulePathOptions, - isPgpmWorkspaceOptions, + PgpmModuleSchemaSource } from './pgpm-module'; +import type { SchemaSource } from './types'; /** * Options for endpoint-based schema source @@ -142,49 +140,49 @@ export function createSchemaSource( const mode = detectSourceMode(options); switch (mode) { - case 'schemaFile': - return new FileSchemaSource({ - schemaPath: options.schemaFile!, - }); + case 'schemaFile': + return new FileSchemaSource({ + schemaPath: options.schemaFile! + }); - case 'endpoint': - return new EndpointSchemaSource({ - endpoint: options.endpoint!, - authorization: options.authorization, - headers: options.headers, - timeout: options.timeout, - }); + case 'endpoint': + return new EndpointSchemaSource({ + endpoint: options.endpoint!, + authorization: options.authorization, + headers: options.headers, + timeout: options.timeout + }); - case 'database': - // Database mode uses db.config for connection (falls back to env vars) - // and db.schemas or db.apiNames for schema selection - return new DatabaseSchemaSource({ - database: options.db?.config?.database ?? '', - schemas: options.db?.schemas, - apiNames: options.db?.apiNames, - }); + case 'database': + // Database mode uses db.config for connection (falls back to env vars) + // and db.schemas or db.apiNames for schema selection + return new DatabaseSchemaSource({ + database: options.db?.config?.database ?? '', + schemas: options.db?.schemas, + apiNames: options.db?.apiNames + }); - case 'pgpm-module': - return new PgpmModuleSchemaSource({ - pgpmModulePath: options.db!.pgpm!.modulePath!, - schemas: options.db?.schemas, - apiNames: options.db?.apiNames, - keepDb: options.db?.keepDb, - }); + case 'pgpm-module': + return new PgpmModuleSchemaSource({ + pgpmModulePath: options.db!.pgpm!.modulePath!, + schemas: options.db?.schemas, + apiNames: options.db?.apiNames, + keepDb: options.db?.keepDb + }); - case 'pgpm-workspace': - return new PgpmModuleSchemaSource({ - pgpmWorkspacePath: options.db!.pgpm!.workspacePath!, - pgpmModuleName: options.db!.pgpm!.moduleName!, - schemas: options.db?.schemas, - apiNames: options.db?.apiNames, - keepDb: options.db?.keepDb, - }); + case 'pgpm-workspace': + return new PgpmModuleSchemaSource({ + pgpmWorkspacePath: options.db!.pgpm!.workspacePath!, + pgpmModuleName: options.db!.pgpm!.moduleName!, + schemas: options.db?.schemas, + apiNames: options.db?.apiNames, + keepDb: options.db?.keepDb + }); - default: - throw new Error( - 'No source specified. Use one of: endpoint, schemaFile, or db (with optional pgpm for module deployment).' - ); + default: + throw new Error( + 'No source specified. Use one of: endpoint, schemaFile, or db (with optional pgpm for module deployment).' + ); } } @@ -199,14 +197,14 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { const sources = [ options.endpoint, options.schemaFile, - options.db, + options.db ].filter(Boolean); if (sources.length === 0) { return { valid: false, error: - 'No source specified. Use one of: endpoint, schemaFile, or db.', + 'No source specified. Use one of: endpoint, schemaFile, or db.' }; } @@ -214,7 +212,7 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { return { valid: false, error: - 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.', + 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.' }; } @@ -224,14 +222,14 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (pgpm.workspacePath && !pgpm.moduleName) { return { valid: false, - error: 'db.pgpm.workspacePath requires db.pgpm.moduleName to be specified.', + error: 'db.pgpm.workspacePath requires db.pgpm.moduleName to be specified.' }; } if (pgpm.moduleName && !pgpm.workspacePath) { return { valid: false, - error: 'db.pgpm.moduleName requires db.pgpm.workspacePath to be specified.', + error: 'db.pgpm.moduleName requires db.pgpm.workspacePath to be specified.' }; } @@ -239,7 +237,7 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (!pgpm.modulePath && !(pgpm.workspacePath && pgpm.moduleName)) { return { valid: false, - error: 'db.pgpm requires either modulePath or both workspacePath and moduleName.', + error: 'db.pgpm requires either modulePath or both workspacePath and moduleName.' }; } } @@ -252,14 +250,14 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (hasSchemas && hasApiNames) { return { valid: false, - error: 'Cannot specify both db.schemas and db.apiNames. Use one or the other.', + error: 'Cannot specify both db.schemas and db.apiNames. Use one or the other.' }; } if (!hasSchemas && !hasApiNames) { return { valid: false, - error: 'Must specify either db.schemas or db.apiNames for database mode.', + error: 'Must specify either db.schemas or db.apiNames for database mode.' }; } } diff --git a/graphql/codegen/src/core/introspect/source/pgpm-module.ts b/graphql/codegen/src/core/introspect/source/pgpm-module.ts index a246d769f..4bbc7e14e 100644 --- a/graphql/codegen/src/core/introspect/source/pgpm-module.ts +++ b/graphql/codegen/src/core/introspect/source/pgpm-module.ts @@ -7,17 +7,17 @@ * 3. Introspecting the database with PostGraphile * 4. Cleaning up the ephemeral database (unless keepDb is true) */ -import { buildSchema, introspectionFromSchema } from 'graphql'; import { PgpmPackage } from '@pgpmjs/core'; +import { buildSchema, introspectionFromSchema } from 'graphql'; +import { getPgPool } from 'pg-cache'; import { createEphemeralDb, type EphemeralDbResult } from 'pgsql-client'; import { deployPgpm } from 'pgsql-seed'; -import { getPgPool } from 'pg-cache'; -import type { SchemaSource, SchemaSourceResult } from './types'; -import { SchemaSourceError } from './types'; import type { IntrospectionQueryResponse } from '../../../types/introspection'; import { buildSchemaSDLFromDatabase } from '../../database'; import { resolveApiSchemas, validateServicesSchemas } from './api-schemas'; +import type { SchemaSource, SchemaSourceResult } from './types'; +import { SchemaSourceError } from './types'; /** * Options for PGPM module schema source using direct module path @@ -147,7 +147,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { try { this.ephemeralDb = createEphemeralDb({ prefix: 'codegen_pgpm_', - verbose: false, + verbose: false }); } catch (err) { throw new SchemaSourceError( @@ -199,7 +199,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { try { sdl = await buildSchemaSDLFromDatabase({ database: dbConfig.database, - schemas, + schemas }); } catch (err) { throw new SchemaSourceError( diff --git a/graphql/codegen/src/core/introspect/transform-schema.ts b/graphql/codegen/src/core/introspect/transform-schema.ts index 40cc4aa9f..a7edd5216 100644 --- a/graphql/codegen/src/core/introspect/transform-schema.ts +++ b/graphql/codegen/src/core/introspect/transform-schema.ts @@ -5,24 +5,24 @@ * format used by code generators. */ import type { - IntrospectionQueryResponse, - IntrospectionType, IntrospectionField, - IntrospectionTypeRef, IntrospectionInputValue, + IntrospectionQueryResponse, + IntrospectionType, + IntrospectionTypeRef } from '../../types/introspection'; import { - unwrapType, getBaseTypeName, isNonNull, + unwrapType } from '../../types/introspection'; import type { - CleanOperation, CleanArgument, - CleanTypeRef, CleanObjectField, - TypeRegistry, + CleanOperation, + CleanTypeRef, ResolvedType, + TypeRegistry } from '../../types/schema'; // ============================================================================ @@ -48,7 +48,7 @@ export function buildTypeRegistry(types: IntrospectionType[]): TypeRegistry { const resolvedType: ResolvedType = { kind: type.kind as ResolvedType['kind'], name: type.name, - description: type.description ?? undefined, + description: type.description ?? undefined }; // Resolve enum values for ENUM types (no circular refs possible) @@ -99,7 +99,7 @@ function transformFieldToCleanObjectFieldShallow( return { name: field.name, type: transformTypeRefShallow(field.type), - description: field.description ?? undefined, + description: field.description ?? undefined }; } @@ -113,7 +113,7 @@ function transformInputValueToCleanArgumentShallow( name: inputValue.name, type: transformTypeRefShallow(inputValue.type), defaultValue: inputValue.defaultValue ?? undefined, - description: inputValue.description ?? undefined, + description: inputValue.description ?? undefined }; } @@ -124,7 +124,7 @@ function transformInputValueToCleanArgumentShallow( function transformTypeRefShallow(typeRef: IntrospectionTypeRef): CleanTypeRef { const cleanRef: CleanTypeRef = { kind: typeRef.kind as CleanTypeRef['kind'], - name: typeRef.name, + name: typeRef.name }; if (typeRef.ofType) { @@ -165,15 +165,15 @@ export function transformSchemaToOperations( // Transform queries const queries: CleanOperation[] = queryTypeDef?.fields ? queryTypeDef.fields.map((field) => - transformFieldToCleanOperation(field, 'query', types) - ) + transformFieldToCleanOperation(field, 'query', types) + ) : []; // Transform mutations const mutations: CleanOperation[] = mutationTypeDef?.fields ? mutationTypeDef.fields.map((field) => - transformFieldToCleanOperation(field, 'mutation', types) - ) + transformFieldToCleanOperation(field, 'mutation', types) + ) : []; return { queries, mutations, typeRegistry }; @@ -200,7 +200,7 @@ function transformFieldToCleanOperation( returnType: transformTypeRefToCleanTypeRef(field.type, types), description: field.description ?? undefined, isDeprecated: field.isDeprecated, - deprecationReason: field.deprecationReason ?? undefined, + deprecationReason: field.deprecationReason ?? undefined }; } @@ -215,7 +215,7 @@ function transformInputValueToCleanArgument( name: inputValue.name, type: transformTypeRefToCleanTypeRef(inputValue.type, types), defaultValue: inputValue.defaultValue ?? undefined, - description: inputValue.description ?? undefined, + description: inputValue.description ?? undefined }; } @@ -237,7 +237,7 @@ function transformTypeRefToCleanTypeRef( ): CleanTypeRef { const cleanRef: CleanTypeRef = { kind: typeRef.kind as CleanTypeRef['kind'], - name: typeRef.name, + name: typeRef.name }; // Recursively transform ofType for wrappers (LIST, NON_NULL) @@ -400,4 +400,4 @@ export function getCustomOperations( } // Re-export utility functions from introspection types -export { unwrapType, getBaseTypeName, isNonNull }; +export { getBaseTypeName, isNonNull,unwrapType }; diff --git a/graphql/codegen/src/core/meta-object/convert.ts b/graphql/codegen/src/core/meta-object/convert.ts index 02bff2fe4..295ae7841 100644 --- a/graphql/codegen/src/core/meta-object/convert.ts +++ b/graphql/codegen/src/core/meta-object/convert.ts @@ -78,11 +78,11 @@ export function convertFromMetaSchema( metaSchema: MetaSchemaInput ): ConvertedMetaObject { const { - _meta: { tables }, + _meta: { tables } } = metaSchema; const result: ConvertedMetaObject = { - tables: [], + tables: [] }; for (const table of tables) { @@ -94,7 +94,7 @@ export function convertFromMetaSchema( foreignConstraints: pickForeignConstraint( table.foreignKeyConstraints, table.relations - ), + ) }); } @@ -136,7 +136,7 @@ function pickForeignConstraint( return { refTable: refTable.name, fromKey, - toKey, + toKey }; }); } @@ -144,13 +144,13 @@ function pickForeignConstraint( function pickField(field: MetaSchemaField): ConvertedField { return { name: field.name, - type: field.type, + type: field.type }; } function pickConstraintField(field: MetaSchemaField): ConvertedConstraint { return { name: field.name, - type: field.type, + type: field.type }; } diff --git a/graphql/codegen/src/core/meta-object/validate.ts b/graphql/codegen/src/core/meta-object/validate.ts index 1481e6b5d..eab7a346b 100644 --- a/graphql/codegen/src/core/meta-object/validate.ts +++ b/graphql/codegen/src/core/meta-object/validate.ts @@ -18,7 +18,7 @@ function getValidator() { return { ajv: cachedAjv, - validator: cachedValidator!, + validator: cachedValidator! }; } @@ -36,6 +36,6 @@ export function validateMetaObject( return { errors: validator.errors, - message: ajv.errorsText(validator.errors, { separator: '\n' }), + message: ajv.errorsText(validator.errors, { separator: '\n' }) }; } diff --git a/graphql/codegen/src/core/output/index.ts b/graphql/codegen/src/core/output/index.ts index 3d6344d9c..529f10e41 100644 --- a/graphql/codegen/src/core/output/index.ts +++ b/graphql/codegen/src/core/output/index.ts @@ -3,9 +3,9 @@ */ export { - writeGeneratedFiles, formatOutput, type GeneratedFile, - type WriteResult, + writeGeneratedFiles, type WriteOptions, + type WriteResult } from './writer'; diff --git a/graphql/codegen/src/core/output/writer.ts b/graphql/codegen/src/core/output/writer.ts index 6db930669..8b04f1613 100644 --- a/graphql/codegen/src/core/output/writer.ts +++ b/graphql/codegen/src/core/output/writer.ts @@ -62,7 +62,7 @@ async function formatFileContent( singleQuote: true, trailingComma: 'es5', tabWidth: 2, - semi: true, + semi: true }); return result.code; } catch { @@ -98,7 +98,7 @@ export async function writeGeneratedFiles( const message = err instanceof Error ? err.message : 'Unknown error'; return { success: false, - errors: [`Failed to create output directory: ${message}`], + errors: [`Failed to create output directory: ${message}`] }; } @@ -171,7 +171,7 @@ export async function writeGeneratedFiles( return { success: errors.length === 0, filesWritten: written, - errors: errors.length > 0 ? errors : undefined, + errors: errors.length > 0 ? errors : undefined }; } @@ -211,7 +211,7 @@ export async function formatOutput( if (!formatFn) { return { success: false, - error: 'oxfmt not available. Install it with: npm install oxfmt', + error: 'oxfmt not available. Install it with: npm install oxfmt' }; } diff --git a/graphql/codegen/src/core/pipeline/index.ts b/graphql/codegen/src/core/pipeline/index.ts index 65aba4e21..f2d33456d 100644 --- a/graphql/codegen/src/core/pipeline/index.ts +++ b/graphql/codegen/src/core/pipeline/index.ts @@ -10,18 +10,18 @@ */ import type { GraphQLSDKConfigTarget } from '../../types/config'; import type { - CleanTable, CleanOperation, - TypeRegistry, + CleanTable, + TypeRegistry } from '../../types/schema'; -import type { SchemaSource } from '../introspect/source'; import { inferTablesFromIntrospection } from '../introspect/infer-tables'; +import type { SchemaSource } from '../introspect/source'; import { filterTables } from '../introspect/transform'; import { - transformSchemaToOperations, filterOperations, - getTableOperationNames, getCustomOperations, + getTableOperationNames, + transformSchemaToOperations } from '../introspect/transform-schema'; // Re-export for convenience @@ -103,7 +103,7 @@ export async function runCodegenPipeline( source, config, verbose = false, - skipCustomOperations = false, + skipCustomOperations = false } = options; const log = verbose ? console.log : () => {}; @@ -120,7 +120,7 @@ export async function runCodegenPipeline( // 3. Filter tables by config (combine exclude and systemExclude) tables = filterTables(tables, config.tables.include, [ ...config.tables.exclude, - ...config.tables.systemExclude, + ...config.tables.systemExclude ]); const filteredTables = tables.length; log(` After filtering: ${filteredTables} tables`); @@ -130,7 +130,7 @@ export async function runCodegenPipeline( const { queries: allQueries, mutations: allMutations, - typeRegistry, + typeRegistry } = transformSchemaToOperations(introspection); const totalQueries = allQueries.length; @@ -178,7 +178,7 @@ export async function runCodegenPipeline( customOperations: { queries: customQueries, mutations: customMutations, - typeRegistry, + typeRegistry }, stats: { totalTables, @@ -186,8 +186,8 @@ export async function runCodegenPipeline( totalQueries, totalMutations, customQueries: customQueries.length, - customMutations: customMutations.length, - }, + customMutations: customMutations.length + } }; } @@ -206,7 +206,7 @@ export function validateTablesFound(tables: CleanTable[]): { return { valid: false, error: - 'No tables found after filtering. Check your include/exclude patterns.', + 'No tables found after filtering. Check your include/exclude patterns.' }; } return { valid: true }; diff --git a/graphql/codegen/src/core/query-builder.ts b/graphql/codegen/src/core/query-builder.ts index 8be4db4ca..403ed2e3b 100644 --- a/graphql/codegen/src/core/query-builder.ts +++ b/graphql/codegen/src/core/query-builder.ts @@ -1,5 +1,5 @@ import { DocumentNode, print as gqlPrint } from 'graphql'; -import { camelize, underscore, pluralize } from 'inflekt'; +import { camelize, pluralize,underscore } from 'inflekt'; import { createOne, @@ -8,11 +8,10 @@ import { getCount, getMany, getOne, - patchOne, + patchOne } from './ast'; import { validateMetaObject } from './meta-object'; import type { - QueryFieldSelection, IntrospectionSchema, MetaObject, MetaTable, @@ -20,7 +19,8 @@ import type { QueryBuilderOptions, QueryBuilderResult, QueryDefinition, - QuerySelectionOptions, + QueryFieldSelection, + QuerySelectionOptions } from './types'; export * as MetaObject from './meta-object'; @@ -46,7 +46,7 @@ export class QueryBuilder { constructor({ meta = {} as MetaObject, - introspection, + introspection }: QueryBuilderOptions) { this._introspection = introspection; this._meta = meta; @@ -144,17 +144,17 @@ export class QueryBuilder { // We only need deleteAction from all of [deleteAction, deleteActionBySlug, deleteActionByName] const getInputName = (mutationType: string): string => { switch (mutationType) { - case 'delete': { - return `Delete${camelize(this._model)}Input`; - } - case 'create': { - return `Create${camelize(this._model)}Input`; - } - case 'patch': { - return `Update${camelize(this._model)}Input`; - } - default: - throw new Error('Unhandled mutation type' + mutationType); + case 'delete': { + return `Delete${camelize(this._model)}Input`; + } + case 'create': { + return `Create${camelize(this._model)}Input`; + } + case 'patch': { + return `Update${camelize(this._model)}Input`; + } + default: + throw new Error('Unhandled mutation type' + mutationType); } }; @@ -208,7 +208,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select, + selection: this._select }); return this; @@ -232,7 +232,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select, + selection: this._select }); return this; @@ -254,7 +254,7 @@ export class QueryBuilder { this._ast = getCount({ queryName: this._queryName, operationName: this._key, - query: defn, + query: defn }); return this; @@ -278,7 +278,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select, + selection: this._select }); return this; @@ -302,7 +302,7 @@ export class QueryBuilder { operationName: this._key, mutationName: this._queryName, mutation: defn, - selection: this._select, + selection: this._select }); return this; @@ -326,7 +326,7 @@ export class QueryBuilder { this._ast = deleteOne({ operationName: this._key, mutationName: this._queryName, - mutation: defn, + mutation: defn }); return this; @@ -351,7 +351,7 @@ export class QueryBuilder { operationName: this._key, mutationName: this._queryName, mutation: defn, - selection: this._select, + selection: this._select }); return this; @@ -370,7 +370,7 @@ export class QueryBuilder { return { _hash, _queryName: this._queryName, - _ast: this._ast, + _ast: this._ast }; } @@ -421,7 +421,7 @@ function pickScalarFields( .map((fieldName) => ({ name: fieldName, isObject: false, - fieldDefn: modelMeta.fields.find((f) => f.name === fieldName), + fieldDefn: modelMeta.fields.find((f) => f.name === fieldName) })); // This is for inferring the sub-selection of a mutation query @@ -498,7 +498,7 @@ function pickAllFields( isObject: true, isBelongTo, selection: subFields.map((name) => ({ name, isObject: false })), - variables: selectOptions.variables as QueryFieldSelection['variables'], + variables: selectOptions.variables as QueryFieldSelection['variables'] }; // Need to further expand selection of object fields, @@ -529,8 +529,8 @@ function pickAllFields( { name: fieldName, isObject: false, - fieldDefn: modelMeta.fields.find((f) => f.name === fieldName), - }, + fieldDefn: modelMeta.fields.find((f) => f.name === fieldName) + } ]; } } diff --git a/graphql/codegen/src/core/watch/cache.ts b/graphql/codegen/src/core/watch/cache.ts index f92764d02..2df07bc6d 100644 --- a/graphql/codegen/src/core/watch/cache.ts +++ b/graphql/codegen/src/core/watch/cache.ts @@ -7,6 +7,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; + import type { IntrospectionQueryResponse } from '../../types/introspection'; import { hashObject } from './hash'; diff --git a/graphql/codegen/src/core/watch/index.ts b/graphql/codegen/src/core/watch/index.ts index 6e220682f..737f372d6 100644 --- a/graphql/codegen/src/core/watch/index.ts +++ b/graphql/codegen/src/core/watch/index.ts @@ -2,17 +2,17 @@ * Watch mode module exports */ -export { SchemaPoller, computeSchemaHash } from './poller'; export { SchemaCache, touchFile } from './cache'; -export { sha256, hashObject, combineHashes } from './hash'; export { debounce, debounceAsync } from './debounce'; -export { WatchOrchestrator, startWatch } from './orchestrator'; +export { combineHashes,hashObject, sha256 } from './hash'; +export type { WatchOrchestratorOptions, WatchStatus } from './orchestrator'; +export { startWatch,WatchOrchestrator } from './orchestrator'; +export { computeSchemaHash,SchemaPoller } from './poller'; export type { - PollResult, - WatchOptions, - PollEventType, - PollEventHandler, - PollEvent, GeneratorType, + PollEvent, + PollEventHandler, + PollEventType, + PollResult, + WatchOptions } from './types'; -export type { WatchOrchestratorOptions, WatchStatus } from './orchestrator'; diff --git a/graphql/codegen/src/core/watch/orchestrator.ts b/graphql/codegen/src/core/watch/orchestrator.ts index c530c113e..dc986a441 100644 --- a/graphql/codegen/src/core/watch/orchestrator.ts +++ b/graphql/codegen/src/core/watch/orchestrator.ts @@ -5,9 +5,9 @@ */ import type { GraphQLSDKConfigTarget } from '../../types/config'; -import type { GeneratorType, WatchOptions, PollEvent } from './types'; -import { SchemaPoller } from './poller'; import { debounce } from './debounce'; +import { SchemaPoller } from './poller'; +import type { GeneratorType, PollEvent,WatchOptions } from './types'; // These will be injected by the CLI layer to avoid circular dependencies // The watch orchestrator doesn't need to know about the full generate commands @@ -82,7 +82,7 @@ export class WatchOrchestrator { lastPollTime: null, lastRegenTime: null, lastError: null, - currentHash: null, + currentHash: null }; // Create debounced regenerate function @@ -105,7 +105,7 @@ export class WatchOrchestrator { debounce: config.watch.debounce, touchFile: config.watch.touchFile, clearScreen: config.watch.clearScreen, - verbose, + verbose }; } @@ -217,18 +217,18 @@ export class WatchOrchestrator { let outputDir: string | undefined; switch (this.options.generatorType) { - case 'react-query': - generateFn = this.options.generateReactQuery; - // React Query hooks go to {output}/hooks - outputDir = this.options.outputDir ?? `${this.options.config.output}/hooks`; - break; - case 'orm': - generateFn = this.options.generateOrm; - // ORM client goes to {output}/orm - outputDir = this.options.outputDir ?? `${this.options.config.output}/orm`; - break; - default: - throw new Error(`Unknown generator type: ${this.options.generatorType}`); + case 'react-query': + generateFn = this.options.generateReactQuery; + // React Query hooks go to {output}/hooks + outputDir = this.options.outputDir ?? `${this.options.config.output}/hooks`; + break; + case 'orm': + generateFn = this.options.generateOrm; + // ORM client goes to {output}/orm + outputDir = this.options.outputDir ?? `${this.options.config.output}/orm`; + break; + default: + throw new Error(`Unknown generator type: ${this.options.generatorType}`); } const result = await generateFn({ @@ -238,7 +238,7 @@ export class WatchOrchestrator { output: outputDir, authorization: this.options.authorization, verbose: this.watchOptions.verbose, - skipCustomOperations: this.options.skipCustomOperations, + skipCustomOperations: this.options.skipCustomOperations }); const duration = Date.now() - startTime; @@ -287,14 +287,14 @@ export class WatchOrchestrator { private logHeader(): void { let generatorName: string; switch (this.options.generatorType) { - case 'react-query': - generatorName = 'React Query hooks'; - break; - case 'orm': - generatorName = 'ORM client'; - break; - default: - throw new Error(`Unknown generator type: ${this.options.generatorType}`); + case 'react-query': + generatorName = 'React Query hooks'; + break; + case 'orm': + generatorName = 'ORM client'; + break; + default: + throw new Error(`Unknown generator type: ${this.options.generatorType}`); } console.log(`\n${'─'.repeat(50)}`); console.log(`graphql-codegen watch mode (${generatorName})`); diff --git a/graphql/codegen/src/core/watch/poller.ts b/graphql/codegen/src/core/watch/poller.ts index c60583f85..78e2937ac 100644 --- a/graphql/codegen/src/core/watch/poller.ts +++ b/graphql/codegen/src/core/watch/poller.ts @@ -6,16 +6,17 @@ */ import { EventEmitter } from 'node:events'; + import type { IntrospectionQueryResponse } from '../../types/introspection'; import { fetchSchema } from '../introspect/fetch-schema'; import { SchemaCache, touchFile } from './cache'; +import { hashObject } from './hash'; import type { - PollResult, PollEvent, PollEventType, - WatchOptions, + PollResult, + WatchOptions } from './types'; -import { hashObject } from './hash'; /** * Schema poller that periodically introspects a GraphQL endpoint @@ -73,7 +74,7 @@ export class SchemaPoller extends EventEmitter { return { success: false, changed: false, - error: 'Poll already in progress', + error: 'Poll already in progress' }; } @@ -87,7 +88,7 @@ export class SchemaPoller extends EventEmitter { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: 30000, + timeout: 30000 }); const duration = Date.now() - startTime; @@ -156,7 +157,7 @@ export class SchemaPoller extends EventEmitter { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: 30000, + timeout: 30000 }); if (schemaResult.success) { @@ -218,7 +219,7 @@ export class SchemaPoller extends EventEmitter { return { type, timestamp: Date.now(), - ...extra, + ...extra }; } } diff --git a/graphql/codegen/src/generators/field-selector.ts b/graphql/codegen/src/generators/field-selector.ts index 8e42e5c9d..43ce4851c 100644 --- a/graphql/codegen/src/generators/field-selector.ts +++ b/graphql/codegen/src/generators/field-selector.ts @@ -7,7 +7,7 @@ import type { CleanTable } from '../types/schema'; import type { FieldSelection, FieldSelectionPreset, - SimpleFieldSelection, + SimpleFieldSelection } from '../types/selection'; /** @@ -39,47 +39,47 @@ function convertPresetToSelection( const options: QuerySelectionOptions = {}; switch (preset) { - case 'minimal': { - // Just id and first display field - const minimalFields = getMinimalFields(table); - minimalFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'minimal': { + // Just id and first display field + const minimalFields = getMinimalFields(table); + minimalFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'display': { - // Common display fields - const displayFields = getDisplayFields(table); - displayFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'display': { + // Common display fields + const displayFields = getDisplayFields(table); + displayFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'all': { - // All non-relational fields (includes complex fields like JSON, geometry, etc.) - const allFields = getNonRelationalFields(table); - allFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'all': { + // All non-relational fields (includes complex fields like JSON, geometry, etc.) + const allFields = getNonRelationalFields(table); + allFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'full': - // All fields including basic relations - table.fields.forEach((field) => { - options[field.name] = true; - }); - break; + case 'full': + // All fields including basic relations + table.fields.forEach((field) => { + options[field.name] = true; + }); + break; - default: { - // Default to display - const defaultFields = getDisplayFields(table); - defaultFields.forEach((field) => { - options[field] = true; - }); - } + default: { + // Default to display + const defaultFields = getDisplayFields(table); + defaultFields.forEach((field) => { + options[field] = true; + }); + } } return options; @@ -118,7 +118,7 @@ function convertCustomSelectionToOptions( // Include with dynamically determined scalar fields from the related table options[relationField] = { select: getRelatedTableScalarFields(relationField, table, allTables), - variables: {}, + variables: {} }; } }); @@ -137,7 +137,7 @@ function convertCustomSelectionToOptions( table, allTables ), - variables: {}, + variables: {} }; } else if (Array.isArray(relationSelection)) { // Include with specific fields @@ -147,7 +147,7 @@ function convertCustomSelectionToOptions( }); options[relationField] = { select: selectObj, - variables: {}, + variables: {} }; } } @@ -305,7 +305,7 @@ function getRelatedTableScalarFields( 'slug', 'code', 'createdAt', - 'updatedAt', + 'updatedAt' ]; const included: string[] = []; @@ -351,7 +351,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'belongsTo', - referencedTable: rel.referencesTable || undefined, + referencedTable: rel.referencesTable || undefined }); } }); @@ -362,7 +362,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'hasOne', - referencedTable: rel.referencedByTable || undefined, + referencedTable: rel.referencedByTable || undefined }); } }); @@ -373,7 +373,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'hasMany', - referencedTable: rel.referencedByTable || undefined, + referencedTable: rel.referencedByTable || undefined }); } }); @@ -384,7 +384,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'manyToMany', - referencedTable: rel.rightTable || undefined, + referencedTable: rel.rightTable || undefined }); } }); @@ -465,6 +465,6 @@ export function validateFieldSelection( return { isValid: errors.length === 0, - errors, + errors }; } diff --git a/graphql/codegen/src/generators/index.ts b/graphql/codegen/src/generators/index.ts index 1bc3c755e..4b38b3518 100644 --- a/graphql/codegen/src/generators/index.ts +++ b/graphql/codegen/src/generators/index.ts @@ -5,26 +5,26 @@ // Field selector utilities export { convertToSelectionOptions, - isRelationalField, getAvailableRelations, - validateFieldSelection, + isRelationalField, + validateFieldSelection } from './field-selector'; // Query generators export { - buildSelect, - buildFindOne, buildCount, - toCamelCasePlural, - toOrderByTypeName, + buildFindOne, + buildSelect, cleanTableToMetaObject, - generateIntrospectionSchema, createASTQueryBuilder, + generateIntrospectionSchema, + toCamelCasePlural, + toOrderByTypeName } from './select'; // Mutation generators export { buildPostGraphileCreate, - buildPostGraphileUpdate, buildPostGraphileDelete, + buildPostGraphileUpdate } from './mutations'; diff --git a/graphql/codegen/src/generators/mutations.ts b/graphql/codegen/src/generators/mutations.ts index a7d495e0b..3d86856e1 100644 --- a/graphql/codegen/src/generators/mutations.ts +++ b/graphql/codegen/src/generators/mutations.ts @@ -3,18 +3,17 @@ * Uses AST-based approach for PostGraphile-compatible mutations */ import * as t from 'gql-ast'; -import { print } from 'graphql'; import type { ArgumentNode, FieldNode, VariableDefinitionNode } from 'graphql'; +import { print } from 'graphql'; import { camelize } from 'inflekt'; import { TypedDocumentString } from '../client/typed-document'; import { getCustomAstForCleanField, - requiresSubfieldSelection, + requiresSubfieldSelection } from '../core/custom-ast'; -import type { CleanTable } from '../types/schema'; import type { MutationOptions } from '../types/mutation'; - +import type { CleanTable } from '../types/schema'; import { isRelationalField } from './field-selector'; /** @@ -55,17 +54,17 @@ export function buildPostGraphileCreate( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Create${table.name}Input` }), - }), - }), + type: t.namedType({ type: `Create${table.name}Input` }) + }) + }) ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }), - }), + value: t.variable({ name: 'input' }) + }) ]; // Get the field selections for the return value using custom AST logic @@ -88,23 +87,23 @@ export function buildPostGraphileCreate( t.field({ name: singularName, selectionSet: t.selectionSet({ - selections: fieldSelections, - }), - }), - ], - }), - }), - ], - }), - }), - ], + selections: fieldSelections + }) + }) + ] + }) + }) + ] + }) + }) + ] }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast, + __ast: ast }) as TypedDocumentString< Record, { input: { [key: string]: Record } } @@ -131,17 +130,17 @@ export function buildPostGraphileUpdate( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Update${table.name}Input` }), - }), - }), + type: t.namedType({ type: `Update${table.name}Input` }) + }) + }) ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }), - }), + value: t.variable({ name: 'input' }) + }) ]; // Get the field selections for the return value using custom AST logic @@ -164,23 +163,23 @@ export function buildPostGraphileUpdate( t.field({ name: singularName, selectionSet: t.selectionSet({ - selections: fieldSelections, - }), - }), - ], - }), - }), - ], - }), - }), - ], + selections: fieldSelections + }) + }) + ] + }) + }) + ] + }) + }) + ] }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast, + __ast: ast }) as TypedDocumentString< Record, { input: { id: string | number; patch: Record } } @@ -206,17 +205,17 @@ export function buildPostGraphileDelete( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Delete${table.name}Input` }), - }), - }), + type: t.namedType({ type: `Delete${table.name}Input` }) + }) + }) ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }), - }), + value: t.variable({ name: 'input' }) + }) ]; // PostGraphile delete mutations typically return clientMutationId @@ -235,20 +234,20 @@ export function buildPostGraphileDelete( name: mutationName, args: mutationArgs, selectionSet: t.selectionSet({ - selections: fieldSelections, - }), - }), - ], - }), - }), - ], + selections: fieldSelections + }) + }) + ] + }) + }) + ] }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast, + __ast: ast }) as TypedDocumentString< Record, { input: { id: string | number } } diff --git a/graphql/codegen/src/generators/select.ts b/graphql/codegen/src/generators/select.ts index edf2cace7..8bd58ccf8 100644 --- a/graphql/codegen/src/generators/select.ts +++ b/graphql/codegen/src/generators/select.ts @@ -3,14 +3,14 @@ * Uses AST-based approach for all query generation */ import * as t from 'gql-ast'; -import { print } from 'graphql'; import type { ArgumentNode, FieldNode, VariableDefinitionNode } from 'graphql'; +import { print } from 'graphql'; import { camelize, pluralize } from 'inflekt'; import { TypedDocumentString } from '../client/typed-document'; import { getCustomAstForCleanField, - requiresSubfieldSelection, + requiresSubfieldSelection } from '../core/custom-ast'; import { QueryBuilder } from '../core/query-builder'; import type { @@ -20,12 +20,11 @@ import type { MetaObject, MutationDefinition, QueryDefinition, - QuerySelectionOptions, + QuerySelectionOptions } from '../core/types'; -import type { CleanTable } from '../types/schema'; import type { QueryOptions } from '../types/query'; +import type { CleanTable } from '../types/schema'; import type { FieldSelection } from '../types/selection'; - import { convertToSelectionOptions, isRelationalField } from './field-selector'; /** @@ -67,8 +66,8 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: field.type.pgAlias, pgType: field.type.pgType, subtype: field.type.subtype, - typmod: field.type.typmod, - }, + typmod: field.type.typmod + } })), primaryConstraints: [] as MetaConstraint[], // Would need to be derived from schema uniqueConstraints: [] as MetaConstraint[], // Would need to be derived from schema @@ -83,9 +82,9 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: null, pgType: null, subtype: null, - typmod: null, + typmod: null } as MetaFieldType, - alias: rel.fieldName || '', + alias: rel.fieldName || '' }, toKey: { name: 'id', @@ -96,11 +95,11 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: null, pgType: null, subtype: null, - typmod: null, - } as MetaFieldType, - }, - })), - })), + typmod: null + } as MetaFieldType + } + })) + })) }; } @@ -125,7 +124,7 @@ export function generateIntrospectionSchema( qtype: 'getMany', model: modelName, selection, - properties: convertFieldsToProperties(table.fields), + properties: convertFieldsToProperties(table.fields) } as QueryDefinition; // Add getOne query (by ID) @@ -134,7 +133,7 @@ export function generateIntrospectionSchema( qtype: 'getOne', model: modelName, selection, - properties: convertFieldsToProperties(table.fields), + properties: convertFieldsToProperties(table.fields) } as QueryDefinition; // Add create mutation @@ -157,11 +156,11 @@ export function generateIntrospectionSchema( isNotNull: true, isArray: false, isArrayNotNull: false, - properties: convertFieldsToNestedProperties(table.fields), - }, - }, - }, - }, + properties: convertFieldsToNestedProperties(table.fields) + } + } + } + } } as MutationDefinition; // Add update mutation @@ -184,11 +183,11 @@ export function generateIntrospectionSchema( isNotNull: true, isArray: false, isArrayNotNull: false, - properties: convertFieldsToNestedProperties(table.fields), - }, - }, - }, - }, + properties: convertFieldsToNestedProperties(table.fields) + } + } + } + } } as MutationDefinition; // Add delete mutation @@ -210,11 +209,11 @@ export function generateIntrospectionSchema( type: 'UUID', isNotNull: true, isArray: false, - isArrayNotNull: false, - }, - }, - }, - }, + isArrayNotNull: false + } + } + } + } } as MutationDefinition; } @@ -233,7 +232,7 @@ function convertFieldsToProperties(fields: CleanTable['fields']) { type: field.type.gqlType, isNotNull: !field.type.gqlType.endsWith('!'), isArray: field.type.isArray, - isArrayNotNull: false, + isArrayNotNull: false }; }); @@ -252,7 +251,7 @@ function convertFieldsToNestedProperties(fields: CleanTable['fields']) { type: field.type.gqlType, isNotNull: false, // Mutations typically allow optional fields isArray: field.type.isArray, - isArrayNotNull: false, + isArrayNotNull: false }; }); @@ -268,7 +267,7 @@ export function createASTQueryBuilder(tables: CleanTable[]): QueryBuilder { return new QueryBuilder({ meta: metaObject, - introspection: introspectionSchema, + introspection: introspectionSchema }); } @@ -370,13 +369,13 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'first' }), - type: t.namedType({ type: 'Int' }), + type: t.namedType({ type: 'Int' }) }) ); queryArgs.push( t.argument({ name: 'first', - value: t.variable({ name: 'first' }), + value: t.variable({ name: 'first' }) }) ); } @@ -385,13 +384,13 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'offset' }), - type: t.namedType({ type: 'Int' }), + type: t.namedType({ type: 'Int' }) }) ); queryArgs.push( t.argument({ name: 'offset', - value: t.variable({ name: 'offset' }), + value: t.variable({ name: 'offset' }) }) ); } @@ -401,13 +400,13 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'after' }), - type: t.namedType({ type: 'Cursor' }), + type: t.namedType({ type: 'Cursor' }) }) ); queryArgs.push( t.argument({ name: 'after', - value: t.variable({ name: 'after' }), + value: t.variable({ name: 'after' }) }) ); } @@ -416,13 +415,13 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'before' }), - type: t.namedType({ type: 'Cursor' }), + type: t.namedType({ type: 'Cursor' }) }) ); queryArgs.push( t.argument({ name: 'before', - value: t.variable({ name: 'before' }), + value: t.variable({ name: 'before' }) }) ); } @@ -432,13 +431,13 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: `${table.name}Filter` }), + type: t.namedType({ type: `${table.name}Filter` }) }) ); queryArgs.push( t.argument({ name: 'filter', - value: t.variable({ name: 'filter' }), + value: t.variable({ name: 'filter' }) }) ); } @@ -451,15 +450,15 @@ function generateSelectQueryAST( // PostGraphile expects [ProductsOrderBy!] - list of non-null enum values type: t.listType({ type: t.nonNullType({ - type: t.namedType({ type: toOrderByTypeName(table.name) }), - }), - }), + type: t.namedType({ type: toOrderByTypeName(table.name) }) + }) + }) }) ); queryArgs.push( t.argument({ name: 'orderBy', - value: t.variable({ name: 'orderBy' }), + value: t.variable({ name: 'orderBy' }) }) ); } @@ -470,9 +469,9 @@ function generateSelectQueryAST( t.field({ name: 'nodes', selectionSet: t.selectionSet({ - selections: fieldSelections, - }), - }), + selections: fieldSelections + }) + }) ]; // Add pageInfo if requested (for cursor-based pagination / infinite scroll) @@ -489,9 +488,9 @@ function generateSelectQueryAST( t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }), - ], - }), + t.field({ name: 'endCursor' }) + ] + }) }) ); } @@ -508,13 +507,13 @@ function generateSelectQueryAST( name: pluralName, args: queryArgs, selectionSet: t.selectionSet({ - selections: connectionSelections, - }), - }), - ], - }), - }), - ], + selections: connectionSelections + }) + }) + ] + }) + }) + ] }); return print(ast); @@ -595,20 +594,20 @@ function generateFieldSelectionsFromOptions( t.argument({ name: 'first', value: t.intValue({ - value: DEFAULT_NESTED_RELATION_FIRST.toString(), - }), - }), + value: DEFAULT_NESTED_RELATION_FIRST.toString() + }) + }) ], selectionSet: t.selectionSet({ selections: [ t.field({ name: 'nodes', selectionSet: t.selectionSet({ - selections: nestedSelections, - }), - }), - ], - }), + selections: nestedSelections + }) + }) + ] + }) }) ); } else { @@ -617,8 +616,8 @@ function generateFieldSelectionsFromOptions( t.field({ name: fieldName, selectionSet: t.selectionSet({ - selections: nestedSelections, - }), + selections: nestedSelections + }) }) ); } @@ -749,9 +748,9 @@ function generateFindOneQueryAST(table: CleanTable): string { t.variableDefinition({ variable: t.variable({ name: 'id' }), type: t.nonNullType({ - type: t.namedType({ type: 'UUID' }), - }), - }), + type: t.namedType({ type: 'UUID' }) + }) + }) ], selectionSet: t.selectionSet({ selections: [ @@ -760,17 +759,17 @@ function generateFindOneQueryAST(table: CleanTable): string { args: [ t.argument({ name: 'id', - value: t.variable({ name: 'id' }), - }), + value: t.variable({ name: 'id' }) + }) ], selectionSet: t.selectionSet({ - selections: fieldSelections, - }), - }), - ], - }), - }), - ], + selections: fieldSelections + }) + }) + ] + }) + }) + ] }); return print(ast); @@ -790,8 +789,8 @@ function generateCountQueryAST(table: CleanTable): string { variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: `${table.name}Filter` }), - }), + type: t.namedType({ type: `${table.name}Filter` }) + }) ], selectionSet: t.selectionSet({ selections: [ @@ -800,17 +799,17 @@ function generateCountQueryAST(table: CleanTable): string { args: [ t.argument({ name: 'filter', - value: t.variable({ name: 'filter' }), - }), + value: t.variable({ name: 'filter' }) + }) ], selectionSet: t.selectionSet({ - selections: [t.field({ name: 'totalCount' })], - }), - }), - ], - }), - }), - ], + selections: [t.field({ name: 'totalCount' })] + }) + }) + ] + }) + }) + ] }); return print(ast); diff --git a/graphql/codegen/src/index.ts b/graphql/codegen/src/index.ts index 19ae82473..9d794f364 100644 --- a/graphql/codegen/src/index.ts +++ b/graphql/codegen/src/index.ts @@ -22,22 +22,22 @@ export * from './client'; export { defineConfig } from './types/config'; // Main generate function (orchestrates the entire pipeline) -export { generate } from './core/generate'; export type { GenerateOptions, GenerateResult } from './core/generate'; +export { generate } from './core/generate'; // Config utilities export { findConfigFile, loadConfigFile } from './core/config'; // CLI shared utilities (for packages/cli to import) -export { codegenQuestions, splitCommas, printResult, camelizeArgv } from './cli/shared'; export type { CodegenAnswers } from './cli/shared'; +export { camelizeArgv,codegenQuestions, printResult, splitCommas } from './cli/shared'; // Database schema utilities (re-exported from core for convenience) -export { - buildSchemaFromDatabase, - buildSchemaSDLFromDatabase, -} from './core/database'; export type { BuildSchemaFromDatabaseOptions, - BuildSchemaFromDatabaseResult, + BuildSchemaFromDatabaseResult +} from './core/database'; +export { + buildSchemaFromDatabase, + buildSchemaSDLFromDatabase } from './core/database'; diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index bae0a864f..09e457006 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -356,7 +356,7 @@ export const DEFAULT_WATCH_CONFIG: WatchConfig = { pollInterval: 3000, debounce: 800, touchFile: undefined, - clearScreen: true, + clearScreen: true }; /** @@ -367,7 +367,7 @@ export const DEFAULT_QUERY_KEY_CONFIG: QueryKeyConfig = { relationships: {}, generateScopedKeys: true, generateCascadeHelpers: true, - generateMutationKeys: true, + generateMutationKeys: true }; /** @@ -380,36 +380,36 @@ export const DEFAULT_CONFIG: GraphQLSDKConfigTarget = { tables: { include: ['*'], exclude: [], - systemExclude: [], + systemExclude: [] }, queries: { include: ['*'], exclude: [], - systemExclude: ['_meta', 'query'], // Internal PostGraphile queries + systemExclude: ['_meta', 'query'] // Internal PostGraphile queries }, mutations: { include: ['*'], exclude: [], - systemExclude: [], + systemExclude: [] }, excludeFields: [], hooks: { queries: true, mutations: true, - queryKeyPrefix: 'graphql', + queryKeyPrefix: 'graphql' }, postgraphile: { - schema: 'public', + schema: 'public' }, codegen: { maxFieldDepth: 2, - skipQueryField: true, + skipQueryField: true }, orm: false, reactQuery: false, browserCompatible: true, queryKeys: DEFAULT_QUERY_KEY_CONFIG, - watch: DEFAULT_WATCH_CONFIG, + watch: DEFAULT_WATCH_CONFIG }; diff --git a/graphql/codegen/src/types/index.ts b/graphql/codegen/src/types/index.ts index 8e23aad73..83a920137 100644 --- a/graphql/codegen/src/types/index.ts +++ b/graphql/codegen/src/types/index.ts @@ -4,59 +4,58 @@ // Schema types export type { - CleanTable, + CleanBelongsToRelation, CleanField, CleanFieldType, - CleanRelations, - CleanBelongsToRelation, - CleanHasOneRelation, CleanHasManyRelation, + CleanHasOneRelation, CleanManyToManyRelation, - TableInflection, - TableQueryNames, - TableConstraints, + CleanRelations, + CleanTable, ConstraintInfo, ForeignKeyConstraint, + TableConstraints, + TableInflection, + TableQueryNames } from './schema'; // Query types export type { - PageInfo, ConnectionResult, - QueryOptions, - OrderByItem, - FilterOperator, FieldFilter, - RelationalFilter, Filter, + FilterOperator, + OrderByItem, + PageInfo, + QueryOptions, + RelationalFilter } from './query'; // Mutation types export type { - MutationOptions, CreateInput, - UpdateInput, DeleteInput, + MutationOptions, MutationResult, + UpdateInput } from './mutation'; // Selection types export type { - SimpleFieldSelection, - FieldSelectionPreset, FieldSelection, + FieldSelectionPreset, SelectionOptions, + SimpleFieldSelection } from './selection'; // Config types export type { GraphQLSDKConfig, - GraphQLSDKConfigTarget, + GraphQLSDKConfigTarget } from './config'; - export { + DEFAULT_CONFIG, defineConfig, getConfigOptions, - mergeConfig, - DEFAULT_CONFIG, + mergeConfig } from './config'; From b5eb861201bca561c0604b6e3e74ee3689a25da7 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Sat, 7 Feb 2026 14:32:30 +0700 Subject: [PATCH 02/23] feat(graphql-codegen): unify ORM and RQ selection core --- .../client-generator.test.ts.snap | 30 +- .../input-types-generator.test.ts.snap | 43 +- .../model-generator.test.ts.snap | 292 ++++-- .../__snapshots__/query-builder.test.ts.snap | 227 +++++ .../react-query-hooks.test.ts.snap | 947 ++++++++++++------ .../__tests__/codegen/query-builder.test.ts | 343 ++++++- .../src/core/codegen/custom-mutations.ts | 53 +- .../src/core/codegen/custom-queries.ts | 238 +++-- graphql/codegen/src/core/codegen/index.ts | 7 + graphql/codegen/src/core/codegen/mutations.ts | 105 +- .../core/codegen/orm/custom-ops-generator.ts | 47 +- .../core/codegen/orm/input-types-generator.ts | 115 ++- .../src/core/codegen/orm/model-generator.ts | 678 ++++++++----- .../src/core/codegen/orm/select-types.ts | 33 +- graphql/codegen/src/core/codegen/queries.ts | 248 +++-- .../src/core/codegen/select-helpers.ts | 9 +- graphql/codegen/src/core/codegen/selection.ts | 87 ++ .../core/codegen/templates/query-builder.ts | 57 +- .../core/codegen/templates/select-types.ts | 30 +- 19 files changed, 2652 insertions(+), 937 deletions(-) create mode 100644 graphql/codegen/src/__tests__/codegen/__snapshots__/query-builder.test.ts.snap create mode 100644 graphql/codegen/src/core/codegen/selection.ts diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap index 31e4f0863..df8457471 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap @@ -317,16 +317,10 @@ export interface DeleteArgs { /** * Recursively validates select objects, rejecting unknown keys. * - * This type ensures that users can only select fields that actually exist - * in the GraphQL schema. It returns \`never\` if any excess keys are found - * at any nesting level, causing a TypeScript compile error. - * - * Why this is needed: - * TypeScript's excess property checking has a quirk where it only catches - * invalid fields when they are the ONLY fields. When mixed with valid fields - * (e.g., \`{ id: true, invalidField: true }\`), the structural typing allows - * the excess property through. This type explicitly checks for and rejects - * such cases. + * NOTE: This type is intentionally NOT used in generated parameter positions + * (conditional types block IDE autocompletion). Parameters use \`S\` directly + * with \`S extends XxxSelect\` constraints, which provides full + * autocompletion via TypeScript's contextual typing. * * @example * // This will cause a type error because 'invalid' doesn't exist: @@ -343,15 +337,25 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Shape[K] extends { select?: infer ShapeNS } - ? { select: DeepExact> } - : T[K] + ? Extract extends { select?: infer ShapeNS } + ? DeepExact< + Omit & { select: DeepExact> }, + Extract + > + : never : T[K] : never; } : never : never; +/** + * Enforces exact select shape while keeping contextual typing on \`S extends XxxSelect\`. + * Use this as an intersection in overloads: + * \`{ select: S } & StrictSelect\`. + */ +export type StrictSelect = S extends DeepExact ? {} : never; + /** * Infer result type from select configuration */ diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap index 523156dd3..816974a38 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/input-types-generator.test.ts.snap @@ -469,7 +469,17 @@ export interface UpdateCommentInput { export interface DeleteCommentInput { clientMutationId?: string; id: string; -}" +} +// ============ Connection Fields Map ============ +export const connectionFieldsMap = { + "User": { + "posts": "Post", + "comments": "Comment" + }, + "Post": { + "comments": "Comment" + } +} as Record>;" `; exports[`generateInputTypesFile generates complete types file for single table 1`] = ` @@ -785,7 +795,9 @@ export interface UpdateUserInput { export interface DeleteUserInput { clientMutationId?: string; id: string; -}" +} +// ============ Connection Fields Map ============ +export const connectionFieldsMap = {} as Record>;" `; exports[`generateInputTypesFile generates custom input types from TypeRegistry 1`] = ` @@ -1102,6 +1114,8 @@ export interface DeleteUserInput { clientMutationId?: string; id: string; } +// ============ Connection Fields Map ============ +export const connectionFieldsMap = {} as Record>; // ============ Custom Input Types (from schema) ============ export interface LoginInput { email: string; @@ -1430,6 +1444,8 @@ export interface DeleteUserInput { clientMutationId?: string; id: string; } +// ============ Connection Fields Map ============ +export const connectionFieldsMap = {} as Record>; // ============ Custom Input Types (from schema) ============ export interface LoginInput { email: string; @@ -1831,7 +1847,13 @@ export interface UpdateProfileInput { export interface DeleteProfileInput { clientMutationId?: string; id: string; -}" +} +// ============ Connection Fields Map ============ +export const connectionFieldsMap = { + "User": { + "posts": "Post" + } +} as Record>;" `; exports[`generateInputTypesFile generates types with manyToMany relations 1`] = ` @@ -2209,7 +2231,16 @@ export interface UpdateCategoryInput { export interface DeleteCategoryInput { clientMutationId?: string; id: string; -}" +} +// ============ Connection Fields Map ============ +export const connectionFieldsMap = { + "Post": { + "comments": "Comment" + }, + "Category": { + "posts": "Post" + } +} as Record>;" `; exports[`generateInputTypesFile handles empty tables array 1`] = ` @@ -2437,5 +2468,7 @@ export interface UUIDListFilter { anyLessThanOrEqualTo?: string; anyGreaterThan?: string; anyGreaterThanOrEqualTo?: string; -}" +} +// ============ Connection Fields Map ============ +export const connectionFieldsMap = {} as Record>;" `; diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap index fafb3a3d1..4ed4ce22b 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap @@ -8,16 +8,25 @@ exports[`model-generator generates model with all CRUD methods 1`] = ` */ import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; -import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; +import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { User, UserWithRelations, UserSelect, UserFilter, UsersOrderBy, CreateUserInput, UpdateUserInput, UserPatch } from "../input-types"; +import { connectionFieldsMap } from "../input-types"; const defaultSelect = { id: true } as const; export class UserModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, UserFilter, UsersOrderBy>): QueryBuilder<{ + findMany(args: FindManyArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ users: ConnectionResult>; - }> { + }>; + findMany(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + users: ConnectionResult>; + }>; + findMany(args?: FindManyArgs) { const { document, variables @@ -29,7 +38,7 @@ export class UserModel { after: args?.after, before: args?.before, offset: args?.offset - }, "UserFilter", "UsersOrderBy"); + }, "UserFilter", "UsersOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -39,17 +48,27 @@ export class UserModel { variables }); } - findFirst(args?: FindFirstArgs, UserFilter>): QueryBuilder<{ + findFirst(args: FindFirstArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ users: { nodes: InferSelectResult[]; }; - }> { + }>; + findFirst(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + users: { + nodes: InferSelectResult[]; + }; + }>; + findFirst(args?: FindFirstArgs) { const { document, variables } = buildFindFirstDocument("User", "users", args?.select ?? defaultSelect, { where: args?.where - }, "UserFilter"); + }, "UserFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -59,16 +78,25 @@ export class UserModel { variables }); } - findOne(args: { + findOne(args: { id: string; - select?: DeepExact; - }): QueryBuilder<{ + select: S; + } & StrictSelect): QueryBuilder<{ user: InferSelectResult | null; - }> { + }>; + findOne(args: { + id: string; + }): QueryBuilder<{ + user: InferSelectResult | null; + }>; + findOne(args: { + id: string; + select?: UserSelect; + }) { const { document, variables - } = buildFindOneDocument("User", "user", args.id, args.select ?? defaultSelect, "id", "UUID!"); + } = buildFindOneDocument("User", "user", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -78,15 +106,25 @@ export class UserModel { variables }); } - create(args: CreateArgs, CreateUserInput["user"]>): QueryBuilder<{ + create(args: CreateArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ createUser: { user: InferSelectResult; }; - }> { + }>; + create(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + createUser: { + user: InferSelectResult; + }; + }>; + create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("User", "createUser", "user", args.select ?? defaultSelect, args.data, "CreateUserInput"); + } = buildCreateDocument("User", "createUser", "user", args.select ?? defaultSelect, args.data, "CreateUserInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -96,17 +134,31 @@ export class UserModel { variables }); } - update(args: UpdateArgs, { + update(args: UpdateArgs): QueryBuilder<{ + }, UserPatch> & { + select: S; + } & StrictSelect): QueryBuilder<{ updateUser: { user: InferSelectResult; }; - }> { + }>; + update(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + updateUser: { + user: InferSelectResult; + }; + }>; + update(args: UpdateArgs) { const { document, variables - } = buildUpdateByPkDocument("User", "updateUser", "user", args.select ?? defaultSelect, args.where.id, args.data, "UpdateUserInput", "id"); + } = buildUpdateByPkDocument("User", "updateUser", "user", args.select ?? defaultSelect, args.where.id, args.data, "UpdateUserInput", "id", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -116,17 +168,31 @@ export class UserModel { variables }); } - delete(args: DeleteArgs<{ + delete(args: DeleteArgs<{ id: string; - }, DeepExact>): QueryBuilder<{ + }, S> & { + select: S; + } & StrictSelect): QueryBuilder<{ deleteUser: { user: InferSelectResult; }; - }> { + }>; + delete(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + deleteUser: { + user: InferSelectResult; + }; + }>; + delete(args: DeleteArgs<{ + id: string; + }, UserSelect>) { const { document, variables - } = buildDeleteByPkDocument("User", "deleteUser", "user", args.where.id, "DeleteUserInput", "id", args.select ?? defaultSelect); + } = buildDeleteByPkDocument("User", "deleteUser", "user", args.where.id, "DeleteUserInput", "id", args.select ?? defaultSelect, connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -147,16 +213,25 @@ exports[`model-generator generates model without update/delete when not availabl */ import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; -import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; +import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, AuditLogsOrderBy, CreateAuditLogInput, UpdateAuditLogInput, AuditLogPatch } from "../input-types"; +import { connectionFieldsMap } from "../input-types"; const defaultSelect = { id: true } as const; export class AuditLogModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, AuditLogFilter, AuditLogsOrderBy>): QueryBuilder<{ + findMany(args: FindManyArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ auditLogs: ConnectionResult>; - }> { + }>; + findMany(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + auditLogs: ConnectionResult>; + }>; + findMany(args?: FindManyArgs) { const { document, variables @@ -168,7 +243,7 @@ export class AuditLogModel { after: args?.after, before: args?.before, offset: args?.offset - }, "AuditLogFilter", "AuditLogsOrderBy"); + }, "AuditLogFilter", "AuditLogsOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -178,17 +253,27 @@ export class AuditLogModel { variables }); } - findFirst(args?: FindFirstArgs, AuditLogFilter>): QueryBuilder<{ + findFirst(args: FindFirstArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ auditLogs: { nodes: InferSelectResult[]; }; - }> { + }>; + findFirst(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + auditLogs: { + nodes: InferSelectResult[]; + }; + }>; + findFirst(args?: FindFirstArgs) { const { document, variables } = buildFindFirstDocument("AuditLog", "auditLogs", args?.select ?? defaultSelect, { where: args?.where - }, "AuditLogFilter"); + }, "AuditLogFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -198,16 +283,25 @@ export class AuditLogModel { variables }); } - findOne(args: { + findOne(args: { id: string; - select?: DeepExact; - }): QueryBuilder<{ + select: S; + } & StrictSelect): QueryBuilder<{ auditLog: InferSelectResult | null; - }> { + }>; + findOne(args: { + id: string; + }): QueryBuilder<{ + auditLog: InferSelectResult | null; + }>; + findOne(args: { + id: string; + select?: AuditLogSelect; + }) { const { document, variables - } = buildFindOneDocument("AuditLog", "auditLog", args.id, args.select ?? defaultSelect, "id", "UUID!"); + } = buildFindOneDocument("AuditLog", "auditLog", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -217,15 +311,25 @@ export class AuditLogModel { variables }); } - create(args: CreateArgs, CreateAuditLogInput["auditLog"]>): QueryBuilder<{ + create(args: CreateArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ createAuditLog: { auditLog: InferSelectResult; }; - }> { + }>; + create(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + createAuditLog: { + auditLog: InferSelectResult; + }; + }>; + create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select ?? defaultSelect, args.data, "CreateAuditLogInput"); + } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select ?? defaultSelect, args.data, "CreateAuditLogInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -246,16 +350,25 @@ exports[`model-generator handles custom query/mutation names 1`] = ` */ import { OrmClient } from "../client"; import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindOneDocument, buildCreateDocument, buildUpdateByPkDocument, buildDeleteByPkDocument } from "../query-builder"; -import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, DeepExact } from "../select-types"; +import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { Organization, OrganizationWithRelations, OrganizationSelect, OrganizationFilter, OrganizationsOrderBy, CreateOrganizationInput, UpdateOrganizationInput, OrganizationPatch } from "../input-types"; +import { connectionFieldsMap } from "../input-types"; const defaultSelect = { id: true } as const; export class OrganizationModel { constructor(private client: OrmClient) {} - findMany(args?: FindManyArgs, OrganizationFilter, OrganizationsOrderBy>): QueryBuilder<{ + findMany(args: FindManyArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ allOrganizations: ConnectionResult>; - }> { + }>; + findMany(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + allOrganizations: ConnectionResult>; + }>; + findMany(args?: FindManyArgs) { const { document, variables @@ -267,7 +380,7 @@ export class OrganizationModel { after: args?.after, before: args?.before, offset: args?.offset - }, "OrganizationFilter", "OrganizationsOrderBy"); + }, "OrganizationFilter", "OrganizationsOrderBy", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -277,17 +390,27 @@ export class OrganizationModel { variables }); } - findFirst(args?: FindFirstArgs, OrganizationFilter>): QueryBuilder<{ + findFirst(args: FindFirstArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ allOrganizations: { nodes: InferSelectResult[]; }; - }> { + }>; + findFirst(args?: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + allOrganizations: { + nodes: InferSelectResult[]; + }; + }>; + findFirst(args?: FindFirstArgs) { const { document, variables } = buildFindFirstDocument("Organization", "allOrganizations", args?.select ?? defaultSelect, { where: args?.where - }, "OrganizationFilter"); + }, "OrganizationFilter", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -297,16 +420,25 @@ export class OrganizationModel { variables }); } - findOne(args: { + findOne(args: { id: string; - select?: DeepExact; - }): QueryBuilder<{ + select: S; + } & StrictSelect): QueryBuilder<{ organizationById: InferSelectResult | null; - }> { + }>; + findOne(args: { + id: string; + }): QueryBuilder<{ + organizationById: InferSelectResult | null; + }>; + findOne(args: { + id: string; + select?: OrganizationSelect; + }) { const { document, variables - } = buildFindOneDocument("Organization", "organizationById", args.id, args.select ?? defaultSelect, "id", "UUID!"); + } = buildFindOneDocument("Organization", "organizationById", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -316,15 +448,25 @@ export class OrganizationModel { variables }); } - create(args: CreateArgs, CreateOrganizationInput["organization"]>): QueryBuilder<{ + create(args: CreateArgs & { + select: S; + } & StrictSelect): QueryBuilder<{ registerOrganization: { organization: InferSelectResult; }; - }> { + }>; + create(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + registerOrganization: { + organization: InferSelectResult; + }; + }>; + create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select ?? defaultSelect, args.data, "CreateOrganizationInput"); + } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select ?? defaultSelect, args.data, "CreateOrganizationInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -334,17 +476,31 @@ export class OrganizationModel { variables }); } - update(args: UpdateArgs, { + update(args: UpdateArgs): QueryBuilder<{ + }, OrganizationPatch> & { + select: S; + } & StrictSelect): QueryBuilder<{ modifyOrganization: { organization: InferSelectResult; }; - }> { + }>; + update(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + modifyOrganization: { + organization: InferSelectResult; + }; + }>; + update(args: UpdateArgs) { const { document, variables - } = buildUpdateByPkDocument("Organization", "modifyOrganization", "organization", args.select ?? defaultSelect, args.where.id, args.data, "UpdateOrganizationInput", "id"); + } = buildUpdateByPkDocument("Organization", "modifyOrganization", "organization", args.select ?? defaultSelect, args.where.id, args.data, "UpdateOrganizationInput", "id", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -354,17 +510,31 @@ export class OrganizationModel { variables }); } - delete(args: DeleteArgs<{ + delete(args: DeleteArgs<{ id: string; - }, DeepExact>): QueryBuilder<{ + }, S> & { + select: S; + } & StrictSelect): QueryBuilder<{ removeOrganization: { organization: InferSelectResult; }; - }> { + }>; + delete(args: Omit, "select"> & { + select?: undefined; + }): QueryBuilder<{ + removeOrganization: { + organization: InferSelectResult; + }; + }>; + delete(args: DeleteArgs<{ + id: string; + }, OrganizationSelect>) { const { document, variables - } = buildDeleteByPkDocument("Organization", "removeOrganization", "organization", args.where.id, "DeleteOrganizationInput", "id", args.select ?? defaultSelect); + } = buildDeleteByPkDocument("Organization", "removeOrganization", "organization", args.where.id, "DeleteOrganizationInput", "id", args.select ?? defaultSelect, connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/query-builder.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/query-builder.test.ts.snap new file mode 100644 index 000000000..f679d5605 --- /dev/null +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/query-builder.test.ts.snap @@ -0,0 +1,227 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`query-builder snapshots findMany document with filter and pagination 1`] = ` +"query UserQuery($where: UserFilter, $orderBy: [UsersOrderBy!], $first: Int) { + users(filter: $where, orderBy: $orderBy, first: $first) { + nodes { + id + name + email + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots findMany document with nested connections via connectionFieldsMap 1`] = ` +"query UserQuery { + users { + nodes { + id + name + posts { + nodes { + id + title + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + comments { + nodes { + id + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots findMany with deeply nested connections (3 levels) 1`] = ` +"query TestQuery { + users { + nodes { + id + posts { + nodes { + id + title + comments { + nodes { + id + body + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots findMany with mixed connection and singular relations 1`] = ` +"query TestQuery { + users { + nodes { + id + name + profile { + bio + avatar + } + posts { + nodes { + id + title + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + comments { + nodes { + id + body + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots findMany with nested connection fields 1`] = ` +"query TestQuery { + users { + nodes { + id + name + posts { + nodes { + id + title + body + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots findMany without connectionFieldsMap (no wrapping) 1`] = ` +"query TestQuery { + users { + nodes { + id + name + posts { + id + title + } + } + totalCount + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } +} +" +`; + +exports[`query-builder snapshots mutation document 1`] = ` +"mutation CreateUserMutation($input: CreateUserInput!) { + createUser(input: $input) { + user { + id + name + email + } + } +} +" +`; diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index 1e1d1710d..9ccd55d96 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -188,26 +188,36 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu */ import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { customMutationKeys } from '../mutation-keys'; import type { LoginVariables } from '../../orm/mutation'; import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { LoginVariables } from '../../orm/mutation'; export type { LoginPayloadSelect } from '../../orm/input-types'; const defaultSelect = { token: true } as const; -export function useLoginMutation( - args?: { select?: DeepExact }, - options?: Omit }, Error, LoginVariables>, 'mutationFn'> +export function useLoginMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, LoginVariables>, 'mutationFn'> +): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; +export function useLoginMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, LoginVariables>, 'mutationFn'> +): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; +export function useLoginMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; return useMutation({ mutationKey: customMutationKeys.login(), - mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as LoginPayloadSelect }).unwrap(), + ...mutationOptions, }); } " @@ -221,26 +231,36 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu */ import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { customMutationKeys } from '../mutation-keys'; import type { RegisterVariables } from '../../orm/mutation'; import type { RegisterPayloadSelect, RegisterPayload } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { RegisterVariables } from '../../orm/mutation'; export type { RegisterPayloadSelect } from '../../orm/input-types'; const defaultSelect = { token: true } as const; -export function useRegisterMutation( - args?: { select?: DeepExact }, - options?: Omit }, Error, RegisterVariables>, 'mutationFn'> +export function useRegisterMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, RegisterVariables>, 'mutationFn'> +): UseMutationResult<{ register: InferSelectResult }, Error, RegisterVariables>; +export function useRegisterMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, RegisterVariables>, 'mutationFn'> +): UseMutationResult<{ register: InferSelectResult }, Error, RegisterVariables>; +export function useRegisterMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; return useMutation({ mutationKey: customMutationKeys.register(), - mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { select: (args?.select ?? defaultSelect) as RegisterPayloadSelect }).unwrap(), + ...mutationOptions, }); } " @@ -254,24 +274,34 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu */ import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { customMutationKeys } from '../mutation-keys'; import type { LogoutPayloadSelect, LogoutPayload } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { LogoutPayloadSelect } from '../../orm/input-types'; const defaultSelect = { success: true } as const; -export function useLogoutMutation( - args?: { select?: DeepExact }, - options?: Omit }, Error, void>, 'mutationFn'> +export function useLogoutMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, void>, 'mutationFn'> +): UseMutationResult<{ logout: InferSelectResult }, Error, void>; +export function useLogoutMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, void>, 'mutationFn'> +): UseMutationResult<{ logout: InferSelectResult }, Error, void>; +export function useLogoutMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; return useMutation({ mutationKey: customMutationKeys.logout(), - mutationFn: () => getClient().mutation.logout({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + mutationFn: () => getClient().mutation.logout({ select: (args?.select ?? defaultSelect) as LogoutPayloadSelect }).unwrap(), + ...mutationOptions, }); } " @@ -285,24 +315,34 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu */ import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { LoginVariables } from '../../orm/mutation'; import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { LoginVariables } from '../../orm/mutation'; export type { LoginPayloadSelect } from '../../orm/input-types'; const defaultSelect = { token: true } as const; -export function useLoginMutation( - args?: { select?: DeepExact }, - options?: Omit }, Error, LoginVariables>, 'mutationFn'> +export function useLoginMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, LoginVariables>, 'mutationFn'> +): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; +export function useLoginMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, LoginVariables>, 'mutationFn'> +): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; +export function useLoginMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; return useMutation({ - mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as LoginPayloadSelect }).unwrap(), + ...mutationOptions, }); } " @@ -316,12 +356,14 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { customQueryKeys } from '../query-keys'; import type { SearchUsersVariables } from '../../orm/query'; import type { UserSelect, User } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { SearchUsersVariables } from '../../orm/query'; export type { UserSelect } from '../../orm/input-types'; @@ -343,16 +385,25 @@ export const searchUsersQueryKey = customQueryKeys.searchUsers; * } * \`\`\` */ -export function useSearchUsersQuery( - variables: SearchUsersVariables, - args?: { select?: DeepExact }, - options?: Omit[] }, Error>, 'queryKey' | 'queryFn'> +export function useSearchUsersQuery[] }>( + params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } & Omit[] }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useSearchUsersQuery[] }>( + params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } & Omit[] }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useSearchUsersQuery( + params: { variables: SearchUsersVariables; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const variables = params?.variables; + const args = buildSelectionArgs(params?.selection); + const { variables: _variables, selection: _selection, ...queryOptions } = params ?? {}; + void _variables; + void _selection; return useQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - enabled: !!variables && options?.enabled !== false, - ...options, + queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + enabled: !!variables && params?.enabled !== false, + ...queryOptions, }); } @@ -364,11 +415,18 @@ export function useSearchUsersQuery( - variables: SearchUsersVariables, - args?: { select?: DeepExact } +export async function fetchSearchUsersQuery( + params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } +): Promise<{ searchUsers: InferSelectResult[] }>; +export async function fetchSearchUsersQuery( + params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } +): Promise<{ searchUsers: InferSelectResult[] }>; +export async function fetchSearchUsersQuery( + params: { variables: SearchUsersVariables; selection?: SelectionConfig }, ) { - return getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); + const variables = params?.variables; + const args = buildSelectionArgs(params?.selection); + return getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -379,14 +437,23 @@ export async function fetchSearchUsersQuery( +export async function prefetchSearchUsersQuery( + queryClient: QueryClient, + params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } +): Promise; +export async function prefetchSearchUsersQuery( + queryClient: QueryClient, + params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } +): Promise; +export async function prefetchSearchUsersQuery( queryClient: QueryClient, - variables: SearchUsersVariables, - args?: { select?: DeepExact } + params: { variables: SearchUsersVariables; selection?: SelectionConfig } ): Promise { + const variables = params?.variables; + const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -400,11 +467,13 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { customQueryKeys } from '../query-keys'; import type { UserSelect, User } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect } from '../../orm/input-types'; @@ -425,14 +494,22 @@ export const currentUserQueryKey = customQueryKeys.currentUser; * } * \`\`\` */ -export function useCurrentUserQuery( - args?: { select?: DeepExact }, - options?: Omit }, Error>, 'queryKey' | 'queryFn'> +export function useCurrentUserQuery }>( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useCurrentUserQuery }>( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useCurrentUserQuery( + params?: { selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -444,10 +521,17 @@ export function useCurrentUserQuery( - args?: { select?: DeepExact } +export async function fetchCurrentUserQuery( + params: { selection: ({ fields: S } & StrictSelect) } +): Promise<{ currentUser: InferSelectResult }>; +export async function fetchCurrentUserQuery( + params?: { selection?: ({ fields?: undefined }) }, +): Promise<{ currentUser: InferSelectResult }>; +export async function fetchCurrentUserQuery( + params?: { selection?: SelectionConfig }, ) { - return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); + const args = buildSelectionArgs(params?.selection); + return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -458,13 +542,22 @@ export async function fetchCurrentUserQuery( +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + params: { selection: ({ fields: S } & StrictSelect) } +): Promise; +export async function prefetchCurrentUserQuery( queryClient: QueryClient, - args?: { select?: DeepExact } + params?: { selection?: ({ fields?: undefined }) } +): Promise; +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + params?: { selection?: SelectionConfig } ): Promise { + const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -478,10 +571,12 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { UserSelect, User } from '../../orm/input-types'; -import type { DeepExact, InferSelectResult } from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect } from '../../orm/input-types'; @@ -502,14 +597,22 @@ export const currentUserQueryKey = () => ['currentUser'] as const; * } * \`\`\` */ -export function useCurrentUserQuery( - args?: { select?: DeepExact }, - options?: Omit }, Error>, 'queryKey' | 'queryFn'> +export function useCurrentUserQuery }>( + params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useCurrentUserQuery }>( + params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useCurrentUserQuery( + params?: { selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), - ...options, + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -521,10 +624,17 @@ export function useCurrentUserQuery( - args?: { select?: DeepExact } +export async function fetchCurrentUserQuery( + params: { selection: ({ fields: S } & StrictSelect) } +): Promise<{ currentUser: InferSelectResult }>; +export async function fetchCurrentUserQuery( + params?: { selection?: ({ fields?: undefined }) }, +): Promise<{ currentUser: InferSelectResult }>; +export async function fetchCurrentUserQuery( + params?: { selection?: SelectionConfig }, ) { - return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(); + const args = buildSelectionArgs(params?.selection); + return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -535,13 +645,22 @@ export async function fetchCurrentUserQuery( +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + params: { selection: ({ fields: S } & StrictSelect) } +): Promise; +export async function prefetchCurrentUserQuery( + queryClient: QueryClient, + params?: { selection?: ({ fields?: undefined }) } +): Promise; +export async function prefetchCurrentUserQuery( queryClient: QueryClient, - args?: { select?: DeepExact } + params?: { selection?: SelectionConfig } ): Promise { + const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -555,8 +674,10 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { userKeys } from '../query-keys'; import { userMutationKeys } from '../mutation-keys'; import type { @@ -564,10 +685,7 @@ import type { UserWithRelations, CreateUserInput, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; @@ -579,24 +697,32 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useCreateUserMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +export function useCreateUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; +export function useCreateUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; +export function useCreateUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.create(), - mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -610,8 +736,10 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { postKeys } from '../query-keys'; import { postMutationKeys } from '../mutation-keys'; import type { @@ -619,10 +747,7 @@ import type { PostWithRelations, CreatePostInput, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { PostSelect, PostWithRelations, CreatePostInput } from '../../orm/input-types'; @@ -634,24 +759,32 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useCreatePostMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreatePostMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> +export function useCreatePostMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> +): UseMutationResult<{ createPost: { post: InferSelectResult } }, Error, CreatePostInput['post']>; +export function useCreatePostMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> +): UseMutationResult<{ createPost: { post: InferSelectResult } }, Error, CreatePostInput['post']>; +export function useCreatePostMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.create(), - mutationFn: (data: CreatePostInput['post']) => getClient().post.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: (data: CreatePostInput['post']) => getClient().post.create({ data, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -665,17 +798,16 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { UserSelect, UserWithRelations, CreateUserInput, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; @@ -687,23 +819,31 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useCreateUserMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +export function useCreateUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; +export function useCreateUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> +): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; +export function useCreateUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options, + ...mutationOptions, }); } " @@ -717,18 +857,17 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { userKeys } from '../query-keys'; import { userMutationKeys } from '../mutation-keys'; import type { UserSelect, UserWithRelations, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations } from '../../orm/input-types'; @@ -740,25 +879,33 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useDeleteUserMutation({ - * select: { id: true }, + * selection: { fields: { id: true } }, * }); * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string }>, 'mutationFn'> +export function useDeleteUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; +export function useDeleteUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; +export function useDeleteUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ queryKey: userKeys.detail(variables.id) }); queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -772,18 +919,17 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { postKeys } from '../query-keys'; import { postMutationKeys } from '../mutation-keys'; import type { PostSelect, PostWithRelations, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { PostSelect, PostWithRelations } from '../../orm/input-types'; @@ -795,25 +941,33 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useDeletePostMutation({ - * select: { id: true }, + * selection: { fields: { id: true } }, * }); * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeletePostMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string }>, 'mutationFn'> +export function useDeletePostMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deletePost: { post: InferSelectResult } }, Error, { id: string }>; +export function useDeletePostMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deletePost: { post: InferSelectResult } }, Error, { id: string }>; +export function useDeletePostMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: ({ id }: { id: string }) => getClient().post.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id }: { id: string }) => getClient().post.delete({ where: { id }, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ queryKey: postKeys.detail(variables.id) }); queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -827,16 +981,15 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { UserSelect, UserWithRelations, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations } from '../../orm/input-types'; @@ -848,24 +1001,32 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useDeleteUserMutation({ - * select: { id: true }, + * selection: { fields: { id: true } }, * }); * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string }>, 'mutationFn'> +export function useDeleteUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; +export function useDeleteUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> +): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; +export function useDeleteUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ queryKey: ['user', 'detail', variables.id] }); queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options, + ...mutationOptions, }); } " @@ -879,8 +1040,10 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { userKeys } from '../query-keys'; import { userMutationKeys } from '../mutation-keys'; import type { @@ -888,10 +1051,7 @@ import type { UserWithRelations, UserPatch, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; @@ -903,25 +1063,33 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useUpdateUserMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +export function useUpdateUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; +export function useUpdateUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; +export function useUpdateUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) }); queryClient.invalidateQueries({ queryKey: userKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -935,8 +1103,10 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { postKeys } from '../query-keys'; import { postMutationKeys } from '../mutation-keys'; import type { @@ -944,10 +1114,7 @@ import type { PostWithRelations, PostPatch, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { PostSelect, PostWithRelations, PostPatch } from '../../orm/input-types'; @@ -959,25 +1126,33 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useUpdatePostMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdatePostMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> +export function useUpdatePostMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> +): UseMutationResult<{ updatePost: { post: InferSelectResult } }, Error, { id: string; patch: PostPatch }>; +export function useUpdatePostMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> +): UseMutationResult<{ updatePost: { post: InferSelectResult } }, Error, { id: string; patch: PostPatch }>; +export function useUpdatePostMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: ({ id, patch }: { id: string; patch: PostPatch }) => getClient().post.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id, patch }: { id: string; patch: PostPatch }) => getClient().post.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: postKeys.detail(variables.id) }); queryClient.invalidateQueries({ queryKey: postKeys.lists() }); }, - ...options, + ...mutationOptions, }); } " @@ -991,17 +1166,16 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu */ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions } from '@tanstack/react-query'; +import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { UserSelect, UserWithRelations, UserPatch, } from '../../orm/input-types'; -import type { - DeepExact, - InferSelectResult, -} from '../../orm/select-types'; +import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; @@ -1013,24 +1187,32 @@ const defaultSelect = { id: true } as const; * @example * \`\`\`tsx * const { mutate, isPending } = useUpdateUserMutation({ - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation( - args?: { select?: DeepExact }, - options?: Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +export function useUpdateUserMutation( + params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; +export function useUpdateUserMutation( + params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> +): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; +export function useUpdateUserMutation( + params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> ) { + const args = buildSelectionArgs(params?.selection); + const { selection: _selection, ...mutationOptions } = params ?? {}; + void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(), + mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), onSuccess: (_, variables) => { queryClient.invalidateQueries({ queryKey: ['user', 'detail', variables.id] }); queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); }, - ...options, + ...mutationOptions, }); } " @@ -1044,8 +1226,10 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildListSelectionArgs } from '../selection'; +import type { ListSelectionConfig } from '../selection'; import { userKeys } from '../query-keys'; import type { UserSelect, @@ -1055,9 +1239,9 @@ import type { } from '../../orm/input-types'; import type { FindManyArgs, - DeepExact, InferSelectResult, ConnectionResult, + StrictSelect, } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; @@ -1073,21 +1257,32 @@ export const usersQueryKey = userKeys.list; * @example * \`\`\`tsx * const { data, isLoading } = useUsersQuery({ - * select: { id: true, name: true }, - * first: 10, - * where: { name: { equalTo: "example" } }, - * orderBy: ['CREATED_AT_DESC'], + * selection: { + * fields: { id: true, name: true }, + * where: { name: { equalTo: "example" } }, + * orderBy: ['CREATED_AT_DESC'], + * first: 10, + * }, * }); * \`\`\` */ -export function useUsersQuery( - args?: FindManyArgs, UserFilter, UsersOrderBy>, - options?: Omit> }, Error>, 'queryKey' | 'queryFn'> +export function useUsersQuery> }>( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUsersQuery> }>( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUsersQuery( + params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const selection = params?.selection; + const args = buildListSelectionArgs(selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany(args).unwrap(), - ...options, + queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -1096,13 +1291,25 @@ export function useUsersQuery * * @example * \`\`\`ts - * const data = await fetchUsersQuery({ first: 10, select: { id: true } }); + * const data = await fetchUsersQuery({ + * selection: { + * fields: { id: true }, + * first: 10, + * }, + * }); * \`\`\` */ -export async function fetchUsersQuery( - args?: FindManyArgs, UserFilter, UsersOrderBy>, +export async function fetchUsersQuery( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } +): Promise<{ users: ConnectionResult> }>; +export async function fetchUsersQuery( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } +): Promise<{ users: ConnectionResult> }>; +export async function fetchUsersQuery( + params?: { selection?: ListSelectionConfig } ) { - return getClient().user.findMany(args).unwrap(); + const args = buildListSelectionArgs(params?.selection); + return getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -1110,16 +1317,25 @@ export async function fetchUsersQuery( +export async function prefetchUsersQuery( + queryClient: QueryClient, + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } +): Promise; +export async function prefetchUsersQuery( queryClient: QueryClient, - args?: FindManyArgs, UserFilter, UsersOrderBy>, + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } +): Promise; +export async function prefetchUsersQuery( + queryClient: QueryClient, + params?: { selection?: ListSelectionConfig } ): Promise { + const args = buildListSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany(args).unwrap(), + queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -1133,8 +1349,10 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildListSelectionArgs } from '../selection'; +import type { ListSelectionConfig } from '../selection'; import { postKeys } from '../query-keys'; import type { PostScope } from '../query-keys'; import type { @@ -1145,9 +1363,9 @@ import type { } from '../../orm/input-types'; import type { FindManyArgs, - DeepExact, InferSelectResult, ConnectionResult, + StrictSelect, } from '../../orm/select-types'; export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from '../../orm/input-types'; @@ -1163,29 +1381,39 @@ export const postsQueryKey = postKeys.list; * @example * \`\`\`tsx * const { data, isLoading } = usePostsQuery({ - * select: { id: true, name: true }, - * first: 10, - * where: { name: { equalTo: "example" } }, - * orderBy: ['CREATED_AT_DESC'], + * selection: { + * fields: { id: true, name: true }, + * where: { name: { equalTo: "example" } }, + * orderBy: ['CREATED_AT_DESC'], + * first: 10, + * }, * }); * \`\`\` * * @example With scope for hierarchical cache invalidation * \`\`\`tsx - * const { data } = usePostsQuery( - * { first: 10 }, - * { scope: { parentId: 'parent-id' } } - * ); + * const { data } = usePostsQuery({ + * selection: { first: 10 }, + * scope: { parentId: 'parent-id' }, + * }); * \`\`\` */ -export function usePostsQuery( - args?: FindManyArgs, PostFilter, PostsOrderBy>, - options?: Omit> }, Error>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +export function usePostsQuery> }>( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +): UseQueryResult; +export function usePostsQuery> }>( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +): UseQueryResult; +export function usePostsQuery( + params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope } ) { - const { scope, ...queryOptions } = options ?? {}; + const selection = params?.selection; + const args = buildListSelectionArgs(selection); + const { scope, selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ queryKey: postKeys.list(args, scope), - queryFn: () => getClient().post.findMany(args).unwrap(), + queryFn: () => getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), ...queryOptions, }); } @@ -1195,13 +1423,25 @@ export function usePostsQuery * * @example * \`\`\`ts - * const data = await fetchPostsQuery({ first: 10, select: { id: true } }); + * const data = await fetchPostsQuery({ + * selection: { + * fields: { id: true }, + * first: 10, + * }, + * }); * \`\`\` */ -export async function fetchPostsQuery( - args?: FindManyArgs, PostFilter, PostsOrderBy>, +export async function fetchPostsQuery( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } +): Promise<{ posts: ConnectionResult> }>; +export async function fetchPostsQuery( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } +): Promise<{ posts: ConnectionResult> }>; +export async function fetchPostsQuery( + params?: { selection?: ListSelectionConfig } ) { - return getClient().post.findMany(args).unwrap(); + const args = buildListSelectionArgs(params?.selection); + return getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(); } /** @@ -1209,17 +1449,25 @@ export async function fetchPostsQuery( +export async function prefetchPostsQuery( + queryClient: QueryClient, + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & { scope?: PostScope } +): Promise; +export async function prefetchPostsQuery( queryClient: QueryClient, - args?: FindManyArgs, PostFilter, PostsOrderBy>, - scope?: PostScope, + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & { scope?: PostScope } +): Promise; +export async function prefetchPostsQuery( + queryClient: QueryClient, + params?: { selection?: ListSelectionConfig } & { scope?: PostScope } ): Promise { + const args = buildListSelectionArgs(params?.selection); await queryClient.prefetchQuery({ - queryKey: postKeys.list(args, scope), - queryFn: () => getClient().post.findMany(args).unwrap(), + queryKey: postKeys.list(args, params?.scope), + queryFn: () => getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), }); } " @@ -1233,8 +1481,10 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook w */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildListSelectionArgs } from '../selection'; +import type { ListSelectionConfig } from '../selection'; import type { UserSelect, UserWithRelations, @@ -1243,16 +1493,16 @@ import type { } from '../../orm/input-types'; import type { FindManyArgs, - DeepExact, InferSelectResult, ConnectionResult, + StrictSelect, } from '../../orm/select-types'; export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; const defaultSelect = { id: true } as const; -export const usersQueryKey = (variables?: FindManyArgs) => ['user', 'list', variables] as const; +export const usersQueryKey = (variables?: FindManyArgs) => ['user', 'list', variables] as const; /** * Query hook for fetching User list @@ -1260,21 +1510,32 @@ export const usersQueryKey = (variables?: FindManyArgs( - args?: FindManyArgs, UserFilter, UsersOrderBy>, - options?: Omit> }, Error>, 'queryKey' | 'queryFn'> +export function useUsersQuery> }>( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUsersQuery> }>( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUsersQuery( + params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const selection = params?.selection; + const args = buildListSelectionArgs(selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany(args).unwrap(), - ...options, + queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -1283,13 +1544,25 @@ export function useUsersQuery * * @example * \`\`\`ts - * const data = await fetchUsersQuery({ first: 10, select: { id: true } }); + * const data = await fetchUsersQuery({ + * selection: { + * fields: { id: true }, + * first: 10, + * }, + * }); * \`\`\` */ -export async function fetchUsersQuery( - args?: FindManyArgs, UserFilter, UsersOrderBy>, +export async function fetchUsersQuery( + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } +): Promise<{ users: ConnectionResult> }>; +export async function fetchUsersQuery( + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } +): Promise<{ users: ConnectionResult> }>; +export async function fetchUsersQuery( + params?: { selection?: ListSelectionConfig } ) { - return getClient().user.findMany(args).unwrap(); + const args = buildListSelectionArgs(params?.selection); + return getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -1297,16 +1570,25 @@ export async function fetchUsersQuery( +export async function prefetchUsersQuery( + queryClient: QueryClient, + params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } +): Promise; +export async function prefetchUsersQuery( + queryClient: QueryClient, + params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } +): Promise; +export async function prefetchUsersQuery( queryClient: QueryClient, - args?: FindManyArgs, UserFilter, UsersOrderBy>, + params?: { selection?: ListSelectionConfig } ): Promise { + const args = buildListSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany(args).unwrap(), + queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -1320,16 +1602,18 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { userKeys } from '../query-keys'; import type { UserSelect, UserWithRelations, } from '../../orm/input-types'; import type { - DeepExact, InferSelectResult, + StrictSelect, } from '../../orm/select-types'; export type { UserSelect, UserWithRelations } from '../../orm/input-types'; @@ -1346,18 +1630,26 @@ export const userQueryKey = userKeys.detail; * \`\`\`tsx * const { data, isLoading } = useUserQuery({ * id: 'some-id', - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * \`\`\` */ -export function useUserQuery( - args: { id: string; select?: DeepExact }, - options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> +export function useUserQuery | null }>( + params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUserQuery | null }>( + params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUserQuery( + params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const args = buildSelectionArgs(params.selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ - queryKey: userKeys.detail(args.id), - queryFn: () => getClient().user.findOne(args).unwrap(), - ...options, + queryKey: userKeys.detail(params.id), + queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -1366,13 +1658,23 @@ export function useUserQuery( * * @example * \`\`\`ts - * const data = await fetchUserQuery({ id: 'some-id', select: { id: true } }); + * const data = await fetchUserQuery({ + * id: 'some-id', + * selection: { fields: { id: true } }, + * }); * \`\`\` */ -export async function fetchUserQuery( - args: { id: string; select?: DeepExact }, +export async function fetchUserQuery( + params: { id: string; selection: ({ fields: S } & StrictSelect) } +): Promise<{ user: InferSelectResult | null }>; +export async function fetchUserQuery( + params: { id: string; selection?: ({ fields?: undefined }) }, +): Promise<{ user: InferSelectResult | null }>; +export async function fetchUserQuery( + params: { id: string; selection?: SelectionConfig }, ) { - return getClient().user.findOne(args).unwrap(); + const args = buildSelectionArgs(params.selection); + return getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -1383,13 +1685,22 @@ export async function fetchUserQuery( +export async function prefetchUserQuery( + queryClient: QueryClient, + params: { id: string; selection: ({ fields: S } & StrictSelect) }, +): Promise; +export async function prefetchUserQuery( queryClient: QueryClient, - args: { id: string; select?: DeepExact }, + params: { id: string; selection?: ({ fields?: undefined }) }, +): Promise; +export async function prefetchUserQuery( + queryClient: QueryClient, + params: { id: string; selection?: SelectionConfig }, ): Promise { + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ - queryKey: userKeys.detail(args.id), - queryFn: () => getClient().user.findOne(args).unwrap(), + queryKey: userKeys.detail(params.id), + queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " @@ -1403,8 +1714,10 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import { postKeys } from '../query-keys'; import type { PostScope } from '../query-keys'; import type { @@ -1412,8 +1725,8 @@ import type { PostWithRelations, } from '../../orm/input-types'; import type { - DeepExact, InferSelectResult, + StrictSelect, } from '../../orm/select-types'; export type { PostSelect, PostWithRelations } from '../../orm/input-types'; @@ -1430,26 +1743,33 @@ export const postQueryKey = postKeys.detail; * \`\`\`tsx * const { data, isLoading } = usePostQuery({ * id: 'some-id', - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * \`\`\` * * @example With scope for hierarchical cache invalidation * \`\`\`tsx - * const { data } = usePostQuery( - * { id: 'some-id' }, - * { scope: { parentId: 'parent-id' } } - * ); + * const { data } = usePostQuery({ + * id: 'some-id', + * scope: { parentId: 'parent-id' }, + * }); * \`\`\` */ -export function usePostQuery( - args: { id: string; select?: DeepExact }, - options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +export function usePostQuery | null }>( + params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +): UseQueryResult; +export function usePostQuery | null }>( + params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } +): UseQueryResult; +export function usePostQuery( + params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope } ) { - const { scope, ...queryOptions } = options ?? {}; + const args = buildSelectionArgs(params.selection); + const { scope, selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ - queryKey: postKeys.detail(args.id, scope), - queryFn: () => getClient().post.findOne(args).unwrap(), + queryKey: postKeys.detail(params.id, scope), + queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), ...queryOptions, }); } @@ -1459,13 +1779,23 @@ export function usePostQuery( * * @example * \`\`\`ts - * const data = await fetchPostQuery({ id: 'some-id', select: { id: true } }); + * const data = await fetchPostQuery({ + * id: 'some-id', + * selection: { fields: { id: true } }, + * }); * \`\`\` */ -export async function fetchPostQuery( - args: { id: string; select?: DeepExact }, +export async function fetchPostQuery( + params: { id: string; selection: ({ fields: S } & StrictSelect) } +): Promise<{ post: InferSelectResult | null }>; +export async function fetchPostQuery( + params: { id: string; selection?: ({ fields?: undefined }) }, +): Promise<{ post: InferSelectResult | null }>; +export async function fetchPostQuery( + params: { id: string; selection?: SelectionConfig }, ) { - return getClient().post.findOne(args).unwrap(); + const args = buildSelectionArgs(params.selection); + return getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(); } /** @@ -1476,14 +1806,22 @@ export async function fetchPostQuery( +export async function prefetchPostQuery( + queryClient: QueryClient, + params: { id: string; selection: ({ fields: S } & StrictSelect) } & { scope?: PostScope }, +): Promise; +export async function prefetchPostQuery( queryClient: QueryClient, - args: { id: string; select?: DeepExact }, - scope?: PostScope, + params: { id: string; selection?: ({ fields?: undefined }) } & { scope?: PostScope }, +): Promise; +export async function prefetchPostQuery( + queryClient: QueryClient, + params: { id: string; selection?: SelectionConfig } & { scope?: PostScope }, ): Promise { + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ - queryKey: postKeys.detail(args.id, scope), - queryFn: () => getClient().post.findOne(args).unwrap(), + queryKey: postKeys.detail(params.id, params.scope), + queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), }); } " @@ -1497,15 +1835,17 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho */ import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, QueryClient } from '@tanstack/react-query'; +import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; import { getClient } from '../client'; +import { buildSelectionArgs } from '../selection'; +import type { SelectionConfig } from '../selection'; import type { UserSelect, UserWithRelations, } from '../../orm/input-types'; import type { - DeepExact, InferSelectResult, + StrictSelect, } from '../../orm/select-types'; export type { UserSelect, UserWithRelations } from '../../orm/input-types'; @@ -1521,18 +1861,26 @@ export const userQueryKey = (id: string) => ['user', 'detail', id] as const; * \`\`\`tsx * const { data, isLoading } = useUserQuery({ * id: 'some-id', - * select: { id: true, name: true }, + * selection: { fields: { id: true, name: true } }, * }); * \`\`\` */ -export function useUserQuery( - args: { id: string; select?: DeepExact }, - options?: Omit | null }, Error>, 'queryKey' | 'queryFn'> +export function useUserQuery | null }>( + params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUserQuery | null }>( + params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> +): UseQueryResult; +export function useUserQuery( + params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> ) { + const args = buildSelectionArgs(params.selection); + const { selection: _selection, ...queryOptions } = params ?? {}; + void _selection; return useQuery({ - queryKey: userQueryKey(args.id), - queryFn: () => getClient().user.findOne(args).unwrap(), - ...options, + queryKey: userQueryKey(params.id), + queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + ...queryOptions, }); } @@ -1541,13 +1889,23 @@ export function useUserQuery( * * @example * \`\`\`ts - * const data = await fetchUserQuery({ id: 'some-id', select: { id: true } }); + * const data = await fetchUserQuery({ + * id: 'some-id', + * selection: { fields: { id: true } }, + * }); * \`\`\` */ -export async function fetchUserQuery( - args: { id: string; select?: DeepExact }, +export async function fetchUserQuery( + params: { id: string; selection: ({ fields: S } & StrictSelect) } +): Promise<{ user: InferSelectResult | null }>; +export async function fetchUserQuery( + params: { id: string; selection?: ({ fields?: undefined }) }, +): Promise<{ user: InferSelectResult | null }>; +export async function fetchUserQuery( + params: { id: string; selection?: SelectionConfig }, ) { - return getClient().user.findOne(args).unwrap(); + const args = buildSelectionArgs(params.selection); + return getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); } /** @@ -1558,13 +1916,22 @@ export async function fetchUserQuery( +export async function prefetchUserQuery( + queryClient: QueryClient, + params: { id: string; selection: ({ fields: S } & StrictSelect) }, +): Promise; +export async function prefetchUserQuery( + queryClient: QueryClient, + params: { id: string; selection?: ({ fields?: undefined }) }, +): Promise; +export async function prefetchUserQuery( queryClient: QueryClient, - args: { id: string; select?: DeepExact }, + params: { id: string; selection?: SelectionConfig }, ): Promise { + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ - queryKey: userQueryKey(args.id), - queryFn: () => getClient().user.findOne(args).unwrap(), + queryKey: userQueryKey(params.id), + queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), }); } " diff --git a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts index 7c0d3b599..3cd47be84 100644 --- a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts +++ b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts @@ -55,20 +55,55 @@ function addVariable( variables[spec.varName] = spec.value; } -function buildSelections(select: Record | undefined): FieldNode[] { +function buildSelections( + select: Record | undefined, + connectionFieldsMap?: Record>, + entityType?: string +): FieldNode[] { if (!select) return []; const fields: FieldNode[] = []; + const entityConnections = entityType ? connectionFieldsMap?.[entityType] : undefined; + for (const [key, value] of Object.entries(select)) { + if (value === false || value === undefined) continue; if (value === true) { fields.push(t.field({ name: key })); - } else if (value && typeof value === 'object' && 'select' in value) { - const nested = value as { select: Record }; - fields.push( - t.field({ - name: key, - selectionSet: t.selectionSet({ selections: buildSelections(nested.select) }) - }) - ); + continue; + } + if (typeof value === 'object' && value !== null) { + const nested = value as { + select?: Record; + first?: number; + filter?: Record; + connection?: boolean; + }; + if (nested.select) { + const relatedEntityType = entityConnections?.[key]; + const nestedSelections = buildSelections(nested.select, connectionFieldsMap, relatedEntityType); + const isConnection = + nested.connection === true || + nested.first !== undefined || + nested.filter !== undefined || + relatedEntityType !== undefined; + + if (isConnection) { + fields.push( + t.field({ + name: key, + selectionSet: t.selectionSet({ + selections: buildConnectionSelections(nestedSelections) + }) + }) + ); + } else { + fields.push( + t.field({ + name: key, + selectionSet: t.selectionSet({ selections: nestedSelections }) + }) + ); + } + } } } return fields; @@ -80,10 +115,11 @@ function buildFindManyDocument( select: TSelect, args: { where?: TWhere; first?: number; orderBy?: string[] }, filterTypeName: string, - orderByTypeName: string + orderByTypeName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; const queryArgs: ArgumentNode[] = []; @@ -175,6 +211,155 @@ describe('query-builder', () => { expect(result[2].name.value).toBe('profile'); expect(result[2].selectionSet?.selections).toHaveLength(1); }); + + it('wraps connection fields in nodes when connectionFieldsMap is provided', () => { + const connectionFieldsMap = { + User: { posts: 'Post', comments: 'Comment' } + }; + + const result = buildSelections( + { id: true, posts: { select: { id: true, title: true } } }, + connectionFieldsMap, + 'User' + ); + + expect(result).toHaveLength(2); + expect(result[0].name.value).toBe('id'); + expect(result[1].name.value).toBe('posts'); + // Should be wrapped in connection structure: nodes, totalCount, pageInfo + const postsSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(postsSelections).toHaveLength(3); + expect(postsSelections[0].name.value).toBe('nodes'); + expect(postsSelections[1].name.value).toBe('totalCount'); + expect(postsSelections[2].name.value).toBe('pageInfo'); + // nodes should contain the actual fields + const nodesSelections = postsSelections[0].selectionSet?.selections as FieldNode[]; + expect(nodesSelections).toHaveLength(2); + expect(nodesSelections[0].name.value).toBe('id'); + expect(nodesSelections[1].name.value).toBe('title'); + }); + + it('does not wrap singular relations in nodes', () => { + const connectionFieldsMap = { + Post: { comments: 'Comment' } + }; + + const result = buildSelections( + { id: true, author: { select: { id: true, name: true } } }, + connectionFieldsMap, + 'Post' + ); + + expect(result).toHaveLength(2); + expect(result[1].name.value).toBe('author'); + // author is NOT in connectionFieldsMap for Post → should NOT be wrapped + const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(authorSelections).toHaveLength(2); + expect(authorSelections[0].name.value).toBe('id'); + expect(authorSelections[1].name.value).toBe('name'); + }); + + it('handles deeply nested connections recursively', () => { + const connectionFieldsMap = { + User: { posts: 'Post' }, + Post: { comments: 'Comment' } + }; + + const result = buildSelections( + { + id: true, + posts: { + select: { + id: true, + comments: { select: { id: true, body: true } } + } + } + }, + connectionFieldsMap, + 'User' + ); + + // posts should be wrapped (User.posts is a connection) + const postsSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(postsSelections[0].name.value).toBe('nodes'); + + // Inside nodes, comments should also be wrapped (Post.comments is a connection) + const nodesFields = postsSelections[0].selectionSet?.selections as FieldNode[]; + const commentsField = nodesFields.find((f) => f.name.value === 'comments')!; + const commentsSelections = commentsField.selectionSet?.selections as FieldNode[]; + expect(commentsSelections[0].name.value).toBe('nodes'); + expect(commentsSelections[1].name.value).toBe('totalCount'); + expect(commentsSelections[2].name.value).toBe('pageInfo'); + }); + + it('works without connectionFieldsMap (backward compat)', () => { + const result = buildSelections({ + id: true, + posts: { select: { id: true } } + }); + + expect(result).toHaveLength(2); + expect(result[1].name.value).toBe('posts'); + // Without map, posts is NOT treated as a connection + const postsSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(postsSelections).toHaveLength(1); + expect(postsSelections[0].name.value).toBe('id'); + }); + + it('still wraps when first/filter provided even without connectionFieldsMap', () => { + const result = buildSelections({ + id: true, + posts: { select: { id: true }, first: 10 } + }); + + expect(result).toHaveLength(2); + const postsSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(postsSelections[0].name.value).toBe('nodes'); + expect(postsSelections[1].name.value).toBe('totalCount'); + }); + + it('handles mixed connection and singular relations on same entity', () => { + const connectionFieldsMap = { + Post: { comments: 'Comment' } + }; + + const result = buildSelections( + { + id: true, + author: { select: { id: true } }, + comments: { select: { id: true, body: true } } + }, + connectionFieldsMap, + 'Post' + ); + + expect(result).toHaveLength(3); + // author = singular → no wrapping + const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(authorSelections[0].name.value).toBe('id'); + + // comments = connection → wrapped + const commentsSelections = result[2].selectionSet?.selections as FieldNode[]; + expect(commentsSelections[0].name.value).toBe('nodes'); + expect(commentsSelections[1].name.value).toBe('totalCount'); + }); + + it('handles entity not in connectionFieldsMap gracefully', () => { + const connectionFieldsMap = { + User: { posts: 'Post' } + }; + + // Comment is not in the map — all nested fields should be singular + const result = buildSelections( + { id: true, author: { select: { id: true } } }, + connectionFieldsMap, + 'Comment' + ); + + expect(result).toHaveLength(2); + const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + expect(authorSelections[0].name.value).toBe('id'); + }); }); describe('buildFindManyDocument', () => { @@ -222,4 +407,140 @@ describe('query-builder', () => { expect(document).toContain('name'); }); }); + + // ========================================================================== + // Snapshot Tests — GraphQL Document Output + // ========================================================================== + + describe('snapshots', () => { + const connectionFieldsMap = { + User: { posts: 'Post', comments: 'Comment' }, + Post: { comments: 'Comment', tags: 'Tag' } + }; + + /** Helper: build a query document and print it for snapshotting */ + function buildQuerySnapshot( + select: Record, + map?: Record> + ): string { + const selections = buildSelections(select, map, 'User'); + const doc = t.document({ + definitions: [ + t.operationDefinition({ + operation: 'query', + name: 'TestQuery', + selectionSet: t.selectionSet({ + selections: [ + t.field({ + name: 'users', + selectionSet: t.selectionSet({ + selections: buildConnectionSelections(selections) + }) + }) + ] + }) + }) + ] + }); + return print(doc); + } + + it('findMany with nested connection fields', () => { + const document = buildQuerySnapshot( + { + id: true, + name: true, + posts: { select: { id: true, title: true, body: true } } + }, + connectionFieldsMap + ); + expect(document).toMatchSnapshot(); + }); + + it('findMany with deeply nested connections (3 levels)', () => { + const document = buildQuerySnapshot( + { + id: true, + posts: { + select: { + id: true, + title: true, + comments: { + select: { id: true, body: true } + } + } + } + }, + connectionFieldsMap + ); + expect(document).toMatchSnapshot(); + }); + + it('findMany with mixed connection and singular relations', () => { + const document = buildQuerySnapshot( + { + id: true, + name: true, + profile: { select: { bio: true, avatar: true } }, + posts: { select: { id: true, title: true } }, + comments: { select: { id: true, body: true } } + }, + connectionFieldsMap + ); + expect(document).toMatchSnapshot(); + }); + + it('findMany without connectionFieldsMap (no wrapping)', () => { + const document = buildQuerySnapshot( + { + id: true, + name: true, + posts: { select: { id: true, title: true } } + } + // no connectionFieldsMap + ); + expect(document).toMatchSnapshot(); + }); + + it('findMany document with filter and pagination', () => { + const { document } = buildFindManyDocument( + 'User', + 'users', + { id: true, name: true, email: true }, + { where: { name: { equalTo: 'test' } }, first: 25, orderBy: ['NAME_ASC', 'CREATED_AT_DESC'] }, + 'UserFilter', + 'UsersOrderBy' + ); + expect(document).toMatchSnapshot(); + }); + + it('mutation document', () => { + const document = buildMutationDocument( + 'CreateUser', + 'createUser', + 'user', + [t.field({ name: 'id' }), t.field({ name: 'name' }), t.field({ name: 'email' })], + 'CreateUserInput' + ); + expect(document).toMatchSnapshot(); + }); + + it('findMany document with nested connections via connectionFieldsMap', () => { + const { document } = buildFindManyDocument( + 'User', + 'users', + { + id: true, + name: true, + posts: { select: { id: true, title: true } }, + comments: { select: { id: true } } + }, + {}, + 'UserFilter', + 'UsersOrderBy', + connectionFieldsMap + ); + expect(document).toMatchSnapshot(); + }); + }); }); diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index ef4cc4b90..dc28b3ea7 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -101,8 +101,10 @@ function generateCustomMutationHookInternal( // Imports lines.push(`import { useMutation } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { customMutationKeys } from '../mutation-keys';`); @@ -130,7 +132,7 @@ function generateCustomMutationHookInternal( } if (hasSelect) { - lines.push(`import type { DeepExact, InferSelectResult } from '../../orm/select-types';`); + lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); } lines.push(''); @@ -153,17 +155,28 @@ function generateCustomMutationHookInternal( // Hook if (hasSelect) { - // With select: generic hook - const selectedResult = wrapInferSelectResult(operation.returnType, payloadTypeName); - const resultTypeStr = `{ ${operation.name}: ${selectedResult} }`; + // With selection.fields: overloaded hook for autocompletion + typed options/result const mutationVarType = hasArgs ? varTypeName : 'void'; - - const optionsType = `Omit, 'mutationFn'>`; - - lines.push(`export function ${hookName}(`); - lines.push(` args?: { select?: DeepExact },`); - lines.push(` options?: ${optionsType}`); + const selectedResultType = (s: string) => + `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; + const mutationOptionsType = (s: string) => + `Omit, 'mutationFn'>`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => `({ fields?: undefined })`; + + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${mutationOptionsType('S')}`); + lines.push(`): UseMutationResult<${selectedResultType('S')}, Error, ${mutationVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${mutationOptionsType('typeof defaultSelect')}`); + lines.push(`): UseMutationResult<${selectedResultType('typeof defaultSelect')}, Error, ${mutationVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useMutation({`); if (useCentralizedKeys) { @@ -171,12 +184,12 @@ function generateCustomMutationHookInternal( } if (hasArgs) { - lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); } else { - lines.push(` mutationFn: () => getClient().mutation.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` mutationFn: () => getClient().mutation.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); } - lines.push(` ...options,`); + lines.push(` ...mutationOptions,`); lines.push(` });`); lines.push(`}`); } else { @@ -186,9 +199,15 @@ function generateCustomMutationHookInternal( const optionsType = `Omit, 'mutationFn'>`; - lines.push(`export function ${hookName}(`); - lines.push(` options?: ${optionsType}`); + if (hasArgs) { + lines.push(`export function ${hookName}(`); + lines.push(` params?: ${optionsType}`); + } else { + lines.push(`export function ${hookName}(`); + lines.push(` params?: ${optionsType}`); + } lines.push(`) {`); + lines.push(` const mutationOptions = params ?? {};`); lines.push(` return useMutation({`); if (useCentralizedKeys) { @@ -201,7 +220,7 @@ function generateCustomMutationHookInternal( lines.push(` mutationFn: () => getClient().mutation.${operation.name}().unwrap(),`); } - lines.push(` ...options,`); + lines.push(` ...mutationOptions,`); lines.push(` });`); lines.push(`}`); } diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index 9c005c5e1..18d11d483 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -89,10 +89,12 @@ export function generateCustomQueryHook( // Imports if (reactQueryEnabled) { lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); } lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { customQueryKeys } from '../query-keys';`); @@ -124,7 +126,7 @@ export function generateCustomQueryHook( } if (hasSelect) { - lines.push(`import type { DeepExact, InferSelectResult } from '../../orm/select-types';`); + lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); } lines.push(''); @@ -178,54 +180,91 @@ export function generateCustomQueryHook( lines.push(` */`); if (hasSelect) { - // With select: generic hook - const selectedResult = wrapInferSelectResult(operation.returnType, payloadTypeName); - const resultTypeStr = `{ ${operation.name}: ${selectedResult} }`; - const paramsArr: string[] = []; + // With selection.fields: overloaded hook for autocompletion + const customSelectResultType = (s: string) => + `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; + const optionsType = (queryData: string, data: string) => + `Omit, 'queryKey' | 'queryFn'>`; + const withFieldsSelectionType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const withoutFieldsSelectionType = () => `({ fields?: undefined })`; + + // Overload 1: with selection.fields if (hasArgs) { - paramsArr.push(` variables${hasRequiredArgs ? '' : '?'}: ${varTypeName},`); + const varTypeStr = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; + lines.push(`export function ${hookName}(`); + lines.push(` params: { variables: ${varTypeStr}; selection: ${withFieldsSelectionType('S')} } & ${optionsType(customSelectResultType('S'), 'TData')}`); + lines.push(`): UseQueryResult;`); + } else { + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${withFieldsSelectionType('S')} } & ${optionsType(customSelectResultType('S'), 'TData')}`); + lines.push(`): UseQueryResult;`); } - paramsArr.push(` args?: { select?: DeepExact },`); - - const optionsType = `Omit, 'queryKey' | 'queryFn'>`; - paramsArr.push(` options?: ${optionsType}`); - lines.push(`export function ${hookName}(`); - lines.push(paramsArr.join('\n')); - lines.push(`) {`); - lines.push(` return useQuery({`); + // Overload 2: without selection.fields (uses default) + if (hasArgs) { + lines.push(`export function ${hookName}(`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} } & ${optionsType(customSelectResultType('typeof defaultSelect'), 'TData')}`); + lines.push(`): UseQueryResult;`); + } else { + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} } & ${optionsType(customSelectResultType('typeof defaultSelect'), 'TData')}`); + lines.push(`): UseQueryResult;`); + } + // Implementation if (hasArgs) { + lines.push(`export function ${hookName}(`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> } & Omit, 'queryKey' | 'queryFn'>`); + lines.push(`) {`); + lines.push(` const variables = params?.variables;`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { variables: _variables, selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _variables;`); + lines.push(` void _selection;`); + lines.push(` return useQuery({`); lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + if (hasRequiredArgs) { + lines.push(` enabled: !!variables && params?.enabled !== false,`); + } + lines.push(` ...queryOptions,`); + lines.push(` });`); + lines.push(`}`); } else { + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'queryKey' | 'queryFn'>`); + lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); + lines.push(` return useQuery({`); lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); - } - - if (hasRequiredArgs) { - lines.push(` enabled: !!variables && options?.enabled !== false,`); + lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + lines.push(` ...queryOptions,`); + lines.push(` });`); + lines.push(`}`); } - - lines.push(` ...options,`); - lines.push(` });`); - lines.push(`}`); } else { // Without select: simple hook (scalar return type) const resultTypeStr = `{ ${operation.name}: ${resultType} }`; - const paramsArr: string[] = []; + const optionsType = `Omit, 'queryKey' | 'queryFn'>`; + lines.push(`export function ${hookName}(`); + if (hasArgs) { + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} } & ${optionsType}`); + } else { + lines.push(` params?: ${optionsType}`); + } + lines.push(`): UseQueryResult {`); if (hasArgs) { - paramsArr.push(` variables${hasRequiredArgs ? '' : '?'}: ${varTypeName},`); + lines.push(` const variables = params?.variables;`); + lines.push(` const { variables: _variables, ...queryOptions } = params ?? {};`); + lines.push(` void _variables;`); + } else { + lines.push(` const queryOptions = params ?? {};`); } - - const optionsType = `Omit, 'queryKey' | 'queryFn'>`; - paramsArr.push(` options?: ${optionsType}`); - - lines.push(`export function ${hookName}(`); - lines.push(paramsArr.join('\n')); - lines.push(`) {`); lines.push(` return useQuery({`); if (hasArgs) { @@ -237,10 +276,10 @@ export function generateCustomQueryHook( } if (hasRequiredArgs) { - lines.push(` enabled: !!variables && options?.enabled !== false,`); + lines.push(` enabled: !!variables && params?.enabled !== false,`); } - lines.push(` ...options,`); + lines.push(` ...queryOptions,`); lines.push(` });`); lines.push(`}`); } @@ -263,36 +302,55 @@ export function generateCustomQueryHook( lines.push(` */`); if (hasSelect) { - const fetchParamsArr: string[] = []; - if (hasArgs) { - fetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); - } - fetchParamsArr.push(`args?: { select?: DeepExact }`); + const customSelectResultType = (s: string) => + `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; + const withFieldsSelectionType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const withoutFieldsSelectionType = () => `({ fields?: undefined })`; - lines.push(`export async function ${fetchFnName}(`); - lines.push(` ${fetchParamsArr.join(',\n ')}`); - lines.push(`) {`); if (hasArgs) { - lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap();`); + const requiredVarType = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; + + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params: { variables: ${requiredVarType}; selection: ${withFieldsSelectionType('S')} }`); + lines.push(`): Promise<${customSelectResultType('S')}>;`); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} }`); + lines.push(`): Promise<${customSelectResultType('typeof defaultSelect')}>;`); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> },`); + lines.push(`) {`); + lines.push(` const variables = params?.variables;`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); + lines.push(`}`); } else { - lines.push(` return getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap();`); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params: { selection: ${withFieldsSelectionType('S')} }`); + lines.push(`): Promise<${customSelectResultType('S')}>;`); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} },`); + lines.push(`): Promise<${customSelectResultType('typeof defaultSelect')}>;`); + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> },`); + lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` return getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); + lines.push(`}`); } - lines.push(`}`); } else { - const fetchParamsArr: string[] = []; - if (hasArgs) { - fetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); - } - - lines.push(`export async function ${fetchFnName}(`); - lines.push(` ${fetchParamsArr.join(',\n ')}`); - lines.push(`) {`); if (hasArgs) { + lines.push(`export async function ${fetchFnName}(`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} }`); + lines.push(`) {`); + lines.push(` const variables = params?.variables;`); lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap();`); + lines.push(`}`); } else { + lines.push(`export async function ${fetchFnName}() {`); lines.push(` return getClient().query.${operation.name}().unwrap();`); + lines.push(`}`); } - lines.push(`}`); } // Prefetch function @@ -315,52 +373,72 @@ export function generateCustomQueryHook( lines.push(` */`); if (hasSelect) { - const prefetchParamsArr: string[] = ['queryClient: QueryClient']; - if (hasArgs) { - prefetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); - } - prefetchParamsArr.push(`args?: { select?: DeepExact }`); - - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` ${prefetchParamsArr.join(',\n ')}`); - lines.push(`): Promise {`); + const withFieldsSelectionType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const withoutFieldsSelectionType = () => `({ fields?: undefined })`; if (hasArgs) { + const requiredVarType = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; + + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params: { variables: ${requiredVarType}; selection: ${withFieldsSelectionType('S')} }`); + lines.push(`): Promise;`); + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} }`); + lines.push(`): Promise;`); + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> }`); + lines.push(`): Promise {`); + lines.push(` const variables = params?.variables;`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); + lines.push(`}`); } else { + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params: { selection: ${withFieldsSelectionType('S')} }`); + lines.push(`): Promise;`); + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} }`); + lines.push(`): Promise;`); + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> }`); + lines.push(`): Promise {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); + lines.push(`}`); } - - lines.push(`}`); } else { - const prefetchParamsArr: string[] = ['queryClient: QueryClient']; - if (hasArgs) { - prefetchParamsArr.push(`variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}`); - } - - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` ${prefetchParamsArr.join(',\n ')}`); - lines.push(`): Promise {`); - if (hasArgs) { + lines.push(`export async function ${prefetchFnName}(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} }`); + lines.push(`): Promise {`); + lines.push(` const variables = params?.variables;`); lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${queryKeyName}(variables),`); lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap(),`); lines.push(` });`); + lines.push(`}`); } else { + lines.push(`export async function ${prefetchFnName}(queryClient: QueryClient): Promise {`); lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${queryKeyName}(),`); lines.push(` queryFn: () => getClient().query.${operation.name}().unwrap(),`); lines.push(` });`); + lines.push(`}`); } - - lines.push(`}`); } } diff --git a/graphql/codegen/src/core/codegen/index.ts b/graphql/codegen/src/core/codegen/index.ts index b77bad93d..57e75750f 100644 --- a/graphql/codegen/src/core/codegen/index.ts +++ b/graphql/codegen/src/core/codegen/index.ts @@ -44,6 +44,7 @@ import { generateMutationKeysFile } from './mutation-keys'; import { generateAllMutationHooks } from './mutations'; import { generateAllQueryHooks } from './queries'; import { generateQueryKeysFile } from './query-keys'; +import { generateSelectionFile } from './selection'; import { getTableNames } from './utils'; // ============================================================================ @@ -129,6 +130,12 @@ export function generate(options: GenerateOptions): GenerateResult { content: generateClientFile() }); + // 1b. Generate selection.ts (shared selection adapters for hooks) + files.push({ + path: 'selection.ts', + content: generateSelectionFile() + }); + // Collect table type names for import path resolution const tableTypeNames = new Set(tables.map((t) => getTableNames(t).typeName)); diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 8e368337e..078521e49 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -64,8 +64,10 @@ export function generateCreateMutationHook( // Imports lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { ${keysName} } from '../query-keys';`); @@ -77,10 +79,7 @@ export function generateCreateMutationHook( lines.push(` ${relationTypeName},`); lines.push(` ${createInputTypeName},`); lines.push(`} from '../../orm/input-types';`); - lines.push(`import type {`); - lines.push(` DeepExact,`); - lines.push(` InferSelectResult,`); - lines.push(`} from '../../orm/select-types';`); + lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); lines.push(''); // Re-export types @@ -97,16 +96,30 @@ export function generateCreateMutationHook( lines.push(` * @example`); lines.push(` * \`\`\`tsx`); lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * select: { id: true, name: true },`); + lines.push(` * selection: { fields: { id: true, name: true } },`); lines.push(` * });`); lines.push(` *`); lines.push(` * mutate({ name: 'New item' });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export function ${hookName}(`); - lines.push(` args?: { select?: DeepExact },`); - lines.push(` options?: Omit } }, Error, ${createInputTypeName}['${singularName}']>, 'mutationFn'>`); + const createResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; + const createVarType = `${createInputTypeName}['${singularName}']`; + const createOptionsType = (s: string) => `Omit, 'mutationFn'>`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => `({ fields?: undefined })`; + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${createOptionsType('S')}`); + lines.push(`): UseMutationResult<${createResultType('S')}, Error, ${createVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${createOptionsType('typeof defaultSelect')}`); + lines.push(`): UseMutationResult<${createResultType('typeof defaultSelect')}, Error, ${createVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` const queryClient = useQueryClient();`); lines.push(` return useMutation({`); @@ -114,7 +127,7 @@ export function generateCreateMutationHook( lines.push(` mutationKey: ${mutationKeysName}.create(),`); } - lines.push(` mutationFn: (data: ${createInputTypeName}['${singularName}']) => getClient().${singularName}.create({ data, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` mutationFn: (data: ${createInputTypeName}['${singularName}']) => getClient().${singularName}.create({ data, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); const listKey = useCentralizedKeys ? `${keysName}.lists()` @@ -122,7 +135,7 @@ export function generateCreateMutationHook( lines.push(` onSuccess: () => {`); lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); lines.push(` },`); - lines.push(` ...options,`); + lines.push(` ...mutationOptions,`); lines.push(` });`); lines.push(`}`); @@ -171,8 +184,10 @@ export function generateUpdateMutationHook( // Imports lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { ${keysName} } from '../query-keys';`); @@ -184,10 +199,7 @@ export function generateUpdateMutationHook( lines.push(` ${relationTypeName},`); lines.push(` ${patchTypeName},`); lines.push(`} from '../../orm/input-types';`); - lines.push(`import type {`); - lines.push(` DeepExact,`); - lines.push(` InferSelectResult,`); - lines.push(`} from '../../orm/select-types';`); + lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); lines.push(''); // Re-export types @@ -204,16 +216,30 @@ export function generateUpdateMutationHook( lines.push(` * @example`); lines.push(` * \`\`\`tsx`); lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * select: { id: true, name: true },`); + lines.push(` * selection: { fields: { id: true, name: true } },`); lines.push(` * });`); lines.push(` *`); lines.push(` * mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export function ${hookName}(`); - lines.push(` args?: { select?: DeepExact },`); - lines.push(` options?: Omit } }, Error, { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }>, 'mutationFn'>`); + const updateResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; + const updateVarType = `{ ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }`; + const updateOptionsType = (s: string) => `Omit, 'mutationFn'>`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => `({ fields?: undefined })`; + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${updateOptionsType('S')}`); + lines.push(`): UseMutationResult<${updateResultType('S')}, Error, ${updateVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${updateOptionsType('typeof defaultSelect')}`); + lines.push(`): UseMutationResult<${updateResultType('typeof defaultSelect')}, Error, ${updateVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` const queryClient = useQueryClient();`); lines.push(` return useMutation({`); @@ -221,7 +247,7 @@ export function generateUpdateMutationHook( lines.push(` mutationKey: ${mutationKeysName}.all,`); } - lines.push(` mutationFn: ({ ${pkField.name}, patch }: { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }) => getClient().${singularName}.update({ where: { ${pkField.name} }, data: patch, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` mutationFn: ({ ${pkField.name}, patch }: { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }) => getClient().${singularName}.update({ where: { ${pkField.name} }, data: patch, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); const detailKey = useCentralizedKeys ? `${keysName}.detail(variables.${pkField.name})` @@ -234,7 +260,7 @@ export function generateUpdateMutationHook( lines.push(` queryClient.invalidateQueries({ queryKey: ${detailKey} });`); lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); lines.push(` },`); - lines.push(` ...options,`); + lines.push(` ...mutationOptions,`); lines.push(` });`); lines.push(`}`); @@ -282,8 +308,10 @@ export function generateDeleteMutationHook( // Imports lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions } from '@tanstack/react-query';`); + lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { ${keysName} } from '../query-keys';`); @@ -294,10 +322,7 @@ export function generateDeleteMutationHook( lines.push(` ${selectTypeName},`); lines.push(` ${relationTypeName},`); lines.push(`} from '../../orm/input-types';`); - lines.push(`import type {`); - lines.push(` DeepExact,`); - lines.push(` InferSelectResult,`); - lines.push(`} from '../../orm/select-types';`); + lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); lines.push(''); // Re-export types @@ -314,16 +339,30 @@ export function generateDeleteMutationHook( lines.push(` * @example`); lines.push(` * \`\`\`tsx`); lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * select: { id: true },`); + lines.push(` * selection: { fields: { id: true } },`); lines.push(` * });`); lines.push(` *`); lines.push(` * mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export function ${hookName}(`); - lines.push(` args?: { select?: DeepExact },`); - lines.push(` options?: Omit } }, Error, { ${pkField.name}: ${pkField.tsType} }>, 'mutationFn'>`); + const deleteResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; + const deleteVarType = `{ ${pkField.name}: ${pkField.tsType} }`; + const deleteOptionsType = (s: string) => `Omit, 'mutationFn'>`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => `({ fields?: undefined })`; + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${deleteOptionsType('S')}`); + lines.push(`): UseMutationResult<${deleteResultType('S')}, Error, ${deleteVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${deleteOptionsType('typeof defaultSelect')}`); + lines.push(`): UseMutationResult<${deleteResultType('typeof defaultSelect')}, Error, ${deleteVarType}>;`); + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); + lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` const queryClient = useQueryClient();`); lines.push(` return useMutation({`); @@ -331,7 +370,7 @@ export function generateDeleteMutationHook( lines.push(` mutationKey: ${mutationKeysName}.all,`); } - lines.push(` mutationFn: ({ ${pkField.name} }: { ${pkField.name}: ${pkField.tsType} }) => getClient().${singularName}.delete({ where: { ${pkField.name} }, select: (args?.select ?? defaultSelect) as DeepExact }).unwrap(),`); + lines.push(` mutationFn: ({ ${pkField.name} }: { ${pkField.name}: ${pkField.tsType} }) => getClient().${singularName}.delete({ where: { ${pkField.name} }, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); const detailKey = useCentralizedKeys ? `${keysName}.detail(variables.${pkField.name})` @@ -344,7 +383,7 @@ export function generateDeleteMutationHook( lines.push(` queryClient.removeQueries({ queryKey: ${detailKey} });`); lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); lines.push(` },`); - lines.push(` ...options,`); + lines.push(` ...mutationOptions,`); lines.push(` });`); lines.push(`}`); diff --git a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts index e38554296..5fbcf5f26 100644 --- a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts @@ -267,26 +267,21 @@ function buildOperationMethod( const optionsParam = t.identifier('options'); optionsParam.optional = true; if (selectTypeName) { - // Use DeepExact to enforce strict field validation - // This catches invalid fields even when mixed with valid ones + const selectProp = t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))) + ); + selectProp.optional = false; optionsParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature( - t.identifier('select'), - t.tsTypeAnnotation( - t.tsTypeReference( - t.identifier('DeepExact'), - t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ]) - ) - ) - ); - prop.optional = true; - return prop; - })() + t.tsIntersectionType([ + t.tsTypeLiteral([selectProp]), + t.tsTypeReference( + t.identifier('StrictSelect'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier(selectTypeName)) + ]) + ) ]) ); } else { @@ -318,6 +313,9 @@ function buildOperationMethod( defaultSelectIdent ) : t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true); + const entityTypeExpr = selectTypeName && payloadTypeName + ? t.stringLiteral(payloadTypeName) + : t.identifier('undefined'); const queryBuilderArgs = t.objectExpression([ t.objectProperty(t.identifier('client'), t.identifier('client'), false, true), @@ -338,7 +336,9 @@ function buildOperationMethod( t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)) ]) ) - ) + ), + t.identifier('connectionFieldsMap'), + entityTypeExpr ]) ) ]); @@ -383,7 +383,6 @@ function buildOperationMethod( defaultType, 'S' ); - (typeParam as any).const = true; arrowFunc.typeParameters = t.tsTypeParameterDeclaration([typeParam]); } @@ -409,11 +408,12 @@ export function generateCustomQueryOpsFile( // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument'])); - statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true)); + statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true)); if (allTypeImports.length > 0) { statements.push(createImportDeclaration('../input-types', allTypeImports, true)); } + statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); // Generate variable interfaces for (const op of operations) { @@ -486,11 +486,12 @@ export function generateCustomMutationOpsFile( // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument'])); - statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'DeepExact'], true)); + statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true)); if (allTypeImports.length > 0) { statements.push(createImportDeclaration('../input-types', allTypeImports, true)); } + statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); // Generate variable interfaces for (const op of operations) { diff --git a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts index ee3468f4f..b4cb1bfe4 100644 --- a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts @@ -1596,6 +1596,90 @@ function generatePayloadTypes( return statements; } +// ============================================================================ +// Connection Fields Map Generator +// ============================================================================ + +/** + * Generate a runtime map of entity type → { fieldName: relatedTypeName } + * for all hasMany and manyToMany relations. Used by buildSelections() + * to detect connection fields that need `nodes { ... }` wrapping. + */ +function generateConnectionFieldsMap( + tables: CleanTable[], + tableByName: Map +): t.Statement[] { + const properties: t.ObjectProperty[] = []; + + for (const table of tables) { + const { typeName } = getTableNames(table); + const fieldEntries: t.ObjectProperty[] = []; + + for (const relation of table.relations.hasMany) { + if (!relation.fieldName) continue; + const relatedTypeName = getRelatedTypeName( + relation.referencedByTable, + tableByName + ); + fieldEntries.push( + t.objectProperty( + t.stringLiteral(relation.fieldName), + t.stringLiteral(relatedTypeName) + ) + ); + } + + for (const relation of table.relations.manyToMany) { + if (!relation.fieldName) continue; + const relatedTypeName = getRelatedTypeName( + relation.rightTable, + tableByName + ); + fieldEntries.push( + t.objectProperty( + t.stringLiteral(relation.fieldName), + t.stringLiteral(relatedTypeName) + ) + ); + } + + if (fieldEntries.length > 0) { + properties.push( + t.objectProperty( + t.stringLiteral(typeName), + t.objectExpression(fieldEntries) + ) + ); + } + } + + const decl = t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier('connectionFieldsMap'), + t.tsAsExpression( + t.objectExpression(properties), + t.tsTypeReference( + t.identifier('Record'), + t.tsTypeParameterInstantiation([ + t.tsStringKeyword(), + t.tsTypeReference( + t.identifier('Record'), + t.tsTypeParameterInstantiation([ + t.tsStringKeyword(), + t.tsStringKeyword() + ]) + ) + ]) + ) + ) + ) + ]); + + const statements: t.Statement[] = [t.exportNamedDeclaration(decl)]; + addSectionComment(statements, 'Connection Fields Map'); + return statements; +} + // ============================================================================ // Main Generator (AST-based) // ============================================================================ @@ -1610,39 +1694,44 @@ export function generateInputTypesFile( usedPayloadTypes?: Set ): GeneratedInputTypesFile { const statements: t.Statement[] = []; + const tablesList = tables ?? []; + const hasTables = tablesList.length > 0; + const tableByName = new Map(tablesList.map((table) => [table.name, table])); // 1. Scalar filter types statements.push(...generateScalarFilterTypes()); // 2. Enum types used by table fields - if (tables && tables.length > 0) { - const enumTypes = collectEnumTypesFromTables(tables, typeRegistry); + if (hasTables) { + const enumTypes = collectEnumTypesFromTables(tablesList, typeRegistry); statements.push(...generateEnumTypes(typeRegistry, enumTypes)); } // 3. Entity and relation types (if tables provided) - if (tables && tables.length > 0) { - const tableByName = new Map(tables.map((table) => [table.name, table])); - - statements.push(...generateEntityTypes(tables)); + if (hasTables) { + statements.push(...generateEntityTypes(tablesList)); statements.push(...generateRelationHelperTypes()); - statements.push(...generateEntityRelationTypes(tables, tableByName)); - statements.push(...generateEntityWithRelations(tables)); - statements.push(...generateEntitySelectTypes(tables, tableByName)); + statements.push(...generateEntityRelationTypes(tablesList, tableByName)); + statements.push(...generateEntityWithRelations(tablesList)); + statements.push(...generateEntitySelectTypes(tablesList, tableByName)); // 4. Table filter types - statements.push(...generateTableFilterTypes(tables)); + statements.push(...generateTableFilterTypes(tablesList)); // 4b. Table condition types (simple equality filter) - statements.push(...generateTableConditionTypes(tables)); + statements.push(...generateTableConditionTypes(tablesList)); // 5. OrderBy types - statements.push(...generateOrderByTypes(tables)); + statements.push(...generateOrderByTypes(tablesList)); // 6. CRUD input types - statements.push(...generateAllCrudInputTypes(tables)); + statements.push(...generateAllCrudInputTypes(tablesList)); } + // 6b. Connection fields map (runtime metadata for buildSelections) + // Always emit this export so generated model/custom-op imports stay valid. + statements.push(...generateConnectionFieldsMap(tablesList, tableByName)); + // 7. Custom input types from TypeRegistry const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined; statements.push( diff --git a/graphql/codegen/src/core/codegen/orm/model-generator.ts b/graphql/codegen/src/core/codegen/orm/model-generator.ts index 5b05c3e41..67f8a29a5 100644 --- a/graphql/codegen/src/core/codegen/orm/model-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/model-generator.ts @@ -2,6 +2,7 @@ * Model class generator for ORM client (Babel AST-based) * * Generates per-table model classes with findMany, findFirst, create, update, delete methods. + * Each method uses function overloads for IDE autocompletion of select objects. */ import * as t from '@babel/types'; @@ -84,7 +85,23 @@ function createClassMethod( return method; } -function createConstTypeParam( +function createDeclareMethod( + name: string, + typeParameters: t.TSTypeParameterDeclaration | null, + params: (t.Identifier | t.TSParameterProperty)[], + returnType: t.TSTypeAnnotation +): t.TSDeclareMethod { + const method = t.tsDeclareMethod( + null, + t.identifier(name), + typeParameters, + params, + returnType + ); + return method; +} + +function createTypeParam( constraintTypeName: string, defaultType?: t.TSType ): t.TSTypeParameterDeclaration { @@ -93,7 +110,6 @@ function createConstTypeParam( defaultType ?? null, 'S' ); - (param as any).const = true; return t.tsTypeParameterDeclaration([param]); } @@ -104,6 +120,51 @@ function tsTypeFromPrimitive(typeName: string): t.TSType { return t.tsTypeReference(t.identifier(typeName)); } +/** Build a required `select: S` property for overload signatures */ +function requiredSelectProp(): t.TSPropertySignature { + const prop = t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))) + ); + prop.optional = false; + return prop; +} + +/** Build an optional `select?: undefined` prop to forbid select in fallback overloads */ +function optionalUndefinedSelectProp(): t.TSPropertySignature { + const prop = t.tsPropertySignature( + t.identifier('select'), + t.tsTypeAnnotation(t.tsUndefinedKeyword()) + ); + prop.optional = true; + return prop; +} + +/** Build `StrictSelect` type reference for overload intersections */ +function strictSelectGuard(selectTypeName: string): t.TSType { + return t.tsTypeReference( + t.identifier('StrictSelect'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier('S')), + t.tsTypeReference(t.identifier(selectTypeName)) + ]) + ); +} + +/** Build `Omit & { select?: undefined }` for fallback overloads */ +function withoutSelect(argsType: t.TSType): t.TSType { + return t.tsIntersectionType([ + t.tsTypeReference( + t.identifier('Omit'), + t.tsTypeParameterInstantiation([ + argsType, + t.tsLiteralType(t.stringLiteral('select')) + ]) + ), + t.tsTypeLiteral([optionalUndefinedSelectProp()]) + ]); +} + export function generateModelFile( table: CleanTable, _useSharedTypes: boolean @@ -125,7 +186,6 @@ export function generateModelFile( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pkFieldTsType = tsTypeFromPrimitive(pkField.tsType); const defaultSelectIdent = t.identifier('defaultSelect'); const defaultSelectFieldName = getDefaultSelectFieldName(table); @@ -143,12 +203,13 @@ export function generateModelFile( ])); statements.push(createImportDeclaration('../select-types', [ 'ConnectionResult', 'FindManyArgs', 'FindFirstArgs', 'CreateArgs', - 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'DeepExact' + 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'StrictSelect' ], true)); statements.push(createImportDeclaration('../input-types', [ typeName, relationTypeName, selectTypeName, whereTypeName, orderByTypeName, createInputTypeName, updateInputTypeName, patchTypeName ], true)); + statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); // Default select (ensures valid GraphQL selection + sound TS return types) statements.push( @@ -173,309 +234,407 @@ export function generateModelFile( paramProp.accessibility = 'private'; classBody.push(t.classMethod('constructor', t.identifier('constructor'), [paramProp], t.blockStatement([]))); - // findMany method - // Use DeepExact to enforce strict field validation - const findManyParam = t.identifier('args'); - findManyParam.optional = true; - findManyParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ])), - t.tsTypeReference(t.identifier(whereTypeName)), - t.tsTypeReference(t.identifier(orderByTypeName)) - ])) - ); - const findManyReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('ConnectionResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ])) - ])) - )) + // Reusable type reference factories + const sRef = () => t.tsTypeReference(t.identifier('S')); + const defaultRef = () => t.tsTypeQuery(defaultSelectIdent); + const selectRef = () => t.tsTypeReference(t.identifier(selectTypeName)); + const pkTsType = () => tsTypeFromPrimitive(pkField.tsType); + + // ── findMany ─────────────────────────────────────────────────────────── + { + const argsType = (sel: t.TSType) => + t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([ + sel, + t.tsTypeReference(t.identifier(whereTypeName)), + t.tsTypeReference(t.identifier(orderByTypeName)) + ])); + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('ConnectionResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ])) + ])) + )) + ]) + ])) + ); + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + argsType(sRef()), + t.tsTypeLiteral([requiredSelectProp()]), + strictSelectGuard(selectTypeName) ]) - ])) - ); - const findManySelectExpr = t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - defaultSelectIdent - ); - const findManyArgs = [ - t.stringLiteral(typeName), - t.stringLiteral(pluralQueryName), - findManySelectExpr, - t.objectExpression([ - t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)), - t.objectProperty(t.identifier('orderBy'), t.tsAsExpression( - t.optionalMemberExpression(t.identifier('args'), t.identifier('orderBy'), false, true), - t.tsUnionType([t.tsArrayType(t.tsStringKeyword()), t.tsUndefinedKeyword()]) - )), - t.objectProperty(t.identifier('first'), t.optionalMemberExpression(t.identifier('args'), t.identifier('first'), false, true)), - t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)), - t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)), - t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)), - t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)) - ]), - t.stringLiteral(whereTypeName), - t.stringLiteral(orderByTypeName) - ]; - classBody.push(createClassMethod('findMany', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findManyParam], findManyReturnType, - buildMethodBody('buildFindManyDocument', findManyArgs, 'query', typeName, pluralQueryName))); - - // findFirst method - const findFirstParam = t.identifier('args'); - findFirstParam.optional = true; - findFirstParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ])), - t.tsTypeReference(t.identifier(whereTypeName)) - ])) - ); - const findFirstReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( + ); + classBody.push(createDeclareMethod('findMany', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.optional = true; + o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); + classBody.push(createDeclareMethod('findMany', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.optional = true; + implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); + const selectExpr = t.logicalExpression('??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + defaultSelectIdent); + const bodyArgs = [ + t.stringLiteral(typeName), + t.stringLiteral(pluralQueryName), + selectExpr, + t.objectExpression([ + t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)), + t.objectProperty(t.identifier('orderBy'), t.tsAsExpression( + t.optionalMemberExpression(t.identifier('args'), t.identifier('orderBy'), false, true), + t.tsUnionType([t.tsArrayType(t.tsStringKeyword()), t.tsUndefinedKeyword()]) + )), + t.objectProperty(t.identifier('first'), t.optionalMemberExpression(t.identifier('args'), t.identifier('first'), false, true)), + t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)), + t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)), + t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)), + t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)) + ]), + t.stringLiteral(whereTypeName), + t.stringLiteral(orderByTypeName), + t.identifier('connectionFieldsMap') + ]; + classBody.push(createClassMethod('findMany', null, [implParam], null, + buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName))); + } + + // ── findFirst ────────────────────────────────────────────────────────── + { + const argsType = (sel: t.TSType) => + t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([ + sel, + t.tsTypeReference(t.identifier(whereTypeName)) + ])); + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ]))) + t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation( + t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ]))) + )) + ]) )) ]) - )) + ])) + ); + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + argsType(sRef()), + t.tsTypeLiteral([requiredSelectProp()]), + strictSelectGuard(selectTypeName) ]) - ])) - ); - const findFirstSelectExpr = t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - defaultSelectIdent - ); - const findFirstArgs = [ - t.stringLiteral(typeName), - t.stringLiteral(pluralQueryName), - findFirstSelectExpr, - t.objectExpression([ - t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)) - ]), - t.stringLiteral(whereTypeName) - ]; - classBody.push(createClassMethod('findFirst', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findFirstParam], findFirstReturnType, - buildMethodBody('buildFindFirstDocument', findFirstArgs, 'query', typeName, pluralQueryName))); - - // findOne method (only if table has valid PK and singular query) + ); + classBody.push(createDeclareMethod('findFirst', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.optional = true; + o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); + classBody.push(createDeclareMethod('findFirst', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.optional = true; + implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); + const selectExpr = t.logicalExpression('??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + defaultSelectIdent); + const bodyArgs = [ + t.stringLiteral(typeName), + t.stringLiteral(pluralQueryName), + selectExpr, + t.objectExpression([ + t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)) + ]), + t.stringLiteral(whereTypeName), + t.identifier('connectionFieldsMap') + ]; + classBody.push(createClassMethod('findFirst', null, [implParam], null, + buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName))); + } + + // ── findOne ──────────────────────────────────────────────────────────── const singleQueryName = table.query?.one; if (singleQueryName && hasValidPrimaryKey(table)) { const pkGqlType = pkField.gqlType.replace(/!/g, '') + '!'; - const findOneParam = t.identifier('args'); - findOneParam.typeAnnotation = t.tsTypeAnnotation( + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(singleQueryName), t.tsTypeAnnotation( + t.tsUnionType([ + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ])), + t.tsNullKeyword() + ]) + )) + ]) + ])) + ); + + const pkProp = () => { + const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); + prop.optional = false; + return prop; + }; + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + t.tsTypeLiteral([pkProp(), requiredSelectProp()]), + strictSelectGuard(selectTypeName) + ]) + ); + classBody.push(createDeclareMethod('findOne', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeLiteral([pkProp()]) + ); + classBody.push(createDeclareMethod('findOne', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.typeAnnotation = t.tsTypeAnnotation( t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature( - t.identifier(pkField.name), - t.tsTypeAnnotation(pkFieldTsType) - ); - prop.optional = false; - return prop; - })(), + pkProp(), (() => { const prop = t.tsPropertySignature( t.identifier('select'), - t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ])) - ) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))) ); prop.optional = true; return prop; })() ]) ); - const findOneReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(singleQueryName), t.tsTypeAnnotation( - t.tsUnionType([ - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ])), - t.tsNullKeyword() - ]) - )) - ]) - ])) - ); - const findOneSelectExpr = t.logicalExpression( - '??', + const selectExpr = t.logicalExpression('??', t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent - ); - const findOneArgs = [ + defaultSelectIdent); + const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(singleQueryName), t.memberExpression(t.identifier('args'), t.identifier(pkField.name)), - findOneSelectExpr, + selectExpr, t.stringLiteral(pkField.name), - t.stringLiteral(pkGqlType) + t.stringLiteral(pkGqlType), + t.identifier('connectionFieldsMap') ]; - classBody.push(createClassMethod('findOne', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [findOneParam], findOneReturnType, - buildMethodBody('buildFindOneDocument', findOneArgs, 'query', typeName, singleQueryName))); + classBody.push(createClassMethod('findOne', null, [implParam], null, + buildMethodBody('buildFindOneDocument', bodyArgs, 'query', typeName, singleQueryName))); } - // create method - const createParam = t.identifier('args'); - createParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('CreateArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ])), - t.tsIndexedAccessType(t.tsTypeReference(t.identifier(createInputTypeName)), t.tsLiteralType(t.stringLiteral(singularName))) - ])) - ); - const createReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(createMutationName), t.tsTypeAnnotation( + // ── create ───────────────────────────────────────────────────────────── + { + const dataType = () => t.tsIndexedAccessType( + t.tsTypeReference(t.identifier(createInputTypeName)), + t.tsLiteralType(t.stringLiteral(singularName)) + ); + const argsType = (sel: t.TSType) => + t.tsTypeReference(t.identifier('CreateArgs'), t.tsTypeParameterInstantiation([sel, dataType()])); + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ])) + t.tsPropertySignature(t.identifier(createMutationName), t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ])) + )) + ]) )) ]) - )) + ])) + ); + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + argsType(sRef()), + t.tsTypeLiteral([requiredSelectProp()]), + strictSelectGuard(selectTypeName) ]) - ])) - ); - const createSelectExpr = t.logicalExpression( - '??', - t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent - ); - const createArgs = [ - t.stringLiteral(typeName), - t.stringLiteral(createMutationName), - t.stringLiteral(entityLower), - createSelectExpr, - t.memberExpression(t.identifier('args'), t.identifier('data')), - t.stringLiteral(createInputTypeName) - ]; - classBody.push(createClassMethod('create', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [createParam], createReturnType, - buildMethodBody('buildCreateDocument', createArgs, 'mutation', typeName, createMutationName))); - - // update method (if available) + ); + classBody.push(createDeclareMethod('create', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); + classBody.push(createDeclareMethod('create', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); + const selectExpr = t.logicalExpression('??', + t.memberExpression(t.identifier('args'), t.identifier('select')), + defaultSelectIdent); + const bodyArgs = [ + t.stringLiteral(typeName), + t.stringLiteral(createMutationName), + t.stringLiteral(entityLower), + selectExpr, + t.memberExpression(t.identifier('args'), t.identifier('data')), + t.stringLiteral(createInputTypeName), + t.identifier('connectionFieldsMap') + ]; + classBody.push(createClassMethod('create', null, [implParam], null, + buildMethodBody('buildCreateDocument', bodyArgs, 'mutation', typeName, createMutationName))); + } + + // ── update ───────────────────────────────────────────────────────────── if (updateMutationName) { - const updateParam = t.identifier('args'); - updateParam.typeAnnotation = t.tsTypeAnnotation( + const whereLiteral = () => t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); + prop.optional = false; + return prop; + })() + ]); + const argsType = (sel: t.TSType) => t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ])), - t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkFieldTsType)); - prop.optional = false; - return prop; - })() - ]), - t.tsTypeReference(t.identifier(patchTypeName)) - ])) - ); - const updateReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(updateMutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ])) - )) - ]) - )) - ]) - ])) + sel, whereLiteral(), t.tsTypeReference(t.identifier(patchTypeName)) + ])); + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(updateMutationName), t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ])) + )) + ]) + )) + ]) + ])) + ); + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + argsType(sRef()), + t.tsTypeLiteral([requiredSelectProp()]), + strictSelectGuard(selectTypeName) + ]) ); - const updateSelectExpr = t.logicalExpression( - '??', + classBody.push(createDeclareMethod('update', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); + classBody.push(createDeclareMethod('update', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); + const selectExpr = t.logicalExpression('??', t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent - ); - const updateArgs = [ + defaultSelectIdent); + const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(updateMutationName), t.stringLiteral(entityLower), - updateSelectExpr, + selectExpr, t.memberExpression( t.memberExpression(t.identifier('args'), t.identifier('where')), t.identifier(pkField.name) ), t.memberExpression(t.identifier('args'), t.identifier('data')), t.stringLiteral(updateInputTypeName), - t.stringLiteral(pkField.name) + t.stringLiteral(pkField.name), + t.identifier('connectionFieldsMap') ]; - classBody.push(createClassMethod('update', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [updateParam], updateReturnType, - buildMethodBody('buildUpdateByPkDocument', updateArgs, 'mutation', typeName, updateMutationName))); + classBody.push(createClassMethod('update', null, [implParam], null, + buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName))); } - // delete method (if available) - supports optional select for returning entity fields + // ── delete ───────────────────────────────────────────────────────────── if (deleteMutationName) { - const deleteParam = t.identifier('args'); - deleteParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkFieldTsType)); - prop.optional = false; - return prop; - })() - ]), - t.tsTypeReference(t.identifier('DeepExact'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) + const whereLiteral = () => t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); + prop.optional = false; + return prop; + })() + ]); + const argsType = (sel: t.TSType) => + t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([whereLiteral(), sel])); + const retType = (sel: t.TSType) => + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(deleteMutationName), t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel + ])) + )) + ]) + )) + ]) ])) - ])) - ); - const deleteReturnType = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(deleteMutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - t.tsTypeReference(t.identifier('S')) - ])) - )) - ]) - )) - ]) - ])) + ); + + // Overload 1: with select (autocompletion) + const o1Param = t.identifier('args'); + o1Param.typeAnnotation = t.tsTypeAnnotation( + t.tsIntersectionType([ + argsType(sRef()), + t.tsTypeLiteral([requiredSelectProp()]), + strictSelectGuard(selectTypeName) + ]) ); - const deleteSelectExpr = t.logicalExpression( - '??', + classBody.push(createDeclareMethod('delete', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); + + // Overload 2: without select (default) + const o2Param = t.identifier('args'); + o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); + classBody.push(createDeclareMethod('delete', null, [o2Param], retType(defaultRef()))); + + // Implementation + const implParam = t.identifier('args'); + implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); + const selectExpr = t.logicalExpression('??', t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent - ); - const deleteArgs = [ + defaultSelectIdent); + const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(deleteMutationName), t.stringLiteral(entityLower), @@ -485,10 +644,11 @@ export function generateModelFile( ), t.stringLiteral(deleteInputTypeName), t.stringLiteral(pkField.name), - deleteSelectExpr + selectExpr, + t.identifier('connectionFieldsMap') ]; - classBody.push(createClassMethod('delete', createConstTypeParam(selectTypeName, t.tsTypeQuery(defaultSelectIdent)), [deleteParam], deleteReturnType, - buildMethodBody('buildDeleteByPkDocument', deleteArgs, 'mutation', typeName, deleteMutationName))); + classBody.push(createClassMethod('delete', null, [implParam], null, + buildMethodBody('buildDeleteByPkDocument', bodyArgs, 'mutation', typeName, deleteMutationName))); } const classDecl = t.classDeclaration(t.identifier(modelName), null, t.classBody(classBody)); diff --git a/graphql/codegen/src/core/codegen/orm/select-types.ts b/graphql/codegen/src/core/codegen/orm/select-types.ts index 973b216c3..446a4b642 100644 --- a/graphql/codegen/src/core/codegen/orm/select-types.ts +++ b/graphql/codegen/src/core/codegen/orm/select-types.ts @@ -53,16 +53,10 @@ export interface NestedSelectConfig { /** * Recursively validates select objects, rejecting unknown keys. * - * This type ensures that users can only select fields that actually exist - * in the GraphQL schema. It returns `never` if any excess keys are found - * at any nesting level, causing a TypeScript compile error. - * - * Why this is needed: - * TypeScript's excess property checking has a quirk where it only catches - * invalid fields when they are the ONLY fields. When mixed with valid fields - * (e.g., `{ id: true, invalidField: true }`), the structural typing allows - * the excess property through. This type explicitly checks for and rejects - * such cases. + * NOTE: This type is intentionally NOT used in generated parameter positions + * (conditional types block IDE autocompletion). Parameters use `S` directly + * with `S extends XxxSelect` constraints, which provides full + * autocompletion via TypeScript's contextual typing. * * @example * // This will cause a type error because 'invalid' doesn't exist: @@ -79,15 +73,25 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Shape[K] extends { select?: infer ShapeNS } - ? { select: DeepExact> } - : T[K] + ? Extract extends { select?: infer ShapeNS } + ? DeepExact< + Omit & { select: DeepExact> }, + Extract + > + : never : T[K] : never; } : never : never; +/** + * Enforces exact select shape while keeping contextual typing on `S extends XxxSelect`. + * Use this as an intersection in overloads: + * `{ select: S } & StrictSelect`. + */ +export type StrictSelect = S extends DeepExact ? {} : never; + /** * Infers the result type from a select configuration * @@ -202,8 +206,9 @@ export interface UpdateArgs { /** * Arguments for delete operations */ -export interface DeleteArgs { +export interface DeleteArgs { where: TWhere; + select?: TSelect; } /** diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index 5e6d7285b..66146e42f 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -56,15 +56,23 @@ export function generateListQueryHook( const relationTypeName = `${typeName}WithRelations`; const defaultFieldName = getDefaultSelectFieldName(table); + const listResultType = (s: string) => `{ ${queryName}: ConnectionResult> }`; + const selectionType = (s: string) => `ListSelectionConfig<${s}, ${filterTypeName}, ${orderByTypeName}>`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & Omit<${selectionType(s)}, 'fields'> & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => + `(Omit<${selectionType(selectTypeName)}, 'fields'> & { fields?: undefined })`; const lines: string[] = []; // Imports if (reactQueryEnabled) { lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); } lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildListSelectionArgs } from '../selection';`); + lines.push(`import type { ListSelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { ${keysName} } from '../query-keys';`); @@ -81,9 +89,9 @@ export function generateListQueryHook( lines.push(`} from '../../orm/input-types';`); lines.push(`import type {`); lines.push(` FindManyArgs,`); - lines.push(` DeepExact,`); lines.push(` InferSelectResult,`); lines.push(` ConnectionResult,`); + lines.push(` StrictSelect,`); lines.push(`} from '../../orm/select-types';`); lines.push(''); @@ -99,7 +107,7 @@ export function generateListQueryHook( lines.push(`/** Query key factory - re-exported from query-keys.ts */`); lines.push(`export const ${queryName}QueryKey = ${keysName}.list;`); } else { - lines.push(`export const ${queryName}QueryKey = (variables?: FindManyArgs) => ['${typeName.toLowerCase()}', 'list', variables] as const;`); + lines.push(`export const ${queryName}QueryKey = (variables?: FindManyArgs) => ['${typeName.toLowerCase()}', 'list', variables] as const;`); } lines.push(''); @@ -112,10 +120,12 @@ export function generateListQueryHook( ` * @example`, ` * \`\`\`tsx`, ` * const { data, isLoading } = ${hookName}({`, - ` * select: { id: true, name: true },`, - ` * first: 10,`, - ` * where: { name: { equalTo: "example" } },`, - ` * orderBy: ['CREATED_AT_DESC'],`, + ` * selection: {`, + ` * fields: { id: true, name: true },`, + ` * where: { name: { equalTo: "example" } },`, + ` * orderBy: ['CREATED_AT_DESC'],`, + ` * first: 10,`, + ` * },`, ` * });`, ` * \`\`\`` ]; @@ -123,45 +133,63 @@ export function generateListQueryHook( docLines.push(` *`); docLines.push(` * @example With scope for hierarchical cache invalidation`); docLines.push(` * \`\`\`tsx`); - docLines.push(` * const { data } = ${hookName}(`); - docLines.push(` * { first: 10 },`); - docLines.push(` * { scope: { parentId: 'parent-id' } }`); - docLines.push(` * );`); + docLines.push(` * const { data } = ${hookName}({`); + docLines.push(` * selection: { first: 10 },`); + docLines.push(` * scope: { parentId: 'parent-id' },`); + docLines.push(` * });`); docLines.push(` * \`\`\``); } docLines.push(` */`); lines.push(...docLines); - let optionsType: string; - if (hasRelationships && useCentralizedKeys) { - optionsType = `Omit> }, Error>, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; - } else { - optionsType = `Omit> }, Error>, 'queryKey' | 'queryFn'>`; - } - - lines.push(`export function ${hookName}(`); - lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); - lines.push(` options?: ${optionsType}`); + const optionsType = (queryData: string, data: string) => + hasRelationships && useCentralizedKeys + ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` + : `Omit, 'queryKey' | 'queryFn'>`; + const implOptionsType = hasRelationships && useCentralizedKeys + ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` + : `Omit, 'queryKey' | 'queryFn'>`; + + // Overload 1: with selection.fields (autocompletion) + lines.push(`export function ${hookName}(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${optionsType(listResultType('S'), 'TData')}`); + lines.push(`): UseQueryResult;`); + + // Overload 2: no fields (default select) + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${optionsType(listResultType('typeof defaultSelect'), 'TData')}`); + lines.push(`): UseQueryResult;`); + + // Implementation + lines.push(`export function ${hookName}(`); + lines.push(` params?: { selection?: ${selectionType(selectTypeName)} } & ${implOptionsType}`); lines.push(`) {`); + lines.push(` const selection = params?.selection;`); + lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(selection);`); if (hasRelationships && useCentralizedKeys) { - lines.push(` const { scope, ...queryOptions } = options ?? {};`); + lines.push(` const { scope, selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); lines.push(` queryKey: ${keysName}.list(args, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` ...queryOptions,`); lines.push(` });`); } else if (useCentralizedKeys) { + lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); lines.push(` queryKey: ${keysName}.list(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); - lines.push(` ...options,`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + lines.push(` ...queryOptions,`); lines.push(` });`); } else { + lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); lines.push(` queryKey: ${queryName}QueryKey(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); - lines.push(` ...options,`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + lines.push(` ...queryOptions,`); lines.push(` });`); } @@ -175,13 +203,25 @@ export function generateListQueryHook( lines.push(` *`); lines.push(` * @example`); lines.push(` * \`\`\`ts`); - lines.push(` * const data = await fetch${ucFirst(pluralName)}Query({ first: 10, select: { id: true } });`); + lines.push(` * const data = await fetch${ucFirst(pluralName)}Query({`); + lines.push(` * selection: {`); + lines.push(` * fields: { id: true },`); + lines.push(` * first: 10,`); + lines.push(` * },`); + lines.push(` * });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); - lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); + lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); + lines.push(` params: { selection: ${selectionWithFieldsType('S')} }`); + lines.push(`): Promise<${listResultType('S')}>;`); + lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} }`); + lines.push(`): Promise<${listResultType('typeof defaultSelect')}>;`); + lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); + lines.push(` params?: { selection?: ${selectionType(selectTypeName)} }`); lines.push(`) {`); - lines.push(` return getClient().${singularName}.findMany(args).unwrap();`); + lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(params?.selection);`); + lines.push(` return getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); lines.push(`}`); lines.push(''); @@ -192,31 +232,37 @@ export function generateListQueryHook( lines.push(` *`); lines.push(` * @example`); lines.push(` * \`\`\`ts`); - lines.push(` * await prefetch${ucFirst(pluralName)}Query(queryClient, { first: 10 });`); + lines.push(` * await prefetch${ucFirst(pluralName)}Query(queryClient, { selection: { first: 10 } });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); + lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); lines.push(` queryClient: QueryClient,`); - lines.push(` args?: FindManyArgs, ${filterTypeName}, ${orderByTypeName}>,`); - if (hasRelationships && useCentralizedKeys) { - lines.push(` scope?: ${scopeTypeName},`); - } + lines.push(` params: { selection: ${selectionWithFieldsType('S')} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); + lines.push(`): Promise;`); + lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); + lines.push(`): Promise;`); + lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params?: { selection?: ${selectionType(selectTypeName)} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); lines.push(`): Promise {`); + lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(params?.selection);`); if (hasRelationships && useCentralizedKeys) { lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.list(args, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` queryKey: ${keysName}.list(args, params?.scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } else if (useCentralizedKeys) { lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${keysName}.list(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } else { lines.push(` await queryClient.prefetchQuery({`); lines.push(` queryKey: ${queryName}QueryKey(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany(args).unwrap(),`); + lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } @@ -260,15 +306,21 @@ export function generateSingleQueryHook( const pkFieldName = pkField?.name ?? 'id'; const pkFieldTsType = pkField?.tsType ?? 'string'; const defaultFieldName = getDefaultSelectFieldName(table); + const singleResultType = (s: string) => `{ ${queryName}: InferSelectResult<${relationTypeName}, ${s}> | null }`; + const selectionWithFieldsType = (s: string) => + `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; + const selectionWithoutFieldsType = () => `({ fields?: undefined })`; const lines: string[] = []; // Imports if (reactQueryEnabled) { lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, QueryClient } from '@tanstack/react-query';`); + lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); } lines.push(`import { getClient } from '../client';`); + lines.push(`import { buildSelectionArgs } from '../selection';`); + lines.push(`import type { SelectionConfig } from '../selection';`); if (useCentralizedKeys) { lines.push(`import { ${keysName} } from '../query-keys';`); @@ -282,8 +334,8 @@ export function generateSingleQueryHook( lines.push(` ${relationTypeName},`); lines.push(`} from '../../orm/input-types';`); lines.push(`import type {`); - lines.push(` DeepExact,`); lines.push(` InferSelectResult,`); + lines.push(` StrictSelect,`); lines.push(`} from '../../orm/select-types';`); lines.push(''); @@ -313,7 +365,7 @@ export function generateSingleQueryHook( ` * \`\`\`tsx`, ` * const { data, isLoading } = ${hookName}({`, ` * ${pkFieldName}: 'some-id',`, - ` * select: { id: true, name: true },`, + ` * selection: { fields: { id: true, name: true } },`, ` * });`, ` * \`\`\`` ]; @@ -321,45 +373,62 @@ export function generateSingleQueryHook( docLines.push(` *`); docLines.push(` * @example With scope for hierarchical cache invalidation`); docLines.push(` * \`\`\`tsx`); - docLines.push(` * const { data } = ${hookName}(`); - docLines.push(` * { ${pkFieldName}: 'some-id' },`); - docLines.push(` * { scope: { parentId: 'parent-id' } }`); - docLines.push(` * );`); + docLines.push(` * const { data } = ${hookName}({`); + docLines.push(` * ${pkFieldName}: 'some-id',`); + docLines.push(` * scope: { parentId: 'parent-id' },`); + docLines.push(` * });`); docLines.push(` * \`\`\``); } docLines.push(` */`); lines.push(...docLines); - let optionsType: string; - if (hasRelationships && useCentralizedKeys) { - optionsType = `Omit | null }, Error>, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }`; - } else { - optionsType = `Omit | null }, Error>, 'queryKey' | 'queryFn'>`; - } - - lines.push(`export function ${hookName}(`); - lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); - lines.push(` options?: ${optionsType}`); + const singleOptionsType = (queryData: string, data: string) => + hasRelationships && useCentralizedKeys + ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` + : `Omit, 'queryKey' | 'queryFn'>`; + const singleImplOptionsType = hasRelationships && useCentralizedKeys + ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` + : `Omit, 'queryKey' | 'queryFn'>`; + + // Overload 1: with selection.fields (provides contextual typing for autocompletion) + lines.push(`export function ${hookName}(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} } & ${singleOptionsType(singleResultType('S'), 'TData')}`); + lines.push(`): UseQueryResult;`); + + // Overload 2: without fields (uses default select) + lines.push(`export function ${hookName}(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} } & ${singleOptionsType(singleResultType('typeof defaultSelect'), 'TData')}`); + lines.push(`): UseQueryResult;`); + + // Implementation + lines.push(`export function ${hookName}(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> } & ${singleImplOptionsType}`); lines.push(`) {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); if (hasRelationships && useCentralizedKeys) { - lines.push(` const { scope, ...queryOptions } = options ?? {};`); + lines.push(` const { scope, selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}, scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` ...queryOptions,`); lines.push(` });`); } else if (useCentralizedKeys) { + lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); - lines.push(` ...options,`); + lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + lines.push(` ...queryOptions,`); lines.push(` });`); } else { + lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); + lines.push(` void _selection;`); lines.push(` return useQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(args.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); - lines.push(` ...options,`); + lines.push(` queryKey: ${queryName}QueryKey(params.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + lines.push(` ...queryOptions,`); lines.push(` });`); } @@ -373,13 +442,23 @@ export function generateSingleQueryHook( lines.push(` *`); lines.push(` * @example`); lines.push(` * \`\`\`ts`); - lines.push(` * const data = await fetch${ucFirst(singularName)}Query({ ${pkFieldName}: 'some-id', select: { id: true } });`); + lines.push(` * const data = await fetch${ucFirst(singularName)}Query({`); + lines.push(` * ${pkFieldName}: 'some-id',`); + lines.push(` * selection: { fields: { id: true } },`); + lines.push(` * });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export async function fetch${ucFirst(singularName)}Query(`); - lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); + lines.push(`export async function fetch${ucFirst(singularName)}Query(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} }`); + lines.push(`): Promise<${singleResultType('S')}>;`); + lines.push(`export async function fetch${ucFirst(singularName)}Query(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} },`); + lines.push(`): Promise<${singleResultType('typeof defaultSelect')}>;`); + lines.push(`export async function fetch${ucFirst(singularName)}Query(`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> },`); lines.push(`) {`); - lines.push(` return getClient().${singularName}.findOne(args).unwrap();`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); + lines.push(` return getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); lines.push(`}`); lines.push(''); @@ -393,28 +472,37 @@ export function generateSingleQueryHook( lines.push(` * await prefetch${ucFirst(singularName)}Query(queryClient, { ${pkFieldName}: 'some-id' });`); lines.push(` * \`\`\``); lines.push(` */`); - lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); + lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); lines.push(` queryClient: QueryClient,`); - lines.push(` args: { ${pkFieldName}: ${pkFieldTsType}; select?: DeepExact },`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); if (hasRelationships && useCentralizedKeys) { - lines.push(` scope?: ${scopeTypeName},`); + // scope is included in params above } + lines.push(`): Promise;`); + lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); + lines.push(`): Promise;`); + lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); + lines.push(` queryClient: QueryClient,`); + lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); lines.push(`): Promise {`); + lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); if (hasRelationships && useCentralizedKeys) { lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}, params.scope),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } else if (useCentralizedKeys) { lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.detail(args.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } else { lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(args.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne(args).unwrap(),`); + lines.push(` queryKey: ${queryName}QueryKey(params.${pkFieldName}),`); + lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); lines.push(` });`); } diff --git a/graphql/codegen/src/core/codegen/select-helpers.ts b/graphql/codegen/src/core/codegen/select-helpers.ts index d17a770e3..4ae52a351 100644 --- a/graphql/codegen/src/core/codegen/select-helpers.ts +++ b/graphql/codegen/src/core/codegen/select-helpers.ts @@ -37,17 +37,18 @@ export function getSelectTypeName(returnType: CleanArgument['type']): string | n */ export function wrapInferSelectResult( typeRef: CleanArgument['type'], - payloadTypeName: string + payloadTypeName: string, + selectType: string = 'S' ): string { if (typeRef.kind === 'NON_NULL' && typeRef.ofType) { - return wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName); + return wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName, selectType); } if (typeRef.kind === 'LIST' && typeRef.ofType) { - return `${wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName)}[]`; + return `${wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName, selectType)}[]`; } - return `InferSelectResult<${payloadTypeName}, S>`; + return `InferSelectResult<${payloadTypeName}, ${selectType}>`; } /** diff --git a/graphql/codegen/src/core/codegen/selection.ts b/graphql/codegen/src/core/codegen/selection.ts new file mode 100644 index 000000000..e205f3ca4 --- /dev/null +++ b/graphql/codegen/src/core/codegen/selection.ts @@ -0,0 +1,87 @@ +/** + * Selection helper generator for React Query hooks + * + * Generates selection.ts as a shared adapter layer between hook-facing + * `selection` params and ORM-facing args (`select`, `where`, `orderBy`, etc.). + */ +import { getGeneratedFileHeader } from './utils'; + +/** + * Generate selection.ts content - shared selection types + runtime mappers + */ +export function generateSelectionFile(): string { + const header = getGeneratedFileHeader('Selection helpers for React Query hooks'); + + const code = ` +export interface SelectionConfig { + fields?: TFields; +} + +export interface ListSelectionConfig + extends SelectionConfig { + where?: TWhere; + orderBy?: TOrderBy[]; + first?: number; + last?: number; + after?: string; + before?: string; + offset?: number; +} + +export function buildSelectionArgs( + selection?: SelectionConfig +): { select?: TFields } | undefined { + if (!selection || selection.fields === undefined) { + return undefined; + } + + return { select: selection.fields }; +} + +export function buildListSelectionArgs( + selection?: ListSelectionConfig +): + | { + select?: TFields; + where?: TWhere; + orderBy?: TOrderBy[]; + first?: number; + last?: number; + after?: string; + before?: string; + offset?: number; + } + | undefined { + if (!selection) { + return undefined; + } + + const hasAnyValues = + selection.fields !== undefined || + selection.where !== undefined || + selection.orderBy !== undefined || + selection.first !== undefined || + selection.last !== undefined || + selection.after !== undefined || + selection.before !== undefined || + selection.offset !== undefined; + + if (!hasAnyValues) { + return undefined; + } + + return { + select: selection.fields, + where: selection.where, + orderBy: selection.orderBy, + first: selection.first, + last: selection.last, + after: selection.after, + before: selection.before, + offset: selection.offset + }; +} +`; + + return header + '\n\n' + code.trim() + '\n'; +} diff --git a/graphql/codegen/src/core/codegen/templates/query-builder.ts b/graphql/codegen/src/core/codegen/templates/query-builder.ts index a678fbbd0..a62cdf075 100644 --- a/graphql/codegen/src/core/codegen/templates/query-builder.ts +++ b/graphql/codegen/src/core/codegen/templates/query-builder.ts @@ -96,13 +96,16 @@ export class QueryBuilder { // ============================================================================ export function buildSelections( - select: Record | undefined + select: Record | undefined, + connectionFieldsMap?: Record>, + entityType?: string ): FieldNode[] { if (!select) { return []; } const fields: FieldNode[] = []; + const entityConnections = entityType ? connectionFieldsMap?.[entityType] : undefined; for (const [key, value] of Object.entries(select)) { if (value === false || value === undefined) { @@ -124,11 +127,13 @@ export function buildSelections( }; if (nested.select) { - const nestedSelections = buildSelections(nested.select); + const relatedEntityType = entityConnections?.[key]; + const nestedSelections = buildSelections(nested.select, connectionFieldsMap, relatedEntityType); const isConnection = nested.connection === true || nested.first !== undefined || - nested.filter !== undefined; + nested.filter !== undefined || + relatedEntityType !== undefined; const args = buildArgs([ buildOptionalArg('first', nested.first), nested.filter @@ -181,10 +186,11 @@ export function buildFindManyDocument( offset?: number; }, filterTypeName: string, - orderByTypeName: string + orderByTypeName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; @@ -267,10 +273,11 @@ export function buildFindFirstDocument( queryField: string, select: TSelect, args: { where?: TWhere }, - filterTypeName: string + filterTypeName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; @@ -326,10 +333,11 @@ export function buildCreateDocument( entityField: string, select: TSelect, data: TData, - inputTypeName: string + inputTypeName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; return { @@ -359,10 +367,11 @@ export function buildUpdateDocument> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; return { @@ -394,10 +403,11 @@ export function buildUpdateByPkDocument( id: string | number, data: TData, inputTypeName: string, - idFieldName: string + idFieldName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; return { @@ -427,10 +437,11 @@ export function buildFindOneDocument( id: string | number, select: TSelect, idArgName: string, - idTypeName: string + idTypeName: string, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = [ @@ -478,10 +489,11 @@ export function buildDeleteDocument> ): { document: string; variables: Record } { const entitySelections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; return { @@ -513,10 +525,11 @@ export function buildDeleteByPkDocument( id: string | number, inputTypeName: string, idFieldName: string, - select?: TSelect + select?: TSelect, + connectionFieldsMap?: Record> ): { document: string; variables: Record } { const entitySelections = select - ? buildSelections(select as Record) + ? buildSelections(select as Record, connectionFieldsMap, operationName) : [t.field({ name: 'id' })]; return { @@ -545,7 +558,9 @@ export function buildCustomDocument( fieldName: string, select: TSelect, args: TArgs, - variableDefinitions: Array<{ name: string; type: string }> + variableDefinitions: Array<{ name: string; type: string }>, + connectionFieldsMap?: Record>, + entityType?: string ): { document: string; variables: Record } { let actualSelect = select; let isConnection = false; @@ -559,7 +574,7 @@ export function buildCustomDocument( } const selections = actualSelect - ? buildSelections(actualSelect as Record) + ? buildSelections(actualSelect as Record, connectionFieldsMap, entityType) : []; const variableDefs = variableDefinitions.map((definition) => diff --git a/graphql/codegen/src/core/codegen/templates/select-types.ts b/graphql/codegen/src/core/codegen/templates/select-types.ts index df14aac7b..5656e4901 100644 --- a/graphql/codegen/src/core/codegen/templates/select-types.ts +++ b/graphql/codegen/src/core/codegen/templates/select-types.ts @@ -64,16 +64,10 @@ export interface DeleteArgs { /** * Recursively validates select objects, rejecting unknown keys. * - * This type ensures that users can only select fields that actually exist - * in the GraphQL schema. It returns `never` if any excess keys are found - * at any nesting level, causing a TypeScript compile error. - * - * Why this is needed: - * TypeScript's excess property checking has a quirk where it only catches - * invalid fields when they are the ONLY fields. When mixed with valid fields - * (e.g., `{ id: true, invalidField: true }`), the structural typing allows - * the excess property through. This type explicitly checks for and rejects - * such cases. + * NOTE: This type is intentionally NOT used in generated parameter positions + * (conditional types block IDE autocompletion). Parameters use `S` directly + * with `S extends XxxSelect` constraints, which provides full + * autocompletion via TypeScript's contextual typing. * * @example * // This will cause a type error because 'invalid' doesn't exist: @@ -90,15 +84,25 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Shape[K] extends { select?: infer ShapeNS } - ? { select: DeepExact> } - : T[K] + ? Extract extends { select?: infer ShapeNS } + ? DeepExact< + Omit & { select: DeepExact> }, + Extract + > + : never : T[K] : never; } : never : never; +/** + * Enforces exact select shape while keeping contextual typing on `S extends XxxSelect`. + * Use this as an intersection in overloads: + * `{ select: S } & StrictSelect`. + */ +export type StrictSelect = S extends DeepExact ? {} : never; + /** * Infer result type from select configuration */ From 4fac2c18346c9cd24a6d6fb213b4ef29d21854e8 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Sat, 7 Feb 2026 14:33:01 +0700 Subject: [PATCH 03/23] fix(codegen-cli): honor source flags and config overrides --- graphql/codegen/src/cli/index.ts | 77 ++++++++++++++++++++-- graphql/codegen/src/cli/shared.ts | 2 +- graphql/codegen/src/core/generate.ts | 7 ++ graphql/codegen/src/types/config.ts | 7 +- packages/cli/__tests__/codegen.test.ts | 2 + packages/cli/src/commands/codegen.ts | 91 ++++++++++++++++++++++++-- 6 files changed, 168 insertions(+), 18 deletions(-) diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index 6698e2a38..ad499af48 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -10,7 +10,13 @@ import { CLI, CLIOptions, getPackageJson,Inquirerer } from 'inquirerer'; import { findConfigFile, loadConfigFile } from '../core/config'; import { generate } from '../core/generate'; import type { GraphQLSDKConfigTarget } from '../types/config'; -import { camelizeArgv, type CodegenAnswers,codegenQuestions, printResult } from './shared'; +import { + camelizeArgv, + type CodegenAnswers, + codegenQuestions, + printResult, + splitCommas +} from './shared'; const usage = ` graphql-codegen - GraphQL SDK generator for Constructive databases @@ -33,8 +39,7 @@ Generator Options: -o, --output Output directory -t, --target Target name (for multi-target configs) -a, --authorization Authorization header value - --browser-compatible Generate browser-compatible code (default: true) - Set to false for Node.js with localhost DNS fix + --browser-compatible Deprecated no-op (retained for compatibility) --dry-run Preview without writing files -v, --verbose Show detailed output @@ -58,15 +63,46 @@ export const commands = async ( process.exit(0); } - const configPath = (argv.config || argv.c || findConfigFile()) as string | undefined; + const hasSourceCliFlags = Boolean( + argv.endpoint || + argv.e || + argv['schema-file'] || + argv.s || + argv.schemas || + argv['api-names'] + ); + const explicitConfigPath = (argv.config || argv.c) as string | undefined; + const autoConfigPath = !explicitConfigPath && !hasSourceCliFlags + ? findConfigFile() + : undefined; + const configPath = (explicitConfigPath || autoConfigPath) as string | undefined; const targetName = (argv.target || argv.t) as string | undefined; // Collect CLI flags that should override config file settings const cliOverrides: Partial = {}; - if (argv['react-query'] === true) cliOverrides.reactQuery = true; + const endpoint = (argv.endpoint || argv.e) as string | undefined; + const schemaFile = (argv['schema-file'] || argv.s) as string | undefined; + const schemas = splitCommas(argv.schemas as string | undefined); + const apiNames = splitCommas(argv['api-names'] as string | undefined); + if (endpoint) { + cliOverrides.endpoint = endpoint; + cliOverrides.schemaFile = undefined; + cliOverrides.db = undefined; + } + if (schemaFile) { + cliOverrides.schemaFile = schemaFile; + cliOverrides.endpoint = undefined; + cliOverrides.db = undefined; + } + if (schemas || apiNames) { + cliOverrides.db = { schemas, apiNames }; + cliOverrides.endpoint = undefined; + cliOverrides.schemaFile = undefined; + } + if (argv['react-query'] === true || argv.reactQuery === true) cliOverrides.reactQuery = true; if (argv.orm === true) cliOverrides.orm = true; if (argv.verbose === true || argv.v === true) cliOverrides.verbose = true; - if (argv['dry-run'] === true) cliOverrides.dryRun = true; + if (argv['dry-run'] === true || argv.dryRun === true) cliOverrides.dryRun = true; if (argv.output || argv.o) cliOverrides.output = (argv.output || argv.o) as string; if (argv.authorization || argv.a) cliOverrides.authorization = (argv.authorization || argv.a) as string; @@ -114,6 +150,35 @@ export const commands = async ( return argv; } + const hasNonInteractiveArgs = Boolean( + endpoint || + schemaFile || + schemas || + apiNames || + argv['react-query'] === true || + argv.reactQuery === true || + argv.orm === true || + argv.output || + argv.o || + argv.authorization || + argv.a || + argv['dry-run'] === true || + argv.dryRun === true || + argv.verbose === true || + argv.v === true || + argv['browser-compatible'] !== undefined || + argv.browserCompatible !== undefined + ); + + if (hasNonInteractiveArgs) { + const result = await generate({ + ...cliOverrides + }); + printResult(result); + prompter.close(); + return argv; + } + // No config file - prompt for options using shared questions const answers = await prompter.prompt(argv as CodegenAnswers, codegenQuestions); diff --git a/graphql/codegen/src/cli/shared.ts b/graphql/codegen/src/cli/shared.ts index a1dee66a5..87a55bac9 100644 --- a/graphql/codegen/src/cli/shared.ts +++ b/graphql/codegen/src/cli/shared.ts @@ -92,7 +92,7 @@ export const codegenQuestions: Question[] = [ }, { name: 'browser-compatible', - message: 'Generate browser-compatible code?', + message: 'Browser-compatible mode (deprecated no-op)?', type: 'confirm', required: false, default: true, diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index e73b34edc..f3f63b948 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -43,6 +43,13 @@ export async function generate(options: GenerateOptions = {}): Promise { return { generate: jest.fn(async () => ({ success: true, message: 'Generated SDK', filesWritten: [] as string[] })), findConfigFile: jest.fn((): string | undefined => undefined), + loadConfigFile: jest.fn(async () => ({ success: false, error: 'not found' })), + splitCommas: splitCommasMock, codegenQuestions: [ { name: 'endpoint', message: 'GraphQL endpoint URL', type: 'text', required: false }, { name: 'schemaFile', message: 'Path to GraphQL schema file', type: 'text', required: false }, diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index 0d41bccae..d532b946f 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -2,10 +2,13 @@ import { CLIOptions, Inquirerer } from 'inquirerer'; import { generate, findConfigFile, + loadConfigFile, codegenQuestions, printResult, camelizeArgv, + splitCommas, type CodegenAnswers, + type GraphQLSDKConfigTarget, } from '@constructive-io/graphql-codegen'; const usage = ` @@ -27,8 +30,7 @@ Generator Options: --orm Generate ORM client --output Output directory (default: codegen) --authorization Authorization header value - --browser-compatible Generate browser-compatible code (default: true) - Set to false for Node.js with localhost DNS fix + --browser-compatible Deprecated no-op (retained for compatibility) --dry-run Preview without writing files --verbose Verbose output @@ -45,11 +47,86 @@ export default async ( process.exit(0); } - // Auto-detect config file if not provided - const config = argv.config || findConfigFile(); - if (config) { - // If config file exists, just run generate with it (config file handles everything) - const result = await generate({}); + const hasSourceCliFlags = Boolean( + argv.endpoint || + argv['schema-file'] || + argv.schemaFile || + argv.schemas || + argv['api-names'] || + argv.apiNames + ); + const explicitConfigPath = argv.config as string | undefined; + const autoConfigPath = !explicitConfigPath && !hasSourceCliFlags + ? findConfigFile() + : undefined; + const configPath = explicitConfigPath || autoConfigPath; + + const endpoint = argv.endpoint as string | undefined; + const schemaFile = (argv['schema-file'] || argv.schemaFile) as string | undefined; + const schemas = splitCommas(argv.schemas as string | undefined); + const apiNames = splitCommas((argv['api-names'] || argv.apiNames) as string | undefined); + + const cliOverrides: Partial = {}; + if (endpoint) { + cliOverrides.endpoint = endpoint; + cliOverrides.schemaFile = undefined; + cliOverrides.db = undefined; + } + if (schemaFile) { + cliOverrides.schemaFile = schemaFile; + cliOverrides.endpoint = undefined; + cliOverrides.db = undefined; + } + if (schemas || apiNames) { + cliOverrides.db = { schemas, apiNames }; + cliOverrides.endpoint = undefined; + cliOverrides.schemaFile = undefined; + } + if (argv['react-query'] === true || argv.reactQuery === true) cliOverrides.reactQuery = true; + if (argv.orm === true) cliOverrides.orm = true; + if (argv.verbose === true) cliOverrides.verbose = true; + if (argv['dry-run'] === true || argv.dryRun === true) cliOverrides.dryRun = true; + if (argv.output) cliOverrides.output = argv.output as string; + if (argv.authorization) cliOverrides.authorization = argv.authorization as string; + if (argv['browser-compatible'] !== undefined) { + cliOverrides.browserCompatible = argv['browser-compatible'] as boolean; + } else if (argv.browserCompatible !== undefined) { + cliOverrides.browserCompatible = argv.browserCompatible as boolean; + } + + if (configPath) { + const loaded = await loadConfigFile(configPath); + if (!loaded.success) { + console.error('x', loaded.error); + process.exit(1); + } + const result = await generate({ + ...(loaded.config as GraphQLSDKConfigTarget), + ...cliOverrides, + }); + printResult(result); + return; + } + + const hasNonInteractiveArgs = Boolean( + endpoint || + schemaFile || + schemas || + apiNames || + argv['react-query'] === true || + argv.reactQuery === true || + argv.orm === true || + argv.output || + argv.authorization || + argv['dry-run'] === true || + argv.dryRun === true || + argv.verbose === true || + argv['browser-compatible'] !== undefined || + argv.browserCompatible !== undefined + ); + + if (hasNonInteractiveArgs) { + const result = await generate({ ...cliOverrides }); printResult(result); return; } From 3639dc4286dc6b6aeb499c1c436186485960a05c Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Sat, 7 Feb 2026 14:35:08 +0700 Subject: [PATCH 04/23] feat(test-codegen-app): add local codegen validation app --- graphql/test-app/.gitignore | 1 + graphql/test-app/README.md | 62 + graphql/test-app/codegen.config.ts | 9 + graphql/test-app/index.html | 12 + graphql/test-app/package.json | 39 + graphql/test-app/scripts/analyze-bundle.ts | 155 + graphql/test-app/src/App.tsx | 1034 ++ graphql/test-app/src/main.tsx | 28 + .../react-query-helpers-options.type-test.ts | 266 + .../react-query-orm-overloads.type-test.ts | 306 + ...select-strictness-regressions.type-test.ts | 200 + graphql/test-app/src/vite-env.d.ts | 1 + graphql/test-app/tests/hook-test-utils.ts | 85 + graphql/test-app/tests/hooks.live.test.ts | 341 + graphql/test-app/tests/live-test-utils.ts | 130 + graphql/test-app/tests/orm.live.test.ts | 232 + graphql/test-app/tsconfig.json | 18 + graphql/test-app/vite.config.ts | 19 + pnpm-lock.yaml | 10233 +++++----------- 19 files changed, 6153 insertions(+), 7018 deletions(-) create mode 100644 graphql/test-app/.gitignore create mode 100644 graphql/test-app/README.md create mode 100644 graphql/test-app/codegen.config.ts create mode 100644 graphql/test-app/index.html create mode 100644 graphql/test-app/package.json create mode 100644 graphql/test-app/scripts/analyze-bundle.ts create mode 100644 graphql/test-app/src/App.tsx create mode 100644 graphql/test-app/src/main.tsx create mode 100644 graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts create mode 100644 graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts create mode 100644 graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts create mode 100644 graphql/test-app/src/vite-env.d.ts create mode 100644 graphql/test-app/tests/hook-test-utils.ts create mode 100644 graphql/test-app/tests/hooks.live.test.ts create mode 100644 graphql/test-app/tests/live-test-utils.ts create mode 100644 graphql/test-app/tests/orm.live.test.ts create mode 100644 graphql/test-app/tsconfig.json create mode 100644 graphql/test-app/vite.config.ts diff --git a/graphql/test-app/.gitignore b/graphql/test-app/.gitignore new file mode 100644 index 000000000..a39b67259 --- /dev/null +++ b/graphql/test-app/.gitignore @@ -0,0 +1 @@ +src/generated/* \ No newline at end of file diff --git a/graphql/test-app/README.md b/graphql/test-app/README.md new file mode 100644 index 000000000..fbb152e9d --- /dev/null +++ b/graphql/test-app/README.md @@ -0,0 +1,62 @@ +# test-codegen-app + +Local integration app for validating `@constructive-io/graphql-codegen` output against a real GraphQL endpoint. + +This package is intended for: +- Generating React Query + ORM output from the current workspace codegen implementation. +- Type-checking real generated artifacts. +- Running live integration checks with real auth credentials. + +This package is not part of CI test gating and is meant for local/manual verification. + +## What It Tests + +- React Query output mode generation and usage. +- ORM output mode generation and usage. +- Generated type fidelity in real app code (`tsc --noEmit`). +- Live endpoint behavior in `tests/*.test.ts`. + +## Local Usage + +Run from repo root. + +1. Generate from `codegen.config.ts`: + +```bash +pnpm --filter @constructive-io/test-codegen-app codegen +``` + +2. Type-check generated output: + +```bash +pnpm --filter @constructive-io/test-codegen-app typecheck +``` + +3. Build the app: + +```bash +pnpm --filter @constructive-io/test-codegen-app build +``` + +4. Run integration tests (non-live mode, skips credentialed tests): + +```bash +pnpm --filter @constructive-io/test-codegen-app test:integration +``` + +5. Run live integration tests: + +```bash +GRAPHQL_TEST_EMAIL="you@example.com" \ +GRAPHQL_TEST_PASSWORD="your-password" \ +pnpm --filter @constructive-io/test-codegen-app test:integration:live +``` + +Optional: +- `GRAPHQL_TEST_ENDPOINT` to override the default endpoint used by live tests. + +## Notes + +- `codegen` uses `graphql/test-app/codegen.config.ts` (currently pointed at the production endpoint for validation). +- `codegen:orm` is available for direct endpoint-driven ORM generation checks. +- Keep this app focused on realistic generated-API usage and regression coverage for codegen refactors. diff --git a/graphql/test-app/codegen.config.ts b/graphql/test-app/codegen.config.ts new file mode 100644 index 000000000..9daf7e78f --- /dev/null +++ b/graphql/test-app/codegen.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from '@constructive-io/graphql-codegen'; + +const config = defineConfig({ + endpoint: 'https://api.launchql.dev/graphql', + output: 'src/generated', + reactQuery: true, +}); + +export default config; diff --git a/graphql/test-app/index.html b/graphql/test-app/index.html new file mode 100644 index 000000000..43074d8dc --- /dev/null +++ b/graphql/test-app/index.html @@ -0,0 +1,12 @@ + + + + + + GraphQL Codegen Test App + + +
+ + + diff --git a/graphql/test-app/package.json b/graphql/test-app/package.json new file mode 100644 index 000000000..139ad00f0 --- /dev/null +++ b/graphql/test-app/package.json @@ -0,0 +1,39 @@ +{ + "name": "@constructive-io/test-codegen-app", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview", + "codegen": "tsx ../codegen/src/cli/index.ts --config codegen.config.ts", + "codegen:orm": "tsx ../codegen/src/cli/index.ts --endpoint http://api.localhost:3000/graphql --output src/generated --orm", + "typecheck": "tsc --noEmit", + "test:types": "tsc --noEmit", + "test:integration": "node --import tsx --test \"tests/**/*.test.ts\"", + "test:integration:live": "GRAPHQL_TEST_LIVE_REQUIRED=1 node --import tsx --test \"tests/**/*.test.ts\"", + "analyze": "tsx scripts/analyze-bundle.ts", + "analyze:visual": "vite build && echo '\nBundle report: dist/bundle-stats.html'" + }, + "dependencies": { + "@0no-co/graphql.web": "^1.2.0", + "@constructive-io/graphql-types": "workspace:^", + "@tanstack/react-query": "^5.90.20", + "gql-ast": "workspace:^", + "graphql": "15.10.1", + "react": "^19.2.3", + "react-dom": "^19.2.3" + }, + "devDependencies": { + "@constructive-io/graphql-codegen": "workspace:^", + "@types/react": "^19.2.13", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^4.5.2", + "react-test-renderer": "^19.2.3", + "rollup-plugin-visualizer": "^6.0.5", + "tsx": "^4.20.3", + "typescript": "^5.8.3", + "vite": "^6.3.5" + } +} diff --git a/graphql/test-app/scripts/analyze-bundle.ts b/graphql/test-app/scripts/analyze-bundle.ts new file mode 100644 index 000000000..7db8f509e --- /dev/null +++ b/graphql/test-app/scripts/analyze-bundle.ts @@ -0,0 +1,155 @@ +/** + * Bundle size analysis script + * + * Builds the app with Rollup's generateBundle hook to capture per-module sizes, + * then groups them by category (generated hooks, ORM, vendor libs, app code). + * + * Usage: tsx scripts/analyze-bundle.ts + */ +import { build } from 'vite'; +import react from '@vitejs/plugin-react'; +import { gzipSync, brotliCompressSync } from 'node:zlib'; + +interface ModuleInfo { + originalLength: number; + renderedLength: number; + removedExports: string[]; + code: string | null; +} + +interface ChunkInfo { + name: string; + size: number; + gzip: number; + brotli: number; + modules: Record; +} + +const chunks: ChunkInfo[] = []; + +const result = await build({ + configFile: false, + plugins: [ + react(), + { + name: 'capture-bundle-stats', + generateBundle(_options, bundle) { + for (const [fileName, chunk] of Object.entries(bundle)) { + if (chunk.type !== 'chunk') continue; + const code = Buffer.from(chunk.code); + chunks.push({ + name: fileName, + size: code.length, + gzip: gzipSync(code).length, + brotli: brotliCompressSync(code).length, + modules: chunk.modules as Record, + }); + } + }, + }, + ], + build: { + write: false, + minify: 'esbuild', + }, + logLevel: 'silent', +}); + +// Categorize modules +interface Category { + label: string; + test: (id: string) => boolean; +} + +const categories: Category[] = [ + { label: 'Generated Hooks', test: (id) => id.includes('generated/hooks') }, + { label: 'Generated ORM', test: (id) => id.includes('generated/orm') }, + { label: 'Generated Types', test: (id) => id.includes('generated/') && (id.includes('types.ts') || id.includes('schema-types')) }, + { label: 'React', test: (id) => /node_modules\/(react|react-dom|scheduler)\//.test(id) }, + { label: 'React Query', test: (id) => id.includes('@tanstack/react-query') }, + { label: 'GraphQL (graphql.web + gql-ast)', test: (id) => id.includes('@0no-co/graphql.web') || id.includes('gql-ast') }, + { label: 'GraphQL (graphql 15)', test: (id) => id.includes('node_modules/graphql') }, + { label: 'App Code', test: (id) => !id.includes('node_modules') && !id.includes('generated/') }, +]; + +const catSizes: Record = {}; +let uncategorized = 0; + +for (const chunk of chunks) { + for (const [moduleId, info] of Object.entries(chunk.modules)) { + let matched = false; + for (const cat of categories) { + if (cat.test(moduleId)) { + if (!catSizes[cat.label]) catSizes[cat.label] = { original: 0, rendered: 0 }; + catSizes[cat.label].original += info.originalLength; + catSizes[cat.label].rendered += info.renderedLength; + matched = true; + break; + } + } + if (!matched) { + uncategorized += info.renderedLength; + } + } +} + +// Output +console.log('\n========================================'); +console.log(' BUNDLE SIZE ANALYSIS'); +console.log('========================================\n'); + +const totalChunk = chunks[0]; +if (totalChunk) { + const fmt = (n: number) => (n / 1024).toFixed(1) + ' KB'; + console.log(`Total bundle: ${fmt(totalChunk.size)} minified | ${fmt(totalChunk.gzip)} gzip | ${fmt(totalChunk.brotli)} brotli\n`); +} + +console.log('By category (rendered/minified size in bundle):'); +console.log('─'.repeat(60)); + +const sorted = Object.entries(catSizes).sort((a, b) => b[1].rendered - a[1].rendered); +const totalRendered = sorted.reduce((sum, [, v]) => sum + v.rendered, 0) + uncategorized; + +for (const [label, { original, rendered }] of sorted) { + const pct = ((rendered / totalRendered) * 100).toFixed(1); + const pad = label.padEnd(35); + const renderedKB = (rendered / 1024).toFixed(1).padStart(8); + const originalKB = (original / 1024).toFixed(1).padStart(8); + console.log(` ${pad} ${renderedKB} KB (${pct.padStart(5)}%) source: ${originalKB} KB`); +} + +if (uncategorized > 0) { + const pct = ((uncategorized / totalRendered) * 100).toFixed(1); + console.log(` ${'Other'.padEnd(35)} ${(uncategorized / 1024).toFixed(1).padStart(8)} KB (${pct.padStart(5)}%)`); +} + +console.log('─'.repeat(60)); +console.log(` ${'TOTAL'.padEnd(35)} ${(totalRendered / 1024).toFixed(1).padStart(8)} KB\n`); + +// Tree-shaking effectiveness +const hooksSource = catSizes['Generated Hooks']; +const ormSource = catSizes['Generated ORM']; +if (hooksSource || ormSource) { + console.log('Tree-shaking effectiveness:'); + console.log('─'.repeat(60)); + if (hooksSource) { + const ratio = ((1 - hooksSource.rendered / hooksSource.original) * 100).toFixed(1); + console.log(` Hooks: ${(hooksSource.original / 1024).toFixed(0)} KB source → ${(hooksSource.rendered / 1024).toFixed(1)} KB in bundle (${ratio}% eliminated)`); + } + if (ormSource) { + const ratio = ((1 - ormSource.rendered / ormSource.original) * 100).toFixed(1); + console.log(` ORM: ${(ormSource.original / 1024).toFixed(0)} KB source → ${(ormSource.rendered / 1024).toFixed(1)} KB in bundle (${ratio}% eliminated)`); + } + console.log(''); +} + +// Module count +let totalModules = 0; +let generatedModules = 0; +for (const chunk of chunks) { + for (const moduleId of Object.keys(chunk.modules)) { + totalModules++; + if (moduleId.includes('generated/')) generatedModules++; + } +} +console.log(`Modules: ${totalModules} total, ${generatedModules} generated (${(totalModules - generatedModules)} vendor/app)\n`); diff --git a/graphql/test-app/src/App.tsx b/graphql/test-app/src/App.tsx new file mode 100644 index 000000000..2c4498806 --- /dev/null +++ b/graphql/test-app/src/App.tsx @@ -0,0 +1,1034 @@ +/** + * Test App for graphql-codegen hooks + * + * Tests the full auth flow (signUp, signIn, signOut) and then + * exercises authenticated queries/mutations with type-safe select. + * + * Run `pnpm codegen` first to generate the hooks from the live API. + */ + +import { useState, FormEvent } from 'react'; +import { useQueryClient } from '@tanstack/react-query'; + +import { + // Client configuration + configure, + + // Auth mutation hooks + useSignUpMutation, + useSignInMutation, + useSignOutMutation, + + // Authenticated query hooks + useCurrentUserQuery, + useCurrentUserIdQuery, + useUsersQuery, + useDatabasesQuery, + useDatabaseQuery, + useSchemasQuery, + useApisQuery, + useDomainsQuery, + + // Authenticated mutation hooks — Users + useCreateUserMutation, + useUpdateUserMutation, + + // Authenticated mutation hooks — Databases + useCreateDatabaseMutation, + useUpdateDatabaseMutation, + useDeleteDatabaseMutation, + + // Authenticated mutation hooks — Schemas, APIs, Sites, Domains + useCreateSchemaMutation, + useCreateApiMutation, + useCreateSiteMutation, + useCreateDomainMutation, +} from './generated/hooks'; + +const ENDPOINT = 'http://api.localhost:3000/graphql'; + +// --------------------------------------------------------------------------- +// Auth helpers +// --------------------------------------------------------------------------- + +function reconfigureClient(token?: string) { + configure({ + endpoint: ENDPOINT, + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); +} + +// --------------------------------------------------------------------------- +// Sign Up Form +// --------------------------------------------------------------------------- +function SignUpForm({ onAuth }: { onAuth: (token: string, userId: string) => void }) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const { mutate, isPending, error } = useSignUpMutation({ + selection: { + fields: { + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + }, + }, + }, + }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + mutate( + { input: { email, password } }, + { + onSuccess: (data) => { + const result = data.signUp.result; + if (result?.accessToken) { + onAuth(result.accessToken, result.userId ?? ''); + } + }, + }, + ); + }; + + return ( +
+

Sign Up

+ setEmail(e.target.value)} required style={inputStyle} /> + setPassword(e.target.value)} required style={inputStyle} /> + + {error &&

{error.message}

} +
+ ); +} + +// --------------------------------------------------------------------------- +// Sign In Form +// --------------------------------------------------------------------------- +function SignInForm({ onAuth }: { onAuth: (token: string, userId: string) => void }) { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const { mutate, isPending, error } = useSignInMutation({ + selection: { + fields: { + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + totpEnabled: true, + }, + }, + }, + }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + mutate( + { input: { email, password } }, + { + onSuccess: (data) => { + // AUTOCOMPLETION TEST: type `data.signIn.result.` — should suggest accessToken, userId, isVerified, totpEnabled + const result = data.signIn.result; + if (result?.accessToken) { + onAuth(result.accessToken, result.userId ?? ''); + } + }, + }, + ); + }; + + return ( +
+

Sign In

+ setEmail(e.target.value)} required style={inputStyle} /> + setPassword(e.target.value)} required style={inputStyle} /> + + {error &&

{error.message}

} +
+ ); +} + +// --------------------------------------------------------------------------- +// Auth Page (unauthenticated) +// --------------------------------------------------------------------------- +function AuthPage({ onAuth }: { onAuth: (token: string, userId: string) => void }) { + const [mode, setMode] = useState<'signin' | 'signup'>('signin'); + + return ( +
+
+ + +
+ {mode === 'signin' ? : } +
+ ); +} + +// =========================================================================== +// SECTION: Identity & Profile +// =========================================================================== + +function CurrentUser() { + const { data, isLoading, error } = useCurrentUserQuery({ + selection: { fields: { id: true, username: true, displayName: true, createdAt: true } }, + }); + + if (isLoading) return

Loading current user...

; + if (error) return

Error: {error.message}

; + + // AUTOCOMPLETION TEST: type `data?.currentUser.` — should suggest id, username, displayName, createdAt + const user = data?.currentUser; + + return ( +
+

Current User

+ {user ? ( +
+
ID
{user.id}
+
Username
{user.username ?? '—'}
+
Display Name
{user.displayName ?? '—'}
+
Created At
{user.createdAt}
+
+ ) : ( +

No user found

+ )} +
+ ); +} + +function CurrentUserId() { + const { data, isLoading } = useCurrentUserIdQuery(); + if (isLoading) return

Loading...

; + + return ( +
+

Current User ID (scalar)

+ {data?.currentUserId ?? 'null'} +
+ ); +} + +function UserList() { + const { data, isLoading, error } = useUsersQuery({ + selection: { + fields: { id: true, username: true, displayName: true, createdAt: true }, + first: 10, + orderBy: ['CREATED_AT_DESC'], + }, + }); + + if (isLoading) return

Loading users...

; + if (error) return

Error: {error.message}

; + + const users = data?.users?.nodes ?? []; + + return ( +
+

Users ({users.length})

+ [user.username, user.displayName, user.createdAt]} + /> +
+ ); +} + +function CreateUserForm() { + const [username, setUsername] = useState(''); + const [displayName, setDisplayName] = useState(''); + const { mutate, isPending, error, data: result } = useCreateUserMutation({ + selection: { fields: { id: true, username: true, displayName: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + mutate({ username, displayName }); + }; + + return ( +
+

Create User

+
+ setUsername(e.target.value)} required style={inputStyle} /> + setDisplayName(e.target.value)} style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createUser.user, null, 2)}
} +
+ ); +} + +function UpdateUserForm({ userId }: { userId: string }) { + const [displayName, setDisplayName] = useState(''); + const { mutate, isPending, error, data: result } = useUpdateUserMutation({ + selection: { fields: { id: true, username: true, displayName: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + mutate({ id: userId, patch: { displayName } }); + }; + + return ( +
+

Update Current User

+
+ setDisplayName(e.target.value)} required style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.updateUser.user, null, 2)}
} +
+ ); +} + +// =========================================================================== +// SECTION: Database Management (full CRUD) +// =========================================================================== + +function DatabaseListWithSchemas() { + const { data, isLoading, error, refetch } = useDatabasesQuery({ + selection: { + fields: { + id: true, + name: true, + label: true, + schemaName: true, + createdAt: true, + schemas: { + first: 20, + filter: { + isPublic: { equalTo: true }, + schemaName: { startsWithInsensitive: 'app_' }, + }, + orderBy: ['SCHEMA_NAME_ASC'], + select: { + id: true, + name: true, + schemaName: true, + isPublic: true, + }, + }, + }, + first: 10, + where: { + and: [ + { name: { includesInsensitive: 'prod' } }, + { createdAt: { greaterThanOrEqualTo: '2026-01-01T00:00:00.000Z' } }, + ], + }, + orderBy: ['CREATED_AT_DESC'], + }, + }); + + if (isLoading) return

Loading databases...

; + if (error) return

Error: {error.message}

; + + // AUTOCOMPLETION TEST: type `db.` — should suggest id, name, label, createdAt, schemas + const databases = data?.databases?.nodes ?? []; + + return ( +
+
+

Databases ({databases.length})

+ +
+ {databases.length === 0 ? ( +

No databases — create one below

+ ) : ( + databases.map((db) => ( +
+
+
+ {db.name} + {db.label && ({db.label})} + {db.id.slice(0, 8)} +
+ {db.createdAt} +
+ {/* NESTED RELATION: schemas under this database */} + {db.schemas?.nodes && db.schemas.nodes.length > 0 && ( +
+ Schemas: {db.schemas.nodes.map((s) => ( + + {s.schemaName ?? s.name} + + ))} +
+ )} +
+ )) + )} +
+ ); +} + +function DatabaseDetail({ databaseId }: { databaseId: string }) { + const { data, isLoading, error } = useDatabaseQuery({ + id: databaseId, + selection: { + fields: { + id: true, + name: true, + label: true, + schemaName: true, + privateSchemaName: true, + createdAt: true, + updatedAt: true, + // NESTED: owner user + owner: { + select: { id: true, username: true, displayName: true }, + }, + }, + }, + }); + + if (isLoading) return

Loading database...

; + if (error) return

Error: {error.message}

; + + // AUTOCOMPLETION TEST: type `data?.database.` — should suggest id, name, label, owner, etc. + const db = data?.database; + if (!db) return

Database not found

; + + return ( +
+

Database Detail

+
+
ID
{db.id}
+
Name
{db.name}
+
Label
{db.label ?? '—'}
+
Public Schema
{db.schemaName ?? '—'}
+
Private Schema
{db.privateSchemaName ?? '—'}
+
Owner
{db.owner?.displayName ?? db.owner?.username ?? '—'}
+
Created
{db.createdAt}
+
Updated
{db.updatedAt}
+
+
+ ); +} + +function CreateDatabaseForm({ userId }: { userId: string }) { + const [name, setName] = useState(''); + const [label, setLabel] = useState(''); + const { mutate, isPending, error, data: result } = useCreateDatabaseMutation({ + selection: { fields: { id: true, name: true, label: true, createdAt: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: mutate arg should suggest ownerId, name, label, schemaName, etc. + mutate({ + ownerId: userId, + name: name.toLowerCase().replace(/[^a-z0-9_]/g, '_'), + label: label || null, + }); + }; + + return ( +
+

Create Database

+
+ setName(e.target.value)} required style={inputStyle} /> + setLabel(e.target.value)} style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createDatabase.database, null, 2)}
} +
+ ); +} + +function UpdateDatabaseForm() { + const [id, setId] = useState(''); + const [label, setLabel] = useState(''); + const { mutate, isPending, error, data: result } = useUpdateDatabaseMutation({ + selection: { fields: { id: true, name: true, label: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: patch should suggest DatabasePatch fields — ownerId, name, label, etc. + mutate({ id, patch: { label } }); + }; + + return ( +
+

Update Database

+
+ setId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setLabel(e.target.value)} required style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.updateDatabase.database, null, 2)}
} +
+ ); +} + +function DeleteDatabaseForm() { + const [id, setId] = useState(''); + const [confirm, setConfirm] = useState(false); + const { mutate, isPending, error, data: result } = useDeleteDatabaseMutation({ + selection: { fields: { id: true, name: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (!confirm) return; + mutate({ id }); + }; + + return ( +
+

Delete Database

+
+ { setId(e.target.value); setConfirm(false); }} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + + +
+ {error &&

{error.message}

} + {result &&
Deleted: {JSON.stringify(result.deleteDatabase.database, null, 2)}
} +
+ ); +} + +// =========================================================================== +// SECTION: Schema & API Management +// =========================================================================== + +function SchemaList() { + const { data, isLoading, error } = useSchemasQuery({ + selection: { + fields: { + id: true, + name: true, + schemaName: true, + isPublic: true, + // NESTED RELATION: parent database + database: { + select: { id: true, name: true }, + }, + }, + first: 20, + }, + }); + + if (isLoading) return

Loading schemas...

; + if (error) return

Error: {error.message}

; + + const schemas = data?.schemas?.nodes ?? []; + + return ( +
+

Schemas ({schemas.length})

+ [ + s.schemaName, + s.name, + s.database?.name ?? '—', + s.isPublic ? 'Yes' : 'No', + ]} + /> +
+ ); +} + +function CreateSchemaForm() { + const [databaseId, setDatabaseId] = useState(''); + const [name, setName] = useState(''); + const [schemaName, setSchemaName] = useState(''); + const { mutate, isPending, error, data: result } = useCreateSchemaMutation({ + selection: { fields: { id: true, name: true, schemaName: true, isPublic: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: mutate arg should suggest databaseId, name, schemaName, label, isPublic, etc. + mutate({ + databaseId, + name, + schemaName: schemaName.toLowerCase().replace(/[^a-z0-9_]/g, '_'), + }); + }; + + return ( +
+

Create Schema

+
+ setDatabaseId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setName(e.target.value)} required style={inputStyle} /> + setSchemaName(e.target.value)} required style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createSchema.schema, null, 2)}
} +
+ ); +} + +function ApiList() { + const { data, isLoading, error } = useApisQuery({ + selection: { + fields: { + id: true, + name: true, + dbname: true, + roleName: true, + anonRole: true, + isPublic: true, + // NESTED RELATION: parent database + database: { + select: { id: true, name: true }, + }, + }, + first: 20, + }, + }); + + if (isLoading) return

Loading APIs...

; + if (error) return

Error: {error.message}

; + + const apis = data?.apis?.nodes ?? []; + + return ( +
+

APIs ({apis.length})

+ [ + api.name, + api.dbname, + api.roleName, + api.database?.name ?? '—', + api.isPublic ? 'Yes' : 'No', + ]} + /> +
+ ); +} + +function CreateApiForm() { + const [databaseId, setDatabaseId] = useState(''); + const [name, setName] = useState(''); + const { mutate, isPending, error, data: result } = useCreateApiMutation({ + selection: { fields: { id: true, name: true, dbname: true, roleName: true, anonRole: true, isPublic: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: mutate arg should suggest databaseId, name, dbname, roleName, anonRole, isPublic + mutate({ databaseId, name }); + }; + + return ( +
+

Create API

+
+ setDatabaseId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setName(e.target.value)} required style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createApi.api, null, 2)}
} +
+ ); +} + +// =========================================================================== +// SECTION: Site & Domain Management +// =========================================================================== + +function CreateSiteForm() { + const [databaseId, setDatabaseId] = useState(''); + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const { mutate, isPending, error, data: result } = useCreateSiteMutation({ + selection: { fields: { id: true, title: true, description: true, dbname: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: mutate arg should suggest databaseId, title, description, favicon, ogImage, etc. + mutate({ databaseId, title, description: description || null }); + }; + + return ( +
+

Create Site

+
+ setDatabaseId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setTitle(e.target.value)} required style={inputStyle} /> + setDescription(e.target.value)} style={inputStyle} /> + +
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createSite.site, null, 2)}
} +
+ ); +} + +function DomainList() { + const { data, isLoading, error } = useDomainsQuery({ + selection: { + fields: { + id: true, + subdomain: true, + domain: true, + // NESTED RELATIONS: api and site via FK + api: { select: { id: true, name: true } }, + site: { select: { id: true, title: true } }, + }, + first: 20, + }, + }); + + if (isLoading) return

Loading domains...

; + if (error) return

Error: {error.message}

; + + // AUTOCOMPLETION TEST: type `d.` — should suggest id, subdomain, domain, api, site + const domains = data?.domains?.nodes ?? []; + + return ( +
+

Domains ({domains.length})

+ [ + d.subdomain ?? '—', + d.domain ?? '—', + d.api?.name ?? '—', + d.site?.title ?? '—', + ]} + /> +
+ ); +} + +function CreateDomainForm() { + const [databaseId, setDatabaseId] = useState(''); + const [apiId, setApiId] = useState(''); + const [siteId, setSiteId] = useState(''); + const [subdomain, setSubdomain] = useState(''); + const [domain, setDomain] = useState(''); + const { mutate, isPending, error, data: result } = useCreateDomainMutation({ + selection: { fields: { id: true, subdomain: true, domain: true } }, + }); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + // AUTOCOMPLETION TEST: mutate arg should suggest databaseId, apiId, siteId, subdomain, domain + mutate({ databaseId, apiId, siteId, subdomain, domain }); + }; + + return ( +
+

Create Domain

+
+
+ setDatabaseId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setApiId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> + setSiteId(e.target.value)} required style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12 }} /> +
+
+ setSubdomain(e.target.value)} style={inputStyle} /> + setDomain(e.target.value)} style={inputStyle} /> + +
+
+ {error &&

{error.message}

} + {result &&
{JSON.stringify(result.createDomain.domain, null, 2)}
} +
+ ); +} + +// =========================================================================== +// Shared: Generic data table component +// =========================================================================== + +function DataTable({ + columns, + rows, + renderRow, +}: { + columns: string[]; + rows: T[]; + renderRow: (row: T) => (string | boolean | null | undefined)[]; +}) { + if (rows.length === 0) return

None

; + + return ( + + + + {columns.map((col) => ( + + ))} + + + + {rows.map((row) => ( + + {renderRow(row).map((cell, i) => ( + + ))} + + ))} + +
{col}
{cell ?? '—'}
+ ); +} + +// =========================================================================== +// Authenticated Dashboard +// =========================================================================== + +function Dashboard({ token, userId, onSignOut }: { token: string; userId: string; onSignOut: () => void }) { + const queryClient = useQueryClient(); + const [activeDbId, setActiveDbId] = useState(null); + + const { mutate: signOut, isPending: signingOut } = useSignOutMutation({ + onSuccess: () => { + queryClient.clear(); + onSignOut(); + }, + }); + + return ( +
+ {/* Header */} +
+
+ Authenticated + {token.slice(0, 20)}... +
+ +
+ + {/* Identity & Profile */} + + + + + {/* Users */} + + + + + + {/* Database Management */} + + + + + + + {/* Database Detail (enter an ID to inspect) */} +
+

Inspect Database (findOne)

+
+ setActiveDbId(e.target.value || null)} + style={{ ...inputStyle, fontFamily: 'monospace', fontSize: 12, flex: 1 }} + /> +
+
+ {activeDbId && } + + {/* Schemas & APIs */} + + + + + + + {/* Sites & Domains */} + + + + +
+ ); +} + +function SectionHeader({ title }: { title: string }) { + return ( +

+ {title} +

+ ); +} + +// =========================================================================== +// Main App +// =========================================================================== + +export default function App() { + const [auth, setAuth] = useState<{ token: string; userId: string } | null>(null); + const queryClient = useQueryClient(); + + const handleAuth = (token: string, userId: string) => { + reconfigureClient(token); + queryClient.clear(); + setAuth({ token, userId }); + }; + + const handleSignOut = () => { + reconfigureClient(); + setAuth(null); + }; + + return ( +
+

GraphQL Codegen Test App

+

+ Auth flow + CRUD mutations + nested relation queries with type-safe select. + Open src/App.tsx in your editor to inspect types. +

+ + {auth ? ( + + ) : ( + + )} +
+ ); +} + +// =========================================================================== +// Styles +// =========================================================================== + +const formStyle: React.CSSProperties = { + display: 'flex', + flexDirection: 'column', + gap: 10, + maxWidth: 360, +}; + +const rowForm: React.CSSProperties = { + display: 'flex', + gap: 8, + alignItems: 'flex-end', + flexWrap: 'wrap', +}; + +const inputStyle: React.CSSProperties = { + padding: '8px 12px', + border: '1px solid #d1d5db', + borderRadius: 6, + fontSize: 14, +}; + +const btnStyle: React.CSSProperties = { + padding: '8px 16px', + background: '#2563eb', + color: '#fff', + border: 'none', + borderRadius: 6, + fontSize: 14, + cursor: 'pointer', + whiteSpace: 'nowrap', +}; + +const btnSmall: React.CSSProperties = { + padding: '4px 10px', + color: '#fff', + border: 'none', + borderRadius: 4, + fontSize: 12, + cursor: 'pointer', +}; + +const tabStyle: React.CSSProperties = { + padding: '6px 16px', + background: 'none', + border: '1px solid #d1d5db', + borderRadius: 6, + fontSize: 14, + cursor: 'pointer', +}; + +const errStyle: React.CSSProperties = { + color: '#dc2626', + fontSize: 13, + marginTop: 4, +}; + +const cardStyle: React.CSSProperties = { + border: '1px solid #e5e7eb', + borderRadius: 8, + padding: 16, + marginBottom: 16, +}; + +const dlStyle: React.CSSProperties = { + display: 'grid', + gridTemplateColumns: 'auto 1fr', + gap: '4px 16px', + margin: 0, + fontSize: 14, +}; + +const tableStyle: React.CSSProperties = { + width: '100%', + borderCollapse: 'collapse', + fontSize: 14, +}; + +const thStyle: React.CSSProperties = { + textAlign: 'left', + borderBottom: '2px solid #e5e7eb', + padding: '6px 8px', +}; + +const tdStyle: React.CSSProperties = { + borderBottom: '1px solid #f3f4f6', + padding: '6px 8px', +}; + +const preStyle: React.CSSProperties = { + background: '#f9fafb', + padding: 12, + borderRadius: 6, + fontSize: 12, + overflow: 'auto', + marginTop: 8, +}; + +const badge: React.CSSProperties = { + display: 'inline-block', + padding: '2px 8px', + borderRadius: 4, + fontSize: 12, + marginRight: 4, +}; diff --git a/graphql/test-app/src/main.tsx b/graphql/test-app/src/main.tsx new file mode 100644 index 000000000..040b1e09f --- /dev/null +++ b/graphql/test-app/src/main.tsx @@ -0,0 +1,28 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { configure } from './generated/hooks'; +import App from './App.tsx'; + +// Configure the generated SDK client (no auth initially — App manages JWT) +configure({ + endpoint: 'http://api.localhost:3000/graphql', +}); + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + refetchOnWindowFocus: false, + }, + }, +}); + +createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts b/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts new file mode 100644 index 000000000..3728e01d0 --- /dev/null +++ b/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts @@ -0,0 +1,266 @@ +/** + * Compile-time regression checks for helper overloads and React Query options. + * + * Focus: + * - fetch/prefetch overload behavior + * - optional variables + required selection.fields custom query overloads + * - default vs explicit selection result narrowing in helpers + * - allowed/disallowed React Query options on generated hooks + */ + +import { QueryClient } from '@tanstack/react-query'; + +import { + fetchDatabasesQuery, + fetchGetObjectAtPathQuery, + prefetchDatabasesQuery, + prefetchGetObjectAtPathQuery, + useCurrentUserQuery, + useDatabasesQuery, + useGetObjectAtPathQuery, + useSignInMutation, +} from '../generated/hooks'; + +type Assert = T; +type HasKey = K extends keyof T ? true : false; +type NotHasKey = K extends keyof T ? false : true; + +function helperOverloadChecks() { + const queryClient = new QueryClient(); + + const defaultDatabasesFetch = fetchDatabasesQuery({ selection: { first: 2 } }); + type DefaultDatabaseNode = Awaited['databases']['nodes'][number]; + type _defaultDatabaseFetchOmitsName = Assert>; + + const selectedDatabasesFetch = fetchDatabasesQuery({ + selection: { + first: 2, + fields: { + id: true, + name: true, + schemas: { + first: 1, + select: { + id: true, + schemaName: true, + }, + }, + }, + }, + }); + type SelectedDatabaseNode = Awaited['databases']['nodes'][number]; + type _selectedDatabaseFetchHasName = Assert>; + + prefetchDatabasesQuery(queryClient, { selection: { first: 2 } }); + prefetchDatabasesQuery(queryClient, { + selection: { + first: 2, + fields: { + id: true, + name: true, + }, + }, + }); + + // @ts-expect-error invalid helper nested select key should be rejected + fetchDatabasesQuery({ selection: { fields: { schemas: { select: { invalidField: true } } } } }); + + const defaultGetObjectFetch = fetchGetObjectAtPathQuery({ variables: undefined }); + type DefaultGetObject = Awaited['getObjectAtPath']; + type _defaultGetObjectOmitsData = Assert>; + + const selectedGetObjectFetch = fetchGetObjectAtPathQuery({ + variables: undefined, + selection: { + fields: { + id: true, + data: true, + }, + }, + }); + type SelectedGetObject = Awaited['getObjectAtPath']; + type _selectedGetObjectHasData = Assert>; + + prefetchGetObjectAtPathQuery(queryClient, { variables: undefined }); + prefetchGetObjectAtPathQuery(queryClient, { + variables: undefined, + selection: { + fields: { + id: true, + }, + }, + }); + + // @ts-expect-error invalid custom helper select key should be rejected + prefetchGetObjectAtPathQuery(queryClient, { + variables: undefined, + selection: { fields: { invalidField: true } }, + }); + + // @ts-expect-error custom helper select overload requires explicit variables + fetchGetObjectAtPathQuery({ selection: { fields: { id: true } } }); + + // @ts-expect-error custom helper prefetch overload requires explicit variables + prefetchGetObjectAtPathQuery(queryClient, { selection: { fields: { id: true } } }); +} + +function reactQueryOptionsChecks() { + useDatabasesQuery({ + selection: { + first: 10, + fields: { + id: true, + name: true, + }, + }, + enabled: true, + staleTime: 30_000, + gcTime: 300_000, + retry: 2, + refetchOnWindowFocus: false, + refetchInterval: false, + refetchOnReconnect: true, + refetchOnMount: 'always', + networkMode: 'always', + notifyOnChangeProps: ['data', 'error'], + meta: { source: 'type-test' }, + }); + + const transformedDatabases = useDatabasesQuery({ + selection: { + first: 10, + fields: { + id: true, + name: true, + }, + }, + select: (data) => data.databases.nodes.map((node) => node.name), + placeholderData: (previousData) => + previousData ?? { + databases: { + nodes: [], + totalCount: 0, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }, + }, + staleTime: (query) => { + const status = query.state.status; + void status; + return 10_000; + }, + throwOnError: (error) => error.message.length > 0, + }); + const transformedDatabaseNames: string[] | undefined = transformedDatabases.data; + void transformedDatabaseNames; + // @ts-expect-error transformed query data should not expose connection shape + transformedDatabases.data.databases; + + const transformedCurrentUser = useCurrentUserQuery({ + select: (data) => data.currentUser.id, + placeholderData: (previousData) => + previousData ?? { + currentUser: { + id: 'fallback-user-id', + }, + }, + enabled: false, + staleTime: 5_000, + gcTime: 60_000, + retry: (failureCount, error) => { + const msg = error.message; + void msg; + return failureCount < 2; + }, + }); + const transformedCurrentUserId: string | undefined = transformedCurrentUser.data; + void transformedCurrentUserId; + // @ts-expect-error transformed query data should not expose object shape + transformedCurrentUser.data.currentUser; + + useGetObjectAtPathQuery({ + variables: undefined, + selection: { + fields: { + id: true, + }, + }, + enabled: false, + staleTime: 5_000, + gcTime: 60_000, + placeholderData: { + getObjectAtPath: { + id: 'placeholder', + }, + }, + select: (data) => data.getObjectAtPath.id, + }); + + // @ts-expect-error unknown React Query option should be rejected + useDatabasesQuery({ selection: { fields: { id: true } }, unknownOption: true }); + + // @ts-expect-error queryKey is owned by generated hooks + useDatabasesQuery({ selection: { fields: { id: true } }, queryKey: ['override'] as const }); + + // @ts-expect-error queryFn is owned by generated hooks + useDatabasesQuery({ + selection: { fields: { id: true } }, + queryFn: async () => + ({ + databases: { + nodes: [], + totalCount: 0, + pageInfo: { + hasNextPage: false, + hasPreviousPage: false, + }, + }, + }) as never, + }); + + useSignInMutation({ + selection: { + fields: { + clientMutationId: true, + }, + }, + retry: 1, + gcTime: 300_000, + meta: { source: 'type-test' }, + networkMode: 'always', + throwOnError: (error) => error.message.length > 0, + onMutate: (variables) => { + const email = variables.input.email; + return { email }; + }, + onError: (error, variables, context) => { + const message = error.message; + const rememberMe = variables.input.rememberMe; + void message; + void rememberMe; + void context; + }, + onSuccess: (data, variables) => { + const clientMutationId = data.signIn.clientMutationId; + const email = variables.input.email; + void clientMutationId; + void email; + }, + }); + + // @ts-expect-error mutationFn is owned by generated hooks + useSignInMutation({ + selection: { fields: { clientMutationId: true } }, + mutationFn: async () => + ({ + signIn: { + clientMutationId: 'x', + }, + }) as never, + }); +} + +void helperOverloadChecks; +void reactQueryOptionsChecks; diff --git a/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts b/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts new file mode 100644 index 000000000..c44f21281 --- /dev/null +++ b/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts @@ -0,0 +1,306 @@ +/** + * Compile-time regression checks for React Query + ORM output modes. + * + * These checks focus on overload behavior introduced to recover contextual + * typing/autocomplete for nested select objects. + */ + +import { + useCurrentUserQuery, + useGetObjectAtPathQuery, + useSignInMutation, + useUserQuery, + useUsersQuery, +} from '../generated/hooks'; +import { createClient } from '../generated/orm'; + +type Assert = T; +type HasKey = K extends keyof T ? true : false; +type NotHasKey = K extends keyof T ? false : true; + +const ormClient = createClient({ + endpoint: 'https://example.invalid/graphql', +}); + +function hookTypeChecks() { + const currentUser = useCurrentUserQuery({ + selection: { + fields: { + id: true, + username: true, + }, + } + }); + + const maybeUsername = currentUser.data?.currentUser.username; + void maybeUsername; + + const defaultUser = useUserQuery({ id: '00000000-0000-0000-0000-000000000000' }); + const defaultUserId = defaultUser.data?.user?.id; + void defaultUserId; + // @ts-expect-error default select for useUserQuery should not expose username + defaultUser.data?.user?.username; + + const selectedUser = useUserQuery({ + id: '00000000-0000-0000-0000-000000000000', + selection: { + fields: { + id: true, + username: true, + }, + }, + }); + const selectedUsername = selectedUser.data?.user?.username; + void selectedUsername; + + const users = useUsersQuery({ + selection: { + fields: { + id: true, + databasesByOwnerId: { + first: 2, + select: { + id: true, + schemaName: true, + name: true + }, + }, + }, + first: 5, + orderBy: ['CREATED_AT_DESC'], + }, + }); + + const nestedSchema = users.data?.users.nodes[0]?.databasesByOwnerId?.nodes[0]?.schemaName; + void nestedSchema; + + // Optional variables + required select args overload (custom query case) + useGetObjectAtPathQuery({ + variables: undefined, + selection: { + fields: { + id: true, + data: true, + }, + }, + }); + + const defaultSignIn = useSignInMutation(); + const defaultSignInClientMutationId = defaultSignIn.data?.signIn.clientMutationId; + void defaultSignInClientMutationId; + // @ts-expect-error default signIn select should not expose result + defaultSignIn.data?.signIn.result; + + useSignInMutation({ + selection: { + fields: { + clientMutationId: true, + result: { + select: { + accessToken: true, + accessTokenExpiresAt: true, + isVerified: true, + totpEnabled: true, + userId: true, + }, + }, + }, + }, + }); + + const validSignInSelect = { + clientMutationId: true, + result: { + select: { + accessToken: true, + isVerified: true, + totpEnabled: true, + userId: true, + }, + }, + }; + useSignInMutation({ selection: { fields: validSignInSelect } }); + + const invalidUsersSelect = { + id: true, + invalidField: true, + }; + // @ts-expect-error invalid variable select key should be rejected + useUsersQuery({ selection: { fields: invalidUsersSelect } }); + + const invalidNestedUsersSelect = { + databasesByOwnerId: { + select: { + id: true, + doesNotExist: true, + }, + }, + }; + // @ts-expect-error invalid nested variable select key should be rejected + useUsersQuery({ selection: { fields: invalidNestedUsersSelect } }); + + const invalidCurrentUserSelect = { + id: true, + nope: true, + }; + // @ts-expect-error invalid variable select key should be rejected + useCurrentUserQuery({ selection: { fields: invalidCurrentUserSelect } }); + + const invalidSignInSelect = { + result: { + select: { + accessToken: true, + nope: true, + }, + }, + }; + // @ts-expect-error invalid mutation variable select key should be rejected + useSignInMutation({ selection: { fields: invalidSignInSelect } }); + + // @ts-expect-error invalid nested select key should be rejected + useUsersQuery({ + selection: { + fields: { + databasesByOwnerId: { + select: { + doesNotExist: true, + }, + }, + }, + }, + }); + + // @ts-expect-error invalid custom query select key should be rejected + useGetObjectAtPathQuery({ + variables: undefined, + selection: { fields: { nope: true } }, + }); +} + +async function ormModelTypeChecks() { + const defaultBuilder = ormClient.user.findOne({ + id: '00000000-0000-0000-0000-000000000000', + }); + type DefaultUser = Awaited>['user']; + type _defaultOmitsUsername = Assert, 'username'>>; + + const defaultSelected = await defaultBuilder.unwrapOr({ user: null }); + + if (defaultSelected.user) { + const id: string = defaultSelected.user.id; + void id; + } + + const explicitlySelected = ormClient.user.findOne({ + id: '00000000-0000-0000-0000-000000000000', + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + schemaName: true, + }, + }, + }, + }); + + type ExplicitUser = Awaited>['user']; + type _explicitHasUsername = Assert, 'username'>>; + + ormClient.user.findMany({ + first: 5, + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + schemaName: true, + }, + }, + }, + }); + + ormClient.query.currentUser({ + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + }, + }, + }, + }); + + ormClient.query.getObjectAtPath( + { + dbId: '00000000-0000-0000-0000-000000000000', + path: ['root'], + refname: 'main', + }, + { + select: { + id: true, + }, + } + ); + + // @ts-expect-error invalid model select key should be rejected + ormClient.user.findMany({ select: { invalidField: true } }); + + // @ts-expect-error invalid custom query select key should be rejected + ormClient.query.currentUser({ select: { invalidField: true } }); + + const invalidModelSelect = { + id: true, + invalidField: true, + }; + // @ts-expect-error invalid model variable select key should be rejected + ormClient.user.findMany({ select: invalidModelSelect }); + + const invalidNestedModelSelect = { + databasesByOwnerId: { + select: { + id: true, + invalidField: true, + }, + }, + }; + // @ts-expect-error invalid nested model variable select key should be rejected + ormClient.user.findMany({ select: invalidNestedModelSelect }); + + const invalidCustomQuerySelect = { + id: true, + invalidField: true, + }; + // @ts-expect-error invalid custom query variable select key should be rejected + ormClient.query.currentUser({ select: invalidCustomQuerySelect }); + + const invalidCustomMutationSelect = { + result: { + select: { + accessToken: true, + invalidField: true, + }, + }, + }; + ormClient.mutation.signIn( + { + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }, + // @ts-expect-error invalid custom mutation variable select key should be rejected + { select: invalidCustomMutationSelect } + ); +} + +void hookTypeChecks; +void ormModelTypeChecks; diff --git a/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts b/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts new file mode 100644 index 000000000..9c4e6a305 --- /dev/null +++ b/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts @@ -0,0 +1,200 @@ +/** + * Additional compile-time regression checks focused on strict selection behavior. + * + * These tests target edge cases around: + * - nested relation select options (filter/orderBy) + * - invalid relation option keys + * - default vs explicit selection result narrowing + * - custom ORM operation option contracts + */ + +import { + useDatabasesQuery, + useSignInMutation, +} from '../generated/hooks'; +import { createClient } from '../generated/orm'; + +type Assert = T; +type HasKey = K extends keyof T ? true : false; +type NotHasKey = K extends keyof T ? false : true; + +const ormClient = createClient({ + endpoint: 'https://example.invalid/graphql', +}); + +function hookStrictnessChecks() { + const defaultDatabases = useDatabasesQuery({ selection: { first: 10 } }); + const defaultDatabaseId = defaultDatabases.data?.databases.nodes[0]?.id; + void defaultDatabaseId; + // @ts-expect-error default select for useDatabasesQuery should not expose name + defaultDatabases.data?.databases.nodes[0]?.name; + + const selectedDatabases = useDatabasesQuery({ + selection: { + first: 10, + where: { + and: [ + { name: { includesInsensitive: 'prod' } }, + { createdAt: { greaterThanOrEqualTo: '2026-01-01T00:00:00.000Z' } }, + ], + }, + orderBy: ['CREATED_AT_DESC'], + fields: { + id: true, + name: true, + schemas: { + first: 5, + filter: { + schemaName: { startsWithInsensitive: 'app_' }, + isPublic: { equalTo: true }, + }, + orderBy: ['SCHEMA_NAME_ASC'], + select: { + id: true, + schemaName: true, + isPublic: true, + }, + }, + }, + }, + }); + const selectedDatabaseName = selectedDatabases.data?.databases.nodes[0]?.name; + const selectedNestedSchema = selectedDatabases.data?.databases.nodes[0]?.schemas?.nodes[0]?.schemaName; + void selectedDatabaseName; + void selectedNestedSchema; + + // @ts-expect-error relation select options should use filter, not where + useDatabasesQuery({ + selection: { + fields: { + schemas: { + where: { + schemaName: { equalTo: 'public' }, + }, + select: { + id: true, + }, + }, + }, + }, + }); + + // @ts-expect-error invalid field inside nested relation filter should be rejected + useDatabasesQuery({ + selection: { + fields: { + schemas: { + filter: { + invalidField: { equalTo: 'x' }, + }, + select: { + id: true, + }, + }, + }, + }, + }); + + // @ts-expect-error invalid orderBy literal should be rejected + useDatabasesQuery({ + selection: { + fields: { + schemas: { + orderBy: ['INVALID_ASC'], + select: { + id: true, + }, + }, + }, + }, + }); + + const validSignInSelect = { + clientMutationId: true, + result: { + select: { + accessToken: true, + userId: true, + }, + }, + }; + useSignInMutation({ selection: { fields: validSignInSelect } }); + + const invalidSignInNested = { + clientMutationId: true, + result: { + select: { + accessToken: true, + invalidNestedField: true, + }, + }, + }; + // @ts-expect-error invalid nested variable select field should be rejected + useSignInMutation({ selection: { fields: invalidSignInNested } }); +} + +async function ormStrictnessChecks() { + const defaultCurrentUser = ormClient.query.currentUser(); + type DefaultCurrentUser = Awaited>['currentUser']; + type _defaultCurrentUserOmitsUsername = Assert>; + + const selectedCurrentUser = ormClient.query.currentUser({ + select: { + id: true, + username: true, + }, + }); + type SelectedCurrentUser = Awaited>['currentUser']; + type _selectedCurrentUserHasUsername = Assert>; + + // @ts-expect-error custom ORM query options object requires select when provided + ormClient.query.currentUser({}); + + const defaultSignIn = ormClient.mutation.signIn({ + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }); + type DefaultSignIn = Awaited>['signIn']; + type _defaultSignInOmitsResult = Assert>; + + const selectedSignIn = ormClient.mutation.signIn( + { + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }, + { + select: { + clientMutationId: true, + result: { + select: { + accessToken: true, + userId: true, + }, + }, + }, + } + ); + type SelectedSignIn = Awaited>['signIn']; + type _selectedSignInHasResult = Assert>; + + ormClient.mutation.signIn( + { + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }, + // @ts-expect-error custom ORM mutation options object requires select when provided + {} + ); +} + +void hookStrictnessChecks; +void ormStrictnessChecks; diff --git a/graphql/test-app/src/vite-env.d.ts b/graphql/test-app/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/graphql/test-app/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/graphql/test-app/tests/hook-test-utils.ts b/graphql/test-app/tests/hook-test-utils.ts new file mode 100644 index 000000000..a2957109f --- /dev/null +++ b/graphql/test-app/tests/hook-test-utils.ts @@ -0,0 +1,85 @@ +import assert from 'node:assert/strict'; + +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import React from 'react'; +import { act, create, type ReactTestRenderer } from 'react-test-renderer'; + +type WaitForOptions = { + timeoutMs?: number; + intervalMs?: number; +}; + +export interface HookHarness { + getResult: () => TResult; + waitFor: (predicate: (result: TResult) => boolean, options?: WaitForOptions) => Promise; + unmount: () => Promise; +} + +const DEFAULT_TIMEOUT_MS = 20_000; +const DEFAULT_INTERVAL_MS = 30; + +function sleep(ms: number): Promise { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} + +export async function renderHookWithClient( + useHook: () => TResult, + queryClient: QueryClient +): Promise> { + // Enables React's act() environment checks for non-Jest runtimes. + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; + + let renderer: ReactTestRenderer | null = null; + let latestResult: TResult | undefined; + + function Probe() { + latestResult = useHook(); + return null; + } + + await act(async () => { + renderer = create( + React.createElement( + QueryClientProvider, + { client: queryClient }, + React.createElement(Probe, null) + ) + ); + }); + + return { + getResult() { + assert.notEqual(latestResult, undefined, 'Hook result is not ready yet'); + return latestResult; + }, + + async waitFor(predicate, options) { + const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS; + const intervalMs = options?.intervalMs ?? DEFAULT_INTERVAL_MS; + const start = Date.now(); + + while (Date.now() - start < timeoutMs) { + const result = this.getResult(); + if (predicate(result)) { + return result; + } + await act(async () => { + await sleep(intervalMs); + }); + } + + throw new Error(`Timed out waiting for hook condition after ${timeoutMs}ms`); + }, + + async unmount() { + if (!renderer) { + return; + } + await act(async () => { + renderer?.unmount(); + }); + }, + }; +} diff --git a/graphql/test-app/tests/hooks.live.test.ts b/graphql/test-app/tests/hooks.live.test.ts new file mode 100644 index 000000000..d780a88b4 --- /dev/null +++ b/graphql/test-app/tests/hooks.live.test.ts @@ -0,0 +1,341 @@ +import assert from 'node:assert/strict'; +import { after, before, describe, test } from 'node:test'; + +import { act } from 'react-test-renderer'; + +import { + currentUserIdQueryKey, + currentUserQueryKey, + fetchCurrentUserIdQuery, + fetchCurrentUserQuery, + fetchUserQuery, + fetchUsersQuery, + prefetchCurrentUserIdQuery, + prefetchCurrentUserQuery, + prefetchUserQuery, + prefetchUsersQuery, + useCheckPasswordMutation, + useCurrentUserIdQuery, + useCurrentUserQuery, + useSignInMutation, + useSignOutMutation, + useUserQuery, + useUsersQuery, + userQueryKey, + usersQueryKey, +} from '../src/generated/hooks'; + +import { renderHookWithClient } from './hook-test-utils'; +import { + assertLiveEnvConfigured, + configureHooks, + createTestQueryClient, + getLiveEnvHelpMessage, + getLiveTestEnv, + type AuthSession, + signIn, + signOut, +} from './live-test-utils'; + +const liveEnv = getLiveTestEnv(); +assertLiveEnvConfigured(liveEnv); + +if (!liveEnv) { + test.skip(getLiveEnvHelpMessage(), () => {}); +} else { + describe('React Query Hooks (live endpoint)', () => { + let sharedSession: AuthSession | null = null; + + const ensureAuth = async (): Promise => { + if (!sharedSession) { + sharedSession = await signIn(liveEnv); + } + configureHooks(liveEnv, sharedSession.token); + return sharedSession; + }; + + before(() => { + configureHooks(liveEnv); + }); + + after(async () => { + if (sharedSession) { + await signOut(liveEnv, sharedSession.token); + sharedSession = null; + } + configureHooks(liveEnv); + }); + + test('useSignInMutation returns token payload when explicit select is provided', async () => { + const queryClient = createTestQueryClient(); + const hook = await renderHookWithClient( + () => + useSignInMutation({ + select: { + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + totpEnabled: true, + }, + }, + }, + }), + queryClient + ); + + let result: + | { + signIn: { + result?: { + accessToken?: string | null; + userId?: string | null; + isVerified?: boolean | null; + totpEnabled?: boolean | null; + } | null; + }; + } + | null = null; + + await act(async () => { + result = await hook.getResult().mutateAsync({ + input: { + email: liveEnv.email, + password: liveEnv.password, + rememberMe: true, + }, + }); + }); + + const token = result?.signIn.result?.accessToken ?? null; + const userId = result?.signIn.result?.userId ?? null; + assert.ok(token, 'useSignInMutation did not return accessToken'); + assert.ok(userId, 'useSignInMutation did not return userId'); + + await signOut(liveEnv, token); + await hook.unmount(); + queryClient.clear(); + }); + + test('useCurrentUserQuery returns nested selected relation fields', async () => { + await ensureAuth(); + const queryClient = createTestQueryClient(); + const hook = await renderHookWithClient( + () => + useCurrentUserQuery({ + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + schemaName: true, + }, + }, + }, + }), + queryClient + ); + + const result = await hook.waitFor((value) => value.isSuccess || value.isError); + assert.equal(result.isError, false, 'useCurrentUserQuery returned an error'); + assert.ok(result.data?.currentUser?.id, 'currentUser.id is missing'); + assert.ok( + Array.isArray(result.data?.currentUser?.databasesByOwnerId?.nodes), + 'currentUser.databasesByOwnerId.nodes should be an array' + ); + + await hook.unmount(); + queryClient.clear(); + }); + + test('useCurrentUserIdQuery matches useCurrentUserQuery id', async () => { + const session = await ensureAuth(); + const queryClient = createTestQueryClient(); + const hook = await renderHookWithClient(() => useCurrentUserIdQuery(), queryClient); + + const result = await hook.waitFor((value) => value.isSuccess || value.isError); + assert.equal(result.isError, false, 'useCurrentUserIdQuery returned an error'); + assert.equal(result.data?.currentUserId, session.userId, 'currentUserId does not match signed in user'); + + await hook.unmount(); + queryClient.clear(); + }); + + test('useUsersQuery and useUserQuery execute with select overloads', async () => { + const session = await ensureAuth(); + const queryClient = createTestQueryClient(); + + const usersHook = await renderHookWithClient( + () => + useUsersQuery({ + first: 5, + orderBy: ['CREATED_AT_DESC'], + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + }, + }, + }, + }), + queryClient + ); + + const usersResult = await usersHook.waitFor((value) => value.isSuccess || value.isError); + assert.equal(usersResult.isError, false, 'useUsersQuery returned an error'); + assert.ok(Array.isArray(usersResult.data?.users.nodes), 'users.nodes should be an array'); + + const userHook = await renderHookWithClient( + () => + useUserQuery({ + id: session.userId, + select: { + id: true, + username: true, + }, + }), + queryClient + ); + + const userResult = await userHook.waitFor((value) => value.isSuccess || value.isError); + assert.equal(userResult.isError, false, 'useUserQuery returned an error'); + assert.equal(userResult.data?.user?.id, session.userId, 'useUserQuery did not return the requested user'); + + await usersHook.unmount(); + await userHook.unmount(); + queryClient.clear(); + }); + + test('fetch/prefetch helpers populate QueryClient cache with expected keys', async () => { + const session = await ensureAuth(); + const queryClient = createTestQueryClient(); + + const currentUser = await fetchCurrentUserQuery({ + select: { + id: true, + username: true, + }, + }); + assert.equal(currentUser.currentUser.id, session.userId, 'fetchCurrentUserQuery returned unexpected user'); + + const currentUserId = await fetchCurrentUserIdQuery(); + assert.equal(currentUserId.currentUserId, session.userId, 'fetchCurrentUserIdQuery returned unexpected user'); + + const usersArgs = { + first: 3, + select: { + id: true, + username: true, + }, + } as const; + const usersData = await fetchUsersQuery(usersArgs); + assert.ok(Array.isArray(usersData.users.nodes), 'fetchUsersQuery users.nodes should be an array'); + + const userData = await fetchUserQuery({ + id: session.userId, + select: { + id: true, + username: true, + }, + }); + assert.equal(userData.user?.id, session.userId, 'fetchUserQuery returned unexpected user'); + + await prefetchCurrentUserQuery(queryClient, { + select: { + id: true, + username: true, + }, + }); + await prefetchCurrentUserIdQuery(queryClient); + await prefetchUsersQuery(queryClient, usersArgs); + await prefetchUserQuery(queryClient, { id: session.userId }); + + assert.ok(queryClient.getQueryData(currentUserQueryKey()), 'currentUser cache entry missing'); + assert.ok(queryClient.getQueryData(currentUserIdQueryKey()), 'currentUserId cache entry missing'); + assert.ok(queryClient.getQueryData(usersQueryKey(usersArgs)), 'users cache entry missing'); + assert.ok( + queryClient.getQueryData(userQueryKey(session.userId)), + 'user detail cache entry missing' + ); + + queryClient.clear(); + }); + + test('useCheckPasswordMutation succeeds for the signed-in user', async () => { + await ensureAuth(); + const queryClient = createTestQueryClient(); + const hook = await renderHookWithClient( + () => + useCheckPasswordMutation({ + select: { + clientMutationId: true, + }, + }), + queryClient + ); + + let result: + | { + checkPassword: { + clientMutationId?: string | null; + }; + } + | null = null; + + await act(async () => { + result = await hook.getResult().mutateAsync({ + input: { + password: liveEnv.password, + }, + }); + }); + + assert.ok(result?.checkPassword, 'useCheckPasswordMutation returned empty payload'); + + await hook.unmount(); + queryClient.clear(); + }); + + test('useSignOutMutation signs out current session', async () => { + const session = await ensureAuth(); + const queryClient = createTestQueryClient(); + const hook = await renderHookWithClient( + () => + useSignOutMutation({ + select: { + clientMutationId: true, + }, + }), + queryClient + ); + + let result: + | { + signOut: { + clientMutationId?: string | null; + }; + } + | null = null; + + await act(async () => { + result = await hook.getResult().mutateAsync({ + input: {}, + }); + }); + + assert.ok(result?.signOut, 'useSignOutMutation returned empty payload'); + sharedSession = null; + configureHooks(liveEnv); + await signOut(liveEnv, session.token); + + await hook.unmount(); + queryClient.clear(); + }); + }); +} diff --git a/graphql/test-app/tests/live-test-utils.ts b/graphql/test-app/tests/live-test-utils.ts new file mode 100644 index 000000000..9befa2135 --- /dev/null +++ b/graphql/test-app/tests/live-test-utils.ts @@ -0,0 +1,130 @@ +import assert from 'node:assert/strict'; + +import { QueryClient } from '@tanstack/react-query'; + +import { configure } from '../src/generated/hooks/client'; +import { createClient } from '../src/generated/orm'; + +export interface LiveTestEnv { + endpoint: string; + email: string; + password: string; +} + +const DEFAULT_ENDPOINT = 'https://api.launchql.dev/graphql'; + +export function getLiveTestEnv(): LiveTestEnv | null { + const email = process.env.GRAPHQL_TEST_EMAIL; + const password = process.env.GRAPHQL_TEST_PASSWORD; + const endpoint = process.env.GRAPHQL_TEST_ENDPOINT ?? DEFAULT_ENDPOINT; + + if (!email || !password) { + return null; + } + + return { + endpoint, + email, + password, + }; +} + +export function getLiveEnvHelpMessage(): string { + return ( + 'Live tests require GRAPHQL_TEST_EMAIL and GRAPHQL_TEST_PASSWORD. ' + + `Optional GRAPHQL_TEST_ENDPOINT (default: ${DEFAULT_ENDPOINT}).` + ); +} + +export function assertLiveEnvConfigured( + env: LiveTestEnv | null +): asserts env is LiveTestEnv { + if (!env) { + if (process.env.GRAPHQL_TEST_LIVE_REQUIRED === '1') { + throw new Error(getLiveEnvHelpMessage()); + } + } +} + +export function createTestQueryClient(): QueryClient { + return new QueryClient({ + defaultOptions: { + queries: { + retry: false, + refetchOnWindowFocus: false, + }, + mutations: { + retry: false, + }, + }, + }); +} + +export function configureHooks(env: LiveTestEnv, token?: string): void { + configure({ + endpoint: env.endpoint, + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); +} + +export interface AuthSession { + token: string; + userId: string; +} + +export async function signIn(env: LiveTestEnv): Promise { + const client = createClient({ endpoint: env.endpoint }); + const result = await client.mutation + .signIn( + { + input: { + email: env.email, + password: env.password, + rememberMe: true, + }, + }, + { + select: { + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + totpEnabled: true, + }, + }, + }, + } + ) + .unwrap(); + + const token = result.signIn.result?.accessToken ?? null; + const userId = result.signIn.result?.userId ?? null; + + assert.ok(token, 'signIn did not return accessToken'); + assert.ok(userId, 'signIn did not return userId'); + + return { token, userId }; +} + +export async function signOut(env: LiveTestEnv, token: string): Promise { + const client = createClient({ + endpoint: env.endpoint, + headers: { Authorization: `Bearer ${token}` }, + }); + + try { + await client.mutation + .signOut( + { input: {} }, + { + select: { + clientMutationId: true, + }, + } + ) + .unwrap(); + } catch { + // Best-effort cleanup: token might already be invalidated. + } +} diff --git a/graphql/test-app/tests/orm.live.test.ts b/graphql/test-app/tests/orm.live.test.ts new file mode 100644 index 000000000..d40952864 --- /dev/null +++ b/graphql/test-app/tests/orm.live.test.ts @@ -0,0 +1,232 @@ +import assert from 'node:assert/strict'; +import { after, describe, test } from 'node:test'; + +import { createClient } from '../src/generated/orm'; + +import { + assertLiveEnvConfigured, + getLiveEnvHelpMessage, + getLiveTestEnv, + type AuthSession, + signIn, + signOut, +} from './live-test-utils'; + +const liveEnv = getLiveTestEnv(); +assertLiveEnvConfigured(liveEnv); + +if (!liveEnv) { + test.skip(getLiveEnvHelpMessage(), () => {}); +} else { + describe('ORM Client (live endpoint)', () => { + let sharedSession: AuthSession | null = null; + + const ensureAuth = async (): Promise => { + if (!sharedSession) { + sharedSession = await signIn(liveEnv); + } + return sharedSession; + }; + + const createAuthedClient = async () => { + const session = await ensureAuth(); + return createClient({ + endpoint: liveEnv.endpoint, + headers: { Authorization: `Bearer ${session.token}` }, + }); + }; + + after(async () => { + if (sharedSession) { + await signOut(liveEnv, sharedSession.token); + sharedSession = null; + } + }); + + test('custom signIn mutation returns token with explicit select', async () => { + const client = createClient({ endpoint: liveEnv.endpoint }); + const result = await client.mutation + .signIn( + { + input: { + email: liveEnv.email, + password: liveEnv.password, + rememberMe: true, + }, + }, + { + select: { + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + }, + }, + }, + } + ) + .unwrap(); + + const token = result.signIn.result?.accessToken ?? null; + assert.ok(token, 'ORM signIn did not return accessToken'); + await signOut(liveEnv, token); + }); + + test('custom signIn mutation default select path returns payload shape', async () => { + const client = createClient({ endpoint: liveEnv.endpoint }); + + const result = await client.mutation.signIn( + { + input: { + email: liveEnv.email, + password: liveEnv.password, + rememberMe: true + } + }, + { + select: { + clientMutationId: true, + result: { + select: { + accessToken: true, + userId: true, + isVerified: true, + totpEnabled: true, + }, + }, + }, + } + ).unwrap(); + + assert.ok(result.signIn, 'Default signIn payload is missing'); + assert.ok(result.signIn?.result?.accessToken, 'Default signIn result is missing accessToken'); + await ensureAuth(); + }); + + test('model findMany builds expected GraphQL and executes nested select', async () => { + const client = await createAuthedClient(); + + const builder = client.user.findMany({ + first: 5, + orderBy: ['CREATED_AT_DESC'], + select: { + id: true, + username: true, + databasesByOwnerId: { + first: 1, + select: { + id: true, + schemaName: true, + }, + }, + }, + }); + + const graphql = builder.toGraphQL(); + const variables = builder.getVariables() ?? {}; + assert.match(graphql, /query UserQuery/); + assert.match(graphql, /users\(/); + assert.match(graphql, /databasesByOwnerId\(first: 1\)/); + assert.equal(variables.first, 5, 'findMany first variable should be emitted'); + + const result = await builder.unwrap(); + assert.ok(Array.isArray(result.users.nodes), 'users.nodes should be an array'); + }); + + test('model findOne overloads work for default and explicit select', async () => { + const session = await ensureAuth(); + const client = await createAuthedClient(); + + const defaultResult = await client.user.findOne({ id: session.userId }).unwrap(); + assert.equal(defaultResult.user?.id, session.userId, 'findOne default select returned wrong user'); + + const explicitResult = await client.user + .findOne({ + id: session.userId, + select: { + id: true, + username: true, + displayName: true, + }, + }) + .unwrap(); + + assert.equal(explicitResult.user?.id, session.userId, 'findOne explicit select returned wrong user'); + }); + + test('model findFirst and custom currentUser/currentUserId queries execute', async () => { + const session = await ensureAuth(); + const client = await createAuthedClient(); + + const firstResult = await client.user + .findFirst({ + select: { + id: true, + username: true, + }, + }) + .unwrap(); + assert.ok(Array.isArray(firstResult.users.nodes), 'findFirst should return users.nodes array'); + + const currentUserResult = await client.query + .currentUser({ + select: { + id: true, + username: true, + }, + }) + .unwrap(); + assert.equal( + currentUserResult.currentUser.id, + session.userId, + 'currentUser query returned unexpected user' + ); + + const currentUserIdResult = await client.query.currentUserId().unwrap(); + assert.equal( + currentUserIdResult.currentUserId, + session.userId, + 'currentUserId query returned unexpected user' + ); + }); + + test('custom checkPassword and signOut mutations execute successfully', async () => { + const session = await ensureAuth(); + const client = await createAuthedClient(); + + const checkPasswordResult = await client.mutation + .checkPassword( + { + input: { + password: liveEnv.password, + }, + }, + { + select: { + clientMutationId: true, + }, + } + ) + .unwrap(); + assert.ok(checkPasswordResult.checkPassword, 'checkPassword payload missing'); + + const signOutResult = await client.mutation + .signOut( + { + input: {}, + }, + { + select: { + clientMutationId: true, + }, + } + ) + .unwrap(); + assert.ok(signOutResult.signOut, 'signOut payload missing'); + + sharedSession = null; + await signOut(liveEnv, session.token); + }); + }); +} diff --git a/graphql/test-app/tsconfig.json b/graphql/test-app/tsconfig.json new file mode 100644 index 000000000..4bad3d23e --- /dev/null +++ b/graphql/test-app/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "strictNullChecks": false, + "noEmit": true, + "esModuleInterop": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "resolveJsonModule": true, + "isolatedModules": true, + "allowImportingTsExtensions": true + }, + "include": ["src"] +} diff --git a/graphql/test-app/vite.config.ts b/graphql/test-app/vite.config.ts new file mode 100644 index 000000000..2e3f72d38 --- /dev/null +++ b/graphql/test-app/vite.config.ts @@ -0,0 +1,19 @@ +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; +import { visualizer } from 'rollup-plugin-visualizer'; + +export default defineConfig({ + plugins: [ + react(), + visualizer({ + filename: 'dist/bundle-stats.html', + open: false, + gzipSize: true, + brotliSize: true, + template: 'treemap', + }), + ], + server: { + port: 5199, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e00bbd5de..b329b3a2c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,7 @@ settings: packageExtensionsChecksum: sha256-x8B4zkJ4KLRX+yspUWxuggXWlz6zrBLSIh72pNhpPiE= importers: + .: devDependencies: '@jest/test-sequencer': @@ -77,10 +78,10 @@ importers: version: link:../../packages/postmaster/dist '@launchql/mjml': specifier: 0.1.1 - version: 0.1.1(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3) + version: 0.1.1(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3) '@launchql/styled-email': specifier: 0.1.0 - version: 0.1.0(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3) + version: 0.1.0(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3) '@pgpmjs/env': specifier: workspace:^ version: link:../../pgpm/env/dist @@ -1239,6 +1240,58 @@ importers: version: 0.1.10 publishDirectory: dist + graphql/test-app: + dependencies: + '@0no-co/graphql.web': + specifier: ^1.2.0 + version: 1.2.0(graphql@15.10.1) + '@constructive-io/graphql-types': + specifier: workspace:^ + version: link:../types/dist + '@tanstack/react-query': + specifier: ^5.90.20 + version: 5.90.20(react@19.2.3) + gql-ast: + specifier: workspace:^ + version: link:../gql-ast/dist + graphql: + specifier: 15.10.1 + version: 15.10.1 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@constructive-io/graphql-codegen': + specifier: workspace:^ + version: link:../codegen/dist + '@types/react': + specifier: ^19.2.13 + version: 19.2.13 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.13) + '@vitejs/plugin-react': + specifier: ^4.5.2 + version: 4.7.0(vite@6.4.1(@types/node@20.19.27)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + react-test-renderer: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + rollup-plugin-visualizer: + specifier: ^6.0.5 + version: 6.0.5(rollup@4.57.1) + tsx: + specifier: ^4.20.3 + version: 4.21.0 + typescript: + specifier: ^5.8.3 + version: 5.9.3 + vite: + specifier: ^6.3.5 + version: 6.4.1(@types/node@20.19.27)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + graphql/types: dependencies: '@pgpmjs/types': @@ -2432,11 +2485,9 @@ importers: publishDirectory: dist packages: + '@0no-co/graphql.web@1.2.0': - resolution: - { - integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==, - } + resolution: {integrity: sha512-/1iHy9TTr63gE1YcR5idjx8UREz1s0kFhydf3bBLCXyqjhkIc6igAzTOx3zPifCwFR87tsh/4Pa9cNts6d2otw==} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 peerDependenciesMeta: @@ -2444,399 +2495,228 @@ packages: optional: true '@aws-crypto/crc32@5.2.0': - resolution: - { - integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} '@aws-crypto/crc32c@5.2.0': - resolution: - { - integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==, - } + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} '@aws-crypto/sha1-browser@5.2.0': - resolution: - { - integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==, - } + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} '@aws-crypto/sha256-browser@5.2.0': - resolution: - { - integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==, - } + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} '@aws-crypto/sha256-js@5.2.0': - resolution: - { - integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} '@aws-crypto/supports-web-crypto@5.2.0': - resolution: - { - integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==, - } + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} '@aws-crypto/util@5.2.0': - resolution: - { - integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==, - } + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} '@aws-sdk/client-s3@3.971.0': - resolution: - { - integrity: sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-BBUne390fKa4C4QvZlUZ5gKcu+Uyid4IyQ20N4jl0vS7SK2xpfXlJcgKqPW5ts6kx6hWTQBk6sH5Lf12RvuJxg==} + engines: {node: '>=20.0.0'} '@aws-sdk/client-sesv2@3.969.0': - resolution: - { - integrity: sha512-YnBJRtueyNAeKJvRNBVAeH9fh5X8KmMa4fp1Zn1Hex0G5bRKm0aUdS4i+p5cOIpCyBV9hyLGGkaCBDC4Han7aw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-YnBJRtueyNAeKJvRNBVAeH9fh5X8KmMa4fp1Zn1Hex0G5bRKm0aUdS4i+p5cOIpCyBV9hyLGGkaCBDC4Han7aw==} + engines: {node: '>=20.0.0'} '@aws-sdk/client-sso@3.969.0': - resolution: - { - integrity: sha512-Qn0Uz6o15q2S+1E6OpwRKmaAMoT4LktEn+Oibk28qb2Mne+emaDawhZXahOJb/wFw5lN2FEH7XoiSNenNNUmCw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-Qn0Uz6o15q2S+1E6OpwRKmaAMoT4LktEn+Oibk28qb2Mne+emaDawhZXahOJb/wFw5lN2FEH7XoiSNenNNUmCw==} + engines: {node: '>=20.0.0'} '@aws-sdk/client-sso@3.971.0': - resolution: - { - integrity: sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-Xx+w6DQqJxDdymYyIxyKJnRzPvVJ4e/Aw0czO7aC9L/iraaV7AG8QtRe93OGW6aoHSh72CIiinnpJJfLsQqP4g==} + engines: {node: '>=20.0.0'} '@aws-sdk/core@3.969.0': - resolution: - { - integrity: sha512-qqmQt4z5rEK1OYVkVkboWgy/58CC5QaQ7oy0tvLe3iri/mfZbgJkA+pkwQyRP827DfCBZ3W7Ki9iwSa+B2U7uQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-qqmQt4z5rEK1OYVkVkboWgy/58CC5QaQ7oy0tvLe3iri/mfZbgJkA+pkwQyRP827DfCBZ3W7Ki9iwSa+B2U7uQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/core@3.970.0': - resolution: - { - integrity: sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-klpzObldOq8HXzDjDlY6K8rMhYZU6mXRz6P9F9N+tWnjoYFfeBMra8wYApydElTUYQKP1O7RLHwH1OKFfKcqIA==} + engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.969.0': - resolution: - { - integrity: sha512-IGNkP54HD3uuLnrPCYsv3ZD478UYq+9WwKrIVJ9Pdi3hxPg8562CH3ZHf8hEgfePN31P9Kj+Zu9kq2Qcjjt61A==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-IGNkP54HD3uuLnrPCYsv3ZD478UYq+9WwKrIVJ9Pdi3hxPg8562CH3ZHf8hEgfePN31P9Kj+Zu9kq2Qcjjt61A==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-env@3.969.0': - resolution: - { - integrity: sha512-yS96heH5XDUqS3qQNcdObKKMOqZaivuNInMVRpRli48aXW8fX1M3fY67K/Onlqa3Wxu6WfDc3ZGF52SywdLvbg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-yS96heH5XDUqS3qQNcdObKKMOqZaivuNInMVRpRli48aXW8fX1M3fY67K/Onlqa3Wxu6WfDc3ZGF52SywdLvbg==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-env@3.970.0': - resolution: - { - integrity: sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-rtVzXzEtAfZBfh+lq3DAvRar4c3jyptweOAJR2DweyXx71QSMY+O879hjpMwES7jl07a3O1zlnFIDo4KP/96kQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-http@3.969.0': - resolution: - { - integrity: sha512-QCEFxBiUYFUW5VG6k8jKhT4luZndpC7uUY4u1olwt+OnJrl3N2yC7oS34isVBa3ioXZ4A0YagbXTa/3mXUhlAA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-QCEFxBiUYFUW5VG6k8jKhT4luZndpC7uUY4u1olwt+OnJrl3N2yC7oS34isVBa3ioXZ4A0YagbXTa/3mXUhlAA==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-http@3.970.0': - resolution: - { - integrity: sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-CjDbWL7JxjLc9ZxQilMusWSw05yRvUJKRpz59IxDpWUnSMHC9JMMUUkOy5Izk8UAtzi6gupRWArp4NG4labt9Q==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-ini@3.969.0': - resolution: - { - integrity: sha512-lsXyTDkUrZPxjr0XruZrqdcHY9zHcIuoY3TOCQEm23VTc8Np2BenTtjGAIexkL3ar69K4u3FVLQroLpmFxeXqA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-lsXyTDkUrZPxjr0XruZrqdcHY9zHcIuoY3TOCQEm23VTc8Np2BenTtjGAIexkL3ar69K4u3FVLQroLpmFxeXqA==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-ini@3.971.0': - resolution: - { - integrity: sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-c0TGJG4xyfTZz3SInXfGU8i5iOFRrLmy4Bo7lMyH+IpngohYMYGYl61omXqf2zdwMbDv+YJ9AviQTcCaEUKi8w==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-login@3.969.0': - resolution: - { - integrity: sha512-bIRFDf54qIUFFLTZNYt40d6EseNeK9w80dHEs7BVEAWoS23c9+MSqkdg/LJBBK9Kgy01vRmjiedfBZN+jGypLw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-bIRFDf54qIUFFLTZNYt40d6EseNeK9w80dHEs7BVEAWoS23c9+MSqkdg/LJBBK9Kgy01vRmjiedfBZN+jGypLw==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-login@3.971.0': - resolution: - { - integrity: sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-yhbzmDOsk0RXD3rTPhZra4AWVnVAC4nFWbTp+sUty1hrOPurUmhuz8bjpLqYTHGnlMbJp+UqkQONhS2+2LzW2g==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-node@3.969.0': - resolution: - { - integrity: sha512-lImMjcy/5SGDIBk7PFJCqFO4rFuapKCvo1z2PidD3Cbz2D7wsJnyqUNQIp5Ix0Xc3/uAYG9zXI9kgaMf1dspIQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-lImMjcy/5SGDIBk7PFJCqFO4rFuapKCvo1z2PidD3Cbz2D7wsJnyqUNQIp5Ix0Xc3/uAYG9zXI9kgaMf1dspIQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-node@3.971.0': - resolution: - { - integrity: sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-epUJBAKivtJqalnEBRsYIULKYV063o/5mXNJshZfyvkAgNIzc27CmmKRXTN4zaNOZg8g/UprFp25BGsi19x3nQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-process@3.969.0': - resolution: - { - integrity: sha512-2qQkM0rwd8Hl9nIHtUaqT8Z/djrulovqx/wBHsbRKaISwc2fiT3De1Lk1jx34Jzrz/dTHAMJJi+cML1N4Lk3kw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-2qQkM0rwd8Hl9nIHtUaqT8Z/djrulovqx/wBHsbRKaISwc2fiT3De1Lk1jx34Jzrz/dTHAMJJi+cML1N4Lk3kw==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-process@3.970.0': - resolution: - { - integrity: sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-0XeT8OaT9iMA62DFV9+m6mZfJhrD0WNKf4IvsIpj2Z7XbaYfz3CoDDvNoALf3rPY9NzyMHgDxOspmqdvXP00mw==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-sso@3.969.0': - resolution: - { - integrity: sha512-JHqXw9Ct3dtZB86/zGFJYWyodr961GyIrqTBhV0brrZFPvcinM9abDSK58jt6GNBM2lqfMCvXL6I4ahNsMdkrg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-JHqXw9Ct3dtZB86/zGFJYWyodr961GyIrqTBhV0brrZFPvcinM9abDSK58jt6GNBM2lqfMCvXL6I4ahNsMdkrg==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-sso@3.971.0': - resolution: - { - integrity: sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-dY0hMQ7dLVPQNJ8GyqXADxa9w5wNfmukgQniLxGVn+dMRx3YLViMp5ZpTSQpFhCWNF0oKQrYAI5cHhUJU1hETw==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-web-identity@3.969.0': - resolution: - { - integrity: sha512-mKCZtqrs3ts3YmIjT4NFlYgT2Oe6syW0nX5m2l7iyrFrLXw26Zo3rx29DjGzycPdJHZZvsIy5y6yqChDuF65ng==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-mKCZtqrs3ts3YmIjT4NFlYgT2Oe6syW0nX5m2l7iyrFrLXw26Zo3rx29DjGzycPdJHZZvsIy5y6yqChDuF65ng==} + engines: {node: '>=20.0.0'} '@aws-sdk/credential-provider-web-identity@3.971.0': - resolution: - { - integrity: sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-F1AwfNLr7H52T640LNON/h34YDiMuIqW/ZreGzhRR6vnFGaSPtNSKAKB2ssAMkLM8EVg8MjEAYD3NCUiEo+t/w==} + engines: {node: '>=20.0.0'} '@aws-sdk/lib-storage@3.958.0': - resolution: - { - integrity: sha512-cd8CTiJ165ep2DKTc2PHHhVCxDn3byv10BXMGn+lkDY3KwMoatcgZ1uhFWCBuJvsCUnSExqGouJN/Q0qgjkWtg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-cd8CTiJ165ep2DKTc2PHHhVCxDn3byv10BXMGn+lkDY3KwMoatcgZ1uhFWCBuJvsCUnSExqGouJN/Q0qgjkWtg==} + engines: {node: '>=18.0.0'} peerDependencies: '@aws-sdk/client-s3': ^3.958.0 '@aws-sdk/middleware-bucket-endpoint@3.969.0': - resolution: - { - integrity: sha512-MlbrlixtkTVhYhoasblKOkr7n2yydvUZjjxTnBhIuHmkyBS1619oGnTfq/uLeGYb4NYXdeQ5OYcqsRGvmWSuTw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-MlbrlixtkTVhYhoasblKOkr7n2yydvUZjjxTnBhIuHmkyBS1619oGnTfq/uLeGYb4NYXdeQ5OYcqsRGvmWSuTw==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-expect-continue@3.969.0': - resolution: - { - integrity: sha512-qXygzSi8osok7tH9oeuS3HoKw6jRfbvg5Me/X5RlHOvSSqQz8c5O9f3MjUApaCUSwbAU92KrbZWasw2PKiaVHg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-qXygzSi8osok7tH9oeuS3HoKw6jRfbvg5Me/X5RlHOvSSqQz8c5O9f3MjUApaCUSwbAU92KrbZWasw2PKiaVHg==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-flexible-checksums@3.971.0': - resolution: - { - integrity: sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-+hGUDUxeIw8s2kkjfeXym0XZxdh0cqkHkDpEanWYdS1gnWkIR+gf9u/DKbKqGHXILPaqHXhWpLTQTVlaB4sI7Q==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.969.0': - resolution: - { - integrity: sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-location-constraint@3.969.0': - resolution: - { - integrity: sha512-zH7pDfMLG/C4GWMOpvJEoYcSpj7XsNP9+irlgqwi667sUQ6doHQJ3yyDut3yiTk0maq1VgmriPFELyI9lrvH/g==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-zH7pDfMLG/C4GWMOpvJEoYcSpj7XsNP9+irlgqwi667sUQ6doHQJ3yyDut3yiTk0maq1VgmriPFELyI9lrvH/g==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-logger@3.969.0': - resolution: - { - integrity: sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-recursion-detection@3.969.0': - resolution: - { - integrity: sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-sdk-s3@3.969.0': - resolution: - { - integrity: sha512-xjcyZrbtvVaqkmjkhmqX+16Wf7zFVS/cYnNFu/JyG6ekkIxSXEAjptNwSEDzlAiLzf0Hf6dYj5erLZYGa40eWg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-xjcyZrbtvVaqkmjkhmqX+16Wf7zFVS/cYnNFu/JyG6ekkIxSXEAjptNwSEDzlAiLzf0Hf6dYj5erLZYGa40eWg==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-sdk-s3@3.970.0': - resolution: - { - integrity: sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-v/Y5F1lbFFY7vMeG5yYxuhnn0CAshz6KMxkz1pDyPxejNE9HtA0w8R6OTBh/bVdIm44QpjhbI7qeLdOE/PLzXQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.971.0': - resolution: - { - integrity: sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-QGVhvRveYG64ZhnS/b971PxXM6N2NU79Fxck4EfQ7am8v1Br0ctoeDDAn9nXNblLGw87we9Z65F7hMxxiFHd3w==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-user-agent@3.969.0': - resolution: - { - integrity: sha512-Y6WkW8QQ2X9jG9HNBWyzp5KlJOCtLqX8VIvGLoGc2wXdZH7dgOy62uFhkfnHbgfiel6fkNYaycjGx/yyxi0JLQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-Y6WkW8QQ2X9jG9HNBWyzp5KlJOCtLqX8VIvGLoGc2wXdZH7dgOy62uFhkfnHbgfiel6fkNYaycjGx/yyxi0JLQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/middleware-user-agent@3.970.0': - resolution: - { - integrity: sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-dnSJGGUGSFGEX2NzvjwSefH+hmZQ347AwbLhAsi0cdnISSge+pcGfOFrJt2XfBIypwFe27chQhlfuf/gWdzpZg==} + engines: {node: '>=20.0.0'} '@aws-sdk/nested-clients@3.969.0': - resolution: - { - integrity: sha512-MJrejgODxVYZjQjSpPLJkVuxnbrue1x1R8+as3anT5V/wk9Qc/Pf5B1IFjM3Ak6uOtzuRYNY4auOvcg4U8twDA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-MJrejgODxVYZjQjSpPLJkVuxnbrue1x1R8+as3anT5V/wk9Qc/Pf5B1IFjM3Ak6uOtzuRYNY4auOvcg4U8twDA==} + engines: {node: '>=20.0.0'} '@aws-sdk/nested-clients@3.971.0': - resolution: - { - integrity: sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-TWaILL8GyYlhGrxxnmbkazM4QsXatwQgoWUvo251FXmUOsiXDFDVX3hoGIfB3CaJhV2pJPfebHUNJtY6TjZ11g==} + engines: {node: '>=20.0.0'} '@aws-sdk/region-config-resolver@3.969.0': - resolution: - { - integrity: sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/signature-v4-multi-region@3.969.0': - resolution: - { - integrity: sha512-pv8BEQOlUzK+ww8ZfXZOnDzLfPO5+O7puBFtU1fE8CdCAQ/RP/B1XY3hxzW9Xs0dax7graYKnY8wd8ooYy7vBw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-pv8BEQOlUzK+ww8ZfXZOnDzLfPO5+O7puBFtU1fE8CdCAQ/RP/B1XY3hxzW9Xs0dax7graYKnY8wd8ooYy7vBw==} + engines: {node: '>=20.0.0'} '@aws-sdk/signature-v4-multi-region@3.970.0': - resolution: - { - integrity: sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-z3syXfuK/x/IsKf/AeYmgc2NT7fcJ+3fHaGO+fkghkV9WEba3fPyOwtTBX4KpFMNb2t50zDGZwbzW1/5ighcUQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/token-providers@3.969.0': - resolution: - { - integrity: sha512-ucs6QczPkvGinbGmhMlPCQnagGJ+xsM6itsSWlJzxo9YsP6jR75cBU8pRdaM7nEbtCDnrUHf8W9g3D2Hd9mgVA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-ucs6QczPkvGinbGmhMlPCQnagGJ+xsM6itsSWlJzxo9YsP6jR75cBU8pRdaM7nEbtCDnrUHf8W9g3D2Hd9mgVA==} + engines: {node: '>=20.0.0'} '@aws-sdk/token-providers@3.971.0': - resolution: - { - integrity: sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-4hKGWZbmuDdONMJV0HJ+9jwTDb0zLfKxcCLx2GEnBY31Gt9GeyIQ+DZ97Bb++0voawj6pnZToFikXTyrEq2x+w==} + engines: {node: '>=20.0.0'} '@aws-sdk/types@3.969.0': - resolution: - { - integrity: sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-arn-parser@3.968.0': - resolution: - { - integrity: sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-gqqvYcitIIM2K4lrDX9de9YvOfXBcVdxfT/iLnvHJd4YHvSXlt+gs+AsL4FfPCxG4IG9A+FyulP9Sb1MEA75vw==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-endpoints@3.969.0': - resolution: - { - integrity: sha512-H2x2UwYiA1pHg40jE+OCSc668W9GXRShTiCWy1UPKtZKREbQ63Mgd7NAj+bEMsZUSCdHywqmSsLqKM9IcqQ3Bg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-H2x2UwYiA1pHg40jE+OCSc668W9GXRShTiCWy1UPKtZKREbQ63Mgd7NAj+bEMsZUSCdHywqmSsLqKM9IcqQ3Bg==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-endpoints@3.970.0': - resolution: - { - integrity: sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-TZNZqFcMUtjvhZoZRtpEGQAdULYiy6rcGiXAbLU7e9LSpIYlRqpLa207oMNfgbzlL2PnHko+eVg8rajDiSOYCg==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-locate-window@3.965.2': - resolution: - { - integrity: sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==} + engines: {node: '>=20.0.0'} '@aws-sdk/util-user-agent-browser@3.969.0': - resolution: - { - integrity: sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==, - } + resolution: {integrity: sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==} '@aws-sdk/util-user-agent-node@3.969.0': - resolution: - { - integrity: sha512-D11ZuXNXdUMv8XTthMx+LPzkYNQAeQ68FnCTGnFLgLpnR8hVTeZMBBKjQ77wYGzWDk/csHKdCy697gU1On5KjA==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-D11ZuXNXdUMv8XTthMx+LPzkYNQAeQ68FnCTGnFLgLpnR8hVTeZMBBKjQ77wYGzWDk/csHKdCy697gU1On5KjA==} + engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: @@ -2844,11 +2724,8 @@ packages: optional: true '@aws-sdk/util-user-agent-node@3.971.0': - resolution: - { - integrity: sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-Eygjo9mFzQYjbGY3MYO6CsIhnTwAMd3WmuFalCykqEmj2r5zf0leWrhPaqvA5P68V5JdGfPYgj7vhNOd6CtRBQ==} + engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: @@ -2856,798 +2733,657 @@ packages: optional: true '@aws-sdk/xml-builder@3.969.0': - resolution: - { - integrity: sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==, - } - engines: { node: '>=20.0.0' } + resolution: {integrity: sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==} + engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.3': - resolution: - { - integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} + engines: {node: '>=18.0.0'} '@babel/code-frame@7.27.1': - resolution: - { - integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} '@babel/code-frame@7.28.6': - resolution: - { - integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} '@babel/compat-data@7.28.6': - resolution: - { - integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + engines: {node: '>=6.9.0'} '@babel/core@7.28.6': - resolution: - { - integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + engines: {node: '>=6.9.0'} '@babel/generator@7.28.6': - resolution: - { - integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} + engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': - resolution: - { - integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.28.6': - resolution: - { - integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} '@babel/helper-globals@7.28.0': - resolution: - { - integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.27.1': - resolution: - { - integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.28.6': - resolution: - { - integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.28.6': - resolution: - { - integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-plugin-utils@7.27.1': - resolution: - { - integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} '@babel/helper-plugin-utils@7.28.6': - resolution: - { - integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': - resolution: - { - integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.28.5': - resolution: - { - integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': - resolution: - { - integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} '@babel/helpers@7.28.6': - resolution: - { - integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} '@babel/parser@7.28.6': - resolution: - { - integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + engines: {node: '>=6.0.0'} hasBin: true '@babel/plugin-syntax-async-generators@7.8.4': - resolution: - { - integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, - } + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-bigint@7.8.3': - resolution: - { - integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, - } + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-properties@7.12.13': - resolution: - { - integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, - } + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-class-static-block@7.14.5': - resolution: - { - integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: - { - integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-import-meta@7.10.4': - resolution: - { - integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, - } + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-json-strings@7.8.3': - resolution: - { - integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, - } + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-jsx@7.27.1': - resolution: - { - integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-jsx@7.28.6': - resolution: - { - integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-logical-assignment-operators@7.10.4': - resolution: - { - integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, - } + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': - resolution: - { - integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, - } + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-numeric-separator@7.10.4': - resolution: - { - integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, - } + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-object-rest-spread@7.8.3': - resolution: - { - integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, - } + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-catch-binding@7.8.3': - resolution: - { - integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, - } + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-optional-chaining@7.8.3': - resolution: - { - integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, - } + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-private-property-in-object@7.14.5': - resolution: - { - integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-top-level-await@7.14.5': - resolution: - { - integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/plugin-syntax-typescript@7.28.6': - resolution: - { - integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 '@babel/runtime-corejs3@7.28.4': - resolution: - { - integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} + engines: {node: '>=6.9.0'} '@babel/runtime@7.28.4': - resolution: - { - integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} '@babel/template@7.27.2': - resolution: - { - integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} '@babel/template@7.28.6': - resolution: - { - integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.28.5': - resolution: - { - integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} '@babel/traverse@7.28.6': - resolution: - { - integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} + engines: {node: '>=6.9.0'} '@babel/types@7.28.5': - resolution: - { - integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} '@babel/types@7.28.6': - resolution: - { - integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': - resolution: - { - integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, - } + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} '@cspotcode/source-map-support@0.8.1': - resolution: - { - integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} '@emnapi/core@1.7.1': - resolution: - { - integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==, - } + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} '@emnapi/core@1.8.1': - resolution: - { - integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==, - } + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} '@emnapi/runtime@1.7.1': - resolution: - { - integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==, - } + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/runtime@1.8.1': - resolution: - { - integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==, - } + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} '@emnapi/wasi-threads@1.1.0': - resolution: - { - integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==, - } + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} '@emotion/is-prop-valid@1.4.0': - resolution: - { - integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==, - } + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} '@emotion/memoize@0.9.0': - resolution: - { - integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==, - } + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} '@emotion/stylis@0.8.5': - resolution: - { - integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==, - } + resolution: {integrity: sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==} '@emotion/unitless@0.7.5': - resolution: - { - integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==, - } + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] '@esbuild/aix-ppc64@0.27.2': - resolution: - { - integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.27.2': - resolution: - { - integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.27.2': - resolution: - { - integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.27.2': - resolution: - { - integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.27.2': - resolution: - { - integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.27.2': - resolution: - { - integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.27.2': - resolution: - { - integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.27.2': - resolution: - { - integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.27.2': - resolution: - { - integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.27.2': - resolution: - { - integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.27.2': - resolution: - { - integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.27.2': - resolution: - { - integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.27.2': - resolution: - { - integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.27.2': - resolution: - { - integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.27.2': - resolution: - { - integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.27.2': - resolution: - { - integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.27.2': - resolution: - { - integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-arm64@0.27.2': - resolution: - { - integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.27.2': - resolution: - { - integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-arm64@0.27.2': - resolution: - { - integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.27.2': - resolution: - { - integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/openharmony-arm64@0.27.2': - resolution: - { - integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.27.2': - resolution: - { - integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.27.2': - resolution: - { - integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.27.2': - resolution: - { - integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.27.2': - resolution: - { - integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + engines: {node: '>=18'} cpu: [x64] os: [win32] '@eslint-community/eslint-utils@4.9.0': - resolution: - { - integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/eslint-utils@4.9.1': - resolution: - { - integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.12.2': - resolution: - { - integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/config-array@0.21.1': - resolution: - { - integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/config-helpers@0.4.2': - resolution: - { - integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.17.0': - resolution: - { - integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.3': - resolution: - { - integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/js@9.39.2': - resolution: - { - integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': - resolution: - { - integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/plugin-kit@0.4.1': - resolution: - { - integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@graphile-contrib/pg-many-to-many@1.0.2': - resolution: - { - integrity: sha512-ChSaSU7/n99Crdlink62cCGqlEYmjUJKizz2Nx0tdGgqSMkf6KTk00D3ILGybScywMcJGjJE2cc6FXYIHVlxCg==, - } + resolution: {integrity: sha512-ChSaSU7/n99Crdlink62cCGqlEYmjUJKizz2Nx0tdGgqSMkf6KTk00D3ILGybScywMcJGjJE2cc6FXYIHVlxCg==} '@graphile-contrib/pg-simplify-inflector@6.1.0': - resolution: - { - integrity: sha512-3eI2FP4ulu/fxwkJBNXhR6XEzqVz4wJWFr4LfeyUNNArUtLFx0DpP6YdcARCYgwLExFcIQNE8fnul3JKiciYIw==, - } + resolution: {integrity: sha512-3eI2FP4ulu/fxwkJBNXhR6XEzqVz4wJWFr4LfeyUNNArUtLFx0DpP6YdcARCYgwLExFcIQNE8fnul3JKiciYIw==} '@graphile/lru@4.11.0': - resolution: - { - integrity: sha512-Fakuk190EAKxWSa9YQyr/87g8mvAv8HBvk6yPCPuIoA3bYXF7n6kl0XSqKjSd5VfjEqhtnzQ6zJGzDf1Gv/tJg==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-Fakuk190EAKxWSa9YQyr/87g8mvAv8HBvk6yPCPuIoA3bYXF7n6kl0XSqKjSd5VfjEqhtnzQ6zJGzDf1Gv/tJg==} + engines: {node: '>=8.6'} '@graphql-typed-document-node/core@3.2.0': - resolution: - { - integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==, - } + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 '@humanfs/core@0.19.1': - resolution: - { - integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} '@humanfs/node@0.16.7': - resolution: - { - integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: '>=12.22' } + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} '@humanwhocodes/retry@0.4.3': - resolution: - { - integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, - } - engines: { node: '>=18.18' } + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} '@hutson/parse-repository-url@3.0.2': - resolution: - { - integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} + engines: {node: '>=6.9.0'} '@inquirer/external-editor@1.0.3': - resolution: - { - integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -3655,10 +3391,7 @@ packages: optional: true '@inquirerer/test@1.3.0': - resolution: - { - integrity: sha512-uKn1yJ66MKaPf8ECxoTRma6+lQSLy1YtBOXHDrTGn/j6xtCDdfDYw34h51gM0MLimPTd1vAoxMG+zQGXRSHZLg==, - } + resolution: {integrity: sha512-uKn1yJ66MKaPf8ECxoTRma6+lQSLy1YtBOXHDrTGn/j6xtCDdfDYw34h51gM0MLimPTd1vAoxMG+zQGXRSHZLg==} peerDependencies: jest: '>=29.0.0' peerDependenciesMeta: @@ -3666,65 +3399,38 @@ packages: optional: true '@inquirerer/utils@3.2.0': - resolution: - { - integrity: sha512-poeZdoOH/iDV9dG/J0apH92T5VOGiAgIp8me1yTK9wpEI2qGENyXXBmiYgcE2s+XMHuDr19R9d1UXE9ptaVneA==, - } + resolution: {integrity: sha512-poeZdoOH/iDV9dG/J0apH92T5VOGiAgIp8me1yTK9wpEI2qGENyXXBmiYgcE2s+XMHuDr19R9d1UXE9ptaVneA==} '@isaacs/balanced-match@4.0.1': - resolution: - { - integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} '@isaacs/brace-expansion@5.0.0': - resolution: - { - integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} '@isaacs/cliui@8.0.2': - resolution: - { - integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} '@isaacs/string-locale-compare@1.1.0': - resolution: - { - integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==, - } + resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} '@istanbuljs/load-nyc-config@1.1.0': - resolution: - { - integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} '@istanbuljs/schema@0.1.3': - resolution: - { - integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} '@jest/console@30.2.0': - resolution: - { - integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/core@30.2.0': - resolution: - { - integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -3732,67 +3438,40 @@ packages: optional: true '@jest/diff-sequences@30.0.1': - resolution: - { - integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/environment@30.2.0': - resolution: - { - integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect-utils@30.2.0': - resolution: - { - integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/expect@30.2.0': - resolution: - { - integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/fake-timers@30.2.0': - resolution: - { - integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/get-type@30.1.0': - resolution: - { - integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/globals@30.2.0': - resolution: - { - integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/pattern@30.0.1': - resolution: - { - integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/reporters@30.2.0': - resolution: - { - integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -3800,3161 +3479,1922 @@ packages: optional: true '@jest/schemas@29.6.3': - resolution: - { - integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} '@jest/schemas@30.0.5': - resolution: - { - integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/snapshot-utils@30.2.0': - resolution: - { - integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/source-map@30.0.1': - resolution: - { - integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-result@30.2.0': - resolution: - { - integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/test-sequencer@30.2.0': - resolution: - { - integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/transform@30.2.0': - resolution: - { - integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/types@26.6.2': - resolution: - { - integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==, - } - engines: { node: '>= 10.14.2' } + resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==} + engines: {node: '>= 10.14.2'} '@jest/types@30.2.0': - resolution: - { - integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jridgewell/gen-mapping@0.3.13': - resolution: - { - integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, - } + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@jridgewell/remapping@2.3.5': - resolution: - { - integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==, - } + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': - resolution: - { - integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.5.5': - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/trace-mapping@0.3.31': - resolution: - { - integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, - } + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': - resolution: - { - integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, - } + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} '@launchql/mjml@0.1.1': - resolution: - { - integrity: sha512-6+OEmECuu5atRZ43ovsMfFs+T4NWNaKbzNG0uA8HYaBSn3kWR7GH3QnmL3lCIeymLtvgua8aZChYvg6SxrQdnw==, - } + resolution: {integrity: sha512-6+OEmECuu5atRZ43ovsMfFs+T4NWNaKbzNG0uA8HYaBSn3kWR7GH3QnmL3lCIeymLtvgua8aZChYvg6SxrQdnw==} peerDependencies: react: '>=16' react-dom: '>=16' '@launchql/protobufjs@7.2.6': - resolution: - { - integrity: sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-vwi1nG2/heVFsIMHQU1KxTjUp5c757CTtRAZn/jutApCkFlle1iv8tzM/DHlSZJKDldxaYqnNYTg0pTyp8Bbtg==} + engines: {node: '>=12.0.0'} '@launchql/styled-email@0.1.0': - resolution: - { - integrity: sha512-ISjzsY+3EOH/qAKHPq3evw9QmmEyA8Vw+0pUf+Zf8l4/rAHJJKrSa/uPiaUf2Abi8yAZKyx2uyaZq4ExNNkD+w==, - } + resolution: {integrity: sha512-ISjzsY+3EOH/qAKHPq3evw9QmmEyA8Vw+0pUf+Zf8l4/rAHJJKrSa/uPiaUf2Abi8yAZKyx2uyaZq4ExNNkD+w==} peerDependencies: react: '>=16' react-dom: '>=16' '@lerna/create@8.2.4': - resolution: - { - integrity: sha512-A8AlzetnS2WIuhijdAzKUyFpR5YbLLfV3luQ4lzBgIBgRfuoBDZeF+RSZPhra+7A6/zTUlrbhKZIOi/MNhqgvQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-A8AlzetnS2WIuhijdAzKUyFpR5YbLLfV3luQ4lzBgIBgRfuoBDZeF+RSZPhra+7A6/zTUlrbhKZIOi/MNhqgvQ==} + engines: {node: '>=18.0.0'} '@napi-rs/wasm-runtime@0.2.12': - resolution: - { - integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==, - } + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} '@napi-rs/wasm-runtime@0.2.4': - resolution: - { - integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==, - } + resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} '@noble/hashes@1.8.0': - resolution: - { - integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==, - } - engines: { node: ^14.21.3 || >=16 } + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} '@nodelib/fs.scandir@2.1.5': - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} '@npmcli/agent@2.2.2': - resolution: - { - integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/arborist@7.5.4': - resolution: - { - integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-nWtIc6QwwoUORCRNzKx4ypHqCk3drI+5aeYdMTQQiRCcn4lOOgfQh7WyZobGYTxXPSq1VwV53lkpN/BRlRk08g==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true '@npmcli/fs@3.1.1': - resolution: - { - integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/git@5.0.8': - resolution: - { - integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/installed-package-contents@2.1.0': - resolution: - { - integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true '@npmcli/map-workspaces@3.0.6': - resolution: - { - integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/metavuln-calculator@7.1.1': - resolution: - { - integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Nkxf96V0lAx3HCpVda7Vw4P23RILgdi/5K1fmj2tZkWIYLpXAN8k2UVVOsW16TsS5F8Ws2I7Cm+PU1/rsVF47g==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/name-from-folder@2.0.0': - resolution: - { - integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/node-gyp@3.0.0': - resolution: - { - integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/package-json@5.2.0': - resolution: - { - integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/promise-spawn@7.0.2': - resolution: - { - integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/query@3.1.0': - resolution: - { - integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} '@npmcli/redact@2.0.1': - resolution: - { - integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==} + engines: {node: ^16.14.0 || >=18.0.0} '@npmcli/run-script@8.1.0': - resolution: - { - integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==} + engines: {node: ^16.14.0 || >=18.0.0} '@nx/devkit@20.8.3': - resolution: - { - integrity: sha512-5lbfJ6ICFOiGeirldQOU5fQ/W/VQ8L3dfWnmHG4UgpWSLoK/YFdRf4lTB4rS0aDXsBL0gyWABz3sZGLPGNYnPA==, - } + resolution: {integrity: sha512-5lbfJ6ICFOiGeirldQOU5fQ/W/VQ8L3dfWnmHG4UgpWSLoK/YFdRf4lTB4rS0aDXsBL0gyWABz3sZGLPGNYnPA==} peerDependencies: nx: '>= 19 <= 21' '@nx/nx-darwin-arm64@20.8.3': - resolution: - { - integrity: sha512-BeYnPAcnaerg6q+qR0bAb0nebwwrsvm4STSVqqVlaqLmmQpU3Bfpx44CEa5d6T9b0V11ZqVE/bkmRhMqhUcrhw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-BeYnPAcnaerg6q+qR0bAb0nebwwrsvm4STSVqqVlaqLmmQpU3Bfpx44CEa5d6T9b0V11ZqVE/bkmRhMqhUcrhw==} + engines: {node: '>= 10'} cpu: [arm64] os: [darwin] '@nx/nx-darwin-x64@20.8.3': - resolution: - { - integrity: sha512-RIFg1VkQ4jhI+ErqEZuIeGBcJGD8t+u9J5CdQBDIASd8QRhtudBkiYLYCJb+qaQly09G7nVfxuyItlS2uRW3qA==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-RIFg1VkQ4jhI+ErqEZuIeGBcJGD8t+u9J5CdQBDIASd8QRhtudBkiYLYCJb+qaQly09G7nVfxuyItlS2uRW3qA==} + engines: {node: '>= 10'} cpu: [x64] os: [darwin] '@nx/nx-freebsd-x64@20.8.3': - resolution: - { - integrity: sha512-boQTgMUdnqpZhHMrV/xgnp/dTg5dfxw8I4d16NBwmW4j+Sez7zi/dydgsJpfZsj8TicOHvPu6KK4W5wzp82NPw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-boQTgMUdnqpZhHMrV/xgnp/dTg5dfxw8I4d16NBwmW4j+Sez7zi/dydgsJpfZsj8TicOHvPu6KK4W5wzp82NPw==} + engines: {node: '>= 10'} cpu: [x64] os: [freebsd] '@nx/nx-linux-arm-gnueabihf@20.8.3': - resolution: - { - integrity: sha512-wpiNyY1igx1rLN3EsTLum2lDtblFijdBZB9/9u/6UDub4z9CaQ4yaC4h9n5v7yFYILwfL44YTsQKzrE+iv0y1Q==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-wpiNyY1igx1rLN3EsTLum2lDtblFijdBZB9/9u/6UDub4z9CaQ4yaC4h9n5v7yFYILwfL44YTsQKzrE+iv0y1Q==} + engines: {node: '>= 10'} cpu: [arm] os: [linux] '@nx/nx-linux-arm64-gnu@20.8.3': - resolution: - { - integrity: sha512-nbi/eZtJfWxuDwdUCiP+VJolFubtrz6XxVtB26eMAkODnREOKELHZtMOrlm8JBZCdtWCvTqibq9Az74XsqSfdA==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-nbi/eZtJfWxuDwdUCiP+VJolFubtrz6XxVtB26eMAkODnREOKELHZtMOrlm8JBZCdtWCvTqibq9Az74XsqSfdA==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@nx/nx-linux-arm64-musl@20.8.3': - resolution: - { - integrity: sha512-LTTGzI8YVPlF1v0YlVf+exM+1q7rpsiUbjTTHJcfHFRU5t4BsiZD54K19Y1UBg1XFx5cwhEaIomSmJ88RwPPVQ==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-LTTGzI8YVPlF1v0YlVf+exM+1q7rpsiUbjTTHJcfHFRU5t4BsiZD54K19Y1UBg1XFx5cwhEaIomSmJ88RwPPVQ==} + engines: {node: '>= 10'} cpu: [arm64] os: [linux] '@nx/nx-linux-x64-gnu@20.8.3': - resolution: - { - integrity: sha512-SlA4GtXvQbSzSIWLgiIiLBOjdINPOUR/im+TUbaEMZ8wiGrOY8cnk0PVt95TIQJVBeXBCeb5HnoY0lHJpMOODg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-SlA4GtXvQbSzSIWLgiIiLBOjdINPOUR/im+TUbaEMZ8wiGrOY8cnk0PVt95TIQJVBeXBCeb5HnoY0lHJpMOODg==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] '@nx/nx-linux-x64-musl@20.8.3': - resolution: - { - integrity: sha512-MNzkEwPktp5SQH9dJDH2wP9hgG9LsBDhKJXJfKw6sUI/6qz5+/aAjFziKy+zBnhU4AO1yXt5qEWzR8lDcIriVQ==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-MNzkEwPktp5SQH9dJDH2wP9hgG9LsBDhKJXJfKw6sUI/6qz5+/aAjFziKy+zBnhU4AO1yXt5qEWzR8lDcIriVQ==} + engines: {node: '>= 10'} cpu: [x64] os: [linux] '@nx/nx-win32-arm64-msvc@20.8.3': - resolution: - { - integrity: sha512-qUV7CyXKwRCM/lkvyS6Xa1MqgAuK5da6w27RAehh7LATBUKn1I4/M7DGn6L7ERCxpZuh1TrDz9pUzEy0R+Ekkg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-qUV7CyXKwRCM/lkvyS6Xa1MqgAuK5da6w27RAehh7LATBUKn1I4/M7DGn6L7ERCxpZuh1TrDz9pUzEy0R+Ekkg==} + engines: {node: '>= 10'} cpu: [arm64] os: [win32] '@nx/nx-win32-x64-msvc@20.8.3': - resolution: - { - integrity: sha512-gX1G8u6W6EPX6PO/wv07+B++UHyCHBXyVWXITA3Kv6HoSajOxIa2Kk1rv1iDQGmX1WWxBaj3bUyYJAFBDITe4w==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-gX1G8u6W6EPX6PO/wv07+B++UHyCHBXyVWXITA3Kv6HoSajOxIa2Kk1rv1iDQGmX1WWxBaj3bUyYJAFBDITe4w==} + engines: {node: '>= 10'} cpu: [x64] os: [win32] '@octokit/auth-token@4.0.0': - resolution: - { - integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} '@octokit/core@5.2.2': - resolution: - { - integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==} + engines: {node: '>= 18'} '@octokit/endpoint@9.0.6': - resolution: - { - integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==} + engines: {node: '>= 18'} '@octokit/graphql@7.1.1': - resolution: - { - integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==} + engines: {node: '>= 18'} '@octokit/openapi-types@24.2.0': - resolution: - { - integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==, - } + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} '@octokit/plugin-enterprise-rest@6.0.1': - resolution: - { - integrity: sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==, - } + resolution: {integrity: sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw==} '@octokit/plugin-paginate-rest@11.4.4-cjs.2': - resolution: - { - integrity: sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-2dK6z8fhs8lla5PaOTgqfCGBxgAv/le+EhPs27KklPhm1bKObpu6lXzwfUEQ16ajXzqNrKMujsFyo9K2eaoISw==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' '@octokit/plugin-request-log@4.0.1': - resolution: - { - integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' '@octokit/plugin-rest-endpoint-methods@13.3.2-cjs.1': - resolution: - { - integrity: sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-VUjIjOOvF2oELQmiFpWA1aOPdawpyaCUqcEBc/UOUnj3Xp6DJGrJ1+bjUIIDzdHjnFNO6q57ODMfdEZnoBkCwQ==} + engines: {node: '>= 18'} peerDependencies: '@octokit/core': ^5 '@octokit/request-error@5.1.1': - resolution: - { - integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==} + engines: {node: '>= 18'} '@octokit/request@8.4.1': - resolution: - { - integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==} + engines: {node: '>= 18'} '@octokit/rest@20.1.2': - resolution: - { - integrity: sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-GmYiltypkHHtihFwPRxlaorG5R9VAHuk/vbszVoRTGXnAsY60wYLkh/E2XiFmdZmqrisw+9FaazS1i5SbdWYgA==} + engines: {node: '>= 18'} '@octokit/types@13.10.0': - resolution: - { - integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==, - } + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} '@one-ini/wasm@0.1.1': - resolution: - { - integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==, - } + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} '@oxfmt/darwin-arm64@0.26.0': - resolution: - { - integrity: sha512-AAGc+8CffkiWeVgtWf4dPfQwHEE5c/j/8NWH7VGVxxJRCZFdmWcqCXprvL2H6qZFewvDLrFbuSPRCqYCpYGaTQ==, - } + resolution: {integrity: sha512-AAGc+8CffkiWeVgtWf4dPfQwHEE5c/j/8NWH7VGVxxJRCZFdmWcqCXprvL2H6qZFewvDLrFbuSPRCqYCpYGaTQ==} cpu: [arm64] os: [darwin] '@oxfmt/darwin-x64@0.26.0': - resolution: - { - integrity: sha512-xFx5ijCTjw577wJvFlZEMmKDnp3HSCcbYdCsLRmC5i3TZZiDe9DEYh3P46uqhzj8BkEw1Vm1ZCWdl48aEYAzvQ==, - } + resolution: {integrity: sha512-xFx5ijCTjw577wJvFlZEMmKDnp3HSCcbYdCsLRmC5i3TZZiDe9DEYh3P46uqhzj8BkEw1Vm1ZCWdl48aEYAzvQ==} cpu: [x64] os: [darwin] '@oxfmt/linux-arm64-gnu@0.26.0': - resolution: - { - integrity: sha512-GubkQeQT5d3B/Jx/IiR7NMkSmXrCZcVI0BPh1i7mpFi8HgD1hQ/LbhiBKAMsMqs5bbugdQOgBEl8bOhe8JhW1g==, - } + resolution: {integrity: sha512-GubkQeQT5d3B/Jx/IiR7NMkSmXrCZcVI0BPh1i7mpFi8HgD1hQ/LbhiBKAMsMqs5bbugdQOgBEl8bOhe8JhW1g==} cpu: [arm64] os: [linux] '@oxfmt/linux-arm64-musl@0.26.0': - resolution: - { - integrity: sha512-OEypUwK69bFPj+aa3/LYCnlIUPgoOLu//WNcriwpnWNmt47808Ht7RJSg+MNK8a7pSZHpXJ5/E6CRK/OTwFdaQ==, - } + resolution: {integrity: sha512-OEypUwK69bFPj+aa3/LYCnlIUPgoOLu//WNcriwpnWNmt47808Ht7RJSg+MNK8a7pSZHpXJ5/E6CRK/OTwFdaQ==} cpu: [arm64] os: [linux] '@oxfmt/linux-x64-gnu@0.26.0': - resolution: - { - integrity: sha512-xO6iEW2bC6ZHyOTPmPWrg/nM6xgzyRPaS84rATy6F8d79wz69LdRdJ3l/PXlkqhi7XoxhvX4ExysA0Nf10ZZEQ==, - } + resolution: {integrity: sha512-xO6iEW2bC6ZHyOTPmPWrg/nM6xgzyRPaS84rATy6F8d79wz69LdRdJ3l/PXlkqhi7XoxhvX4ExysA0Nf10ZZEQ==} cpu: [x64] os: [linux] '@oxfmt/linux-x64-musl@0.26.0': - resolution: - { - integrity: sha512-Z3KuZFC+MIuAyFCXBHY71kCsdRq1ulbsbzTe71v+hrEv7zVBn6yzql+/AZcgfIaKzWO9OXNuz5WWLWDmVALwow==, - } + resolution: {integrity: sha512-Z3KuZFC+MIuAyFCXBHY71kCsdRq1ulbsbzTe71v+hrEv7zVBn6yzql+/AZcgfIaKzWO9OXNuz5WWLWDmVALwow==} cpu: [x64] os: [linux] '@oxfmt/win32-arm64@0.26.0': - resolution: - { - integrity: sha512-3zRbqwVWK1mDhRhTknlQFpRFL9GhEB5GfU6U7wawnuEwpvi39q91kJ+SRJvJnhyPCARkjZBd1V8XnweN5IFd1g==, - } + resolution: {integrity: sha512-3zRbqwVWK1mDhRhTknlQFpRFL9GhEB5GfU6U7wawnuEwpvi39q91kJ+SRJvJnhyPCARkjZBd1V8XnweN5IFd1g==} cpu: [arm64] os: [win32] '@oxfmt/win32-x64@0.26.0': - resolution: - { - integrity: sha512-m8TfIljU22i9UEIkD+slGPifTFeaCwIUfxszN3E6ABWP1KQbtwSw9Ak0TdoikibvukF/dtbeyG3WW63jv9DnEg==, - } + resolution: {integrity: sha512-m8TfIljU22i9UEIkD+slGPifTFeaCwIUfxszN3E6ABWP1KQbtwSw9Ak0TdoikibvukF/dtbeyG3WW63jv9DnEg==} cpu: [x64] os: [win32] '@paralleldrive/cuid2@2.3.1': - resolution: - { - integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==, - } + resolution: {integrity: sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==} '@pgpm/database-jobs@0.16.0': - resolution: - { - integrity: sha512-s8I7958PlhfYXZKhYoU76R03yk6dlevjGk/Uy9uktveJkZ8C3JVsIhP6Lv4lo0SFEZCjFmXRCYpOY5xINIcX4w==, - } + resolution: {integrity: sha512-s8I7958PlhfYXZKhYoU76R03yk6dlevjGk/Uy9uktveJkZ8C3JVsIhP6Lv4lo0SFEZCjFmXRCYpOY5xINIcX4w==} '@pgpm/inflection@0.16.0': - resolution: - { - integrity: sha512-otjWGx+KkB113Wc5I9nsvoqPhBK6zD1ON2OcXw9PQRgqU43Y9f0yZjb559dDzZwDn5XUeiZMf6il5SIvJE5NPg==, - } + resolution: {integrity: sha512-otjWGx+KkB113Wc5I9nsvoqPhBK6zD1ON2OcXw9PQRgqU43Y9f0yZjb559dDzZwDn5XUeiZMf6il5SIvJE5NPg==} '@pgpm/metaschema-modules@0.16.4': - resolution: - { - integrity: sha512-sB3+5yljFEqUXTTHUOHBBxK52CwagHiUBumWjikHVN9C5w6NHUQ+xFde+3RJMCkoqnmcZn6HTGvWCF25QgciiA==, - } + resolution: {integrity: sha512-sB3+5yljFEqUXTTHUOHBBxK52CwagHiUBumWjikHVN9C5w6NHUQ+xFde+3RJMCkoqnmcZn6HTGvWCF25QgciiA==} '@pgpm/metaschema-schema@0.16.3': - resolution: - { - integrity: sha512-sDIWJY+uNaqMMGjL8NWo8ezzXH1OT0qdaqsX+YDrBL6v1u0PphWprdjd7HySzdqIGpPSax8sIy5u4P2M96wR9Q==, - } + resolution: {integrity: sha512-sDIWJY+uNaqMMGjL8NWo8ezzXH1OT0qdaqsX+YDrBL6v1u0PphWprdjd7HySzdqIGpPSax8sIy5u4P2M96wR9Q==} '@pgpm/services@0.16.3': - resolution: - { - integrity: sha512-TfYALB8RKPyR2WZIFH2Pirb5qfx1q2EKbr7gzG/CcZcQMgTGYyDHBtvSqIO4nDfJ6GgYcASoip9T0lzQmwGtlA==, - } + resolution: {integrity: sha512-TfYALB8RKPyR2WZIFH2Pirb5qfx1q2EKbr7gzG/CcZcQMgTGYyDHBtvSqIO4nDfJ6GgYcASoip9T0lzQmwGtlA==} '@pgpm/types@0.16.0': - resolution: - { - integrity: sha512-CioHCxZGQUnpLANw4aMOOq7Z6zi2SXCxJIRZ8CSBPJfJkWU1OgxX+EpSjnm4Td4bznJhOViXniLltibaaGkMPA==, - } + resolution: {integrity: sha512-CioHCxZGQUnpLANw4aMOOq7Z6zi2SXCxJIRZ8CSBPJfJkWU1OgxX+EpSjnm4Td4bznJhOViXniLltibaaGkMPA==} '@pgpm/verify@0.16.0': - resolution: - { - integrity: sha512-uG0zTXAWGLV8wTUiLdBn+2b4AO+gtiw7sZf+TFFU8h/mVGMBTHUb9Gbsl/GL/5/0zZKOxak7cRJ5deec79KB/A==, - } + resolution: {integrity: sha512-uG0zTXAWGLV8wTUiLdBn+2b4AO+gtiw7sZf+TFFU8h/mVGMBTHUb9Gbsl/GL/5/0zZKOxak7cRJ5deec79KB/A==} '@pgsql/types@17.6.2': - resolution: - { - integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==, - } + resolution: {integrity: sha512-1UtbELdbqNdyOShhrVfSz3a1gDi0s9XXiQemx+6QqtsrXe62a6zOGU+vjb2GRfG5jeEokI1zBBcfD42enRv0Rw==} '@pgsql/utils@17.8.11': - resolution: - { - integrity: sha512-gcaS9ATilQyGSIq8596tq+6rcb7TX54sdjOvOzGa9lu9NjqkptEKLbBae5UTjfkFGfH50duDFD1EpFogMnZToA==, - } + resolution: {integrity: sha512-gcaS9ATilQyGSIq8596tq+6rcb7TX54sdjOvOzGa9lu9NjqkptEKLbBae5UTjfkFGfH50duDFD1EpFogMnZToA==} '@pkgjs/parseargs@0.11.0': - resolution: - { - integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} '@pkgr/core@0.2.9': - resolution: - { - integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==, - } - engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@playwright/test@1.57.0': - resolution: - { - integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + engines: {node: '>=18'} hasBin: true '@protobufjs/aspromise@1.1.2': - resolution: - { - integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==, - } + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} '@protobufjs/base64@1.1.2': - resolution: - { - integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==, - } + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} '@protobufjs/codegen@2.0.4': - resolution: - { - integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==, - } + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} '@protobufjs/eventemitter@1.1.0': - resolution: - { - integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==, - } + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} '@protobufjs/fetch@1.1.0': - resolution: - { - integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==, - } + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} '@protobufjs/float@1.0.2': - resolution: - { - integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==, - } + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} '@protobufjs/inquire@1.1.0': - resolution: - { - integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==, - } + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} '@protobufjs/path@1.1.2': - resolution: - { - integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==, - } + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} '@protobufjs/pool@1.1.0': - resolution: - { - integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==, - } + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} '@protobufjs/utf8@1.1.0': - resolution: - { - integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==, - } + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.57.1': + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.57.1': + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.57.1': + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.57.1': + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.57.1': + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.57.1': + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.57.1': + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.57.1': + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.57.1': + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.57.1': + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.57.1': + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.57.1': + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.57.1': + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.57.1': + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} + cpu: [x64] + os: [win32] '@sigstore/bundle@2.3.2': - resolution: - { - integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/core@1.1.0': - resolution: - { - integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/protobuf-specs@0.3.3': - resolution: - { - integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==, - } - engines: { node: ^18.17.0 || >=20.5.0 } + resolution: {integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==} + engines: {node: ^18.17.0 || >=20.5.0} '@sigstore/sign@2.3.2': - resolution: - { - integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/tuf@2.3.4': - resolution: - { - integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==} + engines: {node: ^16.14.0 || >=18.0.0} '@sigstore/verify@1.2.1': - resolution: - { - integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==} + engines: {node: ^16.14.0 || >=18.0.0} '@sinclair/typebox@0.27.8': - resolution: - { - integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==, - } + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} '@sinclair/typebox@0.34.47': - resolution: - { - integrity: sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==, - } + resolution: {integrity: sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==} '@sinonjs/commons@3.0.1': - resolution: - { - integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==, - } + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} '@sinonjs/fake-timers@13.0.5': - resolution: - { - integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==, - } + resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} '@smithy/abort-controller@4.2.7': - resolution: - { - integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==} + engines: {node: '>=18.0.0'} '@smithy/abort-controller@4.2.8': - resolution: - { - integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==} + engines: {node: '>=18.0.0'} '@smithy/chunked-blob-reader-native@4.2.1': - resolution: - { - integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + engines: {node: '>=18.0.0'} '@smithy/chunked-blob-reader@5.2.0': - resolution: - { - integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + engines: {node: '>=18.0.0'} '@smithy/config-resolver@4.4.6': - resolution: - { - integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==} + engines: {node: '>=18.0.0'} '@smithy/core@3.20.0': - resolution: - { - integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==} + engines: {node: '>=18.0.0'} '@smithy/core@3.20.5': - resolution: - { - integrity: sha512-0Tz77Td8ynHaowXfOdrD0F1IH4tgWGUhwmLwmpFyTbr+U9WHXNNp9u/k2VjBXGnSe7BwjBERRpXsokGTXzNjhA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-0Tz77Td8ynHaowXfOdrD0F1IH4tgWGUhwmLwmpFyTbr+U9WHXNNp9u/k2VjBXGnSe7BwjBERRpXsokGTXzNjhA==} + engines: {node: '>=18.0.0'} '@smithy/core@3.20.7': - resolution: - { - integrity: sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-aO7jmh3CtrmPsIJxUwYIzI5WVlMK8BMCPQ4D4nTzqTqBhbzvxHNzBMGcEg13yg/z9R2Qsz49NUFl0F0lVbTVFw==} + engines: {node: '>=18.0.0'} '@smithy/credential-provider-imds@4.2.8': - resolution: - { - integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==} + engines: {node: '>=18.0.0'} '@smithy/eventstream-codec@4.2.8': - resolution: - { - integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==} + engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-browser@4.2.8': - resolution: - { - integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==} + engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-config-resolver@4.3.8': - resolution: - { - integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==} + engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-node@4.2.8': - resolution: - { - integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==} + engines: {node: '>=18.0.0'} '@smithy/eventstream-serde-universal@4.2.8': - resolution: - { - integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==} + engines: {node: '>=18.0.0'} '@smithy/fetch-http-handler@5.3.8': - resolution: - { - integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==} + engines: {node: '>=18.0.0'} '@smithy/fetch-http-handler@5.3.9': - resolution: - { - integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==} + engines: {node: '>=18.0.0'} '@smithy/hash-blob-browser@4.2.9': - resolution: - { - integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-m80d/iicI7DlBDxyQP6Th7BW/ejDGiF0bgI754+tiwK0lgMkcaIBgvwwVc7OFbY4eUzpGtnig52MhPAEJ7iNYg==} + engines: {node: '>=18.0.0'} '@smithy/hash-node@4.2.8': - resolution: - { - integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==} + engines: {node: '>=18.0.0'} '@smithy/hash-stream-node@4.2.8': - resolution: - { - integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-v0FLTXgHrTeheYZFGhR+ehX5qUm4IQsjAiL9qehad2cyjMWcN2QG6/4mSwbSgEQzI7jwfoXj7z4fxZUx/Mhj2w==} + engines: {node: '>=18.0.0'} '@smithy/invalid-dependency@4.2.8': - resolution: - { - integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==} + engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': - resolution: - { - integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} '@smithy/is-array-buffer@4.2.0': - resolution: - { - integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + engines: {node: '>=18.0.0'} '@smithy/md5-js@4.2.8': - resolution: - { - integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-oGMaLj4tVZzLi3itBa9TCswgMBr7k9b+qKYowQ6x1rTyTuO1IU2YHdHUa+891OsOH+wCsH7aTPRsTJO3RMQmjQ==} + engines: {node: '>=18.0.0'} '@smithy/middleware-content-length@4.2.8': - resolution: - { - integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==} + engines: {node: '>=18.0.0'} '@smithy/middleware-endpoint@4.4.1': - resolution: - { - integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==} + engines: {node: '>=18.0.0'} '@smithy/middleware-endpoint@4.4.6': - resolution: - { - integrity: sha512-dpq3bHqbEOBqGBjRVHVFP3eUSPpX0BYtg1D5d5Irgk6orGGAuZfY22rC4sErhg+ZfY/Y0kPqm1XpAmDZg7DeuA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-dpq3bHqbEOBqGBjRVHVFP3eUSPpX0BYtg1D5d5Irgk6orGGAuZfY22rC4sErhg+ZfY/Y0kPqm1XpAmDZg7DeuA==} + engines: {node: '>=18.0.0'} '@smithy/middleware-endpoint@4.4.8': - resolution: - { - integrity: sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-TV44qwB/T0OMMzjIuI+JeS0ort3bvlPJ8XIH0MSlGADraXpZqmyND27ueuAL3E14optleADWqtd7dUgc2w+qhQ==} + engines: {node: '>=18.0.0'} '@smithy/middleware-retry@4.4.22': - resolution: - { - integrity: sha512-vwWDMaObSMjw6WCC/3Ae9G7uul5Sk95jr07CDk1gkIMpaDic0phPS1MpVAZ6+YkF7PAzRlpsDjxPwRlh/S11FQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-vwWDMaObSMjw6WCC/3Ae9G7uul5Sk95jr07CDk1gkIMpaDic0phPS1MpVAZ6+YkF7PAzRlpsDjxPwRlh/S11FQ==} + engines: {node: '>=18.0.0'} '@smithy/middleware-retry@4.4.24': - resolution: - { - integrity: sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-yiUY1UvnbUFfP5izoKLtfxDSTRv724YRRwyiC/5HYY6vdsVDcDOXKSXmkJl/Hovcxt5r+8tZEUAdrOaCJwrl9Q==} + engines: {node: '>=18.0.0'} '@smithy/middleware-serde@4.2.8': - resolution: - { - integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==} + engines: {node: '>=18.0.0'} '@smithy/middleware-serde@4.2.9': - resolution: - { - integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==} + engines: {node: '>=18.0.0'} '@smithy/middleware-stack@4.2.7': - resolution: - { - integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==} + engines: {node: '>=18.0.0'} '@smithy/middleware-stack@4.2.8': - resolution: - { - integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==} + engines: {node: '>=18.0.0'} '@smithy/node-config-provider@4.3.7': - resolution: - { - integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==} + engines: {node: '>=18.0.0'} '@smithy/node-config-provider@4.3.8': - resolution: - { - integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==} + engines: {node: '>=18.0.0'} '@smithy/node-http-handler@4.4.7': - resolution: - { - integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==} + engines: {node: '>=18.0.0'} '@smithy/node-http-handler@4.4.8': - resolution: - { - integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==} + engines: {node: '>=18.0.0'} '@smithy/property-provider@4.2.7': - resolution: - { - integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==} + engines: {node: '>=18.0.0'} '@smithy/property-provider@4.2.8': - resolution: - { - integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==} + engines: {node: '>=18.0.0'} '@smithy/protocol-http@5.3.7': - resolution: - { - integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==} + engines: {node: '>=18.0.0'} '@smithy/protocol-http@5.3.8': - resolution: - { - integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==} + engines: {node: '>=18.0.0'} '@smithy/querystring-builder@4.2.7': - resolution: - { - integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==} + engines: {node: '>=18.0.0'} '@smithy/querystring-builder@4.2.8': - resolution: - { - integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==} + engines: {node: '>=18.0.0'} '@smithy/querystring-parser@4.2.7': - resolution: - { - integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==} + engines: {node: '>=18.0.0'} '@smithy/querystring-parser@4.2.8': - resolution: - { - integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==} + engines: {node: '>=18.0.0'} '@smithy/service-error-classification@4.2.8': - resolution: - { - integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==} + engines: {node: '>=18.0.0'} '@smithy/shared-ini-file-loader@4.4.2': - resolution: - { - integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==} + engines: {node: '>=18.0.0'} '@smithy/shared-ini-file-loader@4.4.3': - resolution: - { - integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==} + engines: {node: '>=18.0.0'} '@smithy/signature-v4@5.3.8': - resolution: - { - integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==} + engines: {node: '>=18.0.0'} '@smithy/smithy-client@4.10.2': - resolution: - { - integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==} + engines: {node: '>=18.0.0'} '@smithy/smithy-client@4.10.7': - resolution: - { - integrity: sha512-Uznt0I9z3os3Z+8pbXrOSCTXCA6vrjyN7Ub+8l2pRDum44vLv8qw0qGVkJN0/tZBZotaEFHrDPKUoPNueTr5Vg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-Uznt0I9z3os3Z+8pbXrOSCTXCA6vrjyN7Ub+8l2pRDum44vLv8qw0qGVkJN0/tZBZotaEFHrDPKUoPNueTr5Vg==} + engines: {node: '>=18.0.0'} '@smithy/smithy-client@4.10.9': - resolution: - { - integrity: sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-Je0EvGXVJ0Vrrr2lsubq43JGRIluJ/hX17aN/W/A0WfE+JpoMdI8kwk2t9F0zTX9232sJDGcoH4zZre6m6f/sg==} + engines: {node: '>=18.0.0'} '@smithy/types@4.11.0': - resolution: - { - integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==} + engines: {node: '>=18.0.0'} '@smithy/types@4.12.0': - resolution: - { - integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==} + engines: {node: '>=18.0.0'} '@smithy/url-parser@4.2.7': - resolution: - { - integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==} + engines: {node: '>=18.0.0'} '@smithy/url-parser@4.2.8': - resolution: - { - integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==} + engines: {node: '>=18.0.0'} '@smithy/util-base64@4.3.0': - resolution: - { - integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + engines: {node: '>=18.0.0'} '@smithy/util-body-length-browser@4.2.0': - resolution: - { - integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + engines: {node: '>=18.0.0'} '@smithy/util-body-length-node@4.2.1': - resolution: - { - integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': - resolution: - { - integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} '@smithy/util-buffer-from@4.2.0': - resolution: - { - integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + engines: {node: '>=18.0.0'} '@smithy/util-config-provider@4.2.0': - resolution: - { - integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + engines: {node: '>=18.0.0'} '@smithy/util-defaults-mode-browser@4.3.21': - resolution: - { - integrity: sha512-DtmVJarzqtjghtGjCw/PFJolcJkP7GkZgy+hWTAN3YLXNH+IC82uMoMhFoC3ZtIz5mOgCm5+hOGi1wfhVYgrxw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-DtmVJarzqtjghtGjCw/PFJolcJkP7GkZgy+hWTAN3YLXNH+IC82uMoMhFoC3ZtIz5mOgCm5+hOGi1wfhVYgrxw==} + engines: {node: '>=18.0.0'} '@smithy/util-defaults-mode-browser@4.3.23': - resolution: - { - integrity: sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-mMg+r/qDfjfF/0psMbV4zd7F/i+rpyp7Hjh0Wry7eY15UnzTEId+xmQTGDU8IdZtDfbGQxuWNfgBZKBj+WuYbA==} + engines: {node: '>=18.0.0'} '@smithy/util-defaults-mode-node@4.2.24': - resolution: - { - integrity: sha512-JelBDKPAVswVY666rezBvY6b0nF/v9TXjUbNwDNAyme7qqKYEX687wJv0uze8lBIZVbg30wlWnlYfVSjjpKYFA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-JelBDKPAVswVY666rezBvY6b0nF/v9TXjUbNwDNAyme7qqKYEX687wJv0uze8lBIZVbg30wlWnlYfVSjjpKYFA==} + engines: {node: '>=18.0.0'} '@smithy/util-defaults-mode-node@4.2.26': - resolution: - { - integrity: sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-EQqe/WkbCinah0h1lMWh9ICl0Ob4lyl20/10WTB35SC9vDQfD8zWsOT+x2FIOXKAoZQ8z/y0EFMoodbcqWJY/w==} + engines: {node: '>=18.0.0'} '@smithy/util-endpoints@3.2.8': - resolution: - { - integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==} + engines: {node: '>=18.0.0'} '@smithy/util-hex-encoding@4.2.0': - resolution: - { - integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + engines: {node: '>=18.0.0'} '@smithy/util-middleware@4.2.7': - resolution: - { - integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==} + engines: {node: '>=18.0.0'} '@smithy/util-middleware@4.2.8': - resolution: - { - integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==} + engines: {node: '>=18.0.0'} '@smithy/util-retry@4.2.8': - resolution: - { - integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==} + engines: {node: '>=18.0.0'} '@smithy/util-stream@4.5.10': - resolution: - { - integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==} + engines: {node: '>=18.0.0'} '@smithy/util-stream@4.5.8': - resolution: - { - integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==} + engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.2.0': - resolution: - { - integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': - resolution: - { - integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} '@smithy/util-utf8@4.2.0': - resolution: - { - integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + engines: {node: '>=18.0.0'} '@smithy/util-waiter@4.2.8': - resolution: - { - integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==} + engines: {node: '>=18.0.0'} '@smithy/uuid@1.1.0': - resolution: - { - integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + engines: {node: '>=18.0.0'} '@styled-system/background@5.1.2': - resolution: - { - integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==, - } + resolution: {integrity: sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==} '@styled-system/border@5.1.5': - resolution: - { - integrity: sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==, - } + resolution: {integrity: sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==} '@styled-system/color@5.1.2': - resolution: - { - integrity: sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==, - } + resolution: {integrity: sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==} '@styled-system/core@5.1.2': - resolution: - { - integrity: sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==, - } + resolution: {integrity: sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==} '@styled-system/css@5.1.5': - resolution: - { - integrity: sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==, - } + resolution: {integrity: sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==} '@styled-system/flexbox@5.1.2': - resolution: - { - integrity: sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==, - } + resolution: {integrity: sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==} '@styled-system/grid@5.1.2': - resolution: - { - integrity: sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==, - } + resolution: {integrity: sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==} '@styled-system/layout@5.1.2': - resolution: - { - integrity: sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==, - } + resolution: {integrity: sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==} '@styled-system/position@5.1.2': - resolution: - { - integrity: sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==, - } + resolution: {integrity: sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==} '@styled-system/shadow@5.1.2': - resolution: - { - integrity: sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==, - } + resolution: {integrity: sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==} '@styled-system/space@5.1.2': - resolution: - { - integrity: sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==, - } + resolution: {integrity: sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==} '@styled-system/typography@5.1.2': - resolution: - { - integrity: sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==, - } + resolution: {integrity: sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==} '@styled-system/variant@5.1.5': - resolution: - { - integrity: sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==, - } + resolution: {integrity: sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==} '@tanstack/query-core@5.90.19': - resolution: - { - integrity: sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA==, - } + resolution: {integrity: sha512-GLW5sjPVIvH491VV1ufddnfldyVB+teCnpPIvweEfkpRx7CfUmUGhoh9cdcUKBh/KwVxk22aNEDxeTsvmyB/WA==} + + '@tanstack/query-core@5.90.20': + resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} '@tanstack/react-query@5.90.19': - resolution: - { - integrity: sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ==, - } + resolution: {integrity: sha512-qTZRZ4QyTzQc+M0IzrbKHxSeISUmRB3RPGmao5bT+sI6ayxSRhn0FXEnT5Hg3as8SBFcRosrXXRFB+yAcxVxJQ==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-query@5.90.20': + resolution: {integrity: sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==} peerDependencies: react: ^18 || ^19 '@testing-library/dom@7.31.2': - resolution: - { - integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==} + engines: {node: '>=10'} '@testing-library/jest-dom@5.11.10': - resolution: - { - integrity: sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==, - } - engines: { node: '>=8', npm: '>=6', yarn: '>=1' } + resolution: {integrity: sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==} + engines: {node: '>=8', npm: '>=6', yarn: '>=1'} '@testing-library/react@11.2.5': - resolution: - { - integrity: sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-yEx7oIa/UWLe2F2dqK0FtMF9sJWNXD+2PPtp39BvE0Kh9MJ9Kl0HrZAgEuhUJR+Lx8Di6Xz+rKwSdEPY2UV8ZQ==} + engines: {node: '>=10'} peerDependencies: react: '*' react-dom: '*' '@tsconfig/node10@1.0.12': - resolution: - { - integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==, - } + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': - resolution: - { - integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==, - } + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} '@tsconfig/node14@1.0.3': - resolution: - { - integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==, - } + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} '@tsconfig/node16@1.0.4': - resolution: - { - integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==, - } + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} '@tufjs/canonical-json@2.0.0': - resolution: - { - integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} + engines: {node: ^16.14.0 || >=18.0.0} '@tufjs/models@2.0.1': - resolution: - { - integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==} + engines: {node: ^16.14.0 || >=18.0.0} '@tybys/wasm-util@0.10.1': - resolution: - { - integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, - } + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} '@tybys/wasm-util@0.9.0': - resolution: - { - integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==, - } + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} '@types/accept-language-parser@1.5.8': - resolution: - { - integrity: sha512-6+dKdh9q/I8xDBnKQKddCBKaWBWLmJ97HTiSbAXVpL7LEgDfOkKF98UVCaZ5KJrtdN5Wa5ndXUiqD3XR9XGqWQ==, - } + resolution: {integrity: sha512-6+dKdh9q/I8xDBnKQKddCBKaWBWLmJ97HTiSbAXVpL7LEgDfOkKF98UVCaZ5KJrtdN5Wa5ndXUiqD3XR9XGqWQ==} '@types/accepts@1.3.7': - resolution: - { - integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==, - } + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} '@types/aria-query@4.2.2': - resolution: - { - integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==, - } + resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} '@types/babel__core@7.20.5': - resolution: - { - integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==, - } + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} '@types/babel__generator@7.27.0': - resolution: - { - integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==, - } + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': - resolution: - { - integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==, - } + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} '@types/babel__traverse@7.28.0': - resolution: - { - integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==, - } + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/body-parser@1.19.6': - resolution: - { - integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==, - } + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} '@types/connect@3.4.38': - resolution: - { - integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==, - } + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} '@types/content-disposition@0.5.9': - resolution: - { - integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==, - } + resolution: {integrity: sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==} '@types/cookiejar@2.1.5': - resolution: - { - integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==, - } + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} '@types/cookies@0.9.2': - resolution: - { - integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==, - } + resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} '@types/cors@2.8.19': - resolution: - { - integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==, - } + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} '@types/estree@1.0.8': - resolution: - { - integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, - } + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/express-serve-static-core@5.1.0': - resolution: - { - integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==, - } + resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} '@types/express@5.0.6': - resolution: - { - integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==, - } + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} '@types/geojson@7946.0.16': - resolution: - { - integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==, - } + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} '@types/graphql-upload@8.0.12': - resolution: - { - integrity: sha512-M0ZPZqNUzKNB16q5woEzgG/Q8DjICV80K7JvDSRnDmDFfrRdfFX/n6PbmqAN7gCzECcHVnw1gk6N4Cg0FwxCqA==, - } + resolution: {integrity: sha512-M0ZPZqNUzKNB16q5woEzgG/Q8DjICV80K7JvDSRnDmDFfrRdfFX/n6PbmqAN7gCzECcHVnw1gk6N4Cg0FwxCqA==} '@types/http-assert@1.5.6': - resolution: - { - integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==, - } + resolution: {integrity: sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==} '@types/http-errors@2.0.5': - resolution: - { - integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==, - } + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} '@types/istanbul-lib-coverage@2.0.6': - resolution: - { - integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==, - } + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} '@types/istanbul-lib-report@3.0.3': - resolution: - { - integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==, - } + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} '@types/istanbul-reports@3.0.4': - resolution: - { - integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==, - } + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} '@types/jest-in-case@1.0.9': - resolution: - { - integrity: sha512-tapHpzWGjCC/hxYJyzbJ/5ZV6rA2153Sve5lGJUAIA1Jzrphfp27TznAWfGeXf+d8TLN7zMujaC0UwNQwSJaQg==, - } + resolution: {integrity: sha512-tapHpzWGjCC/hxYJyzbJ/5ZV6rA2153Sve5lGJUAIA1Jzrphfp27TznAWfGeXf+d8TLN7zMujaC0UwNQwSJaQg==} '@types/jest@30.0.0': - resolution: - { - integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==, - } + resolution: {integrity: sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==} '@types/js-yaml@4.0.9': - resolution: - { - integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==, - } + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} '@types/json-schema@7.0.15': - resolution: - { - integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, - } + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/json5@0.0.30': - resolution: - { - integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==, - } + resolution: {integrity: sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==} '@types/jsonwebtoken@9.0.10': - resolution: - { - integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==, - } + resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} '@types/keygrip@1.0.6': - resolution: - { - integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==, - } + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} '@types/koa-compose@3.2.9': - resolution: - { - integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==, - } + resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} '@types/koa@3.0.1': - resolution: - { - integrity: sha512-VkB6WJUQSe0zBpR+Q7/YIUESGp5wPHcaXr0xueU5W0EOUWtlSbblsl+Kl31lyRQ63nIILh0e/7gXjQ09JXJIHw==, - } + resolution: {integrity: sha512-VkB6WJUQSe0zBpR+Q7/YIUESGp5wPHcaXr0xueU5W0EOUWtlSbblsl+Kl31lyRQ63nIILh0e/7gXjQ09JXJIHw==} '@types/methods@1.1.4': - resolution: - { - integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==, - } + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} '@types/minimatch@3.0.5': - resolution: - { - integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==, - } + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} '@types/minimist@1.2.5': - resolution: - { - integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==, - } + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} '@types/ms@2.1.0': - resolution: - { - integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==, - } + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/node@18.19.130': - resolution: - { - integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==, - } + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} '@types/node@20.19.27': - resolution: - { - integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==, - } + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} '@types/nodemailer@7.0.5': - resolution: - { - integrity: sha512-7WtR4MFJUNN2UFy0NIowBRJswj5KXjXDhlZY43Hmots5eGu5q/dTeFd/I6GgJA/qj3RqO6dDy4SvfcV3fOVeIA==, - } + resolution: {integrity: sha512-7WtR4MFJUNN2UFy0NIowBRJswj5KXjXDhlZY43Hmots5eGu5q/dTeFd/I6GgJA/qj3RqO6dDy4SvfcV3fOVeIA==} '@types/normalize-package-data@2.4.4': - resolution: - { - integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==, - } + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/pg-copy-streams@1.2.5': - resolution: - { - integrity: sha512-7D6/GYW2uHIaVU6S/5omI+6RZnwlZBpLQDZAH83xX1rjxAOK0f6/deKyyUTewxqts145VIGn6XWYz1YGf50G5g==, - } + resolution: {integrity: sha512-7D6/GYW2uHIaVU6S/5omI+6RZnwlZBpLQDZAH83xX1rjxAOK0f6/deKyyUTewxqts145VIGn6XWYz1YGf50G5g==} '@types/pg@8.16.0': - resolution: - { - integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==, - } + resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} '@types/qs@6.14.0': - resolution: - { - integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==, - } + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} '@types/range-parser@1.2.7': - resolution: - { - integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==, - } + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.13': + resolution: {integrity: sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==} '@types/react@19.2.8': - resolution: - { - integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==, - } + resolution: {integrity: sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==} '@types/request-ip@0.0.41': - resolution: - { - integrity: sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==, - } + resolution: {integrity: sha512-Qzz0PM2nSZej4lsLzzNfADIORZhhxO7PED0fXpg4FjXiHuJ/lMyUg+YFF5q8x9HPZH3Gl6N+NOM8QZjItNgGKg==} '@types/semver@7.7.1': - resolution: - { - integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==, - } + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} '@types/send@1.2.1': - resolution: - { - integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==, - } + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} '@types/serve-static@2.2.0': - resolution: - { - integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==, - } + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} '@types/shelljs@0.8.17': - resolution: - { - integrity: sha512-IDksKYmQA2W9MkQjiyptbMmcQx+8+Ol6b7h6dPU5S05JyiQDSb/nZKnrMrZqGwgV6VkVdl6/SPCKPDlMRvqECg==, - } + resolution: {integrity: sha512-IDksKYmQA2W9MkQjiyptbMmcQx+8+Ol6b7h6dPU5S05JyiQDSb/nZKnrMrZqGwgV6VkVdl6/SPCKPDlMRvqECg==} '@types/smtp-server@3.5.12': - resolution: - { - integrity: sha512-IBemrqI6nzvbgwE41Lnd4v4Yf1Kc7F1UHjk1GFBLNhLcI/Zop1ggHQ8g7Y8QYc6jGVgzWQcsa0MBNcGnDY9UGw==, - } + resolution: {integrity: sha512-IBemrqI6nzvbgwE41Lnd4v4Yf1Kc7F1UHjk1GFBLNhLcI/Zop1ggHQ8g7Y8QYc6jGVgzWQcsa0MBNcGnDY9UGw==} '@types/stack-utils@2.0.3': - resolution: - { - integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==, - } + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} '@types/superagent@8.1.9': - resolution: - { - integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==, - } + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} '@types/supertest@6.0.3': - resolution: - { - integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==, - } + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} '@types/testing-library__jest-dom@5.14.9': - resolution: - { - integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==, - } + resolution: {integrity: sha512-FSYhIjFlfOpGSRyVoMBMuS3ws5ehFQODymf3vlI7U1K8c7PHwWwFY7VREfmsuzHSOnoKs/9/Y983ayOs7eRzqw==} '@types/ws@7.4.7': - resolution: - { - integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==, - } + resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} '@types/yargs-parser@21.0.3': - resolution: - { - integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==, - } + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} '@types/yargs@15.0.20': - resolution: - { - integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==, - } + resolution: {integrity: sha512-KIkX+/GgfFitlASYCGoSF+T4XRXhOubJLhkLVtSfsRTe9jWMmuM2g28zQ41BtPTG7TRBb2xHW+LCNVE9QR/vsg==} '@types/yargs@17.0.35': - resolution: - { - integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==, - } + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} '@typescript-eslint/eslint-plugin@8.53.1': - resolution: - { - integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.53.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/parser@8.53.1': - resolution: - { - integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/project-service@8.53.1': - resolution: - { - integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/scope-manager@8.53.1': - resolution: - { - integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.53.1': - resolution: - { - integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/type-utils@8.53.1': - resolution: - { - integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/types@8.53.1': - resolution: - { - integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.53.1': - resolution: - { - integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/utils@8.53.1': - resolution: - { - integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/visitor-keys@8.53.1': - resolution: - { - integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': - resolution: - { - integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==, - } + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: - { - integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==, - } + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} cpu: [arm] os: [android] '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: - { - integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==, - } + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} cpu: [arm64] os: [android] '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: - { - integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==, - } + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} cpu: [arm64] os: [darwin] '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: - { - integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==, - } + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} cpu: [x64] os: [darwin] '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: - { - integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==, - } + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} cpu: [x64] os: [freebsd] '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: - { - integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==, - } + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: - { - integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==, - } + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: - { - integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==, - } + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} cpu: [arm64] os: [linux] '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: - { - integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==, - } + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} cpu: [arm64] os: [linux] '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: - { - integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==, - } + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} cpu: [ppc64] os: [linux] '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: - { - integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==, - } + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} cpu: [riscv64] os: [linux] '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: - { - integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==, - } + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} cpu: [riscv64] os: [linux] '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: - { - integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==, - } + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} cpu: [s390x] os: [linux] '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: - { - integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==, - } + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} cpu: [x64] os: [linux] '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: - { - integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==, - } + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} cpu: [x64] os: [linux] '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: - { - integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} cpu: [wasm32] '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: - { - integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==, - } + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} cpu: [arm64] os: [win32] '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: - { - integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==, - } + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} cpu: [ia32] os: [win32] '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: - { - integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==, - } + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} cpu: [x64] os: [win32] + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@yarnpkg/lockfile@1.1.0': - resolution: - { - integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==, - } + resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} '@yarnpkg/parsers@3.0.2': - resolution: - { - integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==, - } - engines: { node: '>=18.12.0' } + resolution: {integrity: sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA==} + engines: {node: '>=18.12.0'} '@zkochan/js-yaml@0.0.7': - resolution: - { - integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==, - } + resolution: {integrity: sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ==} hasBin: true JSONStream@1.3.5: - resolution: - { - integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, - } + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true abbrev@2.0.0: - resolution: - { - integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} accept-language-parser@1.5.0: - resolution: - { - integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==, - } + resolution: {integrity: sha512-QhyTbMLYo0BBGg1aWbeMG4ekWtds/31BrEU+DONOg/7ax23vxpL03Pb7/zBmha2v7vdD3AyzZVWBVGEZxKOXWw==} accepts@2.0.0: - resolution: - { - integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} acorn-jsx@5.3.2: - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn-walk@8.3.4: - resolution: - { - integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} acorn@8.15.0: - resolution: - { - integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} hasBin: true add-stream@1.0.0: - resolution: - { - integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==, - } + resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} agent-base@7.1.4: - resolution: - { - integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} aggregate-error@3.1.0: - resolution: - { - integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} ajv@7.2.4: - resolution: - { - integrity: sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==, - } + resolution: {integrity: sha512-nBeQgg/ZZA3u3SYxyaDvpvDtgZ/EZPF547ARgZBrG9Bhu1vKDwAIjtIf+sDtJUKa2zOcEbmRLBRSyMraS/Oy1A==} ajv@8.17.1: - resolution: - { - integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==, - } + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} ansi-colors@4.1.3: - resolution: - { - integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} ansi-escapes@4.3.2: - resolution: - { - integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-regex@6.2.2: - resolution: - { - integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: - { - integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} ansi-styles@6.2.3: - resolution: - { - integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} appstash@0.3.0: - resolution: - { - integrity: sha512-F4rMrok4wQYDVitYMWbPQh2MBoKCj7GYzmI/Gw8zDeO2vDLmCmyzmbd0zAwplghB6X3VMGQw/NKcngIc8w6oTA==, - } + resolution: {integrity: sha512-F4rMrok4wQYDVitYMWbPQh2MBoKCj7GYzmI/Gw8zDeO2vDLmCmyzmbd0zAwplghB6X3VMGQw/NKcngIc8w6oTA==} aproba@2.0.0: - resolution: - { - integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==, - } + resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==} arg@4.1.3: - resolution: - { - integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==, - } + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} argparse@1.0.10: - resolution: - { - integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, - } + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} aria-query@4.2.2: - resolution: - { - integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==} + engines: {node: '>=6.0'} array-differ@3.0.0: - resolution: - { - integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} + engines: {node: '>=8'} array-ify@1.0.0: - resolution: - { - integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==, - } + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} array-union@2.1.0: - resolution: - { - integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} arrify@1.0.1: - resolution: - { - integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} arrify@2.0.1: - resolution: - { - integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} asap@2.0.6: - resolution: - { - integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==, - } + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} asn1@0.2.6: - resolution: - { - integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==, - } + resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} assert-plus@1.0.0: - resolution: - { - integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} + engines: {node: '>=0.8'} async-retry@1.3.1: - resolution: - { - integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==, - } + resolution: {integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==} async@3.2.6: - resolution: - { - integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==, - } + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} asynckit@0.4.0: - resolution: - { - integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, - } + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} atob@2.1.2: - resolution: - { - integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==, - } - engines: { node: '>= 4.5.0' } + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} hasBin: true aws-sign2@0.7.0: - resolution: - { - integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==, - } + resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==} aws4@1.13.2: - resolution: - { - integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==, - } + resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} axios@1.13.2: - resolution: - { - integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==, - } + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} babel-jest@30.2.0: - resolution: - { - integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 babel-plugin-istanbul@7.0.1: - resolution: - { - integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} babel-plugin-jest-hoist@30.2.0: - resolution: - { - integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} babel-plugin-styled-components@2.1.4: - resolution: - { - integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==, - } + resolution: {integrity: sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==} peerDependencies: styled-components: '>= 2' babel-preset-current-node-syntax@1.2.0: - resolution: - { - integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==, - } + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 babel-preset-jest@30.2.0: - resolution: - { - integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-beta.1 babel-runtime@6.25.0: - resolution: - { - integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==, - } + resolution: {integrity: sha512-zeCYxDePWYAT/DfmQWIHsMSFW2vv45UIwIAMjGvQVsTd47RwsiRH0uK1yzyWZ7LDBKdhnGDPM6NYEO5CZyhPrg==} backo2@1.0.2: - resolution: - { - integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==, - } + resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==} balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base-64@1.0.0: - resolution: - { - integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==, - } + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} base32.js@0.1.0: - resolution: - { - integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-n3TkB02ixgBOhTvANakDb4xaMXnYUVkNoRFJjQflcqMQhyEKxEHdj3E6N8t8sUQ0mjH/3/JxzlXuz3ul/J90pQ==} + engines: {node: '>=0.12.0'} base64-js@1.5.1: - resolution: - { - integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, - } + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} baseline-browser-mapping@2.9.15: - resolution: - { - integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==, - } + resolution: {integrity: sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==} hasBin: true bcrypt-pbkdf@1.0.2: - resolution: - { - integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==, - } + resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} before-after-hook@2.2.3: - resolution: - { - integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==, - } + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} big-integer@1.6.52: - resolution: - { - integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} + engines: {node: '>=0.6'} bin-links@4.0.4: - resolution: - { - integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} binary-extensions@2.3.0: - resolution: - { - integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} bl@4.1.0: - resolution: - { - integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, - } + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} body-parser@1.19.0: - resolution: - { - integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==} + engines: {node: '>= 0.8'} body-parser@2.2.1: - resolution: - { - integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + engines: {node: '>=18'} boolbase@1.0.0: - resolution: - { - integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, - } + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} bowser@2.13.1: - resolution: - { - integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==, - } + resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} brace-expansion@1.1.12: - resolution: - { - integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==, - } + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} brace-expansion@2.0.2: - resolution: - { - integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, - } + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: - resolution: - { - integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} broadcast-channel@3.7.0: - resolution: - { - integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==, - } + resolution: {integrity: sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==} browserslist@4.28.1: - resolution: - { - integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true bs-logger@0.2.6: - resolution: - { - integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} bser@2.1.1: - resolution: - { - integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==, - } + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} buffer-equal-constant-time@1.0.1: - resolution: - { - integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==, - } + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} buffer@5.6.0: - resolution: - { - integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==, - } + resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} buffer@5.7.1: - resolution: - { - integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==, - } + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} busboy@0.3.1: - resolution: - { - integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==, - } - engines: { node: '>=4.5.0' } + resolution: {integrity: sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==} + engines: {node: '>=4.5.0'} byte-size@8.1.1: - resolution: - { - integrity: sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==, - } - engines: { node: '>=12.17' } + resolution: {integrity: sha512-tUkzZWK0M/qdoLEqikxBWe4kumyuwjl3HO6zHTr4yEI23EojPtLYXdG1+AQY7MN0cGyNDvEaJ8wiYQm6P2bPxg==} + engines: {node: '>=12.17'} bytes@3.1.0: - resolution: - { - integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==} + engines: {node: '>= 0.8'} bytes@3.1.2: - resolution: - { - integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} cacache@18.0.4: - resolution: - { - integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} + engines: {node: ^16.14.0 || >=18.0.0} call-bind-apply-helpers@1.0.2: - resolution: - { - integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} call-bind@1.0.8: - resolution: - { - integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} call-bound@1.0.4: - resolution: - { - integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} camel-case@3.0.0: - resolution: - { - integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==, - } + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} camelcase-keys@6.2.2: - resolution: - { - integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} + engines: {node: '>=8'} camelcase@5.3.1: - resolution: - { - integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} camelcase@6.3.0: - resolution: - { - integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} camelize@1.0.1: - resolution: - { - integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==, - } + resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} caniuse-lite@1.0.30001765: - resolution: - { - integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==, - } + resolution: {integrity: sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==} case@1.6.3: - resolution: - { - integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==} + engines: {node: '>= 0.8.0'} caseless@0.12.0: - resolution: - { - integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==, - } + resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} chalk@3.0.0: - resolution: - { - integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} + engines: {node: '>=8'} chalk@4.1.0: - resolution: - { - integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==} + engines: {node: '>=10'} chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} char-regex@1.0.2: - resolution: - { - integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} chardet@2.1.1: - resolution: - { - integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==, - } + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} cheerio-select@2.1.0: - resolution: - { - integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==, - } + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} cheerio@1.0.0-rc.3: - resolution: - { - integrity: sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==} + engines: {node: '>= 0.6'} cheerio@1.1.2: - resolution: - { - integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==, - } - engines: { node: '>=20.18.1' } + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} chokidar@3.6.0: - resolution: - { - integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, - } - engines: { node: '>= 8.10.0' } + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} chownr@2.0.0: - resolution: - { - integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} ci-info@3.9.0: - resolution: - { - integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} ci-info@4.3.1: - resolution: - { - integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} cjs-module-lexer@2.2.0: - resolution: - { - integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==, - } + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} clean-ansi@0.2.0: - resolution: - { - integrity: sha512-AX26I7oo87AIA4OixLOARtjeNdX85aKGI+HPJ7wQEnXkoC3ytbwIuPu3d5+cmDoh2j1I2pQsQa/z3/FNAR8vOQ==, - } + resolution: {integrity: sha512-AX26I7oo87AIA4OixLOARtjeNdX85aKGI+HPJ7wQEnXkoC3ytbwIuPu3d5+cmDoh2j1I2pQsQa/z3/FNAR8vOQ==} clean-css@4.2.4: - resolution: - { - integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==, - } - engines: { node: '>= 4.0' } + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} clean-stack@2.2.0: - resolution: - { - integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} cli-cursor@3.1.0: - resolution: - { - integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} cli-spinners@2.6.1: - resolution: - { - integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==} + engines: {node: '>=6'} cli-spinners@2.9.2: - resolution: - { - integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} cli-width@3.0.0: - resolution: - { - integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} cliui@6.0.0: - resolution: - { - integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==, - } + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} cliui@7.0.4: - resolution: - { - integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, - } + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} cliui@8.0.1: - resolution: - { - integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} clone-deep@4.0.1: - resolution: - { - integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} + engines: {node: '>=6'} clone@1.0.4: - resolution: - { - integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} cmd-shim@6.0.3: - resolution: - { - integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} co@4.6.0: - resolution: - { - integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==, - } - engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} collect-v8-coverage@1.0.3: - resolution: - { - integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==, - } + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, - } + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: '>=7.0.0' } + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, - } + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} color-support@1.1.3: - resolution: - { - integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==, - } + resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true columnify@1.6.0: - resolution: - { - integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==, - } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} + engines: {node: '>=8.0.0'} combined-stream@1.0.8: - resolution: - { - integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} commander@10.0.1: - resolution: - { - integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} commander@2.17.1: - resolution: - { - integrity: sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==, - } + resolution: {integrity: sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==} commander@2.19.0: - resolution: - { - integrity: sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==, - } + resolution: {integrity: sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==} commander@2.20.3: - resolution: - { - integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, - } + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} commander@5.1.0: - resolution: - { - integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} + engines: {node: '>= 6'} common-ancestor-path@1.0.1: - resolution: - { - integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==, - } + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} compare-func@2.0.0: - resolution: - { - integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==, - } + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} component-emitter@1.3.1: - resolution: - { - integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==, - } + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@2.0.0: - resolution: - { - integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==, - } - engines: { '0': node >= 6.0 } + resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} + engines: {'0': node >= 6.0} config-chain@1.1.13: - resolution: - { - integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==, - } + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} console-control-strings@1.1.0: - resolution: - { - integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==, - } + resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} content-disposition@1.0.1: - resolution: - { - integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} content-type@1.0.5: - resolution: - { - integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} conventional-changelog-angular@7.0.0: - resolution: - { - integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} conventional-changelog-core@5.0.1: - resolution: - { - integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Rvi5pH+LvgsqGwZPZ3Cq/tz4ty7mjijhr3qR4m9IBXNbxGGYgTVVO+duXzz9aArmHxFtwZ+LRkrNIMDQzgoY4A==} + engines: {node: '>=14'} conventional-changelog-preset-loader@3.0.0: - resolution: - { - integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-qy9XbdSLmVnwnvzEisjxdDiLA4OmV3o8db+Zdg4WiFw14fP3B6XNz98X0swPPpkTd/pc1K7+adKgEDM1JCUMiA==} + engines: {node: '>=14'} conventional-changelog-writer@6.0.1: - resolution: - { - integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-359t9aHorPw+U+nHzUXHS5ZnPBOizRxfQsWT5ZDHBfvfxQOAik+yfuhKXG66CN5LEWPpMNnIMHUTCKeYNprvHQ==} + engines: {node: '>=14'} hasBin: true conventional-commits-filter@3.0.0: - resolution: - { - integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-1ymej8b5LouPx9Ox0Dw/qAO2dVdfpRFq28e5Y0jJEU8ZrLdy0vOSkkIInwmxErFGhg6SALro60ZrwYFVTUDo4Q==} + engines: {node: '>=14'} conventional-commits-parser@4.0.0: - resolution: - { - integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==} + engines: {node: '>=14'} hasBin: true conventional-recommended-bump@7.0.1: - resolution: - { - integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Ft79FF4SlOFvX4PkwFDRnaNiIVX7YbmqGU0RwccUaiGvgp3S0a8ipR2/Qxk31vclDNM+GSdJOVs2KrsUCjblVA==} + engines: {node: '>=14'} hasBin: true convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cookie-signature@1.2.2: - resolution: - { - integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==, - } - engines: { node: '>=6.6.0' } + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} cookie@0.7.2: - resolution: - { - integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} cookiejar@2.1.4: - resolution: - { - integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==, - } + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} copyfiles@2.4.1: - resolution: - { - integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==, - } + resolution: {integrity: sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==} hasBin: true core-js-pure@3.47.0: - resolution: - { - integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==, - } + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} core-js@2.6.12: - resolution: - { - integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==, - } + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. core-util-is@1.0.2: - resolution: - { - integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==, - } + resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} core-util-is@1.0.3: - resolution: - { - integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, - } + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cors@2.8.5: - resolution: - { - integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} cosmiconfig@9.0.0: - resolution: - { - integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} peerDependencies: typescript: '>=4.9.5' peerDependenciesMeta: @@ -6962,134 +5402,74 @@ packages: optional: true create-require@1.1.1: - resolution: - { - integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==, - } + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} cron-parser@2.18.0: - resolution: - { - integrity: sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==} + engines: {node: '>=0.8'} cross-spawn@7.0.6: - resolution: - { - integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} css-color-keywords@1.0.0: - resolution: - { - integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==} + engines: {node: '>=4'} css-select@1.2.0: - resolution: - { - integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==, - } + resolution: {integrity: sha512-dUQOBoqdR7QwV90WysXPLXG5LO7nhYBgiWVfxF80DKPF8zx1t/pUd2FYy73emg3zrjtM6dzmYgbHKfV2rxiHQA==} css-select@5.2.2: - resolution: - { - integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==, - } + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} css-to-react-native@3.2.0: - resolution: - { - integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==, - } + resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} css-what@2.1.3: - resolution: - { - integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==, - } + resolution: {integrity: sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==} css-what@6.2.2: - resolution: - { - integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} css.escape@1.5.1: - resolution: - { - integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==, - } + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} css@3.0.0: - resolution: - { - integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==, - } + resolution: {integrity: sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==} cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} hasBin: true csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} csv-parse@6.1.0: - resolution: - { - integrity: sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==, - } + resolution: {integrity: sha512-CEE+jwpgLn+MmtCpVcPtiCZpVtB6Z2OKPTr34pycYYoL7sxdOkXDdQ4lRiw6ioC0q6BLqhc6cKweCVvral8yhw==} csv-parser@2.3.5: - resolution: - { - integrity: sha512-LCHolC4AlNwL+5EuD5LH2VVNKpD8QixZW2zzK1XmrVYUaslFY4c5BooERHOCIubG9iv/DAyFjs4x0HvWNZuyWg==, - } - engines: { node: '>= 8.16.0' } + resolution: {integrity: sha512-LCHolC4AlNwL+5EuD5LH2VVNKpD8QixZW2zzK1XmrVYUaslFY4c5BooERHOCIubG9iv/DAyFjs4x0HvWNZuyWg==} + engines: {node: '>= 8.16.0'} hasBin: true dargs@7.0.0: - resolution: - { - integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} + engines: {node: '>=8'} dashdash@1.14.1: - resolution: - { - integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==} + engines: {node: '>=0.10'} dataloader@2.2.3: - resolution: - { - integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==, - } + resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==} dateformat@3.0.3: - resolution: - { - integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==, - } + resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} debug@2.6.9: - resolution: - { - integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==, - } + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -7097,11 +5477,8 @@ packages: optional: true debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: '>=6.0' } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: @@ -7109,31 +5486,19 @@ packages: optional: true decamelize-keys@1.1.1: - resolution: - { - integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} decamelize@1.2.0: - resolution: - { - integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} decode-uri-component@0.2.2: - resolution: - { - integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} dedent@1.5.3: - resolution: - { - integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==, - } + resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -7141,10 +5506,7 @@ packages: optional: true dedent@1.7.1: - resolution: - { - integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==, - } + resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -7152,238 +5514,130 @@ packages: optional: true deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} deepmerge@4.3.1: - resolution: - { - integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} defaults@1.0.4: - resolution: - { - integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==, - } + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} define-data-property@1.1.4: - resolution: - { - integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} define-lazy-prop@2.0.0: - resolution: - { - integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} delayed-stream@1.0.0: - resolution: - { - integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, - } - engines: { node: '>=0.4.0' } + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} depd@1.1.2: - resolution: - { - integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} depd@2.0.0: - resolution: - { - integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} deprecation@2.3.1: - resolution: - { - integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==, - } + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} detect-indent@5.0.0: - resolution: - { - integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==} + engines: {node: '>=4'} detect-newline@3.1.0: - resolution: - { - integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} detect-node@2.1.0: - resolution: - { - integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==, - } + resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} dezalgo@1.0.4: - resolution: - { - integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==, - } + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} dicer@0.3.0: - resolution: - { - integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==, - } - engines: { node: '>=4.5.0' } + resolution: {integrity: sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==} + engines: {node: '>=4.5.0'} diff-sequences@29.6.3: - resolution: - { - integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} diff@4.0.2: - resolution: - { - integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==, - } - engines: { node: '>=0.3.1' } + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} dom-accessibility-api@0.5.16: - resolution: - { - integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, - } + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dom-serializer@0.1.1: - resolution: - { - integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==, - } + resolution: {integrity: sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==} dom-serializer@0.2.2: - resolution: - { - integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==, - } + resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==} dom-serializer@1.4.1: - resolution: - { - integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==, - } + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} dom-serializer@2.0.0: - resolution: - { - integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, - } + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@1.3.1: - resolution: - { - integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==, - } + resolution: {integrity: sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==} domelementtype@2.3.0: - resolution: - { - integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, - } + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} domhandler@2.4.2: - resolution: - { - integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==, - } + resolution: {integrity: sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==} domhandler@3.3.0: - resolution: - { - integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} + engines: {node: '>= 4'} domhandler@4.3.1: - resolution: - { - integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} domhandler@5.0.3: - resolution: - { - integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} domutils@1.5.1: - resolution: - { - integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==, - } + resolution: {integrity: sha512-gSu5Oi/I+3wDENBsOWBiRK1eoGxcywYSqg3rR960/+EfY0CF4EX1VPkgHOZ3WiS/Jg2DtliF6BhWcHlfpYUcGw==} domutils@1.7.0: - resolution: - { - integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==, - } + resolution: {integrity: sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==} domutils@2.8.0: - resolution: - { - integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==, - } + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} domutils@3.2.2: - resolution: - { - integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==, - } + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} dot-prop@5.3.0: - resolution: - { - integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} dotenv-expand@11.0.7: - resolution: - { - integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} + engines: {node: '>=12'} dotenv@16.4.7: - resolution: - { - integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + engines: {node: '>=12'} drizzle-orm@0.45.1: - resolution: - { - integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==, - } + resolution: {integrity: sha512-Te0FOdKIistGNPMq2jscdqngBRfBpC8uMFVwqjf6gtTVJHIQ/dosgV/CLBU2N4ZJBsXL5savCba9b0YJskKdcA==} peerDependencies: '@aws-sdk/client-rds-data': '>=3' '@cloudflare/workers-types': '>=4' @@ -7475,268 +5729,156 @@ packages: optional: true dunder-proto@1.0.1: - resolution: - { - integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} eastasianwidth@0.2.0: - resolution: - { - integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, - } + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} ecc-jsbn@0.1.2: - resolution: - { - integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==, - } + resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==} ecdsa-sig-formatter@1.0.11: - resolution: - { - integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==, - } + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} editorconfig@1.0.4: - resolution: - { - integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} hasBin: true ee-first@1.1.1: - resolution: - { - integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, - } + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} ejs@3.1.10: - resolution: - { - integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} hasBin: true electron-to-chromium@1.5.267: - resolution: - { - integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==, - } + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emittery@0.13.1: - resolution: - { - integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} encodeurl@2.0.0: - resolution: - { - integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} encoding-sniffer@0.2.1: - resolution: - { - integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==, - } + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} encoding@0.1.13: - resolution: - { - integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==, - } + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} end-of-stream@1.4.5: - resolution: - { - integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==, - } + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} enquirer@2.3.6: - resolution: - { - integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} + engines: {node: '>=8.6'} entities@1.1.2: - resolution: - { - integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==, - } + resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==} entities@2.2.0: - resolution: - { - integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, - } + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} entities@4.5.0: - resolution: - { - integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, - } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} entities@6.0.1: - resolution: - { - integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==, - } - engines: { node: '>=0.12' } + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} env-paths@2.2.1: - resolution: - { - integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} envalid@8.1.1: - resolution: - { - integrity: sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==} + engines: {node: '>=18'} envinfo@7.13.0: - resolution: - { - integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q==} + engines: {node: '>=4'} hasBin: true err-code@2.0.3: - resolution: - { - integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==, - } + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} error-ex@1.3.4: - resolution: - { - integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==, - } + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-define-property@1.0.1: - resolution: - { - integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} es-errors@1.3.0: - resolution: - { - integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} es-object-atoms@1.1.1: - resolution: - { - integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: - resolution: - { - integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true esbuild@0.27.2: - resolution: - { - integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: - resolution: - { - integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} escape-goat@3.0.0: - resolution: - { - integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} escape-html@1.0.3: - resolution: - { - integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, - } + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} escape-string-regexp@2.0.0: - resolution: - { - integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} eslint-config-prettier@10.1.8: - resolution: - { - integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, - } + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-plugin-simple-import-sort@12.1.1: - resolution: - { - integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==, - } + resolution: {integrity: sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==} peerDependencies: eslint: '>=5.0.0' eslint-plugin-unused-imports@4.3.0: - resolution: - { - integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==, - } + resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 eslint: ^9.0.0 || ^8.0.0 @@ -7745,32 +5887,20 @@ packages: optional: true eslint-scope@8.4.0: - resolution: - { - integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: - resolution: - { - integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint@9.39.2: - resolution: - { - integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: jiti: '*' @@ -7779,190 +5909,106 @@ packages: optional: true espree@10.4.0: - resolution: - { - integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==, - } - engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: - resolution: - { - integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} hasBin: true esquery@1.6.0: - resolution: - { - integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} etag@1.8.1: - resolution: - { - integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} eventemitter3@3.1.2: - resolution: - { - integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==, - } + resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} eventemitter3@4.0.7: - resolution: - { - integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==, - } + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} events@3.3.0: - resolution: - { - integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, - } - engines: { node: '>=0.8.x' } + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} execa@5.0.0: - resolution: - { - integrity: sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==} + engines: {node: '>=10'} execa@5.1.1: - resolution: - { - integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} exit-x@0.2.2: - resolution: - { - integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} expect@30.2.0: - resolution: - { - integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} exponential-backoff@3.1.3: - resolution: - { - integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==, - } + resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} express@5.2.1: - resolution: - { - integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} extend@3.0.2: - resolution: - { - integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, - } + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} extsprintf@1.3.0: - resolution: - { - integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==, - } - engines: { '0': node >=0.6.0 } + resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} + engines: {'0': node >=0.6.0} fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-glob@3.3.3: - resolution: - { - integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==, - } - engines: { node: '>=8.6.0' } + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fast-safe-stringify@2.1.1: - resolution: - { - integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==, - } + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} fast-uri@3.1.0: - resolution: - { - integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==, - } + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} fast-xml-parser@5.2.5: - resolution: - { - integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==, - } + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true fastq@1.20.1: - resolution: - { - integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==, - } + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fb-watchman@2.0.2: - resolution: - { - integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==, - } + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -7970,99 +6016,57 @@ packages: optional: true figures@3.2.0: - resolution: - { - integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} file-entry-cache@8.0.0: - resolution: - { - integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, - } - engines: { node: '>=16.0.0' } + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} filelist@1.0.4: - resolution: - { - integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==, - } + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} fill-range@7.1.1: - resolution: - { - integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} finalhandler@1.3.2: - resolution: - { - integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} + engines: {node: '>= 0.8'} finalhandler@2.1.1: - resolution: - { - integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==, - } - engines: { node: '>= 18.0.0' } + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} find-and-require-package-json@0.9.0: - resolution: - { - integrity: sha512-e7+fnRvphmWHHgOdVQct5yLEmw38GD3wpX8CMONT/qn/BLK6F0ft/iPicNKJMX6U4GlTEFzreYbLf+FlCYh4lQ==, - } + resolution: {integrity: sha512-e7+fnRvphmWHHgOdVQct5yLEmw38GD3wpX8CMONT/qn/BLK6F0ft/iPicNKJMX6U4GlTEFzreYbLf+FlCYh4lQ==} find-up@2.1.0: - resolution: - { - integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} + engines: {node: '>=4'} find-up@4.1.0: - resolution: - { - integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} flat-cache@4.0.1: - resolution: - { - integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} flat@5.0.2: - resolution: - { - integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==, - } + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true flatted@3.3.3: - resolution: - { - integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==, - } + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} follow-redirects@1.15.11: - resolution: - { - integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==, - } - engines: { node: '>=4.0' } + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} peerDependencies: debug: '*' peerDependenciesMeta: @@ -8070,1069 +6074,607 @@ packages: optional: true foreground-child@3.3.1: - resolution: - { - integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} forever-agent@0.6.1: - resolution: - { - integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==, - } + resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} form-data@2.3.3: - resolution: - { - integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==, - } - engines: { node: '>= 0.12' } + resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} + engines: {node: '>= 0.12'} form-data@4.0.5: - resolution: - { - integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} formidable@3.5.4: - resolution: - { - integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==, - } - engines: { node: '>=14.0.0' } + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} forwarded@0.2.0: - resolution: - { - integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} fresh@2.0.0: - resolution: - { - integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} front-matter@4.0.2: - resolution: - { - integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==, - } + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} fs-capacitor@6.2.0: - resolution: - { - integrity: sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw==} + engines: {node: '>=10'} fs-capacitor@8.0.0: - resolution: - { - integrity: sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==, - } - engines: { node: ^14.17.0 || >=16.0.0 } + resolution: {integrity: sha512-+Lk6iSKajdGw+7XYxUkwIzreJ2G1JFlYOdnKJv5PzwFLVsoJYBpCuS7WPIUSNT1IbQaEWT1nhYU63Ud03DyzLA==} + engines: {node: ^14.17.0 || >=16.0.0} fs-constants@1.0.0: - resolution: - { - integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==, - } + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} fs-extra@11.3.3: - resolution: - { - integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==, - } - engines: { node: '>=14.14' } + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} fs-minipass@2.1.0: - resolution: - { - integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} fs-minipass@3.0.3: - resolution: - { - integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} fs.realpath@1.0.0: - resolution: - { - integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, - } + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} fsevents@2.3.2: - resolution: - { - integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: - resolution: - { - integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, - } + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} genomic@5.3.0: - resolution: - { - integrity: sha512-59rZ++BMgR4/rbh/j55n0BCYAZI/KrP9l7IgxdOHT+HEMAADA6kGaPOhDBltekw2QpHOAUeOXoRiTvntM7b1Ug==, - } + resolution: {integrity: sha512-59rZ++BMgR4/rbh/j55n0BCYAZI/KrP9l7IgxdOHT+HEMAADA6kGaPOhDBltekw2QpHOAUeOXoRiTvntM7b1Ug==} gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} get-intrinsic@1.3.0: - resolution: - { - integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} get-package-type@0.1.0: - resolution: - { - integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==, - } - engines: { node: '>=8.0.0' } + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} get-pkg-repo@4.2.1: - resolution: - { - integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==, - } - engines: { node: '>=6.9.0' } + resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} + engines: {node: '>=6.9.0'} hasBin: true get-port@5.1.1: - resolution: - { - integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} get-proto@1.0.1: - resolution: - { - integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} get-stream@6.0.0: - resolution: - { - integrity: sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==} + engines: {node: '>=10'} get-stream@6.0.1: - resolution: - { - integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} get-tsconfig@4.13.0: - resolution: - { - integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==, - } + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} getpass@0.1.7: - resolution: - { - integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==, - } + resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} git-raw-commits@3.0.0: - resolution: - { - integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-b5OHmZ3vAgGrDn/X0kS+9qCfNKWe4K/jFnhwzVWWg0/k5eLa3060tZShrRg8Dja5kPc+YjS0Gc6y7cRr44Lpjw==} + engines: {node: '>=14'} hasBin: true git-remote-origin-url@2.0.0: - resolution: - { - integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} + engines: {node: '>=4'} git-semver-tags@5.0.1: - resolution: - { - integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-hIvOeZwRbQ+7YEUmCkHqo8FOLQZCEn18yevLHADlFPZY02KJGsu5FZt9YW/lybfK2uhWFI7Qg/07LekJiTv7iA==} + engines: {node: '>=14'} hasBin: true git-up@7.0.0: - resolution: - { - integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==, - } + resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} git-url-parse@14.0.0: - resolution: - { - integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==, - } + resolution: {integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==} gitconfiglocal@1.0.0: - resolution: - { - integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==, - } + resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: '>=10.13.0' } + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} glob@10.5.0: - resolution: - { - integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==, - } + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true glob@11.1.0: - resolution: - { - integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} hasBin: true glob@13.0.0: - resolution: - { - integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} + engines: {node: 20 || >=22} glob@7.2.3: - resolution: - { - integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, - } + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported glob@9.3.5: - resolution: - { - integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} + engines: {node: '>=16 || 14 >=14.17'} globals@14.0.0: - resolution: - { - integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} gopd@1.2.0: - resolution: - { - integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} graphile-build-pg@4.14.1: - resolution: - { - integrity: sha512-7DIVbcfMU5lXNkGnAeobqm29AvjFYw4/xOlKNQk3NE/mfFDcyPuXYboypmtxzglg1hGXkyONLYnas9vzL+SunQ==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-7DIVbcfMU5lXNkGnAeobqm29AvjFYw4/xOlKNQk3NE/mfFDcyPuXYboypmtxzglg1hGXkyONLYnas9vzL+SunQ==} + engines: {node: '>=8.6'} peerDependencies: pg: '>=6.1.0 <9' graphile-build@4.14.1: - resolution: - { - integrity: sha512-l/ylyMK0vl5LCOScpTsTedNZUqwBgafXS7RPDW1YiQofeioVtTDMdV9k3zRkXdMKtKqJsvOBvjXn64WGLaLInQ==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-l/ylyMK0vl5LCOScpTsTedNZUqwBgafXS7RPDW1YiQofeioVtTDMdV9k3zRkXdMKtKqJsvOBvjXn64WGLaLInQ==} + engines: {node: '>=8.6'} peerDependencies: graphql: '>=0.9 <0.14 || ^14.0.2 || ^15.4.0' graphile-utils@4.14.1: - resolution: - { - integrity: sha512-FgviZVKO3NS8va2inqUVQQFSnFLEG7FiH64BqSVRHSF8jwSXKcpx5NiRibErNvvIdnuzgVAXQ3W4jcXvMSx0Tg==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-FgviZVKO3NS8va2inqUVQQFSnFLEG7FiH64BqSVRHSF8jwSXKcpx5NiRibErNvvIdnuzgVAXQ3W4jcXvMSx0Tg==} + engines: {node: '>=8.6'} peerDependencies: graphile-build: ^4.5.0 graphile-build-pg: ^4.5.0 graphql-parse-resolve-info@4.14.1: - resolution: - { - integrity: sha512-WKHukfEuZamP1ZONR84b8iT+4sJgEhtXMDArm1jpXEsU2vTb5EgkCZ4Obfl+v09oNTKXm0CJjPfBUZ5jcJ2Ykg==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-WKHukfEuZamP1ZONR84b8iT+4sJgEhtXMDArm1jpXEsU2vTb5EgkCZ4Obfl+v09oNTKXm0CJjPfBUZ5jcJ2Ykg==} + engines: {node: '>=8.6'} peerDependencies: graphql: '>=0.9 <0.14 || ^14.0.2 || ^15.4.0 || ^16.3.0' graphql-request@7.4.0: - resolution: - { - integrity: sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==, - } + resolution: {integrity: sha512-xfr+zFb/QYbs4l4ty0dltqiXIp07U6sl+tOKAb0t50/EnQek6CVVBLjETXi+FghElytvgaAWtIOt3EV7zLzIAQ==} peerDependencies: graphql: 14 - 16 graphql-tag@2.12.6: - resolution: - { - integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 graphql-upload@13.0.0: - resolution: - { - integrity: sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >= 16.0.0 } + resolution: {integrity: sha512-YKhx8m/uOtKu4Y1UzBFJhbBGJTlk7k4CydlUUiNrtxnwZv0WigbRHP+DVhRNKt7u7DXOtcKZeYJlGtnMXvreXA==} + engines: {node: ^12.22.0 || ^14.17.0 || >= 16.0.0} peerDependencies: graphql: 0.13.1 - 16 graphql-ws@5.16.2: - resolution: - { - integrity: sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==} + engines: {node: '>=10'} peerDependencies: graphql: '>=0.11 <=16' graphql@15.10.1: - resolution: - { - integrity: sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg==, - } - engines: { node: '>= 10.x' } + resolution: {integrity: sha512-BL/Xd/T9baO6NFzoMpiMD7YUZ62R6viR5tp/MULVEnbYJXZA//kRNW7J0j1w/wXArgL0sCxhDfK5dczSKn3+cg==} + engines: {node: '>= 10.x'} handlebars@4.7.8: - resolution: - { - integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==, - } - engines: { node: '>=0.4.7' } + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} hasBin: true har-schema@2.0.0: - resolution: - { - integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==} + engines: {node: '>=4'} har-validator@5.1.5: - resolution: - { - integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==} + engines: {node: '>=6'} deprecated: this library is no longer supported hard-rejection@2.1.0: - resolution: - { - integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} has-property-descriptors@1.0.2: - resolution: - { - integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==, - } + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} has-symbols@1.1.0: - resolution: - { - integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} has-tostringtag@1.0.2: - resolution: - { - integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} has-unicode@2.0.1: - resolution: - { - integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==, - } + resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==} hasown@2.0.2: - resolution: - { - integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} he@1.2.0: - resolution: - { - integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, - } + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true hoist-non-react-statics@3.3.2: - resolution: - { - integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==, - } + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} hosted-git-info@2.8.9: - resolution: - { - integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, - } + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} hosted-git-info@4.1.0: - resolution: - { - integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} hosted-git-info@7.0.2: - resolution: - { - integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} + engines: {node: ^16.14.0 || >=18.0.0} html-escaper@2.0.2: - resolution: - { - integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, - } + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} html-minifier@3.5.21: - resolution: - { - integrity: sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==} + engines: {node: '>=4'} hasBin: true htmlparser2@10.0.0: - resolution: - { - integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==, - } + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} htmlparser2@3.10.1: - resolution: - { - integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==, - } + resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==} htmlparser2@4.1.0: - resolution: - { - integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==, - } + resolution: {integrity: sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==} http-cache-semantics@4.2.0: - resolution: - { - integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==, - } + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} http-errors@1.7.2: - resolution: - { - integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==} + engines: {node: '>= 0.6'} http-errors@1.8.1: - resolution: - { - integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} http-errors@2.0.1: - resolution: - { - integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} http-proxy-agent@7.0.2: - resolution: - { - integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} http-signature@1.2.0: - resolution: - { - integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==, - } - engines: { node: '>=0.8', npm: '>=1.3.7' } + resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} + engines: {node: '>=0.8', npm: '>=1.3.7'} https-proxy-agent@7.0.6: - resolution: - { - integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} human-signals@2.1.0: - resolution: - { - integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, - } - engines: { node: '>=10.17.0' } + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} iconv-lite@0.4.24: - resolution: - { - integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} iconv-lite@0.6.3: - resolution: - { - integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} iconv-lite@0.7.1: - resolution: - { - integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + engines: {node: '>=0.10.0'} ieee754@1.2.1: - resolution: - { - integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, - } + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} ignore-by-default@1.0.1: - resolution: - { - integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==, - } + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} ignore-walk@6.0.5: - resolution: - { - integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} ignore@5.3.2: - resolution: - { - integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} ignore@7.0.5: - resolution: - { - integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} import-fresh@3.3.1: - resolution: - { - integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} import-local@3.1.0: - resolution: - { - integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} hasBin: true import-local@3.2.0: - resolution: - { - integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} hasBin: true imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: '>=0.8.19' } + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} indent-string@4.0.0: - resolution: - { - integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} inflection@3.0.2: - resolution: - { - integrity: sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-+Bg3+kg+J6JUWn8J6bzFmOWkTQ6L/NHfDRSYU+EVvuKHDxUDHAXgqixHfVlzuBQaPOTac8hn43aPhMNk6rMe3g==} + engines: {node: '>=18.0.0'} inflekt@0.3.0: - resolution: - { - integrity: sha512-YZRDExCvyQmOYld/oI62icM+V9d7wagwWmJ2+xgV0eAN2fkHR2PoHYDO/tW7BVwhi8viECFJiwdv8dPBSMDzHw==, - } + resolution: {integrity: sha512-YZRDExCvyQmOYld/oI62icM+V9d7wagwWmJ2+xgV0eAN2fkHR2PoHYDO/tW7BVwhi8viECFJiwdv8dPBSMDzHw==} inflekt@0.3.1: - resolution: - { - integrity: sha512-z5jvjelE61KiWinkjlainPDROpO3u0NqlUsCoSTxrSV7yNhcnaIb71ckx3utm8GZ2wifjqGFyaqyYomSXEgMfQ==, - } + resolution: {integrity: sha512-z5jvjelE61KiWinkjlainPDROpO3u0NqlUsCoSTxrSV7yNhcnaIb71ckx3utm8GZ2wifjqGFyaqyYomSXEgMfQ==} inflight@1.0.6: - resolution: - { - integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, - } + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.3: - resolution: - { - integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==, - } + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} inherits@2.0.4: - resolution: - { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, - } + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: - resolution: - { - integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, - } + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} ini@4.1.3: - resolution: - { - integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} init-package-json@6.0.3: - resolution: - { - integrity: sha512-Zfeb5ol+H+eqJWHTaGca9BovufyGeIfr4zaaBorPmJBMrJ+KBnN+kQx2ZtXdsotUTgldHmHQV44xvUWOUA7E2w==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Zfeb5ol+H+eqJWHTaGca9BovufyGeIfr4zaaBorPmJBMrJ+KBnN+kQx2ZtXdsotUTgldHmHQV44xvUWOUA7E2w==} + engines: {node: ^16.14.0 || >=18.0.0} inquirer@8.2.7: - resolution: - { - integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} + engines: {node: '>=12.0.0'} inquirerer@4.4.0: - resolution: - { - integrity: sha512-zra0M4Oh+rzgr7PMJy9cNi/LbkJbtB6QRABou65nN6NTwb368/lMJ8ACHXozM7bw3+t5SOI0TP3gxKAyT0BCRw==, - } + resolution: {integrity: sha512-zra0M4Oh+rzgr7PMJy9cNi/LbkJbtB6QRABou65nN6NTwb368/lMJ8ACHXozM7bw3+t5SOI0TP3gxKAyT0BCRw==} inquirerer@4.5.0: - resolution: - { - integrity: sha512-ULWscyMV6Y/OH1XRODvunrQH1EO4r7q+UV/boWFiVIt9h2UZ7wa/Qc+ZpAqUaWynKUhDtY3UqZV4MVrRcEkmNg==, - } + resolution: {integrity: sha512-ULWscyMV6Y/OH1XRODvunrQH1EO4r7q+UV/boWFiVIt9h2UZ7wa/Qc+ZpAqUaWynKUhDtY3UqZV4MVrRcEkmNg==} ip-address@10.1.0: - resolution: - { - integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==, - } - engines: { node: '>= 12' } + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} ipaddr.js@1.9.1: - resolution: - { - integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} ipv6-normalize@1.0.1: - resolution: - { - integrity: sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==, - } + resolution: {integrity: sha512-Bm6H79i01DjgGTCWjUuCjJ6QDo1HB96PT/xCYuyJUP9WFbVDrLSbG4EZCvOCun2rNswZb0c3e4Jt/ws795esHA==} is-arrayish@0.2.1: - resolution: - { - integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, - } + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-binary-path@2.1.0: - resolution: - { - integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} is-ci@3.0.1: - resolution: - { - integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==, - } + resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} hasBin: true is-core-module@2.16.1: - resolution: - { - integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} is-docker@2.2.1: - resolution: - { - integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} hasBin: true is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} is-generator-fn@2.1.0: - resolution: - { - integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} is-interactive@1.0.0: - resolution: - { - integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} is-lambda@1.0.1: - resolution: - { - integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==, - } + resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} is-nan@1.3.2: - resolution: - { - integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} is-obj@2.0.0: - resolution: - { - integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} is-plain-obj@1.1.0: - resolution: - { - integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} is-plain-object@2.0.4: - resolution: - { - integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} is-promise@4.0.0: - resolution: - { - integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==, - } + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} is-ssh@1.4.1: - resolution: - { - integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==, - } + resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==} is-stream@2.0.0: - resolution: - { - integrity: sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==} + engines: {node: '>=8'} is-stream@2.0.1: - resolution: - { - integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} is-text-path@1.0.1: - resolution: - { - integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + engines: {node: '>=0.10.0'} is-typedarray@1.0.0: - resolution: - { - integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==, - } + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} is-unicode-supported@0.1.0: - resolution: - { - integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} is-wsl@2.2.0: - resolution: - { - integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} isarray@0.0.1: - resolution: - { - integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==, - } + resolution: {integrity: sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==} isarray@1.0.0: - resolution: - { - integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, - } + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} isexe@3.1.1: - resolution: - { - integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} isobject@3.0.1: - resolution: - { - integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} isstream@0.1.2: - resolution: - { - integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==, - } + resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} istanbul-lib-coverage@3.2.2: - resolution: - { - integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} istanbul-lib-instrument@6.0.3: - resolution: - { - integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} istanbul-lib-report@3.0.1: - resolution: - { - integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} istanbul-lib-source-maps@5.0.6: - resolution: - { - integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} istanbul-reports@3.2.0: - resolution: - { - integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} iterall@1.3.0: - resolution: - { - integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==, - } + resolution: {integrity: sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==} jackspeak@3.4.3: - resolution: - { - integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==, - } + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} jackspeak@4.1.1: - resolution: - { - integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} jake@10.9.4: - resolution: - { - integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} hasBin: true jest-changed-files@30.2.0: - resolution: - { - integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-circus@30.2.0: - resolution: - { - integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-cli@30.2.0: - resolution: - { - integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -9141,11 +6683,8 @@ packages: optional: true jest-config@30.2.0: - resolution: - { - integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@types/node': '*' esbuild-register: '>=3.4.0' @@ -9159,95 +6698,56 @@ packages: optional: true jest-diff@29.7.0: - resolution: - { - integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-diff@30.2.0: - resolution: - { - integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-docblock@30.2.0: - resolution: - { - integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-each@30.2.0: - resolution: - { - integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-environment-node@30.2.0: - resolution: - { - integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-get-type@29.6.3: - resolution: - { - integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} jest-haste-map@30.2.0: - resolution: - { - integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-in-case@1.0.2: - resolution: - { - integrity: sha512-2DE6Gdwnh5jkCYTePWoQinF+zne3lCADibXoYJEt8PS84JaRug0CyAOrEgzMxbzln3YcSY2PBeru7ct4tbflYA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-2DE6Gdwnh5jkCYTePWoQinF+zne3lCADibXoYJEt8PS84JaRug0CyAOrEgzMxbzln3YcSY2PBeru7ct4tbflYA==} + engines: {node: '>=4'} jest-leak-detector@30.2.0: - resolution: - { - integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-matcher-utils@30.2.0: - resolution: - { - integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@30.2.0: - resolution: - { - integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-mock@30.2.0: - resolution: - { - integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-pnp-resolver@1.2.3: - resolution: - { - integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} peerDependencies: jest-resolve: '*' peerDependenciesMeta: @@ -9255,81 +6755,48 @@ packages: optional: true jest-regex-util@30.0.1: - resolution: - { - integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve-dependencies@30.2.0: - resolution: - { - integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-resolve@30.2.0: - resolution: - { - integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runner@30.2.0: - resolution: - { - integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-runtime@30.2.0: - resolution: - { - integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-snapshot@30.2.0: - resolution: - { - integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-util@30.2.0: - resolution: - { - integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-validate@30.2.0: - resolution: - { - integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-watcher@30.2.0: - resolution: - { - integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-worker@30.2.0: - resolution: - { - integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest@30.2.0: - resolution: - { - integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -9338,1116 +6805,611 @@ packages: optional: true jiti@2.6.1: - resolution: - { - integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==, - } + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true js-beautify@1.15.4: - resolution: - { - integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} hasBin: true js-cookie@3.0.5: - resolution: - { - integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} js-sha3@0.8.0: - resolution: - { - integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==, - } + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@3.14.2: - resolution: - { - integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==, - } + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} hasBin: true js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true js-yaml@4.1.1: - resolution: - { - integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==, - } + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true jsbn@0.1.1: - resolution: - { - integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==, - } + resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} jsesc@3.1.0: - resolution: - { - integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-parse-better-errors@1.0.2: - resolution: - { - integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, - } + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: - resolution: - { - integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, - } + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-parse-even-better-errors@3.0.2: - resolution: - { - integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: - resolution: - { - integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, - } + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} json-schema@0.4.0: - resolution: - { - integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==, - } + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json-stringify-nice@1.1.4: - resolution: - { - integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==, - } + resolution: {integrity: sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==} json-stringify-safe@5.0.1: - resolution: - { - integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==, - } + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} hasBin: true jsonc-parser@3.2.0: - resolution: - { - integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==, - } + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} jsonfile@6.2.0: - resolution: - { - integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, - } + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} jsonparse@1.3.1: - resolution: - { - integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, - } - engines: { '0': node >= 0.2.0 } + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} jsonwebtoken@9.0.3: - resolution: - { - integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==, - } - engines: { node: '>=12', npm: '>=6' } + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} + engines: {node: '>=12', npm: '>=6'} jsprim@1.4.2: - resolution: - { - integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==, - } - engines: { node: '>=0.6.0' } + resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==} + engines: {node: '>=0.6.0'} juice@7.0.0: - resolution: - { - integrity: sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q==} + engines: {node: '>=10.0.0'} hasBin: true just-diff-apply@5.5.0: - resolution: - { - integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==, - } + resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} just-diff@6.0.2: - resolution: - { - integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==, - } + resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} jwa@2.0.1: - resolution: - { - integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==, - } + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} jws@4.0.1: - resolution: - { - integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==, - } + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} keyv@4.5.4: - resolution: - { - integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, - } + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} kind-of@6.0.3: - resolution: - { - integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} komoji@0.8.0: - resolution: - { - integrity: sha512-+Ud4ubAJhhTWneLv8V/1OyrQMwrK7ZCHDY7QJJBjaypvTCM8+ECCfKWVZrYz5NIcswuBfiYsDNYJ5kxGUwsoOw==, - } + resolution: {integrity: sha512-+Ud4ubAJhhTWneLv8V/1OyrQMwrK7ZCHDY7QJJBjaypvTCM8+ECCfKWVZrYz5NIcswuBfiYsDNYJ5kxGUwsoOw==} lerna@8.2.4: - resolution: - { - integrity: sha512-0gaVWDIVT7fLfprfwpYcQajb7dBJv3EGavjG7zvJ+TmGx3/wovl5GklnSwM2/WeE0Z2wrIz7ndWhBcDUHVjOcQ==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-0gaVWDIVT7fLfprfwpYcQajb7dBJv3EGavjG7zvJ+TmGx3/wovl5GklnSwM2/WeE0Z2wrIz7ndWhBcDUHVjOcQ==} + engines: {node: '>=18.0.0'} hasBin: true leven@3.1.0: - resolution: - { - integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} libnpmaccess@8.0.6: - resolution: - { - integrity: sha512-uM8DHDEfYG6G5gVivVl+yQd4pH3uRclHC59lzIbSvy7b5FEwR+mU49Zq1jEyRtRFv7+M99mUW9S0wL/4laT4lw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-uM8DHDEfYG6G5gVivVl+yQd4pH3uRclHC59lzIbSvy7b5FEwR+mU49Zq1jEyRtRFv7+M99mUW9S0wL/4laT4lw==} + engines: {node: ^16.14.0 || >=18.0.0} libnpmpublish@9.0.9: - resolution: - { - integrity: sha512-26zzwoBNAvX9AWOPiqqF6FG4HrSCPsHFkQm7nT+xU1ggAujL/eae81RnCv4CJ2In9q9fh10B88sYSzKCUh/Ghg==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-26zzwoBNAvX9AWOPiqqF6FG4HrSCPsHFkQm7nT+xU1ggAujL/eae81RnCv4CJ2In9q9fh10B88sYSzKCUh/Ghg==} + engines: {node: ^16.14.0 || >=18.0.0} libpg-query@17.7.3: - resolution: - { - integrity: sha512-lHKBvoWRsXt/9bJxpAeFxkLu0CA6tELusqy3o1z6/DwGXSETxhKJDaNlNdrNV8msvXDLBhpg/4RE/fKKs5rYFA==, - } + resolution: {integrity: sha512-lHKBvoWRsXt/9bJxpAeFxkLu0CA6tELusqy3o1z6/DwGXSETxhKJDaNlNdrNV8msvXDLBhpg/4RE/fKKs5rYFA==} lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} lines-and-columns@2.0.3: - resolution: - { - integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} load-json-file@4.0.0: - resolution: - { - integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} load-json-file@6.2.0: - resolution: - { - integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==} + engines: {node: '>=8'} locate-path@2.0.0: - resolution: - { - integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} + engines: {node: '>=4'} locate-path@5.0.0: - resolution: - { - integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} lodash.includes@4.3.0: - resolution: - { - integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==, - } + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} lodash.isboolean@3.0.3: - resolution: - { - integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==, - } + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} lodash.isinteger@4.0.4: - resolution: - { - integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==, - } + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} lodash.ismatch@4.4.0: - resolution: - { - integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==, - } + resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} lodash.isnumber@3.0.3: - resolution: - { - integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==, - } + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} lodash.isplainobject@4.0.6: - resolution: - { - integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, - } + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} lodash.isstring@4.0.1: - resolution: - { - integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==, - } + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} lodash.memoize@4.1.2: - resolution: - { - integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==, - } + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} lodash.once@4.1.1: - resolution: - { - integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==, - } + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} lodash@4.17.21: - resolution: - { - integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, - } + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} log-symbols@4.1.0: - resolution: - { - integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} long-timeout@0.1.1: - resolution: - { - integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==, - } + resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} long@5.3.2: - resolution: - { - integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==, - } + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true lower-case@1.1.4: - resolution: - { - integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==, - } + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} lru-cache@10.4.3: - resolution: - { - integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, - } + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@11.2.4: - resolution: - { - integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} lru-cache@4.1.5: - resolution: - { - integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==, - } + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lru-cache@6.0.0: - resolution: - { - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} lz-string@1.5.0: - resolution: - { - integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, - } + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true mailgun.js@10.4.0: - resolution: - { - integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==} + engines: {node: '>=18.0.0'} makage@0.1.10: - resolution: - { - integrity: sha512-IQKuRbHOrDgVNlydle+XRO5iMyaozBq4Bb9vhEzwxtvzyk08JkQo5qpfFRep0dSum53gECdX2gBoTmkWDHIfJA==, - } + resolution: {integrity: sha512-IQKuRbHOrDgVNlydle+XRO5iMyaozBq4Bb9vhEzwxtvzyk08JkQo5qpfFRep0dSum53gECdX2gBoTmkWDHIfJA==} hasBin: true make-dir@2.1.0: - resolution: - { - integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} make-dir@4.0.0: - resolution: - { - integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} make-error@1.3.6: - resolution: - { - integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==, - } + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} make-fetch-happen@13.0.1: - resolution: - { - integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==} + engines: {node: ^16.14.0 || >=18.0.0} makeerror@1.0.12: - resolution: - { - integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, - } + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} map-obj@1.0.1: - resolution: - { - integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} map-obj@4.3.0: - resolution: - { - integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} match-sorter@6.3.4: - resolution: - { - integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==, - } + resolution: {integrity: sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==} math-intrinsics@1.1.0: - resolution: - { - integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} media-typer@0.3.0: - resolution: - { - integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} media-typer@1.1.0: - resolution: - { - integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} mensch@0.3.4: - resolution: - { - integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==, - } + resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} meow@8.1.2: - resolution: - { - integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} + engines: {node: '>=10'} merge-descriptors@2.0.0: - resolution: - { - integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} merge-stream@2.0.0: - resolution: - { - integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, - } + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} methods@1.1.2: - resolution: - { - integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} micromatch@4.0.8: - resolution: - { - integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} microseconds@0.2.0: - resolution: - { - integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==, - } + resolution: {integrity: sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==} mime-db@1.52.0: - resolution: - { - integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} mime-db@1.54.0: - resolution: - { - integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} mime-types@2.1.35: - resolution: - { - integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} mime-types@3.0.2: - resolution: - { - integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} mime@2.6.0: - resolution: - { - integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==, - } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} hasBin: true mimic-fn@2.1.0: - resolution: - { - integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} min-indent@1.0.1: - resolution: - { - integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} minimatch@10.1.1: - resolution: - { - integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} minimatch@3.0.5: - resolution: - { - integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==, - } + resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} minimatch@5.1.6: - resolution: - { - integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} minimatch@8.0.4: - resolution: - { - integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.1: - resolution: - { - integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.3: - resolution: - { - integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} + engines: {node: '>=16 || 14 >=14.17'} minimatch@9.0.5: - resolution: - { - integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} minimist-options@4.1.0: - resolution: - { - integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} minipass-collect@2.0.1: - resolution: - { - integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} minipass-fetch@3.0.5: - resolution: - { - integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} minipass-flush@1.0.5: - resolution: - { - integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} minipass-pipeline@1.2.4: - resolution: - { - integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} minipass-sized@1.0.3: - resolution: - { - integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} minipass@3.3.6: - resolution: - { - integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} minipass@4.2.8: - resolution: - { - integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} + engines: {node: '>=8'} minipass@5.0.0: - resolution: - { - integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} minipass@7.1.2: - resolution: - { - integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==, - } - engines: { node: '>=16 || 14 >=14.17' } + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} minizlib@2.1.2: - resolution: - { - integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} mjml-accordion@4.7.1: - resolution: - { - integrity: sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg==, - } + resolution: {integrity: sha512-oYwC/CLOUWJ6pRt2saDHj/HytGOHO5B5lKNqUAhKPye5HFNZykKEV5ChmZ2NfGsGU+9BhQ7H5DaCafp4fDmPAg==} mjml-body@4.7.1: - resolution: - { - integrity: sha512-JCrkit+kjCfQyKuVyWSOonM2LGs/o3+63R9l2SleFeXf3+0CaKWaZr/Exzvaeo28c+1o3yRqXbJIpD22SEtJfQ==, - } + resolution: {integrity: sha512-JCrkit+kjCfQyKuVyWSOonM2LGs/o3+63R9l2SleFeXf3+0CaKWaZr/Exzvaeo28c+1o3yRqXbJIpD22SEtJfQ==} mjml-button@4.7.1: - resolution: - { - integrity: sha512-N3WkTMPOvKw2y6sakt1YfYDbOB8apumm1OApPG6J18CHcrX03BwhHPrdfu1JwlRNGwx4kCDdb6zNCGPwuZxkCg==, - } + resolution: {integrity: sha512-N3WkTMPOvKw2y6sakt1YfYDbOB8apumm1OApPG6J18CHcrX03BwhHPrdfu1JwlRNGwx4kCDdb6zNCGPwuZxkCg==} mjml-carousel@4.7.1: - resolution: - { - integrity: sha512-eH3rRyX23ES0BKOn+UUV39+yGNmZVApBVVV0A5znDaNWskCg6/g6ZhEHi4nkWpj+aP2lJKI0HX1nrMfJg0Mxhg==, - } + resolution: {integrity: sha512-eH3rRyX23ES0BKOn+UUV39+yGNmZVApBVVV0A5znDaNWskCg6/g6ZhEHi4nkWpj+aP2lJKI0HX1nrMfJg0Mxhg==} mjml-cli@4.7.1: - resolution: - { - integrity: sha512-xzCtJVKYVhGorvTmnbcMUfZlmJdBnu1UBD9A1H8UUBGMNE/Hs9QpHs9PLCMp8JR/uhSu15IgVjhFN0oSVndMRQ==, - } + resolution: {integrity: sha512-xzCtJVKYVhGorvTmnbcMUfZlmJdBnu1UBD9A1H8UUBGMNE/Hs9QpHs9PLCMp8JR/uhSu15IgVjhFN0oSVndMRQ==} hasBin: true mjml-column@4.7.1: - resolution: - { - integrity: sha512-CGw81TnGiuPR1GblLOez8xeoeAz1SEFjMpqapazjgXUuF5xUxg3qH55Wt4frpXe3VypeZWVYeumr6CwoNaPbKg==, - } + resolution: {integrity: sha512-CGw81TnGiuPR1GblLOez8xeoeAz1SEFjMpqapazjgXUuF5xUxg3qH55Wt4frpXe3VypeZWVYeumr6CwoNaPbKg==} mjml-core@4.7.1: - resolution: - { - integrity: sha512-AMACoq/h440m7SM86As8knW0bNQgjNIzsP/cMF6X9RO07GfszgbaWUq/XCaRNi+q8bWvBJSCXbngDJySVc5ALw==, - } + resolution: {integrity: sha512-AMACoq/h440m7SM86As8knW0bNQgjNIzsP/cMF6X9RO07GfszgbaWUq/XCaRNi+q8bWvBJSCXbngDJySVc5ALw==} mjml-divider@4.7.1: - resolution: - { - integrity: sha512-7+uCUJdqEr6w8AzpF8lhRheelYEgOwiK0KJGlAQN3LF+h2S1rTPEzEB67qL2x5cU+80kPlxtxoQWImDBy0vXqg==, - } + resolution: {integrity: sha512-7+uCUJdqEr6w8AzpF8lhRheelYEgOwiK0KJGlAQN3LF+h2S1rTPEzEB67qL2x5cU+80kPlxtxoQWImDBy0vXqg==} mjml-group@4.7.1: - resolution: - { - integrity: sha512-mAYdhocCzetdhPSws/9/sQ4hcz4kQPX2dNitQmbxNVwoMFYXjp/WcLEfGc5u13Ue7dPfcV6c9lB/Uu5o3NmRvw==, - } + resolution: {integrity: sha512-mAYdhocCzetdhPSws/9/sQ4hcz4kQPX2dNitQmbxNVwoMFYXjp/WcLEfGc5u13Ue7dPfcV6c9lB/Uu5o3NmRvw==} mjml-head-attributes@4.7.1: - resolution: - { - integrity: sha512-nB/bQ3I98Dvy/IkI4nqxTCnLonULkIKc8KrieRTrtPkUV3wskBzngpCgnjKvFPbHWiGlwjHDzcFJc7G0uWeqog==, - } + resolution: {integrity: sha512-nB/bQ3I98Dvy/IkI4nqxTCnLonULkIKc8KrieRTrtPkUV3wskBzngpCgnjKvFPbHWiGlwjHDzcFJc7G0uWeqog==} mjml-head-breakpoint@4.7.1: - resolution: - { - integrity: sha512-0KB5SweIWDvwHkn4VCUsEhCQgfY/0wkNUnSXNoftaRujv0NQFQfOOH4eINy0NZYfDfrE4WYe08z+olHprp+T2A==, - } + resolution: {integrity: sha512-0KB5SweIWDvwHkn4VCUsEhCQgfY/0wkNUnSXNoftaRujv0NQFQfOOH4eINy0NZYfDfrE4WYe08z+olHprp+T2A==} mjml-head-font@4.7.1: - resolution: - { - integrity: sha512-9YGzBcQ2htZ6j266fiLLfzcxqDEDLTvfKtypTjaeRb1w3N8S5wL+/zJA5ZjRL6r39Ij5ZPQSlSDC32KPiwhGkA==, - } + resolution: {integrity: sha512-9YGzBcQ2htZ6j266fiLLfzcxqDEDLTvfKtypTjaeRb1w3N8S5wL+/zJA5ZjRL6r39Ij5ZPQSlSDC32KPiwhGkA==} mjml-head-html-attributes@4.7.1: - resolution: - { - integrity: sha512-2TK2nGpq4rGaghbVx2UNm5TXeZ5BTGYEvtSPoYPNu02KRCj6tb+uedAgFXwJpX+ogRfIfPK50ih+9ZMoHwf2IQ==, - } + resolution: {integrity: sha512-2TK2nGpq4rGaghbVx2UNm5TXeZ5BTGYEvtSPoYPNu02KRCj6tb+uedAgFXwJpX+ogRfIfPK50ih+9ZMoHwf2IQ==} mjml-head-preview@4.7.1: - resolution: - { - integrity: sha512-UHlvvgldiPDODq/5zKMsmXgRb/ZyKygKDUVQSM5bm3HvpKXeyYxJZazcIGmlGICEqv1ced1WGINhCg72dSfN+Q==, - } + resolution: {integrity: sha512-UHlvvgldiPDODq/5zKMsmXgRb/ZyKygKDUVQSM5bm3HvpKXeyYxJZazcIGmlGICEqv1ced1WGINhCg72dSfN+Q==} mjml-head-style@4.7.1: - resolution: - { - integrity: sha512-8Gij99puN1SoOx5tGBjgkh4iCpI+zbwGBiB2Y8VwJrwXQxdJ1Qa902dQP5djoFFG39Bthii/48cS/d1bHigGPQ==, - } + resolution: {integrity: sha512-8Gij99puN1SoOx5tGBjgkh4iCpI+zbwGBiB2Y8VwJrwXQxdJ1Qa902dQP5djoFFG39Bthii/48cS/d1bHigGPQ==} mjml-head-title@4.7.1: - resolution: - { - integrity: sha512-vK3r+DApTXw2EoK/fh8dQOsO438Z7Ksy6iBIb7h04x33d4Z41r6+jtgxGXoKFXnjgr8MyLX5HZyyie5obW+hZg==, - } + resolution: {integrity: sha512-vK3r+DApTXw2EoK/fh8dQOsO438Z7Ksy6iBIb7h04x33d4Z41r6+jtgxGXoKFXnjgr8MyLX5HZyyie5obW+hZg==} mjml-head@4.7.1: - resolution: - { - integrity: sha512-jUcJ674CT1oT8NTQWTjQQBFZu4yklK0oppfGFJ1cq76ze3isMiyhSnGnOHw6FkjLnZtb3gXXaGKX7UZM+UMk/w==, - } + resolution: {integrity: sha512-jUcJ674CT1oT8NTQWTjQQBFZu4yklK0oppfGFJ1cq76ze3isMiyhSnGnOHw6FkjLnZtb3gXXaGKX7UZM+UMk/w==} mjml-hero@4.7.1: - resolution: - { - integrity: sha512-x+29V8zJAs8EV/eTtGbR921pCpitMQOAkyvNANW/3JLDTL2Oio1OYvGPVC3z1wOT9LKuRTxVzNHVt/bBw02CSQ==, - } + resolution: {integrity: sha512-x+29V8zJAs8EV/eTtGbR921pCpitMQOAkyvNANW/3JLDTL2Oio1OYvGPVC3z1wOT9LKuRTxVzNHVt/bBw02CSQ==} mjml-image@4.7.1: - resolution: - { - integrity: sha512-l3uRR2jaM0Bpz4ctdWuxQUFgg+ol6Nt+ODOrnHsGMwpmFOh4hTPTky6KaF0LCXxYmGbI0FoGBna+hVNnkBsQCA==, - } + resolution: {integrity: sha512-l3uRR2jaM0Bpz4ctdWuxQUFgg+ol6Nt+ODOrnHsGMwpmFOh4hTPTky6KaF0LCXxYmGbI0FoGBna+hVNnkBsQCA==} mjml-migrate@4.7.1: - resolution: - { - integrity: sha512-RgrJ9fHg6iRHC2H4pjRDWilBQ1eTH2jRu1ayDplbnepGoql83vLZaYaWc5Q+J+NsaNI16x+bgNB3fQdBiK+mng==, - } + resolution: {integrity: sha512-RgrJ9fHg6iRHC2H4pjRDWilBQ1eTH2jRu1ayDplbnepGoql83vLZaYaWc5Q+J+NsaNI16x+bgNB3fQdBiK+mng==} hasBin: true mjml-navbar@4.7.1: - resolution: - { - integrity: sha512-awdu8zT7xhS+9aCVunqtocUs8KA2xb+UhJ8UGbxVBpYbTNj3rCL9aWUXqWVwMk1la+3ypCkFuDuTl6dIoWPWlA==, - } + resolution: {integrity: sha512-awdu8zT7xhS+9aCVunqtocUs8KA2xb+UhJ8UGbxVBpYbTNj3rCL9aWUXqWVwMk1la+3ypCkFuDuTl6dIoWPWlA==} mjml-parser-xml@4.7.1: - resolution: - { - integrity: sha512-UWfuRpN45k3GUEv2yl8n5Uf98Tg6FyCsyRnqZGo83mgZzlJRDYTdKII9RjZM646/S8+Q8e9qxi3AsL00j6sZsQ==, - } + resolution: {integrity: sha512-UWfuRpN45k3GUEv2yl8n5Uf98Tg6FyCsyRnqZGo83mgZzlJRDYTdKII9RjZM646/S8+Q8e9qxi3AsL00j6sZsQ==} mjml-raw@4.7.1: - resolution: - { - integrity: sha512-mCQFEXINTkC8i7ydP1Km99e0FaZTeu79AoYnTBAILd4QO+RuD3n/PimBGrcGrOUex0JIKa2jyVQOcSCBuG4WpA==, - } + resolution: {integrity: sha512-mCQFEXINTkC8i7ydP1Km99e0FaZTeu79AoYnTBAILd4QO+RuD3n/PimBGrcGrOUex0JIKa2jyVQOcSCBuG4WpA==} mjml-react@1.0.59: - resolution: - { - integrity: sha512-W1ULnMlxJHE0kNpInu+u3CHr6+QcvhoLJ2ov93Pzt2A1wXAv4CJ9T/P5h/BhZn8vvCXgGizcwHv8sfANfQONVw==, - } + resolution: {integrity: sha512-W1ULnMlxJHE0kNpInu+u3CHr6+QcvhoLJ2ov93Pzt2A1wXAv4CJ9T/P5h/BhZn8vvCXgGizcwHv8sfANfQONVw==} peerDependencies: mjml: ^4.1.2 react: ^16.4.0 react-dom: ^16.4.0 mjml-section@4.7.1: - resolution: - { - integrity: sha512-PlhCMsl/bpFwwgQGUopi9OgOGWgRPpEJVKE8hk4He8GXzbfIuDj4DZ9QJSkwIoZ0fZtcgz11Wwb19i9BZcozVw==, - } + resolution: {integrity: sha512-PlhCMsl/bpFwwgQGUopi9OgOGWgRPpEJVKE8hk4He8GXzbfIuDj4DZ9QJSkwIoZ0fZtcgz11Wwb19i9BZcozVw==} mjml-social@4.7.1: - resolution: - { - integrity: sha512-tN/6V3m59izO9rqWpUokHxhwkk2GHkltzIlhI936hAJHh8hFyEO6+ZwQBZm738G00qgfICmQvX5FNq4upkCYjw==, - } + resolution: {integrity: sha512-tN/6V3m59izO9rqWpUokHxhwkk2GHkltzIlhI936hAJHh8hFyEO6+ZwQBZm738G00qgfICmQvX5FNq4upkCYjw==} mjml-spacer@4.7.1: - resolution: - { - integrity: sha512-gQu1+nA9YGnoolfNPvzfVe/RJ8WqS8ho0hthlhiLOC2RnEnmqH7HHSzCFXm4OeN0VgvDQsM7mfYQGl82O58Y+g==, - } + resolution: {integrity: sha512-gQu1+nA9YGnoolfNPvzfVe/RJ8WqS8ho0hthlhiLOC2RnEnmqH7HHSzCFXm4OeN0VgvDQsM7mfYQGl82O58Y+g==} mjml-table@4.7.1: - resolution: - { - integrity: sha512-rPkOtufMiVreb7I7vXk6rDm9i1DXncODnM5JJNhA9Z1dAQwXiz6V5904gAi2cEYfe0M2m0XQ8P5ZCtvqxGkfGA==, - } + resolution: {integrity: sha512-rPkOtufMiVreb7I7vXk6rDm9i1DXncODnM5JJNhA9Z1dAQwXiz6V5904gAi2cEYfe0M2m0XQ8P5ZCtvqxGkfGA==} mjml-text@4.7.1: - resolution: - { - integrity: sha512-hrjxbY59v6hu/Pn0NO+6TMlrdAlRa3M7GVALx/YWYV3hi59zjYfot8Au7Xq64XdcbcI4eiBVbP/AVr8w03HsOw==, - } + resolution: {integrity: sha512-hrjxbY59v6hu/Pn0NO+6TMlrdAlRa3M7GVALx/YWYV3hi59zjYfot8Au7Xq64XdcbcI4eiBVbP/AVr8w03HsOw==} mjml-validator@4.7.1: - resolution: - { - integrity: sha512-Qxubbz5WE182iLSTd/XRuezMr6UE7/u73grDCw0bTIcQsaTAIkWQn2tBI3jj0chWOw+sxwK2C6zPm9B0Cv7BGA==, - } + resolution: {integrity: sha512-Qxubbz5WE182iLSTd/XRuezMr6UE7/u73grDCw0bTIcQsaTAIkWQn2tBI3jj0chWOw+sxwK2C6zPm9B0Cv7BGA==} mjml-wrapper@4.7.1: - resolution: - { - integrity: sha512-6i+ZATUyqIO5YBnx+RFKZ3+6mg3iOCS/EdXGYZSonZ/EHqlt+RJa3fG2BB4dacXqAjghfl6Lk+bLoR47P3xYIQ==, - } + resolution: {integrity: sha512-6i+ZATUyqIO5YBnx+RFKZ3+6mg3iOCS/EdXGYZSonZ/EHqlt+RJa3fG2BB4dacXqAjghfl6Lk+bLoR47P3xYIQ==} mjml@4.7.1: - resolution: - { - integrity: sha512-nwMrmhTI+Aeh9Gav9LHX/i8k8yDi/QpX5h535BlT5oP4NaAUmyxP/UeYUn9yxtPcIzDlM5ullFnRv/71jyHpkQ==, - } + resolution: {integrity: sha512-nwMrmhTI+Aeh9Gav9LHX/i8k8yDi/QpX5h535BlT5oP4NaAUmyxP/UeYUn9yxtPcIzDlM5ullFnRv/71jyHpkQ==} hasBin: true mkdirp@1.0.4: - resolution: - { - integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} hasBin: true mock-req@0.2.0: - resolution: - { - integrity: sha512-IUuwS0W5GjoPyjhuXPQJXpaHfHW7UYFRia8Cchm/xRuyDDclpSQdEoakt3krOpSYvgVlQsbnf0ePDsTRDfp7Dg==, - } + resolution: {integrity: sha512-IUuwS0W5GjoPyjhuXPQJXpaHfHW7UYFRia8Cchm/xRuyDDclpSQdEoakt3krOpSYvgVlQsbnf0ePDsTRDfp7Dg==} modify-values@1.0.1: - resolution: - { - integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} + engines: {node: '>=0.10.0'} moment-timezone@0.5.48: - resolution: - { - integrity: sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==, - } + resolution: {integrity: sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==} moment@2.30.1: - resolution: - { - integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==, - } + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} ms@2.0.0: - resolution: - { - integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==, - } + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} multimatch@5.0.0: - resolution: - { - integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} mute-stream@0.0.8: - resolution: - { - integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==, - } + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} mute-stream@1.0.0: - resolution: - { - integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} nano-time@1.0.0: - resolution: - { - integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==, - } + resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true napi-postinstall@0.3.4: - resolution: - { - integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==, - } - engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} negotiator@0.6.4: - resolution: - { - integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} negotiator@1.0.0: - resolution: - { - integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} neo-async@2.6.2: - resolution: - { - integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, - } + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} nested-obj@0.1.10: - resolution: - { - integrity: sha512-5V2kUPrBee/tmoS2p0IJ35BcaJuW1p1yXF5GP8JpXIkDoPbaYeYypAHizUeZkAUxcC7Rago7izWmEq7qa8+Mhw==, - } + resolution: {integrity: sha512-5V2kUPrBee/tmoS2p0IJ35BcaJuW1p1yXF5GP8JpXIkDoPbaYeYypAHizUeZkAUxcC7Rago7izWmEq7qa8+Mhw==} nested-obj@0.1.5: - resolution: - { - integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==, - } + resolution: {integrity: sha512-04Y7qDMlI8RbYTn0cJAKaw/mLrO9UmLj3xbrjTZKDfOn9f3b/RXEQFIIpveJlwn8KfPwdVFWLZUaL5gNuQ7G0w==} nested-obj@0.2.0: - resolution: - { - integrity: sha512-uPzih1V6f7yb563xUkFA/oainPdrlc0ojpV8OuRAg4qWK70TPt14D5hWuU3ta1eVacJQv+VVuMJRqFRyTgYZ0Q==, - } + resolution: {integrity: sha512-uPzih1V6f7yb563xUkFA/oainPdrlc0ojpV8OuRAg4qWK70TPt14D5hWuU3ta1eVacJQv+VVuMJRqFRyTgYZ0Q==} no-case@2.3.2: - resolution: - { - integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==, - } + resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} node-fetch@2.6.7: - resolution: - { - integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==, - } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -10455,11 +7417,8 @@ packages: optional: true node-fetch@2.7.0: - resolution: - { - integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, - } - engines: { node: 4.x || >=6.0.0 } + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} peerDependencies: encoding: ^0.1.0 peerDependenciesMeta: @@ -10467,173 +7426,98 @@ packages: optional: true node-gyp@10.3.1: - resolution: - { - integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true node-int64@0.4.0: - resolution: - { - integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, - } + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} node-machine-id@1.1.12: - resolution: - { - integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==, - } + resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} node-releases@2.0.27: - resolution: - { - integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==, - } + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} node-schedule@1.3.2: - resolution: - { - integrity: sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw==, - } + resolution: {integrity: sha512-GIND2pHMHiReSZSvS6dpZcDH7pGPGFfWBIEud6S00Q8zEIzAs9ommdyRK1ZbQt8y1LyZsJYZgPnyi7gpU2lcdw==} nodemailer@6.10.1: - resolution: - { - integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} + engines: {node: '>=6.0.0'} nodemailer@7.0.11: - resolution: - { - integrity: sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==, - } - engines: { node: '>=6.0.0' } + resolution: {integrity: sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==} + engines: {node: '>=6.0.0'} nodemon@3.1.11: - resolution: - { - integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} + engines: {node: '>=10'} hasBin: true noms@0.0.0: - resolution: - { - integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==, - } + resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} nopt@7.2.1: - resolution: - { - integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} hasBin: true normalize-package-data@2.5.0: - resolution: - { - integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, - } + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} normalize-package-data@3.0.3: - resolution: - { - integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} normalize-package-data@6.0.2: - resolution: - { - integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} + engines: {node: ^16.14.0 || >=18.0.0} normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} npm-bundled@3.0.1: - resolution: - { - integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-install-checks@6.3.0: - resolution: - { - integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-normalize-package-bin@3.0.1: - resolution: - { - integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-package-arg@11.0.2: - resolution: - { - integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-IGN0IAwmhDJwy13Wc8k+4PEbTPhpJnMtfR53ZbOyjkvmEcLS4nCwp6mvMWjS5sUjeiW3mpx6cHmuhKEu9XmcQw==} + engines: {node: ^16.14.0 || >=18.0.0} npm-packlist@8.0.2: - resolution: - { - integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} npm-pick-manifest@9.1.0: - resolution: - { - integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==} + engines: {node: ^16.14.0 || >=18.0.0} npm-registry-fetch@17.1.0: - resolution: - { - integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==} + engines: {node: ^16.14.0 || >=18.0.0} npm-run-path@4.0.1: - resolution: - { - integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} nth-check@1.0.2: - resolution: - { - integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==, - } + resolution: {integrity: sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==} nth-check@2.1.1: - resolution: - { - integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, - } + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} nx@20.8.3: - resolution: - { - integrity: sha512-8w815WSMWar3A/LFzwtmEY+E8cVW62lMiFuPDXje+C8O8hFndfvscP56QHNMn2Zdhz3q0+BZUe+se4Em1BKYdA==, - } + resolution: {integrity: sha512-8w815WSMWar3A/LFzwtmEY+E8cVW62lMiFuPDXje+C8O8hFndfvscP56QHNMn2Zdhz3q0+BZUe+se4Em1BKYdA==} hasBin: true peerDependencies: '@swc-node/register': ^1.8.0 @@ -10645,470 +7529,263 @@ packages: optional: true oauth-sign@0.9.0: - resolution: - { - integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==, - } + resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} object-inspect@1.13.4: - resolution: - { - integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} object-keys@1.1.1: - resolution: - { - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} object-path@0.11.8: - resolution: - { - integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==, - } - engines: { node: '>= 10.12.0' } + resolution: {integrity: sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==} + engines: {node: '>= 10.12.0'} oblivious-set@1.0.0: - resolution: - { - integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==, - } + resolution: {integrity: sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==} on-finished@2.3.0: - resolution: - { - integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} on-finished@2.4.1: - resolution: - { - integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} once@1.4.0: - resolution: - { - integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, - } + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} onetime@5.1.2: - resolution: - { - integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} open@8.4.2: - resolution: - { - integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} optionator@0.9.4: - resolution: - { - integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} ora@5.3.0: - resolution: - { - integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + engines: {node: '>=10'} ora@5.4.1: - resolution: - { - integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} oxfmt@0.26.0: - resolution: - { - integrity: sha512-UDD1wFNwfeorMm2ZY0xy1KRAAvJ5NjKBfbDmiMwGP7baEHTq65cYpC0aPP+BGHc8weXUbSZaK8MdGyvuRUvS4Q==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-UDD1wFNwfeorMm2ZY0xy1KRAAvJ5NjKBfbDmiMwGP7baEHTq65cYpC0aPP+BGHc8weXUbSZaK8MdGyvuRUvS4Q==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true p-finally@1.0.0: - resolution: - { - integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} p-limit@1.3.0: - resolution: - { - integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} + engines: {node: '>=4'} p-limit@2.3.0: - resolution: - { - integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} p-locate@2.0.0: - resolution: - { - integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} + engines: {node: '>=4'} p-locate@4.1.0: - resolution: - { - integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} p-map-series@2.1.0: - resolution: - { - integrity: sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RpYIIK1zXSNEOdwxcfe7FdvGcs7+y5n8rifMhMNWvaxRNMPINJHF5GDeuVxWqnfrcHPSCnp7Oo5yNXHId9Av2Q==} + engines: {node: '>=8'} p-map@4.0.0: - resolution: - { - integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} p-pipe@3.1.0: - resolution: - { - integrity: sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-08pj8ATpzMR0Y80x50yJHn37NF6vjrqHutASaX5LiH5npS9XPvrUmscd9MF5R4fuYRHOxQR1FfMIlF7AzwoPqw==} + engines: {node: '>=8'} p-queue@6.6.2: - resolution: - { - integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} + engines: {node: '>=8'} p-reduce@2.1.0: - resolution: - { - integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} + engines: {node: '>=8'} p-timeout@3.2.0: - resolution: - { - integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} p-try@1.0.0: - resolution: - { - integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} + engines: {node: '>=4'} p-try@2.2.0: - resolution: - { - integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} p-waterfall@2.1.1: - resolution: - { - integrity: sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-RRTnDb2TBG/epPRI2yYXsimO0v3BXC8Yd3ogr1545IaqKK17VGhbWVeGGN+XfCm/08OK8635nH31c8bATkHuSw==} + engines: {node: '>=8'} package-json-from-dist@1.0.1: - resolution: - { - integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==, - } + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} pacote@18.0.6: - resolution: - { - integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==} + engines: {node: ^16.14.0 || >=18.0.0} hasBin: true param-case@2.1.1: - resolution: - { - integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==, - } + resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} parse-conflict-json@3.0.1: - resolution: - { - integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} parse-json@4.0.0: - resolution: - { - integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} parse-json@5.2.0: - resolution: - { - integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} parse-package-name@1.0.0: - resolution: - { - integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==, - } + resolution: {integrity: sha512-kBeTUtcj+SkyfaW4+KBe0HtsloBJ/mKTPoxpVdA57GZiPerREsUWJOhVj9anXweFiJkm5y8FG1sxFZkZ0SN6wg==} parse-path@7.1.0: - resolution: - { - integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==, - } + resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==} parse-url@8.1.0: - resolution: - { - integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==, - } + resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} parse5-htmlparser2-tree-adapter@7.1.0: - resolution: - { - integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==, - } + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} parse5-parser-stream@7.1.2: - resolution: - { - integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==, - } + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} parse5@3.0.3: - resolution: - { - integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==, - } + resolution: {integrity: sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==} parse5@7.3.0: - resolution: - { - integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==, - } + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} parseurl@1.3.3: - resolution: - { - integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} path-exists@3.0.0: - resolution: - { - integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} path-is-absolute@1.0.1: - resolution: - { - integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} path-scurry@1.11.1: - resolution: - { - integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, - } - engines: { node: '>=16 || 14 >=14.18' } + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} path-scurry@2.0.1: - resolution: - { - integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} path-to-regexp@8.3.0: - resolution: - { - integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==, - } + resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} path-type@3.0.0: - resolution: - { - integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} performance-now@2.1.0: - resolution: - { - integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==, - } + resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} pg-cloudflare@1.3.0: - resolution: - { - integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==, - } + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} pg-connection-string@2.10.0: - resolution: - { - integrity: sha512-ur/eoPKzDx2IjPaYyXS6Y8NSblxM7X64deV2ObV57vhjsWiwLvUD6meukAzogiOsu60GO8m/3Cb6FdJsWNjwXg==, - } + resolution: {integrity: sha512-ur/eoPKzDx2IjPaYyXS6Y8NSblxM7X64deV2ObV57vhjsWiwLvUD6meukAzogiOsu60GO8m/3Cb6FdJsWNjwXg==} pg-connection-string@2.9.1: - resolution: - { - integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==, - } + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} pg-copy-streams@7.0.0: - resolution: - { - integrity: sha512-zBvnY6wtaBRE2ae2xXWOOGMaNVPkXh1vhypAkNSKgMdciJeTyIQAHZaEeRAxUjs/p1El5jgzYmwG5u871Zj3dQ==, - } + resolution: {integrity: sha512-zBvnY6wtaBRE2ae2xXWOOGMaNVPkXh1vhypAkNSKgMdciJeTyIQAHZaEeRAxUjs/p1El5jgzYmwG5u871Zj3dQ==} pg-int8@1.0.1: - resolution: - { - integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==, - } - engines: { node: '>=4.0.0' } + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} pg-pool@3.11.0: - resolution: - { - integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==, - } + resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==} peerDependencies: pg: '>=8.0' pg-proto-parser@1.30.4: - resolution: - { - integrity: sha512-+9/n8zfYQVNRc8KGhxxNXO8NA5OKni01IPtit6+C3sLMtcRVVFCj4W0XtrEGFivNjz2qwUtFmRhG8OGMTxs6hg==, - } + resolution: {integrity: sha512-+9/n8zfYQVNRc8KGhxxNXO8NA5OKni01IPtit6+C3sLMtcRVVFCj4W0XtrEGFivNjz2qwUtFmRhG8OGMTxs6hg==} pg-protocol@1.10.3: - resolution: - { - integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==, - } + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} pg-protocol@1.11.0: - resolution: - { - integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==, - } + resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} pg-sql2@4.14.1: - resolution: - { - integrity: sha512-DvL0K9Pqz47EFq+BaQlGpzsXJnArKoAbxBxtHLy2/p3ey1X7ZwUF79UwFoDSTxQQCIbR4Z5D8CBI0nPfpw9Tmw==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-DvL0K9Pqz47EFq+BaQlGpzsXJnArKoAbxBxtHLy2/p3ey1X7ZwUF79UwFoDSTxQQCIbR4Z5D8CBI0nPfpw9Tmw==} + engines: {node: '>=8.6'} peerDependencies: pg: '>=6.1.0 <9' pg-tsquery@8.4.2: - resolution: - { - integrity: sha512-waJSlBIKE+shDhuDpuQglTH6dG5zakDhnrnxu8XB8V5c7yoDSuy4pOxY6t2dyoxTjaKMcMmlByJN7n9jx9eqMA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-waJSlBIKE+shDhuDpuQglTH6dG5zakDhnrnxu8XB8V5c7yoDSuy4pOxY6t2dyoxTjaKMcMmlByJN7n9jx9eqMA==} + engines: {node: '>=10'} pg-types@2.2.0: - resolution: - { - integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} pg@8.17.1: - resolution: - { - integrity: sha512-EIR+jXdYNSMOrpRp7g6WgQr7SaZNZfS7IzZIO0oTNEeibq956JxeD15t3Jk3zZH0KH8DmOIx38qJfQenoE8bXQ==, - } - engines: { node: '>= 16.0.0' } + resolution: {integrity: sha512-EIR+jXdYNSMOrpRp7g6WgQr7SaZNZfS7IzZIO0oTNEeibq956JxeD15t3Jk3zZH0KH8DmOIx38qJfQenoE8bXQ==} + engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' peerDependenciesMeta: @@ -11116,240 +7793,142 @@ packages: optional: true pgpass@1.0.5: - resolution: - { - integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==, - } + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} pgsql-deparser@17.17.2: - resolution: - { - integrity: sha512-FCjqKY3Sdmce3VUd3CxCXF0kqaZ0s4a6yIMT5UJ9vETh0cF54A8Tpqjn0qBKaPUD8xqTKeLdS+SfiwjAC64wrA==, - } + resolution: {integrity: sha512-FCjqKY3Sdmce3VUd3CxCXF0kqaZ0s4a6yIMT5UJ9vETh0cF54A8Tpqjn0qBKaPUD8xqTKeLdS+SfiwjAC64wrA==} pgsql-parser@17.9.11: - resolution: - { - integrity: sha512-Bqp9uLvJK0Qht9PXzI6eC/Fn+lFRL+2eMvXss4D4qt7lxPLIHS8FMKYOHUQNTI3m6ylExSOdNXhx/DL5UGm3xg==, - } + resolution: {integrity: sha512-Bqp9uLvJK0Qht9PXzI6eC/Fn+lFRL+2eMvXss4D4qt7lxPLIHS8FMKYOHUQNTI3m6ylExSOdNXhx/DL5UGm3xg==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} picomatch@4.0.3: - resolution: - { - integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} pify@2.3.0: - resolution: - { - integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} pify@3.0.0: - resolution: - { - integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} pify@4.0.1: - resolution: - { - integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} pify@5.0.0: - resolution: - { - integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} pirates@4.0.7: - resolution: - { - integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} pkg-dir@4.2.0: - resolution: - { - integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} playwright-core@1.57.0: - resolution: - { - integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + engines: {node: '>=18'} hasBin: true playwright@1.57.0: - resolution: - { - integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + engines: {node: '>=18'} hasBin: true pluralize@7.0.0: - resolution: - { - integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==} + engines: {node: '>=4'} postcss-selector-parser@6.1.2: - resolution: - { - integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} postcss-value-parser@4.2.0: - resolution: - { - integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, - } + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} postgraphile-core@4.14.1: - resolution: - { - integrity: sha512-3U6DAoGUmOikl9dVQhSJcw4cLeG0vQQnvEFw7MR0rvn125c1xdv6UBvamvX0pOzSfz5oBrFRQkZ2LvclAXKyBQ==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-3U6DAoGUmOikl9dVQhSJcw4cLeG0vQQnvEFw7MR0rvn125c1xdv6UBvamvX0pOzSfz5oBrFRQkZ2LvclAXKyBQ==} + engines: {node: '>=8.6'} peerDependencies: graphql: '>=0.9 <0.14 || ^14.0.2 || ^15.4.0' pg: '>=6.1.0 <9' postgraphile@4.14.1: - resolution: - { - integrity: sha512-4Rz//TtnjyZk6CbrcypWJNFRwXupHK+bHvaYaX2RrtxMJ2lTaoMDYOdEFESdo/POie3CAEbsC8ZBqb9eR/EyVw==, - } - engines: { node: '>=8.6' } + resolution: {integrity: sha512-4Rz//TtnjyZk6CbrcypWJNFRwXupHK+bHvaYaX2RrtxMJ2lTaoMDYOdEFESdo/POie3CAEbsC8ZBqb9eR/EyVw==} + engines: {node: '>=8.6'} hasBin: true postgres-array@2.0.0: - resolution: - { - integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} postgres-bytea@1.0.1: - resolution: - { - integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} + engines: {node: '>=0.10.0'} postgres-date@1.0.7: - resolution: - { - integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} postgres-interval@1.2.0: - resolution: - { - integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} prettier@3.8.0: - resolution: - { - integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA==} + engines: {node: '>=14'} hasBin: true pretty-format@26.6.2: - resolution: - { - integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==, - } - engines: { node: '>= 10' } + resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==} + engines: {node: '>= 10'} pretty-format@29.7.0: - resolution: - { - integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} pretty-format@30.2.0: - resolution: - { - integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==, - } - engines: { node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} proc-log@4.2.0: - resolution: - { - integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} process-nextick-args@2.0.1: - resolution: - { - integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, - } + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} proggy@2.0.0: - resolution: - { - integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-69agxLtnI8xBs9gUGqEnK26UfiexpHy+KUpBQWabiytQjnn5wFY8rklAi7GRfABIuPNnQ/ik48+LGLkYYJcy4A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} promise-all-reject-late@1.0.1: - resolution: - { - integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==, - } + resolution: {integrity: sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==} promise-call-limit@3.0.2: - resolution: - { - integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==, - } + resolution: {integrity: sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw==} promise-inflight@1.0.1: - resolution: - { - integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==, - } + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} peerDependencies: bluebird: '*' peerDependenciesMeta: @@ -11357,175 +7936,100 @@ packages: optional: true promise-retry@2.0.1: - resolution: - { - integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} promzard@1.0.2: - resolution: - { - integrity: sha512-2FPputGL+mP3jJ3UZg/Dl9YOkovB7DX0oOr+ck5QbZ5MtORtds8k/BZdn+02peDLI8/YWbmzx34k5fA+fHvCVQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-2FPputGL+mP3jJ3UZg/Dl9YOkovB7DX0oOr+ck5QbZ5MtORtds8k/BZdn+02peDLI8/YWbmzx34k5fA+fHvCVQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} proto-list@1.2.4: - resolution: - { - integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==, - } + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} protocols@2.0.2: - resolution: - { - integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==, - } + resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} proxy-addr@2.0.7: - resolution: - { - integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} proxy-from-env@1.1.0: - resolution: - { - integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==, - } + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} pseudomap@1.0.2: - resolution: - { - integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==, - } + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} psl@1.15.0: - resolution: - { - integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==, - } + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} pstree.remy@1.1.8: - resolution: - { - integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==, - } + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} punycode.js@2.3.1: - resolution: - { - integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} pure-rand@7.0.1: - resolution: - { - integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==, - } + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} qs@6.14.0: - resolution: - { - integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} qs@6.14.1: - resolution: - { - integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + engines: {node: '>=0.6'} qs@6.5.3: - resolution: - { - integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + engines: {node: '>=0.6'} qs@6.7.0: - resolution: - { - integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==} + engines: {node: '>=0.6'} queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} quick-lru@4.0.1: - resolution: - { - integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} + engines: {node: '>=8'} range-parser@1.2.1: - resolution: - { - integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} raw-body@2.4.0: - resolution: - { - integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==} + engines: {node: '>= 0.8'} raw-body@3.0.2: - resolution: - { - integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} react-dom@19.2.3: - resolution: - { - integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==, - } + resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: react: ^19.2.3 react-is@16.13.1: - resolution: - { - integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==, - } + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} react-is@17.0.2: - resolution: - { - integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, - } + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react-is@18.3.1: - resolution: - { - integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==, - } + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} react-query@3.39.3: - resolution: - { - integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==, - } + resolution: {integrity: sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: '*' @@ -11536,947 +8040,568 @@ packages: react-native: optional: true + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react-test-renderer@19.2.3: + resolution: {integrity: sha512-TMR1LnSFiWZMJkCgNf5ATSvAheTT2NvKIwiVwdBPHxjBI7n/JbWd4gaZ16DVd9foAXdvDz+sB5yxZTwMjPRxpw==} + peerDependencies: + react: ^19.2.3 + react@19.2.3: - resolution: - { - integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + engines: {node: '>=0.10.0'} read-cmd-shim@4.0.0: - resolution: - { - integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} read-package-json-fast@3.0.2: - resolution: - { - integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} read-pkg-up@3.0.0: - resolution: - { - integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} + engines: {node: '>=4'} read-pkg-up@7.0.1: - resolution: - { - integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} read-pkg@3.0.0: - resolution: - { - integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} read-pkg@5.2.0: - resolution: - { - integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} read@3.0.1: - resolution: - { - integrity: sha512-SLBrDU/Srs/9EoWhU5GdbAoxG1GzpQHo/6qiGItaoLJ1thmYpcNIM1qISEUvyHBzfGlWIyd6p2DNi1oV1VmAuw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-SLBrDU/Srs/9EoWhU5GdbAoxG1GzpQHo/6qiGItaoLJ1thmYpcNIM1qISEUvyHBzfGlWIyd6p2DNi1oV1VmAuw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} readable-stream@1.0.34: - resolution: - { - integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==, - } + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} readable-stream@2.3.8: - resolution: - { - integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, - } + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} readable-stream@3.6.2: - resolution: - { - integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} readdirp@3.6.0: - resolution: - { - integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, - } - engines: { node: '>=8.10.0' } + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} redent@3.0.0: - resolution: - { - integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} regenerator-runtime@0.10.5: - resolution: - { - integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==, - } + resolution: {integrity: sha512-02YopEIhAgiBHWeoTiA8aitHDt8z6w+rQqNuIftlM+ZtvSl/brTouaU7DW6GO/cHtvxJvS4Hwv2ibKdxIRi24w==} relateurl@0.2.7: - resolution: - { - integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==, - } - engines: { node: '>= 0.10' } + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} remove-accents@0.5.0: - resolution: - { - integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==, - } + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} request-ip@3.3.0: - resolution: - { - integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==, - } + resolution: {integrity: sha512-cA6Xh6e0fDBBBwH77SLJaJPBmD3nWVAcF9/XAcsrIHdjhFzFiB5aNQFytdjCGPezU3ROwrR11IddKAM08vohxA==} request@2.88.2: - resolution: - { - integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==, - } - engines: { node: '>= 6' } + resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==} + engines: {node: '>= 6'} deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 require-directory@2.1.1: - resolution: - { - integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} require-main-filename@2.0.0: - resolution: - { - integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==, - } + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} resolve-cwd@3.0.0: - resolution: - { - integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} resolve.exports@2.0.3: - resolution: - { - integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} resolve@1.22.11: - resolution: - { - integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} hasBin: true restore-cursor@3.1.0: - resolution: - { - integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} retry@0.12.0: - resolution: - { - integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, - } - engines: { node: '>= 4' } + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} reusify@1.1.0: - resolution: - { - integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==, - } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rimraf@3.0.2: - resolution: - { - integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==, - } + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@4.4.1: - resolution: - { - integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==} + engines: {node: '>=14'} hasBin: true rimraf@6.1.2: - resolution: - { - integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} + engines: {node: 20 || >=22} + hasBin: true + + rollup-plugin-visualizer@6.0.5: + resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.57.1: + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true router@2.2.0: - resolution: - { - integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} run-async@2.4.1: - resolution: - { - integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==, - } - engines: { node: '>=0.12.0' } + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} rxjs@7.8.2: - resolution: - { - integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==, - } + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safe-buffer@5.1.2: - resolution: - { - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, - } + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-buffer@5.2.1: - resolution: - { - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, - } + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} safer-buffer@2.1.2: - resolution: - { - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, - } + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@5.7.2: - resolution: - { - integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, - } + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} hasBin: true semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.7.3: - resolution: - { - integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} hasBin: true send@1.2.1: - resolution: - { - integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} serve-static@2.2.1: - resolution: - { - integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==, - } - engines: { node: '>= 18' } + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} set-blocking@2.0.0: - resolution: - { - integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==, - } + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} set-function-length@1.2.2: - resolution: - { - integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} setprototypeof@1.1.1: - resolution: - { - integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==, - } + resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} setprototypeof@1.2.0: - resolution: - { - integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, - } + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} shallow-clone@3.0.1: - resolution: - { - integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} + engines: {node: '>=8'} shallowequal@1.1.0: - resolution: - { - integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==, - } + resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==} shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shelljs@0.10.0: - resolution: - { - integrity: sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==} + engines: {node: '>=18'} side-channel-list@1.0.0: - resolution: - { - integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} side-channel-map@1.0.1: - resolution: - { - integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: - { - integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} side-channel@1.1.0: - resolution: - { - integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, - } + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: '>=14' } + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} sigstore@2.3.1: - resolution: - { - integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==} + engines: {node: ^16.14.0 || >=18.0.0} simple-update-notifier@2.0.0: - resolution: - { - integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} + engines: {node: '>=10'} slash@3.0.0: - resolution: - { - integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} slick@1.12.2: - resolution: - { - integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==, - } + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} smart-buffer@4.2.0: - resolution: - { - integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==, - } - engines: { node: '>= 6.0.0', npm: '>= 3.0.0' } + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} smtp-server@3.18.0: - resolution: - { - integrity: sha512-xINTnh0H8JDAKOAGSnFX8mgXB/L4Oz8dG4P0EgKAzJEszngxEEx4vOys+yNpsUc6yIyTKS8m2BcIffq4Htma/w==, - } - engines: { node: '>=18.18.0' } + resolution: {integrity: sha512-xINTnh0H8JDAKOAGSnFX8mgXB/L4Oz8dG4P0EgKAzJEszngxEEx4vOys+yNpsUc6yIyTKS8m2BcIffq4Htma/w==} + engines: {node: '>=18.18.0'} socks-proxy-agent@8.0.5: - resolution: - { - integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==, - } - engines: { node: '>= 14' } + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} socks@2.8.7: - resolution: - { - integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==, - } - engines: { node: '>= 10.0.0', npm: '>= 3.0.0' } + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} sort-keys@2.0.0: - resolution: - { - integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==} + engines: {node: '>=4'} sorted-array-functions@1.3.0: - resolution: - { - integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==, - } + resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} source-map-resolve@0.6.0: - resolution: - { - integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==, - } + resolution: {integrity: sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==} deprecated: See https://github.com/lydell/source-map-resolve#deprecated source-map-support@0.5.13: - resolution: - { - integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==, - } + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} spdx-correct@3.2.0: - resolution: - { - integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, - } + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} spdx-exceptions@2.5.0: - resolution: - { - integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, - } + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: - resolution: - { - integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, - } + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} spdx-license-ids@3.0.22: - resolution: - { - integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==, - } + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} split2@3.2.2: - resolution: - { - integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==, - } + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} split2@4.2.0: - resolution: - { - integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==, - } - engines: { node: '>= 10.x' } + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} split@1.0.1: - resolution: - { - integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==, - } + resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} sprintf-js@1.0.3: - resolution: - { - integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, - } + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} sshpk@1.18.0: - resolution: - { - integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==} + engines: {node: '>=0.10.0'} hasBin: true ssri@10.0.6: - resolution: - { - integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} stack-utils@2.0.6: - resolution: - { - integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} statuses@1.5.0: - resolution: - { - integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} statuses@2.0.2: - resolution: - { - integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} stream-browserify@3.0.0: - resolution: - { - integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==, - } + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} streamsearch@0.1.2: - resolution: - { - integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-jos8u++JKm0ARcSUTAZXOVC0mSox7Bhn6sBgty73P1f3JGf7yG2clTbBNHUdde/kdvP2FESam+vM6l8jBrNxHA==} + engines: {node: '>=0.8.0'} strfy-js@3.1.10: - resolution: - { - integrity: sha512-KQXNrvhnWpn4ya25WSG6EvJC6oqdeXlwMoitGl3qEJ2wnELV/sQO6uBy6CsIWTsVOMAt0B7/xvM40ucu5c8AuA==, - } + resolution: {integrity: sha512-KQXNrvhnWpn4ya25WSG6EvJC6oqdeXlwMoitGl3qEJ2wnELV/sQO6uBy6CsIWTsVOMAt0B7/xvM40ucu5c8AuA==} string-length@4.0.2: - resolution: - { - integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} string-width@5.1.2: - resolution: - { - integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} string_decoder@0.10.31: - resolution: - { - integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==, - } + resolution: {integrity: sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==} string_decoder@1.1.1: - resolution: - { - integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, - } + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} string_decoder@1.3.0: - resolution: - { - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, - } + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} strip-ansi@7.1.2: - resolution: - { - integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} strip-bom@3.0.0: - resolution: - { - integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} strip-bom@4.0.0: - resolution: - { - integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} strip-final-newline@2.0.0: - resolution: - { - integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} strip-indent@3.0.0: - resolution: - { - integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} strnum@2.1.2: - resolution: - { - integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==, - } + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} styled-components@5.3.11: - resolution: - { - integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==} + engines: {node: '>=10'} peerDependencies: react: '>= 16.8.0' react-dom: '>= 16.8.0' react-is: '>= 16.8.0' styled-system@5.1.5: - resolution: - { - integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==, - } + resolution: {integrity: sha512-7VoD0o2R3RKzOzPK0jYrVnS8iJdfkKsQJNiLRDjikOpQVqQHns/DXWaPZOH4tIKkhAT7I6wIsy9FWTWh2X3q+A==} subscriptions-transport-ws@0.9.19: - resolution: - { - integrity: sha512-dxdemxFFB0ppCLg10FTtRqH/31FNRL1y1BQv8209MK5I4CwALb7iihQg+7p65lFcIl8MHatINWBLOqpgU4Kyyw==, - } + resolution: {integrity: sha512-dxdemxFFB0ppCLg10FTtRqH/31FNRL1y1BQv8209MK5I4CwALb7iihQg+7p65lFcIl8MHatINWBLOqpgU4Kyyw==} deprecated: The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md peerDependencies: graphql: '>=0.10.0' superagent@10.3.0: - resolution: - { - integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==, - } - engines: { node: '>=14.18.0' } + resolution: {integrity: sha512-B+4Ik7ROgVKrQsXTV0Jwp2u+PXYLSlqtDAhYnkkD+zn3yg8s/zjA2MeGayPoY/KICrbitwneDHrjSotxKL+0XQ==} + engines: {node: '>=14.18.0'} supertest@7.2.2: - resolution: - { - integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==, - } - engines: { node: '>=14.18.0' } + resolution: {integrity: sha512-oK8WG9diS3DlhdUkcFn4tkNIiIbBx9lI2ClF8K+b2/m8Eyv47LSawxUzZQSNKUrVb2KsqeTDCcjAAVPYaSLVTA==} + engines: {node: '>=14.18.0'} supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} supports-color@8.1.1: - resolution: - { - integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: '>= 0.4' } + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} symbol-observable@1.2.0: - resolution: - { - integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} + engines: {node: '>=0.10.0'} synckit@0.11.12: - resolution: - { - integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==, - } - engines: { node: ^14.18.0 || >=16.0.0 } + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + engines: {node: ^14.18.0 || >=16.0.0} tar-stream@2.2.0: - resolution: - { - integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} tar@6.2.1: - resolution: - { - integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} + engines: {node: '>=10'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me temp-dir@1.0.0: - resolution: - { - integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} + engines: {node: '>=4'} test-exclude@6.0.0: - resolution: - { - integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} text-extensions@1.9.0: - resolution: - { - integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==, - } - engines: { node: '>=0.10' } + resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} + engines: {node: '>=0.10'} through2@2.0.5: - resolution: - { - integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==, - } + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} through2@3.0.2: - resolution: - { - integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==, - } + resolution: {integrity: sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==} through@2.3.8: - resolution: - { - integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, - } + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} tinyglobby@0.2.12: - resolution: - { - integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} tinyglobby@0.2.15: - resolution: - { - integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, - } - engines: { node: '>=12.0.0' } + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} tinypool@2.0.0: - resolution: - { - integrity: sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==, - } - engines: { node: ^20.0.0 || >=22.0.0 } + resolution: {integrity: sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==} + engines: {node: ^20.0.0 || >=22.0.0} tmp@0.2.5: - resolution: - { - integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==, - } - engines: { node: '>=14.14' } + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} tmpl@1.0.5: - resolution: - { - integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, - } + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: '>=8.0' } + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} toidentifier@1.0.0: - resolution: - { - integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} + engines: {node: '>=0.6'} toidentifier@1.0.1: - resolution: - { - integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, - } - engines: { node: '>=0.6' } + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} touch@3.1.1: - resolution: - { - integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==, - } + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} hasBin: true tough-cookie@2.5.0: - resolution: - { - integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==, - } - engines: { node: '>=0.8' } + resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} + engines: {node: '>=0.8'} tr46@0.0.3: - resolution: - { - integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, - } + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} treeverse@3.0.0: - resolution: - { - integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} trim-newlines@3.0.1: - resolution: - { - integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} + engines: {node: '>=8'} ts-api-utils@2.4.0: - resolution: - { - integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==, - } - engines: { node: '>=18.12' } + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' ts-jest@29.4.6: - resolution: - { - integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0 } + resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@babel/core': '>=7.0.0-beta.0 <8' @@ -12502,10 +8627,7 @@ packages: optional: true ts-node@10.9.2: - resolution: - { - integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==, - } + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: '@swc/core': '>=1.2.50' @@ -12519,491 +8641,315 @@ packages: optional: true tsconfig-paths@4.2.0: - resolution: - { - integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} + engines: {node: '>=6'} tslib@2.8.1: - resolution: - { - integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, - } + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsx@4.21.0: - resolution: - { - integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==, - } - engines: { node: '>=18.0.0' } + resolution: {integrity: sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==} + engines: {node: '>=18.0.0'} hasBin: true tuf-js@2.2.1: - resolution: - { - integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==, - } - engines: { node: ^16.14.0 || >=18.0.0 } + resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==} + engines: {node: ^16.14.0 || >=18.0.0} tunnel-agent@0.6.0: - resolution: - { - integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==, - } + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} tweetnacl@0.14.5: - resolution: - { - integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==, - } + resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==} type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: '>= 0.8.0' } + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} type-detect@4.0.8: - resolution: - { - integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} type-fest@0.18.1: - resolution: - { - integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} + engines: {node: '>=10'} type-fest@0.21.3: - resolution: - { - integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} type-fest@0.4.1: - resolution: - { - integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==} + engines: {node: '>=6'} type-fest@0.6.0: - resolution: - { - integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} type-fest@0.8.1: - resolution: - { - integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} type-fest@4.41.0: - resolution: - { - integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==, - } - engines: { node: '>=16' } + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} type-is@1.6.18: - resolution: - { - integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} type-is@2.0.1: - resolution: - { - integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==, - } - engines: { node: '>= 0.6' } + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} typedarray@0.0.6: - resolution: - { - integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, - } + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: '>=14.17' } + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true uglify-js@3.19.3: - resolution: - { - integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} hasBin: true uglify-js@3.4.10: - resolution: - { - integrity: sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==, - } - engines: { node: '>=0.8.0' } + resolution: {integrity: sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==} + engines: {node: '>=0.8.0'} hasBin: true undefsafe@2.0.5: - resolution: - { - integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==, - } + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} undici-types@5.26.5: - resolution: - { - integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==, - } + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} undici-types@6.21.0: - resolution: - { - integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, - } + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici@7.16.0: - resolution: - { - integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==, - } - engines: { node: '>=20.18.1' } + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + engines: {node: '>=20.18.1'} undici@7.19.0: - resolution: - { - integrity: sha512-Heho1hJD81YChi+uS2RkSjcVO+EQLmLSyUlHyp7Y/wFbxQaGb4WXVKD073JytrjXJVkSZVzoE2MCSOKugFGtOQ==, - } - engines: { node: '>=20.18.1' } + resolution: {integrity: sha512-Heho1hJD81YChi+uS2RkSjcVO+EQLmLSyUlHyp7Y/wFbxQaGb4WXVKD073JytrjXJVkSZVzoE2MCSOKugFGtOQ==} + engines: {node: '>=20.18.1'} unique-filename@3.0.0: - resolution: - { - integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} unique-slug@4.0.0: - resolution: - { - integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} universal-user-agent@6.0.1: - resolution: - { - integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==, - } + resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} universalify@2.0.1: - resolution: - { - integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, - } - engines: { node: '>= 10.0.0' } + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} unload@2.2.0: - resolution: - { - integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==, - } + resolution: {integrity: sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==} unpipe@1.0.0: - resolution: - { - integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} unrs-resolver@1.11.1: - resolution: - { - integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==, - } + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} untildify@4.0.0: - resolution: - { - integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==} + engines: {node: '>=8'} upath@2.0.1: - resolution: - { - integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==, - } - engines: { node: '>=4' } + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + engines: {node: '>=4'} update-browserslist-db@1.2.3: - resolution: - { - integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==, - } + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' upper-case@1.1.3: - resolution: - { - integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==, - } + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} url-join@4.0.1: - resolution: - { - integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, - } + resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} uuid@10.0.0: - resolution: - { - integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==, - } + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true uuid@3.4.0: - resolution: - { - integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==, - } + resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==} deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. hasBin: true v8-compile-cache-lib@3.0.1: - resolution: - { - integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==, - } + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} v8-to-istanbul@9.3.0: - resolution: - { - integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==, - } - engines: { node: '>=10.12.0' } + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} valid-data-url@3.0.1: - resolution: - { - integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} validate-npm-package-license@3.0.4: - resolution: - { - integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, - } + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} validate-npm-package-name@5.0.1: - resolution: - { - integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} vary@1.1.2: - resolution: - { - integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, - } - engines: { node: '>= 0.8' } + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} verror@1.10.0: - resolution: - { - integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==, - } - engines: { '0': node >=0.6.0 } + resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} + engines: {'0': node >=0.6.0} + + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true walk-up-path@3.0.1: - resolution: - { - integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==, - } + resolution: {integrity: sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==} walker@1.0.8: - resolution: - { - integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, - } + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} warning@3.0.0: - resolution: - { - integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==, - } + resolution: {integrity: sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==} wcwidth@1.0.1: - resolution: - { - integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==, - } + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} web-resource-inliner@5.0.0: - resolution: - { - integrity: sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==, - } - engines: { node: '>=10.0.0' } + resolution: {integrity: sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A==} + engines: {node: '>=10.0.0'} webidl-conversions@3.0.1: - resolution: - { - integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, - } + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} whatwg-encoding@3.1.1: - resolution: - { - integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@4.0.0: - resolution: - { - integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, - } - engines: { node: '>=18' } + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} whatwg-url@5.0.0: - resolution: - { - integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, - } + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} which-module@2.0.1: - resolution: - { - integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==, - } + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: '>= 8' } + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} hasBin: true which@4.0.0: - resolution: - { - integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==, - } - engines: { node: ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} hasBin: true wide-align@1.1.5: - resolution: - { - integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==, - } + resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} word-wrap@1.2.5: - resolution: - { - integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, - } - engines: { node: '>=0.10.0' } + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} wordwrap@1.0.0: - resolution: - { - integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==, - } + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} wrap-ansi@6.2.0: - resolution: - { - integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} wrap-ansi@8.1.0: - resolution: - { - integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} wrappy@1.0.2: - resolution: - { - integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, - } + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} write-file-atomic@2.4.3: - resolution: - { - integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==, - } + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} write-file-atomic@5.0.1: - resolution: - { - integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} write-json-file@3.2.0: - resolution: - { - integrity: sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-3xZqT7Byc2uORAatYiP3DHUUAVEkNOswEWNs9H5KXiicRTvzYzYqKjYc4G7p+8pltvAw641lVByKVtMpf+4sYQ==} + engines: {node: '>=6'} write-pkg@4.0.0: - resolution: - { - integrity: sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-v2UQ+50TNf2rNHJ8NyWttfm/EJUBWMJcx6ZTYZr6Qp52uuegWw/lBkCtCbnYZEmPRNL61m+u67dAmGxo+HTULA==} + engines: {node: '>=8'} ws@7.5.10: - resolution: - { - integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==, - } - engines: { node: '>=8.3.0' } + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: ^5.0.2 @@ -13014,114 +8960,67 @@ packages: optional: true xtend@4.0.2: - resolution: - { - integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, - } - engines: { node: '>=0.4' } + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} y18n@4.0.3: - resolution: - { - integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==, - } + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} yallist@2.1.2: - resolution: - { - integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==, - } + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yallist@4.0.0: - resolution: - { - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, - } + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} yaml@2.8.2: - resolution: - { - integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==, - } - engines: { node: '>= 14.6' } + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + engines: {node: '>= 14.6'} hasBin: true yanse@0.2.0: - resolution: - { - integrity: sha512-BN6WYjJRX3mw/LpEC4d2LAlLFFdoFKKYYbd9nvhTvbbEW+/mJJccBGy0DuvcYXg75Xed2ZT8euXtplfLKBfdHA==, - } + resolution: {integrity: sha512-BN6WYjJRX3mw/LpEC4d2LAlLFFdoFKKYYbd9nvhTvbbEW+/mJJccBGy0DuvcYXg75Xed2ZT8euXtplfLKBfdHA==} yargs-parser@18.1.3: - resolution: - { - integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} yargs-parser@20.2.9: - resolution: - { - integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} yargs@15.4.1: - resolution: - { - integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==, - } - engines: { node: '>=8' } + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} yargs@16.2.0: - resolution: - { - integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} yargs@17.7.2: - resolution: - { - integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, - } - engines: { node: '>=12' } + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} yn@3.1.1: - resolution: - { - integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==, - } - engines: { node: '>=6' } + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: '>=10' } + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} snapshots: + '@0no-co/graphql.web@1.2.0(graphql@15.10.1)': optionalDependencies: graphql: 15.10.1 @@ -14125,6 +10024,16 @@ snapshots: '@babel/core': 7.28.6 '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.6)': + dependencies: + '@babel/core': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/runtime-corejs3@7.28.4': dependencies: core-js-pure: 3.47.0 @@ -14217,81 +10126,159 @@ snapshots: '@emotion/unitless@0.7.5': {} + '@esbuild/aix-ppc64@0.25.12': + optional: true + '@esbuild/aix-ppc64@0.27.2': optional: true + '@esbuild/android-arm64@0.25.12': + optional: true + '@esbuild/android-arm64@0.27.2': optional: true + '@esbuild/android-arm@0.25.12': + optional: true + '@esbuild/android-arm@0.27.2': optional: true + '@esbuild/android-x64@0.25.12': + optional: true + '@esbuild/android-x64@0.27.2': optional: true + '@esbuild/darwin-arm64@0.25.12': + optional: true + '@esbuild/darwin-arm64@0.27.2': optional: true + '@esbuild/darwin-x64@0.25.12': + optional: true + '@esbuild/darwin-x64@0.27.2': optional: true + '@esbuild/freebsd-arm64@0.25.12': + optional: true + '@esbuild/freebsd-arm64@0.27.2': optional: true + '@esbuild/freebsd-x64@0.25.12': + optional: true + '@esbuild/freebsd-x64@0.27.2': optional: true + '@esbuild/linux-arm64@0.25.12': + optional: true + '@esbuild/linux-arm64@0.27.2': optional: true + '@esbuild/linux-arm@0.25.12': + optional: true + '@esbuild/linux-arm@0.27.2': optional: true + '@esbuild/linux-ia32@0.25.12': + optional: true + '@esbuild/linux-ia32@0.27.2': optional: true + '@esbuild/linux-loong64@0.25.12': + optional: true + '@esbuild/linux-loong64@0.27.2': optional: true + '@esbuild/linux-mips64el@0.25.12': + optional: true + '@esbuild/linux-mips64el@0.27.2': optional: true + '@esbuild/linux-ppc64@0.25.12': + optional: true + '@esbuild/linux-ppc64@0.27.2': optional: true + '@esbuild/linux-riscv64@0.25.12': + optional: true + '@esbuild/linux-riscv64@0.27.2': optional: true + '@esbuild/linux-s390x@0.25.12': + optional: true + '@esbuild/linux-s390x@0.27.2': optional: true + '@esbuild/linux-x64@0.25.12': + optional: true + '@esbuild/linux-x64@0.27.2': optional: true + '@esbuild/netbsd-arm64@0.25.12': + optional: true + '@esbuild/netbsd-arm64@0.27.2': optional: true + '@esbuild/netbsd-x64@0.25.12': + optional: true + '@esbuild/netbsd-x64@0.27.2': optional: true + '@esbuild/openbsd-arm64@0.25.12': + optional: true + '@esbuild/openbsd-arm64@0.27.2': optional: true + '@esbuild/openbsd-x64@0.25.12': + optional: true + '@esbuild/openbsd-x64@0.27.2': optional: true + '@esbuild/openharmony-arm64@0.25.12': + optional: true + '@esbuild/openharmony-arm64@0.27.2': optional: true + '@esbuild/sunos-x64@0.25.12': + optional: true + '@esbuild/sunos-x64@0.27.2': optional: true + '@esbuild/win32-arm64@0.25.12': + optional: true + '@esbuild/win32-arm64@0.27.2': optional: true + '@esbuild/win32-ia32@0.25.12': + optional: true + '@esbuild/win32-ia32@0.27.2': optional: true + '@esbuild/win32-x64@0.25.12': + optional: true + '@esbuild/win32-x64@0.27.2': optional: true @@ -14632,14 +10619,14 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@launchql/mjml@0.1.1(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3)': + '@launchql/mjml@0.1.1(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 mjml: 4.7.1(encoding@0.1.13) mjml-react: 1.0.59(mjml@4.7.1(encoding@0.1.13))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3) + styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3) styled-system: 5.1.5 transitivePeerDependencies: - '@babel/core' @@ -14661,13 +10648,13 @@ snapshots: '@types/node': 20.19.27 long: 5.3.2 - '@launchql/styled-email@0.1.0(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3)': + '@launchql/styled-email@0.1.0(@babel/core@7.28.6)(encoding@0.1.13)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3)': dependencies: '@babel/runtime': 7.28.4 juice: 7.0.0(encoding@0.1.13) react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3) + styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3) styled-system: 5.1.5 transitivePeerDependencies: - '@babel/core' @@ -15121,6 +11108,83 @@ snapshots: '@protobufjs/utf8@1.1.0': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.57.1': + optional: true + + '@rollup/rollup-android-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-x64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.57.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.57.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.57.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.57.1': + optional: true + '@sigstore/bundle@2.3.2': dependencies: '@sigstore/protobuf-specs': 0.3.3 @@ -15744,11 +11808,18 @@ snapshots: '@tanstack/query-core@5.90.19': {} + '@tanstack/query-core@5.90.20': {} + '@tanstack/react-query@5.90.19(react@19.2.3)': dependencies: '@tanstack/query-core': 5.90.19 react: 19.2.3 + '@tanstack/react-query@5.90.20(react@19.2.3)': + dependencies: + '@tanstack/query-core': 5.90.20 + react: 19.2.3 + '@testing-library/dom@7.31.2': dependencies: '@babel/code-frame': 7.27.1 @@ -15971,6 +12042,14 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/react-dom@19.2.3(@types/react@19.2.13)': + dependencies: + '@types/react': 19.2.13 + + '@types/react@19.2.13': + dependencies: + csstype: 3.2.3 + '@types/react@19.2.8': dependencies: csstype: 3.2.3 @@ -16186,6 +12265,18 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@20.19.27)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.28.6 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.6) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.6) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.1(@types/node@20.19.27)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@yarnpkg/lockfile@1.1.0': {} '@yarnpkg/parsers@3.0.2': @@ -16362,14 +12453,14 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-plugin-styled-components@2.1.4(@babel/core@7.28.6)(styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3))(supports-color@5.5.0): + babel-plugin-styled-components@2.1.4(@babel/core@7.28.6)(styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3))(supports-color@5.5.0): dependencies: '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.6) lodash: 4.17.21 picomatch: 2.3.1 - styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3) + styled-components: 5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3) transitivePeerDependencies: - '@babel/core' - supports-color @@ -17191,6 +13282,35 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -19314,6 +15434,8 @@ snapshots: dependencies: big-integer: 1.6.52 + nanoid@3.3.11: {} + napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} @@ -19702,7 +15824,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.28.6 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -19878,6 +16000,12 @@ snapshots: postcss-value-parser@4.2.0: {} + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + postgraphile-core@4.14.1(graphql@15.10.1)(pg@8.17.1): dependencies: graphile-build: 4.14.1(graphql@15.10.1) @@ -20045,6 +16173,8 @@ snapshots: react-is@18.3.1: {} + react-is@19.2.4: {} + react-query@3.39.3(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@babel/runtime': 7.28.4 @@ -20054,6 +16184,14 @@ snapshots: optionalDependencies: react-dom: 19.2.3(react@19.2.3) + react-refresh@0.17.0: {} + + react-test-renderer@19.2.3(react@19.2.3): + dependencies: + react: 19.2.3 + react-is: 19.2.4 + scheduler: 0.27.0 + react@19.2.3: {} read-cmd-shim@4.0.0: {} @@ -20200,6 +16338,46 @@ snapshots: glob: 13.0.0 package-json-from-dist: 1.0.1 + rollup-plugin-visualizer@6.0.5(rollup@4.57.1): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.57.1 + + rollup@4.57.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.57.1 + '@rollup/rollup-android-arm64': 4.57.1 + '@rollup/rollup-darwin-arm64': 4.57.1 + '@rollup/rollup-darwin-x64': 4.57.1 + '@rollup/rollup-freebsd-arm64': 4.57.1 + '@rollup/rollup-freebsd-x64': 4.57.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 + '@rollup/rollup-linux-arm-musleabihf': 4.57.1 + '@rollup/rollup-linux-arm64-gnu': 4.57.1 + '@rollup/rollup-linux-arm64-musl': 4.57.1 + '@rollup/rollup-linux-loong64-gnu': 4.57.1 + '@rollup/rollup-linux-loong64-musl': 4.57.1 + '@rollup/rollup-linux-ppc64-gnu': 4.57.1 + '@rollup/rollup-linux-ppc64-musl': 4.57.1 + '@rollup/rollup-linux-riscv64-gnu': 4.57.1 + '@rollup/rollup-linux-riscv64-musl': 4.57.1 + '@rollup/rollup-linux-s390x-gnu': 4.57.1 + '@rollup/rollup-linux-x64-gnu': 4.57.1 + '@rollup/rollup-linux-x64-musl': 4.57.1 + '@rollup/rollup-openbsd-x64': 4.57.1 + '@rollup/rollup-openharmony-arm64': 4.57.1 + '@rollup/rollup-win32-arm64-msvc': 4.57.1 + '@rollup/rollup-win32-ia32-msvc': 4.57.1 + '@rollup/rollup-win32-x64-gnu': 4.57.1 + '@rollup/rollup-win32-x64-msvc': 4.57.1 + fsevents: 2.3.3 + router@2.2.0: dependencies: debug: 4.4.3(supports-color@5.5.0) @@ -20370,6 +16548,8 @@ snapshots: sorted-array-functions@1.3.0: {} + source-map-js@1.2.1: {} + source-map-resolve@0.6.0: dependencies: atob: 2.1.2 @@ -20382,6 +16562,8 @@ snapshots: source-map@0.6.1: {} + source-map@0.7.6: {} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 @@ -20492,19 +16674,19 @@ snapshots: strnum@2.1.2: {} - styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3): + styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3): dependencies: '@babel/helper-module-imports': 7.27.1(supports-color@5.5.0) '@babel/traverse': 7.28.6(supports-color@5.5.0) '@emotion/is-prop-valid': 1.4.0 '@emotion/stylis': 0.8.5 '@emotion/unitless': 0.7.5 - babel-plugin-styled-components: 2.1.4(@babel/core@7.28.6)(styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@18.3.1)(react@19.2.3))(supports-color@5.5.0) + babel-plugin-styled-components: 2.1.4(@babel/core@7.28.6)(styled-components@5.3.11(@babel/core@7.28.6)(react-dom@19.2.3(react@19.2.3))(react-is@19.2.4)(react@19.2.3))(supports-color@5.5.0) css-to-react-native: 3.2.0 hoist-non-react-statics: 3.3.2 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - react-is: 18.3.1 + react-is: 19.2.4 shallowequal: 1.1.0 supports-color: 5.5.0 transitivePeerDependencies: @@ -20870,6 +17052,21 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 + vite@6.4.1(@types/node@20.19.27)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.27 + fsevents: 2.3.3 + jiti: 2.6.1 + tsx: 4.21.0 + yaml: 2.8.2 + walk-up-path@3.0.1: {} walker@1.0.8: From 7497345baaa69a671837bf453bc8a72fa1fbf254 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Sat, 7 Feb 2026 14:35:29 +0700 Subject: [PATCH 05/23] ci: ignore test-codegen-app changes --- .github/workflows/docker-launchql.yaml | 4 ++++ .github/workflows/notify-e2e.yml | 2 ++ .github/workflows/run-tests.yaml | 7 ++++++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-launchql.yaml b/.github/workflows/docker-launchql.yaml index 13856efc6..cf2d20d5c 100644 --- a/.github/workflows/docker-launchql.yaml +++ b/.github/workflows/docker-launchql.yaml @@ -6,11 +6,15 @@ on: - main - v1 - release/* + paths-ignore: + - 'graphql/test-app/**' pull_request: branches: - main - v1 types: [opened, reopened, synchronize, ready_for_review] + paths-ignore: + - 'graphql/test-app/**' workflow_dispatch: {} concurrency: diff --git a/.github/workflows/notify-e2e.yml b/.github/workflows/notify-e2e.yml index e12a58c6a..7afa067ea 100644 --- a/.github/workflows/notify-e2e.yml +++ b/.github/workflows/notify-e2e.yml @@ -6,6 +6,8 @@ name: Notify E2E Tests on: push: branches: [main] + paths-ignore: + - 'graphql/test-app/**' jobs: trigger-tests: diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 3142fb930..d1feffd65 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -4,10 +4,14 @@ on: branches: - main - v1 + paths-ignore: + - 'graphql/test-app/**' pull_request: branches: - main - v1 + paths-ignore: + - 'graphql/test-app/**' workflow_dispatch: workflow_call: @@ -190,7 +194,8 @@ jobs: run: pnpm install - name: build - run: pnpm run build + run: | + pnpm -r --filter '!@constructive-io/test-codegen-app' run build - name: seed app_user run: | From 283ddfa9910442429b08a46f5a0127e0da3a8c92 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Sat, 7 Feb 2026 15:12:28 +0700 Subject: [PATCH 06/23] ci: ignore test package --- .github/workflows/docker-launchql.yaml | 2 ++ .github/workflows/notify-e2e.yml | 1 + .github/workflows/run-tests.yaml | 2 ++ package.json | 4 ++-- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-launchql.yaml b/.github/workflows/docker-launchql.yaml index cf2d20d5c..abf4d3196 100644 --- a/.github/workflows/docker-launchql.yaml +++ b/.github/workflows/docker-launchql.yaml @@ -8,6 +8,7 @@ on: - release/* paths-ignore: - 'graphql/test-app/**' + - 'graphql/test-codegen-app/**' pull_request: branches: - main @@ -15,6 +16,7 @@ on: types: [opened, reopened, synchronize, ready_for_review] paths-ignore: - 'graphql/test-app/**' + - 'graphql/test-codegen-app/**' workflow_dispatch: {} concurrency: diff --git a/.github/workflows/notify-e2e.yml b/.github/workflows/notify-e2e.yml index 7afa067ea..7f9e6f3d6 100644 --- a/.github/workflows/notify-e2e.yml +++ b/.github/workflows/notify-e2e.yml @@ -8,6 +8,7 @@ on: branches: [main] paths-ignore: - 'graphql/test-app/**' + - 'graphql/test-codegen-app/**' jobs: trigger-tests: diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index d1feffd65..015753008 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -6,12 +6,14 @@ on: - v1 paths-ignore: - 'graphql/test-app/**' + - 'graphql/test-codegen-app/**' pull_request: branches: - main - v1 paths-ignore: - 'graphql/test-app/**' + - 'graphql/test-codegen-app/**' workflow_dispatch: workflow_call: diff --git a/package.json b/package.json index 6ef5a5fb4..a784ca712 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ }, "scripts": { "clean": "pnpm -r run clean", - "build": "pnpm -r run build", - "build:dev": "pnpm -r run build:dev", + "build": "pnpm -r --filter '!@constructive-io/test-codegen-app' run build", + "build:dev": "pnpm -r --filter '!@constructive-io/test-codegen-app' run build:dev", "lint": "pnpm -r run lint", "internal:deps": "makage update-workspace", "deps": "pnpm up -r -i -L" From d2e748028fa4e1271cb35e1366a1018f1ad957b9 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sat, 7 Feb 2026 21:55:02 +0000 Subject: [PATCH 07/23] fix(codegen): convert string concatenation to Babel AST and template-copy patterns - Convert queries.ts, mutations.ts, custom-queries.ts, custom-mutations.ts from lines.push() string concatenation to Babel AST generation - Convert client.ts and selection.ts from string template literals to template-copy pattern - Add hooks-ast.ts shared helper library for Babel AST node construction - Add template files: hooks-client.ts, hooks-selection.ts - Add AGENTS.md with codegen rules (AST-only, no string concat, templates as files) - Update test snapshots for Babel formatting changes (whitespace/formatting only) --- graphql/codegen/AGENTS.md | 32 + .../react-query-hooks.test.ts.snap | 2580 +++++++++++------ graphql/codegen/src/core/codegen/client.ts | 73 +- .../src/core/codegen/custom-mutations.ts | 306 +- .../src/core/codegen/custom-queries.ts | 768 +++-- graphql/codegen/src/core/codegen/hooks-ast.ts | 874 ++++++ graphql/codegen/src/core/codegen/mutations.ts | 758 +++-- graphql/codegen/src/core/codegen/queries.ts | 1019 ++++--- graphql/codegen/src/core/codegen/selection.ts | 102 +- .../core/codegen/templates/hooks-client.ts | 49 + .../core/codegen/templates/hooks-selection.ts | 79 + 11 files changed, 4607 insertions(+), 2033 deletions(-) create mode 100644 graphql/codegen/AGENTS.md create mode 100644 graphql/codegen/src/core/codegen/hooks-ast.ts create mode 100644 graphql/codegen/src/core/codegen/templates/hooks-client.ts create mode 100644 graphql/codegen/src/core/codegen/templates/hooks-selection.ts diff --git a/graphql/codegen/AGENTS.md b/graphql/codegen/AGENTS.md new file mode 100644 index 000000000..8c0ab6a5f --- /dev/null +++ b/graphql/codegen/AGENTS.md @@ -0,0 +1,32 @@ +# Code Generation Rules + +## 1. Only AST-based code generation + +All per-schema/per-table code generation MUST use Babel AST (`@babel/types` + `generateCode()`). +Never use `lines.push()`, string concatenation, or template literals to build generated TypeScript code. + +Reference implementations: +- `src/core/codegen/orm/model-generator.ts` — per-table ORM model classes +- `src/core/codegen/orm/client-generator.ts` — createClient factory +- `src/core/codegen/orm/custom-ops-generator.ts` — custom query/mutation operations + +## 2. Never any string-based concatenation + +No `lines.push(...)`, no backtick template literals containing code, no string `+` operators +for building generated source files. If you find yourself writing `lines.push(\`import ...\`)`, +stop and use `t.importDeclaration(...)` instead. + +## 3. Giant templates must be actual files that get copied + +Static runtime code that does not vary per-schema belongs in `src/core/codegen/templates/` +as real `.ts` files. These are read at codegen time via `readTemplateFile()` and written +to the output directory with only the header replaced. + +This gives you: +- Syntax highlighting and IDE support while editing templates +- A clear boundary between "code that generates code" and "code that IS the output" + +Reference implementations: +- `src/core/codegen/templates/orm-client.ts` +- `src/core/codegen/templates/query-builder.ts` +- `src/core/codegen/templates/select-types.ts` diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index 9ccd55d96..68b86cdec 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -187,37 +187,53 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { customMutationKeys } from '../mutation-keys'; -import type { LoginVariables } from '../../orm/mutation'; -import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { LoginVariables } from '../../orm/mutation'; -export type { LoginPayloadSelect } from '../../orm/input-types'; - -const defaultSelect = { token: true } as const; - -export function useLoginMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, LoginVariables>, 'mutationFn'> -): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; -export function useLoginMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, LoginVariables>, 'mutationFn'> -): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; -export function useLoginMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +import { useMutation } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { customMutationKeys } from "../mutation-keys"; +import type { LoginVariables } from "../../orm/mutation"; +import type { LoginPayloadSelect, LoginPayload } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { LoginVariables } from "../../orm/mutation"; +export type { LoginPayloadSelect } from "../../orm/input-types"; +const defaultSelect = { + token: true +} as const; +export function useLoginMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ + login: InferSelectResult; +}, Error, LoginVariables>; +export function useLoginMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ + login: InferSelectResult; +}, Error, LoginVariables>; +export function useLoginMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; return useMutation({ mutationKey: customMutationKeys.login(), - mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as LoginPayloadSelect }).unwrap(), - ...mutationOptions, + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { + select: (args?.select ?? defaultSelect) as LoginPayloadSelect + }).unwrap(), + ...mutationOptions }); } " @@ -230,37 +246,53 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { customMutationKeys } from '../mutation-keys'; -import type { RegisterVariables } from '../../orm/mutation'; -import type { RegisterPayloadSelect, RegisterPayload } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { RegisterVariables } from '../../orm/mutation'; -export type { RegisterPayloadSelect } from '../../orm/input-types'; - -const defaultSelect = { token: true } as const; - -export function useRegisterMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, RegisterVariables>, 'mutationFn'> -): UseMutationResult<{ register: InferSelectResult }, Error, RegisterVariables>; -export function useRegisterMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, RegisterVariables>, 'mutationFn'> -): UseMutationResult<{ register: InferSelectResult }, Error, RegisterVariables>; -export function useRegisterMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +import { useMutation } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { customMutationKeys } from "../mutation-keys"; +import type { RegisterVariables } from "../../orm/mutation"; +import type { RegisterPayloadSelect, RegisterPayload } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { RegisterVariables } from "../../orm/mutation"; +export type { RegisterPayloadSelect } from "../../orm/input-types"; +const defaultSelect = { + token: true +} as const; +export function useRegisterMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, RegisterVariables>, "mutationFn">): UseMutationResult<{ + register: InferSelectResult; +}, Error, RegisterVariables>; +export function useRegisterMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, RegisterVariables>, "mutationFn">): UseMutationResult<{ + register: InferSelectResult; +}, Error, RegisterVariables>; +export function useRegisterMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; return useMutation({ mutationKey: customMutationKeys.register(), - mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { select: (args?.select ?? defaultSelect) as RegisterPayloadSelect }).unwrap(), - ...mutationOptions, + mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { + select: (args?.select ?? defaultSelect) as RegisterPayloadSelect + }).unwrap(), + ...mutationOptions }); } " @@ -273,35 +305,51 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { customMutationKeys } from '../mutation-keys'; -import type { LogoutPayloadSelect, LogoutPayload } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { LogoutPayloadSelect } from '../../orm/input-types'; - -const defaultSelect = { success: true } as const; - -export function useLogoutMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, void>, 'mutationFn'> -): UseMutationResult<{ logout: InferSelectResult }, Error, void>; -export function useLogoutMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, void>, 'mutationFn'> -): UseMutationResult<{ logout: InferSelectResult }, Error, void>; -export function useLogoutMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +import { useMutation } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { customMutationKeys } from "../mutation-keys"; +import type { LogoutPayloadSelect, LogoutPayload } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { LogoutPayloadSelect } from "../../orm/input-types"; +const defaultSelect = { + success: true +} as const; +export function useLogoutMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, void>, "mutationFn">): UseMutationResult<{ + logout: InferSelectResult; +}, Error, void>; +export function useLogoutMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, void>, "mutationFn">): UseMutationResult<{ + logout: InferSelectResult; +}, Error, void>; +export function useLogoutMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; return useMutation({ mutationKey: customMutationKeys.logout(), - mutationFn: () => getClient().mutation.logout({ select: (args?.select ?? defaultSelect) as LogoutPayloadSelect }).unwrap(), - ...mutationOptions, + mutationFn: () => getClient().mutation.logout({ + select: (args?.select ?? defaultSelect) as LogoutPayloadSelect + }).unwrap(), + ...mutationOptions }); } " @@ -314,35 +362,51 @@ exports[`Custom Mutation Hook Generators generateCustomMutationHook generates cu * DO NOT EDIT - changes will be overwritten */ -import { useMutation } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { LoginVariables } from '../../orm/mutation'; -import type { LoginPayloadSelect, LoginPayload } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { LoginVariables } from '../../orm/mutation'; -export type { LoginPayloadSelect } from '../../orm/input-types'; - -const defaultSelect = { token: true } as const; - -export function useLoginMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, LoginVariables>, 'mutationFn'> -): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; -export function useLoginMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, LoginVariables>, 'mutationFn'> -): UseMutationResult<{ login: InferSelectResult }, Error, LoginVariables>; -export function useLoginMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +import { useMutation } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { LoginVariables } from "../../orm/mutation"; +import type { LoginPayloadSelect, LoginPayload } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { LoginVariables } from "../../orm/mutation"; +export type { LoginPayloadSelect } from "../../orm/input-types"; +const defaultSelect = { + token: true +} as const; +export function useLoginMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ + login: InferSelectResult; +}, Error, LoginVariables>; +export function useLoginMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ + login: InferSelectResult; +}, Error, LoginVariables>; +export function useLoginMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; return useMutation({ - mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { select: (args?.select ?? defaultSelect) as LoginPayloadSelect }).unwrap(), - ...mutationOptions, + mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { + select: (args?.select ?? defaultSelect) as LoginPayloadSelect + }).unwrap(), + ...mutationOptions }); } " @@ -355,105 +419,141 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { customQueryKeys } from '../query-keys'; -import type { SearchUsersVariables } from '../../orm/query'; -import type { UserSelect, User } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { SearchUsersVariables } from '../../orm/query'; -export type { UserSelect } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { customQueryKeys } from "../query-keys"; +import type { SearchUsersVariables } from "../../orm/query"; +import type { UserSelect, User } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { SearchUsersVariables } from "../../orm/query"; +export type { UserSelect } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const searchUsersQueryKey = customQueryKeys.searchUsers; - /** * Search users by name or email - * + * * @example * \`\`\`tsx * const { data, isLoading } = useSearchUsersQuery({ query, limit }); - * + * * if (data?.searchUsers) { * console.log(data.searchUsers); * } * \`\`\` */ -export function useSearchUsersQuery[] }>( - params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } & Omit[] }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useSearchUsersQuery[] }>( - params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } & Omit[] }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useSearchUsersQuery( - params: { variables: SearchUsersVariables; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useSearchUsersQuery[]; +}>(params: { + variables: SearchUsersVariables; + selection: ({ + fields: S; + } & StrictSelect); +} & Omit[]; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useSearchUsersQuery[]; +}>(params: { + variables: SearchUsersVariables; + selection?: ({ + fields?: undefined; + }); +} & Omit[]; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useSearchUsersQuery(params: { + variables: SearchUsersVariables; + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); - const { variables: _variables, selection: _selection, ...queryOptions } = params ?? {}; + const { + variables: _variables, + selection: _selection, + ...queryOptions + } = params ?? {}; void _variables; void _selection; return useQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().query.searchUsers(variables!, { + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), enabled: !!variables && params?.enabled !== false, - ...queryOptions, + ...queryOptions }); } - /** * Fetch searchUsers without React hooks - * + * * @example * \`\`\`ts * const data = await fetchSearchUsersQuery({ query, limit }); * \`\`\` */ -export async function fetchSearchUsersQuery( - params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } -): Promise<{ searchUsers: InferSelectResult[] }>; -export async function fetchSearchUsersQuery( - params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } -): Promise<{ searchUsers: InferSelectResult[] }>; -export async function fetchSearchUsersQuery( - params: { variables: SearchUsersVariables; selection?: SelectionConfig }, -) { +export async function fetchSearchUsersQuery(params: { + variables: SearchUsersVariables; + selection: ({ + fields: S; + } & StrictSelect); +}): Promise<{ + searchUsers: InferSelectResult[]; +}>; +export async function fetchSearchUsersQuery(params: { + variables: SearchUsersVariables; + selection?: ({ + fields?: undefined; + }); +}): Promise<{ + searchUsers: InferSelectResult[]; +}>; +export async function fetchSearchUsersQuery(params: { + variables: SearchUsersVariables; + selection?: SelectionConfig; +}) { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); - return getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); + return getClient().query.searchUsers(variables!, { + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch searchUsers for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchSearchUsersQuery(queryClient, { query, limit }); * \`\`\` */ -export async function prefetchSearchUsersQuery( - queryClient: QueryClient, - params: { variables: SearchUsersVariables; selection: ({ fields: S } & StrictSelect) } -): Promise; -export async function prefetchSearchUsersQuery( - queryClient: QueryClient, - params: { variables: SearchUsersVariables; selection?: ({ fields?: undefined }) } -): Promise; -export async function prefetchSearchUsersQuery( - queryClient: QueryClient, - params: { variables: SearchUsersVariables; selection?: SelectionConfig } -): Promise { +export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { + variables: SearchUsersVariables; + selection: ({ + fields: S; + } & StrictSelect); +}): Promise; +export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { + variables: SearchUsersVariables; + selection?: ({ + fields?: undefined; + }); +}): Promise; +export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { + variables: SearchUsersVariables; + selection?: SelectionConfig; +}): void { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: searchUsersQueryKey(variables), - queryFn: () => getClient().query.searchUsers(variables!, { select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().query.searchUsers(variables!, { + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -466,98 +566,124 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { customQueryKeys } from '../query-keys'; -import type { UserSelect, User } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { customQueryKeys } from "../query-keys"; +import type { UserSelect, User } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const currentUserQueryKey = customQueryKeys.currentUser; - /** * Get the current authenticated user - * + * * @example * \`\`\`tsx * const { data, isLoading } = useCurrentUserQuery(); - * + * * if (data?.currentUser) { * console.log(data.currentUser); * } * \`\`\` */ -export function useCurrentUserQuery }>( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useCurrentUserQuery }>( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useCurrentUserQuery( - params?: { selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useCurrentUserQuery; +}>(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useCurrentUserQuery; +}>(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useCurrentUserQuery(params?: { + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch currentUser without React hooks - * + * * @example * \`\`\`ts * const data = await fetchCurrentUserQuery(); * \`\`\` */ -export async function fetchCurrentUserQuery( - params: { selection: ({ fields: S } & StrictSelect) } -): Promise<{ currentUser: InferSelectResult }>; -export async function fetchCurrentUserQuery( - params?: { selection?: ({ fields?: undefined }) }, -): Promise<{ currentUser: InferSelectResult }>; -export async function fetchCurrentUserQuery( - params?: { selection?: SelectionConfig }, -) { +export async function fetchCurrentUserQuery(params: { + selection: ({ + fields: S; + } & StrictSelect); +}): Promise<{ + currentUser: InferSelectResult; +}>; +export async function fetchCurrentUserQuery(params?: { + selection?: ({ + fields?: undefined; + }); +}): Promise<{ + currentUser: InferSelectResult; +}>; +export async function fetchCurrentUserQuery(params?: { + selection?: SelectionConfig; +}) { const args = buildSelectionArgs(params?.selection); - return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); + return getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch currentUser for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchCurrentUserQuery(queryClient); * \`\`\` */ -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params: { selection: ({ fields: S } & StrictSelect) } -): Promise; -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params?: { selection?: ({ fields?: undefined }) } -): Promise; -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params?: { selection?: SelectionConfig } -): Promise { +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { + selection: ({ + fields: S; + } & StrictSelect); +}): Promise; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { + selection?: ({ + fields?: undefined; + }); +}): Promise; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { + selection?: SelectionConfig; +}): void { const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -570,97 +696,123 @@ exports[`Custom Query Hook Generators generateCustomQueryHook generates custom q * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { UserSelect, User } from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { UserSelect, User } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory for caching */ -export const currentUserQueryKey = () => ['currentUser'] as const; - +export const currentUserQueryKey = () => ["currentUser"] as const; /** * Get the current authenticated user - * + * * @example * \`\`\`tsx * const { data, isLoading } = useCurrentUserQuery(); - * + * * if (data?.currentUser) { * console.log(data.currentUser); * } * \`\`\` */ -export function useCurrentUserQuery }>( - params: { selection: ({ fields: S } & StrictSelect) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useCurrentUserQuery }>( - params?: { selection?: ({ fields?: undefined }) } & Omit }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useCurrentUserQuery( - params?: { selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useCurrentUserQuery; +}>(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useCurrentUserQuery; +}>(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useCurrentUserQuery(params?: { + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch currentUser without React hooks - * + * * @example * \`\`\`ts * const data = await fetchCurrentUserQuery(); * \`\`\` */ -export async function fetchCurrentUserQuery( - params: { selection: ({ fields: S } & StrictSelect) } -): Promise<{ currentUser: InferSelectResult }>; -export async function fetchCurrentUserQuery( - params?: { selection?: ({ fields?: undefined }) }, -): Promise<{ currentUser: InferSelectResult }>; -export async function fetchCurrentUserQuery( - params?: { selection?: SelectionConfig }, -) { +export async function fetchCurrentUserQuery(params: { + selection: ({ + fields: S; + } & StrictSelect); +}): Promise<{ + currentUser: InferSelectResult; +}>; +export async function fetchCurrentUserQuery(params?: { + selection?: ({ + fields?: undefined; + }); +}): Promise<{ + currentUser: InferSelectResult; +}>; +export async function fetchCurrentUserQuery(params?: { + selection?: SelectionConfig; +}) { const args = buildSelectionArgs(params?.selection); - return getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); + return getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch currentUser for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchCurrentUserQuery(queryClient); * \`\`\` */ -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params: { selection: ({ fields: S } & StrictSelect) } -): Promise; -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params?: { selection?: ({ fields?: undefined }) } -): Promise; -export async function prefetchCurrentUserQuery( - queryClient: QueryClient, - params?: { selection?: SelectionConfig } -): Promise { +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { + selection: ({ + fields: S; + } & StrictSelect); +}): Promise; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { + selection?: ({ + fields?: undefined; + }); +}): Promise; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { + selection?: SelectionConfig; +}): void { const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), - queryFn: () => getClient().query.currentUser({ select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().query.currentUser({ + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -673,56 +825,79 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { userKeys } from '../query-keys'; -import { userMutationKeys } from '../mutation-keys'; -import type { - UserSelect, - UserWithRelations, - CreateUserInput, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { userKeys } from "../query-keys"; +import { userMutationKeys } from "../mutation-keys"; +import type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for creating a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useCreateUserMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> -): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; -export function useCreateUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> -): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; -export function useCreateUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useCreateUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ + createUser: { + user: InferSelectResult; + }; +}, Error, CreateUserInput["user"]>; +export function useCreateUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ + createUser: { + user: InferSelectResult; + }; +}, Error, CreateUserInput["user"]>; +export function useCreateUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.create(), - mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: (data: CreateUserInput["user"]) => getClient().user.create({ + data, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: userKeys.lists() }); + queryClient.invalidateQueries({ + queryKey: userKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -735,56 +910,79 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { postKeys } from '../query-keys'; -import { postMutationKeys } from '../mutation-keys'; -import type { - PostSelect, - PostWithRelations, - CreatePostInput, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { PostSelect, PostWithRelations, CreatePostInput } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { postKeys } from "../query-keys"; +import { postMutationKeys } from "../mutation-keys"; +import type { PostSelect, PostWithRelations, CreatePostInput } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { PostSelect, PostWithRelations, CreatePostInput } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for creating a Post - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useCreatePostMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreatePostMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> -): UseMutationResult<{ createPost: { post: InferSelectResult } }, Error, CreatePostInput['post']>; -export function useCreatePostMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreatePostInput['post']>, 'mutationFn'> -): UseMutationResult<{ createPost: { post: InferSelectResult } }, Error, CreatePostInput['post']>; -export function useCreatePostMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useCreatePostMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, CreatePostInput["post"]>, "mutationFn">): UseMutationResult<{ + createPost: { + post: InferSelectResult; + }; +}, Error, CreatePostInput["post"]>; +export function useCreatePostMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, CreatePostInput["post"]>, "mutationFn">): UseMutationResult<{ + createPost: { + post: InferSelectResult; + }; +}, Error, CreatePostInput["post"]>; +export function useCreatePostMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.create(), - mutationFn: (data: CreatePostInput['post']) => getClient().post.create({ data, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), + mutationFn: (data: CreatePostInput["post"]) => getClient().post.create({ + data, + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: postKeys.lists() }); + queryClient.invalidateQueries({ + queryKey: postKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -797,53 +995,76 @@ exports[`Mutation Hook Generators generateCreateMutationHook generates create mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { - UserSelect, - UserWithRelations, - CreateUserInput, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, CreateUserInput } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for creating a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useCreateUserMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ name: 'New item' }); * \`\`\` */ -export function useCreateUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> -): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; -export function useCreateUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, CreateUserInput['user']>, 'mutationFn'> -): UseMutationResult<{ createUser: { user: InferSelectResult } }, Error, CreateUserInput['user']>; -export function useCreateUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useCreateUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ + createUser: { + user: InferSelectResult; + }; +}, Error, CreateUserInput["user"]>; +export function useCreateUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ + createUser: { + user: InferSelectResult; + }; +}, Error, CreateUserInput["user"]>; +export function useCreateUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: (data: CreateUserInput['user']) => getClient().user.create({ data, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: (data: CreateUserInput["user"]) => getClient().user.create({ + data, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); + queryClient.invalidateQueries({ + queryKey: ["user", "list"] + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -856,56 +1077,98 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { userKeys } from '../query-keys'; -import { userMutationKeys } from '../mutation-keys'; -import type { - UserSelect, - UserWithRelations, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { userKeys } from "../query-keys"; +import { userMutationKeys } from "../mutation-keys"; +import type { UserSelect, UserWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for deleting a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useDeleteUserMutation({ * selection: { fields: { id: true } }, * }); - * + * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; -export function useDeleteUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; -export function useDeleteUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useDeleteUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deleteUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeleteUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deleteUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeleteUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: ({ + id + }: { + id: string; + }) => getClient().user.delete({ + where: { + id + }, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ queryKey: userKeys.detail(variables.id) }); - queryClient.invalidateQueries({ queryKey: userKeys.lists() }); + queryClient.removeQueries({ + queryKey: userKeys.detail(variables.id) + }); + queryClient.invalidateQueries({ + queryKey: userKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -918,56 +1181,98 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { postKeys } from '../query-keys'; -import { postMutationKeys } from '../mutation-keys'; -import type { - PostSelect, - PostWithRelations, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { PostSelect, PostWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { postKeys } from "../query-keys"; +import { postMutationKeys } from "../mutation-keys"; +import type { PostSelect, PostWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { PostSelect, PostWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for deleting a Post - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useDeletePostMutation({ * selection: { fields: { id: true } }, * }); - * + * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeletePostMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deletePost: { post: InferSelectResult } }, Error, { id: string }>; -export function useDeletePostMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deletePost: { post: InferSelectResult } }, Error, { id: string }>; -export function useDeletePostMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useDeletePostMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deletePost: { + post: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeletePostMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deletePost: { + post: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeletePostMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: ({ id }: { id: string }) => getClient().post.delete({ where: { id }, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), + mutationFn: ({ + id + }: { + id: string; + }) => getClient().post.delete({ + where: { + id + }, + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ queryKey: postKeys.detail(variables.id) }); - queryClient.invalidateQueries({ queryKey: postKeys.lists() }); + queryClient.removeQueries({ + queryKey: postKeys.detail(variables.id) + }); + queryClient.invalidateQueries({ + queryKey: postKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -980,53 +1285,95 @@ exports[`Mutation Hook Generators generateDeleteMutationHook generates delete mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { - UserSelect, - UserWithRelations, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { UserSelect, UserWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for deleting a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useDeleteUserMutation({ * selection: { fields: { id: true } }, * }); - * + * * mutate({ id: 'value-to-delete' }); * \`\`\` */ -export function useDeleteUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; -export function useDeleteUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string }>, 'mutationFn'> -): UseMutationResult<{ deleteUser: { user: InferSelectResult } }, Error, { id: string }>; -export function useDeleteUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useDeleteUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deleteUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeleteUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; +}>, "mutationFn">): UseMutationResult<{ + deleteUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; +}>; +export function useDeleteUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ id }: { id: string }) => getClient().user.delete({ where: { id }, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: ({ + id + }: { + id: string; + }) => getClient().user.delete({ + where: { + id + }, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.removeQueries({ queryKey: ['user', 'detail', variables.id] }); - queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); + queryClient.removeQueries({ + queryKey: ["user", "detail", variables.id] + }); + queryClient.invalidateQueries({ + queryKey: ["user", "list"] + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -1039,57 +1386,106 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { userKeys } from '../query-keys'; -import { userMutationKeys } from '../mutation-keys'; -import type { - UserSelect, - UserWithRelations, - UserPatch, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { userKeys } from "../query-keys"; +import { userMutationKeys } from "../mutation-keys"; +import type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for updating a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useUpdateUserMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> -): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; -export function useUpdateUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> -): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; -export function useUpdateUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useUpdateUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; + patch: UserPatch; +}>, "mutationFn">): UseMutationResult<{ + updateUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; + patch: UserPatch; +}>; +export function useUpdateUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; + patch: UserPatch; +}>, "mutationFn">): UseMutationResult<{ + updateUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; + patch: UserPatch; +}>; +export function useUpdateUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: userMutationKeys.all, - mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: ({ + id, + patch + }: { + id: string; + patch: UserPatch; + }) => getClient().user.update({ + where: { + id + }, + data: patch, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ queryKey: userKeys.detail(variables.id) }); - queryClient.invalidateQueries({ queryKey: userKeys.lists() }); + queryClient.invalidateQueries({ + queryKey: userKeys.detail(variables.id) + }); + queryClient.invalidateQueries({ + queryKey: userKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -1102,57 +1498,106 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { postKeys } from '../query-keys'; -import { postMutationKeys } from '../mutation-keys'; -import type { - PostSelect, - PostWithRelations, - PostPatch, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { PostSelect, PostWithRelations, PostPatch } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { postKeys } from "../query-keys"; +import { postMutationKeys } from "../mutation-keys"; +import type { PostSelect, PostWithRelations, PostPatch } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { PostSelect, PostWithRelations, PostPatch } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for updating a Post - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useUpdatePostMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdatePostMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> -): UseMutationResult<{ updatePost: { post: InferSelectResult } }, Error, { id: string; patch: PostPatch }>; -export function useUpdatePostMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: PostPatch }>, 'mutationFn'> -): UseMutationResult<{ updatePost: { post: InferSelectResult } }, Error, { id: string; patch: PostPatch }>; -export function useUpdatePostMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useUpdatePostMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; + patch: PostPatch; +}>, "mutationFn">): UseMutationResult<{ + updatePost: { + post: InferSelectResult; + }; +}, Error, { + id: string; + patch: PostPatch; +}>; +export function useUpdatePostMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; + patch: PostPatch; +}>, "mutationFn">): UseMutationResult<{ + updatePost: { + post: InferSelectResult; + }; +}, Error, { + id: string; + patch: PostPatch; +}>; +export function useUpdatePostMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ mutationKey: postMutationKeys.all, - mutationFn: ({ id, patch }: { id: string; patch: PostPatch }) => getClient().post.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), + mutationFn: ({ + id, + patch + }: { + id: string; + patch: PostPatch; + }) => getClient().post.update({ + where: { + id + }, + data: patch, + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ queryKey: postKeys.detail(variables.id) }); - queryClient.invalidateQueries({ queryKey: postKeys.lists() }); + queryClient.invalidateQueries({ + queryKey: postKeys.detail(variables.id) + }); + queryClient.invalidateQueries({ + queryKey: postKeys.lists() + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -1165,54 +1610,103 @@ exports[`Mutation Hook Generators generateUpdateMutationHook generates update mu * DO NOT EDIT - changes will be overwritten */ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { - UserSelect, - UserWithRelations, - UserPatch, -} from '../../orm/input-types'; -import type { InferSelectResult, StrictSelect } from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, UserPatch } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import type { UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** * Mutation hook for updating a User - * + * * @example * \`\`\`tsx * const { mutate, isPending } = useUpdateUserMutation({ * selection: { fields: { id: true, name: true } }, * }); - * + * * mutate({ id: 'value-here', patch: { name: 'Updated' } }); * \`\`\` */ -export function useUpdateUserMutation( - params: { selection: ({ fields: S } & StrictSelect) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> -): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; -export function useUpdateUserMutation( - params?: { selection?: ({ fields?: undefined }) } & Omit } }, Error, { id: string; patch: UserPatch }>, 'mutationFn'> -): UseMutationResult<{ updateUser: { user: InferSelectResult } }, Error, { id: string; patch: UserPatch }>; -export function useUpdateUserMutation( - params?: { selection?: SelectionConfig } & Omit, 'mutationFn'> -) { +export function useUpdateUserMutation(params: { + selection: ({ + fields: S; + } & StrictSelect); +} & Omit; + }; +}, Error, { + id: string; + patch: UserPatch; +}>, "mutationFn">): UseMutationResult<{ + updateUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; + patch: UserPatch; +}>; +export function useUpdateUserMutation(params?: { + selection?: ({ + fields?: undefined; + }); +} & Omit; + }; +}, Error, { + id: string; + patch: UserPatch; +}>, "mutationFn">): UseMutationResult<{ + updateUser: { + user: InferSelectResult; + }; +}, Error, { + id: string; + patch: UserPatch; +}>; +export function useUpdateUserMutation(params?: { + selection?: SelectionConfig; +} & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); - const { selection: _selection, ...mutationOptions } = params ?? {}; + const { + selection: _selection, + ...mutationOptions + } = params ?? {}; void _selection; const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ id, patch }: { id: string; patch: UserPatch }) => getClient().user.update({ where: { id }, data: patch, select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + mutationFn: ({ + id, + patch + }: { + id: string; + patch: UserPatch; + }) => getClient().user.update({ + where: { + id + }, + data: patch, + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), onSuccess: (_, variables) => { - queryClient.invalidateQueries({ queryKey: ['user', 'detail', variables.id] }); - queryClient.invalidateQueries({ queryKey: ['user', 'list'] }); + queryClient.invalidateQueries({ + queryKey: ["user", "detail", variables.id] + }); + queryClient.invalidateQueries({ + queryKey: ["user", "list"] + }); }, - ...mutationOptions, + ...mutationOptions }); } " @@ -1225,35 +1719,23 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildListSelectionArgs } from '../selection'; -import type { ListSelectionConfig } from '../selection'; -import { userKeys } from '../query-keys'; -import type { - UserSelect, - UserWithRelations, - UserFilter, - UsersOrderBy, -} from '../../orm/input-types'; -import type { - FindManyArgs, - InferSelectResult, - ConnectionResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildListSelectionArgs } from "../selection"; +import type { ListSelectionConfig } from "../selection"; +import { userKeys } from "../query-keys"; +import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; +import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const usersQueryKey = userKeys.list; - /** * Query hook for fetching User list - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUsersQuery({ @@ -1266,29 +1748,46 @@ export const usersQueryKey = userKeys.list; * }); * \`\`\` */ -export function useUsersQuery> }>( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUsersQuery> }>( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUsersQuery( - params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useUsersQuery>; +}>(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUsersQuery>; +}>(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUsersQuery(params?: { + selection?: ListSelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const selection = params?.selection; const args = buildListSelectionArgs(selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch User list without React hooks - * + * * @example * \`\`\`ts * const data = await fetchUsersQuery({ @@ -1299,43 +1798,57 @@ export function useUsersQuery( * }); * \`\`\` */ -export async function fetchUsersQuery( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } -): Promise<{ users: ConnectionResult> }>; -export async function fetchUsersQuery( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } -): Promise<{ users: ConnectionResult> }>; -export async function fetchUsersQuery( - params?: { selection?: ListSelectionConfig } -) { - const args = buildListSelectionArgs(params?.selection); - return getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); +export async function fetchUsersQuery(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +}): Promise<{ + users: ConnectionResult>; +}>; +export async function fetchUsersQuery(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +}): Promise<{ + users: ConnectionResult>; +}>; +export async function fetchUsersQuery(params?: { + selection?: ListSelectionConfig; +}) { + const args = buildListSelectionArgs(selection); + return getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch User list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUsersQuery(queryClient, { selection: { first: 10 } }); * \`\`\` */ -export async function prefetchUsersQuery( - queryClient: QueryClient, - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } -): Promise; -export async function prefetchUsersQuery( - queryClient: QueryClient, - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } -): Promise; -export async function prefetchUsersQuery( - queryClient: QueryClient, - params?: { selection?: ListSelectionConfig } -): Promise { - const args = buildListSelectionArgs(params?.selection); +export async function prefetchUsersQuery(queryClient: QueryClient, params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +}): Promise; +export async function prefetchUsersQuery(queryClient: QueryClient, params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +}): Promise; +export async function prefetchUsersQuery(queryClient: QueryClient, params?: { + selection?: ListSelectionConfig; +}): void { + const args = buildListSelectionArgs(selection); await queryClient.prefetchQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -1348,36 +1861,24 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook f * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildListSelectionArgs } from '../selection'; -import type { ListSelectionConfig } from '../selection'; -import { postKeys } from '../query-keys'; -import type { PostScope } from '../query-keys'; -import type { - PostSelect, - PostWithRelations, - PostFilter, - PostsOrderBy, -} from '../../orm/input-types'; -import type { - FindManyArgs, - InferSelectResult, - ConnectionResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildListSelectionArgs } from "../selection"; +import type { ListSelectionConfig } from "../selection"; +import { postKeys } from "../query-keys"; +import type { PostScope } from "../query-keys"; +import type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; +import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; +export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const postsQueryKey = postKeys.list; - /** * Query hook for fetching Post list - * + * * @example * \`\`\`tsx * const { data, isLoading } = usePostsQuery({ @@ -1389,7 +1890,7 @@ export const postsQueryKey = postKeys.list; * }, * }); * \`\`\` - * + * * @example With scope for hierarchical cache invalidation * \`\`\`tsx * const { data } = usePostsQuery({ @@ -1398,29 +1899,53 @@ export const postsQueryKey = postKeys.list; * }); * \`\`\` */ -export function usePostsQuery> }>( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } -): UseQueryResult; -export function usePostsQuery> }>( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } -): UseQueryResult; -export function usePostsQuery( - params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope } -) { +export function usePostsQuery>; +}>(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn"> & { + scope?: PostScope; +}): UseQueryResult; +export function usePostsQuery>; +}>(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn"> & { + scope?: PostScope; +}): UseQueryResult; +export function usePostsQuery(params?: { + selection?: ListSelectionConfig; +} & Omit, "queryKey" | "queryFn"> & { + scope?: PostScope; +}) { const selection = params?.selection; const args = buildListSelectionArgs(selection); - const { scope, selection: _selection, ...queryOptions } = params ?? {}; + const { + scope, + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: postKeys.list(args, scope), - queryFn: () => getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().post.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch Post list without React hooks - * + * * @example * \`\`\`ts * const data = await fetchPostsQuery({ @@ -1431,43 +1956,63 @@ export function usePostsQuery( * }); * \`\`\` */ -export async function fetchPostsQuery( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } -): Promise<{ posts: ConnectionResult> }>; -export async function fetchPostsQuery( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } -): Promise<{ posts: ConnectionResult> }>; -export async function fetchPostsQuery( - params?: { selection?: ListSelectionConfig } -) { - const args = buildListSelectionArgs(params?.selection); - return getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(); +export async function fetchPostsQuery(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +}): Promise<{ + posts: ConnectionResult>; +}>; +export async function fetchPostsQuery(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +}): Promise<{ + posts: ConnectionResult>; +}>; +export async function fetchPostsQuery(params?: { + selection?: ListSelectionConfig; +}) { + const args = buildListSelectionArgs(selection); + return getClient().post.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(); } - /** * Prefetch Post list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchPostsQuery(queryClient, { selection: { first: 10 } }); * \`\`\` */ -export async function prefetchPostsQuery( - queryClient: QueryClient, - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & { scope?: PostScope } -): Promise; -export async function prefetchPostsQuery( - queryClient: QueryClient, - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & { scope?: PostScope } -): Promise; -export async function prefetchPostsQuery( - queryClient: QueryClient, - params?: { selection?: ListSelectionConfig } & { scope?: PostScope } -): Promise { - const args = buildListSelectionArgs(params?.selection); +export async function prefetchPostsQuery(queryClient: QueryClient, params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +} & { + scope?: PostScope; +}): Promise; +export async function prefetchPostsQuery(queryClient: QueryClient, params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +} & { + scope?: PostScope; +}): Promise; +export async function prefetchPostsQuery(queryClient: QueryClient, params?: { + selection?: ListSelectionConfig; +} & { + scope?: PostScope; +}): void { + const args = buildListSelectionArgs(selection); await queryClient.prefetchQuery({ queryKey: postKeys.list(args, params?.scope), - queryFn: () => getClient().post.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), + queryFn: () => getClient().post.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap() }); } " @@ -1480,33 +2025,21 @@ exports[`Query Hook Generators generateListQueryHook generates list query hook w * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildListSelectionArgs } from '../selection'; -import type { ListSelectionConfig } from '../selection'; -import type { - UserSelect, - UserWithRelations, - UserFilter, - UsersOrderBy, -} from '../../orm/input-types'; -import type { - FindManyArgs, - InferSelectResult, - ConnectionResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - -export const usersQueryKey = (variables?: FindManyArgs) => ['user', 'list', variables] as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildListSelectionArgs } from "../selection"; +import type { ListSelectionConfig } from "../selection"; +import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; +import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; +export const usersQueryKey = (variables?: FindManyArgs) => ["user", "list", variables] as const; /** * Query hook for fetching User list - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUsersQuery({ @@ -1519,29 +2052,46 @@ export const usersQueryKey = (variables?: FindManyArgs> }>( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUsersQuery> }>( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } & Omit> }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUsersQuery( - params?: { selection?: ListSelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useUsersQuery>; +}>(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUsersQuery>; +}>(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +} & Omit>; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUsersQuery(params?: { + selection?: ListSelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const selection = params?.selection; const args = buildListSelectionArgs(selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch User list without React hooks - * + * * @example * \`\`\`ts * const data = await fetchUsersQuery({ @@ -1552,43 +2102,57 @@ export function useUsersQuery( * }); * \`\`\` */ -export async function fetchUsersQuery( - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } -): Promise<{ users: ConnectionResult> }>; -export async function fetchUsersQuery( - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } -): Promise<{ users: ConnectionResult> }>; -export async function fetchUsersQuery( - params?: { selection?: ListSelectionConfig } -) { - const args = buildListSelectionArgs(params?.selection); - return getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); +export async function fetchUsersQuery(params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +}): Promise<{ + users: ConnectionResult>; +}>; +export async function fetchUsersQuery(params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +}): Promise<{ + users: ConnectionResult>; +}>; +export async function fetchUsersQuery(params?: { + selection?: ListSelectionConfig; +}) { + const args = buildListSelectionArgs(selection); + return getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch User list for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUsersQuery(queryClient, { selection: { first: 10 } }); * \`\`\` */ -export async function prefetchUsersQuery( - queryClient: QueryClient, - params: { selection: ({ fields: S } & Omit, 'fields'> & StrictSelect) } -): Promise; -export async function prefetchUsersQuery( - queryClient: QueryClient, - params?: { selection?: (Omit, 'fields'> & { fields?: undefined }) } -): Promise; -export async function prefetchUsersQuery( - queryClient: QueryClient, - params?: { selection?: ListSelectionConfig } -): Promise { - const args = buildListSelectionArgs(params?.selection); +export async function prefetchUsersQuery(queryClient: QueryClient, params: { + selection: { + fields: S; + } & Omit, "fields"> & StrictSelect; +}): Promise; +export async function prefetchUsersQuery(queryClient: QueryClient, params?: { + selection?: Omit, "fields"> & { + fields?: undefined; + }; +}): Promise; +export async function prefetchUsersQuery(queryClient: QueryClient, params?: { + selection?: ListSelectionConfig; +}): void { + const args = buildListSelectionArgs(selection); await queryClient.prefetchQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().user.findMany({ + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -1601,31 +2165,23 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { userKeys } from '../query-keys'; -import type { - UserSelect, - UserWithRelations, -} from '../../orm/input-types'; -import type { - InferSelectResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { userKeys } from "../query-keys"; +import type { UserSelect, UserWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const userQueryKey = userKeys.detail; - /** * Query hook for fetching a single User - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUserQuery({ @@ -1634,28 +2190,49 @@ export const userQueryKey = userKeys.detail; * }); * \`\`\` */ -export function useUserQuery | null }>( - params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUserQuery | null }>( - params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUserQuery( - params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useUserQuery | null; +}>(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUserQuery | null; +}>(params: { + id: string; + selection?: { + fields?: undefined; + }; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUserQuery(params: { + id: string; + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params.selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: userKeys.detail(params.id), - queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch a single User without React hooks - * + * * @example * \`\`\`ts * const data = await fetchUserQuery({ @@ -1664,43 +2241,65 @@ export function useUserQuery( * }); * \`\`\` */ -export async function fetchUserQuery( - params: { id: string; selection: ({ fields: S } & StrictSelect) } -): Promise<{ user: InferSelectResult | null }>; -export async function fetchUserQuery( - params: { id: string; selection?: ({ fields?: undefined }) }, -): Promise<{ user: InferSelectResult | null }>; -export async function fetchUserQuery( - params: { id: string; selection?: SelectionConfig }, -) { +export async function fetchUserQuery(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +}): Promise<{ + user: InferSelectResult | null; +}>; +export async function fetchUserQuery(params: { + id: string; + selection?: { + fields?: undefined; + }; +}): Promise<{ + user: InferSelectResult | null; +}>; +export async function fetchUserQuery(params: { + id: string; + selection?: SelectionConfig; +}) { const args = buildSelectionArgs(params.selection); - return getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); + return getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch a single User for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUserQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection: ({ fields: S } & StrictSelect) }, -): Promise; -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection?: ({ fields?: undefined }) }, -): Promise; -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection?: SelectionConfig }, -): Promise { +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +}): Promise; +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection?: { + fields?: undefined; + }; +}): Promise; +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection?: SelectionConfig; +}): void { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: userKeys.detail(params.id), - queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " @@ -1713,32 +2312,24 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import { postKeys } from '../query-keys'; -import type { PostScope } from '../query-keys'; -import type { - PostSelect, - PostWithRelations, -} from '../../orm/input-types'; -import type { - InferSelectResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { PostSelect, PostWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import { postKeys } from "../query-keys"; +import type { PostScope } from "../query-keys"; +import type { PostSelect, PostWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { PostSelect, PostWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; /** Query key factory - re-exported from query-keys.ts */ export const postQueryKey = postKeys.detail; - /** * Query hook for fetching a single Post - * + * * @example * \`\`\`tsx * const { data, isLoading } = usePostQuery({ @@ -1746,7 +2337,7 @@ export const postQueryKey = postKeys.detail; * selection: { fields: { id: true, name: true } }, * }); * \`\`\` - * + * * @example With scope for hierarchical cache invalidation * \`\`\`tsx * const { data } = usePostQuery({ @@ -1755,28 +2346,56 @@ export const postQueryKey = postKeys.detail; * }); * \`\`\` */ -export function usePostQuery | null }>( - params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } -): UseQueryResult; -export function usePostQuery | null }>( - params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> & { scope?: PostScope } -): UseQueryResult; -export function usePostQuery( - params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> & { scope?: PostScope } -) { +export function usePostQuery | null; +}>(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn"> & { + scope?: PostScope; +}): UseQueryResult; +export function usePostQuery | null; +}>(params: { + id: string; + selection?: { + fields?: undefined; + }; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn"> & { + scope?: PostScope; +}): UseQueryResult; +export function usePostQuery(params: { + id: string; + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn"> & { + scope?: PostScope; +}) { const args = buildSelectionArgs(params.selection); - const { scope, selection: _selection, ...queryOptions } = params ?? {}; + const { + scope, + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: postKeys.detail(params.id, scope), - queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().post.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch a single Post without React hooks - * + * * @example * \`\`\`ts * const data = await fetchPostQuery({ @@ -1785,43 +2404,71 @@ export function usePostQuery( * }); * \`\`\` */ -export async function fetchPostQuery( - params: { id: string; selection: ({ fields: S } & StrictSelect) } -): Promise<{ post: InferSelectResult | null }>; -export async function fetchPostQuery( - params: { id: string; selection?: ({ fields?: undefined }) }, -): Promise<{ post: InferSelectResult | null }>; -export async function fetchPostQuery( - params: { id: string; selection?: SelectionConfig }, -) { +export async function fetchPostQuery(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +}): Promise<{ + post: InferSelectResult | null; +}>; +export async function fetchPostQuery(params: { + id: string; + selection?: { + fields?: undefined; + }; +}): Promise<{ + post: InferSelectResult | null; +}>; +export async function fetchPostQuery(params: { + id: string; + selection?: SelectionConfig; +}) { const args = buildSelectionArgs(params.selection); - return getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(); + return getClient().post.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap(); } - /** * Prefetch a single Post for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchPostQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchPostQuery( - queryClient: QueryClient, - params: { id: string; selection: ({ fields: S } & StrictSelect) } & { scope?: PostScope }, -): Promise; -export async function prefetchPostQuery( - queryClient: QueryClient, - params: { id: string; selection?: ({ fields?: undefined }) } & { scope?: PostScope }, -): Promise; -export async function prefetchPostQuery( - queryClient: QueryClient, - params: { id: string; selection?: SelectionConfig } & { scope?: PostScope }, -): Promise { +export async function prefetchPostQuery(queryClient: QueryClient, params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +} & { + scope?: PostScope; +}): Promise; +export async function prefetchPostQuery(queryClient: QueryClient, params: { + id: string; + selection?: { + fields?: undefined; + }; +} & { + scope?: PostScope; +}): Promise; +export async function prefetchPostQuery(queryClient: QueryClient, params: { + id: string; + selection?: SelectionConfig; +} & { + scope?: PostScope; +}): void { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ - queryKey: postKeys.detail(params.id, params.scope), - queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as PostSelect }).unwrap(), + queryKey: postKeys.detail(params.id, params?.scope), + queryFn: () => getClient().post.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as PostSelect + }).unwrap() }); } " @@ -1834,29 +2481,21 @@ exports[`Query Hook Generators generateSingleQueryHook generates single query ho * DO NOT EDIT - changes will be overwritten */ -import { useQuery } from '@tanstack/react-query'; -import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query'; -import { getClient } from '../client'; -import { buildSelectionArgs } from '../selection'; -import type { SelectionConfig } from '../selection'; -import type { - UserSelect, - UserWithRelations, -} from '../../orm/input-types'; -import type { - InferSelectResult, - StrictSelect, -} from '../../orm/select-types'; - -export type { UserSelect, UserWithRelations } from '../../orm/input-types'; - -const defaultSelect = { id: true } as const; - -export const userQueryKey = (id: string) => ['user', 'detail', id] as const; - +import { useQuery } from "@tanstack/react-query"; +import type { UseQueryOptions, UseQueryResult, QueryClient } from "@tanstack/react-query"; +import { getClient } from "../client"; +import { buildSelectionArgs } from "../selection"; +import type { SelectionConfig } from "../selection"; +import type { UserSelect, UserWithRelations } from "../../orm/input-types"; +import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; +export type { UserSelect, UserWithRelations } from "../../orm/input-types"; +const defaultSelect = { + id: true +} as const; +export const userQueryKey = (id: string) => ["user", "detail", id] as const; /** * Query hook for fetching a single User - * + * * @example * \`\`\`tsx * const { data, isLoading } = useUserQuery({ @@ -1865,28 +2504,49 @@ export const userQueryKey = (id: string) => ['user', 'detail', id] as const; * }); * \`\`\` */ -export function useUserQuery | null }>( - params: { id: string; selection: ({ fields: S } & StrictSelect) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUserQuery | null }>( - params: { id: string; selection?: ({ fields?: undefined }) } & Omit | null }, Error, TData>, 'queryKey' | 'queryFn'> -): UseQueryResult; -export function useUserQuery( - params: { id: string; selection?: SelectionConfig } & Omit, 'queryKey' | 'queryFn'> -) { +export function useUserQuery | null; +}>(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUserQuery | null; +}>(params: { + id: string; + selection?: { + fields?: undefined; + }; +} & Omit | null; +}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; +export function useUserQuery(params: { + id: string; + selection?: SelectionConfig; +} & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params.selection); - const { selection: _selection, ...queryOptions } = params ?? {}; + const { + selection: _selection, + ...queryOptions + } = params ?? {}; void _selection; return useQuery({ queryKey: userQueryKey(params.id), - queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), - ...queryOptions, + queryFn: () => getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(), + ...queryOptions }); } - /** * Fetch a single User without React hooks - * + * * @example * \`\`\`ts * const data = await fetchUserQuery({ @@ -1895,43 +2555,65 @@ export function useUserQuery( * }); * \`\`\` */ -export async function fetchUserQuery( - params: { id: string; selection: ({ fields: S } & StrictSelect) } -): Promise<{ user: InferSelectResult | null }>; -export async function fetchUserQuery( - params: { id: string; selection?: ({ fields?: undefined }) }, -): Promise<{ user: InferSelectResult | null }>; -export async function fetchUserQuery( - params: { id: string; selection?: SelectionConfig }, -) { +export async function fetchUserQuery(params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +}): Promise<{ + user: InferSelectResult | null; +}>; +export async function fetchUserQuery(params: { + id: string; + selection?: { + fields?: undefined; + }; +}): Promise<{ + user: InferSelectResult | null; +}>; +export async function fetchUserQuery(params: { + id: string; + selection?: SelectionConfig; +}) { const args = buildSelectionArgs(params.selection); - return getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(); + return getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap(); } - /** * Prefetch a single User for SSR or cache warming - * + * * @example * \`\`\`ts * await prefetchUserQuery(queryClient, { id: 'some-id' }); * \`\`\` */ -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection: ({ fields: S } & StrictSelect) }, -): Promise; -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection?: ({ fields?: undefined }) }, -): Promise; -export async function prefetchUserQuery( - queryClient: QueryClient, - params: { id: string; selection?: SelectionConfig }, -): Promise { +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection: { + fields: S; + } & StrictSelect; +}): Promise; +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection?: { + fields?: undefined; + }; +}): Promise; +export async function prefetchUserQuery(queryClient: QueryClient, params: { + id: string; + selection?: SelectionConfig; +}): void { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: userQueryKey(params.id), - queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), select: (args?.select ?? defaultSelect) as UserSelect }).unwrap(), + queryFn: () => getClient().user.findOne({ + id: params.id, + ...(args ?? {}), + select: (args?.select ?? defaultSelect) as UserSelect + }).unwrap() }); } " diff --git a/graphql/codegen/src/core/codegen/client.ts b/graphql/codegen/src/core/codegen/client.ts index 483837da4..ed5727e96 100644 --- a/graphql/codegen/src/core/codegen/client.ts +++ b/graphql/codegen/src/core/codegen/client.ts @@ -1,58 +1,39 @@ /** * Client generator - generates client.ts as ORM client wrapper * - * Generates a configure()/getClient() singleton pattern that wraps the ORM client. - * React Query hooks use getClient() to delegate to ORM model methods. + * Uses template-copy pattern: reads hooks-client.ts from templates/ + * and writes it to the output directory with a generated file header. */ -import { getGeneratedFileHeader } from './utils'; - -/** - * Generate client.ts content - ORM client wrapper with configure/getClient - */ -export function generateClientFile(): string { - const header = getGeneratedFileHeader('ORM client wrapper for React Query hooks'); - - const code = ` -import { createClient } from '../orm'; -import type { OrmClientConfig } from '../orm/client'; +import * as fs from 'fs'; +import * as path from 'path'; -export type { OrmClientConfig } from '../orm/client'; -export type { GraphQLAdapter, GraphQLError, QueryResult } from '../orm/client'; -export { GraphQLRequestError } from '../orm/client'; +import { getGeneratedFileHeader } from './utils'; -type OrmClientInstance = ReturnType; -let client: OrmClientInstance | null = null; +function findTemplateFile(templateName: string): string { + const templatePath = path.join(__dirname, 'templates', templateName); + if (fs.existsSync(templatePath)) { + return templatePath; + } + throw new Error( + `Could not find template file: ${templateName}. Searched in: ${templatePath}` + ); +} -/** - * Configure the ORM client for React Query hooks - * - * @example - * \`\`\`ts - * import { configure } from './generated/hooks'; - * - * configure({ - * endpoint: 'https://api.example.com/graphql', - * headers: { Authorization: 'Bearer ' }, - * }); - * \`\`\` - */ -export function configure(config: OrmClientConfig): void { - client = createClient(config); +function readTemplateFile(templateName: string, description: string): string { + const templatePath = findTemplateFile(templateName); + let content = fs.readFileSync(templatePath, 'utf-8'); + const headerPattern = + /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/; + content = content.replace( + headerPattern, + getGeneratedFileHeader(description) + '\n' + ); + return content; } /** - * Get the configured ORM client instance - * @throws Error if configure() has not been called + * Generate client.ts content - ORM client wrapper with configure/getClient */ -export function getClient(): OrmClientInstance { - if (!client) { - throw new Error( - 'ORM client not configured. Call configure() before using hooks.' - ); - } - return client; -} -`; - - return header + '\n' + code.trim() + '\n'; +export function generateClientFile(): string { + return readTemplateFile('hooks-client.ts', 'ORM client wrapper for React Query hooks'); } diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index dc28b3ea7..e5ceab809 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -1,5 +1,5 @@ /** - * Custom mutation hook generators for non-table operations + * Custom mutation hook generators for non-table operations (Babel AST-based) * * Generates hooks for operations discovered via schema introspection * that are NOT table CRUD operations (e.g., login, register, etc.) @@ -13,14 +13,42 @@ * useRegisterMutation.ts * ... */ +import * as t from '@babel/types'; + import type { CleanOperation, TypeRegistry } from '../../types/schema'; +import { asConst } from './babel-ast'; +import { + buildDefaultSelectExpr, + buildSelectionArgsCall, + callExpr, + constDecl, + createFunctionParam, + createImportDeclaration, + createSTypeParam, + createTypeReExport, + customSelectResultTypeLiteral, + destructureParamsWithSelection, + exportDeclareFunction, + exportFunction, + generateHookFileCode, + getClientCustomCallUnwrap, + objectProp, + omitType, + returnUseMutation, + selectionConfigType, + spreadObj, + sRef, + typeofRef, + typeRef, + useMutationOptionsType, + useMutationResultType, + voidStatement +} from './hooks-ast'; import { - buildDefaultSelectLiteral, - getSelectTypeName, - wrapInferSelectResult + getSelectTypeName } from './select-helpers'; import { createTypeTracker, @@ -29,7 +57,7 @@ import { getTypeBaseName, typeRefToTsType } from './type-resolver'; -import { getGeneratedFileHeader,ucFirst } from './utils'; +import { ucFirst } from './utils'; export interface GeneratedCustomMutationFile { fileName: string; @@ -83,8 +111,7 @@ function generateCustomMutationHookInternal( const hasArgs = operation.args.length > 0; - // Resolve types using tracker for import tracking - const resultType = typeRefToTsType(operation.returnType, tracker); + typeRefToTsType(operation.returnType, tracker); for (const arg of operation.args) { typeRefToTsType(arg.type, tracker); } @@ -92,35 +119,29 @@ function generateCustomMutationHookInternal( const selectTypeName = getSelectTypeName(operation.returnType); const payloadTypeName = getTypeBaseName(operation.returnType); const hasSelect = !!selectTypeName && !!payloadTypeName; - const defaultSelectLiteral = - hasSelect && payloadTypeName - ? buildDefaultSelectLiteral(payloadTypeName, typeRegistry) - : null; - const lines: string[] = []; + const statements: t.Statement[] = []; // Imports - lines.push(`import { useMutation } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); + statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(`import { customMutationKeys } from '../mutation-keys';`); + statements.push(createImportDeclaration('../mutation-keys', ['customMutationKeys'])); } - // ORM type imports - variable types come from orm/mutation, entity types from orm/input-types if (hasArgs) { - lines.push(`import type { ${varTypeName} } from '../../orm/mutation';`); + statements.push(createImportDeclaration('../../orm/mutation', [varTypeName], true)); } const inputTypeImports: string[] = []; if (hasSelect) { - inputTypeImports.push(selectTypeName); - inputTypeImports.push(payloadTypeName); + inputTypeImports.push(selectTypeName!); + inputTypeImports.push(payloadTypeName!); } else { - // For scalar/Connection returns, import any non-scalar type used in resultType for (const refType of tracker.referencedTypes) { if (!inputTypeImports.includes(refType)) { inputTypeImports.push(refType); @@ -128,104 +149,201 @@ function generateCustomMutationHookInternal( } } if (inputTypeImports.length > 0) { - lines.push(`import type { ${inputTypeImports.join(', ')} } from '../../orm/input-types';`); + statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true)); } if (hasSelect) { - lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); } - lines.push(''); - - // Re-export variable types for consumer convenience + // Re-exports if (hasArgs) { - lines.push(`export type { ${varTypeName} } from '../../orm/mutation';`); + statements.push(createTypeReExport([varTypeName], '../../orm/mutation')); } if (hasSelect) { - lines.push(`export type { ${selectTypeName} } from '../../orm/input-types';`); - } - if (hasArgs || hasSelect) { - lines.push(''); + statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); } - if (hasSelect && defaultSelectLiteral) { - lines.push(`const defaultSelect = ${defaultSelectLiteral} as const;`); - lines.push(''); + // Default select + if (hasSelect) { + statements.push(constDecl('defaultSelect', asConst(buildDefaultSelectExpr(payloadTypeName!, typeRegistry)))); } // Hook if (hasSelect) { - // With selection.fields: overloaded hook for autocompletion + typed options/result - const mutationVarType = hasArgs ? varTypeName : 'void'; - const selectedResultType = (s: string) => - `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; - const mutationOptionsType = (s: string) => - `Omit, 'mutationFn'>`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => `({ fields?: undefined })`; - - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${mutationOptionsType('S')}`); - lines.push(`): UseMutationResult<${selectedResultType('S')}, Error, ${mutationVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${mutationOptionsType('typeof defaultSelect')}`); - lines.push(`): UseMutationResult<${selectedResultType('typeof defaultSelect')}, Error, ${mutationVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useMutation({`); - - if (useCentralizedKeys) { - lines.push(` mutationKey: customMutationKeys.${operation.name}(),`); - } - + const mutationVarType: t.TSType = hasArgs ? typeRef(varTypeName) : t.tsVoidKeyword(); + + const selectedResultType = (sel: t.TSType) => + customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName!, sel); + + // Overload 1: with selection.fields + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation( + t.tsParenthesizedType( + t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(sRef())) + ]), + typeRef('StrictSelect', [sRef(), typeRef(selectTypeName!)]) + ]) + ) + ) + ) + ]), + useMutationOptionsType(selectedResultType(sRef()), mutationVarType) + ]); + statements.push( + exportDeclareFunction( + hookName, + createSTypeParam(selectTypeName!), + [createFunctionParam('params', o1ParamType)], + useMutationResultType(selectedResultType(sRef()), mutationVarType) + ) + ); + + // Overload 2: without selection.fields + const o2SelectionProp = (() => { + const innerFieldsProp = t.tsPropertySignature( + t.identifier('fields'), + t.tsTypeAnnotation(t.tsUndefinedKeyword()) + ); + innerFieldsProp.optional = true; + const selProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(t.tsParenthesizedType(t.tsTypeLiteral([innerFieldsProp]))) + ); + selProp.optional = true; + return selProp; + })(); + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([o2SelectionProp]), + useMutationOptionsType(selectedResultType(typeofRef('defaultSelect')), mutationVarType) + ]); + statements.push( + exportDeclareFunction( + hookName, + null, + [createFunctionParam('params', o2ParamType, true)], + useMutationResultType(selectedResultType(typeofRef('defaultSelect')), mutationVarType) + ) + ); + + // Implementation + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))) + ); + implSelProp.optional = true; + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral([implSelProp]), + omitType( + typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), mutationVarType]), + ['mutationFn'] + ) + ]); + + const body: t.Statement[] = []; + body.push(buildSelectionArgsCall(selectTypeName!)); + body.push(destructureParamsWithSelection('mutationOptions')); + body.push(voidStatement('_selection')); + + const mutationKeyExpr = useCentralizedKeys + ? callExpr( + t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), + [] + ) + : undefined; + + const selectExpr = t.tsAsExpression( + t.parenthesizedExpression( + t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + t.identifier('defaultSelect') + ) + ), + typeRef(selectTypeName!) + ); + const selectObj = t.objectExpression([objectProp('select', selectExpr)]); + + let mutationFnExpr: t.Expression; if (hasArgs) { - lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + const variablesParam = createFunctionParam('variables', typeRef(varTypeName)); + mutationFnExpr = t.arrowFunctionExpression( + [variablesParam], + getClientCustomCallUnwrap('mutation', operation.name, [t.identifier('variables')], selectObj) + ); } else { - lines.push(` mutationFn: () => getClient().mutation.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); + mutationFnExpr = t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap('mutation', operation.name, [], selectObj) + ); } - lines.push(` ...mutationOptions,`); - lines.push(` });`); - lines.push(`}`); + body.push( + returnUseMutation( + mutationFnExpr, + [spreadObj(t.identifier('mutationOptions'))], + mutationKeyExpr + ) + ); + + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); } else { // Without select: simple hook (scalar return type) - const resultTypeStr = `{ ${operation.name}: ${resultType} }`; - const mutationVarType = hasArgs ? varTypeName : 'void'; - - const optionsType = `Omit, 'mutationFn'>`; - + const resultTypeStr = typeRefToTsType(operation.returnType, tracker); + const resultTypeLiteral = t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(operation.name), + t.tsTypeAnnotation(typeRef(resultTypeStr)) + ) + ]); + const mutationVarType: t.TSType = hasArgs ? typeRef(varTypeName) : t.tsVoidKeyword(); + + const optionsType = omitType( + typeRef('UseMutationOptions', [resultTypeLiteral, typeRef('Error'), mutationVarType]), + ['mutationFn'] + ); + + const body: t.Statement[] = []; + body.push(constDecl('mutationOptions', t.logicalExpression('??', t.identifier('params'), t.objectExpression([])))); + + const mutationKeyExpr = useCentralizedKeys + ? callExpr( + t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), + [] + ) + : undefined; + + let mutationFnExpr: t.Expression; if (hasArgs) { - lines.push(`export function ${hookName}(`); - lines.push(` params?: ${optionsType}`); + const variablesParam = createFunctionParam('variables', typeRef(varTypeName)); + mutationFnExpr = t.arrowFunctionExpression( + [variablesParam], + getClientCustomCallUnwrap('mutation', operation.name, [t.identifier('variables')]) + ); } else { - lines.push(`export function ${hookName}(`); - lines.push(` params?: ${optionsType}`); - } - lines.push(`) {`); - lines.push(` const mutationOptions = params ?? {};`); - lines.push(` return useMutation({`); - - if (useCentralizedKeys) { - lines.push(` mutationKey: customMutationKeys.${operation.name}(),`); + mutationFnExpr = t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap('mutation', operation.name, []) + ); } - if (hasArgs) { - lines.push(` mutationFn: (variables: ${varTypeName}) => getClient().mutation.${operation.name}(variables).unwrap(),`); - } else { - lines.push(` mutationFn: () => getClient().mutation.${operation.name}().unwrap(),`); - } + body.push( + returnUseMutation( + mutationFnExpr, + [spreadObj(t.identifier('mutationOptions'))], + mutationKeyExpr + ) + ); - lines.push(` ...mutationOptions,`); - lines.push(` });`); - lines.push(`}`); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', optionsType, true)], body)); } - const content = getGeneratedFileHeader(`Custom mutation hook for ${operation.name}`) + '\n\n' + lines.join('\n') + '\n'; + const content = generateHookFileCode(`Custom mutation hook for ${operation.name}`, statements); return { fileName, diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index 18d11d483..b94eb1747 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -1,5 +1,5 @@ /** - * Custom query hook generators for non-table operations + * Custom query hook generators for non-table operations (Babel AST-based) * * Generates hooks for operations discovered via schema introspection * that are NOT table CRUD operations (e.g., currentUser, nodeById, etc.) @@ -13,14 +13,48 @@ * useNodeQuery.ts * ... */ +import * as t from '@babel/types'; + import type { CleanOperation, TypeRegistry } from '../../types/schema'; +import { asConst } from './babel-ast'; +import { + addJSDocComment, + buildDefaultSelectExpr, + buildSelectionArgsCall, + callExpr, + constDecl, + createFunctionParam, + createImportDeclaration, + createSAndTDataTypeParams, + createSTypeParam, + createTDataTypeParam, + createTypeReExport, + customSelectResultTypeLiteral, + destructureParamsWithSelection, + exportAsyncDeclareFunction, + exportAsyncFunction, + exportDeclareFunction, + exportFunction, + generateHookFileCode, + getClientCustomCallUnwrap, + objectProp, + omitType, + returnUseQuery, + selectionConfigType, + spreadObj, + sRef, + typeofRef, + typeRef, + useQueryOptionsImplType, + useQueryOptionsType, + voidStatement, + wrapInferSelectResultType +} from './hooks-ast'; import { - buildDefaultSelectLiteral, - getSelectTypeName, - wrapInferSelectResult + getSelectTypeName } from './select-helpers'; import { createTypeTracker, @@ -31,7 +65,7 @@ import { isTypeRequired, typeRefToTsType } from './type-resolver'; -import { getGeneratedFileHeader,ucFirst } from './utils'; +import { ucFirst } from './utils'; export interface GeneratedCustomQueryFile { fileName: string; @@ -70,7 +104,6 @@ export function generateCustomQueryHook( const hasArgs = operation.args.length > 0; const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type)); - // Resolve types using tracker for import tracking const resultType = typeRefToTsType(operation.returnType, tracker); for (const arg of operation.args) { typeRefToTsType(arg.type, tracker); @@ -79,41 +112,34 @@ export function generateCustomQueryHook( const selectTypeName = getSelectTypeName(operation.returnType); const payloadTypeName = getTypeBaseName(operation.returnType); const hasSelect = !!selectTypeName && !!payloadTypeName; - const defaultSelectLiteral = - hasSelect && payloadTypeName - ? buildDefaultSelectLiteral(payloadTypeName, typeRegistry) - : null; - const lines: string[] = []; + const statements: t.Statement[] = []; // Imports if (reactQueryEnabled) { - lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); + statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); } - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(`import { customQueryKeys } from '../query-keys';`); + statements.push(createImportDeclaration('../query-keys', ['customQueryKeys'])); } - // ORM type imports - variable types come from orm/query, entity types from orm/input-types if (hasArgs) { - lines.push(`import type { ${varTypeName} } from '../../orm/query';`); + statements.push(createImportDeclaration('../../orm/query', [varTypeName], true)); } const inputTypeImports: string[] = []; if (hasSelect) { - inputTypeImports.push(selectTypeName); - inputTypeImports.push(payloadTypeName); + inputTypeImports.push(selectTypeName!); + inputTypeImports.push(payloadTypeName!); } else { - // For scalar/Connection returns, import any non-scalar type used in resultType const baseName = getTypeBaseName(operation.returnType); if (baseName && !tracker.referencedTypes.has('__skip__')) { - // Import Connection types and other non-scalar types referenced in the result for (const refType of tracker.referencedTypes) { if (!inputTypeImports.includes(refType)) { inputTypeImports.push(refType); @@ -122,43 +148,101 @@ export function generateCustomQueryHook( } } if (inputTypeImports.length > 0) { - lines.push(`import type { ${inputTypeImports.join(', ')} } from '../../orm/input-types';`); + statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true)); } if (hasSelect) { - lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); } - lines.push(''); - - // Re-export variable types for consumer convenience + // Re-exports if (hasArgs) { - lines.push(`export type { ${varTypeName} } from '../../orm/query';`); + statements.push(createTypeReExport([varTypeName], '../../orm/query')); } if (hasSelect) { - lines.push(`export type { ${selectTypeName} } from '../../orm/input-types';`); - } - if (hasArgs || hasSelect) { - lines.push(''); + statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); } - if (hasSelect && defaultSelectLiteral) { - lines.push(`const defaultSelect = ${defaultSelectLiteral} as const;`); - lines.push(''); + // Default select + if (hasSelect) { + statements.push(constDecl('defaultSelect', asConst(buildDefaultSelectExpr(payloadTypeName!, typeRegistry)))); } // Query key if (useCentralizedKeys) { - lines.push(`/** Query key factory - re-exported from query-keys.ts */`); - lines.push(`export const ${queryKeyName} = customQueryKeys.${operation.name};`); + const keyDecl = t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(queryKeyName), + t.memberExpression(t.identifier('customQueryKeys'), t.identifier(operation.name)) + ) + ]) + ); + addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + statements.push(keyDecl); } else if (hasArgs) { - lines.push(`/** Query key factory for caching */`); - lines.push(`export const ${queryKeyName} = (variables?: ${varTypeName}) => ['${operation.name}', variables] as const;`); + const keyFn = t.arrowFunctionExpression( + [createFunctionParam('variables', typeRef(varTypeName), true)], + asConst(t.arrayExpression([t.stringLiteral(operation.name), t.identifier('variables')])) + ); + const keyDecl = t.exportNamedDeclaration( + t.variableDeclaration('const', [t.variableDeclarator(t.identifier(queryKeyName), keyFn)]) + ); + addJSDocComment(keyDecl, ['Query key factory for caching']); + statements.push(keyDecl); } else { - lines.push(`/** Query key factory for caching */`); - lines.push(`export const ${queryKeyName} = () => ['${operation.name}'] as const;`); + const keyFn = t.arrowFunctionExpression( + [], + asConst(t.arrayExpression([t.stringLiteral(operation.name)])) + ); + const keyDecl = t.exportNamedDeclaration( + t.variableDeclaration('const', [t.variableDeclarator(t.identifier(queryKeyName), keyFn)]) + ); + addJSDocComment(keyDecl, ['Query key factory for caching']); + statements.push(keyDecl); } - lines.push(''); + + // Helper to build select fallback expression + const buildSelectFallback = () => t.tsAsExpression( + t.parenthesizedExpression( + t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + t.identifier('defaultSelect') + ) + ), + typeRef(selectTypeName!) + ); + + // Helper to build query key call expression + const buildQueryKeyCall = (withVars: boolean) => { + if (useCentralizedKeys) { + return withVars + ? callExpr(t.identifier(queryKeyName), [t.identifier('variables')]) + : callExpr(t.identifier(queryKeyName), []); + } + return withVars + ? callExpr(t.identifier(queryKeyName), [t.identifier('variables')]) + : callExpr(t.identifier(queryKeyName), []); + }; + + // Helper to build the fields+StrictSelect intersection type + const fieldsSelectionType = (s: t.TSType) => t.tsParenthesizedType( + t.tsIntersectionType([ + t.tsTypeLiteral([t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s))]), + typeRef('StrictSelect', [s, typeRef(selectTypeName!)]) + ]) + ); + + // Helper for { fields?: undefined } + const noFieldsType = () => { + const fp = t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsUndefinedKeyword())); + fp.optional = true; + return t.tsParenthesizedType(t.tsTypeLiteral([fp])); + }; + + const selectedResultType = (sel: t.TSType) => + customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName!, sel); // Hook if (reactQueryEnabled) { @@ -166,125 +250,204 @@ export function generateCustomQueryHook( const argNames = operation.args.map((a) => a.name).join(', '); const exampleCall = hasArgs ? `${hookName}({ ${argNames} })` : `${hookName}()`; - lines.push(`/**`); - lines.push(` * ${description}`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`tsx`); - lines.push(` * const { data, isLoading } = ${exampleCall};`); - lines.push(` *`); - lines.push(` * if (data?.${operation.name}) {`); - lines.push(` * console.log(data.${operation.name});`); - lines.push(` * }`); - lines.push(` * \`\`\``); - lines.push(` */`); - if (hasSelect) { - // With selection.fields: overloaded hook for autocompletion - const customSelectResultType = (s: string) => - `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; - const optionsType = (queryData: string, data: string) => - `Omit, 'queryKey' | 'queryFn'>`; - - const withFieldsSelectionType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const withoutFieldsSelectionType = () => `({ fields?: undefined })`; - // Overload 1: with selection.fields + const o1Props: t.TSPropertySignature[] = []; if (hasArgs) { - const varTypeStr = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; - lines.push(`export function ${hookName}(`); - lines.push(` params: { variables: ${varTypeStr}; selection: ${withFieldsSelectionType('S')} } & ${optionsType(customSelectResultType('S'), 'TData')}`); - lines.push(`): UseQueryResult;`); - } else { - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${withFieldsSelectionType('S')} } & ${optionsType(customSelectResultType('S'), 'TData')}`); - lines.push(`): UseQueryResult;`); + const varType = hasRequiredArgs + ? typeRef(varTypeName) + : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); + o1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); } - - // Overload 2: without selection.fields (uses default) + o1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral(o1Props), + omitType(typeRef('UseQueryOptions', [selectedResultType(sRef()), typeRef('Error'), typeRef('TData')]), ['queryKey', 'queryFn']) + ]); + const o1 = exportDeclareFunction( + hookName, + createSAndTDataTypeParams(selectTypeName!, selectedResultType(sRef())), + [createFunctionParam('params', o1ParamType)], + typeRef('UseQueryResult', [typeRef('TData')]) + ); + addJSDocComment(o1, [ + description, + '', + '@example', + '```tsx', + `const { data, isLoading } = ${exampleCall};`, + '', + `if (data?.${operation.name}) {`, + ` console.log(data.${operation.name});`, + '}', + '```' + ]); + statements.push(o1); + + // Overload 2: without fields + const o2Props: t.TSPropertySignature[] = []; if (hasArgs) { - lines.push(`export function ${hookName}(`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} } & ${optionsType(customSelectResultType('typeof defaultSelect'), 'TData')}`); - lines.push(`): UseQueryResult;`); - } else { - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} } & ${optionsType(customSelectResultType('typeof defaultSelect'), 'TData')}`); - lines.push(`): UseQueryResult;`); + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + o2Props.push(varProp); } + const selProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); + selProp.optional = true; + o2Props.push(selProp); + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral(o2Props), + omitType(typeRef('UseQueryOptions', [selectedResultType(typeofRef('defaultSelect')), typeRef('Error'), typeRef('TData')]), ['queryKey', 'queryFn']) + ]); + const o2Param = createFunctionParam('params', o2ParamType, !hasRequiredArgs); + statements.push( + exportDeclareFunction( + hookName, + createTDataTypeParam(selectedResultType(typeofRef('defaultSelect'))), + [o2Param], + typeRef('UseQueryResult', [typeRef('TData')]) + ) + ); // Implementation + const implProps: t.TSPropertySignature[] = []; if (hasArgs) { - lines.push(`export function ${hookName}(`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> } & Omit, 'queryKey' | 'queryFn'>`); - lines.push(`) {`); - lines.push(` const variables = params?.variables;`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { variables: _variables, selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _variables;`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - if (hasRequiredArgs) { - lines.push(` enabled: !!variables && params?.enabled !== false,`); - } - lines.push(` ...queryOptions,`); - lines.push(` });`); - lines.push(`}`); - } else { - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'queryKey' | 'queryFn'>`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); - lines.push(`}`); + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + implProps.push(varProp); } - } else { - // Without select: simple hook (scalar return type) - const resultTypeStr = `{ ${operation.name}: ${resultType} }`; - const optionsType = `Omit, 'queryKey' | 'queryFn'>`; - - lines.push(`export function ${hookName}(`); + const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + implSelProp.optional = true; + implProps.push(implSelProp); + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral(implProps), + useQueryOptionsImplType() + ]); + const implParam = createFunctionParam('params', implParamType, !hasRequiredArgs); + + const body: t.Statement[] = []; if (hasArgs) { - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} } & ${optionsType}`); - } else { - lines.push(` params?: ${optionsType}`); + body.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); } - lines.push(`): UseQueryResult {`); + body.push(buildSelectionArgsCall(selectTypeName!)); + if (hasArgs) { - lines.push(` const variables = params?.variables;`); - lines.push(` const { variables: _variables, ...queryOptions } = params ?? {};`); - lines.push(` void _variables;`); + const destructPattern = t.objectPattern([ + t.objectProperty(t.identifier('variables'), t.identifier('_variables'), false, false), + t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false), + t.restElement(t.identifier('queryOptions')) + ]); + body.push(t.variableDeclaration('const', [ + t.variableDeclarator(destructPattern, t.logicalExpression('??', t.identifier('params'), t.objectExpression([]))) + ])); + body.push(voidStatement('_variables')); + body.push(voidStatement('_selection')); } else { - lines.push(` const queryOptions = params ?? {};`); + body.push(destructureParamsWithSelection('queryOptions')); + body.push(voidStatement('_selection')); } - lines.push(` return useQuery({`); + const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const queryFnArgs = hasArgs + ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + : [selectObj]; + const queryFnExpr = t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap('query', operation.name, queryFnArgs as t.Expression[]) + ); + + const extraProps: (t.ObjectProperty | t.SpreadElement)[] = []; + const enabledExpr = hasRequiredArgs + ? t.logicalExpression( + '&&', + t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), + t.binaryExpression( + '!==', + t.optionalMemberExpression(t.identifier('params'), t.identifier('enabled'), false, true), + t.booleanLiteral(false) + ) + ) + : undefined; + extraProps.push(spreadObj(t.identifier('queryOptions'))); + + body.push(returnUseQuery(buildQueryKeyCall(hasArgs), queryFnExpr, extraProps, enabledExpr)); + statements.push(exportFunction(hookName, null, [implParam], body)); + } else { + // Without select: simple hook (scalar return type) + const resultTypeLiteral = t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(typeRef(resultType))) + ]); + + const optType = omitType( + typeRef('UseQueryOptions', [resultTypeLiteral, typeRef('Error'), typeRef('TData')]), + ['queryKey', 'queryFn'] + ); + + let paramType: t.TSType; if (hasArgs) { - lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap(),`); + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + paramType = t.tsIntersectionType([t.tsTypeLiteral([varProp]), optType]); } else { - lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}().unwrap(),`); + paramType = optType; } - if (hasRequiredArgs) { - lines.push(` enabled: !!variables && params?.enabled !== false,`); + const implParam = createFunctionParam('params', paramType, !hasRequiredArgs); + const hookDecl = exportDeclareFunction( + hookName, + createTDataTypeParam(resultTypeLiteral), + [implParam], + typeRef('UseQueryResult', [typeRef('TData')]) + ); + addJSDocComment(hookDecl, [ + description, + '', + '@example', + '```tsx', + `const { data, isLoading } = ${exampleCall};`, + '', + `if (data?.${operation.name}) {`, + ` console.log(data.${operation.name});`, + '}', + '```' + ]); + + const body: t.Statement[] = []; + if (hasArgs) { + body.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + const destructPattern = t.objectPattern([ + t.objectProperty(t.identifier('variables'), t.identifier('_variables'), false, false), + t.restElement(t.identifier('queryOptions')) + ]); + body.push(t.variableDeclaration('const', [ + t.variableDeclarator(destructPattern, t.logicalExpression('??', t.identifier('params'), t.objectExpression([]))) + ])); + body.push(voidStatement('_variables')); + } else { + body.push(constDecl('queryOptions', t.logicalExpression('??', t.identifier('params'), t.objectExpression([])))); } - lines.push(` ...queryOptions,`); - lines.push(` });`); - lines.push(`}`); + const queryFnArgs = hasArgs + ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables')] + : []; + const queryFnExpr = t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap('query', operation.name, queryFnArgs as t.Expression[]) + ); + + const enabledExpr = hasRequiredArgs + ? t.logicalExpression( + '&&', + t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), + t.binaryExpression('!==', t.optionalMemberExpression(t.identifier('params'), t.identifier('enabled'), false, true), t.booleanLiteral(false)) + ) + : undefined; + + body.push(returnUseQuery(buildQueryKeyCall(hasArgs), queryFnExpr, [spreadObj(t.identifier('queryOptions'))], enabledExpr)); + + // We need the implementation version (not declare), with return type + statements.push(hookDecl); + statements.push(exportFunction(hookName, null, [implParam], body, typeRef('UseQueryResult', [typeRef('TData')]))); } - - lines.push(''); } // Fetch function (non-hook) @@ -292,160 +455,243 @@ export function generateCustomQueryHook( const fetchArgNames = operation.args.map((a) => a.name).join(', '); const fetchExampleCall = hasArgs ? `${fetchFnName}({ ${fetchArgNames} })` : `${fetchFnName}()`; - lines.push(`/**`); - lines.push(` * Fetch ${operation.name} without React hooks`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * const data = await ${fetchExampleCall};`); - lines.push(` * \`\`\``); - lines.push(` */`); - if (hasSelect) { - const customSelectResultType = (s: string) => - `{ ${operation.name}: ${wrapInferSelectResult(operation.returnType, payloadTypeName!, s)} }`; - const withFieldsSelectionType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const withoutFieldsSelectionType = () => `({ fields?: undefined })`; + // Overload 1: with fields + const f1Props: t.TSPropertySignature[] = []; + if (hasArgs) { + const varType = hasRequiredArgs ? typeRef(varTypeName) : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); + f1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); + } + f1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + const f1Decl = exportAsyncDeclareFunction( + fetchFnName, + createSTypeParam(selectTypeName!), + [createFunctionParam('params', t.tsTypeLiteral(f1Props))], + typeRef('Promise', [selectedResultType(sRef())]) + ); + addJSDocComment(f1Decl, [ + `Fetch ${operation.name} without React hooks`, + '', + '@example', + '```ts', + `const data = await ${fetchExampleCall};`, + '```' + ]); + statements.push(f1Decl); + + // Overload 2: without fields + const f2Props: t.TSPropertySignature[] = []; + if (hasArgs) { + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + f2Props.push(varProp); + } + const f2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); + f2SelProp.optional = true; + f2Props.push(f2SelProp); + statements.push( + exportAsyncDeclareFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral(f2Props), !hasRequiredArgs)], + typeRef('Promise', [selectedResultType(typeofRef('defaultSelect'))]) + ) + ); + // Implementation + const fImplProps: t.TSPropertySignature[] = []; if (hasArgs) { - const requiredVarType = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; - - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params: { variables: ${requiredVarType}; selection: ${withFieldsSelectionType('S')} }`); - lines.push(`): Promise<${customSelectResultType('S')}>;`); - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} }`); - lines.push(`): Promise<${customSelectResultType('typeof defaultSelect')}>;`); - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> },`); - lines.push(`) {`); - lines.push(` const variables = params?.variables;`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); - lines.push(`}`); - } else { - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params: { selection: ${withFieldsSelectionType('S')} }`); - lines.push(`): Promise<${customSelectResultType('S')}>;`); - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} },`); - lines.push(`): Promise<${customSelectResultType('typeof defaultSelect')}>;`); - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> },`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` return getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); - lines.push(`}`); + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + fImplProps.push(varProp); + } + const fImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + fImplSelProp.optional = true; + fImplProps.push(fImplSelProp); + + const fBody: t.Statement[] = []; + if (hasArgs) { + fBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); } + fBody.push(buildSelectionArgsCall(selectTypeName!)); + const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const fCallArgs = hasArgs + ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + : [selectObj]; + fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, fCallArgs as t.Expression[]))); + statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps), !hasRequiredArgs)], fBody)); } else { + const fBody: t.Statement[] = []; if (hasArgs) { - lines.push(`export async function ${fetchFnName}(`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} }`); - lines.push(`) {`); - lines.push(` const variables = params?.variables;`); - lines.push(` return getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap();`); - lines.push(`}`); + const fProps: t.TSPropertySignature[] = []; + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + fProps.push(varProp); + fBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + const fCallArgs = hasRequiredArgs ? [t.tsNonNullExpression(t.identifier('variables'))] : [t.identifier('variables')]; + fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, fCallArgs as t.Expression[]))); + const fDecl = exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fProps), !hasRequiredArgs)], fBody); + addJSDocComment(fDecl, [ + `Fetch ${operation.name} without React hooks`, + '', + '@example', + '```ts', + `const data = await ${fetchExampleCall};`, + '```' + ]); + statements.push(fDecl); } else { - lines.push(`export async function ${fetchFnName}() {`); - lines.push(` return getClient().query.${operation.name}().unwrap();`); - lines.push(`}`); + fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, []))); + const fDecl = exportAsyncFunction(fetchFnName, null, [], fBody); + addJSDocComment(fDecl, [ + `Fetch ${operation.name} without React hooks`, + '', + '@example', + '```ts', + `const data = await ${fetchExampleCall};`, + '```' + ]); + statements.push(fDecl); } } // Prefetch function if (reactQueryEnabled) { - lines.push(''); - const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`; const prefetchArgNames = operation.args.map((a) => a.name).join(', '); const prefetchExampleCall = hasArgs ? `${prefetchFnName}(queryClient, { ${prefetchArgNames} })` : `${prefetchFnName}(queryClient)`; - lines.push(`/**`); - lines.push(` * Prefetch ${operation.name} for SSR or cache warming`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * await ${prefetchExampleCall};`); - lines.push(` * \`\`\``); - lines.push(` */`); - if (hasSelect) { - const withFieldsSelectionType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const withoutFieldsSelectionType = () => `({ fields?: undefined })`; + // Overload 1: with fields + const p1Props: t.TSPropertySignature[] = []; + if (hasArgs) { + const varType = hasRequiredArgs ? typeRef(varTypeName) : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); + p1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); + } + p1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + const p1Decl = exportAsyncDeclareFunction( + prefetchFnName, + createSTypeParam(selectTypeName!), + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(p1Props))], + typeRef('Promise', [t.tsVoidKeyword()]) + ); + addJSDocComment(p1Decl, [ + `Prefetch ${operation.name} for SSR or cache warming`, + '', + '@example', + '```ts', + `await ${prefetchExampleCall};`, + '```' + ]); + statements.push(p1Decl); + + // Overload 2: without fields + const p2Props: t.TSPropertySignature[] = []; + if (hasArgs) { + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + p2Props.push(varProp); + } + const p2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); + p2SelProp.optional = true; + p2Props.push(p2SelProp); + statements.push( + exportAsyncDeclareFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(p2Props), !hasRequiredArgs)], + typeRef('Promise', [t.tsVoidKeyword()]) + ) + ); + // Implementation + const pImplProps: t.TSPropertySignature[] = []; if (hasArgs) { - const requiredVarType = hasRequiredArgs ? varTypeName : `${varTypeName} | undefined`; - - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { variables: ${requiredVarType}; selection: ${withFieldsSelectionType('S')} }`); - lines.push(`): Promise;`); - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: ${withoutFieldsSelectionType()} }`); - lines.push(`): Promise;`); - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName}; selection?: SelectionConfig<${selectTypeName}> }`); - lines.push(`): Promise {`); - lines.push(` const variables = params?.variables;`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}, { select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - lines.push(`}`); - } else { - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { selection: ${withFieldsSelectionType('S')} }`); - lines.push(`): Promise;`); - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params?: { selection?: ${withoutFieldsSelectionType()} }`); - lines.push(`): Promise;`); - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> }`); - lines.push(`): Promise {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}({ select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - lines.push(`}`); + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + pImplProps.push(varProp); } + const pImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + pImplSelProp.optional = true; + pImplProps.push(pImplSelProp); + + const pBody: t.Statement[] = []; + if (hasArgs) { + pBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + } + pBody.push(buildSelectionArgsCall(selectTypeName!)); + const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const pCallArgs = hasArgs + ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + : [selectObj]; + const prefetchQueryCall = callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), + [t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(hasArgs)), + objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, pCallArgs as t.Expression[]))) + ])] + ); + pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); + statements.push( + exportAsyncFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(pImplProps), !hasRequiredArgs)], + pBody, + t.tsVoidKeyword() + ) + ); } else { + // Without select + const pBody: t.Statement[] = []; + const pParams: t.Identifier[] = [createFunctionParam('queryClient', typeRef('QueryClient'))]; + if (hasArgs) { - lines.push(`export async function ${prefetchFnName}(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params${hasRequiredArgs ? '' : '?'}: { variables${hasRequiredArgs ? '' : '?'}: ${varTypeName} }`); - lines.push(`): Promise {`); - lines.push(` const variables = params?.variables;`); - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryKeyName}(variables),`); - lines.push(` queryFn: () => getClient().query.${operation.name}(${hasRequiredArgs ? 'variables!' : 'variables'}).unwrap(),`); - lines.push(` });`); - lines.push(`}`); + const pProps: t.TSPropertySignature[] = []; + const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + if (!hasRequiredArgs) varProp.optional = true; + pProps.push(varProp); + pParams.push(createFunctionParam('params', t.tsTypeLiteral(pProps), !hasRequiredArgs)); + pBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + const pCallArgs = hasRequiredArgs ? [t.tsNonNullExpression(t.identifier('variables'))] : [t.identifier('variables')]; + const prefetchQueryCall = callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), + [t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(true)), + objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, pCallArgs as t.Expression[]))) + ])] + ); + pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); } else { - lines.push(`export async function ${prefetchFnName}(queryClient: QueryClient): Promise {`); - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryKeyName}(),`); - lines.push(` queryFn: () => getClient().query.${operation.name}().unwrap(),`); - lines.push(` });`); - lines.push(`}`); + const prefetchQueryCall = callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), + [t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(false)), + objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, []))) + ])] + ); + pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); } + + const pDecl = exportAsyncFunction(prefetchFnName, null, pParams, pBody, t.tsVoidKeyword()); + addJSDocComment(pDecl, [ + `Prefetch ${operation.name} for SSR or cache warming`, + '', + '@example', + '```ts', + `await ${prefetchExampleCall};`, + '```' + ]); + statements.push(pDecl); } } const headerText = reactQueryEnabled ? `Custom query hook for ${operation.name}` : `Custom query functions for ${operation.name}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; + const content = generateHookFileCode(headerText, statements); return { fileName, diff --git a/graphql/codegen/src/core/codegen/hooks-ast.ts b/graphql/codegen/src/core/codegen/hooks-ast.ts new file mode 100644 index 000000000..6b3782f43 --- /dev/null +++ b/graphql/codegen/src/core/codegen/hooks-ast.ts @@ -0,0 +1,874 @@ +/** + * Shared Babel AST helpers for React Query hook generators + * + * Provides reusable AST-building functions for the common patterns + * used across queries.ts, mutations.ts, custom-queries.ts, and custom-mutations.ts. + */ +import * as t from '@babel/types'; + +import type { CleanArgument, TypeRegistry } from '../../types/schema'; +import { commentBlock, generateCode } from './babel-ast'; +import { getTypeBaseName, scalarToTsType } from './type-resolver'; +import { getGeneratedFileHeader } from './utils'; + +// ============================================================================ +// Import helpers +// ============================================================================ + +export function createImportDeclaration( + moduleSpecifier: string, + namedImports: string[], + typeOnly: boolean = false +): t.ImportDeclaration { + const specifiers = namedImports.map((name) => + t.importSpecifier(t.identifier(name), t.identifier(name)) + ); + const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier)); + decl.importKind = typeOnly ? 'type' : 'value'; + return decl; +} + +export function createTypeReExport( + names: string[], + moduleSpecifier: string +): t.ExportNamedDeclaration { + const specifiers = names.map((name) => + t.exportSpecifier(t.identifier(name), t.identifier(name)) + ); + const decl = t.exportNamedDeclaration(null, specifiers, t.stringLiteral(moduleSpecifier)); + decl.exportKind = 'type'; + return decl; +} + +// ============================================================================ +// Type reference helpers +// ============================================================================ + +export function typeRef(name: string, params?: t.TSType[]): t.TSTypeReference { + return t.tsTypeReference( + t.identifier(name), + params ? t.tsTypeParameterInstantiation(params) : undefined + ); +} + +export function sRef(): t.TSTypeReference { + return typeRef('S'); +} + +export function typeofRef(name: string): t.TSTypeQuery { + return t.tsTypeQuery(t.identifier(name)); +} + +export function stringLiteralType(value: string): t.TSLiteralType { + return t.tsLiteralType(t.stringLiteral(value)); +} + +// ============================================================================ +// Complex type builders +// ============================================================================ + +export function inferSelectResultType( + entityTypeName: string, + selectType: t.TSType +): t.TSTypeReference { + return typeRef('InferSelectResult', [typeRef(entityTypeName), selectType]); +} + +export function connectionResultType( + entityTypeName: string, + selectType: t.TSType +): t.TSTypeReference { + return typeRef('ConnectionResult', [inferSelectResultType(entityTypeName, selectType)]); +} + +export function typeLiteralWithProps( + props: Array<{ name: string; type: t.TSType; optional?: boolean }> +): t.TSTypeLiteral { + return t.tsTypeLiteral( + props.map((p) => { + const prop = t.tsPropertySignature( + t.identifier(p.name), + t.tsTypeAnnotation(p.type) + ); + if (p.optional) { + prop.optional = true; + } + return prop; + }) + ); +} + +export function queryResultType( + queryName: string, + innerType: t.TSType +): t.TSTypeLiteral { + return typeLiteralWithProps([{ name: queryName, type: innerType }]); +} + +export function listQueryResultType( + queryName: string, + entityTypeName: string, + selectType: t.TSType +): t.TSTypeLiteral { + return queryResultType(queryName, connectionResultType(entityTypeName, selectType)); +} + +export function singleQueryResultType( + queryName: string, + entityTypeName: string, + selectType: t.TSType, + nullable: boolean = true +): t.TSTypeLiteral { + const resultType = inferSelectResultType(entityTypeName, selectType); + const innerType = nullable + ? t.tsUnionType([resultType, t.tsNullKeyword()]) + : resultType; + return queryResultType(queryName, innerType); +} + +export function mutationResultType( + mutationName: string, + entityField: string, + entityTypeName: string, + selectType: t.TSType +): t.TSTypeLiteral { + return queryResultType( + mutationName, + typeLiteralWithProps([{ + name: entityField, + type: inferSelectResultType(entityTypeName, selectType) + }]) + ); +} + +export function omitType(baseType: t.TSType, keys: string[]): t.TSTypeReference { + const keyType = keys.length === 1 + ? stringLiteralType(keys[0]) + : t.tsUnionType(keys.map(stringLiteralType)); + return typeRef('Omit', [baseType, keyType]); +} + +export function listSelectionConfigType( + selectType: t.TSType, + filterTypeName: string, + orderByTypeName: string +): t.TSTypeReference { + return typeRef('ListSelectionConfig', [ + selectType, + typeRef(filterTypeName), + typeRef(orderByTypeName) + ]); +} + +export function selectionConfigType(selectType: t.TSType): t.TSTypeReference { + return typeRef('SelectionConfig', [selectType]); +} + +export function strictSelectType( + selectType: t.TSType, + shapeTypeName: string +): t.TSTypeReference { + return typeRef('StrictSelect', [selectType, typeRef(shapeTypeName)]); +} + +export function withFieldsSelectionType( + selectType: t.TSType, + selectTypeName: string +): t.TSIntersectionType { + return t.tsIntersectionType([ + typeLiteralWithProps([{ name: 'fields', type: selectType }]), + strictSelectType(selectType, selectTypeName) + ]); +} + +export function withFieldsListSelectionType( + selectType: t.TSType, + selectTypeName: string, + filterTypeName: string, + orderByTypeName: string +): t.TSIntersectionType { + return t.tsIntersectionType([ + typeLiteralWithProps([{ name: 'fields', type: selectType }]), + omitType( + listSelectionConfigType(selectType, filterTypeName, orderByTypeName), + ['fields'] + ), + strictSelectType(selectType, selectTypeName) + ]); +} + +export function withoutFieldsSelectionType(): t.TSTypeLiteral { + return typeLiteralWithProps([{ + name: 'fields', + type: t.tsUndefinedKeyword(), + optional: true + }]); +} + +export function withoutFieldsListSelectionType( + selectTypeName: string, + filterTypeName: string, + orderByTypeName: string +): t.TSIntersectionType { + return t.tsIntersectionType([ + omitType( + listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName), + ['fields'] + ), + withoutFieldsSelectionType() + ]); +} + +export function useQueryOptionsType( + queryDataType: t.TSType, + dataType: t.TSType, + extraProps?: t.TSType +): t.TSType { + const base = omitType( + typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), + ['queryKey', 'queryFn'] + ); + if (extraProps) { + return t.tsIntersectionType([base, extraProps]); + } + return base; +} + +export function useQueryOptionsImplType(extraProps?: t.TSType): t.TSType { + const base = omitType( + typeRef('UseQueryOptions', [ + t.tsAnyKeyword(), + typeRef('Error'), + t.tsAnyKeyword(), + t.tsAnyKeyword() + ]), + ['queryKey', 'queryFn'] + ); + if (extraProps) { + return t.tsIntersectionType([base, extraProps]); + } + return base; +} + +export function useMutationOptionsType( + resultType: t.TSType, + varType: t.TSType +): t.TSTypeReference { + return omitType( + typeRef('UseMutationOptions', [resultType, typeRef('Error'), varType]), + ['mutationFn'] + ); +} + +export function useMutationResultType( + resultType: t.TSType, + varType: t.TSType +): t.TSTypeReference { + return typeRef('UseMutationResult', [resultType, typeRef('Error'), varType]); +} + +// ============================================================================ +// Type parameter helpers +// ============================================================================ + +export function createSTypeParam(constraintName: string): t.TSTypeParameterDeclaration { + const param = t.tsTypeParameter(typeRef(constraintName), null, 'S'); + return t.tsTypeParameterDeclaration([param]); +} + +export function createSAndTDataTypeParams( + constraintName: string, + defaultDataType: t.TSType +): t.TSTypeParameterDeclaration { + const sParam = t.tsTypeParameter(typeRef(constraintName), null, 'S'); + const tDataParam = t.tsTypeParameter(null, defaultDataType, 'TData'); + return t.tsTypeParameterDeclaration([sParam, tDataParam]); +} + +export function createTDataTypeParam(defaultType: t.TSType): t.TSTypeParameterDeclaration { + const param = t.tsTypeParameter(null, defaultType, 'TData'); + return t.tsTypeParameterDeclaration([param]); +} + +// ============================================================================ +// Function declaration helpers +// ============================================================================ + +export function createFunctionParam( + name: string, + typeAnnotation: t.TSType, + optional: boolean = false +): t.Identifier { + const param = t.identifier(name); + param.typeAnnotation = t.tsTypeAnnotation(typeAnnotation); + param.optional = optional; + return param; +} + +export function exportDeclareFunction( + name: string, + typeParameters: t.TSTypeParameterDeclaration | null, + params: t.Identifier[], + returnType: t.TSType +): t.ExportNamedDeclaration { + const func = t.tsDeclareFunction( + t.identifier(name), + typeParameters, + params, + t.tsTypeAnnotation(returnType) + ); + return t.exportNamedDeclaration(func); +} + +export function exportFunction( + name: string, + typeParameters: t.TSTypeParameterDeclaration | null, + params: t.Identifier[], + body: t.Statement[], + returnType?: t.TSType +): t.ExportNamedDeclaration { + const func = t.functionDeclaration( + t.identifier(name), + params, + t.blockStatement(body) + ); + func.typeParameters = typeParameters; + if (returnType) { + func.returnType = t.tsTypeAnnotation(returnType); + } + return t.exportNamedDeclaration(func); +} + +export function exportAsyncFunction( + name: string, + typeParameters: t.TSTypeParameterDeclaration | null, + params: t.Identifier[], + body: t.Statement[], + returnType?: t.TSType +): t.ExportNamedDeclaration { + const func = t.functionDeclaration( + t.identifier(name), + params, + t.blockStatement(body) + ); + func.async = true; + func.typeParameters = typeParameters; + if (returnType) { + func.returnType = t.tsTypeAnnotation(returnType); + } + return t.exportNamedDeclaration(func); +} + +export function exportAsyncDeclareFunction( + name: string, + typeParameters: t.TSTypeParameterDeclaration | null, + params: t.Identifier[], + returnType: t.TSType +): t.ExportNamedDeclaration { + const func = t.tsDeclareFunction( + t.identifier(name), + typeParameters, + params, + t.tsTypeAnnotation(returnType) + ); + func.async = true; + return t.exportNamedDeclaration(func); +} + +// ============================================================================ +// Expression helpers +// ============================================================================ + +export function callExpr( + callee: string | t.Expression, + args: (t.Expression | t.SpreadElement)[] +): t.CallExpression { + const calleeExpr = typeof callee === 'string' ? t.identifier(callee) : callee; + return t.callExpression(calleeExpr, args); +} + +export function memberExpr(obj: string, prop: string): t.MemberExpression { + return t.memberExpression(t.identifier(obj), t.identifier(prop)); +} + +export function optionalMemberExpr(obj: string, prop: string): t.OptionalMemberExpression { + return t.optionalMemberExpression(t.identifier(obj), t.identifier(prop), false, true); +} + +export function arrowFn( + params: t.Identifier[], + body: t.Expression | t.BlockStatement +): t.ArrowFunctionExpression { + return t.arrowFunctionExpression(params, body); +} + +export function awaitExpr(expr: t.Expression): t.AwaitExpression { + return t.awaitExpression(expr); +} + +export function spreadObj(expr: t.Expression): t.SpreadElement { + return t.spreadElement(expr); +} + +export function objectProp( + key: string, + value: t.Expression, + shorthand: boolean = false +): t.ObjectProperty { + return t.objectProperty(t.identifier(key), value, false, shorthand); +} + +export function shorthandProp(name: string): t.ObjectProperty { + return t.objectProperty(t.identifier(name), t.identifier(name), false, true); +} + +export function constDecl(name: string, init: t.Expression): t.VariableDeclaration { + return t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(name), init) + ]); +} + +export function asConstExpr(expr: t.Expression): t.TSAsExpression { + return t.tsAsExpression(expr, t.tsTypeReference(t.identifier('const'))); +} + +export function asTypeExpr(expr: t.Expression, typeName: string): t.TSAsExpression { + return t.tsAsExpression(expr, typeRef(typeName)); +} + +// ============================================================================ +// Statement helpers +// ============================================================================ + +export function voidStatement(name: string): t.ExpressionStatement { + return t.expressionStatement(t.unaryExpression('void', t.identifier(name))); +} + +export function returnUseQuery( + queryKeyExpr: t.Expression, + queryFnExpr: t.Expression, + extraProps?: Array, + enabledExpr?: t.Expression +): t.ReturnStatement { + const props: Array = [ + objectProp('queryKey', queryKeyExpr), + objectProp('queryFn', queryFnExpr) + ]; + if (enabledExpr) { + props.push(objectProp('enabled', enabledExpr)); + } + if (extraProps) { + props.push(...extraProps); + } + return t.returnStatement( + callExpr('useQuery', [t.objectExpression(props)]) + ); +} + +export function returnUseMutation( + mutationFnExpr: t.Expression, + extraProps: Array, + mutationKeyExpr?: t.Expression +): t.ReturnStatement { + const props: Array = []; + if (mutationKeyExpr) { + props.push(objectProp('mutationKey', mutationKeyExpr)); + } + props.push(objectProp('mutationFn', mutationFnExpr)); + props.push(...extraProps); + return t.returnStatement( + callExpr('useMutation', [t.objectExpression(props)]) + ); +} + +// ============================================================================ +// Destructuring helpers +// ============================================================================ + +export function destructureWithRest( + source: t.Expression, + keys: string[], + restName: string +): t.VariableDeclaration { + const properties = keys.map((key) => + t.objectProperty(t.identifier(key), t.identifier(`_${key}`), false, false) + ); + const pattern = t.objectPattern([ + ...properties, + t.restElement(t.identifier(restName)) + ]); + return constDecl(restName, t.identifier('__placeholder__')); +} + +export function destructureParamsWithSelection( + restName: string, + extraKeys: string[] = [] +): t.VariableDeclaration { + const properties: (t.ObjectProperty | t.RestElement)[] = []; + for (const key of extraKeys) { + properties.push( + t.objectProperty(t.identifier(key), t.identifier(key), false, true) + ); + } + properties.push( + t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false) + ); + properties.push(t.restElement(t.identifier(restName))); + + const pattern = t.objectPattern(properties); + return t.variableDeclaration('const', [ + t.variableDeclarator( + pattern, + t.logicalExpression('??', t.identifier('params'), t.objectExpression([])) + ) + ]); +} + +export function destructureParamsWithSelectionAndScope( + restName: string +): t.VariableDeclaration { + const pattern = t.objectPattern([ + t.objectProperty(t.identifier('scope'), t.identifier('scope'), false, true), + t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false), + t.restElement(t.identifier(restName)) + ]); + return t.variableDeclaration('const', [ + t.variableDeclarator( + pattern, + t.logicalExpression('??', t.identifier('params'), t.objectExpression([])) + ) + ]); +} + +// ============================================================================ +// JSDoc comment helpers +// ============================================================================ + +export function addJSDocComment(node: T, lines: string[]): T { + const text = lines.length === 1 + ? `* ${lines[0]} ` + : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `; + if (!node.leadingComments) { + node.leadingComments = []; + } + node.leadingComments.push(commentBlock(text)); + return node; +} + +export function addLineComment(node: T, text: string): T { + if (!node.leadingComments) { + node.leadingComments = []; + } + node.leadingComments.push({ + type: 'CommentLine', + value: ` ${text}`, + start: null, + end: null, + loc: null + }); + return node; +} + +// ============================================================================ +// ORM client call builders +// ============================================================================ + +export function getClientCall( + modelName: string, + method: string, + args: t.Expression +): t.CallExpression { + return t.callExpression( + t.memberExpression( + t.memberExpression( + callExpr('getClient', []), + t.identifier(modelName) + ), + t.identifier(method) + ), + [args] + ); +} + +export function getClientCallUnwrap( + modelName: string, + method: string, + args: t.Expression +): t.CallExpression { + return t.callExpression( + t.memberExpression( + getClientCall(modelName, method, args), + t.identifier('unwrap') + ), + [] + ); +} + +export function getClientCustomCall( + operationType: 'query' | 'mutation', + operationName: string, + args: t.Expression[], + optionsArg?: t.Expression +): t.CallExpression { + const callArgs = optionsArg ? [...args, optionsArg] : args; + return t.callExpression( + t.memberExpression( + t.memberExpression( + callExpr('getClient', []), + t.identifier(operationType) + ), + t.identifier(operationName) + ), + callArgs + ); +} + +export function getClientCustomCallUnwrap( + operationType: 'query' | 'mutation', + operationName: string, + args: t.Expression[], + optionsArg?: t.Expression +): t.CallExpression { + return t.callExpression( + t.memberExpression( + getClientCustomCall(operationType, operationName, args, optionsArg), + t.identifier('unwrap') + ), + [] + ); +} + +// ============================================================================ +// Select/args expression builders +// ============================================================================ + +export function buildSelectFallbackExpr( + argsIdent: string, + defaultSelectIdent: string, + selectTypeName: string +): t.TSAsExpression { + return t.tsAsExpression( + t.parenthesizedExpression( + t.logicalExpression( + '??', + t.optionalMemberExpression( + t.identifier(argsIdent), + t.identifier('select'), + false, + true + ), + t.identifier(defaultSelectIdent) + ) + ), + typeRef(selectTypeName) + ); +} + +export function buildFindManyCallExpr( + singularName: string, + argsIdent: string, + selectTypeName: string, + defaultSelectIdent: string = 'defaultSelect' +): t.CallExpression { + const spreadArgs = t.parenthesizedExpression( + t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) + ); + return getClientCallUnwrap( + singularName, + 'findMany', + t.objectExpression([ + t.spreadElement(spreadArgs), + objectProp('select', buildSelectFallbackExpr(argsIdent, defaultSelectIdent, selectTypeName)) + ]) + ); +} + +export function buildFindOneCallExpr( + singularName: string, + pkFieldName: string, + argsIdent: string, + selectTypeName: string, + paramsIdent: string = 'params', + defaultSelectIdent: string = 'defaultSelect' +): t.CallExpression { + return getClientCallUnwrap( + singularName, + 'findOne', + t.objectExpression([ + objectProp( + pkFieldName, + t.memberExpression(t.identifier(paramsIdent), t.identifier(pkFieldName)) + ), + t.spreadElement( + t.parenthesizedExpression( + t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) + ) + ), + objectProp('select', buildSelectFallbackExpr(argsIdent, defaultSelectIdent, selectTypeName)) + ]) + ); +} + +// ============================================================================ +// Code generation +// ============================================================================ + +export function generateHookFileCode( + headerDescription: string, + statements: t.Statement[] +): string { + const header = getGeneratedFileHeader(headerDescription); + const code = generateCode(statements); + return header + '\n\n' + code + '\n'; +} + +// ============================================================================ +// Scope type helper +// ============================================================================ + +export function scopeTypeLiteral(scopeTypeName: string): t.TSTypeLiteral { + return typeLiteralWithProps([{ + name: 'scope', + type: typeRef(scopeTypeName), + optional: true + }]); +} + +// ============================================================================ +// Type conversion helpers (GraphQL -> AST) +// ============================================================================ + +const NON_SELECT_TYPES_AST = new Set([ + 'String', 'Int', 'Float', 'Boolean', 'ID', + 'BigFloat', 'BigInt', 'Cursor', 'Date', 'Datetime', + 'JSON', 'UUID', 'Uuid', 'Time', + 'Query', 'Mutation' +]); + +export function wrapInferSelectResultType( + typeRefNode: CleanArgument['type'], + payloadTypeName: string, + selectType: t.TSType +): t.TSType { + if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) { + return wrapInferSelectResultType( + typeRefNode.ofType as CleanArgument['type'], + payloadTypeName, + selectType + ); + } + if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) { + return t.tsArrayType( + wrapInferSelectResultType( + typeRefNode.ofType as CleanArgument['type'], + payloadTypeName, + selectType + ) + ); + } + return inferSelectResultType(payloadTypeName, selectType); +} + +export function typeRefToTsTypeAST( + typeRefNode: CleanArgument['type'] +): t.TSType { + if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) { + return typeRefToTsTypeAST(typeRefNode.ofType as CleanArgument['type']); + } + if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) { + return t.tsArrayType(typeRefToTsTypeAST(typeRefNode.ofType as CleanArgument['type'])); + } + if (typeRefNode.kind === 'SCALAR') { + const tsType = scalarToTsType(typeRefNode.name ?? 'unknown'); + if (tsType === 'string') return t.tsStringKeyword(); + if (tsType === 'number') return t.tsNumberKeyword(); + if (tsType === 'boolean') return t.tsBooleanKeyword(); + return t.tsUnknownKeyword(); + } + return typeRef(typeRefNode.name ?? 'unknown'); +} + +export function buildDefaultSelectExpr( + typeName: string, + typeRegistry: TypeRegistry, + depth: number = 0 +): t.ObjectExpression { + const resolved = typeRegistry.get(typeName); + const fields = resolved?.fields ?? []; + + if (depth > 3 || fields.length === 0) { + const fieldName = fields.length > 0 ? fields[0].name : 'id'; + return t.objectExpression([objectProp(fieldName, t.booleanLiteral(true))]); + } + + const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); + if (idLike) return t.objectExpression([objectProp(idLike.name, t.booleanLiteral(true))]); + + const scalarField = fields.find((f) => { + const baseName = getTypeBaseName(f.type); + if (!baseName) return false; + if (NON_SELECT_TYPES_AST.has(baseName)) return true; + return typeRegistry.get(baseName)?.kind === 'ENUM'; + }); + if (scalarField) return t.objectExpression([objectProp(scalarField.name, t.booleanLiteral(true))]); + + const first = fields[0]; + const firstBase = getTypeBaseName(first.type); + if ( + !firstBase || + NON_SELECT_TYPES_AST.has(firstBase) || + typeRegistry.get(firstBase)?.kind === 'ENUM' + ) { + return t.objectExpression([objectProp(first.name, t.booleanLiteral(true))]); + } + + const nested = buildDefaultSelectExpr(firstBase, typeRegistry, depth + 1); + return t.objectExpression([ + objectProp(first.name, t.objectExpression([objectProp('select', nested)])) + ]); +} + +export function buildSelectionArgsCall( + selectTypeName: string +): t.VariableDeclaration { + const call = t.callExpression(t.identifier('buildSelectionArgs'), [ + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('selection'), + false, + true + ) + ]); + // @ts-ignore - Babel types support typeParameters on CallExpression for TS + call.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + return constDecl('args', call); +} + +export function buildListSelectionArgsCall( + selectTypeName: string, + filterTypeName: string, + orderByTypeName: string +): t.VariableDeclaration { + const call = t.callExpression(t.identifier('buildListSelectionArgs'), [ + t.identifier('selection') + ]); + // @ts-ignore - Babel types support typeParameters on CallExpression for TS + call.typeParameters = t.tsTypeParameterInstantiation([ + typeRef(selectTypeName), + typeRef(filterTypeName), + typeRef(orderByTypeName) + ]); + return constDecl('args', call); +} + +export function customSelectResultTypeLiteral( + opName: string, + returnType: CleanArgument['type'], + payloadTypeName: string, + selectType: t.TSType +): t.TSTypeLiteral { + return typeLiteralWithProps([{ + name: opName, + type: wrapInferSelectResultType(returnType, payloadTypeName, selectType) + }]); +} diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 078521e49..1edbbe07a 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -1,5 +1,5 @@ /** - * Mutation hook generators - delegates to ORM model methods + * Mutation hook generators - delegates to ORM model methods (Babel AST-based) * * Output structure: * mutations/ @@ -7,7 +7,39 @@ * useUpdateCarMutation.ts -> ORM update * useDeleteCarMutation.ts -> ORM delete */ +import * as t from '@babel/types'; + import type { CleanTable } from '../../types/schema'; +import { asConst } from './babel-ast'; +import { + addJSDocComment, + buildSelectionArgsCall, + callExpr, + constDecl, + createFunctionParam, + createImportDeclaration, + createSTypeParam, + createTypeReExport, + destructureParamsWithSelection, + exportDeclareFunction, + exportFunction, + generateHookFileCode, + getClientCallUnwrap, + inferSelectResultType, + objectProp, + omitType, + returnUseMutation, + selectionConfigType, + shorthandProp, + spreadObj, + sRef, + typeofRef, + typeRef, + typeLiteralWithProps, + useMutationOptionsType, + useMutationResultType, + voidStatement +} from './hooks-ast'; import { getCreateMutationFileName, getCreateMutationHookName, @@ -16,7 +48,6 @@ import { getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, - getGeneratedFileHeader, getPrimaryKeyInfo, getTableNames, getUpdateMutationFileName, @@ -36,6 +67,49 @@ export interface MutationGeneratorOptions { useCentralizedKeys?: boolean; } +function buildMutationResultType( + mutationName: string, + singularName: string, + relationTypeName: string, + selectType: t.TSType +): t.TSTypeLiteral { + return typeLiteralWithProps([{ + name: mutationName, + type: typeLiteralWithProps([{ + name: singularName, + type: inferSelectResultType(relationTypeName, selectType) + }]) + }]); +} + +function buildSelectFallback(selectTypeName: string): t.TSAsExpression { + return t.tsAsExpression( + t.parenthesizedExpression( + t.logicalExpression( + '??', + t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), + t.identifier('defaultSelect') + ) + ), + typeRef(selectTypeName) + ); +} + +function buildFieldsSelectionType(s: t.TSType, selectTypeName: string): t.TSParenthesizedType { + return t.tsParenthesizedType( + t.tsIntersectionType([ + t.tsTypeLiteral([t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s))]), + typeRef('StrictSelect', [s, typeRef(selectTypeName)]) + ]) + ); +} + +function buildNoFieldsType(): t.TSParenthesizedType { + const fp = t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsUndefinedKeyword())); + fp.optional = true; + return t.tsParenthesizedType(t.tsTypeLiteral([fp])); +} + export function generateCreateMutationHook( table: CleanTable, options: MutationGeneratorOptions = {} @@ -45,9 +119,7 @@ export function generateCreateMutationHook( useCentralizedKeys = true } = options; - if (!reactQueryEnabled) { - return null; - } + if (!reactQueryEnabled) return null; const { typeName, singularName } = getTableNames(table); const hookName = getCreateMutationHookName(table); @@ -57,93 +129,143 @@ export function generateCreateMutationHook( const selectTypeName = `${typeName}Select`; const relationTypeName = `${typeName}WithRelations`; const createInputTypeName = `Create${typeName}Input`; - const defaultFieldName = getDefaultSelectFieldName(table); - const lines: string[] = []; + const statements: t.Statement[] = []; // Imports - lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); - - if (useCentralizedKeys) { - lines.push(`import { ${keysName} } from '../query-keys';`); - lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); - } - - lines.push(`import type {`); - lines.push(` ${selectTypeName},`); - lines.push(` ${relationTypeName},`); - lines.push(` ${createInputTypeName},`); - lines.push(`} from '../../orm/input-types';`); - lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); - lines.push(''); - - // Re-export types - lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${createInputTypeName} } from '../../orm/input-types';`); - lines.push(''); - - lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); - lines.push(''); - - // Hook - lines.push(`/**`); - lines.push(` * Mutation hook for creating a ${typeName}`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`tsx`); - lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * selection: { fields: { id: true, name: true } },`); - lines.push(` * });`); - lines.push(` *`); - lines.push(` * mutate({ name: 'New item' });`); - lines.push(` * \`\`\``); - lines.push(` */`); - const createResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; - const createVarType = `${createInputTypeName}['${singularName}']`; - const createOptionsType = (s: string) => `Omit, 'mutationFn'>`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => `({ fields?: undefined })`; - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${createOptionsType('S')}`); - lines.push(`): UseMutationResult<${createResultType('S')}, Error, ${createVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${createOptionsType('typeof defaultSelect')}`); - lines.push(`): UseMutationResult<${createResultType('typeof defaultSelect')}, Error, ${createVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` const queryClient = useQueryClient();`); - lines.push(` return useMutation({`); + statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(` mutationKey: ${mutationKeysName}.create(),`); + statements.push(createImportDeclaration('../query-keys', [keysName])); + statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); } - lines.push(` mutationFn: (data: ${createInputTypeName}['${singularName}']) => getClient().${singularName}.create({ data, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - - const listKey = useCentralizedKeys - ? `${keysName}.lists()` - : `['${typeName.toLowerCase()}', 'list']`; - lines.push(` onSuccess: () => {`); - lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); - lines.push(` },`); - lines.push(` ...mutationOptions,`); - lines.push(` });`); - lines.push(`}`); - - const content = getGeneratedFileHeader(`Create mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; + statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, createInputTypeName], true)); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + + // Re-exports + statements.push(createTypeReExport([selectTypeName, relationTypeName, createInputTypeName], '../../orm/input-types')); + + // Default select + statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); + + // Variable type: CreateTypeName['singularName'] + const createVarType = t.tsIndexedAccessType( + typeRef(createInputTypeName), + t.tsLiteralType(t.stringLiteral(singularName)) + ); + + const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + + // Overload 1: with fields + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + ]), + useMutationOptionsType(resultType(sRef()), createVarType) + ]); + const o1 = exportDeclareFunction( + hookName, + createSTypeParam(selectTypeName), + [createFunctionParam('params', o1ParamType)], + useMutationResultType(resultType(sRef()), createVarType) + ); + addJSDocComment(o1, [ + `Mutation hook for creating a ${typeName}`, + '', + '@example', + '```tsx', + `const { mutate, isPending } = ${hookName}({`, + ' selection: { fields: { id: true, name: true } },', + '});', + '', + "mutate({ name: 'New item' });", + '```' + ]); + statements.push(o1); + + // Overload 2: without fields + const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); + o2SelProp.optional = true; + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([o2SelProp]), + useMutationOptionsType(resultType(typeofRef('defaultSelect')), createVarType) + ]); + statements.push( + exportDeclareFunction( + hookName, + null, + [createFunctionParam('params', o2ParamType, true)], + useMutationResultType(resultType(typeofRef('defaultSelect')), createVarType) + ) + ); + + // Implementation + const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + implSelProp.optional = true; + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral([implSelProp]), + omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), createVarType]), ['mutationFn']) + ]); + + const body: t.Statement[] = []; + body.push(buildSelectionArgsCall(selectTypeName)); + body.push(destructureParamsWithSelection('mutationOptions')); + body.push(voidStatement('_selection')); + body.push(constDecl('queryClient', callExpr('useQueryClient', []))); + + const mutationKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), []) + : undefined; + + // mutationFn: (data: CreateInput['singular']) => getClient().singular.create({ data, select: ... }).unwrap() + const dataParam = createFunctionParam('data', createVarType); + const mutationFnExpr = t.arrowFunctionExpression( + [dataParam], + getClientCallUnwrap(singularName, 'create', t.objectExpression([ + shorthandProp('data'), + objectProp('select', buildSelectFallback(selectTypeName)) + ])) + ); + + // onSuccess: invalidate lists + const listKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) + : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + + const onSuccessFn = t.arrowFunctionExpression( + [], + t.blockStatement([ + t.expressionStatement( + callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])] + ) + ) + ]) + ); + + body.push( + returnUseMutation( + mutationFnExpr, + [ + objectProp('onSuccess', onSuccessFn), + spreadObj(t.identifier('mutationOptions')) + ], + mutationKeyExpr + ) + ); + + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); return { fileName: getCreateMutationFileName(table), - content + content: generateHookFileCode(`Create mutation hook for ${typeName}`, statements) }; } @@ -156,17 +278,9 @@ export function generateUpdateMutationHook( useCentralizedKeys = true } = options; - if (!reactQueryEnabled) { - return null; - } - - if (table.query?.update === null) { - return null; - } - - if (!hasValidPrimaryKey(table)) { - return null; - } + if (!reactQueryEnabled) return null; + if (table.query?.update === null) return null; + if (!hasValidPrimaryKey(table)) return null; const { typeName, singularName } = getTableNames(table); const hookName = getUpdateMutationHookName(table); @@ -180,95 +294,162 @@ export function generateUpdateMutationHook( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const lines: string[] = []; - - // Imports - lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); + const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); - if (useCentralizedKeys) { - lines.push(`import { ${keysName} } from '../query-keys';`); - lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); - } + const statements: t.Statement[] = []; - lines.push(`import type {`); - lines.push(` ${selectTypeName},`); - lines.push(` ${relationTypeName},`); - lines.push(` ${patchTypeName},`); - lines.push(`} from '../../orm/input-types';`); - lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); - lines.push(''); - - // Re-export types - lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${patchTypeName} } from '../../orm/input-types';`); - lines.push(''); - - lines.push(`const defaultSelect = { ${pkField.name}: true } as const;`); - lines.push(''); - - // Hook - lines.push(`/**`); - lines.push(` * Mutation hook for updating a ${typeName}`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`tsx`); - lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * selection: { fields: { id: true, name: true } },`); - lines.push(` * });`); - lines.push(` *`); - lines.push(` * mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`); - lines.push(` * \`\`\``); - lines.push(` */`); - const updateResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; - const updateVarType = `{ ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }`; - const updateOptionsType = (s: string) => `Omit, 'mutationFn'>`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => `({ fields?: undefined })`; - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${updateOptionsType('S')}`); - lines.push(`): UseMutationResult<${updateResultType('S')}, Error, ${updateVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${updateOptionsType('typeof defaultSelect')}`); - lines.push(`): UseMutationResult<${updateResultType('typeof defaultSelect')}, Error, ${updateVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` const queryClient = useQueryClient();`); - lines.push(` return useMutation({`); + // Imports + statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(` mutationKey: ${mutationKeysName}.all,`); + statements.push(createImportDeclaration('../query-keys', [keysName])); + statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); } - lines.push(` mutationFn: ({ ${pkField.name}, patch }: { ${pkField.name}: ${pkField.tsType}; patch: ${patchTypeName} }) => getClient().${singularName}.update({ where: { ${pkField.name} }, data: patch, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - - const detailKey = useCentralizedKeys - ? `${keysName}.detail(variables.${pkField.name})` - : `['${typeName.toLowerCase()}', 'detail', variables.${pkField.name}]`; - const listKey = useCentralizedKeys - ? `${keysName}.lists()` - : `['${typeName.toLowerCase()}', 'list']`; - - lines.push(` onSuccess: (_, variables) => {`); - lines.push(` queryClient.invalidateQueries({ queryKey: ${detailKey} });`); - lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); - lines.push(` },`); - lines.push(` ...mutationOptions,`); - lines.push(` });`); - lines.push(`}`); - - const content = getGeneratedFileHeader(`Update mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; + statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, patchTypeName], true)); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + + // Re-exports + statements.push(createTypeReExport([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types')); + + // Default select + statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(pkField.name, t.booleanLiteral(true))])))); + + // Variable type: { pkField: type; patch: PatchType } + const updateVarType = t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)), + t.tsPropertySignature(t.identifier('patch'), t.tsTypeAnnotation(typeRef(patchTypeName))) + ]); + + const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + + // Overload 1: with fields + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + ]), + useMutationOptionsType(resultType(sRef()), updateVarType) + ]); + const o1 = exportDeclareFunction( + hookName, + createSTypeParam(selectTypeName), + [createFunctionParam('params', o1ParamType)], + useMutationResultType(resultType(sRef()), updateVarType) + ); + addJSDocComment(o1, [ + `Mutation hook for updating a ${typeName}`, + '', + '@example', + '```tsx', + `const { mutate, isPending } = ${hookName}({`, + ' selection: { fields: { id: true, name: true } },', + '});', + '', + `mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`, + '```' + ]); + statements.push(o1); + + // Overload 2: without fields + const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); + o2SelProp.optional = true; + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([o2SelProp]), + useMutationOptionsType(resultType(typeofRef('defaultSelect')), updateVarType) + ]); + statements.push( + exportDeclareFunction( + hookName, + null, + [createFunctionParam('params', o2ParamType, true)], + useMutationResultType(resultType(typeofRef('defaultSelect')), updateVarType) + ) + ); + + // Implementation + const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + implSelProp.optional = true; + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral([implSelProp]), + omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), updateVarType]), ['mutationFn']) + ]); + + const body: t.Statement[] = []; + body.push(buildSelectionArgsCall(selectTypeName)); + body.push(destructureParamsWithSelection('mutationOptions')); + body.push(voidStatement('_selection')); + body.push(constDecl('queryClient', callExpr('useQueryClient', []))); + + const mutationKeyExpr = useCentralizedKeys + ? t.memberExpression(t.identifier(mutationKeysName), t.identifier('all')) + : undefined; + + // mutationFn: ({ pkField, patch }: { pkField: type; patch: PatchType }) => + // getClient().singular.update({ where: { pkField }, data: patch, select: ... }).unwrap() + const destructParam = t.objectPattern([ + shorthandProp(pkField.name), + shorthandProp('patch') + ]); + destructParam.typeAnnotation = t.tsTypeAnnotation(updateVarType); + const mutationFnExpr = t.arrowFunctionExpression( + [destructParam], + getClientCallUnwrap(singularName, 'update', t.objectExpression([ + objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), + objectProp('data', t.identifier('patch')), + objectProp('select', buildSelectFallback(selectTypeName)) + ])) + ); + + // onSuccess: invalidate detail and lists + const detailKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [ + t.memberExpression(t.identifier('variables'), t.identifier(pkField.name)) + ]) + : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.memberExpression(t.identifier('variables'), t.identifier(pkField.name))]); + const listKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) + : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + + const onSuccessParam = t.identifier('_'); + const variablesParam = t.identifier('variables'); + const onSuccessFn = t.arrowFunctionExpression( + [onSuccessParam, variablesParam], + t.blockStatement([ + t.expressionStatement( + callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), + [t.objectExpression([objectProp('queryKey', detailKeyExpr)])] + ) + ), + t.expressionStatement( + callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])] + ) + ) + ]) + ); + + body.push( + returnUseMutation( + mutationFnExpr, + [ + objectProp('onSuccess', onSuccessFn), + spreadObj(t.identifier('mutationOptions')) + ], + mutationKeyExpr + ) + ); + + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); return { fileName: getUpdateMutationFileName(table), - content + content: generateHookFileCode(`Update mutation hook for ${typeName}`, statements) }; } @@ -281,17 +462,9 @@ export function generateDeleteMutationHook( useCentralizedKeys = true } = options; - if (!reactQueryEnabled) { - return null; - } - - if (table.query?.delete === null) { - return null; - } - - if (!hasValidPrimaryKey(table)) { - return null; - } + if (!reactQueryEnabled) return null; + if (table.query?.delete === null) return null; + if (!hasValidPrimaryKey(table)) return null; const { typeName, singularName } = getTableNames(table); const hookName = getDeleteMutationHookName(table); @@ -304,94 +477,155 @@ export function generateDeleteMutationHook( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const lines: string[] = []; + const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); - // Imports - lines.push(`import { useMutation, useQueryClient } from '@tanstack/react-query';`); - lines.push(`import type { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';`); - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); + const statements: t.Statement[] = []; - if (useCentralizedKeys) { - lines.push(`import { ${keysName} } from '../query-keys';`); - lines.push(`import { ${mutationKeysName} } from '../mutation-keys';`); - } - - lines.push(`import type {`); - lines.push(` ${selectTypeName},`); - lines.push(` ${relationTypeName},`); - lines.push(`} from '../../orm/input-types';`); - lines.push(`import type { InferSelectResult, StrictSelect } from '../../orm/select-types';`); - lines.push(''); - - // Re-export types - lines.push(`export type { ${selectTypeName}, ${relationTypeName} } from '../../orm/input-types';`); - lines.push(''); - - lines.push(`const defaultSelect = { ${pkField.name}: true } as const;`); - lines.push(''); - - // Hook - lines.push(`/**`); - lines.push(` * Mutation hook for deleting a ${typeName}`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`tsx`); - lines.push(` * const { mutate, isPending } = ${hookName}({`); - lines.push(` * selection: { fields: { id: true } },`); - lines.push(` * });`); - lines.push(` *`); - lines.push(` * mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`); - lines.push(` * \`\`\``); - lines.push(` */`); - const deleteResultType = (s: string) => `{ ${mutationName}: { ${singularName}: InferSelectResult<${relationTypeName}, ${s}> } }`; - const deleteVarType = `{ ${pkField.name}: ${pkField.tsType} }`; - const deleteOptionsType = (s: string) => `Omit, 'mutationFn'>`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => `({ fields?: undefined })`; - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${deleteOptionsType('S')}`); - lines.push(`): UseMutationResult<${deleteResultType('S')}, Error, ${deleteVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${deleteOptionsType('typeof defaultSelect')}`); - lines.push(`): UseMutationResult<${deleteResultType('typeof defaultSelect')}, Error, ${deleteVarType}>;`); - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: SelectionConfig<${selectTypeName}> } & Omit, 'mutationFn'>`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params?.selection);`); - lines.push(` const { selection: _selection, ...mutationOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` const queryClient = useQueryClient();`); - lines.push(` return useMutation({`); + // Imports + statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(` mutationKey: ${mutationKeysName}.all,`); + statements.push(createImportDeclaration('../query-keys', [keysName])); + statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); } - lines.push(` mutationFn: ({ ${pkField.name} }: { ${pkField.name}: ${pkField.tsType} }) => getClient().${singularName}.delete({ where: { ${pkField.name} }, select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - - const detailKey = useCentralizedKeys - ? `${keysName}.detail(variables.${pkField.name})` - : `['${typeName.toLowerCase()}', 'detail', variables.${pkField.name}]`; - const listKey = useCentralizedKeys - ? `${keysName}.lists()` - : `['${typeName.toLowerCase()}', 'list']`; - - lines.push(` onSuccess: (_, variables) => {`); - lines.push(` queryClient.removeQueries({ queryKey: ${detailKey} });`); - lines.push(` queryClient.invalidateQueries({ queryKey: ${listKey} });`); - lines.push(` },`); - lines.push(` ...mutationOptions,`); - lines.push(` });`); - lines.push(`}`); - - const content = getGeneratedFileHeader(`Delete mutation hook for ${typeName}`) + '\n\n' + lines.join('\n') + '\n'; + statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true)); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + + // Re-exports + statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); + + // Default select + statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(pkField.name, t.booleanLiteral(true))])))); + + // Variable type: { pkField: type } + const deleteVarType = t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)) + ]); + + const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + + // Overload 1: with fields + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + ]), + useMutationOptionsType(resultType(sRef()), deleteVarType) + ]); + const o1 = exportDeclareFunction( + hookName, + createSTypeParam(selectTypeName), + [createFunctionParam('params', o1ParamType)], + useMutationResultType(resultType(sRef()), deleteVarType) + ); + addJSDocComment(o1, [ + `Mutation hook for deleting a ${typeName}`, + '', + '@example', + '```tsx', + `const { mutate, isPending } = ${hookName}({`, + ' selection: { fields: { id: true } },', + '});', + '', + `mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`, + '```' + ]); + statements.push(o1); + + // Overload 2: without fields + const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); + o2SelProp.optional = true; + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([o2SelProp]), + useMutationOptionsType(resultType(typeofRef('defaultSelect')), deleteVarType) + ]); + statements.push( + exportDeclareFunction( + hookName, + null, + [createFunctionParam('params', o2ParamType, true)], + useMutationResultType(resultType(typeofRef('defaultSelect')), deleteVarType) + ) + ); + + // Implementation + const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + implSelProp.optional = true; + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral([implSelProp]), + omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), deleteVarType]), ['mutationFn']) + ]); + + const body: t.Statement[] = []; + body.push(buildSelectionArgsCall(selectTypeName)); + body.push(destructureParamsWithSelection('mutationOptions')); + body.push(voidStatement('_selection')); + body.push(constDecl('queryClient', callExpr('useQueryClient', []))); + + const mutationKeyExpr = useCentralizedKeys + ? t.memberExpression(t.identifier(mutationKeysName), t.identifier('all')) + : undefined; + + // mutationFn: ({ pkField }: { pkField: type }) => + // getClient().singular.delete({ where: { pkField }, select: ... }).unwrap() + const destructParam = t.objectPattern([shorthandProp(pkField.name)]); + destructParam.typeAnnotation = t.tsTypeAnnotation(deleteVarType); + const mutationFnExpr = t.arrowFunctionExpression( + [destructParam], + getClientCallUnwrap(singularName, 'delete', t.objectExpression([ + objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), + objectProp('select', buildSelectFallback(selectTypeName)) + ])) + ); + + // onSuccess: remove detail, invalidate lists + const detailKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [ + t.memberExpression(t.identifier('variables'), t.identifier(pkField.name)) + ]) + : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.memberExpression(t.identifier('variables'), t.identifier(pkField.name))]); + const listKeyExpr = useCentralizedKeys + ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) + : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + + const onSuccessFn = t.arrowFunctionExpression( + [t.identifier('_'), t.identifier('variables')], + t.blockStatement([ + t.expressionStatement( + callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), + [t.objectExpression([objectProp('queryKey', detailKeyExpr)])] + ) + ), + t.expressionStatement( + callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])] + ) + ) + ]) + ); + + body.push( + returnUseMutation( + mutationFnExpr, + [ + objectProp('onSuccess', onSuccessFn), + spreadObj(t.identifier('mutationOptions')) + ], + mutationKeyExpr + ) + ); + + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); return { fileName: getDeleteMutationFileName(table), - content + content: generateHookFileCode(`Delete mutation hook for ${typeName}`, statements) }; } diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index 66146e42f..38e4e5395 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -1,17 +1,64 @@ /** - * Query hook generators - delegates to ORM model methods + * Query hook generators - delegates to ORM model methods (Babel AST-based) * * Output structure: * queries/ * useCarsQuery.ts - List query hook -> ORM findMany * useCarQuery.ts - Single item query hook -> ORM findOne */ +import * as t from '@babel/types'; + import type { CleanTable } from '../../types/schema'; +import { asConst } from './babel-ast'; +import { + addJSDocComment, + buildFindManyCallExpr, + buildFindOneCallExpr, + buildListSelectionArgsCall, + buildSelectionArgsCall, + buildSelectFallbackExpr, + callExpr, + connectionResultType, + constDecl, + createFunctionParam, + createImportDeclaration, + createSAndTDataTypeParams, + createSTypeParam, + createTDataTypeParam, + createTypeReExport, + destructureParamsWithSelection, + destructureParamsWithSelectionAndScope, + exportAsyncDeclareFunction, + exportAsyncFunction, + exportDeclareFunction, + exportFunction, + generateHookFileCode, + inferSelectResultType, + listQueryResultType, + listSelectionConfigType, + objectProp, + omitType, + returnUseQuery, + scopeTypeLiteral, + selectionConfigType, + singleQueryResultType, + spreadObj, + sRef, + typeofRef, + typeRef, + typeLiteralWithProps, + useQueryOptionsType, + useQueryOptionsImplType, + voidStatement, + withFieldsListSelectionType, + withFieldsSelectionType, + withoutFieldsListSelectionType, + withoutFieldsSelectionType +} from './hooks-ast'; import { getAllRowsQueryName, getDefaultSelectFieldName, getFilterTypeName, - getGeneratedFileHeader, getListQueryFileName, getListQueryHookName, getOrderByTypeName, @@ -54,229 +101,346 @@ export function generateListQueryHook( const scopeTypeName = `${typeName}Scope`; const selectTypeName = `${typeName}Select`; const relationTypeName = `${typeName}WithRelations`; - const defaultFieldName = getDefaultSelectFieldName(table); - const listResultType = (s: string) => `{ ${queryName}: ConnectionResult> }`; - const selectionType = (s: string) => `ListSelectionConfig<${s}, ${filterTypeName}, ${orderByTypeName}>`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & Omit<${selectionType(s)}, 'fields'> & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => - `(Omit<${selectionType(selectTypeName)}, 'fields'> & { fields?: undefined })`; - const lines: string[] = []; + const listResultTypeAST = (sel: t.TSType) => listQueryResultType(queryName, relationTypeName, sel); + + const statements: t.Statement[] = []; // Imports if (reactQueryEnabled) { - lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); + statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); } - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildListSelectionArgs } from '../selection';`); - lines.push(`import type { ListSelectionConfig } from '../selection';`); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildListSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['ListSelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(`import { ${keysName} } from '../query-keys';`); + statements.push(createImportDeclaration('../query-keys', [keysName])); if (hasRelationships) { - lines.push(`import type { ${scopeTypeName} } from '../query-keys';`); + statements.push(createImportDeclaration('../query-keys', [scopeTypeName], true)); } } - lines.push(`import type {`); - lines.push(` ${selectTypeName},`); - lines.push(` ${relationTypeName},`); - lines.push(` ${filterTypeName},`); - lines.push(` ${orderByTypeName},`); - lines.push(`} from '../../orm/input-types';`); - lines.push(`import type {`); - lines.push(` FindManyArgs,`); - lines.push(` InferSelectResult,`); - lines.push(` ConnectionResult,`); - lines.push(` StrictSelect,`); - lines.push(`} from '../../orm/select-types';`); - lines.push(''); - - // Re-export types for backwards compat - lines.push(`export type { ${selectTypeName}, ${relationTypeName}, ${filterTypeName}, ${orderByTypeName} } from '../../orm/input-types';`); - lines.push(''); - - lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); - lines.push(''); + statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, filterTypeName, orderByTypeName], true)); + statements.push(createImportDeclaration('../../orm/select-types', ['FindManyArgs', 'InferSelectResult', 'ConnectionResult', 'StrictSelect'], true)); + + // Re-exports + statements.push(createTypeReExport([selectTypeName, relationTypeName, filterTypeName, orderByTypeName], '../../orm/input-types')); + + // Default select + statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); // Query key if (useCentralizedKeys) { - lines.push(`/** Query key factory - re-exported from query-keys.ts */`); - lines.push(`export const ${queryName}QueryKey = ${keysName}.list;`); + const keyDecl = t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(`${queryName}QueryKey`), + t.memberExpression(t.identifier(keysName), t.identifier('list')) + ) + ]) + ); + addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + statements.push(keyDecl); } else { - lines.push(`export const ${queryName}QueryKey = (variables?: FindManyArgs) => ['${typeName.toLowerCase()}', 'list', variables] as const;`); + const keyFn = t.arrowFunctionExpression( + [createFunctionParam('variables', typeRef('FindManyArgs', [t.tsUnknownKeyword(), typeRef(filterTypeName), typeRef(orderByTypeName)]), true)], + asConst(t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list'), t.identifier('variables')])) + ); + statements.push(t.exportNamedDeclaration( + t.variableDeclaration('const', [t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn)]) + )); } - lines.push(''); + + // Helper for query key call + const buildListQueryKey = (argsExpr: t.Expression, scopeExpr?: t.Expression) => { + if (useCentralizedKeys) { + const args = [argsExpr]; + if (scopeExpr) args.push(scopeExpr); + return callExpr(t.memberExpression(t.identifier(keysName), t.identifier('list')), args); + } + return callExpr(t.identifier(`${queryName}QueryKey`), [argsExpr]); + }; + + // Helper for findMany queryFn + const buildFindManyFn = () => t.arrowFunctionExpression( + [], + buildFindManyCallExpr(singularName, 'args', selectTypeName) + ); + + // Options type builder with optional scope + const buildOptionsType = (queryDataType: t.TSType, dataType: t.TSType) => { + const base = omitType( + typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), + ['queryKey', 'queryFn'] + ); + if (hasRelationships && useCentralizedKeys) { + return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); + } + return base; + }; // Hook if (reactQueryEnabled) { const docLines = [ - `/**`, - ` * Query hook for fetching ${typeName} list`, - ` *`, - ` * @example`, - ` * \`\`\`tsx`, - ` * const { data, isLoading } = ${hookName}({`, - ` * selection: {`, - ` * fields: { id: true, name: true },`, - ` * where: { name: { equalTo: "example" } },`, - ` * orderBy: ['CREATED_AT_DESC'],`, - ` * first: 10,`, - ` * },`, - ` * });`, - ` * \`\`\`` + `Query hook for fetching ${typeName} list`, + '', + '@example', + '```tsx', + `const { data, isLoading } = ${hookName}({`, + ' selection: {', + ' fields: { id: true, name: true },', + ' where: { name: { equalTo: "example" } },', + " orderBy: ['CREATED_AT_DESC'],", + ' first: 10,', + ' },', + '});', + '```' ]; if (hasRelationships && useCentralizedKeys) { - docLines.push(` *`); - docLines.push(` * @example With scope for hierarchical cache invalidation`); - docLines.push(` * \`\`\`tsx`); - docLines.push(` * const { data } = ${hookName}({`); - docLines.push(` * selection: { first: 10 },`); - docLines.push(` * scope: { parentId: 'parent-id' },`); - docLines.push(` * });`); - docLines.push(` * \`\`\``); + docLines.push(''); + docLines.push('@example With scope for hierarchical cache invalidation'); + docLines.push('```tsx'); + docLines.push(`const { data } = ${hookName}({`); + docLines.push(' selection: { first: 10 },'); + docLines.push(" scope: { parentId: 'parent-id' },"); + docLines.push('});'); + docLines.push('```'); } - docLines.push(` */`); - lines.push(...docLines); - - const optionsType = (queryData: string, data: string) => - hasRelationships && useCentralizedKeys - ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` - : `Omit, 'queryKey' | 'queryFn'>`; - const implOptionsType = hasRelationships && useCentralizedKeys - ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` - : `Omit, 'queryKey' | 'queryFn'>`; - - // Overload 1: with selection.fields (autocompletion) - lines.push(`export function ${hookName}(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} } & ${optionsType(listResultType('S'), 'TData')}`); - lines.push(`): UseQueryResult;`); - - // Overload 2: no fields (default select) - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} } & ${optionsType(listResultType('typeof defaultSelect'), 'TData')}`); - lines.push(`): UseQueryResult;`); + + // Overload 1: with fields + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) + ) + ]), + buildOptionsType(listResultTypeAST(sRef()), typeRef('TData')) + ]); + const o1 = exportDeclareFunction( + hookName, + createSAndTDataTypeParams(selectTypeName, listResultTypeAST(sRef())), + [createFunctionParam('params', o1ParamType)], + typeRef('UseQueryResult', [typeRef('TData')]) + ); + addJSDocComment(o1, docLines); + statements.push(o1); + + // Overload 2: without fields + const o2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) + ); + o2SelProp.optional = true; + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral([o2SelProp]), + buildOptionsType(listResultTypeAST(typeofRef('defaultSelect')), typeRef('TData')) + ]); + statements.push( + exportDeclareFunction( + hookName, + createTDataTypeParam(listResultTypeAST(typeofRef('defaultSelect'))), + [createFunctionParam('params', o2ParamType, true)], + typeRef('UseQueryResult', [typeRef('TData')]) + ) + ); // Implementation - lines.push(`export function ${hookName}(`); - lines.push(` params?: { selection?: ${selectionType(selectTypeName)} } & ${implOptionsType}`); - lines.push(`) {`); - lines.push(` const selection = params?.selection;`); - lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(selection);`); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + ); + implSelProp.optional = true; + const implOptionsType = (() => { + const base = useQueryOptionsImplType(); + if (hasRelationships && useCentralizedKeys) { + return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); + } + return base; + })(); + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral([implSelProp]), + implOptionsType + ]); + + const body: t.Statement[] = []; + body.push(constDecl('selection', t.optionalMemberExpression(t.identifier('params'), t.identifier('selection'), false, true))); + body.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); if (hasRelationships && useCentralizedKeys) { - lines.push(` const { scope, selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.list(args, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); - } else if (useCentralizedKeys) { - lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.list(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); + body.push(destructureParamsWithSelectionAndScope('queryOptions')); + body.push(voidStatement('_selection')); + body.push(returnUseQuery( + buildListQueryKey(t.identifier('args'), t.identifier('scope')), + buildFindManyFn(), + [spreadObj(t.identifier('queryOptions'))] + )); } else { - lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); + body.push(destructureParamsWithSelection('queryOptions')); + body.push(voidStatement('_selection')); + body.push(returnUseQuery( + buildListQueryKey(t.identifier('args')), + buildFindManyFn(), + [spreadObj(t.identifier('queryOptions'))] + )); } - lines.push(`}`); - lines.push(''); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); } - // Fetch function (non-hook) - lines.push(`/**`); - lines.push(` * Fetch ${typeName} list without React hooks`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * const data = await fetch${ucFirst(pluralName)}Query({`); - lines.push(` * selection: {`); - lines.push(` * fields: { id: true },`); - lines.push(` * first: 10,`); - lines.push(` * },`); - lines.push(` * });`); - lines.push(` * \`\`\``); - lines.push(` */`); - lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} }`); - lines.push(`): Promise<${listResultType('S')}>;`); - lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} }`); - lines.push(`): Promise<${listResultType('typeof defaultSelect')}>;`); - lines.push(`export async function fetch${ucFirst(pluralName)}Query(`); - lines.push(` params?: { selection?: ${selectionType(selectTypeName)} }`); - lines.push(`) {`); - lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(params?.selection);`); - lines.push(` return getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); - lines.push(`}`); - lines.push(''); + // Fetch function + const fetchFnName = `fetch${ucFirst(pluralName)}Query`; + { + // Overload 1: with fields + const f1ParamType = t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) + ) + ]); + const f1Decl = exportAsyncDeclareFunction( + fetchFnName, + createSTypeParam(selectTypeName), + [createFunctionParam('params', f1ParamType)], + typeRef('Promise', [listResultTypeAST(sRef())]) + ); + addJSDocComment(f1Decl, [ + `Fetch ${typeName} list without React hooks`, + '', + '@example', + '```ts', + `const data = await ${fetchFnName}({`, + ' selection: {', + ' fields: { id: true },', + ' first: 10,', + ' },', + '});', + '```' + ]); + statements.push(f1Decl); + + // Overload 2: without fields + const f2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) + ); + f2SelProp.optional = true; + statements.push( + exportAsyncDeclareFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral([f2SelProp]), true)], + typeRef('Promise', [listResultTypeAST(typeofRef('defaultSelect'))]) + ) + ); + + // Implementation + const fImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + ); + fImplSelProp.optional = true; + const fBody: t.Statement[] = []; + fBody.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); + fBody.push(t.returnStatement(buildFindManyCallExpr(singularName, 'args', selectTypeName))); + statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]), true)], fBody)); + } // Prefetch function if (reactQueryEnabled) { - lines.push(`/**`); - lines.push(` * Prefetch ${typeName} list for SSR or cache warming`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * await prefetch${ucFirst(pluralName)}Query(queryClient, { selection: { first: 10 } });`); - lines.push(` * \`\`\``); - lines.push(` */`); - lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { selection: ${selectionWithFieldsType('S')} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); - lines.push(`): Promise;`); - lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params?: { selection?: ${selectionWithoutFieldsType()} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); - lines.push(`): Promise;`); - lines.push(`export async function prefetch${ucFirst(pluralName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params?: { selection?: ${selectionType(selectTypeName)} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''}`); - lines.push(`): Promise {`); - lines.push(` const args = buildListSelectionArgs<${selectTypeName}, ${filterTypeName}, ${orderByTypeName}>(params?.selection);`); - - if (hasRelationships && useCentralizedKeys) { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.list(args, params?.scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } else if (useCentralizedKeys) { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.list(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } else { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(args),`); - lines.push(` queryFn: () => getClient().${singularName}.findMany({ ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } + const prefetchFnName = `prefetch${ucFirst(pluralName)}Query`; + + // Overload 1: with fields + const p1Params: t.TSPropertySignature[] = [ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) + ) + ]; + const p1ParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral(p1Params), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral(p1Params); + const p1Decl = exportAsyncDeclareFunction( + prefetchFnName, + createSTypeParam(selectTypeName), + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p1ParamType)], + typeRef('Promise', [t.tsVoidKeyword()]) + ); + addJSDocComment(p1Decl, [ + `Prefetch ${typeName} list for SSR or cache warming`, + '', + '@example', + '```ts', + `await ${prefetchFnName}(queryClient, { selection: { first: 10 } });`, + '```' + ]); + statements.push(p1Decl); + + // Overload 2: without fields + const p2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) + ); + p2SelProp.optional = true; + const p2ParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral([p2SelProp]), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral([p2SelProp]); + statements.push( + exportAsyncDeclareFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p2ParamType, true)], + typeRef('Promise', [t.tsVoidKeyword()]) + ) + ); - lines.push(`}`); + // Implementation + const pImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + ); + pImplSelProp.optional = true; + const pImplParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral([pImplSelProp]), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral([pImplSelProp]); + + const pBody: t.Statement[] = []; + pBody.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); + + const queryKeyExpr = hasRelationships && useCentralizedKeys + ? buildListQueryKey(t.identifier('args'), t.optionalMemberExpression(t.identifier('params'), t.identifier('scope'), false, true)) + : buildListQueryKey(t.identifier('args')); + + const prefetchCall = callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), + [t.objectExpression([ + objectProp('queryKey', queryKeyExpr), + objectProp('queryFn', buildFindManyFn()) + ])] + ); + pBody.push(t.expressionStatement(t.awaitExpression(prefetchCall))); + + statements.push( + exportAsyncFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType, true)], + pBody, + t.tsVoidKeyword() + ) + ); } const headerText = reactQueryEnabled ? `List query hook for ${typeName}` : `List query functions for ${typeName}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getListQueryFileName(table), - content + content: generateHookFileCode(headerText, statements) }; } @@ -284,9 +448,7 @@ export function generateSingleQueryHook( table: CleanTable, options: QueryGeneratorOptions = {} ): GeneratedQueryFile | null { - if (!hasValidPrimaryKey(table)) { - return null; - } + if (!hasValidPrimaryKey(table)) return null; const { reactQueryEnabled = true, @@ -306,217 +468,382 @@ export function generateSingleQueryHook( const pkFieldName = pkField?.name ?? 'id'; const pkFieldTsType = pkField?.tsType ?? 'string'; const defaultFieldName = getDefaultSelectFieldName(table); - const singleResultType = (s: string) => `{ ${queryName}: InferSelectResult<${relationTypeName}, ${s}> | null }`; - const selectionWithFieldsType = (s: string) => - `({ fields: ${s} } & StrictSelect<${s}, ${selectTypeName}>)`; - const selectionWithoutFieldsType = () => `({ fields?: undefined })`; - const lines: string[] = []; + const pkTsType: t.TSType = pkFieldTsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); + const singleResultTypeAST = (sel: t.TSType) => singleQueryResultType(queryName, relationTypeName, sel); + + const statements: t.Statement[] = []; // Imports if (reactQueryEnabled) { - lines.push(`import { useQuery } from '@tanstack/react-query';`); - lines.push(`import type { UseQueryOptions, UseQueryResult, QueryClient } from '@tanstack/react-query';`); + statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); + statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); } - lines.push(`import { getClient } from '../client';`); - lines.push(`import { buildSelectionArgs } from '../selection';`); - lines.push(`import type { SelectionConfig } from '../selection';`); + statements.push(createImportDeclaration('../client', ['getClient'])); + statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); + statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); if (useCentralizedKeys) { - lines.push(`import { ${keysName} } from '../query-keys';`); + statements.push(createImportDeclaration('../query-keys', [keysName])); if (hasRelationships) { - lines.push(`import type { ${scopeTypeName} } from '../query-keys';`); + statements.push(createImportDeclaration('../query-keys', [scopeTypeName], true)); } } - lines.push(`import type {`); - lines.push(` ${selectTypeName},`); - lines.push(` ${relationTypeName},`); - lines.push(`} from '../../orm/input-types';`); - lines.push(`import type {`); - lines.push(` InferSelectResult,`); - lines.push(` StrictSelect,`); - lines.push(`} from '../../orm/select-types';`); - lines.push(''); + statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true)); + statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); - // Re-export types - lines.push(`export type { ${selectTypeName}, ${relationTypeName} } from '../../orm/input-types';`); - lines.push(''); + // Re-exports + statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); - lines.push(`const defaultSelect = { ${defaultFieldName}: true } as const;`); - lines.push(''); + // Default select + statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); // Query key if (useCentralizedKeys) { - lines.push(`/** Query key factory - re-exported from query-keys.ts */`); - lines.push(`export const ${queryName}QueryKey = ${keysName}.detail;`); + const keyDecl = t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator( + t.identifier(`${queryName}QueryKey`), + t.memberExpression(t.identifier(keysName), t.identifier('detail')) + ) + ]) + ); + addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + statements.push(keyDecl); } else { - lines.push(`export const ${queryName}QueryKey = (id: ${pkFieldTsType}) => ['${typeName.toLowerCase()}', 'detail', id] as const;`); + const keyFn = t.arrowFunctionExpression( + [createFunctionParam('id', pkTsType)], + asConst(t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.identifier('id')])) + ); + statements.push(t.exportNamedDeclaration( + t.variableDeclaration('const', [t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn)]) + )); } - lines.push(''); + + // Helper for query key call + const buildDetailQueryKey = (pkExpr: t.Expression, scopeExpr?: t.Expression) => { + if (useCentralizedKeys) { + const args = [pkExpr]; + if (scopeExpr) args.push(scopeExpr); + return callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), args); + } + return callExpr(t.identifier(`${queryName}QueryKey`), [pkExpr]); + }; + + // Helper for findOne queryFn + const buildFindOneFn = () => t.arrowFunctionExpression( + [], + buildFindOneCallExpr(singularName, pkFieldName, 'args', selectTypeName) + ); + + // Options type builder with optional scope + const buildSingleOptionsType = (queryDataType: t.TSType, dataType: t.TSType) => { + const base = omitType( + typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), + ['queryKey', 'queryFn'] + ); + if (hasRelationships && useCentralizedKeys) { + return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); + } + return base; + }; // Hook if (reactQueryEnabled) { const docLines = [ - `/**`, - ` * Query hook for fetching a single ${typeName}`, - ` *`, - ` * @example`, - ` * \`\`\`tsx`, - ` * const { data, isLoading } = ${hookName}({`, - ` * ${pkFieldName}: 'some-id',`, - ` * selection: { fields: { id: true, name: true } },`, - ` * });`, - ` * \`\`\`` + `Query hook for fetching a single ${typeName}`, + '', + '@example', + '```tsx', + `const { data, isLoading } = ${hookName}({`, + ` ${pkFieldName}: 'some-id',`, + ' selection: { fields: { id: true, name: true } },', + '});', + '```' ]; if (hasRelationships && useCentralizedKeys) { - docLines.push(` *`); - docLines.push(` * @example With scope for hierarchical cache invalidation`); - docLines.push(` * \`\`\`tsx`); - docLines.push(` * const { data } = ${hookName}({`); - docLines.push(` * ${pkFieldName}: 'some-id',`); - docLines.push(` * scope: { parentId: 'parent-id' },`); - docLines.push(` * });`); - docLines.push(` * \`\`\``); + docLines.push(''); + docLines.push('@example With scope for hierarchical cache invalidation'); + docLines.push('```tsx'); + docLines.push(`const { data } = ${hookName}({`); + docLines.push(` ${pkFieldName}: 'some-id',`); + docLines.push(" scope: { parentId: 'parent-id' },"); + docLines.push('});'); + docLines.push('```'); } - docLines.push(` */`); - lines.push(...docLines); - - const singleOptionsType = (queryData: string, data: string) => - hasRelationships && useCentralizedKeys - ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` - : `Omit, 'queryKey' | 'queryFn'>`; - const singleImplOptionsType = hasRelationships && useCentralizedKeys - ? `Omit, 'queryKey' | 'queryFn'> & { scope?: ${scopeTypeName} }` - : `Omit, 'queryKey' | 'queryFn'>`; - - // Overload 1: with selection.fields (provides contextual typing for autocompletion) - lines.push(`export function ${hookName}(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} } & ${singleOptionsType(singleResultType('S'), 'TData')}`); - lines.push(`): UseQueryResult;`); - - // Overload 2: without fields (uses default select) - lines.push(`export function ${hookName}(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} } & ${singleOptionsType(singleResultType('typeof defaultSelect'), 'TData')}`); - lines.push(`): UseQueryResult;`); + + // Overload 1: with fields + const o1Props = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName)) + ) + ]; + const o1ParamType = t.tsIntersectionType([ + t.tsTypeLiteral(o1Props), + buildSingleOptionsType(singleResultTypeAST(sRef()), typeRef('TData')) + ]); + const o1 = exportDeclareFunction( + hookName, + createSAndTDataTypeParams(selectTypeName, singleResultTypeAST(sRef())), + [createFunctionParam('params', o1ParamType)], + typeRef('UseQueryResult', [typeRef('TData')]) + ); + addJSDocComment(o1, docLines); + statements.push(o1); + + // Overload 2: without fields + const o2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsSelectionType()) + ); + o2SelProp.optional = true; + const o2Props = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + o2SelProp + ]; + const o2ParamType = t.tsIntersectionType([ + t.tsTypeLiteral(o2Props), + buildSingleOptionsType(singleResultTypeAST(typeofRef('defaultSelect')), typeRef('TData')) + ]); + statements.push( + exportDeclareFunction( + hookName, + createTDataTypeParam(singleResultTypeAST(typeofRef('defaultSelect'))), + [createFunctionParam('params', o2ParamType)], + typeRef('UseQueryResult', [typeRef('TData')]) + ) + ); // Implementation - lines.push(`export function ${hookName}(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> } & ${singleImplOptionsType}`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + ); + implSelProp.optional = true; + const implProps = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + implSelProp + ]; + const implOptionsType = (() => { + const base = useQueryOptionsImplType(); + if (hasRelationships && useCentralizedKeys) { + return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); + } + return base; + })(); + const implParamType = t.tsIntersectionType([ + t.tsTypeLiteral(implProps), + implOptionsType + ]); + + const body: t.Statement[] = []; + // const args = buildSelectionArgs(params.selection); + const argsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ + t.memberExpression(t.identifier('params'), t.identifier('selection')) + ]); + // @ts-ignore + argsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + body.push(constDecl('args', argsCall)); + + const pkMemberExpr = t.memberExpression(t.identifier('params'), t.identifier(pkFieldName)); if (hasRelationships && useCentralizedKeys) { - lines.push(` const { scope, selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}, scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); - } else if (useCentralizedKeys) { - lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); + body.push(destructureParamsWithSelectionAndScope('queryOptions')); + body.push(voidStatement('_selection')); + body.push(returnUseQuery( + buildDetailQueryKey(pkMemberExpr, t.identifier('scope')), + buildFindOneFn(), + [spreadObj(t.identifier('queryOptions'))] + )); } else { - lines.push(` const { selection: _selection, ...queryOptions } = params ?? {};`); - lines.push(` void _selection;`); - lines.push(` return useQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(params.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` ...queryOptions,`); - lines.push(` });`); + body.push(destructureParamsWithSelection('queryOptions')); + body.push(voidStatement('_selection')); + body.push(returnUseQuery( + buildDetailQueryKey(pkMemberExpr), + buildFindOneFn(), + [spreadObj(t.identifier('queryOptions'))] + )); } - lines.push(`}`); - lines.push(''); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); } // Fetch function - lines.push(`/**`); - lines.push(` * Fetch a single ${typeName} without React hooks`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * const data = await fetch${ucFirst(singularName)}Query({`); - lines.push(` * ${pkFieldName}: 'some-id',`); - lines.push(` * selection: { fields: { id: true } },`); - lines.push(` * });`); - lines.push(` * \`\`\``); - lines.push(` */`); - lines.push(`export async function fetch${ucFirst(singularName)}Query(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} }`); - lines.push(`): Promise<${singleResultType('S')}>;`); - lines.push(`export async function fetch${ucFirst(singularName)}Query(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} },`); - lines.push(`): Promise<${singleResultType('typeof defaultSelect')}>;`); - lines.push(`export async function fetch${ucFirst(singularName)}Query(`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> },`); - lines.push(`) {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); - lines.push(` return getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap();`); - lines.push(`}`); - lines.push(''); + const fetchFnName = `fetch${ucFirst(singularName)}Query`; + { + // Overload 1: with fields + const f1Props = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))) + ]; + const f1Decl = exportAsyncDeclareFunction( + fetchFnName, + createSTypeParam(selectTypeName), + [createFunctionParam('params', t.tsTypeLiteral(f1Props))], + typeRef('Promise', [singleResultTypeAST(sRef())]) + ); + addJSDocComment(f1Decl, [ + `Fetch a single ${typeName} without React hooks`, + '', + '@example', + '```ts', + `const data = await ${fetchFnName}({`, + ` ${pkFieldName}: 'some-id',`, + ' selection: { fields: { id: true } },', + '});', + '```' + ]); + statements.push(f1Decl); + + // Overload 2: without fields + const f2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsSelectionType()) + ); + f2SelProp.optional = true; + const f2Props = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + f2SelProp + ]; + statements.push( + exportAsyncDeclareFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral(f2Props))], + typeRef('Promise', [singleResultTypeAST(typeofRef('defaultSelect'))]) + ) + ); + + // Implementation + const fImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + ); + fImplSelProp.optional = true; + const fImplProps = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + fImplSelProp + ]; + const fBody: t.Statement[] = []; + const fArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ + t.memberExpression(t.identifier('params'), t.identifier('selection')) + ]); + // @ts-ignore + fArgsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + fBody.push(constDecl('args', fArgsCall)); + fBody.push(t.returnStatement(buildFindOneCallExpr(singularName, pkFieldName, 'args', selectTypeName))); + statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], fBody)); + } // Prefetch function if (reactQueryEnabled) { - lines.push(`/**`); - lines.push(` * Prefetch a single ${typeName} for SSR or cache warming`); - lines.push(` *`); - lines.push(` * @example`); - lines.push(` * \`\`\`ts`); - lines.push(` * await prefetch${ucFirst(singularName)}Query(queryClient, { ${pkFieldName}: 'some-id' });`); - lines.push(` * \`\`\``); - lines.push(` */`); - lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection: ${selectionWithFieldsType('S')} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); - if (hasRelationships && useCentralizedKeys) { - // scope is included in params above - } - lines.push(`): Promise;`); - lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: ${selectionWithoutFieldsType()} }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); - lines.push(`): Promise;`); - lines.push(`export async function prefetch${ucFirst(singularName)}Query(`); - lines.push(` queryClient: QueryClient,`); - lines.push(` params: { ${pkFieldName}: ${pkFieldTsType}; selection?: SelectionConfig<${selectTypeName}> }${hasRelationships && useCentralizedKeys ? ` & { scope?: ${scopeTypeName} }` : ''},`); - lines.push(`): Promise {`); - lines.push(` const args = buildSelectionArgs<${selectTypeName}>(params.selection);`); + const prefetchFnName = `prefetch${ucFirst(singularName)}Query`; - if (hasRelationships && useCentralizedKeys) { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}, params.scope),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } else if (useCentralizedKeys) { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${keysName}.detail(params.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } else { - lines.push(` await queryClient.prefetchQuery({`); - lines.push(` queryKey: ${queryName}QueryKey(params.${pkFieldName}),`); - lines.push(` queryFn: () => getClient().${singularName}.findOne({ ${pkFieldName}: params.${pkFieldName}, ...(args ?? {}), select: (args?.select ?? defaultSelect) as ${selectTypeName} }).unwrap(),`); - lines.push(` });`); - } + // Overload 1: with fields + const p1Props: t.TSPropertySignature[] = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))) + ]; + const p1ParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral(p1Props), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral(p1Props); + const p1Decl = exportAsyncDeclareFunction( + prefetchFnName, + createSTypeParam(selectTypeName), + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p1ParamType)], + typeRef('Promise', [t.tsVoidKeyword()]) + ); + addJSDocComment(p1Decl, [ + `Prefetch a single ${typeName} for SSR or cache warming`, + '', + '@example', + '```ts', + `await ${prefetchFnName}(queryClient, { ${pkFieldName}: 'some-id' });`, + '```' + ]); + statements.push(p1Decl); + + // Overload 2: without fields + const p2SelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withoutFieldsSelectionType()) + ); + p2SelProp.optional = true; + const p2Props: t.TSPropertySignature[] = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + p2SelProp + ]; + const p2ParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral(p2Props), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral(p2Props); + statements.push( + exportAsyncDeclareFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p2ParamType)], + typeRef('Promise', [t.tsVoidKeyword()]) + ) + ); - lines.push(`}`); + // Implementation + const pImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + ); + pImplSelProp.optional = true; + const pImplProps: t.TSPropertySignature[] = [ + t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + pImplSelProp + ]; + const pImplParamType = hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([t.tsTypeLiteral(pImplProps), scopeTypeLiteral(scopeTypeName)]) + : t.tsTypeLiteral(pImplProps); + + const pBody: t.Statement[] = []; + const pArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ + t.memberExpression(t.identifier('params'), t.identifier('selection')) + ]); + // @ts-ignore + pArgsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + pBody.push(constDecl('args', pArgsCall)); + + const queryKeyExpr = hasRelationships && useCentralizedKeys + ? buildDetailQueryKey( + t.memberExpression(t.identifier('params'), t.identifier(pkFieldName)), + t.optionalMemberExpression(t.identifier('params'), t.identifier('scope'), false, true) + ) + : buildDetailQueryKey(t.memberExpression(t.identifier('params'), t.identifier(pkFieldName))); + + const prefetchCall = callExpr( + t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), + [t.objectExpression([ + objectProp('queryKey', queryKeyExpr), + objectProp('queryFn', buildFindOneFn()) + ])] + ); + pBody.push(t.expressionStatement(t.awaitExpression(prefetchCall))); + + statements.push( + exportAsyncFunction( + prefetchFnName, + null, + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType)], + pBody, + t.tsVoidKeyword() + ) + ); } const headerText = reactQueryEnabled ? `Single item query hook for ${typeName}` : `Single item query functions for ${typeName}`; - const content = getGeneratedFileHeader(headerText) + '\n\n' + lines.join('\n') + '\n'; return { fileName: getSingleQueryFileName(table), - content + content: generateHookFileCode(headerText, statements) }; } diff --git a/graphql/codegen/src/core/codegen/selection.ts b/graphql/codegen/src/core/codegen/selection.ts index e205f3ca4..e9cd7fe3c 100644 --- a/graphql/codegen/src/core/codegen/selection.ts +++ b/graphql/codegen/src/core/codegen/selection.ts @@ -1,87 +1,39 @@ /** * Selection helper generator for React Query hooks * - * Generates selection.ts as a shared adapter layer between hook-facing - * `selection` params and ORM-facing args (`select`, `where`, `orderBy`, etc.). + * Uses template-copy pattern: reads hooks-selection.ts from templates/ + * and writes it to the output directory with a generated file header. */ -import { getGeneratedFileHeader } from './utils'; - -/** - * Generate selection.ts content - shared selection types + runtime mappers - */ -export function generateSelectionFile(): string { - const header = getGeneratedFileHeader('Selection helpers for React Query hooks'); - - const code = ` -export interface SelectionConfig { - fields?: TFields; -} +import * as fs from 'fs'; +import * as path from 'path'; -export interface ListSelectionConfig - extends SelectionConfig { - where?: TWhere; - orderBy?: TOrderBy[]; - first?: number; - last?: number; - after?: string; - before?: string; - offset?: number; -} +import { getGeneratedFileHeader } from './utils'; -export function buildSelectionArgs( - selection?: SelectionConfig -): { select?: TFields } | undefined { - if (!selection || selection.fields === undefined) { - return undefined; +function findTemplateFile(templateName: string): string { + const templatePath = path.join(__dirname, 'templates', templateName); + if (fs.existsSync(templatePath)) { + return templatePath; } - - return { select: selection.fields }; + throw new Error( + `Could not find template file: ${templateName}. Searched in: ${templatePath}` + ); } -export function buildListSelectionArgs( - selection?: ListSelectionConfig -): - | { - select?: TFields; - where?: TWhere; - orderBy?: TOrderBy[]; - first?: number; - last?: number; - after?: string; - before?: string; - offset?: number; - } - | undefined { - if (!selection) { - return undefined; - } - - const hasAnyValues = - selection.fields !== undefined || - selection.where !== undefined || - selection.orderBy !== undefined || - selection.first !== undefined || - selection.last !== undefined || - selection.after !== undefined || - selection.before !== undefined || - selection.offset !== undefined; - - if (!hasAnyValues) { - return undefined; - } - - return { - select: selection.fields, - where: selection.where, - orderBy: selection.orderBy, - first: selection.first, - last: selection.last, - after: selection.after, - before: selection.before, - offset: selection.offset - }; +function readTemplateFile(templateName: string, description: string): string { + const templatePath = findTemplateFile(templateName); + let content = fs.readFileSync(templatePath, 'utf-8'); + const headerPattern = + /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/; + content = content.replace( + headerPattern, + getGeneratedFileHeader(description) + '\n' + ); + return content; } -`; - return header + '\n\n' + code.trim() + '\n'; +/** + * Generate selection.ts content - shared selection types + runtime mappers + */ +export function generateSelectionFile(): string { + return readTemplateFile('hooks-selection.ts', 'Selection helpers for React Query hooks'); } diff --git a/graphql/codegen/src/core/codegen/templates/hooks-client.ts b/graphql/codegen/src/core/codegen/templates/hooks-client.ts new file mode 100644 index 000000000..197fad4c5 --- /dev/null +++ b/graphql/codegen/src/core/codegen/templates/hooks-client.ts @@ -0,0 +1,49 @@ +/** + * ORM client wrapper for React Query hooks + * + * This is the RUNTIME code that gets copied to generated output. + * Provides configure/getClient singleton for React Query hooks to access the ORM. + * + * NOTE: This file is read at codegen time and written to output. + * Any changes here will affect all generated hook clients. + */ + +import { createClient } from '../orm'; +import type { OrmClientConfig } from '../orm/client'; + +export type { OrmClientConfig } from '../orm/client'; +export type { GraphQLAdapter, GraphQLError, QueryResult } from '../orm/client'; +export { GraphQLRequestError } from '../orm/client'; + +type OrmClientInstance = ReturnType; +let client: OrmClientInstance | null = null; + +/** + * Configure the ORM client for React Query hooks + * + * @example + * ```ts + * import { configure } from './generated/hooks'; + * + * configure({ + * endpoint: 'https://api.example.com/graphql', + * headers: { Authorization: 'Bearer ' }, + * }); + * ``` + */ +export function configure(config: OrmClientConfig): void { + client = createClient(config); +} + +/** + * Get the configured ORM client instance + * @throws Error if configure() has not been called + */ +export function getClient(): OrmClientInstance { + if (!client) { + throw new Error( + 'ORM client not configured. Call configure() before using hooks.' + ); + } + return client; +} diff --git a/graphql/codegen/src/core/codegen/templates/hooks-selection.ts b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts new file mode 100644 index 000000000..4e2741476 --- /dev/null +++ b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts @@ -0,0 +1,79 @@ +/** + * Selection helpers for React Query hooks + * + * This is the RUNTIME code that gets copied to generated output. + * Provides selection types and runtime mappers between hook-facing + * `selection` params and ORM-facing args (`select`, `where`, `orderBy`, etc.). + * + * NOTE: This file is read at codegen time and written to output. + * Any changes here will affect all generated hook selection helpers. + */ + +export interface SelectionConfig { + fields?: TFields; +} + +export interface ListSelectionConfig + extends SelectionConfig { + where?: TWhere; + orderBy?: TOrderBy[]; + first?: number; + last?: number; + after?: string; + before?: string; + offset?: number; +} + +export function buildSelectionArgs( + selection?: SelectionConfig +): { select?: TFields } | undefined { + if (!selection || selection.fields === undefined) { + return undefined; + } + + return { select: selection.fields }; +} + +export function buildListSelectionArgs( + selection?: ListSelectionConfig +): + | { + select?: TFields; + where?: TWhere; + orderBy?: TOrderBy[]; + first?: number; + last?: number; + after?: string; + before?: string; + offset?: number; + } + | undefined { + if (!selection) { + return undefined; + } + + const hasAnyValues = + selection.fields !== undefined || + selection.where !== undefined || + selection.orderBy !== undefined || + selection.first !== undefined || + selection.last !== undefined || + selection.after !== undefined || + selection.before !== undefined || + selection.offset !== undefined; + + if (!hasAnyValues) { + return undefined; + } + + return { + select: selection.fields, + where: selection.where, + orderBy: selection.orderBy, + first: selection.first, + last: selection.last, + after: selection.after, + before: selection.before, + offset: selection.offset + }; +} From 13c06dfc86730a222a63ab5b62f193cfaef57b41 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sat, 7 Feb 2026 22:51:28 +0000 Subject: [PATCH 08/23] refactor(cli): simplify arg parsing with generic transforms, remove --browser-compatible - Add hyphenateKeys, flattenDbFields, buildDbConfig, seedArgvFromConfig, buildGenerateOptions to shared.ts as generic reusable utilities - Replace manual cliOverrides/hasNonInteractiveArgs with prompt->camelize->generate flow - Remove deprecated --browser-compatible flag from CLI, config types, and generate - Simplify both CLI entry points (graphql/codegen/src/cli, packages/cli) - Update test mocks to match new shared utility imports --- graphql/codegen/src/cli/index.ts | 127 ++++--------------------- graphql/codegen/src/cli/shared.ts | 95 ++++++++++++------ graphql/codegen/src/core/generate.ts | 7 -- graphql/codegen/src/index.ts | 13 ++- graphql/codegen/src/types/config.ts | 9 -- packages/cli/__tests__/codegen.test.ts | 16 +++- packages/cli/src/commands/codegen.ts | 113 +++------------------- 7 files changed, 118 insertions(+), 262 deletions(-) diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index ad499af48..cfea02a20 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -5,17 +5,18 @@ * This is a thin wrapper around the core generate() function. * All business logic is in the core modules. */ -import { CLI, CLIOptions, getPackageJson,Inquirerer } from 'inquirerer'; +import { CLI, CLIOptions, getPackageJson, Inquirerer } from 'inquirerer'; import { findConfigFile, loadConfigFile } from '../core/config'; import { generate } from '../core/generate'; import type { GraphQLSDKConfigTarget } from '../types/config'; import { + buildDbConfig, + buildGenerateOptions, camelizeArgv, - type CodegenAnswers, codegenQuestions, printResult, - splitCommas + seedArgvFromConfig } from './shared'; const usage = ` @@ -39,7 +40,6 @@ Generator Options: -o, --output Output directory -t, --target Target name (for multi-target configs) -a, --authorization Authorization header value - --browser-compatible Deprecated no-op (retained for compatibility) --dry-run Preview without writing files -v, --verbose Show detailed output @@ -63,50 +63,16 @@ export const commands = async ( process.exit(0); } - const hasSourceCliFlags = Boolean( - argv.endpoint || - argv.e || - argv['schema-file'] || - argv.s || - argv.schemas || - argv['api-names'] + const hasSourceFlags = Boolean( + argv.endpoint || argv.e || argv['schema-file'] || argv.s || + argv.schemas || argv['api-names'] ); const explicitConfigPath = (argv.config || argv.c) as string | undefined; - const autoConfigPath = !explicitConfigPath && !hasSourceCliFlags - ? findConfigFile() - : undefined; - const configPath = (explicitConfigPath || autoConfigPath) as string | undefined; + const configPath = explicitConfigPath || (!hasSourceFlags ? findConfigFile() : undefined); const targetName = (argv.target || argv.t) as string | undefined; - // Collect CLI flags that should override config file settings - const cliOverrides: Partial = {}; - const endpoint = (argv.endpoint || argv.e) as string | undefined; - const schemaFile = (argv['schema-file'] || argv.s) as string | undefined; - const schemas = splitCommas(argv.schemas as string | undefined); - const apiNames = splitCommas(argv['api-names'] as string | undefined); - if (endpoint) { - cliOverrides.endpoint = endpoint; - cliOverrides.schemaFile = undefined; - cliOverrides.db = undefined; - } - if (schemaFile) { - cliOverrides.schemaFile = schemaFile; - cliOverrides.endpoint = undefined; - cliOverrides.db = undefined; - } - if (schemas || apiNames) { - cliOverrides.db = { schemas, apiNames }; - cliOverrides.endpoint = undefined; - cliOverrides.schemaFile = undefined; - } - if (argv['react-query'] === true || argv.reactQuery === true) cliOverrides.reactQuery = true; - if (argv.orm === true) cliOverrides.orm = true; - if (argv.verbose === true || argv.v === true) cliOverrides.verbose = true; - if (argv['dry-run'] === true || argv.dryRun === true) cliOverrides.dryRun = true; - if (argv.output || argv.o) cliOverrides.output = (argv.output || argv.o) as string; - if (argv.authorization || argv.a) cliOverrides.authorization = (argv.authorization || argv.a) as string; - - // If config file exists, load and run + let fileConfig: GraphQLSDKConfigTarget = {}; + if (configPath) { const loaded = await loadConfigFile(configPath); if (!loaded.success) { @@ -115,12 +81,9 @@ export const commands = async ( } const config = loaded.config as Record; - - // Check if it's a multi-target config (no source fields at top level) const isMulti = !('endpoint' in config || 'schemaFile' in config || 'db' in config); if (isMulti) { - // Multi-target: simple for loop over targets const targets = config as Record; const names = targetName ? [targetName] : Object.keys(targets); @@ -129,10 +92,11 @@ export const commands = async ( process.exit(1); } + const cliOptions = buildDbConfig(camelizeArgv(argv as Record)); let hasError = false; for (const name of names) { console.log(`\n[${name}]`); - const result = await generate({ ...targets[name], ...cliOverrides }); + const result = await generate({ ...targets[name], ...cliOptions } as GraphQLSDKConfigTarget); printResult(result); if (!result.success) hasError = true; } @@ -142,68 +106,13 @@ export const commands = async ( return argv; } - // Single config — merge CLI overrides - const result = await generate({ ...(config as GraphQLSDKConfigTarget), ...cliOverrides }); - printResult(result); - if (!result.success) process.exit(1); - prompter.close(); - return argv; + fileConfig = config as GraphQLSDKConfigTarget; } - const hasNonInteractiveArgs = Boolean( - endpoint || - schemaFile || - schemas || - apiNames || - argv['react-query'] === true || - argv.reactQuery === true || - argv.orm === true || - argv.output || - argv.o || - argv.authorization || - argv.a || - argv['dry-run'] === true || - argv.dryRun === true || - argv.verbose === true || - argv.v === true || - argv['browser-compatible'] !== undefined || - argv.browserCompatible !== undefined - ); - - if (hasNonInteractiveArgs) { - const result = await generate({ - ...cliOverrides - }); - printResult(result); - prompter.close(); - return argv; - } - - // No config file - prompt for options using shared questions - const answers = await prompter.prompt(argv as CodegenAnswers, codegenQuestions); - - // Convert kebab-case CLI args to camelCase for internal use - const camelized = camelizeArgv(answers) as CodegenAnswers; - - // Build db config if schemas or apiNames provided - const db = (camelized.schemas || camelized.apiNames) ? { - schemas: camelized.schemas, - apiNames: camelized.apiNames - } : undefined; - - const result = await generate({ - endpoint: camelized.endpoint, - schemaFile: camelized.schemaFile, - db, - output: camelized.output, - authorization: camelized.authorization, - reactQuery: camelized.reactQuery, - orm: camelized.orm, - browserCompatible: camelized.browserCompatible, - dryRun: camelized.dryRun, - verbose: camelized.verbose - }); - + const seeded = seedArgvFromConfig(argv, fileConfig); + const answers = await prompter.prompt(seeded, codegenQuestions); + const options = buildGenerateOptions(answers, fileConfig); + const result = await generate(options); printResult(result); prompter.close(); return argv; @@ -222,7 +131,7 @@ export const options: Partial = { v: 'verbose' }, boolean: [ - 'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db', 'browser-compatible' + 'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db' ], string: [ 'config', 'endpoint', 'schema-file', 'output', 'target', 'authorization', diff --git a/graphql/codegen/src/cli/shared.ts b/graphql/codegen/src/cli/shared.ts index 87a55bac9..26370919b 100644 --- a/graphql/codegen/src/cli/shared.ts +++ b/graphql/codegen/src/cli/shared.ts @@ -1,27 +1,21 @@ /** * Shared CLI utilities for graphql-codegen * - * These are exported so that packages/cli can use the same questions - * and types, ensuring consistency between the two CLIs. + * These are exported so that packages/cli can use the same questions, + * types, and transform utilities, ensuring consistency between the two CLIs. */ import { camelize } from 'inflekt'; import { inflektTree } from 'inflekt/transform-keys'; import type { Question } from 'inquirerer'; import type { GenerateResult } from '../core/generate'; +import type { GraphQLSDKConfigTarget } from '../types/config'; -/** - * Sanitize function that splits comma-separated strings into arrays - */ export const splitCommas = (input: string | undefined): string[] | undefined => { if (!input) return undefined; return input.split(',').map((s) => s.trim()).filter(Boolean); }; -/** - * Interface for codegen CLI answers - * CLI accepts kebab-case arguments, converted to camelCase for internal use - */ export interface CodegenAnswers { endpoint?: string; schemaFile?: string; @@ -30,15 +24,11 @@ export interface CodegenAnswers { apiNames?: string[]; reactQuery?: boolean; orm?: boolean; - browserCompatible?: boolean; authorization?: string; dryRun?: boolean; verbose?: boolean; } -/** - * Questions for the codegen CLI - */ export const codegenQuestions: Question[] = [ { name: 'endpoint', @@ -90,14 +80,6 @@ export const codegenQuestions: Question[] = [ default: false, useDefault: true }, - { - name: 'browser-compatible', - message: 'Browser-compatible mode (deprecated no-op)?', - type: 'confirm', - required: false, - default: true, - useDefault: true - }, { name: 'authorization', message: 'Authorization header value', @@ -122,9 +104,6 @@ export const codegenQuestions: Question[] = [ } ]; -/** - * Print the result of a generate operation - */ export function printResult(result: GenerateResult): void { if (result.success) { console.log('[ok]', result.message); @@ -138,15 +117,69 @@ export function printResult(result: GenerateResult): void { } } +// ============================================================================ +// Key transform utilities +// ============================================================================ + const isTopLevel = (_key: string, path: string[]) => path.length === 0; -export const camelizeArgv = (argv: Record) => +const skipNonTopLevel = (key: string, path: string[]) => + !isTopLevel(key, path) || key === '_' || key.startsWith('_'); + +export const camelizeArgv = (argv: Record): Record => inflektTree(argv, (key) => { - // inflection.camelize expects underscores, so replace hyphens first const underscored = key.replace(/-/g, '_'); return camelize(underscored, true); - }, { - skip: (key, path) => - !isTopLevel(key, path) || - key === '_' || - key.startsWith('_') + }, { skip: skipNonTopLevel }); + +export const hyphenateKeys = (obj: Record): Record => + inflektTree(obj, (key) => key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()), { + skip: skipNonTopLevel }); + +// ============================================================================ +// Config <-> CLI shape transforms +// ============================================================================ + +export function filterDefined(obj: Record): Record { + return Object.fromEntries( + Object.entries(obj).filter(([, v]) => v !== undefined && v !== null) + ); +} + +export function flattenDbFields(config: Record): Record { + const { db, ...rest } = config; + if (db && typeof db === 'object') { + const dbObj = db as Record; + const schemas = Array.isArray(dbObj.schemas) ? dbObj.schemas.join(',') : dbObj.schemas; + const apiNames = Array.isArray(dbObj.apiNames) ? dbObj.apiNames.join(',') : dbObj.apiNames; + return { ...rest, schemas, apiNames }; + } + return rest; +} + +export function buildDbConfig(options: Record): Record { + const { schemas, apiNames, ...rest } = options; + if (schemas || apiNames) { + return { ...rest, db: { schemas, apiNames } }; + } + return rest; +} + +export function seedArgvFromConfig( + argv: Record, + fileConfig: GraphQLSDKConfigTarget +): Record { + const flat = flattenDbFields(fileConfig as Record); + const hyphenated = hyphenateKeys(flat); + const defined = filterDefined(hyphenated); + return { ...defined, ...argv }; +} + +export function buildGenerateOptions( + answers: Record, + fileConfig: GraphQLSDKConfigTarget = {} +): GraphQLSDKConfigTarget { + const camelized = camelizeArgv(answers); + const withDb = buildDbConfig(camelized); + return { ...fileConfig, ...withDb } as GraphQLSDKConfigTarget; +} diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index f3f63b948..e73b34edc 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -43,13 +43,6 @@ export async function generate(options: GenerateOptions = {}): Promise { splitCommas: splitCommasMock, codegenQuestions: [ { name: 'endpoint', message: 'GraphQL endpoint URL', type: 'text', required: false }, - { name: 'schemaFile', message: 'Path to GraphQL schema file', type: 'text', required: false }, + { name: 'schema-file', message: 'Path to GraphQL schema file', type: 'text', required: false }, { name: 'output', message: 'Output directory', type: 'text', required: false, default: 'codegen', useDefault: true }, { name: 'schemas', message: 'PostgreSQL schemas', type: 'text', required: false, sanitize: splitCommasMock }, - { name: 'apiNames', message: 'API names', type: 'text', required: false, sanitize: splitCommasMock }, - { name: 'reactQuery', message: 'Generate React Query hooks?', type: 'confirm', required: false, default: false, useDefault: true }, + { name: 'api-names', message: 'API names', type: 'text', required: false, sanitize: splitCommasMock }, + { name: 'react-query', message: 'Generate React Query hooks?', type: 'confirm', required: false, default: false, useDefault: true }, { name: 'orm', message: 'Generate ORM client?', type: 'confirm', required: false, default: false, useDefault: true }, { name: 'authorization', message: 'Authorization header value', type: 'text', required: false }, - { name: 'dryRun', message: 'Preview without writing files?', type: 'confirm', required: false, default: false, useDefault: true }, + { name: 'dry-run', message: 'Preview without writing files?', type: 'confirm', required: false, default: false, useDefault: true }, { name: 'verbose', message: 'Verbose output?', type: 'confirm', required: false, default: false, useDefault: true }, ], printResult: jest.fn((result: any) => { @@ -38,6 +38,14 @@ jest.mock('@constructive-io/graphql-codegen', () => { } }), camelizeArgv: jest.fn((argv: Record) => argv), + seedArgvFromConfig: jest.fn((argv: Record, _fileConfig: any) => argv), + buildGenerateOptions: jest.fn((answers: Record, _fileConfig: any) => { + const { schemas, apiNames, ...rest } = answers; + if (schemas || apiNames) { + return { ...rest, db: { schemas, apiNames } }; + } + return rest; + }), }; }) diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index d532b946f..391807fb0 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -5,9 +5,8 @@ import { loadConfigFile, codegenQuestions, printResult, - camelizeArgv, - splitCommas, - type CodegenAnswers, + buildGenerateOptions, + seedArgvFromConfig, type GraphQLSDKConfigTarget, } from '@constructive-io/graphql-codegen'; @@ -30,7 +29,6 @@ Generator Options: --orm Generate ORM client --output Output directory (default: codegen) --authorization Authorization header value - --browser-compatible Deprecated no-op (retained for compatibility) --dry-run Preview without writing files --verbose Verbose output @@ -47,52 +45,13 @@ export default async ( process.exit(0); } - const hasSourceCliFlags = Boolean( - argv.endpoint || - argv['schema-file'] || - argv.schemaFile || - argv.schemas || - argv['api-names'] || - argv.apiNames + const hasSourceFlags = Boolean( + argv.endpoint || argv['schema-file'] || argv.schemas || argv['api-names'] ); - const explicitConfigPath = argv.config as string | undefined; - const autoConfigPath = !explicitConfigPath && !hasSourceCliFlags - ? findConfigFile() - : undefined; - const configPath = explicitConfigPath || autoConfigPath; + const configPath = (argv.config as string | undefined) || + (!hasSourceFlags ? findConfigFile() : undefined); - const endpoint = argv.endpoint as string | undefined; - const schemaFile = (argv['schema-file'] || argv.schemaFile) as string | undefined; - const schemas = splitCommas(argv.schemas as string | undefined); - const apiNames = splitCommas((argv['api-names'] || argv.apiNames) as string | undefined); - - const cliOverrides: Partial = {}; - if (endpoint) { - cliOverrides.endpoint = endpoint; - cliOverrides.schemaFile = undefined; - cliOverrides.db = undefined; - } - if (schemaFile) { - cliOverrides.schemaFile = schemaFile; - cliOverrides.endpoint = undefined; - cliOverrides.db = undefined; - } - if (schemas || apiNames) { - cliOverrides.db = { schemas, apiNames }; - cliOverrides.endpoint = undefined; - cliOverrides.schemaFile = undefined; - } - if (argv['react-query'] === true || argv.reactQuery === true) cliOverrides.reactQuery = true; - if (argv.orm === true) cliOverrides.orm = true; - if (argv.verbose === true) cliOverrides.verbose = true; - if (argv['dry-run'] === true || argv.dryRun === true) cliOverrides.dryRun = true; - if (argv.output) cliOverrides.output = argv.output as string; - if (argv.authorization) cliOverrides.authorization = argv.authorization as string; - if (argv['browser-compatible'] !== undefined) { - cliOverrides.browserCompatible = argv['browser-compatible'] as boolean; - } else if (argv.browserCompatible !== undefined) { - cliOverrides.browserCompatible = argv.browserCompatible as boolean; - } + let fileConfig: GraphQLSDKConfigTarget = {}; if (configPath) { const loaded = await loadConfigFile(configPath); @@ -100,60 +59,12 @@ export default async ( console.error('x', loaded.error); process.exit(1); } - const result = await generate({ - ...(loaded.config as GraphQLSDKConfigTarget), - ...cliOverrides, - }); - printResult(result); - return; - } - - const hasNonInteractiveArgs = Boolean( - endpoint || - schemaFile || - schemas || - apiNames || - argv['react-query'] === true || - argv.reactQuery === true || - argv.orm === true || - argv.output || - argv.authorization || - argv['dry-run'] === true || - argv.dryRun === true || - argv.verbose === true || - argv['browser-compatible'] !== undefined || - argv.browserCompatible !== undefined - ); - - if (hasNonInteractiveArgs) { - const result = await generate({ ...cliOverrides }); - printResult(result); - return; + fileConfig = loaded.config as GraphQLSDKConfigTarget; } - // No config file - prompt for options using shared questions - const answers = await prompter.prompt(argv as CodegenAnswers, codegenQuestions); - // Convert kebab-case CLI args to camelCase for internal use - const camelized = camelizeArgv(answers); - - // Build db config if schemas or apiNames provided - const db = (camelized.schemas || camelized.apiNames) ? { - schemas: camelized.schemas, - apiNames: camelized.apiNames, - } : undefined; - - const result = await generate({ - endpoint: camelized.endpoint, - schemaFile: camelized.schemaFile, - db, - output: camelized.output, - authorization: camelized.authorization, - reactQuery: camelized.reactQuery, - orm: camelized.orm, - browserCompatible: camelized.browserCompatible, - dryRun: camelized.dryRun, - verbose: camelized.verbose, - }); - + const seeded = seedArgvFromConfig(argv as Record, fileConfig); + const answers = await prompter.prompt(seeded, codegenQuestions); + const options = buildGenerateOptions(answers, fileConfig); + const result = await generate(options); printResult(result); }; From 5eb1b8e4f2836d39de506a1288b561d72b01cad8 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sat, 7 Feb 2026 23:45:11 +0000 Subject: [PATCH 09/23] refactor(codegen): remove default selects entirely (Path A) - Remove all defaultSelect constants and overload 2 (without fields) from all hook generators - Remove buildDefaultSelectExpr, buildSelectFallbackExpr, buildDefaultSelectLiteral, getDefaultSelectFieldName - Remove withoutFieldsSelectionType, withoutFieldsListSelectionType, NON_SELECT_TYPES_AST - Remove buildDefaultSelectExpression from ORM custom-ops-generator - Remove defaultSelect/defaultRef from ORM model-generator - Make selection with fields required in all hook and ORM method signatures - Update 25 snapshots to reflect removed overloads --- .../model-generator.test.ts.snap | 167 +---- .../react-query-hooks.test.ts.snap | 668 +++--------------- .../src/core/codegen/custom-mutations.ts | 48 +- .../src/core/codegen/custom-queries.ts | 103 +-- graphql/codegen/src/core/codegen/hooks-ast.ts | 110 +-- graphql/codegen/src/core/codegen/mutations.ts | 95 +-- .../core/codegen/orm/custom-ops-generator.ts | 137 +--- graphql/codegen/src/core/codegen/orm/index.ts | 6 +- .../src/core/codegen/orm/model-generator.ts | 106 +-- graphql/codegen/src/core/codegen/queries.ts | 156 +--- .../src/core/codegen/select-helpers.ts | 41 +- graphql/codegen/src/core/codegen/utils.ts | 30 - 12 files changed, 186 insertions(+), 1481 deletions(-) diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap index 4ed4ce22b..ee27c182a 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/model-generator.test.ts.snap @@ -11,9 +11,6 @@ import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindO import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { User, UserWithRelations, UserSelect, UserFilter, UsersOrderBy, CreateUserInput, UpdateUserInput, UserPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; -const defaultSelect = { - id: true -} as const; export class UserModel { constructor(private client: OrmClient) {} findMany(args: FindManyArgs & { @@ -21,16 +18,11 @@ export class UserModel { } & StrictSelect): QueryBuilder<{ users: ConnectionResult>; }>; - findMany(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - users: ConnectionResult>; - }>; - findMany(args?: FindManyArgs) { + findMany(args: FindManyArgs) { const { document, variables - } = buildFindManyDocument("User", "users", args?.select ?? defaultSelect, { + } = buildFindManyDocument("User", "users", args.select, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -55,18 +47,11 @@ export class UserModel { nodes: InferSelectResult[]; }; }>; - findFirst(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - users: { - nodes: InferSelectResult[]; - }; - }>; - findFirst(args?: FindFirstArgs) { + findFirst(args: FindFirstArgs) { const { document, variables - } = buildFindFirstDocument("User", "users", args?.select ?? defaultSelect, { + } = buildFindFirstDocument("User", "users", args.select, { where: args?.where }, "UserFilter", connectionFieldsMap); return new QueryBuilder({ @@ -86,17 +71,12 @@ export class UserModel { }>; findOne(args: { id: string; - }): QueryBuilder<{ - user: InferSelectResult | null; - }>; - findOne(args: { - id: string; - select?: UserSelect; + select: UserSelect; }) { const { document, variables - } = buildFindOneDocument("User", "user", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); + } = buildFindOneDocument("User", "user", args.id, args.select, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -113,18 +93,11 @@ export class UserModel { user: InferSelectResult; }; }>; - create(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - createUser: { - user: InferSelectResult; - }; - }>; create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("User", "createUser", "user", args.select ?? defaultSelect, args.data, "CreateUserInput", connectionFieldsMap); + } = buildCreateDocument("User", "createUser", "user", args.select, args.data, "CreateUserInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -143,22 +116,13 @@ export class UserModel { user: InferSelectResult; }; }>; - update(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - updateUser: { - user: InferSelectResult; - }; - }>; update(args: UpdateArgs) { const { document, variables - } = buildUpdateByPkDocument("User", "updateUser", "user", args.select ?? defaultSelect, args.where.id, args.data, "UpdateUserInput", "id", connectionFieldsMap); + } = buildUpdateByPkDocument("User", "updateUser", "user", args.select, args.where.id, args.data, "UpdateUserInput", "id", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -177,22 +141,13 @@ export class UserModel { user: InferSelectResult; }; }>; - delete(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - deleteUser: { - user: InferSelectResult; - }; - }>; delete(args: DeleteArgs<{ id: string; }, UserSelect>) { const { document, variables - } = buildDeleteByPkDocument("User", "deleteUser", "user", args.where.id, "DeleteUserInput", "id", args.select ?? defaultSelect, connectionFieldsMap); + } = buildDeleteByPkDocument("User", "deleteUser", "user", args.where.id, "DeleteUserInput", "id", args.select, connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -216,9 +171,6 @@ import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindO import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { AuditLog, AuditLogWithRelations, AuditLogSelect, AuditLogFilter, AuditLogsOrderBy, CreateAuditLogInput, UpdateAuditLogInput, AuditLogPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; -const defaultSelect = { - id: true -} as const; export class AuditLogModel { constructor(private client: OrmClient) {} findMany(args: FindManyArgs & { @@ -226,16 +178,11 @@ export class AuditLogModel { } & StrictSelect): QueryBuilder<{ auditLogs: ConnectionResult>; }>; - findMany(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - auditLogs: ConnectionResult>; - }>; - findMany(args?: FindManyArgs) { + findMany(args: FindManyArgs) { const { document, variables - } = buildFindManyDocument("AuditLog", "auditLogs", args?.select ?? defaultSelect, { + } = buildFindManyDocument("AuditLog", "auditLogs", args.select, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -260,18 +207,11 @@ export class AuditLogModel { nodes: InferSelectResult[]; }; }>; - findFirst(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - auditLogs: { - nodes: InferSelectResult[]; - }; - }>; - findFirst(args?: FindFirstArgs) { + findFirst(args: FindFirstArgs) { const { document, variables - } = buildFindFirstDocument("AuditLog", "auditLogs", args?.select ?? defaultSelect, { + } = buildFindFirstDocument("AuditLog", "auditLogs", args.select, { where: args?.where }, "AuditLogFilter", connectionFieldsMap); return new QueryBuilder({ @@ -291,17 +231,12 @@ export class AuditLogModel { }>; findOne(args: { id: string; - }): QueryBuilder<{ - auditLog: InferSelectResult | null; - }>; - findOne(args: { - id: string; - select?: AuditLogSelect; + select: AuditLogSelect; }) { const { document, variables - } = buildFindOneDocument("AuditLog", "auditLog", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); + } = buildFindOneDocument("AuditLog", "auditLog", args.id, args.select, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -318,18 +253,11 @@ export class AuditLogModel { auditLog: InferSelectResult; }; }>; - create(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - createAuditLog: { - auditLog: InferSelectResult; - }; - }>; create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select ?? defaultSelect, args.data, "CreateAuditLogInput", connectionFieldsMap); + } = buildCreateDocument("AuditLog", "createAuditLog", "auditLog", args.select, args.data, "CreateAuditLogInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -353,9 +281,6 @@ import { QueryBuilder, buildFindManyDocument, buildFindFirstDocument, buildFindO import type { ConnectionResult, FindManyArgs, FindFirstArgs, CreateArgs, UpdateArgs, DeleteArgs, InferSelectResult, StrictSelect } from "../select-types"; import type { Organization, OrganizationWithRelations, OrganizationSelect, OrganizationFilter, OrganizationsOrderBy, CreateOrganizationInput, UpdateOrganizationInput, OrganizationPatch } from "../input-types"; import { connectionFieldsMap } from "../input-types"; -const defaultSelect = { - id: true -} as const; export class OrganizationModel { constructor(private client: OrmClient) {} findMany(args: FindManyArgs & { @@ -363,16 +288,11 @@ export class OrganizationModel { } & StrictSelect): QueryBuilder<{ allOrganizations: ConnectionResult>; }>; - findMany(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - allOrganizations: ConnectionResult>; - }>; - findMany(args?: FindManyArgs) { + findMany(args: FindManyArgs) { const { document, variables - } = buildFindManyDocument("Organization", "allOrganizations", args?.select ?? defaultSelect, { + } = buildFindManyDocument("Organization", "allOrganizations", args.select, { where: args?.where, orderBy: args?.orderBy as string[] | undefined, first: args?.first, @@ -397,18 +317,11 @@ export class OrganizationModel { nodes: InferSelectResult[]; }; }>; - findFirst(args?: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - allOrganizations: { - nodes: InferSelectResult[]; - }; - }>; - findFirst(args?: FindFirstArgs) { + findFirst(args: FindFirstArgs) { const { document, variables - } = buildFindFirstDocument("Organization", "allOrganizations", args?.select ?? defaultSelect, { + } = buildFindFirstDocument("Organization", "allOrganizations", args.select, { where: args?.where }, "OrganizationFilter", connectionFieldsMap); return new QueryBuilder({ @@ -428,17 +341,12 @@ export class OrganizationModel { }>; findOne(args: { id: string; - }): QueryBuilder<{ - organizationById: InferSelectResult | null; - }>; - findOne(args: { - id: string; - select?: OrganizationSelect; + select: OrganizationSelect; }) { const { document, variables - } = buildFindOneDocument("Organization", "organizationById", args.id, args.select ?? defaultSelect, "id", "UUID!", connectionFieldsMap); + } = buildFindOneDocument("Organization", "organizationById", args.id, args.select, "id", "UUID!", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "query", @@ -455,18 +363,11 @@ export class OrganizationModel { organization: InferSelectResult; }; }>; - create(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - registerOrganization: { - organization: InferSelectResult; - }; - }>; create(args: CreateArgs) { const { document, variables - } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select ?? defaultSelect, args.data, "CreateOrganizationInput", connectionFieldsMap); + } = buildCreateDocument("Organization", "registerOrganization", "organization", args.select, args.data, "CreateOrganizationInput", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -485,22 +386,13 @@ export class OrganizationModel { organization: InferSelectResult; }; }>; - update(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - modifyOrganization: { - organization: InferSelectResult; - }; - }>; update(args: UpdateArgs) { const { document, variables - } = buildUpdateByPkDocument("Organization", "modifyOrganization", "organization", args.select ?? defaultSelect, args.where.id, args.data, "UpdateOrganizationInput", "id", connectionFieldsMap); + } = buildUpdateByPkDocument("Organization", "modifyOrganization", "organization", args.select, args.where.id, args.data, "UpdateOrganizationInput", "id", connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", @@ -519,22 +411,13 @@ export class OrganizationModel { organization: InferSelectResult; }; }>; - delete(args: Omit, "select"> & { - select?: undefined; - }): QueryBuilder<{ - removeOrganization: { - organization: InferSelectResult; - }; - }>; delete(args: DeleteArgs<{ id: string; }, OrganizationSelect>) { const { document, variables - } = buildDeleteByPkDocument("Organization", "removeOrganization", "organization", args.where.id, "DeleteOrganizationInput", "id", args.select ?? defaultSelect, connectionFieldsMap); + } = buildDeleteByPkDocument("Organization", "removeOrganization", "organization", args.where.id, "DeleteOrganizationInput", "id", args.select, connectionFieldsMap); return new QueryBuilder({ client: this.client, operation: "mutation", diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index 68b86cdec..fd0eeef96 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -198,9 +198,6 @@ import type { LoginPayloadSelect, LoginPayload } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { LoginVariables } from "../../orm/mutation"; export type { LoginPayloadSelect } from "../../orm/input-types"; -const defaultSelect = { - token: true -} as const; export function useLoginMutation(params: { selection: ({ fields: S; @@ -211,16 +208,7 @@ export function useLoginMutation(params: { login: InferSelectResult; }, Error, LoginVariables>; export function useLoginMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ - login: InferSelectResult; -}, Error, LoginVariables>; -export function useLoginMutation(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -231,7 +219,7 @@ export function useLoginMutation(params?: { return useMutation({ mutationKey: customMutationKeys.login(), mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { - select: (args?.select ?? defaultSelect) as LoginPayloadSelect + select: args.select }).unwrap(), ...mutationOptions }); @@ -257,9 +245,6 @@ import type { RegisterPayloadSelect, RegisterPayload } from "../../orm/input-typ import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { RegisterVariables } from "../../orm/mutation"; export type { RegisterPayloadSelect } from "../../orm/input-types"; -const defaultSelect = { - token: true -} as const; export function useRegisterMutation(params: { selection: ({ fields: S; @@ -270,16 +255,7 @@ export function useRegisterMutation(params: { register: InferSelectResult; }, Error, RegisterVariables>; export function useRegisterMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, RegisterVariables>, "mutationFn">): UseMutationResult<{ - register: InferSelectResult; -}, Error, RegisterVariables>; -export function useRegisterMutation(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -290,7 +266,7 @@ export function useRegisterMutation(params?: { return useMutation({ mutationKey: customMutationKeys.register(), mutationFn: (variables: RegisterVariables) => getClient().mutation.register(variables, { - select: (args?.select ?? defaultSelect) as RegisterPayloadSelect + select: args.select }).unwrap(), ...mutationOptions }); @@ -314,9 +290,6 @@ import { customMutationKeys } from "../mutation-keys"; import type { LogoutPayloadSelect, LogoutPayload } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { LogoutPayloadSelect } from "../../orm/input-types"; -const defaultSelect = { - success: true -} as const; export function useLogoutMutation(params: { selection: ({ fields: S; @@ -327,16 +300,7 @@ export function useLogoutMutation(params: { logout: InferSelectResult; }, Error, void>; export function useLogoutMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, void>, "mutationFn">): UseMutationResult<{ - logout: InferSelectResult; -}, Error, void>; -export function useLogoutMutation(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -347,7 +311,7 @@ export function useLogoutMutation(params?: { return useMutation({ mutationKey: customMutationKeys.logout(), mutationFn: () => getClient().mutation.logout({ - select: (args?.select ?? defaultSelect) as LogoutPayloadSelect + select: args.select }).unwrap(), ...mutationOptions }); @@ -372,9 +336,6 @@ import type { LoginPayloadSelect, LoginPayload } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { LoginVariables } from "../../orm/mutation"; export type { LoginPayloadSelect } from "../../orm/input-types"; -const defaultSelect = { - token: true -} as const; export function useLoginMutation(params: { selection: ({ fields: S; @@ -385,16 +346,7 @@ export function useLoginMutation(params: { login: InferSelectResult; }, Error, LoginVariables>; export function useLoginMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ - login: InferSelectResult; -}, Error, LoginVariables>; -export function useLoginMutation(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -404,7 +356,7 @@ export function useLoginMutation(params?: { void _selection; return useMutation({ mutationFn: (variables: LoginVariables) => getClient().mutation.login(variables, { - select: (args?.select ?? defaultSelect) as LoginPayloadSelect + select: args.select }).unwrap(), ...mutationOptions }); @@ -430,9 +382,6 @@ import type { UserSelect, User } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { SearchUsersVariables } from "../../orm/query"; export type { UserSelect } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const searchUsersQueryKey = customQueryKeys.searchUsers; /** @@ -457,19 +406,9 @@ export function useSearchUsersQuery[]; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useSearchUsersQuery[]; -}>(params: { - variables: SearchUsersVariables; - selection?: ({ - fields?: undefined; - }); -} & Omit[]; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; export function useSearchUsersQuery(params: { variables: SearchUsersVariables; - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); @@ -483,7 +422,7 @@ export function useSearchUsersQuery(params: { return useQuery({ queryKey: searchUsersQueryKey(variables), queryFn: () => getClient().query.searchUsers(variables!, { - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), enabled: !!variables && params?.enabled !== false, ...queryOptions @@ -507,20 +446,12 @@ export async function fetchSearchUsersQuery(params: { }>; export async function fetchSearchUsersQuery(params: { variables: SearchUsersVariables; - selection?: ({ - fields?: undefined; - }); -}): Promise<{ - searchUsers: InferSelectResult[]; -}>; -export async function fetchSearchUsersQuery(params: { - variables: SearchUsersVariables; - selection?: SelectionConfig; + selection: SelectionConfig; }) { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); return getClient().query.searchUsers(variables!, { - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -539,20 +470,14 @@ export async function prefetchSearchUsersQuery(queryClient }): Promise; export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { variables: SearchUsersVariables; - selection?: ({ - fields?: undefined; - }); -}): Promise; -export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { - variables: SearchUsersVariables; - selection?: SelectionConfig; + selection: SelectionConfig; }): void { const variables = params?.variables; const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: searchUsersQueryKey(variables), queryFn: () => getClient().query.searchUsers(variables!, { - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -575,9 +500,6 @@ import { customQueryKeys } from "../query-keys"; import type { UserSelect, User } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const currentUserQueryKey = customQueryKeys.currentUser; /** @@ -601,17 +523,8 @@ export function useCurrentUserQuery; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useCurrentUserQuery; -}>(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; export function useCurrentUserQuery(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -622,7 +535,7 @@ export function useCurrentUserQuery(params?: { return useQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -642,19 +555,12 @@ export async function fetchCurrentUserQuery(params: { }): Promise<{ currentUser: InferSelectResult; }>; -export async function fetchCurrentUserQuery(params?: { - selection?: ({ - fields?: undefined; - }); -}): Promise<{ - currentUser: InferSelectResult; -}>; -export async function fetchCurrentUserQuery(params?: { - selection?: SelectionConfig; +export async function fetchCurrentUserQuery(params: { + selection: SelectionConfig; }) { const args = buildSelectionArgs(params?.selection); return getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -670,19 +576,14 @@ export async function prefetchCurrentUserQuery(queryClient fields: S; } & StrictSelect); }): Promise; -export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { - selection?: ({ - fields?: undefined; - }); -}): Promise; -export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { - selection?: SelectionConfig; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { + selection: SelectionConfig; }): void { const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -704,9 +605,6 @@ import type { SelectionConfig } from "../selection"; import type { UserSelect, User } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory for caching */ export const currentUserQueryKey = () => ["currentUser"] as const; /** @@ -730,17 +628,8 @@ export function useCurrentUserQuery; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useCurrentUserQuery; -}>(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; export function useCurrentUserQuery(params?: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -751,7 +640,7 @@ export function useCurrentUserQuery(params?: { return useQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -771,19 +660,12 @@ export async function fetchCurrentUserQuery(params: { }): Promise<{ currentUser: InferSelectResult; }>; -export async function fetchCurrentUserQuery(params?: { - selection?: ({ - fields?: undefined; - }); -}): Promise<{ - currentUser: InferSelectResult; -}>; -export async function fetchCurrentUserQuery(params?: { - selection?: SelectionConfig; +export async function fetchCurrentUserQuery(params: { + selection: SelectionConfig; }) { const args = buildSelectionArgs(params?.selection); return getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -799,19 +681,14 @@ export async function prefetchCurrentUserQuery(queryClient fields: S; } & StrictSelect); }): Promise; -export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { - selection?: ({ - fields?: undefined; - }); -}): Promise; -export async function prefetchCurrentUserQuery(queryClient: QueryClient, params?: { - selection?: SelectionConfig; +export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { + selection: SelectionConfig; }): void { const args = buildSelectionArgs(params?.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -835,9 +712,6 @@ import { userMutationKeys } from "../mutation-keys"; import type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for creating a User * @@ -863,21 +737,8 @@ export function useCreateUserMutation(params: { user: InferSelectResult; }; }, Error, CreateUserInput["user"]>; -export function useCreateUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ - createUser: { - user: InferSelectResult; - }; -}, Error, CreateUserInput["user"]>; -export function useCreateUserMutation(params?: { - selection?: SelectionConfig; +export function useCreateUserMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -890,7 +751,7 @@ export function useCreateUserMutation(params?: { mutationKey: userMutationKeys.create(), mutationFn: (data: CreateUserInput["user"]) => getClient().user.create({ data, - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ @@ -920,9 +781,6 @@ import { postMutationKeys } from "../mutation-keys"; import type { PostSelect, PostWithRelations, CreatePostInput } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations, CreatePostInput } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for creating a Post * @@ -948,21 +806,8 @@ export function useCreatePostMutation(params: { post: InferSelectResult; }; }, Error, CreatePostInput["post"]>; -export function useCreatePostMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, CreatePostInput["post"]>, "mutationFn">): UseMutationResult<{ - createPost: { - post: InferSelectResult; - }; -}, Error, CreatePostInput["post"]>; -export function useCreatePostMutation(params?: { - selection?: SelectionConfig; +export function useCreatePostMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -975,7 +820,7 @@ export function useCreatePostMutation(params?: { mutationKey: postMutationKeys.create(), mutationFn: (data: CreatePostInput["post"]) => getClient().post.create({ data, - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ @@ -1003,9 +848,6 @@ import type { SelectionConfig } from "../selection"; import type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, CreateUserInput } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for creating a User * @@ -1031,21 +873,8 @@ export function useCreateUserMutation(params: { user: InferSelectResult; }; }, Error, CreateUserInput["user"]>; -export function useCreateUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, CreateUserInput["user"]>, "mutationFn">): UseMutationResult<{ - createUser: { - user: InferSelectResult; - }; -}, Error, CreateUserInput["user"]>; -export function useCreateUserMutation(params?: { - selection?: SelectionConfig; +export function useCreateUserMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { const args = buildSelectionArgs(params?.selection); const { @@ -1057,7 +886,7 @@ export function useCreateUserMutation(params?: { return useMutation({ mutationFn: (data: CreateUserInput["user"]) => getClient().user.create({ data, - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), onSuccess: () => { queryClient.invalidateQueries({ @@ -1087,9 +916,6 @@ import { userMutationKeys } from "../mutation-keys"; import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for deleting a User * @@ -1119,25 +945,8 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; -export function useDeleteUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; -}>, "mutationFn">): UseMutationResult<{ - deleteUser: { - user: InferSelectResult; - }; -}, Error, { - id: string; -}>; -export function useDeleteUserMutation(params?: { - selection?: SelectionConfig; +export function useDeleteUserMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { @@ -1158,7 +967,7 @@ export function useDeleteUserMutation(params?: { where: { id }, - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ @@ -1191,9 +1000,6 @@ import { postMutationKeys } from "../mutation-keys"; import type { PostSelect, PostWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for deleting a Post * @@ -1223,25 +1029,8 @@ export function useDeletePostMutation(params: { }, Error, { id: string; }>; -export function useDeletePostMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; -}>, "mutationFn">): UseMutationResult<{ - deletePost: { - post: InferSelectResult; - }; -}, Error, { - id: string; -}>; -export function useDeletePostMutation(params?: { - selection?: SelectionConfig; +export function useDeletePostMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { @@ -1262,7 +1051,7 @@ export function useDeletePostMutation(params?: { where: { id }, - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ @@ -1293,9 +1082,6 @@ import type { SelectionConfig } from "../selection"; import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for deleting a User * @@ -1325,25 +1111,8 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; -export function useDeleteUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; -}>, "mutationFn">): UseMutationResult<{ - deleteUser: { - user: InferSelectResult; - }; -}, Error, { - id: string; -}>; -export function useDeleteUserMutation(params?: { - selection?: SelectionConfig; +export function useDeleteUserMutation(params: { + selection: SelectionConfig; } & Omit, "mutationFn">) { @@ -1363,7 +1132,7 @@ export function useDeleteUserMutation(params?: { where: { id }, - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), onSuccess: (_, variables) => { queryClient.removeQueries({ @@ -1396,9 +1165,6 @@ import { userMutationKeys } from "../mutation-keys"; import type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for updating a User * @@ -1430,27 +1196,8 @@ export function useUpdateUserMutation(params: { id: string; patch: UserPatch; }>; -export function useUpdateUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; - patch: UserPatch; -}>, "mutationFn">): UseMutationResult<{ - updateUser: { - user: InferSelectResult; - }; -}, Error, { - id: string; - patch: UserPatch; -}>; -export function useUpdateUserMutation(params?: { - selection?: SelectionConfig; +export function useUpdateUserMutation(params: { + selection: SelectionConfig; } & Omit { queryClient.invalidateQueries({ @@ -1508,9 +1255,6 @@ import { postMutationKeys } from "../mutation-keys"; import type { PostSelect, PostWithRelations, PostPatch } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations, PostPatch } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for updating a Post * @@ -1542,27 +1286,8 @@ export function useUpdatePostMutation(params: { id: string; patch: PostPatch; }>; -export function useUpdatePostMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; - patch: PostPatch; -}>, "mutationFn">): UseMutationResult<{ - updatePost: { - post: InferSelectResult; - }; -}, Error, { - id: string; - patch: PostPatch; -}>; -export function useUpdatePostMutation(params?: { - selection?: SelectionConfig; +export function useUpdatePostMutation(params: { + selection: SelectionConfig; } & Omit { queryClient.invalidateQueries({ @@ -1618,9 +1343,6 @@ import type { SelectionConfig } from "../selection"; import type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, UserPatch } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** * Mutation hook for updating a User * @@ -1652,27 +1374,8 @@ export function useUpdateUserMutation(params: { id: string; patch: UserPatch; }>; -export function useUpdateUserMutation(params?: { - selection?: ({ - fields?: undefined; - }); -} & Omit; - }; -}, Error, { - id: string; - patch: UserPatch; -}>, "mutationFn">): UseMutationResult<{ - updateUser: { - user: InferSelectResult; - }; -}, Error, { - id: string; - patch: UserPatch; -}>; -export function useUpdateUserMutation(params?: { - selection?: SelectionConfig; +export function useUpdateUserMutation(params: { + selection: SelectionConfig; } & Omit { queryClient.invalidateQueries({ @@ -1728,9 +1431,6 @@ import { userKeys } from "../query-keys"; import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const usersQueryKey = userKeys.list; /** @@ -1757,19 +1457,10 @@ export function useUsersQuery>; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUsersQuery>; -}>(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -} & Omit>; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUsersQuery(params?: { - selection?: ListSelectionConfig; +export function useUsersQuery(params: { + selection: ListSelectionConfig; } & Omit, "queryKey" | "queryFn">) { - const selection = params?.selection; + const selection = params.selection; const args = buildListSelectionArgs(selection); const { selection: _selection, @@ -1780,7 +1471,7 @@ export function useUsersQuery(params?: { queryKey: userKeys.list(args), queryFn: () => getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -1805,20 +1496,13 @@ export async function fetchUsersQuery(params: { }): Promise<{ users: ConnectionResult>; }>; -export async function fetchUsersQuery(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -}): Promise<{ - users: ConnectionResult>; -}>; -export async function fetchUsersQuery(params?: { - selection?: ListSelectionConfig; +export async function fetchUsersQuery(params: { + selection: ListSelectionConfig; }) { const args = buildListSelectionArgs(selection); return getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -1834,20 +1518,15 @@ export async function prefetchUsersQuery(queryClient: Quer fields: S; } & Omit, "fields"> & StrictSelect; }): Promise; -export async function prefetchUsersQuery(queryClient: QueryClient, params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -}): Promise; -export async function prefetchUsersQuery(queryClient: QueryClient, params?: { - selection?: ListSelectionConfig; +export async function prefetchUsersQuery(queryClient: QueryClient, params: { + selection: ListSelectionConfig; }): void { const args = buildListSelectionArgs(selection); await queryClient.prefetchQuery({ queryKey: userKeys.list(args), queryFn: () => getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -1871,9 +1550,6 @@ import type { PostScope } from "../query-keys"; import type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations, PostFilter, PostsOrderBy } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const postsQueryKey = postKeys.list; /** @@ -1910,23 +1586,12 @@ export function usePostsQuery, "queryKey" | "queryFn"> & { scope?: PostScope; }): UseQueryResult; -export function usePostsQuery>; -}>(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -} & Omit>; -}, Error, TData>, "queryKey" | "queryFn"> & { - scope?: PostScope; -}): UseQueryResult; -export function usePostsQuery(params?: { - selection?: ListSelectionConfig; +export function usePostsQuery(params: { + selection: ListSelectionConfig; } & Omit, "queryKey" | "queryFn"> & { scope?: PostScope; }) { - const selection = params?.selection; + const selection = params.selection; const args = buildListSelectionArgs(selection); const { scope, @@ -1938,7 +1603,7 @@ export function usePostsQuery(params?: { queryKey: postKeys.list(args, scope), queryFn: () => getClient().post.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -1963,20 +1628,13 @@ export async function fetchPostsQuery(params: { }): Promise<{ posts: ConnectionResult>; }>; -export async function fetchPostsQuery(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -}): Promise<{ - posts: ConnectionResult>; -}>; -export async function fetchPostsQuery(params?: { - selection?: ListSelectionConfig; +export async function fetchPostsQuery(params: { + selection: ListSelectionConfig; }) { const args = buildListSelectionArgs(selection); return getClient().post.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(); } /** @@ -1994,15 +1652,8 @@ export async function prefetchPostsQuery(queryClient: Quer } & { scope?: PostScope; }): Promise; -export async function prefetchPostsQuery(queryClient: QueryClient, params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -} & { - scope?: PostScope; -}): Promise; -export async function prefetchPostsQuery(queryClient: QueryClient, params?: { - selection?: ListSelectionConfig; +export async function prefetchPostsQuery(queryClient: QueryClient, params: { + selection: ListSelectionConfig; } & { scope?: PostScope; }): void { @@ -2011,7 +1662,7 @@ export async function prefetchPostsQuery(queryClient: QueryClient, params?: { queryKey: postKeys.list(args, params?.scope), queryFn: () => getClient().post.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap() }); } @@ -2033,9 +1684,6 @@ import type { ListSelectionConfig } from "../selection"; import type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; import type { FindManyArgs, InferSelectResult, ConnectionResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations, UserFilter, UsersOrderBy } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; export const usersQueryKey = (variables?: FindManyArgs) => ["user", "list", variables] as const; /** * Query hook for fetching User list @@ -2061,19 +1709,10 @@ export function useUsersQuery>; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUsersQuery>; -}>(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -} & Omit>; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUsersQuery(params?: { - selection?: ListSelectionConfig; +export function useUsersQuery(params: { + selection: ListSelectionConfig; } & Omit, "queryKey" | "queryFn">) { - const selection = params?.selection; + const selection = params.selection; const args = buildListSelectionArgs(selection); const { selection: _selection, @@ -2084,7 +1723,7 @@ export function useUsersQuery(params?: { queryKey: usersQueryKey(args), queryFn: () => getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -2109,20 +1748,13 @@ export async function fetchUsersQuery(params: { }): Promise<{ users: ConnectionResult>; }>; -export async function fetchUsersQuery(params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -}): Promise<{ - users: ConnectionResult>; -}>; -export async function fetchUsersQuery(params?: { - selection?: ListSelectionConfig; +export async function fetchUsersQuery(params: { + selection: ListSelectionConfig; }) { const args = buildListSelectionArgs(selection); return getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -2138,20 +1770,15 @@ export async function prefetchUsersQuery(queryClient: Quer fields: S; } & Omit, "fields"> & StrictSelect; }): Promise; -export async function prefetchUsersQuery(queryClient: QueryClient, params?: { - selection?: Omit, "fields"> & { - fields?: undefined; - }; -}): Promise; -export async function prefetchUsersQuery(queryClient: QueryClient, params?: { - selection?: ListSelectionConfig; +export async function prefetchUsersQuery(queryClient: QueryClient, params: { + selection: ListSelectionConfig; }): void { const args = buildListSelectionArgs(selection); await queryClient.prefetchQuery({ queryKey: usersQueryKey(args), queryFn: () => getClient().user.findMany({ ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -2174,9 +1801,6 @@ import { userKeys } from "../query-keys"; import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const userQueryKey = userKeys.detail; /** @@ -2200,19 +1824,9 @@ export function useUserQuery | null; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUserQuery | null; -}>(params: { - id: string; - selection?: { - fields?: undefined; - }; -} & Omit | null; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; export function useUserQuery(params: { id: string; - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params.selection); const { @@ -2225,7 +1839,7 @@ export function useUserQuery(params: { queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -2251,21 +1865,13 @@ export async function fetchUserQuery(params: { }>; export async function fetchUserQuery(params: { id: string; - selection?: { - fields?: undefined; - }; -}): Promise<{ - user: InferSelectResult | null; -}>; -export async function fetchUserQuery(params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; }) { const args = buildSelectionArgs(params.selection); return getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -2284,13 +1890,7 @@ export async function prefetchUserQuery(queryClient: Query }): Promise; export async function prefetchUserQuery(queryClient: QueryClient, params: { id: string; - selection?: { - fields?: undefined; - }; -}): Promise; -export async function prefetchUserQuery(queryClient: QueryClient, params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; }): void { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ @@ -2298,7 +1898,7 @@ export async function prefetchUserQuery(queryClient: QueryClient, params: { queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } @@ -2322,9 +1922,6 @@ import type { PostScope } from "../query-keys"; import type { PostSelect, PostWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; /** Query key factory - re-exported from query-keys.ts */ export const postQueryKey = postKeys.detail; /** @@ -2358,21 +1955,9 @@ export function usePostQuery, "queryKey" | "queryFn"> & { scope?: PostScope; }): UseQueryResult; -export function usePostQuery | null; -}>(params: { - id: string; - selection?: { - fields?: undefined; - }; -} & Omit | null; -}, Error, TData>, "queryKey" | "queryFn"> & { - scope?: PostScope; -}): UseQueryResult; export function usePostQuery(params: { id: string; - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn"> & { scope?: PostScope; }) { @@ -2388,7 +1973,7 @@ export function usePostQuery(params: { queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -2414,21 +1999,13 @@ export async function fetchPostQuery(params: { }>; export async function fetchPostQuery(params: { id: string; - selection?: { - fields?: undefined; - }; -}): Promise<{ - post: InferSelectResult | null; -}>; -export async function fetchPostQuery(params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; }) { const args = buildSelectionArgs(params.selection); return getClient().post.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap(); } /** @@ -2449,15 +2026,7 @@ export async function prefetchPostQuery(queryClient: Query }): Promise; export async function prefetchPostQuery(queryClient: QueryClient, params: { id: string; - selection?: { - fields?: undefined; - }; -} & { - scope?: PostScope; -}): Promise; -export async function prefetchPostQuery(queryClient: QueryClient, params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; } & { scope?: PostScope; }): void { @@ -2467,7 +2036,7 @@ export async function prefetchPostQuery(queryClient: QueryClient, params: { queryFn: () => getClient().post.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as PostSelect + select: args.select }).unwrap() }); } @@ -2489,9 +2058,6 @@ import type { SelectionConfig } from "../selection"; import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; -const defaultSelect = { - id: true -} as const; export const userQueryKey = (id: string) => ["user", "detail", id] as const; /** * Query hook for fetching a single User @@ -2514,19 +2080,9 @@ export function useUserQuery | null; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useUserQuery | null; -}>(params: { - id: string; - selection?: { - fields?: undefined; - }; -} & Omit | null; -}, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; export function useUserQuery(params: { id: string; - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { const args = buildSelectionArgs(params.selection); const { @@ -2539,7 +2095,7 @@ export function useUserQuery(params: { queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(), ...queryOptions }); @@ -2565,21 +2121,13 @@ export async function fetchUserQuery(params: { }>; export async function fetchUserQuery(params: { id: string; - selection?: { - fields?: undefined; - }; -}): Promise<{ - user: InferSelectResult | null; -}>; -export async function fetchUserQuery(params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; }) { const args = buildSelectionArgs(params.selection); return getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap(); } /** @@ -2598,13 +2146,7 @@ export async function prefetchUserQuery(queryClient: Query }): Promise; export async function prefetchUserQuery(queryClient: QueryClient, params: { id: string; - selection?: { - fields?: undefined; - }; -}): Promise; -export async function prefetchUserQuery(queryClient: QueryClient, params: { - id: string; - selection?: SelectionConfig; + selection: SelectionConfig; }): void { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ @@ -2612,7 +2154,7 @@ export async function prefetchUserQuery(queryClient: QueryClient, params: { queryFn: () => getClient().user.findOne({ id: params.id, ...(args ?? {}), - select: (args?.select ?? defaultSelect) as UserSelect + select: args.select }).unwrap() }); } diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index e5ceab809..f8f3f56c4 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -19,9 +19,7 @@ import type { CleanOperation, TypeRegistry } from '../../types/schema'; -import { asConst } from './babel-ast'; import { - buildDefaultSelectExpr, buildSelectionArgsCall, callExpr, constDecl, @@ -41,7 +39,6 @@ import { selectionConfigType, spreadObj, sRef, - typeofRef, typeRef, useMutationOptionsType, useMutationResultType, @@ -164,11 +161,6 @@ function generateCustomMutationHookInternal( statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); } - // Default select - if (hasSelect) { - statements.push(constDecl('defaultSelect', asConst(buildDefaultSelectExpr(payloadTypeName!, typeRegistry)))); - } - // Hook if (hasSelect) { const mutationVarType: t.TSType = hasArgs ? typeRef(varTypeName) : t.tsVoidKeyword(); @@ -204,39 +196,11 @@ function generateCustomMutationHookInternal( ) ); - // Overload 2: without selection.fields - const o2SelectionProp = (() => { - const innerFieldsProp = t.tsPropertySignature( - t.identifier('fields'), - t.tsTypeAnnotation(t.tsUndefinedKeyword()) - ); - innerFieldsProp.optional = true; - const selProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(t.tsParenthesizedType(t.tsTypeLiteral([innerFieldsProp]))) - ); - selProp.optional = true; - return selProp; - })(); - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral([o2SelectionProp]), - useMutationOptionsType(selectedResultType(typeofRef('defaultSelect')), mutationVarType) - ]); - statements.push( - exportDeclareFunction( - hookName, - null, - [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(selectedResultType(typeofRef('defaultSelect')), mutationVarType) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))) ); - implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType( @@ -257,17 +221,7 @@ function generateCustomMutationHookInternal( ) : undefined; - const selectExpr = t.tsAsExpression( - t.parenthesizedExpression( - t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - t.identifier('defaultSelect') - ) - ), - typeRef(selectTypeName!) - ); - const selectObj = t.objectExpression([objectProp('select', selectExpr)]); + const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); let mutationFnExpr: t.Expression; if (hasArgs) { diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index b94eb1747..cd98d300f 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -22,7 +22,6 @@ import type { import { asConst } from './babel-ast'; import { addJSDocComment, - buildDefaultSelectExpr, buildSelectionArgsCall, callExpr, constDecl, @@ -46,7 +45,6 @@ import { selectionConfigType, spreadObj, sRef, - typeofRef, typeRef, useQueryOptionsImplType, useQueryOptionsType, @@ -163,11 +161,6 @@ export function generateCustomQueryHook( statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); } - // Default select - if (hasSelect) { - statements.push(constDecl('defaultSelect', asConst(buildDefaultSelectExpr(payloadTypeName!, typeRegistry)))); - } - // Query key if (useCentralizedKeys) { const keyDecl = t.exportNamedDeclaration( @@ -202,18 +195,6 @@ export function generateCustomQueryHook( statements.push(keyDecl); } - // Helper to build select fallback expression - const buildSelectFallback = () => t.tsAsExpression( - t.parenthesizedExpression( - t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - t.identifier('defaultSelect') - ) - ), - typeRef(selectTypeName!) - ); - // Helper to build query key call expression const buildQueryKeyCall = (withVars: boolean) => { if (useCentralizedKeys) { @@ -234,14 +215,7 @@ export function generateCustomQueryHook( ]) ); - // Helper for { fields?: undefined } - const noFieldsType = () => { - const fp = t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsUndefinedKeyword())); - fp.optional = true; - return t.tsParenthesizedType(t.tsTypeLiteral([fp])); - }; - - const selectedResultType = (sel: t.TSType) => + const selectedResultType= (sel: t.TSType) => customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName!, sel); // Hook @@ -284,30 +258,6 @@ export function generateCustomQueryHook( ]); statements.push(o1); - // Overload 2: without fields - const o2Props: t.TSPropertySignature[] = []; - if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); - if (!hasRequiredArgs) varProp.optional = true; - o2Props.push(varProp); - } - const selProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); - selProp.optional = true; - o2Props.push(selProp); - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral(o2Props), - omitType(typeRef('UseQueryOptions', [selectedResultType(typeofRef('defaultSelect')), typeRef('Error'), typeRef('TData')]), ['queryKey', 'queryFn']) - ]); - const o2Param = createFunctionParam('params', o2ParamType, !hasRequiredArgs); - statements.push( - exportDeclareFunction( - hookName, - createTDataTypeParam(selectedResultType(typeofRef('defaultSelect'))), - [o2Param], - typeRef('UseQueryResult', [typeRef('TData')]) - ) - ); - // Implementation const implProps: t.TSPropertySignature[] = []; if (hasArgs) { @@ -316,7 +266,6 @@ export function generateCustomQueryHook( implProps.push(varProp); } const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); - implSelProp.optional = true; implProps.push(implSelProp); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral(implProps), @@ -346,7 +295,7 @@ export function generateCustomQueryHook( body.push(voidStatement('_selection')); } - const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); const queryFnArgs = hasArgs ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] : [selectObj]; @@ -479,25 +428,6 @@ export function generateCustomQueryHook( ]); statements.push(f1Decl); - // Overload 2: without fields - const f2Props: t.TSPropertySignature[] = []; - if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); - if (!hasRequiredArgs) varProp.optional = true; - f2Props.push(varProp); - } - const f2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); - f2SelProp.optional = true; - f2Props.push(f2SelProp); - statements.push( - exportAsyncDeclareFunction( - fetchFnName, - null, - [createFunctionParam('params', t.tsTypeLiteral(f2Props), !hasRequiredArgs)], - typeRef('Promise', [selectedResultType(typeofRef('defaultSelect'))]) - ) - ); - // Implementation const fImplProps: t.TSPropertySignature[] = []; if (hasArgs) { @@ -506,7 +436,6 @@ export function generateCustomQueryHook( fImplProps.push(varProp); } const fImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); - fImplSelProp.optional = true; fImplProps.push(fImplSelProp); const fBody: t.Statement[] = []; @@ -514,12 +443,12 @@ export function generateCustomQueryHook( fBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); } fBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); const fCallArgs = hasArgs ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] : [selectObj]; fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, fCallArgs as t.Expression[]))); - statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps), !hasRequiredArgs)], fBody)); + statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], fBody)); } else { const fBody: t.Statement[] = []; if (hasArgs) { @@ -587,25 +516,6 @@ export function generateCustomQueryHook( ]); statements.push(p1Decl); - // Overload 2: without fields - const p2Props: t.TSPropertySignature[] = []; - if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); - if (!hasRequiredArgs) varProp.optional = true; - p2Props.push(varProp); - } - const p2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(noFieldsType())); - p2SelProp.optional = true; - p2Props.push(p2SelProp); - statements.push( - exportAsyncDeclareFunction( - prefetchFnName, - null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(p2Props), !hasRequiredArgs)], - typeRef('Promise', [t.tsVoidKeyword()]) - ) - ); - // Implementation const pImplProps: t.TSPropertySignature[] = []; if (hasArgs) { @@ -614,7 +524,6 @@ export function generateCustomQueryHook( pImplProps.push(varProp); } const pImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); - pImplSelProp.optional = true; pImplProps.push(pImplSelProp); const pBody: t.Statement[] = []; @@ -622,7 +531,7 @@ export function generateCustomQueryHook( pBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); } pBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([objectProp('select', buildSelectFallback())]); + const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); const pCallArgs = hasArgs ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] : [selectObj]; @@ -638,7 +547,7 @@ export function generateCustomQueryHook( exportAsyncFunction( prefetchFnName, null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(pImplProps), !hasRequiredArgs)], + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(pImplProps))], pBody, t.tsVoidKeyword() ) diff --git a/graphql/codegen/src/core/codegen/hooks-ast.ts b/graphql/codegen/src/core/codegen/hooks-ast.ts index 6b3782f43..0fc7b69ff 100644 --- a/graphql/codegen/src/core/codegen/hooks-ast.ts +++ b/graphql/codegen/src/core/codegen/hooks-ast.ts @@ -6,9 +6,9 @@ */ import * as t from '@babel/types'; -import type { CleanArgument, TypeRegistry } from '../../types/schema'; +import type { CleanArgument } from '../../types/schema'; import { commentBlock, generateCode } from './babel-ast'; -import { getTypeBaseName, scalarToTsType } from './type-resolver'; +import { scalarToTsType } from './type-resolver'; import { getGeneratedFileHeader } from './utils'; // ============================================================================ @@ -197,28 +197,6 @@ export function withFieldsListSelectionType( ]); } -export function withoutFieldsSelectionType(): t.TSTypeLiteral { - return typeLiteralWithProps([{ - name: 'fields', - type: t.tsUndefinedKeyword(), - optional: true - }]); -} - -export function withoutFieldsListSelectionType( - selectTypeName: string, - filterTypeName: string, - orderByTypeName: string -): t.TSIntersectionType { - return t.tsIntersectionType([ - omitType( - listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName), - ['fields'] - ), - withoutFieldsSelectionType() - ]); -} - export function useQueryOptionsType( queryDataType: t.TSType, dataType: t.TSType, @@ -642,33 +620,18 @@ export function getClientCustomCallUnwrap( // Select/args expression builders // ============================================================================ -export function buildSelectFallbackExpr( - argsIdent: string, - defaultSelectIdent: string, - selectTypeName: string -): t.TSAsExpression { - return t.tsAsExpression( - t.parenthesizedExpression( - t.logicalExpression( - '??', - t.optionalMemberExpression( - t.identifier(argsIdent), - t.identifier('select'), - false, - true - ), - t.identifier(defaultSelectIdent) - ) - ), - typeRef(selectTypeName) +export function buildSelectExpr( + argsIdent: string +): t.MemberExpression { + return t.memberExpression( + t.identifier(argsIdent), + t.identifier('select') ); } export function buildFindManyCallExpr( singularName: string, - argsIdent: string, - selectTypeName: string, - defaultSelectIdent: string = 'defaultSelect' + argsIdent: string ): t.CallExpression { const spreadArgs = t.parenthesizedExpression( t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) @@ -678,7 +641,7 @@ export function buildFindManyCallExpr( 'findMany', t.objectExpression([ t.spreadElement(spreadArgs), - objectProp('select', buildSelectFallbackExpr(argsIdent, defaultSelectIdent, selectTypeName)) + objectProp('select', buildSelectExpr(argsIdent)) ]) ); } @@ -687,9 +650,7 @@ export function buildFindOneCallExpr( singularName: string, pkFieldName: string, argsIdent: string, - selectTypeName: string, - paramsIdent: string = 'params', - defaultSelectIdent: string = 'defaultSelect' + paramsIdent: string = 'params' ): t.CallExpression { return getClientCallUnwrap( singularName, @@ -704,7 +665,7 @@ export function buildFindOneCallExpr( t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) ) ), - objectProp('select', buildSelectFallbackExpr(argsIdent, defaultSelectIdent, selectTypeName)) + objectProp('select', buildSelectExpr(argsIdent)) ]) ); } @@ -738,13 +699,6 @@ export function scopeTypeLiteral(scopeTypeName: string): t.TSTypeLiteral { // Type conversion helpers (GraphQL -> AST) // ============================================================================ -const NON_SELECT_TYPES_AST = new Set([ - 'String', 'Int', 'Float', 'Boolean', 'ID', - 'BigFloat', 'BigInt', 'Cursor', 'Date', 'Datetime', - 'JSON', 'UUID', 'Uuid', 'Time', - 'Query', 'Mutation' -]); - export function wrapInferSelectResultType( typeRefNode: CleanArgument['type'], payloadTypeName: string, @@ -788,46 +742,6 @@ export function typeRefToTsTypeAST( return typeRef(typeRefNode.name ?? 'unknown'); } -export function buildDefaultSelectExpr( - typeName: string, - typeRegistry: TypeRegistry, - depth: number = 0 -): t.ObjectExpression { - const resolved = typeRegistry.get(typeName); - const fields = resolved?.fields ?? []; - - if (depth > 3 || fields.length === 0) { - const fieldName = fields.length > 0 ? fields[0].name : 'id'; - return t.objectExpression([objectProp(fieldName, t.booleanLiteral(true))]); - } - - const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); - if (idLike) return t.objectExpression([objectProp(idLike.name, t.booleanLiteral(true))]); - - const scalarField = fields.find((f) => { - const baseName = getTypeBaseName(f.type); - if (!baseName) return false; - if (NON_SELECT_TYPES_AST.has(baseName)) return true; - return typeRegistry.get(baseName)?.kind === 'ENUM'; - }); - if (scalarField) return t.objectExpression([objectProp(scalarField.name, t.booleanLiteral(true))]); - - const first = fields[0]; - const firstBase = getTypeBaseName(first.type); - if ( - !firstBase || - NON_SELECT_TYPES_AST.has(firstBase) || - typeRegistry.get(firstBase)?.kind === 'ENUM' - ) { - return t.objectExpression([objectProp(first.name, t.booleanLiteral(true))]); - } - - const nested = buildDefaultSelectExpr(firstBase, typeRegistry, depth + 1); - return t.objectExpression([ - objectProp(first.name, t.objectExpression([objectProp('select', nested)])) - ]); -} - export function buildSelectionArgsCall( selectTypeName: string ): t.VariableDeclaration { diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 1edbbe07a..3814f15f3 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -10,7 +10,6 @@ import * as t from '@babel/types'; import type { CleanTable } from '../../types/schema'; -import { asConst } from './babel-ast'; import { addJSDocComment, buildSelectionArgsCall, @@ -33,7 +32,6 @@ import { shorthandProp, spreadObj, sRef, - typeofRef, typeRef, typeLiteralWithProps, useMutationOptionsType, @@ -44,7 +42,6 @@ import { getCreateMutationFileName, getCreateMutationHookName, getCreateMutationName, - getDefaultSelectFieldName, getDeleteMutationFileName, getDeleteMutationHookName, getDeleteMutationName, @@ -82,19 +79,6 @@ function buildMutationResultType( }]); } -function buildSelectFallback(selectTypeName: string): t.TSAsExpression { - return t.tsAsExpression( - t.parenthesizedExpression( - t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - t.identifier('defaultSelect') - ) - ), - typeRef(selectTypeName) - ); -} - function buildFieldsSelectionType(s: t.TSType, selectTypeName: string): t.TSParenthesizedType { return t.tsParenthesizedType( t.tsIntersectionType([ @@ -104,12 +88,6 @@ function buildFieldsSelectionType(s: t.TSType, selectTypeName: string): t.TSPare ); } -function buildNoFieldsType(): t.TSParenthesizedType { - const fp = t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(t.tsUndefinedKeyword())); - fp.optional = true; - return t.tsParenthesizedType(t.tsTypeLiteral([fp])); -} - export function generateCreateMutationHook( table: CleanTable, options: MutationGeneratorOptions = {} @@ -129,7 +107,6 @@ export function generateCreateMutationHook( const selectTypeName = `${typeName}Select`; const relationTypeName = `${typeName}WithRelations`; const createInputTypeName = `Create${typeName}Input`; - const defaultFieldName = getDefaultSelectFieldName(table); const statements: t.Statement[] = []; @@ -151,9 +128,6 @@ export function generateCreateMutationHook( // Re-exports statements.push(createTypeReExport([selectTypeName, relationTypeName, createInputTypeName], '../../orm/input-types')); - // Default select - statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); - // Variable type: CreateTypeName['singularName'] const createVarType = t.tsIndexedAccessType( typeRef(createInputTypeName), @@ -189,25 +163,8 @@ export function generateCreateMutationHook( ]); statements.push(o1); - // Overload 2: without fields - const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); - o2SelProp.optional = true; - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral([o2SelProp]), - useMutationOptionsType(resultType(typeofRef('defaultSelect')), createVarType) - ]); - statements.push( - exportDeclareFunction( - hookName, - null, - [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(resultType(typeofRef('defaultSelect')), createVarType) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); - implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), createVarType]), ['mutationFn']) @@ -229,7 +186,7 @@ export function generateCreateMutationHook( [dataParam], getClientCallUnwrap(singularName, 'create', t.objectExpression([ shorthandProp('data'), - objectProp('select', buildSelectFallback(selectTypeName)) + objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) ])) ); @@ -261,7 +218,7 @@ export function generateCreateMutationHook( ) ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); return { fileName: getCreateMutationFileName(table), @@ -316,9 +273,6 @@ export function generateUpdateMutationHook( // Re-exports statements.push(createTypeReExport([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types')); - // Default select - statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(pkField.name, t.booleanLiteral(true))])))); - // Variable type: { pkField: type; patch: PatchType } const updateVarType = t.tsTypeLiteral([ t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)), @@ -354,25 +308,8 @@ export function generateUpdateMutationHook( ]); statements.push(o1); - // Overload 2: without fields - const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); - o2SelProp.optional = true; - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral([o2SelProp]), - useMutationOptionsType(resultType(typeofRef('defaultSelect')), updateVarType) - ]); - statements.push( - exportDeclareFunction( - hookName, - null, - [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(resultType(typeofRef('defaultSelect')), updateVarType) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); - implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), updateVarType]), ['mutationFn']) @@ -400,7 +337,7 @@ export function generateUpdateMutationHook( getClientCallUnwrap(singularName, 'update', t.objectExpression([ objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), objectProp('data', t.identifier('patch')), - objectProp('select', buildSelectFallback(selectTypeName)) + objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) ])) ); @@ -445,7 +382,7 @@ export function generateUpdateMutationHook( ) ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); return { fileName: getUpdateMutationFileName(table), @@ -499,9 +436,6 @@ export function generateDeleteMutationHook( // Re-exports statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); - // Default select - statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(pkField.name, t.booleanLiteral(true))])))); - // Variable type: { pkField: type } const deleteVarType = t.tsTypeLiteral([ t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)) @@ -536,25 +470,8 @@ export function generateDeleteMutationHook( ]); statements.push(o1); - // Overload 2: without fields - const o2SelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildNoFieldsType())); - o2SelProp.optional = true; - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral([o2SelProp]), - useMutationOptionsType(resultType(typeofRef('defaultSelect')), deleteVarType) - ]); - statements.push( - exportDeclareFunction( - hookName, - null, - [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(resultType(typeofRef('defaultSelect')), deleteVarType) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); - implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), deleteVarType]), ['mutationFn']) @@ -578,7 +495,7 @@ export function generateDeleteMutationHook( [destructParam], getClientCallUnwrap(singularName, 'delete', t.objectExpression([ objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), - objectProp('select', buildSelectFallback(selectTypeName)) + objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) ])) ); @@ -621,7 +538,7 @@ export function generateDeleteMutationHook( ) ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); return { fileName: getDeleteMutationFileName(table), diff --git a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts index 5fbcf5f26..beec9d54e 100644 --- a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts @@ -6,8 +6,8 @@ */ import * as t from '@babel/types'; -import type { CleanArgument, CleanOperation, TypeRegistry } from '../../../types/schema'; -import { asConst, generateCode } from '../babel-ast'; +import type { CleanArgument, CleanOperation } from '../../../types/schema'; +import { generateCode } from '../babel-ast'; import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers'; import { getTypeBaseName, @@ -173,77 +173,9 @@ function buildSelectedResultTsType( ); } -function buildDefaultSelectExpression( - typeName: string, - typeRegistry: TypeRegistry, - depth: number = 0 -): t.Expression { - const resolved = typeRegistry.get(typeName); - const fields = resolved?.fields ?? []; - - if (depth > 3 || fields.length === 0) { - // Use first field if available, otherwise fallback to 'id' - const fallbackName = fields.length > 0 ? fields[0].name : 'id'; - return t.objectExpression([t.objectProperty(t.identifier(fallbackName), t.booleanLiteral(true))]); - } - - // Prefer id-like fields - const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); - if (idLike) { - return t.objectExpression([ - t.objectProperty(t.identifier(idLike.name), t.booleanLiteral(true)) - ]); - } - - // Prefer scalar/enum fields - const scalarField = fields.find((f) => { - const baseName = getTypeBaseName(f.type); - if (!baseName) return false; - if (NON_SELECT_TYPES.has(baseName)) return true; - const baseResolved = typeRegistry.get(baseName); - return baseResolved?.kind === 'ENUM'; - }); - - if (scalarField) { - return t.objectExpression([ - t.objectProperty(t.identifier(scalarField.name), t.booleanLiteral(true)) - ]); - } - - // Fallback: first field (ensure valid selection for object fields) - const first = fields[0]; - - const firstBaseName = getTypeBaseName(first.type); - if (!firstBaseName || NON_SELECT_TYPES.has(firstBaseName)) { - return t.objectExpression([ - t.objectProperty(t.identifier(first.name), t.booleanLiteral(true)) - ]); - } - - const nestedResolved = typeRegistry.get(firstBaseName); - if (nestedResolved?.kind === 'ENUM') { - return t.objectExpression([ - t.objectProperty(t.identifier(first.name), t.booleanLiteral(true)) - ]); - } - - return t.objectExpression([ - t.objectProperty( - t.identifier(first.name), - t.objectExpression([ - t.objectProperty( - t.identifier('select'), - buildDefaultSelectExpression(firstBaseName, typeRegistry, depth + 1) - ) - ]) - ) - ]); -} - function buildOperationMethod( op: CleanOperation, - operationType: 'query' | 'mutation', - defaultSelectIdent?: t.Identifier + operationType: 'query' | 'mutation' ): t.ObjectProperty { const hasArgs = op.args.length > 0; const varTypeName = `${ucFirst(op.name)}Variables`; @@ -265,7 +197,7 @@ function buildOperationMethod( } const optionsParam = t.identifier('options'); - optionsParam.optional = true; + optionsParam.optional = !selectTypeName; if (selectTypeName) { const selectProp = t.tsPropertySignature( t.identifier('select'), @@ -306,12 +238,8 @@ function buildOperationMethod( params.push(optionsParam); // Build the QueryBuilder call - const selectExpr = defaultSelectIdent - ? t.logicalExpression( - '??', - t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true), - defaultSelectIdent - ) + const selectExpr = selectTypeName + ? t.memberExpression(t.identifier('options'), t.identifier('select')) : t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true); const entityTypeExpr = selectTypeName && payloadTypeName ? t.stringLiteral(payloadTypeName) @@ -375,12 +303,9 @@ function buildOperationMethod( // Add type parameters to arrow function if we have a select type if (selectTypeName) { - const defaultType = defaultSelectIdent - ? t.tsTypeQuery(defaultSelectIdent) - : null; const typeParam = t.tsTypeParameter( t.tsTypeReference(t.identifier(selectTypeName)), - defaultType, + null, 'S' ); arrowFunc.typeParameters = t.tsTypeParameterDeclaration([typeParam]); @@ -393,8 +318,7 @@ function buildOperationMethod( * Generate the query/index.ts file for custom query operations */ export function generateCustomQueryOpsFile( - operations: CleanOperation[], - typeRegistry: TypeRegistry + operations: CleanOperation[] ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -421,28 +345,9 @@ export function generateCustomQueryOpsFile( if (varInterface) statements.push(varInterface); } - // Default selects (avoid invalid documents when select is omitted) - const defaultSelectIdentsByOpName = new Map(); - for (const op of operations) { - const selectTypeName = getSelectTypeName(op.returnType); - const payloadTypeName = getTypeBaseName(op.returnType); - if (!selectTypeName || !payloadTypeName) continue; - - const ident = t.identifier(`${op.name}DefaultSelect`); - defaultSelectIdentsByOpName.set(op.name, ident); - statements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - ident, - asConst(buildDefaultSelectExpression(payloadTypeName, typeRegistry)) - ) - ]) - ); - } - // Generate factory function const operationProperties = operations.map((op) => - buildOperationMethod(op, 'query', defaultSelectIdentsByOpName.get(op.name)) + buildOperationMethod(op, 'query') ); const returnObj = t.objectExpression(operationProperties); @@ -471,8 +376,7 @@ export function generateCustomQueryOpsFile( * Generate the mutation/index.ts file for custom mutation operations */ export function generateCustomMutationOpsFile( - operations: CleanOperation[], - typeRegistry: TypeRegistry + operations: CleanOperation[] ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -499,28 +403,9 @@ export function generateCustomMutationOpsFile( if (varInterface) statements.push(varInterface); } - // Default selects (avoid invalid documents when select is omitted) - const defaultSelectIdentsByOpName = new Map(); - for (const op of operations) { - const selectTypeName = getSelectTypeName(op.returnType); - const payloadTypeName = getTypeBaseName(op.returnType); - if (!selectTypeName || !payloadTypeName) continue; - - const ident = t.identifier(`${op.name}DefaultSelect`); - defaultSelectIdentsByOpName.set(op.name, ident); - statements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - ident, - asConst(buildDefaultSelectExpression(payloadTypeName, typeRegistry)) - ) - ]) - ); - } - // Generate factory function const operationProperties = operations.map((op) => - buildOperationMethod(op, 'mutation', defaultSelectIdentsByOpName.get(op.name)) + buildOperationMethod(op, 'mutation') ); const returnObj = t.objectExpression(operationProperties); diff --git a/graphql/codegen/src/core/codegen/orm/index.ts b/graphql/codegen/src/core/codegen/orm/index.ts index afeff9380..3ff30cb62 100644 --- a/graphql/codegen/src/core/codegen/orm/index.ts +++ b/graphql/codegen/src/core/codegen/orm/index.ts @@ -128,16 +128,14 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { // 5. Generate custom operations (if any) if (hasCustomQueries && customOperations?.queries) { const queryOpsFile = generateCustomQueryOpsFile( - customOperations.queries, - typeRegistry ?? new Map() + customOperations.queries ); files.push({ path: queryOpsFile.fileName, content: queryOpsFile.content }); } if (hasCustomMutations && customOperations?.mutations) { const mutationOpsFile = generateCustomMutationOpsFile( - customOperations.mutations, - typeRegistry ?? new Map() + customOperations.mutations ); files.push({ path: mutationOpsFile.fileName, content: mutationOpsFile.content }); } diff --git a/graphql/codegen/src/core/codegen/orm/model-generator.ts b/graphql/codegen/src/core/codegen/orm/model-generator.ts index 67f8a29a5..a9935ef18 100644 --- a/graphql/codegen/src/core/codegen/orm/model-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/model-generator.ts @@ -9,7 +9,6 @@ import * as t from '@babel/types'; import type { CleanTable } from '../../../types/schema'; import { asConst, generateCode } from '../babel-ast'; import { - getDefaultSelectFieldName, getFilterTypeName, getGeneratedFileHeader, getOrderByTypeName, @@ -130,16 +129,6 @@ function requiredSelectProp(): t.TSPropertySignature { return prop; } -/** Build an optional `select?: undefined` prop to forbid select in fallback overloads */ -function optionalUndefinedSelectProp(): t.TSPropertySignature { - const prop = t.tsPropertySignature( - t.identifier('select'), - t.tsTypeAnnotation(t.tsUndefinedKeyword()) - ); - prop.optional = true; - return prop; -} - /** Build `StrictSelect` type reference for overload intersections */ function strictSelectGuard(selectTypeName: string): t.TSType { return t.tsTypeReference( @@ -151,20 +140,6 @@ function strictSelectGuard(selectTypeName: string): t.TSType { ); } -/** Build `Omit & { select?: undefined }` for fallback overloads */ -function withoutSelect(argsType: t.TSType): t.TSType { - return t.tsIntersectionType([ - t.tsTypeReference( - t.identifier('Omit'), - t.tsTypeParameterInstantiation([ - argsType, - t.tsLiteralType(t.stringLiteral('select')) - ]) - ), - t.tsTypeLiteral([optionalUndefinedSelectProp()]) - ]); -} - export function generateModelFile( table: CleanTable, _useSharedTypes: boolean @@ -186,10 +161,7 @@ export function generateModelFile( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const defaultSelectIdent = t.identifier('defaultSelect'); - const defaultSelectFieldName = getDefaultSelectFieldName(table); - - const pluralQueryName = table.query?.all ?? pluralName; + const pluralQueryName= table.query?.all ?? pluralName; const createMutationName = table.query?.create ?? `create${typeName}`; const updateMutationName = table.query?.update; const deleteMutationName = table.query?.delete; @@ -211,20 +183,6 @@ export function generateModelFile( ], true)); statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); - // Default select (ensures valid GraphQL selection + sound TS return types) - statements.push( - t.variableDeclaration('const', [ - t.variableDeclarator( - defaultSelectIdent, - asConst( - t.objectExpression([ - t.objectProperty(t.identifier(defaultSelectFieldName), t.booleanLiteral(true)) - ]) - ) - ) - ]) - ); - const classBody: t.ClassBody['body'] = []; // Constructor @@ -236,7 +194,6 @@ export function generateModelFile( // Reusable type reference factories const sRef = () => t.tsTypeReference(t.identifier('S')); - const defaultRef = () => t.tsTypeQuery(defaultSelectIdent); const selectRef = () => t.tsTypeReference(t.identifier(selectTypeName)); const pkTsType = () => tsTypeFromPrimitive(pkField.tsType); @@ -275,19 +232,10 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('findMany', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.optional = true; - o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); - classBody.push(createDeclareMethod('findMany', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); - implParam.optional = true; implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.logicalExpression('??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), @@ -348,19 +296,10 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('findFirst', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.optional = true; - o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); - classBody.push(createDeclareMethod('findFirst', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); - implParam.optional = true; implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.logicalExpression('??', - t.optionalMemberExpression(t.identifier('args'), t.identifier('select'), false, true), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), @@ -413,13 +352,6 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('findOne', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeLiteral([pkProp()]) - ); - classBody.push(createDeclareMethod('findOne', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation( @@ -430,14 +362,11 @@ export function generateModelFile( t.identifier('select'), t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))) ); - prop.optional = true; return prop; })() ]) ); - const selectExpr = t.logicalExpression('??', - t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(singleQueryName), @@ -488,17 +417,10 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('create', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); - classBody.push(createDeclareMethod('create', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.logicalExpression('??', - t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(createMutationName), @@ -554,17 +476,10 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('update', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); - classBody.push(createDeclareMethod('update', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.logicalExpression('??', - t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(updateMutationName), @@ -623,17 +538,10 @@ export function generateModelFile( ); classBody.push(createDeclareMethod('delete', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); - // Overload 2: without select (default) - const o2Param = t.identifier('args'); - o2Param.typeAnnotation = t.tsTypeAnnotation(withoutSelect(argsType(selectRef()))); - classBody.push(createDeclareMethod('delete', null, [o2Param], retType(defaultRef()))); - // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.logicalExpression('??', - t.memberExpression(t.identifier('args'), t.identifier('select')), - defaultSelectIdent); + const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(deleteMutationName), diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index 38e4e5395..a33d52a6d 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -16,7 +16,6 @@ import { buildFindOneCallExpr, buildListSelectionArgsCall, buildSelectionArgsCall, - buildSelectFallbackExpr, callExpr, connectionResultType, constDecl, @@ -24,7 +23,6 @@ import { createImportDeclaration, createSAndTDataTypeParams, createSTypeParam, - createTDataTypeParam, createTypeReExport, destructureParamsWithSelection, destructureParamsWithSelectionAndScope, @@ -44,20 +42,16 @@ import { singleQueryResultType, spreadObj, sRef, - typeofRef, typeRef, typeLiteralWithProps, useQueryOptionsType, useQueryOptionsImplType, voidStatement, withFieldsListSelectionType, - withFieldsSelectionType, - withoutFieldsListSelectionType, - withoutFieldsSelectionType + withFieldsSelectionType } from './hooks-ast'; import { getAllRowsQueryName, - getDefaultSelectFieldName, getFilterTypeName, getListQueryFileName, getListQueryHookName, @@ -101,7 +95,6 @@ export function generateListQueryHook( const scopeTypeName = `${typeName}Scope`; const selectTypeName = `${typeName}Select`; const relationTypeName = `${typeName}WithRelations`; - const defaultFieldName = getDefaultSelectFieldName(table); const listResultTypeAST = (sel: t.TSType) => listQueryResultType(queryName, relationTypeName, sel); @@ -129,9 +122,6 @@ export function generateListQueryHook( // Re-exports statements.push(createTypeReExport([selectTypeName, relationTypeName, filterTypeName, orderByTypeName], '../../orm/input-types')); - // Default select - statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); - // Query key if (useCentralizedKeys) { const keyDecl = t.exportNamedDeclaration( @@ -167,7 +157,7 @@ export function generateListQueryHook( // Helper for findMany queryFn const buildFindManyFn = () => t.arrowFunctionExpression( [], - buildFindManyCallExpr(singularName, 'args', selectTypeName) + buildFindManyCallExpr(singularName, 'args') ); // Options type builder with optional scope @@ -229,32 +219,12 @@ export function generateListQueryHook( addJSDocComment(o1, docLines); statements.push(o1); - // Overload 2: without fields - const o2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) - ); - o2SelProp.optional = true; - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral([o2SelProp]), - buildOptionsType(listResultTypeAST(typeofRef('defaultSelect')), typeRef('TData')) - ]); - statements.push( - exportDeclareFunction( - hookName, - createTDataTypeParam(listResultTypeAST(typeofRef('defaultSelect'))), - [createFunctionParam('params', o2ParamType, true)], - typeRef('UseQueryResult', [typeRef('TData')]) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) ); - implSelProp.optional = true; - const implOptionsType = (() => { + const implOptionsType= (() => { const base = useQueryOptionsImplType(); if (hasRelationships && useCentralizedKeys) { return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); @@ -267,7 +237,7 @@ export function generateListQueryHook( ]); const body: t.Statement[] = []; - body.push(constDecl('selection', t.optionalMemberExpression(t.identifier('params'), t.identifier('selection'), false, true))); + body.push(constDecl('selection', t.memberExpression(t.identifier('params'), t.identifier('selection')))); body.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); if (hasRelationships && useCentralizedKeys) { @@ -288,7 +258,7 @@ export function generateListQueryHook( )); } - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); + statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); } // Fetch function @@ -322,31 +292,15 @@ export function generateListQueryHook( ]); statements.push(f1Decl); - // Overload 2: without fields - const f2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) - ); - f2SelProp.optional = true; - statements.push( - exportAsyncDeclareFunction( - fetchFnName, - null, - [createFunctionParam('params', t.tsTypeLiteral([f2SelProp]), true)], - typeRef('Promise', [listResultTypeAST(typeofRef('defaultSelect'))]) - ) - ); - // Implementation const fImplSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) ); - fImplSelProp.optional = true; const fBody: t.Statement[] = []; fBody.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); - fBody.push(t.returnStatement(buildFindManyCallExpr(singularName, 'args', selectTypeName))); - statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]), true)], fBody)); + fBody.push(t.returnStatement(buildFindManyCallExpr(singularName, 'args'))); + statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]))], fBody)); } // Prefetch function @@ -379,30 +333,11 @@ export function generateListQueryHook( ]); statements.push(p1Decl); - // Overload 2: without fields - const p2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsListSelectionType(selectTypeName, filterTypeName, orderByTypeName)) - ); - p2SelProp.optional = true; - const p2ParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral([p2SelProp]), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral([p2SelProp]); - statements.push( - exportAsyncDeclareFunction( - prefetchFnName, - null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p2ParamType, true)], - typeRef('Promise', [t.tsVoidKeyword()]) - ) - ); - // Implementation const pImplSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) ); - pImplSelProp.optional = true; const pImplParamType = hasRelationships && useCentralizedKeys ? t.tsIntersectionType([t.tsTypeLiteral([pImplSelProp]), scopeTypeLiteral(scopeTypeName)]) : t.tsTypeLiteral([pImplSelProp]); @@ -427,7 +362,7 @@ export function generateListQueryHook( exportAsyncFunction( prefetchFnName, null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType, true)], + [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType)], pBody, t.tsVoidKeyword() ) @@ -467,7 +402,6 @@ export function generateSingleQueryHook( const pkField = pkFields[0]; const pkFieldName = pkField?.name ?? 'id'; const pkFieldTsType = pkField?.tsType ?? 'string'; - const defaultFieldName = getDefaultSelectFieldName(table); const pkTsType: t.TSType = pkFieldTsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); const singleResultTypeAST = (sel: t.TSType) => singleQueryResultType(queryName, relationTypeName, sel); @@ -496,9 +430,6 @@ export function generateSingleQueryHook( // Re-exports statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); - // Default select - statements.push(constDecl('defaultSelect', asConst(t.objectExpression([objectProp(defaultFieldName, t.booleanLiteral(true))])))); - // Query key if (useCentralizedKeys) { const keyDecl = t.exportNamedDeclaration( @@ -534,7 +465,7 @@ export function generateSingleQueryHook( // Helper for findOne queryFn const buildFindOneFn = () => t.arrowFunctionExpression( [], - buildFindOneCallExpr(singularName, pkFieldName, 'args', selectTypeName) + buildFindOneCallExpr(singularName, pkFieldName, 'args') ); // Options type builder with optional scope @@ -594,35 +525,11 @@ export function generateSingleQueryHook( addJSDocComment(o1, docLines); statements.push(o1); - // Overload 2: without fields - const o2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsSelectionType()) - ); - o2SelProp.optional = true; - const o2Props = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - o2SelProp - ]; - const o2ParamType = t.tsIntersectionType([ - t.tsTypeLiteral(o2Props), - buildSingleOptionsType(singleResultTypeAST(typeofRef('defaultSelect')), typeRef('TData')) - ]); - statements.push( - exportDeclareFunction( - hookName, - createTDataTypeParam(singleResultTypeAST(typeofRef('defaultSelect'))), - [createFunctionParam('params', o2ParamType)], - typeRef('UseQueryResult', [typeRef('TData')]) - ) - ); - // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) ); - implSelProp.optional = true; const implProps = [ t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), implSelProp @@ -698,31 +605,11 @@ export function generateSingleQueryHook( ]); statements.push(f1Decl); - // Overload 2: without fields - const f2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsSelectionType()) - ); - f2SelProp.optional = true; - const f2Props = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - f2SelProp - ]; - statements.push( - exportAsyncDeclareFunction( - fetchFnName, - null, - [createFunctionParam('params', t.tsTypeLiteral(f2Props))], - typeRef('Promise', [singleResultTypeAST(typeofRef('defaultSelect'))]) - ) - ); - // Implementation const fImplSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) ); - fImplSelProp.optional = true; const fImplProps = [ t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), fImplSelProp @@ -734,7 +621,7 @@ export function generateSingleQueryHook( // @ts-ignore fArgsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); fBody.push(constDecl('args', fArgsCall)); - fBody.push(t.returnStatement(buildFindOneCallExpr(singularName, pkFieldName, 'args', selectTypeName))); + fBody.push(t.returnStatement(buildFindOneCallExpr(singularName, pkFieldName, 'args'))); statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], fBody)); } @@ -766,34 +653,11 @@ export function generateSingleQueryHook( ]); statements.push(p1Decl); - // Overload 2: without fields - const p2SelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(withoutFieldsSelectionType()) - ); - p2SelProp.optional = true; - const p2Props: t.TSPropertySignature[] = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - p2SelProp - ]; - const p2ParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral(p2Props), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral(p2Props); - statements.push( - exportAsyncDeclareFunction( - prefetchFnName, - null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p2ParamType)], - typeRef('Promise', [t.tsVoidKeyword()]) - ) - ); - // Implementation const pImplSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) ); - pImplSelProp.optional = true; const pImplProps: t.TSPropertySignature[] = [ t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), pImplSelProp diff --git a/graphql/codegen/src/core/codegen/select-helpers.ts b/graphql/codegen/src/core/codegen/select-helpers.ts index 4ae52a351..6360672b2 100644 --- a/graphql/codegen/src/core/codegen/select-helpers.ts +++ b/graphql/codegen/src/core/codegen/select-helpers.ts @@ -3,7 +3,7 @@ * * Used by custom-queries.ts, custom-mutations.ts, and orm/custom-ops-generator.ts */ -import type { CleanArgument, TypeRegistry } from '../../types/schema'; +import type { CleanArgument } from '../../types/schema'; import { SCALAR_NAMES } from './scalars'; import { getTypeBaseName } from './type-resolver'; @@ -50,42 +50,3 @@ export function wrapInferSelectResult( return `InferSelectResult<${payloadTypeName}, ${selectType}>`; } - -/** - * Build a default select literal string for a given type. - * Finds an 'id' or 'nodeId' field, or falls back to first scalar field. - */ -export function buildDefaultSelectLiteral( - typeName: string, - typeRegistry: TypeRegistry, - depth: number = 0 -): string { - const resolved = typeRegistry.get(typeName); - const fields = resolved?.fields ?? []; - - if (depth > 3 || fields.length === 0) { - // Use first field if available, otherwise fallback to 'id' - return fields.length > 0 ? `{ ${fields[0].name}: true }` : `{ id: true }`; - } - - const idLike = fields.find((f) => f.name === 'id' || f.name === 'nodeId'); - if (idLike) return `{ ${idLike.name}: true }`; - - const scalarField = fields.find((f) => { - const baseName = getTypeBaseName(f.type); - if (!baseName) return false; - if (NON_SELECT_TYPES.has(baseName)) return true; - return typeRegistry.get(baseName)?.kind === 'ENUM'; - }); - if (scalarField) return `{ ${scalarField.name}: true }`; - - const first = fields[0]; - - const firstBase = getTypeBaseName(first.type); - if (!firstBase || NON_SELECT_TYPES.has(firstBase) || typeRegistry.get(firstBase)?.kind === 'ENUM') { - return `{ ${first.name}: true }`; - } - - const nested = buildDefaultSelectLiteral(firstBase, typeRegistry, depth + 1); - return `{ ${first.name}: { select: ${nested} } }`; -} diff --git a/graphql/codegen/src/core/codegen/utils.ts b/graphql/codegen/src/core/codegen/utils.ts index 859a67900..2bca88ad7 100644 --- a/graphql/codegen/src/core/codegen/utils.ts +++ b/graphql/codegen/src/core/codegen/utils.ts @@ -401,36 +401,6 @@ export function hasValidPrimaryKey(table: CleanTable): boolean { return false; } -/** - * Get the best field name for a defaultSelect literal. - * Prefers PK field if valid, then 'id'/'nodeId', then first scalar field. - * Unlike getPrimaryKeyInfo(), never returns a fictional 'id' fallback. - */ -export function getDefaultSelectFieldName(table: CleanTable): string { - // 1. Try the actual primary key - const pk = table.constraints?.primaryKey?.[0]; - if (pk && pk.fields.length >= 1) { - return pk.fields[0].name; - } - // 2. Try id / nodeId fields - const idField = table.fields.find((f) => f.name === 'id' || f.name === 'nodeId'); - if (idField) { - return idField.name; - } - // 3. First non-array scalar field - const scalarField = table.fields.find( - (f) => !f.type.isArray && scalarToTsType(f.type.gqlType) !== f.type.gqlType - ); - if (scalarField) { - return scalarField.name; - } - // 4. First field of any kind - if (table.fields.length > 0) { - return table.fields[0].name; - } - return 'id'; -} - // ============================================================================ // Query key generation // ============================================================================ From 838705649dc40b4f8bdda3185231927a7b04862f Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 00:15:32 +0000 Subject: [PATCH 10/23] chore: update makage to 0.1.12 (fix ENOTEMPTY race condition) --- functions/send-email-link/package.json | 2 +- functions/simple-email/package.json | 2 +- graphile/graphile-cache/package.json | 2 +- graphile/graphile-i18n/package.json | 2 +- graphile/graphile-many-to-many/package.json | 2 +- graphile/graphile-meta-schema/package.json | 2 +- .../graphile-pg-type-mappings/package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- .../package.json | 2 +- graphile/graphile-postgis/package.json | 2 +- graphile/graphile-query/package.json | 2 +- graphile/graphile-search-plugin/package.json | 2 +- graphile/graphile-settings/package.json | 2 +- .../graphile-simple-inflector/package.json | 2 +- .../package.json | 2 +- graphile/graphile-test/package.json | 2 +- graphile/graphile-upload-plugin/package.json | 2 +- graphql/env/package.json | 2 +- graphql/explorer/package.json | 2 +- graphql/gql-ast/package.json | 2 +- graphql/playwright-test/package.json | 2 +- graphql/query/package.json | 2 +- graphql/react/package.json | 2 +- graphql/server-test/package.json | 2 +- graphql/server/package.json | 2 +- graphql/test/package.json | 2 +- graphql/types/package.json | 2 +- jobs/job-pg/package.json | 2 +- jobs/job-scheduler/package.json | 2 +- jobs/job-utils/package.json | 2 +- jobs/job-worker/package.json | 2 +- jobs/knative-job-example/package.json | 2 +- jobs/knative-job-fn/package.json | 2 +- jobs/knative-job-server/package.json | 2 +- jobs/knative-job-service/package.json | 2 +- jobs/knative-job-worker/package.json | 2 +- package.json | 2 +- packages/12factor-env/package.json | 2 +- packages/cli/package.json | 2 +- packages/client/package.json | 2 +- packages/csrf/package.json | 2 +- packages/csv-to-pg/package.json | 2 +- packages/oauth/package.json | 2 +- packages/orm/package.json | 2 +- packages/postmaster/package.json | 2 +- packages/query-builder/package.json | 2 +- packages/server-utils/package.json | 2 +- packages/smtppostmaster/package.json | 2 +- packages/url-domains/package.json | 2 +- pgpm/cli/package.json | 2 +- pgpm/core/package.json | 2 +- pgpm/env/package.json | 2 +- pgpm/logger/package.json | 2 +- pgpm/types/package.json | 2 +- pnpm-lock.yaml | 349 ++++++++++-------- postgres/drizzle-orm-test/package.json | 2 +- postgres/introspectron/package.json | 2 +- postgres/pg-ast/package.json | 2 +- postgres/pg-cache/package.json | 2 +- postgres/pg-codegen/package.json | 2 +- postgres/pg-env/package.json | 2 +- postgres/pg-query-context/package.json | 2 +- postgres/pg-seed/package.json | 2 +- postgres/pgsql-client/package.json | 2 +- postgres/pgsql-seed/package.json | 2 +- postgres/pgsql-test/package.json | 2 +- postgres/supabase-test/package.json | 2 +- uploads/content-type-stream/package.json | 2 +- uploads/etag-hash/package.json | 2 +- uploads/etag-stream/package.json | 2 +- uploads/mime-bytes/package.json | 2 +- uploads/s3-streamer/package.json | 2 +- uploads/s3-utils/package.json | 2 +- uploads/stream-to-etag/package.json | 2 +- uploads/upload-names/package.json | 2 +- uploads/uuid-hash/package.json | 2 +- uploads/uuid-stream/package.json | 2 +- 78 files changed, 263 insertions(+), 240 deletions(-) diff --git a/functions/send-email-link/package.json b/functions/send-email-link/package.json index 86e6d7a48..a6ab70cd9 100644 --- a/functions/send-email-link/package.json +++ b/functions/send-email-link/package.json @@ -30,7 +30,7 @@ "build:dev": "makage build --dev" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/knative-job-fn": "workspace:^", diff --git a/functions/simple-email/package.json b/functions/simple-email/package.json index ef497f522..c3c1068f2 100644 --- a/functions/simple-email/package.json +++ b/functions/simple-email/package.json @@ -30,7 +30,7 @@ "build:dev": "makage build --dev" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/knative-job-fn": "workspace:^", diff --git a/graphile/graphile-cache/package.json b/graphile/graphile-cache/package.json index 016a50e25..7d39c7b5e 100644 --- a/graphile/graphile-cache/package.json +++ b/graphile/graphile-cache/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10", + "makage": "^0.1.12", "nodemon": "^3.1.10", "ts-node": "^10.9.2" }, diff --git a/graphile/graphile-i18n/package.json b/graphile/graphile-i18n/package.json index ee59f109a..bf6d28599 100644 --- a/graphile/graphile-i18n/package.json +++ b/graphile/graphile-i18n/package.json @@ -41,7 +41,7 @@ "@types/accept-language-parser": "^1.5.4", "@types/pg": "^8.16.0", "graphile-test": "workspace:^", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-many-to-many/package.json b/graphile/graphile-many-to-many/package.json index 0179bc145..ad74376b2 100644 --- a/graphile/graphile-many-to-many/package.json +++ b/graphile/graphile-many-to-many/package.json @@ -44,7 +44,7 @@ "devDependencies": { "graphile-test": "workspace:^", "graphql": "15.10.1", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^", "postgraphile": "^4.14.1" } diff --git a/graphile/graphile-meta-schema/package.json b/graphile/graphile-meta-schema/package.json index d1e4bfa94..2b61ef6a5 100644 --- a/graphile/graphile-meta-schema/package.json +++ b/graphile/graphile-meta-schema/package.json @@ -43,7 +43,7 @@ "@graphile-contrib/pg-many-to-many": "^1.0.0", "graphile-test": "workspace:^", "graphql-tag": "2.12.6", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-pg-type-mappings/package.json b/graphile/graphile-pg-type-mappings/package.json index e09e59fd6..270adbe1a 100644 --- a/graphile/graphile-pg-type-mappings/package.json +++ b/graphile/graphile-pg-type-mappings/package.json @@ -43,7 +43,7 @@ "graphile-postgis": "workspace:^", "graphile-test": "workspace:^", "graphql-tag": "2.12.6", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-plugin-connection-filter-postgis/package.json b/graphile/graphile-plugin-connection-filter-postgis/package.json index 8f508131d..cfa538b03 100644 --- a/graphile/graphile-plugin-connection-filter-postgis/package.json +++ b/graphile/graphile-plugin-connection-filter-postgis/package.json @@ -54,7 +54,7 @@ "@types/pg": "^8.16.0", "graphile-test": "workspace:^", "graphql": "15.10.1", - "makage": "^0.1.10", + "makage": "^0.1.12", "pg": "^8.17.1", "pgsql-test": "workspace:^", "postgraphile": "^4.14.1" diff --git a/graphile/graphile-plugin-connection-filter/package.json b/graphile/graphile-plugin-connection-filter/package.json index 236b3b306..abbd9e249 100644 --- a/graphile/graphile-plugin-connection-filter/package.json +++ b/graphile/graphile-plugin-connection-filter/package.json @@ -52,7 +52,7 @@ "@graphile-contrib/pg-simplify-inflector": "^6.1.0", "@types/pg": "^8.16.0", "graphile-test": "workspace:^", - "makage": "^0.1.10", + "makage": "^0.1.12", "pg": "^8.17.1", "pgsql-test": "workspace:^" } diff --git a/graphile/graphile-plugin-fulltext-filter/package.json b/graphile/graphile-plugin-fulltext-filter/package.json index b851420a2..03a22c7a7 100644 --- a/graphile/graphile-plugin-fulltext-filter/package.json +++ b/graphile/graphile-plugin-fulltext-filter/package.json @@ -52,7 +52,7 @@ "devDependencies": { "graphile-test": "workspace:^", "graphql": "15.10.1", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" } } diff --git a/graphile/graphile-postgis/package.json b/graphile/graphile-postgis/package.json index 8eddf16b8..7fd679af1 100644 --- a/graphile/graphile-postgis/package.json +++ b/graphile/graphile-postgis/package.json @@ -41,7 +41,7 @@ "devDependencies": { "@types/geojson": "^7946.0.14", "graphile-test": "workspace:^", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-query/package.json b/graphile/graphile-query/package.json index 5eff113a3..9c84686b4 100644 --- a/graphile/graphile-query/package.json +++ b/graphile/graphile-query/package.json @@ -35,7 +35,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [ "graphql", diff --git a/graphile/graphile-search-plugin/package.json b/graphile/graphile-search-plugin/package.json index e2c29b191..0afe09992 100644 --- a/graphile/graphile-search-plugin/package.json +++ b/graphile/graphile-search-plugin/package.json @@ -43,7 +43,7 @@ "graphile-plugin-fulltext-filter": "workspace:^", "graphile-simple-inflector": "workspace:^", "graphile-test": "workspace:^", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-settings/package.json b/graphile/graphile-settings/package.json index 71afd9a4b..2a09516e8 100644 --- a/graphile/graphile-settings/package.json +++ b/graphile/graphile-settings/package.json @@ -60,7 +60,7 @@ "@types/express": "^5.0.6", "@types/pg": "^8.16.0", "@types/request-ip": "^0.0.41", - "makage": "^0.1.10", + "makage": "^0.1.12", "nodemon": "^3.1.10", "ts-node": "^10.9.2" }, diff --git a/graphile/graphile-simple-inflector/package.json b/graphile/graphile-simple-inflector/package.json index 17210ad3f..7e7d32f0f 100644 --- a/graphile/graphile-simple-inflector/package.json +++ b/graphile/graphile-simple-inflector/package.json @@ -42,7 +42,7 @@ "devDependencies": { "graphile-test": "workspace:^", "graphql-tag": "2.12.6", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/graphile/graphile-sql-expression-validator/package.json b/graphile/graphile-sql-expression-validator/package.json index e6c15755b..589f9f7e8 100644 --- a/graphile/graphile-sql-expression-validator/package.json +++ b/graphile/graphile-sql-expression-validator/package.json @@ -42,7 +42,7 @@ "url": "https://github.com/constructive-io/constructive/issues" }, "devDependencies": { - "makage": "^0.1.10", + "makage": "^0.1.12", "ts-jest": "^29.4.6" }, "dependencies": { diff --git a/graphile/graphile-test/package.json b/graphile/graphile-test/package.json index e7ffd005e..06638214e 100644 --- a/graphile/graphile-test/package.json +++ b/graphile/graphile-test/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/pg": "^8.16.0", "graphql-tag": "2.12.6", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/graphql-env": "workspace:^", diff --git a/graphile/graphile-upload-plugin/package.json b/graphile/graphile-upload-plugin/package.json index 18ed175cd..327cd0a8b 100644 --- a/graphile/graphile-upload-plugin/package.json +++ b/graphile/graphile-upload-plugin/package.json @@ -45,7 +45,7 @@ "@types/pg": "^8.16.0", "graphile-test": "workspace:^", "graphql-tag": "^2.12.6", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^", "ts-jest": "^29.4.6" }, diff --git a/graphql/env/package.json b/graphql/env/package.json index 025dfec10..74f4d53d2 100644 --- a/graphql/env/package.json +++ b/graphql/env/package.json @@ -42,6 +42,6 @@ "postgraphile" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/graphql/explorer/package.json b/graphql/explorer/package.json index f6f7e9e84..fe8c613e3 100644 --- a/graphql/explorer/package.json +++ b/graphql/explorer/package.json @@ -54,7 +54,7 @@ "devDependencies": { "@types/express": "^5.0.6", "@types/graphql-upload": "^8.0.12", - "makage": "^0.1.10", + "makage": "^0.1.12", "nodemon": "^3.1.10", "ts-node": "^10.9.2" }, diff --git a/graphql/gql-ast/package.json b/graphql/gql-ast/package.json index 9193de04b..6c6ba7791 100644 --- a/graphql/gql-ast/package.json +++ b/graphql/gql-ast/package.json @@ -39,6 +39,6 @@ "schema" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/graphql/playwright-test/package.json b/graphql/playwright-test/package.json index ba473b4fa..f3d6aabd4 100644 --- a/graphql/playwright-test/package.json +++ b/graphql/playwright-test/package.json @@ -32,7 +32,7 @@ "@playwright/test": "^1.57.0", "@types/express": "^5.0.6", "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/graphql-env": "workspace:^", diff --git a/graphql/query/package.json b/graphql/query/package.json index 435625cb6..332f6b04b 100644 --- a/graphql/query/package.json +++ b/graphql/query/package.json @@ -42,6 +42,6 @@ "database" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/graphql/react/package.json b/graphql/react/package.json index 325948cca..eae70c3c1 100644 --- a/graphql/react/package.json +++ b/graphql/react/package.json @@ -38,6 +38,6 @@ "@testing-library/jest-dom": "5.11.10", "@testing-library/react": "11.2.5", "@types/react": "^19.2.8", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/graphql/server-test/package.json b/graphql/server-test/package.json index 3e5277e82..0a430b3ae 100644 --- a/graphql/server-test/package.json +++ b/graphql/server-test/package.json @@ -32,7 +32,7 @@ "@types/express": "^5.0.6", "@types/pg": "^8.16.0", "@types/supertest": "^6.0.2", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/graphql-env": "workspace:^", diff --git a/graphql/server/package.json b/graphql/server/package.json index 64561cf16..feb31ef70 100644 --- a/graphql/server/package.json +++ b/graphql/server/package.json @@ -84,7 +84,7 @@ "@types/pg": "^8.16.0", "@types/request-ip": "^0.0.41", "graphile-test": "workspace:*", - "makage": "^0.1.10", + "makage": "^0.1.12", "nodemon": "^3.1.10", "rimraf": "^6.1.2", "ts-node": "^10.9.2" diff --git a/graphql/test/package.json b/graphql/test/package.json index df52a84a2..46d030073 100644 --- a/graphql/test/package.json +++ b/graphql/test/package.json @@ -31,7 +31,7 @@ "devDependencies": { "@types/pg": "^8.16.0", "graphql-tag": "2.12.6", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/graphql-env": "workspace:^", diff --git a/graphql/types/package.json b/graphql/types/package.json index d04a2ba02..1751ef21f 100644 --- a/graphql/types/package.json +++ b/graphql/types/package.json @@ -44,6 +44,6 @@ "graphile" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/jobs/job-pg/package.json b/jobs/job-pg/package.json index 7a325007b..45009ca13 100644 --- a/jobs/job-pg/package.json +++ b/jobs/job-pg/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/job-utils": "workspace:^", diff --git a/jobs/job-scheduler/package.json b/jobs/job-scheduler/package.json index 8b9091f5d..d5a8110e8 100644 --- a/jobs/job-scheduler/package.json +++ b/jobs/job-scheduler/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/job-pg": "workspace:^", diff --git a/jobs/job-utils/package.json b/jobs/job-utils/package.json index 16ac237c1..ae76127ce 100644 --- a/jobs/job-utils/package.json +++ b/jobs/job-utils/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/env": "workspace:^", diff --git a/jobs/job-worker/package.json b/jobs/job-worker/package.json index c21d71666..a8af763b5 100644 --- a/jobs/job-worker/package.json +++ b/jobs/job-worker/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/job-utils": "workspace:^", diff --git a/jobs/knative-job-example/package.json b/jobs/knative-job-example/package.json index 1c60dc8c9..25d7c14a7 100644 --- a/jobs/knative-job-example/package.json +++ b/jobs/knative-job-example/package.json @@ -34,7 +34,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/knative-job-fn": "workspace:^" diff --git a/jobs/knative-job-fn/package.json b/jobs/knative-job-fn/package.json index 6c5326ff4..af7a37730 100644 --- a/jobs/knative-job-fn/package.json +++ b/jobs/knative-job-fn/package.json @@ -33,7 +33,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/logger": "workspace:^", diff --git a/jobs/knative-job-server/package.json b/jobs/knative-job-server/package.json index a5d964e00..9a4fd19d4 100644 --- a/jobs/knative-job-server/package.json +++ b/jobs/knative-job-server/package.json @@ -36,7 +36,7 @@ "url": "https://github.com/constructive-io/jobs/issues" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@constructive-io/job-pg": "workspace:^", diff --git a/jobs/knative-job-service/package.json b/jobs/knative-job-service/package.json index df6afc17e..f2bd97507 100644 --- a/jobs/knative-job-service/package.json +++ b/jobs/knative-job-service/package.json @@ -50,7 +50,7 @@ "@pgpm/verify": "^0.16.0", "@pgpmjs/core": "workspace:^", "@types/supertest": "^6.0.3", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^", "supertest": "^7.2.2", "ts-node": "^10.9.2" diff --git a/jobs/knative-job-worker/package.json b/jobs/knative-job-worker/package.json index faa67169b..27cff01a7 100644 --- a/jobs/knative-job-worker/package.json +++ b/jobs/knative-job-worker/package.json @@ -47,7 +47,7 @@ "@pgpm/database-jobs": "^0.16.0", "@pgpm/verify": "^0.16.0", "@pgpmjs/core": "workspace:^", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" } } diff --git a/package.json b/package.json index a784ca712..9d280286c 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "jest": "^30.2.0", "jest-in-case": "^1.0.2", "lerna": "^8.2.3", - "makage": "^0.1.10", + "makage": "^0.1.12", "prettier": "^3.8.0", "ts-jest": "^29.4.6", "ts-node": "^10.9.2", diff --git a/packages/12factor-env/package.json b/packages/12factor-env/package.json index 251c77839..8eca35a8f 100644 --- a/packages/12factor-env/package.json +++ b/packages/12factor-env/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/node": "^20.12.7", - "makage": "^0.1.10", + "makage": "^0.1.12", "ts-node": "^10.9.2" }, "keywords": [ diff --git a/packages/cli/package.json b/packages/cli/package.json index fed983580..4dc2500c2 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -40,7 +40,7 @@ "@types/pg": "^8.16.0", "@types/shelljs": "^0.8.16", "glob": "^13.0.0", - "makage": "^0.1.10", + "makage": "^0.1.12", "pg": "^8.17.1", "ts-node": "^10.9.2" }, diff --git a/packages/client/package.json b/packages/client/package.json index 57ba56322..0c82f14f1 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -38,7 +38,7 @@ ], "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "pg": "^8.17.1" diff --git a/packages/csrf/package.json b/packages/csrf/package.json index 852fef520..9999e727b 100644 --- a/packages/csrf/package.json +++ b/packages/csrf/package.json @@ -21,7 +21,7 @@ }, "devDependencies": { "@types/node": "^20.12.7", - "makage": "^0.1.10", + "makage": "^0.1.12", "ts-node": "^10.9.2" } } diff --git a/packages/csv-to-pg/package.json b/packages/csv-to-pg/package.json index ce03042cd..4660d0d6d 100644 --- a/packages/csv-to-pg/package.json +++ b/packages/csv-to-pg/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/js-yaml": "^4.0.9", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-parser": "^17.9.11" }, "keywords": [ diff --git a/packages/oauth/package.json b/packages/oauth/package.json index e8ba5a4f3..050e95ef1 100644 --- a/packages/oauth/package.json +++ b/packages/oauth/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/node": "^20.12.7", - "makage": "^0.1.10", + "makage": "^0.1.12", "ts-node": "^10.9.2" }, "keywords": [ diff --git a/packages/orm/package.json b/packages/orm/package.json index 3a6a589e7..e5051ced0 100644 --- a/packages/orm/package.json +++ b/packages/orm/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [ "orm", diff --git a/packages/postmaster/package.json b/packages/postmaster/package.json index f53e33ccc..b199607a9 100644 --- a/packages/postmaster/package.json +++ b/packages/postmaster/package.json @@ -34,7 +34,7 @@ "mailgun.js": "^10.2.3" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [ "mailgun", diff --git a/packages/query-builder/package.json b/packages/query-builder/package.json index 0ae8671be..ac407e39b 100644 --- a/packages/query-builder/package.json +++ b/packages/query-builder/package.json @@ -36,6 +36,6 @@ "database" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/packages/server-utils/package.json b/packages/server-utils/package.json index 0b290429b..f927445b8 100644 --- a/packages/server-utils/package.json +++ b/packages/server-utils/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^5.0.6", - "makage": "^0.1.10", + "makage": "^0.1.12", "ts-node": "^10.9.2" }, "keywords": [ diff --git a/packages/smtppostmaster/package.json b/packages/smtppostmaster/package.json index 9aa875932..476b81b22 100644 --- a/packages/smtppostmaster/package.json +++ b/packages/smtppostmaster/package.json @@ -38,7 +38,7 @@ "devDependencies": { "@types/nodemailer": "^7.0.5", "@types/smtp-server": "^3.5.12", - "makage": "^0.1.10", + "makage": "^0.1.12", "smtp-server": "^3.14.0", "ts-node": "^10.9.2" }, diff --git a/packages/url-domains/package.json b/packages/url-domains/package.json index df9912ca8..dd2079a8a 100644 --- a/packages/url-domains/package.json +++ b/packages/url-domains/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/express": "^5.0.6", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [] } diff --git a/pgpm/cli/package.json b/pgpm/cli/package.json index d94077b3c..61ce7fb35 100644 --- a/pgpm/cli/package.json +++ b/pgpm/cli/package.json @@ -40,7 +40,7 @@ "@types/semver": "^7.5.8", "@types/shelljs": "^0.8.16", "glob": "^13.0.0", - "makage": "^0.1.10", + "makage": "^0.1.12", "pg": "^8.17.1", "ts-node": "^10.9.2" }, diff --git a/pgpm/core/package.json b/pgpm/core/package.json index 57589c283..b50928021 100644 --- a/pgpm/core/package.json +++ b/pgpm/core/package.json @@ -45,7 +45,7 @@ "@types/pg": "^8.16.0", "copyfiles": "^2.4.1", "inquirerer": "^4.4.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/env": "workspace:^", diff --git a/pgpm/env/package.json b/pgpm/env/package.json index a390baeeb..a84708df0 100644 --- a/pgpm/env/package.json +++ b/pgpm/env/package.json @@ -41,6 +41,6 @@ "env" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/pgpm/logger/package.json b/pgpm/logger/package.json index 516ae71cd..6591116dd 100644 --- a/pgpm/logger/package.json +++ b/pgpm/logger/package.json @@ -40,6 +40,6 @@ "pgpmjs" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/pgpm/types/package.json b/pgpm/types/package.json index 9d48fd6f4..708f6324e 100644 --- a/pgpm/types/package.json +++ b/pgpm/types/package.json @@ -40,6 +40,6 @@ "migrations" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b329b3a2c..27e20e30b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,8 +53,8 @@ importers: specifier: ^8.2.3 version: 8.2.4(@types/node@20.19.27)(encoding@0.1.13) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 prettier: specifier: ^3.8.0 version: 3.8.0 @@ -99,8 +99,8 @@ importers: version: link:../../packages/smtppostmaster/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist functions/simple-email: @@ -122,8 +122,8 @@ importers: version: link:../../packages/smtppostmaster/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphile/graphile-cache: @@ -148,8 +148,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 nodemon: specifier: ^3.1.10 version: 3.1.11 @@ -198,8 +198,8 @@ importers: specifier: workspace:^ version: link:../graphile-test/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -224,8 +224,8 @@ importers: specifier: 15.10.1 version: 15.10.1 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -259,8 +259,8 @@ importers: specifier: 2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -282,8 +282,8 @@ importers: specifier: 2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -317,8 +317,8 @@ importers: specifier: workspace:^ version: link:../graphile-test/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pg: specifier: ^8.17.1 version: 8.17.1 @@ -355,8 +355,8 @@ importers: specifier: 15.10.1 version: 15.10.1 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pg: specifier: ^8.17.1 version: 8.17.1 @@ -393,8 +393,8 @@ importers: specifier: 15.10.1 version: 15.10.1 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -428,8 +428,8 @@ importers: specifier: workspace:^ version: link:../graphile-test/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -451,8 +451,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphile/graphile-search-plugin: @@ -477,8 +477,8 @@ importers: specifier: workspace:^ version: link:../graphile-test/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -575,8 +575,8 @@ importers: specifier: ^0.0.41 version: 0.0.41 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 nodemon: specifier: ^3.1.10 version: 3.1.11 @@ -601,8 +601,8 @@ importers: specifier: 2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -624,8 +624,8 @@ importers: version: 17.9.11 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 ts-jest: specifier: ^29.4.6 version: 29.4.6(@babel/core@7.28.6)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.6))(jest-util@30.2.0)(jest@30.2.0(@types/node@20.19.27)(ts-node@10.9.2(@types/node@20.19.27)(typescript@5.9.3)))(typescript@5.9.3) @@ -665,8 +665,8 @@ importers: specifier: 2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphile/graphile-upload-plugin: @@ -706,8 +706,8 @@ importers: specifier: ^2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -827,8 +827,8 @@ importers: version: 4.3.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/explorer: @@ -889,8 +889,8 @@ importers: specifier: ^8.0.12 version: 8.0.12 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 nodemon: specifier: ^3.1.10 version: 3.1.11 @@ -906,8 +906,8 @@ importers: version: 15.10.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/playwright-test: @@ -953,8 +953,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/query: @@ -973,8 +973,8 @@ importers: version: 3.0.2 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/react: @@ -1008,8 +1008,8 @@ importers: specifier: ^19.2.8 version: 19.2.8 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/server: @@ -1136,8 +1136,8 @@ importers: specifier: workspace:* version: link:../../graphile/graphile-test/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 nodemon: specifier: ^3.1.10 version: 3.1.11 @@ -1192,8 +1192,8 @@ importers: specifier: ^6.0.2 version: 6.0.3 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/test: @@ -1236,8 +1236,8 @@ importers: specifier: 2.12.6 version: 2.12.6(graphql@15.10.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist graphql/test-app: @@ -1311,8 +1311,8 @@ importers: version: 4.14.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/job-pg: @@ -1328,8 +1328,8 @@ importers: version: 8.17.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/job-scheduler: @@ -1348,8 +1348,8 @@ importers: version: 1.3.2 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/job-utils: @@ -1371,8 +1371,8 @@ importers: version: link:../../postgres/pg-env/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/job-worker: @@ -1388,8 +1388,8 @@ importers: version: 8.17.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/knative-job-example: @@ -1399,8 +1399,8 @@ importers: version: link:../knative-job-fn/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/knative-job-fn: @@ -1416,8 +1416,8 @@ importers: version: 5.2.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/knative-job-server: @@ -1442,8 +1442,8 @@ importers: version: 8.17.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist jobs/knative-job-service: @@ -1522,8 +1522,8 @@ importers: specifier: ^6.0.3 version: 6.0.3 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -1563,8 +1563,8 @@ importers: specifier: workspace:^ version: link:../../pgpm/core/dist makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../../postgres/pgsql-test/dist @@ -1580,8 +1580,8 @@ importers: specifier: ^20.12.7 version: 20.19.27 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) @@ -1666,8 +1666,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pg: specifier: ^8.17.1 version: 8.17.1 @@ -1686,8 +1686,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist packages/csrf: @@ -1696,8 +1696,8 @@ importers: specifier: ^20.12.7 version: 20.19.27 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) @@ -1728,8 +1728,8 @@ importers: specifier: ^4.0.9 version: 4.0.9 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-parser: specifier: ^17.9.11 version: 17.9.11 @@ -1745,8 +1745,8 @@ importers: specifier: ^20.12.7 version: 20.19.27 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) @@ -1762,8 +1762,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist packages/postmaster: @@ -1779,15 +1779,15 @@ importers: version: 10.4.0 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist packages/query-builder: devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist packages/server-utils: @@ -1815,8 +1815,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@20.19.27)(typescript@5.9.3) @@ -1841,8 +1841,8 @@ importers: specifier: ^3.5.12 version: 3.5.12 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 smtp-server: specifier: ^3.14.0 version: 3.18.0 @@ -1861,8 +1861,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist pgpm/cli: @@ -1938,8 +1938,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pg: specifier: ^8.17.1 version: 8.17.1 @@ -2012,8 +2012,8 @@ importers: specifier: ^4.4.0 version: 4.4.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist pgpm/env: @@ -2026,8 +2026,8 @@ importers: version: 4.3.1 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist pgpm/logger: @@ -2037,8 +2037,8 @@ importers: version: 0.2.0 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist pgpm/types: @@ -2048,8 +2048,8 @@ importers: version: link:../../postgres/pg-env/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/drizzle-orm-test: @@ -2068,8 +2068,8 @@ importers: specifier: ^0.45.1 version: 0.45.1(@types/pg@8.16.0)(pg@8.17.1) makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/introspectron: @@ -2094,8 +2094,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pgsql-test: specifier: workspace:^ version: link:../pgsql-test/dist @@ -2111,8 +2111,8 @@ importers: version: 0.2.0 devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 pg-proto-parser: specifier: ^1.30.4 version: 1.30.4 @@ -2143,8 +2143,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pg-codegen: @@ -2184,15 +2184,15 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pg-env: devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pg-query-context: @@ -2205,8 +2205,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pg-seed: @@ -2228,8 +2228,8 @@ importers: specifier: ^1.2.5 version: 1.2.5 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pgsql-client: @@ -2254,8 +2254,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pgsql-seed: @@ -2280,8 +2280,8 @@ importers: specifier: ^8.16.0 version: 8.16.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/pgsql-test: @@ -2324,8 +2324,8 @@ importers: specifier: ^1.2.5 version: 1.2.5 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist postgres/supabase-test: @@ -2344,8 +2344,8 @@ importers: version: link:../pgsql-test/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/content-type-stream: @@ -2361,15 +2361,15 @@ importers: version: link:../uuid-hash/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/etag-hash: devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/etag-stream: @@ -2382,8 +2382,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/mime-bytes: @@ -2392,8 +2392,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/s3-streamer: @@ -2421,8 +2421,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/s3-utils: @@ -2438,8 +2438,8 @@ importers: specifier: ^18.19.69 version: 18.19.130 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/stream-to-etag: @@ -2452,8 +2452,8 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/upload-names: @@ -2462,15 +2462,15 @@ importers: specifier: ^13.0.0 version: 13.0.0 makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/uuid-hash: devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist uploads/uuid-stream: @@ -2480,8 +2480,8 @@ importers: version: link:../uuid-hash/dist devDependencies: makage: - specifier: ^0.1.10 - version: 0.1.10 + specifier: ^0.1.12 + version: 0.1.12 publishDirectory: dist packages: @@ -3409,10 +3409,18 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} + '@isaacs/brace-expansion@5.0.1': + resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} + engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/cliui@9.0.0': + resolution: {integrity: sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==} + engines: {node: '>=18'} + '@isaacs/string-locale-compare@1.1.0': resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} @@ -6226,6 +6234,7 @@ packages: glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@13.0.0: @@ -6655,8 +6664,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.1.1: - resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + jackspeak@4.2.3: + resolution: {integrity: sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==} engines: {node: 20 || >=22} jake@10.9.4: @@ -7050,8 +7059,8 @@ packages: resolution: {integrity: sha512-YrdaZEAJwwjXGBTfZTNQ1LM7tmkdUaz2NpZEu7+zULcG4Wrlhd7cWSNZW0bxT3bP48k5N0mZWz8C2f9gc2+Geg==} engines: {node: '>=18.0.0'} - makage@0.1.10: - resolution: {integrity: sha512-IQKuRbHOrDgVNlydle+XRO5iMyaozBq4Bb9vhEzwxtvzyk08JkQo5qpfFRep0dSum53gECdX2gBoTmkWDHIfJA==} + makage@0.1.12: + resolution: {integrity: sha512-R3bITl50Ts2GzoaErywe8n24Iu2qbvbNOqOyidjDjh6iqK0CAj2VzIk3xRS4z8Q4xDQzaJrcb2+dGDjqRj6ChA==} hasBin: true make-dir@2.1.0: @@ -7157,6 +7166,10 @@ packages: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} + minimatch@10.1.2: + resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} + engines: {node: 20 || >=22} + minimatch@3.0.5: resolution: {integrity: sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==} @@ -10383,6 +10396,10 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@isaacs/brace-expansion@5.0.1': + dependencies: + '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -10392,6 +10409,8 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/cliui@9.0.0': {} + '@isaacs/string-locale-compare@1.1.0': {} '@istanbuljs/load-nyc-config@1.1.0': @@ -13782,8 +13801,8 @@ snapshots: glob@11.1.0: dependencies: foreground-child: 3.3.1 - jackspeak: 4.1.1 - minimatch: 10.1.1 + jackspeak: 4.2.3 + minimatch: 10.1.2 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.1 @@ -14261,9 +14280,9 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.1.1: + jackspeak@4.2.3: dependencies: - '@isaacs/cliui': 8.0.2 + '@isaacs/cliui': 9.0.0 jake@10.9.4: dependencies: @@ -14929,7 +14948,7 @@ snapshots: transitivePeerDependencies: - debug - makage@0.1.10: + makage@0.1.12: dependencies: glob: 11.1.0 yaml: 2.8.2 @@ -15034,6 +15053,10 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.0 + minimatch@10.1.2: + dependencies: + '@isaacs/brace-expansion': 5.0.1 + minimatch@3.0.5: dependencies: brace-expansion: 1.1.12 diff --git a/postgres/drizzle-orm-test/package.json b/postgres/drizzle-orm-test/package.json index 4b15c990f..0865d5ba1 100644 --- a/postgres/drizzle-orm-test/package.json +++ b/postgres/drizzle-orm-test/package.json @@ -48,7 +48,7 @@ "devDependencies": { "@types/pg": "^8.16.0", "drizzle-orm": "^0.45.1", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "pgsql-test": "workspace:^" diff --git a/postgres/introspectron/package.json b/postgres/introspectron/package.json index 7e47ca133..fe16057c1 100644 --- a/postgres/introspectron/package.json +++ b/postgres/introspectron/package.json @@ -32,7 +32,7 @@ "@constructive-io/graphql-test": "workspace:^", "@jest/test-sequencer": "^30.2.0", "@types/pg": "^8.16.0", - "makage": "^0.1.10", + "makage": "^0.1.12", "pgsql-test": "workspace:^" }, "dependencies": { diff --git a/postgres/pg-ast/package.json b/postgres/pg-ast/package.json index 1b6b54ef6..8339b76a2 100644 --- a/postgres/pg-ast/package.json +++ b/postgres/pg-ast/package.json @@ -37,7 +37,7 @@ "parse" ], "devDependencies": { - "makage": "^0.1.10", + "makage": "^0.1.12", "pg-proto-parser": "^1.30.4", "pgsql-deparser": "^17.17.2" }, diff --git a/postgres/pg-cache/package.json b/postgres/pg-cache/package.json index 489515ad9..f179ebb3c 100644 --- a/postgres/pg-cache/package.json +++ b/postgres/pg-cache/package.json @@ -37,7 +37,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [ "postgresql", diff --git a/postgres/pg-codegen/package.json b/postgres/pg-codegen/package.json index cdf47af28..a9d751a27 100644 --- a/postgres/pg-codegen/package.json +++ b/postgres/pg-codegen/package.json @@ -52,6 +52,6 @@ "devDependencies": { "@types/babel__generator": "^7.6.8", "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/postgres/pg-env/package.json b/postgres/pg-env/package.json index a81ee9a7c..6e643fdbc 100644 --- a/postgres/pg-env/package.json +++ b/postgres/pg-env/package.json @@ -36,6 +36,6 @@ "constructive" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/postgres/pg-query-context/package.json b/postgres/pg-query-context/package.json index 39986d9c2..2088cd8d0 100644 --- a/postgres/pg-query-context/package.json +++ b/postgres/pg-query-context/package.json @@ -33,7 +33,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "keywords": [ "postgresql", diff --git a/postgres/pg-seed/package.json b/postgres/pg-seed/package.json index ff4b9188d..9c5046856 100644 --- a/postgres/pg-seed/package.json +++ b/postgres/pg-seed/package.json @@ -44,7 +44,7 @@ "devDependencies": { "@types/pg": "^8.16.0", "@types/pg-copy-streams": "^1.2.5", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "csv-parse": "^6.1.0", diff --git a/postgres/pgsql-client/package.json b/postgres/pgsql-client/package.json index 8ff26be5f..55ce24842 100644 --- a/postgres/pgsql-client/package.json +++ b/postgres/pgsql-client/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/core": "workspace:^", diff --git a/postgres/pgsql-seed/package.json b/postgres/pgsql-seed/package.json index 59beede5d..91bfe8e11 100644 --- a/postgres/pgsql-seed/package.json +++ b/postgres/pgsql-seed/package.json @@ -43,7 +43,7 @@ }, "devDependencies": { "@types/pg": "^8.16.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/core": "workspace:^", diff --git a/postgres/pgsql-test/package.json b/postgres/pgsql-test/package.json index 2d6c8249a..f8bdb79d5 100644 --- a/postgres/pgsql-test/package.json +++ b/postgres/pgsql-test/package.json @@ -58,7 +58,7 @@ "@pgpmjs/core": "workspace:*", "@types/pg": "^8.16.0", "@types/pg-copy-streams": "^1.2.5", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@pgpmjs/env": "workspace:^", diff --git a/postgres/supabase-test/package.json b/postgres/supabase-test/package.json index ed28e7c37..f528dcf01 100644 --- a/postgres/supabase-test/package.json +++ b/postgres/supabase-test/package.json @@ -58,6 +58,6 @@ "pgsql-test": "workspace:^" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/content-type-stream/package.json b/uploads/content-type-stream/package.json index d8e5daf02..9f8c9e267 100644 --- a/uploads/content-type-stream/package.json +++ b/uploads/content-type-stream/package.json @@ -39,6 +39,6 @@ "middleware" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/etag-hash/package.json b/uploads/etag-hash/package.json index 299770226..fe3f1cdce 100644 --- a/uploads/etag-hash/package.json +++ b/uploads/etag-hash/package.json @@ -38,6 +38,6 @@ "md5" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/etag-stream/package.json b/uploads/etag-stream/package.json index 3b2eda77f..acb4ab74f 100644 --- a/uploads/etag-stream/package.json +++ b/uploads/etag-stream/package.json @@ -38,6 +38,6 @@ }, "devDependencies": { "glob": "^13.0.0", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/mime-bytes/package.json b/uploads/mime-bytes/package.json index 073102f3a..59d9970b2 100644 --- a/uploads/mime-bytes/package.json +++ b/uploads/mime-bytes/package.json @@ -31,6 +31,6 @@ "keywords": [], "devDependencies": { "glob": "^13.0.0", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/s3-streamer/package.json b/uploads/s3-streamer/package.json index ffd3d78c5..950be0d50 100644 --- a/uploads/s3-streamer/package.json +++ b/uploads/s3-streamer/package.json @@ -32,7 +32,7 @@ "@constructive-io/s3-utils": "workspace:^", "@pgpmjs/env": "workspace:^", "glob": "^13.0.0", - "makage": "^0.1.10" + "makage": "^0.1.12" }, "dependencies": { "@aws-sdk/client-s3": "^3.971.0", diff --git a/uploads/s3-utils/package.json b/uploads/s3-utils/package.json index a48ada548..7d9ea8479 100644 --- a/uploads/s3-utils/package.json +++ b/uploads/s3-utils/package.json @@ -35,6 +35,6 @@ }, "devDependencies": { "@types/node": "^18.19.69", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/stream-to-etag/package.json b/uploads/stream-to-etag/package.json index 9420bf33f..8dc5af025 100644 --- a/uploads/stream-to-etag/package.json +++ b/uploads/stream-to-etag/package.json @@ -39,6 +39,6 @@ }, "devDependencies": { "glob": "^13.0.0", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/upload-names/package.json b/uploads/upload-names/package.json index de91b8098..a92672735 100644 --- a/uploads/upload-names/package.json +++ b/uploads/upload-names/package.json @@ -31,6 +31,6 @@ "keywords": [], "devDependencies": { "glob": "^13.0.0", - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/uuid-hash/package.json b/uploads/uuid-hash/package.json index 3bd20720d..eced6bfaf 100644 --- a/uploads/uuid-hash/package.json +++ b/uploads/uuid-hash/package.json @@ -35,6 +35,6 @@ "uuid" ], "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } diff --git a/uploads/uuid-stream/package.json b/uploads/uuid-stream/package.json index 303751fd2..5b9a804e6 100644 --- a/uploads/uuid-stream/package.json +++ b/uploads/uuid-stream/package.json @@ -39,6 +39,6 @@ "uuid-hash": "workspace:^" }, "devDependencies": { - "makage": "^0.1.10" + "makage": "^0.1.12" } } From 6cce350b4e8079e10de87751621c4a16561f41f9 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 01:58:12 +0000 Subject: [PATCH 11/23] feat(codegen): add fire-and-forget overload for delete hooks (clientMutationId default) --- .../react-query-hooks.test.ts.snap | 84 +++++++++++++++++-- graphql/codegen/src/core/codegen/mutations.ts | 32 ++++++- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index fd0eeef96..6236bea78 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -917,7 +917,7 @@ import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; /** - * Mutation hook for deleting a User + * Mutation hook for deleting a User with typed selection * * @example * \`\`\`tsx @@ -945,8 +945,32 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; +/** + * Fire-and-forget mutation hook for deleting a User + * Returns only clientMutationId (no entity data selected) + * + * @example + * \`\`\`tsx + * const { mutate, isPending } = useDeleteUserMutation(); + * + * mutate({ id: 'value-to-delete' }); + * \`\`\` + */ +export function useDeleteUserMutation(params?: Omit, "mutationFn">): UseMutationResult<{ + deleteUser: { + clientMutationId: string; + }; +}, Error, { + id: string; +}>; export function useDeleteUserMutation(params: { - selection: SelectionConfig; + selection?: SelectionConfig; } & Omit, "mutationFn">) { @@ -1001,7 +1025,7 @@ import type { PostSelect, PostWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { PostSelect, PostWithRelations } from "../../orm/input-types"; /** - * Mutation hook for deleting a Post + * Mutation hook for deleting a Post with typed selection * * @example * \`\`\`tsx @@ -1029,8 +1053,32 @@ export function useDeletePostMutation(params: { }, Error, { id: string; }>; +/** + * Fire-and-forget mutation hook for deleting a Post + * Returns only clientMutationId (no entity data selected) + * + * @example + * \`\`\`tsx + * const { mutate, isPending } = useDeletePostMutation(); + * + * mutate({ id: 'value-to-delete' }); + * \`\`\` + */ +export function useDeletePostMutation(params?: Omit, "mutationFn">): UseMutationResult<{ + deletePost: { + clientMutationId: string; + }; +}, Error, { + id: string; +}>; export function useDeletePostMutation(params: { - selection: SelectionConfig; + selection?: SelectionConfig; } & Omit, "mutationFn">) { @@ -1083,7 +1131,7 @@ import type { UserSelect, UserWithRelations } from "../../orm/input-types"; import type { InferSelectResult, StrictSelect } from "../../orm/select-types"; export type { UserSelect, UserWithRelations } from "../../orm/input-types"; /** - * Mutation hook for deleting a User + * Mutation hook for deleting a User with typed selection * * @example * \`\`\`tsx @@ -1111,8 +1159,32 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; +/** + * Fire-and-forget mutation hook for deleting a User + * Returns only clientMutationId (no entity data selected) + * + * @example + * \`\`\`tsx + * const { mutate, isPending } = useDeleteUserMutation(); + * + * mutate({ id: 'value-to-delete' }); + * \`\`\` + */ +export function useDeleteUserMutation(params?: Omit, "mutationFn">): UseMutationResult<{ + deleteUser: { + clientMutationId: string; + }; +}, Error, { + id: string; +}>; export function useDeleteUserMutation(params: { - selection: SelectionConfig; + selection?: SelectionConfig; } & Omit, "mutationFn">) { diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 3814f15f3..ce29f23ed 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -443,6 +443,14 @@ export function generateDeleteMutationHook( const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + const clientMutationIdResultType = t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('clientMutationId'), t.tsTypeAnnotation(t.tsStringKeyword())) + ]) + )) + ]); + // Overload 1: with fields const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral([ @@ -457,7 +465,7 @@ export function generateDeleteMutationHook( useMutationResultType(resultType(sRef()), deleteVarType) ); addJSDocComment(o1, [ - `Mutation hook for deleting a ${typeName}`, + `Mutation hook for deleting a ${typeName} with typed selection`, '', '@example', '```tsx', @@ -470,8 +478,30 @@ export function generateDeleteMutationHook( ]); statements.push(o1); + // Overload 2: fire-and-forget (no selection, returns clientMutationId only) + const o2ParamType = useMutationOptionsType(clientMutationIdResultType, deleteVarType); + const o2 = exportDeclareFunction( + hookName, + null, + [createFunctionParam('params', o2ParamType, true)], + useMutationResultType(clientMutationIdResultType, deleteVarType) + ); + addJSDocComment(o2, [ + `Fire-and-forget mutation hook for deleting a ${typeName}`, + 'Returns only clientMutationId (no entity data selected)', + '', + '@example', + '```tsx', + `const { mutate, isPending } = ${hookName}();`, + '', + `mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`, + '```' + ]); + statements.push(o2); + // Implementation const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), deleteVarType]), ['mutationFn']) From 1485b96d43de1828f036eed68cfa4fa6f2e515e6 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 03:30:19 +0000 Subject: [PATCH 12/23] chore(ci): add examples/codegen-integration and clean workflows (remove ignores; separate examples job) --- .github/workflows/examples-integration.yaml | 22 +++++++++++++++++++ .github/workflows/notify-e2e.yml | 3 --- .github/workflows/run-tests.yaml | 8 +------ .../codegen-integration/codegen.config.ts | 14 ++++++++++++ examples/codegen-integration/package.json | 19 ++++++++++++++++ examples/codegen-integration/src/index.ts | 2 ++ examples/codegen-integration/tsconfig.json | 14 ++++++++++++ pnpm-workspace.yaml | 1 + 8 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/examples-integration.yaml create mode 100644 examples/codegen-integration/codegen.config.ts create mode 100644 examples/codegen-integration/package.json create mode 100644 examples/codegen-integration/src/index.ts create mode 100644 examples/codegen-integration/tsconfig.json diff --git a/.github/workflows/examples-integration.yaml b/.github/workflows/examples-integration.yaml new file mode 100644 index 000000000..41873a116 --- /dev/null +++ b/.github/workflows/examples-integration.yaml @@ -0,0 +1,22 @@ +name: Examples Integration +on: + pull_request: + branches: [ main, v1 ] + paths: + - 'examples/**' + - 'graphql/codegen/**' + workflow_dispatch: + +jobs: + examples-types: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - uses: pnpm/action-setup@v2 + with: + version: 10 + - run: pnpm install + - run: pnpm -r --filter @constructive-io/examples-codegen-integration run test:types diff --git a/.github/workflows/notify-e2e.yml b/.github/workflows/notify-e2e.yml index 7f9e6f3d6..e12a58c6a 100644 --- a/.github/workflows/notify-e2e.yml +++ b/.github/workflows/notify-e2e.yml @@ -6,9 +6,6 @@ name: Notify E2E Tests on: push: branches: [main] - paths-ignore: - - 'graphql/test-app/**' - - 'graphql/test-codegen-app/**' jobs: trigger-tests: diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 015753008..c7d75dfa1 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -4,16 +4,10 @@ on: branches: - main - v1 - paths-ignore: - - 'graphql/test-app/**' - - 'graphql/test-codegen-app/**' pull_request: branches: - main - v1 - paths-ignore: - - 'graphql/test-app/**' - - 'graphql/test-codegen-app/**' workflow_dispatch: workflow_call: @@ -197,7 +191,7 @@ jobs: - name: build run: | - pnpm -r --filter '!@constructive-io/test-codegen-app' run build + pnpm -r --filter '!./examples/**' run build - name: seed app_user run: | diff --git a/examples/codegen-integration/codegen.config.ts b/examples/codegen-integration/codegen.config.ts new file mode 100644 index 000000000..48301d8b4 --- /dev/null +++ b/examples/codegen-integration/codegen.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "@constructive-io/graphql-codegen"; + +export default defineConfig({ + endpoint: "http://example.local/graphql", // not used for fixture run + output: "src/generated", + reactQuery: true, + orm: true, + codegen: { + maxFieldDepth: 2, + skipQueryField: true + }, + // Use the example schema fixtures baked into the repo + schemaFile: "../../graphql/codegen/examples/example.schema.graphql" +}); diff --git a/examples/codegen-integration/package.json b/examples/codegen-integration/package.json new file mode 100644 index 000000000..f7c3864b4 --- /dev/null +++ b/examples/codegen-integration/package.json @@ -0,0 +1,19 @@ +{ + "name": "@constructive-io/examples-codegen-integration", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "codegen": "tsx ../../graphql/codegen/src/cli/index.ts --config codegen.config.ts", + "test:types": "pnpm run codegen && tsc --noEmit" + }, + "dependencies": { + "graphql": "15.10.1" + }, + "devDependencies": { + "@constructive-io/graphql-codegen": "workspace:^", + "@constructive-io/graphql-types": "workspace:^", + "tsx": "^4.20.3", + "typescript": "^5.1.6" + } +} diff --git a/examples/codegen-integration/src/index.ts b/examples/codegen-integration/src/index.ts new file mode 100644 index 000000000..c41f590cc --- /dev/null +++ b/examples/codegen-integration/src/index.ts @@ -0,0 +1,2 @@ +// Placeholder file so tsc has something to include +export {}; diff --git a/examples/codegen-integration/tsconfig.json b/examples/codegen-integration/tsconfig.json new file mode 100644 index 000000000..48f02b77a --- /dev/null +++ b/examples/codegen-integration/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "jsx": "react-jsx", + "skipLibCheck": true, + "noEmit": true, + "baseUrl": ".", + "paths": {} + }, + "include": ["src", "codegen.config.ts"] +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 05b598ac6..d612388cd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - 'packages/*' - 'pgpm/*' - 'graphql/*' + - 'examples/*' - 'uploads/*' - 'postgres/*' - 'graphile/*' From c4ecef3bcc121225c0420e0cad8274e130fe92c3 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 03:36:20 +0000 Subject: [PATCH 13/23] chore: fix codegen runner, add missing deps, update build filters for examples --- .github/workflows/examples-integration.yaml | 2 +- examples/codegen-integration/.gitignore | 1 + examples/codegen-integration/package.json | 13 ++++-- .../scripts/codegen-runner.ts | 26 ++++++++++++ package.json | 4 +- pnpm-lock.yaml | 40 ++++++++++++++++++- 6 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 examples/codegen-integration/.gitignore create mode 100644 examples/codegen-integration/scripts/codegen-runner.ts diff --git a/.github/workflows/examples-integration.yaml b/.github/workflows/examples-integration.yaml index 41873a116..de0fd19a6 100644 --- a/.github/workflows/examples-integration.yaml +++ b/.github/workflows/examples-integration.yaml @@ -19,4 +19,4 @@ jobs: with: version: 10 - run: pnpm install - - run: pnpm -r --filter @constructive-io/examples-codegen-integration run test:types + - run: pnpm -r --filter @constructive-io/examples-codegen-integration run test:codegen diff --git a/examples/codegen-integration/.gitignore b/examples/codegen-integration/.gitignore new file mode 100644 index 000000000..d3db8f9b1 --- /dev/null +++ b/examples/codegen-integration/.gitignore @@ -0,0 +1 @@ +src/generated/ diff --git a/examples/codegen-integration/package.json b/examples/codegen-integration/package.json index f7c3864b4..d7990a228 100644 --- a/examples/codegen-integration/package.json +++ b/examples/codegen-integration/package.json @@ -2,13 +2,18 @@ "name": "@constructive-io/examples-codegen-integration", "version": "0.0.0", "private": true, - "type": "module", "scripts": { - "codegen": "tsx ../../graphql/codegen/src/cli/index.ts --config codegen.config.ts", - "test:types": "pnpm run codegen && tsc --noEmit" + "codegen": "tsx scripts/codegen-runner.ts", + "test:types": "pnpm run codegen && tsc --noEmit", + "test:codegen": "pnpm run codegen" }, "dependencies": { - "graphql": "15.10.1" + "@0no-co/graphql.web": "^1.2.0", + "@tanstack/react-query": "^5.90.20", + "gql-ast": "workspace:^", + "graphql": "15.10.1", + "react": "^19.2.3", + "react-dom": "^19.2.3" }, "devDependencies": { "@constructive-io/graphql-codegen": "workspace:^", diff --git a/examples/codegen-integration/scripts/codegen-runner.ts b/examples/codegen-integration/scripts/codegen-runner.ts new file mode 100644 index 000000000..38ad8e9d4 --- /dev/null +++ b/examples/codegen-integration/scripts/codegen-runner.ts @@ -0,0 +1,26 @@ +import path from 'path'; +import { generate } from '../../../graphql/codegen/src/core/generate'; + +const root = path.resolve(__dirname, '..'); + +async function run() { + const result = await generate({ + schemaFile: path.resolve(root, '../../graphql/codegen/examples/example.schema.graphql'), + output: path.resolve(root, 'src/generated'), + reactQuery: true, + orm: true, + codegen: { + maxFieldDepth: 2, + skipQueryField: true + } + }); + + if (!result.success) { + console.error(result.message); + process.exit(1); + } + + console.log(result.message); +} + +run(); diff --git a/package.json b/package.json index 9d280286c..81430d81a 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ }, "scripts": { "clean": "pnpm -r run clean", - "build": "pnpm -r --filter '!@constructive-io/test-codegen-app' run build", - "build:dev": "pnpm -r --filter '!@constructive-io/test-codegen-app' run build:dev", + "build": "pnpm -r --filter '!./examples/**' run build", + "build:dev": "pnpm -r --filter '!./examples/**' run build:dev", "lint": "pnpm -r run lint", "internal:deps": "makage update-workspace", "deps": "pnpm up -r -i -L" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 27e20e30b..95c7ea774 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -68,6 +68,40 @@ importers: specifier: ^5.1.6 version: 5.9.3 + examples/codegen-integration: + dependencies: + '@0no-co/graphql.web': + specifier: ^1.2.0 + version: 1.2.0(graphql@15.10.1) + '@tanstack/react-query': + specifier: ^5.90.20 + version: 5.90.20(react@19.2.3) + gql-ast: + specifier: workspace:^ + version: link:../../graphql/gql-ast/dist + graphql: + specifier: 15.10.1 + version: 15.10.1 + react: + specifier: ^19.2.3 + version: 19.2.3 + react-dom: + specifier: ^19.2.3 + version: 19.2.3(react@19.2.3) + devDependencies: + '@constructive-io/graphql-codegen': + specifier: workspace:^ + version: link:../../graphql/codegen/dist + '@constructive-io/graphql-types': + specifier: workspace:^ + version: link:../../graphql/types/dist + tsx: + specifier: ^4.20.3 + version: 4.21.0 + typescript: + specifier: ^5.1.6 + version: 5.9.3 + functions/send-email-link: dependencies: '@constructive-io/knative-job-fn': @@ -6229,6 +6263,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true glob@11.1.0: @@ -6243,11 +6278,12 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me glob@9.3.5: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} @@ -8533,7 +8569,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me temp-dir@1.0.0: resolution: {integrity: sha512-xZFXEGbG7SNC3itwBzI3RYjq/cEhBkx2hJuKGIUOcEULmkQExXiHat2z/qkISYsuR+IKumhEfKKbV5qXmhICFQ==} From fe2308aea7913ce93a3b9004a0b69e1bffed2177 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 03:41:24 +0000 Subject: [PATCH 14/23] fix: keep test-codegen-app exclusion alongside examples filter in build --- .github/workflows/run-tests.yaml | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index c7d75dfa1..96a9d7d83 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -191,7 +191,7 @@ jobs: - name: build run: | - pnpm -r --filter '!./examples/**' run build + pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build - name: seed app_user run: | diff --git a/package.json b/package.json index 81430d81a..9ff59a85d 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ }, "scripts": { "clean": "pnpm -r run clean", - "build": "pnpm -r --filter '!./examples/**' run build", - "build:dev": "pnpm -r --filter '!./examples/**' run build:dev", + "build": "pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build", + "build:dev": "pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build:dev", "lint": "pnpm -r run lint", "internal:deps": "makage update-workspace", "deps": "pnpm up -r -i -L" From 1f2d244b59895b1a0669f83f0893f61504d7b27c Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 03:49:25 +0000 Subject: [PATCH 15/23] fix(ci): add build step to examples-integration workflow for workspace deps --- .github/workflows/examples-integration.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/examples-integration.yaml b/.github/workflows/examples-integration.yaml index de0fd19a6..e64e5daa4 100644 --- a/.github/workflows/examples-integration.yaml +++ b/.github/workflows/examples-integration.yaml @@ -19,4 +19,6 @@ jobs: with: version: 10 - run: pnpm install + - name: Build codegen and dependencies + run: pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build - run: pnpm -r --filter @constructive-io/examples-codegen-integration run test:codegen From a9bf4dd3b893c757ad724b9ea68078f421ff7345 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 05:45:57 +0000 Subject: [PATCH 16/23] chore(ci): remove all paths-ignore and build filter hacks; rename test-codegen-app build to build:app --- .github/workflows/docker-launchql.yaml | 6 ------ .github/workflows/examples-integration.yaml | 2 +- .github/workflows/run-tests.yaml | 3 +-- graphql/test-app/package.json | 2 +- package.json | 4 ++-- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/workflows/docker-launchql.yaml b/.github/workflows/docker-launchql.yaml index abf4d3196..13856efc6 100644 --- a/.github/workflows/docker-launchql.yaml +++ b/.github/workflows/docker-launchql.yaml @@ -6,17 +6,11 @@ on: - main - v1 - release/* - paths-ignore: - - 'graphql/test-app/**' - - 'graphql/test-codegen-app/**' pull_request: branches: - main - v1 types: [opened, reopened, synchronize, ready_for_review] - paths-ignore: - - 'graphql/test-app/**' - - 'graphql/test-codegen-app/**' workflow_dispatch: {} concurrency: diff --git a/.github/workflows/examples-integration.yaml b/.github/workflows/examples-integration.yaml index e64e5daa4..418e9396e 100644 --- a/.github/workflows/examples-integration.yaml +++ b/.github/workflows/examples-integration.yaml @@ -20,5 +20,5 @@ jobs: version: 10 - run: pnpm install - name: Build codegen and dependencies - run: pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build + run: pnpm run build - run: pnpm -r --filter @constructive-io/examples-codegen-integration run test:codegen diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 96a9d7d83..3142fb930 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -190,8 +190,7 @@ jobs: run: pnpm install - name: build - run: | - pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build + run: pnpm run build - name: seed app_user run: | diff --git a/graphql/test-app/package.json b/graphql/test-app/package.json index 139ad00f0..a1a3619f5 100644 --- a/graphql/test-app/package.json +++ b/graphql/test-app/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "dev": "vite", - "build": "tsc -b && vite build", + "build:app": "tsc -b && vite build", "preview": "vite preview", "codegen": "tsx ../codegen/src/cli/index.ts --config codegen.config.ts", "codegen:orm": "tsx ../codegen/src/cli/index.ts --endpoint http://api.localhost:3000/graphql --output src/generated --orm", diff --git a/package.json b/package.json index 9ff59a85d..9c215bbf4 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ }, "scripts": { "clean": "pnpm -r run clean", - "build": "pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build", - "build:dev": "pnpm -r --filter '!./examples/**' --filter '!@constructive-io/test-codegen-app' run build:dev", + "build": "pnpm -r run build", + "build:dev": "pnpm -r run build:dev", "lint": "pnpm -r run lint", "internal:deps": "makage update-workspace", "deps": "pnpm up -r -i -L" From fd0f8e275f5f77a719595f41635b997d4a288573 Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 05:48:38 +0000 Subject: [PATCH 17/23] chore: remove redundant codegen options (already defaults) --- examples/codegen-integration/scripts/codegen-runner.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/codegen-integration/scripts/codegen-runner.ts b/examples/codegen-integration/scripts/codegen-runner.ts index 38ad8e9d4..1c4f62066 100644 --- a/examples/codegen-integration/scripts/codegen-runner.ts +++ b/examples/codegen-integration/scripts/codegen-runner.ts @@ -8,11 +8,7 @@ async function run() { schemaFile: path.resolve(root, '../../graphql/codegen/examples/example.schema.graphql'), output: path.resolve(root, 'src/generated'), reactQuery: true, - orm: true, - codegen: { - maxFieldDepth: 2, - skipQueryField: true - } + orm: true }); if (!result.success) { From 5c0b123249d0d7a05356121e4723d47d0876c60c Mon Sep 17 00:00:00 2001 From: Dan Lynch Date: Sun, 8 Feb 2026 06:11:18 +0000 Subject: [PATCH 18/23] refactor(codegen): remove dead maxFieldDepth code and enforce trailing commas --- .../client-generator.test.ts.snap | 38 +- .../codegen/client-generator.test.ts | 50 +- .../codegen/input-types-generator.test.ts | 237 +++-- .../__tests__/codegen/model-generator.test.ts | 40 +- .../__tests__/codegen/query-builder.test.ts | 262 +++--- .../codegen/query-keys-factory.test.ts | 167 ++-- .../codegen/react-query-hooks.test.ts | 203 +++-- .../codegen/react-query-optional.test.ts | 158 +++- .../src/__tests__/codegen/scalars.test.ts | 10 +- .../codegen/schema-types-generator.test.ts | 107 ++- .../src/__tests__/codegen/utils.test.ts | 68 +- .../__tests__/config/resolve-config.test.ts | 30 +- .../__tests__/introspect/infer-tables.test.ts | 306 +++---- graphql/codegen/src/cli/index.ts | 63 +- graphql/codegen/src/cli/shared.ts | 81 +- graphql/codegen/src/client/error.ts | 174 ++-- graphql/codegen/src/client/execute.ts | 36 +- graphql/codegen/src/client/index.ts | 9 +- graphql/codegen/src/client/typed-document.ts | 2 +- graphql/codegen/src/core/ast.ts | 345 ++++---- graphql/codegen/src/core/codegen/babel-ast.ts | 28 +- graphql/codegen/src/core/codegen/barrel.ts | 24 +- graphql/codegen/src/core/codegen/client.ts | 9 +- .../src/core/codegen/custom-mutations.ts | 233 +++-- .../src/core/codegen/custom-queries.ts | 726 +++++++++++---- graphql/codegen/src/core/codegen/hooks-ast.ts | 338 ++++--- graphql/codegen/src/core/codegen/index.ts | 83 +- .../codegen/src/core/codegen/invalidation.ts | 260 ++++-- .../codegen/src/core/codegen/mutation-keys.ts | 114 ++- graphql/codegen/src/core/codegen/mutations.ts | 588 +++++++++---- .../codegen/src/core/codegen/orm/barrel.ts | 19 +- .../src/core/codegen/orm/client-generator.ts | 108 +-- .../codegen/src/core/codegen/orm/client.ts | 26 +- .../core/codegen/orm/custom-ops-generator.ts | 222 +++-- graphql/codegen/src/core/codegen/orm/index.ts | 74 +- .../core/codegen/orm/input-types-generator.ts | 377 ++++---- .../src/core/codegen/orm/model-generator.ts | 825 +++++++++++++----- .../src/core/codegen/orm/select-types.ts | 22 +- graphql/codegen/src/core/codegen/queries.ts | 661 ++++++++++---- .../codegen/src/core/codegen/query-keys.ts | 331 ++++--- graphql/codegen/src/core/codegen/scalars.ts | 22 +- .../core/codegen/schema-types-generator.ts | 62 +- .../src/core/codegen/select-helpers.ts | 14 +- graphql/codegen/src/core/codegen/selection.ts | 9 +- .../codegen/src/core/codegen/shared/index.ts | 26 +- .../core/codegen/templates/hooks-client.ts | 2 +- .../core/codegen/templates/hooks-selection.ts | 13 +- .../src/core/codegen/templates/orm-client.ts | 28 +- .../core/codegen/templates/query-builder.ts | 356 ++++---- .../core/codegen/templates/select-types.ts | 10 +- .../codegen/src/core/codegen/type-resolver.ts | 126 ++- graphql/codegen/src/core/codegen/types.ts | 148 +++- graphql/codegen/src/core/codegen/utils.ts | 14 +- graphql/codegen/src/core/config/index.ts | 4 +- graphql/codegen/src/core/config/loader.ts | 14 +- graphql/codegen/src/core/config/resolver.ts | 28 +- graphql/codegen/src/core/custom-ast.ts | 50 +- graphql/codegen/src/core/database/index.ts | 6 +- graphql/codegen/src/core/generate.ts | 73 +- graphql/codegen/src/core/index.ts | 4 +- .../src/core/introspect/fetch-schema.ts | 32 +- graphql/codegen/src/core/introspect/index.ts | 8 +- .../src/core/introspect/infer-tables.ts | 86 +- .../src/core/introspect/schema-query.ts | 8 +- .../src/core/introspect/source/api-schemas.ts | 28 +- .../src/core/introspect/source/database.ts | 20 +- .../src/core/introspect/source/endpoint.ts | 8 +- .../src/core/introspect/source/file.ts | 10 +- .../src/core/introspect/source/index.ts | 124 +-- .../src/core/introspect/source/pgpm-module.ts | 36 +- .../src/core/introspect/source/types.ts | 2 +- .../src/core/introspect/transform-schema.ts | 58 +- .../codegen/src/core/introspect/transform.ts | 6 +- .../codegen/src/core/meta-object/convert.ts | 20 +- .../codegen/src/core/meta-object/validate.ts | 8 +- graphql/codegen/src/core/output/index.ts | 2 +- graphql/codegen/src/core/output/writer.ts | 24 +- graphql/codegen/src/core/pipeline/index.ts | 35 +- graphql/codegen/src/core/query-builder.ts | 135 ++- graphql/codegen/src/core/types.ts | 17 +- graphql/codegen/src/core/watch/cache.ts | 4 +- graphql/codegen/src/core/watch/debounce.ts | 8 +- graphql/codegen/src/core/watch/hash.ts | 2 +- graphql/codegen/src/core/watch/index.ts | 8 +- .../codegen/src/core/watch/orchestrator.ts | 60 +- graphql/codegen/src/core/watch/poller.ts | 22 +- .../codegen/src/generators/field-selector.ts | 134 ++- graphql/codegen/src/generators/index.ts | 6 +- graphql/codegen/src/generators/mutations.ts | 98 +-- graphql/codegen/src/generators/select.ts | 246 +++--- graphql/codegen/src/index.ts | 6 +- graphql/codegen/src/types/config.ts | 26 +- graphql/codegen/src/types/index.ts | 15 +- graphql/codegen/src/types/introspection.ts | 6 +- 94 files changed, 6196 insertions(+), 3575 deletions(-) diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap index df8457471..383dbaa7f 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/client-generator.test.ts.snap @@ -107,13 +107,13 @@ exports[`client-generator generateOrmClientFile generates OrmClient class with e import type { GraphQLAdapter, GraphQLError, - QueryResult + QueryResult, } from '@constructive-io/graphql-types'; export type { GraphQLAdapter, GraphQLError, - QueryResult + QueryResult, } from '@constructive-io/graphql-types'; /** @@ -125,33 +125,35 @@ export class FetchAdapter implements GraphQLAdapter { constructor( private endpoint: string, - headers?: Record + headers?: Record, ) { this.headers = headers ?? {}; } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { const response = await fetch(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers + ...this.headers, }, body: JSON.stringify({ query: document, - variables: variables ?? {} - }) + variables: variables ?? {}, + }), }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: \`HTTP \${response.status}: \${response.statusText}\` }] + errors: [ + { message: \`HTTP \${response.status}: \${response.statusText}\` }, + ], }; } @@ -164,14 +166,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors + errors: json.errors, }; } return { ok: true, data: json.data as T, - errors: undefined + errors: undefined, }; } @@ -204,7 +206,7 @@ export interface OrmClientConfig { export class GraphQLRequestError extends Error { constructor( public readonly errors: GraphQLError[], - public readonly data: unknown = null + public readonly data: unknown = null, ) { const messages = errors.map((e) => e.message).join('; '); super(\`GraphQL Error: \${messages}\`); @@ -222,14 +224,14 @@ export class OrmClient { this.adapter = new FetchAdapter(config.endpoint, config.headers); } else { throw new Error( - 'OrmClientConfig requires either an endpoint or a custom adapter' + 'OrmClientConfig requires either an endpoint or a custom adapter', ); } } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { return this.adapter.execute(document, variables); } @@ -304,7 +306,7 @@ export interface UpdateArgs { export type FindOneArgs< TSelect, TIdName extends string = 'id', - TId = string + TId = string, > = { select?: TSelect; } & Record; @@ -337,9 +339,13 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Extract extends { select?: infer ShapeNS } + ? Extract extends { + select?: infer ShapeNS; + } ? DeepExact< - Omit & { select: DeepExact> }, + Omit & { + select: DeepExact>; + }, Extract > : never diff --git a/graphql/codegen/src/__tests__/codegen/client-generator.test.ts b/graphql/codegen/src/__tests__/codegen/client-generator.test.ts index 3bd0fbc02..aa8f54b04 100644 --- a/graphql/codegen/src/__tests__/codegen/client-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/client-generator.test.ts @@ -7,9 +7,13 @@ import { generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile + generateSelectTypesFile, } from '../../core/codegen/orm/client-generator'; -import type { CleanFieldType, CleanRelations,CleanTable } from '../../types/schema'; +import type { + CleanFieldType, + CleanRelations, + CleanTable, +} from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -17,24 +21,26 @@ import type { CleanFieldType, CleanRelations,CleanTable } from '../../types/sche const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, - string: { gqlType: 'String', isArray: false } as CleanFieldType + string: { gqlType: 'String', isArray: false } as CleanFieldType, }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } @@ -91,13 +97,25 @@ describe('client-generator', () => { createTable({ name: 'User', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' } + query: { + all: 'users', + one: 'user', + create: 'createUser', + update: 'updateUser', + delete: 'deleteUser', + }, }), createTable({ name: 'Post', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', delete: 'deletePost' } - }) + query: { + all: 'posts', + one: 'post', + create: 'createPost', + update: 'updatePost', + delete: 'deletePost', + }, + }), ]; const result = generateCreateClientFile(tables, false, false); @@ -114,8 +132,14 @@ describe('client-generator', () => { createTable({ name: 'User', fields: [{ name: 'id', type: fieldTypes.uuid }], - query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', delete: 'deleteUser' } - }) + query: { + all: 'users', + one: 'user', + create: 'createUser', + update: 'updateUser', + delete: 'deleteUser', + }, + }), ]; const result = generateCreateClientFile(tables, true, true); @@ -124,7 +148,9 @@ describe('client-generator', () => { expect(result.content).toContain('createQueryOperations'); expect(result.content).toContain('createMutationOperations'); expect(result.content).toContain('query: createQueryOperations(client)'); - expect(result.content).toContain('mutation: createMutationOperations(client)'); + expect(result.content).toContain( + 'mutation: createMutationOperations(client)', + ); }); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts index 241a7ae69..d8bf2a8a7 100644 --- a/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/input-types-generator.test.ts @@ -6,7 +6,11 @@ * used to validate the AST-based migration produces equivalent results. */ // Jest globals - no import needed -import { collectInputTypeNames, collectPayloadTypeNames,generateInputTypesFile } from '../../core/codegen/orm/input-types-generator'; +import { + collectInputTypeNames, + collectPayloadTypeNames, + generateInputTypesFile, +} from '../../core/codegen/orm/input-types-generator'; import type { CleanArgument, CleanFieldType, @@ -14,7 +18,7 @@ import type { CleanTable, CleanTypeRef, ResolvedType, - TypeRegistry + TypeRegistry, } from '../../types/schema'; // ============================================================================ @@ -32,7 +36,7 @@ const fieldTypes = { json: { gqlType: 'JSON', isArray: false } as CleanFieldType, bigint: { gqlType: 'BigInt', isArray: false } as CleanFieldType, stringArray: { gqlType: 'String', isArray: true } as CleanFieldType, - intArray: { gqlType: 'Int', isArray: true } as CleanFieldType + intArray: { gqlType: 'Int', isArray: true } as CleanFieldType, }; // ============================================================================ @@ -43,17 +47,19 @@ const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } @@ -61,7 +67,11 @@ function createTypeRegistry(types: Record): TypeRegistry { return new Map(Object.entries(types)); } -function createTypeRef(kind: CleanTypeRef['kind'], name: string | null, ofType?: CleanTypeRef): CleanTypeRef { +function createTypeRef( + kind: CleanTypeRef['kind'], + name: string | null, + ofType?: CleanTypeRef, +): CleanTypeRef { return { kind, name, ofType }; } @@ -89,15 +99,15 @@ const userTable = createTable({ { name: 'age', type: fieldTypes.int }, { name: 'isActive', type: fieldTypes.boolean }, { name: 'createdAt', type: fieldTypes.datetime }, - { name: 'metadata', type: fieldTypes.json } + { name: 'metadata', type: fieldTypes.json }, ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser' - } + delete: 'deleteUser', + }, }); /** @@ -111,7 +121,7 @@ const postTable = createTable({ { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, { name: 'publishedAt', type: fieldTypes.datetime }, - { name: 'tags', type: fieldTypes.stringArray } + { name: 'tags', type: fieldTypes.stringArray }, ], relations: { belongsTo: [ @@ -120,8 +130,8 @@ const postTable = createTable({ isUnique: false, referencesTable: 'User', type: null, - keys: [{ name: 'authorId', type: fieldTypes.uuid }] - } + keys: [{ name: 'authorId', type: fieldTypes.uuid }], + }, ], hasOne: [], hasMany: [ @@ -130,18 +140,18 @@ const postTable = createTable({ isUnique: false, referencedByTable: 'Comment', type: null, - keys: [] - } + keys: [], + }, ], - manyToMany: [] + manyToMany: [], }, query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost' - } + delete: 'deletePost', + }, }); /** @@ -154,7 +164,7 @@ const commentTable = createTable({ { name: 'body', type: fieldTypes.string }, { name: 'postId', type: fieldTypes.uuid }, { name: 'authorId', type: fieldTypes.uuid }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], relations: { belongsTo: [ @@ -163,27 +173,27 @@ const commentTable = createTable({ isUnique: false, referencesTable: 'Post', type: null, - keys: [] + keys: [], }, { fieldName: 'author', isUnique: false, referencesTable: 'User', type: null, - keys: [] - } + keys: [], + }, ], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }, query: { all: 'comments', one: 'comment', create: 'createComment', update: 'updateComment', - delete: 'deleteComment' - } + delete: 'deleteComment', + }, }); /** @@ -200,18 +210,18 @@ const userTableWithRelations = createTable({ isUnique: false, referencedByTable: 'Post', type: null, - keys: [] + keys: [], }, { fieldName: 'comments', isUnique: false, referencedByTable: 'Comment', type: null, - keys: [] - } + keys: [], + }, ], - manyToMany: [] - } + manyToMany: [], + }, }); /** @@ -222,7 +232,7 @@ const categoryTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'slug', type: fieldTypes.string } + { name: 'slug', type: fieldTypes.string }, ], relations: { belongsTo: [], @@ -233,17 +243,17 @@ const categoryTable = createTable({ fieldName: 'posts', rightTable: 'Post', junctionTable: 'PostCategory', - type: null - } - ] + type: null, + }, + ], }, query: { all: 'categories', one: 'category', create: 'createCategory', update: 'updateCategory', - delete: 'deleteCategory' - } + delete: 'deleteCategory', + }, }); /** @@ -255,7 +265,7 @@ const profileTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'bio', type: fieldTypes.string }, { name: 'userId', type: fieldTypes.uuid }, - { name: 'avatarUrl', type: fieldTypes.string } + { name: 'avatarUrl', type: fieldTypes.string }, ], relations: { belongsTo: [ @@ -264,20 +274,20 @@ const profileTable = createTable({ isUnique: true, referencesTable: 'User', type: null, - keys: [] - } + keys: [], + }, ], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }, query: { all: 'profiles', one: 'profile', create: 'createProfile', update: 'updateProfile', - delete: 'deleteProfile' - } + delete: 'deleteProfile', + }, }); // User with hasOne to profile @@ -291,8 +301,8 @@ const userTableWithProfile = createTable({ isUnique: true, referencedByTable: 'Profile', type: null, - keys: [] - } + keys: [], + }, ], hasMany: [ { @@ -300,11 +310,11 @@ const userTableWithProfile = createTable({ isUnique: false, referencedByTable: 'Post', type: null, - keys: [] - } + keys: [], + }, ], - manyToMany: [] - } + manyToMany: [], + }, }); // ============================================================================ @@ -317,23 +327,29 @@ const sampleTypeRegistry = createTypeRegistry({ name: 'LoginInput', inputFields: [ { name: 'email', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'rememberMe', type: createTypeRef('SCALAR', 'Boolean') } - ] + { + name: 'password', + type: createNonNull(createTypeRef('SCALAR', 'String')), + }, + { name: 'rememberMe', type: createTypeRef('SCALAR', 'Boolean') }, + ], }, RegisterInput: { kind: 'INPUT_OBJECT', name: 'RegisterInput', inputFields: [ { name: 'email', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createNonNull(createTypeRef('SCALAR', 'String')) }, - { name: 'name', type: createTypeRef('SCALAR', 'String') } - ] + { + name: 'password', + type: createNonNull(createTypeRef('SCALAR', 'String')), + }, + { name: 'name', type: createTypeRef('SCALAR', 'String') }, + ], }, UserRole: { kind: 'ENUM', name: 'UserRole', - enumValues: ['ADMIN', 'USER', 'GUEST'] + enumValues: ['ADMIN', 'USER', 'GUEST'], }, LoginPayload: { kind: 'OBJECT', @@ -341,9 +357,9 @@ const sampleTypeRegistry = createTypeRegistry({ fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, { name: 'user', type: createTypeRef('OBJECT', 'User') }, - { name: 'expiresAt', type: createTypeRef('SCALAR', 'Datetime') } - ] - } + { name: 'expiresAt', type: createTypeRef('SCALAR', 'Datetime') }, + ], + }, }); // ============================================================================ @@ -376,14 +392,21 @@ describe('generateInputTypesFile', () => { it('generates custom input types from TypeRegistry', () => { const usedInputTypes = new Set(['LoginInput', 'RegisterInput', 'UserRole']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, [userTable]); + const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, [ + userTable, + ]); expect(result.content).toMatchSnapshot(); }); it('generates payload types for custom operations', () => { const usedInputTypes = new Set(['LoginInput']); const usedPayloadTypes = new Set(['LoginPayload']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, [userTable], usedPayloadTypes); + const result = generateInputTypesFile( + sampleTypeRegistry, + usedInputTypes, + [userTable], + usedPayloadTypes, + ); expect(result.content).toMatchSnapshot(); }); @@ -447,20 +470,29 @@ describe('entity types', () => { expect(result.content).toContain('email?: string | null;'); expect(result.content).toContain('age?: number | null;'); expect(result.content).toContain('isActive?: boolean | null;'); - expect(result.content).toContain('metadata?: Record | null;'); + expect(result.content).toContain( + 'metadata?: Record | null;', + ); }); it('generates entity relations interface', () => { - const result = generateInputTypesFile(new Map(), new Set(), [userTableWithRelations, postTable]); + const result = generateInputTypesFile(new Map(), new Set(), [ + userTableWithRelations, + postTable, + ]); expect(result.content).toContain('export interface UserRelations {'); expect(result.content).toContain('posts?: ConnectionResult;'); }); it('generates WithRelations type alias', () => { - const result = generateInputTypesFile(new Map(), new Set(), [userTableWithRelations]); + const result = generateInputTypesFile(new Map(), new Set(), [ + userTableWithRelations, + ]); - expect(result.content).toContain('export type UserWithRelations = User & UserRelations;'); + expect(result.content).toContain( + 'export type UserWithRelations = User & UserRelations;', + ); }); }); @@ -479,7 +511,10 @@ describe('entity select types', () => { }); it('generates select type with belongsTo relation options', () => { - const result = generateInputTypesFile(new Map(), new Set(), [postTable, userTable]); + const result = generateInputTypesFile(new Map(), new Set(), [ + postTable, + userTable, + ]); expect(result.content).toContain('export type PostSelect = {'); // Babel generates multi-line format for object types @@ -488,7 +523,10 @@ describe('entity select types', () => { }); it('generates select type with hasMany relation options', () => { - const result = generateInputTypesFile(new Map(), new Set(), [userTableWithRelations, postTable]); + const result = generateInputTypesFile(new Map(), new Set(), [ + userTableWithRelations, + postTable, + ]); expect(result.content).toContain('posts?: boolean | {'); expect(result.content).toContain('select?: PostSelect;'); @@ -498,7 +536,10 @@ describe('entity select types', () => { }); it('generates select type with manyToMany relation options', () => { - const result = generateInputTypesFile(new Map(), new Set(), [categoryTable, postTable]); + const result = generateInputTypesFile(new Map(), new Set(), [ + categoryTable, + postTable, + ]); expect(result.content).toContain('export type CategorySelect = {'); expect(result.content).toContain('posts?: boolean | {'); @@ -594,7 +635,11 @@ describe('CRUD input types', () => { describe('custom input types', () => { it('generates input types from TypeRegistry', () => { const usedInputTypes = new Set(['LoginInput', 'RegisterInput']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, []); + const result = generateInputTypesFile( + sampleTypeRegistry, + usedInputTypes, + [], + ); expect(result.content).toContain('export interface LoginInput {'); expect(result.content).toContain('email: string;'); // Non-null @@ -604,10 +649,16 @@ describe('custom input types', () => { it('generates enum types from TypeRegistry', () => { const usedInputTypes = new Set(['UserRole']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, []); + const result = generateInputTypesFile( + sampleTypeRegistry, + usedInputTypes, + [], + ); // Babel generates double quotes for string literals - expect(result.content).toContain('export type UserRole = "ADMIN" | "USER" | "GUEST";'); + expect(result.content).toContain( + 'export type UserRole = "ADMIN" | "USER" | "GUEST";', + ); }); }); @@ -619,7 +670,12 @@ describe('payload types', () => { it('generates payload types for custom operations', () => { const usedInputTypes = new Set(); const usedPayloadTypes = new Set(['LoginPayload']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, [], usedPayloadTypes); + const result = generateInputTypesFile( + sampleTypeRegistry, + usedInputTypes, + [], + usedPayloadTypes, + ); expect(result.content).toContain('export interface LoginPayload {'); expect(result.content).toContain('token?: string | null;'); @@ -629,7 +685,12 @@ describe('payload types', () => { it('generates Select types for payload types', () => { const usedInputTypes = new Set(); const usedPayloadTypes = new Set(['LoginPayload']); - const result = generateInputTypesFile(sampleTypeRegistry, usedInputTypes, [], usedPayloadTypes); + const result = generateInputTypesFile( + sampleTypeRegistry, + usedInputTypes, + [], + usedPayloadTypes, + ); expect(result.content).toContain('export type LoginPayloadSelect = {'); expect(result.content).toContain('token?: boolean;'); @@ -646,14 +707,20 @@ describe('collectInputTypeNames', () => { const operations = [ { args: [ - { name: 'input', type: createNonNull(createTypeRef('INPUT_OBJECT', 'LoginInput')) } - ] as CleanArgument[] + { + name: 'input', + type: createNonNull(createTypeRef('INPUT_OBJECT', 'LoginInput')), + }, + ] as CleanArgument[], }, { args: [ - { name: 'data', type: createTypeRef('INPUT_OBJECT', 'RegisterInput') } - ] as CleanArgument[] - } + { + name: 'data', + type: createTypeRef('INPUT_OBJECT', 'RegisterInput'), + }, + ] as CleanArgument[], + }, ]; const result = collectInputTypeNames(operations); @@ -666,9 +733,9 @@ describe('collectInputTypeNames', () => { const operations = [ { args: [ - { name: 'filter', type: createTypeRef('INPUT_OBJECT', 'UserFilter') } - ] as CleanArgument[] - } + { name: 'filter', type: createTypeRef('INPUT_OBJECT', 'UserFilter') }, + ] as CleanArgument[], + }, ]; const result = collectInputTypeNames(operations); @@ -681,7 +748,7 @@ describe('collectPayloadTypeNames', () => { it('collects Payload type names from operations', () => { const operations = [ { returnType: createTypeRef('OBJECT', 'LoginPayload') }, - { returnType: createTypeRef('OBJECT', 'RegisterPayload') } + { returnType: createTypeRef('OBJECT', 'RegisterPayload') }, ]; const result = collectPayloadTypeNames(operations); @@ -692,7 +759,7 @@ describe('collectPayloadTypeNames', () => { it('includes Connection types', () => { const operations = [ - { returnType: createTypeRef('OBJECT', 'UsersConnection') } + { returnType: createTypeRef('OBJECT', 'UsersConnection') }, ]; const result = collectPayloadTypeNames(operations); @@ -717,7 +784,7 @@ describe('edge cases', () => { it('handles table with only id field', () => { const minimalTable = createTable({ name: 'Minimal', - fields: [{ name: 'id', type: fieldTypes.uuid }] + fields: [{ name: 'id', type: fieldTypes.uuid }], }); const result = generateInputTypesFile(new Map(), new Set(), [minimalTable]); @@ -742,6 +809,8 @@ describe('edge cases', () => { // Should generate a fallback type // Babel comment format may have slight differences expect(result.content).toContain("Type 'UnknownType' not found in schema"); - expect(result.content).toContain('export type UnknownType = Record;'); + expect(result.content).toContain( + 'export type UnknownType = Record;', + ); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts index 86de98981..db74b3133 100644 --- a/graphql/codegen/src/__tests__/codegen/model-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/model-generator.test.ts @@ -4,7 +4,11 @@ * Tests the generated model classes with findMany, findFirst, create, update, delete methods. */ import { generateModelFile } from '../../core/codegen/orm/model-generator'; -import type { CleanFieldType, CleanRelations,CleanTable } from '../../types/schema'; +import type { + CleanFieldType, + CleanRelations, + CleanTable, +} from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -15,24 +19,26 @@ const fieldTypes = { string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } @@ -49,15 +55,15 @@ describe('model-generator', () => { { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, { name: 'isActive', type: fieldTypes.boolean }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser' - } + delete: 'deleteUser', + }, }); const result = generateModelFile(table, false); @@ -73,15 +79,15 @@ describe('model-generator', () => { fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'action', type: fieldTypes.string }, - { name: 'timestamp', type: fieldTypes.datetime } + { name: 'timestamp', type: fieldTypes.datetime }, ], query: { all: 'auditLogs', one: 'auditLog', create: 'createAuditLog', update: undefined, - delete: undefined - } + delete: undefined, + }, }); const result = generateModelFile(table, false); @@ -99,15 +105,15 @@ describe('model-generator', () => { name: 'Organization', fields: [ { name: 'id', type: fieldTypes.uuid }, - { name: 'name', type: fieldTypes.string } + { name: 'name', type: fieldTypes.string }, ], query: { all: 'allOrganizations', one: 'organizationById', create: 'registerOrganization', update: 'modifyOrganization', - delete: 'removeOrganization' - } + delete: 'removeOrganization', + }, }); const result = generateModelFile(table, false); @@ -125,15 +131,15 @@ describe('model-generator', () => { fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'price', type: fieldTypes.int } + { name: 'price', type: fieldTypes.int }, ], query: { all: 'products', one: 'product', create: 'createProduct', update: 'updateProduct', - delete: 'deleteProduct' - } + delete: 'deleteProduct', + }, }); const result = generateModelFile(table, false); diff --git a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts index 3cd47be84..89c10ad78 100644 --- a/graphql/codegen/src/__tests__/codegen/query-builder.test.ts +++ b/graphql/codegen/src/__tests__/codegen/query-builder.test.ts @@ -16,7 +16,7 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { return [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections: nodeSelections }) + selectionSet: t.selectionSet({ selections: nodeSelections }), }), t.field({ name: 'totalCount' }), t.field({ @@ -26,10 +26,10 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }) - ] - }) - }) + t.field({ name: 'endCursor' }), + ], + }), + }), ]; } @@ -37,20 +37,20 @@ function addVariable( spec: { varName: string; argName?: string; typeName: string; value: unknown }, definitions: VariableDefinitionNode[], args: ArgumentNode[], - variables: Record + variables: Record, ): void { if (spec.value === undefined) return; definitions.push( t.variableDefinition({ variable: t.variable({ name: spec.varName }), - type: parseType(spec.typeName) - }) + type: parseType(spec.typeName), + }), ); args.push( t.argument({ name: spec.argName ?? spec.varName, - value: t.variable({ name: spec.varName }) - }) + value: t.variable({ name: spec.varName }), + }), ); variables[spec.varName] = spec.value; } @@ -58,11 +58,13 @@ function addVariable( function buildSelections( select: Record | undefined, connectionFieldsMap?: Record>, - entityType?: string + entityType?: string, ): FieldNode[] { if (!select) return []; const fields: FieldNode[] = []; - const entityConnections = entityType ? connectionFieldsMap?.[entityType] : undefined; + const entityConnections = entityType + ? connectionFieldsMap?.[entityType] + : undefined; for (const [key, value] of Object.entries(select)) { if (value === false || value === undefined) continue; @@ -79,7 +81,11 @@ function buildSelections( }; if (nested.select) { const relatedEntityType = entityConnections?.[key]; - const nestedSelections = buildSelections(nested.select, connectionFieldsMap, relatedEntityType); + const nestedSelections = buildSelections( + nested.select, + connectionFieldsMap, + relatedEntityType, + ); const isConnection = nested.connection === true || nested.first !== undefined || @@ -91,16 +97,16 @@ function buildSelections( t.field({ name: key, selectionSet: t.selectionSet({ - selections: buildConnectionSelections(nestedSelections) - }) - }) + selections: buildConnectionSelections(nestedSelections), + }), + }), ); } else { fields.push( t.field({ name: key, - selectionSet: t.selectionSet({ selections: nestedSelections }) - }) + selectionSet: t.selectionSet({ selections: nestedSelections }), + }), ); } } @@ -116,36 +122,68 @@ function buildFindManyDocument( args: { where?: TWhere; first?: number; orderBy?: string[] }, filterTypeName: string, orderByTypeName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; const queryArgs: ArgumentNode[] = []; const variables: Record = {}; - addVariable({ varName: 'where', argName: 'filter', typeName: filterTypeName, value: args.where }, variableDefinitions, queryArgs, variables); - addVariable({ varName: 'orderBy', typeName: `[${orderByTypeName}!]`, value: args.orderBy?.length ? args.orderBy : undefined }, variableDefinitions, queryArgs, variables); - addVariable({ varName: 'first', typeName: 'Int', value: args.first }, variableDefinitions, queryArgs, variables); + addVariable( + { + varName: 'where', + argName: 'filter', + typeName: filterTypeName, + value: args.where, + }, + variableDefinitions, + queryArgs, + variables, + ); + addVariable( + { + varName: 'orderBy', + typeName: `[${orderByTypeName}!]`, + value: args.orderBy?.length ? args.orderBy : undefined, + }, + variableDefinitions, + queryArgs, + variables, + ); + addVariable( + { varName: 'first', typeName: 'Int', value: args.first }, + variableDefinitions, + queryArgs, + variables, + ); const document = t.document({ definitions: [ t.operationDefinition({ operation: 'query', name: operationName + 'Query', - variableDefinitions: variableDefinitions.length ? variableDefinitions : undefined, + variableDefinitions: variableDefinitions.length + ? variableDefinitions + : undefined, selectionSet: t.selectionSet({ selections: [ t.field({ name: queryField, args: queryArgs.length ? queryArgs : undefined, - selectionSet: t.selectionSet({ selections: buildConnectionSelections(selections) }) - }) - ] - }) - }) - ] + selectionSet: t.selectionSet({ + selections: buildConnectionSelections(selections), + }), + }), + ], + }), + }), + ], }); return { document: print(document), variables }; } @@ -155,7 +193,7 @@ function buildMutationDocument( mutationField: string, entityField: string, selections: FieldNode[], - inputTypeName: string + inputTypeName: string, ): string { return print( t.document({ @@ -166,28 +204,33 @@ function buildMutationDocument( variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'input' }), - type: parseType(inputTypeName + '!') - }) + type: parseType(inputTypeName + '!'), + }), ], selectionSet: t.selectionSet({ selections: [ t.field({ name: mutationField, - args: [t.argument({ name: 'input', value: t.variable({ name: 'input' }) })], + args: [ + t.argument({ + name: 'input', + value: t.variable({ name: 'input' }), + }), + ], selectionSet: t.selectionSet({ selections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections }) - }) - ] - }) - }) - ] - }) - }) - ] - }) + selectionSet: t.selectionSet({ selections }), + }), + ], + }), + }), + ], + }), + }), + ], + }), ); } @@ -202,7 +245,7 @@ describe('query-builder', () => { id: true, name: true, ignored: false, - profile: { select: { bio: true } } + profile: { select: { bio: true } }, }); expect(result).toHaveLength(3); @@ -214,13 +257,13 @@ describe('query-builder', () => { it('wraps connection fields in nodes when connectionFieldsMap is provided', () => { const connectionFieldsMap = { - User: { posts: 'Post', comments: 'Comment' } + User: { posts: 'Post', comments: 'Comment' }, }; const result = buildSelections( { id: true, posts: { select: { id: true, title: true } } }, connectionFieldsMap, - 'User' + 'User', ); expect(result).toHaveLength(2); @@ -233,7 +276,8 @@ describe('query-builder', () => { expect(postsSelections[1].name.value).toBe('totalCount'); expect(postsSelections[2].name.value).toBe('pageInfo'); // nodes should contain the actual fields - const nodesSelections = postsSelections[0].selectionSet?.selections as FieldNode[]; + const nodesSelections = postsSelections[0].selectionSet + ?.selections as FieldNode[]; expect(nodesSelections).toHaveLength(2); expect(nodesSelections[0].name.value).toBe('id'); expect(nodesSelections[1].name.value).toBe('title'); @@ -241,19 +285,20 @@ describe('query-builder', () => { it('does not wrap singular relations in nodes', () => { const connectionFieldsMap = { - Post: { comments: 'Comment' } + Post: { comments: 'Comment' }, }; const result = buildSelections( { id: true, author: { select: { id: true, name: true } } }, connectionFieldsMap, - 'Post' + 'Post', ); expect(result).toHaveLength(2); expect(result[1].name.value).toBe('author'); // author is NOT in connectionFieldsMap for Post → should NOT be wrapped - const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + const authorSelections = result[1].selectionSet + ?.selections as FieldNode[]; expect(authorSelections).toHaveLength(2); expect(authorSelections[0].name.value).toBe('id'); expect(authorSelections[1].name.value).toBe('name'); @@ -262,7 +307,7 @@ describe('query-builder', () => { it('handles deeply nested connections recursively', () => { const connectionFieldsMap = { User: { posts: 'Post' }, - Post: { comments: 'Comment' } + Post: { comments: 'Comment' }, }; const result = buildSelections( @@ -271,12 +316,12 @@ describe('query-builder', () => { posts: { select: { id: true, - comments: { select: { id: true, body: true } } - } - } + comments: { select: { id: true, body: true } }, + }, + }, }, connectionFieldsMap, - 'User' + 'User', ); // posts should be wrapped (User.posts is a connection) @@ -284,9 +329,13 @@ describe('query-builder', () => { expect(postsSelections[0].name.value).toBe('nodes'); // Inside nodes, comments should also be wrapped (Post.comments is a connection) - const nodesFields = postsSelections[0].selectionSet?.selections as FieldNode[]; - const commentsField = nodesFields.find((f) => f.name.value === 'comments')!; - const commentsSelections = commentsField.selectionSet?.selections as FieldNode[]; + const nodesFields = postsSelections[0].selectionSet + ?.selections as FieldNode[]; + const commentsField = nodesFields.find( + (f) => f.name.value === 'comments', + )!; + const commentsSelections = commentsField.selectionSet + ?.selections as FieldNode[]; expect(commentsSelections[0].name.value).toBe('nodes'); expect(commentsSelections[1].name.value).toBe('totalCount'); expect(commentsSelections[2].name.value).toBe('pageInfo'); @@ -295,7 +344,7 @@ describe('query-builder', () => { it('works without connectionFieldsMap (backward compat)', () => { const result = buildSelections({ id: true, - posts: { select: { id: true } } + posts: { select: { id: true } }, }); expect(result).toHaveLength(2); @@ -309,7 +358,7 @@ describe('query-builder', () => { it('still wraps when first/filter provided even without connectionFieldsMap', () => { const result = buildSelections({ id: true, - posts: { select: { id: true }, first: 10 } + posts: { select: { id: true }, first: 10 }, }); expect(result).toHaveLength(2); @@ -320,44 +369,47 @@ describe('query-builder', () => { it('handles mixed connection and singular relations on same entity', () => { const connectionFieldsMap = { - Post: { comments: 'Comment' } + Post: { comments: 'Comment' }, }; const result = buildSelections( { id: true, author: { select: { id: true } }, - comments: { select: { id: true, body: true } } + comments: { select: { id: true, body: true } }, }, connectionFieldsMap, - 'Post' + 'Post', ); expect(result).toHaveLength(3); // author = singular → no wrapping - const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + const authorSelections = result[1].selectionSet + ?.selections as FieldNode[]; expect(authorSelections[0].name.value).toBe('id'); // comments = connection → wrapped - const commentsSelections = result[2].selectionSet?.selections as FieldNode[]; + const commentsSelections = result[2].selectionSet + ?.selections as FieldNode[]; expect(commentsSelections[0].name.value).toBe('nodes'); expect(commentsSelections[1].name.value).toBe('totalCount'); }); it('handles entity not in connectionFieldsMap gracefully', () => { const connectionFieldsMap = { - User: { posts: 'Post' } + User: { posts: 'Post' }, }; // Comment is not in the map — all nested fields should be singular const result = buildSelections( { id: true, author: { select: { id: true } } }, connectionFieldsMap, - 'Comment' + 'Comment', ); expect(result).toHaveLength(2); - const authorSelections = result[1].selectionSet?.selections as FieldNode[]; + const authorSelections = result[1].selectionSet + ?.selections as FieldNode[]; expect(authorSelections[0].name.value).toBe('id'); }); }); @@ -368,9 +420,13 @@ describe('query-builder', () => { 'Users', 'users', { id: true, name: true }, - { where: { status: { equalTo: 'active' } }, first: 10, orderBy: ['NAME_ASC'] }, + { + where: { status: { equalTo: 'active' } }, + first: 10, + orderBy: ['NAME_ASC'], + }, 'UserFilter', - 'UsersOrderBy' + 'UsersOrderBy', ); expect(document).toContain('query UsersQuery'); @@ -384,7 +440,7 @@ describe('query-builder', () => { expect(variables).toEqual({ where: { status: { equalTo: 'active' } }, first: 10, - orderBy: ['NAME_ASC'] + orderBy: ['NAME_ASC'], }); }); }); @@ -396,7 +452,7 @@ describe('query-builder', () => { 'createUser', 'user', [t.field({ name: 'id' }), t.field({ name: 'name' })], - 'CreateUserInput' + 'CreateUserInput', ); expect(document).toContain('mutation CreateUserMutation'); @@ -415,13 +471,13 @@ describe('query-builder', () => { describe('snapshots', () => { const connectionFieldsMap = { User: { posts: 'Post', comments: 'Comment' }, - Post: { comments: 'Comment', tags: 'Tag' } + Post: { comments: 'Comment', tags: 'Tag' }, }; /** Helper: build a query document and print it for snapshotting */ function buildQuerySnapshot( select: Record, - map?: Record> + map?: Record>, ): string { const selections = buildSelections(select, map, 'User'); const doc = t.document({ @@ -434,13 +490,13 @@ describe('query-builder', () => { t.field({ name: 'users', selectionSet: t.selectionSet({ - selections: buildConnectionSelections(selections) - }) - }) - ] - }) - }) - ] + selections: buildConnectionSelections(selections), + }), + }), + ], + }), + }), + ], }); return print(doc); } @@ -450,9 +506,9 @@ describe('query-builder', () => { { id: true, name: true, - posts: { select: { id: true, title: true, body: true } } + posts: { select: { id: true, title: true, body: true } }, }, - connectionFieldsMap + connectionFieldsMap, ); expect(document).toMatchSnapshot(); }); @@ -466,12 +522,12 @@ describe('query-builder', () => { id: true, title: true, comments: { - select: { id: true, body: true } - } - } - } + select: { id: true, body: true }, + }, + }, + }, }, - connectionFieldsMap + connectionFieldsMap, ); expect(document).toMatchSnapshot(); }); @@ -483,9 +539,9 @@ describe('query-builder', () => { name: true, profile: { select: { bio: true, avatar: true } }, posts: { select: { id: true, title: true } }, - comments: { select: { id: true, body: true } } + comments: { select: { id: true, body: true } }, }, - connectionFieldsMap + connectionFieldsMap, ); expect(document).toMatchSnapshot(); }); @@ -495,8 +551,8 @@ describe('query-builder', () => { { id: true, name: true, - posts: { select: { id: true, title: true } } - } + posts: { select: { id: true, title: true } }, + }, // no connectionFieldsMap ); expect(document).toMatchSnapshot(); @@ -507,9 +563,13 @@ describe('query-builder', () => { 'User', 'users', { id: true, name: true, email: true }, - { where: { name: { equalTo: 'test' } }, first: 25, orderBy: ['NAME_ASC', 'CREATED_AT_DESC'] }, + { + where: { name: { equalTo: 'test' } }, + first: 25, + orderBy: ['NAME_ASC', 'CREATED_AT_DESC'], + }, 'UserFilter', - 'UsersOrderBy' + 'UsersOrderBy', ); expect(document).toMatchSnapshot(); }); @@ -519,8 +579,12 @@ describe('query-builder', () => { 'CreateUser', 'createUser', 'user', - [t.field({ name: 'id' }), t.field({ name: 'name' }), t.field({ name: 'email' })], - 'CreateUserInput' + [ + t.field({ name: 'id' }), + t.field({ name: 'name' }), + t.field({ name: 'email' }), + ], + 'CreateUserInput', ); expect(document).toMatchSnapshot(); }); @@ -533,12 +597,12 @@ describe('query-builder', () => { id: true, name: true, posts: { select: { id: true, title: true } }, - comments: { select: { id: true } } + comments: { select: { id: true } }, }, {}, 'UserFilter', 'UsersOrderBy', - connectionFieldsMap + connectionFieldsMap, ); expect(document).toMatchSnapshot(); }); diff --git a/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts b/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts index ad96c2697..6a23588ba 100644 --- a/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts +++ b/graphql/codegen/src/__tests__/codegen/query-keys-factory.test.ts @@ -9,35 +9,47 @@ import { generateInvalidationFile } from '../../core/codegen/invalidation'; import { generateMutationKeysFile } from '../../core/codegen/mutation-keys'; import { generateQueryKeysFile } from '../../core/codegen/query-keys'; -import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; -import type { CleanFieldType, CleanOperation, CleanRelations, CleanTable, CleanTypeRef } from '../../types/schema'; +import type { EntityRelationship, QueryKeyConfig } from '../../types/config'; +import type { + CleanFieldType, + CleanOperation, + CleanRelations, + CleanTable, + CleanTypeRef, +} from '../../types/schema'; const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } -function createTypeRef(kind: CleanTypeRef['kind'], name: string | null, ofType?: CleanTypeRef): CleanTypeRef { +function createTypeRef( + kind: CleanTypeRef['kind'], + name: string | null, + ofType?: CleanTypeRef, +): CleanTypeRef { return { kind, name, ofType }; } @@ -47,15 +59,15 @@ const simpleUserTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser' - } + delete: 'deleteUser', + }, }); const postTable = createTable({ @@ -65,15 +77,15 @@ const postTable = createTable({ { name: 'title', type: fieldTypes.string }, { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost' - } + delete: 'deletePost', + }, }); const organizationTable = createTable({ @@ -81,15 +93,15 @@ const organizationTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'slug', type: fieldTypes.string } + { name: 'slug', type: fieldTypes.string }, ], query: { all: 'organizations', one: 'organization', create: 'createOrganization', update: 'updateOrganization', - delete: 'deleteOrganization' - } + delete: 'deleteOrganization', + }, }); const databaseTable = createTable({ @@ -97,15 +109,15 @@ const databaseTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'organizationId', type: fieldTypes.uuid } + { name: 'organizationId', type: fieldTypes.uuid }, ], query: { all: 'databases', one: 'database', create: 'createDatabase', update: 'updateDatabase', - delete: 'deleteDatabase' - } + delete: 'deleteDatabase', + }, }); const tableEntityTable = createTable({ @@ -113,15 +125,15 @@ const tableEntityTable = createTable({ fields: [ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, - { name: 'databaseId', type: fieldTypes.uuid } + { name: 'databaseId', type: fieldTypes.uuid }, ], query: { all: 'tables', one: 'table', create: 'createTable', update: 'updateTable', - delete: 'deleteTable' - } + delete: 'deleteTable', + }, }); const fieldTable = createTable({ @@ -130,15 +142,15 @@ const fieldTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'name', type: fieldTypes.string }, { name: 'tableId', type: fieldTypes.uuid }, - { name: 'type', type: fieldTypes.string } + { name: 'type', type: fieldTypes.string }, ], query: { all: 'fields', one: 'field', create: 'createField', update: 'updateField', - delete: 'deleteField' - } + delete: 'deleteField', + }, }); const simpleConfig: QueryKeyConfig = { @@ -146,17 +158,25 @@ const simpleConfig: QueryKeyConfig = { relationships: {}, generateScopedKeys: true, generateCascadeHelpers: true, - generateMutationKeys: true + generateMutationKeys: true, }; const simpleRelationships: Record = { - post: { parent: 'User', foreignKey: 'authorId' } + post: { parent: 'User', foreignKey: 'authorId' }, }; const hierarchicalRelationships: Record = { database: { parent: 'Organization', foreignKey: 'organizationId' }, - table: { parent: 'Database', foreignKey: 'databaseId', ancestors: ['organization'] }, - field: { parent: 'Table', foreignKey: 'tableId', ancestors: ['database', 'organization'] } + table: { + parent: 'Database', + foreignKey: 'databaseId', + ancestors: ['organization'], + }, + field: { + parent: 'Table', + foreignKey: 'tableId', + ancestors: ['database', 'organization'], + }, }; const sampleCustomQueries: CleanOperation[] = [ @@ -165,18 +185,25 @@ const sampleCustomQueries: CleanOperation[] = [ kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user' + description: 'Get the current authenticated user', }, { name: 'searchUsers', kind: 'query', args: [ - { name: 'query', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'limit', type: createTypeRef('SCALAR', 'Int') } + { + name: 'query', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { name: 'limit', type: createTypeRef('SCALAR', 'Int') }, ], returnType: createTypeRef('LIST', null, createTypeRef('OBJECT', 'User')), - description: 'Search users by name or email' - } + description: 'Search users by name or email', + }, ]; const sampleCustomMutations: CleanOperation[] = [ @@ -184,19 +211,33 @@ const sampleCustomMutations: CleanOperation[] = [ name: 'login', kind: 'mutation', args: [ - { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } + { + name: 'email', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { + name: 'password', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user' + description: 'Authenticate user', }, { name: 'logout', kind: 'mutation', args: [], returnType: createTypeRef('OBJECT', 'LogoutPayload'), - description: 'Log out current user' - } + description: 'Log out current user', + }, ]; describe('generateQueryKeysFile', () => { @@ -204,7 +245,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable], customQueries: [], - config: simpleConfig + config: simpleConfig, }); expect(result.fileName).toBe('query-keys.ts'); expect(result.content).toMatchSnapshot(); @@ -214,7 +255,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable, postTable], customQueries: [], - config: simpleConfig + config: simpleConfig, }); expect(result.content).toMatchSnapshot(); }); @@ -225,8 +266,8 @@ describe('generateQueryKeysFile', () => { customQueries: [], config: { ...simpleConfig, - relationships: simpleRelationships - } + relationships: simpleRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -237,8 +278,8 @@ describe('generateQueryKeysFile', () => { customQueries: [], config: { ...simpleConfig, - relationships: hierarchicalRelationships - } + relationships: hierarchicalRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -247,7 +288,7 @@ describe('generateQueryKeysFile', () => { const result = generateQueryKeysFile({ tables: [simpleUserTable], customQueries: sampleCustomQueries, - config: simpleConfig + config: simpleConfig, }); expect(result.content).toMatchSnapshot(); }); @@ -259,8 +300,8 @@ describe('generateQueryKeysFile', () => { config: { ...simpleConfig, relationships: simpleRelationships, - generateScopedKeys: false - } + generateScopedKeys: false, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -271,7 +312,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable], customMutations: [], - config: simpleConfig + config: simpleConfig, }); expect(result.fileName).toBe('mutation-keys.ts'); expect(result.content).toMatchSnapshot(); @@ -281,7 +322,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable, postTable], customMutations: [], - config: simpleConfig + config: simpleConfig, }); expect(result.content).toMatchSnapshot(); }); @@ -292,8 +333,8 @@ describe('generateMutationKeysFile', () => { customMutations: [], config: { ...simpleConfig, - relationships: simpleRelationships - } + relationships: simpleRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -302,7 +343,7 @@ describe('generateMutationKeysFile', () => { const result = generateMutationKeysFile({ tables: [simpleUserTable], customMutations: sampleCustomMutations, - config: simpleConfig + config: simpleConfig, }); expect(result.content).toMatchSnapshot(); }); @@ -313,8 +354,8 @@ describe('generateMutationKeysFile', () => { customMutations: [], config: { ...simpleConfig, - relationships: hierarchicalRelationships - } + relationships: hierarchicalRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -324,7 +365,7 @@ describe('generateInvalidationFile', () => { it('generates invalidation helpers for a single table without relationships', () => { const result = generateInvalidationFile({ tables: [simpleUserTable], - config: simpleConfig + config: simpleConfig, }); expect(result.fileName).toBe('invalidation.ts'); expect(result.content).toMatchSnapshot(); @@ -333,7 +374,7 @@ describe('generateInvalidationFile', () => { it('generates invalidation helpers for multiple tables', () => { const result = generateInvalidationFile({ tables: [simpleUserTable, postTable], - config: simpleConfig + config: simpleConfig, }); expect(result.content).toMatchSnapshot(); }); @@ -343,8 +384,8 @@ describe('generateInvalidationFile', () => { tables: [simpleUserTable, postTable], config: { ...simpleConfig, - relationships: simpleRelationships - } + relationships: simpleRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -354,8 +395,8 @@ describe('generateInvalidationFile', () => { tables: [organizationTable, databaseTable, tableEntityTable, fieldTable], config: { ...simpleConfig, - relationships: hierarchicalRelationships - } + relationships: hierarchicalRelationships, + }, }); expect(result.content).toMatchSnapshot(); }); @@ -366,8 +407,8 @@ describe('generateInvalidationFile', () => { config: { ...simpleConfig, relationships: simpleRelationships, - generateCascadeHelpers: false - } + generateCascadeHelpers: false, + }, }); expect(result.content).toMatchSnapshot(); }); diff --git a/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts b/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts index a8a187295..cd77501e4 100644 --- a/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts +++ b/graphql/codegen/src/__tests__/codegen/react-query-hooks.test.ts @@ -14,12 +14,19 @@ import { generateCustomQueriesBarrel, generateMainBarrel, generateMutationsBarrel, - generateQueriesBarrel + generateQueriesBarrel, } from '../../core/codegen/barrel'; import { generateCustomMutationHook } from '../../core/codegen/custom-mutations'; import { generateCustomQueryHook } from '../../core/codegen/custom-queries'; -import { generateCreateMutationHook, generateDeleteMutationHook,generateUpdateMutationHook } from '../../core/codegen/mutations'; -import { generateListQueryHook, generateSingleQueryHook } from '../../core/codegen/queries'; +import { + generateCreateMutationHook, + generateDeleteMutationHook, + generateUpdateMutationHook, +} from '../../core/codegen/mutations'; +import { + generateListQueryHook, + generateSingleQueryHook, +} from '../../core/codegen/queries'; import { generateSchemaTypesFile } from '../../core/codegen/schema-types-generator'; import type { CleanFieldType, @@ -28,7 +35,7 @@ import type { CleanTable, CleanTypeRef, ResolvedType, - TypeRegistry + TypeRegistry, } from '../../types/schema'; const fieldTypes = { @@ -36,28 +43,34 @@ const fieldTypes = { string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, - boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType + boolean: { gqlType: 'Boolean', isArray: false } as CleanFieldType, }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } -function createTypeRef(kind: CleanTypeRef['kind'], name: string | null, ofType?: CleanTypeRef): CleanTypeRef { +function createTypeRef( + kind: CleanTypeRef['kind'], + name: string | null, + ofType?: CleanTypeRef, +): CleanTypeRef { return { kind, name, ofType }; } @@ -67,15 +80,15 @@ const simpleUserTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser' - } + delete: 'deleteUser', + }, }); const postTable = createTable({ @@ -86,15 +99,15 @@ const postTable = createTable({ { name: 'content', type: fieldTypes.string }, { name: 'authorId', type: fieldTypes.uuid }, { name: 'published', type: fieldTypes.boolean }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'posts', one: 'post', create: 'createPost', update: 'updatePost', - delete: 'deletePost' - } + delete: 'deletePost', + }, }); const simpleCustomQueries: CleanOperation[] = [ @@ -103,18 +116,25 @@ const simpleCustomQueries: CleanOperation[] = [ kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user' + description: 'Get the current authenticated user', }, { name: 'searchUsers', kind: 'query', args: [ - { name: 'query', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'limit', type: createTypeRef('SCALAR', 'Int') } + { + name: 'query', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { name: 'limit', type: createTypeRef('SCALAR', 'Int') }, ], returnType: createTypeRef('LIST', null, createTypeRef('OBJECT', 'User')), - description: 'Search users by name or email' - } + description: 'Search users by name or email', + }, ]; const simpleCustomMutations: CleanOperation[] = [ @@ -122,28 +142,49 @@ const simpleCustomMutations: CleanOperation[] = [ name: 'login', kind: 'mutation', args: [ - { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } + { + name: 'email', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { + name: 'password', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user' + description: 'Authenticate user', }, { name: 'logout', kind: 'mutation', args: [], returnType: createTypeRef('OBJECT', 'LogoutPayload'), - description: 'Log out current user' + description: 'Log out current user', }, { name: 'register', kind: 'mutation', args: [ - { name: 'input', type: createTypeRef('NON_NULL', null, createTypeRef('INPUT_OBJECT', 'RegisterInput')) } + { + name: 'input', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('INPUT_OBJECT', 'RegisterInput'), + ), + }, ], returnType: createTypeRef('OBJECT', 'RegisterPayload'), - description: 'Register a new user' - } + description: 'Register a new user', + }, ]; function createTypeRegistry(): TypeRegistry { @@ -154,16 +195,14 @@ function createTypeRegistry(): TypeRegistry { name: 'LoginPayload', fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, - { name: 'user', type: createTypeRef('OBJECT', 'User') } - ] + { name: 'user', type: createTypeRef('OBJECT', 'User') }, + ], } as ResolvedType); registry.set('LogoutPayload', { kind: 'OBJECT', name: 'LogoutPayload', - fields: [ - { name: 'success', type: createTypeRef('SCALAR', 'Boolean') } - ] + fields: [{ name: 'success', type: createTypeRef('SCALAR', 'Boolean') }], } as ResolvedType); registry.set('RegisterPayload', { @@ -171,32 +210,44 @@ function createTypeRegistry(): TypeRegistry { name: 'RegisterPayload', fields: [ { name: 'token', type: createTypeRef('SCALAR', 'String') }, - { name: 'user', type: createTypeRef('OBJECT', 'User') } - ] + { name: 'user', type: createTypeRef('OBJECT', 'User') }, + ], } as ResolvedType); registry.set('RegisterInput', { kind: 'INPUT_OBJECT', name: 'RegisterInput', inputFields: [ - { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'name', type: createTypeRef('SCALAR', 'String') } - ] + { + name: 'email', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { + name: 'password', + type: createTypeRef( + 'NON_NULL', + null, + createTypeRef('SCALAR', 'String'), + ), + }, + { name: 'name', type: createTypeRef('SCALAR', 'String') }, + ], } as ResolvedType); registry.set('UserRole', { kind: 'ENUM', name: 'UserRole', - enumValues: ['ADMIN', 'USER', 'GUEST'] + enumValues: ['ADMIN', 'USER', 'GUEST'], } as ResolvedType); registry.set('Query', { kind: 'OBJECT', name: 'Query', - fields: [ - { name: 'currentUser', type: createTypeRef('OBJECT', 'User') } - ] + fields: [{ name: 'currentUser', type: createTypeRef('OBJECT', 'User') }], } as ResolvedType); registry.set('Mutation', { @@ -205,8 +256,8 @@ function createTypeRegistry(): TypeRegistry { fields: [ { name: 'login', type: createTypeRef('OBJECT', 'LoginPayload') }, { name: 'logout', type: createTypeRef('OBJECT', 'LogoutPayload') }, - { name: 'register', type: createTypeRef('OBJECT', 'RegisterPayload') } - ] + { name: 'register', type: createTypeRef('OBJECT', 'RegisterPayload') }, + ], } as ResolvedType); return registry; @@ -217,7 +268,7 @@ describe('Query Hook Generators', () => { it('generates list query hook for simple table', () => { const result = generateListQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result.fileName).toBe('useUsersQuery.ts'); @@ -227,7 +278,7 @@ describe('Query Hook Generators', () => { it('generates list query hook without centralized keys', () => { const result = generateListQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -237,7 +288,7 @@ describe('Query Hook Generators', () => { const result = generateListQueryHook(postTable, { reactQueryEnabled: true, useCentralizedKeys: true, - hasRelationships: true + hasRelationships: true, }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -248,7 +299,7 @@ describe('Query Hook Generators', () => { it('generates single query hook for simple table', () => { const result = generateSingleQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result.fileName).toBe('useUserQuery.ts'); @@ -258,7 +309,7 @@ describe('Query Hook Generators', () => { it('generates single query hook without centralized keys', () => { const result = generateSingleQueryHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -268,7 +319,7 @@ describe('Query Hook Generators', () => { const result = generateSingleQueryHook(postTable, { reactQueryEnabled: true, useCentralizedKeys: true, - hasRelationships: true + hasRelationships: true, }); expect(result).not.toBeNull(); expect(result.content).toMatchSnapshot(); @@ -281,7 +332,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook for simple table', () => { const result = generateCreateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useCreateUserMutation.ts'); @@ -291,7 +342,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook without centralized keys', () => { const result = generateCreateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -300,7 +351,7 @@ describe('Mutation Hook Generators', () => { it('generates create mutation hook for table with relationships', () => { const result = generateCreateMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -311,7 +362,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook for simple table', () => { const result = generateUpdateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useUpdateUserMutation.ts'); @@ -321,7 +372,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook without centralized keys', () => { const result = generateUpdateMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -330,7 +381,7 @@ describe('Mutation Hook Generators', () => { it('generates update mutation hook for table with relationships', () => { const result = generateUpdateMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -341,7 +392,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook for simple table', () => { const result = generateDeleteMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useDeleteUserMutation.ts'); @@ -351,7 +402,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook without centralized keys', () => { const result = generateDeleteMutationHook(simpleUserTable, { reactQueryEnabled: true, - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -360,7 +411,7 @@ describe('Mutation Hook Generators', () => { it('generates delete mutation hook for table with relationships', () => { const result = generateDeleteMutationHook(postTable, { reactQueryEnabled: true, - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -374,7 +425,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useCurrentUserQuery.ts'); @@ -385,7 +436,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[1], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useSearchUsersQuery.ts'); @@ -396,7 +447,7 @@ describe('Custom Query Hook Generators', () => { const result = generateCustomQueryHook({ operation: simpleCustomQueries[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -410,7 +461,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useLoginMutation.ts'); @@ -421,7 +472,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[1], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useLogoutMutation.ts'); @@ -432,7 +483,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[2], typeRegistry: createTypeRegistry(), - useCentralizedKeys: true + useCentralizedKeys: true, }); expect(result).not.toBeNull(); expect(result!.fileName).toBe('useRegisterMutation.ts'); @@ -443,7 +494,7 @@ describe('Custom Mutation Hook Generators', () => { const result = generateCustomMutationHook({ operation: simpleCustomMutations[0], typeRegistry: createTypeRegistry(), - useCentralizedKeys: false + useCentralizedKeys: false, }); expect(result).not.toBeNull(); expect(result!.content).toMatchSnapshot(); @@ -456,7 +507,7 @@ describe('Schema Types Generator', () => { it('generates schema types file with enums and input objects', () => { const result = generateSchemaTypesFile({ typeRegistry: createTypeRegistry(), - tableTypeNames: new Set(['User', 'Post']) + tableTypeNames: new Set(['User', 'Post']), }); expect(result.fileName).toBe('schema-types.ts'); expect(result.content).toMatchSnapshot(); @@ -465,7 +516,7 @@ describe('Schema Types Generator', () => { it('generates schema types file with empty table types', () => { const result = generateSchemaTypesFile({ typeRegistry: createTypeRegistry(), - tableTypeNames: new Set() + tableTypeNames: new Set(), }); expect(result.content).toMatchSnapshot(); }); @@ -503,21 +554,21 @@ describe('Barrel File Generators', () => { hasMutations: true, hasQueryKeys: true, hasMutationKeys: true, - hasInvalidation: true + hasInvalidation: true, }); expect(result).toMatchSnapshot(); }); it('generates main barrel without custom operations', () => { const result = generateMainBarrel([simpleUserTable], { - hasMutations: true + hasMutations: true, }); expect(result).toMatchSnapshot(); }); it('generates main barrel without mutations', () => { const result = generateMainBarrel([simpleUserTable, postTable], { - hasMutations: false + hasMutations: false, }); expect(result).toMatchSnapshot(); }); @@ -526,7 +577,10 @@ describe('Barrel File Generators', () => { describe('generateCustomQueriesBarrel', () => { it('generates custom queries barrel', () => { const customQueryNames = simpleCustomQueries.map((q) => q.name); - const result = generateCustomQueriesBarrel([simpleUserTable], customQueryNames); + const result = generateCustomQueriesBarrel( + [simpleUserTable], + customQueryNames, + ); expect(result).toMatchSnapshot(); }); }); @@ -534,7 +588,10 @@ describe('Barrel File Generators', () => { describe('generateCustomMutationsBarrel', () => { it('generates custom mutations barrel', () => { const customMutationNames = simpleCustomMutations.map((m) => m.name); - const result = generateCustomMutationsBarrel([simpleUserTable], customMutationNames); + const result = generateCustomMutationsBarrel( + [simpleUserTable], + customMutationNames, + ); expect(result).toMatchSnapshot(); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts b/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts index 93143400f..79300323e 100644 --- a/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts +++ b/graphql/codegen/src/__tests__/codegen/react-query-optional.test.ts @@ -6,11 +6,33 @@ * - Mutation generators return null (since they require React Query) * - Standalone fetch functions are still generated for queries */ -import { generateAllCustomMutationHooks,generateCustomMutationHook } from '../../core/codegen/custom-mutations'; -import { generateAllCustomQueryHooks,generateCustomQueryHook } from '../../core/codegen/custom-queries'; -import { generateAllMutationHooks,generateCreateMutationHook, generateDeleteMutationHook, generateUpdateMutationHook } from '../../core/codegen/mutations'; -import { generateAllQueryHooks,generateListQueryHook, generateSingleQueryHook } from '../../core/codegen/queries'; -import type { CleanFieldType, CleanOperation, CleanRelations, CleanTable, CleanTypeRef, TypeRegistry } from '../../types/schema'; +import { + generateAllCustomMutationHooks, + generateCustomMutationHook, +} from '../../core/codegen/custom-mutations'; +import { + generateAllCustomQueryHooks, + generateCustomQueryHook, +} from '../../core/codegen/custom-queries'; +import { + generateAllMutationHooks, + generateCreateMutationHook, + generateDeleteMutationHook, + generateUpdateMutationHook, +} from '../../core/codegen/mutations'; +import { + generateAllQueryHooks, + generateListQueryHook, + generateSingleQueryHook, +} from '../../core/codegen/queries'; +import type { + CleanFieldType, + CleanOperation, + CleanRelations, + CleanTable, + CleanTypeRef, + TypeRegistry, +} from '../../types/schema'; // ============================================================================ // Test Fixtures @@ -20,24 +42,26 @@ const fieldTypes = { uuid: { gqlType: 'UUID', isArray: false } as CleanFieldType, string: { gqlType: 'String', isArray: false } as CleanFieldType, int: { gqlType: 'Int', isArray: false } as CleanFieldType, - datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType + datetime: { gqlType: 'Datetime', isArray: false } as CleanFieldType, }; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; -function createTable(partial: Partial & { name: string }): CleanTable { +function createTable( + partial: Partial & { name: string }, +): CleanTable { return { name: partial.name, fields: partial.fields ?? [], relations: partial.relations ?? emptyRelations, query: partial.query, inflection: partial.inflection, - constraints: partial.constraints + constraints: partial.constraints, }; } @@ -47,18 +71,22 @@ const userTable = createTable({ { name: 'id', type: fieldTypes.uuid }, { name: 'email', type: fieldTypes.string }, { name: 'name', type: fieldTypes.string }, - { name: 'createdAt', type: fieldTypes.datetime } + { name: 'createdAt', type: fieldTypes.datetime }, ], query: { all: 'users', one: 'user', create: 'createUser', update: 'updateUser', - delete: 'deleteUser' - } + delete: 'deleteUser', + }, }); -function createTypeRef(kind: CleanTypeRef['kind'], name: string | null, ofType?: CleanTypeRef): CleanTypeRef { +function createTypeRef( + kind: CleanTypeRef['kind'], + name: string | null, + ofType?: CleanTypeRef, +): CleanTypeRef { return { kind, name, ofType }; } @@ -67,18 +95,24 @@ const sampleQueryOperation: CleanOperation = { kind: 'query', args: [], returnType: createTypeRef('OBJECT', 'User'), - description: 'Get the current authenticated user' + description: 'Get the current authenticated user', }; const sampleMutationOperation: CleanOperation = { name: 'login', kind: 'mutation', args: [ - { name: 'email', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) }, - { name: 'password', type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')) } + { + name: 'email', + type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')), + }, + { + name: 'password', + type: createTypeRef('NON_NULL', null, createTypeRef('SCALAR', 'String')), + }, ], returnType: createTypeRef('OBJECT', 'LoginPayload'), - description: 'Authenticate user' + description: 'Authenticate user', }; const emptyTypeRegistry: TypeRegistry = new Map(); @@ -90,64 +124,88 @@ const emptyTypeRegistry: TypeRegistry = new Map(); describe('Query generators with reactQueryEnabled: false', () => { describe('generateListQueryHook', () => { it('should not include React Query imports when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).not.toContain('@tanstack/react-query'); expect(result.content).not.toContain('useQuery'); expect(result.content).not.toContain('UseQueryOptions'); }); it('should not include useXxxQuery hook when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).not.toContain('export function useUsersQuery'); }); it('should not include prefetch function when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); - expect(result.content).not.toContain('export async function prefetchUsersQuery'); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); + expect(result.content).not.toContain( + 'export async function prefetchUsersQuery', + ); }); it('should still include standalone fetch function when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).toContain('export async function fetchUsersQuery'); }); it('should still include ORM client imports when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).toContain('getClient'); }); it('should still include query key factory when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).toContain('usersQueryKey'); }); it('should update file header when disabled', () => { - const result = generateListQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateListQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).toContain('List query functions for User'); }); }); describe('generateSingleQueryHook', () => { it('should not include React Query imports when disabled', () => { - const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateSingleQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).not.toContain('@tanstack/react-query'); expect(result.content).not.toContain('useQuery'); }); it('should not include useXxxQuery hook when disabled', () => { - const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateSingleQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).not.toContain('export function useUserQuery'); }); it('should still include standalone fetch function when disabled', () => { - const result = generateSingleQueryHook(userTable, { reactQueryEnabled: false }); + const result = generateSingleQueryHook(userTable, { + reactQueryEnabled: false, + }); expect(result.content).toContain('export async function fetchUserQuery'); }); }); describe('generateAllQueryHooks', () => { it('should generate files without React Query when disabled', () => { - const results = generateAllQueryHooks([userTable], { reactQueryEnabled: false }); + const results = generateAllQueryHooks([userTable], { + reactQueryEnabled: false, + }); expect(results.length).toBe(2); // list + single for (const result of results) { expect(result.content).not.toContain('@tanstack/react-query'); @@ -176,7 +234,9 @@ describe('Query generators with reactQueryEnabled: true (default)', () => { it('should include prefetch function by default', () => { const result = generateListQueryHook(userTable); - expect(result.content).toContain('export async function prefetchUsersQuery'); + expect(result.content).toContain( + 'export async function prefetchUsersQuery', + ); }); it('should include standalone fetch function by default', () => { @@ -193,28 +253,36 @@ describe('Query generators with reactQueryEnabled: true (default)', () => { describe('Mutation generators with reactQueryEnabled: false', () => { describe('generateCreateMutationHook', () => { it('should return null when disabled', () => { - const result = generateCreateMutationHook(userTable, { reactQueryEnabled: false }); + const result = generateCreateMutationHook(userTable, { + reactQueryEnabled: false, + }); expect(result).toBeNull(); }); }); describe('generateUpdateMutationHook', () => { it('should return null when disabled', () => { - const result = generateUpdateMutationHook(userTable, { reactQueryEnabled: false }); + const result = generateUpdateMutationHook(userTable, { + reactQueryEnabled: false, + }); expect(result).toBeNull(); }); }); describe('generateDeleteMutationHook', () => { it('should return null when disabled', () => { - const result = generateDeleteMutationHook(userTable, { reactQueryEnabled: false }); + const result = generateDeleteMutationHook(userTable, { + reactQueryEnabled: false, + }); expect(result).toBeNull(); }); }); describe('generateAllMutationHooks', () => { it('should return empty array when disabled', () => { - const results = generateAllMutationHooks([userTable], { reactQueryEnabled: false }); + const results = generateAllMutationHooks([userTable], { + reactQueryEnabled: false, + }); expect(results).toEqual([]); }); }); @@ -251,7 +319,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); expect(result.content).not.toContain('@tanstack/react-query'); expect(result.content).not.toContain('useQuery'); @@ -261,18 +329,22 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); - expect(result.content).not.toContain('export function useCurrentUserQuery'); + expect(result.content).not.toContain( + 'export function useCurrentUserQuery', + ); }); it('should still include standalone fetch function when disabled', () => { const result = generateCustomQueryHook({ operation: sampleQueryOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); - expect(result.content).toContain('export async function fetchCurrentUserQuery'); + expect(result.content).toContain( + 'export async function fetchCurrentUserQuery', + ); }); }); @@ -281,7 +353,7 @@ describe('Custom query generators with reactQueryEnabled: false', () => { const results = generateAllCustomQueryHooks({ operations: [sampleQueryOperation], typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); expect(results.length).toBe(1); expect(results[0].content).not.toContain('@tanstack/react-query'); @@ -299,7 +371,7 @@ describe('Custom mutation generators with reactQueryEnabled: false', () => { const result = generateCustomMutationHook({ operation: sampleMutationOperation, typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); expect(result).toBeNull(); }); @@ -310,7 +382,7 @@ describe('Custom mutation generators with reactQueryEnabled: false', () => { const results = generateAllCustomMutationHooks({ operations: [sampleMutationOperation], typeRegistry: emptyTypeRegistry, - reactQueryEnabled: false + reactQueryEnabled: false, }); expect(results).toEqual([]); }); @@ -326,7 +398,7 @@ describe('Custom mutation generators with reactQueryEnabled: true (default)', () it('should return mutation file by default', () => { const result = generateCustomMutationHook({ operation: sampleMutationOperation, - typeRegistry: emptyTypeRegistry + typeRegistry: emptyTypeRegistry, }); expect(result).not.toBeNull(); expect(result!.content).toContain('useMutation'); diff --git a/graphql/codegen/src/__tests__/codegen/scalars.test.ts b/graphql/codegen/src/__tests__/codegen/scalars.test.ts index 7c81ae5c9..52165537f 100644 --- a/graphql/codegen/src/__tests__/codegen/scalars.test.ts +++ b/graphql/codegen/src/__tests__/codegen/scalars.test.ts @@ -7,7 +7,7 @@ import { SCALAR_NAMES, SCALAR_TS_MAP, scalarToFilterType, - scalarToTsType + scalarToTsType, } from '../../core/codegen/scalars'; describe('scalars', () => { @@ -81,11 +81,15 @@ describe('scalars', () => { }); it('returns unknown for unknown scalars when option set', () => { - expect(scalarToTsType('CustomScalar', { unknownScalar: 'unknown' })).toBe('unknown'); + expect(scalarToTsType('CustomScalar', { unknownScalar: 'unknown' })).toBe( + 'unknown', + ); }); it('uses overrides when provided', () => { - expect(scalarToTsType('JSON', { overrides: { JSON: 'JsonValue' } })).toBe('JsonValue'); + expect(scalarToTsType('JSON', { overrides: { JSON: 'JsonValue' } })).toBe( + 'JsonValue', + ); }); }); diff --git a/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts b/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts index 526013226..4c8ef2a93 100644 --- a/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts +++ b/graphql/codegen/src/__tests__/codegen/schema-types-generator.test.ts @@ -2,22 +2,38 @@ * Snapshot tests for schema-types-generator */ import { generateSchemaTypesFile } from '../../core/codegen/schema-types-generator'; -import type { ResolvedType,TypeRegistry } from '../../types/schema'; +import type { ResolvedType, TypeRegistry } from '../../types/schema'; -function createTypeRegistry(types: Array<[string, ResolvedType]>): TypeRegistry { +function createTypeRegistry( + types: Array<[string, ResolvedType]>, +): TypeRegistry { return new Map(types); } describe('schema-types-generator', () => { it('generates enum types as string unions', () => { const registry = createTypeRegistry([ - ['Status', { kind: 'ENUM', name: 'Status', enumValues: ['ACTIVE', 'INACTIVE', 'PENDING'] }], - ['Priority', { kind: 'ENUM', name: 'Priority', enumValues: ['LOW', 'MEDIUM', 'HIGH'] }] + [ + 'Status', + { + kind: 'ENUM', + name: 'Status', + enumValues: ['ACTIVE', 'INACTIVE', 'PENDING'], + }, + ], + [ + 'Priority', + { + kind: 'ENUM', + name: 'Priority', + enumValues: ['LOW', 'MEDIUM', 'HIGH'], + }, + ], ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set() + tableTypeNames: new Set(), }); expect(result.content).toMatchSnapshot(); @@ -32,11 +48,18 @@ describe('schema-types-generator', () => { kind: 'INPUT_OBJECT', name: 'CreateUserInput', inputFields: [ - { name: 'email', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'String' } } }, + { + name: 'email', + type: { + kind: 'NON_NULL', + name: null, + ofType: { kind: 'SCALAR', name: 'String' }, + }, + }, { name: 'name', type: { kind: 'SCALAR', name: 'String' } }, - { name: 'age', type: { kind: 'SCALAR', name: 'Int' } } - ] - } + { name: 'age', type: { kind: 'SCALAR', name: 'Int' } }, + ], + }, ], [ 'UpdateUserInput', @@ -44,16 +67,23 @@ describe('schema-types-generator', () => { kind: 'INPUT_OBJECT', name: 'UpdateUserInput', inputFields: [ - { name: 'id', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'UUID' } } }, - { name: 'name', type: { kind: 'SCALAR', name: 'String' } } - ] - } - ] + { + name: 'id', + type: { + kind: 'NON_NULL', + name: null, + ofType: { kind: 'SCALAR', name: 'UUID' }, + }, + }, + { name: 'name', type: { kind: 'SCALAR', name: 'String' } }, + ], + }, + ], ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set() + tableTypeNames: new Set(), }); expect(result.content).toMatchSnapshot(); @@ -61,12 +91,19 @@ describe('schema-types-generator', () => { it('generates union types', () => { const registry = createTypeRegistry([ - ['SearchResult', { kind: 'UNION', name: 'SearchResult', possibleTypes: ['User', 'Post', 'Comment'] }] + [ + 'SearchResult', + { + kind: 'UNION', + name: 'SearchResult', + possibleTypes: ['User', 'Post', 'Comment'], + }, + ], ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set() + tableTypeNames: new Set(), }); expect(result.content).toMatchSnapshot(); @@ -80,9 +117,9 @@ describe('schema-types-generator', () => { kind: 'OBJECT', name: 'Mutation', fields: [ - { name: 'login', type: { kind: 'OBJECT', name: 'LoginPayload' } } - ] - } + { name: 'login', type: { kind: 'OBJECT', name: 'LoginPayload' } }, + ], + }, ], [ 'LoginPayload', @@ -90,17 +127,24 @@ describe('schema-types-generator', () => { kind: 'OBJECT', name: 'LoginPayload', fields: [ - { name: 'token', type: { kind: 'NON_NULL', name: null, ofType: { kind: 'SCALAR', name: 'String' } } }, + { + name: 'token', + type: { + kind: 'NON_NULL', + name: null, + ofType: { kind: 'SCALAR', name: 'String' }, + }, + }, { name: 'refreshToken', type: { kind: 'SCALAR', name: 'String' } }, - { name: 'user', type: { kind: 'OBJECT', name: 'User' } } - ] - } - ] + { name: 'user', type: { kind: 'OBJECT', name: 'User' } }, + ], + }, + ], ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(['User']) + tableTypeNames: new Set(['User']), }); expect(result.content).toMatchSnapshot(); @@ -111,12 +155,19 @@ describe('schema-types-generator', () => { const registry = createTypeRegistry([ ['User', { kind: 'ENUM', name: 'User', enumValues: ['ADMIN'] }], ['String', { kind: 'ENUM', name: 'String', enumValues: ['A'] }], - ['CustomEnum', { kind: 'ENUM', name: 'CustomEnum', enumValues: ['VALUE_A', 'VALUE_B'] }] + [ + 'CustomEnum', + { + kind: 'ENUM', + name: 'CustomEnum', + enumValues: ['VALUE_A', 'VALUE_B'], + }, + ], ]); const result = generateSchemaTypesFile({ typeRegistry: registry, - tableTypeNames: new Set(['User']) + tableTypeNames: new Set(['User']), }); expect(result.content).toMatchSnapshot(); diff --git a/graphql/codegen/src/__tests__/codegen/utils.test.ts b/graphql/codegen/src/__tests__/codegen/utils.test.ts index 38f9f066d..4dcb4ad08 100644 --- a/graphql/codegen/src/__tests__/codegen/utils.test.ts +++ b/graphql/codegen/src/__tests__/codegen/utils.test.ts @@ -12,24 +12,27 @@ import { toCamelCase, toPascalCase, toScreamingSnake, - ucFirst + ucFirst, } from '../../core/codegen/utils'; -import type { CleanRelations,CleanTable } from '../../types/schema'; +import type { CleanRelations, CleanTable } from '../../types/schema'; const emptyRelations: CleanRelations = { belongsTo: [], hasOne: [], hasMany: [], - manyToMany: [] + manyToMany: [], }; // Use any for test fixture overrides to avoid strict type requirements -function createTable(name: string, overrides: Record = {}): CleanTable { +function createTable( + name: string, + overrides: Record = {}, +): CleanTable { return { name, fields: [], relations: emptyRelations, - ...overrides + ...overrides, } as CleanTable; } @@ -78,8 +81,8 @@ describe('utils', () => { it('uses inflection overrides when provided', () => { const result = getTableNames( createTable('Person', { - inflection: { tableFieldName: 'individual', allRows: 'people' } - }) + inflection: { tableFieldName: 'individual', allRows: 'people' }, + }), ); expect(result.singularName).toBe('individual'); expect(result.pluralName).toBe('people'); @@ -88,8 +91,14 @@ describe('utils', () => { it('uses query.all for plural name', () => { const result = getTableNames( createTable('Child', { - query: { all: 'children', one: 'child', create: 'createChild', update: 'updateChild', delete: 'deleteChild' } - }) + query: { + all: 'children', + one: 'child', + create: 'createChild', + update: 'updateChild', + delete: 'deleteChild', + }, + }), ); expect(result.pluralName).toBe('children'); }); @@ -98,12 +107,18 @@ describe('utils', () => { describe('type name generators', () => { it('getFilterTypeName returns correct filter type', () => { expect(getFilterTypeName(createTable('User'))).toBe('UserFilter'); - expect(getFilterTypeName(createTable('Post', { inflection: { filterType: 'PostCondition' } }))).toBe('PostCondition'); + expect( + getFilterTypeName( + createTable('Post', { inflection: { filterType: 'PostCondition' } }), + ), + ).toBe('PostCondition'); }); it('getOrderByTypeName returns correct orderBy type', () => { expect(getOrderByTypeName(createTable('User'))).toBe('UsersOrderBy'); - expect(getOrderByTypeName(createTable('Address'))).toBe('AddressesOrderBy'); + expect(getOrderByTypeName(createTable('Address'))).toBe( + 'AddressesOrderBy', + ); }); }); @@ -138,19 +153,30 @@ describe('utils', () => { it('extracts PK from constraints', () => { const table = createTable('User', { constraints: { - primaryKey: [{ name: 'users_pkey', fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }] }] - } + primaryKey: [ + { + name: 'users_pkey', + fields: [ + { name: 'id', type: { gqlType: 'UUID', isArray: false } }, + ], + }, + ], + }, }); const result = getPrimaryKeyInfo(table); - expect(result).toEqual([{ name: 'id', gqlType: 'UUID', tsType: 'string' }]); + expect(result).toEqual([ + { name: 'id', gqlType: 'UUID', tsType: 'string' }, + ]); }); it('falls back to id field', () => { const table = createTable('User', { - fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }] + fields: [{ name: 'id', type: { gqlType: 'UUID', isArray: false } }], }); const result = getPrimaryKeyInfo(table); - expect(result).toEqual([{ name: 'id', gqlType: 'UUID', tsType: 'string' }]); + expect(result).toEqual([ + { name: 'id', gqlType: 'UUID', tsType: 'string' }, + ]); }); it('handles composite keys', () => { @@ -161,11 +187,11 @@ describe('utils', () => { name: 'user_roles_pkey', fields: [ { name: 'userId', type: { gqlType: 'UUID', isArray: false } }, - { name: 'roleId', type: { gqlType: 'UUID', isArray: false } } - ] - } - ] - } + { name: 'roleId', type: { gqlType: 'UUID', isArray: false } }, + ], + }, + ], + }, }); const result = getPrimaryKeyInfo(table); expect(result).toHaveLength(2); diff --git a/graphql/codegen/src/__tests__/config/resolve-config.test.ts b/graphql/codegen/src/__tests__/config/resolve-config.test.ts index c3d5cd162..72a346bd0 100644 --- a/graphql/codegen/src/__tests__/config/resolve-config.test.ts +++ b/graphql/codegen/src/__tests__/config/resolve-config.test.ts @@ -1,16 +1,14 @@ -import type { - GraphQLSDKConfigTarget -} from '../../types/config'; +import type { GraphQLSDKConfigTarget } from '../../types/config'; import { DEFAULT_CONFIG, getConfigOptions, - mergeConfig + mergeConfig, } from '../../types/config'; describe('config resolution', () => { it('resolves config with defaults', () => { const config: GraphQLSDKConfigTarget = { - endpoint: 'https://api.example.com/graphql' + endpoint: 'https://api.example.com/graphql', }; const resolved = getConfigOptions(config); @@ -20,7 +18,9 @@ describe('config resolution', () => { expect(resolved.output).toBe(DEFAULT_CONFIG.output); expect(resolved.tables.include).toEqual(DEFAULT_CONFIG.tables.include); expect(resolved.queries.exclude).toEqual(DEFAULT_CONFIG.queries.exclude); - expect(resolved.queries.systemExclude).toEqual(DEFAULT_CONFIG.queries.systemExclude); + expect(resolved.queries.systemExclude).toEqual( + DEFAULT_CONFIG.queries.systemExclude, + ); }); it('merges nested config values with overrides', () => { @@ -29,9 +29,9 @@ describe('config resolution', () => { tables: { include: ['User'] }, queryKeys: { relationships: { - database: { parent: 'organization', foreignKey: 'organizationId' } - } - } + database: { parent: 'organization', foreignKey: 'organizationId' }, + }, + }, }; const overrides: GraphQLSDKConfigTarget = { @@ -40,9 +40,9 @@ describe('config resolution', () => { tables: { exclude: ['_internal'] }, queryKeys: { relationships: { - table: { parent: 'database', foreignKey: 'databaseId' } - } - } + table: { parent: 'database', foreignKey: 'databaseId' }, + }, + }, }; const merged = mergeConfig(base, overrides); @@ -50,15 +50,15 @@ describe('config resolution', () => { expect(merged.output).toBe('./generated/custom'); expect(merged.headers).toEqual({ Authorization: 'Bearer base', - 'X-Custom': '1' + 'X-Custom': '1', }); expect(merged.tables).toEqual({ include: ['User'], - exclude: ['_internal'] + exclude: ['_internal'], }); expect(merged.queryKeys?.relationships).toEqual({ database: { parent: 'organization', foreignKey: 'organizationId' }, - table: { parent: 'database', foreignKey: 'databaseId' } + table: { parent: 'database', foreignKey: 'databaseId' }, }); }); }); diff --git a/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts b/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts index 80091abed..810aa90c6 100644 --- a/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts +++ b/graphql/codegen/src/__tests__/introspect/infer-tables.test.ts @@ -11,7 +11,7 @@ import type { IntrospectionInputValue, IntrospectionQueryResponse, IntrospectionType, - IntrospectionTypeRef + IntrospectionTypeRef, } from '../../types/introspection'; // ============================================================================ @@ -24,7 +24,7 @@ import type { function makeTypeRef( kind: 'SCALAR' | 'OBJECT' | 'INPUT_OBJECT' | 'ENUM' | 'LIST' | 'NON_NULL', name: string | null, - ofType?: IntrospectionTypeRef | null + ofType?: IntrospectionTypeRef | null, ): IntrospectionTypeRef { return { kind, name, ofType: ofType ?? null }; } @@ -89,7 +89,7 @@ interface TypeDef { function createIntrospection( types: TypeDef[], queryFields: FieldDef[] = [], - mutationFields: FieldDef[] = [] + mutationFields: FieldDef[] = [], ): IntrospectionQueryResponse { const makeField = (f: FieldDef): IntrospectionField => ({ name: f.name, @@ -99,19 +99,19 @@ function createIntrospection( name: a.name, type: a.type, description: null, - defaultValue: null - }) + defaultValue: null, + }), ), deprecationReason: null, description: null, - isDeprecated: false + isDeprecated: false, }); const makeInputField = (f: InputFieldDef): IntrospectionInputValue => ({ name: f.name, type: f.type, description: null, - defaultValue: null + defaultValue: null, }); // Add Query and Mutation types @@ -124,21 +124,21 @@ function createIntrospection( enumValues: null, interfaces: [], possibleTypes: null, - description: null + description: null, }, ...(mutationFields.length > 0 ? [ - { - name: 'Mutation', - kind: 'OBJECT' as const, - fields: mutationFields.map(makeField), - inputFields: null, - enumValues: null, - interfaces: [], - possibleTypes: null, - description: null - } - ] + { + name: 'Mutation', + kind: 'OBJECT' as const, + fields: mutationFields.map(makeField), + inputFields: null, + enumValues: null, + interfaces: [], + possibleTypes: null, + description: null, + }, + ] : []), ...types.map( (t): IntrospectionType => ({ @@ -152,19 +152,19 @@ function createIntrospection( enumValues: t.kind === 'ENUM' ? (t.enumValues ?? []).map( - (v): IntrospectionEnumValue => ({ - name: v, - deprecationReason: null, - description: null, - isDeprecated: false - }) - ) + (v): IntrospectionEnumValue => ({ + name: v, + deprecationReason: null, + description: null, + isDeprecated: false, + }), + ) : null, interfaces: [], possibleTypes: null, - description: null - }) - ) + description: null, + }), + ), ]; return { @@ -173,8 +173,8 @@ function createIntrospection( mutationType: mutationFields.length > 0 ? { name: 'Mutation' } : null, subscriptionType: null, types: allTypes, - directives: [] - } + directives: [], + }, }; } @@ -192,8 +192,8 @@ describe('Entity Detection', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'email', type: scalar('String') } - ] + { name: 'email', type: scalar('String') }, + ], }, // UsersConnection type (indicates User is an entity) { @@ -201,16 +201,16 @@ describe('Entity Detection', () => { kind: 'OBJECT', fields: [ { name: 'nodes', type: list(object('User')) }, - { name: 'pageInfo', type: nonNull(object('PageInfo')) } - ] + { name: 'pageInfo', type: nonNull(object('PageInfo')) }, + ], }, // PageInfo (builtin, should be ignored) - { name: 'PageInfo', kind: 'OBJECT', fields: [] } + { name: 'PageInfo', kind: 'OBJECT', fields: [] }, ], [ // Query for users - { name: 'users', type: object('UsersConnection') } - ] + { name: 'users', type: object('UsersConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -225,27 +225,27 @@ describe('Entity Detection', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Post', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'Comment', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'CommentsConnection', kind: 'OBJECT', fields: [] } + { name: 'CommentsConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'users', type: object('UsersConnection') }, { name: 'posts', type: object('PostsConnection') }, - { name: 'comments', type: object('CommentsConnection') } - ] + { name: 'comments', type: object('CommentsConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -261,17 +261,17 @@ describe('Entity Detection', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, // Does not have Connection (should be ignored) { name: 'AuditLog', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] - } + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + }, ], - [{ name: 'users', type: object('UsersConnection') }] + [{ name: 'users', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -297,12 +297,12 @@ describe('Field Extraction', () => { { name: 'email', type: scalar('String') }, { name: 'age', type: scalar('Int') }, { name: 'isActive', type: scalar('Boolean') }, - { name: 'metadata', type: scalar('JSON') } - ] + { name: 'metadata', type: scalar('JSON') }, + ], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], - [{ name: 'users', type: object('UsersConnection') }] + [{ name: 'users', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -314,7 +314,7 @@ describe('Field Extraction', () => { 'email', 'age', 'isActive', - 'metadata' + 'metadata', ]); expect(fields.find((f) => f.name === 'id')?.type.gqlType).toBe('UUID'); expect(fields.find((f) => f.name === 'email')?.type.gqlType).toBe('String'); @@ -328,12 +328,12 @@ describe('Field Extraction', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'tags', type: list(scalar('String')) } - ] + { name: 'tags', type: list(scalar('String')) }, + ], }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] } + { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, ], - [{ name: 'posts', type: object('PostsConnection') }] + [{ name: 'posts', type: object('PostsConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -354,28 +354,28 @@ describe('Field Extraction', () => { { name: 'id', type: nonNull(scalar('UUID')) }, { name: 'title', type: scalar('String') }, { name: 'author', type: object('User') }, // belongsTo relation - { name: 'comments', type: object('CommentsConnection') } // hasMany relation - ] + { name: 'comments', type: object('CommentsConnection') }, // hasMany relation + ], }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Comment', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'CommentsConnection', kind: 'OBJECT', fields: [] } + { name: 'CommentsConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'posts', type: object('PostsConnection') }, { name: 'users', type: object('UsersConnection') }, - { name: 'comments', type: object('CommentsConnection') } - ] + { name: 'comments', type: object('CommentsConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -402,21 +402,21 @@ describe('Relation Inference', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'author', type: object('User') } - ] + { name: 'author', type: object('User') }, + ], }, { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'posts', type: object('PostsConnection') }, - { name: 'users', type: object('UsersConnection') } - ] + { name: 'users', type: object('UsersConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -435,21 +435,21 @@ describe('Relation Inference', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'posts', type: object('PostsConnection') } - ] + { name: 'posts', type: object('PostsConnection') }, + ], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'Post', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] } + { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'users', type: object('UsersConnection') }, - { name: 'posts', type: object('PostsConnection') } - ] + { name: 'posts', type: object('PostsConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -471,22 +471,22 @@ describe('Relation Inference', () => { // ManyToMany pattern: {entities}By{JunctionTable}{Keys} { name: 'productsByProductCategoryProductIdAndCategoryId', - type: object('ProductsConnection') - } - ] + type: object('ProductsConnection'), + }, + ], }, { name: 'CategoriesConnection', kind: 'OBJECT', fields: [] }, { name: 'Product', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'ProductsConnection', kind: 'OBJECT', fields: [] } + { name: 'ProductsConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'categories', type: object('CategoriesConnection') }, - { name: 'products', type: object('ProductsConnection') } - ] + { name: 'products', type: object('ProductsConnection') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -495,7 +495,7 @@ describe('Relation Inference', () => { expect(categoryTable?.relations.manyToMany).toHaveLength(1); expect(categoryTable?.relations.manyToMany[0].rightTable).toBe('Product'); expect(categoryTable?.relations.manyToMany[0].junctionTable).toBe( - 'ProductCategory' + 'ProductCategory', ); }); }); @@ -511,11 +511,11 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], - [{ name: 'allUsers', type: object('UsersConnection') }] + [{ name: 'allUsers', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -529,18 +529,18 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'users', type: object('UsersConnection') }, { name: 'user', type: object('User'), - args: [{ name: 'id', type: nonNull(scalar('UUID')) }] - } - ] + args: [{ name: 'id', type: nonNull(scalar('UUID')) }], + }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -554,14 +554,14 @@ describe('Query Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], [ - { name: 'users', type: object('UsersConnection') } + { name: 'users', type: object('UsersConnection') }, // No single user query - ] + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -579,10 +579,10 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, - { name: 'CreateUserPayload', kind: 'OBJECT', fields: [] } + { name: 'CreateUserPayload', kind: 'OBJECT', fields: [] }, ], [{ name: 'users', type: object('UsersConnection') }], [ @@ -590,10 +590,10 @@ describe('Mutation Operation Matching', () => { name: 'createUser', type: object('CreateUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('CreateUserInput')) } - ] - } - ] + { name: 'input', type: nonNull(inputObject('CreateUserInput')) }, + ], + }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -607,11 +607,11 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, - { name: 'DeleteUserPayload', kind: 'OBJECT', fields: [] } + { name: 'DeleteUserPayload', kind: 'OBJECT', fields: [] }, ], [{ name: 'users', type: object('UsersConnection') }], [ @@ -619,17 +619,17 @@ describe('Mutation Operation Matching', () => { name: 'updateUser', type: object('UpdateUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('UpdateUserInput')) } - ] + { name: 'input', type: nonNull(inputObject('UpdateUserInput')) }, + ], }, { name: 'deleteUser', type: object('DeleteUserPayload'), args: [ - { name: 'input', type: nonNull(inputObject('DeleteUserInput')) } - ] - } - ] + { name: 'input', type: nonNull(inputObject('DeleteUserInput')) }, + ], + }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -644,16 +644,16 @@ describe('Mutation Operation Matching', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, - { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] } + { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, ], [{ name: 'users', type: object('UsersConnection') }], [ { name: 'updateUserById', type: object('UpdateUserPayload') }, - { name: 'updateUser', type: object('UpdateUserPayload') } - ] + { name: 'updateUser', type: object('UpdateUserPayload') }, + ], ); const tables = inferTablesFromIntrospection(introspection); @@ -673,11 +673,11 @@ describe('Constraint Inference', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, ], - [{ name: 'users', type: object('UsersConnection') }] + [{ name: 'users', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -692,17 +692,17 @@ describe('Constraint Inference', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'userId', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'userId', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UpdateUserInput', kind: 'INPUT_OBJECT', - inputFields: [{ name: 'id', type: nonNull(scalar('UUID')) }] - } + inputFields: [{ name: 'id', type: nonNull(scalar('UUID')) }], + }, ], [{ name: 'users', type: object('UsersConnection') }], - [{ name: 'updateUser', type: object('UpdateUserPayload') }] + [{ name: 'updateUser', type: object('UpdateUserPayload') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -723,14 +723,14 @@ describe('Inflection Building', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { name: 'UserFilter', kind: 'INPUT_OBJECT', inputFields: [] }, { name: 'UserPatch', kind: 'INPUT_OBJECT', inputFields: [] }, - { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] } + { name: 'UpdateUserPayload', kind: 'OBJECT', fields: [] }, ], - [{ name: 'users', type: object('UsersConnection') }] + [{ name: 'users', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -750,12 +750,12 @@ describe('Inflection Building', () => { { name: 'User', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'UsersConnection', kind: 'OBJECT', fields: [] } + { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, // No UserFilter, UserPatch, or UpdateUserPayload ], - [{ name: 'users', type: object('UsersConnection') }] + [{ name: 'users', type: object('UsersConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -776,9 +776,9 @@ describe('Edge Cases', () => { const introspection = createIntrospection( [ // Only built-in types, no entities - { name: 'PageInfo', kind: 'OBJECT', fields: [] } + { name: 'PageInfo', kind: 'OBJECT', fields: [] }, ], - [] + [], ); const tables = inferTablesFromIntrospection(introspection); @@ -793,11 +793,11 @@ describe('Edge Cases', () => { { name: 'Orphan', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'OrphansConnection', kind: 'OBJECT', fields: [] } + { name: 'OrphansConnection', kind: 'OBJECT', fields: [] }, ], - [] // No query fields + [], // No query fields ); const tables = inferTablesFromIntrospection(introspection); @@ -814,8 +814,8 @@ describe('Edge Cases', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'posts', type: object('PostsConnection') } - ] + { name: 'posts', type: object('PostsConnection') }, + ], }, { name: 'UsersConnection', kind: 'OBJECT', fields: [] }, { @@ -823,15 +823,15 @@ describe('Edge Cases', () => { kind: 'OBJECT', fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, - { name: 'author', type: object('User') } - ] + { name: 'author', type: object('User') }, + ], }, - { name: 'PostsConnection', kind: 'OBJECT', fields: [] } + { name: 'PostsConnection', kind: 'OBJECT', fields: [] }, ], [ { name: 'users', type: object('UsersConnection') }, - { name: 'posts', type: object('PostsConnection') } - ] + { name: 'posts', type: object('PostsConnection') }, + ], ); // Should not cause infinite loops @@ -852,11 +852,11 @@ describe('Edge Cases', () => { { name: 'Person', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, - { name: 'PeopleConnection', kind: 'OBJECT', fields: [] } + { name: 'PeopleConnection', kind: 'OBJECT', fields: [] }, ], - [{ name: 'people', type: object('PeopleConnection') }] + [{ name: 'people', type: object('PeopleConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -875,17 +875,17 @@ describe('Edge Cases', () => { fields: [ { name: 'id', type: nonNull(scalar('UUID')) }, { name: 'street', type: scalar('String') }, - { name: 'city', type: scalar('String') } - ] + { name: 'city', type: scalar('String') }, + ], }, { name: 'AddressesConnection', kind: 'OBJECT', fields: [] }, { name: 'AddressesOrderBy', kind: 'ENUM', - enumValues: ['ID_ASC', 'ID_DESC'] - } + enumValues: ['ID_ASC', 'ID_DESC'], + }, ], - [{ name: 'addresses', type: object('AddressesConnection') }] + [{ name: 'addresses', type: object('AddressesConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -903,12 +903,12 @@ describe('Edge Cases', () => { { name: 'Category', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'CategoriesConnection', kind: 'OBJECT', fields: [] }, - { name: 'CategoriesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] } + { name: 'CategoriesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] }, ], - [{ name: 'categories', type: object('CategoriesConnection') }] + [{ name: 'categories', type: object('CategoriesConnection') }], ); const tables = inferTablesFromIntrospection(introspection); @@ -923,13 +923,13 @@ describe('Edge Cases', () => { { name: 'Status', kind: 'OBJECT', - fields: [{ name: 'id', type: nonNull(scalar('UUID')) }] + fields: [{ name: 'id', type: nonNull(scalar('UUID')) }], }, { name: 'StatusesConnection', kind: 'OBJECT', fields: [] }, // Schema has the actual OrderBy enum - { name: 'StatusesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] } + { name: 'StatusesOrderBy', kind: 'ENUM', enumValues: ['ID_ASC'] }, ], - [{ name: 'statuses', type: object('StatusesConnection') }] + [{ name: 'statuses', type: object('StatusesConnection') }], ); const tables = inferTablesFromIntrospection(introspection); diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index cfea02a20..605de2bfa 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -16,7 +16,7 @@ import { camelizeArgv, codegenQuestions, printResult, - seedArgvFromConfig + seedArgvFromConfig, } from './shared'; const usage = ` @@ -50,7 +50,7 @@ Generator Options: export const commands = async ( argv: Record, prompter: Inquirerer, - _options: CLIOptions + _options: CLIOptions, ): Promise> => { if (argv.version) { const pkg = getPackageJson(__dirname); @@ -64,11 +64,16 @@ export const commands = async ( } const hasSourceFlags = Boolean( - argv.endpoint || argv.e || argv['schema-file'] || argv.s || - argv.schemas || argv['api-names'] + argv.endpoint || + argv.e || + argv['schema-file'] || + argv.s || + argv.schemas || + argv['api-names'], ); const explicitConfigPath = (argv.config || argv.c) as string | undefined; - const configPath = explicitConfigPath || (!hasSourceFlags ? findConfigFile() : undefined); + const configPath = + explicitConfigPath || (!hasSourceFlags ? findConfigFile() : undefined); const targetName = (argv.target || argv.t) as string | undefined; let fileConfig: GraphQLSDKConfigTarget = {}; @@ -81,22 +86,34 @@ export const commands = async ( } const config = loaded.config as Record; - const isMulti = !('endpoint' in config || 'schemaFile' in config || 'db' in config); + const isMulti = !( + 'endpoint' in config || + 'schemaFile' in config || + 'db' in config + ); if (isMulti) { const targets = config as Record; const names = targetName ? [targetName] : Object.keys(targets); if (targetName && !targets[targetName]) { - console.error('x', `Target "${targetName}" not found. Available: ${Object.keys(targets).join(', ')}`); + console.error( + 'x', + `Target "${targetName}" not found. Available: ${Object.keys(targets).join(', ')}`, + ); process.exit(1); } - const cliOptions = buildDbConfig(camelizeArgv(argv as Record)); + const cliOptions = buildDbConfig( + camelizeArgv(argv as Record), + ); let hasError = false; for (const name of names) { console.log(`\n[${name}]`); - const result = await generate({ ...targets[name], ...cliOptions } as GraphQLSDKConfigTarget); + const result = await generate({ + ...targets[name], + ...cliOptions, + } as GraphQLSDKConfigTarget); printResult(result); if (!result.success) hasError = true; } @@ -128,17 +145,31 @@ export const options: Partial = { o: 'output', t: 'target', a: 'authorization', - v: 'verbose' + v: 'verbose', }, boolean: [ - 'help', 'version', 'verbose', 'dry-run', 'react-query', 'orm', 'keep-db' + 'help', + 'version', + 'verbose', + 'dry-run', + 'react-query', + 'orm', + 'keep-db', ], string: [ - 'config', 'endpoint', 'schema-file', 'output', 'target', 'authorization', - 'pgpm-module-path', 'pgpm-workspace-path', 'pgpm-module-name', - 'schemas', 'api-names' - ] - } + 'config', + 'endpoint', + 'schema-file', + 'output', + 'target', + 'authorization', + 'pgpm-module-path', + 'pgpm-workspace-path', + 'pgpm-module-name', + 'schemas', + 'api-names', + ], + }, }; if (require.main === module) { diff --git a/graphql/codegen/src/cli/shared.ts b/graphql/codegen/src/cli/shared.ts index 26370919b..32ccde541 100644 --- a/graphql/codegen/src/cli/shared.ts +++ b/graphql/codegen/src/cli/shared.ts @@ -1,6 +1,6 @@ /** * Shared CLI utilities for graphql-codegen - * + * * These are exported so that packages/cli can use the same questions, * types, and transform utilities, ensuring consistency between the two CLIs. */ @@ -11,9 +11,14 @@ import type { Question } from 'inquirerer'; import type { GenerateResult } from '../core/generate'; import type { GraphQLSDKConfigTarget } from '../types/config'; -export const splitCommas = (input: string | undefined): string[] | undefined => { +export const splitCommas = ( + input: string | undefined, +): string[] | undefined => { if (!input) return undefined; - return input.split(',').map((s) => s.trim()).filter(Boolean); + return input + .split(',') + .map((s) => s.trim()) + .filter(Boolean); }; export interface CodegenAnswers { @@ -34,13 +39,13 @@ export const codegenQuestions: Question[] = [ name: 'endpoint', message: 'GraphQL endpoint URL', type: 'text', - required: false + required: false, }, { name: 'schema-file', message: 'Path to GraphQL schema file', type: 'text', - required: false + required: false, }, { name: 'output', @@ -48,21 +53,21 @@ export const codegenQuestions: Question[] = [ type: 'text', required: false, default: 'codegen', - useDefault: true + useDefault: true, }, { name: 'schemas', message: 'PostgreSQL schemas (comma-separated)', type: 'text', required: false, - sanitize: splitCommas + sanitize: splitCommas, }, { name: 'api-names', message: 'API names (comma-separated)', type: 'text', required: false, - sanitize: splitCommas + sanitize: splitCommas, }, { name: 'react-query', @@ -70,7 +75,7 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true + useDefault: true, }, { name: 'orm', @@ -78,13 +83,13 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true + useDefault: true, }, { name: 'authorization', message: 'Authorization header value', type: 'text', - required: false + required: false, }, { name: 'dry-run', @@ -92,7 +97,7 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true + useDefault: true, }, { name: 'verbose', @@ -100,8 +105,8 @@ export const codegenQuestions: Question[] = [ type: 'confirm', required: false, default: false, - useDefault: true - } + useDefault: true, + }, ]; export function printResult(result: GenerateResult): void { @@ -126,38 +131,56 @@ const skipNonTopLevel = (key: string, path: string[]) => !isTopLevel(key, path) || key === '_' || key.startsWith('_'); export const camelizeArgv = (argv: Record): Record => - inflektTree(argv, (key) => { - const underscored = key.replace(/-/g, '_'); - return camelize(underscored, true); - }, { skip: skipNonTopLevel }); + inflektTree( + argv, + (key) => { + const underscored = key.replace(/-/g, '_'); + return camelize(underscored, true); + }, + { skip: skipNonTopLevel }, + ); export const hyphenateKeys = (obj: Record): Record => - inflektTree(obj, (key) => key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()), { - skip: skipNonTopLevel - }); + inflektTree( + obj, + (key) => key.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()), + { + skip: skipNonTopLevel, + }, + ); // ============================================================================ // Config <-> CLI shape transforms // ============================================================================ -export function filterDefined(obj: Record): Record { +export function filterDefined( + obj: Record, +): Record { return Object.fromEntries( - Object.entries(obj).filter(([, v]) => v !== undefined && v !== null) + Object.entries(obj).filter(([, v]) => v !== undefined && v !== null), ); } -export function flattenDbFields(config: Record): Record { +export function flattenDbFields( + config: Record, +): Record { const { db, ...rest } = config; if (db && typeof db === 'object') { const dbObj = db as Record; - const schemas = Array.isArray(dbObj.schemas) ? dbObj.schemas.join(',') : dbObj.schemas; - const apiNames = Array.isArray(dbObj.apiNames) ? dbObj.apiNames.join(',') : dbObj.apiNames; + const schemas = Array.isArray(dbObj.schemas) + ? dbObj.schemas.join(',') + : dbObj.schemas; + const apiNames = Array.isArray(dbObj.apiNames) + ? dbObj.apiNames.join(',') + : dbObj.apiNames; return { ...rest, schemas, apiNames }; } return rest; } -export function buildDbConfig(options: Record): Record { +export function buildDbConfig( + options: Record, +): Record { const { schemas, apiNames, ...rest } = options; if (schemas || apiNames) { return { ...rest, db: { schemas, apiNames } }; @@ -167,7 +190,7 @@ export function buildDbConfig(options: Record): Record, - fileConfig: GraphQLSDKConfigTarget + fileConfig: GraphQLSDKConfigTarget, ): Record { const flat = flattenDbFields(fileConfig as Record); const hyphenated = hyphenateKeys(flat); @@ -177,7 +200,7 @@ export function seedArgvFromConfig( export function buildGenerateOptions( answers: Record, - fileConfig: GraphQLSDKConfigTarget = {} + fileConfig: GraphQLSDKConfigTarget = {}, ): GraphQLSDKConfigTarget { const camelized = camelizeArgv(answers); const withDb = buildDbConfig(camelized); diff --git a/graphql/codegen/src/client/error.ts b/graphql/codegen/src/client/error.ts index 368094f84..53df412c9 100644 --- a/graphql/codegen/src/client/error.ts +++ b/graphql/codegen/src/client/error.ts @@ -43,7 +43,7 @@ export const DataErrorType = { EXCLUSION_VIOLATION: 'EXCLUSION_VIOLATION', // Generic errors - UNKNOWN_ERROR: 'UNKNOWN_ERROR' + UNKNOWN_ERROR: 'UNKNOWN_ERROR', } as const; export type DataErrorType = (typeof DataErrorType)[keyof typeof DataErrorType]; @@ -73,7 +73,11 @@ export class DataError extends Error { public readonly fieldName?: string; public readonly constraint?: string; - constructor(type: DataErrorType, message: string, options: DataErrorOptions = {}) { + constructor( + type: DataErrorType, + message: string, + options: DataErrorOptions = {}, + ) { super(message); this.name = 'DataError'; this.type = type; @@ -91,36 +95,36 @@ export class DataError extends Error { getUserMessage(): string { switch (this.type) { - case DataErrorType.NETWORK_ERROR: - return 'Network error. Please check your connection and try again.'; - case DataErrorType.TIMEOUT_ERROR: - return 'Request timed out. Please try again.'; - case DataErrorType.UNAUTHORIZED: - return 'You are not authorized. Please log in and try again.'; - case DataErrorType.FORBIDDEN: - return 'You do not have permission to access this resource.'; - case DataErrorType.VALIDATION_FAILED: - return 'Validation failed. Please check your input and try again.'; - case DataErrorType.REQUIRED_FIELD_MISSING: - return this.fieldName - ? `The field "${this.fieldName}" is required.` - : 'A required field is missing.'; - case DataErrorType.UNIQUE_VIOLATION: - return this.fieldName - ? `A record with this ${this.fieldName} already exists.` - : 'A record with this value already exists.'; - case DataErrorType.FOREIGN_KEY_VIOLATION: - return 'This record references a record that does not exist.'; - case DataErrorType.NOT_NULL_VIOLATION: - return this.fieldName - ? `The field "${this.fieldName}" cannot be empty.` - : 'A required field cannot be empty.'; - case DataErrorType.CHECK_VIOLATION: - return this.fieldName - ? `The value for "${this.fieldName}" is not valid.` - : 'The value does not meet the required constraints.'; - default: - return this.message || 'An unexpected error occurred.'; + case DataErrorType.NETWORK_ERROR: + return 'Network error. Please check your connection and try again.'; + case DataErrorType.TIMEOUT_ERROR: + return 'Request timed out. Please try again.'; + case DataErrorType.UNAUTHORIZED: + return 'You are not authorized. Please log in and try again.'; + case DataErrorType.FORBIDDEN: + return 'You do not have permission to access this resource.'; + case DataErrorType.VALIDATION_FAILED: + return 'Validation failed. Please check your input and try again.'; + case DataErrorType.REQUIRED_FIELD_MISSING: + return this.fieldName + ? `The field "${this.fieldName}" is required.` + : 'A required field is missing.'; + case DataErrorType.UNIQUE_VIOLATION: + return this.fieldName + ? `A record with this ${this.fieldName} already exists.` + : 'A record with this value already exists.'; + case DataErrorType.FOREIGN_KEY_VIOLATION: + return 'This record references a record that does not exist.'; + case DataErrorType.NOT_NULL_VIOLATION: + return this.fieldName + ? `The field "${this.fieldName}" cannot be empty.` + : 'A required field cannot be empty.'; + case DataErrorType.CHECK_VIOLATION: + return this.fieldName + ? `The value for "${this.fieldName}" is not valid.` + : 'The value does not meet the required constraints.'; + default: + return this.message || 'An unexpected error occurred.'; } } @@ -148,7 +152,7 @@ export const PG_ERROR_CODES = { DATETIME_FIELD_OVERFLOW: '22008', UNDEFINED_TABLE: '42P01', UNDEFINED_COLUMN: '42703', - INSUFFICIENT_PRIVILEGE: '42501' + INSUFFICIENT_PRIVILEGE: '42501', } as const; // ============================================================================ @@ -157,10 +161,14 @@ export const PG_ERROR_CODES = { export const createError = { network: (originalError?: Error) => - new DataError(DataErrorType.NETWORK_ERROR, 'Network error occurred', { originalError }), + new DataError(DataErrorType.NETWORK_ERROR, 'Network error occurred', { + originalError, + }), timeout: (originalError?: Error) => - new DataError(DataErrorType.TIMEOUT_ERROR, 'Request timed out', { originalError }), + new DataError(DataErrorType.TIMEOUT_ERROR, 'Request timed out', { + originalError, + }), unauthorized: (message = 'Authentication required') => new DataError(DataErrorType.UNAUTHORIZED, message), @@ -178,16 +186,38 @@ export const createError = { new DataError(DataErrorType.GRAPHQL_ERROR, message, { code }), uniqueViolation: (message: string, fieldName?: string, constraint?: string) => - new DataError(DataErrorType.UNIQUE_VIOLATION, message, { fieldName, constraint, code: '23505' }), - - foreignKeyViolation: (message: string, fieldName?: string, constraint?: string) => - new DataError(DataErrorType.FOREIGN_KEY_VIOLATION, message, { fieldName, constraint, code: '23503' }), - - notNullViolation: (message: string, fieldName?: string, constraint?: string) => - new DataError(DataErrorType.NOT_NULL_VIOLATION, message, { fieldName, constraint, code: '23502' }), + new DataError(DataErrorType.UNIQUE_VIOLATION, message, { + fieldName, + constraint, + code: '23505', + }), + + foreignKeyViolation: ( + message: string, + fieldName?: string, + constraint?: string, + ) => + new DataError(DataErrorType.FOREIGN_KEY_VIOLATION, message, { + fieldName, + constraint, + code: '23503', + }), + + notNullViolation: ( + message: string, + fieldName?: string, + constraint?: string, + ) => + new DataError(DataErrorType.NOT_NULL_VIOLATION, message, { + fieldName, + constraint, + code: '23502', + }), unknown: (originalError: Error) => - new DataError(DataErrorType.UNKNOWN_ERROR, originalError.message, { originalError }) + new DataError(DataErrorType.UNKNOWN_ERROR, originalError.message, { + originalError, + }), }; // ============================================================================ @@ -208,28 +238,41 @@ function parseGraphQLErrorCode(code: string | undefined): DataErrorType { // GraphQL standard codes if (normalized === 'UNAUTHENTICATED') return DataErrorType.UNAUTHORIZED; if (normalized === 'FORBIDDEN') return DataErrorType.FORBIDDEN; - if (normalized === 'GRAPHQL_VALIDATION_FAILED') return DataErrorType.QUERY_GENERATION_FAILED; + if (normalized === 'GRAPHQL_VALIDATION_FAILED') + return DataErrorType.QUERY_GENERATION_FAILED; // PostgreSQL SQLSTATE codes - if (code === PG_ERROR_CODES.UNIQUE_VIOLATION) return DataErrorType.UNIQUE_VIOLATION; - if (code === PG_ERROR_CODES.FOREIGN_KEY_VIOLATION) return DataErrorType.FOREIGN_KEY_VIOLATION; - if (code === PG_ERROR_CODES.NOT_NULL_VIOLATION) return DataErrorType.NOT_NULL_VIOLATION; - if (code === PG_ERROR_CODES.CHECK_VIOLATION) return DataErrorType.CHECK_VIOLATION; - if (code === PG_ERROR_CODES.EXCLUSION_VIOLATION) return DataErrorType.EXCLUSION_VIOLATION; + if (code === PG_ERROR_CODES.UNIQUE_VIOLATION) + return DataErrorType.UNIQUE_VIOLATION; + if (code === PG_ERROR_CODES.FOREIGN_KEY_VIOLATION) + return DataErrorType.FOREIGN_KEY_VIOLATION; + if (code === PG_ERROR_CODES.NOT_NULL_VIOLATION) + return DataErrorType.NOT_NULL_VIOLATION; + if (code === PG_ERROR_CODES.CHECK_VIOLATION) + return DataErrorType.CHECK_VIOLATION; + if (code === PG_ERROR_CODES.EXCLUSION_VIOLATION) + return DataErrorType.EXCLUSION_VIOLATION; return DataErrorType.UNKNOWN_ERROR; } function classifyByMessage(message: string): DataErrorType { const lower = message.toLowerCase(); - + if (lower.includes('timeout') || lower.includes('timed out')) { return DataErrorType.TIMEOUT_ERROR; } - if (lower.includes('network') || lower.includes('fetch') || lower.includes('failed to fetch')) { + if ( + lower.includes('network') || + lower.includes('fetch') || + lower.includes('failed to fetch') + ) { return DataErrorType.NETWORK_ERROR; } - if (lower.includes('unauthorized') || lower.includes('authentication required')) { + if ( + lower.includes('unauthorized') || + lower.includes('authentication required') + ) { return DataErrorType.UNAUTHORIZED; } if (lower.includes('forbidden') || lower.includes('permission')) { @@ -241,21 +284,30 @@ function classifyByMessage(message: string): DataErrorType { if (lower.includes('foreign key constraint')) { return DataErrorType.FOREIGN_KEY_VIOLATION; } - if (lower.includes('not-null constraint') || lower.includes('null value in column')) { + if ( + lower.includes('not-null constraint') || + lower.includes('null value in column') + ) { return DataErrorType.NOT_NULL_VIOLATION; } - + return DataErrorType.UNKNOWN_ERROR; } -function extractFieldFromError(message: string, constraint?: string, column?: string): string | undefined { +function extractFieldFromError( + message: string, + constraint?: string, + column?: string, +): string | undefined { if (column) return column; const columnMatch = message.match(/column\s+"?([a-z_][a-z0-9_]*)"?/i); if (columnMatch) return columnMatch[1]; if (constraint) { - const constraintMatch = constraint.match(/_([a-z_][a-z0-9_]*)_(?:key|fkey|check|pkey)$/i); + const constraintMatch = constraint.match( + /_([a-z_][a-z0-9_]*)_(?:key|fkey|check|pkey)$/i, + ); if (constraintMatch) return constraintMatch[1]; } @@ -286,14 +338,18 @@ export function parseGraphQLError(error: unknown): DataError { const column = gqlError.extensions?.column as string | undefined; const constraint = gqlError.extensions?.constraint as string | undefined; - const fieldName = extractFieldFromError(gqlError.message, constraint, column); + const fieldName = extractFieldFromError( + gqlError.message, + constraint, + column, + ); if (mappedType !== DataErrorType.UNKNOWN_ERROR) { return new DataError(mappedType, gqlError.message, { code: extCode, fieldName, constraint, - context: gqlError.extensions + context: gqlError.extensions, }); } @@ -303,7 +359,7 @@ export function parseGraphQLError(error: unknown): DataError { code: extCode, fieldName, constraint, - context: gqlError.extensions + context: gqlError.extensions, }); } diff --git a/graphql/codegen/src/client/execute.ts b/graphql/codegen/src/client/execute.ts index 1c4441acf..b8b4e1562 100644 --- a/graphql/codegen/src/client/execute.ts +++ b/graphql/codegen/src/client/execute.ts @@ -5,7 +5,7 @@ import type { DocumentNode } from 'graphql'; import { print } from 'graphql'; -import { createError, type DataError,parseGraphQLError } from './error'; +import { createError, type DataError, parseGraphQLError } from './error'; import { TypedDocumentString } from './typed-document'; // ============================================================================ @@ -17,13 +17,15 @@ type ExecutableDocument = | DocumentNode | string; -type ResultOf = TDocument extends TypedDocumentString - ? TResult - : unknown; +type ResultOf = + TDocument extends TypedDocumentString + ? TResult + : unknown; -type VariablesOf = TDocument extends TypedDocumentString - ? TVariables - : Record; +type VariablesOf = + TDocument extends TypedDocumentString + ? TVariables + : Record; export interface ExecuteOptions { /** Custom headers to include */ @@ -69,7 +71,7 @@ export async function execute( endpoint: string, document: TDocument, variables?: VariablesOf, - options: ExecuteOptions = {} + options: ExecuteOptions = {}, ): Promise> { const { headers = {}, timeout = 30000, signal } = options; @@ -88,13 +90,13 @@ export async function execute( headers: { 'Content-Type': 'application/json', Accept: 'application/graphql-response+json, application/json', - ...headers + ...headers, }, body: JSON.stringify({ query: documentToString(document), - ...(variables !== undefined && { variables }) + ...(variables !== undefined && { variables }), }), - signal: combinedSignal + signal: combinedSignal, }); clearTimeout(timeoutId); @@ -173,7 +175,11 @@ export interface GraphQLClientOptions { * Create a GraphQL client instance */ export function createGraphQLClient(options: GraphQLClientOptions) { - const { endpoint, headers: defaultHeaders = {}, timeout: defaultTimeout = 30000 } = options; + const { + endpoint, + headers: defaultHeaders = {}, + timeout: defaultTimeout = 30000, + } = options; return { /** @@ -182,12 +188,12 @@ export function createGraphQLClient(options: GraphQLClientOptions) { async execute( document: TDocument, variables?: VariablesOf, - options: ExecuteOptions = {} + options: ExecuteOptions = {}, ): Promise> { return execute(endpoint, document, variables, { headers: { ...defaultHeaders, ...options.headers }, timeout: options.timeout ?? defaultTimeout, - signal: options.signal + signal: options.signal, }); }, @@ -196,7 +202,7 @@ export function createGraphQLClient(options: GraphQLClientOptions) { */ getEndpoint(): string { return endpoint; - } + }, }; } diff --git a/graphql/codegen/src/client/index.ts b/graphql/codegen/src/client/index.ts index 2ec241259..c29e97c93 100644 --- a/graphql/codegen/src/client/index.ts +++ b/graphql/codegen/src/client/index.ts @@ -10,7 +10,7 @@ export { type GraphQLError, isDataError, parseGraphQLError, - PG_ERROR_CODES + PG_ERROR_CODES, } from './error'; export { createGraphQLClient, @@ -18,6 +18,9 @@ export { type ExecuteOptions, type GraphQLClient, type GraphQLClientOptions, - type GraphQLResponse + type GraphQLResponse, } from './execute'; -export { type DocumentTypeDecoration,TypedDocumentString } from './typed-document'; +export { + type DocumentTypeDecoration, + TypedDocumentString, +} from './typed-document'; diff --git a/graphql/codegen/src/client/typed-document.ts b/graphql/codegen/src/client/typed-document.ts index 95350ad96..ca3f895a4 100644 --- a/graphql/codegen/src/client/typed-document.ts +++ b/graphql/codegen/src/client/typed-document.ts @@ -33,7 +33,7 @@ export class TypedDocumentString this.value = value; this.__meta__ = { hash: this.generateHash(value), - ...meta + ...meta, }; } diff --git a/graphql/codegen/src/core/ast.ts b/graphql/codegen/src/core/ast.ts index 7c3b95db7..d402ccb46 100644 --- a/graphql/codegen/src/core/ast.ts +++ b/graphql/codegen/src/core/ast.ts @@ -5,7 +5,7 @@ import type { FieldNode, TypeNode, ValueNode, - VariableDefinitionNode + VariableDefinitionNode, } from 'graphql'; import { camelize, singularize } from 'inflekt'; @@ -16,14 +16,12 @@ import type { NestedProperties, ObjectArrayItem, QueryFieldSelection, - QueryProperty + QueryProperty, } from './types'; const NON_MUTABLE_PROPS = ['createdAt', 'createdBy', 'updatedAt', 'updatedBy']; -const objectToArray = ( - obj: Record -): ObjectArrayItem[] => +const objectToArray = (obj: Record): ObjectArrayItem[] => Object.keys(obj).map((k) => ({ key: k, name: obj[k].name || k, @@ -31,7 +29,7 @@ const objectToArray = ( isNotNull: obj[k].isNotNull, isArray: obj[k].isArray, isArrayNotNull: obj[k].isArrayNotNull, - properties: obj[k].properties + properties: obj[k].properties, })); interface CreateGqlMutationParams { @@ -51,32 +49,32 @@ const createGqlMutation = ({ selections, variableDefinitions, modelName, - useModel = true + useModel = true, }: CreateGqlMutationParams): DocumentNode => { const opSel: FieldNode[] = !modelName ? [ - t.field({ - name: operationName, - args: selectArgs, - selectionSet: t.selectionSet({ selections }) - }) - ] + t.field({ + name: operationName, + args: selectArgs, + selectionSet: t.selectionSet({ selections }), + }), + ] : [ - t.field({ - name: operationName, - args: selectArgs, - selectionSet: t.selectionSet({ - selections: useModel - ? [ - t.field({ - name: modelName, - selectionSet: t.selectionSet({ selections }) - }) - ] - : selections - }) - }) - ]; + t.field({ + name: operationName, + args: selectArgs, + selectionSet: t.selectionSet({ + selections: useModel + ? [ + t.field({ + name: modelName, + selectionSet: t.selectionSet({ selections }), + }), + ] + : selections, + }), + }), + ]; return t.document({ definitions: [ @@ -84,16 +82,16 @@ const createGqlMutation = ({ operation: 'mutation', name: mutationName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }) - }) - ] + selectionSet: t.selectionSet({ selections: opSel }), + }), + ], }); }; export const getAll = ({ queryName, operationName, - selection + selection, }: ASTFunctionParams): DocumentNode => { const selections = getSelections(selection); @@ -103,15 +101,15 @@ export const getAll = ({ selectionSet: t.selectionSet({ selections: [ t.field({ - name: 'totalCount' + name: 'totalCount', }), t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections }) - }) - ] - }) - }) + selectionSet: t.selectionSet({ selections }), + }), + ], + }), + }), ]; const ast = t.document({ @@ -119,9 +117,9 @@ export const getAll = ({ t.operationDefinition({ operation: 'query', name: queryName, - selectionSet: t.selectionSet({ selections: opSel }) - }) - ] + selectionSet: t.selectionSet({ selections: opSel }), + }), + ], }); return ast; @@ -130,7 +128,7 @@ export const getAll = ({ export const getCount = ({ queryName, operationName, - query + query, }: Omit): DocumentNode => { const Singular = query.model; const Filter = `${Singular}Filter`; @@ -139,17 +137,17 @@ export const getCount = ({ const variableDefinitions: VariableDefinitionNode[] = [ t.variableDefinition({ variable: t.variable({ name: 'condition' }), - type: t.namedType({ type: Condition }) + type: t.namedType({ type: Condition }), }), t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: Filter }) - }) + type: t.namedType({ type: Filter }), + }), ]; const args: ArgumentNode[] = [ t.argument({ name: 'condition', value: t.variable({ name: 'condition' }) }), - t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }) + t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }), ]; // PostGraphile supports totalCount through connections @@ -160,11 +158,11 @@ export const getCount = ({ selectionSet: t.selectionSet({ selections: [ t.field({ - name: 'totalCount' - }) - ] - }) - }) + name: 'totalCount', + }), + ], + }), + }), ]; const ast = t.document({ @@ -173,9 +171,9 @@ export const getCount = ({ operation: 'query', name: queryName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }) - }) - ] + selectionSet: t.selectionSet({ selections: opSel }), + }), + ], }); return ast; @@ -186,11 +184,10 @@ export const getMany = ({ queryName, operationName, query, - selection + selection, }: ASTFunctionParams): DocumentNode => { const Singular = query.model; - const Plural = - operationName.charAt(0).toUpperCase() + operationName.slice(1); + const Plural = operationName.charAt(0).toUpperCase() + operationName.slice(1); const Condition = `${Singular}Condition`; const Filter = `${Singular}Filter`; const OrderBy = `${Plural}OrderBy`; @@ -199,38 +196,38 @@ export const getMany = ({ const variableDefinitions: VariableDefinitionNode[] = [ t.variableDefinition({ variable: t.variable({ name: 'first' }), - type: t.namedType({ type: 'Int' }) + type: t.namedType({ type: 'Int' }), }), t.variableDefinition({ variable: t.variable({ name: 'last' }), - type: t.namedType({ type: 'Int' }) + type: t.namedType({ type: 'Int' }), }), t.variableDefinition({ variable: t.variable({ name: 'after' }), - type: t.namedType({ type: 'Cursor' }) + type: t.namedType({ type: 'Cursor' }), }), t.variableDefinition({ variable: t.variable({ name: 'before' }), - type: t.namedType({ type: 'Cursor' }) + type: t.namedType({ type: 'Cursor' }), }), t.variableDefinition({ variable: t.variable({ name: 'offset' }), - type: t.namedType({ type: 'Int' }) + type: t.namedType({ type: 'Int' }), }), t.variableDefinition({ variable: t.variable({ name: 'condition' }), - type: t.namedType({ type: Condition }) + type: t.namedType({ type: Condition }), }), t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: Filter }) + type: t.namedType({ type: Filter }), }), t.variableDefinition({ variable: t.variable({ name: 'orderBy' }), type: t.listType({ - type: t.nonNullType({ type: t.namedType({ type: OrderBy }) }) - }) - }) + type: t.nonNullType({ type: t.namedType({ type: OrderBy }) }), + }), + }), ]; const args: ArgumentNode[] = [ @@ -241,44 +238,44 @@ export const getMany = ({ t.argument({ name: 'before', value: t.variable({ name: 'before' }) }), t.argument({ name: 'condition', - value: t.variable({ name: 'condition' }) + value: t.variable({ name: 'condition' }), }), t.argument({ name: 'filter', value: t.variable({ name: 'filter' }) }), - t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }) + t.argument({ name: 'orderBy', value: t.variable({ name: 'orderBy' }) }), ]; const pageInfoFields: FieldNode[] = [ t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'endCursor' }), - t.field({ name: 'startCursor' }) + t.field({ name: 'startCursor' }), ]; const dataField: FieldNode = builder?._edges ? t.field({ - name: 'edges', - selectionSet: t.selectionSet({ - selections: [ - t.field({ name: 'cursor' }), - t.field({ - name: 'node', - selectionSet: t.selectionSet({ selections }) - }) - ] + name: 'edges', + selectionSet: t.selectionSet({ + selections: [ + t.field({ name: 'cursor' }), + t.field({ + name: 'node', + selectionSet: t.selectionSet({ selections }), + }), + ], + }), }) - }) : t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections }) - }); + name: 'nodes', + selectionSet: t.selectionSet({ selections }), + }); const connectionFields: FieldNode[] = [ t.field({ name: 'totalCount' }), t.field({ name: 'pageInfo', - selectionSet: t.selectionSet({ selections: pageInfoFields }) + selectionSet: t.selectionSet({ selections: pageInfoFields }), }), - dataField + dataField, ]; const ast = t.document({ @@ -292,12 +289,12 @@ export const getMany = ({ t.field({ name: operationName, args, - selectionSet: t.selectionSet({ selections: connectionFields }) - }) - ] - }) - }) - ] + selectionSet: t.selectionSet({ selections: connectionFields }), + }), + ], + }), + }), + ], }); return ast; @@ -307,10 +304,10 @@ export const getOne = ({ queryName, operationName, query, - selection + selection, }: ASTFunctionParams): DocumentNode => { const variableDefinitions: VariableDefinitionNode[] = Object.keys( - query.properties + query.properties, ) .map((key) => ({ key, ...query.properties[key] })) .filter((field) => field.isNotNull) @@ -320,7 +317,7 @@ export const getOne = ({ type: fieldType, isNotNull, isArray, - isArrayNotNull + isArrayNotNull, } = field; let type: TypeNode = t.namedType({ type: fieldType }); if (isNotNull) type = t.nonNullType({ type }); @@ -330,7 +327,7 @@ export const getOne = ({ } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type + type, }); }); @@ -341,7 +338,7 @@ export const getOne = ({ .map((field) => { return t.argument({ name: field.name, - value: t.variable({ name: field.name }) + value: t.variable({ name: field.name }), }); }); @@ -351,8 +348,8 @@ export const getOne = ({ t.field({ name: operationName, args: selectArgs, - selectionSet: t.selectionSet({ selections }) - }) + selectionSet: t.selectionSet({ selections }), + }), ]; const ast = t.document({ @@ -361,9 +358,9 @@ export const getOne = ({ operation: 'query', name: queryName, variableDefinitions, - selectionSet: t.selectionSet({ selections: opSel }) - }) - ] + selectionSet: t.selectionSet({ selections: opSel }), + }), + ], }); return ast; }; @@ -372,16 +369,13 @@ export const createOne = ({ mutationName, operationName, mutation, - selection + selection, }: MutationASTParams): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); } - const modelName = camelize( - [singularize(mutation.model)].join('_'), - true - ); + const modelName = camelize([singularize(mutation.model)].join('_'), true); const inputProperties = mutation.properties.input .properties as NestedProperties; @@ -392,10 +386,10 @@ export const createOne = ({ } const allAttrs = objectToArray( - modelProperties.properties as Record + modelProperties.properties as Record, ); const attrs = allAttrs.filter( - (field) => !NON_MUTABLE_PROPS.includes(field.name) + (field) => !NON_MUTABLE_PROPS.includes(field.name), ); const variableDefinitions = getCreateVariablesAst(attrs); @@ -411,14 +405,14 @@ export const createOne = ({ fields: attrs.map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }) - }) - ) - }) - }) - ] - }) - }) + value: t.variable({ name: field.name }), + }), + ), + }), + }), + ], + }), + }), ]; const selections = selection @@ -431,7 +425,7 @@ export const createOne = ({ selectArgs, selections, variableDefinitions, - modelName + modelName, }); return ast; @@ -441,16 +435,13 @@ export const patchOne = ({ mutationName, operationName, mutation, - selection + selection, }: MutationASTParams): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); } - const modelName = camelize( - [singularize(mutation.model)].join('_'), - true - ); + const modelName = camelize([singularize(mutation.model)].join('_'), true); const inputProperties = mutation.properties.input .properties as NestedProperties; @@ -461,10 +452,10 @@ export const patchOne = ({ : []; const patchAttrs = allAttrs.filter( - (prop) => !NON_MUTABLE_PROPS.includes(prop.name) + (prop) => !NON_MUTABLE_PROPS.includes(prop.name), ); const patchByAttrs = objectToArray( - inputProperties as Record + inputProperties as Record, ).filter((n) => n.name !== 'patch'); const patchers = patchByAttrs.map((p) => p.name); @@ -478,8 +469,8 @@ export const patchOne = ({ ...patchByAttrs.map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }) - }) + value: t.variable({ name: field.name }), + }), ), t.objectField({ name: 'patch', @@ -489,14 +480,14 @@ export const patchOne = ({ .map((field) => t.objectField({ name: field.name, - value: t.variable({ name: field.name }) - }) - ) - }) - }) - ] - }) - }) + value: t.variable({ name: field.name }), + }), + ), + }), + }), + ], + }), + }), ]; const selections = selection @@ -509,7 +500,7 @@ export const patchOne = ({ selectArgs, selections, variableDefinitions, - modelName + modelName, }); return ast; @@ -518,21 +509,18 @@ export const patchOne = ({ export const deleteOne = ({ mutationName, operationName, - mutation + mutation, }: Omit): DocumentNode => { if (!mutation.properties?.input?.properties) { throw new Error(`No input field for mutation: ${mutationName}`); } - const modelName = camelize( - [singularize(mutation.model)].join('_'), - true - ); + const modelName = camelize([singularize(mutation.model)].join('_'), true); const inputProperties = mutation.properties.input .properties as NestedProperties; const deleteAttrs = objectToArray( - inputProperties as Record + inputProperties as Record, ); const variableDefinitions: VariableDefinitionNode[] = deleteAttrs.map( @@ -547,9 +535,9 @@ export const deleteOne = ({ } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type + type, }); - } + }, ); const selectArgs: ArgumentNode[] = [ @@ -559,11 +547,11 @@ export const deleteOne = ({ fields: deleteAttrs.map((f) => t.objectField({ name: f.name, - value: t.variable({ name: f.name }) - }) - ) - }) - }) + value: t.variable({ name: f.name }), + }), + ), + }), + }), ]; // so we can support column select grants plugin @@ -575,13 +563,15 @@ export const deleteOne = ({ selections, useModel: false, variableDefinitions, - modelName + modelName, }); return ast; }; -export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[] { +export function getSelections( + selection: QueryFieldSelection[] = [], +): FieldNode[] { const selectionAst = (field: string | QueryFieldSelection): FieldNode => { if (typeof field === 'string') { return t.field({ name: field }); @@ -596,7 +586,10 @@ export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[ 'pgType' in fieldDefn.type ) { const customAst = getCustomAst( - fieldDefn as { name: string; type: { gqlType: string; pgType: string; isArray: boolean } } + fieldDefn as { + name: string; + type: { gqlType: string; pgType: string; isArray: boolean }; + }, ); if (customAst) return customAst; } @@ -613,11 +606,11 @@ export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[ const [argName, argValue] = variable; const argAst = t.argument({ name: argName, - value: getComplexValueAst(argValue) + value: getComplexValueAst(argValue), }); return argAst ? [...acc, argAst] : acc; }, - [] + [], ); const subSelections = @@ -626,19 +619,19 @@ export function getSelections(selection: QueryFieldSelection[] = []): FieldNode[ const selectionSet = isBelongTo ? t.selectionSet({ selections: subSelections }) : t.selectionSet({ - selections: [ - t.field({ name: 'totalCount' }), - t.field({ - name: 'nodes', - selectionSet: t.selectionSet({ selections: subSelections }) - }) - ] - }); + selections: [ + t.field({ name: 'totalCount' }), + t.field({ + name: 'nodes', + selectionSet: t.selectionSet({ selections: subSelections }), + }), + ], + }); return t.field({ name, args, - selectionSet + selectionSet, }); } else { return selectionAst(selectionDefn); @@ -669,7 +662,7 @@ function getComplexValueAst(value: unknown): ValueNode { // Handle arrays if (Array.isArray(value)) { return t.listValue({ - values: value.map((item) => getComplexValueAst(item)) + values: value.map((item) => getComplexValueAst(item)), }); } @@ -680,9 +673,9 @@ function getComplexValueAst(value: unknown): ValueNode { fields: Object.entries(obj).map(([key, val]) => t.objectField({ name: key, - value: getComplexValueAst(val) - }) - ) + value: getComplexValueAst(val), + }), + ), }); } @@ -690,7 +683,7 @@ function getComplexValueAst(value: unknown): ValueNode { } function getCreateVariablesAst( - attrs: ObjectArrayItem[] + attrs: ObjectArrayItem[], ): VariableDefinitionNode[] { return attrs.map((field) => { const { @@ -698,7 +691,7 @@ function getCreateVariablesAst( type: fieldType, isNotNull, isArray, - isArrayNotNull + isArrayNotNull, } = field; let type: TypeNode = t.namedType({ type: fieldType }); if (isNotNull) type = t.nonNullType({ type }); @@ -708,20 +701,24 @@ function getCreateVariablesAst( } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type + type, }); }); } function getUpdateVariablesAst( attrs: ObjectArrayItem[], - patchers: string[] + patchers: string[], ): VariableDefinitionNode[] { const patchVariables: VariableDefinitionNode[] = attrs .filter((field) => !patchers.includes(field.name)) .map((field) => { - const { name: fieldName, type: fieldType, isArray, isArrayNotNull } = - field; + const { + name: fieldName, + type: fieldType, + isArray, + isArrayNotNull, + } = field; let type: TypeNode = t.namedType({ type: fieldType }); if (isArray) { type = t.listType({ type }); @@ -729,14 +726,14 @@ function getUpdateVariablesAst( } return t.variableDefinition({ variable: t.variable({ name: fieldName }), - type + type, }); }); const patcherVariables: VariableDefinitionNode[] = patchers.map((patcher) => { return t.variableDefinition({ variable: t.variable({ name: patcher }), - type: t.nonNullType({ type: t.namedType({ type: 'String' }) }) + type: t.nonNullType({ type: t.namedType({ type: 'String' }) }), }); }); diff --git a/graphql/codegen/src/core/codegen/babel-ast.ts b/graphql/codegen/src/core/codegen/babel-ast.ts index 0b981d4de..40caa2e76 100644 --- a/graphql/codegen/src/core/codegen/babel-ast.ts +++ b/graphql/codegen/src/core/codegen/babel-ast.ts @@ -9,7 +9,7 @@ import generate from '@babel/generator'; import * as t from '@babel/types'; // Re-export for convenience -export { generate,t }; +export { generate, t }; /** * Generate code from an array of statements @@ -29,7 +29,7 @@ export const commentBlock = (value: string): t.CommentBlock => { value, start: null, end: null, - loc: null + loc: null, }; }; @@ -42,7 +42,7 @@ export const commentLine = (value: string): t.CommentLine => { value, start: null, end: null, - loc: null + loc: null, }; }; @@ -50,10 +50,11 @@ export const commentLine = (value: string): t.CommentLine => { * Add a leading JSDoc comment to a node */ export function addJSDocComment(node: T, lines: string[]): T { - const commentText = lines.length === 1 - ? `* ${lines[0]} ` - : `*\n${lines.map(line => ` * ${line}`).join('\n')}\n `; - + const commentText = + lines.length === 1 + ? `* ${lines[0]} ` + : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `; + if (!node.leadingComments) { node.leadingComments = []; } @@ -76,16 +77,15 @@ export function addLineComment(node: T, text: string): T { * Create an 'as const' assertion - common pattern worth abstracting */ export function asConst(expression: t.Expression): t.TSAsExpression { - return t.tsAsExpression( - expression, - t.tsTypeReference(t.identifier('const')) - ); + return t.tsAsExpression(expression, t.tsTypeReference(t.identifier('const'))); } /** * Create an array expression with 'as const' - very common pattern */ -export function constArray(elements: (t.Expression | t.SpreadElement)[]): t.TSAsExpression { +export function constArray( + elements: (t.Expression | t.SpreadElement)[], +): t.TSAsExpression { return asConst(t.arrayExpression(elements)); } @@ -95,7 +95,7 @@ export function constArray(elements: (t.Expression | t.SpreadElement)[]): t.TSAs export function typedParam( name: string, typeAnnotation: t.TSType, - optional: boolean = false + optional: boolean = false, ): t.Identifier { const param = t.identifier(name); param.typeAnnotation = t.tsTypeAnnotation(typeAnnotation); @@ -125,7 +125,7 @@ export function keyofTypeof(name: string): t.TSTypeOperator { export function createTypedCallExpression( callee: t.Expression, args: (t.Expression | t.SpreadElement)[], - typeParams: t.TSType[] + typeParams: t.TSType[], ): t.CallExpression { const call = t.callExpression(callee, args); if (typeParams.length > 0) { diff --git a/graphql/codegen/src/core/codegen/barrel.ts b/graphql/codegen/src/core/codegen/barrel.ts index dc11a6843..f5f23f51e 100644 --- a/graphql/codegen/src/core/codegen/barrel.ts +++ b/graphql/codegen/src/core/codegen/barrel.ts @@ -6,7 +6,7 @@ import * as t from '@babel/types'; import type { CleanTable } from '../../types/schema'; -import { addJSDocComment,generateCode } from './babel-ast'; +import { addJSDocComment, generateCode } from './babel-ast'; import { getOperationHookName } from './type-resolver'; import { getCreateMutationHookName, @@ -14,7 +14,7 @@ import { getListQueryHookName, getSingleQueryHookName, getUpdateMutationHookName, - hasValidPrimaryKey + hasValidPrimaryKey, } from './utils'; /** @@ -47,7 +47,7 @@ export function generateQueriesBarrel(tables: CleanTable[]): string { addJSDocComment(statements[0], [ 'Query hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten' + 'DO NOT EDIT - changes will be overwritten', ]); } @@ -82,7 +82,7 @@ export function generateMutationsBarrel(tables: CleanTable[]): string { addJSDocComment(statements[0], [ 'Mutation hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten' + 'DO NOT EDIT - changes will be overwritten', ]); } @@ -107,7 +107,7 @@ export interface MainBarrelOptions { export function generateMainBarrel( tables: CleanTable[], - options: MainBarrelOptions = {} + options: MainBarrelOptions = {}, ): string { const opts: MainBarrelOptions = options; @@ -115,7 +115,7 @@ export function generateMainBarrel( hasMutations = true, hasQueryKeys = false, hasMutationKeys = false, - hasInvalidation = false + hasInvalidation = false, } = opts; const tableNames = tables.map((tbl) => tbl.name).join(', '); @@ -176,7 +176,7 @@ export function generateMainBarrel( ' const { mutate } = useCreateCarMutation();', ' // ...', '}', - '```' + '```', ]); } @@ -215,7 +215,7 @@ export function generateRootBarrel(options: RootBarrelOptions = {}): string { if (statements.length > 0) { addJSDocComment(statements[0], [ 'Generated SDK - auto-generated, do not edit', - '@generated by @constructive-io/graphql-codegen' + '@generated by @constructive-io/graphql-codegen', ]); } @@ -231,7 +231,7 @@ export function generateRootBarrel(options: RootBarrelOptions = {}): string { */ export function generateCustomQueriesBarrel( tables: CleanTable[], - customQueryNames: string[] + customQueryNames: string[], ): string { const statements: t.Statement[] = []; const exportedHooks = new Set(); @@ -268,7 +268,7 @@ export function generateCustomQueriesBarrel( addJSDocComment(statements[0], [ 'Query hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten' + 'DO NOT EDIT - changes will be overwritten', ]); } @@ -280,7 +280,7 @@ export function generateCustomQueriesBarrel( */ export function generateCustomMutationsBarrel( tables: CleanTable[], - customMutationNames: string[] + customMutationNames: string[], ): string { const statements: t.Statement[] = []; const exportedHooks = new Set(); @@ -324,7 +324,7 @@ export function generateCustomMutationsBarrel( addJSDocComment(statements[0], [ 'Mutation hooks barrel export', '@generated by @constructive-io/graphql-codegen', - 'DO NOT EDIT - changes will be overwritten' + 'DO NOT EDIT - changes will be overwritten', ]); } diff --git a/graphql/codegen/src/core/codegen/client.ts b/graphql/codegen/src/core/codegen/client.ts index ed5727e96..047517f6e 100644 --- a/graphql/codegen/src/core/codegen/client.ts +++ b/graphql/codegen/src/core/codegen/client.ts @@ -15,7 +15,7 @@ function findTemplateFile(templateName: string): string { return templatePath; } throw new Error( - `Could not find template file: ${templateName}. Searched in: ${templatePath}` + `Could not find template file: ${templateName}. Searched in: ${templatePath}`, ); } @@ -26,7 +26,7 @@ function readTemplateFile(templateName: string, description: string): string { /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/; content = content.replace( headerPattern, - getGeneratedFileHeader(description) + '\n' + getGeneratedFileHeader(description) + '\n', ); return content; } @@ -35,5 +35,8 @@ function readTemplateFile(templateName: string, description: string): string { * Generate client.ts content - ORM client wrapper with configure/getClient */ export function generateClientFile(): string { - return readTemplateFile('hooks-client.ts', 'ORM client wrapper for React Query hooks'); + return readTemplateFile( + 'hooks-client.ts', + 'ORM client wrapper for React Query hooks', + ); } diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index f8f3f56c4..420f4a7cb 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -15,10 +15,7 @@ */ import * as t from '@babel/types'; -import type { - CleanOperation, - TypeRegistry -} from '../../types/schema'; +import type { CleanOperation, TypeRegistry } from '../../types/schema'; import { buildSelectionArgsCall, callExpr, @@ -42,17 +39,15 @@ import { typeRef, useMutationOptionsType, useMutationResultType, - voidStatement + voidStatement, } from './hooks-ast'; -import { - getSelectTypeName -} from './select-helpers'; +import { getSelectTypeName } from './select-helpers'; import { createTypeTracker, getOperationFileName, getOperationHookName, getTypeBaseName, - typeRefToTsType + typeRefToTsType, } from './type-resolver'; import { ucFirst } from './utils'; @@ -65,7 +60,6 @@ export interface GeneratedCustomMutationFile { export interface GenerateCustomMutationHookOptions { operation: CleanOperation; typeRegistry: TypeRegistry; - maxDepth?: number; skipQueryField?: boolean; reactQueryEnabled?: boolean; tableTypeNames?: Set; @@ -73,7 +67,7 @@ export interface GenerateCustomMutationHookOptions { } export function generateCustomMutationHook( - options: GenerateCustomMutationHookOptions + options: GenerateCustomMutationHookOptions, ): GeneratedCustomMutationFile | null { const { operation, reactQueryEnabled = true } = options; @@ -85,19 +79,21 @@ export function generateCustomMutationHook( return generateCustomMutationHookInternal(options); } catch (err) { console.error(`Error generating hook for mutation: ${operation.name}`); - console.error(` Args: ${operation.args.length}, Return type: ${operation.returnType.kind}/${operation.returnType.name}`); + console.error( + ` Args: ${operation.args.length}, Return type: ${operation.returnType.kind}/${operation.returnType.name}`, + ); throw err; } } function generateCustomMutationHookInternal( - options: GenerateCustomMutationHookOptions + options: GenerateCustomMutationHookOptions, ): GeneratedCustomMutationFile { const { operation, typeRegistry, tableTypeNames, - useCentralizedKeys = true + useCentralizedKeys = true, } = options; const hookName = getOperationHookName(operation.name, 'mutation'); @@ -120,18 +116,34 @@ function generateCustomMutationHookInternal( const statements: t.Statement[] = []; // Imports - statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', ['useMutation']), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseMutationOptions', 'UseMutationResult'], + true, + ), + ); statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { - statements.push(createImportDeclaration('../mutation-keys', ['customMutationKeys'])); + statements.push( + createImportDeclaration('../mutation-keys', ['customMutationKeys']), + ); } if (hasArgs) { - statements.push(createImportDeclaration('../../orm/mutation', [varTypeName], true)); + statements.push( + createImportDeclaration('../../orm/mutation', [varTypeName], true), + ); } const inputTypeImports: string[] = []; @@ -146,11 +158,19 @@ function generateCustomMutationHookInternal( } } if (inputTypeImports.length > 0) { - statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true)); + statements.push( + createImportDeclaration('../../orm/input-types', inputTypeImports, true), + ); } if (hasSelect) { - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); } // Re-exports @@ -158,15 +178,24 @@ function generateCustomMutationHookInternal( statements.push(createTypeReExport([varTypeName], '../../orm/mutation')); } if (hasSelect) { - statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); + statements.push( + createTypeReExport([selectTypeName!], '../../orm/input-types'), + ); } // Hook if (hasSelect) { - const mutationVarType: t.TSType = hasArgs ? typeRef(varTypeName) : t.tsVoidKeyword(); + const mutationVarType: t.TSType = hasArgs + ? typeRef(varTypeName) + : t.tsVoidKeyword(); const selectedResultType = (sel: t.TSType) => - customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName!, sel); + customSelectResultTypeLiteral( + operation.name, + operation.returnType, + payloadTypeName!, + sel, + ); // Overload 1: with selection.fields const o1ParamType = t.tsIntersectionType([ @@ -177,36 +206,43 @@ function generateCustomMutationHookInternal( t.tsParenthesizedType( t.tsIntersectionType([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(sRef())) + t.tsPropertySignature( + t.identifier('fields'), + t.tsTypeAnnotation(sRef()), + ), ]), - typeRef('StrictSelect', [sRef(), typeRef(selectTypeName!)]) - ]) - ) - ) - ) + typeRef('StrictSelect', [sRef(), typeRef(selectTypeName!)]), + ]), + ), + ), + ), ]), - useMutationOptionsType(selectedResultType(sRef()), mutationVarType) + useMutationOptionsType(selectedResultType(sRef()), mutationVarType), ]); statements.push( exportDeclareFunction( hookName, createSTypeParam(selectTypeName!), [createFunctionParam('params', o1ParamType)], - useMutationResultType(selectedResultType(sRef()), mutationVarType) - ) + useMutationResultType(selectedResultType(sRef()), mutationVarType), + ), ); // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))) + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))), ); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType( - typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), mutationVarType]), - ['mutationFn'] - ) + typeRef('UseMutationOptions', [ + t.tsAnyKeyword(), + typeRef('Error'), + mutationVarType, + ]), + ['mutationFn'], + ), ]); const body: t.Statement[] = []; @@ -216,24 +252,40 @@ function generateCustomMutationHookInternal( const mutationKeyExpr = useCentralizedKeys ? callExpr( - t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), - [] - ) + t.memberExpression( + t.identifier('customMutationKeys'), + t.identifier(operation.name), + ), + [], + ) : undefined; - const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); + const selectObj = t.objectExpression([ + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]); let mutationFnExpr: t.Expression; if (hasArgs) { - const variablesParam = createFunctionParam('variables', typeRef(varTypeName)); + const variablesParam = createFunctionParam( + 'variables', + typeRef(varTypeName), + ); mutationFnExpr = t.arrowFunctionExpression( [variablesParam], - getClientCustomCallUnwrap('mutation', operation.name, [t.identifier('variables')], selectObj) + getClientCustomCallUnwrap( + 'mutation', + operation.name, + [t.identifier('variables')], + selectObj, + ), ); } else { mutationFnExpr = t.arrowFunctionExpression( [], - getClientCustomCallUnwrap('mutation', operation.name, [], selectObj) + getClientCustomCallUnwrap('mutation', operation.name, [], selectObj), ); } @@ -241,48 +293,78 @@ function generateCustomMutationHookInternal( returnUseMutation( mutationFnExpr, [spreadObj(t.identifier('mutationOptions'))], - mutationKeyExpr - ) + mutationKeyExpr, + ), ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType, true)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType, true)], + body, + ), + ); } else { // Without select: simple hook (scalar return type) const resultTypeStr = typeRefToTsType(operation.returnType, tracker); const resultTypeLiteral = t.tsTypeLiteral([ t.tsPropertySignature( t.identifier(operation.name), - t.tsTypeAnnotation(typeRef(resultTypeStr)) - ) + t.tsTypeAnnotation(typeRef(resultTypeStr)), + ), ]); - const mutationVarType: t.TSType = hasArgs ? typeRef(varTypeName) : t.tsVoidKeyword(); + const mutationVarType: t.TSType = hasArgs + ? typeRef(varTypeName) + : t.tsVoidKeyword(); const optionsType = omitType( - typeRef('UseMutationOptions', [resultTypeLiteral, typeRef('Error'), mutationVarType]), - ['mutationFn'] + typeRef('UseMutationOptions', [ + resultTypeLiteral, + typeRef('Error'), + mutationVarType, + ]), + ['mutationFn'], ); const body: t.Statement[] = []; - body.push(constDecl('mutationOptions', t.logicalExpression('??', t.identifier('params'), t.objectExpression([])))); + body.push( + constDecl( + 'mutationOptions', + t.logicalExpression( + '??', + t.identifier('params'), + t.objectExpression([]), + ), + ), + ); const mutationKeyExpr = useCentralizedKeys ? callExpr( - t.memberExpression(t.identifier('customMutationKeys'), t.identifier(operation.name)), - [] - ) + t.memberExpression( + t.identifier('customMutationKeys'), + t.identifier(operation.name), + ), + [], + ) : undefined; let mutationFnExpr: t.Expression; if (hasArgs) { - const variablesParam = createFunctionParam('variables', typeRef(varTypeName)); + const variablesParam = createFunctionParam( + 'variables', + typeRef(varTypeName), + ); mutationFnExpr = t.arrowFunctionExpression( [variablesParam], - getClientCustomCallUnwrap('mutation', operation.name, [t.identifier('variables')]) + getClientCustomCallUnwrap('mutation', operation.name, [ + t.identifier('variables'), + ]), ); } else { mutationFnExpr = t.arrowFunctionExpression( [], - getClientCustomCallUnwrap('mutation', operation.name, []) + getClientCustomCallUnwrap('mutation', operation.name, []), ); } @@ -290,26 +372,35 @@ function generateCustomMutationHookInternal( returnUseMutation( mutationFnExpr, [spreadObj(t.identifier('mutationOptions'))], - mutationKeyExpr - ) + mutationKeyExpr, + ), ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', optionsType, true)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', optionsType, true)], + body, + ), + ); } - const content = generateHookFileCode(`Custom mutation hook for ${operation.name}`, statements); + const content = generateHookFileCode( + `Custom mutation hook for ${operation.name}`, + statements, + ); return { fileName, content, - operationName: operation.name + operationName: operation.name, }; } export interface GenerateAllCustomMutationHooksOptions { operations: CleanOperation[]; typeRegistry: TypeRegistry; - maxDepth?: number; skipQueryField?: boolean; reactQueryEnabled?: boolean; tableTypeNames?: Set; @@ -317,16 +408,15 @@ export interface GenerateAllCustomMutationHooksOptions { } export function generateAllCustomMutationHooks( - options: GenerateAllCustomMutationHooksOptions + options: GenerateAllCustomMutationHooksOptions, ): GeneratedCustomMutationFile[] { const { operations, typeRegistry, - maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true + useCentralizedKeys = true, } = options; return operations @@ -335,12 +425,11 @@ export function generateAllCustomMutationHooks( generateCustomMutationHook({ operation, typeRegistry, - maxDepth, skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys - }) + useCentralizedKeys, + }), ) .filter((result): result is GeneratedCustomMutationFile => result !== null); } diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index cd98d300f..19d17328b 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -15,10 +15,7 @@ */ import * as t from '@babel/types'; -import type { - CleanOperation, - TypeRegistry -} from '../../types/schema'; +import type { CleanOperation, TypeRegistry } from '../../types/schema'; import { asConst } from './babel-ast'; import { addJSDocComment, @@ -49,11 +46,9 @@ import { useQueryOptionsImplType, useQueryOptionsType, voidStatement, - wrapInferSelectResultType + wrapInferSelectResultType, } from './hooks-ast'; -import { - getSelectTypeName -} from './select-helpers'; +import { getSelectTypeName } from './select-helpers'; import { createTypeTracker, getOperationFileName, @@ -61,7 +56,7 @@ import { getQueryKeyName, getTypeBaseName, isTypeRequired, - typeRefToTsType + typeRefToTsType, } from './type-resolver'; import { ucFirst } from './utils'; @@ -74,7 +69,6 @@ export interface GeneratedCustomQueryFile { export interface GenerateCustomQueryHookOptions { operation: CleanOperation; typeRegistry: TypeRegistry; - maxDepth?: number; skipQueryField?: boolean; reactQueryEnabled?: boolean; tableTypeNames?: Set; @@ -82,14 +76,14 @@ export interface GenerateCustomQueryHookOptions { } export function generateCustomQueryHook( - options: GenerateCustomQueryHookOptions + options: GenerateCustomQueryHookOptions, ): GeneratedCustomQueryFile { const { operation, typeRegistry, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true + useCentralizedKeys = true, } = options; const hookName = getOperationHookName(operation.name, 'query'); @@ -100,7 +94,9 @@ export function generateCustomQueryHook( const tracker = createTypeTracker({ tableTypeNames }); const hasArgs = operation.args.length > 0; - const hasRequiredArgs = operation.args.some((arg) => isTypeRequired(arg.type)); + const hasRequiredArgs = operation.args.some((arg) => + isTypeRequired(arg.type), + ); const resultType = typeRefToTsType(operation.returnType, tracker); for (const arg of operation.args) { @@ -115,20 +111,36 @@ export function generateCustomQueryHook( // Imports if (reactQueryEnabled) { - statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', ['useQuery']), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], + true, + ), + ); } statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { - statements.push(createImportDeclaration('../query-keys', ['customQueryKeys'])); + statements.push( + createImportDeclaration('../query-keys', ['customQueryKeys']), + ); } if (hasArgs) { - statements.push(createImportDeclaration('../../orm/query', [varTypeName], true)); + statements.push( + createImportDeclaration('../../orm/query', [varTypeName], true), + ); } const inputTypeImports: string[] = []; @@ -146,11 +158,19 @@ export function generateCustomQueryHook( } } if (inputTypeImports.length > 0) { - statements.push(createImportDeclaration('../../orm/input-types', inputTypeImports, true)); + statements.push( + createImportDeclaration('../../orm/input-types', inputTypeImports, true), + ); } if (hasSelect) { - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); } // Re-exports @@ -158,7 +178,9 @@ export function generateCustomQueryHook( statements.push(createTypeReExport([varTypeName], '../../orm/query')); } if (hasSelect) { - statements.push(createTypeReExport([selectTypeName!], '../../orm/input-types')); + statements.push( + createTypeReExport([selectTypeName!], '../../orm/input-types'), + ); } // Query key @@ -167,29 +189,43 @@ export function generateCustomQueryHook( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(queryKeyName), - t.memberExpression(t.identifier('customQueryKeys'), t.identifier(operation.name)) - ) - ]) + t.memberExpression( + t.identifier('customQueryKeys'), + t.identifier(operation.name), + ), + ), + ]), ); - addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + addJSDocComment(keyDecl, [ + 'Query key factory - re-exported from query-keys.ts', + ]); statements.push(keyDecl); } else if (hasArgs) { const keyFn = t.arrowFunctionExpression( [createFunctionParam('variables', typeRef(varTypeName), true)], - asConst(t.arrayExpression([t.stringLiteral(operation.name), t.identifier('variables')])) + asConst( + t.arrayExpression([ + t.stringLiteral(operation.name), + t.identifier('variables'), + ]), + ), ); const keyDecl = t.exportNamedDeclaration( - t.variableDeclaration('const', [t.variableDeclarator(t.identifier(queryKeyName), keyFn)]) + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(queryKeyName), keyFn), + ]), ); addJSDocComment(keyDecl, ['Query key factory for caching']); statements.push(keyDecl); } else { const keyFn = t.arrowFunctionExpression( [], - asConst(t.arrayExpression([t.stringLiteral(operation.name)])) + asConst(t.arrayExpression([t.stringLiteral(operation.name)])), ); const keyDecl = t.exportNamedDeclaration( - t.variableDeclaration('const', [t.variableDeclarator(t.identifier(queryKeyName), keyFn)]) + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(queryKeyName), keyFn), + ]), ); addJSDocComment(keyDecl, ['Query key factory for caching']); statements.push(keyDecl); @@ -208,21 +244,32 @@ export function generateCustomQueryHook( }; // Helper to build the fields+StrictSelect intersection type - const fieldsSelectionType = (s: t.TSType) => t.tsParenthesizedType( - t.tsIntersectionType([ - t.tsTypeLiteral([t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s))]), - typeRef('StrictSelect', [s, typeRef(selectTypeName!)]) - ]) - ); + const fieldsSelectionType = (s: t.TSType) => + t.tsParenthesizedType( + t.tsIntersectionType([ + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s)), + ]), + typeRef('StrictSelect', [s, typeRef(selectTypeName!)]), + ]), + ); - const selectedResultType= (sel: t.TSType) => - customSelectResultTypeLiteral(operation.name, operation.returnType, payloadTypeName!, sel); + const selectedResultType = (sel: t.TSType) => + customSelectResultTypeLiteral( + operation.name, + operation.returnType, + payloadTypeName!, + sel, + ); // Hook if (reactQueryEnabled) { - const description = operation.description || `Query hook for ${operation.name}`; + const description = + operation.description || `Query hook for ${operation.name}`; const argNames = operation.args.map((a) => a.name).join(', '); - const exampleCall = hasArgs ? `${hookName}({ ${argNames} })` : `${hookName}()`; + const exampleCall = hasArgs + ? `${hookName}({ ${argNames} })` + : `${hookName}()`; if (hasSelect) { // Overload 1: with selection.fields @@ -231,18 +278,35 @@ export function generateCustomQueryHook( const varType = hasRequiredArgs ? typeRef(varTypeName) : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); - o1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); + o1Props.push( + t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(varType), + ), + ); } - o1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + o1Props.push( + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(fieldsSelectionType(sRef())), + ), + ); const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral(o1Props), - omitType(typeRef('UseQueryOptions', [selectedResultType(sRef()), typeRef('Error'), typeRef('TData')]), ['queryKey', 'queryFn']) + omitType( + typeRef('UseQueryOptions', [ + selectedResultType(sRef()), + typeRef('Error'), + typeRef('TData'), + ]), + ['queryKey', 'queryFn'], + ), ]); const o1 = exportDeclareFunction( hookName, createSAndTDataTypeParams(selectTypeName!, selectedResultType(sRef())), [createFunctionParam('params', o1ParamType)], - typeRef('UseQueryResult', [typeRef('TData')]) + typeRef('UseQueryResult', [typeRef('TData')]), ); addJSDocComment(o1, [ description, @@ -254,40 +318,79 @@ export function generateCustomQueryHook( `if (data?.${operation.name}) {`, ` console.log(data.${operation.name});`, '}', - '```' + '```', ]); statements.push(o1); // Implementation const implProps: t.TSPropertySignature[] = []; if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; implProps.push(varProp); } - const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))), + ); implProps.push(implSelProp); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral(implProps), - useQueryOptionsImplType() + useQueryOptionsImplType(), ]); - const implParam = createFunctionParam('params', implParamType, !hasRequiredArgs); + const implParam = createFunctionParam( + 'params', + implParamType, + !hasRequiredArgs, + ); const body: t.Statement[] = []; if (hasArgs) { - body.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + body.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); } body.push(buildSelectionArgsCall(selectTypeName!)); if (hasArgs) { const destructPattern = t.objectPattern([ - t.objectProperty(t.identifier('variables'), t.identifier('_variables'), false, false), - t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false), - t.restElement(t.identifier('queryOptions')) + t.objectProperty( + t.identifier('variables'), + t.identifier('_variables'), + false, + false, + ), + t.objectProperty( + t.identifier('selection'), + t.identifier('_selection'), + false, + false, + ), + t.restElement(t.identifier('queryOptions')), ]); - body.push(t.variableDeclaration('const', [ - t.variableDeclarator(destructPattern, t.logicalExpression('??', t.identifier('params'), t.objectExpression([]))) - ])); + body.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + destructPattern, + t.logicalExpression( + '??', + t.identifier('params'), + t.objectExpression([]), + ), + ), + ]), + ); body.push(voidStatement('_variables')); body.push(voidStatement('_selection')); } else { @@ -295,57 +398,100 @@ export function generateCustomQueryHook( body.push(voidStatement('_selection')); } - const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); + const selectObj = t.objectExpression([ + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]); const queryFnArgs = hasArgs - ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + ? [ + hasRequiredArgs + ? t.tsNonNullExpression(t.identifier('variables')) + : t.identifier('variables'), + selectObj, + ] : [selectObj]; const queryFnExpr = t.arrowFunctionExpression( [], - getClientCustomCallUnwrap('query', operation.name, queryFnArgs as t.Expression[]) + getClientCustomCallUnwrap( + 'query', + operation.name, + queryFnArgs as t.Expression[], + ), ); const extraProps: (t.ObjectProperty | t.SpreadElement)[] = []; const enabledExpr = hasRequiredArgs ? t.logicalExpression( - '&&', - t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), - t.binaryExpression( - '!==', - t.optionalMemberExpression(t.identifier('params'), t.identifier('enabled'), false, true), - t.booleanLiteral(false) + '&&', + t.unaryExpression( + '!', + t.unaryExpression('!', t.identifier('variables')), + ), + t.binaryExpression( + '!==', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('enabled'), + false, + true, + ), + t.booleanLiteral(false), + ), ) - ) : undefined; extraProps.push(spreadObj(t.identifier('queryOptions'))); - body.push(returnUseQuery(buildQueryKeyCall(hasArgs), queryFnExpr, extraProps, enabledExpr)); + body.push( + returnUseQuery( + buildQueryKeyCall(hasArgs), + queryFnExpr, + extraProps, + enabledExpr, + ), + ); statements.push(exportFunction(hookName, null, [implParam], body)); } else { // Without select: simple hook (scalar return type) const resultTypeLiteral = t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(operation.name), t.tsTypeAnnotation(typeRef(resultType))) + t.tsPropertySignature( + t.identifier(operation.name), + t.tsTypeAnnotation(typeRef(resultType)), + ), ]); const optType = omitType( - typeRef('UseQueryOptions', [resultTypeLiteral, typeRef('Error'), typeRef('TData')]), - ['queryKey', 'queryFn'] + typeRef('UseQueryOptions', [ + resultTypeLiteral, + typeRef('Error'), + typeRef('TData'), + ]), + ['queryKey', 'queryFn'], ); let paramType: t.TSType; if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; paramType = t.tsIntersectionType([t.tsTypeLiteral([varProp]), optType]); } else { paramType = optType; } - const implParam = createFunctionParam('params', paramType, !hasRequiredArgs); + const implParam = createFunctionParam( + 'params', + paramType, + !hasRequiredArgs, + ); const hookDecl = exportDeclareFunction( hookName, createTDataTypeParam(resultTypeLiteral), [implParam], - typeRef('UseQueryResult', [typeRef('TData')]) + typeRef('UseQueryResult', [typeRef('TData')]), ); addJSDocComment(hookDecl, [ description, @@ -357,66 +503,148 @@ export function generateCustomQueryHook( `if (data?.${operation.name}) {`, ` console.log(data.${operation.name});`, '}', - '```' + '```', ]); const body: t.Statement[] = []; if (hasArgs) { - body.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + body.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); const destructPattern = t.objectPattern([ - t.objectProperty(t.identifier('variables'), t.identifier('_variables'), false, false), - t.restElement(t.identifier('queryOptions')) + t.objectProperty( + t.identifier('variables'), + t.identifier('_variables'), + false, + false, + ), + t.restElement(t.identifier('queryOptions')), ]); - body.push(t.variableDeclaration('const', [ - t.variableDeclarator(destructPattern, t.logicalExpression('??', t.identifier('params'), t.objectExpression([]))) - ])); + body.push( + t.variableDeclaration('const', [ + t.variableDeclarator( + destructPattern, + t.logicalExpression( + '??', + t.identifier('params'), + t.objectExpression([]), + ), + ), + ]), + ); body.push(voidStatement('_variables')); } else { - body.push(constDecl('queryOptions', t.logicalExpression('??', t.identifier('params'), t.objectExpression([])))); + body.push( + constDecl( + 'queryOptions', + t.logicalExpression( + '??', + t.identifier('params'), + t.objectExpression([]), + ), + ), + ); } const queryFnArgs = hasArgs - ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables')] + ? [ + hasRequiredArgs + ? t.tsNonNullExpression(t.identifier('variables')) + : t.identifier('variables'), + ] : []; const queryFnExpr = t.arrowFunctionExpression( [], - getClientCustomCallUnwrap('query', operation.name, queryFnArgs as t.Expression[]) + getClientCustomCallUnwrap( + 'query', + operation.name, + queryFnArgs as t.Expression[], + ), ); const enabledExpr = hasRequiredArgs ? t.logicalExpression( - '&&', - t.unaryExpression('!', t.unaryExpression('!', t.identifier('variables'))), - t.binaryExpression('!==', t.optionalMemberExpression(t.identifier('params'), t.identifier('enabled'), false, true), t.booleanLiteral(false)) - ) + '&&', + t.unaryExpression( + '!', + t.unaryExpression('!', t.identifier('variables')), + ), + t.binaryExpression( + '!==', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('enabled'), + false, + true, + ), + t.booleanLiteral(false), + ), + ) : undefined; - body.push(returnUseQuery(buildQueryKeyCall(hasArgs), queryFnExpr, [spreadObj(t.identifier('queryOptions'))], enabledExpr)); + body.push( + returnUseQuery( + buildQueryKeyCall(hasArgs), + queryFnExpr, + [spreadObj(t.identifier('queryOptions'))], + enabledExpr, + ), + ); // We need the implementation version (not declare), with return type statements.push(hookDecl); - statements.push(exportFunction(hookName, null, [implParam], body, typeRef('UseQueryResult', [typeRef('TData')]))); + statements.push( + exportFunction( + hookName, + null, + [implParam], + body, + typeRef('UseQueryResult', [typeRef('TData')]), + ), + ); } } // Fetch function (non-hook) const fetchFnName = `fetch${ucFirst(operation.name)}Query`; const fetchArgNames = operation.args.map((a) => a.name).join(', '); - const fetchExampleCall = hasArgs ? `${fetchFnName}({ ${fetchArgNames} })` : `${fetchFnName}()`; + const fetchExampleCall = hasArgs + ? `${fetchFnName}({ ${fetchArgNames} })` + : `${fetchFnName}()`; if (hasSelect) { // Overload 1: with fields const f1Props: t.TSPropertySignature[] = []; if (hasArgs) { - const varType = hasRequiredArgs ? typeRef(varTypeName) : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); - f1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); + const varType = hasRequiredArgs + ? typeRef(varTypeName) + : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); + f1Props.push( + t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(varType), + ), + ); } - f1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + f1Props.push( + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(fieldsSelectionType(sRef())), + ), + ); const f1Decl = exportAsyncDeclareFunction( fetchFnName, createSTypeParam(selectTypeName!), [createFunctionParam('params', t.tsTypeLiteral(f1Props))], - typeRef('Promise', [selectedResultType(sRef())]) + typeRef('Promise', [selectedResultType(sRef())]), ); addJSDocComment(f1Decl, [ `Fetch ${operation.name} without React hooks`, @@ -424,53 +652,132 @@ export function generateCustomQueryHook( '@example', '```ts', `const data = await ${fetchExampleCall};`, - '```' + '```', ]); statements.push(f1Decl); // Implementation const fImplProps: t.TSPropertySignature[] = []; if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; fImplProps.push(varProp); } - const fImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + const fImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))), + ); fImplProps.push(fImplSelProp); const fBody: t.Statement[] = []; if (hasArgs) { - fBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + fBody.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); } fBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); + const selectObj = t.objectExpression([ + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]); const fCallArgs = hasArgs - ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + ? [ + hasRequiredArgs + ? t.tsNonNullExpression(t.identifier('variables')) + : t.identifier('variables'), + selectObj, + ] : [selectObj]; - fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, fCallArgs as t.Expression[]))); - statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], fBody)); + fBody.push( + t.returnStatement( + getClientCustomCallUnwrap( + 'query', + operation.name, + fCallArgs as t.Expression[], + ), + ), + ); + statements.push( + exportAsyncFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], + fBody, + ), + ); } else { const fBody: t.Statement[] = []; if (hasArgs) { const fProps: t.TSPropertySignature[] = []; - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; fProps.push(varProp); - fBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); - const fCallArgs = hasRequiredArgs ? [t.tsNonNullExpression(t.identifier('variables'))] : [t.identifier('variables')]; - fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, fCallArgs as t.Expression[]))); - const fDecl = exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fProps), !hasRequiredArgs)], fBody); + fBody.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); + const fCallArgs = hasRequiredArgs + ? [t.tsNonNullExpression(t.identifier('variables'))] + : [t.identifier('variables')]; + fBody.push( + t.returnStatement( + getClientCustomCallUnwrap( + 'query', + operation.name, + fCallArgs as t.Expression[], + ), + ), + ); + const fDecl = exportAsyncFunction( + fetchFnName, + null, + [ + createFunctionParam( + 'params', + t.tsTypeLiteral(fProps), + !hasRequiredArgs, + ), + ], + fBody, + ); addJSDocComment(fDecl, [ `Fetch ${operation.name} without React hooks`, '', '@example', '```ts', `const data = await ${fetchExampleCall};`, - '```' + '```', ]); statements.push(fDecl); } else { - fBody.push(t.returnStatement(getClientCustomCallUnwrap('query', operation.name, []))); + fBody.push( + t.returnStatement( + getClientCustomCallUnwrap('query', operation.name, []), + ), + ); const fDecl = exportAsyncFunction(fetchFnName, null, [], fBody); addJSDocComment(fDecl, [ `Fetch ${operation.name} without React hooks`, @@ -478,7 +785,7 @@ export function generateCustomQueryHook( '@example', '```ts', `const data = await ${fetchExampleCall};`, - '```' + '```', ]); statements.push(fDecl); } @@ -496,15 +803,30 @@ export function generateCustomQueryHook( // Overload 1: with fields const p1Props: t.TSPropertySignature[] = []; if (hasArgs) { - const varType = hasRequiredArgs ? typeRef(varTypeName) : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); - p1Props.push(t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(varType))); + const varType = hasRequiredArgs + ? typeRef(varTypeName) + : t.tsUnionType([typeRef(varTypeName), t.tsUndefinedKeyword()]); + p1Props.push( + t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(varType), + ), + ); } - p1Props.push(t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(fieldsSelectionType(sRef())))); + p1Props.push( + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(fieldsSelectionType(sRef())), + ), + ); const p1Decl = exportAsyncDeclareFunction( prefetchFnName, createSTypeParam(selectTypeName!), - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(p1Props))], - typeRef('Promise', [t.tsVoidKeyword()]) + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', t.tsTypeLiteral(p1Props)), + ], + typeRef('Promise', [t.tsVoidKeyword()]), ); addJSDocComment(p1Decl, [ `Prefetch ${operation.name} for SSR or cache warming`, @@ -512,86 +834,185 @@ export function generateCustomQueryHook( '@example', '```ts', `await ${prefetchExampleCall};`, - '```' + '```', ]); statements.push(p1Decl); // Implementation const pImplProps: t.TSPropertySignature[] = []; if (hasArgs) { - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; pImplProps.push(varProp); } - const pImplSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!)))); + const pImplSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName!))), + ); pImplProps.push(pImplSelProp); const pBody: t.Statement[] = []; if (hasArgs) { - pBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); + pBody.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); } pBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select')))]); + const selectObj = t.objectExpression([ + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]); const pCallArgs = hasArgs - ? [hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), selectObj] + ? [ + hasRequiredArgs + ? t.tsNonNullExpression(t.identifier('variables')) + : t.identifier('variables'), + selectObj, + ] : [selectObj]; const prefetchQueryCall = callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression([ - objectProp('queryKey', buildQueryKeyCall(hasArgs)), - objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, pCallArgs as t.Expression[]))) - ])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('prefetchQuery'), + ), + [ + t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(hasArgs)), + objectProp( + 'queryFn', + t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap( + 'query', + operation.name, + pCallArgs as t.Expression[], + ), + ), + ), + ]), + ], ); pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); statements.push( exportAsyncFunction( prefetchFnName, null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', t.tsTypeLiteral(pImplProps))], + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', t.tsTypeLiteral(pImplProps)), + ], pBody, - t.tsVoidKeyword() - ) + t.tsVoidKeyword(), + ), ); } else { // Without select const pBody: t.Statement[] = []; - const pParams: t.Identifier[] = [createFunctionParam('queryClient', typeRef('QueryClient'))]; + const pParams: t.Identifier[] = [ + createFunctionParam('queryClient', typeRef('QueryClient')), + ]; if (hasArgs) { const pProps: t.TSPropertySignature[] = []; - const varProp = t.tsPropertySignature(t.identifier('variables'), t.tsTypeAnnotation(typeRef(varTypeName))); + const varProp = t.tsPropertySignature( + t.identifier('variables'), + t.tsTypeAnnotation(typeRef(varTypeName)), + ); if (!hasRequiredArgs) varProp.optional = true; pProps.push(varProp); - pParams.push(createFunctionParam('params', t.tsTypeLiteral(pProps), !hasRequiredArgs)); - pBody.push(constDecl('variables', t.optionalMemberExpression(t.identifier('params'), t.identifier('variables'), false, true))); - const pCallArgs = hasRequiredArgs ? [t.tsNonNullExpression(t.identifier('variables'))] : [t.identifier('variables')]; + pParams.push( + createFunctionParam( + 'params', + t.tsTypeLiteral(pProps), + !hasRequiredArgs, + ), + ); + pBody.push( + constDecl( + 'variables', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + ), + ); + const pCallArgs = hasRequiredArgs + ? [t.tsNonNullExpression(t.identifier('variables'))] + : [t.identifier('variables')]; const prefetchQueryCall = callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression([ - objectProp('queryKey', buildQueryKeyCall(true)), - objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, pCallArgs as t.Expression[]))) - ])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('prefetchQuery'), + ), + [ + t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(true)), + objectProp( + 'queryFn', + t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap( + 'query', + operation.name, + pCallArgs as t.Expression[], + ), + ), + ), + ]), + ], ); pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); } else { const prefetchQueryCall = callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression([ - objectProp('queryKey', buildQueryKeyCall(false)), - objectProp('queryFn', t.arrowFunctionExpression([], getClientCustomCallUnwrap('query', operation.name, []))) - ])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('prefetchQuery'), + ), + [ + t.objectExpression([ + objectProp('queryKey', buildQueryKeyCall(false)), + objectProp( + 'queryFn', + t.arrowFunctionExpression( + [], + getClientCustomCallUnwrap('query', operation.name, []), + ), + ), + ]), + ], ); pBody.push(t.expressionStatement(t.awaitExpression(prefetchQueryCall))); } - const pDecl = exportAsyncFunction(prefetchFnName, null, pParams, pBody, t.tsVoidKeyword()); + const pDecl = exportAsyncFunction( + prefetchFnName, + null, + pParams, + pBody, + t.tsVoidKeyword(), + ); addJSDocComment(pDecl, [ `Prefetch ${operation.name} for SSR or cache warming`, '', '@example', '```ts', `await ${prefetchExampleCall};`, - '```' + '```', ]); statements.push(pDecl); } @@ -605,14 +1026,13 @@ export function generateCustomQueryHook( return { fileName, content, - operationName: operation.name + operationName: operation.name, }; } export interface GenerateAllCustomQueryHooksOptions { operations: CleanOperation[]; typeRegistry: TypeRegistry; - maxDepth?: number; skipQueryField?: boolean; reactQueryEnabled?: boolean; tableTypeNames?: Set; @@ -620,16 +1040,15 @@ export interface GenerateAllCustomQueryHooksOptions { } export function generateAllCustomQueryHooks( - options: GenerateAllCustomQueryHooksOptions + options: GenerateAllCustomQueryHooksOptions, ): GeneratedCustomQueryFile[] { const { operations, typeRegistry, - maxDepth = 2, skipQueryField = true, reactQueryEnabled = true, tableTypeNames, - useCentralizedKeys = true + useCentralizedKeys = true, } = options; return operations @@ -638,11 +1057,10 @@ export function generateAllCustomQueryHooks( generateCustomQueryHook({ operation, typeRegistry, - maxDepth, skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys - }) + useCentralizedKeys, + }), ); } diff --git a/graphql/codegen/src/core/codegen/hooks-ast.ts b/graphql/codegen/src/core/codegen/hooks-ast.ts index 0fc7b69ff..a6620e951 100644 --- a/graphql/codegen/src/core/codegen/hooks-ast.ts +++ b/graphql/codegen/src/core/codegen/hooks-ast.ts @@ -18,24 +18,31 @@ import { getGeneratedFileHeader } from './utils'; export function createImportDeclaration( moduleSpecifier: string, namedImports: string[], - typeOnly: boolean = false + typeOnly: boolean = false, ): t.ImportDeclaration { const specifiers = namedImports.map((name) => - t.importSpecifier(t.identifier(name), t.identifier(name)) + t.importSpecifier(t.identifier(name), t.identifier(name)), + ); + const decl = t.importDeclaration( + specifiers, + t.stringLiteral(moduleSpecifier), ); - const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier)); decl.importKind = typeOnly ? 'type' : 'value'; return decl; } export function createTypeReExport( names: string[], - moduleSpecifier: string + moduleSpecifier: string, ): t.ExportNamedDeclaration { const specifiers = names.map((name) => - t.exportSpecifier(t.identifier(name), t.identifier(name)) + t.exportSpecifier(t.identifier(name), t.identifier(name)), + ); + const decl = t.exportNamedDeclaration( + null, + specifiers, + t.stringLiteral(moduleSpecifier), ); - const decl = t.exportNamedDeclaration(null, specifiers, t.stringLiteral(moduleSpecifier)); decl.exportKind = 'type'; return decl; } @@ -47,7 +54,7 @@ export function createTypeReExport( export function typeRef(name: string, params?: t.TSType[]): t.TSTypeReference { return t.tsTypeReference( t.identifier(name), - params ? t.tsTypeParameterInstantiation(params) : undefined + params ? t.tsTypeParameterInstantiation(params) : undefined, ); } @@ -69,38 +76,40 @@ export function stringLiteralType(value: string): t.TSLiteralType { export function inferSelectResultType( entityTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeReference { return typeRef('InferSelectResult', [typeRef(entityTypeName), selectType]); } export function connectionResultType( entityTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeReference { - return typeRef('ConnectionResult', [inferSelectResultType(entityTypeName, selectType)]); + return typeRef('ConnectionResult', [ + inferSelectResultType(entityTypeName, selectType), + ]); } export function typeLiteralWithProps( - props: Array<{ name: string; type: t.TSType; optional?: boolean }> + props: Array<{ name: string; type: t.TSType; optional?: boolean }>, ): t.TSTypeLiteral { return t.tsTypeLiteral( props.map((p) => { const prop = t.tsPropertySignature( t.identifier(p.name), - t.tsTypeAnnotation(p.type) + t.tsTypeAnnotation(p.type), ); if (p.optional) { prop.optional = true; } return prop; - }) + }), ); } export function queryResultType( queryName: string, - innerType: t.TSType + innerType: t.TSType, ): t.TSTypeLiteral { return typeLiteralWithProps([{ name: queryName, type: innerType }]); } @@ -108,16 +117,19 @@ export function queryResultType( export function listQueryResultType( queryName: string, entityTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeLiteral { - return queryResultType(queryName, connectionResultType(entityTypeName, selectType)); + return queryResultType( + queryName, + connectionResultType(entityTypeName, selectType), + ); } export function singleQueryResultType( queryName: string, entityTypeName: string, selectType: t.TSType, - nullable: boolean = true + nullable: boolean = true, ): t.TSTypeLiteral { const resultType = inferSelectResultType(entityTypeName, selectType); const innerType = nullable @@ -130,33 +142,39 @@ export function mutationResultType( mutationName: string, entityField: string, entityTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeLiteral { return queryResultType( mutationName, - typeLiteralWithProps([{ - name: entityField, - type: inferSelectResultType(entityTypeName, selectType) - }]) + typeLiteralWithProps([ + { + name: entityField, + type: inferSelectResultType(entityTypeName, selectType), + }, + ]), ); } -export function omitType(baseType: t.TSType, keys: string[]): t.TSTypeReference { - const keyType = keys.length === 1 - ? stringLiteralType(keys[0]) - : t.tsUnionType(keys.map(stringLiteralType)); +export function omitType( + baseType: t.TSType, + keys: string[], +): t.TSTypeReference { + const keyType = + keys.length === 1 + ? stringLiteralType(keys[0]) + : t.tsUnionType(keys.map(stringLiteralType)); return typeRef('Omit', [baseType, keyType]); } export function listSelectionConfigType( selectType: t.TSType, filterTypeName: string, - orderByTypeName: string + orderByTypeName: string, ): t.TSTypeReference { return typeRef('ListSelectionConfig', [ selectType, typeRef(filterTypeName), - typeRef(orderByTypeName) + typeRef(orderByTypeName), ]); } @@ -166,18 +184,18 @@ export function selectionConfigType(selectType: t.TSType): t.TSTypeReference { export function strictSelectType( selectType: t.TSType, - shapeTypeName: string + shapeTypeName: string, ): t.TSTypeReference { return typeRef('StrictSelect', [selectType, typeRef(shapeTypeName)]); } export function withFieldsSelectionType( selectType: t.TSType, - selectTypeName: string + selectTypeName: string, ): t.TSIntersectionType { return t.tsIntersectionType([ typeLiteralWithProps([{ name: 'fields', type: selectType }]), - strictSelectType(selectType, selectTypeName) + strictSelectType(selectType, selectTypeName), ]); } @@ -185,26 +203,26 @@ export function withFieldsListSelectionType( selectType: t.TSType, selectTypeName: string, filterTypeName: string, - orderByTypeName: string + orderByTypeName: string, ): t.TSIntersectionType { return t.tsIntersectionType([ typeLiteralWithProps([{ name: 'fields', type: selectType }]), omitType( listSelectionConfigType(selectType, filterTypeName, orderByTypeName), - ['fields'] + ['fields'], ), - strictSelectType(selectType, selectTypeName) + strictSelectType(selectType, selectTypeName), ]); } export function useQueryOptionsType( queryDataType: t.TSType, dataType: t.TSType, - extraProps?: t.TSType + extraProps?: t.TSType, ): t.TSType { const base = omitType( typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), - ['queryKey', 'queryFn'] + ['queryKey', 'queryFn'], ); if (extraProps) { return t.tsIntersectionType([base, extraProps]); @@ -218,9 +236,9 @@ export function useQueryOptionsImplType(extraProps?: t.TSType): t.TSType { t.tsAnyKeyword(), typeRef('Error'), t.tsAnyKeyword(), - t.tsAnyKeyword() + t.tsAnyKeyword(), ]), - ['queryKey', 'queryFn'] + ['queryKey', 'queryFn'], ); if (extraProps) { return t.tsIntersectionType([base, extraProps]); @@ -230,17 +248,17 @@ export function useQueryOptionsImplType(extraProps?: t.TSType): t.TSType { export function useMutationOptionsType( resultType: t.TSType, - varType: t.TSType + varType: t.TSType, ): t.TSTypeReference { return omitType( typeRef('UseMutationOptions', [resultType, typeRef('Error'), varType]), - ['mutationFn'] + ['mutationFn'], ); } export function useMutationResultType( resultType: t.TSType, - varType: t.TSType + varType: t.TSType, ): t.TSTypeReference { return typeRef('UseMutationResult', [resultType, typeRef('Error'), varType]); } @@ -249,21 +267,25 @@ export function useMutationResultType( // Type parameter helpers // ============================================================================ -export function createSTypeParam(constraintName: string): t.TSTypeParameterDeclaration { +export function createSTypeParam( + constraintName: string, +): t.TSTypeParameterDeclaration { const param = t.tsTypeParameter(typeRef(constraintName), null, 'S'); return t.tsTypeParameterDeclaration([param]); } export function createSAndTDataTypeParams( constraintName: string, - defaultDataType: t.TSType + defaultDataType: t.TSType, ): t.TSTypeParameterDeclaration { const sParam = t.tsTypeParameter(typeRef(constraintName), null, 'S'); const tDataParam = t.tsTypeParameter(null, defaultDataType, 'TData'); return t.tsTypeParameterDeclaration([sParam, tDataParam]); } -export function createTDataTypeParam(defaultType: t.TSType): t.TSTypeParameterDeclaration { +export function createTDataTypeParam( + defaultType: t.TSType, +): t.TSTypeParameterDeclaration { const param = t.tsTypeParameter(null, defaultType, 'TData'); return t.tsTypeParameterDeclaration([param]); } @@ -275,7 +297,7 @@ export function createTDataTypeParam(defaultType: t.TSType): t.TSTypeParameterDe export function createFunctionParam( name: string, typeAnnotation: t.TSType, - optional: boolean = false + optional: boolean = false, ): t.Identifier { const param = t.identifier(name); param.typeAnnotation = t.tsTypeAnnotation(typeAnnotation); @@ -287,13 +309,13 @@ export function exportDeclareFunction( name: string, typeParameters: t.TSTypeParameterDeclaration | null, params: t.Identifier[], - returnType: t.TSType + returnType: t.TSType, ): t.ExportNamedDeclaration { const func = t.tsDeclareFunction( t.identifier(name), typeParameters, params, - t.tsTypeAnnotation(returnType) + t.tsTypeAnnotation(returnType), ); return t.exportNamedDeclaration(func); } @@ -303,12 +325,12 @@ export function exportFunction( typeParameters: t.TSTypeParameterDeclaration | null, params: t.Identifier[], body: t.Statement[], - returnType?: t.TSType + returnType?: t.TSType, ): t.ExportNamedDeclaration { const func = t.functionDeclaration( t.identifier(name), params, - t.blockStatement(body) + t.blockStatement(body), ); func.typeParameters = typeParameters; if (returnType) { @@ -322,12 +344,12 @@ export function exportAsyncFunction( typeParameters: t.TSTypeParameterDeclaration | null, params: t.Identifier[], body: t.Statement[], - returnType?: t.TSType + returnType?: t.TSType, ): t.ExportNamedDeclaration { const func = t.functionDeclaration( t.identifier(name), params, - t.blockStatement(body) + t.blockStatement(body), ); func.async = true; func.typeParameters = typeParameters; @@ -341,13 +363,13 @@ export function exportAsyncDeclareFunction( name: string, typeParameters: t.TSTypeParameterDeclaration | null, params: t.Identifier[], - returnType: t.TSType + returnType: t.TSType, ): t.ExportNamedDeclaration { const func = t.tsDeclareFunction( t.identifier(name), typeParameters, params, - t.tsTypeAnnotation(returnType) + t.tsTypeAnnotation(returnType), ); func.async = true; return t.exportNamedDeclaration(func); @@ -359,7 +381,7 @@ export function exportAsyncDeclareFunction( export function callExpr( callee: string | t.Expression, - args: (t.Expression | t.SpreadElement)[] + args: (t.Expression | t.SpreadElement)[], ): t.CallExpression { const calleeExpr = typeof callee === 'string' ? t.identifier(callee) : callee; return t.callExpression(calleeExpr, args); @@ -369,13 +391,21 @@ export function memberExpr(obj: string, prop: string): t.MemberExpression { return t.memberExpression(t.identifier(obj), t.identifier(prop)); } -export function optionalMemberExpr(obj: string, prop: string): t.OptionalMemberExpression { - return t.optionalMemberExpression(t.identifier(obj), t.identifier(prop), false, true); +export function optionalMemberExpr( + obj: string, + prop: string, +): t.OptionalMemberExpression { + return t.optionalMemberExpression( + t.identifier(obj), + t.identifier(prop), + false, + true, + ); } export function arrowFn( params: t.Identifier[], - body: t.Expression | t.BlockStatement + body: t.Expression | t.BlockStatement, ): t.ArrowFunctionExpression { return t.arrowFunctionExpression(params, body); } @@ -391,7 +421,7 @@ export function spreadObj(expr: t.Expression): t.SpreadElement { export function objectProp( key: string, value: t.Expression, - shorthand: boolean = false + shorthand: boolean = false, ): t.ObjectProperty { return t.objectProperty(t.identifier(key), value, false, shorthand); } @@ -400,9 +430,12 @@ export function shorthandProp(name: string): t.ObjectProperty { return t.objectProperty(t.identifier(name), t.identifier(name), false, true); } -export function constDecl(name: string, init: t.Expression): t.VariableDeclaration { +export function constDecl( + name: string, + init: t.Expression, +): t.VariableDeclaration { return t.variableDeclaration('const', [ - t.variableDeclarator(t.identifier(name), init) + t.variableDeclarator(t.identifier(name), init), ]); } @@ -410,7 +443,10 @@ export function asConstExpr(expr: t.Expression): t.TSAsExpression { return t.tsAsExpression(expr, t.tsTypeReference(t.identifier('const'))); } -export function asTypeExpr(expr: t.Expression, typeName: string): t.TSAsExpression { +export function asTypeExpr( + expr: t.Expression, + typeName: string, +): t.TSAsExpression { return t.tsAsExpression(expr, typeRef(typeName)); } @@ -426,11 +462,11 @@ export function returnUseQuery( queryKeyExpr: t.Expression, queryFnExpr: t.Expression, extraProps?: Array, - enabledExpr?: t.Expression + enabledExpr?: t.Expression, ): t.ReturnStatement { const props: Array = [ objectProp('queryKey', queryKeyExpr), - objectProp('queryFn', queryFnExpr) + objectProp('queryFn', queryFnExpr), ]; if (enabledExpr) { props.push(objectProp('enabled', enabledExpr)); @@ -438,15 +474,13 @@ export function returnUseQuery( if (extraProps) { props.push(...extraProps); } - return t.returnStatement( - callExpr('useQuery', [t.objectExpression(props)]) - ); + return t.returnStatement(callExpr('useQuery', [t.objectExpression(props)])); } export function returnUseMutation( mutationFnExpr: t.Expression, extraProps: Array, - mutationKeyExpr?: t.Expression + mutationKeyExpr?: t.Expression, ): t.ReturnStatement { const props: Array = []; if (mutationKeyExpr) { @@ -455,7 +489,7 @@ export function returnUseMutation( props.push(objectProp('mutationFn', mutationFnExpr)); props.push(...extraProps); return t.returnStatement( - callExpr('useMutation', [t.objectExpression(props)]) + callExpr('useMutation', [t.objectExpression(props)]), ); } @@ -466,30 +500,35 @@ export function returnUseMutation( export function destructureWithRest( source: t.Expression, keys: string[], - restName: string + restName: string, ): t.VariableDeclaration { const properties = keys.map((key) => - t.objectProperty(t.identifier(key), t.identifier(`_${key}`), false, false) + t.objectProperty(t.identifier(key), t.identifier(`_${key}`), false, false), ); const pattern = t.objectPattern([ ...properties, - t.restElement(t.identifier(restName)) + t.restElement(t.identifier(restName)), ]); return constDecl(restName, t.identifier('__placeholder__')); } export function destructureParamsWithSelection( restName: string, - extraKeys: string[] = [] + extraKeys: string[] = [], ): t.VariableDeclaration { const properties: (t.ObjectProperty | t.RestElement)[] = []; for (const key of extraKeys) { properties.push( - t.objectProperty(t.identifier(key), t.identifier(key), false, true) + t.objectProperty(t.identifier(key), t.identifier(key), false, true), ); } properties.push( - t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false) + t.objectProperty( + t.identifier('selection'), + t.identifier('_selection'), + false, + false, + ), ); properties.push(t.restElement(t.identifier(restName))); @@ -497,24 +536,29 @@ export function destructureParamsWithSelection( return t.variableDeclaration('const', [ t.variableDeclarator( pattern, - t.logicalExpression('??', t.identifier('params'), t.objectExpression([])) - ) + t.logicalExpression('??', t.identifier('params'), t.objectExpression([])), + ), ]); } export function destructureParamsWithSelectionAndScope( - restName: string + restName: string, ): t.VariableDeclaration { const pattern = t.objectPattern([ t.objectProperty(t.identifier('scope'), t.identifier('scope'), false, true), - t.objectProperty(t.identifier('selection'), t.identifier('_selection'), false, false), - t.restElement(t.identifier(restName)) + t.objectProperty( + t.identifier('selection'), + t.identifier('_selection'), + false, + false, + ), + t.restElement(t.identifier(restName)), ]); return t.variableDeclaration('const', [ t.variableDeclarator( pattern, - t.logicalExpression('??', t.identifier('params'), t.objectExpression([])) - ) + t.logicalExpression('??', t.identifier('params'), t.objectExpression([])), + ), ]); } @@ -523,9 +567,10 @@ export function destructureParamsWithSelectionAndScope( // ============================================================================ export function addJSDocComment(node: T, lines: string[]): T { - const text = lines.length === 1 - ? `* ${lines[0]} ` - : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `; + const text = + lines.length === 1 + ? `* ${lines[0]} ` + : `*\n${lines.map((line) => ` * ${line}`).join('\n')}\n `; if (!node.leadingComments) { node.leadingComments = []; } @@ -542,7 +587,7 @@ export function addLineComment(node: T, text: string): T { value: ` ${text}`, start: null, end: null, - loc: null + loc: null, }); return node; } @@ -554,31 +599,28 @@ export function addLineComment(node: T, text: string): T { export function getClientCall( modelName: string, method: string, - args: t.Expression + args: t.Expression, ): t.CallExpression { return t.callExpression( t.memberExpression( - t.memberExpression( - callExpr('getClient', []), - t.identifier(modelName) - ), - t.identifier(method) + t.memberExpression(callExpr('getClient', []), t.identifier(modelName)), + t.identifier(method), ), - [args] + [args], ); } export function getClientCallUnwrap( modelName: string, method: string, - args: t.Expression + args: t.Expression, ): t.CallExpression { return t.callExpression( t.memberExpression( getClientCall(modelName, method, args), - t.identifier('unwrap') + t.identifier('unwrap'), ), - [] + [], ); } @@ -586,18 +628,18 @@ export function getClientCustomCall( operationType: 'query' | 'mutation', operationName: string, args: t.Expression[], - optionsArg?: t.Expression + optionsArg?: t.Expression, ): t.CallExpression { const callArgs = optionsArg ? [...args, optionsArg] : args; return t.callExpression( t.memberExpression( t.memberExpression( callExpr('getClient', []), - t.identifier(operationType) + t.identifier(operationType), ), - t.identifier(operationName) + t.identifier(operationName), ), - callArgs + callArgs, ); } @@ -605,14 +647,14 @@ export function getClientCustomCallUnwrap( operationType: 'query' | 'mutation', operationName: string, args: t.Expression[], - optionsArg?: t.Expression + optionsArg?: t.Expression, ): t.CallExpression { return t.callExpression( t.memberExpression( getClientCustomCall(operationType, operationName, args, optionsArg), - t.identifier('unwrap') + t.identifier('unwrap'), ), - [] + [], ); } @@ -620,29 +662,24 @@ export function getClientCustomCallUnwrap( // Select/args expression builders // ============================================================================ -export function buildSelectExpr( - argsIdent: string -): t.MemberExpression { - return t.memberExpression( - t.identifier(argsIdent), - t.identifier('select') - ); +export function buildSelectExpr(argsIdent: string): t.MemberExpression { + return t.memberExpression(t.identifier(argsIdent), t.identifier('select')); } export function buildFindManyCallExpr( singularName: string, - argsIdent: string + argsIdent: string, ): t.CallExpression { const spreadArgs = t.parenthesizedExpression( - t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) + t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])), ); return getClientCallUnwrap( singularName, 'findMany', t.objectExpression([ t.spreadElement(spreadArgs), - objectProp('select', buildSelectExpr(argsIdent)) - ]) + objectProp('select', buildSelectExpr(argsIdent)), + ]), ); } @@ -650,7 +687,7 @@ export function buildFindOneCallExpr( singularName: string, pkFieldName: string, argsIdent: string, - paramsIdent: string = 'params' + paramsIdent: string = 'params', ): t.CallExpression { return getClientCallUnwrap( singularName, @@ -658,15 +695,22 @@ export function buildFindOneCallExpr( t.objectExpression([ objectProp( pkFieldName, - t.memberExpression(t.identifier(paramsIdent), t.identifier(pkFieldName)) + t.memberExpression( + t.identifier(paramsIdent), + t.identifier(pkFieldName), + ), ), t.spreadElement( t.parenthesizedExpression( - t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])) - ) + t.logicalExpression( + '??', + t.identifier(argsIdent), + t.objectExpression([]), + ), + ), ), - objectProp('select', buildSelectExpr(argsIdent)) - ]) + objectProp('select', buildSelectExpr(argsIdent)), + ]), ); } @@ -676,7 +720,7 @@ export function buildFindOneCallExpr( export function generateHookFileCode( headerDescription: string, - statements: t.Statement[] + statements: t.Statement[], ): string { const header = getGeneratedFileHeader(headerDescription); const code = generateCode(statements); @@ -688,11 +732,13 @@ export function generateHookFileCode( // ============================================================================ export function scopeTypeLiteral(scopeTypeName: string): t.TSTypeLiteral { - return typeLiteralWithProps([{ - name: 'scope', - type: typeRef(scopeTypeName), - optional: true - }]); + return typeLiteralWithProps([ + { + name: 'scope', + type: typeRef(scopeTypeName), + optional: true, + }, + ]); } // ============================================================================ @@ -702,13 +748,13 @@ export function scopeTypeLiteral(scopeTypeName: string): t.TSTypeLiteral { export function wrapInferSelectResultType( typeRefNode: CleanArgument['type'], payloadTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSType { if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) { return wrapInferSelectResultType( typeRefNode.ofType as CleanArgument['type'], payloadTypeName, - selectType + selectType, ); } if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) { @@ -716,21 +762,23 @@ export function wrapInferSelectResultType( wrapInferSelectResultType( typeRefNode.ofType as CleanArgument['type'], payloadTypeName, - selectType - ) + selectType, + ), ); } return inferSelectResultType(payloadTypeName, selectType); } export function typeRefToTsTypeAST( - typeRefNode: CleanArgument['type'] + typeRefNode: CleanArgument['type'], ): t.TSType { if (typeRefNode.kind === 'NON_NULL' && typeRefNode.ofType) { return typeRefToTsTypeAST(typeRefNode.ofType as CleanArgument['type']); } if (typeRefNode.kind === 'LIST' && typeRefNode.ofType) { - return t.tsArrayType(typeRefToTsTypeAST(typeRefNode.ofType as CleanArgument['type'])); + return t.tsArrayType( + typeRefToTsTypeAST(typeRefNode.ofType as CleanArgument['type']), + ); } if (typeRefNode.kind === 'SCALAR') { const tsType = scalarToTsType(typeRefNode.name ?? 'unknown'); @@ -743,34 +791,36 @@ export function typeRefToTsTypeAST( } export function buildSelectionArgsCall( - selectTypeName: string + selectTypeName: string, ): t.VariableDeclaration { const call = t.callExpression(t.identifier('buildSelectionArgs'), [ t.optionalMemberExpression( t.identifier('params'), t.identifier('selection'), false, - true - ) + true, + ), ]); // @ts-ignore - Babel types support typeParameters on CallExpression for TS - call.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + call.typeParameters = t.tsTypeParameterInstantiation([ + typeRef(selectTypeName), + ]); return constDecl('args', call); } export function buildListSelectionArgsCall( selectTypeName: string, filterTypeName: string, - orderByTypeName: string + orderByTypeName: string, ): t.VariableDeclaration { const call = t.callExpression(t.identifier('buildListSelectionArgs'), [ - t.identifier('selection') + t.identifier('selection'), ]); // @ts-ignore - Babel types support typeParameters on CallExpression for TS call.typeParameters = t.tsTypeParameterInstantiation([ typeRef(selectTypeName), typeRef(filterTypeName), - typeRef(orderByTypeName) + typeRef(orderByTypeName), ]); return constDecl('args', call); } @@ -779,10 +829,12 @@ export function customSelectResultTypeLiteral( opName: string, returnType: CleanArgument['type'], payloadTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeLiteral { - return typeLiteralWithProps([{ - name: opName, - type: wrapInferSelectResultType(returnType, payloadTypeName, selectType) - }]); + return typeLiteralWithProps([ + { + name: opName, + type: wrapInferSelectResultType(returnType, payloadTypeName, selectType), + }, + ]); } diff --git a/graphql/codegen/src/core/codegen/index.ts b/graphql/codegen/src/core/codegen/index.ts index 57e75750f..312dda394 100644 --- a/graphql/codegen/src/core/codegen/index.ts +++ b/graphql/codegen/src/core/codegen/index.ts @@ -22,19 +22,22 @@ * useLoginMutation.ts - Custom mutation -> ORM mutation.xxx * ... */ -import type { GraphQLSDKConfigTarget, QueryKeyConfig } from '../../types/config'; +import type { + GraphQLSDKConfigTarget, + QueryKeyConfig, +} from '../../types/config'; import { DEFAULT_QUERY_KEY_CONFIG } from '../../types/config'; import type { CleanOperation, CleanTable, - TypeRegistry + TypeRegistry, } from '../../types/schema'; import { generateCustomMutationsBarrel, generateCustomQueriesBarrel, generateMainBarrel, generateMutationsBarrel, - generateQueriesBarrel + generateQueriesBarrel, } from './barrel'; import { generateClientFile } from './client'; import { generateAllCustomMutationHooks } from './custom-mutations'; @@ -99,7 +102,7 @@ export interface GenerateOptions { */ export function generateAllFiles( tables: CleanTable[], - config: GraphQLSDKConfigTarget + config: GraphQLSDKConfigTarget, ): GenerateResult { return generate({ tables, config }); } @@ -115,25 +118,25 @@ export function generate(options: GenerateOptions): GenerateResult { const files: GeneratedFile[] = []; // Extract codegen options - const maxDepth = config.codegen.maxFieldDepth; const skipQueryField = config.codegen.skipQueryField; const reactQueryEnabled = config.reactQuery; // Query key configuration (use defaults if not provided) - const queryKeyConfig: QueryKeyConfig = config.queryKeys ?? DEFAULT_QUERY_KEY_CONFIG; + const queryKeyConfig: QueryKeyConfig = + config.queryKeys ?? DEFAULT_QUERY_KEY_CONFIG; const useCentralizedKeys = queryKeyConfig.generateScopedKeys; const hasRelationships = Object.keys(queryKeyConfig.relationships).length > 0; // 1. Generate client.ts (ORM client wrapper) files.push({ path: 'client.ts', - content: generateClientFile() + content: generateClientFile(), }); // 1b. Generate selection.ts (shared selection adapters for hooks) files.push({ path: 'selection.ts', - content: generateSelectionFile() + content: generateSelectionFile(), }); // Collect table type names for import path resolution @@ -149,11 +152,11 @@ export function generate(options: GenerateOptions): GenerateResult { const queryKeysResult = generateQueryKeysFile({ tables, customQueries: customOperations?.queries ?? [], - config: queryKeyConfig + config: queryKeyConfig, }); files.push({ path: queryKeysResult.fileName, - content: queryKeysResult.content + content: queryKeysResult.content, }); hasQueryKeys = true; } @@ -164,11 +167,11 @@ export function generate(options: GenerateOptions): GenerateResult { const mutationKeysResult = generateMutationKeysFile({ tables, customMutations: customOperations?.mutations ?? [], - config: queryKeyConfig + config: queryKeyConfig, }); files.push({ path: mutationKeysResult.fileName, - content: mutationKeysResult.content + content: mutationKeysResult.content, }); hasMutationKeys = true; } @@ -178,11 +181,11 @@ export function generate(options: GenerateOptions): GenerateResult { if (useCentralizedKeys && queryKeyConfig.generateCascadeHelpers) { const invalidationResult = generateInvalidationFile({ tables, - config: queryKeyConfig + config: queryKeyConfig, }); files.push({ path: invalidationResult.fileName, - content: invalidationResult.content + content: invalidationResult.content, }); hasInvalidation = true; } @@ -191,12 +194,12 @@ export function generate(options: GenerateOptions): GenerateResult { const queryHooks = generateAllQueryHooks(tables, { reactQueryEnabled, useCentralizedKeys, - hasRelationships + hasRelationships, }); for (const hook of queryHooks) { files.push({ path: `queries/${hook.fileName}`, - content: hook.content + content: hook.content, }); } @@ -210,17 +213,16 @@ export function generate(options: GenerateOptions): GenerateResult { customQueryHooks = generateAllCustomQueryHooks({ operations: customOperations.queries, typeRegistry: customOperations.typeRegistry, - maxDepth, skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys + useCentralizedKeys, }); for (const hook of customQueryHooks) { files.push({ path: `queries/${hook.fileName}`, - content: hook.content + content: hook.content, }); } } @@ -231,21 +233,21 @@ export function generate(options: GenerateOptions): GenerateResult { content: customQueryHooks.length > 0 ? generateCustomQueriesBarrel( - tables, - customQueryHooks.map((h) => h.operationName) - ) - : generateQueriesBarrel(tables) + tables, + customQueryHooks.map((h) => h.operationName), + ) + : generateQueriesBarrel(tables), }); // 6. Generate table-based mutation hooks (mutations/*.ts) const mutationHooks = generateAllMutationHooks(tables, { reactQueryEnabled, - useCentralizedKeys + useCentralizedKeys, }); for (const hook of mutationHooks) { files.push({ path: `mutations/${hook.fileName}`, - content: hook.content + content: hook.content, }); } @@ -259,17 +261,16 @@ export function generate(options: GenerateOptions): GenerateResult { customMutationHooks = generateAllCustomMutationHooks({ operations: customOperations.mutations, typeRegistry: customOperations.typeRegistry, - maxDepth, skipQueryField, reactQueryEnabled, tableTypeNames, - useCentralizedKeys + useCentralizedKeys, }); for (const hook of customMutationHooks) { files.push({ path: `mutations/${hook.fileName}`, - content: hook.content + content: hook.content, }); } } @@ -284,10 +285,10 @@ export function generate(options: GenerateOptions): GenerateResult { content: customMutationHooks.length > 0 ? generateCustomMutationsBarrel( - tables, - customMutationHooks.map((h) => h.operationName) - ) - : generateMutationsBarrel(tables) + tables, + customMutationHooks.map((h) => h.operationName), + ) + : generateMutationsBarrel(tables), }); } @@ -299,8 +300,8 @@ export function generate(options: GenerateOptions): GenerateResult { hasMutations, hasQueryKeys, hasMutationKeys, - hasInvalidation - }) + hasInvalidation, + }), }); return { @@ -311,8 +312,8 @@ export function generate(options: GenerateOptions): GenerateResult { mutationHooks: mutationHooks.length, customQueryHooks: customQueryHooks.length, customMutationHooks: customMutationHooks.length, - totalFiles: files.length - } + totalFiles: files.length, + }, }; } @@ -325,16 +326,16 @@ export { generateCustomQueriesBarrel, generateMainBarrel, generateMutationsBarrel, - generateQueriesBarrel + generateQueriesBarrel, } from './barrel'; export { generateClientFile } from './client'; export { generateAllCustomMutationHooks, - generateCustomMutationHook + generateCustomMutationHook, } from './custom-mutations'; export { generateAllCustomQueryHooks, - generateCustomQueryHook + generateCustomQueryHook, } from './custom-queries'; export { generateInvalidationFile } from './invalidation'; export { generateMutationKeysFile } from './mutation-keys'; @@ -342,11 +343,11 @@ export { generateAllMutationHooks, generateCreateMutationHook, generateDeleteMutationHook, - generateUpdateMutationHook + generateUpdateMutationHook, } from './mutations'; export { generateAllQueryHooks, generateListQueryHook, - generateSingleQueryHook + generateSingleQueryHook, } from './queries'; export { generateQueryKeysFile } from './query-keys'; diff --git a/graphql/codegen/src/core/codegen/invalidation.ts b/graphql/codegen/src/core/codegen/invalidation.ts index ef9a5bf4b..c2932af32 100644 --- a/graphql/codegen/src/core/codegen/invalidation.ts +++ b/graphql/codegen/src/core/codegen/invalidation.ts @@ -6,16 +6,21 @@ */ import * as t from '@babel/types'; -import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; +import type { EntityRelationship, QueryKeyConfig } from '../../types/config'; import type { CleanTable } from '../../types/schema'; import { addJSDocComment, addLineComment, asConst, generateCode, - typedParam + typedParam, } from './babel-ast'; -import { getGeneratedFileHeader, getTableNames, lcFirst,ucFirst } from './utils'; +import { + getGeneratedFileHeader, + getTableNames, + lcFirst, + ucFirst, +} from './utils'; export interface InvalidationGeneratorOptions { tables: CleanTable[]; @@ -31,7 +36,7 @@ export interface GeneratedInvalidationFile { * Build a map of parent -> children for cascade invalidation */ function buildChildrenMap( - relationships: Record + relationships: Record, ): Map { const childrenMap = new Map(); @@ -51,7 +56,7 @@ function buildChildrenMap( */ function getAllDescendants( entity: string, - childrenMap: Map + childrenMap: Map, ): string[] { const descendants: string[] = []; const queue = [entity.toLowerCase()]; @@ -75,7 +80,7 @@ function buildEntityInvalidateProperty( table: CleanTable, relationships: Record, childrenMap: Map, - allTables: CleanTable[] + allTables: CleanTable[], ): t.ObjectProperty { const { typeName, singularName } = getTableNames(table); const entityKey = typeName.toLowerCase(); @@ -89,20 +94,31 @@ function buildEntityInvalidateProperty( const innerProperties: t.ObjectProperty[] = []; // Helper to create QueryClient type reference - const queryClientTypeRef = () => t.tsTypeReference(t.identifier('QueryClient')); - const stringOrNumberType = () => t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]); + const queryClientTypeRef = () => + t.tsTypeReference(t.identifier('QueryClient')); + const stringOrNumberType = () => + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]); // Helper to create queryClient.invalidateQueries({ queryKey: ... }) const invalidateCall = (queryKeyExpr: t.Expression) => t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), queryKeyExpr)])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('invalidateQueries'), + ), + [ + t.objectExpression([ + t.objectProperty(t.identifier('queryKey'), queryKeyExpr), + ]), + ], ); // all property const allArrowFn = t.arrowFunctionExpression( [typedParam('queryClient', queryClientTypeRef())], - invalidateCall(t.memberExpression(t.identifier(keysName), t.identifier('all'))) + invalidateCall( + t.memberExpression(t.identifier(keysName), t.identifier('all')), + ), ); const allProp = t.objectProperty(t.identifier('all'), allArrowFn); addJSDocComment(allProp, [`Invalidate all ${singularName} queries`]); @@ -112,15 +128,19 @@ function buildEntityInvalidateProperty( let listsProp: t.ObjectProperty; if (hasParent) { const scopeTypeName = `${typeName}Scope`; - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const listsArrowFn = t.arrowFunctionExpression( [typedParam('queryClient', queryClientTypeRef()), scopeParam], invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('lists')), - [t.identifier('scope')] - ) - ) + [t.identifier('scope')], + ), + ), ); listsProp = t.objectProperty(t.identifier('lists'), listsArrowFn); } else { @@ -129,9 +149,9 @@ function buildEntityInvalidateProperty( invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('lists')), - [] - ) - ) + [], + ), + ), ); listsProp = t.objectProperty(t.identifier('lists'), listsArrowFn); } @@ -142,26 +162,37 @@ function buildEntityInvalidateProperty( let detailProp: t.ObjectProperty; if (hasParent) { const scopeTypeName = `${typeName}Scope`; - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const detailArrowFn = t.arrowFunctionExpression( - [typedParam('queryClient', queryClientTypeRef()), typedParam('id', stringOrNumberType()), scopeParam], + [ + typedParam('queryClient', queryClientTypeRef()), + typedParam('id', stringOrNumberType()), + scopeParam, + ], invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.identifier('id'), t.identifier('scope')] - ) - ) + [t.identifier('id'), t.identifier('scope')], + ), + ), ); detailProp = t.objectProperty(t.identifier('detail'), detailArrowFn); } else { const detailArrowFn = t.arrowFunctionExpression( - [typedParam('queryClient', queryClientTypeRef()), typedParam('id', stringOrNumberType())], + [ + typedParam('queryClient', queryClientTypeRef()), + typedParam('id', stringOrNumberType()), + ], invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.identifier('id')] - ) - ) + [t.identifier('id')], + ), + ), ); detailProp = t.objectProperty(t.identifier('detail'), detailArrowFn); } @@ -177,9 +208,9 @@ function buildEntityInvalidateProperty( invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.identifier('id')] - ) - ) + [t.identifier('id')], + ), + ), ); addLineComment(selfDetailStmt, `Invalidate this ${singularName}`); cascadeStatements.push(selfDetailStmt); @@ -189,17 +220,17 @@ function buildEntityInvalidateProperty( invalidateCall( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('lists')), - [] - ) - ) - ) + [], + ), + ), + ), ); // Comment: Cascade to child entities let firstCascade = true; for (const descendant of descendants) { const descendantTable = allTables.find( - (tbl) => getTableNames(tbl).typeName.toLowerCase() === descendant + (tbl) => getTableNames(tbl).typeName.toLowerCase() === descendant, ); if (descendantTable) { const { typeName: descTypeName } = getTableNames(descendantTable); @@ -219,16 +250,22 @@ function buildEntityInvalidateProperty( cascadeStmt = t.expressionStatement( invalidateCall( t.callExpression( - t.memberExpression(t.identifier(descKeysName), t.identifier(`by${ucFirst(typeName)}`)), - [t.identifier('id')] - ) - ) + t.memberExpression( + t.identifier(descKeysName), + t.identifier(`by${ucFirst(typeName)}`), + ), + [t.identifier('id')], + ), + ), ); } else { cascadeStmt = t.expressionStatement( invalidateCall( - t.memberExpression(t.identifier(descKeysName), t.identifier('all')) - ) + t.memberExpression( + t.identifier(descKeysName), + t.identifier('all'), + ), + ), ); } @@ -242,18 +279,27 @@ function buildEntityInvalidateProperty( } const withChildrenArrowFn = t.arrowFunctionExpression( - [typedParam('queryClient', queryClientTypeRef()), typedParam('id', stringOrNumberType())], - t.blockStatement(cascadeStatements) + [ + typedParam('queryClient', queryClientTypeRef()), + typedParam('id', stringOrNumberType()), + ], + t.blockStatement(cascadeStatements), + ); + const withChildrenProp = t.objectProperty( + t.identifier('withChildren'), + withChildrenArrowFn, ); - const withChildrenProp = t.objectProperty(t.identifier('withChildren'), withChildrenArrowFn); addJSDocComment(withChildrenProp, [ `Invalidate ${singularName} and all child entities`, - `Cascades to: ${descendants.join(', ')}` + `Cascades to: ${descendants.join(', ')}`, ]); innerProperties.push(withChildrenProp); } - const entityProp = t.objectProperty(t.identifier(singularName), t.objectExpression(innerProperties)); + const entityProp = t.objectProperty( + t.identifier(singularName), + t.objectExpression(innerProperties), + ); addJSDocComment(entityProp, [`Invalidate ${singularName} queries`]); return entityProp; } @@ -263,54 +309,80 @@ function buildEntityInvalidateProperty( */ function buildEntityRemoveProperty( table: CleanTable, - relationships: Record + relationships: Record, ): t.ObjectProperty { const { typeName, singularName } = getTableNames(table); const keysName = `${lcFirst(typeName)}Keys`; const relationship = relationships[typeName.toLowerCase()]; // Helper types - const queryClientTypeRef = () => t.tsTypeReference(t.identifier('QueryClient')); - const stringOrNumberType = () => t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]); + const queryClientTypeRef = () => + t.tsTypeReference(t.identifier('QueryClient')); + const stringOrNumberType = () => + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]); // Helper to create queryClient.removeQueries({ queryKey: ... }) const removeCall = (queryKeyExpr: t.Expression) => t.callExpression( - t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), - [t.objectExpression([t.objectProperty(t.identifier('queryKey'), queryKeyExpr)])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('removeQueries'), + ), + [ + t.objectExpression([ + t.objectProperty(t.identifier('queryKey'), queryKeyExpr), + ]), + ], ); let removeProp: t.ObjectProperty; if (relationship) { const scopeTypeName = `${typeName}Scope`; - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const removeArrowFn = t.arrowFunctionExpression( - [typedParam('queryClient', queryClientTypeRef()), typedParam('id', stringOrNumberType()), scopeParam], + [ + typedParam('queryClient', queryClientTypeRef()), + typedParam('id', stringOrNumberType()), + scopeParam, + ], t.blockStatement([ t.expressionStatement( removeCall( t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.identifier('id'), t.identifier('scope')] - ) - ) - ) - ]) + t.memberExpression( + t.identifier(keysName), + t.identifier('detail'), + ), + [t.identifier('id'), t.identifier('scope')], + ), + ), + ), + ]), ); removeProp = t.objectProperty(t.identifier(singularName), removeArrowFn); } else { const removeArrowFn = t.arrowFunctionExpression( - [typedParam('queryClient', queryClientTypeRef()), typedParam('id', stringOrNumberType())], + [ + typedParam('queryClient', queryClientTypeRef()), + typedParam('id', stringOrNumberType()), + ], t.blockStatement([ t.expressionStatement( removeCall( t.callExpression( - t.memberExpression(t.identifier(keysName), t.identifier('detail')), - [t.identifier('id')] - ) - ) - ) - ]) + t.memberExpression( + t.identifier(keysName), + t.identifier('detail'), + ), + [t.identifier('id')], + ), + ), + ), + ]), ); removeProp = t.objectProperty(t.identifier(singularName), removeArrowFn); } @@ -323,7 +395,7 @@ function buildEntityRemoveProperty( * Generate the complete invalidation.ts file */ export function generateInvalidationFile( - options: InvalidationGeneratorOptions + options: InvalidationGeneratorOptions, ): GeneratedInvalidationFile { const { tables, config } = options; const { relationships, generateCascadeHelpers } = config; @@ -334,8 +406,13 @@ export function generateInvalidationFile( // Import QueryClient type const queryClientImport = t.importDeclaration( - [t.importSpecifier(t.identifier('QueryClient'), t.identifier('QueryClient'))], - t.stringLiteral('@tanstack/react-query') + [ + t.importSpecifier( + t.identifier('QueryClient'), + t.identifier('QueryClient'), + ), + ], + t.stringLiteral('@tanstack/react-query'), ); queryClientImport.importKind = 'type'; statements.push(queryClientImport); @@ -348,9 +425,11 @@ export function generateInvalidationFile( } statements.push( t.importDeclaration( - keyImports.map(name => t.importSpecifier(t.identifier(name), t.identifier(name))), - t.stringLiteral('./query-keys') - ) + keyImports.map((name) => + t.importSpecifier(t.identifier(name), t.identifier(name)), + ), + t.stringLiteral('./query-keys'), + ), ); // Import scope types if needed @@ -363,8 +442,10 @@ export function generateInvalidationFile( } if (scopeTypes.length > 0) { const scopeImport = t.importDeclaration( - scopeTypes.map(name => t.importSpecifier(t.identifier(name), t.identifier(name))), - t.stringLiteral('./query-keys') + scopeTypes.map((name) => + t.importSpecifier(t.identifier(name), t.identifier(name)), + ), + t.stringLiteral('./query-keys'), ); scopeImport.importKind = 'type'; statements.push(scopeImport); @@ -373,16 +454,18 @@ export function generateInvalidationFile( // Generate invalidate object const invalidateProperties: t.ObjectProperty[] = []; for (const table of tables) { - invalidateProperties.push(buildEntityInvalidateProperty(table, relationships, childrenMap, tables)); + invalidateProperties.push( + buildEntityInvalidateProperty(table, relationships, childrenMap, tables), + ); } const invalidateDecl = t.exportNamedDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('invalidate'), - asConst(t.objectExpression(invalidateProperties)) - ) - ]) + asConst(t.objectExpression(invalidateProperties)), + ), + ]), ); // Build JSDoc for invalidate @@ -398,12 +481,14 @@ export function generateInvalidationFile( 'invalidate.user.lists(queryClient);', '', '// Invalidate specific user', - 'invalidate.user.detail(queryClient, userId);' + 'invalidate.user.detail(queryClient, userId);', ]; if (generateCascadeHelpers && Object.keys(relationships).length > 0) { invalidateDocLines.push(''); invalidateDocLines.push('// Cascade invalidate (entity + all children)'); - invalidateDocLines.push('invalidate.database.withChildren(queryClient, databaseId);'); + invalidateDocLines.push( + 'invalidate.database.withChildren(queryClient, databaseId);', + ); } invalidateDocLines.push('```'); addJSDocComment(invalidateDecl, invalidateDocLines); @@ -419,15 +504,15 @@ export function generateInvalidationFile( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('remove'), - asConst(t.objectExpression(removeProperties)) - ) - ]) + asConst(t.objectExpression(removeProperties)), + ), + ]), ); addJSDocComment(removeDecl, [ 'Remove queries from cache (for delete operations)', '', 'Use these when an entity is deleted to remove it from cache', - 'instead of just invalidating (which would trigger a refetch).' + 'instead of just invalidating (which would trigger a refetch).', ]); statements.push(removeDecl); @@ -460,7 +545,10 @@ ${description} const line = codeLines[i]; // Detect invalidation section (after imports) - if (!addedInvalidationSection && line.includes('* Type-safe query invalidation helpers')) { + if ( + !addedInvalidationSection && + line.includes('* Type-safe query invalidation helpers') + ) { content += `// ============================================================================ // Invalidation Helpers // ============================================================================ @@ -485,6 +573,6 @@ ${description} return { fileName: 'invalidation.ts', - content + content, }; } diff --git a/graphql/codegen/src/core/codegen/mutation-keys.ts b/graphql/codegen/src/core/codegen/mutation-keys.ts index 5816e3ee1..b9ca8df09 100644 --- a/graphql/codegen/src/core/codegen/mutation-keys.ts +++ b/graphql/codegen/src/core/codegen/mutation-keys.ts @@ -9,14 +9,14 @@ */ import * as t from '@babel/types'; -import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; -import type { CleanOperation,CleanTable } from '../../types/schema'; +import type { EntityRelationship, QueryKeyConfig } from '../../types/config'; +import type { CleanOperation, CleanTable } from '../../types/schema'; import { addJSDocComment, asConst, constArray, generateCode, - typedParam + typedParam, } from './babel-ast'; import { getGeneratedFileHeader, getTableNames, lcFirst } from './utils'; @@ -36,7 +36,7 @@ export interface GeneratedMutationKeysFile { */ function generateEntityMutationKeysDeclaration( table: CleanTable, - relationships: Record + relationships: Record, ): t.ExportNamedDeclaration { const { typeName, singularName } = getTableNames(table); const entityKey = typeName.toLowerCase(); @@ -49,7 +49,7 @@ function generateEntityMutationKeysDeclaration( // all property const allProp = t.objectProperty( t.identifier('all'), - constArray([t.stringLiteral('mutation'), t.stringLiteral(entityKey)]) + constArray([t.stringLiteral('mutation'), t.stringLiteral(entityKey)]), ); addJSDocComment(allProp, [`All ${singularName} mutation keys`]); properties.push(allProp); @@ -74,16 +74,16 @@ function generateEntityMutationKeysDeclaration( t.identifier(relationship.foreignKey), t.identifier(relationship.foreignKey), false, - true - ) - ]) + true, + ), + ]), ]), constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), - t.stringLiteral('create') - ]) - ) + t.stringLiteral('create'), + ]), + ), ); createProp = t.objectProperty(t.identifier('create'), arrowFn); @@ -93,8 +93,8 @@ function generateEntityMutationKeysDeclaration( constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), - t.stringLiteral('create') - ]) + t.stringLiteral('create'), + ]), ); createProp = t.objectProperty(t.identifier('create'), arrowFn); @@ -104,13 +104,18 @@ function generateEntityMutationKeysDeclaration( // update property const updateArrowFn = t.arrowFunctionExpression( - [typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]))], + [ + typedParam( + 'id', + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]), + ), + ], constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), t.stringLiteral('update'), - t.identifier('id') - ]) + t.identifier('id'), + ]), ); const updateProp = t.objectProperty(t.identifier('update'), updateArrowFn); addJSDocComment(updateProp, [`Update ${singularName} mutation key`]); @@ -118,13 +123,18 @@ function generateEntityMutationKeysDeclaration( // delete property const deleteArrowFn = t.arrowFunctionExpression( - [typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]))], + [ + typedParam( + 'id', + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]), + ), + ], constArray([ t.stringLiteral('mutation'), t.stringLiteral(entityKey), t.stringLiteral('delete'), - t.identifier('id') - ]) + t.identifier('id'), + ]), ); const deleteProp = t.objectProperty(t.identifier('delete'), deleteArrowFn); addJSDocComment(deleteProp, [`Delete ${singularName} mutation key`]); @@ -134,9 +144,9 @@ function generateEntityMutationKeysDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(keysName), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); } @@ -144,7 +154,7 @@ function generateEntityMutationKeysDeclaration( * Generate custom mutation keys declaration */ function generateCustomMutationKeysDeclaration( - operations: CleanOperation[] + operations: CleanOperation[], ): t.ExportNamedDeclaration | null { if (operations.length === 0) return null; @@ -167,17 +177,17 @@ function generateCustomMutationKeysDeclaration( constArray([ t.stringLiteral('mutation'), t.stringLiteral(op.name), - t.identifier('identifier') + t.identifier('identifier'), ]), - constArray([t.stringLiteral('mutation'), t.stringLiteral(op.name)]) - ) + constArray([t.stringLiteral('mutation'), t.stringLiteral(op.name)]), + ), ); prop = t.objectProperty(t.identifier(op.name), arrowFn); } else { const arrowFn = t.arrowFunctionExpression( [], - constArray([t.stringLiteral('mutation'), t.stringLiteral(op.name)]) + constArray([t.stringLiteral('mutation'), t.stringLiteral(op.name)]), ); prop = t.objectProperty(t.identifier(op.name), arrowFn); @@ -191,9 +201,9 @@ function generateCustomMutationKeysDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('customMutationKeys'), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); } @@ -202,7 +212,7 @@ function generateCustomMutationKeysDeclaration( */ function generateUnifiedMutationStoreDeclaration( tables: CleanTable[], - hasCustomMutations: boolean + hasCustomMutations: boolean, ): t.ExportNamedDeclaration { const properties: t.ObjectProperty[] = []; @@ -210,13 +220,16 @@ function generateUnifiedMutationStoreDeclaration( const { typeName } = getTableNames(table); const keysName = `${lcFirst(typeName)}MutationKeys`; properties.push( - t.objectProperty(t.identifier(lcFirst(typeName)), t.identifier(keysName)) + t.objectProperty(t.identifier(lcFirst(typeName)), t.identifier(keysName)), ); } if (hasCustomMutations) { properties.push( - t.objectProperty(t.identifier('custom'), t.identifier('customMutationKeys')) + t.objectProperty( + t.identifier('custom'), + t.identifier('customMutationKeys'), + ), ); } @@ -224,9 +237,9 @@ function generateUnifiedMutationStoreDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('mutationKeys'), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); addJSDocComment(decl, [ @@ -244,7 +257,7 @@ function generateUnifiedMutationStoreDeclaration( '', '// Check if a specific user is being updated', 'const isUpdating = useIsMutating({ mutationKey: mutationKeys.user.update(userId) });', - '```' + '```', ]); return decl; @@ -254,7 +267,7 @@ function generateUnifiedMutationStoreDeclaration( * Generate the complete mutation-keys.ts file */ export function generateMutationKeysFile( - options: MutationKeyGeneratorOptions + options: MutationKeyGeneratorOptions, ): GeneratedMutationKeysFile { const { tables, customMutations, config } = options; const { relationships } = config; @@ -263,18 +276,28 @@ export function generateMutationKeysFile( // Generate entity mutation keys for (const table of tables) { - statements.push(generateEntityMutationKeysDeclaration(table, relationships)); + statements.push( + generateEntityMutationKeysDeclaration(table, relationships), + ); } // Generate custom mutation keys - const mutationOperations = customMutations.filter((op) => op.kind === 'mutation'); - const customKeysDecl = generateCustomMutationKeysDeclaration(mutationOperations); + const mutationOperations = customMutations.filter( + (op) => op.kind === 'mutation', + ); + const customKeysDecl = + generateCustomMutationKeysDeclaration(mutationOperations); if (customKeysDecl) { statements.push(customKeysDecl); } // Generate unified store - statements.push(generateUnifiedMutationStoreDeclaration(tables, mutationOperations.length > 0)); + statements.push( + generateUnifiedMutationStoreDeclaration( + tables, + mutationOperations.length > 0, + ), + ); // Generate code from AST const code = generateCode(statements); @@ -310,7 +333,10 @@ ${description} const line = codeLines[i]; // Detect custom mutation keys section - if (!addedCustomSection && line.startsWith('export const customMutationKeys')) { + if ( + !addedCustomSection && + line.startsWith('export const customMutationKeys') + ) { content += ` // ============================================================================ // Custom Mutation Keys @@ -336,6 +362,6 @@ ${description} return { fileName: 'mutation-keys.ts', - content + content, }; } diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index ce29f23ed..29c9becec 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -36,7 +36,7 @@ import { typeLiteralWithProps, useMutationOptionsType, useMutationResultType, - voidStatement + voidStatement, } from './hooks-ast'; import { getCreateMutationFileName, @@ -51,7 +51,7 @@ import { getUpdateMutationHookName, getUpdateMutationName, hasValidPrimaryKey, - lcFirst + lcFirst, } from './utils'; export interface GeneratedMutationFile { @@ -68,34 +68,40 @@ function buildMutationResultType( mutationName: string, singularName: string, relationTypeName: string, - selectType: t.TSType + selectType: t.TSType, ): t.TSTypeLiteral { - return typeLiteralWithProps([{ - name: mutationName, - type: typeLiteralWithProps([{ - name: singularName, - type: inferSelectResultType(relationTypeName, selectType) - }]) - }]); + return typeLiteralWithProps([ + { + name: mutationName, + type: typeLiteralWithProps([ + { + name: singularName, + type: inferSelectResultType(relationTypeName, selectType), + }, + ]), + }, + ]); } -function buildFieldsSelectionType(s: t.TSType, selectTypeName: string): t.TSParenthesizedType { +function buildFieldsSelectionType( + s: t.TSType, + selectTypeName: string, +): t.TSParenthesizedType { return t.tsParenthesizedType( t.tsIntersectionType([ - t.tsTypeLiteral([t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s))]), - typeRef('StrictSelect', [s, typeRef(selectTypeName)]) - ]) + t.tsTypeLiteral([ + t.tsPropertySignature(t.identifier('fields'), t.tsTypeAnnotation(s)), + ]), + typeRef('StrictSelect', [s, typeRef(selectTypeName)]), + ]), ); } export function generateCreateMutationHook( table: CleanTable, - options: MutationGeneratorOptions = {} + options: MutationGeneratorOptions = {}, ): GeneratedMutationFile | null { - const { - reactQueryEnabled = true, - useCentralizedKeys = true - } = options; + const { reactQueryEnabled = true, useCentralizedKeys = true } = options; if (!reactQueryEnabled) return null; @@ -111,43 +117,81 @@ export function generateCreateMutationHook( const statements: t.Statement[] = []; // Imports - statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', [ + 'useMutation', + 'useQueryClient', + ]), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseMutationOptions', 'UseMutationResult'], + true, + ), + ); statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { statements.push(createImportDeclaration('../query-keys', [keysName])); - statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); + statements.push( + createImportDeclaration('../mutation-keys', [mutationKeysName]), + ); } - statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, createInputTypeName], true)); - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/input-types', + [selectTypeName, relationTypeName, createInputTypeName], + true, + ), + ); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); // Re-exports - statements.push(createTypeReExport([selectTypeName, relationTypeName, createInputTypeName], '../../orm/input-types')); + statements.push( + createTypeReExport( + [selectTypeName, relationTypeName, createInputTypeName], + '../../orm/input-types', + ), + ); // Variable type: CreateTypeName['singularName'] const createVarType = t.tsIndexedAccessType( typeRef(createInputTypeName), - t.tsLiteralType(t.stringLiteral(singularName)) + t.tsLiteralType(t.stringLiteral(singularName)), ); - const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + const resultType = (sel: t.TSType) => + buildMutationResultType(mutationName, singularName, relationTypeName, sel); // Overload 1: with fields const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName)), + ), ]), - useMutationOptionsType(resultType(sRef()), createVarType) + useMutationOptionsType(resultType(sRef()), createVarType), ]); const o1 = exportDeclareFunction( hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], - useMutationResultType(resultType(sRef()), createVarType) + useMutationResultType(resultType(sRef()), createVarType), ); addJSDocComment(o1, [ `Mutation hook for creating a ${typeName}`, @@ -159,15 +203,25 @@ export function generateCreateMutationHook( '});', '', "mutate({ name: 'New item' });", - '```' + '```', ]); statements.push(o1); // Implementation - const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), - omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), createVarType]), ['mutationFn']) + omitType( + typeRef('UseMutationOptions', [ + t.tsAnyKeyword(), + typeRef('Error'), + createVarType, + ]), + ['mutationFn'], + ), ]); const body: t.Statement[] = []; @@ -177,34 +231,56 @@ export function generateCreateMutationHook( body.push(constDecl('queryClient', callExpr('useQueryClient', []))); const mutationKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(mutationKeysName), t.identifier('create')), []) + ? callExpr( + t.memberExpression( + t.identifier(mutationKeysName), + t.identifier('create'), + ), + [], + ) : undefined; // mutationFn: (data: CreateInput['singular']) => getClient().singular.create({ data, select: ... }).unwrap() const dataParam = createFunctionParam('data', createVarType); const mutationFnExpr = t.arrowFunctionExpression( [dataParam], - getClientCallUnwrap(singularName, 'create', t.objectExpression([ - shorthandProp('data'), - objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) - ])) + getClientCallUnwrap( + singularName, + 'create', + t.objectExpression([ + shorthandProp('data'), + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]), + ), ); // onSuccess: invalidate lists const listKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + ? callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('lists')), + [], + ) + : t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('list'), + ]); const onSuccessFn = t.arrowFunctionExpression( [], t.blockStatement([ t.expressionStatement( callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([objectProp('queryKey', listKeyExpr)])] - ) - ) - ]) + t.memberExpression( + t.identifier('queryClient'), + t.identifier('invalidateQueries'), + ), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])], + ), + ), + ]), ); body.push( @@ -212,28 +288,35 @@ export function generateCreateMutationHook( mutationFnExpr, [ objectProp('onSuccess', onSuccessFn), - spreadObj(t.identifier('mutationOptions')) + spreadObj(t.identifier('mutationOptions')), ], - mutationKeyExpr - ) + mutationKeyExpr, + ), ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType)], + body, + ), + ); return { fileName: getCreateMutationFileName(table), - content: generateHookFileCode(`Create mutation hook for ${typeName}`, statements) + content: generateHookFileCode( + `Create mutation hook for ${typeName}`, + statements, + ), }; } export function generateUpdateMutationHook( table: CleanTable, - options: MutationGeneratorOptions = {} + options: MutationGeneratorOptions = {}, ): GeneratedMutationFile | null { - const { - reactQueryEnabled = true, - useCentralizedKeys = true - } = options; + const { reactQueryEnabled = true, useCentralizedKeys = true } = options; if (!reactQueryEnabled) return null; if (table.query?.update === null) return null; @@ -251,48 +334,93 @@ export function generateUpdateMutationHook( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); + const pkTsType = + pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); const statements: t.Statement[] = []; // Imports - statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', [ + 'useMutation', + 'useQueryClient', + ]), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseMutationOptions', 'UseMutationResult'], + true, + ), + ); statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { statements.push(createImportDeclaration('../query-keys', [keysName])); - statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); + statements.push( + createImportDeclaration('../mutation-keys', [mutationKeysName]), + ); } - statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, patchTypeName], true)); - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/input-types', + [selectTypeName, relationTypeName, patchTypeName], + true, + ), + ); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); // Re-exports - statements.push(createTypeReExport([selectTypeName, relationTypeName, patchTypeName], '../../orm/input-types')); + statements.push( + createTypeReExport( + [selectTypeName, relationTypeName, patchTypeName], + '../../orm/input-types', + ), + ); // Variable type: { pkField: type; patch: PatchType } const updateVarType = t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)), - t.tsPropertySignature(t.identifier('patch'), t.tsTypeAnnotation(typeRef(patchTypeName))) + t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkTsType), + ), + t.tsPropertySignature( + t.identifier('patch'), + t.tsTypeAnnotation(typeRef(patchTypeName)), + ), ]); - const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + const resultType = (sel: t.TSType) => + buildMutationResultType(mutationName, singularName, relationTypeName, sel); // Overload 1: with fields const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName)), + ), ]), - useMutationOptionsType(resultType(sRef()), updateVarType) + useMutationOptionsType(resultType(sRef()), updateVarType), ]); const o1 = exportDeclareFunction( hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], - useMutationResultType(resultType(sRef()), updateVarType) + useMutationResultType(resultType(sRef()), updateVarType), ); addJSDocComment(o1, [ `Mutation hook for updating a ${typeName}`, @@ -304,15 +432,25 @@ export function generateUpdateMutationHook( '});', '', `mutate({ ${pkField.name}: 'value-here', patch: { name: 'Updated' } });`, - '```' + '```', ]); statements.push(o1); // Implementation - const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), - omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), updateVarType]), ['mutationFn']) + omitType( + typeRef('UseMutationOptions', [ + t.tsAnyKeyword(), + typeRef('Error'), + updateVarType, + ]), + ['mutationFn'], + ), ]); const body: t.Statement[] = []; @@ -329,27 +467,53 @@ export function generateUpdateMutationHook( // getClient().singular.update({ where: { pkField }, data: patch, select: ... }).unwrap() const destructParam = t.objectPattern([ shorthandProp(pkField.name), - shorthandProp('patch') + shorthandProp('patch'), ]); destructParam.typeAnnotation = t.tsTypeAnnotation(updateVarType); const mutationFnExpr = t.arrowFunctionExpression( [destructParam], - getClientCallUnwrap(singularName, 'update', t.objectExpression([ - objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), - objectProp('data', t.identifier('patch')), - objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) - ])) + getClientCallUnwrap( + singularName, + 'update', + t.objectExpression([ + objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), + objectProp('data', t.identifier('patch')), + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]), + ), ); // onSuccess: invalidate detail and lists const detailKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [ - t.memberExpression(t.identifier('variables'), t.identifier(pkField.name)) - ]) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.memberExpression(t.identifier('variables'), t.identifier(pkField.name))]); + ? callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('detail')), + [ + t.memberExpression( + t.identifier('variables'), + t.identifier(pkField.name), + ), + ], + ) + : t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('detail'), + t.memberExpression( + t.identifier('variables'), + t.identifier(pkField.name), + ), + ]); const listKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + ? callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('lists')), + [], + ) + : t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('list'), + ]); const onSuccessParam = t.identifier('_'); const variablesParam = t.identifier('variables'); @@ -358,17 +522,23 @@ export function generateUpdateMutationHook( t.blockStatement([ t.expressionStatement( callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([objectProp('queryKey', detailKeyExpr)])] - ) + t.memberExpression( + t.identifier('queryClient'), + t.identifier('invalidateQueries'), + ), + [t.objectExpression([objectProp('queryKey', detailKeyExpr)])], + ), ), t.expressionStatement( callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([objectProp('queryKey', listKeyExpr)])] - ) - ) - ]) + t.memberExpression( + t.identifier('queryClient'), + t.identifier('invalidateQueries'), + ), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])], + ), + ), + ]), ); body.push( @@ -376,28 +546,35 @@ export function generateUpdateMutationHook( mutationFnExpr, [ objectProp('onSuccess', onSuccessFn), - spreadObj(t.identifier('mutationOptions')) + spreadObj(t.identifier('mutationOptions')), ], - mutationKeyExpr - ) + mutationKeyExpr, + ), ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType)], + body, + ), + ); return { fileName: getUpdateMutationFileName(table), - content: generateHookFileCode(`Update mutation hook for ${typeName}`, statements) + content: generateHookFileCode( + `Update mutation hook for ${typeName}`, + statements, + ), }; } export function generateDeleteMutationHook( table: CleanTable, - options: MutationGeneratorOptions = {} + options: MutationGeneratorOptions = {}, ): GeneratedMutationFile | null { - const { - reactQueryEnabled = true, - useCentralizedKeys = true - } = options; + const { reactQueryEnabled = true, useCentralizedKeys = true } = options; if (!reactQueryEnabled) return null; if (table.query?.delete === null) return null; @@ -414,55 +591,103 @@ export function generateDeleteMutationHook( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pkTsType = pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); + const pkTsType = + pkField.tsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); const statements: t.Statement[] = []; // Imports - statements.push(createImportDeclaration('@tanstack/react-query', ['useMutation', 'useQueryClient'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseMutationOptions', 'UseMutationResult'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', [ + 'useMutation', + 'useQueryClient', + ]), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseMutationOptions', 'UseMutationResult'], + true, + ), + ); statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { statements.push(createImportDeclaration('../query-keys', [keysName])); - statements.push(createImportDeclaration('../mutation-keys', [mutationKeysName])); + statements.push( + createImportDeclaration('../mutation-keys', [mutationKeysName]), + ); } - statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true)); - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/input-types', + [selectTypeName, relationTypeName], + true, + ), + ); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); // Re-exports - statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); + statements.push( + createTypeReExport( + [selectTypeName, relationTypeName], + '../../orm/input-types', + ), + ); // Variable type: { pkField: type } const deleteVarType = t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType)) + t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkTsType), + ), ]); - const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); + const resultType = (sel: t.TSType) => + buildMutationResultType(mutationName, singularName, relationTypeName, sel); const clientMutationIdResultType = t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(mutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('clientMutationId'), t.tsTypeAnnotation(t.tsStringKeyword())) - ]) - )) + t.tsPropertySignature( + t.identifier(mutationName), + t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('clientMutationId'), + t.tsTypeAnnotation(t.tsStringKeyword()), + ), + ]), + ), + ), ]); // Overload 1: with fields const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName))) + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(buildFieldsSelectionType(sRef(), selectTypeName)), + ), ]), - useMutationOptionsType(resultType(sRef()), deleteVarType) + useMutationOptionsType(resultType(sRef()), deleteVarType), ]); const o1 = exportDeclareFunction( hookName, createSTypeParam(selectTypeName), [createFunctionParam('params', o1ParamType)], - useMutationResultType(resultType(sRef()), deleteVarType) + useMutationResultType(resultType(sRef()), deleteVarType), ); addJSDocComment(o1, [ `Mutation hook for deleting a ${typeName} with typed selection`, @@ -474,17 +699,20 @@ export function generateDeleteMutationHook( '});', '', `mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`, - '```' + '```', ]); statements.push(o1); // Overload 2: fire-and-forget (no selection, returns clientMutationId only) - const o2ParamType = useMutationOptionsType(clientMutationIdResultType, deleteVarType); + const o2ParamType = useMutationOptionsType( + clientMutationIdResultType, + deleteVarType, + ); const o2 = exportDeclareFunction( hookName, null, [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(clientMutationIdResultType, deleteVarType) + useMutationResultType(clientMutationIdResultType, deleteVarType), ); addJSDocComment(o2, [ `Fire-and-forget mutation hook for deleting a ${typeName}`, @@ -495,16 +723,26 @@ export function generateDeleteMutationHook( `const { mutate, isPending } = ${hookName}();`, '', `mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`, - '```' + '```', ]); statements.push(o2); // Implementation - const implSelProp = t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName)))); + const implSelProp = t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ); implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), - omitType(typeRef('UseMutationOptions', [t.tsAnyKeyword(), typeRef('Error'), deleteVarType]), ['mutationFn']) + omitType( + typeRef('UseMutationOptions', [ + t.tsAnyKeyword(), + typeRef('Error'), + deleteVarType, + ]), + ['mutationFn'], + ), ]); const body: t.Statement[] = []; @@ -523,38 +761,70 @@ export function generateDeleteMutationHook( destructParam.typeAnnotation = t.tsTypeAnnotation(deleteVarType); const mutationFnExpr = t.arrowFunctionExpression( [destructParam], - getClientCallUnwrap(singularName, 'delete', t.objectExpression([ - objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), - objectProp('select', t.memberExpression(t.identifier('args'), t.identifier('select'))) - ])) + getClientCallUnwrap( + singularName, + 'delete', + t.objectExpression([ + objectProp('where', t.objectExpression([shorthandProp(pkField.name)])), + objectProp( + 'select', + t.memberExpression(t.identifier('args'), t.identifier('select')), + ), + ]), + ), ); // onSuccess: remove detail, invalidate lists const detailKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), [ - t.memberExpression(t.identifier('variables'), t.identifier(pkField.name)) - ]) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.memberExpression(t.identifier('variables'), t.identifier(pkField.name))]); + ? callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('detail')), + [ + t.memberExpression( + t.identifier('variables'), + t.identifier(pkField.name), + ), + ], + ) + : t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('detail'), + t.memberExpression( + t.identifier('variables'), + t.identifier(pkField.name), + ), + ]); const listKeyExpr = useCentralizedKeys - ? callExpr(t.memberExpression(t.identifier(keysName), t.identifier('lists')), []) - : t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list')]); + ? callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('lists')), + [], + ) + : t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('list'), + ]); const onSuccessFn = t.arrowFunctionExpression( [t.identifier('_'), t.identifier('variables')], t.blockStatement([ t.expressionStatement( callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('removeQueries')), - [t.objectExpression([objectProp('queryKey', detailKeyExpr)])] - ) + t.memberExpression( + t.identifier('queryClient'), + t.identifier('removeQueries'), + ), + [t.objectExpression([objectProp('queryKey', detailKeyExpr)])], + ), ), t.expressionStatement( callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('invalidateQueries')), - [t.objectExpression([objectProp('queryKey', listKeyExpr)])] - ) - ) - ]) + t.memberExpression( + t.identifier('queryClient'), + t.identifier('invalidateQueries'), + ), + [t.objectExpression([objectProp('queryKey', listKeyExpr)])], + ), + ), + ]), ); body.push( @@ -562,23 +832,33 @@ export function generateDeleteMutationHook( mutationFnExpr, [ objectProp('onSuccess', onSuccessFn), - spreadObj(t.identifier('mutationOptions')) + spreadObj(t.identifier('mutationOptions')), ], - mutationKeyExpr - ) + mutationKeyExpr, + ), ); - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType)], + body, + ), + ); return { fileName: getDeleteMutationFileName(table), - content: generateHookFileCode(`Delete mutation hook for ${typeName}`, statements) + content: generateHookFileCode( + `Delete mutation hook for ${typeName}`, + statements, + ), }; } export function generateAllMutationHooks( tables: CleanTable[], - options: MutationGeneratorOptions = {} + options: MutationGeneratorOptions = {}, ): GeneratedMutationFile[] { const files: GeneratedMutationFile[] = []; diff --git a/graphql/codegen/src/core/codegen/orm/barrel.ts b/graphql/codegen/src/core/codegen/orm/barrel.ts index b1a9e63a0..bc90b0445 100644 --- a/graphql/codegen/src/core/codegen/orm/barrel.ts +++ b/graphql/codegen/src/core/codegen/orm/barrel.ts @@ -7,7 +7,7 @@ import * as t from '@babel/types'; import type { CleanTable } from '../../../types/schema'; import { generateCode } from '../babel-ast'; -import { getGeneratedFileHeader,getTableNames, lcFirst } from '../utils'; +import { getGeneratedFileHeader, getTableNames, lcFirst } from '../utils'; export interface GeneratedBarrelFile { fileName: string; @@ -17,7 +17,9 @@ export interface GeneratedBarrelFile { /** * Generate the models/index.ts barrel file */ -export function generateModelsBarrel(tables: CleanTable[]): GeneratedBarrelFile { +export function generateModelsBarrel( + tables: CleanTable[], +): GeneratedBarrelFile { const statements: t.Statement[] = []; // Export all model classes (Select types are now in input-types.ts) @@ -26,13 +28,14 @@ export function generateModelsBarrel(tables: CleanTable[]): GeneratedBarrelFile const modelName = `${typeName}Model`; // Use same naming logic as model-generator to avoid "index.ts" clash with barrel file const baseFileName = lcFirst(typeName); - const moduleFileName = baseFileName === 'index' ? `${baseFileName}Model` : baseFileName; + const moduleFileName = + baseFileName === 'index' ? `${baseFileName}Model` : baseFileName; // Create: export { ModelName } from './moduleName'; const exportDecl = t.exportNamedDeclaration( null, [t.exportSpecifier(t.identifier(modelName), t.identifier(modelName))], - t.stringLiteral(`./${moduleFileName}`) + t.stringLiteral(`./${moduleFileName}`), ); statements.push(exportDecl); } @@ -42,14 +45,16 @@ export function generateModelsBarrel(tables: CleanTable[]): GeneratedBarrelFile return { fileName: 'models/index.ts', - content: header + '\n' + code + content: header + '\n' + code, }; } /** * Generate the types.ts file that re-exports all types */ -export function generateTypesBarrel(_useSharedTypes: boolean): GeneratedBarrelFile { +export function generateTypesBarrel( + _useSharedTypes: boolean, +): GeneratedBarrelFile { // Always re-export from input-types since that's where all types are generated const content = `/** * Types re-export @@ -63,6 +68,6 @@ export * from './input-types'; return { fileName: 'types.ts', - content + content, }; } diff --git a/graphql/codegen/src/core/codegen/orm/client-generator.ts b/graphql/codegen/src/core/codegen/orm/client-generator.ts index 293f0be52..822399f7e 100644 --- a/graphql/codegen/src/core/codegen/orm/client-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/client-generator.ts @@ -8,8 +8,8 @@ import * as fs from 'fs'; import * as path from 'path'; import type { CleanTable } from '../../../types/schema'; -import { commentBlock,generateCode } from '../babel-ast'; -import { getGeneratedFileHeader,getTableNames, lcFirst } from '../utils'; +import { commentBlock, generateCode } from '../babel-ast'; +import { getGeneratedFileHeader, getTableNames, lcFirst } from '../utils'; export interface GeneratedClientFile { fileName: string; @@ -29,7 +29,7 @@ function findTemplateFile(templateName: string): string { throw new Error( `Could not find template file: ${templateName}. ` + - `Searched in: ${templatePath}` + `Searched in: ${templatePath}`, ); } @@ -47,7 +47,7 @@ function readTemplateFile(templateName: string, description: string): string { content = content.replace( headerPattern, - getGeneratedFileHeader(description) + '\n' + getGeneratedFileHeader(description) + '\n', ); return content; @@ -62,7 +62,10 @@ function readTemplateFile(templateName: string, description: string): string { export function generateOrmClientFile(): GeneratedClientFile { return { fileName: 'client.ts', - content: readTemplateFile('orm-client.ts', 'ORM Client - Runtime GraphQL executor') + content: readTemplateFile( + 'orm-client.ts', + 'ORM Client - Runtime GraphQL executor', + ), }; } @@ -76,8 +79,8 @@ export function generateQueryBuilderFile(): GeneratedClientFile { fileName: 'query-builder.ts', content: readTemplateFile( 'query-builder.ts', - 'Query Builder - Builds and executes GraphQL operations' - ) + 'Query Builder - Builds and executes GraphQL operations', + ), }; } @@ -89,21 +92,24 @@ export function generateQueryBuilderFile(): GeneratedClientFile { export function generateSelectTypesFile(): GeneratedClientFile { return { fileName: 'select-types.ts', - content: readTemplateFile('select-types.ts', 'Type utilities for select inference') + content: readTemplateFile( + 'select-types.ts', + 'Type utilities for select inference', + ), }; } function createImportDeclaration( moduleSpecifier: string, namedImports: string[], - typeOnly: boolean = false + typeOnly: boolean = false, ): t.ImportDeclaration { const specifiers = namedImports.map((name) => - t.importSpecifier(t.identifier(name), t.identifier(name)) + t.importSpecifier(t.identifier(name), t.identifier(name)), ); const decl = t.importDeclaration( specifiers, - t.stringLiteral(moduleSpecifier) + t.stringLiteral(moduleSpecifier), ); decl.importKind = typeOnly ? 'type' : 'value'; return decl; @@ -115,7 +121,7 @@ function createImportDeclaration( export function generateCreateClientFile( tables: CleanTable[], hasCustomQueries: boolean, - hasCustomMutations: boolean + hasCustomMutations: boolean, ): GeneratedClientFile { const statements: t.Statement[] = []; @@ -123,7 +129,7 @@ export function generateCreateClientFile( // Import OrmClient (value) and OrmClientConfig (type) separately statements.push(createImportDeclaration('./client', ['OrmClient'])); statements.push( - createImportDeclaration('./client', ['OrmClientConfig'], true) + createImportDeclaration('./client', ['OrmClientConfig'], true), ); // Import models @@ -132,19 +138,19 @@ export function generateCreateClientFile( const modelName = `${typeName}Model`; const fileName = lcFirst(typeName); statements.push( - createImportDeclaration(`./models/${fileName}`, [modelName]) + createImportDeclaration(`./models/${fileName}`, [modelName]), ); } // Import custom operations if (hasCustomQueries) { statements.push( - createImportDeclaration('./query', ['createQueryOperations']) + createImportDeclaration('./query', ['createQueryOperations']), ); } if (hasCustomMutations) { statements.push( - createImportDeclaration('./mutation', ['createMutationOperations']) + createImportDeclaration('./mutation', ['createMutationOperations']), ); } @@ -155,22 +161,22 @@ export function generateCreateClientFile( [ t.exportSpecifier( t.identifier('OrmClientConfig'), - t.identifier('OrmClientConfig') + t.identifier('OrmClientConfig'), ), t.exportSpecifier( t.identifier('QueryResult'), - t.identifier('QueryResult') + t.identifier('QueryResult'), ), t.exportSpecifier( t.identifier('GraphQLError'), - t.identifier('GraphQLError') + t.identifier('GraphQLError'), ), t.exportSpecifier( t.identifier('GraphQLAdapter'), - t.identifier('GraphQLAdapter') - ) + t.identifier('GraphQLAdapter'), + ), ], - t.stringLiteral('./client') + t.stringLiteral('./client'), ); typeExportDecl.exportKind = 'type'; statements.push(typeExportDecl); @@ -182,11 +188,11 @@ export function generateCreateClientFile( [ t.exportSpecifier( t.identifier('GraphQLRequestError'), - t.identifier('GraphQLRequestError') - ) + t.identifier('GraphQLRequestError'), + ), ], - t.stringLiteral('./client') - ) + t.stringLiteral('./client'), + ), ); // export { QueryBuilder } from './query-builder'; @@ -196,11 +202,11 @@ export function generateCreateClientFile( [ t.exportSpecifier( t.identifier('QueryBuilder'), - t.identifier('QueryBuilder') - ) + t.identifier('QueryBuilder'), + ), ], - t.stringLiteral('./query-builder') - ) + t.stringLiteral('./query-builder'), + ), ); // export * from './select-types'; @@ -217,11 +223,11 @@ export function generateCreateClientFile( [ t.exportSpecifier( t.identifier('createQueryOperations'), - t.identifier('createQueryOperations') - ) + t.identifier('createQueryOperations'), + ), ], - t.stringLiteral('./query') - ) + t.stringLiteral('./query'), + ), ); } if (hasCustomMutations) { @@ -231,11 +237,11 @@ export function generateCreateClientFile( [ t.exportSpecifier( t.identifier('createMutationOperations'), - t.identifier('createMutationOperations') - ) + t.identifier('createMutationOperations'), + ), ], - t.stringLiteral('./mutation') - ) + t.stringLiteral('./mutation'), + ), ); } @@ -248,8 +254,8 @@ export function generateCreateClientFile( returnProperties.push( t.objectProperty( t.identifier(singularName), - t.newExpression(t.identifier(modelName), [t.identifier('client')]) - ) + t.newExpression(t.identifier(modelName), [t.identifier('client')]), + ), ); } @@ -258,9 +264,9 @@ export function generateCreateClientFile( t.objectProperty( t.identifier('query'), t.callExpression(t.identifier('createQueryOperations'), [ - t.identifier('client') - ]) - ) + t.identifier('client'), + ]), + ), ); } @@ -269,9 +275,9 @@ export function generateCreateClientFile( t.objectProperty( t.identifier('mutation'), t.callExpression(t.identifier('createMutationOperations'), [ - t.identifier('client') - ]) - ) + t.identifier('client'), + ]), + ), ); } @@ -279,21 +285,21 @@ export function generateCreateClientFile( const clientDecl = t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('client'), - t.newExpression(t.identifier('OrmClient'), [t.identifier('config')]) - ) + t.newExpression(t.identifier('OrmClient'), [t.identifier('config')]), + ), ]); const returnStmt = t.returnStatement(t.objectExpression(returnProperties)); const configParam = t.identifier('config'); configParam.typeAnnotation = t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('OrmClientConfig')) + t.tsTypeReference(t.identifier('OrmClientConfig')), ); const createClientFunc = t.functionDeclaration( t.identifier('createClient'), [configParam], - t.blockStatement([clientDecl, returnStmt]) + t.blockStatement([clientDecl, returnStmt]), ); // Add JSDoc comment @@ -330,6 +336,6 @@ export function generateCreateClientFile( return { fileName: 'index.ts', - content: header + '\n' + code + content: header + '\n' + code, }; } diff --git a/graphql/codegen/src/core/codegen/orm/client.ts b/graphql/codegen/src/core/codegen/orm/client.ts index d217115f0..194f5f9de 100644 --- a/graphql/codegen/src/core/codegen/orm/client.ts +++ b/graphql/codegen/src/core/codegen/orm/client.ts @@ -26,7 +26,7 @@ export type QueryResult = export interface GraphQLAdapter { execute( document: string, - variables?: Record + variables?: Record, ): Promise>; setHeaders?(headers: Record): void; getEndpoint?(): string; @@ -40,33 +40,35 @@ export class FetchAdapter implements GraphQLAdapter { constructor( private endpoint: string, - headers?: Record + headers?: Record, ) { this.headers = headers ?? {}; } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { const response = await fetch(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers + ...this.headers, }, body: JSON.stringify({ query: document, - variables: variables ?? {} - }) + variables: variables ?? {}, + }), }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }] + errors: [ + { message: `HTTP ${response.status}: ${response.statusText}` }, + ], }; } @@ -79,14 +81,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors + errors: json.errors, }; } return { ok: true, data: json.data as T, - errors: undefined + errors: undefined, }; } @@ -114,7 +116,7 @@ export interface OrmClientConfig { export class GraphQLRequestError extends Error { constructor( public readonly errors: GraphQLError[], - public readonly data: unknown = null + public readonly data: unknown = null, ) { const messages = errors.map((e) => e.message).join('; '); super(`GraphQL Error: ${messages}`); @@ -132,14 +134,14 @@ export class OrmClient { this.adapter = new FetchAdapter(config.endpoint, config.headers); } else { throw new Error( - 'OrmClientConfig requires either an endpoint or a custom adapter' + 'OrmClientConfig requires either an endpoint or a custom adapter', ); } } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { return this.adapter.execute(document, variables); } diff --git a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts index beec9d54e..c170e9738 100644 --- a/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/custom-ops-generator.ts @@ -12,9 +12,9 @@ import { NON_SELECT_TYPES, getSelectTypeName } from '../select-helpers'; import { getTypeBaseName, isTypeRequired, - typeRefToTsType + typeRefToTsType, } from '../type-resolver'; -import { getGeneratedFileHeader,ucFirst } from '../utils'; +import { getGeneratedFileHeader, ucFirst } from '../utils'; export interface GeneratedCustomOpsFile { fileName: string; @@ -51,7 +51,7 @@ function collectInputTypeNamesFromOps(operations: CleanOperation[]): string[] { * Filters out scalar types */ function collectPayloadTypeNamesFromOps( - operations: CleanOperation[] + operations: CleanOperation[], ): string[] { const payloadTypes = new Set(); @@ -73,9 +73,7 @@ function collectPayloadTypeNamesFromOps( * Collect Connection and other non-scalar return type names that need importing * (for typing QueryBuilder results on scalar/Connection operations) */ -function collectRawReturnTypeNames( - operations: CleanOperation[] -): string[] { +function collectRawReturnTypeNames(operations: CleanOperation[]): string[] { const types = new Set(); for (const op of operations) { @@ -95,18 +93,21 @@ function collectRawReturnTypeNames( function createImportDeclaration( moduleSpecifier: string, namedImports: string[], - typeOnly: boolean = false + typeOnly: boolean = false, ): t.ImportDeclaration { const specifiers = namedImports.map((name) => - t.importSpecifier(t.identifier(name), t.identifier(name)) + t.importSpecifier(t.identifier(name), t.identifier(name)), + ); + const decl = t.importDeclaration( + specifiers, + t.stringLiteral(moduleSpecifier), ); - const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier)); decl.importKind = typeOnly ? 'type' : 'value'; return decl; } function createVariablesInterface( - op: CleanOperation + op: CleanOperation, ): t.ExportNamedDeclaration | null { if (op.args.length === 0) return null; @@ -115,7 +116,7 @@ function createVariablesInterface( const optional = !isTypeRequired(arg.type); const prop = t.tsPropertySignature( t.identifier(arg.name), - t.tsTypeAnnotation(parseTypeAnnotation(typeRefToTsType(arg.type))) + t.tsTypeAnnotation(parseTypeAnnotation(typeRefToTsType(arg.type))), ); prop.optional = optional; return prop; @@ -125,7 +126,7 @@ function createVariablesInterface( t.identifier(varTypeName), null, null, - t.tsInterfaceBody(props) + t.tsInterfaceBody(props), ); return t.exportNamedDeclaration(interfaceDecl); } @@ -139,7 +140,9 @@ function parseTypeAnnotation(typeStr: string): t.TSType { if (typeStr === 'unknown') return t.tsUnknownKeyword(); if (typeStr.includes(' | ')) { - const parts = typeStr.split(' | ').map((p) => parseTypeAnnotation(p.trim())); + const parts = typeStr + .split(' | ') + .map((p) => parseTypeAnnotation(p.trim())); return t.tsUnionType(parts); } @@ -152,15 +155,21 @@ function parseTypeAnnotation(typeStr: string): t.TSType { function buildSelectedResultTsType( typeRef: CleanArgument['type'], - payloadTypeName: string + payloadTypeName: string, ): t.TSType { if (typeRef.kind === 'NON_NULL' && typeRef.ofType) { - return buildSelectedResultTsType(typeRef.ofType as CleanArgument['type'], payloadTypeName); + return buildSelectedResultTsType( + typeRef.ofType as CleanArgument['type'], + payloadTypeName, + ); } if (typeRef.kind === 'LIST' && typeRef.ofType) { return t.tsArrayType( - buildSelectedResultTsType(typeRef.ofType as CleanArgument['type'], payloadTypeName) + buildSelectedResultTsType( + typeRef.ofType as CleanArgument['type'], + payloadTypeName, + ), ); } @@ -168,20 +177,20 @@ function buildSelectedResultTsType( t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier(payloadTypeName)), - t.tsTypeReference(t.identifier('S')) - ]) + t.tsTypeReference(t.identifier('S')), + ]), ); } function buildOperationMethod( op: CleanOperation, - operationType: 'query' | 'mutation' + operationType: 'query' | 'mutation', ): t.ObjectProperty { const hasArgs = op.args.length > 0; const varTypeName = `${ucFirst(op.name)}Variables`; const varDefs = op.args.map((arg) => ({ name: arg.name, - type: formatGraphQLType(arg.type) + type: formatGraphQLType(arg.type), })); const selectTypeName = getSelectTypeName(op.returnType); @@ -192,7 +201,9 @@ function buildOperationMethod( if (hasArgs) { const argsParam = t.identifier('args'); - argsParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier(varTypeName))); + argsParam.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeReference(t.identifier(varTypeName)), + ); params.push(argsParam); } @@ -201,7 +212,7 @@ function buildOperationMethod( if (selectTypeName) { const selectProp = t.tsPropertySignature( t.identifier('select'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))), ); selectProp.optional = false; optionsParam.typeAnnotation = t.tsTypeAnnotation( @@ -211,10 +222,10 @@ function buildOperationMethod( t.identifier('StrictSelect'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ]) - ) - ]) + t.tsTypeReference(t.identifier(selectTypeName)), + ]), + ), + ]), ); } else { optionsParam.typeAnnotation = t.tsTypeAnnotation( @@ -225,14 +236,17 @@ function buildOperationMethod( t.tsTypeAnnotation( t.tsTypeReference( t.identifier('Record'), - t.tsTypeParameterInstantiation([t.tsStringKeyword(), t.tsUnknownKeyword()]) - ) - ) + t.tsTypeParameterInstantiation([ + t.tsStringKeyword(), + t.tsUnknownKeyword(), + ]), + ), + ), ); prop.optional = true; return prop; - })() - ]) + })(), + ]), ); } params.push(optionsParam); @@ -240,15 +254,29 @@ function buildOperationMethod( // Build the QueryBuilder call const selectExpr = selectTypeName ? t.memberExpression(t.identifier('options'), t.identifier('select')) - : t.optionalMemberExpression(t.identifier('options'), t.identifier('select'), false, true); - const entityTypeExpr = selectTypeName && payloadTypeName - ? t.stringLiteral(payloadTypeName) - : t.identifier('undefined'); + : t.optionalMemberExpression( + t.identifier('options'), + t.identifier('select'), + false, + true, + ); + const entityTypeExpr = + selectTypeName && payloadTypeName + ? t.stringLiteral(payloadTypeName) + : t.identifier('undefined'); const queryBuilderArgs = t.objectExpression([ - t.objectProperty(t.identifier('client'), t.identifier('client'), false, true), + t.objectProperty( + t.identifier('client'), + t.identifier('client'), + false, + true, + ), t.objectProperty(t.identifier('operation'), t.stringLiteral(operationType)), - t.objectProperty(t.identifier('operationName'), t.stringLiteral(ucFirst(op.name))), + t.objectProperty( + t.identifier('operationName'), + t.stringLiteral(ucFirst(op.name)), + ), t.objectProperty(t.identifier('fieldName'), t.stringLiteral(op.name)), t.spreadElement( t.callExpression(t.identifier('buildCustomDocument'), [ @@ -261,17 +289,19 @@ function buildOperationMethod( varDefs.map((v) => t.objectExpression([ t.objectProperty(t.identifier('name'), t.stringLiteral(v.name)), - t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)) - ]) - ) + t.objectProperty(t.identifier('type'), t.stringLiteral(v.type)), + ]), + ), ), t.identifier('connectionFieldsMap'), - entityTypeExpr - ]) - ) + entityTypeExpr, + ]), + ), ]); - const newExpr = t.newExpression(t.identifier('QueryBuilder'), [queryBuilderArgs]); + const newExpr = t.newExpression(t.identifier('QueryBuilder'), [ + queryBuilderArgs, + ]); // Add type parameter to QueryBuilder for typed .unwrap() results if (selectTypeName && payloadTypeName) { @@ -281,10 +311,10 @@ function buildOperationMethod( t.tsPropertySignature( t.identifier(op.name), t.tsTypeAnnotation( - buildSelectedResultTsType(op.returnType, payloadTypeName) - ) - ) - ]) + buildSelectedResultTsType(op.returnType, payloadTypeName), + ), + ), + ]), ]); } else { // Scalar/Connection type: use raw TS type directly @@ -293,9 +323,9 @@ function buildOperationMethod( t.tsTypeLiteral([ t.tsPropertySignature( t.identifier(op.name), - t.tsTypeAnnotation(parseTypeAnnotation(rawTsType)) - ) - ]) + t.tsTypeAnnotation(parseTypeAnnotation(rawTsType)), + ), + ]), ]); } @@ -306,7 +336,7 @@ function buildOperationMethod( const typeParam = t.tsTypeParameter( t.tsTypeReference(t.identifier(selectTypeName)), null, - 'S' + 'S', ); arrowFunc.typeParameters = t.tsTypeParameterDeclaration([typeParam]); } @@ -318,7 +348,7 @@ function buildOperationMethod( * Generate the query/index.ts file for custom query operations */ export function generateCustomQueryOpsFile( - operations: CleanOperation[] + operations: CleanOperation[], ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -327,17 +357,39 @@ export function generateCustomQueryOpsFile( const payloadTypeNames = collectPayloadTypeNamesFromOps(operations); const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`); const rawReturnTypeNames = collectRawReturnTypeNames(operations); - const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames, ...rawReturnTypeNames])]; + const allTypeImports = [ + ...new Set([ + ...inputTypeNames, + ...payloadTypeNames, + ...selectTypeNames, + ...rawReturnTypeNames, + ]), + ]; // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); - statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument'])); - statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration('../query-builder', [ + 'QueryBuilder', + 'buildCustomDocument', + ]), + ); + statements.push( + createImportDeclaration( + '../select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); if (allTypeImports.length > 0) { - statements.push(createImportDeclaration('../input-types', allTypeImports, true)); + statements.push( + createImportDeclaration('../input-types', allTypeImports, true), + ); } - statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); + statements.push( + createImportDeclaration('../input-types', ['connectionFieldsMap']), + ); // Generate variable interfaces for (const op of operations) { @@ -347,19 +399,21 @@ export function generateCustomQueryOpsFile( // Generate factory function const operationProperties = operations.map((op) => - buildOperationMethod(op, 'query') + buildOperationMethod(op, 'query'), ); const returnObj = t.objectExpression(operationProperties); const returnStmt = t.returnStatement(returnObj); const clientParam = t.identifier('client'); - clientParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('OrmClient'))); + clientParam.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('OrmClient')), + ); const factoryFunc = t.functionDeclaration( t.identifier('createQueryOperations'), [clientParam], - t.blockStatement([returnStmt]) + t.blockStatement([returnStmt]), ); statements.push(t.exportNamedDeclaration(factoryFunc)); @@ -368,7 +422,7 @@ export function generateCustomQueryOpsFile( return { fileName: 'query/index.ts', - content: header + '\n' + code + content: header + '\n' + code, }; } @@ -376,7 +430,7 @@ export function generateCustomQueryOpsFile( * Generate the mutation/index.ts file for custom mutation operations */ export function generateCustomMutationOpsFile( - operations: CleanOperation[] + operations: CleanOperation[], ): GeneratedCustomOpsFile { const statements: t.Statement[] = []; @@ -385,17 +439,39 @@ export function generateCustomMutationOpsFile( const payloadTypeNames = collectPayloadTypeNamesFromOps(operations); const selectTypeNames = payloadTypeNames.map((p) => `${p}Select`); const rawReturnTypeNames = collectRawReturnTypeNames(operations); - const allTypeImports = [...new Set([...inputTypeNames, ...payloadTypeNames, ...selectTypeNames, ...rawReturnTypeNames])]; + const allTypeImports = [ + ...new Set([ + ...inputTypeNames, + ...payloadTypeNames, + ...selectTypeNames, + ...rawReturnTypeNames, + ]), + ]; // Add imports statements.push(createImportDeclaration('../client', ['OrmClient'])); - statements.push(createImportDeclaration('../query-builder', ['QueryBuilder', 'buildCustomDocument'])); - statements.push(createImportDeclaration('../select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration('../query-builder', [ + 'QueryBuilder', + 'buildCustomDocument', + ]), + ); + statements.push( + createImportDeclaration( + '../select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); if (allTypeImports.length > 0) { - statements.push(createImportDeclaration('../input-types', allTypeImports, true)); + statements.push( + createImportDeclaration('../input-types', allTypeImports, true), + ); } - statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); + statements.push( + createImportDeclaration('../input-types', ['connectionFieldsMap']), + ); // Generate variable interfaces for (const op of operations) { @@ -405,19 +481,21 @@ export function generateCustomMutationOpsFile( // Generate factory function const operationProperties = operations.map((op) => - buildOperationMethod(op, 'mutation') + buildOperationMethod(op, 'mutation'), ); const returnObj = t.objectExpression(operationProperties); const returnStmt = t.returnStatement(returnObj); const clientParam = t.identifier('client'); - clientParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('OrmClient'))); + clientParam.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('OrmClient')), + ); const factoryFunc = t.functionDeclaration( t.identifier('createMutationOperations'), [clientParam], - t.blockStatement([returnStmt]) + t.blockStatement([returnStmt]), ); statements.push(t.exportNamedDeclaration(factoryFunc)); @@ -426,7 +504,7 @@ export function generateCustomMutationOpsFile( return { fileName: 'mutation/index.ts', - content: header + '\n' + code + content: header + '\n' + code, }; } diff --git a/graphql/codegen/src/core/codegen/orm/index.ts b/graphql/codegen/src/core/codegen/orm/index.ts index 3ff30cb62..489607f56 100644 --- a/graphql/codegen/src/core/codegen/orm/index.ts +++ b/graphql/codegen/src/core/codegen/orm/index.ts @@ -5,19 +5,27 @@ * and produces the complete ORM client output. */ import type { GraphQLSDKConfigTarget } from '../../../types/config'; -import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema'; +import type { + CleanOperation, + CleanTable, + TypeRegistry, +} from '../../../types/schema'; import { generateModelsBarrel, generateTypesBarrel } from './barrel'; import { generateCreateClientFile, generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile + generateSelectTypesFile, } from './client-generator'; import { generateCustomMutationOpsFile, - generateCustomQueryOpsFile + generateCustomQueryOpsFile, } from './custom-ops-generator'; -import { collectInputTypeNames, collectPayloadTypeNames,generateInputTypesFile } from './input-types-generator'; +import { + collectInputTypeNames, + collectPayloadTypeNames, + generateInputTypesFile, +} from './input-types-generator'; import { generateAllModelFiles } from './model-generator'; export interface GeneratedFile { @@ -70,17 +78,23 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { files.push({ path: clientFile.fileName, content: clientFile.content }); const queryBuilderFile = generateQueryBuilderFile(); - files.push({ path: queryBuilderFile.fileName, content: queryBuilderFile.content }); + files.push({ + path: queryBuilderFile.fileName, + content: queryBuilderFile.content, + }); const selectTypesFile = generateSelectTypesFile(); - files.push({ path: selectTypesFile.fileName, content: selectTypesFile.content }); + files.push({ + path: selectTypesFile.fileName, + content: selectTypesFile.content, + }); // 2. Generate model files const modelFiles = generateAllModelFiles(tables, useSharedTypes); for (const modelFile of modelFiles) { files.push({ path: `models/${modelFile.fileName}`, - content: modelFile.content + content: modelFile.content, }); } @@ -90,10 +104,13 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { // 4. Generate comprehensive input types (entities, filters, orderBy, CRUD inputs, custom inputs, payload types) // Always generate if we have tables or custom operations - if (tables.length > 0 || (typeRegistry && (hasCustomQueries || hasCustomMutations))) { + if ( + tables.length > 0 || + (typeRegistry && (hasCustomQueries || hasCustomMutations)) + ) { const allOps = [ ...(customOperations?.queries ?? []), - ...(customOperations?.mutations ?? []) + ...(customOperations?.mutations ?? []), ]; const usedInputTypes = collectInputTypeNames(allOps); const usedPayloadTypes = collectPayloadTypeNames(allOps); @@ -106,7 +123,7 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { const crudPayloadTypes = [ `Create${typeName}Payload`, `Update${typeName}Payload`, - `Delete${typeName}Payload` + `Delete${typeName}Payload`, ]; for (const payloadType of crudPayloadTypes) { if (typeRegistry.has(payloadType)) { @@ -120,24 +137,28 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { typeRegistry ?? new Map(), usedInputTypes, tables, - usedPayloadTypes + usedPayloadTypes, ); - files.push({ path: inputTypesFile.fileName, content: inputTypesFile.content }); + files.push({ + path: inputTypesFile.fileName, + content: inputTypesFile.content, + }); } // 5. Generate custom operations (if any) if (hasCustomQueries && customOperations?.queries) { - const queryOpsFile = generateCustomQueryOpsFile( - customOperations.queries - ); + const queryOpsFile = generateCustomQueryOpsFile(customOperations.queries); files.push({ path: queryOpsFile.fileName, content: queryOpsFile.content }); } if (hasCustomMutations && customOperations?.mutations) { const mutationOpsFile = generateCustomMutationOpsFile( - customOperations.mutations + customOperations.mutations, ); - files.push({ path: mutationOpsFile.fileName, content: mutationOpsFile.content }); + files.push({ + path: mutationOpsFile.fileName, + content: mutationOpsFile.content, + }); } // 6. Generate types barrel @@ -145,7 +166,11 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { files.push({ path: typesBarrel.fileName, content: typesBarrel.content }); // 7. Generate main index.ts with createClient - const indexFile = generateCreateClientFile(tables, hasCustomQueries, hasCustomMutations); + const indexFile = generateCreateClientFile( + tables, + hasCustomQueries, + hasCustomMutations, + ); files.push({ path: indexFile.fileName, content: indexFile.content }); return { @@ -154,8 +179,8 @@ export function generateOrm(options: GenerateOrmOptions): GenerateOrmResult { tables: tables.length, customQueries: customOperations?.queries.length ?? 0, customMutations: customOperations?.mutations.length ?? 0, - totalFiles: files.length - } + totalFiles: files.length, + }, }; } @@ -164,7 +189,10 @@ export { generateModelsBarrel, generateTypesBarrel } from './barrel'; export { generateOrmClientFile, generateQueryBuilderFile, - generateSelectTypesFile + generateSelectTypesFile, } from './client-generator'; -export { generateCustomMutationOpsFile,generateCustomQueryOpsFile } from './custom-ops-generator'; -export { generateAllModelFiles,generateModelFile } from './model-generator'; +export { + generateCustomMutationOpsFile, + generateCustomQueryOpsFile, +} from './custom-ops-generator'; +export { generateAllModelFiles, generateModelFile } from './model-generator'; diff --git a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts index b4cb1bfe4..3334a5b11 100644 --- a/graphql/codegen/src/core/codegen/orm/input-types-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/input-types-generator.ts @@ -16,10 +16,10 @@ import { pluralize } from 'inflekt'; import type { CleanArgument, CleanTable, - TypeRegistry + TypeRegistry, } from '../../../types/schema'; -import { addLineComment,generateCode } from '../babel-ast'; -import { scalarToFilterType,scalarToTsType } from '../scalars'; +import { addLineComment, generateCode } from '../babel-ast'; +import { scalarToFilterType, scalarToTsType } from '../scalars'; import { getTypeBaseName } from '../type-resolver'; import { getConditionTypeName, @@ -28,7 +28,7 @@ import { getOrderByTypeName, getPrimaryKeyInfo, getTableNames, - isRelationField + isRelationField, } from '../utils'; export interface GeneratedInputTypesFile { @@ -45,7 +45,7 @@ const EXCLUDED_MUTATION_FIELDS = [ 'id', 'createdAt', 'updatedAt', - 'nodeId' + 'nodeId', ] as const; // ============================================================================ @@ -56,7 +56,7 @@ const EXCLUDED_MUTATION_FIELDS = [ * Overrides for input-type generation */ const INPUT_SCALAR_OVERRIDES: Record = { - JSON: 'Record' + JSON: 'Record', }; /** @@ -65,7 +65,7 @@ const INPUT_SCALAR_OVERRIDES: Record = { function scalarToInputTs(scalar: string): string { return scalarToTsType(scalar, { unknownScalar: 'name', - overrides: INPUT_SCALAR_OVERRIDES + overrides: INPUT_SCALAR_OVERRIDES, }); } @@ -124,28 +124,30 @@ function parseTypeString(typeStr: string): t.TSType { const match = typeStr.match(/^([^<]+)<(.+)>$/); if (match) { const [, baseName, params] = match; - const typeParams = params.split(',').map((p) => parseTypeString(p.trim())); + const typeParams = params + .split(',') + .map((p) => parseTypeString(p.trim())); return t.tsTypeReference( t.identifier(baseName), - t.tsTypeParameterInstantiation(typeParams) + t.tsTypeParameterInstantiation(typeParams), ); } } // Handle primitive types switch (typeStr) { - case 'string': - return t.tsStringKeyword(); - case 'number': - return t.tsNumberKeyword(); - case 'boolean': - return t.tsBooleanKeyword(); - case 'null': - return t.tsNullKeyword(); - case 'unknown': - return t.tsUnknownKeyword(); - default: - return t.tsTypeReference(t.identifier(typeStr)); + case 'string': + return t.tsStringKeyword(); + case 'number': + return t.tsNumberKeyword(); + case 'boolean': + return t.tsBooleanKeyword(); + case 'null': + return t.tsNullKeyword(); + case 'unknown': + return t.tsUnknownKeyword(); + default: + return t.tsTypeReference(t.identifier(typeStr)); } } @@ -155,11 +157,11 @@ function parseTypeString(typeStr: string): t.TSType { function createPropertySignature( name: string, typeStr: string, - optional: boolean + optional: boolean, ): t.TSPropertySignature { const prop = t.tsPropertySignature( t.identifier(name), - t.tsTypeAnnotation(parseTypeString(typeStr)) + t.tsTypeAnnotation(parseTypeString(typeStr)), ); prop.optional = optional; return prop; @@ -170,17 +172,17 @@ function createPropertySignature( */ function createExportedInterface( name: string, - properties: Array<{ name: string; type: string; optional: boolean }> + properties: Array<{ name: string; type: string; optional: boolean }>, ): t.ExportNamedDeclaration { const props = properties.map((p) => - createPropertySignature(p.name, p.type, p.optional) + createPropertySignature(p.name, p.type, p.optional), ); const body = t.tsInterfaceBody(props); const interfaceDecl = t.tsInterfaceDeclaration( t.identifier(name), null, null, - body + body, ); return t.exportNamedDeclaration(interfaceDecl); } @@ -190,12 +192,12 @@ function createExportedInterface( */ function createExportedTypeAlias( name: string, - typeStr: string + typeStr: string, ): t.ExportNamedDeclaration { const typeAlias = t.tsTypeAliasDeclaration( t.identifier(name), null, - parseTypeString(typeStr) + parseTypeString(typeStr), ); return t.exportNamedDeclaration(typeAlias); } @@ -204,9 +206,7 @@ function createExportedTypeAlias( * Create a union type from string literals */ function createStringLiteralUnion(values: string[]): t.TSUnionType { - return t.tsUnionType( - values.map((v) => t.tsLiteralType(t.stringLiteral(v))) - ); + return t.tsUnionType(values.map((v) => t.tsLiteralType(t.stringLiteral(v)))); } /** @@ -214,7 +214,7 @@ function createStringLiteralUnion(values: string[]): t.TSUnionType { */ function addSectionComment( statements: t.Statement[], - sectionName: string + sectionName: string, ): void { if (statements.length > 0) { addLineComment(statements[0], `============ ${sectionName} ============`); @@ -258,79 +258,79 @@ const SCALAR_FILTER_CONFIGS: ScalarFilterConfig[] = [ { name: 'StringFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'] + operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'], }, { name: 'IntFilter', tsType: 'number', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'FloatFilter', tsType: 'number', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'BooleanFilter', tsType: 'boolean', operators: ['equality'] }, { name: 'UUIDFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray'] + operators: ['equality', 'distinct', 'inArray'], }, { name: 'DatetimeFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'DateFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'JSONFilter', tsType: 'Record', - operators: ['equality', 'distinct', 'json'] + operators: ['equality', 'distinct', 'json'], }, { name: 'BigIntFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'BigFloatFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison'] + operators: ['equality', 'distinct', 'inArray', 'comparison'], }, { name: 'BitStringFilter', tsType: 'string', operators: ['equality'] }, { name: 'InternetAddressFilter', tsType: 'string', - operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'] + operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'], }, { name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] }, // List filters (for array fields like string[], int[], uuid[]) { name: 'StringListFilter', tsType: 'string[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'] + operators: ['equality', 'distinct', 'comparison', 'listArray'], }, { name: 'IntListFilter', tsType: 'number[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'] + operators: ['equality', 'distinct', 'comparison', 'listArray'], }, { name: 'UUIDListFilter', tsType: 'string[]', - operators: ['equality', 'distinct', 'comparison', 'listArray'] - } + operators: ['equality', 'distinct', 'comparison', 'listArray'], + }, ]; /** * Build filter properties based on operator sets */ function buildScalarFilterProperties( - config: ScalarFilterConfig + config: ScalarFilterConfig, ): InterfaceProperty[] { const { tsType, operators } = config; const props: InterfaceProperty[] = []; @@ -340,7 +340,7 @@ function buildScalarFilterProperties( props.push( { name: 'isNull', type: 'boolean', optional: true }, { name: 'equalTo', type: tsType, optional: true }, - { name: 'notEqualTo', type: tsType, optional: true } + { name: 'notEqualTo', type: tsType, optional: true }, ); } @@ -348,7 +348,7 @@ function buildScalarFilterProperties( if (operators.includes('distinct')) { props.push( { name: 'distinctFrom', type: tsType, optional: true }, - { name: 'notDistinctFrom', type: tsType, optional: true } + { name: 'notDistinctFrom', type: tsType, optional: true }, ); } @@ -356,7 +356,7 @@ function buildScalarFilterProperties( if (operators.includes('inArray')) { props.push( { name: 'in', type: `${tsType}[]`, optional: true }, - { name: 'notIn', type: `${tsType}[]`, optional: true } + { name: 'notIn', type: `${tsType}[]`, optional: true }, ); } @@ -366,7 +366,7 @@ function buildScalarFilterProperties( { name: 'lessThan', type: tsType, optional: true }, { name: 'lessThanOrEqualTo', type: tsType, optional: true }, { name: 'greaterThan', type: tsType, optional: true }, - { name: 'greaterThanOrEqualTo', type: tsType, optional: true } + { name: 'greaterThanOrEqualTo', type: tsType, optional: true }, ); } @@ -388,7 +388,7 @@ function buildScalarFilterProperties( { name: 'like', type: 'string', optional: true }, { name: 'notLike', type: 'string', optional: true }, { name: 'likeInsensitive', type: 'string', optional: true }, - { name: 'notLikeInsensitive', type: 'string', optional: true } + { name: 'notLikeInsensitive', type: 'string', optional: true }, ); } @@ -399,7 +399,7 @@ function buildScalarFilterProperties( { name: 'containedBy', type: 'Record', optional: true }, { name: 'containsKey', type: 'string', optional: true }, { name: 'containsAllKeys', type: 'string[]', optional: true }, - { name: 'containsAnyKeys', type: 'string[]', optional: true } + { name: 'containsAnyKeys', type: 'string[]', optional: true }, ); } @@ -410,7 +410,7 @@ function buildScalarFilterProperties( { name: 'containsOrEqualTo', type: 'string', optional: true }, { name: 'containedBy', type: 'string', optional: true }, { name: 'containedByOrEqualTo', type: 'string', optional: true }, - { name: 'containsOrContainedBy', type: 'string', optional: true } + { name: 'containsOrContainedBy', type: 'string', optional: true }, ); } @@ -432,7 +432,7 @@ function buildScalarFilterProperties( { name: 'anyLessThan', type: baseType, optional: true }, { name: 'anyLessThanOrEqualTo', type: baseType, optional: true }, { name: 'anyGreaterThan', type: baseType, optional: true }, - { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true } + { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true }, ); } @@ -447,7 +447,7 @@ function generateScalarFilterTypes(): t.Statement[] { for (const config of SCALAR_FILTER_CONFIGS) { statements.push( - createExportedInterface(config.name, buildScalarFilterProperties(config)) + createExportedInterface(config.name, buildScalarFilterProperties(config)), ); } @@ -464,7 +464,7 @@ function generateScalarFilterTypes(): t.Statement[] { */ function isLikelyEnumType( typeName: string, - typeRegistry: TypeRegistry + typeRegistry: TypeRegistry, ): boolean { const typeInfo = typeRegistry.get(typeName); return typeInfo?.kind === 'ENUM'; @@ -475,7 +475,7 @@ function isLikelyEnumType( */ function collectEnumTypesFromTables( tables: CleanTable[], - typeRegistry: TypeRegistry + typeRegistry: TypeRegistry, ): Set { const enumTypes = new Set(); @@ -498,7 +498,7 @@ function collectEnumTypesFromTables( */ function generateEnumTypes( typeRegistry: TypeRegistry, - enumTypeNames: Set + enumTypeNames: Set, ): t.Statement[] { if (enumTypeNames.size === 0) return []; @@ -512,7 +512,7 @@ function generateEnumTypes( const typeAlias = t.tsTypeAliasDeclaration( t.identifier(typeName), null, - unionType + unionType, ); statements.push(t.exportNamedDeclaration(typeAlias)); } @@ -544,7 +544,7 @@ function buildEntityProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: isNullable ? `${tsType} | null` : tsType, - optional: isNullable + optional: isNullable, }); } @@ -560,7 +560,7 @@ function generateEntityTypes(tables: CleanTable[]): t.Statement[] { for (const table of tables) { const { typeName } = getTableNames(table); statements.push( - createExportedInterface(typeName, buildEntityProperties(table)) + createExportedInterface(typeName, buildEntityProperties(table)), ); } @@ -584,16 +584,14 @@ function generateRelationHelperTypes(): t.Statement[] { const connectionResultProps: t.TSPropertySignature[] = [ createPropertySignature('nodes', 'T[]', false), createPropertySignature('totalCount', 'number', false), - createPropertySignature('pageInfo', 'PageInfo', false) + createPropertySignature('pageInfo', 'PageInfo', false), ]; const connectionResultBody = t.tsInterfaceBody(connectionResultProps); const connectionResultDecl = t.tsInterfaceDeclaration( t.identifier('ConnectionResult'), - t.tsTypeParameterDeclaration([ - t.tsTypeParameter(null, null, 'T') - ]), + t.tsTypeParameterDeclaration([t.tsTypeParameter(null, null, 'T')]), null, - connectionResultBody + connectionResultBody, ); statements.push(t.exportNamedDeclaration(connectionResultDecl)); @@ -603,8 +601,8 @@ function generateRelationHelperTypes(): t.Statement[] { { name: 'hasNextPage', type: 'boolean', optional: false }, { name: 'hasPreviousPage', type: 'boolean', optional: false }, { name: 'startCursor', type: 'string | null', optional: true }, - { name: 'endCursor', type: 'string | null', optional: true } - ]) + { name: 'endCursor', type: 'string | null', optional: true }, + ]), ); addSectionComment(statements, 'Relation Helper Types'); @@ -617,7 +615,7 @@ function generateRelationHelperTypes(): t.Statement[] { function getRelatedTypeName( tableName: string, - tableByName: Map + tableByName: Map, ): string { const relatedTable = tableByName.get(tableName); return relatedTable ? getTableNames(relatedTable).typeName : tableName; @@ -625,7 +623,7 @@ function getRelatedTypeName( function getRelatedOrderByName( tableName: string, - tableByName: Map + tableByName: Map, ): string { const relatedTable = tableByName.get(tableName); if (relatedTable) { @@ -641,7 +639,7 @@ function getRelatedOrderByName( function getRelatedFilterName( tableName: string, - tableByName: Map + tableByName: Map, ): string { const relatedTable = tableByName.get(tableName); return relatedTable ? getFilterTypeName(relatedTable) : `${tableName}Filter`; @@ -652,7 +650,7 @@ function getRelatedFilterName( */ function buildEntityRelationProperties( table: CleanTable, - tableByName: Map + tableByName: Map, ): InterfaceProperty[] { const properties: InterfaceProperty[] = []; @@ -660,12 +658,12 @@ function buildEntityRelationProperties( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.referencesTable, - tableByName + tableByName, ); properties.push({ name: relation.fieldName, type: `${relatedTypeName} | null`, - optional: true + optional: true, }); } @@ -673,12 +671,12 @@ function buildEntityRelationProperties( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.referencedByTable, - tableByName + tableByName, ); properties.push({ name: relation.fieldName, type: `${relatedTypeName} | null`, - optional: true + optional: true, }); } @@ -686,12 +684,12 @@ function buildEntityRelationProperties( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.referencedByTable, - tableByName + tableByName, ); properties.push({ name: relation.fieldName, type: `ConnectionResult<${relatedTypeName}>`, - optional: true + optional: true, }); } @@ -699,12 +697,12 @@ function buildEntityRelationProperties( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.rightTable, - tableByName + tableByName, ); properties.push({ name: relation.fieldName, type: `ConnectionResult<${relatedTypeName}>`, - optional: true + optional: true, }); } @@ -716,7 +714,7 @@ function buildEntityRelationProperties( */ function generateEntityRelationTypes( tables: CleanTable[], - tableByName: Map + tableByName: Map, ): t.Statement[] { const statements: t.Statement[] = []; @@ -725,8 +723,8 @@ function generateEntityRelationTypes( statements.push( createExportedInterface( `${typeName}Relations`, - buildEntityRelationProperties(table, tableByName) - ) + buildEntityRelationProperties(table, tableByName), + ), ); } @@ -747,8 +745,8 @@ function generateEntityWithRelations(tables: CleanTable[]): t.Statement[] { statements.push( createExportedTypeAlias( `${typeName}WithRelations`, - `${typeName} & ${typeName}Relations` - ) + `${typeName} & ${typeName}Relations`, + ), ); } @@ -767,7 +765,7 @@ function generateEntityWithRelations(tables: CleanTable[]): t.Statement[] { */ function buildSelectTypeLiteral( table: CleanTable, - tableByName: Map + tableByName: Map, ): t.TSTypeLiteral { const members: t.TSTypeElement[] = []; @@ -776,7 +774,7 @@ function buildSelectTypeLiteral( if (!isRelationField(field.name, table)) { const prop = t.tsPropertySignature( t.identifier(field.name), - t.tsTypeAnnotation(t.tsBooleanKeyword()) + t.tsTypeAnnotation(t.tsBooleanKeyword()), ); prop.optional = true; members.push(prop); @@ -788,7 +786,7 @@ function buildSelectTypeLiteral( if (relation.fieldName) { const relatedTypeName = getRelatedTypeName( relation.referencesTable, - tableByName + tableByName, ); const prop = t.tsPropertySignature( t.identifier(relation.fieldName), @@ -800,15 +798,15 @@ function buildSelectTypeLiteral( const selectProp = t.tsPropertySignature( t.identifier('select'), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)) - ) + t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)), + ), ); selectProp.optional = true; return selectProp; - })() - ]) - ]) - ) + })(), + ]), + ]), + ), ); prop.optional = true; members.push(prop); @@ -820,15 +818,15 @@ function buildSelectTypeLiteral( if (relation.fieldName) { const relatedTypeName = getRelatedTypeName( relation.referencedByTable, - tableByName + tableByName, ); const filterName = getRelatedFilterName( relation.referencedByTable, - tableByName + tableByName, ); const orderByName = getRelatedOrderByName( relation.referencedByTable, - tableByName + tableByName, ); const prop = t.tsPropertySignature( t.identifier(relation.fieldName), @@ -840,8 +838,8 @@ function buildSelectTypeLiteral( const p = t.tsPropertySignature( t.identifier('select'), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)) - ) + t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)), + ), ); p.optional = true; return p; @@ -849,7 +847,7 @@ function buildSelectTypeLiteral( (() => { const p = t.tsPropertySignature( t.identifier('first'), - t.tsTypeAnnotation(t.tsNumberKeyword()) + t.tsTypeAnnotation(t.tsNumberKeyword()), ); p.optional = true; return p; @@ -857,7 +855,9 @@ function buildSelectTypeLiteral( (() => { const p = t.tsPropertySignature( t.identifier('filter'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))) + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier(filterName)), + ), ); p.optional = true; return p; @@ -866,15 +866,15 @@ function buildSelectTypeLiteral( const p = t.tsPropertySignature( t.identifier('orderBy'), t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier(orderByName))) - ) + t.tsArrayType(t.tsTypeReference(t.identifier(orderByName))), + ), ); p.optional = true; return p; - })() - ]) - ]) - ) + })(), + ]), + ]), + ), ); prop.optional = true; members.push(prop); @@ -886,12 +886,12 @@ function buildSelectTypeLiteral( if (relation.fieldName) { const relatedTypeName = getRelatedTypeName( relation.rightTable, - tableByName + tableByName, ); const filterName = getRelatedFilterName(relation.rightTable, tableByName); const orderByName = getRelatedOrderByName( relation.rightTable, - tableByName + tableByName, ); const prop = t.tsPropertySignature( t.identifier(relation.fieldName), @@ -903,8 +903,8 @@ function buildSelectTypeLiteral( const p = t.tsPropertySignature( t.identifier('select'), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)) - ) + t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)), + ), ); p.optional = true; return p; @@ -912,7 +912,7 @@ function buildSelectTypeLiteral( (() => { const p = t.tsPropertySignature( t.identifier('first'), - t.tsTypeAnnotation(t.tsNumberKeyword()) + t.tsTypeAnnotation(t.tsNumberKeyword()), ); p.optional = true; return p; @@ -920,7 +920,9 @@ function buildSelectTypeLiteral( (() => { const p = t.tsPropertySignature( t.identifier('filter'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(filterName))) + t.tsTypeAnnotation( + t.tsTypeReference(t.identifier(filterName)), + ), ); p.optional = true; return p; @@ -929,15 +931,15 @@ function buildSelectTypeLiteral( const p = t.tsPropertySignature( t.identifier('orderBy'), t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier(orderByName))) - ) + t.tsArrayType(t.tsTypeReference(t.identifier(orderByName))), + ), ); p.optional = true; return p; - })() - ]) - ]) - ) + })(), + ]), + ]), + ), ); prop.optional = true; members.push(prop); @@ -949,7 +951,7 @@ function buildSelectTypeLiteral( if (relation.fieldName) { const relatedTypeName = getRelatedTypeName( relation.referencedByTable, - tableByName + tableByName, ); const prop = t.tsPropertySignature( t.identifier(relation.fieldName), @@ -961,15 +963,15 @@ function buildSelectTypeLiteral( const selectProp = t.tsPropertySignature( t.identifier('select'), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)) - ) + t.tsTypeReference(t.identifier(`${relatedTypeName}Select`)), + ), ); selectProp.optional = true; return selectProp; - })() - ]) - ]) - ) + })(), + ]), + ]), + ), ); prop.optional = true; members.push(prop); @@ -984,7 +986,7 @@ function buildSelectTypeLiteral( */ function generateEntitySelectTypes( tables: CleanTable[], - tableByName: Map + tableByName: Map, ): t.Statement[] { const statements: t.Statement[] = []; @@ -993,7 +995,7 @@ function generateEntitySelectTypes( const typeAlias = t.tsTypeAliasDeclaration( t.identifier(`${typeName}Select`), null, - buildSelectTypeLiteral(table, tableByName) + buildSelectTypeLiteral(table, tableByName), ); statements.push(t.exportNamedDeclaration(typeAlias)); } @@ -1048,7 +1050,7 @@ function generateTableFilterTypes(tables: CleanTable[]): t.Statement[] { for (const table of tables) { const filterName = getFilterTypeName(table); statements.push( - createExportedInterface(filterName, buildTableFilterProperties(table)) + createExportedInterface(filterName, buildTableFilterProperties(table)), ); } @@ -1079,7 +1081,7 @@ function buildTableConditionProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: `${tsType} | null`, - optional: true + optional: true, }); } @@ -1097,8 +1099,8 @@ function generateTableConditionTypes(tables: CleanTable[]): t.Statement[] { statements.push( createExportedInterface( conditionName, - buildTableConditionProperties(table) - ) + buildTableConditionProperties(table), + ), ); } @@ -1141,7 +1143,7 @@ function generateOrderByTypes(tables: CleanTable[]): t.Statement[] { const typeAlias = t.tsTypeAliasDeclaration( t.identifier(enumName), null, - unionType + unionType, ); statements.push(t.exportNamedDeclaration(typeAlias)); } @@ -1160,14 +1162,14 @@ function generateOrderByTypes(tables: CleanTable[]): t.Statement[] { * Build the nested data object fields for Create input */ function buildCreateDataFields( - table: CleanTable + table: CleanTable, ): Array<{ name: string; type: string; optional: boolean }> { const fields: Array<{ name: string; type: string; optional: boolean }> = []; for (const field of table.fields) { if ( EXCLUDED_MUTATION_FIELDS.includes( - field.name as (typeof EXCLUDED_MUTATION_FIELDS)[number] + field.name as (typeof EXCLUDED_MUTATION_FIELDS)[number], ) ) continue; @@ -1187,7 +1189,9 @@ function buildCreateDataFields( /** * Build Create input interface as AST */ -function buildCreateInputInterface(table: CleanTable): t.ExportNamedDeclaration { +function buildCreateInputInterface( + table: CleanTable, +): t.ExportNamedDeclaration { const { typeName, singularName } = getTableNames(table); const fields = buildCreateDataFields(table); @@ -1195,7 +1199,7 @@ function buildCreateInputInterface(table: CleanTable): t.ExportNamedDeclaration const nestedProps: t.TSPropertySignature[] = fields.map((field) => { const prop = t.tsPropertySignature( t.identifier(field.name), - t.tsTypeAnnotation(parseTypeString(field.type)) + t.tsTypeAnnotation(parseTypeString(field.type)), ); prop.optional = field.optional; return prop; @@ -1208,15 +1212,15 @@ function buildCreateInputInterface(table: CleanTable): t.ExportNamedDeclaration (() => { const prop = t.tsPropertySignature( t.identifier('clientMutationId'), - t.tsTypeAnnotation(t.tsStringKeyword()) + t.tsTypeAnnotation(t.tsStringKeyword()), ); prop.optional = true; return prop; })(), t.tsPropertySignature( t.identifier(singularName), - t.tsTypeAnnotation(nestedObjectType) - ) + t.tsTypeAnnotation(nestedObjectType), + ), ]; const body = t.tsInterfaceBody(mainProps); @@ -1224,7 +1228,7 @@ function buildCreateInputInterface(table: CleanTable): t.ExportNamedDeclaration t.identifier(`Create${typeName}Input`), null, null, - body + body, ); return t.exportNamedDeclaration(interfaceDecl); } @@ -1238,7 +1242,7 @@ function buildPatchProperties(table: CleanTable): InterfaceProperty[] { for (const field of table.fields) { if ( EXCLUDED_MUTATION_FIELDS.includes( - field.name as (typeof EXCLUDED_MUTATION_FIELDS)[number] + field.name as (typeof EXCLUDED_MUTATION_FIELDS)[number], ) ) continue; @@ -1251,7 +1255,7 @@ function buildPatchProperties(table: CleanTable): InterfaceProperty[] { properties.push({ name: field.name, type: `${tsType} | null`, - optional: true + optional: true, }); } @@ -1276,7 +1280,7 @@ function generateCrudInputTypes(table: CleanTable): t.Statement[] { // Patch interface statements.push( - createExportedInterface(patchName, buildPatchProperties(table)) + createExportedInterface(patchName, buildPatchProperties(table)), ); // Update input @@ -1284,16 +1288,16 @@ function generateCrudInputTypes(table: CleanTable): t.Statement[] { createExportedInterface(`Update${typeName}Input`, [ { name: 'clientMutationId', type: 'string', optional: true }, { name: pkFieldName, type: pkFieldTsType, optional: false }, - { name: 'patch', type: patchName, optional: false } - ]) + { name: 'patch', type: patchName, optional: false }, + ]), ); // Delete input statements.push( createExportedInterface(`Delete${typeName}Input`, [ { name: 'clientMutationId', type: 'string', optional: true }, - { name: pkFieldName, type: pkFieldTsType, optional: false } - ]) + { name: pkFieldName, type: pkFieldTsType, optional: false }, + ]), ); return statements; @@ -1323,7 +1327,7 @@ function generateAllCrudInputTypes(tables: CleanTable[]): t.Statement[] { * Collect all input type names used by operations */ export function collectInputTypeNames( - operations: Array<{ args: CleanArgument[] }> + operations: Array<{ args: CleanArgument[] }>, ): Set { const inputTypes = new Set(); @@ -1369,7 +1373,7 @@ function buildTableCrudTypeNames(tables: CleanTable[]): Set { function generateCustomInputTypes( typeRegistry: TypeRegistry, usedInputTypes: Set, - tableCrudTypes?: Set + tableCrudTypes?: Set, ): t.Statement[] { const statements: t.Statement[] = []; const generatedTypes = new Set(); @@ -1401,7 +1405,7 @@ function generateCustomInputTypes( addLineComment(commentStmt, ` Type '${typeName}' not found in schema`); statements.push(commentStmt); statements.push( - createExportedTypeAlias(typeName, 'Record') + createExportedTypeAlias(typeName, 'Record'), ); continue; } @@ -1431,7 +1435,7 @@ function generateCustomInputTypes( const typeAlias = t.tsTypeAliasDeclaration( t.identifier(typeName), null, - unionType + unionType, ); statements.push(t.exportNamedDeclaration(typeAlias)); } else { @@ -1457,7 +1461,7 @@ function generateCustomInputTypes( * Collect all payload type names from operation return types */ export function collectPayloadTypeNames( - operations: Array<{ returnType: CleanArgument['type'] }> + operations: Array<{ returnType: CleanArgument['type'] }>, ): Set { const payloadTypes = new Set(); @@ -1477,7 +1481,7 @@ export function collectPayloadTypeNames( function generatePayloadTypes( typeRegistry: TypeRegistry, usedPayloadTypes: Set, - alreadyGeneratedTypes: Set + alreadyGeneratedTypes: Set, ): t.Statement[] { const statements: t.Statement[] = []; const generatedTypes = new Set(alreadyGeneratedTypes); @@ -1498,7 +1502,7 @@ function generatePayloadTypes( 'BigFloat', 'Cursor', 'Query', - 'Mutation' + 'Mutation', ]); // Process all types - no artificial limit @@ -1528,7 +1532,7 @@ function generatePayloadTypes( interfaceProps.push({ name: field.name, type: isNullable ? `${tsType} | null` : tsType, - optional: isNullable + optional: isNullable, }); // Follow nested OBJECT types @@ -1562,13 +1566,13 @@ function generatePayloadTypes( const p = t.tsPropertySignature( t.identifier('select'), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier(`${baseType}Select`)) - ) + t.tsTypeReference(t.identifier(`${baseType}Select`)), + ), ); p.optional = true; return p; - })() - ]) + })(), + ]), ]); } else { propType = t.tsBooleanKeyword(); @@ -1576,7 +1580,7 @@ function generatePayloadTypes( const prop = t.tsPropertySignature( t.identifier(field.name), - t.tsTypeAnnotation(propType) + t.tsTypeAnnotation(propType), ); prop.optional = true; selectMembers.push(prop); @@ -1585,13 +1589,16 @@ function generatePayloadTypes( const selectTypeAlias = t.tsTypeAliasDeclaration( t.identifier(`${typeName}Select`), null, - t.tsTypeLiteral(selectMembers) + t.tsTypeLiteral(selectMembers), ); statements.push(t.exportNamedDeclaration(selectTypeAlias)); } if (statements.length > 0) { - addSectionComment(statements, 'Payload/Return Types (for custom operations)'); + addSectionComment( + statements, + 'Payload/Return Types (for custom operations)', + ); } return statements; } @@ -1607,7 +1614,7 @@ function generatePayloadTypes( */ function generateConnectionFieldsMap( tables: CleanTable[], - tableByName: Map + tableByName: Map, ): t.Statement[] { const properties: t.ObjectProperty[] = []; @@ -1619,13 +1626,13 @@ function generateConnectionFieldsMap( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.referencedByTable, - tableByName + tableByName, ); fieldEntries.push( t.objectProperty( t.stringLiteral(relation.fieldName), - t.stringLiteral(relatedTypeName) - ) + t.stringLiteral(relatedTypeName), + ), ); } @@ -1633,13 +1640,13 @@ function generateConnectionFieldsMap( if (!relation.fieldName) continue; const relatedTypeName = getRelatedTypeName( relation.rightTable, - tableByName + tableByName, ); fieldEntries.push( t.objectProperty( t.stringLiteral(relation.fieldName), - t.stringLiteral(relatedTypeName) - ) + t.stringLiteral(relatedTypeName), + ), ); } @@ -1647,8 +1654,8 @@ function generateConnectionFieldsMap( properties.push( t.objectProperty( t.stringLiteral(typeName), - t.objectExpression(fieldEntries) - ) + t.objectExpression(fieldEntries), + ), ); } } @@ -1666,13 +1673,13 @@ function generateConnectionFieldsMap( t.identifier('Record'), t.tsTypeParameterInstantiation([ t.tsStringKeyword(), - t.tsStringKeyword() - ]) - ) - ]) - ) - ) - ) + t.tsStringKeyword(), + ]), + ), + ]), + ), + ), + ), ]); const statements: t.Statement[] = [t.exportNamedDeclaration(decl)]; @@ -1691,7 +1698,7 @@ export function generateInputTypesFile( typeRegistry: TypeRegistry, usedInputTypes: Set, tables?: CleanTable[], - usedPayloadTypes?: Set + usedPayloadTypes?: Set, ): GeneratedInputTypesFile { const statements: t.Statement[] = []; const tablesList = tables ?? []; @@ -1735,7 +1742,7 @@ export function generateInputTypesFile( // 7. Custom input types from TypeRegistry const tableCrudTypes = tables ? buildTableCrudTypeNames(tables) : undefined; statements.push( - ...generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes) + ...generateCustomInputTypes(typeRegistry, usedInputTypes, tableCrudTypes), ); // 8. Payload/return types for custom operations @@ -1751,8 +1758,8 @@ export function generateInputTypesFile( ...generatePayloadTypes( typeRegistry, usedPayloadTypes, - alreadyGeneratedTypes - ) + alreadyGeneratedTypes, + ), ); } @@ -1762,6 +1769,6 @@ export function generateInputTypesFile( return { fileName: 'input-types.ts', - content: header + '\n' + code + content: header + '\n' + code, }; } diff --git a/graphql/codegen/src/core/codegen/orm/model-generator.ts b/graphql/codegen/src/core/codegen/orm/model-generator.ts index a9935ef18..ddce20029 100644 --- a/graphql/codegen/src/core/codegen/orm/model-generator.ts +++ b/graphql/codegen/src/core/codegen/orm/model-generator.ts @@ -15,7 +15,7 @@ import { getPrimaryKeyInfo, getTableNames, hasValidPrimaryKey, - lcFirst + lcFirst, } from '../utils'; export interface GeneratedModelFile { @@ -28,12 +28,15 @@ export interface GeneratedModelFile { function createImportDeclaration( moduleSpecifier: string, namedImports: string[], - typeOnly: boolean = false + typeOnly: boolean = false, ): t.ImportDeclaration { const specifiers = namedImports.map((name) => - t.importSpecifier(t.identifier(name), t.identifier(name)) + t.importSpecifier(t.identifier(name), t.identifier(name)), + ); + const decl = t.importDeclaration( + specifiers, + t.stringLiteral(moduleSpecifier), ); - const decl = t.importDeclaration(specifiers, t.stringLiteral(moduleSpecifier)); decl.importKind = typeOnly ? 'type' : 'value'; return decl; } @@ -43,29 +46,55 @@ function buildMethodBody( args: t.Expression[], operation: string, typeName: string, - fieldName: string + fieldName: string, ): t.Statement[] { const destructureDecl = t.variableDeclaration('const', [ t.variableDeclarator( t.objectPattern([ - t.objectProperty(t.identifier('document'), t.identifier('document'), false, true), - t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true) + t.objectProperty( + t.identifier('document'), + t.identifier('document'), + false, + true, + ), + t.objectProperty( + t.identifier('variables'), + t.identifier('variables'), + false, + true, + ), ]), - t.callExpression(t.identifier(builderFn), args) - ) + t.callExpression(t.identifier(builderFn), args), + ), ]); const returnStmt = t.returnStatement( t.newExpression(t.identifier('QueryBuilder'), [ t.objectExpression([ - t.objectProperty(t.identifier('client'), t.memberExpression(t.thisExpression(), t.identifier('client'))), + t.objectProperty( + t.identifier('client'), + t.memberExpression(t.thisExpression(), t.identifier('client')), + ), t.objectProperty(t.identifier('operation'), t.stringLiteral(operation)), - t.objectProperty(t.identifier('operationName'), t.stringLiteral(typeName)), + t.objectProperty( + t.identifier('operationName'), + t.stringLiteral(typeName), + ), t.objectProperty(t.identifier('fieldName'), t.stringLiteral(fieldName)), - t.objectProperty(t.identifier('document'), t.identifier('document'), false, true), - t.objectProperty(t.identifier('variables'), t.identifier('variables'), false, true) - ]) - ]) + t.objectProperty( + t.identifier('document'), + t.identifier('document'), + false, + true, + ), + t.objectProperty( + t.identifier('variables'), + t.identifier('variables'), + false, + true, + ), + ]), + ]), ); return [destructureDecl, returnStmt]; @@ -76,9 +105,14 @@ function createClassMethod( typeParameters: t.TSTypeParameterDeclaration | null, params: (t.Identifier | t.TSParameterProperty)[], returnType: t.TSTypeAnnotation | null, - body: t.Statement[] + body: t.Statement[], ): t.ClassMethod { - const method = t.classMethod('method', t.identifier(name), params, t.blockStatement(body)); + const method = t.classMethod( + 'method', + t.identifier(name), + params, + t.blockStatement(body), + ); method.typeParameters = typeParameters; method.returnType = returnType; return method; @@ -88,26 +122,26 @@ function createDeclareMethod( name: string, typeParameters: t.TSTypeParameterDeclaration | null, params: (t.Identifier | t.TSParameterProperty)[], - returnType: t.TSTypeAnnotation + returnType: t.TSTypeAnnotation, ): t.TSDeclareMethod { const method = t.tsDeclareMethod( null, t.identifier(name), typeParameters, params, - returnType + returnType, ); return method; } function createTypeParam( constraintTypeName: string, - defaultType?: t.TSType + defaultType?: t.TSType, ): t.TSTypeParameterDeclaration { const param = t.tsTypeParameter( t.tsTypeReference(t.identifier(constraintTypeName)), defaultType ?? null, - 'S' + 'S', ); return t.tsTypeParameterDeclaration([param]); } @@ -123,7 +157,7 @@ function tsTypeFromPrimitive(typeName: string): t.TSType { function requiredSelectProp(): t.TSPropertySignature { const prop = t.tsPropertySignature( t.identifier('select'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier('S'))), ); prop.optional = false; return prop; @@ -135,19 +169,20 @@ function strictSelectGuard(selectTypeName: string): t.TSType { t.identifier('StrictSelect'), t.tsTypeParameterInstantiation([ t.tsTypeReference(t.identifier('S')), - t.tsTypeReference(t.identifier(selectTypeName)) - ]) + t.tsTypeReference(t.identifier(selectTypeName)), + ]), ); } export function generateModelFile( table: CleanTable, - _useSharedTypes: boolean + _useSharedTypes: boolean, ): GeneratedModelFile { const { typeName, singularName, pluralName } = getTableNames(table); const modelName = `${typeName}Model`; const baseFileName = lcFirst(typeName); - const fileName = baseFileName === 'index' ? `${baseFileName}Model.ts` : `${baseFileName}.ts`; + const fileName = + baseFileName === 'index' ? `${baseFileName}Model.ts` : `${baseFileName}.ts`; const entityLower = singularName; const selectTypeName = `${typeName}Select`; @@ -161,7 +196,7 @@ export function generateModelFile( const pkFields = getPrimaryKeyInfo(table); const pkField = pkFields[0]; - const pluralQueryName= table.query?.all ?? pluralName; + const pluralQueryName = table.query?.all ?? pluralName; const createMutationName = table.query?.create ?? `create${typeName}`; const updateMutationName = table.query?.update; const deleteMutationName = table.query?.delete; @@ -169,28 +204,70 @@ export function generateModelFile( const statements: t.Statement[] = []; statements.push(createImportDeclaration('../client', ['OrmClient'])); - statements.push(createImportDeclaration('../query-builder', [ - 'QueryBuilder', 'buildFindManyDocument', 'buildFindFirstDocument', 'buildFindOneDocument', - 'buildCreateDocument', 'buildUpdateByPkDocument', 'buildDeleteByPkDocument' - ])); - statements.push(createImportDeclaration('../select-types', [ - 'ConnectionResult', 'FindManyArgs', 'FindFirstArgs', 'CreateArgs', - 'UpdateArgs', 'DeleteArgs', 'InferSelectResult', 'StrictSelect' - ], true)); - statements.push(createImportDeclaration('../input-types', [ - typeName, relationTypeName, selectTypeName, whereTypeName, orderByTypeName, - createInputTypeName, updateInputTypeName, patchTypeName - ], true)); - statements.push(createImportDeclaration('../input-types', ['connectionFieldsMap'])); + statements.push( + createImportDeclaration('../query-builder', [ + 'QueryBuilder', + 'buildFindManyDocument', + 'buildFindFirstDocument', + 'buildFindOneDocument', + 'buildCreateDocument', + 'buildUpdateByPkDocument', + 'buildDeleteByPkDocument', + ]), + ); + statements.push( + createImportDeclaration( + '../select-types', + [ + 'ConnectionResult', + 'FindManyArgs', + 'FindFirstArgs', + 'CreateArgs', + 'UpdateArgs', + 'DeleteArgs', + 'InferSelectResult', + 'StrictSelect', + ], + true, + ), + ); + statements.push( + createImportDeclaration( + '../input-types', + [ + typeName, + relationTypeName, + selectTypeName, + whereTypeName, + orderByTypeName, + createInputTypeName, + updateInputTypeName, + patchTypeName, + ], + true, + ), + ); + statements.push( + createImportDeclaration('../input-types', ['connectionFieldsMap']), + ); const classBody: t.ClassBody['body'] = []; // Constructor const constructorParam = t.identifier('client'); - constructorParam.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('OrmClient'))); + constructorParam.typeAnnotation = t.tsTypeAnnotation( + t.tsTypeReference(t.identifier('OrmClient')), + ); const paramProp = t.tsParameterProperty(constructorParam); paramProp.accessibility = 'private'; - classBody.push(t.classMethod('constructor', t.identifier('constructor'), [paramProp], t.blockStatement([]))); + classBody.push( + t.classMethod( + 'constructor', + t.identifier('constructor'), + [paramProp], + t.blockStatement([]), + ), + ); // Reusable type reference factories const sRef = () => t.tsTypeReference(t.identifier('S')); @@ -200,25 +277,40 @@ export function generateModelFile( // ── findMany ─────────────────────────────────────────────────────────── { const argsType = (sel: t.TSType) => - t.tsTypeReference(t.identifier('FindManyArgs'), t.tsTypeParameterInstantiation([ - sel, - t.tsTypeReference(t.identifier(whereTypeName)), - t.tsTypeReference(t.identifier(orderByTypeName)) - ])); + t.tsTypeReference( + t.identifier('FindManyArgs'), + t.tsTypeParameterInstantiation([ + sel, + t.tsTypeReference(t.identifier(whereTypeName)), + t.tsTypeReference(t.identifier(orderByTypeName)), + ]), + ); const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('ConnectionResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ])) - ])) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(pluralQueryName), + t.tsTypeAnnotation( + t.tsTypeReference( + t.identifier('ConnectionResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + ]), + ), + ), + ), + ]), + ]), + ), ); // Overload 1: with select (autocompletion) @@ -227,62 +319,161 @@ export function generateModelFile( t.tsIntersectionType([ argsType(sRef()), t.tsTypeLiteral([requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'findMany', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('findMany', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), + ); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), selectExpr, t.objectExpression([ - t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)), - t.objectProperty(t.identifier('orderBy'), t.tsAsExpression( - t.optionalMemberExpression(t.identifier('args'), t.identifier('orderBy'), false, true), - t.tsUnionType([t.tsArrayType(t.tsStringKeyword()), t.tsUndefinedKeyword()]) - )), - t.objectProperty(t.identifier('first'), t.optionalMemberExpression(t.identifier('args'), t.identifier('first'), false, true)), - t.objectProperty(t.identifier('last'), t.optionalMemberExpression(t.identifier('args'), t.identifier('last'), false, true)), - t.objectProperty(t.identifier('after'), t.optionalMemberExpression(t.identifier('args'), t.identifier('after'), false, true)), - t.objectProperty(t.identifier('before'), t.optionalMemberExpression(t.identifier('args'), t.identifier('before'), false, true)), - t.objectProperty(t.identifier('offset'), t.optionalMemberExpression(t.identifier('args'), t.identifier('offset'), false, true)) + t.objectProperty( + t.identifier('where'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('where'), + false, + true, + ), + ), + t.objectProperty( + t.identifier('orderBy'), + t.tsAsExpression( + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('orderBy'), + false, + true, + ), + t.tsUnionType([ + t.tsArrayType(t.tsStringKeyword()), + t.tsUndefinedKeyword(), + ]), + ), + ), + t.objectProperty( + t.identifier('first'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('first'), + false, + true, + ), + ), + t.objectProperty( + t.identifier('last'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('last'), + false, + true, + ), + ), + t.objectProperty( + t.identifier('after'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('after'), + false, + true, + ), + ), + t.objectProperty( + t.identifier('before'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('before'), + false, + true, + ), + ), + t.objectProperty( + t.identifier('offset'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('offset'), + false, + true, + ), + ), ]), t.stringLiteral(whereTypeName), t.stringLiteral(orderByTypeName), - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('findMany', null, [implParam], null, - buildMethodBody('buildFindManyDocument', bodyArgs, 'query', typeName, pluralQueryName))); + classBody.push( + createClassMethod( + 'findMany', + null, + [implParam], + null, + buildMethodBody( + 'buildFindManyDocument', + bodyArgs, + 'query', + typeName, + pluralQueryName, + ), + ), + ); } // ── findFirst ────────────────────────────────────────────────────────── { const argsType = (sel: t.TSType) => - t.tsTypeReference(t.identifier('FindFirstArgs'), t.tsTypeParameterInstantiation([ - sel, - t.tsTypeReference(t.identifier(whereTypeName)) - ])); + t.tsTypeReference( + t.identifier('FindFirstArgs'), + t.tsTypeParameterInstantiation([ + sel, + t.tsTypeReference(t.identifier(whereTypeName)), + ]), + ); const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(pluralQueryName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier('nodes'), t.tsTypeAnnotation( - t.tsArrayType(t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ]))) - )) - ]) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(pluralQueryName), + t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('nodes'), + t.tsTypeAnnotation( + t.tsArrayType( + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + ), + ), + ), + ]), + ), + ), + ]), + ]), + ), ); // Overload 1: with select (autocompletion) @@ -291,27 +482,58 @@ export function generateModelFile( t.tsIntersectionType([ argsType(sRef()), t.tsTypeLiteral([requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'findFirst', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('findFirst', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), + ); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(pluralQueryName), selectExpr, t.objectExpression([ - t.objectProperty(t.identifier('where'), t.optionalMemberExpression(t.identifier('args'), t.identifier('where'), false, true)) + t.objectProperty( + t.identifier('where'), + t.optionalMemberExpression( + t.identifier('args'), + t.identifier('where'), + false, + true, + ), + ), ]), t.stringLiteral(whereTypeName), - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('findFirst', null, [implParam], null, - buildMethodBody('buildFindFirstDocument', bodyArgs, 'query', typeName, pluralQueryName))); + classBody.push( + createClassMethod( + 'findFirst', + null, + [implParam], + null, + buildMethodBody( + 'buildFindFirstDocument', + bodyArgs, + 'query', + typeName, + pluralQueryName, + ), + ), + ); } // ── findOne ──────────────────────────────────────────────────────────── @@ -321,23 +543,35 @@ export function generateModelFile( const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(singleQueryName), t.tsTypeAnnotation( - t.tsUnionType([ - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ])), - t.tsNullKeyword() - ]) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(singleQueryName), + t.tsTypeAnnotation( + t.tsUnionType([ + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + t.tsNullKeyword(), + ]), + ), + ), + ]), + ]), + ), ); const pkProp = () => { - const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); + const prop = t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkTsType()), + ); prop.optional = false; return prop; }; @@ -347,10 +581,17 @@ export function generateModelFile( o1Param.typeAnnotation = t.tsTypeAnnotation( t.tsIntersectionType([ t.tsTypeLiteral([pkProp(), requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'findOne', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('findOne', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); @@ -360,13 +601,16 @@ export function generateModelFile( (() => { const prop = t.tsPropertySignature( t.identifier('select'), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(selectTypeName))), ); return prop; - })() - ]) + })(), + ]), + ); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), ); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(singleQueryName), @@ -374,36 +618,65 @@ export function generateModelFile( selectExpr, t.stringLiteral(pkField.name), t.stringLiteral(pkGqlType), - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('findOne', null, [implParam], null, - buildMethodBody('buildFindOneDocument', bodyArgs, 'query', typeName, singleQueryName))); + classBody.push( + createClassMethod( + 'findOne', + null, + [implParam], + null, + buildMethodBody( + 'buildFindOneDocument', + bodyArgs, + 'query', + typeName, + singleQueryName, + ), + ), + ); } // ── create ───────────────────────────────────────────────────────────── { - const dataType = () => t.tsIndexedAccessType( - t.tsTypeReference(t.identifier(createInputTypeName)), - t.tsLiteralType(t.stringLiteral(singularName)) - ); + const dataType = () => + t.tsIndexedAccessType( + t.tsTypeReference(t.identifier(createInputTypeName)), + t.tsLiteralType(t.stringLiteral(singularName)), + ); const argsType = (sel: t.TSType) => - t.tsTypeReference(t.identifier('CreateArgs'), t.tsTypeParameterInstantiation([sel, dataType()])); + t.tsTypeReference( + t.identifier('CreateArgs'), + t.tsTypeParameterInstantiation([sel, dataType()]), + ); const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(createMutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ])) - )) - ]) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(createMutationName), + t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(entityLower), + t.tsTypeAnnotation( + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + ), + ), + ]), + ), + ), + ]), + ]), + ), ); // Overload 1: with select (autocompletion) @@ -412,15 +685,25 @@ export function generateModelFile( t.tsIntersectionType([ argsType(sRef()), t.tsTypeLiteral([requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'create', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('create', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), + ); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(createMutationName), @@ -428,41 +711,75 @@ export function generateModelFile( selectExpr, t.memberExpression(t.identifier('args'), t.identifier('data')), t.stringLiteral(createInputTypeName), - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('create', null, [implParam], null, - buildMethodBody('buildCreateDocument', bodyArgs, 'mutation', typeName, createMutationName))); + classBody.push( + createClassMethod( + 'create', + null, + [implParam], + null, + buildMethodBody( + 'buildCreateDocument', + bodyArgs, + 'mutation', + typeName, + createMutationName, + ), + ), + ); } // ── update ───────────────────────────────────────────────────────────── if (updateMutationName) { - const whereLiteral = () => t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); - prop.optional = false; - return prop; - })() - ]); + const whereLiteral = () => + t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkTsType()), + ); + prop.optional = false; + return prop; + })(), + ]); const argsType = (sel: t.TSType) => - t.tsTypeReference(t.identifier('UpdateArgs'), t.tsTypeParameterInstantiation([ - sel, whereLiteral(), t.tsTypeReference(t.identifier(patchTypeName)) - ])); + t.tsTypeReference( + t.identifier('UpdateArgs'), + t.tsTypeParameterInstantiation([ + sel, + whereLiteral(), + t.tsTypeReference(t.identifier(patchTypeName)), + ]), + ); const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(updateMutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ])) - )) - ]) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(updateMutationName), + t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(entityLower), + t.tsTypeAnnotation( + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + ), + ), + ]), + ), + ), + ]), + ]), + ), ); // Overload 1: with select (autocompletion) @@ -471,15 +788,25 @@ export function generateModelFile( t.tsIntersectionType([ argsType(sRef()), t.tsTypeLiteral([requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'update', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('update', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), + ); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(updateMutationName), @@ -487,44 +814,76 @@ export function generateModelFile( selectExpr, t.memberExpression( t.memberExpression(t.identifier('args'), t.identifier('where')), - t.identifier(pkField.name) + t.identifier(pkField.name), ), t.memberExpression(t.identifier('args'), t.identifier('data')), t.stringLiteral(updateInputTypeName), t.stringLiteral(pkField.name), - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('update', null, [implParam], null, - buildMethodBody('buildUpdateByPkDocument', bodyArgs, 'mutation', typeName, updateMutationName))); + classBody.push( + createClassMethod( + 'update', + null, + [implParam], + null, + buildMethodBody( + 'buildUpdateByPkDocument', + bodyArgs, + 'mutation', + typeName, + updateMutationName, + ), + ), + ); } // ── delete ───────────────────────────────────────────────────────────── if (deleteMutationName) { - const whereLiteral = () => t.tsTypeLiteral([ - (() => { - const prop = t.tsPropertySignature(t.identifier(pkField.name), t.tsTypeAnnotation(pkTsType())); - prop.optional = false; - return prop; - })() - ]); + const whereLiteral = () => + t.tsTypeLiteral([ + (() => { + const prop = t.tsPropertySignature( + t.identifier(pkField.name), + t.tsTypeAnnotation(pkTsType()), + ); + prop.optional = false; + return prop; + })(), + ]); const argsType = (sel: t.TSType) => - t.tsTypeReference(t.identifier('DeleteArgs'), t.tsTypeParameterInstantiation([whereLiteral(), sel])); + t.tsTypeReference( + t.identifier('DeleteArgs'), + t.tsTypeParameterInstantiation([whereLiteral(), sel]), + ); const retType = (sel: t.TSType) => t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('QueryBuilder'), t.tsTypeParameterInstantiation([ - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(deleteMutationName), t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature(t.identifier(entityLower), t.tsTypeAnnotation( - t.tsTypeReference(t.identifier('InferSelectResult'), t.tsTypeParameterInstantiation([ - t.tsTypeReference(t.identifier(relationTypeName)), - sel - ])) - )) - ]) - )) - ]) - ])) + t.tsTypeReference( + t.identifier('QueryBuilder'), + t.tsTypeParameterInstantiation([ + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(deleteMutationName), + t.tsTypeAnnotation( + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(entityLower), + t.tsTypeAnnotation( + t.tsTypeReference( + t.identifier('InferSelectResult'), + t.tsTypeParameterInstantiation([ + t.tsTypeReference(t.identifier(relationTypeName)), + sel, + ]), + ), + ), + ), + ]), + ), + ), + ]), + ]), + ), ); // Overload 1: with select (autocompletion) @@ -533,44 +892,76 @@ export function generateModelFile( t.tsIntersectionType([ argsType(sRef()), t.tsTypeLiteral([requiredSelectProp()]), - strictSelectGuard(selectTypeName) - ]) + strictSelectGuard(selectTypeName), + ]), + ); + classBody.push( + createDeclareMethod( + 'delete', + createTypeParam(selectTypeName), + [o1Param], + retType(sRef()), + ), ); - classBody.push(createDeclareMethod('delete', createTypeParam(selectTypeName), [o1Param], retType(sRef()))); // Implementation const implParam = t.identifier('args'); implParam.typeAnnotation = t.tsTypeAnnotation(argsType(selectRef())); - const selectExpr = t.memberExpression(t.identifier('args'), t.identifier('select')); + const selectExpr = t.memberExpression( + t.identifier('args'), + t.identifier('select'), + ); const bodyArgs = [ t.stringLiteral(typeName), t.stringLiteral(deleteMutationName), t.stringLiteral(entityLower), t.memberExpression( t.memberExpression(t.identifier('args'), t.identifier('where')), - t.identifier(pkField.name) + t.identifier(pkField.name), ), t.stringLiteral(deleteInputTypeName), t.stringLiteral(pkField.name), selectExpr, - t.identifier('connectionFieldsMap') + t.identifier('connectionFieldsMap'), ]; - classBody.push(createClassMethod('delete', null, [implParam], null, - buildMethodBody('buildDeleteByPkDocument', bodyArgs, 'mutation', typeName, deleteMutationName))); + classBody.push( + createClassMethod( + 'delete', + null, + [implParam], + null, + buildMethodBody( + 'buildDeleteByPkDocument', + bodyArgs, + 'mutation', + typeName, + deleteMutationName, + ), + ), + ); } - const classDecl = t.classDeclaration(t.identifier(modelName), null, t.classBody(classBody)); + const classDecl = t.classDeclaration( + t.identifier(modelName), + null, + t.classBody(classBody), + ); statements.push(t.exportNamedDeclaration(classDecl)); const header = getGeneratedFileHeader(`${typeName} model for ORM client`); const code = generateCode(statements); - return { fileName, content: header + '\n' + code, modelName, tableName: table.name }; + return { + fileName, + content: header + '\n' + code, + modelName, + tableName: table.name, + }; } export function generateAllModelFiles( tables: CleanTable[], - useSharedTypes: boolean + useSharedTypes: boolean, ): GeneratedModelFile[] { return tables.map((table) => generateModelFile(table, useSharedTypes)); } diff --git a/graphql/codegen/src/core/codegen/orm/select-types.ts b/graphql/codegen/src/core/codegen/orm/select-types.ts index 446a4b642..d03418113 100644 --- a/graphql/codegen/src/core/codegen/orm/select-types.ts +++ b/graphql/codegen/src/core/codegen/orm/select-types.ts @@ -73,9 +73,13 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Extract extends { select?: infer ShapeNS } + ? Extract extends { + select?: infer ShapeNS; + } ? DeepExact< - Omit & { select: DeepExact> }, + Omit & { + select: DeepExact>; + }, Extract > : never @@ -104,7 +108,9 @@ export type StrictSelect = S extends DeepExact ? {} : never; export type InferSelectResult = TSelect extends undefined ? TEntity : { - [K in keyof TSelect as TSelect[K] extends false | undefined ? never : K]: TSelect[K] extends true + [K in keyof TSelect as TSelect[K] extends false | undefined + ? never + : K]: TSelect[K] extends true ? K extends keyof TEntity ? TEntity[K] : never @@ -112,7 +118,9 @@ export type InferSelectResult = TSelect extends undefined ? K extends keyof TEntity ? NonNullable extends ConnectionResult ? ConnectionResult> - : InferSelectResult, NestedSelect> | (null extends TEntity[K] ? null : never) + : + | InferSelectResult, NestedSelect> + | (null extends TEntity[K] ? null : never) : never : K extends keyof TEntity ? TEntity[K] @@ -236,7 +244,11 @@ export type ConnectionQueryResult = QueryResult<{ /** * Type for mutation that returns a payload with the entity */ -export type MutationResult = QueryResult<{ +export type MutationResult< + TEntity, + TSelect, + TPayloadKey extends string, +> = QueryResult<{ [K in TPayloadKey]: { [EntityKey: string]: ResolveSelectResult; }; diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index a33d52a6d..f8b426bec 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -48,7 +48,7 @@ import { useQueryOptionsImplType, voidStatement, withFieldsListSelectionType, - withFieldsSelectionType + withFieldsSelectionType, } from './hooks-ast'; import { getAllRowsQueryName, @@ -63,7 +63,7 @@ import { getTableNames, hasValidPrimaryKey, lcFirst, - ucFirst + ucFirst, } from './utils'; export interface GeneratedQueryFile { @@ -79,12 +79,12 @@ export interface QueryGeneratorOptions { export function generateListQueryHook( table: CleanTable, - options: QueryGeneratorOptions = {} + options: QueryGeneratorOptions = {}, ): GeneratedQueryFile { const { reactQueryEnabled = true, useCentralizedKeys = true, - hasRelationships = false + hasRelationships = false, } = options; const { typeName, pluralName, singularName } = getTableNames(table); const hookName = getListQueryHookName(table); @@ -96,31 +96,63 @@ export function generateListQueryHook( const selectTypeName = `${typeName}Select`; const relationTypeName = `${typeName}WithRelations`; - const listResultTypeAST = (sel: t.TSType) => listQueryResultType(queryName, relationTypeName, sel); + const listResultTypeAST = (sel: t.TSType) => + listQueryResultType(queryName, relationTypeName, sel); const statements: t.Statement[] = []; // Imports if (reactQueryEnabled) { - statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', ['useQuery']), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], + true, + ), + ); } statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildListSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['ListSelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildListSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['ListSelectionConfig'], true), + ); if (useCentralizedKeys) { statements.push(createImportDeclaration('../query-keys', [keysName])); if (hasRelationships) { - statements.push(createImportDeclaration('../query-keys', [scopeTypeName], true)); + statements.push( + createImportDeclaration('../query-keys', [scopeTypeName], true), + ); } } - statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName, filterTypeName, orderByTypeName], true)); - statements.push(createImportDeclaration('../../orm/select-types', ['FindManyArgs', 'InferSelectResult', 'ConnectionResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/input-types', + [selectTypeName, relationTypeName, filterTypeName, orderByTypeName], + true, + ), + ); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['FindManyArgs', 'InferSelectResult', 'ConnectionResult', 'StrictSelect'], + true, + ), + ); // Re-exports - statements.push(createTypeReExport([selectTypeName, relationTypeName, filterTypeName, orderByTypeName], '../../orm/input-types')); + statements.push( + createTypeReExport( + [selectTypeName, relationTypeName, filterTypeName, orderByTypeName], + '../../orm/input-types', + ), + ); // Query key if (useCentralizedKeys) { @@ -128,43 +160,69 @@ export function generateListQueryHook( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(`${queryName}QueryKey`), - t.memberExpression(t.identifier(keysName), t.identifier('list')) - ) - ]) + t.memberExpression(t.identifier(keysName), t.identifier('list')), + ), + ]), ); - addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + addJSDocComment(keyDecl, [ + 'Query key factory - re-exported from query-keys.ts', + ]); statements.push(keyDecl); } else { const keyFn = t.arrowFunctionExpression( - [createFunctionParam('variables', typeRef('FindManyArgs', [t.tsUnknownKeyword(), typeRef(filterTypeName), typeRef(orderByTypeName)]), true)], - asConst(t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('list'), t.identifier('variables')])) + [ + createFunctionParam( + 'variables', + typeRef('FindManyArgs', [ + t.tsUnknownKeyword(), + typeRef(filterTypeName), + typeRef(orderByTypeName), + ]), + true, + ), + ], + asConst( + t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('list'), + t.identifier('variables'), + ]), + ), + ); + statements.push( + t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn), + ]), + ), ); - statements.push(t.exportNamedDeclaration( - t.variableDeclaration('const', [t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn)]) - )); } // Helper for query key call - const buildListQueryKey = (argsExpr: t.Expression, scopeExpr?: t.Expression) => { + const buildListQueryKey = ( + argsExpr: t.Expression, + scopeExpr?: t.Expression, + ) => { if (useCentralizedKeys) { const args = [argsExpr]; if (scopeExpr) args.push(scopeExpr); - return callExpr(t.memberExpression(t.identifier(keysName), t.identifier('list')), args); + return callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('list')), + args, + ); } return callExpr(t.identifier(`${queryName}QueryKey`), [argsExpr]); }; // Helper for findMany queryFn - const buildFindManyFn = () => t.arrowFunctionExpression( - [], - buildFindManyCallExpr(singularName, 'args') - ); + const buildFindManyFn = () => + t.arrowFunctionExpression([], buildFindManyCallExpr(singularName, 'args')); // Options type builder with optional scope const buildOptionsType = (queryDataType: t.TSType, dataType: t.TSType) => { const base = omitType( typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), - ['queryKey', 'queryFn'] + ['queryKey', 'queryFn'], ); if (hasRelationships && useCentralizedKeys) { return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); @@ -187,7 +245,7 @@ export function generateListQueryHook( ' first: 10,', ' },', '});', - '```' + '```', ]; if (hasRelationships && useCentralizedKeys) { docLines.push(''); @@ -205,16 +263,23 @@ export function generateListQueryHook( t.tsTypeLiteral([ t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) - ) + t.tsTypeAnnotation( + withFieldsListSelectionType( + sRef(), + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ), + ), ]), - buildOptionsType(listResultTypeAST(sRef()), typeRef('TData')) + buildOptionsType(listResultTypeAST(sRef()), typeRef('TData')), ]); const o1 = exportDeclareFunction( hookName, createSAndTDataTypeParams(selectTypeName, listResultTypeAST(sRef())), [createFunctionParam('params', o1ParamType)], - typeRef('UseQueryResult', [typeRef('TData')]) + typeRef('UseQueryResult', [typeRef('TData')]), ); addJSDocComment(o1, docLines); statements.push(o1); @@ -222,9 +287,15 @@ export function generateListQueryHook( // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), + ), ); - const implOptionsType= (() => { + const implOptionsType = (() => { const base = useQueryOptionsImplType(); if (hasRelationships && useCentralizedKeys) { return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); @@ -233,32 +304,54 @@ export function generateListQueryHook( })(); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), - implOptionsType + implOptionsType, ]); const body: t.Statement[] = []; - body.push(constDecl('selection', t.memberExpression(t.identifier('params'), t.identifier('selection')))); - body.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); + body.push( + constDecl( + 'selection', + t.memberExpression(t.identifier('params'), t.identifier('selection')), + ), + ); + body.push( + buildListSelectionArgsCall( + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ); if (hasRelationships && useCentralizedKeys) { body.push(destructureParamsWithSelectionAndScope('queryOptions')); body.push(voidStatement('_selection')); - body.push(returnUseQuery( - buildListQueryKey(t.identifier('args'), t.identifier('scope')), - buildFindManyFn(), - [spreadObj(t.identifier('queryOptions'))] - )); + body.push( + returnUseQuery( + buildListQueryKey(t.identifier('args'), t.identifier('scope')), + buildFindManyFn(), + [spreadObj(t.identifier('queryOptions'))], + ), + ); } else { body.push(destructureParamsWithSelection('queryOptions')); body.push(voidStatement('_selection')); - body.push(returnUseQuery( - buildListQueryKey(t.identifier('args')), - buildFindManyFn(), - [spreadObj(t.identifier('queryOptions'))] - )); + body.push( + returnUseQuery( + buildListQueryKey(t.identifier('args')), + buildFindManyFn(), + [spreadObj(t.identifier('queryOptions'))], + ), + ); } - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType)], + body, + ), + ); } // Fetch function @@ -268,14 +361,21 @@ export function generateListQueryHook( const f1ParamType = t.tsTypeLiteral([ t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) - ) + t.tsTypeAnnotation( + withFieldsListSelectionType( + sRef(), + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ), + ), ]); const f1Decl = exportAsyncDeclareFunction( fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', f1ParamType)], - typeRef('Promise', [listResultTypeAST(sRef())]) + typeRef('Promise', [listResultTypeAST(sRef())]), ); addJSDocComment(f1Decl, [ `Fetch ${typeName} list without React hooks`, @@ -288,19 +388,38 @@ export function generateListQueryHook( ' first: 10,', ' },', '});', - '```' + '```', ]); statements.push(f1Decl); // Implementation const fImplSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), + ), ); const fBody: t.Statement[] = []; - fBody.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); + fBody.push( + buildListSelectionArgsCall( + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ); fBody.push(t.returnStatement(buildFindManyCallExpr(singularName, 'args'))); - statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]))], fBody)); + statements.push( + exportAsyncFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]))], + fBody, + ), + ); } // Prefetch function @@ -311,17 +430,31 @@ export function generateListQueryHook( const p1Params: t.TSPropertySignature[] = [ t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(withFieldsListSelectionType(sRef(), selectTypeName, filterTypeName, orderByTypeName)) - ) + t.tsTypeAnnotation( + withFieldsListSelectionType( + sRef(), + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ), + ), ]; - const p1ParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral(p1Params), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral(p1Params); + const p1ParamType = + hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([ + t.tsTypeLiteral(p1Params), + scopeTypeLiteral(scopeTypeName), + ]) + : t.tsTypeLiteral(p1Params); const p1Decl = exportAsyncDeclareFunction( prefetchFnName, createSTypeParam(selectTypeName), - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p1ParamType)], - typeRef('Promise', [t.tsVoidKeyword()]) + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', p1ParamType), + ], + typeRef('Promise', [t.tsVoidKeyword()]), ); addJSDocComment(p1Decl, [ `Prefetch ${typeName} list for SSR or cache warming`, @@ -329,32 +462,62 @@ export function generateListQueryHook( '@example', '```ts', `await ${prefetchFnName}(queryClient, { selection: { first: 10 } });`, - '```' + '```', ]); statements.push(p1Decl); // Implementation const pImplSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(listSelectionConfigType(typeRef(selectTypeName), filterTypeName, orderByTypeName)) + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), + ), ); - const pImplParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral([pImplSelProp]), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral([pImplSelProp]); + const pImplParamType = + hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([ + t.tsTypeLiteral([pImplSelProp]), + scopeTypeLiteral(scopeTypeName), + ]) + : t.tsTypeLiteral([pImplSelProp]); const pBody: t.Statement[] = []; - pBody.push(buildListSelectionArgsCall(selectTypeName, filterTypeName, orderByTypeName)); + pBody.push( + buildListSelectionArgsCall( + selectTypeName, + filterTypeName, + orderByTypeName, + ), + ); - const queryKeyExpr = hasRelationships && useCentralizedKeys - ? buildListQueryKey(t.identifier('args'), t.optionalMemberExpression(t.identifier('params'), t.identifier('scope'), false, true)) - : buildListQueryKey(t.identifier('args')); + const queryKeyExpr = + hasRelationships && useCentralizedKeys + ? buildListQueryKey( + t.identifier('args'), + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('scope'), + false, + true, + ), + ) + : buildListQueryKey(t.identifier('args')); const prefetchCall = callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression([ - objectProp('queryKey', queryKeyExpr), - objectProp('queryFn', buildFindManyFn()) - ])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('prefetchQuery'), + ), + [ + t.objectExpression([ + objectProp('queryKey', queryKeyExpr), + objectProp('queryFn', buildFindManyFn()), + ]), + ], ); pBody.push(t.expressionStatement(t.awaitExpression(prefetchCall))); @@ -362,10 +525,13 @@ export function generateListQueryHook( exportAsyncFunction( prefetchFnName, null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType)], + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', pImplParamType), + ], pBody, - t.tsVoidKeyword() - ) + t.tsVoidKeyword(), + ), ); } @@ -375,20 +541,20 @@ export function generateListQueryHook( return { fileName: getListQueryFileName(table), - content: generateHookFileCode(headerText, statements) + content: generateHookFileCode(headerText, statements), }; } export function generateSingleQueryHook( table: CleanTable, - options: QueryGeneratorOptions = {} + options: QueryGeneratorOptions = {}, ): GeneratedQueryFile | null { if (!hasValidPrimaryKey(table)) return null; const { reactQueryEnabled = true, useCentralizedKeys = true, - hasRelationships = false + hasRelationships = false, } = options; const { typeName, singularName } = getTableNames(table); const hookName = getSingleQueryHookName(table); @@ -403,32 +569,65 @@ export function generateSingleQueryHook( const pkFieldName = pkField?.name ?? 'id'; const pkFieldTsType = pkField?.tsType ?? 'string'; - const pkTsType: t.TSType = pkFieldTsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); - const singleResultTypeAST = (sel: t.TSType) => singleQueryResultType(queryName, relationTypeName, sel); + const pkTsType: t.TSType = + pkFieldTsType === 'string' ? t.tsStringKeyword() : t.tsNumberKeyword(); + const singleResultTypeAST = (sel: t.TSType) => + singleQueryResultType(queryName, relationTypeName, sel); const statements: t.Statement[] = []; // Imports if (reactQueryEnabled) { - statements.push(createImportDeclaration('@tanstack/react-query', ['useQuery'])); - statements.push(createImportDeclaration('@tanstack/react-query', ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], true)); + statements.push( + createImportDeclaration('@tanstack/react-query', ['useQuery']), + ); + statements.push( + createImportDeclaration( + '@tanstack/react-query', + ['UseQueryOptions', 'UseQueryResult', 'QueryClient'], + true, + ), + ); } statements.push(createImportDeclaration('../client', ['getClient'])); - statements.push(createImportDeclaration('../selection', ['buildSelectionArgs'])); - statements.push(createImportDeclaration('../selection', ['SelectionConfig'], true)); + statements.push( + createImportDeclaration('../selection', ['buildSelectionArgs']), + ); + statements.push( + createImportDeclaration('../selection', ['SelectionConfig'], true), + ); if (useCentralizedKeys) { statements.push(createImportDeclaration('../query-keys', [keysName])); if (hasRelationships) { - statements.push(createImportDeclaration('../query-keys', [scopeTypeName], true)); + statements.push( + createImportDeclaration('../query-keys', [scopeTypeName], true), + ); } } - statements.push(createImportDeclaration('../../orm/input-types', [selectTypeName, relationTypeName], true)); - statements.push(createImportDeclaration('../../orm/select-types', ['InferSelectResult', 'StrictSelect'], true)); + statements.push( + createImportDeclaration( + '../../orm/input-types', + [selectTypeName, relationTypeName], + true, + ), + ); + statements.push( + createImportDeclaration( + '../../orm/select-types', + ['InferSelectResult', 'StrictSelect'], + true, + ), + ); // Re-exports - statements.push(createTypeReExport([selectTypeName, relationTypeName], '../../orm/input-types')); + statements.push( + createTypeReExport( + [selectTypeName, relationTypeName], + '../../orm/input-types', + ), + ); // Query key if (useCentralizedKeys) { @@ -436,43 +635,65 @@ export function generateSingleQueryHook( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(`${queryName}QueryKey`), - t.memberExpression(t.identifier(keysName), t.identifier('detail')) - ) - ]) + t.memberExpression(t.identifier(keysName), t.identifier('detail')), + ), + ]), ); - addJSDocComment(keyDecl, ['Query key factory - re-exported from query-keys.ts']); + addJSDocComment(keyDecl, [ + 'Query key factory - re-exported from query-keys.ts', + ]); statements.push(keyDecl); } else { const keyFn = t.arrowFunctionExpression( [createFunctionParam('id', pkTsType)], - asConst(t.arrayExpression([t.stringLiteral(typeName.toLowerCase()), t.stringLiteral('detail'), t.identifier('id')])) + asConst( + t.arrayExpression([ + t.stringLiteral(typeName.toLowerCase()), + t.stringLiteral('detail'), + t.identifier('id'), + ]), + ), + ); + statements.push( + t.exportNamedDeclaration( + t.variableDeclaration('const', [ + t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn), + ]), + ), ); - statements.push(t.exportNamedDeclaration( - t.variableDeclaration('const', [t.variableDeclarator(t.identifier(`${queryName}QueryKey`), keyFn)]) - )); } // Helper for query key call - const buildDetailQueryKey = (pkExpr: t.Expression, scopeExpr?: t.Expression) => { + const buildDetailQueryKey = ( + pkExpr: t.Expression, + scopeExpr?: t.Expression, + ) => { if (useCentralizedKeys) { const args = [pkExpr]; if (scopeExpr) args.push(scopeExpr); - return callExpr(t.memberExpression(t.identifier(keysName), t.identifier('detail')), args); + return callExpr( + t.memberExpression(t.identifier(keysName), t.identifier('detail')), + args, + ); } return callExpr(t.identifier(`${queryName}QueryKey`), [pkExpr]); }; // Helper for findOne queryFn - const buildFindOneFn = () => t.arrowFunctionExpression( - [], - buildFindOneCallExpr(singularName, pkFieldName, 'args') - ); + const buildFindOneFn = () => + t.arrowFunctionExpression( + [], + buildFindOneCallExpr(singularName, pkFieldName, 'args'), + ); // Options type builder with optional scope - const buildSingleOptionsType = (queryDataType: t.TSType, dataType: t.TSType) => { + const buildSingleOptionsType = ( + queryDataType: t.TSType, + dataType: t.TSType, + ) => { const base = omitType( typeRef('UseQueryOptions', [queryDataType, typeRef('Error'), dataType]), - ['queryKey', 'queryFn'] + ['queryKey', 'queryFn'], ); if (hasRelationships && useCentralizedKeys) { return t.tsIntersectionType([base, scopeTypeLiteral(scopeTypeName)]); @@ -491,7 +712,7 @@ export function generateSingleQueryHook( ` ${pkFieldName}: 'some-id',`, ' selection: { fields: { id: true, name: true } },', '});', - '```' + '```', ]; if (hasRelationships && useCentralizedKeys) { docLines.push(''); @@ -506,21 +727,24 @@ export function generateSingleQueryHook( // Overload 1: with fields const o1Props = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName)) - ) + t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName)), + ), ]; const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral(o1Props), - buildSingleOptionsType(singleResultTypeAST(sRef()), typeRef('TData')) + buildSingleOptionsType(singleResultTypeAST(sRef()), typeRef('TData')), ]); const o1 = exportDeclareFunction( hookName, createSAndTDataTypeParams(selectTypeName, singleResultTypeAST(sRef())), [createFunctionParam('params', o1ParamType)], - typeRef('UseQueryResult', [typeRef('TData')]) + typeRef('UseQueryResult', [typeRef('TData')]), ); addJSDocComment(o1, docLines); statements.push(o1); @@ -528,11 +752,14 @@ export function generateSingleQueryHook( // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), ); const implProps = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - implSelProp + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + implSelProp, ]; const implOptionsType = (() => { const base = useQueryOptionsImplType(); @@ -543,39 +770,53 @@ export function generateSingleQueryHook( })(); const implParamType = t.tsIntersectionType([ t.tsTypeLiteral(implProps), - implOptionsType + implOptionsType, ]); const body: t.Statement[] = []; // const args = buildSelectionArgs(params.selection); const argsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')) + t.memberExpression(t.identifier('params'), t.identifier('selection')), ]); // @ts-ignore - argsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + argsCall.typeParameters = t.tsTypeParameterInstantiation([ + typeRef(selectTypeName), + ]); body.push(constDecl('args', argsCall)); - const pkMemberExpr = t.memberExpression(t.identifier('params'), t.identifier(pkFieldName)); + const pkMemberExpr = t.memberExpression( + t.identifier('params'), + t.identifier(pkFieldName), + ); if (hasRelationships && useCentralizedKeys) { body.push(destructureParamsWithSelectionAndScope('queryOptions')); body.push(voidStatement('_selection')); - body.push(returnUseQuery( - buildDetailQueryKey(pkMemberExpr, t.identifier('scope')), - buildFindOneFn(), - [spreadObj(t.identifier('queryOptions'))] - )); + body.push( + returnUseQuery( + buildDetailQueryKey(pkMemberExpr, t.identifier('scope')), + buildFindOneFn(), + [spreadObj(t.identifier('queryOptions'))], + ), + ); } else { body.push(destructureParamsWithSelection('queryOptions')); body.push(voidStatement('_selection')); - body.push(returnUseQuery( - buildDetailQueryKey(pkMemberExpr), - buildFindOneFn(), - [spreadObj(t.identifier('queryOptions'))] - )); + body.push( + returnUseQuery(buildDetailQueryKey(pkMemberExpr), buildFindOneFn(), [ + spreadObj(t.identifier('queryOptions')), + ]), + ); } - statements.push(exportFunction(hookName, null, [createFunctionParam('params', implParamType)], body)); + statements.push( + exportFunction( + hookName, + null, + [createFunctionParam('params', implParamType)], + body, + ), + ); } // Fetch function @@ -583,14 +824,20 @@ export function generateSingleQueryHook( { // Overload 1: with fields const f1Props = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))) + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName)), + ), ]; const f1Decl = exportAsyncDeclareFunction( fetchFnName, createSTypeParam(selectTypeName), [createFunctionParam('params', t.tsTypeLiteral(f1Props))], - typeRef('Promise', [singleResultTypeAST(sRef())]) + typeRef('Promise', [singleResultTypeAST(sRef())]), ); addJSDocComment(f1Decl, [ `Fetch a single ${typeName} without React hooks`, @@ -601,28 +848,44 @@ export function generateSingleQueryHook( ` ${pkFieldName}: 'some-id',`, ' selection: { fields: { id: true } },', '});', - '```' + '```', ]); statements.push(f1Decl); // Implementation const fImplSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), ); const fImplProps = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - fImplSelProp + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + fImplSelProp, ]; const fBody: t.Statement[] = []; const fArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')) + t.memberExpression(t.identifier('params'), t.identifier('selection')), ]); // @ts-ignore - fArgsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + fArgsCall.typeParameters = t.tsTypeParameterInstantiation([ + typeRef(selectTypeName), + ]); fBody.push(constDecl('args', fArgsCall)); - fBody.push(t.returnStatement(buildFindOneCallExpr(singularName, pkFieldName, 'args'))); - statements.push(exportAsyncFunction(fetchFnName, null, [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], fBody)); + fBody.push( + t.returnStatement( + buildFindOneCallExpr(singularName, pkFieldName, 'args'), + ), + ); + statements.push( + exportAsyncFunction( + fetchFnName, + null, + [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], + fBody, + ), + ); } // Prefetch function @@ -631,17 +894,30 @@ export function generateSingleQueryHook( // Overload 1: with fields const p1Props: t.TSPropertySignature[] = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - t.tsPropertySignature(t.identifier('selection'), t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName))) + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(withFieldsSelectionType(sRef(), selectTypeName)), + ), ]; - const p1ParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral(p1Props), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral(p1Props); + const p1ParamType = + hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([ + t.tsTypeLiteral(p1Props), + scopeTypeLiteral(scopeTypeName), + ]) + : t.tsTypeLiteral(p1Props); const p1Decl = exportAsyncDeclareFunction( prefetchFnName, createSTypeParam(selectTypeName), - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', p1ParamType)], - typeRef('Promise', [t.tsVoidKeyword()]) + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', p1ParamType), + ], + typeRef('Promise', [t.tsVoidKeyword()]), ); addJSDocComment(p1Decl, [ `Prefetch a single ${typeName} for SSR or cache warming`, @@ -649,44 +925,72 @@ export function generateSingleQueryHook( '@example', '```ts', `await ${prefetchFnName}(queryClient, { ${pkFieldName}: 'some-id' });`, - '```' + '```', ]); statements.push(p1Decl); // Implementation const pImplSelProp = t.tsPropertySignature( t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))) + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), ); const pImplProps: t.TSPropertySignature[] = [ - t.tsPropertySignature(t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType)), - pImplSelProp + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + pImplSelProp, ]; - const pImplParamType = hasRelationships && useCentralizedKeys - ? t.tsIntersectionType([t.tsTypeLiteral(pImplProps), scopeTypeLiteral(scopeTypeName)]) - : t.tsTypeLiteral(pImplProps); + const pImplParamType = + hasRelationships && useCentralizedKeys + ? t.tsIntersectionType([ + t.tsTypeLiteral(pImplProps), + scopeTypeLiteral(scopeTypeName), + ]) + : t.tsTypeLiteral(pImplProps); const pBody: t.Statement[] = []; const pArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')) + t.memberExpression(t.identifier('params'), t.identifier('selection')), ]); // @ts-ignore - pArgsCall.typeParameters = t.tsTypeParameterInstantiation([typeRef(selectTypeName)]); + pArgsCall.typeParameters = t.tsTypeParameterInstantiation([ + typeRef(selectTypeName), + ]); pBody.push(constDecl('args', pArgsCall)); - const queryKeyExpr = hasRelationships && useCentralizedKeys - ? buildDetailQueryKey( - t.memberExpression(t.identifier('params'), t.identifier(pkFieldName)), - t.optionalMemberExpression(t.identifier('params'), t.identifier('scope'), false, true) - ) - : buildDetailQueryKey(t.memberExpression(t.identifier('params'), t.identifier(pkFieldName))); + const queryKeyExpr = + hasRelationships && useCentralizedKeys + ? buildDetailQueryKey( + t.memberExpression( + t.identifier('params'), + t.identifier(pkFieldName), + ), + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('scope'), + false, + true, + ), + ) + : buildDetailQueryKey( + t.memberExpression( + t.identifier('params'), + t.identifier(pkFieldName), + ), + ); const prefetchCall = callExpr( - t.memberExpression(t.identifier('queryClient'), t.identifier('prefetchQuery')), - [t.objectExpression([ - objectProp('queryKey', queryKeyExpr), - objectProp('queryFn', buildFindOneFn()) - ])] + t.memberExpression( + t.identifier('queryClient'), + t.identifier('prefetchQuery'), + ), + [ + t.objectExpression([ + objectProp('queryKey', queryKeyExpr), + objectProp('queryFn', buildFindOneFn()), + ]), + ], ); pBody.push(t.expressionStatement(t.awaitExpression(prefetchCall))); @@ -694,10 +998,13 @@ export function generateSingleQueryHook( exportAsyncFunction( prefetchFnName, null, - [createFunctionParam('queryClient', typeRef('QueryClient')), createFunctionParam('params', pImplParamType)], + [ + createFunctionParam('queryClient', typeRef('QueryClient')), + createFunctionParam('params', pImplParamType), + ], pBody, - t.tsVoidKeyword() - ) + t.tsVoidKeyword(), + ), ); } @@ -707,13 +1014,13 @@ export function generateSingleQueryHook( return { fileName: getSingleQueryFileName(table), - content: generateHookFileCode(headerText, statements) + content: generateHookFileCode(headerText, statements), }; } export function generateAllQueryHooks( tables: CleanTable[], - options: QueryGeneratorOptions = {} + options: QueryGeneratorOptions = {}, ): GeneratedQueryFile[] { const files: GeneratedQueryFile[] = []; for (const table of tables) { diff --git a/graphql/codegen/src/core/codegen/query-keys.ts b/graphql/codegen/src/core/codegen/query-keys.ts index b9c09c468..9447cb1e4 100644 --- a/graphql/codegen/src/core/codegen/query-keys.ts +++ b/graphql/codegen/src/core/codegen/query-keys.ts @@ -10,17 +10,22 @@ */ import * as t from '@babel/types'; -import type { EntityRelationship,QueryKeyConfig } from '../../types/config'; -import type { CleanOperation,CleanTable } from '../../types/schema'; +import type { EntityRelationship, QueryKeyConfig } from '../../types/config'; +import type { CleanOperation, CleanTable } from '../../types/schema'; import { addJSDocComment, asConst, constArray, generateCode, keyofTypeof, - typedParam + typedParam, } from './babel-ast'; -import { getGeneratedFileHeader, getTableNames, lcFirst,ucFirst } from './utils'; +import { + getGeneratedFileHeader, + getTableNames, + lcFirst, + ucFirst, +} from './utils'; export interface QueryKeyGeneratorOptions { tables: CleanTable[]; @@ -38,7 +43,7 @@ export interface GeneratedQueryKeysFile { */ function getAncestors( entityName: string, - relationships: Record + relationships: Record, ): string[] { const relationship = relationships[entityName.toLowerCase()]; if (!relationship) return []; @@ -62,7 +67,7 @@ function getAncestors( */ function generateScopeTypeDeclaration( entityName: string, - relationships: Record + relationships: Record, ): t.ExportNamedDeclaration | null { const relationship = relationships[entityName.toLowerCase()]; if (!relationship) return null; @@ -80,7 +85,7 @@ function generateScopeTypeDeclaration( fkField = rel.foreignKey; } else { const directRel = Object.entries(relationships).find( - ([, r]) => r.parent === parent + ([, r]) => r.parent === parent, ); if (directRel) { fkField = directRel[1].foreignKey; @@ -89,7 +94,7 @@ function generateScopeTypeDeclaration( const signature = t.tsPropertySignature( t.identifier(fkField), - t.tsTypeAnnotation(t.tsStringKeyword()) + t.tsTypeAnnotation(t.tsStringKeyword()), ); signature.optional = true; members.push(signature); @@ -99,18 +104,21 @@ function generateScopeTypeDeclaration( t.tsTypeAliasDeclaration( t.identifier(typeName), null, - t.tsTypeLiteral(members) - ) + t.tsTypeLiteral(members), + ), ); } /** * Build the 'all' property: all: ['entityKey'] as const */ -function buildAllProperty(entityKey: string, singularName: string): t.ObjectProperty { +function buildAllProperty( + entityKey: string, + singularName: string, +): t.ObjectProperty { const prop = t.objectProperty( t.identifier('all'), - constArray([t.stringLiteral(entityKey)]) + constArray([t.stringLiteral(entityKey)]), ); addJSDocComment(prop, [`All ${singularName} queries`]); return prop; @@ -123,7 +131,7 @@ function buildByParentProperty( entityKey: string, typeName: string, parent: string, - fkField: string + fkField: string, ): t.ObjectProperty { const parentUpper = ucFirst(parent); const parentLower = lcFirst(parent); @@ -133,16 +141,20 @@ function buildByParentProperty( constArray([ t.stringLiteral(entityKey), t.objectExpression([ - t.objectProperty(t.identifier(fkField), t.identifier(fkField), false, true) - ]) - ]) + t.objectProperty( + t.identifier(fkField), + t.identifier(fkField), + false, + true, + ), + ]), + ]), ); - const prop = t.objectProperty( - t.identifier(`by${parentUpper}`), - arrowFn - ); - addJSDocComment(prop, [`${typeName} queries scoped to a specific ${parentLower}`]); + const prop = t.objectProperty(t.identifier(`by${parentUpper}`), arrowFn); + addJSDocComment(prop, [ + `${typeName} queries scoped to a specific ${parentLower}`, + ]); return prop; } @@ -153,10 +165,14 @@ function buildScopedProperty( keysName: string, typeName: string, relationship: EntityRelationship, - ancestors: string[] + ancestors: string[], ): t.ObjectProperty { const scopeTypeName = `${typeName}Scope`; - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const statements: t.Statement[] = []; @@ -167,20 +183,25 @@ function buildScopedProperty( t.identifier('scope'), t.identifier(relationship.foreignKey), false, - true + true, ), t.blockStatement([ t.returnStatement( t.callExpression( t.memberExpression( t.identifier(keysName), - t.identifier(`by${ucFirst(relationship.parent)}`) + t.identifier(`by${ucFirst(relationship.parent)}`), ), - [t.memberExpression(t.identifier('scope'), t.identifier(relationship.foreignKey))] - ) - ) - ]) - ) + [ + t.memberExpression( + t.identifier('scope'), + t.identifier(relationship.foreignKey), + ), + ], + ), + ), + ]), + ), ); } @@ -193,32 +214,37 @@ function buildScopedProperty( t.identifier('scope'), t.identifier(fkField), false, - true + true, ), t.blockStatement([ t.returnStatement( t.callExpression( t.memberExpression( t.identifier(keysName), - t.identifier(`by${ucFirst(ancestor)}`) + t.identifier(`by${ucFirst(ancestor)}`), ), - [t.memberExpression(t.identifier('scope'), t.identifier(fkField))] - ) - ) - ]) - ) + [ + t.memberExpression( + t.identifier('scope'), + t.identifier(fkField), + ), + ], + ), + ), + ]), + ), ); } statements.push( t.returnStatement( - t.memberExpression(t.identifier(keysName), t.identifier('all')) - ) + t.memberExpression(t.identifier(keysName), t.identifier('all')), + ), ); const arrowFn = t.arrowFunctionExpression( [scopeParam], - t.blockStatement(statements) + t.blockStatement(statements), ); const prop = t.objectProperty(t.identifier('scoped'), arrowFn); @@ -229,8 +255,15 @@ function buildScopedProperty( /** * Build lists property (scoped version) */ -function buildScopedListsProperty(keysName: string, scopeTypeName: string): t.ObjectProperty { - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); +function buildScopedListsProperty( + keysName: string, + scopeTypeName: string, +): t.ObjectProperty { + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const arrowFn = t.arrowFunctionExpression( [scopeParam], @@ -238,11 +271,11 @@ function buildScopedListsProperty(keysName: string, scopeTypeName: string): t.Ob t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('scoped')), - [t.identifier('scope')] - ) + [t.identifier('scope')], + ), ), - t.stringLiteral('list') - ]) + t.stringLiteral('list'), + ]), ); const prop = t.objectProperty(t.identifier('lists'), arrowFn); @@ -253,9 +286,20 @@ function buildScopedListsProperty(keysName: string, scopeTypeName: string): t.Ob /** * Build list property (scoped version) */ -function buildScopedListProperty(keysName: string, scopeTypeName: string): t.ObjectProperty { - const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), true); - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); +function buildScopedListProperty( + keysName: string, + scopeTypeName: string, +): t.ObjectProperty { + const variablesParam = typedParam( + 'variables', + t.tsTypeReference(t.identifier('object')), + true, + ); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const arrowFn = t.arrowFunctionExpression( [variablesParam, scopeParam], @@ -263,11 +307,11 @@ function buildScopedListProperty(keysName: string, scopeTypeName: string): t.Obj t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('lists')), - [t.identifier('scope')] - ) + [t.identifier('scope')], + ), ), - t.identifier('variables') - ]) + t.identifier('variables'), + ]), ); const prop = t.objectProperty(t.identifier('list'), arrowFn); @@ -278,8 +322,15 @@ function buildScopedListProperty(keysName: string, scopeTypeName: string): t.Obj /** * Build details property (scoped version) */ -function buildScopedDetailsProperty(keysName: string, scopeTypeName: string): t.ObjectProperty { - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); +function buildScopedDetailsProperty( + keysName: string, + scopeTypeName: string, +): t.ObjectProperty { + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const arrowFn = t.arrowFunctionExpression( [scopeParam], @@ -287,11 +338,11 @@ function buildScopedDetailsProperty(keysName: string, scopeTypeName: string): t. t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('scoped')), - [t.identifier('scope')] - ) + [t.identifier('scope')], + ), ), - t.stringLiteral('detail') - ]) + t.stringLiteral('detail'), + ]), ); const prop = t.objectProperty(t.identifier('details'), arrowFn); @@ -302,9 +353,19 @@ function buildScopedDetailsProperty(keysName: string, scopeTypeName: string): t. /** * Build detail property (scoped version) */ -function buildScopedDetailProperty(keysName: string, scopeTypeName: string): t.ObjectProperty { - const idParam = typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()])); - const scopeParam = typedParam('scope', t.tsTypeReference(t.identifier(scopeTypeName)), true); +function buildScopedDetailProperty( + keysName: string, + scopeTypeName: string, +): t.ObjectProperty { + const idParam = typedParam( + 'id', + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]), + ); + const scopeParam = typedParam( + 'scope', + t.tsTypeReference(t.identifier(scopeTypeName)), + true, + ); const arrowFn = t.arrowFunctionExpression( [idParam, scopeParam], @@ -312,11 +373,11 @@ function buildScopedDetailProperty(keysName: string, scopeTypeName: string): t.O t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('details')), - [t.identifier('scope')] - ) + [t.identifier('scope')], + ), ), - t.identifier('id') - ]) + t.identifier('id'), + ]), ); const prop = t.objectProperty(t.identifier('detail'), arrowFn); @@ -331,9 +392,11 @@ function buildSimpleListsProperty(keysName: string): t.ObjectProperty { const arrowFn = t.arrowFunctionExpression( [], constArray([ - t.spreadElement(t.memberExpression(t.identifier(keysName), t.identifier('all'))), - t.stringLiteral('list') - ]) + t.spreadElement( + t.memberExpression(t.identifier(keysName), t.identifier('all')), + ), + t.stringLiteral('list'), + ]), ); const prop = t.objectProperty(t.identifier('lists'), arrowFn); @@ -345,7 +408,11 @@ function buildSimpleListsProperty(keysName: string): t.ObjectProperty { * Build simple (non-scoped) list property */ function buildSimpleListProperty(keysName: string): t.ObjectProperty { - const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), true); + const variablesParam = typedParam( + 'variables', + t.tsTypeReference(t.identifier('object')), + true, + ); const arrowFn = t.arrowFunctionExpression( [variablesParam], @@ -353,11 +420,11 @@ function buildSimpleListProperty(keysName: string): t.ObjectProperty { t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('lists')), - [] - ) + [], + ), ), - t.identifier('variables') - ]) + t.identifier('variables'), + ]), ); const prop = t.objectProperty(t.identifier('list'), arrowFn); @@ -372,9 +439,11 @@ function buildSimpleDetailsProperty(keysName: string): t.ObjectProperty { const arrowFn = t.arrowFunctionExpression( [], constArray([ - t.spreadElement(t.memberExpression(t.identifier(keysName), t.identifier('all'))), - t.stringLiteral('detail') - ]) + t.spreadElement( + t.memberExpression(t.identifier(keysName), t.identifier('all')), + ), + t.stringLiteral('detail'), + ]), ); const prop = t.objectProperty(t.identifier('details'), arrowFn); @@ -386,7 +455,10 @@ function buildSimpleDetailsProperty(keysName: string): t.ObjectProperty { * Build simple (non-scoped) detail property */ function buildSimpleDetailProperty(keysName: string): t.ObjectProperty { - const idParam = typedParam('id', t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()])); + const idParam = typedParam( + 'id', + t.tsUnionType([t.tsStringKeyword(), t.tsNumberKeyword()]), + ); const arrowFn = t.arrowFunctionExpression( [idParam], @@ -394,11 +466,11 @@ function buildSimpleDetailProperty(keysName: string): t.ObjectProperty { t.spreadElement( t.callExpression( t.memberExpression(t.identifier(keysName), t.identifier('details')), - [] - ) + [], + ), ), - t.identifier('id') - ]) + t.identifier('id'), + ]), ); const prop = t.objectProperty(t.identifier('detail'), arrowFn); @@ -412,7 +484,7 @@ function buildSimpleDetailProperty(keysName: string): t.ObjectProperty { function generateEntityKeysDeclaration( table: CleanTable, relationships: Record, - generateScopedKeys: boolean + generateScopedKeys: boolean, ): t.ExportNamedDeclaration { const { typeName, singularName } = getTableNames(table); const entityKey = typeName.toLowerCase(); @@ -434,10 +506,14 @@ function generateEntityKeysDeclaration( if (relationship.parent === parent) { fkField = relationship.foreignKey; } - properties.push(buildByParentProperty(entityKey, typeName, parent, fkField)); + properties.push( + buildByParentProperty(entityKey, typeName, parent, fkField), + ); } - properties.push(buildScopedProperty(keysName, typeName, relationship, ancestors)); + properties.push( + buildScopedProperty(keysName, typeName, relationship, ancestors), + ); const scopeTypeName = `${typeName}Scope`; properties.push(buildScopedListsProperty(keysName, scopeTypeName)); @@ -455,9 +531,9 @@ function generateEntityKeysDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier(keysName), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); } @@ -465,7 +541,7 @@ function generateEntityKeysDeclaration( * Generate query keys declaration for custom operations */ function generateCustomQueryKeysDeclaration( - operations: CleanOperation[] + operations: CleanOperation[], ): t.ExportNamedDeclaration | null { if (operations.length === 0) return null; @@ -473,25 +549,27 @@ function generateCustomQueryKeysDeclaration( for (const op of operations) { const hasArgs = op.args.length > 0; - const hasRequiredArgs = op.args.some( - (arg) => arg.type.kind === 'NON_NULL' - ); + const hasRequiredArgs = op.args.some((arg) => arg.type.kind === 'NON_NULL'); let prop: t.ObjectProperty; if (hasArgs) { - const variablesParam = typedParam('variables', t.tsTypeReference(t.identifier('object')), !hasRequiredArgs); + const variablesParam = typedParam( + 'variables', + t.tsTypeReference(t.identifier('object')), + !hasRequiredArgs, + ); const arrowFn = t.arrowFunctionExpression( [variablesParam], - constArray([t.stringLiteral(op.name), t.identifier('variables')]) + constArray([t.stringLiteral(op.name), t.identifier('variables')]), ); prop = t.objectProperty(t.identifier(op.name), arrowFn); } else { const arrowFn = t.arrowFunctionExpression( [], - constArray([t.stringLiteral(op.name)]) + constArray([t.stringLiteral(op.name)]), ); prop = t.objectProperty(t.identifier(op.name), arrowFn); @@ -505,9 +583,9 @@ function generateCustomQueryKeysDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('customQueryKeys'), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); } @@ -516,7 +594,7 @@ function generateCustomQueryKeysDeclaration( */ function generateUnifiedStoreDeclaration( tables: CleanTable[], - hasCustomQueries: boolean + hasCustomQueries: boolean, ): t.ExportNamedDeclaration { const properties: t.ObjectProperty[] = []; @@ -524,13 +602,13 @@ function generateUnifiedStoreDeclaration( const { typeName } = getTableNames(table); const keysName = `${lcFirst(typeName)}Keys`; properties.push( - t.objectProperty(t.identifier(lcFirst(typeName)), t.identifier(keysName)) + t.objectProperty(t.identifier(lcFirst(typeName)), t.identifier(keysName)), ); } if (hasCustomQueries) { properties.push( - t.objectProperty(t.identifier('custom'), t.identifier('customQueryKeys')) + t.objectProperty(t.identifier('custom'), t.identifier('customQueryKeys')), ); } @@ -538,9 +616,9 @@ function generateUnifiedStoreDeclaration( t.variableDeclaration('const', [ t.variableDeclarator( t.identifier('queryKeys'), - asConst(t.objectExpression(properties)) - ) - ]) + asConst(t.objectExpression(properties)), + ), + ]), ); addJSDocComment(decl, [ @@ -558,7 +636,7 @@ function generateUnifiedStoreDeclaration( '', '// Invalidate specific user', 'queryClient.invalidateQueries({ queryKey: queryKeys.user.detail(userId) });', - '```' + '```', ]); return decl; @@ -568,7 +646,7 @@ function generateUnifiedStoreDeclaration( * Generate the complete query-keys.ts file */ export function generateQueryKeysFile( - options: QueryKeyGeneratorOptions + options: QueryKeyGeneratorOptions, ): GeneratedQueryKeysFile { const { tables, customQueries, config } = options; const { relationships, generateScopedKeys } = config; @@ -593,7 +671,9 @@ export function generateQueryKeysFile( // Generate entity keys for (const table of tables) { - statements.push(generateEntityKeysDeclaration(table, relationships, generateScopedKeys)); + statements.push( + generateEntityKeysDeclaration(table, relationships, generateScopedKeys), + ); } // Generate custom query keys @@ -604,17 +684,21 @@ export function generateQueryKeysFile( } // Generate unified store - statements.push(generateUnifiedStoreDeclaration(tables, queryOperations.length > 0)); + statements.push( + generateUnifiedStoreDeclaration(tables, queryOperations.length > 0), + ); // Generate QueryKeyScope type const scopeTypeDecl = t.exportNamedDeclaration( t.tsTypeAliasDeclaration( t.identifier('QueryKeyScope'), null, - keyofTypeof('queryKeys') - ) + keyofTypeof('queryKeys'), + ), ); - addJSDocComment(scopeTypeDecl, ['Type representing all available query key scopes']); + addJSDocComment(scopeTypeDecl, [ + 'Type representing all available query key scopes', + ]); statements.push(scopeTypeDecl); // Generate code from AST @@ -641,7 +725,7 @@ ${description} // Add scope types section if present if (generateScopedKeys && Object.keys(relationships).length > 0) { - const hasScopes = tables.some(table => { + const hasScopes = tables.some((table) => { const { typeName } = getTableNames(table); return !!relationships[typeName.toLowerCase()]; }); @@ -656,7 +740,8 @@ ${description} // Insert section comments into the generated code const codeLines = code.split('\n'); - let inScopeTypes = generateScopedKeys && Object.keys(relationships).length > 0; + let inScopeTypes = + generateScopedKeys && Object.keys(relationships).length > 0; let addedEntitySection = false; let addedCustomSection = false; let addedUnifiedSection = false; @@ -665,7 +750,11 @@ ${description} const line = codeLines[i]; // Detect transition from scope types to entity keys - if (inScopeTypes && line.startsWith('export const') && line.includes('Keys =')) { + if ( + inScopeTypes && + line.startsWith('export const') && + line.includes('Keys =') + ) { content += `// ============================================================================ // Entity Query Keys // ============================================================================ @@ -676,7 +765,10 @@ ${description} } // Detect custom query keys section - if (!addedCustomSection && line.startsWith('export const customQueryKeys')) { + if ( + !addedCustomSection && + line.startsWith('export const customQueryKeys') + ) { content += ` // ============================================================================ // Custom Query Keys @@ -704,16 +796,19 @@ ${description} if (!addedEntitySection && !inScopeTypes) { const firstExportIndex = content.indexOf('\nexport const'); if (firstExportIndex !== -1) { - content = content.slice(0, firstExportIndex) + ` + content = + content.slice(0, firstExportIndex) + + ` // ============================================================================ // Entity Query Keys // ============================================================================ -` + content.slice(firstExportIndex); +` + + content.slice(firstExportIndex); } } return { fileName: 'query-keys.ts', - content + content, }; } diff --git a/graphql/codegen/src/core/codegen/scalars.ts b/graphql/codegen/src/core/codegen/scalars.ts index f13047967..579eb3398 100644 --- a/graphql/codegen/src/core/codegen/scalars.ts +++ b/graphql/codegen/src/core/codegen/scalars.ts @@ -40,7 +40,7 @@ export const SCALAR_TS_MAP: Record = { TsQuery: 'string', // File upload - Upload: 'File' + Upload: 'File', }; export const SCALAR_FILTER_MAP: Record = { @@ -59,7 +59,7 @@ export const SCALAR_FILTER_MAP: Record = { BitString: 'BitStringFilter', InternetAddress: 'InternetAddressFilter', FullText: 'FullTextFilter', - Interval: 'StringFilter' + Interval: 'StringFilter', }; export const SCALAR_NAMES = new Set(Object.keys(SCALAR_TS_MAP)); @@ -70,18 +70,28 @@ const LIST_FILTER_SCALARS = new Set(['String', 'Int', 'UUID']); /** All base filter type names - skip these in schema-types.ts to avoid duplicates */ export const BASE_FILTER_TYPE_NAMES = new Set([ ...new Set(Object.values(SCALAR_FILTER_MAP)), - ...Array.from(LIST_FILTER_SCALARS).map((s) => `${s}ListFilter`) + ...Array.from(LIST_FILTER_SCALARS).map((s) => `${s}ListFilter`), ]); export function scalarToTsType( scalarName: string, - options: { unknownScalar?: 'unknown' | 'name'; overrides?: Record } = {} + options: { + unknownScalar?: 'unknown' | 'name'; + overrides?: Record; + } = {}, ): string { - return options.overrides?.[scalarName] ?? SCALAR_TS_MAP[scalarName] ?? (options.unknownScalar === 'unknown' ? 'unknown' : scalarName); + return ( + options.overrides?.[scalarName] ?? + SCALAR_TS_MAP[scalarName] ?? + (options.unknownScalar === 'unknown' ? 'unknown' : scalarName) + ); } /** Get the filter type for a scalar (handles both scalar and array types) */ -export function scalarToFilterType(scalarName: string, isArray = false): string | null { +export function scalarToFilterType( + scalarName: string, + isArray = false, +): string | null { const baseName = scalarName === 'ID' ? 'UUID' : scalarName; if (isArray) { return LIST_FILTER_SCALARS.has(baseName) ? `${baseName}ListFilter` : null; diff --git a/graphql/codegen/src/core/codegen/schema-types-generator.ts b/graphql/codegen/src/core/codegen/schema-types-generator.ts index 0e8e4b6a1..c2be2b36b 100644 --- a/graphql/codegen/src/core/codegen/schema-types-generator.ts +++ b/graphql/codegen/src/core/codegen/schema-types-generator.ts @@ -16,13 +16,13 @@ import * as t from '@babel/types'; import type { CleanArgument, ResolvedType, - TypeRegistry + TypeRegistry, } from '../../types/schema'; import { generateCode } from './babel-ast'; import { BASE_FILTER_TYPE_NAMES, SCALAR_NAMES, - scalarToTsType + scalarToTsType, } from './scalars'; import { getTypeBaseName } from './type-resolver'; import { getGeneratedFileHeader } from './utils'; @@ -50,7 +50,7 @@ const SKIP_TYPES = new Set([ '__InputValue', '__EnumValue', '__Directive', - ...BASE_FILTER_TYPE_NAMES + ...BASE_FILTER_TYPE_NAMES, ]); const SKIP_TYPE_PATTERNS: RegExp[] = []; @@ -80,7 +80,7 @@ function isRequired(typeRef: CleanArgument['type']): boolean { function shouldSkipType( typeName: string, - tableTypeNames: Set + tableTypeNames: Set, ): boolean { if (SKIP_TYPES.has(typeName)) return true; if (tableTypeNames.has(typeName)) return true; @@ -94,7 +94,7 @@ function shouldSkipType( function generateEnumTypes( typeRegistry: TypeRegistry, - tableTypeNames: Set + tableTypeNames: Set, ): { statements: t.Statement[]; generatedTypes: Set } { const statements: t.Statement[] = []; const generatedTypes = new Set(); @@ -105,12 +105,12 @@ function generateEnumTypes( if (!typeInfo.enumValues || typeInfo.enumValues.length === 0) continue; const unionType = t.tsUnionType( - typeInfo.enumValues.map((v) => t.tsLiteralType(t.stringLiteral(v))) + typeInfo.enumValues.map((v) => t.tsLiteralType(t.stringLiteral(v))), ); const typeAlias = t.tsTypeAliasDeclaration( t.identifier(typeName), null, - unionType + unionType, ); statements.push(t.exportNamedDeclaration(typeAlias)); generatedTypes.add(typeName); @@ -122,7 +122,7 @@ function generateEnumTypes( function generateInputObjectTypes( typeRegistry: TypeRegistry, tableTypeNames: Set, - alreadyGenerated: Set + alreadyGenerated: Set, ): { statements: t.Statement[]; generatedTypes: Set } { const statements: t.Statement[] = []; const generatedTypes = new Set(alreadyGenerated); @@ -157,7 +157,7 @@ function generateInputObjectTypes( const prop = t.tsPropertySignature( t.identifier(field.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(tsType))), ); prop.optional = optional; properties.push(prop); @@ -180,7 +180,7 @@ function generateInputObjectTypes( t.identifier(typeName), null, null, - t.tsInterfaceBody(properties) + t.tsInterfaceBody(properties), ); statements.push(t.exportNamedDeclaration(interfaceDecl)); } @@ -191,7 +191,7 @@ function generateInputObjectTypes( function generateUnionTypes( typeRegistry: TypeRegistry, tableTypeNames: Set, - alreadyGenerated: Set + alreadyGenerated: Set, ): { statements: t.Statement[]; generatedTypes: Set } { const statements: t.Statement[] = []; const generatedTypes = new Set(alreadyGenerated); @@ -200,15 +200,16 @@ function generateUnionTypes( if (typeInfo.kind !== 'UNION') continue; if (shouldSkipType(typeName, tableTypeNames)) continue; if (generatedTypes.has(typeName)) continue; - if (!typeInfo.possibleTypes || typeInfo.possibleTypes.length === 0) continue; + if (!typeInfo.possibleTypes || typeInfo.possibleTypes.length === 0) + continue; const unionType = t.tsUnionType( - typeInfo.possibleTypes.map((pt) => t.tsTypeReference(t.identifier(pt))) + typeInfo.possibleTypes.map((pt) => t.tsTypeReference(t.identifier(pt))), ); const typeAlias = t.tsTypeAliasDeclaration( t.identifier(typeName), null, - unionType + unionType, ); statements.push(t.exportNamedDeclaration(typeAlias)); generatedTypes.add(typeName); @@ -225,7 +226,7 @@ export interface PayloadTypesResult { function collectReturnTypesFromRootTypes( typeRegistry: TypeRegistry, - tableTypeNames: Set + tableTypeNames: Set, ): Set { const returnTypes = new Set(); @@ -254,7 +255,7 @@ function collectReturnTypesFromRootTypes( function generatePayloadObjectTypes( typeRegistry: TypeRegistry, tableTypeNames: Set, - alreadyGenerated: Set + alreadyGenerated: Set, ): PayloadTypesResult { const statements: t.Statement[] = []; const generatedTypes = new Set(alreadyGenerated); @@ -262,7 +263,7 @@ function generatePayloadObjectTypes( const typesToGenerate = collectReturnTypesFromRootTypes( typeRegistry, - tableTypeNames + tableTypeNames, ); for (const typeName of Array.from(typesToGenerate)) { @@ -299,7 +300,7 @@ function generatePayloadObjectTypes( const prop = t.tsPropertySignature( t.identifier(field.name), - t.tsTypeAnnotation(t.tsTypeReference(t.identifier(finalType))) + t.tsTypeAnnotation(t.tsTypeReference(t.identifier(finalType))), ); prop.optional = isNullable; properties.push(prop); @@ -325,7 +326,7 @@ function generatePayloadObjectTypes( t.identifier(typeName), null, null, - t.tsInterfaceBody(properties) + t.tsInterfaceBody(properties), ); statements.push(t.exportNamedDeclaration(interfaceDecl)); } @@ -334,7 +335,7 @@ function generatePayloadObjectTypes( } export function generateSchemaTypesFile( - options: GenerateSchemaTypesOptions + options: GenerateSchemaTypesOptions, ): GeneratedSchemaTypesFile { const { typeRegistry, tableTypeNames } = options; @@ -347,33 +348,35 @@ export function generateSchemaTypesFile( const unionResult = generateUnionTypes( typeRegistry, tableTypeNames, - generatedTypes + generatedTypes, ); generatedTypes = new Set([...generatedTypes, ...unionResult.generatedTypes]); const inputResult = generateInputObjectTypes( typeRegistry, tableTypeNames, - generatedTypes + generatedTypes, ); generatedTypes = new Set([...generatedTypes, ...inputResult.generatedTypes]); const payloadResult = generatePayloadObjectTypes( typeRegistry, tableTypeNames, - generatedTypes + generatedTypes, ); const referencedTableTypes = Array.from( - payloadResult.referencedTableTypes + payloadResult.referencedTableTypes, ).sort(); const baseFilterImports = Array.from(BASE_FILTER_TYPE_NAMES).sort(); const allTypesImports = [...referencedTableTypes, ...baseFilterImports]; if (allTypesImports.length > 0) { const typesImport = t.importDeclaration( - allTypesImports.map((ti) => t.importSpecifier(t.identifier(ti), t.identifier(ti))), - t.stringLiteral('./types') + allTypesImports.map((ti) => + t.importSpecifier(t.identifier(ti), t.identifier(ti)), + ), + t.stringLiteral('./types'), ); typesImport.importKind = 'type'; allStatements.push(typesImport); @@ -385,12 +388,15 @@ export function generateSchemaTypesFile( allStatements.push(...payloadResult.statements); const code = generateCode(allStatements); - const content = getGeneratedFileHeader('GraphQL schema types for custom operations') + '\n\n' + code; + const content = + getGeneratedFileHeader('GraphQL schema types for custom operations') + + '\n\n' + + code; return { fileName: 'schema-types.ts', content, generatedEnums: Array.from(enumResult.generatedTypes).sort(), - referencedTableTypes + referencedTableTypes, }; } diff --git a/graphql/codegen/src/core/codegen/select-helpers.ts b/graphql/codegen/src/core/codegen/select-helpers.ts index 6360672b2..76d6d89ab 100644 --- a/graphql/codegen/src/core/codegen/select-helpers.ts +++ b/graphql/codegen/src/core/codegen/select-helpers.ts @@ -13,14 +13,16 @@ import { getTypeBaseName } from './type-resolver'; export const NON_SELECT_TYPES = new Set([ ...SCALAR_NAMES, 'Query', - 'Mutation' + 'Mutation', ]); /** * Get the Select type name for a return type. * Returns null for scalar types, Connection types, and root types. */ -export function getSelectTypeName(returnType: CleanArgument['type']): string | null { +export function getSelectTypeName( + returnType: CleanArgument['type'], +): string | null { const baseName = getTypeBaseName(returnType); if ( baseName && @@ -38,10 +40,14 @@ export function getSelectTypeName(returnType: CleanArgument['type']): string | n export function wrapInferSelectResult( typeRef: CleanArgument['type'], payloadTypeName: string, - selectType: string = 'S' + selectType: string = 'S', ): string { if (typeRef.kind === 'NON_NULL' && typeRef.ofType) { - return wrapInferSelectResult(typeRef.ofType as CleanArgument['type'], payloadTypeName, selectType); + return wrapInferSelectResult( + typeRef.ofType as CleanArgument['type'], + payloadTypeName, + selectType, + ); } if (typeRef.kind === 'LIST' && typeRef.ofType) { diff --git a/graphql/codegen/src/core/codegen/selection.ts b/graphql/codegen/src/core/codegen/selection.ts index e9cd7fe3c..73a3fb5c7 100644 --- a/graphql/codegen/src/core/codegen/selection.ts +++ b/graphql/codegen/src/core/codegen/selection.ts @@ -15,7 +15,7 @@ function findTemplateFile(templateName: string): string { return templatePath; } throw new Error( - `Could not find template file: ${templateName}. Searched in: ${templatePath}` + `Could not find template file: ${templateName}. Searched in: ${templatePath}`, ); } @@ -26,7 +26,7 @@ function readTemplateFile(templateName: string, description: string): string { /\/\*\*[\s\S]*?\* NOTE: This file is read at codegen time and written to output\.[\s\S]*?\*\/\n*/; content = content.replace( headerPattern, - getGeneratedFileHeader(description) + '\n' + getGeneratedFileHeader(description) + '\n', ); return content; } @@ -35,5 +35,8 @@ function readTemplateFile(templateName: string, description: string): string { * Generate selection.ts content - shared selection types + runtime mappers */ export function generateSelectionFile(): string { - return readTemplateFile('hooks-selection.ts', 'Selection helpers for React Query hooks'); + return readTemplateFile( + 'hooks-selection.ts', + 'Selection helpers for React Query hooks', + ); } diff --git a/graphql/codegen/src/core/codegen/shared/index.ts b/graphql/codegen/src/core/codegen/shared/index.ts index ccc31ead8..730194b94 100644 --- a/graphql/codegen/src/core/codegen/shared/index.ts +++ b/graphql/codegen/src/core/codegen/shared/index.ts @@ -14,8 +14,12 @@ import * as t from '@babel/types'; import type { GraphQLSDKConfigTarget } from '../../../types/config'; -import type { CleanOperation, CleanTable, TypeRegistry } from '../../../types/schema'; -import { addJSDocComment,generateCode } from '../babel-ast'; +import type { + CleanOperation, + CleanTable, + TypeRegistry, +} from '../../../types/schema'; +import { addJSDocComment, generateCode } from '../babel-ast'; import { generateSchemaTypesFile } from '../schema-types-generator'; import { generateTypesFile } from '../types'; import { getTableNames } from '../utils'; @@ -51,7 +55,9 @@ export interface GenerateSharedResult { /** * Generate shared types that can be imported by both React Query SDK and ORM client */ -export function generateSharedTypes(options: GenerateSharedOptions): GenerateSharedResult { +export function generateSharedTypes( + options: GenerateSharedOptions, +): GenerateSharedResult { const { tables, customOperations } = options; const files: GeneratedFile[] = []; @@ -65,14 +71,14 @@ export function generateSharedTypes(options: GenerateSharedOptions): GenerateSha if (customOperations && customOperations.typeRegistry) { const schemaTypesResult = generateSchemaTypesFile({ typeRegistry: customOperations.typeRegistry, - tableTypeNames + tableTypeNames, }); // Only include if there's meaningful content if (schemaTypesResult.content.split('\n').length > 10) { files.push({ path: 'schema-types.ts', - content: schemaTypesResult.content + content: schemaTypesResult.content, }); hasSchemaTypes = true; generatedEnumNames = schemaTypesResult.generatedEnums || []; @@ -83,21 +89,21 @@ export function generateSharedTypes(options: GenerateSharedOptions): GenerateSha files.push({ path: 'types.ts', content: generateTypesFile(tables, { - enumsFromSchemaTypes: generatedEnumNames - }) + enumsFromSchemaTypes: generatedEnumNames, + }), }); // 3. Generate barrel export (index.ts) const barrelContent = generateSharedBarrel(hasSchemaTypes); files.push({ path: 'index.ts', - content: barrelContent + content: barrelContent, }); return { files, generatedEnumNames, - hasSchemaTypes + hasSchemaTypes, }; } @@ -119,7 +125,7 @@ function generateSharedBarrel(hasSchemaTypes: boolean): string { if (statements.length > 0) { addJSDocComment(statements[0], [ 'Shared types - auto-generated, do not edit', - '@generated by @constructive-io/graphql-codegen' + '@generated by @constructive-io/graphql-codegen', ]); } diff --git a/graphql/codegen/src/core/codegen/templates/hooks-client.ts b/graphql/codegen/src/core/codegen/templates/hooks-client.ts index 197fad4c5..dbbaa1bc5 100644 --- a/graphql/codegen/src/core/codegen/templates/hooks-client.ts +++ b/graphql/codegen/src/core/codegen/templates/hooks-client.ts @@ -42,7 +42,7 @@ export function configure(config: OrmClientConfig): void { export function getClient(): OrmClientInstance { if (!client) { throw new Error( - 'ORM client not configured. Call configure() before using hooks.' + 'ORM client not configured. Call configure() before using hooks.', ); } return client; diff --git a/graphql/codegen/src/core/codegen/templates/hooks-selection.ts b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts index 4e2741476..66f7ed055 100644 --- a/graphql/codegen/src/core/codegen/templates/hooks-selection.ts +++ b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts @@ -13,8 +13,11 @@ export interface SelectionConfig { fields?: TFields; } -export interface ListSelectionConfig - extends SelectionConfig { +export interface ListSelectionConfig< + TFields, + TWhere, + TOrderBy, +> extends SelectionConfig { where?: TWhere; orderBy?: TOrderBy[]; first?: number; @@ -25,7 +28,7 @@ export interface ListSelectionConfig } export function buildSelectionArgs( - selection?: SelectionConfig + selection?: SelectionConfig, ): { select?: TFields } | undefined { if (!selection || selection.fields === undefined) { return undefined; @@ -35,7 +38,7 @@ export function buildSelectionArgs( } export function buildListSelectionArgs( - selection?: ListSelectionConfig + selection?: ListSelectionConfig, ): | { select?: TFields; @@ -74,6 +77,6 @@ export function buildListSelectionArgs( last: selection.last, after: selection.after, before: selection.before, - offset: selection.offset + offset: selection.offset, }; } diff --git a/graphql/codegen/src/core/codegen/templates/orm-client.ts b/graphql/codegen/src/core/codegen/templates/orm-client.ts index c6993a338..9d474a76e 100644 --- a/graphql/codegen/src/core/codegen/templates/orm-client.ts +++ b/graphql/codegen/src/core/codegen/templates/orm-client.ts @@ -11,13 +11,13 @@ import type { GraphQLAdapter, GraphQLError, - QueryResult + QueryResult, } from '@constructive-io/graphql-types'; export type { GraphQLAdapter, GraphQLError, - QueryResult + QueryResult, } from '@constructive-io/graphql-types'; /** @@ -29,33 +29,35 @@ export class FetchAdapter implements GraphQLAdapter { constructor( private endpoint: string, - headers?: Record + headers?: Record, ) { this.headers = headers ?? {}; } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { const response = await fetch(this.endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', - ...this.headers + ...this.headers, }, body: JSON.stringify({ query: document, - variables: variables ?? {} - }) + variables: variables ?? {}, + }), }); if (!response.ok) { return { ok: false, data: null, - errors: [{ message: `HTTP ${response.status}: ${response.statusText}` }] + errors: [ + { message: `HTTP ${response.status}: ${response.statusText}` }, + ], }; } @@ -68,14 +70,14 @@ export class FetchAdapter implements GraphQLAdapter { return { ok: false, data: null, - errors: json.errors + errors: json.errors, }; } return { ok: true, data: json.data as T, - errors: undefined + errors: undefined, }; } @@ -108,7 +110,7 @@ export interface OrmClientConfig { export class GraphQLRequestError extends Error { constructor( public readonly errors: GraphQLError[], - public readonly data: unknown = null + public readonly data: unknown = null, ) { const messages = errors.map((e) => e.message).join('; '); super(`GraphQL Error: ${messages}`); @@ -126,14 +128,14 @@ export class OrmClient { this.adapter = new FetchAdapter(config.endpoint, config.headers); } else { throw new Error( - 'OrmClientConfig requires either an endpoint or a custom adapter' + 'OrmClientConfig requires either an endpoint or a custom adapter', ); } } async execute( document: string, - variables?: Record + variables?: Record, ): Promise> { return this.adapter.execute(document, variables); } diff --git a/graphql/codegen/src/core/codegen/templates/query-builder.ts b/graphql/codegen/src/core/codegen/templates/query-builder.ts index a62cdf075..122fb9d55 100644 --- a/graphql/codegen/src/core/codegen/templates/query-builder.ts +++ b/graphql/codegen/src/core/codegen/templates/query-builder.ts @@ -14,10 +14,10 @@ import type { ArgumentNode, EnumValueNode, FieldNode, - VariableDefinitionNode + VariableDefinitionNode, } from 'graphql'; -import { GraphQLRequestError,OrmClient, QueryResult } from './client'; +import { GraphQLRequestError, OrmClient, QueryResult } from './client'; export interface QueryBuilderConfig { client: OrmClient; @@ -42,7 +42,7 @@ export class QueryBuilder { async execute(): Promise> { return this.config.client.execute( this.config.document, - this.config.variables + this.config.variables, ); } @@ -73,7 +73,7 @@ export class QueryBuilder { * Execute and unwrap, calling onError callback on failure */ async unwrapOrElse( - onError: (errors: import('./client').GraphQLError[]) => D + onError: (errors: import('./client').GraphQLError[]) => D, ): Promise { const result = await this.execute(); if (!result.ok) { @@ -98,14 +98,16 @@ export class QueryBuilder { export function buildSelections( select: Record | undefined, connectionFieldsMap?: Record>, - entityType?: string + entityType?: string, ): FieldNode[] { if (!select) { return []; } const fields: FieldNode[] = []; - const entityConnections = entityType ? connectionFieldsMap?.[entityType] : undefined; + const entityConnections = entityType + ? connectionFieldsMap?.[entityType] + : undefined; for (const [key, value] of Object.entries(select)) { if (value === false || value === undefined) { @@ -128,7 +130,11 @@ export function buildSelections( if (nested.select) { const relatedEntityType = entityConnections?.[key]; - const nestedSelections = buildSelections(nested.select, connectionFieldsMap, relatedEntityType); + const nestedSelections = buildSelections( + nested.select, + connectionFieldsMap, + relatedEntityType, + ); const isConnection = nested.connection === true || nested.first !== undefined || @@ -137,9 +143,12 @@ export function buildSelections( const args = buildArgs([ buildOptionalArg('first', nested.first), nested.filter - ? t.argument({ name: 'filter', value: buildValueAst(nested.filter) }) + ? t.argument({ + name: 'filter', + value: buildValueAst(nested.filter), + }) : null, - buildEnumListArg('orderBy', nested.orderBy) + buildEnumListArg('orderBy', nested.orderBy), ]); if (isConnection) { @@ -148,17 +157,17 @@ export function buildSelections( name: key, args, selectionSet: t.selectionSet({ - selections: buildConnectionSelections(nestedSelections) - }) - }) + selections: buildConnectionSelections(nestedSelections), + }), + }), ); } else { fields.push( t.field({ name: key, args, - selectionSet: t.selectionSet({ selections: nestedSelections }) - }) + selectionSet: t.selectionSet({ selections: nestedSelections }), + }), ); } } @@ -187,10 +196,14 @@ export function buildFindManyDocument( }, filterTypeName: string, orderByTypeName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; @@ -198,50 +211,55 @@ export function buildFindManyDocument( const variables: Record = {}; addVariable( - { varName: 'where', argName: 'filter', typeName: filterTypeName, value: args.where }, + { + varName: 'where', + argName: 'filter', + typeName: filterTypeName, + value: args.where, + }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'orderBy', typeName: '[' + orderByTypeName + '!]', - value: args.orderBy?.length ? args.orderBy : undefined + value: args.orderBy?.length ? args.orderBy : undefined, }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'first', typeName: 'Int', value: args.first }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'last', typeName: 'Int', value: args.last }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'after', typeName: 'Cursor', value: args.after }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'before', typeName: 'Cursor', value: args.before }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( { varName: 'offset', typeName: 'Int', value: args.offset }, variableDefinitions, queryArgs, - variables + variables, ); const document = t.document({ @@ -249,20 +267,22 @@ export function buildFindManyDocument( t.operationDefinition({ operation: 'query', name: operationName + 'Query', - variableDefinitions: variableDefinitions.length ? variableDefinitions : undefined, + variableDefinitions: variableDefinitions.length + ? variableDefinitions + : undefined, selectionSet: t.selectionSet({ selections: [ t.field({ name: queryField, args: queryArgs.length ? queryArgs : undefined, selectionSet: t.selectionSet({ - selections: buildConnectionSelections(selections) - }) - }) - ] - }) - }) - ] + selections: buildConnectionSelections(selections), + }), + }), + ], + }), + }), + ], }); return { document: print(document), variables }; @@ -274,10 +294,14 @@ export function buildFindFirstDocument( select: TSelect, args: { where?: TWhere }, filterTypeName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = []; @@ -289,13 +313,18 @@ export function buildFindFirstDocument( { varName: 'first', typeName: 'Int', value: 1 }, variableDefinitions, queryArgs, - variables + variables, ); addVariable( - { varName: 'where', argName: 'filter', typeName: filterTypeName, value: args.where }, + { + varName: 'where', + argName: 'filter', + typeName: filterTypeName, + value: args.where, + }, variableDefinitions, queryArgs, - variables + variables, ); const document = t.document({ @@ -313,15 +342,15 @@ export function buildFindFirstDocument( selections: [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections }) - }) - ] - }) - }) - ] - }) - }) - ] + selectionSet: t.selectionSet({ selections }), + }), + ], + }), + }), + ], + }), + }), + ], }); return { document: print(document), variables }; @@ -334,10 +363,14 @@ export function buildCreateDocument( select: TSelect, data: TData, inputTypeName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; return { @@ -348,19 +381,23 @@ export function buildCreateDocument( resultSelections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections }) - }) - ] + selectionSet: t.selectionSet({ selections }), + }), + ], }), variables: { input: { - [entityField]: data - } - } + [entityField]: data, + }, + }, }; } -export function buildUpdateDocument( +export function buildUpdateDocument< + TSelect, + TWhere extends { id: string }, + TData, +>( operationName: string, mutationField: string, entityField: string, @@ -368,10 +405,14 @@ export function buildUpdateDocument> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; return { @@ -382,16 +423,16 @@ export function buildUpdateDocument( data: TData, inputTypeName: string, idFieldName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; return { @@ -418,16 +463,16 @@ export function buildUpdateByPkDocument( resultSelections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections }) - }) - ] + selectionSet: t.selectionSet({ selections }), + }), + ], }), variables: { input: { [idFieldName]: id, - patch: data - } - } + patch: data, + }, + }, }; } @@ -438,24 +483,28 @@ export function buildFindOneDocument( select: TSelect, idArgName: string, idTypeName: string, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const selections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; const variableDefinitions: VariableDefinitionNode[] = [ t.variableDefinition({ variable: t.variable({ name: idArgName }), - type: parseType(idTypeName) - }) + type: parseType(idTypeName), + }), ]; const queryArgs: ArgumentNode[] = [ t.argument({ name: idArgName, - value: t.variable({ name: idArgName }) - }) + value: t.variable({ name: idArgName }), + }), ]; const document = t.document({ @@ -469,31 +518,38 @@ export function buildFindOneDocument( t.field({ name: queryField, args: queryArgs, - selectionSet: t.selectionSet({ selections }) - }) - ] - }) - }) - ] + selectionSet: t.selectionSet({ selections }), + }), + ], + }), + }), + ], }); return { document: print(document), - variables: { [idArgName]: id } + variables: { [idArgName]: id }, }; } -export function buildDeleteDocument( +export function buildDeleteDocument< + TWhere extends { id: string }, + TSelect = undefined, +>( operationName: string, mutationField: string, entityField: string, where: TWhere, inputTypeName: string, select?: TSelect, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const entitySelections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; return { @@ -505,16 +561,16 @@ export function buildDeleteDocument( inputTypeName: string, idFieldName: string, select?: TSelect, - connectionFieldsMap?: Record> + connectionFieldsMap?: Record>, ): { document: string; variables: Record } { const entitySelections = select - ? buildSelections(select as Record, connectionFieldsMap, operationName) + ? buildSelections( + select as Record, + connectionFieldsMap, + operationName, + ) : [t.field({ name: 'id' })]; return { @@ -540,15 +600,15 @@ export function buildDeleteByPkDocument( resultSelections: [ t.field({ name: entityField, - selectionSet: t.selectionSet({ selections: entitySelections }) - }) - ] + selectionSet: t.selectionSet({ selections: entitySelections }), + }), + ], }), variables: { input: { - [idFieldName]: id - } - } + [idFieldName]: id, + }, + }, }; } @@ -560,7 +620,7 @@ export function buildCustomDocument( args: TArgs, variableDefinitions: Array<{ name: string; type: string }>, connectionFieldsMap?: Record>, - entityType?: string + entityType?: string, ): { document: string; variables: Record } { let actualSelect = select; let isConnection = false; @@ -574,20 +634,24 @@ export function buildCustomDocument( } const selections = actualSelect - ? buildSelections(actualSelect as Record, connectionFieldsMap, entityType) + ? buildSelections( + actualSelect as Record, + connectionFieldsMap, + entityType, + ) : []; const variableDefs = variableDefinitions.map((definition) => t.variableDefinition({ variable: t.variable({ name: definition.name }), - type: parseType(definition.type) - }) + type: parseType(definition.type), + }), ); const fieldArgs = variableDefinitions.map((definition) => t.argument({ name: definition.name, - value: t.variable({ name: definition.name }) - }) + value: t.variable({ name: definition.name }), + }), ); const fieldSelections = isConnection @@ -607,17 +671,17 @@ export function buildCustomDocument( args: fieldArgs.length ? fieldArgs : undefined, selectionSet: fieldSelections.length ? t.selectionSet({ selections: fieldSelections }) - : undefined - }) - ] - }) - }) - ] + : undefined, + }), + ], + }), + }), + ], }); return { document: print(document), - variables: (args ?? {}) as Record + variables: (args ?? {}) as Record, }; } @@ -631,7 +695,7 @@ function buildArgs(args: Array): ArgumentNode[] { function buildOptionalArg( name: string, - value: number | string | undefined + value: number | string | undefined, ): ArgumentNode | null { if (value === undefined) { return null; @@ -645,7 +709,7 @@ function buildOptionalArg( function buildEnumListArg( name: string, - values: string[] | undefined + values: string[] | undefined, ): ArgumentNode | null { if (!values || values.length === 0) { return null; @@ -653,15 +717,15 @@ function buildEnumListArg( return t.argument({ name, value: t.listValue({ - values: values.map((value) => buildEnumValue(value)) - }) + values: values.map((value) => buildEnumValue(value)), + }), }); } function buildEnumValue(value: string): EnumValueNode { return { kind: 'EnumValue', - value + value, }; } @@ -670,7 +734,7 @@ function buildPageInfoSelections(): FieldNode[] { t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }) + t.field({ name: 'endCursor' }), ]; } @@ -678,13 +742,13 @@ function buildConnectionSelections(nodeSelections: FieldNode[]): FieldNode[] { return [ t.field({ name: 'nodes', - selectionSet: t.selectionSet({ selections: nodeSelections }) + selectionSet: t.selectionSet({ selections: nodeSelections }), }), t.field({ name: 'totalCount' }), t.field({ name: 'pageInfo', - selectionSet: t.selectionSet({ selections: buildPageInfoSelections() }) - }) + selectionSet: t.selectionSet({ selections: buildPageInfoSelections() }), + }), ]; } @@ -711,8 +775,8 @@ function buildInputMutationDocument(config: InputMutationConfig): string { variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'input' }), - type: parseType(config.inputTypeName + '!') - }) + type: parseType(config.inputTypeName + '!'), + }), ], selectionSet: t.selectionSet({ selections: [ @@ -721,17 +785,17 @@ function buildInputMutationDocument(config: InputMutationConfig): string { args: [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }) - }) + value: t.variable({ name: 'input' }), + }), ], selectionSet: t.selectionSet({ - selections: config.resultSelections - }) - }) - ] - }) - }) - ] + selections: config.resultSelections, + }), + }), + ], + }), + }), + ], }); return print(document); } @@ -740,27 +804,27 @@ function addVariable( spec: VariableSpec, definitions: VariableDefinitionNode[], args: ArgumentNode[], - variables: Record + variables: Record, ): void { if (spec.value === undefined) return; definitions.push( t.variableDefinition({ variable: t.variable({ name: spec.varName }), - type: parseType(spec.typeName) - }) + type: parseType(spec.typeName), + }), ); args.push( t.argument({ name: spec.argName ?? spec.varName, - value: t.variable({ name: spec.varName }) - }) + value: t.variable({ name: spec.varName }), + }), ); variables[spec.varName] = spec.value; } function buildValueAst( - value: unknown + value: unknown, ): | ReturnType | ReturnType @@ -790,7 +854,7 @@ function buildValueAst( if (Array.isArray(value)) { return t.listValue({ - values: value.map((item) => buildValueAst(item)) + values: value.map((item) => buildValueAst(item)), }); } @@ -800,9 +864,9 @@ function buildValueAst( fields: Object.entries(obj).map(([key, val]) => t.objectField({ name: key, - value: buildValueAst(val) - }) - ) + value: buildValueAst(val), + }), + ), }); } diff --git a/graphql/codegen/src/core/codegen/templates/select-types.ts b/graphql/codegen/src/core/codegen/templates/select-types.ts index 5656e4901..e430f4339 100644 --- a/graphql/codegen/src/core/codegen/templates/select-types.ts +++ b/graphql/codegen/src/core/codegen/templates/select-types.ts @@ -51,7 +51,7 @@ export interface UpdateArgs { export type FindOneArgs< TSelect, TIdName extends string = 'id', - TId = string + TId = string, > = { select?: TSelect; } & Record; @@ -84,9 +84,13 @@ export type DeepExact = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? T[K] extends { select: infer NS } - ? Extract extends { select?: infer ShapeNS } + ? Extract extends { + select?: infer ShapeNS; + } ? DeepExact< - Omit & { select: DeepExact> }, + Omit & { + select: DeepExact>; + }, Extract > : never diff --git a/graphql/codegen/src/core/codegen/type-resolver.ts b/graphql/codegen/src/core/codegen/type-resolver.ts index 9e8856aa3..32e0ee01b 100644 --- a/graphql/codegen/src/core/codegen/type-resolver.ts +++ b/graphql/codegen/src/core/codegen/type-resolver.ts @@ -4,11 +4,8 @@ * Utilities for converting CleanTypeRef and other GraphQL types * into TypeScript type strings and interface definitions. */ -import type { - CleanObjectField, - CleanTypeRef -} from '../../types/schema'; -import { SCALAR_NAMES,scalarToTsType as resolveScalarToTs } from './scalars'; +import type { CleanTypeRef } from '../../types/schema'; +import { SCALAR_NAMES, scalarToTsType as resolveScalarToTs } from './scalars'; // ============================================================================ // Type Tracker for Collecting Referenced Types @@ -30,7 +27,7 @@ const SKIP_TYPE_TRACKING = new Set([ '__EnumValue', '__Directive', // Connection types (handled separately) - 'PageInfo' + 'PageInfo', ]); /** @@ -87,7 +84,7 @@ export function createTypeTracker(options?: TypeTrackerOptions): TypeTracker { }, reset() { referencedTypes.clear(); - } + }, }; } @@ -113,44 +110,47 @@ export function scalarToTsType(scalarName: string): string { * @param typeRef - The GraphQL type reference * @param tracker - Optional TypeTracker to collect referenced types */ -export function typeRefToTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): string { +export function typeRefToTsType( + typeRef: CleanTypeRef, + tracker?: TypeTracker, +): string { switch (typeRef.kind) { - case 'NON_NULL': - // Non-null wrapper - unwrap and return the inner type - if (typeRef.ofType) { - return typeRefToTsType(typeRef.ofType, tracker); - } - return 'unknown'; + case 'NON_NULL': + // Non-null wrapper - unwrap and return the inner type + if (typeRef.ofType) { + return typeRefToTsType(typeRef.ofType, tracker); + } + return 'unknown'; - case 'LIST': - // List wrapper - wrap inner type in array - if (typeRef.ofType) { - const innerType = typeRefToTsType(typeRef.ofType, tracker); - return `${innerType}[]`; - } - return 'unknown[]'; + case 'LIST': + // List wrapper - wrap inner type in array + if (typeRef.ofType) { + const innerType = typeRefToTsType(typeRef.ofType, tracker); + return `${innerType}[]`; + } + return 'unknown[]'; - case 'SCALAR': - // Scalar type - map to TS type - return scalarToTsType(typeRef.name ?? 'unknown'); + case 'SCALAR': + // Scalar type - map to TS type + return scalarToTsType(typeRef.name ?? 'unknown'); - case 'ENUM': { - // Enum type - use the GraphQL enum name and track it - const typeName = typeRef.name ?? 'string'; - tracker?.track(typeName); - return typeName; - } + case 'ENUM': { + // Enum type - use the GraphQL enum name and track it + const typeName = typeRef.name ?? 'string'; + tracker?.track(typeName); + return typeName; + } - case 'OBJECT': - case 'INPUT_OBJECT': { - // Object types - use the GraphQL type name and track it - const typeName = typeRef.name ?? 'unknown'; - tracker?.track(typeName); - return typeName; - } + case 'OBJECT': + case 'INPUT_OBJECT': { + // Object types - use the GraphQL type name and track it + const typeName = typeRef.name ?? 'unknown'; + tracker?.track(typeName); + return typeName; + } - default: - return 'unknown'; + default: + return 'unknown'; } } @@ -161,7 +161,10 @@ export function typeRefToTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): s * @param typeRef - The GraphQL type reference * @param tracker - Optional TypeTracker to collect referenced types */ -export function typeRefToNullableTsType(typeRef: CleanTypeRef, tracker?: TypeTracker): string { +export function typeRefToNullableTsType( + typeRef: CleanTypeRef, + tracker?: TypeTracker, +): string { const baseType = typeRefToTsType(typeRef, tracker); // If the outer type is NON_NULL, it's required @@ -219,41 +222,16 @@ export function getBaseTypeKind(typeRef: CleanTypeRef): CleanTypeRef['kind'] { /** * Check if a field should be skipped in selections */ -export function shouldSkipField(fieldName: string, skipQueryField: boolean): boolean { +export function shouldSkipField( + fieldName: string, + skipQueryField: boolean, +): boolean { if (skipQueryField && fieldName === 'query') return true; if (fieldName === 'nodeId') return true; if (fieldName === '__typename') return true; return false; } -/** - * Filter fields to only include selectable scalar and object fields - */ -export function getSelectableFields( - fields: CleanObjectField[] | undefined, - skipQueryField: boolean, - maxDepth: number = 2, - currentDepth: number = 0 -): CleanObjectField[] { - if (!fields || currentDepth >= maxDepth) return []; - - return fields.filter((field) => { - // Skip internal fields - if (shouldSkipField(field.name, skipQueryField)) return false; - - // Get base type kind - const baseKind = getBaseTypeKind(field.type); - - // Include scalars and enums - if (baseKind === 'SCALAR' || baseKind === 'ENUM') return true; - - // Include objects up to max depth - if (baseKind === 'OBJECT' && currentDepth < maxDepth - 1) return true; - - return false; - }); -} - // ============================================================================ // Type Name Utilities // ============================================================================ @@ -271,7 +249,7 @@ export function operationNameToPascal(name: string): string { */ export function getOperationVariablesTypeName( operationName: string, - kind: 'query' | 'mutation' + kind: 'query' | 'mutation', ): string { const pascal = operationNameToPascal(operationName); return `${pascal}${kind === 'query' ? 'Query' : 'Mutation'}Variables`; @@ -283,7 +261,7 @@ export function getOperationVariablesTypeName( */ export function getOperationResultTypeName( operationName: string, - kind: 'query' | 'mutation' + kind: 'query' | 'mutation', ): string { const pascal = operationNameToPascal(operationName); return `${pascal}${kind === 'query' ? 'Query' : 'Mutation'}Result`; @@ -295,7 +273,7 @@ export function getOperationResultTypeName( */ export function getOperationHookName( operationName: string, - kind: 'query' | 'mutation' + kind: 'query' | 'mutation', ): string { const pascal = operationNameToPascal(operationName); return `use${pascal}${kind === 'query' ? 'Query' : 'Mutation'}`; @@ -307,7 +285,7 @@ export function getOperationHookName( */ export function getOperationFileName( operationName: string, - kind: 'query' | 'mutation' + kind: 'query' | 'mutation', ): string { return `${getOperationHookName(operationName, kind)}.ts`; } @@ -326,7 +304,7 @@ export function getQueryKeyName(operationName: string): string { */ export function getDocumentConstName( operationName: string, - kind: 'query' | 'mutation' + kind: 'query' | 'mutation', ): string { return `${operationName}${kind === 'query' ? 'Query' : 'Mutation'}Document`; } diff --git a/graphql/codegen/src/core/codegen/types.ts b/graphql/codegen/src/core/codegen/types.ts index 1fb93e70c..9f2825eef 100644 --- a/graphql/codegen/src/core/codegen/types.ts +++ b/graphql/codegen/src/core/codegen/types.ts @@ -5,7 +5,11 @@ import * as t from '@babel/types'; import type { CleanTable } from '../../types/schema'; import { generateCode } from './babel-ast'; -import { fieldTypeToTs, getGeneratedFileHeader,getScalarFields } from './utils'; +import { + fieldTypeToTs, + getGeneratedFileHeader, + getScalarFields, +} from './utils'; interface InterfaceProperty { name: string; @@ -17,32 +21,100 @@ interface InterfaceProperty { // Filter Types Configuration // ============================================================================ -type FilterOps = 'equality' | 'distinct' | 'inArray' | 'comparison' | 'string' | 'json' | 'inet' | 'fulltext' | 'listArray'; +type FilterOps = + | 'equality' + | 'distinct' + | 'inArray' + | 'comparison' + | 'string' + | 'json' + | 'inet' + | 'fulltext' + | 'listArray'; /** All filter type configurations - scalar and list filters */ -const FILTER_CONFIGS: Array<{ name: string; tsType: string; operators: FilterOps[] }> = [ +const FILTER_CONFIGS: Array<{ + name: string; + tsType: string; + operators: FilterOps[]; +}> = [ // Scalar filters - { name: 'StringFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'] }, - { name: 'IntFilter', tsType: 'number', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, - { name: 'FloatFilter', tsType: 'number', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, + { + name: 'StringFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison', 'string'], + }, + { + name: 'IntFilter', + tsType: 'number', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, + { + name: 'FloatFilter', + tsType: 'number', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, { name: 'BooleanFilter', tsType: 'boolean', operators: ['equality'] }, - { name: 'UUIDFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray'] }, - { name: 'DatetimeFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, - { name: 'DateFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, - { name: 'JSONFilter', tsType: 'Record', operators: ['equality', 'distinct', 'json'] }, - { name: 'BigIntFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, - { name: 'BigFloatFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison'] }, + { + name: 'UUIDFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray'], + }, + { + name: 'DatetimeFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, + { + name: 'DateFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, + { + name: 'JSONFilter', + tsType: 'Record', + operators: ['equality', 'distinct', 'json'], + }, + { + name: 'BigIntFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, + { + name: 'BigFloatFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison'], + }, { name: 'BitStringFilter', tsType: 'string', operators: ['equality'] }, - { name: 'InternetAddressFilter', tsType: 'string', operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'] }, + { + name: 'InternetAddressFilter', + tsType: 'string', + operators: ['equality', 'distinct', 'inArray', 'comparison', 'inet'], + }, { name: 'FullTextFilter', tsType: 'string', operators: ['fulltext'] }, // List filters - { name: 'StringListFilter', tsType: 'string[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] }, - { name: 'IntListFilter', tsType: 'number[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] }, - { name: 'UUIDListFilter', tsType: 'string[]', operators: ['equality', 'distinct', 'comparison', 'listArray'] } + { + name: 'StringListFilter', + tsType: 'string[]', + operators: ['equality', 'distinct', 'comparison', 'listArray'], + }, + { + name: 'IntListFilter', + tsType: 'number[]', + operators: ['equality', 'distinct', 'comparison', 'listArray'], + }, + { + name: 'UUIDListFilter', + tsType: 'string[]', + operators: ['equality', 'distinct', 'comparison', 'listArray'], + }, ]; /** Build filter properties based on operator sets */ -function buildFilterProperties(tsType: string, operators: FilterOps[]): InterfaceProperty[] { +function buildFilterProperties( + tsType: string, + operators: FilterOps[], +): InterfaceProperty[] { const props: InterfaceProperty[] = []; // Equality operators (isNull, equalTo, notEqualTo) @@ -50,7 +122,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac props.push( { name: 'isNull', type: 'boolean', optional: true }, { name: 'equalTo', type: tsType, optional: true }, - { name: 'notEqualTo', type: tsType, optional: true } + { name: 'notEqualTo', type: tsType, optional: true }, ); } @@ -58,7 +130,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac if (operators.includes('distinct')) { props.push( { name: 'distinctFrom', type: tsType, optional: true }, - { name: 'notDistinctFrom', type: tsType, optional: true } + { name: 'notDistinctFrom', type: tsType, optional: true }, ); } @@ -66,7 +138,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac if (operators.includes('inArray')) { props.push( { name: 'in', type: `${tsType}[]`, optional: true }, - { name: 'notIn', type: `${tsType}[]`, optional: true } + { name: 'notIn', type: `${tsType}[]`, optional: true }, ); } @@ -76,7 +148,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'lessThan', type: tsType, optional: true }, { name: 'lessThanOrEqualTo', type: tsType, optional: true }, { name: 'greaterThan', type: tsType, optional: true }, - { name: 'greaterThanOrEqualTo', type: tsType, optional: true } + { name: 'greaterThanOrEqualTo', type: tsType, optional: true }, ); } @@ -98,7 +170,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'like', type: 'string', optional: true }, { name: 'notLike', type: 'string', optional: true }, { name: 'likeInsensitive', type: 'string', optional: true }, - { name: 'notLikeInsensitive', type: 'string', optional: true } + { name: 'notLikeInsensitive', type: 'string', optional: true }, ); } @@ -109,7 +181,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'containedBy', type: 'unknown', optional: true }, { name: 'containsKey', type: 'string', optional: true }, { name: 'containsAllKeys', type: 'string[]', optional: true }, - { name: 'containsAnyKeys', type: 'string[]', optional: true } + { name: 'containsAnyKeys', type: 'string[]', optional: true }, ); } @@ -118,7 +190,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac props.push( { name: 'contains', type: 'string', optional: true }, { name: 'containedBy', type: 'string', optional: true }, - { name: 'containsOrContainedBy', type: 'string', optional: true } + { name: 'containsOrContainedBy', type: 'string', optional: true }, ); } @@ -140,7 +212,7 @@ function buildFilterProperties(tsType: string, operators: FilterOps[]): Interfac { name: 'anyLessThan', type: baseType, optional: true }, { name: 'anyLessThanOrEqualTo', type: baseType, optional: true }, { name: 'anyGreaterThan', type: baseType, optional: true }, - { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true } + { name: 'anyGreaterThanOrEqualTo', type: baseType, optional: true }, ); } @@ -162,7 +234,9 @@ function parseTypeAnnotation(typeStr: string): t.TSType { if (typeStr === 'unknown') return t.tsUnknownKeyword(); if (typeStr.includes(' | ')) { - const parts = typeStr.split(' | ').map((p) => parseTypeAnnotation(p.trim())); + const parts = typeStr + .split(' | ') + .map((p) => parseTypeAnnotation(p.trim())); return t.tsUnionType(parts); } @@ -177,12 +251,12 @@ function parseTypeAnnotation(typeStr: string): t.TSType { function createInterfaceDeclaration( name: string, - properties: InterfaceProperty[] + properties: InterfaceProperty[], ): t.ExportNamedDeclaration { const props = properties.map((prop) => { const propSig = t.tsPropertySignature( t.identifier(prop.name), - t.tsTypeAnnotation(parseTypeAnnotation(prop.type)) + t.tsTypeAnnotation(parseTypeAnnotation(prop.type)), ); propSig.optional = prop.optional ?? false; return propSig; @@ -192,7 +266,7 @@ function createInterfaceDeclaration( t.identifier(name), null, null, - t.tsInterfaceBody(props) + t.tsInterfaceBody(props), ); return t.exportNamedDeclaration(interfaceDecl); } @@ -202,7 +276,7 @@ function createInterfaceDeclaration( */ export function generateTypesFile( tables: CleanTable[], - options: GenerateTypesOptions = {} + options: GenerateTypesOptions = {}, ): string { const { enumsFromSchemaTypes = [] } = options; const enumSet = new Set(enumsFromSchemaTypes); @@ -227,7 +301,10 @@ export function generateTypesFile( const specifiers = Array.from(usedEnums) .sort() .map((name) => t.importSpecifier(t.identifier(name), t.identifier(name))); - const importDecl = t.importDeclaration(specifiers, t.stringLiteral('./schema-types')); + const importDecl = t.importDeclaration( + specifiers, + t.stringLiteral('./schema-types'), + ); importDecl.importKind = 'type'; statements.push(importDecl); } @@ -238,7 +315,7 @@ export function generateTypesFile( const properties: InterfaceProperty[] = scalarFields.map((field) => ({ name: field.name, - type: `${fieldTypeToTs(field.type)} | null` + type: `${fieldTypeToTs(field.type)} | null`, })); statements.push(createInterfaceDeclaration(table.name, properties)); @@ -246,7 +323,12 @@ export function generateTypesFile( // Generate all filter types for (const { name, tsType, operators } of FILTER_CONFIGS) { - statements.push(createInterfaceDeclaration(name, buildFilterProperties(tsType, operators))); + statements.push( + createInterfaceDeclaration( + name, + buildFilterProperties(tsType, operators), + ), + ); } const header = getGeneratedFileHeader('Entity types and filter types'); diff --git a/graphql/codegen/src/core/codegen/utils.ts b/graphql/codegen/src/core/codegen/utils.ts index 2bca88ad7..586201c38 100644 --- a/graphql/codegen/src/core/codegen/utils.ts +++ b/graphql/codegen/src/core/codegen/utils.ts @@ -6,9 +6,9 @@ import { pluralize } from 'inflekt'; import type { CleanField, CleanFieldType, - CleanTable + CleanTable, } from '../../types/schema'; -import { scalarToFilterType,scalarToTsType } from './scalars'; +import { scalarToFilterType, scalarToTsType } from './scalars'; // ============================================================================ // String manipulation @@ -78,7 +78,7 @@ export function getTableNames(table: CleanTable): TableNames { typeName, singularName, pluralName, - pluralTypeName + pluralTypeName, }; } @@ -305,7 +305,7 @@ export function fieldTypeToTs(fieldType: CleanFieldType): string { */ export function getScalarFilterType( gqlType: string, - isArray = false + isArray = false, ): string | null { const cleanType = gqlType.replace(/!/g, ''); return scalarToFilterType(cleanType, isArray); @@ -361,8 +361,8 @@ export function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[] { { name: idField.name, gqlType: idField.type.gqlType, - tsType: fieldTypeToTs(idField.type) - } + tsType: fieldTypeToTs(idField.type), + }, ]; } // Last resort: assume 'id' of type string (UUID) @@ -371,7 +371,7 @@ export function getPrimaryKeyInfo(table: CleanTable): PrimaryKeyField[] { return pk.fields.map((f) => ({ name: f.name, gqlType: f.type.gqlType, - tsType: fieldTypeToTs(f.type) + tsType: fieldTypeToTs(f.type), })); } diff --git a/graphql/codegen/src/core/config/index.ts b/graphql/codegen/src/core/config/index.ts index 99dea07ad..a79e71987 100644 --- a/graphql/codegen/src/core/config/index.ts +++ b/graphql/codegen/src/core/config/index.ts @@ -6,11 +6,11 @@ export { CONFIG_FILENAME, findConfigFile, loadConfigFile, - type LoadConfigFileResult + type LoadConfigFileResult, } from './loader'; export { type ConfigOverrideOptions, loadAndResolveConfig, type LoadConfigResult, - loadWatchConfig + loadWatchConfig, } from './resolver'; diff --git a/graphql/codegen/src/core/config/loader.ts b/graphql/codegen/src/core/config/loader.ts index 668b3adc9..87afc3b61 100644 --- a/graphql/codegen/src/core/config/loader.ts +++ b/graphql/codegen/src/core/config/loader.ts @@ -15,7 +15,7 @@ export const CONFIG_FILENAME = 'graphql-codegen.config.ts'; * Find the nearest config file by walking up directories */ export function findConfigFile( - startDir: string = process.cwd() + startDir: string = process.cwd(), ): string | null { let currentDir = startDir; @@ -49,14 +49,14 @@ export interface LoadConfigFileResult { * tsx or ts-node installed. */ export async function loadConfigFile( - configPath: string + configPath: string, ): Promise { const resolvedPath = path.resolve(configPath); if (!fs.existsSync(resolvedPath)) { return { success: false, - error: `Config file not found: ${resolvedPath}` + error: `Config file not found: ${resolvedPath}`, }; } @@ -65,7 +65,7 @@ export async function loadConfigFile( // jiti handles .ts, .js, .mjs, .cjs and ESM/CJS interop const jiti = createJiti(__filename, { interopDefault: true, - debug: process.env.JITI_DEBUG === '1' + debug: process.env.JITI_DEBUG === '1', }); // jiti.import() with { default: true } returns mod?.default ?? mod @@ -74,19 +74,19 @@ export async function loadConfigFile( if (!config || typeof config !== 'object') { return { success: false, - error: 'Config file must export a configuration object' + error: 'Config file must export a configuration object', }; } return { success: true, - config + config, }; } catch (err) { const message = err instanceof Error ? err.message : 'Unknown error'; return { success: false, - error: `Failed to load config file: ${message}` + error: `Failed to load config file: ${message}`, }; } } diff --git a/graphql/codegen/src/core/config/resolver.ts b/graphql/codegen/src/core/config/resolver.ts index aa71fe60b..8f81b7110 100644 --- a/graphql/codegen/src/core/config/resolver.ts +++ b/graphql/codegen/src/core/config/resolver.ts @@ -6,9 +6,9 @@ */ import type { GraphQLSDKConfig, - GraphQLSDKConfigTarget + GraphQLSDKConfigTarget, } from '../../types/config'; -import { getConfigOptions,mergeConfig } from '../../types/config'; +import { getConfigOptions, mergeConfig } from '../../types/config'; import { findConfigFile, loadConfigFile } from './loader'; /** @@ -38,7 +38,7 @@ export interface LoadConfigResult { * 3. Returns fully resolved configuration ready for use */ export async function loadAndResolveConfig( - options: ConfigOverrideOptions + options: ConfigOverrideOptions, ): Promise { // Destructure CLI-only fields, rest is config overrides const { config: configPath, ...overrides } = options; @@ -47,13 +47,13 @@ export async function loadAndResolveConfig( const sources = [ overrides.endpoint, overrides.schemaFile, - overrides.db + overrides.db, ].filter(Boolean); if (sources.length > 1) { return { success: false, error: - 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.' + 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.', }; } @@ -77,21 +77,19 @@ export async function loadAndResolveConfig( // Check if we have a source (endpoint, schemaFile, or db) const hasSource = - mergedConfig.endpoint || - mergedConfig.schemaFile || - mergedConfig.db; + mergedConfig.endpoint || mergedConfig.schemaFile || mergedConfig.db; if (!hasSource) { return { success: false, error: - 'No source specified. Use --endpoint, --schema-file, or --db, or create a config file with "graphql-codegen init".' + 'No source specified. Use --endpoint, --schema-file, or --db, or create a config file with "graphql-codegen init".', }; } return { success: true, - config: getConfigOptions(mergedConfig) + config: getConfigOptions(mergedConfig), }; } @@ -134,12 +132,12 @@ export async function loadWatchConfig(options: { const watchOverrides: GraphQLSDKConfigTarget = { watch: { ...(options.pollInterval !== undefined && { - pollInterval: options.pollInterval + pollInterval: options.pollInterval, }), ...(options.debounce !== undefined && { debounce: options.debounce }), ...(options.touch !== undefined && { touchFile: options.touch }), - ...(options.clear !== undefined && { clearScreen: options.clear }) - } + ...(options.clear !== undefined && { clearScreen: options.clear }), + }, }; let mergedConfig = mergeConfig(baseConfig, sourceOverrides); @@ -147,14 +145,14 @@ export async function loadWatchConfig(options: { if (!mergedConfig.endpoint) { console.error( - 'x No endpoint specified. Watch mode only supports live endpoints.' + 'x No endpoint specified. Watch mode only supports live endpoints.', ); return null; } if (mergedConfig.schemaFile) { console.error( - 'x Watch mode is only supported with an endpoint, not schemaFile.' + 'x Watch mode is only supported with an endpoint, not schemaFile.', ); return null; } diff --git a/graphql/codegen/src/core/custom-ast.ts b/graphql/codegen/src/core/custom-ast.ts index 02ea4642b..c7560a56c 100644 --- a/graphql/codegen/src/core/custom-ast.ts +++ b/graphql/codegen/src/core/custom-ast.ts @@ -22,7 +22,7 @@ export function getCustomAst(fieldDefn?: MetaField): FieldNode | null { } return t.field({ - name: fieldDefn.name + name: fieldDefn.name, }); } @@ -57,7 +57,7 @@ export function getCustomAstForCleanField(field: CleanField): FieldNode { // Return simple field for scalar types return t.field({ - name + name, }); } @@ -72,7 +72,7 @@ export function requiresSubfieldSelection(field: CleanField): boolean { 'GeometryPoint', 'Interval', 'GeometryGeometryCollection', - 'GeoJSON' + 'GeoJSON', ]; return complexTypes.includes(gqlType); @@ -85,8 +85,8 @@ export function geometryPointAst(name: string): FieldNode { return t.field({ name, selectionSet: t.selectionSet({ - selections: toFieldArray(['x', 'y']) - }) + selections: toFieldArray(['x', 'y']), + }), }); } @@ -101,8 +101,8 @@ export function geometryCollectionAst(name: string): FieldNode { kind: 'NamedType', name: { kind: 'Name', - value: 'GeometryPoint' - } + value: 'GeometryPoint', + }, }, selectionSet: { kind: 'SelectionSet', @@ -111,18 +111,18 @@ export function geometryCollectionAst(name: string): FieldNode { kind: 'Field', name: { kind: 'Name', - value: 'x' - } + value: 'x', + }, }, { kind: 'Field', name: { kind: 'Name', - value: 'y' - } - } - ] - } + value: 'y', + }, + }, + ], + }, }; return t.field({ @@ -133,11 +133,11 @@ export function geometryCollectionAst(name: string): FieldNode { name: 'geometries', selectionSet: t.selectionSet({ // eslint-disable-next-line @typescript-eslint/no-explicit-any - selections: [inlineFragment as any] // gql-ast limitation with inline fragments - }) - }) - ] - }) + selections: [inlineFragment as any], // gql-ast limitation with inline fragments + }), + }), + ], + }), }); } @@ -148,8 +148,8 @@ export function geometryAst(name: string): FieldNode { return t.field({ name, selectionSet: t.selectionSet({ - selections: toFieldArray(['geojson']) - }) + selections: toFieldArray(['geojson']), + }), }); } @@ -166,9 +166,9 @@ export function intervalAst(name: string): FieldNode { 'minutes', 'months', 'seconds', - 'years' - ]) - }) + 'years', + ]), + }), }); } @@ -182,6 +182,6 @@ function toFieldArray(strArr: string[]): FieldNode[] { export function isIntervalType(obj: unknown): boolean { if (!obj || typeof obj !== 'object') return false; return ['days', 'hours', 'minutes', 'months', 'seconds', 'years'].every( - (key) => Object.prototype.hasOwnProperty.call(obj, key) + (key) => Object.prototype.hasOwnProperty.call(obj, key), ); } diff --git a/graphql/codegen/src/core/database/index.ts b/graphql/codegen/src/core/database/index.ts index 0ca12ff14..6916dce38 100644 --- a/graphql/codegen/src/core/database/index.ts +++ b/graphql/codegen/src/core/database/index.ts @@ -37,7 +37,7 @@ export interface BuildSchemaFromDatabaseResult { * @returns The path to the generated schema file and the SDL content */ export async function buildSchemaFromDatabase( - options: BuildSchemaFromDatabaseOptions + options: BuildSchemaFromDatabaseOptions, ): Promise { const { database, schemas, outDir, filename = 'schema.graphql' } = options; @@ -48,7 +48,7 @@ export async function buildSchemaFromDatabase( const sdl = await buildSchemaSDL({ database, schemas, - graphile: { pgSettings: async () => ({ role: 'administrator' }) } + graphile: { pgSettings: async () => ({ role: 'administrator' }) }, }); // Write schema to file @@ -75,6 +75,6 @@ export async function buildSchemaSDLFromDatabase(options: { return buildSchemaSDL({ database, schemas, - graphile: { pgSettings: async () => ({ role: 'administrator' }) } + graphile: { pgSettings: async () => ({ role: 'administrator' }) }, }); } diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index e73b34edc..35ace365d 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -38,7 +38,9 @@ export interface GenerateResult { * This is the primary entry point for programmatic usage. * For multiple configs, call this function in a loop. */ -export async function generate(options: GenerateOptions = {}): Promise { +export async function generate( + options: GenerateOptions = {}, +): Promise { // Apply defaults to get resolved config const config = getConfigOptions(options); const outputRoot = config.output; @@ -47,13 +49,15 @@ export async function generate(options: GenerateOptions = {}): Promise t.name), - filesWritten: allFilesWritten + filesWritten: allFilesWritten, }; } diff --git a/graphql/codegen/src/core/index.ts b/graphql/codegen/src/core/index.ts index 86a01af13..3cae1c15c 100644 --- a/graphql/codegen/src/core/index.ts +++ b/graphql/codegen/src/core/index.ts @@ -17,10 +17,10 @@ export * from './ast'; export * from './custom-ast'; // Query builder -export { MetaObject,QueryBuilder } from './query-builder'; +export { MetaObject, QueryBuilder } from './query-builder'; // Meta object utilities -export { convertFromMetaSchema,validateMetaObject } from './meta-object'; +export { convertFromMetaSchema, validateMetaObject } from './meta-object'; // Configuration loading and resolution export * from './config'; diff --git a/graphql/codegen/src/core/introspect/fetch-schema.ts b/graphql/codegen/src/core/introspect/fetch-schema.ts index 7cd561125..269fd5468 100644 --- a/graphql/codegen/src/core/introspect/fetch-schema.ts +++ b/graphql/codegen/src/core/introspect/fetch-schema.ts @@ -21,7 +21,7 @@ function makeRequest( url: URL, options: http.RequestOptions, body: string, - timeout: number + timeout: number, ): Promise { return new Promise((resolve, reject) => { const protocol = url.protocol === 'https:' ? https : http; @@ -36,7 +36,7 @@ function makeRequest( resolve({ statusCode: res.statusCode || 0, statusMessage: res.statusMessage || '', - data + data, }); }); }); @@ -75,7 +75,7 @@ export interface FetchSchemaResult { * Fetch the full schema introspection from a GraphQL endpoint */ export async function fetchSchema( - options: FetchSchemaOptions + options: FetchSchemaOptions, ): Promise { const { endpoint, authorization, headers = {}, timeout = 30000 } = options; @@ -84,7 +84,7 @@ export async function fetchSchema( const requestHeaders: Record = { 'Content-Type': 'application/json', Accept: 'application/json', - ...headers + ...headers, }; if (authorization) { @@ -93,12 +93,12 @@ export async function fetchSchema( const body = JSON.stringify({ query: SCHEMA_INTROSPECTION_QUERY, - variables: {} + variables: {}, }); const requestOptions: http.RequestOptions = { method: 'POST', - headers: requestHeaders + headers: requestHeaders, }; try { @@ -108,7 +108,7 @@ export async function fetchSchema( return { success: false, error: `HTTP ${response.statusCode}: ${response.statusMessage}`, - statusCode: response.statusCode + statusCode: response.statusCode, }; } @@ -122,7 +122,7 @@ export async function fetchSchema( return { success: false, error: `GraphQL errors: ${errorMessages}`, - statusCode: response.statusCode + statusCode: response.statusCode, }; } @@ -131,21 +131,21 @@ export async function fetchSchema( success: false, error: 'No __schema field in response. Introspection may be disabled on this endpoint.', - statusCode: response.statusCode + statusCode: response.statusCode, }; } return { success: true, data: json.data, - statusCode: response.statusCode + statusCode: response.statusCode, }; } catch (err) { if (err instanceof Error) { if (err.message.includes('timeout')) { return { success: false, - error: `Request timeout after ${timeout}ms` + error: `Request timeout after ${timeout}ms`, }; } @@ -153,31 +153,31 @@ export async function fetchSchema( if (errorCode === 'ECONNREFUSED') { return { success: false, - error: `Connection refused - is the server running at ${endpoint}?` + error: `Connection refused - is the server running at ${endpoint}?`, }; } if (errorCode === 'ENOTFOUND') { return { success: false, - error: `DNS lookup failed for ${url.hostname} - check the endpoint URL` + error: `DNS lookup failed for ${url.hostname} - check the endpoint URL`, }; } if (errorCode === 'ECONNRESET') { return { success: false, - error: `Connection reset by server at ${endpoint}` + error: `Connection reset by server at ${endpoint}`, }; } return { success: false, - error: err.message + error: err.message, }; } return { success: false, - error: 'Unknown error occurred' + error: 'Unknown error occurred', }; } } diff --git a/graphql/codegen/src/core/introspect/index.ts b/graphql/codegen/src/core/introspect/index.ts index 5ddf28346..dae6b7a4e 100644 --- a/graphql/codegen/src/core/introspect/index.ts +++ b/graphql/codegen/src/core/introspect/index.ts @@ -7,20 +7,20 @@ export type { InferTablesOptions } from './infer-tables'; export { inferTablesFromIntrospection } from './infer-tables'; // Pluralization utilities (from inflekt) -export { pluralize,singularize } from 'inflekt'; +export { pluralize, singularize } from 'inflekt'; // Schema sources export type { CreateSchemaSourceOptions, SchemaSource, - SchemaSourceResult + SchemaSourceResult, } from './source'; export { createSchemaSource, EndpointSchemaSource, FileSchemaSource, SchemaSourceError, - validateSourceOptions + validateSourceOptions, } from './source'; // Schema fetching (still used by watch mode) @@ -28,4 +28,4 @@ export type { FetchSchemaOptions, FetchSchemaResult } from './fetch-schema'; export { fetchSchema } from './fetch-schema'; // Transform utilities (only filterTables, getTableNames, findTable are still useful) -export { filterTables,findTable, getTableNames } from './transform'; +export { filterTables, findTable, getTableNames } from './transform'; diff --git a/graphql/codegen/src/core/introspect/infer-tables.ts b/graphql/codegen/src/core/introspect/infer-tables.ts index ef53092be..90f34ee56 100644 --- a/graphql/codegen/src/core/introspect/infer-tables.ts +++ b/graphql/codegen/src/core/introspect/infer-tables.ts @@ -18,9 +18,9 @@ import type { IntrospectionField, IntrospectionQueryResponse, IntrospectionType, - IntrospectionTypeRef + IntrospectionTypeRef, } from '../../types/introspection'; -import { getBaseTypeName, isList,unwrapType } from '../../types/introspection'; +import { getBaseTypeName, isList, unwrapType } from '../../types/introspection'; import type { CleanBelongsToRelation, CleanField, @@ -32,7 +32,7 @@ import type { ConstraintInfo, TableConstraints, TableInflection, - TableQueryNames + TableQueryNames, } from '../../types/schema'; // ============================================================================ @@ -64,7 +64,7 @@ const PATTERNS = { // Mutation name patterns (camelCase) createMutation: /^create([A-Z][a-zA-Z0-9]*)$/, updateMutation: /^update([A-Z][a-zA-Z0-9]*)$/, - deleteMutation: /^delete([A-Z][a-zA-Z0-9]*)$/ + deleteMutation: /^delete([A-Z][a-zA-Z0-9]*)$/, }; /** @@ -89,7 +89,7 @@ const BUILTIN_TYPES = new Set([ 'Time', 'JSON', 'BigInt', - 'BigFloat' + 'BigFloat', ]); /** @@ -119,7 +119,7 @@ export interface InferTablesOptions { */ export function inferTablesFromIntrospection( introspection: IntrospectionQueryResponse, - options: InferTablesOptions = {} + options: InferTablesOptions = {}, ): CleanTable[] { const { __schema: schema } = introspection; const { types, queryType, mutationType } = schema; @@ -147,7 +147,7 @@ export function inferTablesFromIntrospection( entityType, typeMap, queryFields, - mutationFields + mutationFields, ); // Only include tables that have at least one real operation @@ -211,7 +211,7 @@ function buildCleanTable( entityType: IntrospectionType, typeMap: Map, queryFields: IntrospectionField[], - mutationFields: IntrospectionField[] + mutationFields: IntrospectionField[], ): BuildCleanTableResult { // Extract scalar fields from entity type const fields = extractEntityFields(entityType, typeMap); @@ -245,7 +245,7 @@ function buildCleanTable( one: queryOps.one ?? lcFirst(entityName), create: mutationOps.create ?? `create${entityName}`, update: mutationOps.update, - delete: mutationOps.delete + delete: mutationOps.delete, }; return { @@ -255,9 +255,9 @@ function buildCleanTable( relations, inflection, query, - constraints + constraints, }, - hasRealOperation + hasRealOperation, }; } @@ -271,7 +271,7 @@ function buildCleanTable( */ function extractEntityFields( entityType: IntrospectionType, - typeMap: Map + typeMap: Map, ): CleanField[] { const fields: CleanField[] = []; @@ -296,7 +296,7 @@ function extractEntityFields( // Include scalar, enum, and other non-relation fields fields.push({ name: field.name, - type: convertToCleanFieldType(field.type) + type: convertToCleanFieldType(field.type), }); } @@ -308,7 +308,7 @@ function extractEntityFields( */ function isEntityType( typeName: string, - typeMap: Map + typeMap: Map, ): boolean { const connectionName = `${pluralize(typeName)}Connection`; return typeMap.has(connectionName); @@ -318,14 +318,14 @@ function isEntityType( * Convert IntrospectionTypeRef to CleanFieldType */ function convertToCleanFieldType( - typeRef: IntrospectionTypeRef + typeRef: IntrospectionTypeRef, ): CleanFieldType { const baseType = unwrapType(typeRef); const isArray = isList(typeRef); return { gqlType: baseType.name ?? 'Unknown', - isArray + isArray, // PostgreSQL-specific fields are not available from introspection // They were optional anyway and not used by generators }; @@ -340,7 +340,7 @@ function convertToCleanFieldType( */ function inferRelations( entityType: IntrospectionType, - typeMap: Map + typeMap: Map, ): CleanRelations { const belongsTo: CleanBelongsToRelation[] = []; const hasMany: CleanHasManyRelation[] = []; @@ -372,7 +372,7 @@ function inferRelations( isUnique: false, // Can't determine from introspection alone referencesTable: baseTypeName, type: baseTypeName, - keys: [] // Would need FK info to populate + keys: [], // Would need FK info to populate }); } } @@ -389,7 +389,7 @@ function inferRelations( function inferHasManyOrManyToMany( field: IntrospectionField, connectionTypeName: string, - typeMap: Map + typeMap: Map, ): | { type: 'hasMany'; relation: CleanHasManyRelation } | { type: 'manyToMany'; relation: CleanManyToManyRelation } { @@ -416,7 +416,7 @@ function inferHasManyOrManyToMany( // e.g., "productsByProductCategoryProductIdAndCategoryId" → "ProductCategory" // The junction table name ends where the first field key begins (identified by capital letter after lowercase) const junctionMatch = field.name.match( - /By([A-Z][a-z]+(?:[A-Z][a-z]+)*?)(?:[A-Z][a-z]+Id)/ + /By([A-Z][a-z]+(?:[A-Z][a-z]+)*?)(?:[A-Z][a-z]+Id)/, ); const junctionTable = junctionMatch ? junctionMatch[1] : 'Unknown'; @@ -426,8 +426,8 @@ function inferHasManyOrManyToMany( fieldName: field.name, rightTable: actualEntityName, junctionTable, - type: connectionTypeName - } + type: connectionTypeName, + }, }; } @@ -438,8 +438,8 @@ function inferHasManyOrManyToMany( isUnique: false, referencedByTable: relatedEntityName, type: connectionTypeName, - keys: [] - } + keys: [], + }, }; } @@ -462,7 +462,7 @@ interface QueryOperations { function matchQueryOperations( entityName: string, queryFields: IntrospectionField[], - typeMap: Map + typeMap: Map, ): QueryOperations { const pluralName = pluralize(entityName); const connectionTypeName = `${pluralName}Connection`; @@ -488,7 +488,7 @@ function matchQueryOperations( (arg) => arg.name === 'id' || arg.name === 'nodeId' || - arg.name.toLowerCase().endsWith('id') + arg.name.toLowerCase().endsWith('id'), ); if (hasIdArg) { @@ -519,7 +519,7 @@ interface MutationOperations { */ function matchMutationOperations( entityName: string, - mutationFields: IntrospectionField[] + mutationFields: IntrospectionField[], ): MutationOperations { let create: string | null = null; let update: string | null = null; @@ -573,7 +573,7 @@ function matchMutationOperations( */ function inferConstraints( entityName: string, - typeMap: Map + typeMap: Map, ): TableConstraints { const primaryKey: ConstraintInfo[] = []; @@ -588,7 +588,7 @@ function inferConstraints( const inputToCheck = updateInput || deleteInput; if (inputToCheck?.inputFields) { const idField = inputToCheck.inputFields.find( - (f) => f.name === 'id' || f.name === 'nodeId' + (f) => f.name === 'id' || f.name === 'nodeId', ); if (idField) { @@ -597,9 +597,9 @@ function inferConstraints( fields: [ { name: idField.name, - type: convertToCleanFieldType(idField.type) - } - ] + type: convertToCleanFieldType(idField.type), + }, + ], }); } } @@ -609,7 +609,7 @@ function inferConstraints( const entityType = typeMap.get(entityName); if (entityType?.fields) { const idField = entityType.fields.find( - (f) => f.name === 'id' || f.name === 'nodeId' + (f) => f.name === 'id' || f.name === 'nodeId', ); if (idField) { @@ -618,9 +618,9 @@ function inferConstraints( fields: [ { name: idField.name, - type: convertToCleanFieldType(idField.type) - } - ] + type: convertToCleanFieldType(idField.type), + }, + ], }); } } @@ -629,7 +629,7 @@ function inferConstraints( return { primaryKey, foreignKey: [], // Would need FK info to populate - unique: [] // Would need constraint info to populate + unique: [], // Would need constraint info to populate }; } @@ -642,7 +642,7 @@ function inferConstraints( */ function buildInflection( entityName: string, - typeMap: Map + typeMap: Map, ): TableInflection { const pluralName = pluralize(entityName); const singularFieldName = lcFirst(entityName); @@ -682,7 +682,7 @@ function buildInflection( tableType: entityName, typeName: entityName, updateByPrimaryKey: `update${entityName}`, - updatePayloadType: hasUpdatePayload ? `Update${entityName}Payload` : null + updatePayloadType: hasUpdatePayload ? `Update${entityName}Payload` : null, }; } @@ -698,7 +698,7 @@ function buildInflection( function findOrderByType( entityName: string, pluralName: string, - typeMap: Map + typeMap: Map, ): string | null { // Try the standard pattern first: {PluralName}OrderBy const standardName = `${pluralName}OrderBy`; @@ -711,7 +711,7 @@ function findOrderByType( const candidates = [ `${entityName}sOrderBy`, // Simple 's' plural: User -> UsersOrderBy `${entityName}esOrderBy`, // 'es' plural: Address -> AddressesOrderBy - `${entityName}OrderBy` // No change (already plural or singular OK) + `${entityName}OrderBy`, // No change (already plural or singular OK) ]; // Check each candidate @@ -748,7 +748,7 @@ function findOrderByType( * Build a map of type name → IntrospectionType for efficient lookup */ function buildTypeMap( - types: IntrospectionType[] + types: IntrospectionType[], ): Map { const map = new Map(); for (const type of types) { @@ -761,7 +761,7 @@ function buildTypeMap( * Get fields from a type, returning empty array if null */ function getTypeFields( - type: IntrospectionType | undefined + type: IntrospectionType | undefined, ): IntrospectionField[] { return type?.fields ?? []; } diff --git a/graphql/codegen/src/core/introspect/schema-query.ts b/graphql/codegen/src/core/introspect/schema-query.ts index 6cdf5dbfa..d1ebdcf4c 100644 --- a/graphql/codegen/src/core/introspect/schema-query.ts +++ b/graphql/codegen/src/core/introspect/schema-query.ts @@ -1,6 +1,6 @@ /** * GraphQL Schema Introspection Query - * + * * Full introspection query that captures all queries, mutations, and types * from a GraphQL endpoint via the standard __schema query. */ @@ -9,12 +9,12 @@ import type { IntrospectionQueryResponse } from '../../types/introspection'; /** * Full schema introspection query - * + * * Captures: * - All Query fields with args and return types - * - All Mutation fields with args and return types + * - All Mutation fields with args and return types * - All types (OBJECT, INPUT_OBJECT, ENUM, SCALAR) for resolution - * + * * Uses a recursive TypeRef fragment to handle deeply nested type wrappers * (e.g., [String!]! = NON_NULL(LIST(NON_NULL(SCALAR)))) */ diff --git a/graphql/codegen/src/core/introspect/source/api-schemas.ts b/graphql/codegen/src/core/introspect/source/api-schemas.ts index 4b81f5280..d4a17b62b 100644 --- a/graphql/codegen/src/core/introspect/source/api-schemas.ts +++ b/graphql/codegen/src/core/introspect/source/api-schemas.ts @@ -27,7 +27,7 @@ export interface ServicesSchemaValidation { * @returns Validation result */ export async function validateServicesSchemas( - pool: Pool + pool: Pool, ): Promise { try { // Check for services_public.apis table @@ -39,7 +39,8 @@ export async function validateServicesSchemas( if (apisCheck.rows.length === 0) { return { valid: false, - error: 'services_public.apis table not found. The database must have the services schema deployed.' + error: + 'services_public.apis table not found. The database must have the services schema deployed.', }; } @@ -52,7 +53,8 @@ export async function validateServicesSchemas( if (apiSchemasCheck.rows.length === 0) { return { valid: false, - error: 'services_public.api_schemas table not found. The database must have the services schema deployed.' + error: + 'services_public.api_schemas table not found. The database must have the services schema deployed.', }; } @@ -65,7 +67,8 @@ export async function validateServicesSchemas( if (metaschemaCheck.rows.length === 0) { return { valid: false, - error: 'metaschema_public.schema table not found. The database must have the metaschema deployed.' + error: + 'metaschema_public.schema table not found. The database must have the metaschema deployed.', }; } @@ -73,7 +76,7 @@ export async function validateServicesSchemas( } catch (err) { return { valid: false, - error: `Failed to validate services schemas: ${err instanceof Error ? err.message : 'Unknown error'}` + error: `Failed to validate services schemas: ${err instanceof Error ? err.message : 'Unknown error'}`, }; } } @@ -91,7 +94,7 @@ export async function validateServicesSchemas( */ export async function resolveApiSchemas( pool: Pool, - apiNames: string[] + apiNames: string[], ): Promise { // First validate that the required schemas exist const validation = await validateServicesSchemas(pool); @@ -109,13 +112,13 @@ export async function resolveApiSchemas( WHERE api.name = ANY($1) ORDER BY ms.schema_name `, - [apiNames] + [apiNames], ); if (result.rows.length === 0) { throw new Error( `No schemas found for API names: ${apiNames.join(', ')}. ` + - 'Ensure the APIs exist and have schemas assigned in services_public.api_schemas.' + 'Ensure the APIs exist and have schemas assigned in services_public.api_schemas.', ); } @@ -130,8 +133,9 @@ export async function resolveApiSchemas( */ export function createDatabasePool(database: string): Pool { // Check if it's a connection string or just a database name - const isConnectionString = database.startsWith('postgres://') || database.startsWith('postgresql://'); - + const isConnectionString = + database.startsWith('postgres://') || database.startsWith('postgresql://'); + if (isConnectionString) { // Parse connection string and extract database name // Format: postgres://user:password@host:port/database @@ -142,10 +146,10 @@ export function createDatabasePool(database: string): Pool { port: parseInt(url.port || '5432', 10), user: url.username, password: url.password, - database: dbName + database: dbName, }); } - + // Use environment variables for connection, just override database name const config = getPgEnvOptions({ database }); return getPgPool(config); diff --git a/graphql/codegen/src/core/introspect/source/database.ts b/graphql/codegen/src/core/introspect/source/database.ts index 55dfb0355..5071fbe0b 100644 --- a/graphql/codegen/src/core/introspect/source/database.ts +++ b/graphql/codegen/src/core/introspect/source/database.ts @@ -8,7 +8,11 @@ import { buildSchema, introspectionFromSchema } from 'graphql'; import type { IntrospectionQueryResponse } from '../../../types/introspection'; import { buildSchemaSDLFromDatabase } from '../../database'; -import { createDatabasePool, resolveApiSchemas, validateServicesSchemas } from './api-schemas'; +import { + createDatabasePool, + resolveApiSchemas, + validateServicesSchemas, +} from './api-schemas'; import type { SchemaSource, SchemaSourceResult } from './types'; import { SchemaSourceError } from './types'; @@ -66,7 +70,7 @@ export class DatabaseSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to resolve API schemas: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } } else { @@ -78,13 +82,13 @@ export class DatabaseSchemaSource implements SchemaSource { try { sdl = await buildSchemaSDLFromDatabase({ database, - schemas + schemas, }); } catch (err) { throw new SchemaSourceError( `Failed to introspect database: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -92,7 +96,7 @@ export class DatabaseSchemaSource implements SchemaSource { if (!sdl.trim()) { throw new SchemaSourceError( 'Database introspection returned empty schema', - this.describe() + this.describe(), ); } @@ -104,7 +108,7 @@ export class DatabaseSchemaSource implements SchemaSource { throw new SchemaSourceError( `Invalid GraphQL SDL from database: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -116,13 +120,13 @@ export class DatabaseSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to generate introspection: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } // Convert graphql-js introspection result to our mutable type const introspection: IntrospectionQueryResponse = JSON.parse( - JSON.stringify(introspectionResult) + JSON.stringify(introspectionResult), ) as IntrospectionQueryResponse; return { introspection }; diff --git a/graphql/codegen/src/core/introspect/source/endpoint.ts b/graphql/codegen/src/core/introspect/source/endpoint.ts index 20ec8d931..bc952290e 100644 --- a/graphql/codegen/src/core/introspect/source/endpoint.ts +++ b/graphql/codegen/src/core/introspect/source/endpoint.ts @@ -45,25 +45,25 @@ export class EndpointSchemaSource implements SchemaSource { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: this.options.timeout + timeout: this.options.timeout, }); if (!result.success) { throw new SchemaSourceError( result.error ?? 'Unknown error fetching schema', - this.describe() + this.describe(), ); } if (!result.data) { throw new SchemaSourceError( 'No introspection data returned', - this.describe() + this.describe(), ); } return { - introspection: result.data + introspection: result.data, }; } diff --git a/graphql/codegen/src/core/introspect/source/file.ts b/graphql/codegen/src/core/introspect/source/file.ts index 58ffe6b33..0451be703 100644 --- a/graphql/codegen/src/core/introspect/source/file.ts +++ b/graphql/codegen/src/core/introspect/source/file.ts @@ -43,7 +43,7 @@ export class FileSchemaSource implements SchemaSource { if (!fs.existsSync(absolutePath)) { throw new SchemaSourceError( `Schema file not found: ${absolutePath}`, - this.describe() + this.describe(), ); } @@ -55,7 +55,7 @@ export class FileSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to read schema file: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -72,7 +72,7 @@ export class FileSchemaSource implements SchemaSource { throw new SchemaSourceError( `Invalid GraphQL SDL: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -84,14 +84,14 @@ export class FileSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to generate introspection: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } // Convert graphql-js introspection result to our mutable type // The graphql-js types are readonly, but our types are mutable const introspection: IntrospectionQueryResponse = JSON.parse( - JSON.stringify(introspectionResult) + JSON.stringify(introspectionResult), ) as IntrospectionQueryResponse; return { introspection }; diff --git a/graphql/codegen/src/core/introspect/source/index.ts b/graphql/codegen/src/core/introspect/source/index.ts index c96b498be..8587d0bf1 100644 --- a/graphql/codegen/src/core/introspect/source/index.ts +++ b/graphql/codegen/src/core/introspect/source/index.ts @@ -18,9 +18,7 @@ import type { DbConfig } from '../../../types/config'; import { DatabaseSchemaSource } from './database'; import { EndpointSchemaSource } from './endpoint'; import { FileSchemaSource } from './file'; -import { - PgpmModuleSchemaSource -} from './pgpm-module'; +import { PgpmModuleSchemaSource } from './pgpm-module'; import type { SchemaSource } from './types'; /** @@ -105,15 +103,23 @@ export interface CreateSchemaSourceOptions { /** * Detect which source mode is being used based on options */ -export type SourceMode = 'endpoint' | 'schemaFile' | 'database' | 'pgpm-module' | 'pgpm-workspace'; +export type SourceMode = + | 'endpoint' + | 'schemaFile' + | 'database' + | 'pgpm-module' + | 'pgpm-workspace'; -export function detectSourceMode(options: CreateSchemaSourceOptions): SourceMode | null { +export function detectSourceMode( + options: CreateSchemaSourceOptions, +): SourceMode | null { if (options.endpoint) return 'endpoint'; if (options.schemaFile) return 'schemaFile'; if (options.db) { // Check for PGPM modes first if (options.db.pgpm?.modulePath) return 'pgpm-module'; - if (options.db.pgpm?.workspacePath && options.db.pgpm?.moduleName) return 'pgpm-workspace'; + if (options.db.pgpm?.workspacePath && options.db.pgpm?.moduleName) + return 'pgpm-workspace'; // Default to database mode if db is specified without pgpm return 'database'; } @@ -135,54 +141,54 @@ export function detectSourceMode(options: CreateSchemaSourceOptions): SourceMode * @throws Error if no valid source is provided */ export function createSchemaSource( - options: CreateSchemaSourceOptions + options: CreateSchemaSourceOptions, ): SchemaSource { const mode = detectSourceMode(options); switch (mode) { - case 'schemaFile': - return new FileSchemaSource({ - schemaPath: options.schemaFile! - }); + case 'schemaFile': + return new FileSchemaSource({ + schemaPath: options.schemaFile!, + }); - case 'endpoint': - return new EndpointSchemaSource({ - endpoint: options.endpoint!, - authorization: options.authorization, - headers: options.headers, - timeout: options.timeout - }); + case 'endpoint': + return new EndpointSchemaSource({ + endpoint: options.endpoint!, + authorization: options.authorization, + headers: options.headers, + timeout: options.timeout, + }); - case 'database': - // Database mode uses db.config for connection (falls back to env vars) - // and db.schemas or db.apiNames for schema selection - return new DatabaseSchemaSource({ - database: options.db?.config?.database ?? '', - schemas: options.db?.schemas, - apiNames: options.db?.apiNames - }); + case 'database': + // Database mode uses db.config for connection (falls back to env vars) + // and db.schemas or db.apiNames for schema selection + return new DatabaseSchemaSource({ + database: options.db?.config?.database ?? '', + schemas: options.db?.schemas, + apiNames: options.db?.apiNames, + }); - case 'pgpm-module': - return new PgpmModuleSchemaSource({ - pgpmModulePath: options.db!.pgpm!.modulePath!, - schemas: options.db?.schemas, - apiNames: options.db?.apiNames, - keepDb: options.db?.keepDb - }); + case 'pgpm-module': + return new PgpmModuleSchemaSource({ + pgpmModulePath: options.db!.pgpm!.modulePath!, + schemas: options.db?.schemas, + apiNames: options.db?.apiNames, + keepDb: options.db?.keepDb, + }); - case 'pgpm-workspace': - return new PgpmModuleSchemaSource({ - pgpmWorkspacePath: options.db!.pgpm!.workspacePath!, - pgpmModuleName: options.db!.pgpm!.moduleName!, - schemas: options.db?.schemas, - apiNames: options.db?.apiNames, - keepDb: options.db?.keepDb - }); + case 'pgpm-workspace': + return new PgpmModuleSchemaSource({ + pgpmWorkspacePath: options.db!.pgpm!.workspacePath!, + pgpmModuleName: options.db!.pgpm!.moduleName!, + schemas: options.db?.schemas, + apiNames: options.db?.apiNames, + keepDb: options.db?.keepDb, + }); - default: - throw new Error( - 'No source specified. Use one of: endpoint, schemaFile, or db (with optional pgpm for module deployment).' - ); + default: + throw new Error( + 'No source specified. Use one of: endpoint, schemaFile, or db (with optional pgpm for module deployment).', + ); } } @@ -194,17 +200,14 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { error?: string; } { // Count primary sources - const sources = [ - options.endpoint, - options.schemaFile, - options.db - ].filter(Boolean); + const sources = [options.endpoint, options.schemaFile, options.db].filter( + Boolean, + ); if (sources.length === 0) { return { valid: false, - error: - 'No source specified. Use one of: endpoint, schemaFile, or db.' + error: 'No source specified. Use one of: endpoint, schemaFile, or db.', }; } @@ -212,7 +215,7 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { return { valid: false, error: - 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.' + 'Multiple sources specified. Use only one of: endpoint, schemaFile, or db.', }; } @@ -222,14 +225,16 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (pgpm.workspacePath && !pgpm.moduleName) { return { valid: false, - error: 'db.pgpm.workspacePath requires db.pgpm.moduleName to be specified.' + error: + 'db.pgpm.workspacePath requires db.pgpm.moduleName to be specified.', }; } if (pgpm.moduleName && !pgpm.workspacePath) { return { valid: false, - error: 'db.pgpm.moduleName requires db.pgpm.workspacePath to be specified.' + error: + 'db.pgpm.moduleName requires db.pgpm.workspacePath to be specified.', }; } @@ -237,7 +242,8 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (!pgpm.modulePath && !(pgpm.workspacePath && pgpm.moduleName)) { return { valid: false, - error: 'db.pgpm requires either modulePath or both workspacePath and moduleName.' + error: + 'db.pgpm requires either modulePath or both workspacePath and moduleName.', }; } } @@ -250,14 +256,16 @@ export function validateSourceOptions(options: CreateSchemaSourceOptions): { if (hasSchemas && hasApiNames) { return { valid: false, - error: 'Cannot specify both db.schemas and db.apiNames. Use one or the other.' + error: + 'Cannot specify both db.schemas and db.apiNames. Use one or the other.', }; } if (!hasSchemas && !hasApiNames) { return { valid: false, - error: 'Must specify either db.schemas or db.apiNames for database mode.' + error: + 'Must specify either db.schemas or db.apiNames for database mode.', }; } } diff --git a/graphql/codegen/src/core/introspect/source/pgpm-module.ts b/graphql/codegen/src/core/introspect/source/pgpm-module.ts index 4bbc7e14e..2a02fa70b 100644 --- a/graphql/codegen/src/core/introspect/source/pgpm-module.ts +++ b/graphql/codegen/src/core/introspect/source/pgpm-module.ts @@ -84,13 +84,15 @@ export interface PgpmWorkspaceOptions { keepDb?: boolean; } -export type PgpmModuleSchemaSourceOptions = PgpmModulePathOptions | PgpmWorkspaceOptions; +export type PgpmModuleSchemaSourceOptions = + | PgpmModulePathOptions + | PgpmWorkspaceOptions; /** * Type guard to check if options use direct module path */ export function isPgpmModulePathOptions( - options: PgpmModuleSchemaSourceOptions + options: PgpmModuleSchemaSourceOptions, ): options is PgpmModulePathOptions { return 'pgpmModulePath' in options; } @@ -99,7 +101,7 @@ export function isPgpmModulePathOptions( * Type guard to check if options use workspace + module name */ export function isPgpmWorkspaceOptions( - options: PgpmModuleSchemaSourceOptions + options: PgpmModuleSchemaSourceOptions, ): options is PgpmWorkspaceOptions { return 'pgpmWorkspacePath' in options && 'pgpmModuleName' in options; } @@ -130,7 +132,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to resolve module path: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -139,7 +141,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { if (!pkg.isInModule()) { throw new SchemaSourceError( `Not a valid PGPM module: ${modulePath}. Directory must contain pgpm.plan and .control files.`, - this.describe() + this.describe(), ); } @@ -147,13 +149,13 @@ export class PgpmModuleSchemaSource implements SchemaSource { try { this.ephemeralDb = createEphemeralDb({ prefix: 'codegen_pgpm_', - verbose: false + verbose: false, }); } catch (err) { throw new SchemaSourceError( `Failed to create ephemeral database: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -167,7 +169,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to deploy PGPM module: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -187,7 +189,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to resolve API schemas: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } } else { @@ -199,13 +201,13 @@ export class PgpmModuleSchemaSource implements SchemaSource { try { sdl = await buildSchemaSDLFromDatabase({ database: dbConfig.database, - schemas + schemas, }); } catch (err) { throw new SchemaSourceError( `Failed to introspect database: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -213,7 +215,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { if (!sdl.trim()) { throw new SchemaSourceError( 'Database introspection returned empty schema', - this.describe() + this.describe(), ); } @@ -225,7 +227,7 @@ export class PgpmModuleSchemaSource implements SchemaSource { throw new SchemaSourceError( `Invalid GraphQL SDL from database: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } @@ -237,13 +239,13 @@ export class PgpmModuleSchemaSource implements SchemaSource { throw new SchemaSourceError( `Failed to generate introspection: ${err instanceof Error ? err.message : 'Unknown error'}`, this.describe(), - err instanceof Error ? err : undefined + err instanceof Error ? err : undefined, ); } // Convert graphql-js introspection result to our mutable type const introspection: IntrospectionQueryResponse = JSON.parse( - JSON.stringify(introspectionResult) + JSON.stringify(introspectionResult), ) as IntrospectionQueryResponse; return { introspection }; @@ -252,7 +254,9 @@ export class PgpmModuleSchemaSource implements SchemaSource { teardown({ keepDb }); if (keepDb) { - console.log(`[pgpm-module] Kept ephemeral database: ${dbConfig.database}`); + console.log( + `[pgpm-module] Kept ephemeral database: ${dbConfig.database}`, + ); } } } diff --git a/graphql/codegen/src/core/introspect/source/types.ts b/graphql/codegen/src/core/introspect/source/types.ts index 18f301cad..2dfc59234 100644 --- a/graphql/codegen/src/core/introspect/source/types.ts +++ b/graphql/codegen/src/core/introspect/source/types.ts @@ -51,7 +51,7 @@ export class SchemaSourceError extends Error { /** * Original error that caused the failure */ - public readonly cause?: Error + public readonly cause?: Error, ) { super(`${message} (source: ${source})`); this.name = 'SchemaSourceError'; diff --git a/graphql/codegen/src/core/introspect/transform-schema.ts b/graphql/codegen/src/core/introspect/transform-schema.ts index a7edd5216..c872da11c 100644 --- a/graphql/codegen/src/core/introspect/transform-schema.ts +++ b/graphql/codegen/src/core/introspect/transform-schema.ts @@ -9,12 +9,12 @@ import type { IntrospectionInputValue, IntrospectionQueryResponse, IntrospectionType, - IntrospectionTypeRef + IntrospectionTypeRef, } from '../../types/introspection'; import { getBaseTypeName, isNonNull, - unwrapType + unwrapType, } from '../../types/introspection'; import type { CleanArgument, @@ -22,7 +22,7 @@ import type { CleanOperation, CleanTypeRef, ResolvedType, - TypeRegistry + TypeRegistry, } from '../../types/schema'; // ============================================================================ @@ -48,7 +48,7 @@ export function buildTypeRegistry(types: IntrospectionType[]): TypeRegistry { const resolvedType: ResolvedType = { kind: type.kind as ResolvedType['kind'], name: type.name, - description: type.description ?? undefined + description: type.description ?? undefined, }; // Resolve enum values for ENUM types (no circular refs possible) @@ -74,14 +74,14 @@ export function buildTypeRegistry(types: IntrospectionType[]): TypeRegistry { // Resolve fields for OBJECT types if (type.kind === 'OBJECT' && type.fields) { resolvedType.fields = type.fields.map((field) => - transformFieldToCleanObjectFieldShallow(field) + transformFieldToCleanObjectFieldShallow(field), ); } // Resolve input fields for INPUT_OBJECT types if (type.kind === 'INPUT_OBJECT' && type.inputFields) { resolvedType.inputFields = type.inputFields.map((field) => - transformInputValueToCleanArgumentShallow(field) + transformInputValueToCleanArgumentShallow(field), ); } } @@ -94,12 +94,12 @@ export function buildTypeRegistry(types: IntrospectionType[]): TypeRegistry { * (shallow transformation to avoid circular refs) */ function transformFieldToCleanObjectFieldShallow( - field: IntrospectionField + field: IntrospectionField, ): CleanObjectField { return { name: field.name, type: transformTypeRefShallow(field.type), - description: field.description ?? undefined + description: field.description ?? undefined, }; } @@ -107,13 +107,13 @@ function transformFieldToCleanObjectFieldShallow( * Transform input value to CleanArgument without resolving nested types */ function transformInputValueToCleanArgumentShallow( - inputValue: IntrospectionInputValue + inputValue: IntrospectionInputValue, ): CleanArgument { return { name: inputValue.name, type: transformTypeRefShallow(inputValue.type), defaultValue: inputValue.defaultValue ?? undefined, - description: inputValue.description ?? undefined + description: inputValue.description ?? undefined, }; } @@ -124,7 +124,7 @@ function transformInputValueToCleanArgumentShallow( function transformTypeRefShallow(typeRef: IntrospectionTypeRef): CleanTypeRef { const cleanRef: CleanTypeRef = { kind: typeRef.kind as CleanTypeRef['kind'], - name: typeRef.name + name: typeRef.name, }; if (typeRef.ofType) { @@ -148,7 +148,7 @@ export interface TransformSchemaResult { * Transform introspection response to clean operations */ export function transformSchemaToOperations( - response: IntrospectionQueryResponse + response: IntrospectionQueryResponse, ): TransformSchemaResult { const { __schema: schema } = response; const { types, queryType, mutationType } = schema; @@ -165,15 +165,15 @@ export function transformSchemaToOperations( // Transform queries const queries: CleanOperation[] = queryTypeDef?.fields ? queryTypeDef.fields.map((field) => - transformFieldToCleanOperation(field, 'query', types) - ) + transformFieldToCleanOperation(field, 'query', types), + ) : []; // Transform mutations const mutations: CleanOperation[] = mutationTypeDef?.fields ? mutationTypeDef.fields.map((field) => - transformFieldToCleanOperation(field, 'mutation', types) - ) + transformFieldToCleanOperation(field, 'mutation', types), + ) : []; return { queries, mutations, typeRegistry }; @@ -189,18 +189,18 @@ export function transformSchemaToOperations( function transformFieldToCleanOperation( field: IntrospectionField, kind: 'query' | 'mutation', - types: IntrospectionType[] + types: IntrospectionType[], ): CleanOperation { return { name: field.name, kind, args: field.args.map((arg) => - transformInputValueToCleanArgument(arg, types) + transformInputValueToCleanArgument(arg, types), ), returnType: transformTypeRefToCleanTypeRef(field.type, types), description: field.description ?? undefined, isDeprecated: field.isDeprecated, - deprecationReason: field.deprecationReason ?? undefined + deprecationReason: field.deprecationReason ?? undefined, }; } @@ -209,13 +209,13 @@ function transformFieldToCleanOperation( */ function transformInputValueToCleanArgument( inputValue: IntrospectionInputValue, - types: IntrospectionType[] + types: IntrospectionType[], ): CleanArgument { return { name: inputValue.name, type: transformTypeRefToCleanTypeRef(inputValue.type, types), defaultValue: inputValue.defaultValue ?? undefined, - description: inputValue.description ?? undefined + description: inputValue.description ?? undefined, }; } @@ -233,11 +233,11 @@ function transformInputValueToCleanArgument( */ function transformTypeRefToCleanTypeRef( typeRef: IntrospectionTypeRef, - types: IntrospectionType[] + types: IntrospectionType[], ): CleanTypeRef { const cleanRef: CleanTypeRef = { kind: typeRef.kind as CleanTypeRef['kind'], - name: typeRef.name + name: typeRef.name, }; // Recursively transform ofType for wrappers (LIST, NON_NULL) @@ -273,7 +273,7 @@ function transformTypeRefToCleanTypeRef( export function filterOperations( operations: CleanOperation[], include?: string[], - exclude?: string[] + exclude?: string[], ): CleanOperation[] { let result = operations; @@ -297,7 +297,7 @@ function matchesPatterns(name: string, patterns: string[]): boolean { if (pattern === '*') return true; if (pattern.includes('*')) { const regex = new RegExp( - '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$' + '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', ); return regex.test(name); } @@ -344,7 +344,7 @@ export function getTableOperationNames( update: string | null; delete: string | null; }; - }> + }>, ): TableOperationNames { const queries = new Set(); const mutations = new Set(); @@ -375,7 +375,7 @@ export function getTableOperationNames( */ export function isTableOperation( operation: CleanOperation, - tableOperationNames: TableOperationNames + tableOperationNames: TableOperationNames, ): boolean { if (operation.kind === 'query') { return tableOperationNames.queries.has(operation.name); @@ -394,10 +394,10 @@ export function isTableOperation( */ export function getCustomOperations( operations: CleanOperation[], - tableOperationNames: TableOperationNames + tableOperationNames: TableOperationNames, ): CleanOperation[] { return operations.filter((op) => !isTableOperation(op, tableOperationNames)); } // Re-export utility functions from introspection types -export { getBaseTypeName, isNonNull,unwrapType }; +export { getBaseTypeName, isNonNull, unwrapType }; diff --git a/graphql/codegen/src/core/introspect/transform.ts b/graphql/codegen/src/core/introspect/transform.ts index 41d8de0f1..95ce1a2e0 100644 --- a/graphql/codegen/src/core/introspect/transform.ts +++ b/graphql/codegen/src/core/introspect/transform.ts @@ -19,7 +19,7 @@ export function getTableNames(tables: CleanTable[]): string[] { */ export function findTable( tables: CleanTable[], - name: string + name: string, ): CleanTable | undefined { return tables.find((t) => t.name === name); } @@ -30,7 +30,7 @@ export function findTable( export function filterTables( tables: CleanTable[], include?: string[], - exclude?: string[] + exclude?: string[], ): CleanTable[] { let result = tables; @@ -53,7 +53,7 @@ function matchesPatterns(name: string, patterns: string[]): boolean { return patterns.some((pattern) => { if (pattern.includes('*')) { const regex = new RegExp( - '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$' + '^' + pattern.replace(/\*/g, '.*').replace(/\?/g, '.') + '$', ); return regex.test(name); } diff --git a/graphql/codegen/src/core/meta-object/convert.ts b/graphql/codegen/src/core/meta-object/convert.ts index 295ae7841..72ec26134 100644 --- a/graphql/codegen/src/core/meta-object/convert.ts +++ b/graphql/codegen/src/core/meta-object/convert.ts @@ -75,14 +75,14 @@ interface ConvertedMetaObject { * Convert from raw _meta schema response to internal MetaObject format */ export function convertFromMetaSchema( - metaSchema: MetaSchemaInput + metaSchema: MetaSchemaInput, ): ConvertedMetaObject { const { - _meta: { tables } + _meta: { tables }, } = metaSchema; const result: ConvertedMetaObject = { - tables: [] + tables: [], }; for (const table of tables) { @@ -93,8 +93,8 @@ export function convertFromMetaSchema( uniqueConstraints: pickArrayConstraint(table.uniqueConstraints), foreignConstraints: pickForeignConstraint( table.foreignKeyConstraints, - table.relations - ) + table.relations, + ), }); } @@ -102,7 +102,7 @@ export function convertFromMetaSchema( } function pickArrayConstraint( - constraints: MetaSchemaConstraint[] + constraints: MetaSchemaConstraint[], ): ConvertedConstraint[] { if (constraints.length === 0) return []; const c = constraints[0]; @@ -111,7 +111,7 @@ function pickArrayConstraint( function pickForeignConstraint( constraints: MetaSchemaForeignConstraint[], - relations: MetaSchemaRelations + relations: MetaSchemaRelations, ): ConvertedForeignConstraint[] { if (constraints.length === 0) return []; @@ -136,7 +136,7 @@ function pickForeignConstraint( return { refTable: refTable.name, fromKey, - toKey + toKey, }; }); } @@ -144,13 +144,13 @@ function pickForeignConstraint( function pickField(field: MetaSchemaField): ConvertedField { return { name: field.name, - type: field.type + type: field.type, }; } function pickConstraintField(field: MetaSchemaField): ConvertedConstraint { return { name: field.name, - type: field.type + type: field.type, }; } diff --git a/graphql/codegen/src/core/meta-object/validate.ts b/graphql/codegen/src/core/meta-object/validate.ts index eab7a346b..802e0aad1 100644 --- a/graphql/codegen/src/core/meta-object/validate.ts +++ b/graphql/codegen/src/core/meta-object/validate.ts @@ -18,7 +18,7 @@ function getValidator() { return { ajv: cachedAjv, - validator: cachedValidator! + validator: cachedValidator!, }; } @@ -26,9 +26,7 @@ function getValidator() { * Validate a MetaObject against the JSON schema * @returns true if valid, or an object with errors and message if invalid */ -export function validateMetaObject( - obj: unknown -): true | ValidationResult { +export function validateMetaObject(obj: unknown): true | ValidationResult { const { ajv, validator } = getValidator(); const valid = validator(obj); @@ -36,6 +34,6 @@ export function validateMetaObject( return { errors: validator.errors, - message: ajv.errorsText(validator.errors, { separator: '\n' }) + message: ajv.errorsText(validator.errors, { separator: '\n' }), }; } diff --git a/graphql/codegen/src/core/output/index.ts b/graphql/codegen/src/core/output/index.ts index 529f10e41..d5047a2fc 100644 --- a/graphql/codegen/src/core/output/index.ts +++ b/graphql/codegen/src/core/output/index.ts @@ -7,5 +7,5 @@ export { type GeneratedFile, writeGeneratedFiles, type WriteOptions, - type WriteResult + type WriteResult, } from './writer'; diff --git a/graphql/codegen/src/core/output/writer.ts b/graphql/codegen/src/core/output/writer.ts index 8b04f1613..9ea041587 100644 --- a/graphql/codegen/src/core/output/writer.ts +++ b/graphql/codegen/src/core/output/writer.ts @@ -33,7 +33,7 @@ export interface WriteOptions { type OxfmtFormatFn = ( fileName: string, sourceText: string, - options?: Record + options?: Record, ) => Promise<{ code: string; errors: unknown[] }>; /** @@ -55,14 +55,14 @@ async function getOxfmtFormat(): Promise { async function formatFileContent( fileName: string, content: string, - formatFn: OxfmtFormatFn + formatFn: OxfmtFormatFn, ): Promise { try { const result = await formatFn(fileName, content, { singleQuote: true, trailingComma: 'es5', tabWidth: 2, - semi: true + semi: true, }); return result.code; } catch { @@ -83,7 +83,7 @@ export async function writeGeneratedFiles( files: GeneratedFile[], outputDir: string, subdirs: string[], - options: WriteOptions = {} + options: WriteOptions = {}, ): Promise { const { showProgress = true, formatFiles = true } = options; const errors: string[] = []; @@ -98,7 +98,7 @@ export async function writeGeneratedFiles( const message = err instanceof Error ? err.message : 'Unknown error'; return { success: false, - errors: [`Failed to create output directory: ${message}`] + errors: [`Failed to create output directory: ${message}`], }; } @@ -132,7 +132,7 @@ export async function writeGeneratedFiles( const progress = Math.round(((i + 1) / total) * 100); if (isTTY) { process.stdout.write( - `\rWriting files: ${i + 1}/${total} (${progress}%)` + `\rWriting files: ${i + 1}/${total} (${progress}%)`, ); } else if (i % 100 === 0 || i === total - 1) { // Non-TTY: periodic updates for CI/CD @@ -171,7 +171,7 @@ export async function writeGeneratedFiles( return { success: errors.length === 0, filesWritten: written, - errors: errors.length > 0 ? errors : undefined + errors: errors.length > 0 ? errors : undefined, }; } @@ -205,13 +205,13 @@ function findTsFiles(dir: string): string[] { * This function is kept for backwards compatibility. */ export async function formatOutput( - outputDir: string + outputDir: string, ): Promise<{ success: boolean; error?: string }> { const formatFn = await getOxfmtFormat(); if (!formatFn) { return { success: false, - error: 'oxfmt not available. Install it with: npm install oxfmt' + error: 'oxfmt not available. Install it with: npm install oxfmt', }; } @@ -223,7 +223,11 @@ export async function formatOutput( for (const filePath of tsFiles) { const content = fs.readFileSync(filePath, 'utf-8'); - const formatted = await formatFileContent(path.basename(filePath), content, formatFn); + const formatted = await formatFileContent( + path.basename(filePath), + content, + formatFn, + ); fs.writeFileSync(filePath, formatted, 'utf-8'); } diff --git a/graphql/codegen/src/core/pipeline/index.ts b/graphql/codegen/src/core/pipeline/index.ts index f2d33456d..55ba15cee 100644 --- a/graphql/codegen/src/core/pipeline/index.ts +++ b/graphql/codegen/src/core/pipeline/index.ts @@ -12,7 +12,7 @@ import type { GraphQLSDKConfigTarget } from '../../types/config'; import type { CleanOperation, CleanTable, - TypeRegistry + TypeRegistry, } from '../../types/schema'; import { inferTablesFromIntrospection } from '../introspect/infer-tables'; import type { SchemaSource } from '../introspect/source'; @@ -21,12 +21,15 @@ import { filterOperations, getCustomOperations, getTableOperationNames, - transformSchemaToOperations + transformSchemaToOperations, } from '../introspect/transform-schema'; // Re-export for convenience export type { SchemaSource } from '../introspect/source'; -export { createSchemaSource, validateSourceOptions } from '../introspect/source'; +export { + createSchemaSource, + validateSourceOptions, +} from '../introspect/source'; // ============================================================================ // Pipeline Types @@ -97,13 +100,13 @@ export interface CodegenPipelineResult { * 5. Separate table operations from custom operations */ export async function runCodegenPipeline( - options: CodegenPipelineOptions + options: CodegenPipelineOptions, ): Promise { const { source, config, verbose = false, - skipCustomOperations = false + skipCustomOperations = false, } = options; const log = verbose ? console.log : () => {}; @@ -120,7 +123,7 @@ export async function runCodegenPipeline( // 3. Filter tables by config (combine exclude and systemExclude) tables = filterTables(tables, config.tables.include, [ ...config.tables.exclude, - ...config.tables.systemExclude + ...config.tables.systemExclude, ]); const filteredTables = tables.length; log(` After filtering: ${filteredTables} tables`); @@ -130,7 +133,7 @@ export async function runCodegenPipeline( const { queries: allQueries, mutations: allMutations, - typeRegistry + typeRegistry, } = transformSchemaToOperations(introspection); const totalQueries = allQueries.length; @@ -149,27 +152,27 @@ export async function runCodegenPipeline( const filteredQueries = filterOperations( allQueries, config.queries.include, - [...config.queries.exclude, ...config.queries.systemExclude] + [...config.queries.exclude, ...config.queries.systemExclude], ); const filteredMutations = filterOperations( allMutations, config.mutations.include, - [...config.mutations.exclude, ...config.mutations.systemExclude] + [...config.mutations.exclude, ...config.mutations.systemExclude], ); log( - ` After config filtering: ${filteredQueries.length} queries, ${filteredMutations.length} mutations` + ` After config filtering: ${filteredQueries.length} queries, ${filteredMutations.length} mutations`, ); // Remove table operations (already handled by table generators) customQueries = getCustomOperations(filteredQueries, tableOperationNames); customMutations = getCustomOperations( filteredMutations, - tableOperationNames + tableOperationNames, ); log( - ` Custom operations: ${customQueries.length} queries, ${customMutations.length} mutations` + ` Custom operations: ${customQueries.length} queries, ${customMutations.length} mutations`, ); } @@ -178,7 +181,7 @@ export async function runCodegenPipeline( customOperations: { queries: customQueries, mutations: customMutations, - typeRegistry + typeRegistry, }, stats: { totalTables, @@ -186,8 +189,8 @@ export async function runCodegenPipeline( totalQueries, totalMutations, customQueries: customQueries.length, - customMutations: customMutations.length - } + customMutations: customMutations.length, + }, }; } @@ -206,7 +209,7 @@ export function validateTablesFound(tables: CleanTable[]): { return { valid: false, error: - 'No tables found after filtering. Check your include/exclude patterns.' + 'No tables found after filtering. Check your include/exclude patterns.', }; } return { valid: true }; diff --git a/graphql/codegen/src/core/query-builder.ts b/graphql/codegen/src/core/query-builder.ts index 403ed2e3b..7d6bbbe5d 100644 --- a/graphql/codegen/src/core/query-builder.ts +++ b/graphql/codegen/src/core/query-builder.ts @@ -1,5 +1,5 @@ import { DocumentNode, print as gqlPrint } from 'graphql'; -import { camelize, pluralize,underscore } from 'inflekt'; +import { camelize, pluralize, underscore } from 'inflekt'; import { createOne, @@ -8,7 +8,7 @@ import { getCount, getMany, getOne, - patchOne + patchOne, } from './ast'; import { validateMetaObject } from './meta-object'; import type { @@ -20,7 +20,7 @@ import type { QueryBuilderResult, QueryDefinition, QueryFieldSelection, - QuerySelectionOptions + QuerySelectionOptions, } from './types'; export * as MetaObject from './meta-object'; @@ -44,10 +44,7 @@ export class QueryBuilder { private _mutation!: string; private _select!: QueryFieldSelection[]; - constructor({ - meta = {} as MetaObject, - introspection - }: QueryBuilderOptions) { + constructor({ meta = {} as MetaObject, introspection }: QueryBuilderOptions) { this._introspection = introspection; this._meta = meta; this.clear(); @@ -58,7 +55,7 @@ export class QueryBuilder { const result = validateMetaObject(this._meta); if (typeof result === 'object' && result.errors) { throw new Error( - `QueryBuilder: meta object is invalid:\n${result.message}` + `QueryBuilder: meta object is invalid:\n${result.message}`, ); } } @@ -67,7 +64,10 @@ export class QueryBuilder { * Save all gql queries and mutations by model name for quicker lookup */ initModelMap(): void { - this._models = {} as Record>; + this._models = {} as Record< + string, + Record + >; for (const [key, defn] of Object.entries(this._introspection)) { if (!this._models[defn.model]) { @@ -102,7 +102,7 @@ export class QueryBuilder { } const matchQuery = Object.entries(queries).find( - ([_, defn]) => defn.qtype === this._op + ([_, defn]) => defn.qtype === this._op, ); if (!matchQuery) { @@ -132,39 +132,39 @@ export class QueryBuilder { [] as Array<{ defn: QueryDefinition | MutationDefinition; mutationKey: string; - }> + }>, ); if (matchingDefns.length === 0) { throw new Error( - 'no mutation found for ' + this._model + ':' + this._mutation + 'no mutation found for ' + this._model + ':' + this._mutation, ); } // We only need deleteAction from all of [deleteAction, deleteActionBySlug, deleteActionByName] const getInputName = (mutationType: string): string => { switch (mutationType) { - case 'delete': { - return `Delete${camelize(this._model)}Input`; - } - case 'create': { - return `Create${camelize(this._model)}Input`; - } - case 'patch': { - return `Update${camelize(this._model)}Input`; - } - default: - throw new Error('Unhandled mutation type' + mutationType); + case 'delete': { + return `Delete${camelize(this._model)}Input`; + } + case 'create': { + return `Create${camelize(this._model)}Input`; + } + case 'patch': { + return `Update${camelize(this._model)}Input`; + } + default: + throw new Error('Unhandled mutation type' + mutationType); } }; const matchDefn = matchingDefns.find( - ({ defn }) => defn.properties.input.type === getInputName(this._mutation) + ({ defn }) => defn.properties.input.type === getInputName(this._mutation), ); if (!matchDefn) { throw new Error( - 'no mutation found for ' + this._model + ':' + this._mutation + 'no mutation found for ' + this._model + ':' + this._mutation, ); } @@ -194,10 +194,7 @@ export class QueryBuilder { this._key = this._findQuery(); this.queryName( - camelize( - ['get', underscore(this._key), 'query'].join('_'), - true - ) + camelize(['get', underscore(this._key), 'query'].join('_'), true), ); const defn = this._introspection[this._key]; @@ -208,7 +205,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select + selection: this._select, }); return this; @@ -219,10 +216,7 @@ export class QueryBuilder { this._key = this._findQuery(); this.queryName( - camelize( - ['get', underscore(this._key), 'query', 'all'].join('_'), - true - ) + camelize(['get', underscore(this._key), 'query', 'all'].join('_'), true), ); const defn = this._introspection[this._key]; @@ -232,7 +226,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select + selection: this._select, }); return this; @@ -245,8 +239,8 @@ export class QueryBuilder { this.queryName( camelize( ['get', underscore(this._key), 'count', 'query'].join('_'), - true - ) + true, + ), ); const defn = this._introspection[this._key]; @@ -254,7 +248,7 @@ export class QueryBuilder { this._ast = getCount({ queryName: this._queryName, operationName: this._key, - query: defn + query: defn, }); return this; @@ -265,10 +259,7 @@ export class QueryBuilder { this._key = this._findQuery(); this.queryName( - camelize( - ['get', underscore(this._key), 'query'].join('_'), - true - ) + camelize(['get', underscore(this._key), 'query'].join('_'), true), ); const defn = this._introspection[this._key]; @@ -278,7 +269,7 @@ export class QueryBuilder { queryName: this._queryName, operationName: this._key, query: defn, - selection: this._select + selection: this._select, }); return this; @@ -290,10 +281,7 @@ export class QueryBuilder { this._key = this._findMutation(); this.queryName( - camelize( - [underscore(this._key), 'mutation'].join('_'), - true - ) + camelize([underscore(this._key), 'mutation'].join('_'), true), ); const defn = this._introspection[this._key] as MutationDefinition; @@ -302,7 +290,7 @@ export class QueryBuilder { operationName: this._key, mutationName: this._queryName, mutation: defn, - selection: this._select + selection: this._select, }); return this; @@ -314,10 +302,7 @@ export class QueryBuilder { this._key = this._findMutation(); this.queryName( - camelize( - [underscore(this._key), 'mutation'].join('_'), - true - ) + camelize([underscore(this._key), 'mutation'].join('_'), true), ); const defn = this._introspection[this._key] as MutationDefinition; @@ -326,7 +311,7 @@ export class QueryBuilder { this._ast = deleteOne({ operationName: this._key, mutationName: this._queryName, - mutation: defn + mutation: defn, }); return this; @@ -338,10 +323,7 @@ export class QueryBuilder { this._key = this._findMutation(); this.queryName( - camelize( - [underscore(this._key), 'mutation'].join('_'), - true - ) + camelize([underscore(this._key), 'mutation'].join('_'), true), ); const defn = this._introspection[this._key] as MutationDefinition; @@ -351,7 +333,7 @@ export class QueryBuilder { operationName: this._key, mutationName: this._queryName, mutation: defn, - selection: this._select + selection: this._select, }); return this; @@ -370,18 +352,18 @@ export class QueryBuilder { return { _hash, _queryName: this._queryName, - _ast: this._ast + _ast: this._ast, }; } // Bind methods that will be called with different this context pickScalarFields: ( selection: QuerySelectionOptions | null, - defn: QueryDefinition + defn: QueryDefinition, ) => QueryFieldSelection[]; pickAllFields: ( selection: QuerySelectionOptions, - defn: QueryDefinition + defn: QueryDefinition, ) => QueryFieldSelection[]; } @@ -394,7 +376,7 @@ export class QueryBuilder { function pickScalarFields( this: QueryBuilder, selection: QuerySelectionOptions | null, - defn: QueryDefinition + defn: QueryDefinition, ): QueryFieldSelection[] { const model = defn.model; const modelMeta = this._meta.tables.find((t) => t.name === model); @@ -416,12 +398,12 @@ function pickScalarFields( .filter( (fieldName) => !isRelationalField(fieldName, modelMeta) && - isInTableSchema(fieldName) + isInTableSchema(fieldName), ) .map((fieldName) => ({ name: fieldName, isObject: false, - fieldDefn: modelMeta.fields.find((f) => f.name === fieldName) + fieldDefn: modelMeta.fields.find((f) => f.name === fieldName), })); // This is for inferring the sub-selection of a mutation query @@ -445,7 +427,7 @@ function pickScalarFields( function pickAllFields( this: QueryBuilder, selection: QuerySelectionOptions, - defn: QueryDefinition + defn: QueryDefinition, ): QueryFieldSelection[] { const model = defn.model; const modelMeta = this._meta.tables.find((t) => t.name === model); @@ -476,7 +458,7 @@ function pickAllFields( const referencedForeignConstraint = modelMeta.foreignConstraints.find( (constraint) => constraint.fromKey.name === fieldName || - constraint.fromKey.alias === fieldName + constraint.fromKey.alias === fieldName, ); const selectOptions = fieldOptions as { @@ -498,7 +480,7 @@ function pickAllFields( isObject: true, isBelongTo, selection: subFields.map((name) => ({ name, isObject: false })), - variables: selectOptions.variables as QueryFieldSelection['variables'] + variables: selectOptions.variables as QueryFieldSelection['variables'], }; // Need to further expand selection of object fields, @@ -507,13 +489,13 @@ function pickAllFields( // location is non-scalar and non-relational, thus need to further expand into { x y ... } if (isBelongTo) { const getManyName = modelNameToGetMany( - referencedForeignConstraint.refTable + referencedForeignConstraint.refTable, ); const refDefn = this._introspection[getManyName]; fieldSelection.selection = pickScalarFields.call( this, { [fieldName]: true }, - refDefn + refDefn, ); } @@ -529,8 +511,8 @@ function pickAllFields( { name: fieldName, isObject: false, - fieldDefn: modelMeta.fields.find((f) => f.name === fieldName) - } + fieldDefn: modelMeta.fields.find((f) => f.name === fieldName), + }, ]; } } @@ -542,12 +524,12 @@ function pickAllFields( function isFieldInDefinition( fieldName: string, defn: QueryDefinition, - modelMeta: MetaTable + modelMeta: MetaTable, ): boolean { const isReferenced = !!modelMeta.foreignConstraints.find( (constraint) => constraint.fromKey.name === fieldName || - constraint.fromKey.alias === fieldName + constraint.fromKey.alias === fieldName, ); return ( @@ -570,7 +552,7 @@ function isRelationalField(fieldName: string, modelMeta: MetaTable): boolean { return ( !modelMeta.primaryConstraints.find((field) => field.name === fieldName) && !!modelMeta.foreignConstraints.find( - (constraint) => constraint.fromKey.name === fieldName + (constraint) => constraint.fromKey.name === fieldName, ) ); } @@ -578,8 +560,5 @@ function isRelationalField(fieldName: string, modelMeta: MetaTable): boolean { // Get getMany op name from model // ie. UserSetting => userSettings function modelNameToGetMany(model: string): string { - return camelize( - pluralize(underscore(model)), - true - ); + return camelize(pluralize(underscore(model)), true); } diff --git a/graphql/codegen/src/core/types.ts b/graphql/codegen/src/core/types.ts index fc9b252e3..f3fc365a6 100644 --- a/graphql/codegen/src/core/types.ts +++ b/graphql/codegen/src/core/types.ts @@ -1,9 +1,18 @@ -import type { DocumentNode, FieldNode, SelectionSetNode, VariableDefinitionNode } from 'graphql'; +import type { + DocumentNode, + FieldNode, + SelectionSetNode, + VariableDefinitionNode, +} from 'graphql'; import type { CleanField } from '../types/schema'; // GraphQL AST types (re-export what we need from gql-ast) -export type ASTNode = DocumentNode | FieldNode | SelectionSetNode | VariableDefinitionNode; +export type ASTNode = + | DocumentNode + | FieldNode + | SelectionSetNode + | VariableDefinitionNode; // Nested property structure for complex mutation inputs export interface NestedProperties { @@ -164,7 +173,7 @@ export interface ObjectArrayItem extends QueryProperty { // Type guards for runtime validation export function isGraphQLVariableValue( - value: unknown + value: unknown, ): value is GraphQLVariableValue { return ( value === null || @@ -183,7 +192,7 @@ export function isGraphQLVariables(obj: unknown): obj is GraphQLVariables { if (Array.isArray(value)) { if ( !value.every( - (item) => isGraphQLVariableValue(item) || isGraphQLVariables(item) + (item) => isGraphQLVariableValue(item) || isGraphQLVariables(item), ) ) { return false; diff --git a/graphql/codegen/src/core/watch/cache.ts b/graphql/codegen/src/core/watch/cache.ts index 2df07bc6d..0b23a7db6 100644 --- a/graphql/codegen/src/core/watch/cache.ts +++ b/graphql/codegen/src/core/watch/cache.ts @@ -24,7 +24,7 @@ export class SchemaCache { * This is the hot path - must be efficient */ async hasChanged( - schema: IntrospectionQueryResponse + schema: IntrospectionQueryResponse, ): Promise<{ changed: boolean; newHash: string }> { const newHash = await this.computeHash(schema); const changed = this.currentHash === null || this.currentHash !== newHash; @@ -56,7 +56,7 @@ export class SchemaCache { * Compute hash from schema response */ private async computeHash( - schema: IntrospectionQueryResponse + schema: IntrospectionQueryResponse, ): Promise { return hashObject(schema); } diff --git a/graphql/codegen/src/core/watch/debounce.ts b/graphql/codegen/src/core/watch/debounce.ts index 93d06a21f..cf5d15de6 100644 --- a/graphql/codegen/src/core/watch/debounce.ts +++ b/graphql/codegen/src/core/watch/debounce.ts @@ -8,7 +8,7 @@ */ export function debounce unknown>( func: T, - wait: number + wait: number, ): { (...args: Parameters): void; cancel: () => void; @@ -56,9 +56,11 @@ export function debounce unknown>( /** * Creates an async debounced function */ -export function debounceAsync Promise>( +export function debounceAsync< + T extends (...args: unknown[]) => Promise, +>( func: T, - wait: number + wait: number, ): { (...args: Parameters): Promise; cancel: () => void; diff --git a/graphql/codegen/src/core/watch/hash.ts b/graphql/codegen/src/core/watch/hash.ts index 75d3443b9..2f5aa99ca 100644 --- a/graphql/codegen/src/core/watch/hash.ts +++ b/graphql/codegen/src/core/watch/hash.ts @@ -37,7 +37,7 @@ function sortReplacer(_key: string, value: unknown): unknown { sorted[key] = (value as Record)[key]; return sorted; }, - {} as Record + {} as Record, ); } return value; diff --git a/graphql/codegen/src/core/watch/index.ts b/graphql/codegen/src/core/watch/index.ts index 737f372d6..a1f3fe6f2 100644 --- a/graphql/codegen/src/core/watch/index.ts +++ b/graphql/codegen/src/core/watch/index.ts @@ -4,15 +4,15 @@ export { SchemaCache, touchFile } from './cache'; export { debounce, debounceAsync } from './debounce'; -export { combineHashes,hashObject, sha256 } from './hash'; +export { combineHashes, hashObject, sha256 } from './hash'; export type { WatchOrchestratorOptions, WatchStatus } from './orchestrator'; -export { startWatch,WatchOrchestrator } from './orchestrator'; -export { computeSchemaHash,SchemaPoller } from './poller'; +export { startWatch, WatchOrchestrator } from './orchestrator'; +export { computeSchemaHash, SchemaPoller } from './poller'; export type { GeneratorType, PollEvent, PollEventHandler, PollEventType, PollResult, - WatchOptions + WatchOptions, } from './types'; diff --git a/graphql/codegen/src/core/watch/orchestrator.ts b/graphql/codegen/src/core/watch/orchestrator.ts index dc986a441..93f1f8a80 100644 --- a/graphql/codegen/src/core/watch/orchestrator.ts +++ b/graphql/codegen/src/core/watch/orchestrator.ts @@ -7,7 +7,7 @@ import type { GraphQLSDKConfigTarget } from '../../types/config'; import { debounce } from './debounce'; import { SchemaPoller } from './poller'; -import type { GeneratorType, PollEvent,WatchOptions } from './types'; +import type { GeneratorType, PollEvent, WatchOptions } from './types'; // These will be injected by the CLI layer to avoid circular dependencies // The watch orchestrator doesn't need to know about the full generate commands @@ -82,13 +82,13 @@ export class WatchOrchestrator { lastPollTime: null, lastRegenTime: null, lastError: null, - currentHash: null + currentHash: null, }; // Create debounced regenerate function this.debouncedRegenerate = debounce( () => this.regenerate(), - options.config.watch.debounce + options.config.watch.debounce, ); // Set up event handlers @@ -105,7 +105,7 @@ export class WatchOrchestrator { debounce: config.watch.debounce, touchFile: config.watch.touchFile, clearScreen: config.watch.clearScreen, - verbose + verbose, }; } @@ -172,7 +172,7 @@ export class WatchOrchestrator { // Start polling loop this.poller.start(); this.log( - `Watching for schema changes (poll interval: ${this.watchOptions.pollInterval}ms)` + `Watching for schema changes (poll interval: ${this.watchOptions.pollInterval}ms)`, ); } @@ -217,18 +217,22 @@ export class WatchOrchestrator { let outputDir: string | undefined; switch (this.options.generatorType) { - case 'react-query': - generateFn = this.options.generateReactQuery; - // React Query hooks go to {output}/hooks - outputDir = this.options.outputDir ?? `${this.options.config.output}/hooks`; - break; - case 'orm': - generateFn = this.options.generateOrm; - // ORM client goes to {output}/orm - outputDir = this.options.outputDir ?? `${this.options.config.output}/orm`; - break; - default: - throw new Error(`Unknown generator type: ${this.options.generatorType}`); + case 'react-query': + generateFn = this.options.generateReactQuery; + // React Query hooks go to {output}/hooks + outputDir = + this.options.outputDir ?? `${this.options.config.output}/hooks`; + break; + case 'orm': + generateFn = this.options.generateOrm; + // ORM client goes to {output}/orm + outputDir = + this.options.outputDir ?? `${this.options.config.output}/orm`; + break; + default: + throw new Error( + `Unknown generator type: ${this.options.generatorType}`, + ); } const result = await generateFn({ @@ -238,7 +242,7 @@ export class WatchOrchestrator { output: outputDir, authorization: this.options.authorization, verbose: this.watchOptions.verbose, - skipCustomOperations: this.options.skipCustomOperations + skipCustomOperations: this.options.skipCustomOperations, }); const duration = Date.now() - startTime; @@ -287,14 +291,16 @@ export class WatchOrchestrator { private logHeader(): void { let generatorName: string; switch (this.options.generatorType) { - case 'react-query': - generatorName = 'React Query hooks'; - break; - case 'orm': - generatorName = 'ORM client'; - break; - default: - throw new Error(`Unknown generator type: ${this.options.generatorType}`); + case 'react-query': + generatorName = 'React Query hooks'; + break; + case 'orm': + generatorName = 'ORM client'; + break; + default: + throw new Error( + `Unknown generator type: ${this.options.generatorType}`, + ); } console.log(`\n${'─'.repeat(50)}`); console.log(`graphql-codegen watch mode (${generatorName})`); @@ -322,7 +328,7 @@ export class WatchOrchestrator { * Start watch mode for a generator */ export async function startWatch( - options: WatchOrchestratorOptions + options: WatchOrchestratorOptions, ): Promise { const orchestrator = new WatchOrchestrator(options); await orchestrator.start(); diff --git a/graphql/codegen/src/core/watch/poller.ts b/graphql/codegen/src/core/watch/poller.ts index 78e2937ac..1851e5e3f 100644 --- a/graphql/codegen/src/core/watch/poller.ts +++ b/graphql/codegen/src/core/watch/poller.ts @@ -15,7 +15,7 @@ import type { PollEvent, PollEventType, PollResult, - WatchOptions + WatchOptions, } from './types'; /** @@ -74,7 +74,7 @@ export class SchemaPoller extends EventEmitter { return { success: false, changed: false, - error: 'Poll already in progress' + error: 'Poll already in progress', }; } @@ -88,7 +88,7 @@ export class SchemaPoller extends EventEmitter { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: 30000 + timeout: 30000, }); const duration = Date.now() - startTime; @@ -97,7 +97,7 @@ export class SchemaPoller extends EventEmitter { if (!schemaResult.success) { return this.handleError( `__schema fetch failed: ${schemaResult.error}`, - duration + duration, ); } @@ -120,14 +120,14 @@ export class SchemaPoller extends EventEmitter { this.emit( 'schema-changed', - this.createEvent('schema-changed', { hash: newHash, duration }) + this.createEvent('schema-changed', { hash: newHash, duration }), ); return { success: true, changed: true, hash: newHash, schema }; } this.emit( 'schema-unchanged', - this.createEvent('schema-unchanged', { duration }) + this.createEvent('schema-unchanged', { duration }), ); this.emit('poll-success', this.createEvent('poll-success', { duration })); return { success: true, changed: false, hash: newHash, schema }; @@ -157,7 +157,7 @@ export class SchemaPoller extends EventEmitter { endpoint: this.options.endpoint, authorization: this.options.authorization, headers: this.options.headers, - timeout: 30000 + timeout: 30000, }); if (schemaResult.success) { @@ -194,7 +194,7 @@ export class SchemaPoller extends EventEmitter { this.consecutiveErrors++; this.emit( 'poll-error', - this.createEvent('poll-error', { error, duration }) + this.createEvent('poll-error', { error, duration }), ); // Slow down polling after multiple consecutive errors @@ -214,12 +214,12 @@ export class SchemaPoller extends EventEmitter { private createEvent( type: PollEventType, - extra?: Partial + extra?: Partial, ): PollEvent { return { type, timestamp: Date.now(), - ...extra + ...extra, }; } } @@ -228,7 +228,7 @@ export class SchemaPoller extends EventEmitter { * Utility to compute schema hash without full poll */ export async function computeSchemaHash( - schema: IntrospectionQueryResponse + schema: IntrospectionQueryResponse, ): Promise { return hashObject(schema); } diff --git a/graphql/codegen/src/generators/field-selector.ts b/graphql/codegen/src/generators/field-selector.ts index 43ce4851c..a4e23f115 100644 --- a/graphql/codegen/src/generators/field-selector.ts +++ b/graphql/codegen/src/generators/field-selector.ts @@ -7,7 +7,7 @@ import type { CleanTable } from '../types/schema'; import type { FieldSelection, FieldSelectionPreset, - SimpleFieldSelection + SimpleFieldSelection, } from '../types/selection'; /** @@ -16,7 +16,7 @@ import type { export function convertToSelectionOptions( table: CleanTable, allTables: CleanTable[], - selection?: FieldSelection + selection?: FieldSelection, ): QuerySelectionOptions | null { if (!selection) { return convertPresetToSelection(table, 'display'); @@ -34,52 +34,52 @@ export function convertToSelectionOptions( */ function convertPresetToSelection( table: CleanTable, - preset: FieldSelectionPreset + preset: FieldSelectionPreset, ): QuerySelectionOptions { const options: QuerySelectionOptions = {}; switch (preset) { - case 'minimal': { - // Just id and first display field - const minimalFields = getMinimalFields(table); - minimalFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'minimal': { + // Just id and first display field + const minimalFields = getMinimalFields(table); + minimalFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'display': { - // Common display fields - const displayFields = getDisplayFields(table); - displayFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'display': { + // Common display fields + const displayFields = getDisplayFields(table); + displayFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'all': { - // All non-relational fields (includes complex fields like JSON, geometry, etc.) - const allFields = getNonRelationalFields(table); - allFields.forEach((field) => { - options[field] = true; - }); - break; - } + case 'all': { + // All non-relational fields (includes complex fields like JSON, geometry, etc.) + const allFields = getNonRelationalFields(table); + allFields.forEach((field) => { + options[field] = true; + }); + break; + } - case 'full': - // All fields including basic relations - table.fields.forEach((field) => { - options[field.name] = true; - }); - break; + case 'full': + // All fields including basic relations + table.fields.forEach((field) => { + options[field.name] = true; + }); + break; - default: { - // Default to display - const defaultFields = getDisplayFields(table); - defaultFields.forEach((field) => { - options[field] = true; - }); - } + default: { + // Default to display + const defaultFields = getDisplayFields(table); + defaultFields.forEach((field) => { + options[field] = true; + }); + } } return options; @@ -91,7 +91,7 @@ function convertPresetToSelection( function convertCustomSelectionToOptions( table: CleanTable, allTables: CleanTable[], - selection: SimpleFieldSelection + selection: SimpleFieldSelection, ): QuerySelectionOptions { const options: QuerySelectionOptions = {}; @@ -118,7 +118,7 @@ function convertCustomSelectionToOptions( // Include with dynamically determined scalar fields from the related table options[relationField] = { select: getRelatedTableScalarFields(relationField, table, allTables), - variables: {} + variables: {}, }; } }); @@ -135,9 +135,9 @@ function convertCustomSelectionToOptions( select: getRelatedTableScalarFields( relationField, table, - allTables + allTables, ), - variables: {} + variables: {}, }; } else if (Array.isArray(relationSelection)) { // Include with specific fields @@ -147,11 +147,11 @@ function convertCustomSelectionToOptions( }); options[relationField] = { select: selectObj, - variables: {} + variables: {}, }; } } - } + }, ); } @@ -188,7 +188,7 @@ function getDisplayFields(table: CleanTable): string[] { // This is completely dynamic based on what the schema actually provides const maxDisplayFields = Math.max( 5, - Math.floor(nonRelationalFields.length / 2) + Math.floor(nonRelationalFields.length / 2), ); return nonRelationalFields.slice(0, maxDisplayFields); } @@ -208,7 +208,7 @@ function getNonRelationalFields(table: CleanTable): string[] { */ export function isRelationalField( fieldName: string, - table: CleanTable + table: CleanTable, ): boolean { const { belongsTo, hasOne, hasMany, manyToMany } = table.relations; @@ -227,14 +227,14 @@ export function isRelationalField( function getRelatedTableScalarFields( relationField: string, table: CleanTable, - allTables: CleanTable[] + allTables: CleanTable[], ): Record { // Find the related table name let referencedTableName: string | undefined; // Check belongsTo relations const belongsToRel = table.relations.belongsTo.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (belongsToRel) { referencedTableName = belongsToRel.referencesTable; @@ -243,7 +243,7 @@ function getRelatedTableScalarFields( // Check hasOne relations if (!referencedTableName) { const hasOneRel = table.relations.hasOne.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (hasOneRel) { referencedTableName = hasOneRel.referencedByTable; @@ -253,7 +253,7 @@ function getRelatedTableScalarFields( // Check hasMany relations if (!referencedTableName) { const hasManyRel = table.relations.hasMany.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (hasManyRel) { referencedTableName = hasManyRel.referencedByTable; @@ -263,7 +263,7 @@ function getRelatedTableScalarFields( // Check manyToMany relations if (!referencedTableName) { const manyToManyRel = table.relations.manyToMany.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (manyToManyRel) { referencedTableName = manyToManyRel.rightTable; @@ -305,7 +305,7 @@ function getRelatedTableScalarFields( 'slug', 'code', 'createdAt', - 'updatedAt' + 'updatedAt', ]; const included: string[] = []; @@ -332,9 +332,7 @@ function getRelatedTableScalarFields( /** * Get all available relation fields from a table */ -export function getAvailableRelations( - table: CleanTable -): Array<{ +export function getAvailableRelations(table: CleanTable): Array<{ fieldName: string; type: 'belongsTo' | 'hasOne' | 'hasMany' | 'manyToMany'; referencedTable?: string; @@ -351,7 +349,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'belongsTo', - referencedTable: rel.referencesTable || undefined + referencedTable: rel.referencesTable || undefined, }); } }); @@ -362,7 +360,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'hasOne', - referencedTable: rel.referencedByTable || undefined + referencedTable: rel.referencedByTable || undefined, }); } }); @@ -373,7 +371,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'hasMany', - referencedTable: rel.referencedByTable || undefined + referencedTable: rel.referencedByTable || undefined, }); } }); @@ -384,7 +382,7 @@ export function getAvailableRelations( relations.push({ fieldName: rel.fieldName, type: 'manyToMany', - referencedTable: rel.rightTable || undefined + referencedTable: rel.rightTable || undefined, }); } }); @@ -397,7 +395,7 @@ export function getAvailableRelations( */ export function validateFieldSelection( selection: FieldSelection, - table: CleanTable + table: CleanTable, ): { isValid: boolean; errors: string[] } { const errors: string[] = []; @@ -412,9 +410,7 @@ export function validateFieldSelection( if (selection.select) { selection.select.forEach((field) => { if (!tableFieldNames.includes(field)) { - errors.push( - `Field '${field}' does not exist in table '${table.name}'` - ); + errors.push(`Field '${field}' does not exist in table '${table.name}'`); } }); } @@ -424,7 +420,7 @@ export function validateFieldSelection( selection.includeRelations.forEach((field) => { if (!isRelationalField(field, table)) { errors.push( - `Field '${field}' is not a relational field in table '${table.name}'` + `Field '${field}' is not a relational field in table '${table.name}'`, ); } }); @@ -435,7 +431,7 @@ export function validateFieldSelection( Object.keys(selection.include).forEach((field) => { if (!isRelationalField(field, table)) { errors.push( - `Field '${field}' is not a relational field in table '${table.name}'` + `Field '${field}' is not a relational field in table '${table.name}'`, ); } }); @@ -446,7 +442,7 @@ export function validateFieldSelection( selection.exclude.forEach((field) => { if (!tableFieldNames.includes(field)) { errors.push( - `Exclude field '${field}' does not exist in table '${table.name}'` + `Exclude field '${field}' does not exist in table '${table.name}'`, ); } }); @@ -465,6 +461,6 @@ export function validateFieldSelection( return { isValid: errors.length === 0, - errors + errors, }; } diff --git a/graphql/codegen/src/generators/index.ts b/graphql/codegen/src/generators/index.ts index 4b38b3518..d4420dfee 100644 --- a/graphql/codegen/src/generators/index.ts +++ b/graphql/codegen/src/generators/index.ts @@ -7,7 +7,7 @@ export { convertToSelectionOptions, getAvailableRelations, isRelationalField, - validateFieldSelection + validateFieldSelection, } from './field-selector'; // Query generators @@ -19,12 +19,12 @@ export { createASTQueryBuilder, generateIntrospectionSchema, toCamelCasePlural, - toOrderByTypeName + toOrderByTypeName, } from './select'; // Mutation generators export { buildPostGraphileCreate, buildPostGraphileDelete, - buildPostGraphileUpdate + buildPostGraphileUpdate, } from './mutations'; diff --git a/graphql/codegen/src/generators/mutations.ts b/graphql/codegen/src/generators/mutations.ts index 3d86856e1..7e5b2ea97 100644 --- a/graphql/codegen/src/generators/mutations.ts +++ b/graphql/codegen/src/generators/mutations.ts @@ -10,7 +10,7 @@ import { camelize } from 'inflekt'; import { TypedDocumentString } from '../client/typed-document'; import { getCustomAstForCleanField, - requiresSubfieldSelection + requiresSubfieldSelection, } from '../core/custom-ast'; import type { MutationOptions } from '../types/mutation'; import type { CleanTable } from '../types/schema'; @@ -41,7 +41,7 @@ function generateFieldSelections(table: CleanTable): FieldNode[] { export function buildPostGraphileCreate( table: CleanTable, _allTables: CleanTable[], - _options: MutationOptions = {} + _options: MutationOptions = {}, ): TypedDocumentString< Record, { input: { [key: string]: Record } } @@ -54,17 +54,17 @@ export function buildPostGraphileCreate( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Create${table.name}Input` }) - }) - }) + type: t.namedType({ type: `Create${table.name}Input` }), + }), + }), ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }) - }) + value: t.variable({ name: 'input' }), + }), ]; // Get the field selections for the return value using custom AST logic @@ -87,23 +87,23 @@ export function buildPostGraphileCreate( t.field({ name: singularName, selectionSet: t.selectionSet({ - selections: fieldSelections - }) - }) - ] - }) - }) - ] - }) - }) - ] + selections: fieldSelections, + }), + }), + ], + }), + }), + ], + }), + }), + ], }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast + __ast: ast, }) as TypedDocumentString< Record, { input: { [key: string]: Record } } @@ -117,7 +117,7 @@ export function buildPostGraphileCreate( export function buildPostGraphileUpdate( table: CleanTable, _allTables: CleanTable[], - _options: MutationOptions = {} + _options: MutationOptions = {}, ): TypedDocumentString< Record, { input: { id: string | number; patch: Record } } @@ -130,17 +130,17 @@ export function buildPostGraphileUpdate( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Update${table.name}Input` }) - }) - }) + type: t.namedType({ type: `Update${table.name}Input` }), + }), + }), ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }) - }) + value: t.variable({ name: 'input' }), + }), ]; // Get the field selections for the return value using custom AST logic @@ -163,23 +163,23 @@ export function buildPostGraphileUpdate( t.field({ name: singularName, selectionSet: t.selectionSet({ - selections: fieldSelections - }) - }) - ] - }) - }) - ] - }) - }) - ] + selections: fieldSelections, + }), + }), + ], + }), + }), + ], + }), + }), + ], }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast + __ast: ast, }) as TypedDocumentString< Record, { input: { id: string | number; patch: Record } } @@ -193,7 +193,7 @@ export function buildPostGraphileUpdate( export function buildPostGraphileDelete( table: CleanTable, _allTables: CleanTable[], - _options: MutationOptions = {} + _options: MutationOptions = {}, ): TypedDocumentString< Record, { input: { id: string | number } } @@ -205,17 +205,17 @@ export function buildPostGraphileDelete( t.variableDefinition({ variable: t.variable({ name: 'input' }), type: t.nonNullType({ - type: t.namedType({ type: `Delete${table.name}Input` }) - }) - }) + type: t.namedType({ type: `Delete${table.name}Input` }), + }), + }), ]; // Create the mutation arguments const mutationArgs: ArgumentNode[] = [ t.argument({ name: 'input', - value: t.variable({ name: 'input' }) - }) + value: t.variable({ name: 'input' }), + }), ]; // PostGraphile delete mutations typically return clientMutationId @@ -234,20 +234,20 @@ export function buildPostGraphileDelete( name: mutationName, args: mutationArgs, selectionSet: t.selectionSet({ - selections: fieldSelections - }) - }) - ] - }) - }) - ] + selections: fieldSelections, + }), + }), + ], + }), + }), + ], }); // Print the AST to get the query string const queryString = print(ast); return new TypedDocumentString(queryString, { - __ast: ast + __ast: ast, }) as TypedDocumentString< Record, { input: { id: string | number } } diff --git a/graphql/codegen/src/generators/select.ts b/graphql/codegen/src/generators/select.ts index 8bd58ccf8..493eac8f0 100644 --- a/graphql/codegen/src/generators/select.ts +++ b/graphql/codegen/src/generators/select.ts @@ -10,7 +10,7 @@ import { camelize, pluralize } from 'inflekt'; import { TypedDocumentString } from '../client/typed-document'; import { getCustomAstForCleanField, - requiresSubfieldSelection + requiresSubfieldSelection, } from '../core/custom-ast'; import { QueryBuilder } from '../core/query-builder'; import type { @@ -20,7 +20,7 @@ import type { MetaObject, MutationDefinition, QueryDefinition, - QuerySelectionOptions + QuerySelectionOptions, } from '../core/types'; import type { QueryOptions } from '../types/query'; import type { CleanTable } from '../types/schema'; @@ -66,8 +66,8 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: field.type.pgAlias, pgType: field.type.pgType, subtype: field.type.subtype, - typmod: field.type.typmod - } + typmod: field.type.typmod, + }, })), primaryConstraints: [] as MetaConstraint[], // Would need to be derived from schema uniqueConstraints: [] as MetaConstraint[], // Would need to be derived from schema @@ -82,9 +82,9 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: null, pgType: null, subtype: null, - typmod: null + typmod: null, } as MetaFieldType, - alias: rel.fieldName || '' + alias: rel.fieldName || '', }, toKey: { name: 'id', @@ -95,11 +95,11 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { pgAlias: null, pgType: null, subtype: null, - typmod: null - } as MetaFieldType - } - })) - })) + typmod: null, + } as MetaFieldType, + }, + })), + })), }; } @@ -108,7 +108,7 @@ export function cleanTableToMetaObject(tables: CleanTable[]): MetaObject { * This creates a minimal schema for AST generation */ export function generateIntrospectionSchema( - tables: CleanTable[] + tables: CleanTable[], ): IntrospectionSchema { const schema: IntrospectionSchema = {}; @@ -124,7 +124,7 @@ export function generateIntrospectionSchema( qtype: 'getMany', model: modelName, selection, - properties: convertFieldsToProperties(table.fields) + properties: convertFieldsToProperties(table.fields), } as QueryDefinition; // Add getOne query (by ID) @@ -133,7 +133,7 @@ export function generateIntrospectionSchema( qtype: 'getOne', model: modelName, selection, - properties: convertFieldsToProperties(table.fields) + properties: convertFieldsToProperties(table.fields), } as QueryDefinition; // Add create mutation @@ -156,11 +156,11 @@ export function generateIntrospectionSchema( isNotNull: true, isArray: false, isArrayNotNull: false, - properties: convertFieldsToNestedProperties(table.fields) - } - } - } - } + properties: convertFieldsToNestedProperties(table.fields), + }, + }, + }, + }, } as MutationDefinition; // Add update mutation @@ -183,11 +183,11 @@ export function generateIntrospectionSchema( isNotNull: true, isArray: false, isArrayNotNull: false, - properties: convertFieldsToNestedProperties(table.fields) - } - } - } - } + properties: convertFieldsToNestedProperties(table.fields), + }, + }, + }, + }, } as MutationDefinition; // Add delete mutation @@ -209,11 +209,11 @@ export function generateIntrospectionSchema( type: 'UUID', isNotNull: true, isArray: false, - isArrayNotNull: false - } - } - } - } + isArrayNotNull: false, + }, + }, + }, + }, } as MutationDefinition; } @@ -232,7 +232,7 @@ function convertFieldsToProperties(fields: CleanTable['fields']) { type: field.type.gqlType, isNotNull: !field.type.gqlType.endsWith('!'), isArray: field.type.isArray, - isArrayNotNull: false + isArrayNotNull: false, }; }); @@ -251,7 +251,7 @@ function convertFieldsToNestedProperties(fields: CleanTable['fields']) { type: field.type.gqlType, isNotNull: false, // Mutations typically allow optional fields isArray: field.type.isArray, - isArrayNotNull: false + isArrayNotNull: false, }; }); @@ -267,7 +267,7 @@ export function createASTQueryBuilder(tables: CleanTable[]): QueryBuilder { return new QueryBuilder({ meta: metaObject, - introspection: introspectionSchema + introspection: introspectionSchema, }); } @@ -278,13 +278,13 @@ export function createASTQueryBuilder(tables: CleanTable[]): QueryBuilder { export function buildSelect( table: CleanTable, allTables: readonly CleanTable[], - options: QueryOptions = {} + options: QueryOptions = {}, ): TypedDocumentString, QueryOptions> { const tableList = Array.from(allTables); const selection = convertFieldSelectionToSelectionOptions( table, tableList, - options.fieldSelection + options.fieldSelection, ); // Generate query directly using AST @@ -292,7 +292,7 @@ export function buildSelect( table, tableList, selection, - options + options, ); return new TypedDocumentString(queryString, {}) as TypedDocumentString< @@ -306,7 +306,7 @@ export function buildSelect( */ export function buildFindOne( table: CleanTable, - _pkField: string = 'id' + _pkField: string = 'id', ): TypedDocumentString, Record> { const queryString = generateFindOneQueryAST(table); @@ -320,7 +320,7 @@ export function buildFindOne( * Build a count query for a table */ export function buildCount( - table: CleanTable + table: CleanTable, ): TypedDocumentString< { [key: string]: { totalCount: number } }, { condition?: Record; filter?: Record } @@ -336,7 +336,7 @@ export function buildCount( function convertFieldSelectionToSelectionOptions( table: CleanTable, allTables: CleanTable[], - options?: FieldSelection + options?: FieldSelection, ): QuerySelectionOptions | null { return convertToSelectionOptions(table, allTables, options); } @@ -348,7 +348,7 @@ function generateSelectQueryAST( table: CleanTable, allTables: CleanTable[], selection: QuerySelectionOptions | null, - options: QueryOptions + options: QueryOptions, ): string { const pluralName = toCamelCasePlural(table.name); @@ -356,7 +356,7 @@ function generateSelectQueryAST( const fieldSelections = generateFieldSelectionsFromOptions( table, allTables, - selection + selection, ); // Build the query AST @@ -369,14 +369,14 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'first' }), - type: t.namedType({ type: 'Int' }) - }) + type: t.namedType({ type: 'Int' }), + }), ); queryArgs.push( t.argument({ name: 'first', - value: t.variable({ name: 'first' }) - }) + value: t.variable({ name: 'first' }), + }), ); } @@ -384,14 +384,14 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'offset' }), - type: t.namedType({ type: 'Int' }) - }) + type: t.namedType({ type: 'Int' }), + }), ); queryArgs.push( t.argument({ name: 'offset', - value: t.variable({ name: 'offset' }) - }) + value: t.variable({ name: 'offset' }), + }), ); } @@ -400,14 +400,14 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'after' }), - type: t.namedType({ type: 'Cursor' }) - }) + type: t.namedType({ type: 'Cursor' }), + }), ); queryArgs.push( t.argument({ name: 'after', - value: t.variable({ name: 'after' }) - }) + value: t.variable({ name: 'after' }), + }), ); } @@ -415,14 +415,14 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'before' }), - type: t.namedType({ type: 'Cursor' }) - }) + type: t.namedType({ type: 'Cursor' }), + }), ); queryArgs.push( t.argument({ name: 'before', - value: t.variable({ name: 'before' }) - }) + value: t.variable({ name: 'before' }), + }), ); } @@ -431,14 +431,14 @@ function generateSelectQueryAST( variableDefinitions.push( t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: `${table.name}Filter` }) - }) + type: t.namedType({ type: `${table.name}Filter` }), + }), ); queryArgs.push( t.argument({ name: 'filter', - value: t.variable({ name: 'filter' }) - }) + value: t.variable({ name: 'filter' }), + }), ); } @@ -450,16 +450,16 @@ function generateSelectQueryAST( // PostGraphile expects [ProductsOrderBy!] - list of non-null enum values type: t.listType({ type: t.nonNullType({ - type: t.namedType({ type: toOrderByTypeName(table.name) }) - }) - }) - }) + type: t.namedType({ type: toOrderByTypeName(table.name) }), + }), + }), + }), ); queryArgs.push( t.argument({ name: 'orderBy', - value: t.variable({ name: 'orderBy' }) - }) + value: t.variable({ name: 'orderBy' }), + }), ); } @@ -469,9 +469,9 @@ function generateSelectQueryAST( t.field({ name: 'nodes', selectionSet: t.selectionSet({ - selections: fieldSelections - }) - }) + selections: fieldSelections, + }), + }), ]; // Add pageInfo if requested (for cursor-based pagination / infinite scroll) @@ -488,10 +488,10 @@ function generateSelectQueryAST( t.field({ name: 'hasNextPage' }), t.field({ name: 'hasPreviousPage' }), t.field({ name: 'startCursor' }), - t.field({ name: 'endCursor' }) - ] - }) - }) + t.field({ name: 'endCursor' }), + ], + }), + }), ); } @@ -507,13 +507,13 @@ function generateSelectQueryAST( name: pluralName, args: queryArgs, selectionSet: t.selectionSet({ - selections: connectionSelections - }) - }) - ] - }) - }) - ] + selections: connectionSelections, + }), + }), + ], + }), + }), + ], }); return print(ast); @@ -525,7 +525,7 @@ function generateSelectQueryAST( function generateFieldSelectionsFromOptions( table: CleanTable, allTables: CleanTable[], - selection: QuerySelectionOptions | null + selection: QuerySelectionOptions | null, ): FieldNode[] { const DEFAULT_NESTED_RELATION_FIRST = 20; @@ -568,7 +568,7 @@ function generateFieldSelectionsFromOptions( if (include) { // Check if this nested field requires subfield selection const nestedFieldDef = relatedTable?.fields.find( - (f) => f.name === nestedField + (f) => f.name === nestedField, ); if (nestedFieldDef && requiresSubfieldSelection(nestedFieldDef)) { // Use custom AST generation for complex nested fields @@ -594,21 +594,21 @@ function generateFieldSelectionsFromOptions( t.argument({ name: 'first', value: t.intValue({ - value: DEFAULT_NESTED_RELATION_FIRST.toString() - }) - }) + value: DEFAULT_NESTED_RELATION_FIRST.toString(), + }), + }), ], selectionSet: t.selectionSet({ selections: [ t.field({ name: 'nodes', selectionSet: t.selectionSet({ - selections: nestedSelections - }) - }) - ] - }) - }) + selections: nestedSelections, + }), + }), + ], + }), + }), ); } else { // For belongsTo/hasOne relations, use direct selection @@ -616,9 +616,9 @@ function generateFieldSelectionsFromOptions( t.field({ name: fieldName, selectionSet: t.selectionSet({ - selections: nestedSelections - }) - }) + selections: nestedSelections, + }), + }), ); } } @@ -632,7 +632,7 @@ function generateFieldSelectionsFromOptions( */ function getRelationInfo( fieldName: string, - table: CleanTable + table: CleanTable, ): { type: string; relation: unknown } | null { const { belongsTo, hasOne, hasMany, manyToMany } = table.relations; @@ -669,14 +669,14 @@ function getRelationInfo( function findRelatedTable( relationField: string, table: CleanTable, - allTables: CleanTable[] + allTables: CleanTable[], ): CleanTable | null { // Find the related table name let referencedTableName: string | undefined; // Check belongsTo relations const belongsToRel = table.relations.belongsTo.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (belongsToRel) { referencedTableName = belongsToRel.referencesTable; @@ -685,7 +685,7 @@ function findRelatedTable( // Check hasOne relations if (!referencedTableName) { const hasOneRel = table.relations.hasOne.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (hasOneRel) { referencedTableName = hasOneRel.referencedByTable; @@ -695,7 +695,7 @@ function findRelatedTable( // Check hasMany relations if (!referencedTableName) { const hasManyRel = table.relations.hasMany.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (hasManyRel) { referencedTableName = hasManyRel.referencedByTable; @@ -705,7 +705,7 @@ function findRelatedTable( // Check manyToMany relations if (!referencedTableName) { const manyToManyRel = table.relations.manyToMany.find( - (rel) => rel.fieldName === relationField + (rel) => rel.fieldName === relationField, ); if (manyToManyRel) { referencedTableName = manyToManyRel.rightTable; @@ -748,9 +748,9 @@ function generateFindOneQueryAST(table: CleanTable): string { t.variableDefinition({ variable: t.variable({ name: 'id' }), type: t.nonNullType({ - type: t.namedType({ type: 'UUID' }) - }) - }) + type: t.namedType({ type: 'UUID' }), + }), + }), ], selectionSet: t.selectionSet({ selections: [ @@ -759,17 +759,17 @@ function generateFindOneQueryAST(table: CleanTable): string { args: [ t.argument({ name: 'id', - value: t.variable({ name: 'id' }) - }) + value: t.variable({ name: 'id' }), + }), ], selectionSet: t.selectionSet({ - selections: fieldSelections - }) - }) - ] - }) - }) - ] + selections: fieldSelections, + }), + }), + ], + }), + }), + ], }); return print(ast); @@ -789,8 +789,8 @@ function generateCountQueryAST(table: CleanTable): string { variableDefinitions: [ t.variableDefinition({ variable: t.variable({ name: 'filter' }), - type: t.namedType({ type: `${table.name}Filter` }) - }) + type: t.namedType({ type: `${table.name}Filter` }), + }), ], selectionSet: t.selectionSet({ selections: [ @@ -799,17 +799,17 @@ function generateCountQueryAST(table: CleanTable): string { args: [ t.argument({ name: 'filter', - value: t.variable({ name: 'filter' }) - }) + value: t.variable({ name: 'filter' }), + }), ], selectionSet: t.selectionSet({ - selections: [t.field({ name: 'totalCount' })] - }) - }) - ] - }) - }) - ] + selections: [t.field({ name: 'totalCount' })], + }), + }), + ], + }), + }), + ], }); return print(ast); diff --git a/graphql/codegen/src/index.ts b/graphql/codegen/src/index.ts index 681af96af..8a444168c 100644 --- a/graphql/codegen/src/index.ts +++ b/graphql/codegen/src/index.ts @@ -40,15 +40,15 @@ export { hyphenateKeys, printResult, seedArgvFromConfig, - splitCommas + splitCommas, } from './cli/shared'; // Database schema utilities (re-exported from core for convenience) export type { BuildSchemaFromDatabaseOptions, - BuildSchemaFromDatabaseResult + BuildSchemaFromDatabaseResult, } from './core/database'; export { buildSchemaFromDatabase, - buildSchemaSDLFromDatabase + buildSchemaSDLFromDatabase, } from './core/database'; diff --git a/graphql/codegen/src/types/config.ts b/graphql/codegen/src/types/config.ts index b171e9d92..91cc9fd2d 100644 --- a/graphql/codegen/src/types/config.ts +++ b/graphql/codegen/src/types/config.ts @@ -240,8 +240,6 @@ export interface GraphQLSDKConfigTarget { * Code generation options */ codegen?: { - /** Max depth for nested object field selection (default: 2) */ - maxFieldDepth?: number; /** Skip 'query' field on mutation payloads (default: true) */ skipQueryField?: boolean; }; @@ -347,7 +345,7 @@ export const DEFAULT_WATCH_CONFIG: WatchConfig = { pollInterval: 3000, debounce: 800, touchFile: undefined, - clearScreen: true + clearScreen: true, }; /** @@ -358,7 +356,7 @@ export const DEFAULT_QUERY_KEY_CONFIG: QueryKeyConfig = { relationships: {}, generateScopedKeys: true, generateCascadeHelpers: true, - generateMutationKeys: true + generateMutationKeys: true, }; /** @@ -371,38 +369,36 @@ export const DEFAULT_CONFIG: GraphQLSDKConfigTarget = { tables: { include: ['*'], exclude: [], - systemExclude: [] + systemExclude: [], }, queries: { include: ['*'], exclude: [], - systemExclude: ['_meta', 'query'] // Internal PostGraphile queries + systemExclude: ['_meta', 'query'], // Internal PostGraphile queries }, mutations: { include: ['*'], exclude: [], - systemExclude: [] + systemExclude: [], }, excludeFields: [], hooks: { queries: true, mutations: true, - queryKeyPrefix: 'graphql' + queryKeyPrefix: 'graphql', }, postgraphile: { - schema: 'public' + schema: 'public', }, codegen: { - maxFieldDepth: 2, - skipQueryField: true + skipQueryField: true, }, orm: false, reactQuery: false, queryKeys: DEFAULT_QUERY_KEY_CONFIG, - watch: DEFAULT_WATCH_CONFIG + watch: DEFAULT_WATCH_CONFIG, }; - /** * Helper function to define configuration with type checking */ @@ -417,7 +413,7 @@ export function defineConfig(config: GraphQLSDKConfig): GraphQLSDKConfig { */ export function mergeConfig( base: GraphQLSDKConfigTarget, - overrides: GraphQLSDKConfigTarget + overrides: GraphQLSDKConfigTarget, ): GraphQLSDKConfigTarget { return deepmerge(base, overrides, { arrayMerge: replaceArrays }); } @@ -427,7 +423,7 @@ export function mergeConfig( * Similar to getEnvOptions pattern from @pgpmjs/env. */ export function getConfigOptions( - overrides: GraphQLSDKConfigTarget = {} + overrides: GraphQLSDKConfigTarget = {}, ): GraphQLSDKConfigTarget { return deepmerge(DEFAULT_CONFIG, overrides, { arrayMerge: replaceArrays }); } diff --git a/graphql/codegen/src/types/index.ts b/graphql/codegen/src/types/index.ts index 83a920137..35dbbbfc6 100644 --- a/graphql/codegen/src/types/index.ts +++ b/graphql/codegen/src/types/index.ts @@ -16,7 +16,7 @@ export type { ForeignKeyConstraint, TableConstraints, TableInflection, - TableQueryNames + TableQueryNames, } from './schema'; // Query types @@ -28,7 +28,7 @@ export type { OrderByItem, PageInfo, QueryOptions, - RelationalFilter + RelationalFilter, } from './query'; // Mutation types @@ -37,7 +37,7 @@ export type { DeleteInput, MutationOptions, MutationResult, - UpdateInput + UpdateInput, } from './mutation'; // Selection types @@ -45,17 +45,14 @@ export type { FieldSelection, FieldSelectionPreset, SelectionOptions, - SimpleFieldSelection + SimpleFieldSelection, } from './selection'; // Config types -export type { - GraphQLSDKConfig, - GraphQLSDKConfigTarget -} from './config'; +export type { GraphQLSDKConfig, GraphQLSDKConfigTarget } from './config'; export { DEFAULT_CONFIG, defineConfig, getConfigOptions, - mergeConfig + mergeConfig, } from './config'; diff --git a/graphql/codegen/src/types/introspection.ts b/graphql/codegen/src/types/introspection.ts index e68d581e8..9642f06f9 100644 --- a/graphql/codegen/src/types/introspection.ts +++ b/graphql/codegen/src/types/introspection.ts @@ -1,6 +1,6 @@ /** * GraphQL Introspection Types - * + * * Standard types for GraphQL schema introspection via __schema query. * These mirror the GraphQL introspection spec. */ @@ -159,7 +159,9 @@ export function isNamedType(kind: IntrospectionTypeKind): boolean { /** * Unwrap a type reference to get the base named type */ -export function unwrapType(typeRef: IntrospectionTypeRef): IntrospectionTypeRef { +export function unwrapType( + typeRef: IntrospectionTypeRef, +): IntrospectionTypeRef { let current = typeRef; while (current.ofType) { current = current.ofType; From 97f9cf3c76b1867b838e1e10cee97c4c6b199a98 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 9 Feb 2026 12:39:32 +0700 Subject: [PATCH 19/23] refactor(codegen): normalize cli source args --- .../src/__tests__/config/cli-shared.test.ts | 39 +++++++++++++ graphql/codegen/src/cli/index.ts | 19 +++---- graphql/codegen/src/cli/shared.ts | 57 ++++++++++++++++++- graphql/codegen/src/index.ts | 2 + packages/cli/__tests__/codegen.test.ts | 21 ++++++- packages/cli/src/commands/codegen.ts | 5 +- 6 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 graphql/codegen/src/__tests__/config/cli-shared.test.ts diff --git a/graphql/codegen/src/__tests__/config/cli-shared.test.ts b/graphql/codegen/src/__tests__/config/cli-shared.test.ts new file mode 100644 index 000000000..db1900d26 --- /dev/null +++ b/graphql/codegen/src/__tests__/config/cli-shared.test.ts @@ -0,0 +1,39 @@ +import { + buildGenerateOptions, + seedArgvFromConfig, +} from '../../cli/shared'; + +describe('CLI shared utilities', () => { + it('preserves explicit false booleans from argv when merging with file config', () => { + const fileConfig = { + reactQuery: true, + orm: true, + dryRun: true, + }; + + const seeded = seedArgvFromConfig( + { reactQuery: false, orm: false }, + fileConfig, + ); + + expect(seeded).toMatchObject({ + reactQuery: false, + orm: false, + }); + }); + + it('normalizes list options through buildGenerateOptions in non-interactive flows', () => { + const options = buildGenerateOptions( + { + schemas: 'public, app', + apiNames: 'core, admin', + }, + {}, + ); + + expect(options.db).toEqual({ + schemas: ['public', 'app'], + apiNames: ['core', 'admin'], + }); + }); +}); diff --git a/graphql/codegen/src/cli/index.ts b/graphql/codegen/src/cli/index.ts index 605de2bfa..8ab33c45a 100644 --- a/graphql/codegen/src/cli/index.ts +++ b/graphql/codegen/src/cli/index.ts @@ -15,6 +15,8 @@ import { buildGenerateOptions, camelizeArgv, codegenQuestions, + hasResolvedCodegenSource, + normalizeCodegenListOptions, printResult, seedArgvFromConfig, } from './shared'; @@ -105,7 +107,9 @@ export const commands = async ( } const cliOptions = buildDbConfig( - camelizeArgv(argv as Record), + normalizeCodegenListOptions( + camelizeArgv(argv as Record), + ), ); let hasError = false; for (const name of names) { @@ -127,7 +131,9 @@ export const commands = async ( } const seeded = seedArgvFromConfig(argv, fileConfig); - const answers = await prompter.prompt(seeded, codegenQuestions); + const answers = hasResolvedCodegenSource(seeded) + ? seeded + : await prompter.prompt(seeded, codegenQuestions); const options = buildGenerateOptions(answers, fileConfig); const result = await generate(options); printResult(result); @@ -147,15 +153,6 @@ export const options: Partial = { a: 'authorization', v: 'verbose', }, - boolean: [ - 'help', - 'version', - 'verbose', - 'dry-run', - 'react-query', - 'orm', - 'keep-db', - ], string: [ 'config', 'endpoint', diff --git a/graphql/codegen/src/cli/shared.ts b/graphql/codegen/src/cli/shared.ts index 32ccde541..9b08cb8f5 100644 --- a/graphql/codegen/src/cli/shared.ts +++ b/graphql/codegen/src/cli/shared.ts @@ -21,6 +21,25 @@ export const splitCommas = ( .filter(Boolean); }; +function normalizeListOption( + input: unknown, +): string[] | undefined { + if (Array.isArray(input)) { + return input + .flatMap((item) => + typeof item === 'string' ? (splitCommas(item) ?? []) : [String(item)], + ) + .map((s) => s.trim()) + .filter(Boolean); + } + + if (typeof input === 'string') { + return splitCommas(input); + } + + return undefined; +} + export interface CodegenAnswers { endpoint?: string; schemaFile?: string; @@ -188,6 +207,41 @@ export function buildDbConfig( return rest; } +/** + * Normalizes top-level list-like CLI options to string arrays. + * This keeps non-interactive paths equivalent to prompt sanitize behavior. + */ +export function normalizeCodegenListOptions( + options: Record, +): Record { + return { + ...options, + schemas: normalizeListOption(options.schemas), + apiNames: normalizeListOption(options.apiNames), + }; +} + +/** + * Returns true when source options are already available, so prompting can be skipped. + */ +export function hasResolvedCodegenSource( + options: Record, +): boolean { + const normalized = normalizeCodegenListOptions(camelizeArgv(options)); + const db = normalized.db as Record | undefined; + const dbSchemas = normalizeListOption(db?.schemas); + const dbApiNames = normalizeListOption(db?.apiNames); + + return Boolean( + normalized.endpoint || + normalized.schemaFile || + (normalized.schemas as string[] | undefined)?.length || + (normalized.apiNames as string[] | undefined)?.length || + dbSchemas?.length || + dbApiNames?.length, + ); +} + export function seedArgvFromConfig( argv: Record, fileConfig: GraphQLSDKConfigTarget, @@ -203,6 +257,7 @@ export function buildGenerateOptions( fileConfig: GraphQLSDKConfigTarget = {}, ): GraphQLSDKConfigTarget { const camelized = camelizeArgv(answers); - const withDb = buildDbConfig(camelized); + const normalized = normalizeCodegenListOptions(camelized); + const withDb = buildDbConfig(normalized); return { ...fileConfig, ...withDb } as GraphQLSDKConfigTarget; } diff --git a/graphql/codegen/src/index.ts b/graphql/codegen/src/index.ts index 8a444168c..d7b71d1b1 100644 --- a/graphql/codegen/src/index.ts +++ b/graphql/codegen/src/index.ts @@ -37,7 +37,9 @@ export { codegenQuestions, filterDefined, flattenDbFields, + hasResolvedCodegenSource, hyphenateKeys, + normalizeCodegenListOptions, printResult, seedArgvFromConfig, splitCommas, diff --git a/packages/cli/__tests__/codegen.test.ts b/packages/cli/__tests__/codegen.test.ts index cf941bfaa..c61cdc34b 100644 --- a/packages/cli/__tests__/codegen.test.ts +++ b/packages/cli/__tests__/codegen.test.ts @@ -39,10 +39,27 @@ jest.mock('@constructive-io/graphql-codegen', () => { }), camelizeArgv: jest.fn((argv: Record) => argv), seedArgvFromConfig: jest.fn((argv: Record, _fileConfig: any) => argv), + hasResolvedCodegenSource: jest.fn((argv: Record) => { + const db = argv.db as Record | undefined; + return Boolean( + argv.endpoint || + argv['schema-file'] || + argv.schemas || + argv['api-names'] || + db?.schemas || + db?.apiNames + ); + }), buildGenerateOptions: jest.fn((answers: Record, _fileConfig: any) => { const { schemas, apiNames, ...rest } = answers; + const normalizedSchemas = Array.isArray(schemas) + ? schemas + : splitCommasMock(schemas as string | undefined); + const normalizedApiNames = Array.isArray(apiNames) + ? apiNames + : splitCommasMock(apiNames as string | undefined); if (schemas || apiNames) { - return { ...rest, db: { schemas, apiNames } }; + return { ...rest, db: { schemas: normalizedSchemas, apiNames: normalizedApiNames } }; } return rest; }), @@ -101,6 +118,7 @@ describe('codegen command', () => { await codegenCommand(argv, mockPrompter as any, {} as any) + expect(mockPrompter.prompt).not.toHaveBeenCalled() expect(mockGenerate).toHaveBeenCalled() const call = mockGenerate.mock.calls[0][0] expect(call).toMatchObject({ @@ -125,6 +143,7 @@ describe('codegen command', () => { await codegenCommand(argv, mockPrompter as any, {} as any) + expect(mockPrompter.prompt).not.toHaveBeenCalled() expect(mockGenerate).toHaveBeenCalled() const call = mockGenerate.mock.calls[0][0] expect(call.db).toEqual({ schemas: ['public', 'app'], apiNames: undefined }) diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index 391807fb0..58d47abe7 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -7,6 +7,7 @@ import { printResult, buildGenerateOptions, seedArgvFromConfig, + hasResolvedCodegenSource, type GraphQLSDKConfigTarget, } from '@constructive-io/graphql-codegen'; @@ -63,7 +64,9 @@ export default async ( } const seeded = seedArgvFromConfig(argv as Record, fileConfig); - const answers = await prompter.prompt(seeded, codegenQuestions); + const answers = hasResolvedCodegenSource(seeded) + ? seeded + : await prompter.prompt(seeded, codegenQuestions); const options = buildGenerateOptions(answers, fileConfig); const result = await generate(options); printResult(result); From 022f6004fce51e1cf4fee7df995b104b534809f2 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 9 Feb 2026 12:40:09 +0700 Subject: [PATCH 20/23] refactor(codegen): require explicit selection in hooks --- .../react-query-hooks.test.ts.snap | 290 ++++++------------ graphql/codegen/src/core/codegen/barrel.ts | 8 +- .../src/core/codegen/custom-mutations.ts | 13 +- .../src/core/codegen/custom-queries.ts | 178 +++++++---- graphql/codegen/src/core/codegen/hooks-ast.ts | 37 +-- graphql/codegen/src/core/codegen/mutations.ts | 39 --- graphql/codegen/src/core/codegen/queries.ts | 164 +++++----- .../core/codegen/templates/hooks-selection.ts | 52 +--- 8 files changed, 329 insertions(+), 452 deletions(-) diff --git a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap index 6236bea78..b5bbe99d3 100644 --- a/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap +++ b/graphql/codegen/src/__tests__/codegen/__snapshots__/react-query-hooks.test.ts.snap @@ -50,8 +50,12 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel with al * import { useCarsQuery, useCreateCarMutation } from './generated'; * * function MyComponent() { - * const { data, isLoading } = useCarsQuery({ first: 10 }); - * const { mutate } = useCreateCarMutation(); + * const { data, isLoading } = useCarsQuery({ + * selection: { fields: { id: true }, first: 10 }, + * }); + * const { mutate } = useCreateCarMutation({ + * selection: { fields: { id: true } }, + * }); * // ... * } * \`\`\` @@ -88,8 +92,12 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel without * import { useCarsQuery, useCreateCarMutation } from './generated'; * * function MyComponent() { - * const { data, isLoading } = useCarsQuery({ first: 10 }); - * const { mutate } = useCreateCarMutation(); + * const { data, isLoading } = useCarsQuery({ + * selection: { fields: { id: true }, first: 10 }, + * }); + * const { mutate } = useCreateCarMutation({ + * selection: { fields: { id: true } }, + * }); * // ... * } * \`\`\` @@ -123,8 +131,12 @@ exports[`Barrel File Generators generateMainBarrel generates main barrel without * import { useCarsQuery, useCreateCarMutation } from './generated'; * * function MyComponent() { - * const { data, isLoading } = useCarsQuery({ first: 10 }); - * const { mutate } = useCreateCarMutation(); + * const { data, isLoading } = useCarsQuery({ + * selection: { fields: { id: true }, first: 10 }, + * }); + * const { mutate } = useCreateCarMutation({ + * selection: { fields: { id: true } }, + * }); * // ... * } * \`\`\` @@ -207,10 +219,10 @@ export function useLoginMutation(params: { }, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ login: InferSelectResult; }, Error, LoginVariables>; -export function useLoginMutation(params?: { +export function useLoginMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -254,10 +266,10 @@ export function useRegisterMutation(params: { }, Error, RegisterVariables>, "mutationFn">): UseMutationResult<{ register: InferSelectResult; }, Error, RegisterVariables>; -export function useRegisterMutation(params?: { +export function useRegisterMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -299,10 +311,10 @@ export function useLogoutMutation(params: { }, Error, void>, "mutationFn">): UseMutationResult<{ logout: InferSelectResult; }, Error, void>; -export function useLogoutMutation(params?: { +export function useLogoutMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -345,10 +357,10 @@ export function useLoginMutation(params: { }, Error, LoginVariables>, "mutationFn">): UseMutationResult<{ login: InferSelectResult; }, Error, LoginVariables>; -export function useLoginMutation(params?: { +export function useLoginMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -389,7 +401,7 @@ export const searchUsersQueryKey = customQueryKeys.searchUsers; * * @example * \`\`\`tsx - * const { data, isLoading } = useSearchUsersQuery({ query, limit }); + * const { data, isLoading } = useSearchUsersQuery({ variables: { query, limit }, selection: { fields: { id: true } } }); * * if (data?.searchUsers) { * console.log(data.searchUsers); @@ -410,8 +422,8 @@ export function useSearchUsersQuery(params: { variables: SearchUsersVariables; selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { - const variables = params?.variables; - const args = buildSelectionArgs(params?.selection); + const variables = params.variables; + const args = buildSelectionArgs(params.selection); const { variables: _variables, selection: _selection, @@ -433,7 +445,7 @@ export function useSearchUsersQuery(params: { * * @example * \`\`\`ts - * const data = await fetchSearchUsersQuery({ query, limit }); + * const data = await fetchSearchUsersQuery({ variables: { query, limit }, selection: { fields: { id: true } } }); * \`\`\` */ export async function fetchSearchUsersQuery(params: { @@ -448,8 +460,8 @@ export async function fetchSearchUsersQuery(params: { variables: SearchUsersVariables; selection: SelectionConfig; }) { - const variables = params?.variables; - const args = buildSelectionArgs(params?.selection); + const variables = params.variables; + const args = buildSelectionArgs(params.selection); return getClient().query.searchUsers(variables!, { select: args.select }).unwrap(); @@ -459,7 +471,7 @@ export async function fetchSearchUsersQuery(params: { * * @example * \`\`\`ts - * await prefetchSearchUsersQuery(queryClient, { query, limit }); + * await prefetchSearchUsersQuery(queryClient, { variables: { query, limit }, selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { @@ -471,9 +483,9 @@ export async function prefetchSearchUsersQuery(queryClient export async function prefetchSearchUsersQuery(queryClient: QueryClient, params: { variables: SearchUsersVariables; selection: SelectionConfig; -}): void { - const variables = params?.variables; - const args = buildSelectionArgs(params?.selection); +}): Promise { + const variables = params.variables; + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: searchUsersQueryKey(variables), queryFn: () => getClient().query.searchUsers(variables!, { @@ -507,7 +519,7 @@ export const currentUserQueryKey = customQueryKeys.currentUser; * * @example * \`\`\`tsx - * const { data, isLoading } = useCurrentUserQuery(); + * const { data, isLoading } = useCurrentUserQuery({ selection: { fields: { id: true } } }); * * if (data?.currentUser) { * console.log(data.currentUser); @@ -523,10 +535,10 @@ export function useCurrentUserQuery; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useCurrentUserQuery(params?: { +export function useCurrentUserQuery(params: { selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...queryOptions @@ -545,7 +557,7 @@ export function useCurrentUserQuery(params?: { * * @example * \`\`\`ts - * const data = await fetchCurrentUserQuery(); + * const data = await fetchCurrentUserQuery({ selection: { fields: { id: true } } }); * \`\`\` */ export async function fetchCurrentUserQuery(params: { @@ -558,7 +570,7 @@ export async function fetchCurrentUserQuery(params: { export async function fetchCurrentUserQuery(params: { selection: SelectionConfig; }) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); return getClient().query.currentUser({ select: args.select }).unwrap(); @@ -568,7 +580,7 @@ export async function fetchCurrentUserQuery(params: { * * @example * \`\`\`ts - * await prefetchCurrentUserQuery(queryClient); + * await prefetchCurrentUserQuery(queryClient, { selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { @@ -578,8 +590,8 @@ export async function prefetchCurrentUserQuery(queryClient }): Promise; export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { selection: SelectionConfig; -}): void { - const args = buildSelectionArgs(params?.selection); +}): Promise { + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ @@ -612,7 +624,7 @@ export const currentUserQueryKey = () => ["currentUser"] as const; * * @example * \`\`\`tsx - * const { data, isLoading } = useCurrentUserQuery(); + * const { data, isLoading } = useCurrentUserQuery({ selection: { fields: { id: true } } }); * * if (data?.currentUser) { * console.log(data.currentUser); @@ -628,10 +640,10 @@ export function useCurrentUserQuery; }, Error, TData>, "queryKey" | "queryFn">): UseQueryResult; -export function useCurrentUserQuery(params?: { +export function useCurrentUserQuery(params: { selection: SelectionConfig; } & Omit, "queryKey" | "queryFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...queryOptions @@ -650,7 +662,7 @@ export function useCurrentUserQuery(params?: { * * @example * \`\`\`ts - * const data = await fetchCurrentUserQuery(); + * const data = await fetchCurrentUserQuery({ selection: { fields: { id: true } } }); * \`\`\` */ export async function fetchCurrentUserQuery(params: { @@ -663,7 +675,7 @@ export async function fetchCurrentUserQuery(params: { export async function fetchCurrentUserQuery(params: { selection: SelectionConfig; }) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); return getClient().query.currentUser({ select: args.select }).unwrap(); @@ -673,7 +685,7 @@ export async function fetchCurrentUserQuery(params: { * * @example * \`\`\`ts - * await prefetchCurrentUserQuery(queryClient); + * await prefetchCurrentUserQuery(queryClient, { selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { @@ -683,8 +695,8 @@ export async function prefetchCurrentUserQuery(queryClient }): Promise; export async function prefetchCurrentUserQuery(queryClient: QueryClient, params: { selection: SelectionConfig; -}): void { - const args = buildSelectionArgs(params?.selection); +}): Promise { + const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: currentUserQueryKey(), queryFn: () => getClient().query.currentUser({ @@ -740,7 +752,7 @@ export function useCreateUserMutation(params: { export function useCreateUserMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -809,7 +821,7 @@ export function useCreatePostMutation(params: { export function useCreatePostMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -876,7 +888,7 @@ export function useCreateUserMutation(params: { export function useCreateUserMutation(params: { selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -945,36 +957,12 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; -/** - * Fire-and-forget mutation hook for deleting a User - * Returns only clientMutationId (no entity data selected) - * - * @example - * \`\`\`tsx - * const { mutate, isPending } = useDeleteUserMutation(); - * - * mutate({ id: 'value-to-delete' }); - * \`\`\` - */ -export function useDeleteUserMutation(params?: Omit, "mutationFn">): UseMutationResult<{ - deleteUser: { - clientMutationId: string; - }; -}, Error, { - id: string; -}>; export function useDeleteUserMutation(params: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1053,36 +1041,12 @@ export function useDeletePostMutation(params: { }, Error, { id: string; }>; -/** - * Fire-and-forget mutation hook for deleting a Post - * Returns only clientMutationId (no entity data selected) - * - * @example - * \`\`\`tsx - * const { mutate, isPending } = useDeletePostMutation(); - * - * mutate({ id: 'value-to-delete' }); - * \`\`\` - */ -export function useDeletePostMutation(params?: Omit, "mutationFn">): UseMutationResult<{ - deletePost: { - clientMutationId: string; - }; -}, Error, { - id: string; -}>; export function useDeletePostMutation(params: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1159,36 +1123,12 @@ export function useDeleteUserMutation(params: { }, Error, { id: string; }>; -/** - * Fire-and-forget mutation hook for deleting a User - * Returns only clientMutationId (no entity data selected) - * - * @example - * \`\`\`tsx - * const { mutate, isPending } = useDeleteUserMutation(); - * - * mutate({ id: 'value-to-delete' }); - * \`\`\` - */ -export function useDeleteUserMutation(params?: Omit, "mutationFn">): UseMutationResult<{ - deleteUser: { - clientMutationId: string; - }; -}, Error, { - id: string; -}>; export function useDeleteUserMutation(params: { - selection?: SelectionConfig; + selection: SelectionConfig; } & Omit, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1274,7 +1214,7 @@ export function useUpdateUserMutation(params: { id: string; patch: UserPatch; }>, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1364,7 +1304,7 @@ export function useUpdatePostMutation(params: { id: string; patch: PostPatch; }>, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1452,7 +1392,7 @@ export function useUpdateUserMutation(params: { id: string; patch: UserPatch; }>, "mutationFn">) { - const args = buildSelectionArgs(params?.selection); + const args = buildSelectionArgs(params.selection); const { selection: _selection, ...mutationOptions @@ -1532,8 +1472,7 @@ export function useUsersQuery; } & Omit, "queryKey" | "queryFn">) { - const selection = params.selection; - const args = buildListSelectionArgs(selection); + const args = buildListSelectionArgs(params.selection); const { selection: _selection, ...queryOptions @@ -1541,10 +1480,7 @@ export function useUsersQuery(params: { void _selection; return useQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(), + queryFn: () => getClient().user.findMany(args).unwrap(), ...queryOptions }); } @@ -1571,18 +1507,15 @@ export async function fetchUsersQuery(params: { export async function fetchUsersQuery(params: { selection: ListSelectionConfig; }) { - const args = buildListSelectionArgs(selection); - return getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(); + const args = buildListSelectionArgs(params.selection); + return getClient().user.findMany(args).unwrap(); } /** * Prefetch User list for SSR or cache warming * * @example * \`\`\`ts - * await prefetchUsersQuery(queryClient, { selection: { first: 10 } }); + * await prefetchUsersQuery(queryClient, { selection: { fields: { id: true }, first: 10 } }); * \`\`\` */ export async function prefetchUsersQuery(queryClient: QueryClient, params: { @@ -1592,14 +1525,11 @@ export async function prefetchUsersQuery(queryClient: Quer }): Promise; export async function prefetchUsersQuery(queryClient: QueryClient, params: { selection: ListSelectionConfig; -}): void { - const args = buildListSelectionArgs(selection); +}): Promise { + const args = buildListSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: userKeys.list(args), - queryFn: () => getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap() + queryFn: () => getClient().user.findMany(args).unwrap() }); } " @@ -1642,7 +1572,7 @@ export const postsQueryKey = postKeys.list; * @example With scope for hierarchical cache invalidation * \`\`\`tsx * const { data } = usePostsQuery({ - * selection: { first: 10 }, + * selection: { fields: { id: true }, first: 10 }, * scope: { parentId: 'parent-id' }, * }); * \`\`\` @@ -1663,8 +1593,7 @@ export function usePostsQuery(params: { } & Omit, "queryKey" | "queryFn"> & { scope?: PostScope; }) { - const selection = params.selection; - const args = buildListSelectionArgs(selection); + const args = buildListSelectionArgs(params.selection); const { scope, selection: _selection, @@ -1673,10 +1602,7 @@ export function usePostsQuery(params: { void _selection; return useQuery({ queryKey: postKeys.list(args, scope), - queryFn: () => getClient().post.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(), + queryFn: () => getClient().post.findMany(args).unwrap(), ...queryOptions }); } @@ -1703,18 +1629,15 @@ export async function fetchPostsQuery(params: { export async function fetchPostsQuery(params: { selection: ListSelectionConfig; }) { - const args = buildListSelectionArgs(selection); - return getClient().post.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(); + const args = buildListSelectionArgs(params.selection); + return getClient().post.findMany(args).unwrap(); } /** * Prefetch Post list for SSR or cache warming * * @example * \`\`\`ts - * await prefetchPostsQuery(queryClient, { selection: { first: 10 } }); + * await prefetchPostsQuery(queryClient, { selection: { fields: { id: true }, first: 10 } }); * \`\`\` */ export async function prefetchPostsQuery(queryClient: QueryClient, params: { @@ -1728,14 +1651,11 @@ export async function prefetchPostsQuery(queryClient: QueryClient, params: { selection: ListSelectionConfig; } & { scope?: PostScope; -}): void { - const args = buildListSelectionArgs(selection); +}): Promise { + const args = buildListSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: postKeys.list(args, params?.scope), - queryFn: () => getClient().post.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap() + queryFn: () => getClient().post.findMany(args).unwrap() }); } " @@ -1784,8 +1704,7 @@ export function useUsersQuery; } & Omit, "queryKey" | "queryFn">) { - const selection = params.selection; - const args = buildListSelectionArgs(selection); + const args = buildListSelectionArgs(params.selection); const { selection: _selection, ...queryOptions @@ -1793,10 +1712,7 @@ export function useUsersQuery(params: { void _selection; return useQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(), + queryFn: () => getClient().user.findMany(args).unwrap(), ...queryOptions }); } @@ -1823,18 +1739,15 @@ export async function fetchUsersQuery(params: { export async function fetchUsersQuery(params: { selection: ListSelectionConfig; }) { - const args = buildListSelectionArgs(selection); - return getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap(); + const args = buildListSelectionArgs(params.selection); + return getClient().user.findMany(args).unwrap(); } /** * Prefetch User list for SSR or cache warming * * @example * \`\`\`ts - * await prefetchUsersQuery(queryClient, { selection: { first: 10 } }); + * await prefetchUsersQuery(queryClient, { selection: { fields: { id: true }, first: 10 } }); * \`\`\` */ export async function prefetchUsersQuery(queryClient: QueryClient, params: { @@ -1844,14 +1757,11 @@ export async function prefetchUsersQuery(queryClient: Quer }): Promise; export async function prefetchUsersQuery(queryClient: QueryClient, params: { selection: ListSelectionConfig; -}): void { - const args = buildListSelectionArgs(selection); +}): Promise { + const args = buildListSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: usersQueryKey(args), - queryFn: () => getClient().user.findMany({ - ...(args ?? {}), - select: args.select - }).unwrap() + queryFn: () => getClient().user.findMany(args).unwrap() }); } " @@ -1910,7 +1820,6 @@ export function useUserQuery(params: { queryKey: userKeys.detail(params.id), queryFn: () => getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(), ...queryOptions @@ -1942,7 +1851,6 @@ export async function fetchUserQuery(params: { const args = buildSelectionArgs(params.selection); return getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(); } @@ -1951,7 +1859,7 @@ export async function fetchUserQuery(params: { * * @example * \`\`\`ts - * await prefetchUserQuery(queryClient, { id: 'some-id' }); + * await prefetchUserQuery(queryClient, { id: 'some-id', selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchUserQuery(queryClient: QueryClient, params: { @@ -1963,13 +1871,12 @@ export async function prefetchUserQuery(queryClient: Query export async function prefetchUserQuery(queryClient: QueryClient, params: { id: string; selection: SelectionConfig; -}): void { +}): Promise { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: userKeys.detail(params.id), queryFn: () => getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap() }); @@ -2011,6 +1918,7 @@ export const postQueryKey = postKeys.detail; * \`\`\`tsx * const { data } = usePostQuery({ * id: 'some-id', + * selection: { fields: { id: true } }, * scope: { parentId: 'parent-id' }, * }); * \`\`\` @@ -2044,7 +1952,6 @@ export function usePostQuery(params: { queryKey: postKeys.detail(params.id, scope), queryFn: () => getClient().post.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(), ...queryOptions @@ -2076,7 +1983,6 @@ export async function fetchPostQuery(params: { const args = buildSelectionArgs(params.selection); return getClient().post.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(); } @@ -2085,7 +1991,7 @@ export async function fetchPostQuery(params: { * * @example * \`\`\`ts - * await prefetchPostQuery(queryClient, { id: 'some-id' }); + * await prefetchPostQuery(queryClient, { id: 'some-id', selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchPostQuery(queryClient: QueryClient, params: { @@ -2101,13 +2007,12 @@ export async function prefetchPostQuery(queryClient: QueryClient, params: { selection: SelectionConfig; } & { scope?: PostScope; -}): void { +}): Promise { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: postKeys.detail(params.id, params?.scope), queryFn: () => getClient().post.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap() }); @@ -2166,7 +2071,6 @@ export function useUserQuery(params: { queryKey: userQueryKey(params.id), queryFn: () => getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(), ...queryOptions @@ -2198,7 +2102,6 @@ export async function fetchUserQuery(params: { const args = buildSelectionArgs(params.selection); return getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap(); } @@ -2207,7 +2110,7 @@ export async function fetchUserQuery(params: { * * @example * \`\`\`ts - * await prefetchUserQuery(queryClient, { id: 'some-id' }); + * await prefetchUserQuery(queryClient, { id: 'some-id', selection: { fields: { id: true } } }); * \`\`\` */ export async function prefetchUserQuery(queryClient: QueryClient, params: { @@ -2219,13 +2122,12 @@ export async function prefetchUserQuery(queryClient: Query export async function prefetchUserQuery(queryClient: QueryClient, params: { id: string; selection: SelectionConfig; -}): void { +}): Promise { const args = buildSelectionArgs(params.selection); await queryClient.prefetchQuery({ queryKey: userQueryKey(params.id), queryFn: () => getClient().user.findOne({ id: params.id, - ...(args ?? {}), select: args.select }).unwrap() }); diff --git a/graphql/codegen/src/core/codegen/barrel.ts b/graphql/codegen/src/core/codegen/barrel.ts index f5f23f51e..64032e99c 100644 --- a/graphql/codegen/src/core/codegen/barrel.ts +++ b/graphql/codegen/src/core/codegen/barrel.ts @@ -172,8 +172,12 @@ export function generateMainBarrel( "import { useCarsQuery, useCreateCarMutation } from './generated';", '', 'function MyComponent() {', - ' const { data, isLoading } = useCarsQuery({ first: 10 });', - ' const { mutate } = useCreateCarMutation();', + ' const { data, isLoading } = useCarsQuery({', + ' selection: { fields: { id: true }, first: 10 },', + ' });', + ' const { mutate } = useCreateCarMutation({', + ' selection: { fields: { id: true } },', + ' });', ' // ...', '}', '```', diff --git a/graphql/codegen/src/core/codegen/custom-mutations.ts b/graphql/codegen/src/core/codegen/custom-mutations.ts index 420f4a7cb..ebd589bf3 100644 --- a/graphql/codegen/src/core/codegen/custom-mutations.ts +++ b/graphql/codegen/src/core/codegen/custom-mutations.ts @@ -260,7 +260,7 @@ function generateCustomMutationHookInternal( ) : undefined; - const selectObj = t.objectExpression([ + const selectArgExpr = t.objectExpression([ objectProp( 'select', t.memberExpression(t.identifier('args'), t.identifier('select')), @@ -279,13 +279,18 @@ function generateCustomMutationHookInternal( 'mutation', operation.name, [t.identifier('variables')], - selectObj, + selectArgExpr, ), ); } else { mutationFnExpr = t.arrowFunctionExpression( [], - getClientCustomCallUnwrap('mutation', operation.name, [], selectObj), + getClientCustomCallUnwrap( + 'mutation', + operation.name, + [], + selectArgExpr, + ), ); } @@ -301,7 +306,7 @@ function generateCustomMutationHookInternal( exportFunction( hookName, null, - [createFunctionParam('params', implParamType, true)], + [createFunctionParam('params', implParamType)], body, ), ); diff --git a/graphql/codegen/src/core/codegen/custom-queries.ts b/graphql/codegen/src/core/codegen/custom-queries.ts index 19d17328b..1252b1f14 100644 --- a/graphql/codegen/src/core/codegen/custom-queries.ts +++ b/graphql/codegen/src/core/codegen/custom-queries.ts @@ -268,8 +268,12 @@ export function generateCustomQueryHook( operation.description || `Query hook for ${operation.name}`; const argNames = operation.args.map((a) => a.name).join(', '); const exampleCall = hasArgs - ? `${hookName}({ ${argNames} })` - : `${hookName}()`; + ? hasSelect + ? `${hookName}({ variables: { ${argNames} }, selection: { fields: { id: true } } })` + : `${hookName}({ variables: { ${argNames} } })` + : hasSelect + ? `${hookName}({ selection: { fields: { id: true } } })` + : `${hookName}()`; if (hasSelect) { // Overload 1: with selection.fields @@ -344,7 +348,7 @@ export function generateCustomQueryHook( const implParam = createFunctionParam( 'params', implParamType, - !hasRequiredArgs, + false, ); const body: t.Statement[] = []; @@ -352,12 +356,16 @@ export function generateCustomQueryHook( body.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.memberExpression( + t.identifier('params'), + t.identifier('variables'), + ) + : t.logicalExpression( + '??', + t.memberExpression(t.identifier('params'), t.identifier('variables')), + t.objectExpression([]), + ), ), ); } @@ -398,7 +406,7 @@ export function generateCustomQueryHook( body.push(voidStatement('_selection')); } - const selectObj = t.objectExpression([ + const selectArgExpr = t.objectExpression([ objectProp( 'select', t.memberExpression(t.identifier('args'), t.identifier('select')), @@ -409,9 +417,9 @@ export function generateCustomQueryHook( hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), - selectObj, + selectArgExpr, ] - : [selectObj]; + : [selectArgExpr]; const queryFnExpr = t.arrowFunctionExpression( [], getClientCustomCallUnwrap( @@ -511,12 +519,23 @@ export function generateCustomQueryHook( body.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ) + : t.logicalExpression( + '??', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + t.objectExpression([]), + ), ), ); const destructPattern = t.objectPattern([ @@ -604,7 +623,7 @@ export function generateCustomQueryHook( statements.push( exportFunction( hookName, - null, + createTDataTypeParam(resultTypeLiteral), [implParam], body, typeRef('UseQueryResult', [typeRef('TData')]), @@ -617,8 +636,12 @@ export function generateCustomQueryHook( const fetchFnName = `fetch${ucFirst(operation.name)}Query`; const fetchArgNames = operation.args.map((a) => a.name).join(', '); const fetchExampleCall = hasArgs - ? `${fetchFnName}({ ${fetchArgNames} })` - : `${fetchFnName}()`; + ? hasSelect + ? `${fetchFnName}({ variables: { ${fetchArgNames} }, selection: { fields: { id: true } } })` + : `${fetchFnName}({ variables: { ${fetchArgNames} } })` + : hasSelect + ? `${fetchFnName}({ selection: { fields: { id: true } } })` + : `${fetchFnName}()`; if (hasSelect) { // Overload 1: with fields @@ -677,17 +700,18 @@ export function generateCustomQueryHook( fBody.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.memberExpression(t.identifier('params'), t.identifier('variables')) + : t.logicalExpression( + '??', + t.memberExpression(t.identifier('params'), t.identifier('variables')), + t.objectExpression([]), + ), ), ); } fBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([ + const selectArgExpr = t.objectExpression([ objectProp( 'select', t.memberExpression(t.identifier('args'), t.identifier('select')), @@ -698,9 +722,9 @@ export function generateCustomQueryHook( hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), - selectObj, + selectArgExpr, ] - : [selectObj]; + : [selectArgExpr]; fBody.push( t.returnStatement( getClientCustomCallUnwrap( @@ -714,7 +738,13 @@ export function generateCustomQueryHook( exportAsyncFunction( fetchFnName, null, - [createFunctionParam('params', t.tsTypeLiteral(fImplProps))], + [ + createFunctionParam( + 'params', + t.tsTypeLiteral(fImplProps), + false, + ), + ], fBody, ), ); @@ -731,12 +761,23 @@ export function generateCustomQueryHook( fBody.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ) + : t.logicalExpression( + '??', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + t.objectExpression([]), + ), ), ); const fCallArgs = hasRequiredArgs @@ -796,8 +837,12 @@ export function generateCustomQueryHook( const prefetchFnName = `prefetch${ucFirst(operation.name)}Query`; const prefetchArgNames = operation.args.map((a) => a.name).join(', '); const prefetchExampleCall = hasArgs - ? `${prefetchFnName}(queryClient, { ${prefetchArgNames} })` - : `${prefetchFnName}(queryClient)`; + ? hasSelect + ? `${prefetchFnName}(queryClient, { variables: { ${prefetchArgNames} }, selection: { fields: { id: true } } })` + : `${prefetchFnName}(queryClient, { variables: { ${prefetchArgNames} } })` + : hasSelect + ? `${prefetchFnName}(queryClient, { selection: { fields: { id: true } } })` + : `${prefetchFnName}(queryClient)`; if (hasSelect) { // Overload 1: with fields @@ -859,17 +904,21 @@ export function generateCustomQueryHook( pBody.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.memberExpression( + t.identifier('params'), + t.identifier('variables'), + ) + : t.logicalExpression( + '??', + t.memberExpression(t.identifier('params'), t.identifier('variables')), + t.objectExpression([]), + ), ), ); } pBody.push(buildSelectionArgsCall(selectTypeName!)); - const selectObj = t.objectExpression([ + const selectArgExpr = t.objectExpression([ objectProp( 'select', t.memberExpression(t.identifier('args'), t.identifier('select')), @@ -880,9 +929,9 @@ export function generateCustomQueryHook( hasRequiredArgs ? t.tsNonNullExpression(t.identifier('variables')) : t.identifier('variables'), - selectObj, + selectArgExpr, ] - : [selectObj]; + : [selectArgExpr]; const prefetchQueryCall = callExpr( t.memberExpression( t.identifier('queryClient'), @@ -912,10 +961,14 @@ export function generateCustomQueryHook( null, [ createFunctionParam('queryClient', typeRef('QueryClient')), - createFunctionParam('params', t.tsTypeLiteral(pImplProps)), + createFunctionParam( + 'params', + t.tsTypeLiteral(pImplProps), + false, + ), ], pBody, - t.tsVoidKeyword(), + typeRef('Promise', [t.tsVoidKeyword()]), ), ); } else { @@ -943,12 +996,23 @@ export function generateCustomQueryHook( pBody.push( constDecl( 'variables', - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('variables'), - false, - true, - ), + hasRequiredArgs + ? t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ) + : t.logicalExpression( + '??', + t.optionalMemberExpression( + t.identifier('params'), + t.identifier('variables'), + false, + true, + ), + t.objectExpression([]), + ), ), ); const pCallArgs = hasRequiredArgs @@ -1004,7 +1068,7 @@ export function generateCustomQueryHook( null, pParams, pBody, - t.tsVoidKeyword(), + typeRef('Promise', [t.tsVoidKeyword()]), ); addJSDocComment(pDecl, [ `Prefetch ${operation.name} for SSR or cache warming`, diff --git a/graphql/codegen/src/core/codegen/hooks-ast.ts b/graphql/codegen/src/core/codegen/hooks-ast.ts index a6620e951..1d60d7e38 100644 --- a/graphql/codegen/src/core/codegen/hooks-ast.ts +++ b/graphql/codegen/src/core/codegen/hooks-ast.ts @@ -662,25 +662,11 @@ export function getClientCustomCallUnwrap( // Select/args expression builders // ============================================================================ -export function buildSelectExpr(argsIdent: string): t.MemberExpression { - return t.memberExpression(t.identifier(argsIdent), t.identifier('select')); -} - export function buildFindManyCallExpr( singularName: string, argsIdent: string, ): t.CallExpression { - const spreadArgs = t.parenthesizedExpression( - t.logicalExpression('??', t.identifier(argsIdent), t.objectExpression([])), - ); - return getClientCallUnwrap( - singularName, - 'findMany', - t.objectExpression([ - t.spreadElement(spreadArgs), - objectProp('select', buildSelectExpr(argsIdent)), - ]), - ); + return getClientCallUnwrap(singularName, 'findMany', t.identifier(argsIdent)); } export function buildFindOneCallExpr( @@ -700,16 +686,10 @@ export function buildFindOneCallExpr( t.identifier(pkFieldName), ), ), - t.spreadElement( - t.parenthesizedExpression( - t.logicalExpression( - '??', - t.identifier(argsIdent), - t.objectExpression([]), - ), - ), + objectProp( + 'select', + t.memberExpression(t.identifier(argsIdent), t.identifier('select')), ), - objectProp('select', buildSelectExpr(argsIdent)), ]), ); } @@ -794,12 +774,7 @@ export function buildSelectionArgsCall( selectTypeName: string, ): t.VariableDeclaration { const call = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.optionalMemberExpression( - t.identifier('params'), - t.identifier('selection'), - false, - true, - ), + t.memberExpression(t.identifier('params'), t.identifier('selection')), ]); // @ts-ignore - Babel types support typeParameters on CallExpression for TS call.typeParameters = t.tsTypeParameterInstantiation([ @@ -814,7 +789,7 @@ export function buildListSelectionArgsCall( orderByTypeName: string, ): t.VariableDeclaration { const call = t.callExpression(t.identifier('buildListSelectionArgs'), [ - t.identifier('selection'), + t.memberExpression(t.identifier('params'), t.identifier('selection')), ]); // @ts-ignore - Babel types support typeParameters on CallExpression for TS call.typeParameters = t.tsTypeParameterInstantiation([ diff --git a/graphql/codegen/src/core/codegen/mutations.ts b/graphql/codegen/src/core/codegen/mutations.ts index 29c9becec..d99b95acb 100644 --- a/graphql/codegen/src/core/codegen/mutations.ts +++ b/graphql/codegen/src/core/codegen/mutations.ts @@ -659,20 +659,6 @@ export function generateDeleteMutationHook( const resultType = (sel: t.TSType) => buildMutationResultType(mutationName, singularName, relationTypeName, sel); - const clientMutationIdResultType = t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier(mutationName), - t.tsTypeAnnotation( - t.tsTypeLiteral([ - t.tsPropertySignature( - t.identifier('clientMutationId'), - t.tsTypeAnnotation(t.tsStringKeyword()), - ), - ]), - ), - ), - ]); - // Overload 1: with fields const o1ParamType = t.tsIntersectionType([ t.tsTypeLiteral([ @@ -703,36 +689,11 @@ export function generateDeleteMutationHook( ]); statements.push(o1); - // Overload 2: fire-and-forget (no selection, returns clientMutationId only) - const o2ParamType = useMutationOptionsType( - clientMutationIdResultType, - deleteVarType, - ); - const o2 = exportDeclareFunction( - hookName, - null, - [createFunctionParam('params', o2ParamType, true)], - useMutationResultType(clientMutationIdResultType, deleteVarType), - ); - addJSDocComment(o2, [ - `Fire-and-forget mutation hook for deleting a ${typeName}`, - 'Returns only clientMutationId (no entity data selected)', - '', - '@example', - '```tsx', - `const { mutate, isPending } = ${hookName}();`, - '', - `mutate({ ${pkField.name}: ${pkField.tsType === 'string' ? "'value-to-delete'" : '123'} });`, - '```', - ]); - statements.push(o2); - // Implementation const implSelProp = t.tsPropertySignature( t.identifier('selection'), t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), ); - implSelProp.optional = true; const implParamType = t.tsIntersectionType([ t.tsTypeLiteral([implSelProp]), omitType( diff --git a/graphql/codegen/src/core/codegen/queries.ts b/graphql/codegen/src/core/codegen/queries.ts index f8b426bec..451370e02 100644 --- a/graphql/codegen/src/core/codegen/queries.ts +++ b/graphql/codegen/src/core/codegen/queries.ts @@ -252,7 +252,7 @@ export function generateListQueryHook( docLines.push('@example With scope for hierarchical cache invalidation'); docLines.push('```tsx'); docLines.push(`const { data } = ${hookName}({`); - docLines.push(' selection: { first: 10 },'); + docLines.push(' selection: { fields: { id: true }, first: 10 },'); docLines.push(" scope: { parentId: 'parent-id' },"); docLines.push('});'); docLines.push('```'); @@ -308,12 +308,6 @@ export function generateListQueryHook( ]); const body: t.Statement[] = []; - body.push( - constDecl( - 'selection', - t.memberExpression(t.identifier('params'), t.identifier('selection')), - ), - ); body.push( buildListSelectionArgsCall( selectTypeName, @@ -393,16 +387,18 @@ export function generateListQueryHook( statements.push(f1Decl); // Implementation - const fImplSelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation( - listSelectionConfigType( - typeRef(selectTypeName), - filterTypeName, - orderByTypeName, + const fImplParamType = t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), ), ), - ); + ]); const fBody: t.Statement[] = []; fBody.push( buildListSelectionArgsCall( @@ -416,7 +412,7 @@ export function generateListQueryHook( exportAsyncFunction( fetchFnName, null, - [createFunctionParam('params', t.tsTypeLiteral([fImplSelProp]))], + [createFunctionParam('params', fImplParamType)], fBody, ), ); @@ -461,30 +457,41 @@ export function generateListQueryHook( '', '@example', '```ts', - `await ${prefetchFnName}(queryClient, { selection: { first: 10 } });`, + `await ${prefetchFnName}(queryClient, { selection: { fields: { id: true }, first: 10 } });`, '```', ]); statements.push(p1Decl); // Implementation - const pImplSelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation( - listSelectionConfigType( - typeRef(selectTypeName), - filterTypeName, - orderByTypeName, - ), - ), - ); const pImplParamType = hasRelationships && useCentralizedKeys ? t.tsIntersectionType([ - t.tsTypeLiteral([pImplSelProp]), + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), + ), + ), + ]), scopeTypeLiteral(scopeTypeName), ]) - : t.tsTypeLiteral([pImplSelProp]); - + : t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation( + listSelectionConfigType( + typeRef(selectTypeName), + filterTypeName, + orderByTypeName, + ), + ), + ), + ]); const pBody: t.Statement[] = []; pBody.push( buildListSelectionArgsCall( @@ -530,7 +537,7 @@ export function generateListQueryHook( createFunctionParam('params', pImplParamType), ], pBody, - t.tsVoidKeyword(), + typeRef('Promise', [t.tsVoidKeyword()]), ), ); } @@ -720,6 +727,7 @@ export function generateSingleQueryHook( docLines.push('```tsx'); docLines.push(`const { data } = ${hookName}({`); docLines.push(` ${pkFieldName}: 'some-id',`); + docLines.push(' selection: { fields: { id: true } },'); docLines.push(" scope: { parentId: 'parent-id' },"); docLines.push('});'); docLines.push('```'); @@ -750,16 +758,15 @@ export function generateSingleQueryHook( statements.push(o1); // Implementation - const implSelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), - ); const implProps = [ t.tsPropertySignature( t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType), ), - implSelProp, + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ), ]; const implOptionsType = (() => { const base = useQueryOptionsImplType(); @@ -774,15 +781,7 @@ export function generateSingleQueryHook( ]); const body: t.Statement[] = []; - // const args = buildSelectionArgs(params.selection); - const argsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')), - ]); - // @ts-ignore - argsCall.typeParameters = t.tsTypeParameterInstantiation([ - typeRef(selectTypeName), - ]); - body.push(constDecl('args', argsCall)); + body.push(buildSelectionArgsCall(selectTypeName)); const pkMemberExpr = t.memberExpression( t.identifier('params'), @@ -853,31 +852,23 @@ export function generateSingleQueryHook( statements.push(f1Decl); // Implementation - const fImplSelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + const fBody: t.Statement[] = []; + fBody.push(buildSelectionArgsCall(selectTypeName)); + fBody.push( + t.returnStatement( + buildFindOneCallExpr(singularName, pkFieldName, 'args'), + ), ); const fImplProps = [ t.tsPropertySignature( t.identifier(pkFieldName), t.tsTypeAnnotation(pkTsType), ), - fImplSelProp, - ]; - const fBody: t.Statement[] = []; - const fArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')), - ]); - // @ts-ignore - fArgsCall.typeParameters = t.tsTypeParameterInstantiation([ - typeRef(selectTypeName), - ]); - fBody.push(constDecl('args', fArgsCall)); - fBody.push( - t.returnStatement( - buildFindOneCallExpr(singularName, pkFieldName, 'args'), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), ), - ); + ]; statements.push( exportAsyncFunction( fetchFnName, @@ -924,40 +915,39 @@ export function generateSingleQueryHook( '', '@example', '```ts', - `await ${prefetchFnName}(queryClient, { ${pkFieldName}: 'some-id' });`, + `await ${prefetchFnName}(queryClient, { ${pkFieldName}: 'some-id', selection: { fields: { id: true } } });`, '```', ]); statements.push(p1Decl); // Implementation - const pImplSelProp = t.tsPropertySignature( - t.identifier('selection'), - t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), - ); - const pImplProps: t.TSPropertySignature[] = [ - t.tsPropertySignature( - t.identifier(pkFieldName), - t.tsTypeAnnotation(pkTsType), - ), - pImplSelProp, - ]; const pImplParamType = hasRelationships && useCentralizedKeys ? t.tsIntersectionType([ - t.tsTypeLiteral(pImplProps), + t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ), + ]), scopeTypeLiteral(scopeTypeName), ]) - : t.tsTypeLiteral(pImplProps); - + : t.tsTypeLiteral([ + t.tsPropertySignature( + t.identifier(pkFieldName), + t.tsTypeAnnotation(pkTsType), + ), + t.tsPropertySignature( + t.identifier('selection'), + t.tsTypeAnnotation(selectionConfigType(typeRef(selectTypeName))), + ), + ]); const pBody: t.Statement[] = []; - const pArgsCall = t.callExpression(t.identifier('buildSelectionArgs'), [ - t.memberExpression(t.identifier('params'), t.identifier('selection')), - ]); - // @ts-ignore - pArgsCall.typeParameters = t.tsTypeParameterInstantiation([ - typeRef(selectTypeName), - ]); - pBody.push(constDecl('args', pArgsCall)); + pBody.push(buildSelectionArgsCall(selectTypeName)); const queryKeyExpr = hasRelationships && useCentralizedKeys @@ -1003,7 +993,7 @@ export function generateSingleQueryHook( createFunctionParam('params', pImplParamType), ], pBody, - t.tsVoidKeyword(), + typeRef('Promise', [t.tsVoidKeyword()]), ), ); } diff --git a/graphql/codegen/src/core/codegen/templates/hooks-selection.ts b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts index 66f7ed055..abb9ae48b 100644 --- a/graphql/codegen/src/core/codegen/templates/hooks-selection.ts +++ b/graphql/codegen/src/core/codegen/templates/hooks-selection.ts @@ -10,7 +10,7 @@ */ export interface SelectionConfig { - fields?: TFields; + fields: TFields; } export interface ListSelectionConfig< @@ -28,47 +28,23 @@ export interface ListSelectionConfig< } export function buildSelectionArgs( - selection?: SelectionConfig, -): { select?: TFields } | undefined { - if (!selection || selection.fields === undefined) { - return undefined; - } - + selection: SelectionConfig, +): { select: TFields } { return { select: selection.fields }; } export function buildListSelectionArgs( - selection?: ListSelectionConfig, -): - | { - select?: TFields; - where?: TWhere; - orderBy?: TOrderBy[]; - first?: number; - last?: number; - after?: string; - before?: string; - offset?: number; - } - | undefined { - if (!selection) { - return undefined; - } - - const hasAnyValues = - selection.fields !== undefined || - selection.where !== undefined || - selection.orderBy !== undefined || - selection.first !== undefined || - selection.last !== undefined || - selection.after !== undefined || - selection.before !== undefined || - selection.offset !== undefined; - - if (!hasAnyValues) { - return undefined; - } - + selection: ListSelectionConfig, +): { + select: TFields; + where?: TWhere; + orderBy?: TOrderBy[]; + first?: number; + last?: number; + after?: string; + before?: string; + offset?: number; +} { return { select: selection.fields, where: selection.where, From 15343383668467b4461a86b183c527b7362973b2 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 9 Feb 2026 12:40:27 +0700 Subject: [PATCH 21/23] fix(codegen): prune stale generated files --- .../codegen/write-generated-files.test.ts | 61 ++++++++++++ graphql/codegen/src/core/generate.ts | 99 +++++++------------ graphql/codegen/src/core/output/writer.ts | 30 +++++- 3 files changed, 125 insertions(+), 65 deletions(-) create mode 100644 graphql/codegen/src/__tests__/codegen/write-generated-files.test.ts diff --git a/graphql/codegen/src/__tests__/codegen/write-generated-files.test.ts b/graphql/codegen/src/__tests__/codegen/write-generated-files.test.ts new file mode 100644 index 000000000..cf39779a8 --- /dev/null +++ b/graphql/codegen/src/__tests__/codegen/write-generated-files.test.ts @@ -0,0 +1,61 @@ +import * as fs from 'node:fs'; +import * as os from 'node:os'; +import * as path from 'node:path'; + +import { writeGeneratedFiles } from '../../core/output'; + +describe('writeGeneratedFiles', () => { + let tempDir: string; + + beforeEach(() => { + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegen-write-test-')); + }); + + afterEach(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + it('removes stale TypeScript files when pruneStaleFiles is enabled', async () => { + const staleRoot = path.join(tempDir, 'stale.ts'); + const staleNested = path.join(tempDir, 'nested', 'old.ts'); + fs.mkdirSync(path.dirname(staleNested), { recursive: true }); + fs.writeFileSync(staleRoot, 'export const stale = true;\n'); + fs.writeFileSync(staleNested, 'export const old = true;\n'); + + const result = await writeGeneratedFiles( + [{ path: 'nested/new.ts', content: 'export const fresh = true;\n' }], + tempDir, + [], + { + showProgress: false, + formatFiles: false, + pruneStaleFiles: true, + }, + ); + + expect(result.success).toBe(true); + expect(fs.existsSync(staleRoot)).toBe(false); + expect(fs.existsSync(staleNested)).toBe(false); + expect(fs.existsSync(path.join(tempDir, 'nested', 'new.ts'))).toBe(true); + }); + + it('keeps existing files when pruneStaleFiles is disabled', async () => { + const staleRoot = path.join(tempDir, 'stale.ts'); + fs.writeFileSync(staleRoot, 'export const stale = true;\n'); + + const result = await writeGeneratedFiles( + [{ path: 'fresh.ts', content: 'export const fresh = true;\n' }], + tempDir, + [], + { + showProgress: false, + formatFiles: false, + pruneStaleFiles: false, + }, + ); + + expect(result.success).toBe(true); + expect(fs.existsSync(staleRoot)).toBe(true); + expect(fs.existsSync(path.join(tempDir, 'fresh.ts'))).toBe(true); + }); +}); diff --git a/graphql/codegen/src/core/generate.ts b/graphql/codegen/src/core/generate.ts index 35ace365d..ac09d72f0 100644 --- a/graphql/codegen/src/core/generate.ts +++ b/graphql/codegen/src/core/generate.ts @@ -4,7 +4,7 @@ * This is the primary entry point for programmatic usage. * The CLI is a thin wrapper around this function. */ -import path from 'path'; +import path from 'node:path'; import type { GraphQLSDKConfigTarget } from '../types/config'; import { getConfigOptions } from '../types/config'; @@ -79,12 +79,12 @@ export async function generate( endpoint: config.endpoint || undefined, schemaFile: config.schemaFile || undefined, db: config.db, - authorization: options.authorization || config.headers?.['Authorization'], + authorization: options.authorization || config.headers?.Authorization, headers: config.headers, }); // Run pipeline - let pipelineResult; + let pipelineResult: Awaited>; try { console.log(`Fetching schema from ${source.describe()}...`); pipelineResult = await runCodegenPipeline({ @@ -115,6 +115,7 @@ export async function generate( const allFilesWritten: string[] = []; const bothEnabled = runReactQuery && runOrm; + const filesToWrite: Array<{ path: string; content: string }> = []; // Generate shared types when both are enabled if (bothEnabled) { @@ -128,28 +129,11 @@ export async function generate( }, config, }); - - if (!options.dryRun) { - const writeResult = await writeGeneratedFiles( - sharedResult.files, - outputRoot, - [], - ); - if (!writeResult.success) { - return { - success: false, - message: `Failed to write shared types: ${writeResult.errors?.join(', ')}`, - output: outputRoot, - errors: writeResult.errors, - }; - } - allFilesWritten.push(...(writeResult.filesWritten ?? [])); - } + filesToWrite.push(...sharedResult.files); } // Generate React Query hooks if (runReactQuery) { - const hooksDir = path.join(outputRoot, 'hooks'); console.log('Generating React Query hooks...'); const { files } = generateReactQueryFiles({ tables, @@ -161,27 +145,16 @@ export async function generate( config, sharedTypesPath: bothEnabled ? '..' : undefined, }); - - if (!options.dryRun) { - const writeResult = await writeGeneratedFiles(files, hooksDir, [ - 'queries', - 'mutations', - ]); - if (!writeResult.success) { - return { - success: false, - message: `Failed to write React Query hooks: ${writeResult.errors?.join(', ')}`, - output: outputRoot, - errors: writeResult.errors, - }; - } - allFilesWritten.push(...(writeResult.filesWritten ?? [])); - } + filesToWrite.push( + ...files.map((file) => ({ + ...file, + path: path.posix.join('hooks', file.path), + })), + ); } // Generate ORM client if (runOrm) { - const ormDir = path.join(outputRoot, 'orm'); console.log('Generating ORM client...'); const { files } = generateOrmFiles({ tables, @@ -193,38 +166,36 @@ export async function generate( config, sharedTypesPath: bothEnabled ? '..' : undefined, }); - - if (!options.dryRun) { - const writeResult = await writeGeneratedFiles(files, ormDir, [ - 'models', - 'query', - 'mutation', - ]); - if (!writeResult.success) { - return { - success: false, - message: `Failed to write ORM client: ${writeResult.errors?.join(', ')}`, - output: outputRoot, - errors: writeResult.errors, - }; - } - allFilesWritten.push(...(writeResult.filesWritten ?? [])); - } + filesToWrite.push( + ...files.map((file) => ({ + ...file, + path: path.posix.join('orm', file.path), + })), + ); } // Generate barrel file at output root // This re-exports from the appropriate subdirectories based on which generators are enabled + const barrelContent = generateRootBarrel({ + hasTypes: bothEnabled, + hasHooks: runReactQuery, + hasOrm: runOrm, + }); + filesToWrite.push({ path: 'index.ts', content: barrelContent }); + if (!options.dryRun) { - const barrelContent = generateRootBarrel({ - hasTypes: bothEnabled, - hasHooks: runReactQuery, - hasOrm: runOrm, + const writeResult = await writeGeneratedFiles(filesToWrite, outputRoot, [], { + pruneStaleFiles: true, }); - await writeGeneratedFiles( - [{ path: 'index.ts', content: barrelContent }], - outputRoot, - [], - ); + if (!writeResult.success) { + return { + success: false, + message: `Failed to write generated files: ${writeResult.errors?.join(', ')}`, + output: outputRoot, + errors: writeResult.errors, + }; + } + allFilesWritten.push(...(writeResult.filesWritten ?? [])); } const generators = [runReactQuery && 'React Query', runOrm && 'ORM'] diff --git a/graphql/codegen/src/core/output/writer.ts b/graphql/codegen/src/core/output/writer.ts index 9ea041587..630a047d5 100644 --- a/graphql/codegen/src/core/output/writer.ts +++ b/graphql/codegen/src/core/output/writer.ts @@ -17,6 +17,7 @@ export type { GeneratedFile }; export interface WriteResult { success: boolean; filesWritten?: string[]; + filesRemoved?: string[]; errors?: string[]; } @@ -28,6 +29,8 @@ export interface WriteOptions { showProgress?: boolean; /** Format files with oxfmt after writing (default: true) */ formatFiles?: boolean; + /** Remove stale .ts files in outputDir that are not in current file list (default: false) */ + pruneStaleFiles?: boolean; } type OxfmtFormatFn = ( @@ -85,9 +88,14 @@ export async function writeGeneratedFiles( subdirs: string[], options: WriteOptions = {}, ): Promise { - const { showProgress = true, formatFiles = true } = options; + const { + showProgress = true, + formatFiles = true, + pruneStaleFiles = false, + } = options; const errors: string[] = []; const written: string[] = []; + const removed: string[] = []; const total = files.length; const isTTY = process.stdout.isTTY; @@ -117,6 +125,25 @@ export async function writeGeneratedFiles( return { success: false, errors }; } + if (pruneStaleFiles) { + const expectedFiles = new Set( + files.map((file) => path.resolve(outputDir, file.path)), + ); + const existingTsFiles = findTsFiles(outputDir); + + for (const existingFile of existingTsFiles) { + const absolutePath = path.resolve(existingFile); + if (expectedFiles.has(absolutePath)) continue; + try { + fs.rmSync(absolutePath, { force: true }); + removed.push(absolutePath); + } catch (err) { + const message = err instanceof Error ? err.message : 'Unknown error'; + errors.push(`Failed to remove stale file ${absolutePath}: ${message}`); + } + } + } + // Get oxfmt format function if formatting is enabled const formatFn = formatFiles ? await getOxfmtFormat() : null; if (formatFiles && !formatFn && showProgress) { @@ -171,6 +198,7 @@ export async function writeGeneratedFiles( return { success: errors.length === 0, filesWritten: written, + filesRemoved: removed, errors: errors.length > 0 ? errors : undefined, }; } From 1ca2b496575f6da7f0e5d5673a28f66e00406826 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 9 Feb 2026 12:40:45 +0700 Subject: [PATCH 22/23] chore(test-codegen-app): use local graphql endpoint --- graphql/test-app/codegen.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql/test-app/codegen.config.ts b/graphql/test-app/codegen.config.ts index 9daf7e78f..a54b824d1 100644 --- a/graphql/test-app/codegen.config.ts +++ b/graphql/test-app/codegen.config.ts @@ -1,7 +1,7 @@ import { defineConfig } from '@constructive-io/graphql-codegen'; const config = defineConfig({ - endpoint: 'https://api.launchql.dev/graphql', + endpoint: 'http://api.localhost:3000/graphql', output: 'src/generated', reactQuery: true, }); From 6cde9776556888b7878eb9d64efbb0776e3d5116 Mon Sep 17 00:00:00 2001 From: yyyyaaa Date: Mon, 9 Feb 2026 12:40:57 +0700 Subject: [PATCH 23/23] test(test-codegen-app): align type tests with selection api --- graphql/test-app/src/App.tsx | 5 + .../react-query-helpers-options.type-test.ts | 139 +++++++++++------- .../react-query-orm-overloads.type-test.ts | 137 +++++++++-------- ...select-strictness-regressions.type-test.ts | 84 ++++++----- 4 files changed, 203 insertions(+), 162 deletions(-) diff --git a/graphql/test-app/src/App.tsx b/graphql/test-app/src/App.tsx index 2c4498806..14e947184 100644 --- a/graphql/test-app/src/App.tsx +++ b/graphql/test-app/src/App.tsx @@ -813,6 +813,11 @@ function Dashboard({ token, userId, onSignOut }: { token: string; userId: string const [activeDbId, setActiveDbId] = useState(null); const { mutate: signOut, isPending: signingOut } = useSignOutMutation({ + selection: { + fields: { + clientMutationId: true, + }, + }, onSuccess: () => { queryClient.clear(); onSignOut(); diff --git a/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts b/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts index 3728e01d0..005cb8e0d 100644 --- a/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts +++ b/graphql/test-app/src/type-tests/react-query-helpers-options.type-test.ts @@ -2,23 +2,25 @@ * Compile-time regression checks for helper overloads and React Query options. * * Focus: - * - fetch/prefetch overload behavior - * - optional variables + required selection.fields custom query overloads - * - default vs explicit selection result narrowing in helpers - * - allowed/disallowed React Query options on generated hooks + * - fetch/prefetch signatures for selection-enabled queries + * - optional variables flow for non-selection custom queries + * - pass-through and blocked React Query options */ import { QueryClient } from '@tanstack/react-query'; import { fetchDatabasesQuery, - fetchGetObjectAtPathQuery, + fetchStepsRequiredQuery, + fetchUserByUsernameQuery, prefetchDatabasesQuery, - prefetchGetObjectAtPathQuery, + prefetchStepsRequiredQuery, + prefetchUserByUsernameQuery, useCurrentUserQuery, useDatabasesQuery, - useGetObjectAtPathQuery, useSignInMutation, + useStepsRequiredQuery, + useUserByUsernameQuery, } from '../generated/hooks'; type Assert = T; @@ -28,9 +30,20 @@ type NotHasKey = K extends keyof T ? false : true; function helperOverloadChecks() { const queryClient = new QueryClient(); - const defaultDatabasesFetch = fetchDatabasesQuery({ selection: { first: 2 } }); - type DefaultDatabaseNode = Awaited['databases']['nodes'][number]; - type _defaultDatabaseFetchOmitsName = Assert>; + const idOnlyDatabasesFetch = fetchDatabasesQuery({ + selection: { + first: 2, + fields: { + id: true, + }, + }, + }); + type IdOnlyDatabaseNode = Awaited< + typeof idOnlyDatabasesFetch + >['databases']['nodes'][number]; + type _idOnlyDatabaseFetchOmitsName = Assert< + NotHasKey + >; const selectedDatabasesFetch = fetchDatabasesQuery({ selection: { @@ -48,42 +61,43 @@ function helperOverloadChecks() { }, }, }); - type SelectedDatabaseNode = Awaited['databases']['nodes'][number]; - type _selectedDatabaseFetchHasName = Assert>; + type SelectedDatabaseNode = Awaited< + typeof selectedDatabasesFetch + >['databases']['nodes'][number]; + type _selectedDatabaseFetchHasName = Assert< + HasKey + >; - prefetchDatabasesQuery(queryClient, { selection: { first: 2 } }); prefetchDatabasesQuery(queryClient, { selection: { first: 2, fields: { id: true, - name: true, }, }, }); - // @ts-expect-error invalid helper nested select key should be rejected - fetchDatabasesQuery({ selection: { fields: { schemas: { select: { invalidField: true } } } } }); - - const defaultGetObjectFetch = fetchGetObjectAtPathQuery({ variables: undefined }); - type DefaultGetObject = Awaited['getObjectAtPath']; - type _defaultGetObjectOmitsData = Assert>; + // @ts-expect-error selection.fields is required + fetchDatabasesQuery({ selection: { first: 2 } }); - const selectedGetObjectFetch = fetchGetObjectAtPathQuery({ - variables: undefined, + const selectedUserByUsernameFetch = fetchUserByUsernameQuery({ + variables: { username: 'dev' }, selection: { fields: { id: true, - data: true, + username: true, }, }, }); - type SelectedGetObject = Awaited['getObjectAtPath']; - type _selectedGetObjectHasData = Assert>; + type SelectedUserByUsername = Awaited< + typeof selectedUserByUsernameFetch + >['userByUsername']; + type _selectedUserByUsernameHasUsername = Assert< + HasKey + >; - prefetchGetObjectAtPathQuery(queryClient, { variables: undefined }); - prefetchGetObjectAtPathQuery(queryClient, { - variables: undefined, + prefetchUserByUsernameQuery(queryClient, { + variables: { username: 'dev' }, selection: { fields: { id: true, @@ -91,17 +105,23 @@ function helperOverloadChecks() { }, }); - // @ts-expect-error invalid custom helper select key should be rejected - prefetchGetObjectAtPathQuery(queryClient, { - variables: undefined, - selection: { fields: { invalidField: true } }, - }); - - // @ts-expect-error custom helper select overload requires explicit variables - fetchGetObjectAtPathQuery({ selection: { fields: { id: true } } }); + // @ts-expect-error selection is required for selection-enabled custom query helpers + fetchUserByUsernameQuery({ variables: { username: 'dev' } }); + // @ts-expect-error variables are required for this custom query helper + fetchUserByUsernameQuery({ selection: { fields: { id: true } } }); - // @ts-expect-error custom helper prefetch overload requires explicit variables - prefetchGetObjectAtPathQuery(queryClient, { selection: { fields: { id: true } } }); + // Optional variables flow for custom queries without selection support. + fetchStepsRequiredQuery(); + fetchStepsRequiredQuery({ variables: { vlevel: '1', first: 10 } }); + prefetchStepsRequiredQuery(queryClient); + prefetchStepsRequiredQuery(queryClient, { variables: { vlevel: '1' } }); + useStepsRequiredQuery(); + useStepsRequiredQuery({ + variables: { vlevel: '1' }, + enabled: false, + }); + // @ts-expect-error selection is not available on this custom query + useStepsRequiredQuery({ selection: { fields: { id: true } } }); } function reactQueryOptionsChecks() { @@ -158,12 +178,18 @@ function reactQueryOptionsChecks() { // @ts-expect-error transformed query data should not expose connection shape transformedDatabases.data.databases; - const transformedCurrentUser = useCurrentUserQuery({ - select: (data) => data.currentUser.id, + const currentUserResult = useCurrentUserQuery({ + selection: { + fields: { + id: true, + username: true, + }, + }, placeholderData: (previousData) => previousData ?? { currentUser: { id: 'fallback-user-id', + username: 'fallback-user', }, }, enabled: false, @@ -175,13 +201,11 @@ function reactQueryOptionsChecks() { return failureCount < 2; }, }); - const transformedCurrentUserId: string | undefined = transformedCurrentUser.data; - void transformedCurrentUserId; - // @ts-expect-error transformed query data should not expose object shape - transformedCurrentUser.data.currentUser; + const currentUserId: string | undefined = currentUserResult.data?.currentUser.id; + void currentUserId; - useGetObjectAtPathQuery({ - variables: undefined, + useUserByUsernameQuery({ + variables: { username: 'dev' }, selection: { fields: { id: true, @@ -189,13 +213,6 @@ function reactQueryOptionsChecks() { }, enabled: false, staleTime: 5_000, - gcTime: 60_000, - placeholderData: { - getObjectAtPath: { - id: 'placeholder', - }, - }, - select: (data) => data.getObjectAtPath.id, }); // @ts-expect-error unknown React Query option should be rejected @@ -204,9 +221,9 @@ function reactQueryOptionsChecks() { // @ts-expect-error queryKey is owned by generated hooks useDatabasesQuery({ selection: { fields: { id: true } }, queryKey: ['override'] as const }); - // @ts-expect-error queryFn is owned by generated hooks useDatabasesQuery({ selection: { fields: { id: true } }, + // @ts-expect-error queryFn is owned by generated hooks queryFn: async () => ({ databases: { @@ -224,6 +241,14 @@ function reactQueryOptionsChecks() { selection: { fields: { clientMutationId: true, + result: { + select: { + accessToken: true, + isVerified: true, + totpEnabled: true, + userId: true, + }, + }, }, }, retry: 1, @@ -243,16 +268,16 @@ function reactQueryOptionsChecks() { void context; }, onSuccess: (data, variables) => { - const clientMutationId = data.signIn.clientMutationId; + const accessToken = data.signIn.result.accessToken; const email = variables.input.email; - void clientMutationId; + void accessToken; void email; }, }); - // @ts-expect-error mutationFn is owned by generated hooks useSignInMutation({ selection: { fields: { clientMutationId: true } }, + // @ts-expect-error mutationFn is owned by generated hooks mutationFn: async () => ({ signIn: { diff --git a/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts b/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts index c44f21281..eea13ad36 100644 --- a/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts +++ b/graphql/test-app/src/type-tests/react-query-orm-overloads.type-test.ts @@ -1,14 +1,13 @@ /** * Compile-time regression checks for React Query + ORM output modes. * - * These checks focus on overload behavior introduced to recover contextual - * typing/autocomplete for nested select objects. + * These checks focus on overload behavior and nested select contextual typing. */ import { useCurrentUserQuery, - useGetObjectAtPathQuery, useSignInMutation, + useUserByUsernameQuery, useUserQuery, useUsersQuery, } from '../generated/hooks'; @@ -29,17 +28,14 @@ function hookTypeChecks() { id: true, username: true, }, - } + }, }); const maybeUsername = currentUser.data?.currentUser.username; void maybeUsername; - const defaultUser = useUserQuery({ id: '00000000-0000-0000-0000-000000000000' }); - const defaultUserId = defaultUser.data?.user?.id; - void defaultUserId; - // @ts-expect-error default select for useUserQuery should not expose username - defaultUser.data?.user?.username; + // @ts-expect-error selection is required + useUserQuery({ id: '00000000-0000-0000-0000-000000000000' }); const selectedUser = useUserQuery({ id: '00000000-0000-0000-0000-000000000000', @@ -57,12 +53,13 @@ function hookTypeChecks() { selection: { fields: { id: true, - databasesByOwnerId: { + username: true, + ownedDatabases: { first: 2, select: { id: true, schemaName: true, - name: true + name: true, }, }, }, @@ -71,25 +68,26 @@ function hookTypeChecks() { }, }); - const nestedSchema = users.data?.users.nodes[0]?.databasesByOwnerId?.nodes[0]?.schemaName; + const nestedSchema = users.data?.users.nodes[0]?.ownedDatabases?.nodes[0]?.schemaName; void nestedSchema; - // Optional variables + required select args overload (custom query case) - useGetObjectAtPathQuery({ - variables: undefined, + useUserByUsernameQuery({ + variables: { username: 'dev' }, selection: { fields: { id: true, - data: true, + username: true, }, }, }); - const defaultSignIn = useSignInMutation(); - const defaultSignInClientMutationId = defaultSignIn.data?.signIn.clientMutationId; - void defaultSignInClientMutationId; - // @ts-expect-error default signIn select should not expose result - defaultSignIn.data?.signIn.result; + // @ts-expect-error variables are required for this custom query + useUserByUsernameQuery({ + selection: { fields: { id: true } }, + }); + + // @ts-expect-error selection is required for custom mutation hooks + useSignInMutation(); useSignInMutation({ selection: { @@ -129,7 +127,7 @@ function hookTypeChecks() { useUsersQuery({ selection: { fields: invalidUsersSelect } }); const invalidNestedUsersSelect = { - databasesByOwnerId: { + ownedDatabases: { select: { id: true, doesNotExist: true, @@ -156,47 +154,29 @@ function hookTypeChecks() { }; // @ts-expect-error invalid mutation variable select key should be rejected useSignInMutation({ selection: { fields: invalidSignInSelect } }); - - // @ts-expect-error invalid nested select key should be rejected - useUsersQuery({ - selection: { - fields: { - databasesByOwnerId: { - select: { - doesNotExist: true, - }, - }, - }, - }, - }); - - // @ts-expect-error invalid custom query select key should be rejected - useGetObjectAtPathQuery({ - variables: undefined, - selection: { fields: { nope: true } }, - }); } async function ormModelTypeChecks() { - const defaultBuilder = ormClient.user.findOne({ + // @ts-expect-error findOne requires explicit select + ormClient.user.findOne({ id: '00000000-0000-0000-0000-000000000000', }); - type DefaultUser = Awaited>['user']; - type _defaultOmitsUsername = Assert, 'username'>>; - - const defaultSelected = await defaultBuilder.unwrapOr({ user: null }); - if (defaultSelected.user) { - const id: string = defaultSelected.user.id; - void id; - } + const idOnlyBuilder = ormClient.user.findOne({ + id: '00000000-0000-0000-0000-000000000000', + select: { + id: true, + }, + }); + type IdOnlyUser = Awaited>['user']; + type _idOnlyOmitsUsername = Assert, 'username'>>; const explicitlySelected = ormClient.user.findOne({ id: '00000000-0000-0000-0000-000000000000', select: { id: true, username: true, - databasesByOwnerId: { + ownedDatabases: { first: 1, select: { id: true, @@ -214,7 +194,7 @@ async function ormModelTypeChecks() { select: { id: true, username: true, - databasesByOwnerId: { + ownedDatabases: { first: 1, select: { id: true, @@ -228,7 +208,7 @@ async function ormModelTypeChecks() { select: { id: true, username: true, - databasesByOwnerId: { + ownedDatabases: { first: 1, select: { id: true, @@ -236,25 +216,52 @@ async function ormModelTypeChecks() { }, }, }); + // @ts-expect-error custom ORM query requires options with select + ormClient.query.currentUser(); - ormClient.query.getObjectAtPath( + ormClient.query.userByUsername( { - dbId: '00000000-0000-0000-0000-000000000000', - path: ['root'], - refname: 'main', + username: 'dev', }, { select: { id: true, }, - } + }, ); + // @ts-expect-error custom ORM query requires options with select + ormClient.query.userByUsername({ + username: 'dev', + }); - // @ts-expect-error invalid model select key should be rejected - ormClient.user.findMany({ select: { invalidField: true } }); - - // @ts-expect-error invalid custom query select key should be rejected - ormClient.query.currentUser({ select: { invalidField: true } }); + ormClient.mutation.signIn( + { + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }, + { + select: { + clientMutationId: true, + result: { + select: { + accessToken: true, + userId: true, + }, + }, + }, + }, + ); + // @ts-expect-error custom ORM mutation requires options with select + ormClient.mutation.signIn({ + input: { + email: 'dev@example.com', + password: 'password', + rememberMe: true, + }, + }); const invalidModelSelect = { id: true, @@ -264,7 +271,7 @@ async function ormModelTypeChecks() { ormClient.user.findMany({ select: invalidModelSelect }); const invalidNestedModelSelect = { - databasesByOwnerId: { + ownedDatabases: { select: { id: true, invalidField: true, @@ -298,7 +305,7 @@ async function ormModelTypeChecks() { }, }, // @ts-expect-error invalid custom mutation variable select key should be rejected - { select: invalidCustomMutationSelect } + { select: invalidCustomMutationSelect }, ); } diff --git a/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts b/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts index 9c4e6a305..926dbd653 100644 --- a/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts +++ b/graphql/test-app/src/type-tests/select-strictness-regressions.type-test.ts @@ -1,17 +1,8 @@ /** * Additional compile-time regression checks focused on strict selection behavior. - * - * These tests target edge cases around: - * - nested relation select options (filter/orderBy) - * - invalid relation option keys - * - default vs explicit selection result narrowing - * - custom ORM operation option contracts */ -import { - useDatabasesQuery, - useSignInMutation, -} from '../generated/hooks'; +import { useDatabasesQuery, useSignInMutation } from '../generated/hooks'; import { createClient } from '../generated/orm'; type Assert = T; @@ -23,11 +14,8 @@ const ormClient = createClient({ }); function hookStrictnessChecks() { - const defaultDatabases = useDatabasesQuery({ selection: { first: 10 } }); - const defaultDatabaseId = defaultDatabases.data?.databases.nodes[0]?.id; - void defaultDatabaseId; - // @ts-expect-error default select for useDatabasesQuery should not expose name - defaultDatabases.data?.databases.nodes[0]?.name; + // @ts-expect-error selection.fields is required + useDatabasesQuery({ selection: { first: 10 } }); const selectedDatabases = useDatabasesQuery({ selection: { @@ -63,8 +51,8 @@ function hookStrictnessChecks() { void selectedDatabaseName; void selectedNestedSchema; - // @ts-expect-error relation select options should use filter, not where useDatabasesQuery({ + // @ts-expect-error relation select options should use filter, not where selection: { fields: { schemas: { @@ -79,12 +67,12 @@ function hookStrictnessChecks() { }, }); - // @ts-expect-error invalid field inside nested relation filter should be rejected useDatabasesQuery({ selection: { fields: { schemas: { filter: { + // @ts-expect-error invalid field inside nested relation filter should be rejected invalidField: { equalTo: 'x' }, }, select: { @@ -95,11 +83,11 @@ function hookStrictnessChecks() { }, }); - // @ts-expect-error invalid orderBy literal should be rejected useDatabasesQuery({ selection: { fields: { schemas: { + // @ts-expect-error invalid orderBy literal should be rejected orderBy: ['INVALID_ASC'], select: { id: true, @@ -109,6 +97,24 @@ function hookStrictnessChecks() { }, }); + const invalidDatabaseSelect = { + id: true, + invalidField: true, + }; + // @ts-expect-error invalid variable select field should be rejected + useDatabasesQuery({ selection: { fields: invalidDatabaseSelect } }); + + const invalidNestedDatabaseSelect = { + schemas: { + select: { + id: true, + invalidField: true, + }, + }, + }; + // @ts-expect-error invalid nested variable select field should be rejected + useDatabasesQuery({ selection: { fields: invalidNestedDatabaseSelect } }); + const validSignInSelect = { clientMutationId: true, result: { @@ -134,33 +140,33 @@ function hookStrictnessChecks() { } async function ormStrictnessChecks() { - const defaultCurrentUser = ormClient.query.currentUser(); - type DefaultCurrentUser = Awaited>['currentUser']; - type _defaultCurrentUserOmitsUsername = Assert>; - const selectedCurrentUser = ormClient.query.currentUser({ select: { id: true, - username: true, }, }); type SelectedCurrentUser = Awaited>['currentUser']; - type _selectedCurrentUserHasUsername = Assert>; - - // @ts-expect-error custom ORM query options object requires select when provided - ormClient.query.currentUser({}); + type _selectedCurrentUserOmitsUsername = Assert< + NotHasKey + >; - const defaultSignIn = ormClient.mutation.signIn({ - input: { - email: 'dev@example.com', - password: 'password', - rememberMe: true, + const selectedCurrentUserWithUsername = ormClient.query.currentUser({ + select: { + id: true, + username: true, }, }); - type DefaultSignIn = Awaited>['signIn']; - type _defaultSignInOmitsResult = Assert>; + type SelectedCurrentUserWithUsername = Awaited< + ReturnType + >['currentUser']; + type _selectedCurrentUserHasUsername = Assert< + HasKey + >; + + // @ts-expect-error custom ORM query requires options with select + ormClient.query.currentUser(); - const selectedSignIn = ormClient.mutation.signIn( + ormClient.mutation.signIn( { input: { email: 'dev@example.com', @@ -178,10 +184,8 @@ async function ormStrictnessChecks() { }, }, }, - } + }, ); - type SelectedSignIn = Awaited>['signIn']; - type _selectedSignInHasResult = Assert>; ormClient.mutation.signIn( { @@ -191,8 +195,8 @@ async function ormStrictnessChecks() { rememberMe: true, }, }, - // @ts-expect-error custom ORM mutation options object requires select when provided - {} + // @ts-expect-error custom ORM mutation options require select + {}, ); }