@btravstack/pothos 0.32.0 → 0.34.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/README.md CHANGED
@@ -6,12 +6,14 @@ contract-first services. The default export is the plugin, listed after
6
6
  `@unthrown/pothos`'s:
7
7
  `plugins: [RelayPlugin, ErrorsPlugin, UnthrownPlugin, OrpcPlugin]`.
8
8
 
9
- - `t.procedureField(procedure, { type, call, input, output })` validates the
9
+ - `t.procedureField(procedure, { type, call, input, output, refusals? })` validates the
10
10
  field's input through the procedure's own contract before calling it,
11
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.
12
+ declares as the GraphQL error class registered for its code — a member of
13
+ the field's result union. Field-local `refusals` override the builder's
14
+ shared `orpc: { refusals }` defaults. Together the maps must cover every
15
+ error `call` can return and `InvalidInput`; a missing code does not compile.
16
+ A declared error handled by a narrower `call` needs no entry.
15
17
  - `t.procedureConnection(procedure, { …, connection })` is the same for a
16
18
  Relay connection, with the decoded page folded by `connection`.
17
19
  - `UNAUTHORIZED` and `FORBIDDEN` are answered as GraphQL's own error, with
package/dist/index.d.mts CHANGED
@@ -72,30 +72,36 @@ type Covered<Registry, Failure> = Registry extends Refusals<Failure> ? {} : {
72
72
  };
73
73
  /**
74
74
  * `t.procedureField`'s options: `resultField`'s, with `call`, `input` and
75
- * `output` in place of `resolve`, and the builder's `orpc.refusals` in place
76
- * of `refusals`. The registry must cover every error `call` can return but
75
+ * `output` in place of `resolve`. Field-local `refusals` override the builder's
76
+ * `orpc.refusals`. Together they must cover every error `call` can return but
77
77
  * the access refusals, and `InvalidInput`; an uncovered case is a compile
78
78
  * error naming it. A declared error `call` handles itself needs no entry.
79
79
  */
80
- 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"> & {
80
+ 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, Local extends Readonly<Record<string, ErrorClass>> = {}> = Omit<ResultFieldOptions<Types, ParentShape, Type, Nullable, Args, Kind, Failure>, "refusals" | "resolve"> & {
81
81
  readonly call: Call<Types["Context"], z.input<Schema>, Failure>;
82
+ /** Local refusal classes, overriding the builder's shared defaults by code. */
83
+ readonly refusals?: Local & ([keyof Local] extends [never] ? Readonly<Record<string, never>> : unknown);
82
84
  readonly input: (args: InputShapeFromFields<Args>, parent: ParentShape) => NoInfer<z.input<Schema>>;
83
85
  readonly output: z.ZodType<ShapeFromTypeParam<Types, Type, false>>;
84
86
  readonly tap?: (value: ShapeFromTypeParam<Types, Type, false>, context: Types["Context"]) => void;
85
- } & Covered<Types["Refusals"], Exclude<Failure, Access> | InvalidInput>;
87
+ } & Covered<Omit<Types["Refusals"], keyof Local> & Local, Exclude<Failure, Access> | InvalidInput>;
86
88
  /**
87
89
  * `t.procedureConnection`'s options: `resultConnection`'s, with `call`,
88
90
  * `input` and `output` in place of `resolve`, `connection` folding the
89
91
  * decoded page into the connection's shape, and an optional `connectionType`
90
- * for fields beside its edges and page info.
92
+ * for fields beside its edges and page info. Local `refusals` override the
93
+ * builder's shared defaults, together covering `call`'s failures and input
94
+ * validation.
91
95
  */
