@btravstack/pothos 0.0.0-stage → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Benoit TRAVERS
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,32 @@
1
- # Temporary Holding Version
1
+ # @btravstack/pothos
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [Pothos](https://pothos-graphql.dev) fields that call
4
+ [oRPC](https://orpc.dev) procedures, for a GraphQL gateway in front of
5
+ contract-first services. The default export is the plugin, listed after
6
+ `@unthrown/pothos`'s:
7
+ `plugins: [RelayPlugin, ErrorsPlugin, UnthrownPlugin, OrpcPlugin]`.
8
+
9
+ - `t.procedureField(procedure, { type, call, input, output })` validates the
10
+ field's input through the procedure's own contract before calling it,
11
+ decodes the answer with `output`, and answers each error the procedure
12
+ declares as the GraphQL error class the builder's `orpc: { refusals }`
13
+ registers for its code — a member of the field's result union. A registry
14
+ that misses a code `call` can fail with does not compile.
15
+ - `t.procedureConnection(procedure, { …, connection })` is the same for a
16
+ Relay connection, with the decoded page folded by `connection`.
17
+ - `UNAUTHORIZED` and `FORBIDDEN` are answered as GraphQL's own error, with
18
+ `extensions.code`, whether the procedure declares them or not.
19
+ - `loadProcedure` and `procedureErrors` are the same answer through a
20
+ DataLoader; `contractCall`, `contractInput` and `contractOutput` are the
21
+ contract half on its own.
22
+
23
+ Every dependency is a peer: Pothos plugins and `graphql` each need one copy.
24
+
25
+ ```sh
26
+ pnpm add @btravstack/pothos @pothos/core @pothos/plugin-errors @pothos/plugin-relay \
27
+ @unthrown/pothos@^0.1.0 graphql unthrown zod \
28
+ @orpc/client@2.0.0-beta.28 @orpc/contract@2.0.0-beta.28
29
+ ```
30
+
31
+ See
32
+ [Resolve GraphQL fields from oRPC procedures](https://btravstack.github.io/btravstack/how-to/resolve-graphql-fields-from-orpc).
@@ -0,0 +1,159 @@
1
+ import { BasePlugin, FieldKind, FieldNullability, InputFieldMap, InputShapeFromFields, ObjectRef, OutputType, SchemaTypes, ShapeFromTypeParam, TypeParam } from "@pothos/core";
2
+ import { ResultConnectionOptions } from "@unthrown/pothos/relay";
3
+ import { CaseOf, Caseable, Refusals, ResultFieldOptions } from "@unthrown/pothos";
4
+ import { AsyncResult } from "unthrown";
5
+ import { z } from "zod";
6
+ import { AnySchema, ErrorMap, ProcedureContract } from "@orpc/contract";
7
+ import { ConnectionShapeForType, DefaultConnectionArguments } from "@pothos/plugin-relay";
8
+ //#region src/plugin.d.ts
9
+ declare const pluginName = "orpc";
10
+ declare class PothosOrpcPlugin<Types extends SchemaTypes> extends BasePlugin<Types> {}
11
+ declare global {
12
+ export namespace PothosSchemaTypes {
13
+ interface Plugins<Types extends SchemaTypes> {
14
+ orpc: PothosOrpcPlugin<Types>;
15
+ }
16
+ }
17
+ }
18
+ //#endregion
19
+ //#region src/contract.d.ts
20
+ /**
21
+ * The one Zod schema a procedure's contract declares for its input, typed as
22
+ * declared — to derive a GraphQL argument's shape from it (`.shape`, a
23
+ * `z.preprocess` around it) when the schema is built.
24
+ *
25
+ * A procedure whose contract holds anything but exactly one Zod input schema
26
+ * is a mistake in the schema's source, not a request's: it throws, once, as
27
+ * the schema is built.
28
+ */
29
+ export declare const contractInput: <Input extends z.ZodType, Output extends AnySchema, Errors extends ErrorMap>(procedure: ProcedureContract<Input, Output, Errors>) => Input;
30
+ declare const InvalidInput_base: import("unthrown").TaggedErrorConstructor<"InvalidInput">;
31
+ /**
32
+ * An input the procedure's contract refuses, caught before the procedure is
33
+ * called: each of the contract's issues, with its message and path.
34
+ */
35
+ export declare class InvalidInput extends InvalidInput_base<{
36
+ readonly issues: readonly {
37
+ readonly message: string;
38
+ readonly path: readonly PropertyKey[];
39
+ }[];
40
+ }> {
41
+ message: string;
42
+ }
43
+ /**
44
+ * `call`, handed the input as the procedure's contract parses it and encodes
45
+ * it back for the wire — its defaults applied, its codecs run, asynchronous
46
+ * refinements included — or `InvalidInput` without calling it. A schema with a
47
+ * transform cannot be encoded back: `call` gets the input as given, which the
48
+ * contract admitted. A contract whose input is not one Zod
49
+ * schema is a defect.
50
+ */
51
+ export declare const contractCall: <Schema extends z.ZodType, OutputSchema extends AnySchema, Errors extends ErrorMap, Output, Failure>(procedure: ProcedureContract<Schema, OutputSchema, Errors>, input: unknown, call: (validated: z.input<Schema>) => AsyncResult<Output, Failure>) => AsyncResult<Output, Failure | InvalidInput>;
52
+ /**
53
+ * A procedure's answer decoded by `schema` — the shape the GraphQL field
54
+ * resolves, codecs run. An answer the schema refuses is a defect: the service
55
+ * broke its own contract.
56
+ */
57
+ export declare const contractOutput: <Schema extends z.ZodType>(schema: Schema, value: unknown) => AsyncResult<z.infer<Schema>, never>;
58
+ //#endregion
59
+ //#region src/procedure-field.d.ts
60
+ type ErrorClass = new (failure: never) => Error;
61
+ type Access = {
62
+ readonly code: "UNAUTHORIZED" | "FORBIDDEN";
63
+ };
64
+ type Procedure<Schema extends z.ZodType> = ProcedureContract<Schema, AnySchema, ErrorMap>;
65
+ type AnyProcedure = Procedure<z.ZodType>;
66
+ type Call<Context, Input, Failure> = (context: Context) => (input: Input) => AsyncResult<unknown, Failure>;
67
+ type Covered<Registry, Failure> = Registry extends Refusals<Failure> ? {} : {
68
+ readonly uncovered: Exclude<CaseOf<Failure>, keyof Registry>;
69
+ };
70
+ /**
71
+ * `t.procedureField`'s options: `resultField`'s, with `call`, `input` and
72
+ * `output` in place of `resolve`, and the builder's `orpc.refusals` in place
73
+ * of `refusals`. The registry must cover every error `call` can return but
74
+ * the access refusals, and `InvalidInput`; an uncovered case is a compile
75
+ * error naming it. A declared error `call` handles itself needs no entry.
76
+ */
77
+ type ProcedureFieldOptions<Types extends SchemaTypes, ParentShape, Type extends TypeParam<Types>, Nullable extends FieldNullability<Type>, Args extends InputFieldMap, Kind extends FieldKind, Schema extends z.ZodType, Failure> = Omit<ResultFieldOptions<Types, ParentShape, Type, Nullable, Args, Kind, Failure>, "refusals" | "resolve"> & {
78
+ readonly call: Call<Types["Context"], z.input<Schema>, Failure>;
79
+ readonly input: (args: InputShapeFromFields<Args>, parent: ParentShape) => NoInfer<z.input<Schema>>;
80
+ readonly output: z.ZodType<ShapeFromTypeParam<Types, Type, false>>;
81
+ readonly tap?: (value: ShapeFromTypeParam<Types, Type, false>, context: Types["Context"]) => void;
82
+ } & Covered<Types["Refusals"], Exclude<Failure, Access> | InvalidInput>;
83
+ /**
84
+ * `t.procedureConnection`'s options: `resultConnection`'s, with `call`,
85
+ * `input` and `output` in place of `resolve`, `connection` folding the
86
+ * decoded page into the connection's shape, and an optional `connectionType`
87
+ * for fields beside its edges and page info.
88
+ */
89
+ type ProcedureConnectionOptions<Types extends SchemaTypes, ParentShape, Type extends OutputType<Types>, Nullable extends boolean, EdgeNullability extends FieldNullability<[unknown]>, NodeNullability extends boolean, Args extends InputFieldMap, Kind extends FieldKind, Schema extends z.ZodType, Page, Failure, Connection extends ConnectionShapeForType<Types, Type, false, EdgeNullability, NodeNullability> = ConnectionShapeForType<Types, Type, false, EdgeNullability, NodeNullability>> = Omit<ResultConnectionOptions<Types, ParentShape, Type, Nullable, EdgeNullability, NodeNullability, Args, Kind, Failure, Connection>, "refusals" | "resolve"> & {
90
+ readonly call: Call<Types["Context"], z.input<Schema>, Failure>;
91
+ readonly input: (args: DefaultConnectionArguments & InputShapeFromFields<Args>, parent: ParentShape) => NoInfer<z.input<Schema>>;
92
+ readonly output: z.ZodType<Page>;
93
+ readonly connectionType?: ObjectRef<Types, Connection>;
94
+ readonly connection: (page: Page, args: DefaultConnectionArguments & InputShapeFromFields<Args>) => Connection;
95
+ } & Covered<Types["Refusals"], Exclude<Failure, Access> | InvalidInput>;
96
+ declare global {
97
+ export namespace PothosSchemaTypes {
98
+ interface UserSchemaTypes {
99
+ Refusals: Readonly<Record<string, ErrorClass>>;
100
+ }
101
+ interface ExtendDefaultTypes<PartialTypes extends Partial<UserSchemaTypes>> {
102
+ Refusals: PartialTypes["Refusals"] & {};
103
+ }
104
+ interface SchemaBuilderOptions<Types extends SchemaTypes> {
105
+ orpc?: {
106
+ readonly refusals: Types["Refusals"];
107
+ };
108
+ }
109
+ interface RootFieldBuilder<Types extends SchemaTypes, ParentShape, Kind extends FieldKind = FieldKind> {
110
+ /**
111
+ * A field answering an oRPC procedure: `input` maps the field's
112
+ * arguments to the procedure's input, the contract validates it before
113
+ * `call` runs, and `output` decodes the answer. A declared error is
114
+ * answered as its registered refusal, a member of the field's result
115
+ * union; the service refusing access is GraphQL's own error, with
116
+ * `extensions.code`.
117
+ */
118
+ procedureField: <Type extends TypeParam<Types>, Schema extends z.ZodType, Failure, Nullable extends FieldNullability<Type> = Types["DefaultFieldNullability"], Args extends InputFieldMap = {}>(procedure: Procedure<Schema>, options: ProcedureFieldOptions<Types, ParentShape, Type, Nullable, Args, Kind, Schema, Failure>) => FieldRef<Types, ShapeFromTypeParam<Types, Type, Nullable>, Kind>;
119
+ /**
120
+ * A Relay connection answering an oRPC procedure: `procedureField`'s
121
+ * contract, with the decoded page folded by `connection`.
122
+ */
123
+ procedureConnection: <Type extends OutputType<Types>, Schema extends z.ZodType, Page, Failure, Nullable extends boolean, Args extends InputFieldMap = {}, EdgeNullability extends FieldNullability<[unknown]> = Types["DefaultEdgesNullability"], NodeNullability extends boolean = Types["DefaultNodeNullability"], Connection extends ConnectionShapeForType<Types, Type, false, EdgeNullability, NodeNullability> = ConnectionShapeForType<Types, Type, false, EdgeNullability, NodeNullability>>(procedure: Procedure<Schema>, options: ProcedureConnectionOptions<Types, ParentShape, Type, Nullable, EdgeNullability, NodeNullability, Args, Kind, Schema, Page, Failure, Connection>) => FieldRef<Types, ConnectionShapeForType<Types, Type, Nullable, EdgeNullability, NodeNullability>>;
124
+ }
125
+ }
126
+ }
127
+ /**
128
+ * The refusals a procedure can answer, read from `registry`: one per error
129
+ * code its contract declares, and `InvalidInput`. A code the registry lacks is
130
+ * left out, and so are `UNAUTHORIZED` and `FORBIDDEN`, answered as GraphQL's
131
+ * own error.
132
+ */
133
+ export declare const refusalsOf: <Failure>(registry: Readonly<Record<string, ErrorClass>>, procedure: AnyProcedure) => Refusals<Failure>;
134
+ /**
135
+ * `call` handed the input the procedure's contract accepts, its answer decoded
136
+ * by `output`. `UNAUTHORIZED` and `FORBIDDEN`, declared or not, leave the
137
+ * error channel: they travel as a `GraphQLError` carrying the code in
138
+ * `extensions.code`, an error of the operation, never a refusal of the field.
139
+ */
140
+ export declare const callProcedure: <Schema extends z.ZodType, Output, Failure>(procedure: Procedure<Schema>, input: unknown, call: (input: z.input<Schema>) => AsyncResult<unknown, Failure>, output: z.ZodType<Output>) => AsyncResult<Output, Exclude<Failure, Access> | InvalidInput>;
141
+ /**
142
+ * What a DataLoader answers for one key read through `procedure`: the decoded
143
+ * value, or an `Error` — the registered refusal for a declared error, the
144
+ * defect otherwise — so one key's failure never fails the batch.
145
+ *
146
+ * **It answers a `Promise`, and that is the boundary**: a DataLoader's batch
147
+ * function answers a value or an `Error` per key.
148
+ */
149
+ export declare const loadProcedure: <Schema extends z.ZodType, Output, Failure extends Caseable>(registry: Readonly<Record<string, ErrorClass>>, procedure: Procedure<Schema>, call: (input: z.input<Schema>) => AsyncResult<unknown, Failure>, input: unknown, output: z.ZodType<Output>) => Promise<Output | Error>;
150
+ /**
151
+ * The `errors` option of a field that reads `procedure` through
152
+ * `loadProcedure`: the registered refusals it can answer, as the field's
153
+ * result union.
154
+ */
155
+ export declare const procedureErrors: (registry: Readonly<Record<string, ErrorClass>>, procedure: AnyProcedure) => {
156
+ readonly types: (new (...args: never[]) => Error)[];
157
+ };
158
+ //#endregion
159
+ export { type ProcedureConnectionOptions, type ProcedureFieldOptions, pluginName as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,112 @@
1
+ import SchemaBuilder, { BasePlugin, RootFieldBuilder } from "@pothos/core";
2
+ import { ORPCError } from "@orpc/client";
3
+ import "@unthrown/pothos/relay";
4
+ import { outcomeOf } from "@unthrown/pothos";
5
+ import { GraphQLError } from "graphql";
6
+ import { Err, P, TaggedError, fromPromise, fromSafePromise, fromSafeThrowable } from "unthrown";
7
+ import { z } from "zod";
8
+ //#region src/contract.ts
9
+ /**
10
+ * The one Zod schema a procedure's contract declares for its input, typed as
11
+ * declared — to derive a GraphQL argument's shape from it (`.shape`, a
12
+ * `z.preprocess` around it) when the schema is built.
13
+ *
14
+ * A procedure whose contract holds anything but exactly one Zod input schema
15
+ * is a mistake in the schema's source, not a request's: it throws, once, as
16
+ * the schema is built.
17
+ */
18
+ const contractInput = (procedure) => {
19
+ const [schema] = z.tuple([z.custom((value) => value instanceof z.ZodType)]).parse(procedure["~orpc"].inputSchemas);
20
+ return schema;
21
+ };
22
+ /**
23
+ * An input the procedure's contract refuses, caught before the procedure is
24
+ * called: each of the contract's issues, with its message and path.
25
+ */
26
+ var InvalidInput = class extends TaggedError("InvalidInput") {
27
+ message = "Input does not satisfy the service contract";
28
+ };
29
+ /**
30
+ * `call`, handed the input as the procedure's contract parses it and encodes
31
+ * it back for the wire — its defaults applied, its codecs run, asynchronous
32
+ * refinements included — or `InvalidInput` without calling it. A schema with a
33
+ * transform cannot be encoded back: `call` gets the input as given, which the
34
+ * contract admitted. A contract whose input is not one Zod
35
+ * schema is a defect.
36
+ */
37
+ const contractCall = (procedure, input, call) => fromSafeThrowable(() => contractInput(procedure))().toAsync().flatMap((schema) => fromSafePromise(schema.safeParseAsync(input)).flatMap((parsed) => parsed.success ? wireInput(schema, parsed.data, input) : Err(new InvalidInput({ issues: parsed.error.issues })))).flatMap((validated) => call(validated));
38
+ const wireInput = (schema, parsed, given) => fromPromise(z.encodeAsync(schema, parsed), (cause, defect) => cause instanceof z.core.$ZodEncodeError ? "Unencodable" : defect(cause)).recoverErrCases((matcher) => matcher.with("Unencodable", () => given));
39
+ /**
40
+ * A procedure's answer decoded by `schema` — the shape the GraphQL field
41
+ * resolves, codecs run. An answer the schema refuses is a defect: the service
42
+ * broke its own contract.
43
+ */
44
+ const contractOutput = (schema, value) => fromSafePromise(schema.parseAsync(value));
45
+ //#endregion
46
+ //#region src/procedure-field.ts
47
+ const ACCESS_CODES = /* @__PURE__ */ new Set(["UNAUTHORIZED", "FORBIDDEN"]);
48
+ /**
49
+ * The refusals a procedure can answer, read from `registry`: one per error
50
+ * code its contract declares, and `InvalidInput`. A code the registry lacks is
51
+ * left out, and so are `UNAUTHORIZED` and `FORBIDDEN`, answered as GraphQL's
52
+ * own error.
53
+ */
54
+ const refusalsOf = (registry, procedure) => Object.fromEntries([...Object.keys(procedure["~orpc"].errorMap), "InvalidInput"].flatMap((code) => {
55
+ const Refusal = ACCESS_CODES.has(code) ? void 0 : registry[code];
56
+ return Refusal === void 0 ? [] : [[code, Refusal]];
57
+ }));
58
+ const accessRefusalOf = (cause) => cause instanceof ORPCError && (cause.code === "UNAUTHORIZED" || cause.code === "FORBIDDEN") ? new GraphQLError("Service refused access", { extensions: { code: cause.code } }) : void 0;
59
+ /**
60
+ * `call` handed the input the procedure's contract accepts, its answer decoded
61
+ * by `output`. `UNAUTHORIZED` and `FORBIDDEN`, declared or not, leave the
62
+ * error channel: they travel as a `GraphQLError` carrying the code in
63
+ * `extensions.code`, an error of the operation, never a refusal of the field.
64
+ */
65
+ const callProcedure = (procedure, input, call, output) => contractCall(procedure, input, (validated) => withoutAccess(call(validated))).flatMap((value) => contractOutput(output, value)).recoverDefect((cause) => {
66
+ throw accessRefusalOf(cause) ?? cause;
67
+ });
68
+ function withoutAccess(called) {
69
+ return called.flatMapErrCases((matcher, defect) => matcher.with({ code: "UNAUTHORIZED" }, { code: "FORBIDDEN" }, (failure) => defect(failure)).with(P._, (failure) => Err(failure)));
70
+ }
71
+ /**
72
+ * What a DataLoader answers for one key read through `procedure`: the decoded
73
+ * value, or an `Error` — the registered refusal for a declared error, the
74
+ * defect otherwise — so one key's failure never fails the batch.
75
+ *
76
+ * **It answers a `Promise`, and that is the boundary**: a DataLoader's batch
77
+ * function answers a value or an `Error` per key.
78
+ */
79
+ const loadProcedure = async (registry, procedure, call, input, output) => outcomeOf(callProcedure(procedure, input, call, output), refusalsOf(registry, procedure));
80
+ /**
81
+ * The `errors` option of a field that reads `procedure` through
82
+ * `loadProcedure`: the registered refusals it can answer, as the field's
83
+ * result union.
84
+ */
85
+ const procedureErrors = (registry, procedure) => ({ types: Object.values(refusalsOf(registry, procedure)) });
86
+ const defineProcedureFields = () => {
87
+ const fieldBuilder = RootFieldBuilder.prototype;
88
+ fieldBuilder.procedureField = function procedureField(procedure, { call, input, output, tap, ...options }) {
89
+ return this.resultField({
90
+ ...options,
91
+ refusals: refusalsOf(this.builder.options.orpc?.refusals ?? {}, procedure),
92
+ resolve: (parent, args, context) => callProcedure(procedure, input(args, parent), call(context), output).tap((value) => {
93
+ tap?.(value, context);
94
+ })
95
+ });
96
+ };
97
+ fieldBuilder.procedureConnection = function procedureConnection(procedure, { call, input, output, connection, connectionType, ...options }) {
98
+ return this.resultConnection({
99
+ ...options,
100
+ refusals: refusalsOf(this.builder.options.orpc?.refusals ?? {}, procedure),
101
+ resolve: (parent, args, context) => callProcedure(procedure, input(args, parent), call(context), output).map((page) => connection(page, args))
102
+ }, connectionType);
103
+ };
104
+ };
105
+ //#endregion
106
+ //#region src/plugin.ts
107
+ const pluginName = "orpc";
108
+ var PothosOrpcPlugin = class extends BasePlugin {};
109
+ SchemaBuilder.registerPlugin(pluginName, PothosOrpcPlugin);
110
+ defineProcedureFields();
111
+ //#endregion
112
+ export { InvalidInput, callProcedure, contractCall, contractInput, contractOutput, pluginName as default, loadProcedure, procedureErrors, refusalsOf };
package/package.json CHANGED
@@ -1,6 +1,81 @@
1
1
  {
2
2
  "name": "@btravstack/pothos",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.28.0",
4
+ "description": "Pothos fields that call oRPC procedures, each declared error answered as a typed GraphQL refusal",
5
+ "keywords": [
6
+ "graphql",
7
+ "orpc",
8
+ "pothos",
9
+ "typescript",
10
+ "unthrown"
11
+ ],
12
+ "homepage": "https://github.com/btravstack/btravstack#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/btravstack/btravstack/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Benoit TRAVERS <benoit.travers.fr@gmail.com>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/btravstack/btravstack.git",
21
+ "directory": "packages/pothos"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "type": "module",
27
+ "sideEffects": [
28
+ "./dist/index.mjs"
29
+ ],
30
+ "main": "./dist/index.mjs",
31
+ "module": "./dist/index.mjs",
32
+ "types": "./dist/index.d.mts",
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/index.d.mts",
37
+ "default": "./dist/index.mjs"
38
+ }
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "devDependencies": {
43
+ "@btravstack/tsconfig": "0.4.0",
44
+ "@orpc/client": "2.0.0-beta.28",
45
+ "@orpc/contract": "2.0.0-beta.28",
46
+ "@pothos/core": "4.15.1",
47
+ "@pothos/plugin-errors": "4.9.3",
48
+ "@pothos/plugin-relay": "4.8.1",
49
+ "@types/node": "26.6.4",
50
+ "@unthrown/pothos": "0.1.0",
51
+ "@unthrown/vitest": "5.12.0",
52
+ "@vitest/coverage-v8": "5.0.3",
53
+ "graphql": "17.0.2",
54
+ "tsdown": "0.23.0",
55
+ "typescript": "7.0.2",
56
+ "typescript-consumer": "npm:typescript@5.9.3",
57
+ "unthrown": "5.12.0",
58
+ "vitest": "5.0.3",
59
+ "zod": "4.6.5"
60
+ },
61
+ "peerDependencies": {
62
+ "@orpc/client": "^2.0.0-beta.28",
63
+ "@orpc/contract": "^2.0.0-beta.28",
64
+ "@pothos/core": "^4.15.0",
65
+ "@pothos/plugin-errors": "^4.9.0",
66
+ "@pothos/plugin-relay": "^4.8.0",
67
+ "@unthrown/pothos": "^0.1.0",
68
+ "graphql": "^16.10.0 || ^17.0.0",
69
+ "unthrown": "^5.12.0",
70
+ "zod": "^4.3.0"
71
+ },
72
+ "engines": {
73
+ "node": ">=22"
74
+ },
75
+ "scripts": {
76
+ "build": "tsdown src/index.ts --format esm --dts --clean",
77
+ "dev": "tsdown src/index.ts --format esm --dts --watch",
78
+ "test": "vitest run --coverage",
79
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test-d.json && node ./node_modules/typescript-consumer/bin/tsc --noEmit -p tsconfig.test-d.json"
80
+ }
6
81
  }