@orpc/openapi 0.0.0-next.dda04c5 → 0.0.0-next.de2bec7

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 +126 -14
  2. package/dist/adapters/aws-lambda/index.d.mts +8 -5
  3. package/dist/adapters/aws-lambda/index.d.ts +8 -5
  4. package/dist/adapters/aws-lambda/index.mjs +5 -5
  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 +11 -5
  9. package/dist/adapters/fetch/index.d.ts +11 -5
  10. package/dist/adapters/fetch/index.mjs +3 -3
  11. package/dist/adapters/node/index.d.mts +11 -5
  12. package/dist/adapters/node/index.d.ts +11 -5
  13. package/dist/adapters/node/index.mjs +3 -3
  14. package/dist/adapters/standard/index.d.mts +8 -23
  15. package/dist/adapters/standard/index.d.ts +8 -23
  16. package/dist/adapters/standard/index.mjs +1 -1
  17. package/dist/index.d.mts +10 -3
  18. package/dist/index.d.ts +10 -3
  19. package/dist/index.mjs +2 -2
  20. package/dist/plugins/index.d.mts +20 -3
  21. package/dist/plugins/index.d.ts +20 -3
  22. package/dist/plugins/index.mjs +69 -20
  23. package/dist/shared/{openapi.C_UtQ8Us.mjs → openapi.BB-W-NKv.mjs} +33 -8
  24. package/dist/shared/{openapi.B3hexduL.d.mts → openapi.BGy4N6eR.d.mts} +25 -6
  25. package/dist/shared/{openapi.B3hexduL.d.ts → openapi.BGy4N6eR.d.ts} +25 -6
  26. package/dist/shared/{openapi.DrrBsJ0w.mjs → openapi.BwdtJjDu.mjs} +191 -51
  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.D3j94c9n.d.mts +0 -12
  31. package/dist/shared/openapi.D3j94c9n.d.ts +0 -12
@@ -3,7 +3,7 @@ import { toHttpPath } from '@orpc/client/standard';
3
3
  import { fallbackContractConfig, getEventIteratorSchemaDetails } from '@orpc/contract';
4
4
  import { standardizeHTTPPath, StandardOpenAPIJsonSerializer, getDynamicParams } from '@orpc/openapi-client/standard';
5
5
  import { isProcedure, resolveContractProcedures } from '@orpc/server';
6
- import { isObject, stringifyJSON, findDeepMatches, toArray, clone } from '@orpc/shared';
6
+ import { isObject, stringifyJSON, findDeepMatches, toArray, clone, value } from '@orpc/shared';
7
7
  import { TypeName } from 'json-schema-typed/draft-2020-12';
8
8
 
9
9
  const OPERATION_EXTENDER_SYMBOL = Symbol("ORPC_OPERATION_EXTENDER");
@@ -108,22 +108,47 @@ 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
- if (Object.keys(schema).some((k) => k !== "type" && k !== "properties" && k !== "required" && LOGIC_KEYWORDS.includes(k))) {
131
+ if (Object.keys(schema).some(
132
+ (k) => !["type", "properties", "required", "additionalProperties"].includes(k) && LOGIC_KEYWORDS.includes(k) && schema[k] !== void 0
133
+ )) {
118
134
  return [{ type: "object" }, schema];
119
135
  }
120
136
  const matched = { ...schema };
121
137
  const rest = { ...schema };
122
- matched.properties = schema.properties && Object.entries(schema.properties).filter(([key]) => separatedProperties.includes(key)).reduce((acc, [key, value]) => {
123
- acc[key] = value;
138
+ matched.properties = separatedProperties.reduce((acc, key) => {
139
+ const keySchema = schema.properties?.[key] ?? schema.additionalProperties;
140
+ if (keySchema !== void 0) {
141
+ acc[key] = keySchema;
142
+ }
124
143
  return acc;
125
144
  }, {});
145
+ if (Object.keys(matched.properties).length === 0) {
146
+ matched.properties = void 0;
147
+ }
126
148
  matched.required = schema.required?.filter((key) => separatedProperties.includes(key));
149
+ if (matched.required?.length === 0) {
150
+ matched.required = void 0;
151
+ }
127
152
  matched.examples = schema.examples?.map((example) => {
128
153
  if (!isObject(example)) {
129
154
  return example;
@@ -135,11 +160,14 @@ function separateObjectSchema(schema, separatedProperties) {
135
160
  return acc;
136
161
  }, {});
137
162
  });
138
- 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]) => {
139
164
  acc[key] = value;
140
165
  return acc;
141
- }, {});
166
+ }, void 0);
142
167
  rest.required = schema.required?.filter((key) => !separatedProperties.includes(key));