92
- 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"> & {
96
+ 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>, Local extends Readonly<Record<string, ErrorClass>> = {}> = Omit<ResultConnectionOptions<Types, ParentShape, Type, Nullable, EdgeNullability, NodeNullability, Args, Kind, Failure, Connection>, "refusals" | "resolve"> & {
93
97
  readonly call: Call<Types["Context"], z.input<Schema>, Failure>;
98
+ /** Local refusal classes, overriding the builder's shared defaults by code. */
99
+ readonly refusals?: Local & ([keyof Local] extends [never] ? Readonly<Record<string, never>> : unknown);
94
100
  readonly input: (args: DefaultConnectionArguments & InputShapeFromFields<Args>, parent: ParentShape) => NoInfer<z.input<Schema>>;
95
101
  readonly output: z.ZodType<Page>;
96
102
  readonly connectionType?: ObjectRef<Types, Connection>;
97
103
  readonly connection: (page: Page, args: DefaultConnectionArguments & InputShapeFromFields<Args>) => Connection;
98
- } & Covered<Types["Refusals"], Exclude<Failure, Access> | InvalidInput>;
104
+ } & Covered<Omit<Types["Refusals"], keyof Local> & Local, Exclude<Failure, Access> | InvalidInput>;
99
105
  declare global {
100
106
  export namespace PothosSchemaTypes {
101
107
  interface UserSchemaTypes {
@@ -118,12 +124,12 @@ declare global {
118
124
  * union; the service refusing access is GraphQL's own error, with
119
125
  * `extensions.code`.
120
126
  */
121
- 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>;
127
+ procedureField: <Type extends TypeParam<Types>, Schema extends z.ZodType, Failure, Nullable extends FieldNullability<Type> = Types["DefaultFieldNullability"], Args extends InputFieldMap = {}, const Local extends Readonly<Record<string, ErrorClass>> = {}>(procedure: Procedure<Schema>, options: ProcedureFieldOptions<Types, ParentShape, Type, Nullable, Args, Kind, Schema, Failure, Local>) => FieldRef<Types, ShapeFromTypeParam<Types, Type, Nullable>, Kind>;
122
128
  /**
123
129
  * A Relay connection answering an oRPC procedure: `procedureField`'s
124
130
  * contract, with the decoded page folded by `connection`.
125
131
  */
126
- 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>>;
132
+ 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>, const Local extends Readonly<Record<string, ErrorClass>> = {}>(procedure: Procedure<Schema>, options: ProcedureConnectionOptions<Types, ParentShape, Type, Nullable, EdgeNullability, NodeNullability, Args, Kind, Schema, Page, Failure, Connection, Local>) => FieldRef<Types, ConnectionShapeForType<Types, Type, Nullable, EdgeNullability, NodeNullability>>;
127
133
  }
128
134
  }
129
135
  }
package/dist/index.mjs CHANGED
@@ -102,19 +102,25 @@ const loadProcedure = async (registry, procedure, call, input, output) => {
102
102
  const procedureErrors = (registry, procedure) => ({ types: Object.values(refusalsOf(registry, procedure)) });
103
103
  const defineProcedureFields = () => {
104
104
  const fieldBuilder = RootFieldBuilder.prototype;
105
- fieldBuilder.procedureField = function procedureField(procedure, { call, input, output, tap, ...options }) {
105
+ fieldBuilder.procedureField = function procedureField(procedure, { call, input, output, tap, refusals, ...options }) {
106
106
  return this.resultField({
107
107
  ...options,
108
- refusals: refusalsOf(this.builder.options.orpc?.refusals ?? {}, procedure),
108
+ refusals: refusalsOf({
109
+ ...this.builder.options.orpc?.refusals,
110
+ ...refusals
111
+ }, procedure),
109
112
  resolve: (parent, args, context) => callProcedure(procedure, input(args, parent), call(context), output).tap((value) => {
110
113
  tap?.(value, context);
111
114
  })
112
115
  });
113
116
  };
114
- fieldBuilder.procedureConnection = function procedureConnection(procedure, { call, input, output, connection, connectionType, ...options }) {
117
+ fieldBuilder.procedureConnection = function procedureConnection(procedure, { call, input, output, connection, connectionType, refusals, ...options }) {
115
118
  return this.resultConnection({
116
119
  ...options,
117
- refusals: refusalsOf(this.builder.options.orpc?.refusals ?? {}, procedure),
120
+ refusals: refusalsOf({
121
+ ...this.builder.options.orpc?.refusals,
122
+ ...refusals
123
+ }, procedure),
118
124
  resolve: (parent, args, context) => callProcedure(procedure, input(args, parent), call(context), output).map((page) => connection(page, args))
119
125
  }, connectionType);
120
126
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/pothos",
3
- "version": "0.32.0",
3
+ "version": "0.34.0",
4
4
  "description": "Pothos fields that call oRPC procedures, each declared error answered as a typed GraphQL refusal",
5
5
  "keywords": [
6
6
  "graphql",