@orpc/openapi 1.1.1 → 1.3.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
@@ -49,6 +49,7 @@ You can find the full documentation [here](https://orpc.unnoq.com).
49
49
  - [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
50
50
  - [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
51
51
  - [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
52
+ - [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with NestJS.
52
53
  - [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
53
54
  - [@orpc/react-query](https://www.npmjs.com/package/@orpc/react-query): Integration with [React Query](https://tanstack.com/query/latest/docs/framework/react/overview).
54
55
  - [@orpc/vue-query](https://www.npmjs.com/package/@orpc/vue-query): Integration with [Vue Query](https://tanstack.com/query/latest/docs/framework/vue/overview).
@@ -8,7 +8,7 @@ import '@orpc/server/standard';
8
8
  * OpenAPI Handler for Fetch Server
9
9
  *
10
10
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-handler OpenAPI Handler Docs}
11
- * @see {@link https://orpc.unnoq.com/docs/integrations/fetch-server Fetch Server Integration Docs}
11
+ * @see {@link https://orpc.unnoq.com/docs/adapters/http HTTP Adapter Docs}
12
12
  */
13
13
  declare class OpenAPIHandler<T extends Context> extends FetchHandler<T> {
14
14
  constructor(router: Router<any, T>, options?: NoInfer<StandardOpenAPIHandlerOptions<T> & FetchHandlerOptions<T>>);
@@ -8,7 +8,7 @@ import '@orpc/server/standard';
8
8
  * OpenAPI Handler for Fetch Server
9
9
  *
10
10
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-handler OpenAPI Handler Docs}
11
- * @see {@link https://orpc.unnoq.com/docs/integrations/fetch-server Fetch Server Integration Docs}
11
+ * @see {@link https://orpc.unnoq.com/docs/adapters/http HTTP Adapter Docs}
12
12
  */
13
13
  declare class OpenAPIHandler<T extends Context> extends FetchHandler<T> {
14
14
  constructor(router: Router<any, T>, options?: NoInfer<StandardOpenAPIHandlerOptions<T> & FetchHandlerOptions<T>>);
@@ -1,7 +1,8 @@
1
1
  import { FetchHandler } from '@orpc/server/fetch';
2
+ import '@orpc/client';
2
3
  import '@orpc/contract';
3
4
  import '@orpc/shared';
4
- import { a as StandardOpenAPIHandler } from '../../shared/openapi.p5tsmBXx.mjs';
5
+ import { a as StandardOpenAPIHandler } from '../../shared/openapi.C_UtQ8Us.mjs';
5
6
  import '@orpc/client/standard';
6
7
  import '@orpc/server';
7
8
  import 'rou3';
@@ -8,7 +8,7 @@ import '@orpc/server/standard';
8
8
  * OpenAPI Handler for Node Server
9
9
  *
10
10
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-handler OpenAPI Handler Docs}
11
- * @see {@link https://orpc.unnoq.com/docs/integrations/node Node Integration Docs}
11
+ * @see {@link https://orpc.unnoq.com/docs/adapters/http HTTP Adapter Docs}
12
12
  */
13
13
  declare class OpenAPIHandler<T extends Context> extends NodeHttpHandler<T> {
14
14
  constructor(router: Router<any, T>, options?: NoInfer<StandardOpenAPIHandlerOptions<T> & NodeHttpHandlerOptions<T>>);
@@ -8,7 +8,7 @@ import '@orpc/server/standard';
8
8
  * OpenAPI Handler for Node Server
9
9
  *
10
10
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-handler OpenAPI Handler Docs}
11
- * @see {@link https://orpc.unnoq.com/docs/integrations/node Node Integration Docs}
11
+ * @see {@link https://orpc.unnoq.com/docs/adapters/http HTTP Adapter Docs}
12
12
  */
13
13
  declare class OpenAPIHandler<T extends Context> extends NodeHttpHandler<T> {
14
14
  constructor(router: Router<any, T>, options?: NoInfer<StandardOpenAPIHandlerOptions<T> & NodeHttpHandlerOptions<T>>);
@@ -1,7 +1,8 @@
1
1
  import { NodeHttpHandler } from '@orpc/server/node';
2
+ import '@orpc/client';
2
3
  import '@orpc/contract';
3
4
  import '@orpc/shared';
4
- import { a as StandardOpenAPIHandler } from '../../shared/openapi.p5tsmBXx.mjs';
5
+ import { a as StandardOpenAPIHandler } from '../../shared/openapi.C_UtQ8Us.mjs';
5
6
  import '@orpc/client/standard';
6
7
  import '@orpc/server';
7
8
  import 'rou3';
@@ -6,6 +6,7 @@ import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
6
6
  export { a as StandardOpenAPIHandler, S as StandardOpenAPIHandlerOptions } from '../../shared/openapi.D3j94c9n.mjs';
7
7
 
8
8
  declare class StandardOpenAPICodec implements StandardCodec {
9
+ #private;
9
10
  private readonly serializer;
10
11
  constructor(serializer: StandardOpenAPISerializer);
11
12
  decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
@@ -6,6 +6,7 @@ import { StandardLazyRequest, StandardResponse } from '@orpc/standard-server';
6
6
  export { a as StandardOpenAPIHandler, S as StandardOpenAPIHandlerOptions } from '../../shared/openapi.D3j94c9n.js';
7
7
 
8
8
  declare class StandardOpenAPICodec implements StandardCodec {
9
+ #private;
9
10
  private readonly serializer;
10
11
  constructor(serializer: StandardOpenAPISerializer);
11
12
  decode(request: StandardLazyRequest, params: StandardParams | undefined, procedure: AnyProcedure): Promise<unknown>;
@@ -1,6 +1,7 @@
1
- export { S as StandardOpenAPICodec, a as StandardOpenAPIHandler, b as StandardOpenAPIMatcher, d as decodeParams, t as toRou3Pattern } from '../../shared/openapi.p5tsmBXx.mjs';
1
+ export { S as StandardOpenAPICodec, a as StandardOpenAPIHandler, b as StandardOpenAPIMatcher, d as decodeParams, t as toRou3Pattern } from '../../shared/openapi.C_UtQ8Us.mjs';
2
2
  import '@orpc/openapi-client/standard';
3
3
  import '@orpc/server/standard';
4
+ import '@orpc/client';
4
5
  import '@orpc/contract';
5
6
  import '@orpc/shared';
6
7
  import '@orpc/client/standard';
package/dist/index.d.mts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { AnyContractProcedure } from '@orpc/contract';
2
2
  import { OpenAPIV3_1 } from 'openapi-types';
3
3
  export { OpenAPIV3_1 as OpenAPI } from 'openapi-types';
4
- export { d as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, S as SchemaConvertOptions, c as SchemaConverter } from './shared/openapi.DP97kr00.mjs';
4
+ export { d as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, S as SchemaConvertOptions, c as SchemaConverter } from './shared/openapi.CwdCLgSU.mjs';
5
5
  import { HTTPPath, HTTPMethod } from '@orpc/client';
6
6
  import { JSONSchema } from 'json-schema-typed/draft-2020-12';
7
- export { JSONSchema, Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
7
+ export { JSONSchema, ContentEncoding as JSONSchemaContentEncoding, Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
8
+ import { JsonifiedClient } from '@orpc/openapi-client';
9
+ import { AnyRouter, ClientContext, Lazyable, CreateProcedureClientOptions, InferRouterInitialContext, Schema, ErrorMap, Meta, RouterClient } from '@orpc/server';
10
+ import { MaybeOptionalOptions } from '@orpc/shared';
8
11
  import '@orpc/openapi-client/standard';
9
- import '@orpc/server';
10
- import '@orpc/shared';
11
12
 
12
13
  type OverrideOperationValue = Partial<OpenAPIV3_1.OperationObject> | ((current: OpenAPIV3_1.OperationObject, procedure: AnyContractProcedure) => OpenAPIV3_1.OperationObject);
13
14
  /**
@@ -66,6 +67,8 @@ declare function checkParamsSchema(schema: ObjectSchema, params: string[]): bool
66
67
  */
67
68
  declare function toOpenAPISchema(schema: JSONSchema): OpenAPIV3_1.SchemaObject & object;
68
69
 
70
+ declare function createJsonifiedRouterClient<T extends AnyRouter, TClientContext extends ClientContext>(router: Lazyable<T | undefined>, ...rest: MaybeOptionalOptions<CreateProcedureClientOptions<InferRouterInitialContext<T>, Schema<unknown, unknown>, ErrorMap, Meta, TClientContext>>): JsonifiedClient<RouterClient<T, TClientContext>>;
71
+
69
72
  /**
70
73
  *@internal
71
74
  */
@@ -87,10 +90,16 @@ declare function separateObjectSchema(schema: ObjectSchema, separatedProperties:
87
90
  */
88
91
  declare function filterSchemaBranches(schema: JSONSchema, check: (schema: JSONSchema) => boolean, matches?: JSONSchema[]): [matches: JSONSchema[], rest: JSONSchema | undefined];
89
92
  declare function applySchemaOptionality(required: boolean, schema: JSONSchema): JSONSchema;
93
+ /**
94
+ * Takes a JSON schema and, if it's primarily a union type (anyOf, oneOf),
95
+ * recursively expands it into an array of its constituent, non-union base schemas.
96
+ * If the schema is not a simple union or is a base type, it's returned as a single-element array.
97
+ */
98
+ declare function expandUnionSchema(schema: JSONSchema): JSONSchema[];
90
99
 
91
100
  declare const oo: {
92
101
  spec: typeof customOpenAPIOperation;
93
102
  };
94
103
 
95
- export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, customOpenAPIOperation, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
104
+ export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
96
105
  export type { FileSchema, ObjectSchema, OverrideOperationValue };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { AnyContractProcedure } from '@orpc/contract';
2
2
  import { OpenAPIV3_1 } from 'openapi-types';
3
3
  export { OpenAPIV3_1 as OpenAPI } from 'openapi-types';
4
- export { d as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, S as SchemaConvertOptions, c as SchemaConverter } from './shared/openapi.DP97kr00.js';
4
+ export { d as CompositeSchemaConverter, C as ConditionalSchemaConverter, b as OpenAPIGenerator, a as OpenAPIGeneratorGenerateOptions, O as OpenAPIGeneratorOptions, S as SchemaConvertOptions, c as SchemaConverter } from './shared/openapi.CwdCLgSU.js';
5
5
  import { HTTPPath, HTTPMethod } from '@orpc/client';
6
6
  import { JSONSchema } from 'json-schema-typed/draft-2020-12';
7
- export { JSONSchema, Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
7
+ export { JSONSchema, ContentEncoding as JSONSchemaContentEncoding, Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
8
+ import { JsonifiedClient } from '@orpc/openapi-client';
9
+ import { AnyRouter, ClientContext, Lazyable, CreateProcedureClientOptions, InferRouterInitialContext, Schema, ErrorMap, Meta, RouterClient } from '@orpc/server';
10
+ import { MaybeOptionalOptions } from '@orpc/shared';
8
11
  import '@orpc/openapi-client/standard';
9
- import '@orpc/server';
10
- import '@orpc/shared';
11
12
 
12
13
  type OverrideOperationValue = Partial<OpenAPIV3_1.OperationObject> | ((current: OpenAPIV3_1.OperationObject, procedure: AnyContractProcedure) => OpenAPIV3_1.OperationObject);
13
14
  /**
@@ -66,6 +67,8 @@ declare function checkParamsSchema(schema: ObjectSchema, params: string[]): bool
66
67
  */
67
68
  declare function toOpenAPISchema(schema: JSONSchema): OpenAPIV3_1.SchemaObject & object;
68
69
 
70
+ declare function createJsonifiedRouterClient<T extends AnyRouter, TClientContext extends ClientContext>(router: Lazyable<T | undefined>, ...rest: MaybeOptionalOptions<CreateProcedureClientOptions<InferRouterInitialContext<T>, Schema<unknown, unknown>, ErrorMap, Meta, TClientContext>>): JsonifiedClient<RouterClient<T, TClientContext>>;
71
+
69
72
  /**
70
73
  *@internal
71
74
  */
@@ -87,10 +90,16 @@ declare function separateObjectSchema(schema: ObjectSchema, separatedProperties:
87
90
  */
88
91
  declare function filterSchemaBranches(schema: JSONSchema, check: (schema: JSONSchema) => boolean, matches?: JSONSchema[]): [matches: JSONSchema[], rest: JSONSchema | undefined];
89
92
  declare function applySchemaOptionality(required: boolean, schema: JSONSchema): JSONSchema;
93
+ /**
94
+ * Takes a JSON schema and, if it's primarily a union type (anyOf, oneOf),
95
+ * recursively expands it into an array of its constituent, non-union base schemas.
96
+ * If the schema is not a simple union or is a base type, it's returned as a single-element array.
97
+ */
98
+ declare function expandUnionSchema(schema: JSONSchema): JSONSchema[];
90
99
 
91
100
  declare const oo: {
92
101
  spec: typeof customOpenAPIOperation;
93
102
  };
94
103
 
95
- export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, customOpenAPIOperation, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
104
+ export { LOGIC_KEYWORDS, applyCustomOpenAPIOperation, applySchemaOptionality, checkParamsSchema, createJsonifiedRouterClient, customOpenAPIOperation, expandUnionSchema, filterSchemaBranches, getCustomOpenAPIOperation, isAnySchema, isFileSchema, isObjectSchema, oo, separateObjectSchema, toOpenAPIContent, toOpenAPIEventIteratorContent, toOpenAPIMethod, toOpenAPIParameters, toOpenAPIPath, toOpenAPISchema };
96
105
  export type { FileSchema, ObjectSchema, OverrideOperationValue };
package/dist/index.mjs CHANGED
@@ -1,15 +1,41 @@
1
- import { c as customOpenAPIOperation } from './shared/openapi.fMEQd3Yd.mjs';
2
- export { C as CompositeSchemaConverter, L as LOGIC_KEYWORDS, O as OpenAPIGenerator, a as applyCustomOpenAPIOperation, n as applySchemaOptionality, h as checkParamsSchema, m as filterSchemaBranches, g as getCustomOpenAPIOperation, l as isAnySchema, j as isFileSchema, k as isObjectSchema, s as separateObjectSchema, d as toOpenAPIContent, e as toOpenAPIEventIteratorContent, b as toOpenAPIMethod, f as toOpenAPIParameters, t as toOpenAPIPath, i as toOpenAPISchema } from './shared/openapi.fMEQd3Yd.mjs';
3
- export { Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
4
- import '@orpc/client';
1
+ import { c as customOpenAPIOperation } from './shared/openapi.PDTdnRIU.mjs';
2
+ export { C as CompositeSchemaConverter, L as LOGIC_KEYWORDS, O as OpenAPIGenerator, a as applyCustomOpenAPIOperation, n as applySchemaOptionality, h as checkParamsSchema, o as expandUnionSchema, m as filterSchemaBranches, g as getCustomOpenAPIOperation, l as isAnySchema, j as isFileSchema, k as isObjectSchema, s as separateObjectSchema, d as toOpenAPIContent, e as toOpenAPIEventIteratorContent, b as toOpenAPIMethod, f as toOpenAPIParameters, t as toOpenAPIPath, i as toOpenAPISchema } from './shared/openapi.PDTdnRIU.mjs';
3
+ import { createORPCErrorFromJson } from '@orpc/client';
4
+ import { StandardOpenAPISerializer, StandardOpenAPIJsonSerializer, StandardBracketNotationSerializer } from '@orpc/openapi-client/standard';
5
+ import { ORPCError, createRouterClient } from '@orpc/server';
6
+ import { resolveMaybeOptionalOptions } from '@orpc/shared';
7
+ export { ContentEncoding as JSONSchemaContentEncoding, Format as JSONSchemaFormat } from 'json-schema-typed/draft-2020-12';
5
8
  import '@orpc/client/standard';
6
9
  import '@orpc/contract';
7
- import '@orpc/openapi-client/standard';
8
- import '@orpc/server';
9
- import '@orpc/shared';
10
+
11
+ function createJsonifiedRouterClient(router, ...rest) {
12
+ const options = resolveMaybeOptionalOptions(rest);
13
+ const serializer = new StandardOpenAPISerializer(new StandardOpenAPIJsonSerializer(), new StandardBracketNotationSerializer());
14
+ options.interceptors ??= [];
15
+ options.interceptors.unshift(async (options2) => {
16
+ try {
17
+ return serializer.deserialize(
18
+ serializer.serialize(
19
+ await options2.next()
20
+ )
21
+ );
22
+ } catch (e) {
23
+ if (e instanceof ORPCError) {
24
+ throw createORPCErrorFromJson(serializer.deserialize(
25
+ serializer.serialize(
26
+ e.toJSON(),
27
+ { outputFormat: "plain" }
28
+ )
29
+ ));
30
+ }
31
+ throw e;
32
+ }
33
+ });
34
+ return createRouterClient(router, options);
35
+ }
10
36
 
11
37
  const oo = {
12
38
  spec: customOpenAPIOperation
13
39
  };
14
40
 
15
- export { customOpenAPIOperation, oo };
41
+ export { createJsonifiedRouterClient, customOpenAPIOperation, oo };
@@ -1,10 +1,10 @@
1
1
  import { Context, HTTPPath, Router } from '@orpc/server';
2
2
  import { StandardHandlerInterceptorOptions, StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
3
- import { Value } from '@orpc/shared';
4
- import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.DP97kr00.mjs';
3
+ import { Value, Promisable } from '@orpc/shared';
4
+ import { OpenAPIV3_1 } from 'openapi-types';
5
+ import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.CwdCLgSU.mjs';
5
6
  import '@orpc/contract';
6
7
  import '@orpc/openapi-client/standard';
7
- import 'openapi-types';
8
8
  import 'json-schema-typed/draft-2020-12';
9
9
 
10
10
  interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGeneratorOptions {
@@ -12,7 +12,7 @@ interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGenera
12
12
  * Options to pass to the OpenAPI generate.
13
13
  *
14
14
  */
15
- specGenerateOptions?: Value<OpenAPIGeneratorGenerateOptions, [StandardHandlerInterceptorOptions<T>]>;
15
+ specGenerateOptions?: Value<Promisable<OpenAPIGeneratorGenerateOptions>, [StandardHandlerInterceptorOptions<T>]>;
16
16
  /**
17
17
  * The URL path at which to serve the OpenAPI JSON.
18
18
  *
@@ -30,27 +30,27 @@ interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGenera
30
30
  *
31
31
  * @default 'API Reference'
32
32
  */
33
- docsTitle?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
33
+ docsTitle?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
34
34
  /**
35
35
  * Arbitrary configuration object for the UI.
36
36
  */
37
- docsConfig?: Value<object, [StandardHandlerInterceptorOptions<T>]>;
37
+ docsConfig?: Value<Promisable<Record<string, unknown>>, [StandardHandlerInterceptorOptions<T>]>;
38
38
  /**
39
39
  * HTML to inject into the <head> of the docs page.
40
40
  *
41
41
  * @default ''
42
42
  */
43
- docsHead?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
43
+ docsHead?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
44
44
  /**
45
45
  * URL of the external script bundle for the reference UI.
46
46
  *
47
47
  * @default 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'
48
48
  */
49
- docsScriptUrl?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
49
+ docsScriptUrl?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
50
50
  /**
51
51
  * Override function to generate the full HTML for the docs page.
52
52
  */
53
- renderDocsHtml?: (specUrl: string, title: string, head: string, scriptUrl: string, config: object | undefined) => string;
53
+ renderDocsHtml?: (specUrl: string, title: string, head: string, scriptUrl: string, config: Record<string, unknown> | undefined, spec: OpenAPIV3_1.Document) => string;
54
54
  }
55
55
  declare class OpenAPIReferencePlugin<T extends Context> implements StandardHandlerPlugin<T> {
56
56
  private readonly generator;
@@ -1,10 +1,10 @@
1
1
  import { Context, HTTPPath, Router } from '@orpc/server';
2
2
  import { StandardHandlerInterceptorOptions, StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
3
- import { Value } from '@orpc/shared';
4
- import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.DP97kr00.js';
3
+ import { Value, Promisable } from '@orpc/shared';
4
+ import { OpenAPIV3_1 } from 'openapi-types';
5
+ import { O as OpenAPIGeneratorOptions, a as OpenAPIGeneratorGenerateOptions } from '../shared/openapi.CwdCLgSU.js';
5
6
  import '@orpc/contract';
6
7
  import '@orpc/openapi-client/standard';
7
- import 'openapi-types';
8
8
  import 'json-schema-typed/draft-2020-12';
9
9
 
10
10
  interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGeneratorOptions {
@@ -12,7 +12,7 @@ interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGenera
12
12
  * Options to pass to the OpenAPI generate.
13
13
  *
14
14
  */
15
- specGenerateOptions?: Value<OpenAPIGeneratorGenerateOptions, [StandardHandlerInterceptorOptions<T>]>;
15
+ specGenerateOptions?: Value<Promisable<OpenAPIGeneratorGenerateOptions>, [StandardHandlerInterceptorOptions<T>]>;
16
16
  /**
17
17
  * The URL path at which to serve the OpenAPI JSON.
18
18
  *
@@ -30,27 +30,27 @@ interface OpenAPIReferencePluginOptions<T extends Context> extends OpenAPIGenera
30
30
  *
31
31
  * @default 'API Reference'
32
32
  */
33
- docsTitle?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
33
+ docsTitle?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
34
34
  /**
35
35
  * Arbitrary configuration object for the UI.
36
36
  */
37
- docsConfig?: Value<object, [StandardHandlerInterceptorOptions<T>]>;
37
+ docsConfig?: Value<Promisable<Record<string, unknown>>, [StandardHandlerInterceptorOptions<T>]>;
38
38
  /**
39
39
  * HTML to inject into the <head> of the docs page.
40
40
  *
41
41
  * @default ''
42
42
  */
43
- docsHead?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
43
+ docsHead?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
44
44
  /**
45
45
  * URL of the external script bundle for the reference UI.
46
46
  *
47
47
  * @default 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'
48
48
  */
49
- docsScriptUrl?: Value<string, [StandardHandlerInterceptorOptions<T>]>;
49
+ docsScriptUrl?: Value<Promisable<string>, [StandardHandlerInterceptorOptions<T>]>;
50
50
  /**
51
51
  * Override function to generate the full HTML for the docs page.
52
52
  */
53
- renderDocsHtml?: (specUrl: string, title: string, head: string, scriptUrl: string, config: object | undefined) => string;
53
+ renderDocsHtml?: (specUrl: string, title: string, head: string, scriptUrl: string, config: Record<string, unknown> | undefined, spec: OpenAPIV3_1.Document) => string;
54
54
  }
55
55
  declare class OpenAPIReferencePlugin<T extends Context> implements StandardHandlerPlugin<T> {
56
56
  private readonly generator;
@@ -1,5 +1,5 @@
1
- import { stringifyJSON, value } from '@orpc/shared';
2
- import { O as OpenAPIGenerator } from '../shared/openapi.fMEQd3Yd.mjs';
1
+ import { stringifyJSON, once, value } from '@orpc/shared';
2
+ import { O as OpenAPIGenerator } from '../shared/openapi.PDTdnRIU.mjs';
3
3
  import '@orpc/client';
4
4
  import '@orpc/client/standard';
5
5
  import '@orpc/contract';
@@ -27,25 +27,32 @@ class OpenAPIReferencePlugin {
27
27
  this.specPath = options.specPath ?? "/spec.json";
28
28
  this.generator = new OpenAPIGenerator(options);
29
29
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
30
- this.renderDocsHtml = options.renderDocsHtml ?? ((specUrl, title, head, scriptUrl, config) => `
31
- <!doctype html>
32
- <html>
33
- <head>
34
- <meta charset="utf-8" />
35
- <meta name="viewport" content="width=device-width, initial-scale=1" />
36
- <title>${esc(title)}</title>
37
- ${head}
38
- </head>
39
- <body>
40
- <script
41
- id="api-reference"
42
- data-url="${esc(specUrl)}"
43
- ${config !== void 0 ? `data-configuration="${esc(stringifyJSON(config))}"` : ""}
44
- ><\/script>
45
- <script src="${esc(scriptUrl)}"><\/script>
46
- </body>
47
- </html>
48
- `);
30
+ this.renderDocsHtml = options.renderDocsHtml ?? ((specUrl, title, head, scriptUrl, config, spec) => {
31
+ const finalConfig = {
32
+ content: stringifyJSON(spec),
33
+ ...config
34
+ };
35
+ return `
36
+ <!doctype html>
37
+ <html>
38
+ <head>
39
+ <meta charset="utf-8" />
40
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
41
+ <title>${esc(title)}</title>
42
+ ${head}
43
+ </head>
44
+ <body>
45
+ <div id="app" data-config="${esc(stringifyJSON(finalConfig))}"></div>
46
+
47
+ <script src="${esc(scriptUrl)}"><\/script>
48
+
49
+ <script>
50
+ Scalar.createApiReference('#app', JSON.parse(document.getElementById('app').dataset.config))
51
+ <\/script>
52
+ </body>
53
+ </html>
54
+ `;
55
+ });
49
56
  }
50
57
  init(options, router) {
51
58
  options.interceptors ??= [];
@@ -58,11 +65,14 @@ class OpenAPIReferencePlugin {
58
65
  const requestPathname = options2.request.url.pathname.replace(/\/$/, "") || "/";
59
66
  const docsUrl = new URL(`${prefix}${this.docsPath}`.replace(/\/$/, ""), options2.request.url.origin);
60
67
  const specUrl = new URL(`${prefix}${this.specPath}`.replace(/\/$/, ""), options2.request.url.origin);
61
- if (requestPathname === specUrl.pathname) {
62
- const spec = await this.generator.generate(router, {
68
+ const generateSpec = once(async () => {
69
+ return await this.generator.generate(router, {
63
70
  servers: [{ url: new URL(prefix, options2.request.url.origin).toString() }],
64
71
  ...await value(this.specGenerateOptions, options2)
65
72
  });
73
+ });
74
+ if (requestPathname === specUrl.pathname) {
75
+ const spec = await generateSpec();
66
76
  return {
67
77
  matched: true,
68
78
  response: {
@@ -78,7 +88,8 @@ class OpenAPIReferencePlugin {
78
88
  await value(this.docsTitle, options2),
79
89
  await value(this.docsHead, options2),
80
90
  await value(this.docsScriptUrl, options2),
81
- await value(this.docsConfig, options2)
91
+ await value(this.docsConfig, options2),
92
+ await generateSpec()
82
93
  );
83
94
  return {
84
95
  matched: true,
@@ -1,7 +1,8 @@
1
1
  import { standardizeHTTPPath, StandardOpenAPIJsonSerializer, StandardBracketNotationSerializer, StandardOpenAPISerializer } from '@orpc/openapi-client/standard';
2
2
  import { StandardHandler } from '@orpc/server/standard';
3
+ import { isORPCErrorStatus } from '@orpc/client';
3
4
  import { fallbackContractConfig } from '@orpc/contract';
4
- import { isObject } from '@orpc/shared';
5
+ import { isObject, stringifyJSON } from '@orpc/shared';
5
6
  import { toHttpPath } from '@orpc/client/standard';
6
7
  import { traverseContractProcedures, isProcedure, getLazyMeta, unlazy, getRouter, createContractedProcedure } from '@orpc/server';
7
8
  import { createRouter, addRoute, findRoute } from 'rou3';
@@ -52,13 +53,21 @@ class StandardOpenAPICodec {
52
53
  body: this.serializer.serialize(output)
53
54
  };
54
55
  }
55
- if (!isObject(output)) {
56
- throw new Error(
57
- 'Invalid output structure for "detailed" output. Expected format: { body: any, headers?: Record<string, string | string[] | undefined> }'
58
- );
56
+ if (!this.#isDetailedOutput(output)) {
57
+ throw new Error(`
58
+ Invalid "detailed" output structure:
59
+ \u2022 Expected an object with optional properties:
60
+ - status (number 200-399)
61
+ - headers (Record<string, string | string[]>)
62
+ - body (any)
63
+ \u2022 No extra keys allowed.
64
+
65
+ Actual value:
66
+ ${stringifyJSON(output)}
67
+ `);
59
68
  }
60
69
  return {
61
- status: successStatus,
70
+ status: output.status ?? successStatus,
62
71
  headers: output.headers ?? {},
63
72
  body: this.serializer.serialize(output.body)
64
73
  };
@@ -70,6 +79,18 @@ class StandardOpenAPICodec {
70
79
  body: this.serializer.serialize(error.toJSON(), { outputFormat: "plain" })
71
80
  };
72
81
  }
82
+ #isDetailedOutput(output) {
83
+ if (!isObject(output)) {
84
+ return false;
85
+ }
86
+ if (output.headers && !isObject(output.headers)) {
87
+ return false;
88
+ }
89
+ if (output.status !== void 0 && (typeof output.status !== "number" || !Number.isInteger(output.status) || isORPCErrorStatus(output.status))) {
90
+ return false;
91
+ }
92
+ return true;
93
+ }
73
94
  }
74
95
 
75
96
  function toRou3Pattern(path) {
@@ -1,6 +1,6 @@
1
- import { AnySchema, AnyContractRouter } from '@orpc/contract';
1
+ import { AnySchema, AnyContractProcedure, AnyContractRouter } from '@orpc/contract';
2
2
  import { StandardOpenAPIJsonSerializerOptions } from '@orpc/openapi-client/standard';
3
- import { AnyRouter } from '@orpc/server';
3
+ import { AnyProcedure, AnyRouter } from '@orpc/server';
4
4
  import { OpenAPIV3_1 } from 'openapi-types';
5
5
  import { Promisable } from '@orpc/shared';
6
6
  import { JSONSchema } from 'json-schema-typed/draft-2020-12';
@@ -24,6 +24,12 @@ interface OpenAPIGeneratorOptions extends StandardOpenAPIJsonSerializerOptions {
24
24
  schemaConverters?: ConditionalSchemaConverter[];
25
25
  }
26
26
  interface OpenAPIGeneratorGenerateOptions extends Partial<Omit<OpenAPIV3_1.Document, 'openapi'>> {
27
+ /**
28
+ * Exclude procedures from the OpenAPI specification.
29
+ *
30
+ * @default () => false
31
+ */
32
+ exclude?: (procedure: AnyProcedure | AnyContractProcedure, path: readonly string[]) => boolean;
27
33
  }
28
34
  /**
29
35
  * The generator that converts oRPC routers/contracts to OpenAPI specifications.
@@ -1,6 +1,6 @@
1
- import { AnySchema, AnyContractRouter } from '@orpc/contract';
1
+ import { AnySchema, AnyContractProcedure, AnyContractRouter } from '@orpc/contract';
2
2
  import { StandardOpenAPIJsonSerializerOptions } from '@orpc/openapi-client/standard';
3
- import { AnyRouter } from '@orpc/server';
3
+ import { AnyProcedure, AnyRouter } from '@orpc/server';
4
4
  import { OpenAPIV3_1 } from 'openapi-types';
5
5
  import { Promisable } from '@orpc/shared';
6
6
  import { JSONSchema } from 'json-schema-typed/draft-2020-12';
@@ -24,6 +24,12 @@ interface OpenAPIGeneratorOptions extends StandardOpenAPIJsonSerializerOptions {
24
24
  schemaConverters?: ConditionalSchemaConverter[];
25
25
  }
26
26
  interface OpenAPIGeneratorGenerateOptions extends Partial<Omit<OpenAPIV3_1.Document, 'openapi'>> {
27
+ /**
28
+ * Exclude procedures from the OpenAPI specification.
29
+ *
30
+ * @default () => false
31
+ */
32
+ exclude?: (procedure: AnyProcedure | AnyContractProcedure, path: readonly string[]) => boolean;
27
33
  }
28
34
  /**
29
35
  * The generator that converts oRPC routers/contracts to OpenAPI specifications.
@@ -1,9 +1,9 @@
1
- import { fallbackORPCErrorStatus, fallbackORPCErrorMessage } from '@orpc/client';
1
+ import { isORPCErrorStatus, fallbackORPCErrorStatus, fallbackORPCErrorMessage } from '@orpc/client';
2
2
  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, findDeepMatches, toArray, clone } from '@orpc/shared';
6
+ import { isObject, findDeepMatches, toArray, clone, stringifyJSON } from '@orpc/shared';
7
7
  import 'json-schema-typed/draft-2020-12';
8
8
 
9
9
  const OPERATION_EXTENDER_SYMBOL = Symbol("ORPC_OPERATION_EXTENDER");
@@ -184,6 +184,18 @@ function applySchemaOptionality(required, schema) {
184
184
  ]
185
185
  };
186
186
  }
187
+ function expandUnionSchema(schema) {
188
+ if (typeof schema === "object") {
189
+ for (const keyword of ["anyOf", "oneOf"]) {
190
+ if (schema[keyword] && Object.keys(schema).every(
191
+ (k) => k === keyword || !LOGIC_KEYWORDS.includes(k)
192
+ )) {
193
+ return schema[keyword].flatMap((s) => expandUnionSchema(s));
194
+ }
195
+ }
196
+ }
197
+ return [schema];
198
+ }
187
199
 
188
200
  function toOpenAPIPath(path) {
189
201
  return standardizeHTTPPath(path).replace(/\/\{\+([^}]+)\}/g, "/{$1}");
@@ -312,14 +324,18 @@ class OpenAPIGenerator {
312
324
  * @see {@link https://orpc.unnoq.com/docs/openapi/openapi-specification OpenAPI Specification Docs}
313
325
  */
314
326
  async generate(router, options = {}) {
327
+ const exclude = options.exclude ?? (() => false);
315
328
  const doc = {
316
329
  ...clone(options),
317
330
  info: options.info ?? { title: "API Reference", version: "0.0.0" },
318
- openapi: "3.1.1"
331
+ openapi: "3.1.1",
332
+ exclude: void 0
319
333
  };
320
334
  const contracts = [];
321
335
  await resolveContractProcedures({ path: [], router }, ({ contract, path }) => {
322
- contracts.push({ contract, path });
336
+ if (!exclude(contract, path)) {
337
+ contracts.push({ contract, path });
338
+ }
323
339
  });
324
340
  const errors = [];
325
341
  for (const { contract, path } of contracts) {
@@ -459,36 +475,71 @@ ${errors.join("\n\n")}`
459
475
  return;
460
476
  }
461
477
  const [required, json] = await this.converter.convert(outputSchema, { strategy: "output" });
462
- ref.responses ??= {};
463
- ref.responses[status] = {
464
- description
465
- };
466
478
  if (outputStructure === "compact") {
479
+ ref.responses ??= {};
480
+ ref.responses[status] = {
481
+ description
482
+ };
467
483
  ref.responses[status].content = toOpenAPIContent(applySchemaOptionality(required, json));
468
484
  return;
469
485
  }
470
- const error = new OpenAPIGeneratorError(
471
- 'When output structure is "detailed", output schema must satisfy: { headers?: Record<string, unknown>, body?: unknown }'
472
- );
473
- if (!isObjectSchema(json)) {
474
- throw error;
475
- }
476
- if (json.properties?.headers !== void 0) {
477
- if (!isObjectSchema(json.properties.headers)) {
486
+ const handledStatuses = /* @__PURE__ */ new Set();
487
+ for (const item of expandUnionSchema(json)) {
488
+ const error = new OpenAPIGeneratorError(`
489
+ When output structure is "detailed", output schema must satisfy:
490
+ {
491
+ status?: number, // must be a literal number and in the range of 200-399
492
+ headers?: Record<string, unknown>,
493
+ body?: unknown
494
+ }
495
+
496
+ But got: ${stringifyJSON(item)}
497
+ `);
498
+ if (!isObjectSchema(item)) {
478
499
  throw error;
479
500
  }
480
- for (const key in json.properties.headers.properties) {
481
- ref.responses[status].headers ??= {};
482
- ref.responses[status].headers[key] = {
483
- schema: toOpenAPISchema(json.properties.headers.properties[key]),
484
- required: json.properties.headers.required?.includes(key)
485
- };
501
+ let schemaStatus;
502
+ let schemaDescription;
503
+ if (item.properties?.status !== void 0) {
504
+ if (typeof item.properties.status !== "object" || item.properties.status.const === void 0 || typeof item.properties.status.const !== "number" || !Number.isInteger(item.properties.status.const) || isORPCErrorStatus(item.properties.status.const)) {
505
+ throw error;
506
+ }
507
+ schemaStatus = item.properties.status.const;
508
+ schemaDescription = item.properties.status.description;
509
+ }
510
+ const itemStatus = schemaStatus ?? status;
511
+ const itemDescription = schemaDescription ?? description;
512
+ if (handledStatuses.has(itemStatus)) {
513
+ throw new OpenAPIGeneratorError(`
514
+ When output structure is "detailed", each success status must be unique.
515
+ But got status: ${itemStatus} used more than once.
516
+ `);
517
+ }
518
+ handledStatuses.add(itemStatus);
519
+ ref.responses ??= {};
520
+ ref.responses[itemStatus] = {
521
+ description: itemDescription
522
+ };
523
+ if (item.properties?.headers !== void 0) {
524
+ if (!isObjectSchema(item.properties.headers)) {
525
+ throw error;
526
+ }
527
+ for (const key in item.properties.headers.properties) {
528
+ const headerSchema = item.properties.headers.properties[key];
529
+ if (headerSchema !== void 0) {
530
+ ref.responses[itemStatus].headers ??= {};
531
+ ref.responses[itemStatus].headers[key] = {
532
+ schema: toOpenAPISchema(headerSchema),
533
+ required: item.properties.headers.required?.includes(key)
534
+ };
535
+ }
536
+ }
537
+ }
538
+ if (item.properties?.body !== void 0) {
539
+ ref.responses[itemStatus].content = toOpenAPIContent(
540
+ applySchemaOptionality(item.required?.includes("body") ?? false, item.properties.body)
541
+ );
486
542
  }
487
- }
488
- if (json.properties?.body !== void 0) {
489
- ref.responses[status].content = toOpenAPIContent(
490
- applySchemaOptionality(json.required?.includes("body") ?? false, json.properties.body)
491
- );
492
543
  }
493
544
  }
494
545
  async #errorResponse(ref, def) {
@@ -541,4 +592,4 @@ ${errors.join("\n\n")}`
541
592
  }
542
593
  }
543
594
 
544
- 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, separateObjectSchema as s, toOpenAPIPath as t };
595
+ 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, separateObjectSchema as s, toOpenAPIPath as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@orpc/openapi",
3
3
  "type": "module",
4
- "version": "1.1.1",
4
+ "version": "1.3.0",
5
5
  "license": "MIT",
6
6
  "homepage": "https://orpc.unnoq.com",
7
7
  "repository": {
@@ -47,15 +47,15 @@
47
47
  "json-schema-typed": "^8.0.1",
48
48
  "openapi-types": "^12.1.3",
49
49
  "rou3": "^0.6.0",
50
- "@orpc/client": "1.1.1",
51
- "@orpc/openapi-client": "1.1.1",
52
- "@orpc/server": "1.1.1",
53
- "@orpc/contract": "1.1.1",
54
- "@orpc/standard-server": "1.1.1",
55
- "@orpc/shared": "1.1.1"
50
+ "@orpc/client": "1.3.0",
51
+ "@orpc/contract": "1.3.0",
52
+ "@orpc/server": "1.3.0",
53
+ "@orpc/shared": "1.3.0",
54
+ "@orpc/standard-server": "1.3.0",
55
+ "@orpc/openapi-client": "1.3.0"
56
56
  },
57
57
  "devDependencies": {
58
- "zod": "^3.24.2"
58
+ "zod": "^3.25.11"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "unbuild",