@orpc/openapi 0.0.0-next.4748dc5 → 0.0.0-next.47dbd93

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.
Files changed (31) hide show
  1. package/README.md +132 -13
  2. package/dist/adapters/aws-lambda/index.d.mts +4 -3
  3. package/dist/adapters/aws-lambda/index.d.ts +4 -3
  4. package/dist/adapters/aws-lambda/index.mjs +3 -3
  5. package/dist/adapters/fastify/index.d.mts +23 -0
  6. package/dist/adapters/fastify/index.d.ts +23 -0
  7. package/dist/adapters/fastify/index.mjs +18 -0
  8. package/dist/adapters/fetch/index.d.mts +4 -3
  9. package/dist/adapters/fetch/index.d.ts +4 -3
  10. package/dist/adapters/fetch/index.mjs +3 -3
  11. package/dist/adapters/node/index.d.mts +4 -3
  12. package/dist/adapters/node/index.d.ts +4 -3
  13. package/dist/adapters/node/index.mjs +3 -3
  14. package/dist/adapters/standard/index.d.mts +7 -16
  15. package/dist/adapters/standard/index.d.ts +7 -16
  16. package/dist/adapters/standard/index.mjs +1 -1
  17. package/dist/index.d.mts +12 -5
  18. package/dist/index.d.ts +12 -5
  19. package/dist/index.mjs +3 -3
  20. package/dist/plugins/index.d.mts +4 -2
  21. package/dist/plugins/index.d.ts +4 -2
  22. package/dist/plugins/index.mjs +21 -12
  23. package/dist/shared/{openapi.BVXcB0u4.mjs → openapi.BB-W-NKv.mjs} +20 -3
  24. package/dist/shared/{openapi.CfjfVeBJ.d.mts → openapi.BGy4N6eR.d.mts} +16 -4
  25. package/dist/shared/{openapi.CfjfVeBJ.d.ts → openapi.BGy4N6eR.d.ts} +16 -4
  26. package/dist/shared/{openapi.BlSv9FKY.mjs → openapi.BwdtJjDu.mjs} +173 -46
  27. package/dist/shared/openapi.DwaweYRb.d.mts +54 -0
  28. package/dist/shared/openapi.DwaweYRb.d.ts +54 -0
  29. package/package.json +20 -13
  30. package/dist/shared/openapi.CQmjvnb0.d.mts +0 -31
  31. package/dist/shared/openapi.CQmjvnb0.d.ts +0 -31
@@ -4,7 +4,7 @@ import { fallbackContractConfig, getEventIteratorSchemaDetails } from '@orpc/con
4
4
  import { standardizeHTTPPath, StandardOpenAPIJsonSerializer, getDynamicParams } from '@orpc/openapi-client/standard';
5
5
  import { isProcedure, resolveContractProcedures } from '@orpc/server';
6
6
  import { isObject, stringifyJSON, findDeepMatches, toArray, clone, value } from '@orpc/shared';
7
- import { TypeName } from '@orpc/interop/json-schema-typed/draft-2020-12';
7
+ import { TypeName } from 'json-schema-typed/draft-2020-12';
8
8
 
9
9
  const OPERATION_EXTENDER_SYMBOL = Symbol("ORPC_OPERATION_EXTENDER");