168
+ if (rest.required?.length === 0) {
169
+ rest.required = void 0;
170
+ }
143
171
  rest.examples = schema.examples?.map((example) => {
144
172
  if (!isObject(example)) {
145
173
  return example;
@@ -250,7 +278,7 @@ function toOpenAPIContent(schema) {
250
278
  schema: toOpenAPISchema(file)
251
279
  };
252
280
  }
253
- if (restSchema !== void 0) {
281
+ if (restSchema !== void 0 && !isAnySchema(restSchema) && !isNeverSchema(restSchema)) {
254
282
  content["application/json"] = {
255
283
  schema: toOpenAPISchema(restSchema)
256
284
  };
@@ -354,6 +382,107 @@ function resolveOpenAPIJsonSchemaRef(doc, schema) {
354
382
  const resolved = doc.components?.schemas?.[name];
355
383
  return resolved ?? schema;
356
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
+ }
357
486
 
358
487
  class CompositeSchemaConverter {
359
488
  converters;
@@ -382,37 +511,38 @@ class OpenAPIGenerator {
382
511
  /**
383
512
  * Generates OpenAPI specifications from oRPC routers/contracts.
384
513
  *
385
- * @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}
386
515
  */
387
- async generate(router, options = {}) {
388
- const exclude = options.exclude ?? (() => false);
516
+ async generate(router, { customErrorResponseBodySchema, commonSchemas, filter: baseFilter, exclude, ...baseDoc } = {}) {
517
+ const filter = baseFilter ?? (({ contract, path }) => {
518
+ return !(exclude?.(contract, path) ?? false);
519
+ });
389
520
  const doc = {
390
- ...clone(options),
391
- info: options.info ?? { title: "API Reference", version: "0.0.0" },
392
- openapi: "3.1.1",
393
- exclude: void 0,
394
- commonSchemas: void 0
521
+ ...clone(baseDoc),
522
+ info: baseDoc.info ?? { title: "API Reference", version: "0.0.0" },
523
+ openapi: "3.1.1"
395
524
  };
396
- const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc, options.commonSchemas);
525
+ const { baseSchemaConvertOptions, undefinedErrorJsonSchema } = await this.#resolveCommonSchemas(doc, commonSchemas);
397
526
  const contracts = [];
398
- await resolveContractProcedures({ path: [], router }, ({ contract, path }) => {
399
- if (!exclude(contract, path)) {
400
- contracts.push({ contract, path });
527
+ await resolveContractProcedures({ path: [], router }, (traverseOptions) => {
528
+ if (!value(filter, traverseOptions)) {
529
+ return;
401
530
  }
531
+ contracts.push(traverseOptions);
402
532
  });
403
533
  const errors = [];
404
534
  for (const { contract, path } of contracts) {
405
- const operationId = path.join(".");
535
+ const stringPath = path.join(".");
406
536
  try {
407
537
  const def = contract["~orpc"];
408
538
  const method = toOpenAPIMethod(fallbackContractConfig("defaultMethod", def.route.method));
409
539
  const httpPath = toOpenAPIPath(def.route.path ?? toHttpPath(path));
410
540
  let operationObjectRef;
411
- if (def.route.spec !== void 0) {
541
+ if (def.route.spec !== void 0 && typeof def.route.spec !== "function") {
412
542
  operationObjectRef = def.route.spec;
413
543
  } else {
414
544
  operationObjectRef = {
415
- operationId,
545
+ operationId: def.route.operationId ?? stringPath,
416
546
  summary: def.route.summary,
417
547
  description: def.route.description,
418
548
  deprecated: def.route.deprecated,
@@ -420,7 +550,10 @@ class OpenAPIGenerator {
420
550
  };
421
551
  await this.#request(doc, operationObjectRef, def, baseSchemaConvertOptions);
422
552
  await this.#successResponse(doc, operationObjectRef, def, baseSchemaConvertOptions);
423
- await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema);
553
+ await this.#errorResponse(operationObjectRef, def, baseSchemaConvertOptions, undefinedErrorJsonSchema, customErrorResponseBodySchema);
554
+ }
555
+ if (typeof def.route.spec === "function") {
556
+ operationObjectRef = def.route.spec(operationObjectRef);
424
557
  }
425
558
  doc.paths ??= {};
426
559
  doc.paths[httpPath] ??= {};
@@ -430,7 +563,7 @@ class OpenAPIGenerator {
430
563
  throw e;
431
564
  }
432
565
  errors.push(
433
- `[OpenAPIGenerator] Error occurred while generating OpenAPI for procedure at path: ${operationId}
566
+ `[OpenAPIGenerator] Error occurred while generating OpenAPI for procedure at path: ${stringPath}
434
567
  ${e.message}`
435
568
  );
436
569
  }
@@ -530,13 +663,16 @@ ${errors.join("\n\n")}`
530
663
  def.inputSchema,
531
664
  {
532
665
  ...baseSchemaConvertOptions,
533
- strategy: "input",
534
- minStructureDepthForRef: dynamicParams?.length || inputStructure === "detailed" ? 1 : 0
666
+ strategy: "input"
535
667
  }
536
668
  );
669
+ let omitResponseBody = false;
537
670
  if (isAnySchema(schema) && !dynamicParams?.length) {
538
671
  return;
539
672
  }
673
+ if (inputStructure === "detailed" || inputStructure === "compact" && (dynamicParams?.length || method === "GET")) {
674
+ schema = simplifyComposedObjectJsonSchemasAndRefs(schema, doc);
675
+ }
540
676
  if (inputStructure === "compact") {
541
677
  if (dynamicParams?.length) {
542
678
  const error2 = new OpenAPIGeneratorError(
@@ -548,6 +684,7 @@ ${errors.join("\n\n")}`
548
684
  const [paramsSchema, rest] = separateObjectSchema(schema, dynamicParams);
549
685
  schema = rest;
550
686
  required = rest.required ? rest.required.length !== 0 : false;
687
+ omitResponseBody = !required && !rest.properties;
551
688
  if (!checkParamsSchema(paramsSchema, dynamicParams)) {
552
689
  throw error2;
553
690
  }
@@ -562,7 +699,7 @@ ${errors.join("\n\n")}`
562
699
  }
563
700
  ref.parameters ??= [];
564
701
  ref.parameters.push(...toOpenAPIParameters(schema, "query"));
565
- } else {
702
+ } else if (!omitResponseBody) {
566
703
  ref.requestBody = {
567
704
  required,
568
705
  content: toOpenAPIContent(schema)
@@ -576,7 +713,7 @@ ${errors.join("\n\n")}`
576
713
  if (!isObjectSchema(schema)) {
577
714
  throw error;
578
715
  }
579
- 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;
580
717
  if (dynamicParams?.length && (resolvedParamSchema === void 0 || !isObjectSchema(resolvedParamSchema) || !checkParamsSchema(resolvedParamSchema, dynamicParams))) {
581
718
  throw new OpenAPIGeneratorError(
582
719
  'When input structure is "detailed" and path has dynamic params, the "params" schema must be an object with all dynamic params as required.'
@@ -585,7 +722,7 @@ ${errors.join("\n\n")}`
585
722
  for (const from of ["params", "query", "headers"]) {
586
723
  const fromSchema = schema.properties?.[from];
587
724
  if (fromSchema !== void 0) {
588
- const resolvedSchema = resolveOpenAPIJsonSchemaRef(doc, fromSchema);
725
+ const resolvedSchema = simplifyComposedObjectJsonSchemasAndRefs(fromSchema, doc);
589
726
  if (!isObjectSchema(resolvedSchema)) {
590
727
  throw error;
591
728
  }
@@ -646,13 +783,14 @@ ${errors.join("\n\n")}`
646
783
 
647
784
  But got: ${stringifyJSON(item)}
648
785
  `);
649
- if (!isObjectSchema(item)) {
786
+ const simplifiedItem = simplifyComposedObjectJsonSchemasAndRefs(item, doc);
787
+ if (!isObjectSchema(simplifiedItem)) {
650
788
  throw error;
651
789
  }
652
790
  let schemaStatus;
653
791
  let schemaDescription;
654
- if (item.properties?.status !== void 0) {
655
- const statusSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.status);
792
+ if (simplifiedItem.properties?.status !== void 0) {
793
+ const statusSchema = resolveOpenAPIJsonSchemaRef(doc, simplifiedItem.properties.status);
656
794
  if (typeof statusSchema !== "object" || statusSchema.const === void 0 || typeof statusSchema.const !== "number" || !Number.isInteger(statusSchema.const) || isORPCErrorStatus(statusSchema.const)) {
657
795
  throw error;
658
796
  }
@@ -672,8 +810,8 @@ ${errors.join("\n\n")}`
672
810
  ref.responses[itemStatus] = {
673
811
  description: itemDescription
674
812
  };
675
- if (item.properties?.headers !== void 0) {
676
- const headersSchema = resolveOpenAPIJsonSchemaRef(doc, item.properties.headers);
813
+ if (simplifiedItem.properties?.headers !== void 0) {
814
+ const headersSchema = simplifyComposedObjectJsonSchemasAndRefs(simplifiedItem.properties.headers, doc);
677
815
  if (!isObjectSchema(headersSchema)) {
678
816
  throw error;
679
817
  }
@@ -683,50 +821,52 @@ ${errors.join("\n\n")}`
683
821
  ref.responses[itemStatus].headers ??= {};
684
822
  ref.responses[itemStatus].headers[key] = {
685
823
  schema: toOpenAPISchema(headerSchema),
686
- required: item.required?.includes("headers") && headersSchema.required?.includes(key)
824
+ required: simplifiedItem.required?.includes("headers") && headersSchema.required?.includes(key)
687
825
  };
688
826
  }
689
827
  }
690
828
  }
691
- if (item.properties?.body !== void 0) {
829
+ if (simplifiedItem.properties?.body !== void 0) {
692
830
  ref.responses[itemStatus].content = toOpenAPIContent(
693
- applySchemaOptionality(item.required?.includes("body") ?? false, item.properties.body)
831
+ applySchemaOptionality(simplifiedItem.required?.includes("body") ?? false, simplifiedItem.properties.body)
694
832
  );
695
833
  }
696
834
  }
697
835
  }
698
- async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema) {
836
+ async #errorResponse(ref, def, baseSchemaConvertOptions, undefinedErrorSchema, customErrorResponseBodySchema) {
699
837
  const errorMap = def.errorMap;
700
- const errors = {};
838
+ const errorResponsesByStatus = {};
701
839
  for (const code in errorMap) {
702
840
  const config = errorMap[code];
703
841
  if (!config) {
704
842
  continue;
705
843
  }
706
844
  const status = fallbackORPCErrorStatus(code, config.status);
707
- const message = fallbackORPCErrorMessage(code, config.message);
845
+ const defaultMessage = fallbackORPCErrorMessage(code, config.message);
846
+ errorResponsesByStatus[status] ??= { status, definedErrorDefinitions: [], errorSchemaVariants: [] };
708
847
  const [dataRequired, dataSchema] = await this.converter.convert(config.data, { ...baseSchemaConvertOptions, strategy: "output" });
709
- errors[status] ??= [];
710
- errors[status].push({
848
+ errorResponsesByStatus[status].definedErrorDefinitions.push([code, defaultMessage, dataRequired, dataSchema]);
849
+ errorResponsesByStatus[status].errorSchemaVariants.push({
711
850
  type: "object",
712
851
  properties: {
713
852
  defined: { const: true },
714
853
  code: { const: code },
715
854
  status: { const: status },
716
- message: { type: "string", default: message },
855
+ message: { type: "string", default: defaultMessage },
717
856
  data: dataSchema
718
857
  },
719
858
  required: dataRequired ? ["defined", "code", "status", "message", "data"] : ["defined", "code", "status", "message"]
720
859
  });
721
860
  }
722
861
  ref.responses ??= {};
723
- for (const status in errors) {
724
- const schemas = errors[status];
725
- ref.responses[status] = {
726
- description: status,
727
- 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 ?? {
728
868
  oneOf: [
729
- ...schemas,
869
+ ...errorResponse.errorSchemaVariants,
730
870
  undefinedErrorSchema
731
871
  ]
732
872
  })
@@ -735,4 +875,4 @@ ${errors.join("\n\n")}`
735
875
  }
736
876
  }
737
877
 
738
- 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.dda04c5",
4
+ "version": "0.0.0-next.de2bec7",
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
- "json-schema-typed": "^8.0.1",
53
- "rou3": "^0.7.3",
54
- "@orpc/client": "0.0.0-next.dda04c5",
55
- "@orpc/contract": "0.0.0-next.dda04c5",
56
- "@orpc/openapi-client": "0.0.0-next.dda04c5",
57
- "@orpc/server": "0.0.0-next.dda04c5",
58
- "@orpc/shared": "0.0.0-next.dda04c5",
59
- "@orpc/standard-server": "0.0.0-next.dda04c5"
57
+ "json-schema-typed": "^8.0.2",
58
+ "rou3": "^0.7.12",
59
+ "@orpc/client": "0.0.0-next.de2bec7",
60
+ "@orpc/contract": "0.0.0-next.de2bec7",
61
+ "@orpc/interop": "0.0.0-next.de2bec7",
62
+ "@orpc/server": "0.0.0-next.de2bec7",
63
+ "@orpc/shared": "0.0.0-next.de2bec7",
64
+ "@orpc/openapi-client": "0.0.0-next.de2bec7",
65
+ "@orpc/standard-server": "0.0.0-next.de2bec7"
60
66
  },
61
67
  "devDependencies": {
62
- "zod": "^3.25.67"
68
+ "fastify": "^5.8.3",
69
+ "zod": "^4.3.6"
63
70
  },
64
71
  "scripts": {
65
72
  "build": "unbuild",
@@ -1,12 +0,0 @@
1
- import { StandardOpenAPIJsonSerializerOptions } from '@orpc/openapi-client/standard';
2
- import { Context, Router } from '@orpc/server';
3
- import { StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
-
5
- interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions {
6
- }
7
- declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
8
- constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
9
- }
10
-
11
- export { StandardOpenAPIHandler as a };
12
- export type { StandardOpenAPIHandlerOptions as S };
@@ -1,12 +0,0 @@
1
- import { StandardOpenAPIJsonSerializerOptions } from '@orpc/openapi-client/standard';
2
- import { Context, Router } from '@orpc/server';
3
- import { StandardHandlerOptions, StandardHandler } from '@orpc/server/standard';
4
-
5
- interface StandardOpenAPIHandlerOptions<T extends Context> extends StandardHandlerOptions<T>, StandardOpenAPIJsonSerializerOptions {
6
- }
7
- declare class StandardOpenAPIHandler<T extends Context> extends StandardHandler<T> {
8
- constructor(router: Router<any, T>, options: NoInfer<StandardOpenAPIHandlerOptions<T>>);
9
- }
10
-
11
- export { StandardOpenAPIHandler as a };
12
- export type { StandardOpenAPIHandlerOptions as S };