@orpc/openapi 1.14.11 → 1.14.13

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 (48) hide show
  1. package/README.md +71 -52
  2. package/dist/adapters/aws-lambda/index.d.mts +20 -0
  3. package/dist/adapters/aws-lambda/index.d.ts +20 -0
  4. package/dist/adapters/aws-lambda/index.mjs +18 -0
  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 +16 -20
  9. package/dist/adapters/fetch/index.d.ts +16 -20
  10. package/dist/adapters/fetch/index.mjs +8 -24
  11. package/dist/adapters/node/index.d.mts +13 -8
  12. package/dist/adapters/node/index.d.ts +13 -8
  13. package/dist/adapters/node/index.mjs +7 -10
  14. package/dist/adapters/standard/index.d.mts +16 -46
  15. package/dist/adapters/standard/index.d.ts +16 -46
  16. package/dist/adapters/standard/index.mjs +6 -9
  17. package/dist/index.d.mts +105 -117
  18. package/dist/index.d.ts +105 -117
  19. package/dist/index.mjs +32 -850
  20. package/dist/plugins/index.d.mts +51 -55
  21. package/dist/plugins/index.d.ts +51 -55
  22. package/dist/plugins/index.mjs +142 -147
  23. package/dist/shared/openapi.BGy4N6eR.d.mts +120 -0
  24. package/dist/shared/openapi.BGy4N6eR.d.ts +120 -0
  25. package/dist/shared/openapi.BwdtJjDu.mjs +878 -0
  26. package/dist/shared/openapi.CdeSk3f7.d.mts +54 -0
  27. package/dist/shared/openapi.CdeSk3f7.d.ts +54 -0
  28. package/dist/shared/openapi.DPiCV5hl.mjs +208 -0
  29. package/package.json +24 -46
  30. package/dist/extensions/route.d.mts +0 -43
  31. package/dist/extensions/route.d.ts +0 -43
  32. package/dist/extensions/route.mjs +0 -14
  33. package/dist/helpers/index.d.mts +0 -51
  34. package/dist/helpers/index.d.ts +0 -51
  35. package/dist/helpers/index.mjs +0 -39
  36. package/dist/shared/openapi.B6hEbRyF.d.ts +0 -83
  37. package/dist/shared/openapi.B9PQzqBn.mjs +0 -49
  38. package/dist/shared/openapi.BOOA-bde.d.mts +0 -142
  39. package/dist/shared/openapi.BOOA-bde.d.ts +0 -142
  40. package/dist/shared/openapi.BafbB3uM.d.mts +0 -83
  41. package/dist/shared/openapi.Bt87OzTt.mjs +0 -131
  42. package/dist/shared/openapi.ByT4oUeY.d.mts +0 -18
  43. package/dist/shared/openapi.ByT4oUeY.d.ts +0 -18
  44. package/dist/shared/openapi.C-p_Q2lb.mjs +0 -359
  45. package/dist/shared/openapi.CVgUshDP.mjs +0 -318
  46. package/dist/shared/openapi.DNNo0V-l.d.ts +0 -313
  47. package/dist/shared/openapi.hg_rhZ4x.d.mts +0 -313
  48. package/dist/shared/openapi.zZH_UksW.mjs +0 -278
