diff --git a/.changeset/slow-buses-decide.md b/.changeset/slow-buses-decide.md new file mode 100644 index 0000000..d6e3b28 --- /dev/null +++ b/.changeset/slow-buses-decide.md @@ -0,0 +1,5 @@ +--- +"orchid-graphql": minor +--- + +Basic type check for `graph.resolve()` query argument. Use latest orchid-orm types. diff --git a/packages/orchid/package.json b/packages/orchid/package.json index 21a6f17..cad9416 100644 --- a/packages/orchid/package.json +++ b/packages/orchid/package.json @@ -23,7 +23,7 @@ "scripts": { "dev": "vite-node --watch playground/readme.sample.ts", "build": "tsup", - "test": "vitest run && tsc --noEmit", + "test": "vitest run && tsc --noEmit && tstyche", "prepublishOnly": "pnpm build" }, "dependencies": { @@ -31,7 +31,7 @@ }, "peerDependencies": { "graphql": "^16", - "orchid-orm": "^1.22.0" + "orchid-orm": "^1.31.5" }, "devDependencies": { "@apollo/server": "^4.7.1", @@ -39,8 +39,9 @@ "graphql-orm": "workspace:*", "graphql-request": "^6.0.0", "graphql-tag": "^2.12.6", - "orchid-orm": "^1.30.0", + "orchid-orm": "^1.31.5", "tsconfig-vite-node": "^1.1.2", + "tstyche": "^2.0.0", "tsup": "^8.1.0", "vite": "^5.3.1", "vite-node": "^1.6.0", diff --git a/packages/orchid/src/orm/orm.ts b/packages/orchid/src/orm/orm.ts index a4951a4..935d3b8 100644 --- a/packages/orchid/src/orm/orm.ts +++ b/packages/orchid/src/orm/orm.ts @@ -1,9 +1,9 @@ import { OrmAdapter } from "graphql-orm" -import type { Query } from "orchid-orm" -import { DbTable, raw } from "orchid-orm" +import type { Query, Table } from "orchid-orm" +import { raw } from "orchid-orm" export type OrchidOrm = OrmAdapter< - DbTable, + Table, Query, // TODO: Type as QueryTransform once it's published. Pick, "then" | "catch"> @@ -17,7 +17,7 @@ export const orm: OrchidOrm = { // Reflection get_table_table(table) { - return table.table + return table.table! }, get_table_relations(table) { @@ -47,9 +47,10 @@ export const orm: OrchidOrm = { }, select_relation(query, { relation, as, modify }) { + // as any casts needed in orchid-orm 1.31+ return query.select({ - [as]: (q) => modify((q as any)[relation]), - }) + [as]: (q: any) => modify(q[relation]), + } as any) }, // Find diff --git a/packages/orchid/src/resolvers/graph.ts b/packages/orchid/src/resolvers/graph.ts index f1a79f2..3bafe74 100644 --- a/packages/orchid/src/resolvers/graph.ts +++ b/packages/orchid/src/resolvers/graph.ts @@ -1,4 +1,10 @@ -import { GraphResolver, GraphResolverOptions, TableResolver } from "graphql-orm" +import { + GraphResolveOptions, + GraphResolver, + GraphResolverOptions, + TableResolver, +} from "graphql-orm" +import { Query } from "orchid-orm" import { OrchidOrm, orm } from "../orm/orm" @@ -6,5 +12,29 @@ export function createGraphResolver( types: Record>, options?: GraphResolverOptions, ) { - return new GraphResolver(orm, types, options) + return new OrchidGraphResolver(types, options) +} + +class OrchidGraphResolver extends GraphResolver { + constructor( + public readonly types: Record>, + public readonly options: GraphResolverOptions = {}, + ) { + super(orm, types, options) + } + + resolve( + query: Query & { + returnType: unknown extends T + ? any + : T extends unknown[] + ? undefined | "all" + : T extends undefined + ? "one" + : "oneOrThrow" + }, + options: GraphResolveOptions, + ): Promise { + return super.resolve(query, options) + } } diff --git a/packages/orchid/src/resolvers/table.ts b/packages/orchid/src/resolvers/table.ts index 7d4115f..6f0d41f 100644 --- a/packages/orchid/src/resolvers/table.ts +++ b/packages/orchid/src/resolvers/table.ts @@ -1,11 +1,11 @@ import { TableResolver, TableResolverOptions } from "graphql-orm" -import { DbTable, Table } from "orchid-orm" +import { Table } from "orchid-orm" import { OrchidOrm, orm } from "../orm/orm" -export function defineTableResolver( - table: DbTable, +export function defineTableResolver( + table: Table, options: TableResolverOptions = {}, ) { - return new TableResolver(orm, table as unknown as DbTable, options) + return new TableResolver(orm, table as any, options) } diff --git a/packages/orchid/tsconfig.json b/packages/orchid/tsconfig.json index 490c781..3e87f24 100644 --- a/packages/orchid/tsconfig.json +++ b/packages/orchid/tsconfig.json @@ -5,5 +5,6 @@ "orchid-graphql": ["./src"], "graphql-orm": ["../base/src"] } - } + }, + "exclude": ["./typetests"] } diff --git a/packages/orchid/typetests/main.tst.ts b/packages/orchid/typetests/main.tst.ts new file mode 100644 index 0000000..5cd09d9 --- /dev/null +++ b/packages/orchid/typetests/main.tst.ts @@ -0,0 +1,66 @@ +import * as r from "orchid-graphql" +import { createBaseTable, orchidORM } from "orchid-orm" +import { expect, test } from "tstyche" + +const BaseTable = createBaseTable() + +class PostTable extends BaseTable { + readonly table = "post" + + columns = this.setColumns((t) => ({ + id: t.serial().primaryKey(), + text: t.text(), + })) +} + +const db = orchidORM( + {}, + { + post: PostTable, + }, +) + +// Similar to db.post, but not necessary so. +interface Post { + id: number + text: number + excerpt: string + tags: string[] +} + +const graph = r.graph({ + Post: r.table(db.post), +}) + +const ctx = 0 as any + +test("valid query type", () => { + expect(graph.resolve(db.post, ctx)).type.toBe>() + expect(graph.resolve(db.post.all(), ctx)).type.toBe>() + expect(graph.resolve(db.post.where({ text: "foo" }), ctx)).type.toBe< + Promise + >() + expect(graph.resolve(db.post.find(1), ctx)).type.toBe>() + expect( + graph.resolve(db.post.findOptional(1), ctx), + ).type.toBe>() +}) + +test("invalid query type", () => { + expect(graph.resolve(db.post.all(), ctx)).type.toRaiseError() + expect(graph.resolve(db.post.all(), ctx)).type.toRaiseError() + expect(graph.resolve(db.post.count(), ctx)).type.toRaiseError() + expect(graph.resolve(db.post.get("text"), ctx)).type.toRaiseError() + expect(graph.resolve(db.post.findOptional(1), ctx)).type.toRaiseError() + expect(graph.resolve(db.post.find(1), ctx)).type.toRaiseError() +}) + +test("query type inference", () => { + ;((): Promise => { + return graph.resolve(db.post.find(1), ctx) + })() + ;((): Promise => { + // @ts-expect-error find(1) query should not be allowed for Post[] return type + return graph.resolve(db.post.find(1), ctx) + })() +}) diff --git a/packages/orchid/typetests/tsconfig.json b/packages/orchid/typetests/tsconfig.json new file mode 100644 index 0000000..18de9c5 --- /dev/null +++ b/packages/orchid/typetests/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../tsconfig.json", + "include": ["./"], + "exclude": [] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76f8f78..6c5fa54 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,11 +123,14 @@ importers: specifier: ^2.12.6 version: 2.12.6(graphql@16.6.0) orchid-orm: - specifier: ^1.30.0 - version: 1.30.0(typescript@5.4.5) + specifier: ^1.31.5 + version: 1.31.5(typescript@5.4.5) tsconfig-vite-node: specifier: ^1.1.2 version: 1.1.2 + tstyche: + specifier: ^2.0.0 + version: 2.0.0(typescript@5.4.5) tsup: specifier: ^8.1.0 version: 8.1.0(typescript@5.4.5) @@ -4097,20 +4100,20 @@ packages: type-check: 0.4.0 dev: true - /orchid-core@0.17.0: - resolution: {integrity: sha512-7/iIlyALjNXI43x8e8enQN/hwo02BfkoQdYbXADksc5cFQTd6DKI/BG9LdPUb43ZtZzbj75rtGdtmtA70DBEMg==} + /orchid-core@0.18.2: + resolution: {integrity: sha512-FP9lT/1nbSOxn7cE3inEthwS4fxVhTcpaXpi/LjpWIut+p00V3Edzc2h8JZQRLeW+IGLBlf+pNof/2gAuGFG/g==} dev: true - /orchid-orm@1.30.0(typescript@5.4.5): - resolution: {integrity: sha512-s2vT2gEaEzYbeIvXB0H0mtqYisVAgxRPZNpSN+mwG9ZpML3LvJnaZSTEiGSirSB7bbIJoee4UtU/kUymt9dNDg==} + /orchid-orm@1.31.5(typescript@5.4.5): + resolution: {integrity: sha512-iIb1Vybf5nlbt1KRIuLf7wJFOJwttjXLBkvgvd6oQFlHI1dJLk3Qi9GaSZFnQ26+S0I1jwnJ3SSn6LkWcbU2Xg==} peerDependencies: typescript: '*' dependencies: inflection: 2.0.1 - orchid-core: 0.17.0 - pqb: 0.34.0 + orchid-core: 0.18.2 + pqb: 0.35.5 prompts: 2.4.2 - rake-db: 2.21.0 + rake-db: 2.21.6 typescript: 5.4.5 transitivePeerDependencies: - pg-native @@ -4452,11 +4455,11 @@ packages: resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} dev: true - /pqb@0.34.0: - resolution: {integrity: sha512-/DOrlWseOO3umFhBvNskaGux4gvtu3eHTRNuCe5q67r4PpC7cnm2SxS6GGZBGVHe8BlBruJTF+LT2kpDGCkRMQ==} + /pqb@0.35.5: + resolution: {integrity: sha512-gpAF/T93xozw0EdoErzNOkV0bZi6PT6OwuoM55jI8DknEwiNkyft5wYpWt2rRcQdPLteQfsfcaMbu0TtAZN62A==} dependencies: '@types/pg': 8.10.1 - orchid-core: 0.17.0 + orchid-core: 0.18.2 pg: 8.11.0 transitivePeerDependencies: - pg-native @@ -4567,11 +4570,11 @@ packages: engines: {node: '>=8'} dev: true - /rake-db@2.21.0: - resolution: {integrity: sha512-YNKJjhXhBVCKS6F6pGIKAkID44M5wy4lS61DUf02MW3MpKbUNTB48boX2hXuENKZRLGK/ETiXw8iqCl3TKeuwg==} + /rake-db@2.21.6: + resolution: {integrity: sha512-MhPxpPq7y5kbh+Z9xEN1qxGmJmRKBTpdwt5O92tPFBfbhf76LW8TXXjN/nP9TZa/kwi7p9cN1W1Vk57BdzSzNQ==} dependencies: - orchid-core: 0.17.0 - pqb: 0.34.0 + orchid-core: 0.18.2 + pqb: 0.35.5 prompts: 2.4.2 transitivePeerDependencies: - pg-native @@ -5343,6 +5346,19 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + /tstyche@2.0.0(typescript@5.4.5): + resolution: {integrity: sha512-1LUCZEmMLRL7P0qDNtjx8oEEpU4qVUNggpsitl3XSGyuorbSNfees+EmMDC0VZ9FuClD0Far262U9oAT6Vz83Q==} + engines: {node: '>=16.14'} + hasBin: true + peerDependencies: + typescript: 4.x || 5.x + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.4.5 + dev: true + /tsup@8.1.0(typescript@5.4.5): resolution: {integrity: sha512-UFdfCAXukax+U6KzeTNO2kAARHcWxmKsnvSPXUcfA1D+kU05XDccCrkffCQpFaWDsZfV0jMyTsxU39VfCp6EOg==} engines: {node: '>=18'}