10
10
  function customOpenAPIOperation(o, extend) {
@@ -108,11 +108,25 @@ function isAnySchema(schema) {
108
108
  if (schema === true) {
109
109
  return true;
110
110
  }
111
- if (Object.keys(schema).every((k) => !LOGIC_KEYWORDS.includes(k))) {
111
+ if (Object.keys(schema).filter((v) => schema[v] !== void 0).every((k) => !LOGIC_KEYWORDS.includes(k))) {
112
112
  return true;
113
113
  }
114
114
  return false;
115
115
  }
116
+ function isNeverSchema(schema) {
117
+ if (schema === false) {
118
+ return true;
119
+ }
120
+ if (typeof schema === "object" && schema.not !== void 0) {
121
+ if (schema.not === true) {
122
+ return true;
123
+ }
124
+ if (typeof schema.not === "object" && Object.keys(schema.not).length === 0) {
125
+ return true;
126
+ }
127
+ }
128
+ return false;
129
+ }
116
130
  function separateObjectSchema(schema, separatedProperties) {
117
131
  if (Object.keys(schema).some(
118
132
  (k) => !["type", "properties", "required", "additionalProperties"].includes(k) && LOGIC_KEYWORDS.includes(k) && schema[k] !== void 0
@@ -128,7 +142,13 @@ function separateObjectSchema(schema, separatedProperties) {
128
142
  }
129
143
  return acc;
130
144
  }, {});
145
+ if (Object.keys(matched.properties).length === 0) {
146
+ matched.properties = void 0;
147
+ }
131
148
  matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
149
+ if (matched.required?.length === 0) {
150
+ matched.required = void 0;
151
+ }
132
152
  matched.examples = schema.examples?.map((example) => {
133
153
  if (!isObject(example)) {
134
154
  return example;
@@ -140,11 +160,14 @@ function separateObjectSchema(schema, separatedProperties) {
140
160
  return acc;
141
161
  }, {});
142
162
  });
143
- rest.properties = schema.properties && Object.entries(schema.properties).filter(([key]) => !separatedProperties.includes(key)).reduce((acc, [key, value]) => {
163
+ rest.properties = schema.properties && Object.entries(schema.properties).filter(([key]) => !separatedProperties.includes(key)).reduce((acc = {}, [key, value]) => {
144
164
  acc[key] = value;
145
165
  return acc;
146
- }, {});
166
+ }, void 0);
147
167
  rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
168
+ if (rest.required?.length === 0) {
169
+ rest.required = void 0;
170
+ }
148
171
  rest.examples = schema.examples?.map((example) => {
149
172
  if (!isObject(example)) {
150
173
  return example;
@@ -255,7 +278,7 @@ function toOpenAPIContent(schema) {
255
278
  schema: toOpenAPISchema(file)
256
279
  };
257
280
  }
258
- if (restSchema !== void 0) {
281
+ if (restSchema !== void 0 && !isAnySchema(restSchema) && !isNeverSchema(restSchema)) {
259
282
  content["application/json"] = {
260
283
  schema: toOpenAPISchema(restSchema)
261
284
  };
@@ -359,6 +382,107 @@ function resolveOpenAPIJsonSchemaRef(doc, schema) {
359
382
  const resolved = doc.components?.schemas?.[name];
360
383
  return resolved ?? schema;
361
384
  }
385
+ function simplifyComposedObjectJsonSchemasAndRefs(schema, doc) {
386
+ if (doc) {
387
+ schema = resolveOpenAPIJsonSchemaRef(doc, schema);
388
+ }
389
+ if (typeof schema !== "object" || !schema.anyOf && !schema.oneOf && !schema.allOf) {
390
+ return schema;
391
+ }
392
+ const unionSchemas = [
393
+ ...toArray(schema.anyOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc))),
394
+ ...toArray(schema.oneOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc)))
395
+ ];
396
+ const objectUnionSchemas = [];
397
+ for (const u of unionSchemas) {
398
+ if (!isObjectSchema(u)) {
399
+ return schema;
400
+ }
401
+ objectUnionSchemas.push(u);
402
+ }
403
+ const mergedUnionPropertyMap = /* @__PURE__ */ new Map();
404
+ for (const u of objectUnionSchemas) {
405
+ if (u.properties) {
406
+ for (const [key, value] of Object.entries(u.properties)) {
407
+ let entry = mergedUnionPropertyMap.get(key);
408
+ if (!entry) {
409
+ const required = objectUnionSchemas.every((s) => s.required?.includes(key));
410
+ entry = { required, schemas: [] };
411
+ mergedUnionPropertyMap.set(key, entry);
412
+ }
413
+ entry.schemas.push(value);
414
+ }
415
+ }
416
+ }
417
+ const intersectionSchemas = toArray(schema.allOf?.map((s) => simplifyComposedObjectJsonSchemasAndRefs(s, doc)));
418
+ const objectIntersectionSchemas = [];
419
+ for (const u of intersectionSchemas) {
420
+ if (!isObjectSchema(u)) {
421
+ return schema;
422
+ }
423
+ objectIntersectionSchemas.push(u);
424
+ }
425
+ if (isObjectSchema(schema)) {
426
+ objectIntersectionSchemas.push(schema);
427
+ }
428
+ const mergedInteractionPropertyMap = /* @__PURE__ */ new Map();
429
+ for (const u of objectIntersectionSchemas) {
430
+ if (u.properties) {
431
+ for (const [key, value] of Object.entries(u.properties)) {
432
+ let entry = mergedInteractionPropertyMap.get(key);
433
+ if (!entry) {
434
+ const required = objectIntersectionSchemas.some((s) => s.required?.includes(key));
435
+ entry = { required, schemas: [] };
436
+ mergedInteractionPropertyMap.set(key, entry);
437
+ }
438
+ entry.schemas.push(value);
439
+ }
440
+ }
441
+ }
442
+ const resultObjectSchema = { type: "object", properties: {}, required: [] };
443
+ const keys = /* @__PURE__ */ new Set([
444
+ ...mergedUnionPropertyMap.keys(),
445
+ ...mergedInteractionPropertyMap.keys()
446
+ ]);
447
+ if (keys.size === 0) {
448
+ return schema;
449
+ }
450
+ const deduplicateSchemas = (schemas) => {
451
+ const seen = /* @__PURE__ */ new Set();
452
+ const result = [];
453
+ for (const schema2 of schemas) {
454
+ const key = stringifyJSON(schema2);
455
+ if (!seen.has(key)) {
456
+ seen.add(key);
457
+ result.push(schema2);
458
+ }
459
+ }
460
+ return result;
461
+ };
462
+ for (const key of keys) {
463
+ const unionEntry = mergedUnionPropertyMap.get(key);
464
+ const intersectionEntry = mergedInteractionPropertyMap.get(key);
465
+ resultObjectSchema.properties[key] = (() => {
466
+ const dedupedUnionSchemas = unionEntry ? deduplicateSchemas(unionEntry.schemas) : [];
467
+ const dedupedIntersectionSchemas = intersectionEntry ? deduplicateSchemas(intersectionEntry.schemas) : [];
468
+ if (!dedupedUnionSchemas.length) {
469
+ return dedupedIntersectionSchemas.length === 1 ? dedupedIntersectionSchemas[0] : { allOf: dedupedIntersectionSchemas };
470
+ }
471
+ if (!dedupedIntersectionSchemas.length) {
472
+ return dedupedUnionSchemas.length === 1 ? dedupedUnionSchemas[0] : { anyOf: dedupedUnionSchemas };
473
+ }
474
+ const allOf = deduplicateSchemas([
475
+ ...dedupedIntersectionSchemas,
476
+ dedupedUnionSchemas.length === 1 ? dedupedUnionSchemas[0] : { anyOf: dedupedUnionSchemas }
477
+ ]);
478
+ return allOf.length === 1 ? allOf[0] : { allOf };
479
+ })();
480
+ if (unionEntry?.required || intersectionEntry?.required) {
481
+ resultObjectSchema.required.push(key);
482
+ }
483
+ }
484
+ return resultObjectSchema;
485
+ }
362
486
 
363
487
  class CompositeSchemaConverter {
364
488
  converters;
@@ -387,21 +511,18 @@ class OpenAPIGenerator {
387
511
  /**
388
512
  * Generates OpenAPI specifications from oRPC routers/contracts.
389
513
  *
390
- * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-specification OpenAPI Specification Docs}
514
+ * @see {@link https://orpc.dev/docs/openapi/openapi-specification OpenAPI Specification Docs}
391
515
  */
392
- async generate(router, options = {}) {
393
- const filter = options.filter ?? (({ contract, path }) => {
394
- return !(options.exclude?.(contract, path) ?? false);
516
+ async generate(router, { customErrorResponseBodySchema, commonSchemas, filter: baseFilter, exclude, ...baseDoc } = {}) {
517
+ const filter = baseFilter ?? (({ contract, path }) => {
518
+ return !(exclude?.(contract, path) ?? false);
395
519
  });
396
520
  const doc = {
397
- ...clone(options),
398
- info: options.info ?? { title: "API Reference", version: "0.0.0" },
399
- openapi: "3.1.1",
400
- exclude: void 0,
401
- filter: void 0,
402
- commonSchemas: void 0
521
+ ...clone(baseDoc),
522
+ info: baseDoc.info ?? { title: "API Reference", version: "0.0.0" },
523
+ openapi: "3.1.1"
403
524
  };
404
- const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc, options.commonSchemas);
525
+ const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc, commonSchemas);
405
526
  const contracts = [];
406
527
  await resolveContractProcedures({ path: [], router }, (traverseOptions) => {
407
528
  if (!value(filter, traverseOptions)) {
@@ -429,7 +550,7 @@ class OpenAPIGenerator {
429
550
  };
430
551
  await this.#request(doc, operationObjectRef, def, baseSchemaConvertOptions);
431
552
  await this.#successResponse(doc, operationObjectRef, def, baseSchemaConvertOptions);
432
- await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema);
553
+ await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema, customErrorResponseBodySchema);
433
554
  }
434
555
  if (typeof def.route.spec === "function") {
435
556
  operationObjectRef = def.route.spec(operationObjectRef);
@@ -542,13 +663,16 @@ ${errors.join("\n\n")}`
542
663
  def.inputSchema,
543
664
  {
544
665
  ...baseSchemaConvertOptions,
545
- strategy: "input",
546
- minStructureDepthForRef: dynamicParams?.length || inputStructure === "detailed" ? 1 : 0
666
+ strategy: "input"
547
667
  }
548
668
  );
669
+ let omitResponseBody = false;
549
670
  if (isAnySchema(schema) && !dynamicParams?.length) {
550
671
  return;
551
672
  }
673
+ if (inputStructure === "detailed" || inputStructure === "compact" && (dynamicParams?.length || method === "GET")) {
674
+ schema = simplifyComposedObjectJsonSchemasAndRefs(schema, doc);
675
+ }
552
676
  if (inputStructure === "compact") {
553
677
  if (dynamicParams?.length) {
554
678
  const error2 = new OpenAPIGeneratorError(
@@ -560,6 +684,7 @@ ${errors.join("\n\n")}`
560
684
  const [paramsSchema, rest] = separateObjectSchema(schema, dynamicParams);
561
685
  schema = rest;
562
686
  required = rest.required ? rest.required.length !== 0 : false;
687
+ omitResponseBody = !required && !rest.properties;
563
688
  if (!checkParamsSchema(paramsSchema, dynamicParams)) {
564
689
  throw error2;
565
690
  }
@@ -567,15 +692,14 @@ ${errors.join("\n\n")}`
567
692
  ref.parameters.push(...toOpenAPIParameters(paramsSchema, "path"));
568
693
  }
569
694
  if (method === "GET") {
570
- const resolvedSchema = resolveOpenAPIJsonSchemaRef(doc, schema);
571
- if (!isObjectSchema(resolvedSchema)) {
695
+ if (!isObjectSchema(schema)) {
572
696
  throw new OpenAPIGeneratorError(
573
697
  'When method is "GET", input schema must satisfy: object | any | unknown'
574
698
  );
575
699
  }
576
700
  ref.parameters ??= [];
577
- ref.parameters.push(...toOpenAPIParameters(resolvedSchema, "query"));
578
- } else {
701
+ ref.parameters.push(...toOpenAPIParameters(schema, "query"));
702
+ } else if (!omitResponseBody) {
579
703
  ref.requestBody = {
580
704
  required,
581
705
  content: toOpenAPIContent(schema)
@@ -589,7 +713,7 @@ ${errors.join("\n\n")}`
589
713
  if (!isObjectSchema(schema)) {
590
714
  throw error;
591
715
  }
592
- const resolvedParamSchema = schema.properties?.params !== void 0 ? resolveOpenAPIJsonSchemaRef(doc, schema.properties.params) : void 0;
716
+ const resolvedParamSchema = schema.properties?.params !== void 0 ? simplifyComposedObjectJsonSchemasAndRefs(schema.properties.params, doc) : void 0;
593
717
  if (dynamicParams?.length && (resolvedParamSchema === void 0 || !isObjectSchema(resolvedParamSchema) || !checkParamsSchema(resolvedParamSchema, dynamicParams))) {
594
718
  throw new OpenAPIGeneratorError(
595
719
  'When input structure is "detailed" and path has dynamic params, the "params" schema must be an object with all dynamic params as required.'
@@ -598,7 +722,7 @@ ${errors.join("\n\n")}`
598
722
  for (const from of ["params", "query", "headers"]) {
599
723
  const fromSchema = schema.properties?.[from];
600
724
  if (fromSchema !== void 0) {
601
- const resolvedSchema = resolveOpenAPIJsonSchemaRef(doc, fromSchema);
725
+ const resolvedSchema = simplifyComposedObjectJsonSchemasAndRefs(fromSchema, doc);
602
726
  if (!isObjectSchema(resolvedSchema)) {
603
727
  throw error;
604
728
  }
@@ -659,13 +783,14 @@ ${errors.join("\n\n")}`
659
783
 
660
784
  But got: ${stringifyJSON(item)}
661
785
  `);
662
- if (!isObjectSchema(item)) {
786
+ const simplifiedItem = simplifyComposedObjectJsonSchemasAndRefs(item, doc);
787
+ if (!isObjectSchema(simplifiedItem)) {
663
788
  throw error;
664
789
  }
665
790
  let schemaStatus;
666
791
  let schemaDescription;
667
- if (item.properties?.status !== void 0) {
668
- const statusSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.status);
792
+ if (simplifiedItem.properties?.status !== void 0) {
793
+ const statusSchema = resolveOpenAPIJsonSchemaRef(doc, simplifiedItem.properties.status);
669
794
  if (typeof statusSchema !== "object" || statusSchema.const === void 0 || typeof statusSchema.const !== "number" || !Number.isInteger(statusSchema.const) || isORPCErrorStatus(statusSchema.const)) {
670
795
  throw error;
671
796
  }
@@ -685,8 +810,8 @@ ${errors.join("\n\n")}`
685
810
  ref.responses[itemStatus] = {
686
811
  description: itemDescription
687
812
  };
688
- if (item.properties?.headers !== void 0) {
689
- const headersSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.headers);
813
+ if (simplifiedItem.properties?.headers !== void 0) {
814
+ const headersSchema = simplifyComposedObjectJsonSchemasAndRefs(simplifiedItem.properties.headers, doc);
690
815
  if (!isObjectSchema(headersSchema)) {
691
816
  throw error;
692
817
  }
@@ -696,50 +821,52 @@ ${errors.join("\n\n")}`
696
821
  ref.responses[itemStatus].headers ??= {};
697
822
  ref.responses[itemStatus].headers[key] = {
698
823
  schema: toOpenAPISchema(headerSchema),
699
- required: item.required?.includes("headers") && headersSchema.required?.includes(key)
824
+ required: simplifiedItem.required?.includes("headers") && headersSchema.required?.includes(key)
700
825
  };
701
826
  }
702
827
  }
703
828
  }
704
- if (item.properties?.body !== void 0) {
829
+ if (simplifiedItem.properties?.body !== void 0) {
705
830
  ref.responses[itemStatus].content = toOpenAPIContent(
706
- applySchemaOptionality(item.required?.includes("body") ?? false, item.properties.body)
831
+ applySchemaOptionality(simplifiedItem.required?.includes("body") ?? false, simplifiedItem.properties.body)
707
832
  );
708
833
  }
709
834
  }
710
835
  }
711
- async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema) {
836
+ async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema, customErrorResponseBodySchema) {
712
837
  const errorMap = def.errorMap;
713
- const errors = {};
838
+ const errorResponsesByStatus = {};
714
839
  for (const code in errorMap) {
715
840
  const config = errorMap[code];
716
841
  if (!config) {
717
842
  continue;
718
843
  }
719
844
  const status = fallbackORPCErrorStatus(code, config.status);
720
- const message = fallbackORPCErrorMessage(code, config.message);
845
+ const defaultMessage = fallbackORPCErrorMessage(code, config.message);
846
+ errorResponsesByStatus[status] ??= { status, definedErrorDefinitions: [], errorSchemaVariants: [] };
721
847
  const [dataRequired, dataSchema] = await this.converter.convert(config.data, { ...baseSchemaConvertOptions, strategy: "output" });
722
- errors[status] ??= [];
723
- errors[status].push({
848
+ errorResponsesByStatus[status].definedErrorDefinitions.push([code, defaultMessage, dataRequired, dataSchema]);
849
+ errorResponsesByStatus[status].errorSchemaVariants.push({
724
850
  type: "object",
725
851
  properties: {
726
852
  defined: { const: true },
727
853
  code: { const: code },
728
854
  status: { const: status },
729
- message: { type: "string", default: message },
855
+ message: { type: "string", default: defaultMessage },
730
856
  data: dataSchema
731
857
  },
732
858
  required: dataRequired ? ["defined", "code", "status", "message", "data"] : ["defined", "code", "status", "message"]
733
859
  });
734
860
  }
735
861
  ref.responses ??= {};
736
- for (const status in errors) {
737
- const schemas = errors[status];
738
- ref.responses[status] = {
739
- description: status,
740
- content: toOpenAPIContent({
862
+ for (const statusString in errorResponsesByStatus) {
863
+ const errorResponse = errorResponsesByStatus[statusString];
864
+ const customBodySchema = value(customErrorResponseBodySchema, errorResponse.definedErrorDefinitions, errorResponse.status);
865
+ ref.responses[statusString] = {
866
+ description: statusString,
867
+ content: toOpenAPIContent(customBodySchema ?? {
741
868
  oneOf: [
742
- ...schemas,
869
+ ...errorResponse.errorSchemaVariants,
743
870
  undefinedErrorSchema
744
871
  ]
745
872
  })
@@ -748,4 +875,4 @@ ${errors.join("\n\n")}`
748
875
  }
749
876
  }
750
877
 
751
- export { CompositeSchemaConverter as C, LOGIC_KEYWORDS as L, OpenAPIGenerator as O, applyCustomOpenAPIOperation as a, toOpenAPIMethod as b, customOpenAPIOperation as c, toOpenAPIContent as d, toOpenAPIEventIteratorContent as e, toOpenAPIParameters as f, getCustomOpenAPIOperation as g, checkParamsSchema as h, toOpenAPISchema as i, isFileSchema as j, isObjectSchema as k, isAnySchema as l, filterSchemaBranches as m, applySchemaOptionality as n, expandUnionSchema as o, expandArrayableSchema as p, isPrimitiveSchema as q, resolveOpenAPIJsonSchemaRef as r, separateObjectSchema as s, toOpenAPIPath as t };
878
+ export { CompositeSchemaConverter as C, LOGIC_KEYWORDS as L, OpenAPIGenerator as O, applyCustomOpenAPIOperation as a, toOpenAPIMethod as b, customOpenAPIOperation as c, toOpenAPIContent as d, toOpenAPIEventIteratorContent as e, toOpenAPIParameters as f, getCustomOpenAPIOperation as g, checkParamsSchema as h, toOpenAPISchema as i, isFileSchema as j, isObjectSchema as k, isAnySchema as l, isNeverSchema as m, separateObjectSchema as n, filterSchemaBranches as o, applySchemaOptionality as p, expandUnionSchema as q, resolveOpenAPIJsonSchemaRef as r, simplifyComposedObjectJsonSchemasAndRefs as s, toOpenAPIPath as t, expandArrayableSchema as u, isPrimitiveSchema as v };
@@ -0,0 +1,54 @@
1
+ import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
2
+ import { AnyProcedure, TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
3
+ import { StandardCodec, StandardParams, StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
+ import { ORPCError, HTTPPath } from '@orpc/client';
5
+ import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
6
+ import { Value } from '@orpc/shared';
7
+
8
+ interface StandardOpenAPICodecOptions {
9
+ /**
10
+ * Customize how an ORPC error is encoded into a response body.
11
+ * Use this if your API needs a different error output structure.
12
+ *
13
+ * @remarks
14
+ * - Return `null | undefined` to fallback to default behavior
15
+ *
16
+ * @default ((e) => e.toJSON())
17
+ */
18
+ customErrorResponseBodyEncoder?: (error: ORPCError<any, any>) => unknown;
19
+ }
20
+ declare class StandardOpenAPICodec implements StandardCodec {
21
+ #private;
22
+ private readonly serializer;
23
+ private readonly customErrorResponseBodyEncoder;
24
+ constructor(serializer: StandardOpenAPISerializer, options?: StandardOpenAPICodecOptions);
25
+ decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
26
+ encode(output: unknown, procedure: AnyProcedure): StandardResponse;
27
+ encodeError(error: ORPCError<any, any>): StandardResponse;
28
+ }
29
+
30
+ interface StandardOpenAPIMatcherOptions {
31
+ /**
32
+ * Filter procedures. Return `false` to exclude a procedure from matching.
33
+ *
34
+ * @default true
35
+ */
36
+ filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
37
+ }
38
+ declare class StandardOpenAPIMatcher implements StandardMatcher {
39
+ private readonly filter;
40
+ private readonly tree;
41
+ private pendingRouters;
42
+ constructor(options?: StandardOpenAPIMatcherOptions);
43
+ init(router: AnyRouter, path?: readonly string[]): void;
44
+ match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
45
+ }
46
+
47
+ interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions, StandardOpenAPICodecOptions {
48
+ }
49
+ declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
50
+ constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
51
+ }
52
+
53
+ export { StandardOpenAPICodec as a, StandardOpenAPIHandler as c, StandardOpenAPIMatcher as e };
54
+ export type { StandardOpenAPICodecOptions as S, StandardOpenAPIHandlerOptions as b, StandardOpenAPIMatcherOptions as d };
@@ -0,0 +1,54 @@
1
+ import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
2
+ import { AnyProcedure, TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
3
+ import { StandardCodec, StandardParams, StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
+ import { ORPCError, HTTPPath } from '@orpc/client';
5
+ import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
6
+ import { Value } from '@orpc/shared';
7
+
8
+ interface StandardOpenAPICodecOptions {
9
+ /**
10
+ * Customize how an ORPC error is encoded into a response body.
11
+ * Use this if your API needs a different error output structure.
12
+ *
13
+ * @remarks
14
+ * - Return `null | undefined` to fallback to default behavior
15
+ *
16
+ * @default ((e) => e.toJSON())
17
+ */
18
+ customErrorResponseBodyEncoder?: (error: ORPCError<any, any>) => unknown;
19
+ }
20
+ declare class StandardOpenAPICodec implements StandardCodec {
21
+ #private;
22
+ private readonly serializer;
23
+ private readonly customErrorResponseBodyEncoder;
24
+ constructor(serializer: StandardOpenAPISerializer, options?: StandardOpenAPICodecOptions);
25
+ decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
26
+ encode(output: unknown, procedure: AnyProcedure): StandardResponse;
27
+ encodeError(error: ORPCError<any, any>): StandardResponse;
28
+ }
29
+
30
+ interface StandardOpenAPIMatcherOptions {
31
+ /**
32
+ * Filter procedures. Return `false` to exclude a procedure from matching.
33
+ *
34
+ * @default true
35
+ */
36
+ filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
37
+ }
38
+ declare class StandardOpenAPIMatcher implements StandardMatcher {
39
+ private readonly filter;
40
+ private readonly tree;
41
+ private pendingRouters;
42
+ constructor(options?: StandardOpenAPIMatcherOptions);
43
+ init(router: AnyRouter, path?: readonly string[]): void;
44
+ match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
45
+ }
46
+
47
+ interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions, StandardOpenAPICodecOptions {
48
+ }
49
+ declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
50
+ constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
51
+ }
52
+
53
+ export { StandardOpenAPICodec as a, StandardOpenAPIHandler as c, StandardOpenAPIMatcher as e };
54
+ export type { StandardOpenAPICodecOptions as S, StandardOpenAPIHandlerOptions as b, StandardOpenAPIMatcherOptions as d };
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@orpc/openapi",
3
3
  "type": "module",
4
- "version": "0.0.0-next.4748dc5",
4
+ "version": "0.0.0-next.47dbd93",
5
5
  "license": "MIT",
6
- "homepage": "https://orpc.unnoq.com",
6
+ "homepage": "https://orpc.dev",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/unnoq/orpc.git",
9
+ "url": "git+https://github.com/middleapi/orpc.git",
10
10
  "directory": "packages/openapi"
11
11
  },
12
12
  "keywords": [
13
- "unnoq",
14
13
  "orpc"
15
14
  ],
15
+ "sideEffects": false,
16
16
  "exports": {
17
17
  ".": {
18
18
  "types": "./dist/index.d.mts",
@@ -39,6 +39,11 @@
39
39
  "import": "./dist/adapters/node/index.mjs",
40
40
  "default": "./dist/adapters/node/index.mjs"
41
41
  },
42
+ "./fastify": {
43
+ "types": "./dist/adapters/fastify/index.d.mts",
44
+ "import": "./dist/adapters/fastify/index.mjs",
45
+ "default": "./dist/adapters/fastify/index.mjs"
46
+ },
42
47
  "./aws-lambda": {
43
48
  "types": "./dist/adapters/aws-lambda/index.d.mts",
44
49
  "import": "./dist/adapters/aws-lambda/index.mjs",
@@ -49,17 +54,19 @@
49
54
  "dist"
50
55
  ],
51
56
  "dependencies": {
52
- "rou3": "^0.7.5",
53
- "@orpc/client": "0.0.0-next.4748dc5",
54
- "@orpc/contract": "0.0.0-next.4748dc5",
55
- "@orpc/interop": "0.0.0-next.4748dc5",
56
- "@orpc/openapi-client": "0.0.0-next.4748dc5",
57
- "@orpc/server": "0.0.0-next.4748dc5",
58
- "@orpc/standard-server": "0.0.0-next.4748dc5",
59
- "@orpc/shared": "0.0.0-next.4748dc5"
57
+ "json-schema-typed": "^8.0.2",
58
+ "rou3": "^0.7.12",
59
+ "@orpc/contract": "0.0.0-next.47dbd93",
60
+ "@orpc/client": "0.0.0-next.47dbd93",
61
+ "@orpc/interop": "0.0.0-next.47dbd93",
62
+ "@orpc/openapi-client": "0.0.0-next.47dbd93",
63
+ "@orpc/shared": "0.0.0-next.47dbd93",
64
+ "@orpc/server": "0.0.0-next.47dbd93",
65
+ "@orpc/standard-server": "0.0.0-next.47dbd93"
60
66
  },
61
67
  "devDependencies": {
62
- "zod": "^4.1.11"
68
+ "fastify": "^5.8.3",
69
+ "zod": "^4.3.6"
63
70
  },
64
71
  "scripts": {
65
72
  "build": "unbuild",
@@ -1,31 +0,0 @@
1
- import { StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
2
- import { TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
3
- import { StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
- import { HTTPPath } from '@orpc/client';
5
- import { Value } from '@orpc/shared';
6
-
7
- interface StandardOpenAPIMatcherOptions {
8
- /**
9
- * Filter procedures. Return `false` to exclude a procedure from matching.
10
- *
11
- * @default true
12
- */
13
- filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
14
- }
15
- declare class StandardOpenAPIMatcher implements StandardMatcher {
16
- private readonly filter;
17
- private readonly tree;
18
- private pendingRouters;
19
- constructor(options?: StandardOpenAPIMatcherOptions);
20
- init(router: AnyRouter, path?: readonly string[]): void;
21
- match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
22
- }
23
-
24
- interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions {
25
- }
26
- declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
27
- constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
28
- }
29
-
30
- export { StandardOpenAPIHandler as a, StandardOpenAPIMatcher as c };
31
- export type { StandardOpenAPIHandlerOptions as S, StandardOpenAPIMatcherOptions as b };
@@ -1,31 +0,0 @@
1
- import { StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions } from '@orpc/openapi-client/standard';
2
- import { TraverseContractProcedureCallbackOptions, AnyRouter, Context, Router } from '@orpc/server';
3
- import { StandardMatcher, StandardMatchResult, StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
- import { HTTPPath } from '@orpc/client';
5
- import { Value } from '@orpc/shared';
6
-
7
- interface StandardOpenAPIMatcherOptions {
8
- /**
9
- * Filter procedures. Return `false` to exclude a procedure from matching.
10
- *
11
- * @default true
12
- */
13
- filter?: Value<boolean, [options: TraverseContractProcedureCallbackOptions]>;
14
- }
15
- declare class StandardOpenAPIMatcher implements StandardMatcher {
16
- private readonly filter;
17
- private readonly tree;
18
- private pendingRouters;
19
- constructor(options?: StandardOpenAPIMatcherOptions);
20
- init(router: AnyRouter, path?: readonly string[]): void;
21
- match(method: string, pathname: HTTPPath): Promise<StandardMatchResult>;
22
- }
23
-
24
- interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions, StandardBracketNotationSerializerOptions, StandardOpenAPIMatcherOptions {
25
- }
26
- declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
27
- constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
28
- }
29
-
30
- export { StandardOpenAPIHandler as a, StandardOpenAPIMatcher as c };
31
- export type { StandardOpenAPIHandlerOptions as S, StandardOpenAPIMatcherOptions as b };