package/dist/index.mjs CHANGED
@@ -1,859 +1,41 @@
1
- export { B as BracketNotationSerializer } from './shared/openapi.Bt87OzTt.mjs';
2
- import { createContractClientFactory, getAsyncIteratorObjectSchemaDetails, ProcedureContract, resolveMetaPlugins } from '@orpc/contract';
3
- import { D as DEFAULT_OPENAPI_METHOD, a as DEFAULT_OPENAPI_INPUT_STRUCTURE, c as DEFAULT_OPENAPI_SUCCESS_DESCRIPTION, b as DEFAULT_OPENAPI_OUTPUT_STRUCTURE, i as isBodylessMethod, O as OpenAPISerializer, g as getDynamicPathParams } from './shared/openapi.zZH_UksW.mjs';
4
- export { d as OpenAPIJsonSerializer } from './shared/openapi.zZH_UksW.mjs';
5
- import { g as getOpenAPIMeta, o as openapi } from './shared/openapi.B9PQzqBn.mjs';
6
- import { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
7
- export { COMMON_ERROR_STATUS_MAP } from '@orpc/client';
8
- import { encodeJsonPointerSegment, ensureJsonSchemaObject, mapJsonSchemaRefs, decodeJsonPointerSegment, isUnconstrainedSchema, combineJsonSchemasWithComposition, extractJsonObjectSchemaEntries, combineJsonObjectSchemaEntries, flattenJsonUnionSchema, isJsonPrimitiveSchema, matchArrayableJsonSchema, isJsonFileSchema, DelegatingJsonSchemaConverter, StandardJsonSchemaConverter } from '@orpc/json-schema';
9
- import { DEFAULT_SUCCESS_STATUS, DEFAULT_ERROR_STATUS, walkProcedureContractsAsync } from '@orpc/server';
10
- import { isDeepEqual, value, findDeepMatches, isPlainObject, stringifyJSON, toArray, clone, pathToHttpPath, mergeHttpPath, isTypescriptObject } from '@orpc/shared';
11
- import '@standardserver/core';
1
+ import { c as customOpenAPIOperation } from './shared/openapi.BwdtJjDu.mjs';
2
+ export { C as CompositeSchemaConverter, L as LOGIC_KEYWORDS, O as OpenAPIGenerator, a as applyCustomOpenAPIOperation, p as applySchemaOptionality, h as checkParamsSchema, u as expandArrayableSchema, q as expandUnionSchema, o as filterSchemaBranches, g as getCustomOpenAPIOperation, l as isAnySchema, j as isFileSchema, m as isNeverSchema, k as isObjectSchema, v as isPrimitiveSchema, r as resolveOpenAPIJsonSchemaRef, n as separateObjectSchema, s as simplifyComposedObjectJsonSchemasAndRefs, d as toOpenAPIContent, e as toOpenAPIEventIteratorContent, b as toOpenAPIMethod, f as toOpenAPIParameters, t as toOpenAPIPath, i as toOpenAPISchema } from './shared/openapi.BwdtJjDu.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, TypeName as JSONSchemaTypeName } from 'json-schema-typed/draft-2020-12';
8
+ import '@orpc/client/standard';
9
+ import '@orpc/contract';
12
10
 
13
- function createContractJsonifiedClientFactory(link, options = {}) {
14
- return createContractClientFactory(link, options);
15
- }
16
-
17
- class OpenAPIComponentRegistry {
18
- constructor(doc, customComponentName) {
19
- this.doc = doc;
20
- this.customComponentName = customComponentName;
21
- }
22
- /**
23
- * Registers `schema` as a component under `preferredName` (or an equivalent/postfixed name)
24
- * and returns a `$ref` to it.
25
- */
26
- register(preferredName, schema) {
27
- const { $defs, ...body } = schema;
28
- let defName = preferredName;
29
- if ($defs) {
30
- for (let i = 2; defName in $defs; i++) {
31
- defName = `${preferredName}${i}`;
32
- }
33
- }
34
- return this.hoistDefs({
35
- $defs: { ...$defs, [defName]: body },
36
- $ref: `#/$defs/${encodeJsonPointerSegment(defName)}`
37
- });
38
- }
39
- /**
40
- * Moves a schema's root-level `$defs` into `doc.components.schemas` and rewrites
41
- * its refs accordingly.
42
- */
43
- hoistDefs(schema, direction) {
44
- if (typeof schema !== "object" || !schema.$defs) {
45
- return schema;
46
- }
47
- const { $defs, ...rest } = schema;
48
- const defs = {};
49
- const preferredNames = {};
50
- for (const defName of Object.keys($defs)) {
51
- const defSchema = $defs[defName];
52
- if (defSchema === void 0) {
53
- continue;
54
- }
55
- const normalized = ensureJsonSchemaObject(defSchema);
56
- defs[defName] = normalized;
57
- preferredNames[defName] = this.customComponentName?.(defName, normalized) ?? defName;
58
- }
59
- const defNames = Object.keys(defs);
60
- if (defNames.length === 0) {
61
- return schema;
62
- }
63
- this.doc.components ??= {};
64
- this.doc.components.schemas ??= {};
65
- const componentsSchemas = this.doc.components.schemas;
66
- const identityRenameMap = Object.fromEntries(
67
- defNames.map((defName) => [defName, preferredNames[defName]])
68
- );
69
- const renameMap = {};
70
- const pendingSchemas = [];
71
- for (const defName of defNames) {
72
- const cleanSchema = defs[defName];
73
- const candidateSchemas = Object.fromEntries(
74
- defNames.map((currentDefName) => [
75
- preferredNames[currentDefName],
76
- rewriteComponentSchemaRefs(defs[currentDefName], {
77
- ...identityRenameMap,
78
- ...renameMap
79
- })
80
- ])
81
- );
82
- const preferredName = preferredNames[defName];
83
- const prelimSchema = candidateSchemas[preferredName];
84
- const [componentName, reuseExisting] = resolveComponentName(
85
- componentsSchemas,
86
- new Set(Object.values(renameMap)),
87
- preferredName,
88
- prelimSchema,
89
- candidateSchemas,
90
- direction
91
- );
92
- renameMap[defName] = componentName;
93
- if (!reuseExisting) {
94
- pendingSchemas.push({ cleanSchema, componentName });
95
- }
96
- }
97
- for (const { cleanSchema, componentName } of pendingSchemas) {
98
- componentsSchemas[componentName] = rewriteComponentSchemaRefs(
99
- cleanSchema,
100
- renameMap
101
- );
102
- }
103
- return rewriteComponentSchemaRefs(rest, renameMap);
104
- }
105
- toOpenAPISchema(schema, direction) {
106
- return ensureJsonSchemaObject(this.hoistDefs(schema, direction));
107
- }
108
- }
109
- function resolveComponentName(componentsSchemas, claimedNames, preferredName, schema, candidateSchemas, direction) {
110
- let mintName;
111
- for (let i = 1; ; i++) {
112
- const [componentName, mintable, tail] = componentNameCandidate(preferredName, direction, i);
113
- const existingSchema = componentsSchemas[componentName];
114
- if (existingSchema === void 0) {
115
- if (mintable && !claimedNames.has(componentName)) {
116
- mintName ??= componentName;
117
- if (tail) {
118
- return [mintName, false];
119
- }
120
- }
121
- continue;
122
- }
123
- if (areSchemasEquivalentForReuse(
124
- schema,
125
- existingSchema,
126
- schema,
127
- existingSchema,
128
- candidateSchemas,
129
- componentsSchemas,
130
- /* @__PURE__ */ new Map([[preferredName, componentName]]),
131
- /* @__PURE__ */ new Map([[componentName, preferredName]])
132
- )) {
133
- return [componentName, true];
134
- }
135
- }
136
- }
137
- function componentNameCandidate(preferredName, direction, attempt) {
138
- if (attempt === 1) {
139
- return [preferredName, true, false];
140
- }
141
- if (direction !== void 0) {
142
- if (attempt === 2) {
143
- return [`${preferredName}${direction === "input" ? "Input" : "Output"}`, true, false];
144
- }
145
- if (attempt === 3) {
146
- return [`${preferredName}${direction === "input" ? "Output" : "Input"}`, false, false];
147
- }
148
- return [`${preferredName}${attempt - 2}`, true, true];
149
- }
150
- return [`${preferredName}${attempt}`, true, true];
151
- }
152
- function definedKeysOf(object) {
153
- return Object.keys(object).filter((key) => object[key] !== void 0).sort();
154
- }
155
- function areSchemasEquivalentForReuse(candidate, existing, candidateRootSchema, existingRootSchema, candidateSchemas, existingSchemas, candidateToExistingComponentNames, existingToCandidateComponentNames, visited = /* @__PURE__ */ new WeakMap()) {
156
- if (candidate === existing) {
157
- return true;
158
- }
159
- if (typeof candidate !== typeof existing) {
160
- return false;
161
- }
162
- if (candidate === null || existing === null) {
163
- return candidate === existing;
164
- }
165
- if (typeof candidate !== "object" || typeof existing !== "object") {
166
- return isDeepEqual(candidate, existing);
167
- }
168
- const seenExisting = visited.get(candidate);
169
- if (seenExisting?.has(existing)) {
170
- return true;
171
- }
172
- if (seenExisting) {
173
- seenExisting.add(existing);
174
- } else {
175
- visited.set(candidate, new WeakSet([existing]));
176
- }
177
- if (Array.isArray(candidate) || Array.isArray(existing)) {
178
- if (!Array.isArray(candidate) || !Array.isArray(existing) || candidate.length !== existing.length) {
179
- return false;
180
- }
181
- return candidate.every((item, index) => areSchemasEquivalentForReuse(
182
- item,
183
- existing[index],
184
- candidateRootSchema,
185
- existingRootSchema,
186
- candidateSchemas,
187
- existingSchemas,
188
- candidateToExistingComponentNames,
189
- existingToCandidateComponentNames,
190
- visited
191
- ));
192
- }
193
- const candidateObject = candidate;
194
- const existingObject = existing;
195
- const candidateKeys = definedKeysOf(candidateObject);
196
- const existingKeys = definedKeysOf(existingObject);
197
- if (!isDeepEqual(candidateKeys, existingKeys)) {
198
- return false;
199
- }
200
- return candidateKeys.every((key) => {
201
- const candidateValue = candidateObject[key];
202
- const existingValue = existingObject[key];
203
- if (key === "$ref" && typeof candidateValue === "string" && typeof existingValue === "string") {
204
- return areSchemaRefsEquivalentForReuse(
205
- candidateValue,
206
- existingValue,
207
- candidateRootSchema,
208
- existingRootSchema,
209
- candidateSchemas,
210
- existingSchemas,
211
- candidateToExistingComponentNames,
212
- existingToCandidateComponentNames,
213
- visited
214
- );
215
- }
216
- return areSchemasEquivalentForReuse(
217
- candidateValue,
218
- existingValue,
219
- candidateRootSchema,
220
- existingRootSchema,
221
- candidateSchemas,
222
- existingSchemas,
223
- candidateToExistingComponentNames,
224
- existingToCandidateComponentNames,
225
- visited
226
- );
227
- });
228
- }
229
- function parseComponentRefName(ref) {
230
- if (!ref.startsWith("#/components/schemas/")) {
231
- return void 0;
232
- }
233
- return ref.slice("#/components/schemas/".length).split("/").map(decodeJsonPointerSegment).join("/");
234
- }
235
- function resolveSchemaComparisonRef(ref, rootSchema, componentsSchemas) {
236
- const localDefName = parseLocalDefRefName(ref);
237
- if (localDefName !== void 0 && typeof rootSchema === "object" && rootSchema !== null) {
238
- const localDef = rootSchema.$defs?.[localDefName];
239
- if (localDef !== void 0) {
240
- return {
241
- schema: localDef,
242
- rootSchema
243
- };
244
- }
245
- }
246
- const componentName = parseComponentRefName(ref);
247
- if (componentName !== void 0) {
248
- const componentSchema = componentsSchemas[componentName];
249
- if (componentSchema !== void 0) {
250
- return {
251
- schema: componentSchema,
252
- rootSchema: componentSchema
253
- };
254
- }
255
- }
256
- return void 0;
257
- }
258
- function areSchemaRefsEquivalentForReuse(candidateRef, existingRef, candidateRootSchema, existingRootSchema, candidateSchemas, existingSchemas, candidateToExistingComponentNames, existingToCandidateComponentNames, visited) {
259
- const candidateComponentName = parseComponentRefName(candidateRef);
260
- const existingComponentName = parseComponentRefName(existingRef);
261
- if (candidateComponentName === void 0 !== (existingComponentName === void 0)) {
262
- return false;
263
- }
264
- if (candidateComponentName !== void 0 && existingComponentName !== void 0) {
265
- const mappedExisting = candidateToExistingComponentNames.get(candidateComponentName);
266
- if (mappedExisting !== void 0 && mappedExisting !== existingComponentName) {
267
- return false;
268
- }
269
- const mappedCandidate = existingToCandidateComponentNames.get(existingComponentName);
270
- if (mappedCandidate !== void 0 && mappedCandidate !== candidateComponentName) {
271
- return false;
272
- }
273
- candidateToExistingComponentNames.set(candidateComponentName, existingComponentName);
274
- existingToCandidateComponentNames.set(existingComponentName, candidateComponentName);
275
- }
276
- const resolvedCandidate = resolveSchemaComparisonRef(candidateRef, candidateRootSchema, candidateSchemas);
277
- const resolvedExisting = resolveSchemaComparisonRef(existingRef, existingRootSchema, existingSchemas);
278
- if (resolvedCandidate === void 0 || resolvedExisting === void 0) {
279
- return candidateRef === existingRef;
280
- }
281
- return areSchemasEquivalentForReuse(
282
- resolvedCandidate.schema,
283
- resolvedExisting.schema,
284
- resolvedCandidate.rootSchema,
285
- resolvedExisting.rootSchema,
286
- candidateSchemas,
287
- existingSchemas,
288
- candidateToExistingComponentNames,
289
- existingToCandidateComponentNames,
290
- visited
291
- );
292
- }
293
- function parseLocalDefRefName(ref) {
294
- if (!ref.startsWith("#/$defs/")) {
295
- return void 0;
296
- }
297
- return ref.slice("#/$defs/".length).split("/").map(decodeJsonPointerSegment).join("/");
298
- }
299
- function rewriteComponentSchemaRefs(schema, renameMap) {
300
- return mapJsonSchemaRefs(schema, (ref) => {
301
- const refName = parseLocalDefRefName(ref);
302
- if (refName === void 0) {
303
- return ref;
304
- }
305
- const renamedName = renameMap[refName];
306
- if (renamedName === void 0) {
307
- return ref;
308
- }
309
- return `#/components/schemas/${encodeJsonPointerSegment(renamedName)}`;
310
- });
311
- }
312
-
313
- class OpenAPIGeneratorError extends TypeError {
314
- }
315
- function toOpenAPIPath(path, dynamicPathParams) {
316
- if (!dynamicPathParams?.length) {
317
- return path;
318
- }
319
- let normalized = "";
320
- let currentIndex = 0;
321
- for (const param of dynamicPathParams) {
322
- normalized += path.slice(currentIndex, param.startIndex);
323
- normalized += `{${param.parameterName}}`;
324
- currentIndex = param.startIndex + param.segment.length;
325
- }
326
- normalized += path.slice(currentIndex);
327
- return normalized;
328
- }
329
- function buildRequest(ctx, operation, def, meta, dynamicPathParams) {
330
- const method = meta?.method ?? DEFAULT_OPENAPI_METHOD;
331
- const inputStructure = meta?.inputStructure ?? DEFAULT_OPENAPI_INPUT_STRUCTURE;
332
- if (inputStructure === "compact") {
333
- const iteratorDetails = getAsyncIteratorObjectDetails(def.inputSchemas);
334
- if (iteratorDetails) {
335
- operation.requestBody = {
336
- required: true,
337
- content: toAsyncIteratorObjectContent(
338
- ctx,
339
- "input",
340
- ctx.convertSchemas(iteratorDetails[0], "input"),
341
- ctx.convertSchemas(iteratorDetails[1], "input")
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()
342
20
  )
343
- };
344
- return;
345
- }
346
- }
347
- const dynamicParams = dynamicPathParams?.map((v) => v.parameterName);
348
- const [schema, optional] = ctx.convertSchemas(def.inputSchemas, "input");
349
- if (isUnconstrainedSchema(schema) && !dynamicParams?.length) {
350
- return;
351
- }
352
- const parts = inputStructure === "compact" ? extractCompactRequestParts(schema, optional, method, dynamicParams) : extractDetailedRequestParts(schema);
353
- renderPathParameters(ctx, operation, dynamicParams, parts.paramsEntries, meta?.paramsStyles);
354
- renderQueryParameters(ctx, operation, parts.queryEntries, meta?.queryStyles);
355
- renderHeaderParameters(ctx, operation, parts.headersEntries);
356
- if (parts.bodySchema !== void 0) {
357
- operation.requestBody = {
358
- required: parts.bodyOptional ? void 0 : true,
359
- content: toBodyContent(ctx, "input", parts.bodySchema)
360
- };
361
- }
362
- }
363
- function extractCompactRequestParts(schema, optional, method, dynamicParams) {
364
- const entries = extractJsonObjectSchemaEntries(schema);
365
- if (!entries && isBodylessMethod(method)) {
366
- throw new OpenAPIGeneratorError(
367
- `method is ${method} but the input schema is not an object.
368
- ${method} sends every input field as a query parameter, so the input must be an object schema.
369
- Fix: make the input an object, or use a method with a request body (POST, PUT, PATCH, DELETE).`
370
- );
371
- }
372
- const paramsEntries = entries?.filter(([name]) => dynamicParams?.includes(name));
373
- const restEntries = entries?.filter(([name]) => !dynamicParams?.includes(name));
374
- const hasBody = !isBodylessMethod(method);
375
- const bodySchema = !hasBody ? void 0 : !dynamicParams?.length ? schema : restEntries ? combineJsonObjectSchemaEntries(restEntries) : void 0;
376
- return {
377
- paramsEntries,
378
- queryEntries: isBodylessMethod(method) ? restEntries : void 0,
379
- headersEntries: void 0,
380
- bodySchema,
381
- bodyOptional: !dynamicParams?.length ? optional : restEntries?.every(([, , optional2]) => optional2)
382
- };
383
- }
384
- function extractDetailedRequestParts(schema) {
385
- const entries = extractJsonObjectSchemaEntries(schema);
386
- if (!entries) {
387
- throw new OpenAPIGeneratorError(
388
- `inputStructure is "detailed" but the input schema is not an object.
389
- Expected an object shaped like: { params?: object, query?: object, headers?: object, body?: unknown }`
390
- );
391
- }
392
- const section = (name) => entries.find(([entryName]) => entryName === name);
393
- return {
394
- paramsEntries: extractJsonObjectSchemaEntries(section("params")?.[1] ?? false),
395
- queryEntries: extractJsonObjectSchemaEntries(section("query")?.[1] ?? false),
396
- headersEntries: extractJsonObjectSchemaEntries(section("headers")?.[1] ?? false),
397
- bodySchema: section("body")?.[1],
398
- bodyOptional: section("body")?.[2]
399
- };
400
- }
401
- function renderPathParameters(ctx, operation, dynamicParams, paramsEntries, paramsStyles) {
402
- if (!dynamicParams?.length) {
403
- return;
404
- }
405
- if (!paramsEntries) {
406
- throw new OpenAPIGeneratorError(
407
- `the route declares path params (${dynamicParams.map((p) => `{${p}}`).join(", ")}) but there is no object schema to source them from.
408
- compact mode: the input schema must be an object containing each param.
409
- detailed mode: the input's "params" section must be an object containing each param.`
410
- );
411
- }
412
- for (const name of dynamicParams) {
413
- const entry = paramsEntries.find(([entryName]) => entryName === name);
414
- if (!entry) {
415
- throw new OpenAPIGeneratorError(
416
- `dynamic param "{${name}}" is missing from the input schema.
417
- Route params: ${dynamicParams.map((p) => `{${p}}`).join(", ")}
418
- Schema keys: ${paramsEntries.map(([n]) => n).join(", ") || "(none)"}`
419
21
  );
420
- }
421
- if (entry[2]) {
422
- throw new OpenAPIGeneratorError(
423
- `dynamic param "${name}" is optional in the input schema.
424
- OpenAPI requires path params to be required. Make "${name}" required.`
425
- );
426
- }
427
- const style = paramsStyles?.[name];
428
- const parameter = {
429
- in: "path",
430
- required: true,
431
- name,
432
- schema: ctx.registry.toOpenAPISchema(entry[1], "input")
433
- };
434
- if (style === "comma-delimited-array" || style === "comma-delimited-object") {
435
- parameter.style = "simple";
436
- parameter.explode = false;
437
- }
438
- operation.parameters ??= [];
439
- operation.parameters.push(parameter);
440
- }
441
- }
442
- function renderQueryParameters(ctx, operation, queryEntries, queryStyles) {
443
- for (const [name, schema, optional] of queryEntries ?? []) {
444
- const style = queryStyles?.[name];
445
- const parameter = {
446
- in: "query",
447
- name,
448
- schema: ctx.registry.toOpenAPISchema(schema, "input"),
449
- allowEmptyValue: true,
450
- allowReserved: true
451
- };
452
- if (!optional) {
453
- parameter.required = true;
454
- }
455
- if (style === "comma-delimited-array" || style === "comma-delimited-object") {
456
- parameter.explode = false;
457
- } else if (style === "pipe-delimited-array" || style === "pipe-delimited-object") {
458
- parameter.style = "pipeDelimited";
459
- } else if (style === "space-delimited-array" || style === "space-delimited-object") {
460
- parameter.style = "spaceDelimited";
461
- } else if (style === "json") {
462
- parameter.content = {
463
- "application/json": { schema: parameter.schema }
464
- };
465
- delete parameter.schema;
466
- } else if (style === void 0) {
467
- if (flattenJsonUnionSchema(schema).some((s) => !isJsonPrimitiveSchema(s))) {
468
- const arrayable = matchArrayableJsonSchema(schema);
469
- if (!arrayable || flattenJsonUnionSchema(arrayable[0]).some((s) => !isJsonPrimitiveSchema(s))) {
470
- parameter.style = "deepObject";
471
- parameter.explode = true;
472
- }
473
- }
474
- } else ;
475
- operation.parameters ??= [];
476
- operation.parameters.push(parameter);
477
- }
478
- }
479
- function renderHeaderParameters(ctx, operation, headersEntries) {
480
- for (const [name, schema, optional] of headersEntries ?? []) {
481
- operation.parameters ??= [];
482
- operation.parameters.push({
483
- in: "header",
484
- name,
485
- required: optional ? void 0 : true,
486
- schema: ctx.registry.toOpenAPISchema(schema, "input")
487
- });
488
- }
489
- }
490
- function buildSuccessResponse(ctx, operation, def, meta) {
491
- const status = meta?.successStatus ?? DEFAULT_SUCCESS_STATUS;
492
- const description = meta?.successDescription ?? DEFAULT_OPENAPI_SUCCESS_DESCRIPTION;
493
- const outputStructure = meta?.outputStructure ?? DEFAULT_OPENAPI_OUTPUT_STRUCTURE;
494
- if (outputStructure === "compact") {
495
- const iteratorDetails = getAsyncIteratorObjectDetails(def.outputSchemas);
496
- if (iteratorDetails) {
497
- operation.responses ??= {};
498
- operation.responses[status] = {
499
- description,
500
- content: toAsyncIteratorObjectContent(
501
- ctx,
502
- "output",
503
- ctx.convertSchemas(iteratorDetails[0], "output"),
504
- ctx.convertSchemas(iteratorDetails[1], "output")
505
- )
506
- };
507
- return;
508
- }
509
- }
510
- const [schema] = ctx.convertSchemas(def.outputSchemas, "output");
511
- if (isUnconstrainedSchema(schema) || outputStructure === "compact") {
512
- operation.responses ??= {};
513
- operation.responses[status] = {
514
- description,
515
- content: toBodyContent(ctx, "output", schema)
516
- };
517
- return;
518
- }
519
- for (const [responseStatus, parts] of extractDetailedResponseParts(schema, status)) {
520
- const responseObject = {
521
- description: parts.descriptions.length ? parts.descriptions.join(", ") : description
522
- };
523
- if (parts.bodies.length) {
524
- responseObject.content = toBodyContent(ctx, "output", combineJsonSchemasWithComposition("anyOf", parts.bodies));
525
- }
526
- if (parts.headers.length) {
527
- const entries = extractJsonObjectSchemaEntries(combineJsonSchemasWithComposition("anyOf", parts.headers));
528
- entries?.forEach(([name, schema2, optional]) => {
529
- responseObject.headers ??= {};
530
- responseObject.headers[name] = {
531
- required: optional ? void 0 : true,
532
- schema: ctx.registry.toOpenAPISchema(schema2, "output")
533
- };
534
- });
535
- }
536
- operation.responses ??= {};
537
- operation.responses[responseStatus] = responseObject;
538
- }
539
- }
540
- function extractDetailedResponseParts(schema, defaultStatus) {
541
- const partsByStatus = /* @__PURE__ */ new Map();
542
- for (const item of flattenJsonUnionSchema(schema)) {
543
- const entries = extractJsonObjectSchemaEntries(item);
544
- if (!entries) {
545
- throw new OpenAPIGeneratorError(
546
- `outputStructure is "detailed" but the output schema (or one of its union members) is not an object.
547
- Expected each member shaped like: { status?: number (< 400), headers?: object, body?: unknown }`
548
- );
549
- }
550
- const statusSchema = entries.find(([name]) => name === "status")?.[1];
551
- if (statusSchema !== void 0 && (typeof statusSchema !== "object" || !Number.isInteger(statusSchema.const) || statusSchema.const >= 400)) {
552
- throw new OpenAPIGeneratorError(
553
- `invalid "status" field in the detailed output schema.
554
- Expected: a literal (const) integer below 400
555
- Received: ${stringifyJSON(statusSchema)}`
556
- );
557
- }
558
- const status = (statusSchema?.const || void 0) ?? defaultStatus;
559
- let parts = partsByStatus.get(status);
560
- if (!parts) {
561
- parts = { descriptions: [], bodies: [], headers: [] };
562
- partsByStatus.set(status, parts);
563
- }
564
- if (statusSchema?.description !== void 0) {
565
- parts.descriptions.push(statusSchema.description);
566
- }
567
- const headersSchema = entries.find(([name]) => name === "headers")?.[1];
568
- if (headersSchema !== void 0) {
569
- parts.headers.push(headersSchema);
570
- }
571
- const bodySchema = entries.find(([name]) => name === "body")?.[1];
572
- if (bodySchema !== void 0) {
573
- parts.bodies.push(bodySchema);
574
- }
575
- }
576
- return partsByStatus;
577
- }
578
- function buildErrorResponse(ctx, operation, def) {
579
- const definitionsByStatus = /* @__PURE__ */ new Map();
580
- for (const code in def.errorMap) {
581
- const config = def.errorMap[code];
582
- if (!config) {
583
- continue;
584
- }
585
- const status = ctx.errorStatusMap[code] ?? DEFAULT_ERROR_STATUS;
586
- const [dataJsonSchema, dataOptional] = ctx.convertSchemas(config.data !== void 0 ? [config.data] : void 0, "output");
587
- let definitions = definitionsByStatus.get(status);
588
- if (!definitions) {
589
- definitions = [];
590
- definitionsByStatus.set(status, definitions);
591
- }
592
- definitions.push({ code, dataJsonSchema, dataOptional, defaultMessage: config.message });
593
- }
594
- if (!definitionsByStatus.size) {
595
- return;
596
- }
597
- const undefinedErrorSchema = ctx.registry.register("UndefinedError", {
598
- type: "object",
599
- properties: {
600
- defined: { const: false },
601
- inferable: { type: "boolean" },
602
- code: { type: "string" },
603
- status: { type: "number" },
604
- message: { type: "string" },
605
- data: {}
606
- },
607
- required: ["defined", "inferable", "code", "status", "message"]
608
- });
609
- for (const [status, definitions] of definitionsByStatus.entries()) {
610
- const descriptions = definitions.map(({ defaultMessage }) => defaultMessage).filter((m) => m !== void 0);
611
- const customBodySchema = value(
612
- ctx.customErrorResponseBodySchema,
613
- definitions.map((def2) => ({ ...def2, dataJsonSchema: ctx.registry.hoistDefs(def2.dataJsonSchema, "output") })),
614
- status
615
- );
616
- const responseSchema = customBodySchema ?? combineJsonSchemasWithComposition("oneOf", [
617
- ...definitions.map(({ code, dataJsonSchema, dataOptional }) => {
618
- return ctx.registry.register(toErrorComponentName(code), combineJsonObjectSchemaEntries([
619
- ["defined", { const: true }, false],
620
- ["inferable", { type: "boolean" }, false],
621
- ["code", { const: code }, false],
622
- ["status", { const: status }, false],
623
- // avoid using the defaultMessage here to improve component reusability
624
- ["message", { type: "string" }, false],
625
- ["data", ctx.registry.hoistDefs(dataJsonSchema, "output"), dataOptional]
626
- ]));
627
- }),
628
- undefinedErrorSchema
629
- ]);
630
- operation.responses ??= {};
631
- operation.responses[status] = {
632
- description: descriptions.length ? descriptions.join(", ") : status.toString(),
633
- content: {
634
- "application/json": {
635
- schema: ctx.registry.toOpenAPISchema(responseSchema, "output")
636
- }
22
+ } catch (e) {
23
+ if (e instanceof ORPCError) {
24
+ throw createORPCErrorFromJson(serializer.deserialize(
25
+ serializer.serialize(
26
+ e.toJSON(),
27
+ { outputFormat: "plain" }
28
+ )
29
+ ));
637
30
  }
638
- };
639
- }
640
- }
641
- function toErrorComponentName(code) {
642
- const name = code.split(/[^a-z0-9]+/i).filter(Boolean).map((part) => {
643
- const rest = part.slice(1);
644
- return part.charAt(0).toUpperCase() + (rest === rest.toUpperCase() ? rest.toLowerCase() : rest);
645
- }).join("");
646
- return name || "Error";
647
- }
648
- function getAsyncIteratorObjectDetails(schemas) {
649
- if (!schemas || schemas.length === 0) {
650
- return void 0;
651
- }
652
- const yieldSchemas = [];
653
- const returnSchemas = [];
654
- for (const s of schemas) {
655
- const details = getAsyncIteratorObjectSchemaDetails(s);
656
- if (!details) {
657
- return void 0;
658
- }
659
- yieldSchemas.push(details.yieldSchema);
660
- if (details.returnSchema) {
661
- returnSchemas.push(details.returnSchema);
662
- }
663
- }
664
- return [yieldSchemas, returnSchemas];
665
- }
666
- function toAsyncIteratorObjectContent(ctx, direction, [yieldSchema, yieldOptional], [returnSchema, returnOptional]) {
667
- const schema = combineJsonSchemasWithComposition("oneOf", [
668
- combineJsonObjectSchemaEntries([
669
- ["event", { const: "message" }, false],
670
- ["data", yieldSchema, yieldOptional],
671
- ["id", { type: "string" }, true],
672
- ["retry", { type: "number" }, true]
673
- ]),
674
- combineJsonObjectSchemaEntries([
675
- ["event", { const: "close" }, false],
676
- ["data", returnSchema, returnOptional],
677
- ["id", { type: "string" }, true],
678
- ["retry", { type: "number" }, true]
679
- ]),
680
- {
681
- type: "object",
682
- properties: {
683
- event: { const: "error" },
684
- data: {},
685
- id: { type: "string" },
686
- retry: { type: "number" }
687
- },
688
- required: ["event"]
689
- }
690
- ]);
691
- return {
692
- "text/event-stream": {
693
- schema: ctx.registry.toOpenAPISchema(schema, direction)
694
- }
695
- };
696
- }
697
- function toBodyContent(ctx, direction, schema) {
698
- const fileSchemasByMediaType = /* @__PURE__ */ new Map();
699
- const rest = flattenJsonUnionSchema(schema).filter((s) => {
700
- if (!isJsonFileSchema(s)) {
701
- return !isUnconstrainedSchema(s);
702
- }
703
- const contentMediaType = s.contentMediaType ?? "*/*";
704
- const schemas = fileSchemasByMediaType.get(contentMediaType);
705
- if (schemas) {
706
- schemas.push(s);
707
- } else {
708
- fileSchemasByMediaType.set(contentMediaType, [s]);
31
+ throw e;
709
32
  }
710
- return false;
711
33
  });
712
- const content = {};
713
- if (rest.length > 0) {
714
- const restSchema = fileSchemasByMediaType.size ? combineJsonSchemasWithComposition("anyOf", rest) : schema;
715
- const hasNestedFiles = findDeepMatches(
716
- (v) => isPlainObject(v) && isJsonFileSchema(v),
717
- restSchema
718
- ).values.length > 0;
719
- const contentType = hasNestedFiles ? "multipart/form-data" : "application/json";
720
- const fileSchemas = fileSchemasByMediaType.get(contentType);
721
- fileSchemasByMediaType.delete(contentType);
722
- content[contentType] = {
723
- schema: ctx.registry.toOpenAPISchema(combineJsonSchemasWithComposition("anyOf", [restSchema, ...fileSchemas ?? []]), direction)
724
- };
725
- }
726
- for (const [contentType, schemas] of fileSchemasByMediaType.entries()) {
727
- content[contentType] = {
728
- schema: ctx.registry.toOpenAPISchema(combineJsonSchemasWithComposition("anyOf", schemas), direction)
729
- };
730
- }
731
- return content;
34
+ return createRouterClient(router, options);
732
35
  }
733
36
 
734
- class OpenAPIGenerator {
735
- serializer;
736
- converter;
737
- constructor(options = {}) {
738
- this.serializer = options.serializer ?? new OpenAPISerializer();
739
- this.converter = new DelegatingJsonSchemaConverter([
740
- ...toArray(options.converters),
741
- new StandardJsonSchemaConverter()
742
- ]);
743
- }
744
- async generate(router, options = {}) {
745
- const doc = {
746
- ...clone(options.base),
747
- openapi: options.base?.openapi ?? "3.1.2",
748
- info: options.base?.info ?? { title: "API Reference", version: "0.0.0" }
749
- };
750
- const ctx = {
751
- registry: new OpenAPIComponentRegistry(doc, options.customComponentName),
752
- convertSchemas: (schemas, direction) => this.convertSchemas(schemas, direction),
753
- errorStatusMap: options.errorStatusMap ?? COMMON_ERROR_STATUS_MAP,
754
- customErrorResponseBodySchema: options.customErrorResponseBodySchema
755
- };
756
- const errors = [];
757
- await walkProcedureContractsAsync(router, (contract, path) => {
758
- if (value(options.filter, contract, path) === false) {
759
- return;
760
- }
761
- try {
762
- const def = contract["~orpc"];
763
- const meta = getOpenAPIMeta(contract);
764
- const method = (meta?.method ?? DEFAULT_OPENAPI_METHOD).toLowerCase();
765
- const postPath = meta?.path ?? pathToHttpPath(path);
766
- const httpPath = meta?.prefix ? mergeHttpPath(meta.prefix, postPath) : postPath;
767
- const dynamicPathParams = getDynamicPathParams(httpPath);
768
- const openApiPath = toOpenAPIPath(httpPath, dynamicPathParams);
769
- let operation;
770
- if (meta?.spec !== void 0 && typeof meta.spec !== "function") {
771
- operation = meta.spec;
772
- } else {
773
- operation = {
774
- operationId: meta?.operationId ?? path.join("."),
775
- summary: meta?.summary,
776
- description: meta?.description,
777
- deprecated: meta?.deprecated,
778
- tags: meta?.tags?.map((tag) => tag)
779
- };
780
- buildRequest(ctx, operation, def, meta, dynamicPathParams);
781
- buildSuccessResponse(ctx, operation, def, meta);
782
- buildErrorResponse(ctx, operation, def);
783
- }
784
- if (typeof meta?.spec === "function") {
785
- operation = meta.spec(operation);
786
- }
787
- doc.paths ??= {};
788
- doc.paths[openApiPath] ??= {};
789
- doc.paths[openApiPath][method] = operation;
790
- } catch (e) {
791
- if (!(e instanceof OpenAPIGeneratorError)) {
792
- throw e;
793
- }
794
- errors.push(`Procedure at ${path.join(".") || "(root)"}: ${e.message}`);
795
- }
796
- });
797
- if (errors.length) {
798
- throw new OpenAPIGeneratorError(
799
- `[OpenAPIGenerator] Failed to generate the OpenAPI document (${errors.length} error${errors.length === 1 ? "" : "s"}):
800
-
801
- ${errors.join("\n\n")}`
802
- );
803
- }
804
- return this.serializer.serialize(doc, { asFormData: false, useFormDataForBlobFields: false });
805
- }
806
- convertSchema(schema, direction) {
807
- const [jsonSchema, optional] = this.converter.convert(schema, direction);
808
- return [strip$schemaField(jsonSchema), optional];
809
- }
810
- convertSchemas(schemas, direction) {
811
- if (!schemas || schemas.length <= 1) {
812
- return this.convertSchema(schemas?.[0], direction);
813
- }
814
- const results = schemas.map((s) => this.convertSchema(s, direction));
815
- return [
816
- combineJsonSchemasWithComposition("allOf", results.map(([jsonSchema]) => jsonSchema)),
817
- results.every(([, optional]) => optional)
818
- ];
819
- }
820
- }
821
- function strip$schemaField(schema) {
822
- if (typeof schema !== "object") {
823
- return schema;
824
- }
825
- const { $schema, ...rest } = schema;
826
- return rest;
827
- }
828
-
829
- function populateRouterContractOpenAPIPaths(router, options = {}) {
830
- const path = toArray(options.path);
831
- if (router instanceof ProcedureContract) {
832
- if (getOpenAPIMeta(router)?.path !== void 0) {
833
- return router;
834
- }
835
- const [meta, metaPlugins] = resolveMetaPlugins(
836
- router["~orpc"].meta,
837
- router["~orpc"].metaPlugins,
838
- [openapi({ path: pathToHttpPath(path) })]
839
- );
840
- return new ProcedureContract({
841
- ...router["~orpc"],
842
- meta,
843
- metaPlugins
844
- });
845
- }
846
- if (!isTypescriptObject(router)) {
847
- return router;
848
- }
849
- const populated = {};
850
- for (const key in router) {
851
- populated[key] = populateRouterContractOpenAPIPaths(
852
- router[key],
853
- { ...options, path: [...path, key] }
854
- );
855
- }
856
- return populated;
857
- }
37
+ const oo = {
38
+ spec: customOpenAPIOperation
39
+ };
858
40
 
859
- export { DEFAULT_OPENAPI_INPUT_STRUCTURE, DEFAULT_OPENAPI_METHOD, DEFAULT_OPENAPI_OUTPUT_STRUCTURE, DEFAULT_OPENAPI_SUCCESS_DESCRIPTION, OpenAPIGenerator, OpenAPIGeneratorError, OpenAPISerializer, createContractJsonifiedClientFactory, getDynamicPathParams, getOpenAPIMeta, isBodylessMethod, openapi, populateRouterContractOpenAPIPaths };
41
+ export { createJsonifiedRouterClient, customOpenAPIOperation, oo };